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 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); 54*aa3b483fSShuo 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 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 679c5137aeSShuo Liu /* Remove from global VM list */ 6872f293deSShuo Liu write_lock_bh(&acrn_vm_list_lock); 699c5137aeSShuo Liu list_del_init(&vm->list); 7072f293deSShuo Liu write_unlock_bh(&acrn_vm_list_lock); 7172f293deSShuo Liu 72d8ad5151SShuo Liu acrn_ioeventfd_deinit(vm); 73*aa3b483fSShuo Liu acrn_irqfd_deinit(vm); 7472f293deSShuo Liu acrn_ioreq_deinit(vm); 75*aa3b483fSShuo Liu 76c7cf8d27SShuo Liu if (vm->monitor_page) { 77c7cf8d27SShuo Liu put_page(vm->monitor_page); 78c7cf8d27SShuo Liu vm->monitor_page = NULL; 79c7cf8d27SShuo Liu } 809c5137aeSShuo Liu 819c5137aeSShuo Liu ret = hcall_destroy_vm(vm->vmid); 829c5137aeSShuo Liu if (ret < 0) { 839c5137aeSShuo Liu dev_err(acrn_dev.this_device, 849c5137aeSShuo Liu "Failed to destroy VM %u\n", vm->vmid); 859c5137aeSShuo Liu clear_bit(ACRN_VM_FLAG_DESTROYED, &vm->flags); 869c5137aeSShuo Liu return ret; 879c5137aeSShuo Liu } 8888f537d5SShuo 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 /** 97c7cf8d27SShuo Liu * acrn_inject_msi() - 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 */ 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