VHE: Add EL0 exception handling
Added code to handle SVC's from EL0 partitions. Also added code to abort
on EL0 partition on unexpected errors.
Change-Id: I262b00592819f63ed075759790d59d414ea47a1c
Signed-off-by: Raghu Krishnamurthy <raghu.ncstate@gmail.com>
diff --git a/src/arch/aarch64/hypervisor/handler.c b/src/arch/aarch64/hypervisor/handler.c
index 880d07f..9bcce55 100644
--- a/src/arch/aarch64/hypervisor/handler.c
+++ b/src/arch/aarch64/hypervisor/handler.c
@@ -1064,13 +1064,27 @@
{
struct vcpu *vcpu = current();
struct vcpu_fault_info info;
- struct vcpu *new_vcpu;
+ struct vcpu *new_vcpu = NULL;
uintreg_t ec = GET_ESR_EC(esr);
+ bool is_el0_partition = vcpu->vm->el0_partition;
switch (ec) {
case EC_WFI_WFE:
/* Skip the instruction. */
vcpu->regs.pc += GET_NEXT_PC_INC(esr);
+
+ /*
+ * For EL0 partitions, treat both WFI and WFE the same way so
+ * that FFA_RUN can be called on the partition to resume it. If
+ * we treat WFI using api_wait_for_interrupt, the VCPU will be
+ * in blocked waiting for interrupt but we cannot inject
+ * interrupts into EL0 partitions.
+ */
+ if (is_el0_partition) {
+ api_yield(vcpu, &new_vcpu);
+ return new_vcpu;
+ }
+
/* Check TI bit of ISS, 0 = WFI, 1 = WFE. */
if (esr & 1) {
/* WFE */
@@ -1090,6 +1104,12 @@
if (vcpu_handle_page_fault(vcpu, &info)) {
return NULL;
}
+
+ if (is_el0_partition) {
+ dlog_warning("Data abort on EL0 partition\n");
+ return api_abort(vcpu);
+ }
+
/* Inform the EL1 of the data abort. */
inject_el1_data_abort_exception(vcpu, esr, far);
@@ -1101,13 +1121,25 @@
if (vcpu_handle_page_fault(vcpu, &info)) {
return NULL;
}
+
+ if (is_el0_partition) {
+ dlog_warning("Instruction abort on EL0 partition\n");
+ return api_abort(vcpu);
+ }
+
/* Inform the EL1 of the instruction abort. */
inject_el1_instruction_abort_exception(vcpu, esr, far);
/* Schedule the same VM to continue running. */
return NULL;
-
+ case EC_SVC:
+ CHECK(is_el0_partition);
+ return hvc_handler(vcpu);
case EC_HVC:
+ if (is_el0_partition) {
+ dlog_warning("Unexpected HVC Trap on EL0 partition\n");
+ return api_abort(vcpu);
+ }
return hvc_handler(vcpu);
case EC_SMC: {
@@ -1135,6 +1167,10 @@
break;
}
+ if (is_el0_partition) {
+ return api_abort(vcpu);
+ }
+
/*
* The exception wasn't handled. Inject to the VM to give it chance to
* handle as an unknown exception.
diff --git a/src/arch/aarch64/sysregs.h b/src/arch/aarch64/sysregs.h
index 5f7a7c4..b0a504e 100644
--- a/src/arch/aarch64/sysregs.h
+++ b/src/arch/aarch64/sysregs.h
@@ -121,6 +121,11 @@
#define EC_WFI_WFE UINT64_C(0x1)
/**
+ * ESR code for SVC instruction execution.
+ */
+#define EC_SVC UINT64_C(0x15)
+
+/**
* ESR code for HVC instruction execution.
*/
#define EC_HVC UINT64_C(0x16)
diff --git a/src/vcpu.c b/src/vcpu.c
index 818af70..68acd00 100644
--- a/src/vcpu.c
+++ b/src/vcpu.c
@@ -164,18 +164,31 @@
* anything else to recover from it. (Acquiring/releasing the lock
* ensured that the invalidations have completed.)
*/
- resume = vm_mem_get_mode(locked_vm, f->ipaddr, ipa_add(f->ipaddr, 1),
- &mode) &&
- (mode & mask) == f->mode;
+ if (!locked_vm.vm->el0_partition) {
+ resume = vm_mem_get_mode(locked_vm, f->ipaddr,
+ ipa_add(f->ipaddr, 1), &mode) &&
+ (mode & mask) == f->mode;
+ } else {
+ /*
+ * For EL0 partitions we need to get the mode for the faulting
+ * vaddr.
+ */
+ resume =
+ vm_mem_get_mode(locked_vm, ipa_init(va_addr(f->vaddr)),
+ ipa_add(ipa_init(va_addr(f->vaddr)), 1),
+ &mode) &&
+ (mode & mask) == f->mode;
+ }
vm_unlock(&locked_vm);
if (!resume) {
dlog_warning(
- "Stage-2 page fault: pc=%#x, vmid=%#x, vcpu=%u, "
- "vaddr=%#x, ipaddr=%#x, mode=%#x\n",
- f->pc, vm->id, vcpu_index(current), f->vaddr, f->ipaddr,
- f->mode);
+ "Stage-%d page fault: pc=%#x, vmid=%#x, vcpu=%u, "
+ "vaddr=%#x, ipaddr=%#x, mode=%#x %#x\n",
+ current->vm->el0_partition ? 1 : 2, f->pc, vm->id,
+ vcpu_index(current), f->vaddr, f->ipaddr, f->mode,
+ mode);
}
return resume;