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 /* Remove from global VM list */ 68 write_lock_bh(&acrn_vm_list_lock); 69 list_del_init(&vm->list); 70 write_unlock_bh(&acrn_vm_list_lock); 71 72 acrn_ioeventfd_deinit(vm); 73 acrn_irqfd_deinit(vm); 74 acrn_ioreq_deinit(vm); 75 76 if (vm->monitor_page) { 77 put_page(vm->monitor_page); 78 vm->monitor_page = NULL; 79 } 80 81 ret = hcall_destroy_vm(vm->vmid); 82 if (ret < 0) { 83 dev_err(acrn_dev.this_device, 84 "Failed to destroy VM %u\n", vm->vmid); 85 clear_bit(ACRN_VM_FLAG_DESTROYED, &vm->flags); 86 return ret; 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 } 127