diff --git a/hypervisor/arch/x86/guest/vmcall.c b/hypervisor/arch/x86/guest/vmcall.c index 38465c079..2db7c1b3e 100644 --- a/hypervisor/arch/x86/guest/vmcall.c +++ b/hypervisor/arch/x86/guest/vmcall.c @@ -65,6 +65,10 @@ static const struct hc_dispatch hc_dispatch_table[] = { .handler = hcall_assign_mmiodev}, [HC_IDX(HC_DEASSIGN_MMIODEV)] = { .handler = hcall_deassign_mmiodev}, + [HC_IDX(HC_CREATE_VDEV)] = { + .handler = hcall_create_vdev}, + [HC_IDX(HC_DESTROY_VDEV)] = { + .handler = hcall_destroy_vdev}, [HC_IDX(HC_SET_PTDEV_INTR_INFO)] = { .handler = hcall_set_ptdev_intr_info}, [HC_IDX(HC_RESET_PTDEV_INTR_INFO)] = { diff --git a/hypervisor/common/hypercall.c b/hypervisor/common/hypercall.c index b2e295858..24282667a 100644 --- a/hypervisor/common/hypercall.c +++ b/hypervisor/common/hypercall.c @@ -23,6 +23,22 @@ #define DBG_LEVEL_HYCALL 6U +typedef int32_t (*emul_dev_op) (struct acrn_vm *vm, struct acrn_emul_dev *dev); +struct emul_dev_ops { + /* + * The low 32 bits represent the vendor id and device id of PCI device, + * and the high 32 bits represent the device number of the legacy device + */ + uint64_t dev_id; + emul_dev_op create; + emul_dev_op destroy; + +}; + +static struct emul_dev_ops emul_dev_ops_tbl[] = { + {0, NULL, NULL}, /* implemented in next patch */ +}; + bool is_hypercall_from_ring0(void) { uint16_t cs_sel; @@ -1197,3 +1213,82 @@ int32_t hcall_set_callback_vector(__unused struct acrn_vm *vm, __unused struct a return ret; } + +/* + * @pre dev != NULL + */ +static struct emul_dev_ops *find_emul_dev_ops(struct acrn_emul_dev *dev) +{ + struct emul_dev_ops *op = NULL; + uint32_t i; + + for (i = 0U; i < ARRAY_SIZE(emul_dev_ops_tbl); i++) { + if (emul_dev_ops_tbl[i].dev_id == dev->dev_id.value) { + op = &emul_dev_ops_tbl[i]; + break; + } + } + return op; +} + +/** + * @brief Create an emulated device in hypervisor. + * + * @param vm pointer to VM data structure + * @param vmid ID of the VM + * @param param guest physical address. This gpa points to data structure of + * acrn_emul_dev including information about PCI or legacy devices + * + * @pre Pointer vm shall point to SOS_VM + * @return 0 on success, non-zero on error. + */ +int32_t hcall_create_vdev(struct acrn_vm *vm, struct acrn_vm *target_vm, __unused uint64_t param1, uint64_t param2) +{ + int32_t ret = -EINVAL; + struct acrn_emul_dev dev; + struct emul_dev_ops *op; + + /* We should only create a device to a post-launched VM at creating time for safety, not runtime or other cases*/ + if (is_created_vm(target_vm)) { + if (copy_from_gpa(vm, &dev, param2, sizeof(dev)) == 0) { + op = find_emul_dev_ops(&dev); + if ((op != NULL) && (op->create != NULL)) { + ret = op->create(target_vm, &dev); + } + } + } else { + pr_err("%s, vm[%d] is not a postlaunched VM, or not in CREATED status to create a vdev\n", __func__, target_vm->vm_id); + } + return ret; +} + +/** + * @brief Destroy an emulated device in hypervisor. + * + * @param vm pointer to VM data structure + * @param vmid ID of the VM + * @param param guest physical address. This gpa points to data structure of + * acrn_emul_dev including information about PCI or legacy devices + * + * @pre Pointer vm shall point to SOS_VM + * @return 0 on success, non-zero on error. + */ +int32_t hcall_destroy_vdev(struct acrn_vm *vm, struct acrn_vm *target_vm, __unused uint64_t param1, uint64_t param2) +{ + int32_t ret = -EINVAL; + struct acrn_emul_dev dev; + struct emul_dev_ops *op; + + /* We should only destroy a device to a post-launched VM at creating or pausing time for safety, not runtime or other cases*/ + if (is_created_vm(target_vm) || is_paused_vm(target_vm)) { + if (copy_from_gpa(vm, &dev, param2, sizeof(dev)) == 0) { + op = find_emul_dev_ops(&dev); + if ((op != NULL) && (op->destroy != NULL)) { + ret = op->destroy(target_vm, &dev); + } + } + } else { + pr_err("%s, vm[%d] is not a postlaunched VM, or not in CREATED/PAUSED status to destroy a vdev\n", __func__, target_vm->vm_id); + } + return ret; +} diff --git a/hypervisor/include/common/hypercall.h b/hypervisor/include/common/hypercall.h index 46686fb9c..763da4805 100644 --- a/hypervisor/include/common/hypercall.h +++ b/hypervisor/include/common/hypercall.h @@ -333,6 +333,34 @@ int32_t hcall_assign_mmiodev(struct acrn_vm *vm, struct acrn_vm *target_vm, uint */ int32_t hcall_deassign_mmiodev(struct acrn_vm *vm, struct acrn_vm *target_vm, uint64_t param1, uint64_t param2); +/** + * @brief Create an emulated device in hypervisor. + * + * @param vm Pointer to VM data structure + * @param target_vm Pointer to target VM data structure + * @param param1 not used + * @param param2 guest physical address. This gpa points to data structure of + * acrn_emul_dev including information about PCI or legacy devices + * + * @pre Pointer vm shall point to SOS_VM + * @return 0 on success, non-zero on error. + */ +int32_t hcall_create_vdev(struct acrn_vm *vm, struct acrn_vm *target_vm, uint64_t param1, uint64_t param2); + +/** + * @brief Destroy an emulated device in hypervisor. + * + * @param vm Pointer to VM data structure + * @param target_vm Pointer to target VM data structure + * @param param1 not used + * @param param2 guest physical address. This gpa points to data structure of + * acrn_emul_dev including information about PCI or legacy devices + * + * @pre Pointer vm shall point to SOS_VM + * @return 0 on success, non-zero on error. + */ +int32_t hcall_destroy_vdev(struct acrn_vm *vm, struct acrn_vm *target_vm, uint64_t param1, uint64_t param2); + /** * @brief Set interrupt mapping info of ptdev. * diff --git a/hypervisor/include/public/acrn_hv_defs.h b/hypervisor/include/public/acrn_hv_defs.h index 33101b633..fd28f2c5c 100644 --- a/hypervisor/include/public/acrn_hv_defs.h +++ b/hypervisor/include/public/acrn_hv_defs.h @@ -69,6 +69,8 @@ #define HC_DEASSIGN_PCIDEV BASE_HC_ID(HC_ID, HC_ID_PCI_BASE + 0x06UL) #define HC_ASSIGN_MMIODEV BASE_HC_ID(HC_ID, HC_ID_PCI_BASE + 0x07UL) #define HC_DEASSIGN_MMIODEV BASE_HC_ID(HC_ID, HC_ID_PCI_BASE + 0x08UL) +#define HC_CREATE_VDEV BASE_HC_ID(HC_ID, HC_ID_PCI_BASE + 0x09UL) +#define HC_DESTROY_VDEV BASE_HC_ID(HC_ID, HC_ID_PCI_BASE + 0x0AUL) /* DEBUG */ #define HC_ID_DBG_BASE 0x60UL @@ -323,6 +325,49 @@ struct acrn_mmiodev { } __attribute__((aligned(8))); +/** + * @brief Info to create or destroy a virtual PCI or legacy device for a VM + * + * the parameter for HC_CREATE_VDEV or HC_DESTROY_VDEV hypercall + */ +struct acrn_emul_dev { + /* + * the identifier of the device, the low 32 bits represent the vendor + * id and device id of PCI device and the high 32 bits represent the + * device number of the legacy device + */ + union dev_id_info { + uint64_t value; + struct fields_info { + uint16_t vendor_id; + uint16_t device_id; + uint32_t legacy_device_number; + } fields; + } dev_id; + + /* + * the slot of the device, if the device is a PCI device, the slot + * represents BDF, otherwise it represents legacy device slot number + */ + uint32_t slot; + + /** reserved for extension */ + uint32_t reserved0; + + /** the IO resource address of the device, initialized by ACRN-DM. */ + uint32_t io_addr[6]; + + /** the IO resource size of the device, initialized by ACRN-DM. */ + uint32_t io_size[6]; + + /** the options for the virtual device, initialized by ACRN-DM. */ + uint8_t args[128]; + + /** reserved for extension */ + uint64_t reserved1[8]; + +} __attribute__((aligned(8))); + /** * Hypervisor api version info, return it for HC_GET_API_VERSION hypercall */