hv: add default handlers for PIO/MMIO access
Add the default handlers for PIO and MMIO access which returns all FFs on read and discards write. These default handlers are registered when SOS VM or pre-launched VM is created. v3 -> v4: - use single layer if in hv_emulate_pio - change the implementation of pio_default_read v2 -> v3: - use runtime vm type instead of CONFIG_PARTITION_MODE - revise the pio/mmio emulation functions - revise the pio/mmio default read functions according to MISRA C - revise the commit message v1 -> v2: - add default handlers members in struct acrn_vm and add interfaces to register default handlers for PIO and MMIO. Tracked-On: #2860 Signed-off-by: Jian Jun Chen <jian.jun.chen@intel.com> Acked-by: Eddie Dong <eddie.dong@intel.com>
This commit is contained in:
parent
01b28c8e03
commit
cee45a80d9
|
@ -17,6 +17,11 @@
|
|||
#include <trace.h>
|
||||
#include <logmsg.h>
|
||||
|
||||
#define MMIO_DEFAULT_VALUE_SIZE_1 (0xFFUL)
|
||||
#define MMIO_DEFAULT_VALUE_SIZE_2 (0xFFFFUL)
|
||||
#define MMIO_DEFAULT_VALUE_SIZE_4 (0xFFFFFFFFUL)
|
||||
#define MMIO_DEFAULT_VALUE_SIZE_8 (0xFFFFFFFFFFFFFFFFUL)
|
||||
|
||||
/**
|
||||
* @brief General complete-work for port I/O emulation
|
||||
*
|
||||
|
@ -63,18 +68,6 @@ static void emulate_mmio_complete(struct acrn_vcpu *vcpu, const struct io_reques
|
|||
}
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PARTITION_MODE
|
||||
static void io_instr_dest_handler(struct io_request *io_req)
|
||||
{
|
||||
struct pio_request *pio_req = &io_req->reqs.pio;
|
||||
|
||||
if (pio_req->direction == REQUEST_READ) {
|
||||
pio_req->value = 0xFFFFFFFFU;
|
||||
}
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
static void complete_ioreq(struct acrn_vcpu *vcpu, struct io_request *io_req)
|
||||
{
|
||||
union vhm_request_buffer *req_buf = NULL;
|
||||
|
@ -183,7 +176,57 @@ static void dm_emulate_io_complete(struct acrn_vcpu *vcpu)
|
|||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @pre width < 8U
|
||||
*/
|
||||
static bool pio_default_read(__unused struct acrn_vm *vm, struct acrn_vcpu *vcpu,
|
||||
__unused uint16_t addr, size_t width)
|
||||
{
|
||||
struct pio_request *pio_req = &vcpu->req.reqs.pio;
|
||||
|
||||
pio_req->value = (uint32_t)((1UL << (width * 8U)) - 1UL);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool pio_default_write(__unused struct acrn_vm *vm, __unused uint16_t addr,
|
||||
__unused size_t width, __unused uint32_t v)
|
||||
{
|
||||
return true; /* ignore write */
|
||||
}
|
||||
|
||||
/**
|
||||
* @pre (io_req->reqs.mmio.size == 1U) || (io_req->reqs.mmio.size == 2U) ||
|
||||
* (io_req->reqs.mmio.size == 4U) || (io_req->reqs.mmio.size == 8U)
|
||||
*/
|
||||
static int32_t mmio_default_access_handler(struct io_request *io_req,
|
||||
__unused void *handler_private_data)
|
||||
{
|
||||
struct mmio_request *mmio = &io_req->reqs.mmio;
|
||||
|
||||
if (mmio->direction == REQUEST_READ) {
|
||||
switch (mmio->size) {
|
||||
case 1U:
|
||||
mmio->value = MMIO_DEFAULT_VALUE_SIZE_1;
|
||||
break;
|
||||
case 2U:
|
||||
mmio->value = MMIO_DEFAULT_VALUE_SIZE_2;
|
||||
break;
|
||||
case 4U:
|
||||
mmio->value = MMIO_DEFAULT_VALUE_SIZE_4;
|
||||
break;
|
||||
case 8U:
|
||||
mmio->value = MMIO_DEFAULT_VALUE_SIZE_8;
|
||||
break;
|
||||
default:
|
||||
/* This case is unreachable, this is guaranteed by the design. */
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Try handling the given request by any port I/O handler registered in the
|
||||
|
@ -204,6 +247,8 @@ hv_emulate_pio(struct acrn_vcpu *vcpu, struct io_request *io_req)
|
|||
struct acrn_vm *vm = vcpu->vm;
|
||||
struct pio_request *pio_req = &io_req->reqs.pio;
|
||||
struct vm_io_handler_desc *handler;
|
||||
io_read_fn_t io_read = vm->arch_vm.default_io_read;
|
||||
io_write_fn_t io_write = vm->arch_vm.default_io_write;
|
||||
|
||||
port = (uint16_t)pio_req->address;
|
||||
size = (uint16_t)pio_req->size;
|
||||
|
@ -215,34 +260,30 @@ hv_emulate_pio(struct acrn_vcpu *vcpu, struct io_request *io_req)
|
|||
continue;
|
||||
}
|
||||
|
||||
status = 0;
|
||||
|
||||
if (pio_req->direction == REQUEST_WRITE) {
|
||||
if (handler->io_write != NULL) {
|
||||
if (!(handler->io_write(vm, port, size, pio_req->value))) {
|
||||
/*
|
||||
* If io_write return false, it indicates that we need continue
|
||||
* to emulate in DM.
|
||||
*/
|
||||
status = -ENODEV;
|
||||
}
|
||||
}
|
||||
pr_dbg("IO write on port %04x, data %08x", port, pio_req->value);
|
||||
} else {
|
||||
if (handler->io_read != NULL) {
|
||||
if (!(handler->io_read(vm, vcpu, port, size))) {
|
||||
/*
|
||||
* If io_read return false, it indicates that we need continue
|
||||
* to emulate in DM.
|
||||
*/
|
||||
status = -ENODEV;
|
||||
}
|
||||
}
|
||||
pr_dbg("IO read on port %04x, data %08x", port, pio_req->value);
|
||||
if (handler->io_read != NULL) {
|
||||
io_read = handler->io_read;
|
||||
}
|
||||
if (handler->io_write != NULL) {
|
||||
io_write = handler->io_write;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if ((pio_req->direction == REQUEST_WRITE) && (io_write != NULL)) {
|
||||
if (io_write(vm, port, size, pio_req->value)) {
|
||||
status = 0;
|
||||
}
|
||||
} else if ((pio_req->direction == REQUEST_READ) && (io_read != NULL)) {
|
||||
if (io_read(vm, vcpu, port, size)) {
|
||||
status = 0;
|
||||
}
|
||||
} else {
|
||||
/* do nothing */
|
||||
}
|
||||
|
||||
pr_dbg("IO %s on port %04x, data %08x",
|
||||
(pio_req->direction == REQUEST_READ) ? "read" : "write", port, pio_req->value);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
|
@ -264,6 +305,8 @@ hv_emulate_mmio(struct acrn_vcpu *vcpu, struct io_request *io_req)
|
|||
uint64_t address, size;
|
||||
struct mmio_request *mmio_req = &io_req->reqs.mmio;
|
||||
struct mem_io_node *mmio_handler = NULL;
|
||||
hv_mem_io_handler_t read_write = vcpu->vm->default_read_write;
|
||||
void *handler_private_data = NULL;
|
||||
|
||||
address = mmio_req->address;
|
||||
size = mmio_req->size;
|
||||
|
@ -283,9 +326,9 @@ hv_emulate_mmio(struct acrn_vcpu *vcpu, struct io_request *io_req)
|
|||
status = -EIO;
|
||||
emulation_done = true;
|
||||
} else {
|
||||
/* Handle this MMIO operation */
|
||||
if (mmio_handler->read_write != NULL) {
|
||||
status = mmio_handler->read_write(io_req, mmio_handler->handler_private_data);
|
||||
read_write = mmio_handler->read_write;
|
||||
handler_private_data = mmio_handler->handler_private_data;
|
||||
emulation_done = true;
|
||||
}
|
||||
}
|
||||
|
@ -295,6 +338,10 @@ hv_emulate_mmio(struct acrn_vcpu *vcpu, struct io_request *io_req)
|
|||
}
|
||||
}
|
||||
|
||||
if ((status == -ENODEV) && (read_write != NULL)) {
|
||||
status = read_write(io_req, handler_private_data);
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
|
@ -304,6 +351,10 @@ hv_emulate_mmio(struct acrn_vcpu *vcpu, struct io_request *io_req)
|
|||
* Handle an I/O request by either invoking a hypervisor-internal handler or
|
||||
* deliver to VHM.
|
||||
*
|
||||
* @pre vcpu != NULL
|
||||
* @pre vcpu->vm != NULL
|
||||
* @pre vcpu->vm->vm_id < CONFIG_MAX_VM_NUM
|
||||
*
|
||||
* @param vcpu The virtual CPU that triggers the MMIO access
|
||||
* @param io_req The I/O request holding the details of the MMIO access
|
||||
*
|
||||
|
@ -317,6 +368,9 @@ static int32_t
|
|||
emulate_io(struct acrn_vcpu *vcpu, struct io_request *io_req)
|
||||
{
|
||||
int32_t status;
|
||||
struct acrn_vm_config *vm_config;
|
||||
|
||||
vm_config = get_vm_config(vcpu->vm->vm_id);
|
||||
|
||||
switch (io_req->type) {
|
||||
case REQ_PORTIO:
|
||||
|
@ -338,16 +392,7 @@ emulate_io(struct acrn_vcpu *vcpu, struct io_request *io_req)
|
|||
break;
|
||||
}
|
||||
|
||||
if (status == -ENODEV) {
|
||||
#ifdef CONFIG_PARTITION_MODE
|
||||
/*
|
||||
* No handler from HV side, return all FFs on read
|
||||
* and discard writes.
|
||||
*/
|
||||
io_instr_dest_handler(io_req);
|
||||
status = 0;
|
||||
|
||||
#else
|
||||
if ((status == -ENODEV) && (vm_config->type == NORMAL_VM)) {
|
||||
/*
|
||||
* No handler from HV side, search from VHM in Dom0
|
||||
*
|
||||
|
@ -366,7 +411,6 @@ emulate_io(struct acrn_vcpu *vcpu, struct io_request *io_req)
|
|||
pio_req->direction, io_req->type,
|
||||
pio_req->address, pio_req->size);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
return status;
|
||||
|
@ -601,3 +645,24 @@ int32_t register_mmio_emulation_handler(struct acrn_vm *vm,
|
|||
/* Return status to caller */
|
||||
return status;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Register port I/O default handler
|
||||
*
|
||||
* @param vm The VM to which the port I/O handlers are registered
|
||||
*/
|
||||
void register_pio_default_emulation_handler(struct acrn_vm *vm)
|
||||
{
|
||||
vm->arch_vm.default_io_read = pio_default_read;
|
||||
vm->arch_vm.default_io_write = pio_default_write;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Register MMIO default handler
|
||||
*
|
||||
* @param vm The VM to which the MMIO handler is registered
|
||||
*/
|
||||
void register_mmio_default_emulation_handler(struct acrn_vm *vm)
|
||||
{
|
||||
vm->default_read_write = mmio_default_access_handler;
|
||||
}
|
||||
|
|
|
@ -339,6 +339,12 @@ int32_t create_vm(uint16_t vm_id, struct acrn_vm_config *vm_config, struct acrn_
|
|||
vm->arch_vm.nworld_eptp = vm->arch_vm.ept_mem_ops.get_pml4_page(vm->arch_vm.ept_mem_ops.info);
|
||||
sanitize_pte((uint64_t *)vm->arch_vm.nworld_eptp);
|
||||
|
||||
/* Register default handlers for PIO & MMIO if it is SOS VM or Pre-launched VM */
|
||||
if ((vm_config->type == SOS_VM) || (vm_config->type == PRE_LAUNCHED_VM)) {
|
||||
register_pio_default_emulation_handler(vm);
|
||||
register_mmio_default_emulation_handler(vm);
|
||||
}
|
||||
|
||||
if (is_sos_vm(vm)) {
|
||||
/* Only for SOS_VM */
|
||||
create_sos_vm_e820(vm);
|
||||
|
|
|
@ -84,4 +84,17 @@ int32_t register_mmio_emulation_handler(struct acrn_vm *vm,
|
|||
hv_mem_io_handler_t read_write, uint64_t start,
|
||||
uint64_t end, void *handler_private_data);
|
||||
|
||||
/**
|
||||
* @brief Register port I/O default handler
|
||||
*
|
||||
* @param vm The VM to which the port I/O handlers are registered
|
||||
*/
|
||||
void register_pio_default_emulation_handler(struct acrn_vm *vm);
|
||||
|
||||
/**
|
||||
* @brief Register MMIO default handler
|
||||
*
|
||||
* @param vm The VM to which the MMIO handler is registered
|
||||
*/
|
||||
void register_mmio_default_emulation_handler(struct acrn_vm *vm);
|
||||
#endif /* IO_EMUL_H */
|
||||
|
|
|
@ -112,6 +112,8 @@ struct vm_arch {
|
|||
struct acrn_vioapic vioapic; /* Virtual IOAPIC base address */
|
||||
struct acrn_vpic vpic; /* Virtual PIC */
|
||||
struct vm_io_handler_desc emul_pio[EMUL_PIO_IDX_MAX];
|
||||
io_read_fn_t default_io_read;
|
||||
io_write_fn_t default_io_write;
|
||||
|
||||
/* reference to virtual platform to come here (as needed) */
|
||||
} __aligned(PAGE_SIZE);
|
||||
|
@ -132,6 +134,7 @@ struct acrn_vm {
|
|||
|
||||
uint16_t emul_mmio_regions; /* Number of emulated mmio regions */
|
||||
struct mem_io_node emul_mmio[CONFIG_MAX_EMULATED_MMIO_REGIONS];
|
||||
hv_mem_io_handler_t default_read_write;
|
||||
|
||||
uint8_t GUID[16];
|
||||
struct secure_world_control sworld_control;
|
||||
|
|
Loading…
Reference in New Issue