hv: trap vm0 write/read pm1a/pm1b registers

ACRN needs to trap the pm1a/pm1b written/read from VM0. So we
could know when should we put the system to S3.

We will have two path back to VM0:
 - S3 enter/exit sucess. Will reset VM0 and jump to VM0 wakeup vec
   with real mode
 - S3 enter/exit failed. Will return to the next instruction of
   pm1a/pm1b register writing. VM0 will read the pm1a/pm1b evt
   register to check whether it's waked up or not.

Signed-off-by: Victor Sun <victor.sun@intel.com>
Acked-by: Eddie Dong <eddie.dong@intel.com>
This commit is contained in:
Yin Fengwei 2018-06-14 22:25:40 +08:00 committed by lijinxia
parent 1e8006bca1
commit 711cff0251
5 changed files with 109 additions and 1 deletions

View File

@ -121,3 +121,98 @@ int vm_load_pm_s_state(struct vm *vm)
return -1; 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);
}

View File

@ -143,7 +143,8 @@ int create_vm(struct vm_description *vm_desc, struct vm **rtn_vm)
if (is_vm0(vm)) { if (is_vm0(vm)) {
/* Load pm S state data */ /* 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 */ /* Create virtual uart */
vm->vuart = vuart_init(vm); vm->vuart = vuart_init(vm);

View File

@ -6,6 +6,9 @@
struct run_context cpu_ctx; struct run_context cpu_ctx;
/* whether the host enter s3 success */
uint8_t host_enter_s3_success = 1;
void restore_msrs(void) void restore_msrs(void)
{ {
#ifdef STACK_PROTECTOR #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 pm1b_cnt_val)
{ {
uint32_t pcpu_id; uint32_t pcpu_id;
int ret;
uint64_t pmain_entry_saved; uint64_t pmain_entry_saved;
uint32_t guest_wakeup_vec32; uint32_t guest_wakeup_vec32;
uint64_t *pmain_entry; uint64_t *pmain_entry;
/* We assume enter s3 success by default */
host_enter_s3_success = 1;
if (vm->pm.sx_state_data == NULL) { if (vm->pm.sx_state_data == NULL) {
pr_err("No Sx state info avaiable. No Sx support"); pr_err("No Sx state info avaiable. No Sx support");
host_enter_s3_success = 0;
return -1; return -1;
} }

View File

@ -11,5 +11,6 @@ void vm_setup_cpu_state(struct vm *vm);
int vm_load_pm_s_state(struct vm *vm); int vm_load_pm_s_state(struct vm *vm);
int validate_pstate(struct vm *vm, uint64_t perf_ctl); int validate_pstate(struct vm *vm, uint64_t perf_ctl);
struct cpu_cx_data* get_target_cx(struct vm *vm, uint8_t cn); struct cpu_cx_data* get_target_cx(struct vm *vm, uint8_t cn);
void register_pm1ab_handler(struct vm *vm);
#endif /* PM_H */ #endif /* PM_H */

View File

@ -5,8 +5,12 @@
#ifndef HOST_PM_H #ifndef HOST_PM_H
#define BIT_SLP_TYPx 10U
#define BIT_SLP_EN 13U
#define BIT_WAK_STS 15U #define BIT_WAK_STS 15U
extern uint8_t host_enter_s3_success;
int enter_s3(struct vm *vm, uint32_t pm1a_cnt_val, int enter_s3(struct vm *vm, uint32_t pm1a_cnt_val,
uint32_t pm1b_cnt_val); uint32_t pm1b_cnt_val);
extern void __enter_s3(struct vm *vm, uint32_t pm1a_cnt_val, extern void __enter_s3(struct vm *vm, uint32_t pm1a_cnt_val,