From 9aa3fe646ba3535bb1b82c861480c1892b618d3a Mon Sep 17 00:00:00 2001 From: Zide Chen Date: Wed, 8 May 2019 22:34:32 -0700 Subject: [PATCH] hv: emulate reset register 0xcf9 and 0x64 - post-launched RTVM: intercept both PIO ports so that hypervisor has a chance to set VM_POWERING_OFF flag. - all other type of VMs: deny these 2 ports from guest access so that guests are not able to reset host. Tracked-On: #2700 Signed-off-by: Zide Chen Signed-off-by: Sainath Grandhi Acked-by: Eddie Dong --- hypervisor/arch/x86/guest/vm.c | 3 + hypervisor/arch/x86/guest/vm_reset.c | 96 +++++++++++++++++++- hypervisor/include/arch/x86/guest/vm_reset.h | 1 + hypervisor/include/arch/x86/guest/vmx_io.h | 4 +- 4 files changed, 102 insertions(+), 2 deletions(-) diff --git a/hypervisor/arch/x86/guest/vm.c b/hypervisor/arch/x86/guest/vm.c index d314004e01..e70778bdce 100644 --- a/hypervisor/arch/x86/guest/vm.c +++ b/hypervisor/arch/x86/guest/vm.c @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -423,6 +424,8 @@ int32_t create_vm(uint16_t vm_id, struct acrn_vm_config *vm_config, struct acrn_ vpci_init(vm); + register_reset_port_handler(vm); + /* vpic wire_mode default is INTR */ vm->wire_mode = VPIC_WIRE_INTR; diff --git a/hypervisor/arch/x86/guest/vm_reset.c b/hypervisor/arch/x86/guest/vm_reset.c index b004ef9780..2a2b46ab17 100644 --- a/hypervisor/arch/x86/guest/vm_reset.c +++ b/hypervisor/arch/x86/guest/vm_reset.c @@ -5,8 +5,102 @@ */ #include -#include +#include +#include #include +#include + +/** + * @pre vcpu != NULL && vm != NULL + */ +static bool handle_reset_reg_read(struct acrn_vm *vm, struct acrn_vcpu *vcpu, __unused uint16_t addr, + __unused size_t bytes) +{ + bool ret = true; + + if (is_postlaunched_vm(vm)) { + /* re-inject to DM */ + ret = false; + } else { + /* + * - keyboard control/status register 0x64: ACRN doesn't expose kbd controller to the guest. + * - reset control register 0xcf9: hide this from guests for now. + */ + vcpu->req.reqs.pio.value = ~0U; + } + + return ret; +} + +/** + * @pre vm != NULL + */ +static bool handle_common_reset_reg_write(struct acrn_vm *vm, bool reset) +{ + bool ret = true; + + if (is_postlaunched_vm(vm)) { + /* re-inject to DM */ + ret = false; + + if (reset && is_rt_vm(vm)) { + vm->state = VM_POWERING_OFF; + } + } else { + /* + * ignore writes from SOS or pre-launched VMs. + * equivalent to hide this port from guests. + */ + } + + return ret; +} + +/** + * @pre vm != NULL + */ +static bool 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(vm, ((bytes == 1U) && (val == 0xfeU))); +} + +/* + * Reset Control register at I/O port 0xcf9. + * Bit 1 - 0: "soft" reset. Force processor begin execution at power-on reset vector. + * 1: "hard" reset. e.g. assert PLTRST# (if implemented) to do a host reset. + * Bit 2 - initiates a system reset when it transitions from 0 to 1. + * Bit 3 - 1: full reset (aka code reset), SLP_S3#/4#/5# or similar pins are asserted for full power cycle. + * 0: will be dropped if system in S3/S4/S5. + */ +/** + * @pre vm != NULL + */ +static bool handle_cf9_write(struct acrn_vm *vm, __unused uint16_t addr, size_t bytes, uint32_t val) +{ + /* We don't differentiate among hard/soft/warm/cold reset */ + return handle_common_reset_reg_write(vm, ((bytes == 1U) && ((val & 0x4U) == 0x4U) && ((val & 0xaU) != 0U))); +} + +/** + * @pre vm != NULL + */ +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 vm_io_range io_range = { + .flags = IO_ATTR_RW, + .len = 1U + }; + + io_range.base = 0x64U; + register_pio_emulation_handler(vm, KB_PIO_IDX, &io_range, handle_reset_reg_read, handle_kb_write); + + io_range.base = 0xcf9U; + register_pio_emulation_handler(vm, CF9_PIO_IDX, &io_range, handle_reset_reg_read, handle_cf9_write); + } +} void shutdown_vm_from_idle(uint16_t pcpu_id) { diff --git a/hypervisor/include/arch/x86/guest/vm_reset.h b/hypervisor/include/arch/x86/guest/vm_reset.h index d83ba307ee..1a5d05642c 100644 --- a/hypervisor/include/arch/x86/guest/vm_reset.h +++ b/hypervisor/include/arch/x86/guest/vm_reset.h @@ -7,6 +7,7 @@ #ifndef VM_RESET_H_ #define VM_RESET_H_ +void register_reset_port_handler(struct acrn_vm *vm); void shutdown_vm_from_idle(uint16_t pcpu_id); #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 8a42ff02ca..739973ca2e 100644 --- a/hypervisor/include/arch/x86/guest/vmx_io.h +++ b/hypervisor/include/arch/x86/guest/vmx_io.h @@ -24,7 +24,9 @@ #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 EMUL_PIO_IDX_MAX (VIRTUAL_PM1A_CNT_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) /** * @brief The handler of VM exits on I/O instructions