diff --git a/hypervisor/arch/x86/guest/io_emul.c b/hypervisor/arch/x86/guest/io_emul.c index c81d610ba..4f13658bd 100644 --- a/hypervisor/arch/x86/guest/io_emul.c +++ b/hypervisor/arch/x86/guest/io_emul.c @@ -17,6 +17,11 @@ #include #include +#define MMIO_DEFAULT_VALUE_SIZE_1 (0xFFUL) +#define MMIO_DEFAULT_VALUE_SIZE_2 (0xFFFFUL) +#define MMIO_DEFAULT_VALUE_SIZE_4 (0xFFFFFFFFUL) +#define MMIO_DEFAULT_VALUE_SIZE_8 (0xFFFFFFFFFFFFFFFFUL) + /** * @brief General complete-work for port I/O emulation * @@ -63,18 +68,6 @@ static void emulate_mmio_complete(struct acrn_vcpu *vcpu, const struct io_reques } } -#ifdef CONFIG_PARTITION_MODE -static void io_instr_dest_handler(struct io_request *io_req) -{ - struct pio_request *pio_req = &io_req->reqs.pio; - - if (pio_req->direction == REQUEST_READ) { - pio_req->value = 0xFFFFFFFFU; - } -} - -#else - static void complete_ioreq(struct acrn_vcpu *vcpu, struct io_request *io_req) { union vhm_request_buffer *req_buf = NULL; @@ -183,7 +176,57 @@ static void dm_emulate_io_complete(struct acrn_vcpu *vcpu) } } } -#endif + +/** + * @pre width < 8U + */ +static bool pio_default_read(__unused struct acrn_vm *vm, struct acrn_vcpu *vcpu, + __unused uint16_t addr, size_t width) +{ + struct pio_request *pio_req = &vcpu->req.reqs.pio; + + pio_req->value = (uint32_t)((1UL << (width * 8U)) - 1UL); + + return true; +} + +static bool pio_default_write(__unused struct acrn_vm *vm, __unused uint16_t addr, + __unused size_t width, __unused uint32_t v) +{ + return true; /* ignore write */ +} + +/** + * @pre (io_req->reqs.mmio.size == 1U) || (io_req->reqs.mmio.size == 2U) || + * (io_req->reqs.mmio.size == 4U) || (io_req->reqs.mmio.size == 8U) + */ +static int32_t mmio_default_access_handler(struct io_request *io_req, + __unused void *handler_private_data) +{ + struct mmio_request *mmio = &io_req->reqs.mmio; + + if (mmio->direction == REQUEST_READ) { + switch (mmio->size) { + case 1U: + mmio->value = MMIO_DEFAULT_VALUE_SIZE_1; + break; + case 2U: + mmio->value = MMIO_DEFAULT_VALUE_SIZE_2; + break; + case 4U: + mmio->value = MMIO_DEFAULT_VALUE_SIZE_4; + break; + case 8U: + mmio->value = MMIO_DEFAULT_VALUE_SIZE_8; + break; + default: + /* This case is unreachable, this is guaranteed by the design. */ + break; + } + } + + return 0; +} /** * Try handling the given request by any port I/O handler registered in the @@ -204,6 +247,8 @@ hv_emulate_pio(struct acrn_vcpu *vcpu, struct io_request *io_req) struct acrn_vm *vm = vcpu->vm; struct pio_request *pio_req = &io_req->reqs.pio; struct vm_io_handler_desc *handler; + io_read_fn_t io_read = vm->arch_vm.default_io_read; + io_write_fn_t io_write = vm->arch_vm.default_io_write; port = (uint16_t)pio_req->address; size = (uint16_t)pio_req->size; @@ -215,34 +260,30 @@ hv_emulate_pio(struct acrn_vcpu *vcpu, struct io_request *io_req) continue; } - status = 0; - - if (pio_req->direction == REQUEST_WRITE) { - if (handler->io_write != NULL) { - if (!(handler->io_write(vm, port, size, pio_req->value))) { - /* - * If io_write return false, it indicates that we need continue - * to emulate in DM. - */ - status = -ENODEV; - } - } - pr_dbg("IO write on port %04x, data %08x", port, pio_req->value); - } else { - if (handler->io_read != NULL) { - if (!(handler->io_read(vm, vcpu, port, size))) { - /* - * If io_read return false, it indicates that we need continue - * to emulate in DM. - */ - status = -ENODEV; - } - } - pr_dbg("IO read on port %04x, data %08x", port, pio_req->value); + if (handler->io_read != NULL) { + io_read = handler->io_read; + } + if (handler->io_write != NULL) { + io_write = handler->io_write; } break; } + if ((pio_req->direction == REQUEST_WRITE) && (io_write != NULL)) { + if (io_write(vm, port, size, pio_req->value)) { + status = 0; + } + } else if ((pio_req->direction == REQUEST_READ) && (io_read != NULL)) { + if (io_read(vm, vcpu, port, size)) { + status = 0; + } + } else { + /* do nothing */ + } + + pr_dbg("IO %s on port %04x, data %08x", + (pio_req->direction == REQUEST_READ) ? "read" : "write", port, pio_req->value); + return status; } @@ -264,6 +305,8 @@ hv_emulate_mmio(struct acrn_vcpu *vcpu, struct io_request *io_req) uint64_t address, size; struct mmio_request *mmio_req = &io_req->reqs.mmio; struct mem_io_node *mmio_handler = NULL; + hv_mem_io_handler_t read_write = vcpu->vm->default_read_write; + void *handler_private_data = NULL; address = mmio_req->address; size = mmio_req->size; @@ -283,9 +326,9 @@ hv_emulate_mmio(struct acrn_vcpu *vcpu, struct io_request *io_req) status = -EIO; emulation_done = true; } else { - /* Handle this MMIO operation */ if (mmio_handler->read_write != NULL) { - status = mmio_handler->read_write(io_req, mmio_handler->handler_private_data); + read_write = mmio_handler->read_write; + handler_private_data = mmio_handler->handler_private_data; emulation_done = true; } } @@ -295,6 +338,10 @@ hv_emulate_mmio(struct acrn_vcpu *vcpu, struct io_request *io_req) } } + if ((status == -ENODEV) && (read_write != NULL)) { + status = read_write(io_req, handler_private_data); + } + return status; } @@ -304,6 +351,10 @@ hv_emulate_mmio(struct acrn_vcpu *vcpu, struct io_request *io_req) * Handle an I/O request by either invoking a hypervisor-internal handler or * deliver to VHM. * + * @pre vcpu != NULL + * @pre vcpu->vm != NULL + * @pre vcpu->vm->vm_id < CONFIG_MAX_VM_NUM + * * @param vcpu The virtual CPU that triggers the MMIO access * @param io_req The I/O request holding the details of the MMIO access * @@ -317,6 +368,9 @@ static int32_t emulate_io(struct acrn_vcpu *vcpu, struct io_request *io_req) { int32_t status; + struct acrn_vm_config *vm_config; + + vm_config = get_vm_config(vcpu->vm->vm_id); switch (io_req->type) { case REQ_PORTIO: @@ -338,16 +392,7 @@ emulate_io(struct acrn_vcpu *vcpu, struct io_request *io_req) break; } - if (status == -ENODEV) { -#ifdef CONFIG_PARTITION_MODE - /* - * No handler from HV side, return all FFs on read - * and discard writes. - */ - io_instr_dest_handler(io_req); - status = 0; - -#else + if ((status == -ENODEV) && (vm_config->type == NORMAL_VM)) { /* * No handler from HV side, search from VHM in Dom0 * @@ -366,7 +411,6 @@ emulate_io(struct acrn_vcpu *vcpu, struct io_request *io_req) pio_req->direction, io_req->type, pio_req->address, pio_req->size); } -#endif } return status; @@ -601,3 +645,24 @@ int32_t register_mmio_emulation_handler(struct acrn_vm *vm, /* Return status to caller */ return status; } + +/** + * @brief Register port I/O default handler + * + * @param vm The VM to which the port I/O handlers are registered + */ +void register_pio_default_emulation_handler(struct acrn_vm *vm) +{ + vm->arch_vm.default_io_read = pio_default_read; + vm->arch_vm.default_io_write = pio_default_write; +} + +/** + * @brief Register MMIO default handler + * + * @param vm The VM to which the MMIO handler is registered + */ +void register_mmio_default_emulation_handler(struct acrn_vm *vm) +{ + vm->default_read_write = mmio_default_access_handler; +} diff --git a/hypervisor/arch/x86/guest/vm.c b/hypervisor/arch/x86/guest/vm.c index 50e3647f8..4c1897f75 100644 --- a/hypervisor/arch/x86/guest/vm.c +++ b/hypervisor/arch/x86/guest/vm.c @@ -339,6 +339,12 @@ int32_t create_vm(uint16_t vm_id, struct acrn_vm_config *vm_config, struct acrn_ vm->arch_vm.nworld_eptp = vm->arch_vm.ept_mem_ops.get_pml4_page(vm->arch_vm.ept_mem_ops.info); sanitize_pte((uint64_t *)vm->arch_vm.nworld_eptp); + /* Register default handlers for PIO & MMIO if it is SOS VM or Pre-launched VM */ + if ((vm_config->type == SOS_VM) || (vm_config->type == PRE_LAUNCHED_VM)) { + register_pio_default_emulation_handler(vm); + register_mmio_default_emulation_handler(vm); + } + if (is_sos_vm(vm)) { /* Only for SOS_VM */ create_sos_vm_e820(vm); diff --git a/hypervisor/include/arch/x86/guest/io_emul.h b/hypervisor/include/arch/x86/guest/io_emul.h index 4d973681e..1de32168f 100644 --- a/hypervisor/include/arch/x86/guest/io_emul.h +++ b/hypervisor/include/arch/x86/guest/io_emul.h @@ -84,4 +84,17 @@ int32_t register_mmio_emulation_handler(struct acrn_vm *vm, hv_mem_io_handler_t read_write, uint64_t start, uint64_t end, void *handler_private_data); +/** + * @brief Register port I/O default handler + * + * @param vm The VM to which the port I/O handlers are registered + */ +void register_pio_default_emulation_handler(struct acrn_vm *vm); + +/** + * @brief Register MMIO default handler + * + * @param vm The VM to which the MMIO handler is registered + */ +void register_mmio_default_emulation_handler(struct acrn_vm *vm); #endif /* IO_EMUL_H */ diff --git a/hypervisor/include/arch/x86/guest/vm.h b/hypervisor/include/arch/x86/guest/vm.h index e3f366260..c5ce6d780 100644 --- a/hypervisor/include/arch/x86/guest/vm.h +++ b/hypervisor/include/arch/x86/guest/vm.h @@ -112,6 +112,8 @@ struct vm_arch { struct acrn_vioapic vioapic; /* Virtual IOAPIC base address */ struct acrn_vpic vpic; /* Virtual PIC */ struct vm_io_handler_desc emul_pio[EMUL_PIO_IDX_MAX]; + io_read_fn_t default_io_read; + io_write_fn_t default_io_write; /* reference to virtual platform to come here (as needed) */ } __aligned(PAGE_SIZE); @@ -132,6 +134,7 @@ struct acrn_vm { uint16_t emul_mmio_regions; /* Number of emulated mmio regions */ struct mem_io_node emul_mmio[CONFIG_MAX_EMULATED_MMIO_REGIONS]; + hv_mem_io_handler_t default_read_write; uint8_t GUID[16]; struct secure_world_control sworld_control;