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