Report FAR_EL2's value on injecting EL1 exception

Propagate the value of FAR_EL2 to FAR_EL1 when injecting an
exception, only when the value is defined and known.

Fixes a bug where EL1 resumes running although it cannot
determine the faulting address from FAR_EL1.

Signed-off-by: Fuad Tabba <tabba@google.com>
Change-Id: If6c72b74a89251b2f18d14b96af63dc3adc1b739
diff --git a/src/arch/aarch64/hypervisor/exceptions.S b/src/arch/aarch64/hypervisor/exceptions.S
index beecc64..9e279c2 100644
--- a/src/arch/aarch64/hypervisor/exceptions.S
+++ b/src/arch/aarch64/hypervisor/exceptions.S
@@ -79,8 +79,9 @@
 	sub x18, x18, #0x18
 	cbz x18, system_register_access
 
-	/* Read syndrome register and call C handler. */
+	/* Call C handler passing the syndrome and fault address registers. */
 	mrs x0, esr_el2
+	mrs x1, far_el2
 	bl sync_lower_exception
 
 	/* Switch vCPU if requested by handler. */
diff --git a/src/arch/aarch64/hypervisor/handler.c b/src/arch/aarch64/hypervisor/handler.c
index 6fad7fc..e1f0329 100644
--- a/src/arch/aarch64/hypervisor/handler.c
+++ b/src/arch/aarch64/hypervisor/handler.c
@@ -521,17 +521,18 @@
 /**
  * Injects an exception with the specified Exception Syndrom Register value into
  * the EL1.
- * See Arm Architecture Reference Manual Armv8-A, page D13-2924.
  *
  * NOTE: This function assumes that the lazy registers haven't been saved, and
  * writes to the lazy registers of the CPU directly instead of the vCPU.
  */
-static void inject_el1_exception(struct vcpu *vcpu, uintreg_t esr_el1_value)
+static void inject_el1_exception(struct vcpu *vcpu, uintreg_t esr_el1_value,
+				 uintreg_t far_el1_value)
 {
 	uintreg_t handler_address = get_el1_exception_handler_addr(vcpu);
 
 	/* Update the CPU state to inject the exception. */
 	write_msr(esr_el1, esr_el1_value);
+	write_msr(far_el1, far_el1_value);
 	write_msr(elr_el1, vcpu->regs.pc);
 	write_msr(spsr_el1, vcpu->regs.spsr);
 
@@ -540,7 +541,6 @@
 	 * EL1h mode is used because by default, taking an exception selects the
 	 * stack pointer for the target Exception level. The software can change
 	 * that later in the handler if needed.
-	 * See Arm Architecture Reference Manual Armv8-A, page D13-2924
 	 */
 	vcpu->regs.spsr = PSR_D | PSR_A | PSR_I | PSR_F | PSR_PE_MODE_EL1H;
 
@@ -552,7 +552,8 @@
  * Injects a Data Abort exception (same exception level).
  */
 static void inject_el1_data_abort_exception(struct vcpu *vcpu,
-					    uintreg_t esr_el2)
+					    uintreg_t esr_el2,
+					    uintreg_t far_el2)
 {
 	/*
 	 *  ISS encoding remains the same, but the EC is changed to reflect
@@ -565,14 +566,15 @@
 	dlog_notice("Injecting Data Abort exception into VM%d.\n",
 		    vcpu->vm->id);
 
-	inject_el1_exception(vcpu, esr_el1_value);
+	inject_el1_exception(vcpu, esr_el1_value, far_el2);
 }
 
 /**
  * Injects a Data Abort exception (same exception level).
  */
 static void inject_el1_instruction_abort_exception(struct vcpu *vcpu,
-						   uintreg_t esr_el2)
+						   uintreg_t esr_el2,
+						   uintreg_t far_el2)
 {
 	/*
 	 *  ISS encoding remains the same, but the EC is changed to reflect
@@ -586,7 +588,7 @@
 	dlog_notice("Injecting Instruction Abort exception into VM%d.\n",
 		    vcpu->vm->id);
 
-	inject_el1_exception(vcpu, esr_el1_value);
+	inject_el1_exception(vcpu, esr_el1_value, far_el2);
 }
 
 /**
@@ -596,6 +598,12 @@
 {
 	uintreg_t esr_el1_value =
 		GET_ESR_IL(esr_el2) | (EC_UNKNOWN << ESR_EC_OFFSET);
+
+	/*
+	 * The value of the far_el2 register is UNKNOWN in this case,
+	 * therefore, don't propagate it to avoid leaking sensitive information.
+	 */
+	uintreg_t far_el1_value = 0;
 	char *direction_str;
 
 	direction_str = ISS_IS_READ(esr_el2) ? "read" : "write";
@@ -609,7 +617,7 @@
 	dlog_notice("Injecting Unknown Reason exception into VM%d.\n",
 		    vcpu->vm->id);
 
-	inject_el1_exception(vcpu, esr_el1_value);
+	inject_el1_exception(vcpu, esr_el1_value, far_el1_value);
 }
 
 struct vcpu *hvc_handler(struct vcpu *vcpu)
