1// SPDX-License-Identifier: GPL-2.0
2/*
3 * ACRN Hypervisor Service Module (HSM)
4 *
5 * Copyright (C) 2020 Intel Corporation. All rights reserved.
6 *
7 * Authors:
8 * Fengwei Yin <fengwei.yin@intel.com>
9 * Yakui Zhao <yakui.zhao@intel.com>
10 */
11
12#include <linux/cpu.h>
13#include <linux/io.h>
14#include <linux/mm.h>
15#include <linux/module.h>
16#include <linux/slab.h>
17
18#include <asm/acrn.h>
19#include <asm/hypervisor.h>
20
21#include "acrn_drv.h"
22
23/*
24 * When /dev/acrn_hsm is opened, a 'struct acrn_vm' object is created to
25 * represent a VM instance and continues to be associated with the opened file
26 * descriptor. All ioctl operations on this file descriptor will be targeted to
27 * the VM instance. Release of this file descriptor will destroy the object.
28 */
29static int acrn_dev_open(struct inode *inode, struct file *filp)
30{
31 struct acrn_vm *vm;
32
33 vm = kzalloc(size: sizeof(*vm), GFP_KERNEL);
34 if (!vm)
35 return -ENOMEM;
36
37 vm->vmid = ACRN_INVALID_VMID;
38 filp->private_data = vm;
39 return 0;
40}
41
42static int pmcmd_ioctl(u64 cmd, void __user *uptr)
43{
44 struct acrn_pstate_data *px_data;
45 struct acrn_cstate_data *cx_data;
46 u64 *pm_info;
47 int ret = 0;
48
49 switch (cmd & PMCMD_TYPE_MASK) {
50 case ACRN_PMCMD_GET_PX_CNT:
51 case ACRN_PMCMD_GET_CX_CNT:
52 pm_info = kmalloc(size: sizeof(u64), GFP_KERNEL);
53 if (!pm_info)
54 return -ENOMEM;
55
56 ret = hcall_get_cpu_state(cmd, virt_to_phys(address: pm_info));
57 if (ret < 0) {
58 kfree(objp: pm_info);
59 break;
60 }
61
62 if (copy_to_user(to: uptr, from: pm_info, n: sizeof(u64)))
63 ret = -EFAULT;
64 kfree(objp: pm_info);
65 break;
66 case ACRN_PMCMD_GET_PX_DATA:
67 px_data = kmalloc(size: sizeof(*px_data), GFP_KERNEL);
68 if (!px_data)
69 return -ENOMEM;
70
71 ret = hcall_get_cpu_state(cmd, virt_to_phys(address: px_data));
72 if (ret < 0) {
73 kfree(objp: px_data);
74 break;
75 }
76
77 if (copy_to_user(to: uptr, from: px_data, n: sizeof(*px_data)))
78 ret = -EFAULT;
79 kfree(objp: px_data);
80 break;
81 case ACRN_PMCMD_GET_CX_DATA:
82 cx_data = kmalloc(size: sizeof(*cx_data), GFP_KERNEL);
83 if (!cx_data)
84 return -ENOMEM;
85
86 ret = hcall_get_cpu_state(cmd, virt_to_phys(address: cx_data));
87 if (ret < 0) {
88 kfree(objp: cx_data);
89 break;
90 }
91
92 if (copy_to_user(to: uptr, from: cx_data, n: sizeof(*cx_data)))
93 ret = -EFAULT;
94 kfree(objp: cx_data);
95 break;
96 default:
97 break;
98 }
99
100 return ret;
101}
102
103/*
104 * HSM relies on hypercall layer of the ACRN hypervisor to do the
105 * sanity check against the input parameters.
106 */
107static long acrn_dev_ioctl(struct file *filp, unsigned int cmd,
108 unsigned long ioctl_param)
109{
110 struct acrn_vm *vm = filp->private_data;
111 struct acrn_vm_creation *vm_param;
112 struct acrn_vcpu_regs *cpu_regs;
113 struct acrn_ioreq_notify notify;
114 struct acrn_ptdev_irq *irq_info;
115 struct acrn_ioeventfd ioeventfd;
116 struct acrn_vm_memmap memmap;
117 struct acrn_mmiodev *mmiodev;
118 struct acrn_msi_entry *msi;
119 struct acrn_pcidev *pcidev;
120 struct acrn_irqfd irqfd;
121 struct acrn_vdev *vdev;
122 struct page *page;
123 u64 cstate_cmd;
124 int i, ret = 0;
125
126 if (vm->vmid == ACRN_INVALID_VMID && cmd != ACRN_IOCTL_CREATE_VM) {
127 dev_dbg(acrn_dev.this_device,
128 "ioctl 0x%x: Invalid VM state!\n", cmd);
129 return -EINVAL;
130 }
131
132 switch (cmd) {
133 case ACRN_IOCTL_CREATE_VM:
134 vm_param = memdup_user((void __user *)ioctl_param,
135 sizeof(struct acrn_vm_creation));
136 if (IS_ERR(ptr: vm_param))
137 return PTR_ERR(ptr: vm_param);
138
139 if ((vm_param->reserved0 | vm_param->reserved1) != 0) {
140 kfree(objp: vm_param);
141 return -EINVAL;
142 }
143
144 vm = acrn_vm_create(vm, vm_param);
145 if (!vm) {
146 ret = -EINVAL;
147 kfree(objp: vm_param);
148 break;
149 }
150
151 if (copy_to_user(to: (void __user *)ioctl_param, from: vm_param,
152 n: sizeof(struct acrn_vm_creation))) {
153 acrn_vm_destroy(vm);
154 ret = -EFAULT;
155 }
156
157 kfree(objp: vm_param);
158 break;
159 case ACRN_IOCTL_START_VM:
160 ret = hcall_start_vm(vmid: vm->vmid);
161 if (ret < 0)
162 dev_dbg(acrn_dev.this_device,
163 "Failed to start VM %u!\n", vm->vmid);
164 break;
165 case ACRN_IOCTL_PAUSE_VM:
166 ret = hcall_pause_vm(vmid: vm->vmid);
167 if (ret < 0)
168 dev_dbg(acrn_dev.this_device,
169 "Failed to pause VM %u!\n", vm->vmid);
170 break;
171 case ACRN_IOCTL_RESET_VM:
172 ret = hcall_reset_vm(vmid: vm->vmid);
173 if (ret < 0)
174 dev_dbg(acrn_dev.this_device,
175 "Failed to restart VM %u!\n", vm->vmid);
176 break;
177 case ACRN_IOCTL_DESTROY_VM:
178 ret = acrn_vm_destroy(vm);
179 break;
180 case ACRN_IOCTL_SET_VCPU_REGS:
181 cpu_regs = memdup_user((void __user *)ioctl_param,
182 sizeof(struct acrn_vcpu_regs));
183 if (IS_ERR(ptr: cpu_regs))
184 return PTR_ERR(ptr: cpu_regs);
185
186 for (i = 0; i < ARRAY_SIZE(cpu_regs->reserved); i++)
187 if (cpu_regs->reserved[i]) {
188 kfree(objp: cpu_regs);
189 return -EINVAL;
190 }
191
192 for (i = 0; i < ARRAY_SIZE(cpu_regs->vcpu_regs.reserved_32); i++)
193 if (cpu_regs->vcpu_regs.reserved_32[i]) {
194 kfree(objp: cpu_regs);
195 return -EINVAL;
196 }
197
198 for (i = 0; i < ARRAY_SIZE(cpu_regs->vcpu_regs.reserved_64); i++)
199 if (cpu_regs->vcpu_regs.reserved_64[i]) {
200 kfree(objp: cpu_regs);
201 return -EINVAL;
202 }
203
204 for (i = 0; i < ARRAY_SIZE(cpu_regs->vcpu_regs.gdt.reserved); i++)
205 if (cpu_regs->vcpu_regs.gdt.reserved[i] |
206 cpu_regs->vcpu_regs.idt.reserved[i]) {
207 kfree(objp: cpu_regs);
208 return -EINVAL;
209 }
210
211 ret = hcall_set_vcpu_regs(vmid: vm->vmid, virt_to_phys(address: cpu_regs));
212 if (ret < 0)
213 dev_dbg(acrn_dev.this_device,
214 "Failed to set regs state of VM%u!\n",
215 vm->vmid);
216 kfree(objp: cpu_regs);
217 break;
218 case ACRN_IOCTL_SET_MEMSEG:
219 if (copy_from_user(to: &memmap, from: (void __user *)ioctl_param,
220 n: sizeof(memmap)))
221 return -EFAULT;
222
223 ret = acrn_vm_memseg_map(vm, memmap: &memmap);
224 break;
225 case ACRN_IOCTL_UNSET_MEMSEG:
226 if (copy_from_user(to: &memmap, from: (void __user *)ioctl_param,
227 n: sizeof(memmap)))
228 return -EFAULT;
229
230 ret = acrn_vm_memseg_unmap(vm, memmap: &memmap);
231 break;
232 case ACRN_IOCTL_ASSIGN_MMIODEV:
233 mmiodev = memdup_user((void __user *)ioctl_param,
234 sizeof(struct acrn_mmiodev));
235 if (IS_ERR(ptr: mmiodev))
236 return PTR_ERR(ptr: mmiodev);
237
238 ret = hcall_assign_mmiodev(vmid: vm->vmid, virt_to_phys(address: mmiodev));
239 if (ret < 0)
240 dev_dbg(acrn_dev.this_device,
241 "Failed to assign MMIO device!\n");
242 kfree(objp: mmiodev);
243 break;
244 case ACRN_IOCTL_DEASSIGN_MMIODEV:
245 mmiodev = memdup_user((void __user *)ioctl_param,
246 sizeof(struct acrn_mmiodev));
247 if (IS_ERR(ptr: mmiodev))
248 return PTR_ERR(ptr: mmiodev);
249
250 ret = hcall_deassign_mmiodev(vmid: vm->vmid, virt_to_phys(address: mmiodev));
251 if (ret < 0)
252 dev_dbg(acrn_dev.this_device,
253 "Failed to deassign MMIO device!\n");
254 kfree(objp: mmiodev);
255 break;
256 case ACRN_IOCTL_ASSIGN_PCIDEV:
257 pcidev = memdup_user((void __user *)ioctl_param,
258 sizeof(struct acrn_pcidev));
259 if (IS_ERR(ptr: pcidev))
260 return PTR_ERR(ptr: pcidev);
261
262 ret = hcall_assign_pcidev(vmid: vm->vmid, virt_to_phys(address: pcidev));
263 if (ret < 0)
264 dev_dbg(acrn_dev.this_device,
265 "Failed to assign pci device!\n");
266 kfree(objp: pcidev);
267 break;
268 case ACRN_IOCTL_DEASSIGN_PCIDEV:
269 pcidev = memdup_user((void __user *)ioctl_param,
270 sizeof(struct acrn_pcidev));
271 if (IS_ERR(ptr: pcidev))
272 return PTR_ERR(ptr: pcidev);
273
274 ret = hcall_deassign_pcidev(vmid: vm->vmid, virt_to_phys(address: pcidev));
275 if (ret < 0)
276 dev_dbg(acrn_dev.this_device,
277 "Failed to deassign pci device!\n");
278 kfree(objp: pcidev);
279 break;
280 case ACRN_IOCTL_CREATE_VDEV:
281 vdev = memdup_user((void __user *)ioctl_param,
282 sizeof(struct acrn_vdev));
283 if (IS_ERR(ptr: vdev))
284 return PTR_ERR(ptr: vdev);
285
286 ret = hcall_create_vdev(vmid: vm->vmid, virt_to_phys(address: vdev));
287 if (ret < 0)
288 dev_dbg(acrn_dev.this_device,
289 "Failed to create virtual device!\n");
290 kfree(objp: vdev);
291 break;
292 case ACRN_IOCTL_DESTROY_VDEV:
293 vdev = memdup_user((void __user *)ioctl_param,
294 sizeof(struct acrn_vdev));
295 if (IS_ERR(ptr: vdev))
296 return PTR_ERR(ptr: vdev);
297 ret = hcall_destroy_vdev(vmid: vm->vmid, virt_to_phys(address: vdev));
298 if (ret < 0)
299 dev_dbg(acrn_dev.this_device,
300 "Failed to destroy virtual device!\n");
301 kfree(objp: vdev);
302 break;
303 case ACRN_IOCTL_SET_PTDEV_INTR:
304 irq_info = memdup_user((void __user *)ioctl_param,
305 sizeof(struct acrn_ptdev_irq));
306 if (IS_ERR(ptr: irq_info))
307 return PTR_ERR(ptr: irq_info);
308
309 ret = hcall_set_ptdev_intr(vmid: vm->vmid, virt_to_phys(address: irq_info));
310 if (ret < 0)
311 dev_dbg(acrn_dev.this_device,
312 "Failed to configure intr for ptdev!\n");
313 kfree(objp: irq_info);
314 break;
315 case ACRN_IOCTL_RESET_PTDEV_INTR:
316 irq_info = memdup_user((void __user *)ioctl_param,
317 sizeof(struct acrn_ptdev_irq));
318 if (IS_ERR(ptr: irq_info))
319 return PTR_ERR(ptr: irq_info);
320
321 ret = hcall_reset_ptdev_intr(vmid: vm->vmid, virt_to_phys(address: irq_info));
322 if (ret < 0)
323 dev_dbg(acrn_dev.this_device,
324 "Failed to reset intr for ptdev!\n");
325 kfree(objp: irq_info);
326 break;
327 case ACRN_IOCTL_SET_IRQLINE:
328 ret = hcall_set_irqline(vmid: vm->vmid, op: ioctl_param);
329 if (ret < 0)
330 dev_dbg(acrn_dev.this_device,
331 "Failed to set interrupt line!\n");
332 break;
333 case ACRN_IOCTL_INJECT_MSI:
334 msi = memdup_user((void __user *)ioctl_param,
335 sizeof(struct acrn_msi_entry));
336 if (IS_ERR(ptr: msi))
337 return PTR_ERR(ptr: msi);
338
339 ret = hcall_inject_msi(vmid: vm->vmid, virt_to_phys(address: msi));
340 if (ret < 0)
341 dev_dbg(acrn_dev.this_device,
342 "Failed to inject MSI!\n");
343 kfree(objp: msi);
344 break;
345 case ACRN_IOCTL_VM_INTR_MONITOR:
346 ret = pin_user_pages_fast(start: ioctl_param, nr_pages: 1,
347 gup_flags: FOLL_WRITE | FOLL_LONGTERM, pages: &page);
348 if (unlikely(ret != 1)) {
349 dev_dbg(acrn_dev.this_device,
350 "Failed to pin intr hdr buffer!\n");
351 return -EFAULT;
352 }
353
354 ret = hcall_vm_intr_monitor(vmid: vm->vmid, page_to_phys(page));
355 if (ret < 0) {
356 unpin_user_page(page);
357 dev_dbg(acrn_dev.this_device,
358 "Failed to monitor intr data!\n");
359 return ret;
360 }
361 if (vm->monitor_page)
362 unpin_user_page(page: vm->monitor_page);
363 vm->monitor_page = page;
364 break;
365 case ACRN_IOCTL_CREATE_IOREQ_CLIENT:
366 if (vm->default_client)
367 return -EEXIST;
368 if (!acrn_ioreq_client_create(vm, NULL, NULL, is_default: true, name: "acrndm"))
369 ret = -EINVAL;
370 break;
371 case ACRN_IOCTL_DESTROY_IOREQ_CLIENT:
372 if (vm->default_client)
373 acrn_ioreq_client_destroy(client: vm->default_client);
374 break;
375 case ACRN_IOCTL_ATTACH_IOREQ_CLIENT:
376 if (vm->default_client)
377 ret = acrn_ioreq_client_wait(client: vm->default_client);
378 else
379 ret = -ENODEV;
380 break;
381 case ACRN_IOCTL_NOTIFY_REQUEST_FINISH:
382 if (copy_from_user(to: &notify, from: (void __user *)ioctl_param,
383 n: sizeof(struct acrn_ioreq_notify)))
384 return -EFAULT;
385
386 if (notify.reserved != 0)
387 return -EINVAL;
388
389 ret = acrn_ioreq_request_default_complete(vm, vcpu: notify.vcpu);
390 break;
391 case ACRN_IOCTL_CLEAR_VM_IOREQ:
392 acrn_ioreq_request_clear(vm);
393 break;
394 case ACRN_IOCTL_PM_GET_CPU_STATE:
395 if (copy_from_user(to: &cstate_cmd, from: (void __user *)ioctl_param,
396 n: sizeof(cstate_cmd)))
397 return -EFAULT;
398
399 ret = pmcmd_ioctl(cmd: cstate_cmd, uptr: (void __user *)ioctl_param);
400 break;
401 case ACRN_IOCTL_IOEVENTFD:
402 if (copy_from_user(to: &ioeventfd, from: (void __user *)ioctl_param,
403 n: sizeof(ioeventfd)))
404 return -EFAULT;
405
406 if (ioeventfd.reserved != 0)
407 return -EINVAL;
408
409 ret = acrn_ioeventfd_config(vm, args: &ioeventfd);
410 break;
411 case ACRN_IOCTL_IRQFD:
412 if (copy_from_user(to: &irqfd, from: (void __user *)ioctl_param,
413 n: sizeof(irqfd)))
414 return -EFAULT;
415 ret = acrn_irqfd_config(vm, args: &irqfd);
416 break;
417 default:
418 dev_dbg(acrn_dev.this_device, "Unknown IOCTL 0x%x!\n", cmd);
419 ret = -ENOTTY;
420 }
421
422 return ret;
423}
424
425static int acrn_dev_release(struct inode *inode, struct file *filp)
426{
427 struct acrn_vm *vm = filp->private_data;
428
429 acrn_vm_destroy(vm);
430 kfree(objp: vm);
431 return 0;
432}
433
434static ssize_t remove_cpu_store(struct device *dev,
435 struct device_attribute *attr,
436 const char *buf, size_t count)
437{
438 u64 cpu, lapicid;
439 int ret;
440
441 if (kstrtoull(s: buf, base: 0, res: &cpu) < 0)
442 return -EINVAL;
443
444 if (cpu >= num_possible_cpus() || cpu == 0 || !cpu_is_hotpluggable(cpu))
445 return -EINVAL;
446
447 if (cpu_online(cpu))
448 remove_cpu(cpu);
449
450 lapicid = cpu_data(cpu).topo.apicid;
451 dev_dbg(dev, "Try to remove cpu %lld with lapicid %lld\n", cpu, lapicid);
452 ret = hcall_sos_remove_cpu(cpu: lapicid);
453 if (ret < 0) {
454 dev_err(dev, "Failed to remove cpu %lld!\n", cpu);
455 goto fail_remove;
456 }
457
458 return count;
459
460fail_remove:
461 add_cpu(cpu);
462 return ret;
463}
464static DEVICE_ATTR_WO(remove_cpu);
465
466static umode_t acrn_attr_visible(struct kobject *kobj, struct attribute *a, int n)
467{
468 if (a == &dev_attr_remove_cpu.attr)
469 return IS_ENABLED(CONFIG_HOTPLUG_CPU) ? a->mode : 0;
470
471 return a->mode;
472}
473
474static struct attribute *acrn_attrs[] = {
475 &dev_attr_remove_cpu.attr,
476 NULL
477};
478
479static struct attribute_group acrn_attr_group = {
480 .attrs = acrn_attrs,
481 .is_visible = acrn_attr_visible,
482};
483
484static const struct attribute_group *acrn_attr_groups[] = {
485 &acrn_attr_group,
486 NULL
487};
488
489static const struct file_operations acrn_fops = {
490 .owner = THIS_MODULE,
491 .open = acrn_dev_open,
492 .release = acrn_dev_release,
493 .unlocked_ioctl = acrn_dev_ioctl,
494};
495
496struct miscdevice acrn_dev = {
497 .minor = MISC_DYNAMIC_MINOR,
498 .name = "acrn_hsm",
499 .fops = &acrn_fops,
500 .groups = acrn_attr_groups,
501};
502
503static int __init hsm_init(void)
504{
505 int ret;
506
507 if (x86_hyper_type != X86_HYPER_ACRN)
508 return -ENODEV;
509
510 if (!(cpuid_eax(ACRN_CPUID_FEATURES) & ACRN_FEATURE_PRIVILEGED_VM))
511 return -EPERM;
512
513 ret = misc_register(misc: &acrn_dev);
514 if (ret) {
515 pr_err("Create misc dev failed!\n");
516 return ret;
517 }
518
519 ret = acrn_ioreq_intr_setup();
520 if (ret) {
521 pr_err("Setup I/O request handler failed!\n");
522 misc_deregister(misc: &acrn_dev);
523 return ret;
524 }
525 return 0;
526}
527
528static void __exit hsm_exit(void)
529{
530 acrn_ioreq_intr_remove();
531 misc_deregister(misc: &acrn_dev);
532}
533module_init(hsm_init);
534module_exit(hsm_exit);
535
536MODULE_AUTHOR("Intel Corporation");
537MODULE_LICENSE("GPL");
538MODULE_DESCRIPTION("ACRN Hypervisor Service Module (HSM)");
539

source code of linux/drivers/virt/acrn/hsm.c