HV: refine EPT violation VM-exit handler

- refine EPT violation vmexit handler
- add check for mmio access that spans devices

Signed-off-by: Yonghua Huang <yonghua.huang@intel.com>
This commit is contained in:
Yonghua Huang 2018-05-15 19:35:12 +08:00 committed by lijinxia
parent a6780652f3
commit 25219e29a5

View File

@ -229,62 +229,17 @@ int is_ept_supported(void)
return status; return status;
} }
static int check_hv_mmio_range(struct vm *vm, struct mem_io *mmio) static int hv_emulate_mmio(struct vcpu *vcpu, struct mem_io *mmio,
struct mem_io_node *mmio_handler)
{ {
int status = false; if ((mmio->paddr % mmio->access_size) != 0) {
struct list_head *pos; pr_err("access size not align with paddr");
struct mem_io_node *mmio_node; return -EINVAL;
list_for_each(pos, &vm->mmio_list) {
mmio_node = list_entry(pos, struct mem_io_node, list);
/* Check if this handler's range covers this memory access */
if ((mmio->paddr >= mmio_node->range_start) &&
(mmio->paddr + mmio->access_size <=
mmio_node->range_end)) {
status = true;
/* Break from loop - only 1 handler allowed to support
* a given memory range
*/
break;
} }
}
/* Return success for now */
return status;
}
static int hv_emulate_mmio(struct vcpu *vcpu, struct mem_io *mmio)
{
int status = -EINVAL;
struct list_head *pos;
struct mem_io_node *mmio_node;
struct vm *vm = vcpu->vm;
list_for_each(pos, &vm->mmio_list) {
mmio_node = list_entry(pos, struct mem_io_node, list);
/* Check if this handler's range covers this memory access */
if ((mmio->paddr >= mmio_node->range_start) &&
(mmio->paddr + mmio->access_size
<= mmio_node->range_end)) {
ASSERT((mmio->paddr % mmio->access_size) == 0,
"access size not align with paddr");
/* Handle this MMIO operation */ /* Handle this MMIO operation */
status = mmio_node->read_write(vcpu, mmio, return mmio_handler->read_write(vcpu, mmio,
mmio_node->handler_private_data); mmio_handler->handler_private_data);
/* Break from loop - only 1 handler allowed to support
* given memory range
*/
break;
}
}
/* Return success for now */
return status;
} }
int register_mmio_emulation_handler(struct vm *vm, int register_mmio_emulation_handler(struct vm *vm,
@ -409,9 +364,12 @@ static int dm_emulate_mmio_pre(struct vcpu *vcpu, uint64_t exit_qual)
int ept_violation_vmexit_handler(struct vcpu *vcpu) int ept_violation_vmexit_handler(struct vcpu *vcpu)
{ {
int status; int status = -EINVAL;
uint64_t exit_qual; uint64_t exit_qual;
uint64_t gpa; uint64_t gpa;
struct list_head *pos;
struct mem_io *mmio = &vcpu->mmio;
struct mem_io_node *mmio_handler = NULL;
/* Handle page fault from guest */ /* Handle page fault from guest */
exit_qual = vcpu->arch_vcpu.exit_qualification; exit_qual = vcpu->arch_vcpu.exit_qualification;
@ -419,22 +377,22 @@ int ept_violation_vmexit_handler(struct vcpu *vcpu)
/* Specify if read or write operation */ /* Specify if read or write operation */
if (exit_qual & 0x2) { if (exit_qual & 0x2) {
/* Write operation */ /* Write operation */
vcpu->mmio.read_write = HV_MEM_IO_WRITE; mmio->read_write = HV_MEM_IO_WRITE;
/* Get write value from appropriate register in context */ /* Get write value from appropriate register in context */
/* TODO: Need to figure out how to determine value being /* TODO: Need to figure out how to determine value being
* written * written
*/ */
vcpu->mmio.value = 0; mmio->value = 0;
} else { } else {
/* Read operation */ /* Read operation */
vcpu->mmio.read_write = HV_MEM_IO_READ; mmio->read_write = HV_MEM_IO_READ;
/* Get sign extension requirements for read */ /* Get sign extension requirements for read */
/* TODO: Need to determine how sign extension is determined for /* TODO: Need to determine how sign extension is determined for
* reads * reads
*/ */
vcpu->mmio.sign_extend_read = 0; mmio->sign_extend_read = 0;
} }
/* Get the guest physical address */ /* Get the guest physical address */
@ -444,21 +402,28 @@ int ept_violation_vmexit_handler(struct vcpu *vcpu)
/* Adjust IPA appropriately and OR page offset to get full IPA of abort /* Adjust IPA appropriately and OR page offset to get full IPA of abort
*/ */
vcpu->mmio.paddr = gpa; mmio->paddr = gpa;
/* Check if the MMIO access has a HV registered handler */ list_for_each(pos, &vcpu->vm->mmio_list) {
status = check_hv_mmio_range((struct vm *) vcpu->vm, &vcpu->mmio); mmio_handler = list_entry(pos, struct mem_io_node, list);
if ((mmio->paddr + mmio->access_size <=
mmio_handler->range_start) ||
(mmio->paddr >= mmio_handler->range_end))
continue;
if (status == true) { else if (!((mmio->paddr >= mmio_handler->range_start) &&
/* Fetch and decode current vcpu instruction */ (mmio->paddr + mmio->access_size <=
status = analyze_instruction(vcpu, &vcpu->mmio); mmio_handler->range_end))) {
pr_fatal("Err MMIO, addr:0x%llx, size:%x",
mmio->paddr, mmio->access_size);
return -EIO;
}
if (status != 0) if (analyze_instruction(vcpu, mmio) != 0)
goto out; goto out;
if (vcpu->mmio.read_write == HV_MEM_IO_WRITE) { if (mmio->read_write == HV_MEM_IO_WRITE) {
status = emulate_instruction(vcpu, &vcpu->mmio); if (emulate_instruction(vcpu, mmio) != 0)
if (status != 0)
goto out; goto out;
} }
@ -467,15 +432,18 @@ int ept_violation_vmexit_handler(struct vcpu *vcpu)
* instruction emulation. For MMIO read, * instruction emulation. For MMIO read,
* call hv_emulate_mmio at first. * call hv_emulate_mmio at first.
*/ */
status = hv_emulate_mmio(vcpu, &vcpu->mmio); hv_emulate_mmio(vcpu, mmio, mmio_handler);
if (mmio->read_write == HV_MEM_IO_READ) {
if (vcpu->mmio.read_write == HV_MEM_IO_READ) {
/* Emulate instruction and update vcpu register set */ /* Emulate instruction and update vcpu register set */
status = emulate_instruction(vcpu, &vcpu->mmio); if (emulate_instruction(vcpu, mmio) != 0)
if (status != 0)
goto out; goto out;
} }
} else {
status = 0;
break;
}
if (status != 0) {
/* /*
* No mmio handler from HV side, search from VHM in Dom0 * No mmio handler from HV side, search from VHM in Dom0
* *
@ -486,9 +454,9 @@ int ept_violation_vmexit_handler(struct vcpu *vcpu)
*/ */
memset(&vcpu->req, 0, sizeof(struct vhm_request)); memset(&vcpu->req, 0, sizeof(struct vhm_request));
status = dm_emulate_mmio_pre(vcpu, exit_qual); if (dm_emulate_mmio_pre(vcpu, exit_qual) != 0)
if (status != 0)
goto out; goto out;
status = acrn_insert_request_wait(vcpu, &vcpu->req); status = acrn_insert_request_wait(vcpu, &vcpu->req);
} }
@ -501,8 +469,6 @@ out:
pr_fatal("Guest Physical Address address: 0x%016llx", pr_fatal("Guest Physical Address address: 0x%016llx",
gpa); gpa);
ASSERT(status == true, "EPT violation");
return status; return status;
} }