• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Copyright (c) 2023 MediaTek Inc.
4  */
5 
6 #include <linux/debugfs.h>
7 #include <linux/device.h>
8 #include <linux/file.h>
9 #include <linux/kdev_t.h>
10 #include <linux/kobject.h>
11 #include <linux/miscdevice.h>
12 #include <linux/module.h>
13 #include <linux/of.h>
14 #include <linux/platform_device.h>
15 #include <linux/sysfs.h>
16 #include <linux/soc/mediatek/gzvm_drv.h>
17 
18 static struct gzvm_driver gzvm_drv = {
19 	.drv_version = {
20 		.major = GZVM_DRV_MAJOR_VERSION,
21 		.minor = GZVM_DRV_MINOR_VERSION,
22 		.sub = 0,
23 	},
24 };
25 
demand_paging_batch_pages_show(struct kobject * kobj,struct kobj_attribute * attr,char * buf)26 static ssize_t demand_paging_batch_pages_show(struct kobject *kobj,
27 					      struct kobj_attribute *attr,
28 					      char *buf)
29 {
30 	return sprintf(buf, "%u\n", gzvm_drv.demand_paging_batch_pages);
31 }
32 
demand_paging_batch_pages_store(struct kobject * kobj,struct kobj_attribute * attr,const char * buf,size_t count)33 static ssize_t demand_paging_batch_pages_store(struct kobject *kobj,
34 					       struct kobj_attribute *attr,
35 					       const char *buf, size_t count)
36 {
37 	int ret;
38 	u32 temp;
39 
40 	ret = kstrtoint(buf, 10, &temp);
41 	if (ret < 0)
42 		return ret;
43 
44 	if (temp == 0 || (PMD_SIZE % (PAGE_SIZE * temp)) != 0)
45 		return -EINVAL;
46 
47 	gzvm_drv.demand_paging_batch_pages = temp;
48 
49 	return count;
50 }
51 
destroy_batch_pages_show(struct kobject * kobj,struct kobj_attribute * attr,char * buf)52 static ssize_t destroy_batch_pages_show(struct kobject *kobj,
53 					struct kobj_attribute *attr,
54 					char *buf)
55 {
56 	return sprintf(buf, "%u\n", gzvm_drv.destroy_batch_pages);
57 }
58 
destroy_batch_pages_store(struct kobject * kobj,struct kobj_attribute * attr,const char * buf,size_t count)59 static ssize_t destroy_batch_pages_store(struct kobject *kobj,
60 					 struct kobj_attribute *attr,
61 					 const char *buf, size_t count)
62 {
63 	int ret;
64 	u32 temp;
65 
66 	ret = kstrtoint(buf, 10, &temp);
67 	if (ret < 0)
68 		return ret;
69 
70 	// destroy page batch size should be power of 2
71 	if ((temp & (temp - 1)) != 0)
72 		return -EINVAL;
73 
74 	gzvm_drv.destroy_batch_pages = temp;
75 
76 	return count;
77 }
78 
79 /* /sys/kernel/gzvm/demand_paging_batch_pages */
80 static struct kobj_attribute demand_paging_batch_pages_attr = {
81 		.attr = {
82 			.name = "demand_paging_batch_pages",
83 			.mode = 0660,
84 		},
85 		.show = demand_paging_batch_pages_show,
86 		.store = demand_paging_batch_pages_store,
87 };
88 
89 /* /sys/kernel/gzvm/destroy_batch_pages */
90 static struct kobj_attribute destroy_batch_pages_attr = {
91 		.attr = {
92 			.name = "destroy_batch_pages",
93 			.mode = 0660,
94 		},
95 		.show = destroy_batch_pages_show,
96 		.store = destroy_batch_pages_store,
97 };
98 
gzvm_drv_sysfs_init(void)99 static int gzvm_drv_sysfs_init(void)
100 {
101 	int ret = 0;
102 
103 	gzvm_drv.sysfs_root_dir = kobject_create_and_add("gzvm", kernel_kobj);
104 
105 	if (!gzvm_drv.sysfs_root_dir)
106 		return -ENOMEM;
107 
108 	ret = sysfs_create_file(gzvm_drv.sysfs_root_dir,
109 				&demand_paging_batch_pages_attr.attr);
110 	if (ret)
111 		pr_debug("failed to create demand_batch_pages in /sys/kernel/gzvm\n");
112 
113 	ret = sysfs_create_file(gzvm_drv.sysfs_root_dir,
114 				&destroy_batch_pages_attr.attr);
115 	if (ret)
116 		pr_debug("failed to create destroy_batch_pages in /sys/kernel/gzvm\n");
117 
118 	return ret;
119 }
120 
gzvm_drv_sysfs_exit(void)121 static void gzvm_drv_sysfs_exit(void)
122 {
123 	kobject_del(gzvm_drv.sysfs_root_dir);
124 }
125 
gzvm_drv_debug_init(void)126 static int gzvm_drv_debug_init(void)
127 {
128 	if (!debugfs_initialized()) {
129 		pr_warn("debugfs not initialized!\n");
130 		return 0;
131 	}
132 
133 	gzvm_drv.gzvm_debugfs_dir = debugfs_create_dir("gzvm", NULL);
134 	if (!gzvm_drv.gzvm_debugfs_dir)
135 		return -ENOMEM;
136 
137 	return 0;
138 }
139 
gzvm_drv_debug_exit(void)140 static void gzvm_drv_debug_exit(void)
141 {
142 	debugfs_remove_recursive(gzvm_drv.gzvm_debugfs_dir);
143 }
144 
145 /**
146  * gzvm_err_to_errno() - Convert geniezone return value to standard errno
147  *
148  * @err: Return value from geniezone function return
149  *
150  * Return: Standard errno
151  */
gzvm_err_to_errno(unsigned long err)152 int gzvm_err_to_errno(unsigned long err)
153 {
154 	int gz_err = (int)err;
155 
156 	switch (gz_err) {
157 	case 0:
158 		return 0;
159 	case ERR_NO_MEMORY:
160 		return -ENOMEM;
161 	case ERR_INVALID_ARGS:
162 		return -EINVAL;
163 	case ERR_NOT_SUPPORTED:
164 		fallthrough;
165 	case ERR_NOT_IMPLEMENTED:
166 		return -EOPNOTSUPP;
167 	case ERR_FAULT:
168 		return -EFAULT;
169 	case ERR_BUSY:
170 		return -EAGAIN;
171 	default:
172 		break;
173 	}
174 
175 	return -EINVAL;
176 }
177 
178 /**
179  * gzvm_dev_ioctl_check_extension() - Check if given capability is support
180  *				      or not
181  *
182  * @gzvm: Pointer to struct gzvm
183  * @args: Pointer in u64 from userspace
184  *
185  * Return:
186  * * 0			- Supported, no error
187  * * -EOPNOTSUPP	- Unsupported
188  * * -EFAULT		- Failed to get data from userspace
189  */
gzvm_dev_ioctl_check_extension(struct gzvm * gzvm,unsigned long args)190 long gzvm_dev_ioctl_check_extension(struct gzvm *gzvm, unsigned long args)
191 {
192 	__u64 cap;
193 	void __user *argp = (void __user *)args;
194 
195 	if (copy_from_user(&cap, argp, sizeof(uint64_t)))
196 		return -EFAULT;
197 	return gzvm_arch_check_extension(gzvm, cap, argp);
198 }
199 
gzvm_dev_ioctl(struct file * filp,unsigned int cmd,unsigned long user_args)200 static long gzvm_dev_ioctl(struct file *filp, unsigned int cmd,
201 			   unsigned long user_args)
202 {
203 	switch (cmd) {
204 	case GZVM_CREATE_VM:
205 		return gzvm_dev_ioctl_create_vm(&gzvm_drv, user_args);
206 	case GZVM_CHECK_EXTENSION:
207 		if (!user_args)
208 			return -EINVAL;
209 		return gzvm_dev_ioctl_check_extension(NULL, user_args);
210 	default:
211 		break;
212 	}
213 
214 	return -ENOTTY;
215 }
216 
gzvm_dev_open(struct inode * inode,struct file * file)217 static int gzvm_dev_open(struct inode *inode, struct file *file)
218 {
219 	/*
220 	 * Reference count to prevent this module is unload without destroying
221 	 * VM
222 	 */
223 	try_module_get(THIS_MODULE);
224 	return 0;
225 }
226 
gzvm_dev_release(struct inode * inode,struct file * file)227 static int gzvm_dev_release(struct inode *inode, struct file *file)
228 {
229 	module_put(THIS_MODULE);
230 	return 0;
231 }
232 
233 static const struct file_operations gzvm_chardev_ops = {
234 	.unlocked_ioctl = gzvm_dev_ioctl,
235 	.llseek		= noop_llseek,
236 	.open		= gzvm_dev_open,
237 	.release	= gzvm_dev_release,
238 };
239 
240 static struct miscdevice gzvm_dev = {
241 	.minor = MISC_DYNAMIC_MINOR,
242 	.name = KBUILD_MODNAME,
243 	.fops = &gzvm_chardev_ops,
244 };
245 
gzvm_query_hyp_batch_pages(void)246 static int gzvm_query_hyp_batch_pages(void)
247 {
248 	struct gzvm_enable_cap cap = {0};
249 	int ret;
250 
251 	gzvm_drv.demand_paging_batch_pages = GZVM_DRV_DEMAND_PAGING_BATCH_PAGES;
252 	cap.cap = GZVM_CAP_QUERY_HYP_BATCH_PAGES;
253 
254 	ret = gzvm_arch_query_hyp_batch_pages(&cap, NULL);
255 	if (!ret)
256 		gzvm_drv.demand_paging_batch_pages = cap.args[0];
257 
258 	/*
259 	 * We have initialized demand_paging_batch_pages, and to maintain
260 	 * compatibility with older GZ version, we can ignore the return value.
261 	 */
262 	if (ret == -EINVAL)
263 		return 0;
264 	return ret;
265 }
266 
gzvm_query_destroy_batch_pages(void)267 static int gzvm_query_destroy_batch_pages(void)
268 {
269 	int ret;
270 	struct gzvm_enable_cap cap = {0};
271 
272 	gzvm_drv.destroy_batch_pages = GZVM_DRV_DESTROY_PAGING_BATCH_PAGES;
273 	cap.cap = GZVM_CAP_QUERY_DESTROY_BATCH_PAGES;
274 
275 	ret = gzvm_arch_query_destroy_batch_pages(&cap, NULL);
276 	if (!ret)
277 		gzvm_drv.destroy_batch_pages = cap.args[0];
278 	return ret;
279 }
280 
gzvm_drv_probe(struct platform_device * pdev)281 static int gzvm_drv_probe(struct platform_device *pdev)
282 {
283 	int ret;
284 
285 	if (gzvm_arch_probe(gzvm_drv.drv_version, &gzvm_drv.hyp_version) != 0) {
286 		dev_err(&pdev->dev, "Not found available conduit\n");
287 		return -ENODEV;
288 	}
289 
290 	pr_debug("Found GenieZone hypervisor version %u.%u.%llu\n",
291 		 gzvm_drv.hyp_version.major, gzvm_drv.hyp_version.minor,
292 		 gzvm_drv.hyp_version.sub);
293 
294 	ret = gzvm_arch_drv_init();
295 	if (ret)
296 		return ret;
297 
298 	ret = misc_register(&gzvm_dev);
299 	if (ret)
300 		return ret;
301 
302 	ret = gzvm_drv_irqfd_init();
303 	if (ret)
304 		return ret;
305 
306 	ret = gzvm_drv_debug_init();
307 	if (ret)
308 		return ret;
309 
310 	ret = gzvm_drv_sysfs_init();
311 	if (ret)
312 		return ret;
313 
314 	ret = gzvm_query_hyp_batch_pages();
315 	if (ret)
316 		return ret;
317 
318 	ret = gzvm_query_destroy_batch_pages();
319 	if (ret)
320 		return ret;
321 
322 	return 0;
323 }
324 
gzvm_drv_remove(struct platform_device * pdev)325 static void gzvm_drv_remove(struct platform_device *pdev)
326 {
327 	gzvm_drv_irqfd_exit();
328 	misc_deregister(&gzvm_dev);
329 	gzvm_drv_debug_exit();
330 	gzvm_drv_sysfs_exit();
331 }
332 
333 static const struct of_device_id gzvm_of_match[] = {
334 	{ .compatible = "mediatek,geniezone" },
335 	{/* sentinel */},
336 };
337 
338 static struct platform_driver gzvm_driver = {
339 	.probe = gzvm_drv_probe,
340 	.remove = gzvm_drv_remove,
341 	.driver = {
342 		.name = KBUILD_MODNAME,
343 		.of_match_table = gzvm_of_match,
344 	},
345 };
346 
347 module_platform_driver(gzvm_driver);
348 
349 MODULE_DEVICE_TABLE(of, gzvm_of_match);
350 MODULE_AUTHOR("MediaTek");
351 MODULE_DESCRIPTION("GenieZone interface for VMM");
352 MODULE_LICENSE("GPL");
353