| 1 | |
| 2 | |
| 3 | * ACRN_HSM: Virtual Machine management
|
| 4 | *
|
| 5 | * Copyright (C) 2020 Intel Corporation. All rights reserved.
|
| 6 | *
|
| 7 | * Authors:
|
| 8 | * Jason Chen CJ <[email protected]>
|
| 9 | * Yakui Zhao <[email protected]>
|
| 10 |
|
| 11 | #include <linux/io.h>
|
| 12 | #include <linux/mm.h>
|
| 13 | #include <linux/slab.h>
|
| 14 |
|
| 15 | #include "acrn_drv.h"
|
| 16 |
|
| 17 |
|
| 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 |
|
| 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 |
|
| 110 | msi = kzalloc_obj(*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 | }
|