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;