diff --git a/hypervisor/arch/x86/guest/pm.c b/hypervisor/arch/x86/guest/pm.c index 69cb0a775..60a23a53d 100644 --- a/hypervisor/arch/x86/guest/pm.c +++ b/hypervisor/arch/x86/guest/pm.c @@ -309,8 +309,88 @@ static void register_rt_vm_pm1a_ctl_handler(struct acrn_vm *vm) &rt_vm_pm1a_io_read, &rt_vm_pm1a_io_write); } +/* + * @pre vcpu != NULL + */ +static bool prelaunched_vm_sleep_io_read(struct acrn_vcpu *vcpu, __unused uint16_t addr, __unused size_t width) +{ + vcpu->req.reqs.pio.value = 0U; + + return true; +} + +/* + * @pre vcpu != NULL + * @pre vcpu->vm != NULL + */ +static bool prelaunched_vm_sleep_io_write(struct acrn_vcpu *vcpu, 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; + + /* ACPI sleep control register: + * + * Bits 2~4: SLP_TYPx, defines the type of sleeping state the system enters when the + * SLP_EN bit is set to one. This 3-bit field defines the type of hardware sleep state + * the system enters when the SLP_EN bit is set. The \_S5 object contains 3-bit binary + * value (0x5) associated with the S5 sleeping state + * + * Bit 5: SLP_EN, This is a write-only bit and reads to it always return a zero. Setting + * this bit causes the system to sequence into the sleeping state associated with the + * SLP_TYPx fields programmed with the values from the \_S5 object + */ + slp_type = (v >> 2U) & 0x7U; + slp_en = (v >> 5U) & 0x1U; + + if (slp_en && (slp_type == 5U)) { + get_vm_lock(vm); + if (is_rt_vm(vm)) { + vm->state = VM_READY_TO_POWEROFF; + } + pause_vm(vm); + put_vm_lock(vm); + + bitmap_set_nolock(vm->vm_id, &per_cpu(shutdown_vm_bitmap, pcpuid_from_vcpu(vcpu))); + make_shutdown_vm_request(pcpuid_from_vcpu(vcpu)); + } + } + + return true; +} + +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 + * a standard mechanism for system sleep state entry on HW-Reduced ACPI systems. When + * 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); +} + void init_guest_pm(struct acrn_vm *vm) { + struct pm_s_state_data *sx_data = get_host_sstate_data(); + + /* + * In enter_s5(), it will call save_s5_reg_val() to initialize system_pm1a_cnt_val/system_pm1b_cnt_val when the + * vm is SOS. + * If there is no SOS, save_s5_reg_val() will not be called and these 2 variables will not be initialized properly + * so shutdown_system() will fail, explicitly init here to avoid this + */ + save_s5_reg_val((sx_data->s5_pkg.val_pm1a << BIT_SLP_TYPx) | (1U << BIT_SLP_EN), + (sx_data->s5_pkg.val_pm1b << BIT_SLP_TYPx) | (1U << BIT_SLP_EN)); + vm_setup_cpu_state(vm); if (is_sos_vm(vm)) { @@ -318,10 +398,11 @@ void init_guest_pm(struct acrn_vm *vm) if (vm_load_pm_s_state(vm) == 0) { register_pm1ab_handler(vm); } - } - - /* Intercept the virtual pm port for RTVM */ - if (is_rt_vm(vm)) { + } else if (is_postlaunched_vm(vm) && is_rt_vm(vm)) { + /* Intercept the virtual pm port for post launched RTVM */ register_rt_vm_pm1a_ctl_handler(vm); + } else if (is_prelaunched_vm(vm)) { + /* Intercept the virtual sleep control/status registers for pre-launched VM */ + register_prelaunched_vm_sleep_handler(vm); } } diff --git a/hypervisor/include/arch/x86/guest/vmx_io.h b/hypervisor/include/arch/x86/guest/vmx_io.h index 5790d0b87..98fce24d6 100644 --- a/hypervisor/include/arch/x86/guest/vmx_io.h +++ b/hypervisor/include/arch/x86/guest/vmx_io.h @@ -27,8 +27,8 @@ #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 EMUL_PIO_IDX_MAX (PIO_RESET_REG_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/public/acrn_common.h b/hypervisor/include/public/acrn_common.h index 4289f37e9..6bed48b91 100644 --- a/hypervisor/include/public/acrn_common.h +++ b/hypervisor/include/public/acrn_common.h @@ -54,6 +54,7 @@ #define GUEST_FLAG_RT (1UL << 4U) /* Whether the vm is RT-VM */ /* TODO: We may need to get this addr from guest ACPI instead of hardcode here */ +#define VIRTUAL_SLEEP_CTL_ADDR 0x400U /* Pre-launched VM uses ACPI reduced HW mode and sleep control register */ #define VIRTUAL_PM1A_CNT_ADDR 0x404U #define VIRTUAL_PM1A_SCI_EN 0x0001 #define VIRTUAL_PM1A_SLP_TYP 0x1c00U