hv: add support for shutdown for pre-launched VMs

Currently, ACRN only support shutdown when triple fault happens, because ACRN
doesn't present/emulate a virtual HW, i.e. port IO, to support shutdown. This
patch emulate a virtual shutdown component, and the vACPI method for guest OS
to use.

Pre-launched VM uses ACPI reduced HW mode, intercept the virtual sleep control/status
registers for pre-launched VMs shutdown

Tracked-On: #5411
Signed-off-by: dongshen <dongsheng.x.zhang@intel.com>
This commit is contained in:
dongshen 2020-10-22 16:06:16 -07:00 committed by wenlingz
parent 8f79ceefbd
commit ca5683f78d
3 changed files with 88 additions and 6 deletions

View File

@ -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);
}
}

View File

@ -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
*

View File

@ -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