.macro invalid_host_el2_vect
        .align 7
+
+       /*
+        * Test whether the SP has overflowed, without corrupting a GPR.
+        * nVHE hypervisor stacks are aligned so that the PAGE_SHIFT bit
+        * of SP should always be 1.
+        */
+       add     sp, sp, x0                      // sp' = sp + x0
+       sub     x0, sp, x0                      // x0' = sp' - x0 = (sp + x0) - x0 = sp
+       tbz     x0, #PAGE_SHIFT, .L__hyp_sp_overflow\@
+       sub     x0, sp, x0                      // x0'' = sp' - x0' = (sp + x0) - sp = x0
+       sub     sp, sp, x0                      // sp'' = sp' - x0 = (sp + x0) - x0 = sp
+
        /* If a guest is loaded, panic out of it. */
        stp     x0, x1, [sp, #-16]!
        get_loaded_vcpu x0, x1
         * been partially clobbered by __host_enter.
         */
        b       hyp_panic
+
+.L__hyp_sp_overflow\@:
+       /*
+        * Reset SP to the top of the stack, to allow handling the hyp_panic.
+        * This corrupts the stack but is ok, since we won't be attempting
+        * any unwinding here.
+        */
+       ldr_this_cpu    x0, kvm_init_params + NVHE_INIT_STACK_HYP_VA, x1
+       mov     sp, x0
+
+       b       hyp_panic_bad_stack
+       ASM_BUG()
 .endm
 
 .macro invalid_host_el1_vect
 
        return exit_code;
 }
 
-void __noreturn hyp_panic(void)
+asmlinkage void __noreturn hyp_panic(void)
 {
        u64 spsr = read_sysreg_el2(SYS_SPSR);
        u64 elr = read_sysreg_el2(SYS_ELR);
        unreachable();
 }
 
+asmlinkage void __noreturn hyp_panic_bad_stack(void)
+{
+       hyp_panic();
+}
+
 asmlinkage void kvm_unexpected_el2_exception(void)
 {
        return __kvm_unexpected_el2_exception();