GNU Linux-libre 6.9.1-gnu
[releases.git] / drivers / virt / acrn / vm.c
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(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(&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, vm_param->ioreq_buf) < 0) {
44                 hcall_destroy_vm(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(&vm->list, &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, &vm->flags))
65                 return 0;
66
67         ret = hcall_destroy_vm(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, &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(&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(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(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(vm->vmid, virt_to_phys(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(msi);
125         return ret;
126 }