diff --git a/hypervisor/acpi_parser/acpi_ext.c b/hypervisor/acpi_parser/acpi_ext.c index b48a8adaa..709e2c3b4 100644 --- a/hypervisor/acpi_parser/acpi_ext.c +++ b/hypervisor/acpi_parser/acpi_ext.c @@ -35,6 +35,7 @@ #include #include #include +#include /* Per ACPI spec: * There are two fundamental types of ACPI tables: @@ -56,6 +57,8 @@ /* FACP field offsets */ #define OFFSET_FACS_ADDR 36U +#define OFFSET_RESET_REGISTER 116U +#define OFFSET_RESET_VALUE 128U #define OFFSET_FACS_X_ADDR 132U #define OFFSET_PM1A_EVT 148U #define OFFSET_PM1A_CNT 172U @@ -154,5 +157,14 @@ void acpi_fixup(void) sx_data->wake_vector_32 = (uint32_t *)(facs_addr + OFFSET_WAKE_VECTOR_32); sx_data->wake_vector_64 = (uint64_t *)(facs_addr + OFFSET_WAKE_VECTOR_64); } + + const struct acpi_table_header *table = (const struct acpi_table_header *)facp_addr; + + if (table->revision >= 2U) { + struct acpi_reset_reg *rr_data = get_host_reset_reg_data(); + + get_acpi_dt_gas(facp_addr, OFFSET_RESET_REGISTER, &(rr_data->reg)); + rr_data->val = *(facp_addr + OFFSET_RESET_VALUE); + } } } diff --git a/hypervisor/arch/x86/configs/apl-mrb/platform_acpi_info.h b/hypervisor/arch/x86/configs/apl-mrb/platform_acpi_info.h index 4c99286b9..0d9378bd3 100644 --- a/hypervisor/arch/x86/configs/apl-mrb/platform_acpi_info.h +++ b/hypervisor/arch/x86/configs/apl-mrb/platform_acpi_info.h @@ -20,6 +20,11 @@ #define WAKE_VECTOR_32 0x7A86BBDCUL #define WAKE_VECTOR_64 0x7A86BBE8UL +/* reset register */ +#define RESET_REGISTER_ADDRESS 0xCF9U +#define RESET_REGISTER_VALUE 0x0EU +#define RESET_REGISTER_SPACE_ID SPACE_SYSTEM_IO + /* DRHD of DMAR */ #define DRHD_COUNT 2U diff --git a/hypervisor/arch/x86/configs/platform_acpi_info.h b/hypervisor/arch/x86/configs/platform_acpi_info.h index 619959dea..19aaa3534 100644 --- a/hypervisor/arch/x86/configs/platform_acpi_info.h +++ b/hypervisor/arch/x86/configs/platform_acpi_info.h @@ -20,6 +20,10 @@ #define WAKE_VECTOR_32 0UL #define WAKE_VECTOR_64 0UL +#define RESET_REGISTER_ADDRESS 0UL +#define RESET_REGISTER_VALUE 0UL +#define RESET_REGISTER_SPACE_ID 0UL + /* DRHD of DMAR */ #define DRHD_COUNT 0U diff --git a/hypervisor/arch/x86/guest/vm_reset.c b/hypervisor/arch/x86/guest/vm_reset.c index d487ef858..91f7e495d 100644 --- a/hypervisor/arch/x86/guest/vm_reset.c +++ b/hypervisor/arch/x86/guest/vm_reset.c @@ -9,6 +9,25 @@ #include #include #include +#include +#include + +/* host reset register defined in ACPI */ +static struct acpi_reset_reg host_reset_reg = { + .reg = { + .space_id = RESET_REGISTER_SPACE_ID, + .bit_width = RESET_REGISTER_BIT_WIDTH, + .bit_offset = RESET_REGISTER_BIT_OFFSET, + .access_size = RESET_REGISTER_ACCESS_SIZE, + .address = RESET_REGISTER_ADDRESS, + }, + .val = RESET_REGISTER_VALUE +}; + +struct acpi_reset_reg *get_host_reset_reg_data(void) +{ + return &host_reset_reg; +} /** * @pre vm != NULL @@ -74,6 +93,7 @@ static bool handle_reset_reg_read(struct acrn_vm *vm, struct acrn_vcpu *vcpu, __ /* * - keyboard control/status register 0x64: ACRN doesn't expose kbd controller to the guest. * - reset control register 0xcf9: hide this from guests for now. + * - FADT reset register: the read behavior is not defined in spec, keep it simple to return all '1'. */ vcpu->req.reqs.pio.value = ~0U; } @@ -131,6 +151,24 @@ static bool handle_cf9_write(struct acrn_vm *vm, __unused uint16_t addr, size_t return handle_common_reset_reg_write(vm, ((bytes == 1U) && ((val & 0x4U) == 0x4U) && ((val & 0xaU) != 0U))); } +static bool handle_reset_reg_write(__unused struct acrn_vm *vm, uint16_t addr, size_t bytes, uint32_t val) +{ + if (bytes == 1U) { + if (val == host_reset_reg.val) { + /* ignore reset request */ + } else { + /* + * ACPI defines the reset value but doesn't specify the meaning of other values. + * in the case the reset register (e.g. PIO 0xB2) has other purpose other than reset, + * we can't ignore the write with other values. + */ + pio_write8((uint8_t)val, addr); + } + } + + return true; +} + /** * @pre vm != NULL */ @@ -138,6 +176,8 @@ void register_reset_port_handler(struct acrn_vm *vm) { /* Don't support SOS and pre-launched VM re-launch for now. */ if (!is_postlaunched_vm(vm) || is_rt_vm(vm)) { + struct acpi_generic_address *gas = &(host_reset_reg.reg); + struct vm_io_range io_range = { .flags = IO_ATTR_RW, .len = 1U @@ -148,6 +188,23 @@ void register_reset_port_handler(struct acrn_vm *vm) io_range.base = 0xcf9U; register_pio_emulation_handler(vm, CF9_PIO_IDX, &io_range, handle_reset_reg_read, handle_cf9_write); + + /* + * - pre-launched VMs don't support ACPI; + * - ACPI reset register is fixed at 0xcf9 for post-launched VMs; + * - here is taking care of SOS only: + * Don't support MMIO or PCI based reset register for now. + * ACPI Spec: Register_Bit_Width must be 8 and Register_Bit_Offset must be 0. + */ + if (is_sos_vm(vm) && + (gas->space_id == SPACE_SYSTEM_IO) && + (gas->bit_width == 8U) && (gas->bit_offset == 0U) && + (gas->address != 0xcf9U) && (gas->address != 0x64U)) { + + io_range.base = (uint16_t)host_reset_reg.reg.address; + register_pio_emulation_handler(vm, PIO_RESET_REG_IDX, &io_range, + handle_reset_reg_read, handle_reset_reg_write); + } } } diff --git a/hypervisor/include/arch/x86/default_acpi_info.h b/hypervisor/include/arch/x86/default_acpi_info.h index b3ce49fde..b2eb17c4e 100644 --- a/hypervisor/include/arch/x86/default_acpi_info.h +++ b/hypervisor/include/arch/x86/default_acpi_info.h @@ -48,4 +48,9 @@ #define S5_PKG_VAL_PM1B 0U #define S5_PKG_RESERVED 0U +/* reset register */ +#define RESET_REGISTER_BIT_WIDTH 0x08U +#define RESET_REGISTER_BIT_OFFSET 0U +#define RESET_REGISTER_ACCESS_SIZE 0x01U + #endif /* DEFAULT_ACPI_INFO_H */ diff --git a/hypervisor/include/arch/x86/guest/vm_reset.h b/hypervisor/include/arch/x86/guest/vm_reset.h index 1593367d3..a10db6668 100644 --- a/hypervisor/include/arch/x86/guest/vm_reset.h +++ b/hypervisor/include/arch/x86/guest/vm_reset.h @@ -7,8 +7,16 @@ #ifndef VM_RESET_H_ #define VM_RESET_H_ +#include + +struct acpi_reset_reg { + struct acpi_generic_address reg; + uint8_t val; +}; + void register_reset_port_handler(struct acrn_vm *vm); void shutdown_vm_from_idle(uint16_t pcpu_id); void triple_fault_shutdown_vm(struct acrn_vm *vm); +struct acpi_reset_reg *get_host_reset_reg_data(void); #endif /* VM_RESET_H_ */ diff --git a/hypervisor/include/arch/x86/guest/vmx_io.h b/hypervisor/include/arch/x86/guest/vmx_io.h index 739973ca2..df38c844c 100644 --- a/hypervisor/include/arch/x86/guest/vmx_io.h +++ b/hypervisor/include/arch/x86/guest/vmx_io.h @@ -26,7 +26,8 @@ #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 EMUL_PIO_IDX_MAX (CF9_PIO_IDX + 1U) +#define PIO_RESET_REG_IDX (CF9_PIO_IDX + 1U) +#define EMUL_PIO_IDX_MAX (PIO_RESET_REG_IDX + 1U) /** * @brief The handler of VM exits on I/O instructions