diff --git a/hypervisor/Makefile b/hypervisor/Makefile index b104e96441..63c1953d91 100644 --- a/hypervisor/Makefile +++ b/hypervisor/Makefile @@ -207,6 +207,7 @@ COMMON_C_SRCS += boot/bare.c # dm componment COMMON_C_SRCS += dm/vuart.c +COMMON_C_SRCS += dm/io_req.c ifeq ($(ARCH),x86) COMMON_C_SRCS += common/efi_mmap.c @@ -215,7 +216,6 @@ COMMON_C_SRCS += common/hv_main.c COMMON_C_SRCS += common/hypercall.c COMMON_C_SRCS += common/ptdev.c COMMON_C_SRCS += dm/vrtc.c -COMMON_C_SRCS += dm/io_req.c COMMON_C_SRCS += dm/vpci/vdev.c COMMON_C_SRCS += dm/vpci/vpci.c COMMON_C_SRCS += dm/vpci/vroot_port.c diff --git a/hypervisor/arch/riscv/guest/vm.c b/hypervisor/arch/riscv/guest/vm.c index 590f171ca4..8c48ce254a 100644 --- a/hypervisor/arch/riscv/guest/vm.c +++ b/hypervisor/arch/riscv/guest/vm.c @@ -122,6 +122,43 @@ static void fdt_set_hart_isa_str_all(void *fdt, const char *isa_str) } } +static int fdt_set_hsm(void *fdt) +{ + int soc_offset, plic_offset, new_offset, child, ret = 0; + const char *comp; + + soc_offset = fdt_path_offset(fdt, "/soc"); + if (soc_offset < 0) { + ret = soc_offset; + } else { + child = fdt_first_subnode(fdt, soc_offset); + while (child >= 0) { + comp = fdt_getprop(fdt, child, "compatible", NULL); + if (comp && strstr_s(comp, 8, "plic", 8)) { + plic_offset = child; + break; + } + child = fdt_next_subnode(fdt, child); + } + + if (plic_offset < 0) { + ret = -FDT_ERR_NOTFOUND; + } else { + new_offset = fdt_add_subnode(fdt, soc_offset, "hsm"); + if (new_offset < 0) { + ret = new_offset; + } else { + fdt_setprop_string(fdt, new_offset, "compatible", "riscv,hsm"); + fdt_setprop_cell(fdt, new_offset, "interrupts", HYPERVISOR_CALLBACK_HSM_VECTOR); + fdt_setprop_cell(fdt, new_offset, "interrupt-parent", 0x9); + } + } + } + + return ret; + +} + void arch_init_service_vm_vfdt(struct acrn_vm *vm) { /* TODO: For now hardcode the isa string. @@ -131,4 +168,5 @@ void arch_init_service_vm_vfdt(struct acrn_vm *vm) */ const char *isa_str = "rv64imafdc_zicsr_zifencei_sstc"; fdt_set_hart_isa_str_all(vm_get_vfdt(vm), isa_str); + fdt_set_hsm(vm_get_vfdt(vm)); } diff --git a/hypervisor/arch/x86/guest/pm.c b/hypervisor/arch/x86/guest/pm.c index 13b95534a0..b61a837452 100644 --- a/hypervisor/arch/x86/guest/pm.c +++ b/hypervisor/arch/x86/guest/pm.c @@ -141,18 +141,9 @@ static inline uint8_t get_slp_typx(uint32_t pm1_cnt) return (uint8_t)((pm1_cnt & 0x1fffU) >> BIT_SLP_TYPx); } -static bool pm1ab_io_read(struct acrn_vcpu *vcpu, uint16_t addr, size_t width) +static inline void enter_s5(struct acrn_vm *vm, uint32_t pm1a_cnt_val, uint32_t pm1b_cnt_val) { - struct acrn_pio_request *pio_req = &vcpu->req.reqs.pio_request; - - pio_req->value = pio_read(addr, width); - - return true; -} - -static inline void enter_s5(struct acrn_vcpu *vcpu, uint32_t pm1a_cnt_val, uint32_t pm1b_cnt_val) -{ - struct acrn_vm *vm = vcpu->vm; + struct acrn_vcpu *vcpu = vcpu_from_vid(vm, BSP_CPU_ID); uint16_t pcpu_id = pcpuid_from_vcpu(vcpu); get_vm_lock(vm); @@ -190,15 +181,13 @@ static inline void enter_s3(struct acrn_vm *vm, uint32_t pm1a_cnt_val, uint32_t } /** - * @pre vcpu != NULL - * @pre vcpu->vm != NULL + * @pre vm != NULL */ -static bool pm1ab_io_write(struct acrn_vcpu *vcpu, uint16_t addr, size_t width, uint32_t v) +static bool pm1ab_io_write(struct acrn_vm *vm, uint16_t addr, size_t width, uint32_t v) { static uint32_t pm1a_cnt_ready = 0U; uint32_t pm1a_cnt_val; bool to_write = true; - struct acrn_vm *vm = vcpu->vm; if (width == 2U) { uint8_t val = get_slp_typx(v); @@ -211,7 +200,7 @@ static bool pm1ab_io_write(struct acrn_vcpu *vcpu, uint16_t addr, size_t width, if (vm->arch_vm.pm.sx_state_data->s3_pkg.val_pm1a == val) { enter_s3(vm, v, 0U); } else if (vm->arch_vm.pm.sx_state_data->s5_pkg.val_pm1a == val) { - enter_s5(vcpu, v, 0U); + enter_s5(vm, v, 0U); } else { /* other Sx value should be ignored */ } @@ -227,7 +216,7 @@ static bool pm1ab_io_write(struct acrn_vcpu *vcpu, uint16_t addr, size_t width, if (vm->arch_vm.pm.sx_state_data->s3_pkg.val_pm1b == val) { enter_s3(vm, pm1a_cnt_val, v); } else if (vm->arch_vm.pm.sx_state_data->s5_pkg.val_pm1b == val) { - enter_s5(vcpu, pm1a_cnt_val, v); + enter_s5(vm, pm1a_cnt_val, v); } else { /* other Sx value should be ignored */ } @@ -249,17 +238,25 @@ static bool pm1ab_io_write(struct acrn_vcpu *vcpu, uint16_t addr, size_t width, return true; } -static void register_gas_io_handler(struct acrn_vm *vm, uint32_t pio_idx, const struct acrn_acpi_generic_address *gas) +static int32_t pm1ab_pio_handler(struct io_request *io_req, void *private_data) { - struct vm_io_range gas_io; + struct acrn_pio_request *pio_req = &io_req->reqs.pio_request; + struct acrn_vm *vm = (struct acrn_vm *)private_data; - if ((gas->address != 0UL) && (gas->space_id == SPACE_SYSTEM_IO) && (gas->bit_width != 0U)) { - gas_io.base = (uint16_t)gas->address; - gas_io.len = gas->bit_width / 8; + if (pio_req->direction == ACRN_IOREQ_DIR_WRITE) { + pm1ab_io_write(vm, pio_req->address, pio_req->size, pio_req->value); + } else { + pio_req->value = pio_read(pio_req->address, pio_req->size); + } - register_pio_emulation_handler(vm, pio_idx, &gas_io, &pm1ab_io_read, &pm1ab_io_write); + return 0; +} - pr_dbg("Enable PM1A trap for VM %d, port 0x%x, size %d\n", vm->vm_id, gas_io.base, gas_io.len); +static void register_gas_io_handler(struct acrn_vm *vm, const struct acrn_acpi_generic_address *gas) +{ + if ((gas->address != 0UL) && (gas->space_id == SPACE_SYSTEM_IO) && (gas->bit_width != 0U)) { + register_pio_emulation_handler(vm, (uint16_t)gas->address, gas->bit_width / 8, pm1ab_pio_handler, vm); + pr_dbg("Enable PM1A trap for VM %d, port 0x%x, size %d\n", vm->vm_id, gas->address, gas->bit_width / 8); } } @@ -267,70 +264,48 @@ static void register_pm1ab_handler(struct acrn_vm *vm) { struct pm_s_state_data *sx_data = vm->arch_vm.pm.sx_state_data; - 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)); -} - -static bool rt_vm_pm1a_io_read(__unused struct acrn_vcpu *vcpu, - __unused uint16_t addr, __unused size_t width) -{ - return false; + 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)); } /* - * retval true means that we complete the emulation in HV and no need to re-inject the request to DM. - * retval false means that we should re-inject the request to DM. + * retval -ENODEV means that we should re-inject the request to DM. */ -/** - * @pre vcpu != NULL - * @pre vcpu->vm != NULL - */ -static bool rt_vm_pm1a_io_write(struct acrn_vcpu *vcpu, uint16_t addr, size_t width, uint32_t v) +static int32_t rtvm_pm1a_pio_handler(struct io_request *io_req, void *private_data) { - if (width != 2U) { - pr_dbg("Invalid address (0x%x) or width (0x%x)", addr, width); - } else { - if (((v & VIRTUAL_PM1A_SLP_EN) != 0U) && (((v & VIRTUAL_PM1A_SLP_TYP) >> 10U) == 5U)) { - poweroff_if_rt_vm(vcpu->vm); + struct acrn_pio_request *pio_req = &io_req->reqs.pio_request; + struct acrn_vm *vm = (struct acrn_vm *)private_data; + + if (pio_req->direction == ACRN_IOREQ_DIR_WRITE) { + if (pio_req->size != 2U) { + pr_dbg("%s Invalid width (0x%x)", __func__, pio_req->size); + } else { + if (((pio_req->value & VIRTUAL_PM1A_SLP_EN) != 0U) && + (((pio_req->value & VIRTUAL_PM1A_SLP_TYP) >> 10U) == 5U)) { + poweroff_if_rt_vm(vm); + } } } - return false; -} - -static void register_rt_vm_pm1a_ctl_handler(struct acrn_vm *vm) -{ - struct vm_io_range io_range; - - io_range.base = VIRTUAL_PM1A_CNT_ADDR; - io_range.len = 1U; - - register_pio_emulation_handler(vm, VIRTUAL_PM1A_CNT_PIO_IDX, &io_range, - &rt_vm_pm1a_io_read, &rt_vm_pm1a_io_write); + return -ENODEV; } -/* - * @pre vcpu != NULL - */ -static bool prelaunched_vm_sleep_io_read(struct acrn_vcpu *vcpu, __unused uint16_t addr, __unused size_t width) +static inline void register_rt_vm_pm1a_ctl_handler(struct acrn_vm *vm) { - vcpu->req.reqs.pio_request.value = 0U; - - return true; + register_pio_emulation_handler(vm, VIRTUAL_PM1A_CNT_ADDR, 1U, rtvm_pm1a_pio_handler, vm); } /* - * @pre vcpu != NULL - * @pre vcpu->vm != NULL + * @pre vm != NULL */ -static bool prelaunched_vm_sleep_io_write(struct acrn_vcpu *vcpu, uint16_t addr, size_t width, uint32_t v) +static void prelaunched_vm_sleep_io_write(struct acrn_vm *vm, uint16_t addr, size_t width, uint32_t v) { if ((width == 1U) && (addr == VIRTUAL_SLEEP_CTL_ADDR)) { bool slp_en; uint32_t slp_type; - struct acrn_vm *vm = vcpu->vm; + struct acrn_vcpu *vcpu = vcpu_from_vid(vm, BSP_CPU_ID); /* ACPI sleep control register: * @@ -356,14 +331,24 @@ static bool prelaunched_vm_sleep_io_write(struct acrn_vcpu *vcpu, uint16_t addr, make_shutdown_vm_request(pcpuid_from_vcpu(vcpu)); } } +} - return true; +static int32_t prelaunched_vm_sleep_pio_handler(struct io_request *io_req, void *private_data) +{ + struct acrn_pio_request *pio_req = &io_req->reqs.pio_request; + struct acrn_vm *vm = (struct acrn_vm *)private_data; + + if (pio_req->direction == ACRN_IOREQ_DIR_WRITE) { + prelaunched_vm_sleep_io_write(vm, pio_req->address, pio_req->size, pio_req->value); + } else { + pio_req->value = 0U; + } + + return 0; } static void register_prelaunched_vm_sleep_handler(struct acrn_vm *vm) { - struct vm_io_range io_range; - /* ACPI reduced HW mode is used for pre-launched VM * * The optional ACPI sleep registers (SLEEP_CONTROL_REG and SLEEP_STATUS_REG) specify @@ -371,11 +356,7 @@ static void register_prelaunched_vm_sleep_handler(struct acrn_vm *vm) * implemented, the Sleep registers are a replacement for the SLP_TYP, SLP_EN and WAK_STS * registers in the PM1_BLK. */ - io_range.base = VIRTUAL_SLEEP_CTL_ADDR; - io_range.len = 2U; - - register_pio_emulation_handler(vm, SLEEP_CTL_PIO_IDX, &io_range, - &prelaunched_vm_sleep_io_read, &prelaunched_vm_sleep_io_write); + register_pio_emulation_handler(vm, VIRTUAL_SLEEP_CTL_ADDR, 2U, prelaunched_vm_sleep_pio_handler, vm); } void init_guest_pm(struct acrn_vm *vm) diff --git a/hypervisor/arch/x86/guest/vm_reset.c b/hypervisor/arch/x86/guest/vm_reset.c index 365ccdc40d..c3e8015fa5 100644 --- a/hypervisor/arch/x86/guest/vm_reset.c +++ b/hypervisor/arch/x86/guest/vm_reset.c @@ -68,21 +68,15 @@ void triple_fault_shutdown_vm(struct acrn_vcpu *vcpu) } } -/** - * @pre vcpu != NULL - * @pre vcpu->vm != NULL - */ -static bool handle_reset_reg_read(struct acrn_vcpu *vcpu, __unused uint16_t addr, - __unused size_t bytes) +static int32_t handle_reset_reg_read(struct acrn_vm *vm, uint32_t *val) { - bool ret = true; - struct acrn_vm *vm = vcpu->vm; + int32_t ret = 0; if (is_postlaunched_vm(vm)) { /* re-inject to DM */ - ret = false; + ret = -ENODEV; } else { - vcpu->req.reqs.pio_request.value = vm->arch_vm.reset_control; + *val = vm->arch_vm.reset_control; } return ret; @@ -91,10 +85,10 @@ static bool handle_reset_reg_read(struct acrn_vcpu *vcpu, __unused uint16_t addr /** * @pre vm != NULL */ -static bool handle_common_reset_reg_write(struct acrn_vcpu *vcpu, bool reset, bool warm) +static int32_t handle_common_reset_reg_write(struct acrn_vm *vm, bool reset, bool warm) { - struct acrn_vm *vm = vcpu->vm; - bool ret = true; + struct acrn_vcpu *vcpu = vcpu_from_vid(vm, BSP_CPU_ID); + int32_t ret = true; get_vm_lock(vm); if (reset) { @@ -104,7 +98,7 @@ static bool handle_common_reset_reg_write(struct acrn_vcpu *vcpu, bool reset, bo reset_host(warm); } else if (is_postlaunched_vm(vm)) { /* re-inject to DM */ - ret = false; + ret = -ENODEV; } else { /* * If it's Service VM reset while RTVM is still alive @@ -119,7 +113,7 @@ static bool handle_common_reset_reg_write(struct acrn_vcpu *vcpu, bool reset, bo } else { if (is_postlaunched_vm(vm)) { /* If post-launched VM write none reset value, re-inject to DM */ - ret = false; + ret = -ENODEV; } /* * Ignore writes from Service VM and pre-launched VM. @@ -131,26 +125,38 @@ static bool handle_common_reset_reg_write(struct acrn_vcpu *vcpu, bool reset, bo return ret; } -/** - * @pre vcpu != NULL - * @pre vcpu->vm != NULL - */ -static bool handle_kb_write(struct acrn_vcpu *vcpu, __unused uint16_t addr, size_t bytes, uint32_t val) +static int32_t handle_kb_write(struct acrn_vm *vm, __unused uint16_t addr, size_t bytes, uint32_t val) { /* ignore commands other than system reset */ - return handle_common_reset_reg_write(vcpu, ((bytes == 1U) && (val == 0xfeU)), false); + return handle_common_reset_reg_write(vm, ((bytes == 1U) && (val == 0xfeU)), false); } -static bool handle_kb_read(struct acrn_vcpu *vcpu, uint16_t addr, size_t bytes) +static uint32_t handle_kb_read(struct acrn_vm *vm, uint16_t addr, size_t bytes) { - if (is_service_vm(vcpu->vm) && (bytes == 1U)) { + uint32_t value = ~0U; + + if (is_service_vm(vm) && (bytes == 1U)) { /* In case i8042 is defined as ACPI PNP device in BIOS, HV need expose physical 0x64 port. */ - vcpu->req.reqs.pio_request.value = pio_read8(addr); + value = pio_read8(addr); + } + /* ACRN will not expose kbd controller to the guest in other case. */ + + return value; +} + +static int32_t kb_pio_handler(struct io_request *io_req, void *private_data) +{ + struct acrn_pio_request *pio_req = &io_req->reqs.pio_request; + struct acrn_vm *vm = (struct acrn_vm *)private_data; + int32_t ret = 0; + + if (pio_req->direction == ACRN_IOREQ_DIR_READ) { + pio_req->value = handle_kb_read(vm, pio_req->address, pio_req->size); } else { - /* ACRN will not expose kbd controller to the guest in this case. */ - vcpu->req.reqs.pio_request.value = ~0U; + ret = handle_kb_write(vm, pio_req->address, pio_req->size, pio_req->value); } - return true; + + return ret; } @@ -166,29 +172,38 @@ static bool handle_kb_read(struct acrn_vcpu *vcpu, uint16_t addr, size_t bytes) * @pre vcpu != NULL * @pre vcpu->vm != NULL */ -static bool handle_cf9_write(struct acrn_vcpu *vcpu, __unused uint16_t addr, size_t bytes, uint32_t val) +static bool handle_cf9_write(struct acrn_vm *vm, __unused uint16_t addr, size_t bytes, uint32_t val) { - struct acrn_vm *vm = vcpu->vm; - vm->arch_vm.reset_control = val & 0xeU; - return handle_common_reset_reg_write(vcpu, + return handle_common_reset_reg_write(vm, ((bytes == 1U) && ((val & 0x4U) == 0x4U) && ((val & 0xaU) != 0U)), ((val & 0x8U) == 0U)); } -/** - * @pre vcpu != NULL - * @pre vcpu->vm != NULL - */ -static bool handle_reset_reg_write(struct acrn_vcpu *vcpu, uint16_t addr, size_t bytes, uint32_t val) +static int32_t cf9_pio_handler(struct io_request *io_req, void *private_data) { - bool ret = true; + struct acrn_pio_request *pio_req = &io_req->reqs.pio_request; + struct acrn_vm *vm = (struct acrn_vm *)private_data; + int32_t ret; + + if (pio_req->direction == ACRN_IOREQ_DIR_READ) { + ret = handle_reset_reg_read(vm, &pio_req->value); + } else { + ret = handle_cf9_write(vm, pio_req->address, pio_req->size, pio_req->value); + } + + return ret; +} + +static int32_t handle_reset_reg_write(struct acrn_vm *vm, uint16_t addr, size_t bytes, uint32_t val) +{ + int32_t ret = 0; if (bytes == 1U) { struct acpi_reset_reg *reset_reg = get_host_reset_reg_data(); if (val == reset_reg->val) { - ret = handle_common_reset_reg_write(vcpu, true, false); + ret = handle_common_reset_reg_write(vm, true, false); } else { /* * ACPI defines the reset value but doesn't specify the meaning of other values. @@ -202,6 +217,21 @@ static bool handle_reset_reg_write(struct acrn_vcpu *vcpu, uint16_t addr, size_t return ret; } +static int32_t reset_reg_pio_handler(struct io_request *io_req, void *private_data) +{ + struct acrn_pio_request *pio_req = &io_req->reqs.pio_request; + struct acrn_vm *vm = (struct acrn_vm *)private_data; + int32_t ret = 0; + + if (pio_req->direction == ACRN_IOREQ_DIR_READ) { + pio_req->value = handle_kb_read(vm, pio_req->address, pio_req->size); + } else { + ret = handle_reset_reg_write(vm, pio_req->address, pio_req->size, pio_req->value); + } + + return ret; +} + /** * @pre vm != NULL */ @@ -212,16 +242,10 @@ void register_reset_port_handler(struct acrn_vm *vm) struct acpi_reset_reg *reset_reg = get_host_reset_reg_data(); struct acrn_acpi_generic_address *gas = &(reset_reg->reg); - struct vm_io_range io_range = { - .len = 1U - }; - - io_range.base = 0x64U; - register_pio_emulation_handler(vm, KB_PIO_IDX, &io_range, handle_kb_read, handle_kb_write); + register_pio_emulation_handler(vm, 0x64U, 1U, kb_pio_handler, vm); /* ACPI reset register is fixed at 0xcf9 for post-launched and pre-launched VMs */ - io_range.base = 0xcf9U; - register_pio_emulation_handler(vm, CF9_PIO_IDX, &io_range, handle_reset_reg_read, handle_cf9_write); + register_pio_emulation_handler(vm, 0xcf9U, 1U, cf9_pio_handler, vm); /* * - here is taking care of Service VM only: @@ -233,9 +257,8 @@ void register_reset_port_handler(struct acrn_vm *vm) (gas->bit_width == 8U) && (gas->bit_offset == 0U) && (gas->address != 0xcf9U) && (gas->address != 0x64U)) { - io_range.base = (uint16_t)reset_reg->reg.address; - register_pio_emulation_handler(vm, PIO_RESET_REG_IDX, &io_range, - handle_reset_reg_read, handle_reset_reg_write); + register_pio_emulation_handler(vm, (uint16_t)reset_reg->reg.address, 1U, + reset_reg_pio_handler, vm); } } } diff --git a/hypervisor/arch/x86/guest/vmx_io.c b/hypervisor/arch/x86/guest/vmx_io.c index 55168d5d1c..fd35c98771 100644 --- a/hypervisor/arch/x86/guest/vmx_io.c +++ b/hypervisor/arch/x86/guest/vmx_io.c @@ -75,8 +75,8 @@ int32_t pio_instr_vmexit_handler(struct acrn_vcpu *vcpu) exit_qual = vcpu->arch.exit_qualification; io_req->io_type = ACRN_IOREQ_TYPE_PORTIO; - pio_req->size = vm_exit_io_instruction_size(exit_qual) + 1UL; - pio_req->address = vm_exit_io_instruction_port_number(exit_qual); + pio_req->size = (uint16_t)(vm_exit_io_instruction_size(exit_qual) + 1UL); + pio_req->address = (uint16_t)vm_exit_io_instruction_port_number(exit_qual); if (vm_exit_io_instruction_access_direction(exit_qual) == 0UL) { mask = 0xFFFFFFFFU >> (32U - (8U * pio_req->size)); pio_req->direction = ACRN_IOREQ_DIR_WRITE; diff --git a/hypervisor/common/vm.c b/hypervisor/common/vm.c index ddfa3d27ce..8cbc399917 100644 --- a/hypervisor/common/vm.c +++ b/hypervisor/common/vm.c @@ -383,8 +383,7 @@ int32_t create_vm(uint16_t vm_id, uint64_t pcpu_bitmap, struct acrn_vm_config *v } spinlock_init(&vm->stg2pt_lock); - spinlock_init(&vm->emul_mmio_lock); - vm->nr_emul_mmio_regions = 0U; + spinlock_init(&vm->emul_io_lock); /* TODO: Some logic inside arch_init_vm can also be moved to common but * we didn't come up with abstraction good enough to capture dependencies. Leave those diff --git a/hypervisor/dm/io_req.c b/hypervisor/dm/io_req.c index 4049b13f18..a5b3fba3eb 100644 --- a/hypervisor/dm/io_req.c +++ b/hypervisor/dm/io_req.c @@ -512,29 +512,19 @@ static void dm_emulate_io_complete(struct acrn_vcpu *vcpu) } /** - * @pre width < 8U - * @pre vcpu != NULL - * @pre vcpu->vm != NULL + * @pre io_req->reqs.pio.size < 8U */ -static bool pio_default_read(struct acrn_vcpu *vcpu, - __unused uint16_t addr, size_t width) +static int32_t pio_default_access_handler(struct io_request *io_req, + __unused void *handler_private_data) { - struct acrn_pio_request *pio_req = &vcpu->req.reqs.pio_request; + struct acrn_pio_request *pio_req = &io_req->reqs.pio_request; - pio_req->value = (uint32_t)((1UL << (width * 8U)) - 1UL); + if (pio_req->direction == ACRN_IOREQ_DIR_READ) { + pio_req->value = (uint32_t)((1UL << (pio_req->size * 8U)) - 1UL); - return true; -} + } -/** - * @pre width < 8U - * @pre vcpu != NULL - * @pre vcpu->vm != NULL - */ -static bool pio_default_write(__unused struct acrn_vcpu *vcpu, __unused uint16_t addr, - __unused size_t width, __unused uint32_t v) -{ - return true; /* ignore write */ + return 0; } /** @@ -569,6 +559,58 @@ static int32_t mmio_default_access_handler(struct io_request *io_req, return 0; } +static int32_t hv_emulate_io(struct acrn_vm *vm, struct io_request *io_req, + uint64_t address, uint64_t size, hv_io_handler_t default_read_write) +{ + int32_t status = -ENODEV; + bool hold_lock = true; + uint16_t idx; + uint64_t base, end; + struct io_node *io_handler = NULL; + hv_io_handler_t read_write = default_read_write; + void *handler_private_data = NULL; + + spinlock_obtain(&vm->emul_io_lock); + for (idx = 0U; (idx <= CONFIG_MAX_EMULATED_MMIO_REGIONS) && + (bitmap_test(idx & 0x3FU, vm->emul_io_bitmap + (idx >> 6U))); idx++) { + io_handler = &(vm->emul_io[idx]); + if (io_handler->read_write != NULL) { + base = io_handler->range_start; + end = io_handler->range_end; + + if (((address + size) <= base) || (address >= end)) { + continue; + } else { + if ((address >= base) && ((address + size) <= end)) { + hold_lock = io_handler->hold_lock; + read_write = io_handler->read_write; + handler_private_data = io_handler->handler_private_data; + } else { + pr_fatal("Err PIO/MMIO, address:0x%lx, size:%x", address, size); + status = -EIO; + } + break; + } + } + } + + if ((status == -ENODEV) && (read_write != NULL)) { + /* This io_handler will never modify once register, so we don't + * need to hold the lock when handling the MMIO access. + */ + if (!hold_lock) { + spinlock_release(&vm->emul_io_lock); + } + status = read_write(io_req, handler_private_data); + if (!hold_lock) { + spinlock_obtain(&vm->emul_io_lock); + } + } + spinlock_release(&vm->emul_io_lock); + + return status; +} + /** * Try handling the given request by any port I/O handler registered in the * hypervisor. @@ -582,55 +624,15 @@ static int32_t mmio_default_access_handler(struct io_request *io_req, static int32_t hv_emulate_pio(struct acrn_vcpu *vcpu, struct io_request *io_req) { - int32_t status = -ENODEV; - uint16_t port, size; - uint32_t idx; struct acrn_vm *vm = vcpu->vm; struct acrn_pio_request *pio_req = &io_req->reqs.pio_request; - struct vm_io_handler_desc *handler; - io_read_fn_t io_read = NULL; - io_write_fn_t io_write = NULL; + hv_io_handler_t read_write = NULL; if (is_service_vm(vcpu->vm) || is_prelaunched_vm(vcpu->vm)) { - io_read = pio_default_read; - io_write = pio_default_write; - } - - port = (uint16_t)pio_req->address; - size = (uint16_t)pio_req->size; - - for (idx = 0U; idx < EMUL_PIO_IDX_MAX; idx++) { - handler = &(vm->emul_pio[idx]); - - if ((port < handler->port_start) || (port >= handler->port_end)) { - continue; - } - - if (handler->io_read != NULL) { - io_read = handler->io_read; - } - if (handler->io_write != NULL) { - io_write = handler->io_write; - } - break; + read_write = pio_default_access_handler; } - if ((pio_req->direction == ACRN_IOREQ_DIR_WRITE) && (io_write != NULL)) { - if (io_write(vcpu, port, size, pio_req->value)) { - status = 0; - } - } else if ((pio_req->direction == ACRN_IOREQ_DIR_READ) && (io_read != NULL)) { - if (io_read(vcpu, port, size)) { - status = 0; - } - } else { - /* do nothing */ - } - - pr_dbg("IO %s on port %04x, data %08x", - (pio_req->direction == ACRN_IOREQ_DIR_READ) ? "read" : "write", port, pio_req->value); - - return status; + return hv_emulate_io(vm, io_req, pio_req->address, pio_req->size, read_write); } /** @@ -646,60 +648,15 @@ hv_emulate_pio(struct acrn_vcpu *vcpu, struct io_request *io_req) static int32_t hv_emulate_mmio(struct acrn_vcpu *vcpu, struct io_request *io_req) { - int32_t status = -ENODEV; - bool hold_lock = true; - uint16_t idx; - uint64_t address, size, base, end; + struct acrn_vm *vm = vcpu->vm; struct acrn_mmio_request *mmio_req = &io_req->reqs.mmio_request; - struct mem_io_node *mmio_handler = NULL; - hv_mem_io_handler_t read_write = NULL; - void *handler_private_data = NULL; + hv_io_handler_t read_write = NULL; - if (is_service_vm(vcpu->vm) || is_prelaunched_vm(vcpu->vm)) { + if (is_service_vm(vm) || is_prelaunched_vm(vm)) { read_write = mmio_default_access_handler; } - address = mmio_req->address; - size = mmio_req->size; - - spinlock_obtain(&vcpu->vm->emul_mmio_lock); - for (idx = 0U; idx <= vcpu->vm->nr_emul_mmio_regions; idx++) { - mmio_handler = &(vcpu->vm->emul_mmio[idx]); - if (mmio_handler->read_write != NULL) { - base = mmio_handler->range_start; - end = mmio_handler->range_end; - - if (((address + size) <= base) || (address >= end)) { - continue; - } else { - if ((address >= base) && ((address + size) <= end)) { - hold_lock = mmio_handler->hold_lock; - read_write = mmio_handler->read_write; - handler_private_data = mmio_handler->handler_private_data; - } else { - pr_fatal("Err MMIO, address:0x%lx, size:%x", address, size); - status = -EIO; - } - break; - } - } - } - - if ((status == -ENODEV) && (read_write != NULL)) { - /* This mmio_handler will never modify once register, so we don't - * need to hold the lock when handling the MMIO access. - */ - if (!hold_lock) { - spinlock_release(&vcpu->vm->emul_mmio_lock); - } - status = read_write(io_req, handler_private_data); - if (!hold_lock) { - spinlock_obtain(&vcpu->vm->emul_mmio_lock); - } - } - spinlock_release(&vcpu->vm->emul_mmio_lock); - - return status; + return hv_emulate_io(vm, io_req, mmio_req->address, mmio_req->size, read_write); } /** @@ -782,82 +739,70 @@ emulate_io(struct acrn_vcpu *vcpu, struct io_request *io_req) /** - * @brief Register a port I/O handler - * - * @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_pio_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) -{ - if (is_service_vm(vm)) { - deny_guest_pio_access(vm, range->base, range->len); - } - vm->emul_pio[pio_idx].port_start = range->base; - vm->emul_pio[pio_idx].port_end = range->base + range->len; - vm->emul_pio[pio_idx].io_read = io_read_fn_ptr; - vm->emul_pio[pio_idx].io_write = io_write_fn_ptr; -} - -/** - * @brief Find match MMIO node + * @brief Find a free MMIO node * - * This API find match MMIO node from \p vm. + * This API find a free MMIO node from \p vm under vm->emul_io_lock protection. * * @param vm The VM to which the MMIO node is belong to. * - * @return If there's a match mmio_node return it, otherwise return NULL; + * @return If there's a free mmio_node return it, otherwise return NULL; */ -static inline struct mem_io_node *find_match_mmio_node(struct acrn_vm *vm, - uint64_t start, uint64_t end) +static inline struct io_node *find_free_io_node(struct acrn_vm *vm) { - bool found = false; - uint16_t idx; - struct mem_io_node *mmio_node; + uint16_t idx = ffz64_ex(vm->emul_io_bitmap, CONFIG_MAX_EMULATED_MMIO_REGIONS); + struct io_node *io_node = NULL; - for (idx = 0U; idx < CONFIG_MAX_EMULATED_MMIO_REGIONS; idx++) { - mmio_node = &(vm->emul_mmio[idx]); - if ((mmio_node->range_start == start) && (mmio_node->range_end == end)) { - found = true; - break; - } + if (idx < CONFIG_MAX_EMULATED_MMIO_REGIONS) { + bitmap_set_non_atomic(idx & 0x3FU, + vm->emul_io_bitmap + (idx >> 6U)); + io_node = &(vm->emul_io[idx]); + } else { + pr_info("%s, vm[%d] no free mmio region\n", __func__, vm->vm_id); } - if (!found) { - pr_info("%s, vm[%d] no match mmio region [0x%lx, 0x%lx] is found", - __func__, vm->vm_id, start, end); - mmio_node = NULL; + return io_node; +} + +static void register_io_emulation_handler(struct acrn_vm *vm, + hv_io_handler_t read_write, uint64_t start, + uint64_t end, void *handler_private_data, bool hold_lock) +{ + struct io_node *io_node; + + /* Ensure both a read/write handler and range check function exist */ + if ((read_write != NULL) && (end > start)) { + spinlock_obtain(&vm->emul_io_lock); + io_node = find_free_io_node(vm); + if (io_node != NULL) { + /* Fill in information for this node */ + io_node->hold_lock = hold_lock; + io_node->read_write = read_write; + io_node->handler_private_data = handler_private_data; + io_node->range_start = start; + io_node->range_end = end; + } + spinlock_release(&vm->emul_io_lock); } - return mmio_node; } /** - * @brief Find a free MMIO node - * - * This API find a free MMIO node from \p vm. - * - * @param vm The VM to which the MMIO node is belong to. + * @brief Register a port I/O handler * - * @return If there's a free mmio_node return it, otherwise return NULL; + * @param vm The VM to which the port I/O handlers are registered + * @param pio_base The emulated port io range base + * @param pio_size The emulated port io range size + * @param read_write The handler for emulating accesses to the given range + * @param handler_private_data Handler-specific data which will be passed to \p read_write when called */ -static inline struct mem_io_node *find_free_mmio_node(struct acrn_vm *vm) +void register_pio_emulation_handler(struct acrn_vm *vm, uint16_t pio_base, uint16_t pio_size, + hv_io_handler_t read_write, void *handler_private_data) { - uint16_t idx; - struct mem_io_node *mmio_node = find_match_mmio_node(vm, 0UL, 0UL); - - if (mmio_node != NULL) { - idx = (uint16_t)(uint64_t)(mmio_node - &(vm->emul_mmio[0U])); - if (vm->nr_emul_mmio_regions < idx) { - vm->nr_emul_mmio_regions = idx; - } + if (is_service_vm(vm)) { + deny_guest_pio_access(vm, pio_base, pio_size); } - return mmio_node; + register_io_emulation_handler(vm, read_write, pio_base, pio_size, handler_private_data, false); } /** @@ -872,26 +817,10 @@ static inline struct mem_io_node *find_free_mmio_node(struct acrn_vm *vm) * @param handler_private_data Handler-specific data which will be passed to \p read_write when called */ void register_mmio_emulation_handler(struct acrn_vm *vm, - hv_mem_io_handler_t read_write, uint64_t start, + hv_io_handler_t read_write, uint64_t start, uint64_t end, void *handler_private_data, bool hold_lock) { - struct mem_io_node *mmio_node; - - /* Ensure both a read/write handler and range check function exist */ - if ((read_write != NULL) && (end > start)) { - spinlock_obtain(&vm->emul_mmio_lock); - mmio_node = find_free_mmio_node(vm); - if (mmio_node != NULL) { - /* Fill in information for this node */ - mmio_node->hold_lock = hold_lock; - mmio_node->read_write = read_write; - mmio_node->handler_private_data = handler_private_data; - mmio_node->range_start = start; - mmio_node->range_end = end; - } - spinlock_release(&vm->emul_mmio_lock); - } - + register_io_emulation_handler(vm, read_write, start, end, handler_private_data, hold_lock); } /** @@ -906,18 +835,27 @@ void register_mmio_emulation_handler(struct acrn_vm *vm, void unregister_mmio_emulation_handler(struct acrn_vm *vm, uint64_t start, uint64_t end) { - struct mem_io_node *mmio_node; + struct io_node *mmio_node; + uint16_t idx; - spinlock_obtain(&vm->emul_mmio_lock); - mmio_node = find_match_mmio_node(vm, start, end); - if (mmio_node != NULL) { - (void)memset(mmio_node, 0U, sizeof(struct mem_io_node)); + spinlock_obtain(&vm->emul_io_lock); + for (idx = 0U; idx < CONFIG_MAX_EMULATED_MMIO_REGIONS; idx++) { + if (bitmap_test(idx & 0x3FU, vm->emul_io_bitmap + (idx >> 6U))) { + mmio_node = &(vm->emul_io[idx]); + if ((mmio_node->range_start == start) && (mmio_node->range_end == end)) { + (void)memset(mmio_node, 0U, sizeof(struct io_node)); + bitmap_clear_non_atomic(idx & 0x3FU, + vm->emul_io_bitmap + (idx >> 6U)); + + break; + } + } } - spinlock_release(&vm->emul_mmio_lock); + spinlock_release(&vm->emul_io_lock); } void deinit_emul_io(struct acrn_vm *vm) { - (void)memset(vm->emul_mmio, 0U, sizeof(vm->emul_mmio)); - (void)memset(vm->emul_pio, 0U, sizeof(vm->emul_pio)); + (void)memset(vm->emul_io_bitmap, 0U, sizeof(vm->emul_io_bitmap)); + (void)memset(vm->emul_io, 0U, sizeof(vm->emul_io)); } diff --git a/hypervisor/dm/vpci/vpci.c b/hypervisor/dm/vpci/vpci.c index c7031d8573..e2e1a3d858 100644 --- a/hypervisor/dm/vpci/vpci.c +++ b/hypervisor/dm/vpci/vpci.c @@ -47,24 +47,16 @@ static int32_t vpci_read_cfg(struct acrn_vpci *vpci, union pci_bdf bdf, uint32_t static int32_t vpci_write_cfg(struct acrn_vpci *vpci, union pci_bdf bdf, uint32_t offset, uint32_t bytes, uint32_t val); static struct pci_vdev *find_available_vdev(struct acrn_vpci *vpci, union pci_bdf bdf); -/** - * @pre vcpu != NULL - * @pre vcpu->vm != NULL - */ -static bool vpci_pio_cfgaddr_read(struct acrn_vcpu *vcpu, uint16_t addr, size_t bytes) +static uint32_t vpci_pio_cfgaddr_read(struct acrn_vpci *vpci, uint16_t addr, size_t bytes) { uint32_t val = ~0U; - struct acrn_vpci *vpci = &vcpu->vm->vpci; union pci_cfg_addr_reg *cfg_addr = &vpci->addr; - struct acrn_pio_request *pio_req = &vcpu->req.reqs.pio_request; if ((addr == (uint16_t)PCI_CONFIG_ADDR) && (bytes == 4U)) { val = cfg_addr->value; } - pio_req->value = val; - - return true; + return val; } /** @@ -74,10 +66,9 @@ static bool vpci_pio_cfgaddr_read(struct acrn_vcpu *vcpu, uint16_t addr, size_t * @retval true on success. * @retval false. (ACRN will deliver this IO request to DM to handle for post-launched VM) */ -static bool vpci_pio_cfgaddr_write(struct acrn_vcpu *vcpu, uint16_t addr, size_t bytes, uint32_t val) +static int32_t vpci_pio_cfgaddr_write(struct acrn_vpci *vpci, uint16_t addr, size_t bytes, uint32_t val) { - bool ret = true; - struct acrn_vpci *vpci = &vcpu->vm->vpci; + int32_t ret = 0; union pci_cfg_addr_reg *cfg_addr = &vpci->addr; union pci_bdf vbdf; @@ -85,7 +76,7 @@ static bool vpci_pio_cfgaddr_write(struct acrn_vcpu *vcpu, uint16_t addr, size_t /* unmask reserved fields: BITs 24-30 and BITs 0-1 */ cfg_addr->value = val & (~0x7f000003U); - if (is_postlaunched_vm(vcpu->vm)) { + if (is_postlaunched_vm(container_of(vpci, struct acrn_vm, vpci))) { const struct pci_vdev *vdev; vbdf.value = cfg_addr->bits.bdf; @@ -95,7 +86,7 @@ static bool vpci_pio_cfgaddr_write(struct acrn_vcpu *vcpu, uint16_t addr, size_t * still need to deliver to ACRN DM to handle. */ if ((vdev == NULL) || is_quirk_ptdev(vdev)) { - ret = false; + ret = -ENODEV; } } } @@ -103,54 +94,49 @@ static bool vpci_pio_cfgaddr_write(struct acrn_vcpu *vcpu, uint16_t addr, size_t return ret; } +static int32_t vpci_cfgaddr_pio_handler(struct io_request *io_req, void *private_data) +{ + struct acrn_pio_request *pio_req = &io_req->reqs.pio_request; + struct acrn_vpci *vpci = (struct acrn_vpci *)private_data; + int32_t ret = 0; + + if (pio_req->direction == ACRN_IOREQ_DIR_READ) { + pio_req->value = vpci_pio_cfgaddr_read(vpci, pio_req->address, pio_req->size); + } else { + ret = vpci_pio_cfgaddr_write(vpci, pio_req->address, pio_req->size, pio_req->value); + } + + return ret; +} + /** - * @pre vcpu != NULL - * @pre vcpu->vm != NULL - * @pre vcpu->vm->vm_id < CONFIG_MAX_VM_NUM - * @pre (get_vm_config(vcpu->vm->vm_id)->load_order == PRE_LAUNCHED_VM) - * || (get_vm_config(vcpu->vm->vm_id)->load_order == SERVICE_VM) - * - * @retval true on success. - * @retval false. (ACRN will deliver this IO request to DM to handle for post-launched VM) + * @retval -ENODEV. (ACRN will deliver this IO request to DM to handle for post-launched VM) */ -static bool vpci_pio_cfgdata_read(struct acrn_vcpu *vcpu, uint16_t addr, size_t bytes) +static int32_t vpci_pio_cfgdata_read(struct acrn_vpci *vpci, uint16_t addr, size_t bytes, uint32_t *val) { int32_t ret = 0; - struct acrn_vm *vm = vcpu->vm; - struct acrn_vpci *vpci = &vm->vpci; union pci_cfg_addr_reg cfg_addr; union pci_bdf bdf; - uint32_t val = ~0U; - struct acrn_pio_request *pio_req = &vcpu->req.reqs.pio_request; + *val = ~0U; cfg_addr.value = atomic_readandclear32(&vpci->addr.value); if (cfg_addr.bits.enable != 0U) { uint32_t offset = (uint16_t)cfg_addr.bits.reg_num + (addr - PCI_CONFIG_DATA); if (pci_is_valid_access(offset, bytes)) { bdf.value = cfg_addr.bits.bdf; - ret = vpci_read_cfg(vpci, bdf, offset, bytes, &val); + ret = vpci_read_cfg(vpci, bdf, offset, bytes, val); } } - pio_req->value = val; - return (ret == 0); + return ret; } /** - * @pre vcpu != NULL - * @pre vcpu->vm != NULL - * @pre vcpu->vm->vm_id < CONFIG_MAX_VM_NUM - * @pre (get_vm_config(vcpu->vm->vm_id)->load_order == PRE_LAUNCHED_VM) - * || (get_vm_config(vcpu->vm->vm_id)->load_order == SERVICE_VM) - * - * @retval true on success. - * @retval false. (ACRN will deliver this IO request to DM to handle for post-launched VM) + * @retval -ENODEV. (ACRN will deliver this IO request to DM to handle for post-launched VM) */ -static bool vpci_pio_cfgdata_write(struct acrn_vcpu *vcpu, uint16_t addr, size_t bytes, uint32_t val) +static int32_t vpci_pio_cfgdata_write(struct acrn_vpci *vpci, uint16_t addr, size_t bytes, uint32_t val) { int32_t ret = 0; - struct acrn_vm *vm = vcpu->vm; - struct acrn_vpci *vpci = &vm->vpci; union pci_cfg_addr_reg cfg_addr; union pci_bdf bdf; @@ -163,7 +149,22 @@ static bool vpci_pio_cfgdata_write(struct acrn_vcpu *vcpu, uint16_t addr, size_t } } - return (ret == 0); + return ret; +} + +static int32_t vpci_cfgdata_pio_handler(struct io_request *io_req, void *private_data) +{ + struct acrn_pio_request *pio_req = &io_req->reqs.pio_request; + struct acrn_vpci *vpci = (struct acrn_vpci *)private_data; + int32_t ret; + + if (pio_req->direction == ACRN_IOREQ_DIR_READ) { + ret = vpci_pio_cfgdata_read(vpci, pio_req->address, pio_req->size, &pio_req->value); + } else { + ret = vpci_pio_cfgdata_write(vpci, pio_req->address, pio_req->size, pio_req->value); + } + + return ret; } /** @@ -215,16 +216,6 @@ static int32_t vpci_mmio_cfg_access(struct io_request *io_req, void *private_dat */ int32_t init_vpci(struct acrn_vm *vm) { - struct vm_io_range pci_cfgaddr_range = { - .base = PCI_CONFIG_ADDR, - .len = 1U - }; - - struct vm_io_range pci_cfgdata_range = { - .base = PCI_CONFIG_DATA, - .len = 4U - }; - struct acrn_vm_config *vm_config; struct pci_mmcfg_region *pci_mmcfg; int32_t ret = 0; @@ -258,12 +249,10 @@ int32_t init_vpci(struct acrn_vm *vm) vm->vpci.pci_mmcfg.address + get_pci_mmcfg_size(&vm->vpci.pci_mmcfg), &vm->vpci, false); /* Intercept and handle I/O ports CF8h */ - register_pio_emulation_handler(vm, PCI_CFGADDR_PIO_IDX, &pci_cfgaddr_range, - vpci_pio_cfgaddr_read, vpci_pio_cfgaddr_write); + register_pio_emulation_handler(vm, PCI_CONFIG_ADDR, 1U, vpci_cfgaddr_pio_handler, &vm->vpci); /* Intercept and handle I/O ports CFCh -- CFFh */ - register_pio_emulation_handler(vm, PCI_CFGDATA_PIO_IDX, &pci_cfgdata_range, - vpci_pio_cfgdata_read, vpci_pio_cfgdata_write); + register_pio_emulation_handler(vm, PCI_CONFIG_DATA, 4U, vpci_cfgdata_pio_handler, &vm->vpci); spinlock_init(&vm->vpci.lock); } diff --git a/hypervisor/dm/vpic.c b/hypervisor/dm/vpic.c index 0743426f67..eb0f9b7d21 100644 --- a/hypervisor/dm/vpic.c +++ b/hypervisor/dm/vpic.c @@ -782,13 +782,11 @@ static int32_t vpic_write(struct acrn_vpic *vpic, struct i8259_reg_state *i8259, return error; } -static int32_t vpic_primary_handler(struct acrn_vpic *vpic, bool in, uint16_t port, - size_t bytes, uint32_t *eax) +static int32_t vpic_primary_handler(struct acrn_vpic *vpic, + bool in, uint16_t port, size_t bytes, uint32_t *eax) { - struct i8259_reg_state *i8259; int32_t ret; - - i8259 = &vpic->i8259[0]; + struct i8259_reg_state *i8259 = &vpic->i8259[0]; if (bytes != 1U) { ret = -1; @@ -801,46 +799,25 @@ static int32_t vpic_primary_handler(struct acrn_vpic *vpic, bool in, uint16_t po return ret; } -/** - * @pre vcpu != NULL - * @pre vcpu->vm != NULL - */ -static bool vpic_primary_io_read(struct acrn_vcpu *vcpu, uint16_t addr, size_t width) +static int32_t vpic_primary_pio_handler(struct io_request *io_req, void *private_data) { - struct acrn_pio_request *pio_req = &vcpu->req.reqs.pio_request; + struct acrn_pio_request *pio_req = &io_req->reqs.pio_request; + struct acrn_vpic *vpic = (struct acrn_vpic *)private_data; - if (vpic_primary_handler(vm_pic(vcpu->vm), true, addr, width, &pio_req->value) < 0) { - pr_err("Primary vPIC read port 0x%x width=%d failed\n", - addr, width); - } - - return true; -} - -/** - * @pre vcpu != NULL - * @pre vcpu->vm != NULL - */ -static bool vpic_primary_io_write(struct acrn_vcpu *vcpu, uint16_t addr, size_t width, - uint32_t v) -{ - uint32_t val = v; - - if (vpic_primary_handler(vm_pic(vcpu->vm), false, addr, width, &val) < 0) { + if (vpic_primary_handler(vpic, (pio_req->direction == ACRN_IOREQ_DIR_READ), + pio_req->address, pio_req->size, &pio_req->value) < 0) { pr_err("%s: write port 0x%x width=%d value 0x%x failed\n", - __func__, addr, width, val); + __func__, pio_req->address, pio_req->size, pio_req->value); } - return true; + return 0; } static int32_t vpic_secondary_handler(struct acrn_vpic *vpic, bool in, uint16_t port, size_t bytes, uint32_t *eax) { - struct i8259_reg_state *i8259; int32_t ret; - - i8259 = &vpic->i8259[1]; + struct i8259_reg_state *i8259 = &vpic->i8259[1]; if (bytes != 1U) { ret = -1; @@ -853,36 +830,18 @@ static int32_t vpic_secondary_handler(struct acrn_vpic *vpic, bool in, uint16_t return ret; } -/** - * @pre vcpu != NULL - * @pre vcpu->vm != NULL - */ -static bool vpic_secondary_io_read(struct acrn_vcpu *vcpu, uint16_t addr, size_t width) +static int32_t vpic_secondary_pio_handler(struct io_request *io_req, void *private_data) { - struct acrn_pio_request *pio_req = &vcpu->req.reqs.pio_request; + struct acrn_pio_request *pio_req = &io_req->reqs.pio_request; + struct acrn_vpic *vpic = (struct acrn_vpic *)private_data; - if (vpic_secondary_handler(vm_pic(vcpu->vm), true, addr, width, &pio_req->value) < 0) { - pr_err("Secondary vPIC read port 0x%x width=%d failed\n", - addr, width); - } - return true; -} - -/** - * @pre vcpu != NULL - * @pre vcpu->vm != NULL - */ -static bool vpic_secondary_io_write(struct acrn_vcpu *vcpu, uint16_t addr, size_t width, - uint32_t v) -{ - uint32_t val = v; - - if (vpic_secondary_handler(vm_pic(vcpu->vm), false, addr, width, &val) < 0) { + if (vpic_secondary_handler(vpic, (pio_req->direction == ACRN_IOREQ_DIR_READ), + pio_req->address, pio_req->size, &pio_req->value) < 0) { pr_err("%s: write port 0x%x width=%d value 0x%x failed\n", - __func__, addr, width, val); + __func__, pio_req->address, pio_req->size, pio_req->value); } - return true; + return 0; } static int32_t vpic_elc_handler(struct acrn_vpic *vpic, bool in, uint16_t port, size_t bytes, @@ -931,65 +890,32 @@ static int32_t vpic_elc_handler(struct acrn_vpic *vpic, bool in, uint16_t port, return ret; } -/** - * @pre vcpu != NULL - * @pre vcpu->vm != NULL - */ -static bool vpic_elc_io_read(struct acrn_vcpu *vcpu, uint16_t addr, size_t width) -{ - struct acrn_pio_request *pio_req = &vcpu->req.reqs.pio_request; - - if (vpic_elc_handler(vm_pic(vcpu->vm), true, addr, width, &pio_req->value) < 0) { - pr_err("pic elc read port 0x%x width=%d failed", addr, width); - } - - return true; -} - -/** - * @pre vcpu != NULL - * @pre vcpu->vm != NULL - */ -static bool vpic_elc_io_write(struct acrn_vcpu *vcpu, uint16_t addr, size_t width, - uint32_t v) +static int32_t vpic_elc_pio_handler(struct io_request *io_req, void *private_data) { - uint32_t val = v; + struct acrn_pio_request *pio_req = &io_req->reqs.pio_request; + struct acrn_vpic *vpic = (struct acrn_vpic *)private_data; - if (vpic_elc_handler(vm_pic(vcpu->vm), false, addr, width, &val) < 0) { + if (vpic_elc_handler(vpic, (pio_req->direction == ACRN_IOREQ_DIR_READ), + pio_req->address, pio_req->size, &pio_req->value) < 0) { pr_err("%s: write port 0x%x width=%d value 0x%x failed\n", - __func__, addr, width, val); + __func__, pio_req->address, pio_req->size, pio_req->value); } - return true; + return 0; } -static void vpic_register_io_handler(struct acrn_vm *vm) +static void vpic_register_io_handler(struct acrn_vm *vm, struct acrn_vpic *vpic) { - struct vm_io_range primary_vPIC_range = { - .base = 0x20U, - .len = 2U - }; - struct vm_io_range secondary_vPIC_range = { - .base = 0xa0U, - .len = 2U - }; - struct vm_io_range elcr_range = { - .base = 0x4d0U, - .len = 2U - }; - - register_pio_emulation_handler(vm, PIC_PRIMARY_PIO_IDX, &primary_vPIC_range, - vpic_primary_io_read, vpic_primary_io_write); - register_pio_emulation_handler(vm, PIC_SECONDARY_PIO_IDX, &secondary_vPIC_range, - vpic_secondary_io_read, vpic_secondary_io_write); - register_pio_emulation_handler(vm, PIC_ELC_PIO_IDX, &elcr_range, - vpic_elc_io_read, vpic_elc_io_write); + register_pio_emulation_handler(vm, 0x20U, 2U, vpic_primary_pio_handler, vpic); + register_pio_emulation_handler(vm, 0xa0U, 2U, vpic_secondary_pio_handler, vpic); + register_pio_emulation_handler(vm, 0x4d0U, 2U, vpic_elc_pio_handler, vpic); } + void vpic_init(struct acrn_vm *vm) { struct acrn_vpic *vpic = vm_pic(vm); - vpic_register_io_handler(vm); + vpic_register_io_handler(vm, vpic); vpic->i8259[0].mask = 0xffU; vpic->i8259[1].mask = 0xffU; diff --git a/hypervisor/dm/vrtc.c b/hypervisor/dm/vrtc.c index d938d08f88..f3b1469bdd 100644 --- a/hypervisor/dm/vrtc.c +++ b/hypervisor/dm/vrtc.c @@ -516,7 +516,7 @@ static void vrtc_set_reg_b(struct acrn_vrtc *vrtc, uint8_t newval) * - For a non-Service VM, it will return false indicating the read operation failed if the address is greater than * RTC_CENTURY. Otherwise, the read operation will be emulated. * - * @param[inout] vcpu Pointer to the virtual CPU that is reading from the virtual RTC. The value read from the virtual + * @param[inout] vrtc Pointer to the virtual RTC. The value read from the virtual * RTC will be stored in the PIO request. * @param[in] addr The address port to read from. * @param[in] width The width of the data to be read. This is not used in this function. @@ -526,47 +526,41 @@ static void vrtc_set_reg_b(struct acrn_vrtc *vrtc, uint8_t newval) * @retval true Successfully read from the virtual RTC device. * @retval false Failed to read from the virtual RTC device. * - * @pre vcpu != NULL - * @pre vcpu->vm != NULL * @pre addr == 0x70U || addr == 0x71U * * @post N/A */ -static bool vrtc_read(struct acrn_vcpu *vcpu, uint16_t addr, __unused size_t width) +static uint32_t vrtc_read(struct acrn_vrtc *vrtc, uint16_t addr, __unused size_t width) { uint8_t offset; time_t current; - struct acrn_vrtc *vrtc = &vcpu->vm->vrtc; - struct acrn_pio_request *pio_req = &vcpu->req.reqs.pio_request; - struct acrn_vm *vm = vcpu->vm; - bool ret = true; + uint32_t value = ~0U; offset = vrtc->addr; if (addr == CMOS_ADDR_PORT) { - pio_req->value = offset; + value = offset; } else { - if (is_service_vm(vm)) { - pio_req->value = cmos_get_reg_val(offset); + if (is_service_vm(vrtc->vm)) { + value = cmos_get_reg_val(offset); } else { if (offset <= RTC_CENTURY) { current = vrtc_get_current_time(vrtc); secs_to_rtc(current, vrtc); if(offset == 0xCU) { - pio_req->value = vrtc_get_reg_c(vrtc); + value = vrtc_get_reg_c(vrtc); } else { - pio_req->value = *((uint8_t *)&vrtc->rtcdev + offset); + value = *((uint8_t *)&vrtc->rtcdev + offset); } - RTC_DEBUG("read 0x%x, 0x%x", offset, pio_req->value); + RTC_DEBUG("read 0x%x, 0x%x", offset, value); } else { pr_err("vrtc read invalid addr 0x%x", offset); - ret = false; } } } - return ret; + return value; } static inline bool vrtc_is_time_register(uint32_t offset) @@ -591,30 +585,21 @@ static inline bool vrtc_is_time_register(uint32_t offset) * update the virtual register value and RTC time. And for Post-launched VM, it will send a VM event to notify the * VM of the change in the RTC time if the address port is in the range of the time registers. * - * @param[inout] vcpu Pointer to the virtual CPU that is writing to the virtual RTC. + * @param[inout] vrtc Pointer to the virtual RTC. * @param[in] addr The address port to write to. * @param[in] width Width of the value to be written to the virtual RTC. * @param[in] value Value to be written to the virtual RTC. * - * @return A boolean value indicating whether the write operation is handled successfully, which is always true in - * current design. It either updates the physical registers, updates the virtual registers, or ignores the - * write. - * - * @retval true The write operation is handled successfully. - * - * @pre vcpu != NULL - * @pre vcpu->vm != NULL * @pre addr == 0x70U || addr == 0x71U * * @post N/A * * @remark N/A */ -static bool vrtc_write(struct acrn_vcpu *vcpu, uint16_t addr, size_t width, +static void vrtc_write(struct acrn_vrtc *vrtc, uint16_t addr, size_t width, uint32_t value) { time_t current, after; - struct acrn_vrtc *vrtc = &vcpu->vm->vrtc; struct acrn_vrtc temp_vrtc; uint8_t mask = 0xFFU; struct vm_event rtc_chg_event; @@ -623,14 +608,14 @@ static bool vrtc_write(struct acrn_vcpu *vcpu, uint16_t addr, size_t width, if ((width == 1U) && (addr == CMOS_ADDR_PORT)) { vrtc->addr = (uint8_t)(value & 0x7FU); } else { - if (is_service_vm(vcpu->vm)) { + if (is_service_vm(vrtc->vm)) { if (vrtc_is_time_register(vrtc->addr)) { current = vrtc_get_physical_rtc_time(&temp_vrtc); - cmos_set_reg_val(vcpu->vm->vrtc.addr, (uint8_t)(value & 0xFFU)); + cmos_set_reg_val(vrtc->vm->vrtc.addr, (uint8_t)(value & 0xFFU)); after = vrtc_get_physical_rtc_time(&temp_vrtc); vrtc_update_basetime(after, current - after); } else { - cmos_set_reg_val(vcpu->vm->vrtc.addr, (uint8_t)(value & 0xFFU)); + cmos_set_reg_val(vrtc->vm->vrtc.addr, (uint8_t)(value & 0xFFU)); } } else { switch (vrtc->addr) { @@ -665,20 +650,33 @@ static bool vrtc_write(struct acrn_vcpu *vcpu, uint16_t addr, size_t width, vrtc->offset_rtctime += after - current; vrtc->last_rtctime = VRTC_BROKEN_TIME; spinlock_release(&vrtc_rebase_lock); - if (is_postlaunched_vm(vcpu->vm) && vrtc_is_time_register(vrtc->addr)) { + if (is_postlaunched_vm(vrtc->vm) && vrtc_is_time_register(vrtc->addr)) { rtc_chg_event.type = VM_EVENT_RTC_CHG; edata->delta_time = after - current; edata->last_time = current; - send_vm_event(vcpu->vm, &rtc_chg_event); + send_vm_event(vrtc->vm, &rtc_chg_event); } break; } } } +} + +static int32_t vrtc_pio_handler(struct io_request *io_req, void *private_data) +{ + struct acrn_vrtc *vrtc = (struct acrn_vrtc *)private_data; + struct acrn_pio_request *pio_req = &io_req->reqs.pio_request; - return true; + if (pio_req->direction == ACRN_IOREQ_DIR_READ) { + pio_req->value = vrtc_read(vrtc, pio_req->address, pio_req->size); + } else { + vrtc_write(vrtc, pio_req->address, pio_req->size, pio_req->value); + } + + return 0; } + #define CALIBRATE_PERIOD (3 * 3600 * 1000) /* By ms, totally 3 hours. */ static struct hv_timer calibrate_timer; @@ -803,14 +801,11 @@ void resume_vrtc(void) */ void vrtc_init(struct acrn_vm *vm) { - struct vm_io_range range = { - .base = CMOS_ADDR_PORT, .len = 2U}; - /* Initializing the CMOS RAM offset to 0U */ vm->vrtc.addr = 0U; vm->vrtc.vm = vm; - register_pio_emulation_handler(vm, RTC_PIO_IDX, &range, vrtc_read, vrtc_write); + register_pio_emulation_handler(vm, CMOS_ADDR_PORT, 2U, vrtc_pio_handler, &vm->vrtc); if (is_service_vm(vm)) { calibrate_setup_timer(); diff --git a/hypervisor/dm/vuart.c b/hypervisor/dm/vuart.c index d693624951..13f3fde64e 100644 --- a/hypervisor/dm/vuart.c +++ b/hypervisor/dm/vuart.c @@ -165,22 +165,6 @@ static uint8_t vuart_intr_reason(const struct acrn_vuart *vu) return ret; } -static struct acrn_vuart *find_vuart_by_port(struct acrn_vm *vm, uint16_t offset) -{ - uint8_t i; - struct acrn_vuart *vu, *ret_vu = NULL; - - /* TODO: support pci vuart find */ - for (i = 0U; i < MAX_VUART_NUM_PER_VM; i++) { - vu = &vm->vuart[i]; - if ((vu->active) && (vu->port_base == (offset & ~0x7U))) { - ret_vu = vu; - break; - } - } - return ret_vu; -} - static void vuart_trigger_level_intr(const struct acrn_vuart *vu, bool assert) { arch_trigger_level_intr(vu->vm, vu->irq, assert); @@ -422,44 +406,6 @@ void vuart_write_reg(struct acrn_vuart *vu, uint16_t offset, uint8_t value_u8) } } -/** - * @brief Write a value to a port in the legacy virtual UART. - * - * This function writes a value to the legacy virtual UART (vUART) based on the specified port address. It is used to - * handle I/O port write operations in the VM by updating the vUART's register. This function is typically called when - * the vCPU needs to write data to the vUART during emulation of I/O port operations. - * - * - Based on the specified port address, it first finds the vUART device within the VM corresponding to the given vCPU. - * - If the vUART is found, the value is written to the corresponding register. For detailed write operations, refer to - * vuart_write_reg(). - * - If the vUART is not found, the write operation is ignored. - * - * @param[inout] vcpu A pointer to the vCPU that initiates the write operation. - * @param[in] offset_arg The port address to write to. - * @param[in] width The width of the write operation (unused in this function). - * @param[in] value The value to be written to the register. - * - * @return Always returns true. - * - * @pre vcpu != NULL - * @pre vcpu->vm != NULL - * - * @post N/A - */ -static bool vuart_write(struct acrn_vcpu *vcpu, uint16_t offset_arg, - __unused size_t width, uint32_t value) -{ - uint16_t offset = offset_arg; - struct acrn_vuart *vu = find_vuart_by_port(vcpu->vm, offset); - uint8_t value_u8 = (uint8_t)value; - - if (vu != NULL) { - offset -= vu->port_base; - vuart_write_reg(vu, offset, value_u8); - } - return true; -} - static void notify_target(const struct acrn_vuart *vu) { struct acrn_vuart *t_vu; @@ -591,60 +537,25 @@ uint8_t vuart_read_reg(struct acrn_vuart *vu, uint16_t offset) return reg; } -/** - * @brief Read a value from a port in the legacy virtual UART. - * - * This function reads a value from the legacy virtual UART (vUART) based on the specified port address. It is used to - * handle I/O port read operations in the VM by retrieving the value from the vUART's registers. This function is - * typically called when the vCPU needs to read data from the vUART during emulation of I/O port operations. - * - * - Based on the specified port address, it first finds the vUART device within the VM corresponding to the given vCPU. - * - If the vUART is found, the value is read from the corresponding virtual register and stored in the vCPU's PIO - * request. For detailed read operations, refer to vuart_read_reg(). - * - If the vUART is not found, the vCPU's PIO request remains unchanged. - * - * @param[inout] vcpu A pointer to the vCPU that initiates the read operation. - * @param[in] offset_arg The port address to read from. - * @param[in] width The width of the read operation (unused in this function). - * - * @return Always returns true. - * - * @pre vcpu != NULL - * @pre vcpu->vm != NULL - * - * @post N/A - */ -static bool vuart_read(struct acrn_vcpu *vcpu, uint16_t offset_arg, __unused size_t width) +static int32_t vuart_pio_handler(struct io_request *io_req, __unused void *private_data) { - uint16_t offset = offset_arg; - struct acrn_vuart *vu = find_vuart_by_port(vcpu->vm, offset); - struct acrn_pio_request *pio_req = &vcpu->req.reqs.pio_request; + struct acrn_pio_request *pio_req = &io_req->reqs.pio_request; + struct acrn_vuart *vu = (struct acrn_vuart *)private_data; + uint16_t offset = pio_req->address; + int32_t ret = 0; if (vu != NULL) { offset -= vu->port_base; - pio_req->value = (uint32_t)vuart_read_reg(vu, offset); - } - return true; -} - -/* - * @pre: vuart_idx < MAX_VUART_NUM_PER_VM - */ -static bool vuart_register_io_handler(struct acrn_vm *vm, uint16_t port_base, uint32_t vuart_idx) -{ - bool ret = true; - - struct vm_io_range range = { - .base = port_base, - .len = 8U - }; - if (vuart_idx < MAX_VUART_NUM_PER_VM) { - register_pio_emulation_handler(vm, UART_PIO_IDX0 + vuart_idx, &range, vuart_read, vuart_write); + if (pio_req->direction == ACRN_IOREQ_DIR_READ) { + pio_req->value = (uint32_t)vuart_read_reg(vu, offset); + } else { + vuart_write_reg(vu, offset, (uint8_t)pio_req->value); + } } else { - printf("Not support vuart index %d, will not register \n", vuart_idx); - ret = false; + ret = -ENODEV; } + return ret; } @@ -778,10 +689,11 @@ void init_legacy_vuarts(struct acrn_vm *vm, const struct vuart_config *vu_config setup_vuart(vm, i); vu->port_base = vu_config[i].addr.port_base; vu->irq = vu_config[i].irq; - if (vuart_register_io_handler(vm, vu->port_base, i) != 0U) { - vu->active = true; - vu->escaping = false; - } + + register_pio_emulation_handler(vm, vu->port_base, 8U, vuart_pio_handler, vu); + vu->active = true; + vu->escaping = false; + /* * The first vuart is used for VM console. * The rest of vuarts are used for connection. diff --git a/hypervisor/include/arch/riscv/asm/guest/vcpu.h b/hypervisor/include/arch/riscv/asm/guest/vcpu.h index c81cc7ac57..d63da94532 100644 --- a/hypervisor/include/arch/riscv/asm/guest/vcpu.h +++ b/hypervisor/include/arch/riscv/asm/guest/vcpu.h @@ -81,6 +81,18 @@ struct intr_excp_ctx; int32_t riscv_process_vcpu_requests(struct acrn_vcpu *vcpu); +static inline int32_t emulate_instruction(struct acrn_vcpu *vcpu) +{ + (void)vcpu; + return 0; +} + +static inline void emulate_pio_complete(struct acrn_vcpu *vcpu, const struct io_request *io_req) +{ + (void)vcpu; + (void)io_req; +} + #endif /* ASSEMBLER */ #endif /* RISCV_VCPU_H */ diff --git a/hypervisor/include/arch/riscv/asm/guest/vm.h b/hypervisor/include/arch/riscv/asm/guest/vm.h index d4499a3e57..20999e41d6 100644 --- a/hypervisor/include/arch/riscv/asm/guest/vm.h +++ b/hypervisor/include/arch/riscv/asm/guest/vm.h @@ -13,11 +13,6 @@ #include -#define INVALID_PIO_IDX -1U -#define UART_PIO_IDX0 INVALID_PIO_IDX -/* FIXME: dummy. to be implemented later */ -#define EMUL_PIO_IDX_MAX 1U - struct vm_arch { const struct acrn_vsbi_extension *vsbi_exts[MAX_NUM_SUPPORTED_VSBI_EXT]; uint16_t n_vsbi_exts; @@ -33,4 +28,11 @@ struct acrn_vm; uint32_t vcpu_get_vhartid(struct acrn_vcpu *vcpu); struct acrn_vcpu *vcpu_from_vhartid(struct acrn_vm *vm, uint32_t vhartid); +static inline void deny_guest_pio_access(struct acrn_vm *vm, uint16_t port_address, uint32_t nbytes) +{ + (void)vm; + (void)port_address; + (void)nbytes; +} + #endif /* RISCV_VM_H_ */ diff --git a/hypervisor/include/arch/riscv/asm/irq.h b/hypervisor/include/arch/riscv/asm/irq.h index 938d0de552..7116301c95 100644 --- a/hypervisor/include/arch/riscv/asm/irq.h +++ b/hypervisor/include/arch/riscv/asm/irq.h @@ -51,6 +51,11 @@ struct riscv_irq_data { /* Can be extended when PROFILING_ON is supported, similar to "struct x86_irq_data". */ }; +/* FIXME: need parse DT to find an available vectorfor HSM */ +#define HYPERVISOR_CALLBACK_HSM_VECTOR 0x20U + +static inline void arch_fire_hsm_interrupt(void) {} + struct intr_excp_ctx { struct cpu_regs regs; }; diff --git a/hypervisor/include/arch/x86/asm/guest/vmx_io.h b/hypervisor/include/arch/x86/asm/guest/vmx_io.h index c54919632e..645ee9190b 100644 --- a/hypervisor/include/arch/x86/asm/guest/vmx_io.h +++ b/hypervisor/include/arch/x86/asm/guest/vmx_io.h @@ -9,25 +9,6 @@ #include -/* Define emulated port IO index */ -#define PIC_PRIMARY_PIO_IDX 0U -#define PIC_SECONDARY_PIO_IDX (PIC_PRIMARY_PIO_IDX + 1U) -#define PIC_ELC_PIO_IDX (PIC_SECONDARY_PIO_IDX + 1U) -#define PCI_CFGADDR_PIO_IDX (PIC_ELC_PIO_IDX + 1U) -#define PCI_CFGDATA_PIO_IDX (PCI_CFGADDR_PIO_IDX + 1U) -/* MAX_VUART_NUM_PER_VM is 8, so allocate UART_PIO_IDX0~UART_PIO_IDX0 + 7 for 8 vuart */ -#define UART_PIO_IDX0 (PCI_CFGDATA_PIO_IDX + 1U) -#define PM1A_EVT_PIO_IDX (UART_PIO_IDX0 + MAX_VUART_NUM_PER_VM) -#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 VIRTUAL_PM1A_CNT_PIO_IDX (RTC_PIO_IDX + 1U) -#define KB_PIO_IDX (VIRTUAL_PM1A_CNT_PIO_IDX + 1U) -#define CF9_PIO_IDX (KB_PIO_IDX + 1U) -#define PIO_RESET_REG_IDX (CF9_PIO_IDX + 1U) -#define SLEEP_CTL_PIO_IDX (PIO_RESET_REG_IDX + 1U) -#define EMUL_PIO_IDX_MAX (SLEEP_CTL_PIO_IDX + 1U) /** * @brief The handler of VM exits on I/O instructions * diff --git a/hypervisor/include/common/vm.h b/hypervisor/include/common/vm.h index f8d6d4ae24..d00410e1ca 100644 --- a/hypervisor/include/common/vm.h +++ b/hypervisor/include/common/vm.h @@ -30,6 +30,8 @@ #define NEED_SHUTDOWN_VM (2U) +#define EMUL_IO_BITMAP_SIZE INT_DIV_ROUNDUP(CONFIG_MAX_EMULATED_MMIO_REGIONS, 64U) + struct vm_hw_info { /* vcpu array of this VM */ struct acrn_vcpu vcpu_array[MAX_VCPUS_PER_VM]; @@ -92,10 +94,9 @@ struct acrn_vm { struct acrn_vpci vpci; struct acrn_vrtc vrtc; - spinlock_t emul_mmio_lock; /* Used to protect emulation mmio_node concurrent access for a VM */ - uint16_t nr_emul_mmio_regions; /* the emulated mmio_region number */ - struct mem_io_node emul_mmio[CONFIG_MAX_EMULATED_MMIO_REGIONS]; - struct vm_io_handler_desc emul_pio[EMUL_PIO_IDX_MAX]; + spinlock_t emul_io_lock; /* Used to protect emulation pio/mmio node concurrent access for a VM */ + uint64_t emul_io_bitmap[EMUL_IO_BITMAP_SIZE]; + struct io_node emul_io[CONFIG_MAX_EMULATED_MMIO_REGIONS]; /* Pointer to root stage2 pagetable */ void *root_stg2ptp; diff --git a/hypervisor/include/dm/io_req.h b/hypervisor/include/dm/io_req.h index 9e1b8c9c3f..c840365794 100644 --- a/hypervisor/include/dm/io_req.h +++ b/hypervisor/include/dm/io_req.h @@ -45,15 +45,7 @@ struct asyncio_desc { struct list_head list; }; -/** - * @brief Definition of a IO port range - */ -struct vm_io_range { - uint16_t base; /**< IO port base */ - uint16_t len; /**< IO port range */ -}; -struct vm_io_handler_desc; struct acrn_vm; struct acrn_vcpu; @@ -63,64 +55,13 @@ bool (*io_read_fn_t)(struct acrn_vcpu *vcpu, uint16_t port, size_t size); typedef bool (*io_write_fn_t)(struct acrn_vcpu *vcpu, uint16_t port, size_t size, uint32_t val); -/** - * @brief Describes a single IO handler description entry. - */ -struct vm_io_handler_desc { - - /** - * @brief The base port number of the IO range for this description. - */ - uint16_t port_start; - - /** - * @brief The last port number of the IO range for this description (non-inclusive). - */ - uint16_t port_end; - - /** - * @brief A pointer to the "read" function. - * - * The read function is called from the hypervisor whenever - * a read access to a range described in "ranges" occur. - * The arguments to the callback are: - * - * - The address of the port to read from. - * - The width of the read operation (1,2 or 4). - * - * The implementation must return the ports content as - * byte, word or doubleword (depending on the width). - * - * If the pointer is null, a read of 1's is assumed. - */ - io_read_fn_t io_read; - - /** - * @brief A pointer to the "write" function. - * - * The write function is called from the hypervisor code - * whenever a write access to a range described in "ranges" - * occur. The arguments to the callback are: - * - * - The address of the port to write to. - * - The width of the write operation (1,2 or 4). - * - The value to write as byte, word or doubleword - * (depending on the width) - * - * The implementation must write the value to the port. - * - * If the pointer is null, the write access is ignored. - */ - io_write_fn_t io_write; -}; - /* Typedef for MMIO handler and range check routine */ -typedef int32_t (*hv_mem_io_handler_t)(struct io_request *io_req, void *handler_private_data); +typedef int32_t (*hv_io_handler_t)(struct io_request *io_req, void *handler_private_data); /** - * @brief Structure for MMIO handler node + * @brief Structure for PIO/MMIO handler node */ -struct mem_io_node { +struct io_node { /** * @brief Whether the lock needs to hold when handle the MMIO access @@ -133,7 +74,7 @@ struct mem_io_node { * * The function for handling MMIO accesses to the specified range. */ - hv_mem_io_handler_t read_write; + hv_io_handler_t read_write; /** * @brief Private data used by the handler @@ -251,14 +192,13 @@ int32_t emulate_io(struct acrn_vcpu *vcpu, struct io_request *io_req); * @brief Register a port I/O handler * * @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 + * @param pio_base The emulated port io range base + * @param pio_size The emulated port io range size + * @param read_write The handler for emulating accesses to the given range + * @param handler_private_data Handler-specific data which will be passed to \p read_write when called */ -void register_pio_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); +void register_pio_emulation_handler(struct acrn_vm *vm, uint16_t pio_base, uint16_t pio_size, + hv_io_handler_t read_write, void *handler_private_data); /** * @brief Register a MMIO handler @@ -273,7 +213,7 @@ void register_pio_emulation_handler(struct acrn_vm *vm, uint32_t pio_idx, * @param hold_lock Whether hold the lock to handle the MMIO access */ void register_mmio_emulation_handler(struct acrn_vm *vm, - hv_mem_io_handler_t read_write, uint64_t start, + hv_io_handler_t read_write, uint64_t start, uint64_t end, void *handler_private_data, bool hold_lock); /**