hv: emulate ACPI reset register for Service OS guest
Handle the PIO reset register that is defined in host ACPI: Parse host FADT table to get the host reset register info, and emulate it for Service OS: - return all '1' for guest reads because the read behavior is not defined in ACPI. - ignore guest writes with the reset value to stop it from resetting host; if guest writes other values, passthru it to hardware in case the reset register supports other functionalities. Tracked-On: #2700 Signed-off-by: Sainath Grandhi <sainath.grandhi@intel.com> Signed-off-by: Zide Chen <zide.chen@intel.com> Acked-by: Eddie Dong <eddie.dong@intel.com>
This commit is contained in:
parent
26f08680eb
commit
865ee2956e
|
@ -35,6 +35,7 @@
|
|||
#include <logmsg.h>
|
||||
#include <host_pm.h>
|
||||
#include <acrn_common.h>
|
||||
#include <vm_reset.h>
|
||||
|
||||
/* Per ACPI spec:
|
||||
* There are two fundamental types of ACPI tables:
|
||||
|
@ -56,6 +57,8 @@
|
|||
|
||||
/* FACP field offsets */
|
||||
#define OFFSET_FACS_ADDR 36U
|
||||
#define OFFSET_RESET_REGISTER 116U
|
||||
#define OFFSET_RESET_VALUE 128U
|
||||
#define OFFSET_FACS_X_ADDR 132U
|
||||
#define OFFSET_PM1A_EVT 148U
|
||||
#define OFFSET_PM1A_CNT 172U
|
||||
|
@ -154,5 +157,14 @@ void acpi_fixup(void)
|
|||
sx_data->wake_vector_32 = (uint32_t *)(facs_addr + OFFSET_WAKE_VECTOR_32);
|
||||
sx_data->wake_vector_64 = (uint64_t *)(facs_addr + OFFSET_WAKE_VECTOR_64);
|
||||
}
|
||||
|
||||
const struct acpi_table_header *table = (const struct acpi_table_header *)facp_addr;
|
||||
|
||||
if (table->revision >= 2U) {
|
||||
struct acpi_reset_reg *rr_data = get_host_reset_reg_data();
|
||||
|
||||
get_acpi_dt_gas(facp_addr, OFFSET_RESET_REGISTER, &(rr_data->reg));
|
||||
rr_data->val = *(facp_addr + OFFSET_RESET_VALUE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,6 +20,11 @@
|
|||
#define WAKE_VECTOR_32 0x7A86BBDCUL
|
||||
#define WAKE_VECTOR_64 0x7A86BBE8UL
|
||||
|
||||
/* reset register */
|
||||
#define RESET_REGISTER_ADDRESS 0xCF9U
|
||||
#define RESET_REGISTER_VALUE 0x0EU
|
||||
#define RESET_REGISTER_SPACE_ID SPACE_SYSTEM_IO
|
||||
|
||||
/* DRHD of DMAR */
|
||||
#define DRHD_COUNT 2U
|
||||
|
||||
|
|
|
@ -20,6 +20,10 @@
|
|||
#define WAKE_VECTOR_32 0UL
|
||||
#define WAKE_VECTOR_64 0UL
|
||||
|
||||
#define RESET_REGISTER_ADDRESS 0UL
|
||||
#define RESET_REGISTER_VALUE 0UL
|
||||
#define RESET_REGISTER_SPACE_ID 0UL
|
||||
|
||||
/* DRHD of DMAR */
|
||||
#define DRHD_COUNT 0U
|
||||
|
||||
|
|
|
@ -9,6 +9,25 @@
|
|||
#include <logmsg.h>
|
||||
#include <per_cpu.h>
|
||||
#include <vm_reset.h>
|
||||
#include <default_acpi_info.h>
|
||||
#include <platform_acpi_info.h>
|
||||
|
||||
/* host reset register defined in ACPI */
|
||||
static struct acpi_reset_reg host_reset_reg = {
|
||||
.reg = {
|
||||
.space_id = RESET_REGISTER_SPACE_ID,
|
||||
.bit_width = RESET_REGISTER_BIT_WIDTH,
|
||||
.bit_offset = RESET_REGISTER_BIT_OFFSET,
|
||||
.access_size = RESET_REGISTER_ACCESS_SIZE,
|
||||
.address = RESET_REGISTER_ADDRESS,
|
||||
},
|
||||
.val = RESET_REGISTER_VALUE
|
||||
};
|
||||
|
||||
struct acpi_reset_reg *get_host_reset_reg_data(void)
|
||||
{
|
||||
return &host_reset_reg;
|
||||
}
|
||||
|
||||
/**
|
||||
* @pre vm != NULL
|
||||
|
@ -74,6 +93,7 @@ static bool handle_reset_reg_read(struct acrn_vm *vm, struct acrn_vcpu *vcpu, __
|
|||
/*
|
||||
* - keyboard control/status register 0x64: ACRN doesn't expose kbd controller to the guest.
|
||||
* - reset control register 0xcf9: hide this from guests for now.
|
||||
* - FADT reset register: the read behavior is not defined in spec, keep it simple to return all '1'.
|
||||
*/
|
||||
vcpu->req.reqs.pio.value = ~0U;
|
||||
}
|
||||
|
@ -131,6 +151,24 @@ static bool handle_cf9_write(struct acrn_vm *vm, __unused uint16_t addr, size_t
|
|||
return handle_common_reset_reg_write(vm, ((bytes == 1U) && ((val & 0x4U) == 0x4U) && ((val & 0xaU) != 0U)));
|
||||
}
|
||||
|
||||
static bool handle_reset_reg_write(__unused struct acrn_vm *vm, uint16_t addr, size_t bytes, uint32_t val)
|
||||
{
|
||||
if (bytes == 1U) {
|
||||
if (val == host_reset_reg.val) {
|
||||
/* ignore reset request */
|
||||
} else {
|
||||
/*
|
||||
* ACPI defines the reset value but doesn't specify the meaning of other values.
|
||||
* in the case the reset register (e.g. PIO 0xB2) has other purpose other than reset,
|
||||
* we can't ignore the write with other values.
|
||||
*/
|
||||
pio_write8((uint8_t)val, addr);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @pre vm != NULL
|
||||
*/
|
||||
|
@ -138,6 +176,8 @@ 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 acpi_generic_address *gas = &(host_reset_reg.reg);
|
||||
|
||||
struct vm_io_range io_range = {
|
||||
.flags = IO_ATTR_RW,
|
||||
.len = 1U
|
||||
|
@ -148,6 +188,23 @@ void register_reset_port_handler(struct acrn_vm *vm)
|
|||
|
||||
io_range.base = 0xcf9U;
|
||||
register_pio_emulation_handler(vm, CF9_PIO_IDX, &io_range, handle_reset_reg_read, handle_cf9_write);
|
||||
|
||||
/*
|
||||
* - pre-launched VMs don't support ACPI;
|
||||
* - ACPI reset register is fixed at 0xcf9 for post-launched VMs;
|
||||
* - here is taking care of SOS only:
|
||||
* Don't support MMIO or PCI based reset register for now.
|
||||
* ACPI Spec: Register_Bit_Width must be 8 and Register_Bit_Offset must be 0.
|
||||
*/
|
||||
if (is_sos_vm(vm) &&
|
||||
(gas->space_id == SPACE_SYSTEM_IO) &&
|
||||
(gas->bit_width == 8U) && (gas->bit_offset == 0U) &&
|
||||
(gas->address != 0xcf9U) && (gas->address != 0x64U)) {
|
||||
|
||||
io_range.base = (uint16_t)host_reset_reg.reg.address;
|
||||
register_pio_emulation_handler(vm, PIO_RESET_REG_IDX, &io_range,
|
||||
handle_reset_reg_read, handle_reset_reg_write);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -48,4 +48,9 @@
|
|||
#define S5_PKG_VAL_PM1B 0U
|
||||
#define S5_PKG_RESERVED 0U
|
||||
|
||||
/* reset register */
|
||||
#define RESET_REGISTER_BIT_WIDTH 0x08U
|
||||
#define RESET_REGISTER_BIT_OFFSET 0U
|
||||
#define RESET_REGISTER_ACCESS_SIZE 0x01U
|
||||
|
||||
#endif /* DEFAULT_ACPI_INFO_H */
|
||||
|
|
|
@ -7,8 +7,16 @@
|
|||
#ifndef VM_RESET_H_
|
||||
#define VM_RESET_H_
|
||||
|
||||
#include <acrn_common.h>
|
||||
|
||||
struct acpi_reset_reg {
|
||||
struct acpi_generic_address reg;
|
||||
uint8_t val;
|
||||
};
|
||||
|
||||
void register_reset_port_handler(struct acrn_vm *vm);
|
||||
void shutdown_vm_from_idle(uint16_t pcpu_id);
|
||||
void triple_fault_shutdown_vm(struct acrn_vm *vm);
|
||||
struct acpi_reset_reg *get_host_reset_reg_data(void);
|
||||
|
||||
#endif /* VM_RESET_H_ */
|
||||
|
|
|
@ -26,7 +26,8 @@
|
|||
#define VIRTUAL_PM1A_CNT_PIO_IDX (RTC_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)
|
||||
#define PIO_RESET_REG_IDX (CF9_PIO_IDX + 1U)
|
||||
#define EMUL_PIO_IDX_MAX (PIO_RESET_REG_IDX + 1U)
|
||||
|
||||
/**
|
||||
* @brief The handler of VM exits on I/O instructions
|
||||
|
|
Loading…
Reference in New Issue