+/*
+ * NMI
+ * This may or may not be fatal but extreme care is required
+ * because it may fall when control was already in another trampoline.
+ *
+ * We get here on IST2 stack which is used for NMIs only.
+ * We must be aware of the interrupted state:
+ * - from user-space, we
+ * - copy state to the PCB and continue;
+ * - from kernel-space, we
+ * - copy state to the kernel stack and continue, but
+ * - check what GSBASE was active, set the kernel base and
+ * - ensure that the active state is restored when the NMI is dismissed.
+ */
+Entry(idt64_nmi)
+ push %rax /* save RAX to ISF64_ERR */
+ push %rcx /* save RCX to ISF64_TRAPFN */
+ push %rdx /* save RDX to ISF64_TRAPNO */
+ testb $3, ISF64_CS(%rsp) /* NMI from user-space? */
+ je 1f
+
+ /* From user-space: copy interrupt state to user PCB */
+ swapgs
+ mov %gs:CPU_UBER_ISF, %rcx /* PCB stack addr */
+ add $(ISF64_SIZE), %rcx /* adjust to base of ISF */
+ swapgs /* swap back for L_dispatch */
+ jmp 4f /* Copy state to PCB */
+
+1:
+ /*
+ * From kernel-space:
+ * Determine whether the kernel or user GS is set.
+ * Set the kernel and ensure that we'll swap back correctly at IRET.
+ */
+ mov $(MSR_IA32_GS_BASE), %ecx
+ rdmsr /* read kernel gsbase */
+ test $0x80000000, %edx /* test MSB of address */
+ jne 2f
+ swapgs /* so swap */
+ movl $1, ISF64_CS+4(%rsp) /* and set flag in CS slot */
+2:
+ /*
+ * Determine whether we're on the kernel or interrupt stack
+ * when the NMI hit.
+ */
+ mov ISF64_RSP(%rsp), %rcx
+ mov %gs:CPU_KERNEL_STACK, %rax
+ xor %rcx, %rax
+ and EXT(kernel_stack_mask)(%rip), %rax
+ test %rax, %rax /* are we on the kernel stack? */
+ je 3f /* yes */
+
+ mov %gs:CPU_INT_STACK_TOP, %rax
+ dec %rax /* intr stack top is byte above max */
+ xor %rcx, %rax
+ and EXT(kernel_stack_mask)(%rip), %rax
+ test %rax, %rax /* are we on the interrupt stack? */
+ je 3f /* yes */
+
+ mov %gs:CPU_KERNEL_STACK, %rcx
+3:
+ /* 16-byte-align kernel/interrupt stack for state push */
+ and $0xFFFFFFFFFFFFFFF0, %rcx
+
+4:
+ /*
+ * Copy state from NMI stack (RSP) to the save area (RCX) which is
+ * the PCB for user or kernel/interrupt stack from kernel.
+ * ISF64_ERR(RSP) saved RAX
+ * ISF64_TRAPFN(RSP) saved RCX
+ * ISF64_TRAPNO(RSP) saved RDX
+ */
+ xchg %rsp, %rcx /* set for pushes */
+ push ISF64_SS(%rcx)
+ push ISF64_RSP(%rcx)
+ push ISF64_RFLAGS(%rcx)
+ push ISF64_CS(%rcx)
+ push ISF64_RIP(%rcx)
+ push $(0) /* error code 0 */
+ lea HNDL_ALLINTRS(%rip), %rax
+ push %rax /* trapfn allintrs */
+ push $(T_NMI) /* trapno T_NMI */
+ mov ISF64_ERR(%rcx), %rax
+ mov ISF64_TRAPNO(%rcx), %rdx
+ mov ISF64_TRAPFN(%rcx), %rcx
+ jmp L_dispatch