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:
Jian Jun Chen 2019-03-26 11:23:16 +08:00 committed by wenlingz
parent 01b28c8e03
commit cee45a80d9
4 changed files with 137 additions and 50 deletions

View File

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

View File

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

View File

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

View File

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