diff --git a/doc/developer-guides/hld/hv-io-emulation.rst b/doc/developer-guides/hld/hv-io-emulation.rst index 4a7cf370f..132fdb8e6 100644 --- a/doc/developer-guides/hld/hv-io-emulation.rst +++ b/doc/developer-guides/hld/hv-io-emulation.rst @@ -316,9 +316,6 @@ I/O bitmaps and register or unregister I/O handlers: .. doxygenfunction:: allow_guest_pio_access :project: Project ACRN -.. doxygenfunction:: free_io_emulation_resource - :project: Project ACRN - .. doxygenfunction:: register_io_emulation_handler :project: Project ACRN diff --git a/hypervisor/arch/x86/guest/pm.c b/hypervisor/arch/x86/guest/pm.c index c49115798..de1731614 100644 --- a/hypervisor/arch/x86/guest/pm.c +++ b/hypervisor/arch/x86/guest/pm.c @@ -187,7 +187,7 @@ static void pm1ab_io_write(__unused struct acrn_vm *vm, uint16_t addr, size_t wi } static void -register_gas_io_handler(struct acrn_vm *vm, const struct acpi_generic_address *gas) +register_gas_io_handler(struct acrn_vm *vm, uint32_t pio_idx, const struct acpi_generic_address *gas) { uint8_t io_len[5] = {0, 1, 2, 4, 8}; struct vm_io_range gas_io; @@ -203,7 +203,7 @@ register_gas_io_handler(struct acrn_vm *vm, const struct acpi_generic_address *g gas_io.base = (uint16_t)gas->address; gas_io.len = io_len[gas->access_size]; - register_io_emulation_handler(vm, &gas_io, + register_io_emulation_handler(vm, pio_idx, &gas_io, &pm1ab_io_read, &pm1ab_io_write); pr_dbg("Enable PM1A trap for VM %d, port 0x%x, size %d\n", @@ -214,8 +214,8 @@ void register_pm1ab_handler(struct acrn_vm *vm) { struct pm_s_state_data *sx_data = vm->pm.sx_state_data; - register_gas_io_handler(vm, &(sx_data->pm1a_evt)); - register_gas_io_handler(vm, &(sx_data->pm1b_evt)); - register_gas_io_handler(vm, &(sx_data->pm1a_cnt)); - register_gas_io_handler(vm, &(sx_data->pm1b_cnt)); + register_gas_io_handler(vm, PM1A_EVT_PIO_IDX, &(sx_data->pm1a_evt)); + register_gas_io_handler(vm, PM1B_EVT_PIO_IDX, &(sx_data->pm1b_evt)); + register_gas_io_handler(vm, PM1A_CNT_PIO_IDX, &(sx_data->pm1a_cnt)); + register_gas_io_handler(vm, PM1B_CNT_PIO_IDX, &(sx_data->pm1b_cnt)); } diff --git a/hypervisor/arch/x86/guest/vm.c b/hypervisor/arch/x86/guest/vm.c index 34fec7e57..5e87b21e6 100644 --- a/hypervisor/arch/x86/guest/vm.c +++ b/hypervisor/arch/x86/guest/vm.c @@ -211,9 +211,6 @@ int shutdown_vm(struct acrn_vm *vm) /* Free EPT allocated resources assigned to VM */ destroy_ept(vm); - /* TODO: De-initialize I/O Emulation */ - free_io_emulation_resource(vm); - /* Free iommu */ if (vm->iommu != NULL) { destroy_iommu_domain(vm->iommu); diff --git a/hypervisor/arch/x86/io.c b/hypervisor/arch/x86/io.c index 0ab23ee6d..af8fc5777 100644 --- a/hypervisor/arch/x86/io.c +++ b/hypervisor/arch/x86/io.c @@ -196,39 +196,40 @@ hv_emulate_pio(const struct acrn_vcpu *vcpu, struct io_request *io_req) int32_t status = -ENODEV; uint16_t port, size; uint32_t mask; + uint32_t idx; struct acrn_vm *vm = vcpu->vm; struct pio_request *pio_req = &io_req->reqs.pio; - struct vm_io_handler *handler; + struct vm_io_handler_desc *handler; port = (uint16_t)pio_req->address; size = (uint16_t)pio_req->size; mask = 0xFFFFFFFFU >> (32U - 8U * size); - for (handler = vm->arch_vm.io_handler; - handler != NULL; handler = handler->next) { - uint16_t base = handler->desc.addr; - uint16_t end = base + (uint16_t)handler->desc.len; + for (idx = 0U; idx < EMUL_PIO_IDX_MAX; idx++) { + handler = &(vm->arch_vm.emul_pio[idx]); + if (handler->len == 0U) { + continue; + } + uint16_t base = handler->addr; + uint16_t end = base + (uint16_t)handler->len; if ((port >= end) || (port + size <= base)) { continue; } else if (!((port >= base) && ((port + size) <= end))) { - pr_fatal("Err:IO, port 0x%04x, size=%hu spans devices", - port, size); + pr_fatal("Err:IO, port 0x%04x, size=%hu spans devices", port, size); status = -EIO; break; } else { if (pio_req->direction == REQUEST_WRITE) { - handler->desc.io_write(vm, port, size, - pio_req->value & mask); - - pr_dbg("IO write on port %04x, data %08x", port, - pio_req->value & mask); + if (handler->io_write) { + handler->io_write(vm, port, size, pio_req->value & mask); + } + pr_dbg("IO write on port %04x, data %08x", port, pio_req->value & mask); } else { - pio_req->value = handler->desc.io_read(vm, port, - size); - - pr_dbg("IO read on port %04x, data %08x", - port, pio_req->value); + if (handler->io_read) { + pio_req->value = handler->io_read(vm, port, size); + } + pr_dbg("IO read on port %04x, data %08x", port, pio_req->value); } status = 0; break; @@ -394,37 +395,6 @@ int32_t pio_instr_vmexit_handler(struct acrn_vcpu *vcpu) return status; } -static void register_io_handler(struct acrn_vm *vm, struct vm_io_handler *hdlr) -{ - if (vm->arch_vm.io_handler != NULL) { - hdlr->next = vm->arch_vm.io_handler; - } - - vm->arch_vm.io_handler = hdlr; -} - -static void empty_io_handler_list(struct acrn_vm *vm) -{ - struct vm_io_handler *handler = vm->arch_vm.io_handler; - struct vm_io_handler *tmp; - - while (handler != NULL) { - tmp = handler; - handler = tmp->next; - free(tmp); - } - vm->arch_vm.io_handler = NULL; -} - -/** - * @brief Free I/O bitmaps and port I/O handlers of \p vm - * - * @param vm The VM whose I/O bitmaps and handlers are to be freed - */ -void free_io_emulation_resource(struct acrn_vm *vm) -{ - empty_io_handler_list(vm); -} /** * @brief Allow a VM to access a port I/O range @@ -464,27 +434,6 @@ static void deny_guest_pio_access(struct acrn_vm *vm, uint16_t port_address, } } -static struct vm_io_handler *create_io_handler(uint32_t port, uint32_t len, - io_read_fn_t io_read_fn_ptr, - io_write_fn_t io_write_fn_ptr) -{ - - struct vm_io_handler *handler; - - handler = calloc(1U, sizeof(struct vm_io_handler)); - - if (handler != NULL) { - handler->desc.addr = port; - handler->desc.len = len; - handler->desc.io_read = io_read_fn_ptr; - handler->desc.io_write = io_write_fn_ptr; - } else { - pr_err("Error: out of memory"); - } - - return handler; -} - /** * @brief Initialize the I/O bitmap for \p vm * @@ -503,30 +452,23 @@ void setup_io_bitmap(struct acrn_vm *vm) /** * @brief Register a port I/O handler * - * @param vm The VM to which the port I/O handlers are registered - * @param range The port I/O range that the given handlers can emulate + * @param vm The VM to which the port I/O handlers are registered + * @param pio_idx The emulated port io index + * @param range The emulated port io range * @param io_read_fn_ptr The handler for emulating reads from the given range * @param io_write_fn_ptr The handler for emulating writes to the given range + * @pre pio_idx < EMUL_PIO_IDX_MAX */ -void register_io_emulation_handler(struct acrn_vm *vm, const struct vm_io_range *range, - io_read_fn_t io_read_fn_ptr, - io_write_fn_t io_write_fn_ptr) +void register_io_emulation_handler(struct acrn_vm *vm, uint32_t pio_idx, + const struct vm_io_range *range, io_read_fn_t io_read_fn_ptr, io_write_fn_t io_write_fn_ptr) { - struct vm_io_handler *handler = NULL; - - if ((io_read_fn_ptr == NULL) || (io_write_fn_ptr == NULL)) { - pr_err("Invalid IO handler."); - return; - } - if (is_vm0(vm)) { deny_guest_pio_access(vm, range->base, range->len); } - - handler = create_io_handler(range->base, - range->len, io_read_fn_ptr, io_write_fn_ptr); - - register_io_handler(vm, handler); + vm->arch_vm.emul_pio[pio_idx].addr = range->base; + vm->arch_vm.emul_pio[pio_idx].len = range->len; + vm->arch_vm.emul_pio[pio_idx].io_read = io_read_fn_ptr; + vm->arch_vm.emul_pio[pio_idx].io_write = io_write_fn_ptr; } /** diff --git a/hypervisor/debug/vuart.c b/hypervisor/debug/vuart.c index 8f0fc85a1..2f5c6fd77 100644 --- a/hypervisor/debug/vuart.c +++ b/hypervisor/debug/vuart.c @@ -327,7 +327,7 @@ static void vuart_register_io_handler(struct acrn_vm *vm) .len = 8U }; - register_io_emulation_handler(vm, &range, vuart_read, vuart_write); + register_io_emulation_handler(vm, UART_PIO_IDX, &range, vuart_read, vuart_write); } /** diff --git a/hypervisor/dm/vpci/vpci.c b/hypervisor/dm/vpci/vpci.c index 3c6b05f86..cc29339d9 100644 --- a/hypervisor/dm/vpci/vpci.c +++ b/hypervisor/dm/vpci/vpci.c @@ -131,7 +131,7 @@ void vpci_init(struct acrn_vm *vm) #endif if ((vpci->ops->init != NULL) && (vpci->ops->init(vm) == 0)) { - register_io_emulation_handler(vm, &pci_cfg_range, + register_io_emulation_handler(vm, PCI_PIO_IDX, &pci_cfg_range, &pci_cfg_io_read, &pci_cfg_io_write); /* This is a tmp solution to avoid sos reboot failure, it need pass-thru IO port CF9 for Reset Control * register. diff --git a/hypervisor/dm/vpic.c b/hypervisor/dm/vpic.c index 30f01b75c..6aa94bd2c 100644 --- a/hypervisor/dm/vpic.c +++ b/hypervisor/dm/vpic.c @@ -866,11 +866,11 @@ static void vpic_register_io_handler(struct acrn_vm *vm) .len = 2U }; - register_io_emulation_handler(vm, &master_range, + register_io_emulation_handler(vm, PIC_MASTER_PIO_IDX, &master_range, &vpic_master_io_read, &vpic_master_io_write); - register_io_emulation_handler(vm, &slave_range, + register_io_emulation_handler(vm, PIC_SLAVE_PIO_IDX, &slave_range, &vpic_slave_io_read, &vpic_slave_io_write); - register_io_emulation_handler(vm, &elcr_range, + register_io_emulation_handler(vm, PIC_ELC_PIO_IDX, &elcr_range, &vpic_elc_io_read, &vpic_elc_io_write); } diff --git a/hypervisor/dm/vrtc.c b/hypervisor/dm/vrtc.c index 0b8e325a2..195835e23 100644 --- a/hypervisor/dm/vrtc.c +++ b/hypervisor/dm/vrtc.c @@ -77,5 +77,5 @@ void vrtc_init(struct acrn_vm *vm) /* Initializing the CMOS RAM offset to 0U */ vm->vrtc_offset = 0U; - register_io_emulation_handler(vm, &range, vrtc_read, vrtc_write); + register_io_emulation_handler(vm, RTC_PIO_IDX, &range, vrtc_read, vrtc_write); } diff --git a/hypervisor/include/arch/x86/guest/vm.h b/hypervisor/include/arch/x86/guest/vm.h index 7428136a9..de315f071 100644 --- a/hypervisor/include/arch/x86/guest/vm.h +++ b/hypervisor/include/arch/x86/guest/vm.h @@ -103,15 +103,7 @@ struct vm_arch { void *tmp_pg_array; /* Page array for tmp guest paging struct */ struct acrn_vioapic vioapic; /* Virtual IOAPIC base address */ struct acrn_vpic vpic; /* Virtual PIC */ - /** - * A link to the IO handler of this VM. - * We only register io handle to this link - * when create VM on sequences and ungister it when - * destory VM. So there no need lock to prevent preempt. - * Besides, there only a few io handlers now, we don't - * need binary search temporary. - */ - struct vm_io_handler *io_handler; + struct vm_io_handler_desc emul_pio[EMUL_PIO_IDX_MAX]; /* reference to virtual platform to come here (as needed) */ } __aligned(CPU_PAGE_SIZE); diff --git a/hypervisor/include/arch/x86/io.h b/hypervisor/include/arch/x86/io.h index 8beafc18e..1eaf3756e 100644 --- a/hypervisor/include/arch/x86/io.h +++ b/hypervisor/include/arch/x86/io.h @@ -9,6 +9,19 @@ #include +/* Define emulated port IO index */ +#define PIC_MASTER_PIO_IDX 0U +#define PIC_SLAVE_PIO_IDX (PIC_MASTER_PIO_IDX + 1U) +#define PIC_ELC_PIO_IDX (PIC_SLAVE_PIO_IDX + 1U) +#define PCI_PIO_IDX (PIC_ELC_PIO_IDX + 1U) +#define UART_PIO_IDX (PCI_PIO_IDX + 1U) +#define PM1A_EVT_PIO_IDX (UART_PIO_IDX + 1U) +#define PM1A_CNT_PIO_IDX (PM1A_EVT_PIO_IDX + 1U) +#define PM1B_EVT_PIO_IDX (PM1A_CNT_PIO_IDX + 1U) +#define PM1B_CNT_PIO_IDX (PM1B_EVT_PIO_IDX + 1U) +#define RTC_PIO_IDX (PM1B_CNT_PIO_IDX + 1U) +#define EMUL_PIO_IDX_MAX (RTC_PIO_IDX + 1U) + /* Write 1 byte to specified I/O port */ static inline void pio_write8(uint8_t value, uint16_t port) { diff --git a/hypervisor/include/arch/x86/ioreq.h b/hypervisor/include/arch/x86/ioreq.h index ac07cc2cd..c19f3d6f5 100644 --- a/hypervisor/include/arch/x86/ioreq.h +++ b/hypervisor/include/arch/x86/ioreq.h @@ -47,7 +47,7 @@ struct vm_io_range { uint32_t flags; /**< IO port attributes */ }; -struct vm_io_handler; +struct vm_io_handler_desc; struct acrn_vm; struct acrn_vcpu; @@ -108,10 +108,6 @@ struct vm_io_handler_desc { io_write_fn_t io_write; }; -struct vm_io_handler { - struct vm_io_handler *next; - struct vm_io_handler_desc desc; -}; #define IO_ATTR_R 0U #define IO_ATTR_RW 1U @@ -187,13 +183,6 @@ int32_t pio_instr_vmexit_handler(struct acrn_vcpu *vcpu); */ void setup_io_bitmap(struct acrn_vm *vm); -/** - * @brief Free I/O bitmaps and port I/O handlers of \p vm - * - * @param vm The VM whose I/O bitmaps and handlers are to be freed - */ -void free_io_emulation_resource(struct acrn_vm *vm); - /** * @brief Allow a VM to access a port I/O range * @@ -210,14 +199,15 @@ void allow_guest_pio_access(struct acrn_vm *vm, uint16_t port_address, /** * @brief Register a port I/O handler * - * @param vm The VM to which the port I/O handlers are registered - * @param range The port I/O range that the given handlers can emulate + * @param vm The VM to which the port I/O handlers are registered + * @param pio_idx The emulated port io index + * @param range The emulated port io range * @param io_read_fn_ptr The handler for emulating reads from the given range * @param io_write_fn_ptr The handler for emulating writes to the given range + * @pre pio_idx < EMUL_PIO_IDX_MAX */ -void register_io_emulation_handler(struct acrn_vm *vm, const struct vm_io_range *range, - io_read_fn_t io_read_fn_ptr, - io_write_fn_t io_write_fn_ptr); +void register_io_emulation_handler(struct acrn_vm *vm, uint32_t pio_idx, + const struct vm_io_range *range, io_read_fn_t io_read_fn_ptr, io_write_fn_t io_write_fn_ptr); /** * @brief Register a MMIO handler