1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * ACRN_HSM: Virtual Machine management |
4 | * |
5 | * Copyright (C) 2020 Intel Corporation. All rights reserved. |
6 | * |
7 | * Authors: |
8 | * Jason Chen CJ <jason.cj.chen@intel.com> |
9 | * Yakui Zhao <yakui.zhao@intel.com> |
10 | */ |
11 | #include <linux/io.h> |
12 | #include <linux/mm.h> |
13 | #include <linux/slab.h> |
14 | |
15 | #include "acrn_drv.h" |
16 | |
17 | /* List of VMs */ |
18 | LIST_HEAD(acrn_vm_list); |
19 | /* |
20 | * acrn_vm_list is read in a worker thread which dispatch I/O requests and |
21 | * is wrote in VM creation ioctl. Use the rwlock mechanism to protect it. |
22 | */ |
23 | DEFINE_RWLOCK(acrn_vm_list_lock); |
24 | |
25 | struct acrn_vm *acrn_vm_create(struct acrn_vm *vm, |
26 | struct acrn_vm_creation *vm_param) |
27 | { |
28 | int ret; |
29 | |
30 | ret = hcall_create_vm(virt_to_phys(address: vm_param)); |
31 | if (ret < 0 || vm_param->vmid == ACRN_INVALID_VMID) { |
32 | dev_err(acrn_dev.this_device, |
33 | "Failed to create VM! Error: %d\n" , ret); |
34 | return NULL; |
35 | } |
36 | |
37 | mutex_init(&vm->regions_mapping_lock); |
38 | INIT_LIST_HEAD(list: &vm->ioreq_clients); |
39 | spin_lock_init(&vm->ioreq_clients_lock); |
40 | vm->vmid = vm_param->vmid; |
41 | vm->vcpu_num = vm_param->vcpu_num; |
42 | |
43 | if (acrn_ioreq_init(vm, buf_vma: vm_param->ioreq_buf) < 0) { |
44 | hcall_destroy_vm(vmid: vm_param->vmid); |
45 | vm->vmid = ACRN_INVALID_VMID; |
46 | return NULL; |
47 | } |
48 | |
49 | write_lock_bh(&acrn_vm_list_lock); |
50 | list_add(new: &vm->list, head: &acrn_vm_list); |
51 | write_unlock_bh(&acrn_vm_list_lock); |
52 | |
53 | acrn_ioeventfd_init(vm); |
54 | acrn_irqfd_init(vm); |
55 | dev_dbg(acrn_dev.this_device, "VM %u created.\n" , vm->vmid); |
56 | return vm; |
57 | } |
58 | |
59 | int acrn_vm_destroy(struct acrn_vm *vm) |
60 | { |
61 | int ret; |
62 | |
63 | if (vm->vmid == ACRN_INVALID_VMID || |
64 | test_and_set_bit(ACRN_VM_FLAG_DESTROYED, addr: &vm->flags)) |
65 | return 0; |
66 | |
67 | ret = hcall_destroy_vm(vmid: vm->vmid); |
68 | if (ret < 0) { |
69 | dev_err(acrn_dev.this_device, |
70 | "Failed to destroy VM %u\n" , vm->vmid); |
71 | clear_bit(ACRN_VM_FLAG_DESTROYED, addr: &vm->flags); |
72 | return ret; |
73 | } |
74 | |
75 | /* Remove from global VM list */ |
76 | write_lock_bh(&acrn_vm_list_lock); |
77 | list_del_init(entry: &vm->list); |
78 | write_unlock_bh(&acrn_vm_list_lock); |
79 | |
80 | acrn_ioeventfd_deinit(vm); |
81 | acrn_irqfd_deinit(vm); |
82 | acrn_ioreq_deinit(vm); |
83 | |
84 | if (vm->monitor_page) { |
85 | put_page(page: vm->monitor_page); |
86 | vm->monitor_page = NULL; |
87 | } |
88 | |
89 | acrn_vm_all_ram_unmap(vm); |
90 | |
91 | dev_dbg(acrn_dev.this_device, "VM %u destroyed.\n" , vm->vmid); |
92 | vm->vmid = ACRN_INVALID_VMID; |
93 | return 0; |
94 | } |
95 | |
96 | /** |
97 | * acrn_msi_inject() - Inject a MSI interrupt into a User VM |
98 | * @vm: User VM |
99 | * @msi_addr: The MSI address |
100 | * @msi_data: The MSI data |
101 | * |
102 | * Return: 0 on success, <0 on error |
103 | */ |
104 | int acrn_msi_inject(struct acrn_vm *vm, u64 msi_addr, u64 msi_data) |
105 | { |
106 | struct acrn_msi_entry *msi; |
107 | int ret; |
108 | |
109 | /* might be used in interrupt context, so use GFP_ATOMIC */ |
110 | msi = kzalloc(size: sizeof(*msi), GFP_ATOMIC); |
111 | if (!msi) |
112 | return -ENOMEM; |
113 | |
114 | /* |
115 | * msi_addr: addr[19:12] with dest vcpu id |
116 | * msi_data: data[7:0] with vector |
117 | */ |
118 | msi->msi_addr = msi_addr; |
119 | msi->msi_data = msi_data; |
120 | ret = hcall_inject_msi(vmid: vm->vmid, virt_to_phys(address: msi)); |
121 | if (ret < 0) |
122 | dev_err(acrn_dev.this_device, |
123 | "Failed to inject MSI to VM %u!\n" , vm->vmid); |
124 | kfree(objp: msi); |
125 | return ret; |
126 | } |
127 | |