diff --git a/xen/arch/arm/arm32/entry.S b/xen/arch/arm/arm32/entry.S index 774e7c6766..4b47f9b11e 100644 --- a/xen/arch/arm/arm32/entry.S +++ b/xen/arch/arm/arm32/entry.S @@ -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) \ diff --git a/xen/arch/arm/arm32/traps.c b/xen/arch/arm/arm32/traps.c index f8cf864150..7bfc5ffe9d 100644 --- a/xen/arch/arm/arm32/traps.c +++ b/xen/arch/arm/arm32/traps.c @@ -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); } /* diff --git a/xen/include/asm-arm/arm32/processor.h b/xen/include/asm-arm/arm32/processor.h index 11366bbf13..db3b17bc24 100644 --- a/xen/include/asm-arm/arm32/processor.h +++ b/xen/include/asm-arm/arm32/processor.h @@ -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 */ diff --git a/xen/include/asm-arm/processor.h b/xen/include/asm-arm/processor.h index 15bf89073d..416388425d 100644 --- a/xen/include/asm-arm/processor.h +++ b/xen/include/asm-arm/processor.h @@ -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 */ /*