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 dev_dbg(acrn_dev.this_device, "VM %u created.\n", vm->vmid); 55 return vm; 56 } 57 58 int acrn_vm_destroy(struct acrn_vm *vm) 59 { 60 int ret; 61 62 if (vm->vmid == ACRN_INVALID_VMID || 63 test_and_set_bit(ACRN_VM_FLAG_DESTROYED, &vm->flags)) 64 return 0; 65 66 /* Remove from global VM list */ 67 write_lock_bh(&acrn_vm_list_lock); 68 list_del_init(&vm->list); 69 write_unlock_bh(&acrn_vm_list_lock); 70 71 acrn_ioeventfd_deinit(vm); 72 acrn_ioreq_deinit(vm); 73 if (vm->monitor_page) { 74 put_page(vm->monitor_page); 75 vm->monitor_page = NULL; 76 } 77 78 ret = hcall_destroy_vm(vm->vmid); 79 if (ret < 0) { 80 dev_err(acrn_dev.this_device, 81 "Failed to destroy VM %u\n", vm->vmid); 82 clear_bit(ACRN_VM_FLAG_DESTROYED, &vm->flags); 83 return ret; 84 } 85 86 acrn_vm_all_ram_unmap(vm); 87 88 dev_dbg(acrn_dev.this_device, "VM %u destroyed.\n", vm->vmid); 89 vm->vmid = ACRN_INVALID_VMID; 90 return 0; 91 } 92 93 /** 94 * acrn_inject_msi() - Inject a MSI interrupt into a User VM 95 * @vm: User VM 96 * @msi_addr: The MSI address 97 * @msi_data: The MSI data 98 * 99 * Return: 0 on success, <0 on error 100 */ 101 int acrn_msi_inject(struct acrn_vm *vm, u64 msi_addr, u64 msi_data) 102 { 103 struct acrn_msi_entry *msi; 104 int ret; 105 106 /* might be used in interrupt context, so use GFP_ATOMIC */ 107 msi = kzalloc(sizeof(*msi), GFP_ATOMIC); 108 if (!msi) 109 return -ENOMEM; 110 111 /* 112 * msi_addr: addr[19:12] with dest vcpu id 113 * msi_data: data[7:0] with vector 114 */ 115 msi->msi_addr = msi_addr; 116 msi->msi_data = msi_data; 117 ret = hcall_inject_msi(vm->vmid, virt_to_phys(msi)); 118 if (ret < 0) 119 dev_err(acrn_dev.this_device, 120 "Failed to inject MSI to VM %u!\n", vm->vmid); 121 kfree(msi); 122 return ret; 123 } 124