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;