diff --git a/xen/arch/arm/arm64/entry.S b/xen/arch/arm/arm64/entry.S index 0ed0073267..1840659c83 100644 --- a/xen/arch/arm/arm64/entry.S +++ b/xen/arch/arm/arm64/entry.S @@ -173,6 +173,43 @@ hyp_error_invalid: entry hyp=1 invalid BAD_ERROR +hyp_error: + /* + * Only two possibilities: + * 1) Either we come from the exit path, having just unmasked + * PSTATE.A: change the return code to an EL2 fault, and + * carry on, as we're already in a sane state to handle it. + * 2) Or we come from anywhere else, and that's a bug: we panic. + */ + entry hyp=1 + msr daifclr, #2 + + /* + * The ELR_EL2 may be modified by an interrupt, so we have to use the + * saved value in cpu_user_regs to check whether we come from 1) or + * not. + */ + ldr x0, [sp, #UREGS_PC] + adr x1, abort_guest_exit_start + cmp x0, x1 + adr x1, abort_guest_exit_end + ccmp x0, x1, #4, ne + mov x0, sp + mov x1, #BAD_ERROR + + /* + * Not equal, the exception come from 2). It's a bug, we have to + * panic the hypervisor. + */ + b.ne do_bad_mode + + /* + * Otherwise, the exception come from 1). It happened because of + * the guest. Crash this guest. + */ + bl do_trap_guest_error + exit hyp=1 + /* Traps taken in Current EL with SP_ELx */ hyp_sync: entry hyp=1 @@ -189,15 +226,29 @@ hyp_irq: guest_sync: entry hyp=0, compat=0 + bl check_pending_vserror + /* + * If x0 is Non-zero, a vSError took place, the initial exception + * doesn't have any significance to be handled. Exit ASAP + */ + cbnz x0, 1f msr daifclr, #2 mov x0, sp bl do_trap_hypervisor +1: exit hyp=0, compat=0 guest_irq: entry hyp=0, compat=0 + bl check_pending_vserror + /* + * If x0 is Non-zero, a vSError took place, the initial exception + * doesn't have any significance to be handled. Exit ASAP + */ + cbnz x0, 1f mov x0, sp bl do_trap_irq +1: exit hyp=0, compat=0 guest_fiq_invalid: @@ -213,15 +264,29 @@ guest_error: guest_sync_compat: entry hyp=0, compat=1 + bl check_pending_vserror + /* + * If x0 is Non-zero, a vSError took place, the initial exception + * doesn't have any significance to be handled. Exit ASAP + */ + cbnz x0, 1f msr daifclr, #2 mov x0, sp bl do_trap_hypervisor +1: exit hyp=0, compat=1 guest_irq_compat: entry hyp=0, compat=1 + bl check_pending_vserror + /* + * If x0 is Non-zero, a vSError took place, the initial exception + * doesn't have any significance to be handled. Exit ASAP + */ + cbnz x0, 1f mov x0, sp bl do_trap_irq +1: exit hyp=0, compat=1 guest_fiq_invalid_compat: @@ -269,6 +334,62 @@ 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. + */ +check_pending_vserror: + /* + * Save elr_el2 to check whether the pending SError exception takes + * place while we are doing this sync exception. + */ + mrs x0, elr_el2 + + /* Synchronize against in-flight ld/st */ + dsb sy + + /* + * Unmask PSTATE asynchronous abort bit. If there is a pending + * SError, the EL2 error exception will happen after PSTATE.A + * is cleared. + */ + msr daifclr, #4 + + /* + * This is our single instruction exception window. A pending + * SError is guaranteed to occur at the earliest when we unmask + * it, and at the latest just after the ISB. + * + * If a pending SError occurs, the program will jump to EL2 error + * exception handler, and the elr_el2 will be set to + * abort_guest_exit_start or abort_guest_exit_end. + */ +abort_guest_exit_start: + + isb + +abort_guest_exit_end: + /* Mask PSTATE asynchronous abort bit, close the checking window. */ + msr daifset, #4 + + /* + * Compare elr_el2 and the saved value to check whether we are + * returning from a valid exception caused by pending SError. + */ + mrs x1, elr_el2 + cmp x0, x1 + + /* + * Not equal, the pending SError exception took place, set + * x0 to non-zero. + */ + cset x0, ne + + ret + /* * Exception vectors. */ @@ -287,7 +408,7 @@ ENTRY(hyp_traps_vector) ventry hyp_sync // Synchronous EL2h ventry hyp_irq // IRQ EL2h ventry hyp_fiq_invalid // FIQ EL2h - ventry hyp_error_invalid // Error EL2h + ventry hyp_error // Error EL2h ventry guest_sync // Synchronous 64-bit EL0/EL1 ventry guest_irq // IRQ 64-bit EL0/EL1