xref: /openbmc/linux/drivers/virt/acrn/vm.c (revision 4c4c1257)
19c5137aeSShuo Liu // SPDX-License-Identifier: GPL-2.0
29c5137aeSShuo Liu /*
39c5137aeSShuo Liu  * ACRN_HSM: Virtual Machine management
49c5137aeSShuo Liu  *
59c5137aeSShuo Liu  * Copyright (C) 2020 Intel Corporation. All rights reserved.
69c5137aeSShuo Liu  *
79c5137aeSShuo Liu  * Authors:
89c5137aeSShuo Liu  *	Jason Chen CJ <jason.cj.chen@intel.com>
99c5137aeSShuo Liu  *	Yakui Zhao <yakui.zhao@intel.com>
109c5137aeSShuo Liu  */
119c5137aeSShuo Liu #include <linux/io.h>
129c5137aeSShuo Liu #include <linux/mm.h>
139c5137aeSShuo Liu #include <linux/slab.h>
149c5137aeSShuo Liu 
159c5137aeSShuo Liu #include "acrn_drv.h"
169c5137aeSShuo Liu 
179c5137aeSShuo Liu /* List of VMs */
1872f293deSShuo Liu LIST_HEAD(acrn_vm_list);
1972f293deSShuo Liu /*
2072f293deSShuo Liu  * acrn_vm_list is read in a worker thread which dispatch I/O requests and
2172f293deSShuo Liu  * is wrote in VM creation ioctl. Use the rwlock mechanism to protect it.
2272f293deSShuo Liu  */
2372f293deSShuo Liu DEFINE_RWLOCK(acrn_vm_list_lock);
249c5137aeSShuo Liu 
acrn_vm_create(struct acrn_vm * vm,struct acrn_vm_creation * vm_param)259c5137aeSShuo Liu struct acrn_vm *acrn_vm_create(struct acrn_vm *vm,
269c5137aeSShuo Liu 			       struct acrn_vm_creation *vm_param)
279c5137aeSShuo Liu {
289c5137aeSShuo Liu 	int ret;
299c5137aeSShuo Liu 
309c5137aeSShuo Liu 	ret = hcall_create_vm(virt_to_phys(vm_param));
319c5137aeSShuo Liu 	if (ret < 0 || vm_param->vmid == ACRN_INVALID_VMID) {
329c5137aeSShuo Liu 		dev_err(acrn_dev.this_device,
339c5137aeSShuo Liu 			"Failed to create VM! Error: %d\n", ret);
349c5137aeSShuo Liu 		return NULL;
359c5137aeSShuo Liu 	}
369c5137aeSShuo Liu 
3788f537d5SShuo Liu 	mutex_init(&vm->regions_mapping_lock);
3872f293deSShuo Liu 	INIT_LIST_HEAD(&vm->ioreq_clients);
3972f293deSShuo Liu 	spin_lock_init(&vm->ioreq_clients_lock);
409c5137aeSShuo Liu 	vm->vmid = vm_param->vmid;
419c5137aeSShuo Liu 	vm->vcpu_num = vm_param->vcpu_num;
429c5137aeSShuo Liu 
4372f293deSShuo Liu 	if (acrn_ioreq_init(vm, vm_param->ioreq_buf) < 0) {
4472f293deSShuo Liu 		hcall_destroy_vm(vm_param->vmid);
4572f293deSShuo Liu 		vm->vmid = ACRN_INVALID_VMID;
4672f293deSShuo Liu 		return NULL;
4772f293deSShuo Liu 	}
4872f293deSShuo Liu 
4972f293deSShuo Liu 	write_lock_bh(&acrn_vm_list_lock);
509c5137aeSShuo Liu 	list_add(&vm->list, &acrn_vm_list);
5172f293deSShuo Liu 	write_unlock_bh(&acrn_vm_list_lock);
529c5137aeSShuo Liu 
53d8ad5151SShuo Liu 	acrn_ioeventfd_init(vm);
54aa3b483fSShuo Liu 	acrn_irqfd_init(vm);
559c5137aeSShuo Liu 	dev_dbg(acrn_dev.this_device, "VM %u created.\n", vm->vmid);
569c5137aeSShuo Liu 	return vm;
579c5137aeSShuo Liu }
589c5137aeSShuo Liu 
acrn_vm_destroy(struct acrn_vm * vm)599c5137aeSShuo Liu int acrn_vm_destroy(struct acrn_vm *vm)
609c5137aeSShuo Liu {
619c5137aeSShuo Liu 	int ret;
629c5137aeSShuo Liu 
639c5137aeSShuo Liu 	if (vm->vmid == ACRN_INVALID_VMID ||
649c5137aeSShuo Liu 	    test_and_set_bit(ACRN_VM_FLAG_DESTROYED, &vm->flags))
659c5137aeSShuo Liu 		return 0;
669c5137aeSShuo Liu 
67*4c4c1257SShuo Liu 	ret = hcall_destroy_vm(vm->vmid);
68*4c4c1257SShuo Liu 	if (ret < 0) {
69*4c4c1257SShuo Liu 		dev_err(acrn_dev.this_device,
70*4c4c1257SShuo Liu 			"Failed to destroy VM %u\n", vm->vmid);
71*4c4c1257SShuo Liu 		clear_bit(ACRN_VM_FLAG_DESTROYED, &vm->flags);
72*4c4c1257SShuo Liu 		return ret;
73*4c4c1257SShuo Liu 	}
74*4c4c1257SShuo Liu 
759c5137aeSShuo Liu 	/* Remove from global VM list */
7672f293deSShuo Liu 	write_lock_bh(&acrn_vm_list_lock);
779c5137aeSShuo Liu 	list_del_init(&vm->list);
7872f293deSShuo Liu 	write_unlock_bh(&acrn_vm_list_lock);
7972f293deSShuo Liu 
80d8ad5151SShuo Liu 	acrn_ioeventfd_deinit(vm);
81aa3b483fSShuo Liu 	acrn_irqfd_deinit(vm);
8272f293deSShuo Liu 	acrn_ioreq_deinit(vm);
83aa3b483fSShuo Liu 
84c7cf8d27SShuo Liu 	if (vm->monitor_page) {
85c7cf8d27SShuo Liu 		put_page(vm->monitor_page);
86c7cf8d27SShuo Liu 		vm->monitor_page = NULL;
87c7cf8d27SShuo Liu 	}
889c5137aeSShuo Liu 
8988f537d5SShuo Liu 	acrn_vm_all_ram_unmap(vm);
9088f537d5SShuo Liu 
919c5137aeSShuo Liu 	dev_dbg(acrn_dev.this_device, "VM %u destroyed.\n", vm->vmid);
929c5137aeSShuo Liu 	vm->vmid = ACRN_INVALID_VMID;
939c5137aeSShuo Liu 	return 0;
949c5137aeSShuo Liu }
95c7cf8d27SShuo Liu 
96c7cf8d27SShuo Liu /**
971c15b334SShuo Liu  * acrn_msi_inject() - Inject a MSI interrupt into a User VM
98c7cf8d27SShuo Liu  * @vm:		User VM
99c7cf8d27SShuo Liu  * @msi_addr:	The MSI address
100c7cf8d27SShuo Liu  * @msi_data:	The MSI data
101c7cf8d27SShuo Liu  *
102c7cf8d27SShuo Liu  * Return: 0 on success, <0 on error
103c7cf8d27SShuo Liu  */
acrn_msi_inject(struct acrn_vm * vm,u64 msi_addr,u64 msi_data)104c7cf8d27SShuo Liu int acrn_msi_inject(struct acrn_vm *vm, u64 msi_addr, u64 msi_data)
105c7cf8d27SShuo Liu {
106c7cf8d27SShuo Liu 	struct acrn_msi_entry *msi;
107c7cf8d27SShuo Liu 	int ret;
108c7cf8d27SShuo Liu 
109c7cf8d27SShuo Liu 	/* might be used in interrupt context, so use GFP_ATOMIC */
110c7cf8d27SShuo Liu 	msi = kzalloc(sizeof(*msi), GFP_ATOMIC);
111c7cf8d27SShuo Liu 	if (!msi)
112c7cf8d27SShuo Liu 		return -ENOMEM;
113c7cf8d27SShuo Liu 
114c7cf8d27SShuo Liu 	/*
115c7cf8d27SShuo Liu 	 * msi_addr: addr[19:12] with dest vcpu id
116c7cf8d27SShuo Liu 	 * msi_data: data[7:0] with vector
117c7cf8d27SShuo Liu 	 */
118c7cf8d27SShuo Liu 	msi->msi_addr = msi_addr;
119c7cf8d27SShuo Liu 	msi->msi_data = msi_data;
120c7cf8d27SShuo Liu 	ret = hcall_inject_msi(vm->vmid, virt_to_phys(msi));
121c7cf8d27SShuo Liu 	if (ret < 0)
122c7cf8d27SShuo Liu 		dev_err(acrn_dev.this_device,
123c7cf8d27SShuo Liu 			"Failed to inject MSI to VM %u!\n", vm->vmid);
124c7cf8d27SShuo Liu 	kfree(msi);
125c7cf8d27SShuo Liu 	return ret;
126c7cf8d27SShuo Liu }
127