diff --git a/xen/arch/arm/arm32/entry.S b/xen/arch/arm/arm32/entry.S
index 34156c4404..b31056a616 100644
--- a/xen/arch/arm/arm32/entry.S
+++ b/xen/arch/arm/arm32/entry.S
@@ -27,6 +27,10 @@
 /*
  * 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.
  */
 arch_enter_hypervisor_from_guest_preirq:
 #ifdef CONFIG_ARM32_HARDEN_BRANCH_PREDICTOR
@@ -56,18 +60,35 @@ arch_enter_hypervisor_from_guest_preirq:
         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
@@ -112,11 +133,11 @@ abort_guest_exit_end:
         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
@@ -179,12 +200,28 @@ ENDPROC(arch_enter_hypervisor_from_guest_preirq)
 
 1:
         /* Trap from the guest */
+        /*
+         * arch_enter_hypervisor_from_guest_preirq 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      arch_enter_hypervisor_from_guest_preirq
         .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. */
diff --git a/xen/arch/arm/arm64/entry.S b/xen/arch/arm/arm64/entry.S
index a8ba7ab961..d35855af96 100644
--- a/xen/arch/arm/arm64/entry.S
+++ b/xen/arch/arm/arm64/entry.S
@@ -184,18 +184,41 @@
         .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:
@@ -436,13 +459,17 @@ return_from_trap:
         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.
@@ -487,11 +514,12 @@ abort_guest_exit_end:
 
         /*
          * 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.