@@ -764,7 +772,7 @@
 	return r;
 }
 
-struct vcpu *sync_lower_exception(uintreg_t esr)
+struct vcpu *sync_lower_exception(uintreg_t esr, uintreg_t far)
 {
 	struct vcpu *vcpu = current();
 	struct vcpu_fault_info info;
@@ -795,7 +803,7 @@
 			return NULL;
 		}
 		/* Inform the EL1 of the data abort. */
-		inject_el1_data_abort_exception(vcpu, esr);
+		inject_el1_data_abort_exception(vcpu, esr, far);
 
 		/* Schedule the same VM to continue running. */
 		return NULL;
@@ -806,7 +814,7 @@
 			return NULL;
 		}
 		/* Inform the EL1 of the instruction abort. */
-		inject_el1_instruction_abort_exception(vcpu, esr);
+		inject_el1_instruction_abort_exception(vcpu, esr, far);
 
 		/* Schedule the same VM to continue running. */
 		return NULL;
diff --git a/test/vmapi/common/exception_handler.c b/test/vmapi/common/exception_handler.c
index e820a3a..c3c3334 100644
--- a/test/vmapi/common/exception_handler.c
+++ b/test/vmapi/common/exception_handler.c
@@ -93,7 +93,16 @@
 bool exception_handler_yield_unknown(void)
 {
 	uintreg_t esr_el1 = read_msr(ESR_EL1);
+	uintreg_t far_el1 = read_msr(FAR_EL1);
+
 	EXPECT_EQ(GET_ESR_EC(esr_el1), EC_UNKNOWN);
+
+	/*
+	 * For unknown exceptions, the value of far_el1 is UNKNOWN.
+	 * Hafnium sets it to 0.
+	 */
+	EXPECT_EQ(far_el1, 0);
+
 	return exception_handler_yield();
 }
 
@@ -105,7 +114,11 @@
 bool exception_handler_yield_data_abort(void)
 {
 	uintreg_t esr_el1 = read_msr(ESR_EL1);
+	uintreg_t far_el1 = read_msr(FAR_EL1);
+
 	EXPECT_EQ(GET_ESR_EC(esr_el1), EC_DATA_ABORT_SAME_EL);
+	EXPECT_NE(far_el1, 0);
+
 	return exception_handler_yield();
 }
 
@@ -117,7 +130,11 @@
 bool exception_handler_yield_instruction_abort(void)
 {
 	uintreg_t esr_el1 = read_msr(ESR_EL1);
+	uintreg_t far_el1 = read_msr(FAR_EL1);
+
 	EXPECT_EQ(GET_ESR_EC(esr_el1), EC_INSTRUCTION_ABORT_SAME_EL);
+	EXPECT_NE(far_el1, 0);
+
 	return exception_handler_yield();
 }