/*
* Actions that needs to be done after entering the hypervisor from the
* guest and before the interrupts are unmasked.
+ *
+ * @return:
+ * r4: Set to a non-zero value if a pending Abort exception took place.
+ * Otherwise, it will be set to zero.
*/
prepare_context_from_guest:
#ifdef CONFIG_ARM32_HARDEN_BRANCH_PREDICTOR
SAVE_ONE_BANKED(R11_fiq); SAVE_ONE_BANKED(R12_fiq);
/*
- * If the SKIP_SYNCHRONIZE_SERROR_ENTRY_EXIT has been set in the cpu
- * feature, the checking of pending SErrors will be skipped.
+ * We may have entered the hypervisor with pending asynchronous Abort
+ * generated by the guest. If we need to categorize them, then
+ * we need to consume any outstanding asynchronous Abort.
+ * Otherwise, they can be consumed later on.
*/
alternative_if SKIP_SYNCHRONIZE_SERROR_ENTRY_EXIT
+ mov r4, #0 /* r4 := No Abort was consumed */
b skip_check
alternative_else_nop_endif
/*
- * Start to check pending virtual abort in the gap of Guest -> HYP
- * world switch.
+ * Consume pending asynchronous Abort generated by the guest if any.
+ *
+ * The only way to consume an Abort interrupt is to unmask it. So
+ * Abort exception will be unmaked for a small window and then masked
+ * it again.
+ *
+ * It is fine to unmask asynchronous Abort exception as we fully
+ * control the state of the processor and only limited code will
+ * be executed if the exception returns (see do_trap_data_abort()).
*
- * Save ELR_hyp to check whether the pending virtual abort exception
+ * TODO: The asynchronous abort path should be reworked to
+ * inject the virtual asynchronous Abort in enter_hypervisor_*
+ * rather than do_trap_data_abort(). This should make easier to
+ * understand the path.
+ */
+
+ /*
+ * save elr_hyp to check whether the pending virtual abort exception
* takes place while we are doing this trap exception.
*/
mrs r1, ELR_hyp
cmp r1, r2
/*
- * Not equal, the pending virtual abort exception took place, the
- * initial exception does not have any significance to be handled.
- * Exit ASAP.
+ * Set r4 depending on whether an asynchronous abort were
+ * consumed.
*/
- bne return_from_trap
+ movne r4, #1
+ moveq r4, #0
skip_check:
b enter_hypervisor_from_guest_preirq
1:
/* Trap from the guest */
+ /*
+ * prepare_context_from_guest will return with r4 set to
+ * a non-zero value if an asynchronous Abort was consumed.
+ *
+ * When an asynchronous Abort has been consumed (r4 != 0), we may have
+ * injected a virtual asynchronous Abort to the guest.
+ *
+ * In this case, the initial exception will be discarded (PC has
+ * been adjusted by inject_vabt_exception()). However, we still
+ * want to give an opportunity to reschedule the vCPU. So we
+ * only want to skip the handling of the initial exception (i.e.
+ * do_trap_*()).
+ */
bl prepare_context_from_guest
.if \guest_iflags != n
cpsie \guest_iflags
.endif
- bl enter_hypervisor_from_guest
+ adr lr, 2f
+ cmp r4, #0
+ adrne lr, return_from_trap
+ b enter_hypervisor_from_guest
2:
/* We are ready to handle the trap, setup the registers and jump. */
.macro guest_vector compat, iflags, trap, save_x0_x1=1
entry hyp=0, compat=\compat, save_x0_x1=\save_x0_x1
/*
- * The vSError will be checked while SKIP_SYNCHRONIZE_SERROR_ENTRY_EXIT
- * is not set. If a vSError took place, the initial exception will be
- * skipped. Exit ASAP
+ * We may have entered the hypervisor with pending SErrors
+ * generated by the guest. If we need to categorize them, then
+ * we need to check any outstanding SErrors will be consumed.
+ *
+ * The function check_pending_guest_serror() will unmask SError
+ * exception temporarily. This is fine to do before enter_*
+ * helpers are called because we fully control the state of the
+ * processor and only limited code willl be executed (see
+ * do_trap_hyp_serror()).
+ *
+ * When a SError has been consumed (x19 != 0), we may have injected a
+ * virtual SError to the guest.
+ *
+ * In this case, the initial exception will be discarded (PC has
+ * been adjusted by inject_vabt_exception()). However, we still
+ * want to give an opportunity to reschedule the vCPU. So we
+ * only want to skip the handling of the initial exception (i.e.
+ * do_trap_*()).
+ *
+ * TODO: The SErrors path should be reworked to inject the vSError in
+ * enter_hypervisor_* rather than do_trap_hyp_serror. This should make
+ * easier to understand the path.
*/
alternative_if_not SKIP_SYNCHRONIZE_SERROR_ENTRY_EXIT
- bl check_pending_vserror
- cbnz x0, 1f
+ bl check_pending_guest_serror
alternative_else_nop_endif
bl enter_hypervisor_from_guest_preirq
msr daifclr, \iflags
bl enter_hypervisor_from_guest
+
+ alternative_if SKIP_SYNCHRONIZE_SERROR_ENTRY_EXIT
+ cbnz x19, 1f
+ alternative_else_nop_endif
+
mov x0, sp
bl do_trap_\trap
1:
eret
/*
- * This function is used to check pending virtual SError in the gap of
- * EL1 -> EL2 world switch.
- * The x0 register will be used to indicate the results of detection.
- * x0 -- Non-zero indicates a pending virtual SError took place.
- * x0 -- Zero indicates no pending virtual SError took place.
+ * Consume pending SError generated by the guest if any.
+ *
+ * @return:
+ * x19: Set to a non-zero value if a pending Abort exception took place.
+ * Otherwise, it will be set to zero.
+ *
+ * Without RAS extension, the only way to consume a SError is to unmask
+ * it. So the function will unmask SError exception for a small window and
+ * then mask it again.
*/
-check_pending_vserror:
+check_pending_guest_serror:
/*
* Save elr_el2 to check whether the pending SError exception takes
* place while we are doing this sync exception.
/*
* Not equal, the pending SError exception took place, set
- * x0 to non-zero.
+ * x19 to non-zero.
*/
- cset x0, ne
+ cset x19, ne
ret
+ENDPROC(check_pending_guest_serror)
/*
* Exception vectors.