diff --git a/hypervisor/arch/x86/guest/pm.c b/hypervisor/arch/x86/guest/pm.c index c6f1ca68f..d7ab4b899 100644 --- a/hypervisor/arch/x86/guest/pm.c +++ b/hypervisor/arch/x86/guest/pm.c @@ -121,3 +121,98 @@ int vm_load_pm_s_state(struct vm *vm) return -1; } } + +static inline uint16_t s3_enabled(uint16_t pm1_cnt) +{ + return pm1_cnt & (1 << BIT_SLP_EN); +} + +static inline uint8_t get_slp_typx(uint16_t pm1_cnt) +{ + return (pm1_cnt & 0x1fff) >> BIT_SLP_TYPx; +} + +static uint32_t pm1ab_io_read(__unused struct vm_io_handler *hdlr, + __unused struct vm *vm, uint16_t addr, size_t width) +{ + uint32_t val = io_read(addr, width); + + if (host_enter_s3_success == 0) { + /* If host S3 enter failes, we should set BIT_WAK_STS + * bit for vm0 and let vm0 back from S3 failure path. + */ + if (addr == vm->pm.sx_state_data->pm1a_evt.address) { + val |= (1 << BIT_WAK_STS); + } + } + return val; +} + +static void pm1ab_io_write(__unused struct vm_io_handler *hdlr, + __unused struct vm *vm, uint16_t addr, size_t width, + uint32_t v) +{ + static uint32_t pm1a_cnt_ready = 0; + + if (width == 2) { + uint8_t val = get_slp_typx(v); + + if ((addr == vm->pm.sx_state_data->pm1a_cnt.address) + && (val == vm->pm.sx_state_data->s3_pkg.val_pm1a) + && s3_enabled(v)) { + + if (vm->pm.sx_state_data->pm1b_cnt.address) { + pm1a_cnt_ready = v; + } else { + enter_s3(vm, v, 0); + } + return; + } + + if ((addr == vm->pm.sx_state_data->pm1b_cnt.address) + && (val == vm->pm.sx_state_data->s3_pkg.val_pm1b) + && s3_enabled(v)) { + + if (pm1a_cnt_ready) { + enter_s3(vm, pm1a_cnt_ready, v); + pm1a_cnt_ready = 0; + } else { + /* the case broke ACPI spec */ + pr_err("PM1B_CNT write error!"); + } + return; + } + } + + io_write(v, addr, width); +} + +void register_gas_io_handler(struct vm *vm, struct acpi_generic_address *gas) +{ + uint8_t io_len[5] = {0, 1, 2, 4, 8}; + struct vm_io_range gas_io; + + if ((gas->address == 0) + || (gas->space_id != SPACE_SYSTEM_IO) + || (gas->access_size == 0) + || (gas->access_size > 4)) + return; + + gas_io.flags = IO_ATTR_RW, + gas_io.base = gas->address, + gas_io.len = io_len[gas->access_size]; + + register_io_emulation_handler(vm, &gas_io, + &pm1ab_io_read, &pm1ab_io_write); + + pr_dbg("Enable PM1A trap for VM %d, port 0x%x, size %d\n", + vm->attr.id, gas_io.base, gas_io.len); +} + +void register_pm1ab_handler(struct vm *vm) +{ + register_gas_io_handler(vm, &vm->pm.sx_state_data->pm1a_evt); + register_gas_io_handler(vm, &vm->pm.sx_state_data->pm1b_evt); + register_gas_io_handler(vm, &vm->pm.sx_state_data->pm1a_cnt); + register_gas_io_handler(vm, &vm->pm.sx_state_data->pm1b_cnt); +} diff --git a/hypervisor/arch/x86/guest/vm.c b/hypervisor/arch/x86/guest/vm.c index 4e25ac827..31a367680 100644 --- a/hypervisor/arch/x86/guest/vm.c +++ b/hypervisor/arch/x86/guest/vm.c @@ -143,7 +143,8 @@ int create_vm(struct vm_description *vm_desc, struct vm **rtn_vm) if (is_vm0(vm)) { /* Load pm S state data */ - vm_load_pm_s_state(vm); + if (vm_load_pm_s_state(vm) == 0) + register_pm1ab_handler(vm); /* Create virtual uart */ vm->vuart = vuart_init(vm); diff --git a/hypervisor/arch/x86/pm.c b/hypervisor/arch/x86/pm.c index a01b83ee0..9674ae9cc 100644 --- a/hypervisor/arch/x86/pm.c +++ b/hypervisor/arch/x86/pm.c @@ -6,6 +6,9 @@ struct run_context cpu_ctx; +/* whether the host enter s3 success */ +uint8_t host_enter_s3_success = 1; + void restore_msrs(void) { #ifdef STACK_PROTECTOR @@ -71,12 +74,16 @@ int enter_s3(struct vm *vm, uint32_t pm1a_cnt_val, uint32_t pm1b_cnt_val) { uint32_t pcpu_id; + int ret; uint64_t pmain_entry_saved; uint32_t guest_wakeup_vec32; uint64_t *pmain_entry; + /* We assume enter s3 success by default */ + host_enter_s3_success = 1; if (vm->pm.sx_state_data == NULL) { pr_err("No Sx state info avaiable. No Sx support"); + host_enter_s3_success = 0; return -1; } diff --git a/hypervisor/include/arch/x86/guest/guest_pm.h b/hypervisor/include/arch/x86/guest/guest_pm.h index dc5e44068..ea149261a 100644 --- a/hypervisor/include/arch/x86/guest/guest_pm.h +++ b/hypervisor/include/arch/x86/guest/guest_pm.h @@ -11,5 +11,6 @@ void vm_setup_cpu_state(struct vm *vm); int vm_load_pm_s_state(struct vm *vm); int validate_pstate(struct vm *vm, uint64_t perf_ctl); struct cpu_cx_data* get_target_cx(struct vm *vm, uint8_t cn); +void register_pm1ab_handler(struct vm *vm); #endif /* PM_H */ diff --git a/hypervisor/include/arch/x86/host_pm.h b/hypervisor/include/arch/x86/host_pm.h index f5fc7eae8..6dd75c8ba 100644 --- a/hypervisor/include/arch/x86/host_pm.h +++ b/hypervisor/include/arch/x86/host_pm.h @@ -5,8 +5,12 @@ #ifndef HOST_PM_H +#define BIT_SLP_TYPx 10U +#define BIT_SLP_EN 13U #define BIT_WAK_STS 15U +extern uint8_t host_enter_s3_success; + int enter_s3(struct vm *vm, uint32_t pm1a_cnt_val, uint32_t pm1b_cnt_val); extern void __enter_s3(struct vm *vm, uint32_t pm1a_cnt_val,