]> xenbits.xensource.com Git - xen.git/commitdiff
arm32: handle async aborts delivered while at HYP
authorWei Chen <Wei.Chen@arm.com>
Tue, 29 Nov 2016 14:59:55 +0000 (15:59 +0100)
committerJan Beulich <jbeulich@suse.com>
Tue, 29 Nov 2016 14:59:55 +0000 (15:59 +0100)
If guest generates an asynchronous abort and then traps into HYP
(by HVC or IRQ) before the abort has been delivered, the hypervisor
could not catch it, because the PSTATE.A bit is masked all the time
in hypervisor. So this asynchronous abort may be slipped to next
running guest with PSTATE.A bit unmasked.

In order to avoid this, it is necessary to take the abort at HYP, by
clearing the PSTATE.A bit. In this patch, we unmask the PSTATE.A bit
to open a window to catch guest-generated asynchronous abort in all
Guest -> HYP switch paths. If we caught such asynchronous abort in
checking window, the HYP data abort exception will be triggered and
the abort source guest will be crashed.

This is part of XSA-201.

Signed-off-by: Wei Chen <Wei.Chen@arm.com>
Reviewed-by: Julien Grall <julien.grall@arm.com>
Acked-by: Stefano Stabellini <sstabellini@kernel.org>
xen/arch/arm/arm32/entry.S
xen/arch/arm/arm32/traps.c
xen/include/asm-arm/arm32/processor.h
xen/include/asm-arm/processor.h

index 774e7c6766e2d1838f0b06948f8dc8e9b1617c7e..4b47f9b11e93e21b8648629e4eb7a562d9cb4a22 100644 (file)
@@ -42,6 +42,61 @@ save_guest_regs:
         SAVE_BANKED(fiq)
         SAVE_ONE_BANKED(R8_fiq); SAVE_ONE_BANKED(R9_fiq); SAVE_ONE_BANKED(R10_fiq)
         SAVE_ONE_BANKED(R11_fiq); SAVE_ONE_BANKED(R12_fiq);
+        /*
+         * Start to check pending virtual abort in the gap of Guest -> HYP
+         * world switch.
+         *
+         * Save ELR_hyp to check whether the pending virtual abort exception
+         * takes place while we are doing this trap exception.
+         */
+        mrs r1, ELR_hyp
+
+        /*
+         * Force loads and stores to complete before unmasking asynchronous
+         * aborts and forcing the delivery of the exception.
+         */
+        dsb sy
+
+        /*
+         * Unmask asynchronous abort bit. If there is a pending asynchronous
+         * abort, the data_abort exception will happen after A bit is cleared.
+         */
+        cpsie a
+
+        /*
+         * This is our single instruction exception window. A pending
+         * asynchronous abort is guaranteed to occur at the earliest when we
+         * unmask it, and at the latest just after the ISB.
+         *
+         * If a pending abort occurs, the program will jump to data_abort
+         * exception handler, and the ELR_hyp will be set to
+         * abort_guest_exit_start or abort_guest_exit_end.
+         */
+        .global abort_guest_exit_start
+abort_guest_exit_start:
+
+        isb
+
+        .global abort_guest_exit_end
+abort_guest_exit_end:
+        /* Mask CPSR asynchronous abort bit, close the checking window. */
+        cpsid a
+
+        /*
+         * Compare ELR_hyp and the saved value to check whether we are
+         * returning from a valid exception caused by pending virtual
+         * abort.
+         */
+        mrs r2, 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.
+         */
+        bne return_from_trap
+
         mov pc, lr
 
 #define DEFINE_TRAP_ENTRY(trap)                                         \
index f8cf8641505b0c8a1dc259c721ed243ace510a78..7bfc5ffe9d4a52b2cfad5c0a90dafb8afa85b41b 100644 (file)
@@ -63,7 +63,10 @@ asmlinkage void do_trap_prefetch_abort(struct cpu_user_regs *regs)
 
 asmlinkage void do_trap_data_abort(struct cpu_user_regs *regs)
 {
-    do_unexpected_trap("Data Abort", regs);
+    if ( VABORT_GEN_BY_GUEST(regs) )
+        do_trap_guest_error(regs);
+    else
+        do_unexpected_trap("Data Abort", regs);
 }
 
 /*
index 11366bbf13a3786dd0b8ceec2619a80a1701c1bf..db3b17bc241ba4a5f97724a2a1ee299787b4bf42 100644 (file)
@@ -55,6 +55,17 @@ struct cpu_user_regs
 
     uint32_t pad1; /* Doubleword-align the user half of the frame */
 };
+
+/* Functions for pending virtual abort checking window. */
+void abort_guest_exit_start(void);
+void abort_guest_exit_end(void);
+
+#define VABORT_GEN_BY_GUEST(r)  \
+( \
+    ( (unsigned long)abort_guest_exit_start == (r)->pc ) || \
+    ( (unsigned long)abort_guest_exit_end == (r)->pc ) \
+)
+
 #endif
 
 /* Layout as used in assembly, with src/dest registers mixed in */
index 15bf89073dc07bb3b72b917c5c2f6c0bfc443314..416388425d811d59d9d429a17586a5ebd65d9c11 100644 (file)
@@ -690,6 +690,8 @@ void vcpu_regs_user_to_hyp(struct vcpu *vcpu,
 int call_smc(register_t function_id, register_t arg0, register_t arg1,
              register_t arg2);
 
+void do_trap_guest_error(struct cpu_user_regs *regs);
+
 #endif /* __ASSEMBLY__ */
 #endif /* __ASM_ARM_PROCESSOR_H */
 /*