parlib: Improve panic() and assert()
[akaros.git] / kern / arch / x86 / trapentry64.S
index 39b45f4..e7aa859 100644 (file)
@@ -1,12 +1,13 @@
 /* See COPYRIGHT for copyright information.
  * The two TRAP* macros (minus the .data parts) are from the JOS project.
  * Everything else:
- * Copyright (c) 2009 The Regents of the University of California
+ * Copyright (c) 2009, 2013 The Regents of the University of California
  * Barret Rhoden <brho@cs.berkeley.edu>
  * See LICENSE for details.
  */
 #include <arch/mmu.h>
 #include <arch/trap.h>
+#include <arch/x86.h>
 #include <ros/memlayout.h>
 
 ###################################################################
@@ -32,8 +33,7 @@
 
 /* Use TRAPHANDLER_NOEC for traps where the CPU doesn't push an error code.
  * It pushes a 0 in place of the error code, so the trap frame has the same
- * format in either case.
- */
+ * format in either case.  */
 #define TRAPHANDLER_NOEC(name, num)            \
        .text;                                                          \
        .globl name;                                            \
        .quad name;                                                     \
        .long num
 
-/* Same as above, but takes a specific function to jump to.  See comments
- * below from _allirqs for details.
- */
-#define IRQ_HANDLER_SPEC(name, num, func)                                      \
-       .text;                                                                                                 \
-       .globl name;                                                                                   \
-       .type name, @function;                                                                 \
-       .align 2;                                                                                              \
-       name:                                                                                                  \
-       pushq $0;                                                                  \
-       pushq $(num);                                                              \
-       cld;                                                                       \
-       pushq %ds;                                                                 \
-       pushq %es;                                                                 \
-       pushq %fs;                                                                 \
-       pushq %gs;                                                                 \
-       # pushal;                                                                  \
-       movw $0, %ax;                                                              \
-       movw %ax, %gs;                                                             \
-       movw %ax, %fs;                                                             \
-       movw $GD_KD, %ax;                                                          \
-       movw %ax, %ds;                                                             \
-       movw %ax, %es;                                                             \
-       pushq %rsp;                                                                \
-       movq $0, %rbp;                                                             \
-       call (func);                                                               \
-       popq %rsp;                                                                 \
-       # popal;                                                                   \
-       popq %gs;                                                                  \
-       popq %fs;                                                                  \
-       popq %es;                                                                  \
-       popq %ds;                                                                  \
-       addl $0x8, %rsp;                                                           \
-       iret;                                                                      \
-       .data;                                                                     \
-       .quad name;                                                                \
+#define NMI_HANDLER(name, num)                 \
+       .text;                                                          \
+       .globl name;                                            \
+       .type name, @function;                          \
+       .align 2;                                                       \
+       name:                                                           \
+       pushq $0;                                                       \
+       pushq $(num);                                           \
+       jmp _nmi_entry;                                         \
+       .data;                                                          \
+       .quad name;                                                     \
+       .long num
+
+#define DOUBLEFAULT_HANDLER(name, num) \
+       .text;                                                          \
+       .globl name;                                            \
+       .type name, @function;                          \
+       .align 2;                                                       \
+       name:                                                           \
+       pushq $(num);                                           \
+       jmp _dblf_entry;                                        \
+       .data;                                                          \
+       .quad name;                                                     \
+       .long num
+
+/* Only used in the kernel during SMP boot.  Send a LAPIC_EOI and iret. */
+#define POKE_HANDLER(name, num)                        \
+       .text;                                                          \
+       .globl name;                                            \
+       .type name, @function;                          \
+       .align 2;                                                       \
+       name:;                                                          \
+       pushq %rax;                     \
+       pushq %rcx;                     \
+       pushq %rdx;                     \
+       movq $0, %rax;                  \
+       movq $0, %rdx;                  \
+       movq $(MSR_LAPIC_EOI), %rcx;    \
+       wrmsr;                          \
+       popq %rdx;                      \
+       popq %rcx;                      \
+       popq %rax;                      \
+       iretq;                                                          \
+       .data;                                                          \
+       .quad name;                                                     \
        .long num
 
+# Set data for the trap_tbl symbol.  Set text for the entry_points.  The various
+# trap/irq handler macros will toggle data on and off as needed.
 .data
 .globl trap_tbl
 trap_tbl:
+.text
+.globl __asm_entry_points_start
+__asm_entry_points_start:
 
-/*
- * Generate entry points for the different traps.
- */
+/* Generate entry points for the different traps.  Note that all of these bounce
+ * off the corresponding trap.c function, such as handle_irqs, and that the name
+ * e.g. ISR_divide_error is soley for the little stup that jumps to something
+ * like _alltraps.
+ *
+ * Technically, these HANDLER entries do not need to be in numeric order.
+ * trap.c will do a 'foreach (up to last-1), set the IDT for the number to point
+ * to the func' in the order in which they appear in the trap tbl, so the 'last
+ * one wins'. */
 TRAPHANDLER_NOEC(ISR_divide_error, T_DIVIDE)
 TRAPHANDLER_NOEC(ISR_debug_exceptions, T_DEBUG)
-TRAPHANDLER_NOEC(ISR_NMI, T_NMI)
+NMI_HANDLER(ISR_NMI, T_NMI)
 TRAPHANDLER_NOEC(ISR_breakpoint, T_BRKPT)
 TRAPHANDLER_NOEC(ISR_overflow, T_OFLOW)
 TRAPHANDLER_NOEC(ISR_bounds_check, T_BOUND)
 TRAPHANDLER_NOEC(ISR_invalid_opcode, T_ILLOP)
 TRAPHANDLER_NOEC(ISR_device_not_available, T_DEVICE)
-/* supposedly, DF generates an error code, but the one time we've had a DF so
- * far, it didn't.  eventually, this should probably be handled with a task gate
- * it might have pushed a 0, but just the rest of the stack was corrupt
- */
-TRAPHANDLER_NOEC(ISR_double_fault, T_DBLFLT)
+DOUBLEFAULT_HANDLER(ISR_double_fault, T_DBLFLT)
 /* 9 reserved */
 TRAPHANDLER(ISR_invalid_TSS, T_TSS)
 TRAPHANDLER(ISR_segment_not_present, T_SEGNP)
@@ -131,6 +147,7 @@ TRAPHANDLER(ISR_alignment_check, T_ALIGN)
 TRAPHANDLER_NOEC(ISR_machine_check, T_MCHK)
 TRAPHANDLER_NOEC(ISR_simd_error, T_SIMDERR)
 /* 20 - 31 reserved */
+/* 32-47 are PIC/8259 IRQ vectors */
 IRQ_HANDLER(IRQ0, 32)
 IRQ_HANDLER(IRQ1, 33)
 IRQ_HANDLER(IRQ2, 34)
@@ -147,23 +164,206 @@ IRQ_HANDLER(IRQ12, 44)
 IRQ_HANDLER(IRQ13, 45)
 IRQ_HANDLER(IRQ14, 46)
 IRQ_HANDLER(IRQ15, 47)
-/* 25 general purpose vectors, for use by the LAPIC.  Can expand later. */
-IRQ_HANDLER(IRQ198, I_TESTING) # used in testing.c
+TRAPHANDLER_NOEC(ISR_syscall, T_SYSCALL)
+/* 49-223 are IOAPIC routing vectors (from IOAPIC to LAPIC) */
+IRQ_HANDLER(IRQ17, 49)
+IRQ_HANDLER(IRQ18, 50)
+IRQ_HANDLER(IRQ19, 51)
+IRQ_HANDLER(IRQ20, 52)
+IRQ_HANDLER(IRQ21, 53)
+IRQ_HANDLER(IRQ22, 54)
+IRQ_HANDLER(IRQ23, 55)
+IRQ_HANDLER(IRQ24, 56)
+IRQ_HANDLER(IRQ25, 57)
+IRQ_HANDLER(IRQ26, 58)
+IRQ_HANDLER(IRQ27, 59)
+IRQ_HANDLER(IRQ28, 60)
+IRQ_HANDLER(IRQ29, 61)
+IRQ_HANDLER(IRQ30, 62)
+IRQ_HANDLER(IRQ31, 63)
+IRQ_HANDLER(IRQ32, 64)
+IRQ_HANDLER(IRQ33, 65)
+IRQ_HANDLER(IRQ34, 66)
+IRQ_HANDLER(IRQ35, 67)
+IRQ_HANDLER(IRQ36, 68)
+IRQ_HANDLER(IRQ37, 69)
+IRQ_HANDLER(IRQ38, 70)
+IRQ_HANDLER(IRQ39, 71)
+IRQ_HANDLER(IRQ40, 72)
+IRQ_HANDLER(IRQ41, 73)
+IRQ_HANDLER(IRQ42, 74)
+IRQ_HANDLER(IRQ43, 75)
+IRQ_HANDLER(IRQ44, 76)
+IRQ_HANDLER(IRQ45, 77)
+IRQ_HANDLER(IRQ46, 78)
+IRQ_HANDLER(IRQ47, 79)
+IRQ_HANDLER(IRQ48, 80)
+IRQ_HANDLER(IRQ49, 81)
+IRQ_HANDLER(IRQ50, 82)
+IRQ_HANDLER(IRQ51, 83)
+IRQ_HANDLER(IRQ52, 84)
+IRQ_HANDLER(IRQ53, 85)
+IRQ_HANDLER(IRQ54, 86)
+IRQ_HANDLER(IRQ55, 87)
+IRQ_HANDLER(IRQ56, 88)
+IRQ_HANDLER(IRQ57, 89)
+IRQ_HANDLER(IRQ58, 90)
+IRQ_HANDLER(IRQ59, 91)
+IRQ_HANDLER(IRQ60, 92)
+IRQ_HANDLER(IRQ61, 93)
+IRQ_HANDLER(IRQ62, 94)
+IRQ_HANDLER(IRQ63, 95)
+IRQ_HANDLER(IRQ64, 96)
+IRQ_HANDLER(IRQ65, 97)
+IRQ_HANDLER(IRQ66, 98)
+IRQ_HANDLER(IRQ67, 99)
+IRQ_HANDLER(IRQ68, 100)
+IRQ_HANDLER(IRQ69, 101)
+IRQ_HANDLER(IRQ70, 102)
+IRQ_HANDLER(IRQ71, 103)
+IRQ_HANDLER(IRQ72, 104)
+IRQ_HANDLER(IRQ73, 105)
+IRQ_HANDLER(IRQ74, 106)
+IRQ_HANDLER(IRQ75, 107)
+IRQ_HANDLER(IRQ76, 108)
+IRQ_HANDLER(IRQ77, 109)
+IRQ_HANDLER(IRQ78, 110)
+IRQ_HANDLER(IRQ79, 111)
+IRQ_HANDLER(IRQ80, 112)
+IRQ_HANDLER(IRQ81, 113)
+IRQ_HANDLER(IRQ82, 114)
+IRQ_HANDLER(IRQ83, 115)
+IRQ_HANDLER(IRQ84, 116)
+IRQ_HANDLER(IRQ85, 117)
+IRQ_HANDLER(IRQ86, 118)
+IRQ_HANDLER(IRQ87, 119)
+IRQ_HANDLER(IRQ88, 120)
+IRQ_HANDLER(IRQ89, 121)
+IRQ_HANDLER(IRQ90, 122)
+IRQ_HANDLER(IRQ91, 123)
+IRQ_HANDLER(IRQ92, 124)
+IRQ_HANDLER(IRQ93, 125)
+IRQ_HANDLER(IRQ94, 126)
+IRQ_HANDLER(IRQ95, 127)
+IRQ_HANDLER(IRQ96, 128)
+IRQ_HANDLER(IRQ97, 129)
+IRQ_HANDLER(IRQ98, 130)
+IRQ_HANDLER(IRQ99, 131)
+IRQ_HANDLER(IRQ100, 132)
+IRQ_HANDLER(IRQ101, 133)
+IRQ_HANDLER(IRQ102, 134)
+IRQ_HANDLER(IRQ103, 135)
+IRQ_HANDLER(IRQ104, 136)
+IRQ_HANDLER(IRQ105, 137)
+IRQ_HANDLER(IRQ106, 138)
+IRQ_HANDLER(IRQ107, 139)
+IRQ_HANDLER(IRQ108, 140)
+IRQ_HANDLER(IRQ109, 141)
+IRQ_HANDLER(IRQ110, 142)
+IRQ_HANDLER(IRQ111, 143)
+IRQ_HANDLER(IRQ112, 144)
+IRQ_HANDLER(IRQ113, 145)
+IRQ_HANDLER(IRQ114, 146)
+IRQ_HANDLER(IRQ115, 147)
+IRQ_HANDLER(IRQ116, 148)
+IRQ_HANDLER(IRQ117, 149)
+IRQ_HANDLER(IRQ118, 150)
+IRQ_HANDLER(IRQ119, 151)
+IRQ_HANDLER(IRQ120, 152)
+IRQ_HANDLER(IRQ121, 153)
+IRQ_HANDLER(IRQ122, 154)
+IRQ_HANDLER(IRQ123, 155)
+IRQ_HANDLER(IRQ124, 156)
+IRQ_HANDLER(IRQ125, 157)
+IRQ_HANDLER(IRQ126, 158)
+IRQ_HANDLER(IRQ127, 159)
+IRQ_HANDLER(IRQ128, 160)
+IRQ_HANDLER(IRQ129, 161)
+IRQ_HANDLER(IRQ130, 162)
+IRQ_HANDLER(IRQ131, 163)
+IRQ_HANDLER(IRQ132, 164)
+IRQ_HANDLER(IRQ133, 165)
+IRQ_HANDLER(IRQ134, 166)
+IRQ_HANDLER(IRQ135, 167)
+IRQ_HANDLER(IRQ136, 168)
+IRQ_HANDLER(IRQ137, 169)
+IRQ_HANDLER(IRQ138, 170)
+IRQ_HANDLER(IRQ139, 171)
+IRQ_HANDLER(IRQ140, 172)
+IRQ_HANDLER(IRQ141, 173)
+IRQ_HANDLER(IRQ142, 174)
+IRQ_HANDLER(IRQ143, 175)
+IRQ_HANDLER(IRQ144, 176)
+IRQ_HANDLER(IRQ145, 177)
+IRQ_HANDLER(IRQ146, 178)
+IRQ_HANDLER(IRQ147, 179)
+IRQ_HANDLER(IRQ148, 180)
+IRQ_HANDLER(IRQ149, 181)
+IRQ_HANDLER(IRQ150, 182)
+IRQ_HANDLER(IRQ151, 183)
+IRQ_HANDLER(IRQ152, 184)
+IRQ_HANDLER(IRQ153, 185)
+IRQ_HANDLER(IRQ154, 186)
+IRQ_HANDLER(IRQ155, 187)
+IRQ_HANDLER(IRQ156, 188)
+IRQ_HANDLER(IRQ157, 189)
+IRQ_HANDLER(IRQ158, 190)
+IRQ_HANDLER(IRQ159, 191)
+IRQ_HANDLER(IRQ160, 192)
+IRQ_HANDLER(IRQ161, 193)
+IRQ_HANDLER(IRQ162, 194)
+IRQ_HANDLER(IRQ163, 195)
+IRQ_HANDLER(IRQ164, 196)
+IRQ_HANDLER(IRQ165, 197)
+IRQ_HANDLER(IRQ166, 198)
+IRQ_HANDLER(IRQ167, 199)
+IRQ_HANDLER(IRQ168, 200)
+IRQ_HANDLER(IRQ169, 201)
+IRQ_HANDLER(IRQ170, 202)
+IRQ_HANDLER(IRQ171, 203)
+IRQ_HANDLER(IRQ172, 204)
+IRQ_HANDLER(IRQ173, 205)
+IRQ_HANDLER(IRQ174, 206)
+IRQ_HANDLER(IRQ175, 207)
+IRQ_HANDLER(IRQ176, 208)
+IRQ_HANDLER(IRQ177, 209)
+IRQ_HANDLER(IRQ178, 210)
+IRQ_HANDLER(IRQ179, 211)
+IRQ_HANDLER(IRQ180, 212)
+IRQ_HANDLER(IRQ181, 213)
+IRQ_HANDLER(IRQ182, 214)
+IRQ_HANDLER(IRQ183, 215)
+IRQ_HANDLER(IRQ184, 216)
+IRQ_HANDLER(IRQ185, 217)
+IRQ_HANDLER(IRQ186, 218)
+IRQ_HANDLER(IRQ187, 219)
+IRQ_HANDLER(IRQ188, 220)
+IRQ_HANDLER(IRQ189, 221)
+IRQ_HANDLER(IRQ190, 222)
+IRQ_HANDLER(IRQ191, 223)
+/* 224-239 are OS IPI vectors (0xe0-0xef) */
+IRQ_HANDLER(IRQ192, I_SMP_CALL0)
+IRQ_HANDLER(IRQ193, I_SMP_CALL1)
+IRQ_HANDLER(IRQ194, I_SMP_CALL2)
+IRQ_HANDLER(IRQ195, I_SMP_CALL3)
+IRQ_HANDLER(IRQ196, I_SMP_CALL4)
+IRQ_HANDLER(IRQ197, 229)
+IRQ_HANDLER(IRQ198, 230)
 IRQ_HANDLER(IRQ199, 231)
 IRQ_HANDLER(IRQ200, 232)
 IRQ_HANDLER(IRQ201, 233)
 IRQ_HANDLER(IRQ202, 234)
 IRQ_HANDLER(IRQ203, 235)
 IRQ_HANDLER(IRQ204, 236)
-IRQ_HANDLER(IRQ205, 237)
-IRQ_HANDLER(IRQ206, 238)
-IRQ_HANDLER(IRQ207, 239)
-/* 0xf0 - start of the SMP_CALL IPIS */
-IRQ_HANDLER(IRQ208, I_SMP_CALL0)
-IRQ_HANDLER(IRQ209, I_SMP_CALL1)
-IRQ_HANDLER(IRQ210, I_SMP_CALL2)
-IRQ_HANDLER(IRQ211, I_SMP_CALL3)
-IRQ_HANDLER(IRQ212, I_SMP_CALL4)
+IRQ_HANDLER(IRQ205, I_TESTING)
+POKE_HANDLER(IRQ206, I_POKE_CORE)
+IRQ_HANDLER(IRQ207, I_KERNEL_MSG)
+/* 240-255 are LAPIC vectors (0xf0-0xff), hightest priority class */
+IRQ_HANDLER(IRQ208, 240)
+IRQ_HANDLER(IRQ209, 241)
+IRQ_HANDLER(IRQ210, 242)
+IRQ_HANDLER(IRQ211, 243)
+IRQ_HANDLER(IRQ212, 244)
 IRQ_HANDLER(IRQ213, 245)
 IRQ_HANDLER(IRQ214, 246)
 IRQ_HANDLER(IRQ215, 247)
@@ -174,10 +374,7 @@ IRQ_HANDLER(IRQ219, 251)
 IRQ_HANDLER(IRQ220, 252)
 IRQ_HANDLER(IRQ221, 253)
 IRQ_HANDLER(IRQ222, 254)
-IRQ_HANDLER(IRQ223, I_KERNEL_MSG)
-
-/* Technically, these HANDLER entries do not need to be in numeric order */
-TRAPHANDLER_NOEC(ISR_syscall, T_SYSCALL)
+IRQ_HANDLER(IRQ223, 255)
 /* But make sure default is last!! */
 TRAPHANDLER_NOEC(ISR_default, T_DEFAULT)
 
@@ -185,96 +382,573 @@ TRAPHANDLER_NOEC(ISR_default, T_DEFAULT)
 .globl trap_tbl_end
 trap_tbl_end:
 
-/* Keep the exit paths of _alltraps, _allirqs, and sysenter_handler in sync
- * with the corrrsponding pop_tf's.
- */
 .text
 _alltraps:
        cld
-       # pushq %ds
-       # pushq %es
-       pushq %fs
-       pushq %gs
-       # pushal
-       movw $0, %ax;
-       movw %ax, %gs;
-       movw %ax, %fs;
-       movw $GD_KD, %ax                # data segments aren't accessible by default
-       pushq %rsp
+       pushq %r15
+       pushq %r14
+       pushq %r13
+       pushq %r12
+       pushq %r11
+       pushq %r10
+       pushq %r9
+       pushq %r8
+       pushq %rdi
+       pushq %rsi
+       pushq %rbp
+       pushq %rdx
+       pushq %rcx
+       pushq %rbx
+       pushq %rax
+       cmpw $GD_KT, 0x90(%rsp) # 0x90 - diff btw tf_cs and tf_rax
+       je trap_all_tf
+       # this is a user TF.  we need to swapgs to get the kernel's gs and mark the
+       # context as partial
+       swapgs                                  # user's GS is now in MSR_KERNEL_GS_BASE
+       movl $0x1, 0xac(%rsp)   # 0xac - diff btw tf_padding0 and tf_rax
+trap_all_tf:
+       pushq $0                                # fsbase space
+       pushq $0                                # gsbase space
        movq $0, %rbp                   # so we can backtrace to this point
+       movq %rsp, %rdi
        call trap
-       popq %rsp
-       # popal
-       popq %gs
-       popq %fs
-       # popq %es
-       # popq %ds
-       addq $0x8, %rsp                 # skip trapno and err
-       iret
+       # the return paths are only used by the kernel
+       addq $0x10, %rsp                        # skip fs/gs base
+       popq %rax
+       popq %rbx
+       popq %rcx
+       popq %rdx
+       popq %rbp
+       popq %rsi
+       popq %rdi
+       popq %r8
+       popq %r9
+       popq %r10
+       popq %r11
+       popq %r12
+       popq %r13
+       popq %r14
+       popq %r15
+       addq $0x10, %rsp                        # skip trapno and err
+       iretq
 
-/* will need to think about when we reenable interrupts.  right now, iret does it,
- * if the previous EFLAGS had interrupts enabled
- */
+# might merge this with _alltraps
 _allirqs:
        cld
-       # pushq %ds
-       # pushq %es
-       pushq %fs
-       pushq %gs
-       # pushal
-       movw $0, %ax;
-       movw %ax, %gs;
-       movw %ax, %fs;
-       movw $GD_KD, %ax                # data segments aren't accessible by default
-       movw %ax, %ds
-       movw %ax, %es
-       pushq %rsp
+       pushq %r15
+       pushq %r14
+       pushq %r13
+       pushq %r12
+       pushq %r11
+       pushq %r10
+       pushq %r9
+       pushq %r8
+       pushq %rdi
+       pushq %rsi
+       pushq %rbp
+       pushq %rdx
+       pushq %rcx
+       pushq %rbx
+       pushq %rax
+       cmpw $GD_KT, 0x90(%rsp) # 0x90 - diff btw tf_cs and tf_rax
+       je irq_all_tf
+       # this is a user TF.  we need to swapgs to get the kernel's gs and mark the
+       # context as partial
+       swapgs                                  # user's GS is now in MSR_KERNEL_GS_BASE
+       movl $0x1, 0xac(%rsp)   # 0xac - diff btw tf_padding0 and tf_rax
+irq_all_tf:
+       pushq $0                                # fsbase space
+       pushq $0                                # gsbase space
+       movq $0, %rbp                   # so we can backtrace to this point
+       movq %rsp, %rdi
+       call handle_irq
+       # the return paths are only used by the kernel
+       addq $0x10, %rsp                        # skip fs/gs base
+       popq %rax
+       popq %rbx
+       popq %rcx
+       popq %rdx
+       popq %rbp
+       popq %rsi
+       popq %rdi
+       popq %r8
+       popq %r9
+       popq %r10
+       popq %r11
+       popq %r12
+       popq %r13
+       popq %r14
+       popq %r15
+       addq $0x10, %rsp                        # skip trapno and err
+       iretq
+
+# Similar to the NMI handler, we come in on a special stack, but we can trust
+# the contents of GS for kernel contexts.  This is mostly true.  If we double
+# faulted in this file, for instance, then GS could be garbage.  But that should
+# never happen.  Most double faults will be for running into a guard page on a
+# stack.
+#
+# Since the double fault is so bad, we just want to get into the kernel's C code
+# where we can run some diagnostics.  We (currently) won't try to recover, since
+# it's probably a kernel bug.
+_dblf_entry:
+       cld
+       pushq %r15
+       pushq %r14
+       pushq %r13
+       pushq %r12
+       pushq %r11
+       pushq %r10
+       pushq %r9
+       pushq %r8
+       pushq %rdi
+       pushq %rsi
+       pushq %rbp
+       pushq %rdx
+       pushq %rcx
+       pushq %rbx
+       pushq %rax
+       cmpw $GD_KT, 0x90(%rsp) # 0x90 - diff btw tf_cs and tf_rax
+       je dblf_all_tf
+       # this is a user TF.  we need to swapgs to get the kernel's gs and mark the
+       # context as partial
+       swapgs                                  # user's GS is now in MSR_KERNEL_GS_BASE
+       movl $0x1, 0xac(%rsp)   # 0xac - diff btw tf_padding0 and tf_rax
+dblf_all_tf:
+       pushq $0                                # fsbase space
+       pushq $0                                # gsbase space
+       movq $0, %rbp                   # so we can backtrace to this point
+       movq %rsp, %rdi
+       call handle_double_fault
+dblf_spin:
+       jmp dblf_spin
+
+# Unlike normal trap and IRQ handlers, both user and kernel TFs are handled the
+# similarly.  Both come in here and return from here.  We cannot do anything
+# fancy like proc_restartcore() from NMI context.
+#
+# All NMIs will come in fresh on the same stack (IST1, per-core).  We don't need
+# to find a stackpointer, but we do need to find GS.
+#
+# Regardless of whether or not the interrupted context was in the kernel, we
+# can't trust GS.  Basically we can never tell from looking at GS and KERN_GS
+# whether swapgs occurred, since the user could have put kernel addrs in their
+# GS.  But we can use the stack for storage to bootstrap GS.  %rsp at entry
+# points to pcpui *.
+#
+# We can also tell if GS was set correctly or not and avoid the wrmsr calls.
+# This isn't a huge deal.  But note that we don't know whether or not the kernel
+# actually set the gsbase.  We just know if it was correct or not.  The user
+# could have set it and the kernel hadn't had a chance to swapgs yet.  The NMI
+# handler doesn't care, since all TFs come in and go out via this asm.
+#
+# If we want to be paranoid, we can completely ignore user TFs and just save and
+# restore GS with the same mechanism we use for the kernel.  But since we came
+# in on a user TF, we can use swapgs.  Note that even for nested NMI handlers,
+# we have a user TF for only the first time, and only the last exit will swapgs
+# back to the way it was initially.
+_nmi_entry:
+       cld
+       pushq %r15
+       pushq %r14
+       pushq %r13
+       pushq %r12
+       pushq %r11
+       pushq %r10
+       pushq %r9
+       pushq %r8
+       pushq %rdi
+       pushq %rsi
+       pushq %rbp
+       pushq %rdx
+       pushq %rcx
+       pushq %rbx
+       pushq %rax
+       cmpw $GD_KT, 0x90(%rsp) # 0x90 - diff btw tf_cs and tf_rax
+       je nmi_kern_tf
+       # this is a user TF.  we need to swapgs to get the kernel's gs and mark the
+       # context as partial
+       swapgs                                  # user's GS is now in MSR_KERNEL_GS_BASE
+       movl $0x1, 0xac(%rsp)   # 0xac - diff btw tf_padding0 and tf_rax
+       pushq $0                                # fsbase space
+       pushq $0                                # gsbase space
+       jmp nmi_all_tf
+nmi_kern_tf:
+       # this is a kernel TF.  but we don't know if they set up gs yet, so we'll
+       # save and restore whatever they had loaded and use our own
+       pushq $0                                # fsbase space
+       # Get the current GS base into rax
+       movl $MSR_GS_BASE, %ecx
+       rdmsr
+       shlq $32, %rdx
+       orq %rdx, %rax
+       # Get the real GS base from the top of the stack.  This was set in smp_boot,
+       # and our rsp pointed to it when we entered the kernel.
+       movq 0xb8(%rsp), %rdx   # 0xb8 from fs_base to the top
+       # Compare them.  If they are the same, we can just push 0 for gsbase (which
+       # later will mean "no need to restore GS".
+       cmpq %rdx, %rax
+       je nmi_gs_ok
+       # They weren't the same.  Save the old one and set the new one.
+       pushq %rax                              # gsbase space
+       movq %rdx, %rax
+       shrq $32, %rdx
+       andl $0xffffffff, %eax
+       wrmsr
+       jmp nmi_all_tf
+nmi_gs_ok:
+       pushq $0                                # gsbase space
+nmi_all_tf:
+       # At this point, GS is set correctly, either due to swapgs (user TF), wrmsr
+       # (kern TF with bad GS), or it was already fine (and gsbase in the TF = 0).
        movq $0, %rbp                   # so we can backtrace to this point
-       call irq_handler
-       popq %rsp
-       # popal
-       popq %gs
-       popq %fs
-       # popq %es
-       # popq %ds
-       addq $0x8, %rsp                 # skip IRQ number and err (which is 0)
-       iret
+       movq %rsp, %rdi
+       call handle_nmi
+       # Unlike in normal IRQs/Traps, both user and kernel contexts return via this
+       # path.
+       cmpw $GD_KT, 0xa0(%rsp) # 0xa0 - diff btw tf_cs and tf_gsbase
+       je nmi_kern_restore_gs
+       # User TF.  Restore whatever was there with swapgs.  We don't care what it
+       # was, nor do we care what was in the TF.
+       swapgs                                  # user's GS is now in MSR_GS_BASE
+       addq $0x10, %rsp                # skip gs/fs base
+       jmp nmi_popal
+nmi_kern_restore_gs:
+       popq %rax                               # fetch saved gsbase
+       addq $0x08, %rsp                # skip fs base
+       cmpq $0, %rax
+       je nmi_popal
+       # gsbase in the TF != 0, which means we need to restore that gsbase
+       movl $MSR_GS_BASE, %ecx
+       movq %rax, %rdx
+       shrq $32, %rdx
+       andl $0xffffffff, %eax
+       wrmsr
+nmi_popal:
+       popq %rax
+       popq %rbx
+       popq %rcx
+       popq %rdx
+       popq %rbp
+       popq %rsi
+       popq %rdi
+       popq %r8
+       popq %r9
+       popq %r10
+       popq %r11
+       popq %r12
+       popq %r13
+       popq %r14
+       popq %r15
+       addq $0x10, %rsp                        # skip trapno and err
+       iretq
+
+.globl __nmi_pop_ok_start;
+.globl __nmi_pop_ok_end;
+.globl __nmi_pop_fail_start;
+.globl __nmi_pop_fail_end;
+
+# extern void nmi_try_to_pop(struct hw_trapframe *tf, int *status,
+#                            int old_val, int new_val);
+#
+# __nmi_bottom_half calls this to atomically pop a hw_tf (%rdi) and set
+# &pcpui->nmi_status (%rsi) with compare and swap to NMI_NORMAL_OPN (%ecx) given
+# that it was NMI_IN_PROGRESS (%edx)
+#
+# (Careful, nmi_status is an int, not a long.)
+#
+# If the real NMI handler interrupts us, it'll move us to the fail section of
+# the code.  That code is identical to 'ok', up until ok's final statement.
+#
+# In that event, we'll need a little help returning: specifically to bootstrap
+# pcpui and our current stackpointer.  pcpui is already saved near the top of
+# stack.  We'll save rsp ourselves.
+.globl nmi_try_to_pop;
+.type nmi_try_to_pop, @function;
+nmi_try_to_pop:
+__nmi_pop_ok_start:
+       # careful only to use caller-saved or argument registers before saving
+       movl %edx, %eax                 # load old_val into eax for the CAS
+       cmpxchgl %ecx, (%rsi)   # no need for LOCK, since an NMI would serialize
+       jz nmi_ok_cas_worked    # ZF = 1 on successful CAS
+       ret
+nmi_ok_cas_worked:
+       # save callee-saved regs (the pops below clobber them, and we might return)
+       pushq %rbp
+       pushq %rbx
+       pushq %r12
+       pushq %r13
+       pushq %r14
+       pushq %r15
+       # We need to save the current rsp into the scratch space at the top of the
+       # stack.  This assumes we're within the top page of our stack, which should
+       # always be true.  Careful not to use rdi, which still has an argument.
+       movq %rsp, %rbx
+       # Want to round rbx up to PGSIZE, then subtract 8, to get our slot.
+       movq $0xfff, %rax
+       notq %rax                               # rax = 0xfffffffffffff000
+       andq %rax, %rbx                 # round down rbx
+       addq $0x1000, %rbx              # add PGSIZE, assuming rsp was not page aligned
+       subq $0x8, %rbx                 # point to the scratch space
+       movq %rsp, (%rbx)               # save rsp in the scratch space
+       # We jump our rsp to the base of the HW_TF.  This is still on the same
+       # stack, just farther back than where our caller is.  We need to be careful
+       # to not clobber the stack.  Otherwise we'll have chaos.
+       movq %rdi, %rsp
+       # From here down is the same as the normal NMI exit path, but with 'ok' in
+       # the symbol names.
+       cmpw $GD_KT, 0xa0(%rsp) # 0xa0 - diff btw tf_cs and tf_gsbase
+       je nmi_ok_kern_restore_gs
+       # User TF.  Restore whatever was there with swapgs.  We don't care what it
+       # was, nor do we care what was in the TF.
+       swapgs                                  # user's GS is now in MSR_GS_BASE
+       addq $0x10, %rsp                # skip gs/fs base
+       jmp nmi_ok_popal
+nmi_ok_kern_restore_gs:
+       popq %rax                               # fetch saved gsbase
+       addq $0x08, %rsp                # skip fs base
+       cmpq $0, %rax
+       je nmi_ok_popal
+       # gsbase in the TF != 0, which means we need to restore that gsbase
+       movl $MSR_GS_BASE, %ecx
+       movq %rax, %rdx
+       shrq $32, %rdx
+       andl $0xffffffff, %eax
+       wrmsr
+nmi_ok_popal:
+       popq %rax
+       popq %rbx
+       popq %rcx
+       popq %rdx
+       popq %rbp
+       popq %rsi
+       popq %rdi
+       popq %r8
+       popq %r9
+       popq %r10
+       popq %r11
+       popq %r12
+       popq %r13
+       popq %r14
+       popq %r15
+       addq $0x10, %rsp                        # skip trapno and err
+       iretq
+__nmi_pop_ok_end:
+
+# This is the 'fail' case.  It is identical to the 'ok' case, up until the
+# iretq, other than 'ok' replaced with 'fail'.  In place of iretq, we undo the
+# entire operation.
+__nmi_pop_fail_start:
+       # careful only to use caller-saved or argument registers before saving
+       movl %edx, %eax                 # load old_val into eax for the CAS
+       cmpxchgl %ecx, (%rsi)   # no need for LOCK, since an NMI would serialize
+       jz nmi_fail_cas_worked  # ZF = 1 on successful CAS
+       ret
+nmi_fail_cas_worked:
+       # save callee-saved regs (the pops below clobber them, and we might return)
+       pushq %rbp
+       pushq %rbx
+       pushq %r12
+       pushq %r13
+       pushq %r14
+       pushq %r15
+       # We need to save the current rsp into the scratch space at the top of the
+       # stack.  This assumes we're within the top page of our stack, which should
+       # always be true.  Careful not to use rdi, which still has an argument.
+       movq %rsp, %rbx
+       # Want to round rbx up to PGSIZE, then subtract 8, to get our slot.
+       movq $0xfff, %rax
+       notq %rax                               # rax = 0xfffffffffffff000
+       andq %rax, %rbx                 # round down rbx
+       addq $0x1000, %rbx              # add PGSIZE, assuming rsp was not page aligned
+       subq $0x8, %rbx                 # point to the scratch space
+       movq %rsp, (%rbx)               # save rsp in the scratch space
+       # We jump our rsp to the base of the HW_TF.  This is still on the same
+       # stack, just farther back than where our caller is.  We need to be careful
+       # to not clobber the stack.  Otherwise we'll have chaos.
+       movq %rdi, %rsp
+       # From here down is the same as the normal NMI exit path and the ok path,
+       # but with 'fail' in the symbol names.
+       cmpw $GD_KT, 0xa0(%rsp) # 0xa0 - diff btw tf_cs and tf_gsbase
+       je nmi_fail_kern_restore_gs
+       # User TF.  Restore whatever was there with swapgs.  We don't care what it
+       # was, nor do we care what was in the TF.
+       swapgs                                  # user's GS is now in MSR_GS_BASE
+       addq $0x10, %rsp                # skip gs/fs base
+       jmp nmi_fail_popal
+nmi_fail_kern_restore_gs:
+       popq %rax                               # fetch saved gsbase
+       addq $0x08, %rsp                # skip fs base
+       cmpq $0, %rax
+       je nmi_fail_popal
+       # gsbase in the TF != 0, which means we need to restore that gsbase
+       movl $MSR_GS_BASE, %ecx
+       movq %rax, %rdx
+       shrq $32, %rdx
+       andl $0xffffffff, %eax
+       wrmsr
+nmi_fail_popal:
+       popq %rax
+       popq %rbx
+       popq %rcx
+       popq %rdx
+       popq %rbp
+       popq %rsi
+       popq %rdi
+       popq %r8
+       popq %r9
+       popq %r10
+       popq %r11
+       popq %r12
+       popq %r13
+       popq %r14
+       popq %r15
+       addq $0x10, %rsp                # skip trapno and err
+       # Here's is where we differ from OK.  Time to undo everything and return
+       # rsp currently is pointing at tf->tf_rip.  Remember that we don't want to
+       # write anything to the stack - everything in the TF is still the way it was
+       # when we started to pop.
+       #
+       # First off, let's get the stack addr of the pcpui pointer loaded
+       movq %rsp, %rbx
+       movq $0xfff, %rax
+       notq %rax                               # rax = 0xfffffffffffff000
+       andq %rax, %rbx                 # round down rbx
+       addq $0x1000, %rbx              # add PGSIZE, assuming rsp was not page aligned
+       subq $0x10, %rbx                # point to the pcpui pointer
+       # Now let's start to unwind
+       subq $0x98, %rsp                # jump from rip to tf_gsbase (top of hw_tf)
+       # Need to restore gs, just like on an NMI entry
+       cmpw $GD_KT, 0xa0(%rsp) # 0xa0 - diff btw tf_cs and tf_gsbase
+       je nmi_pop_fail_kern_tf
+       # This is a user TF.  We need to swapgs to get the kernel's gs
+       # We don't need to mark the context as partial (we never do for NMIs,
+       # actually), and in general, we don't want to write anything on the stack.
+       swapgs                                  # user's GS is now in MSR_KERNEL_GS_BASE
+       jmp nmi_pop_fail_all_tf
+nmi_pop_fail_kern_tf:
+       # Kernel TF.  We basically need to do the same thing on entry, since we
+       # might have restored some weird GS base.  We can tell based on tf_gsbase
+       # 0 for gsbase means we didn't need to change GS
+       cmpq $0, (%rsp)
+       je nmi_pop_fail_gs_fine
+       # rbx points to where pcpui* is stored
+       mov (%rbx), %rdx
+       movl $MSR_GS_BASE, %ecx
+       movq %rdx, %rax
+       shrq $32, %rdx
+       andl $0xffffffff, %eax
+       wrmsr
+nmi_pop_fail_gs_fine:
+nmi_pop_fail_all_tf:
+       addq $0x8, %rbx                 # move to the scratch slot, holding rsp
+       mov (%rbx), %rsp
+       # restore callee-saved regs
+       popq %r15
+       popq %r14
+       popq %r13
+       popq %r12
+       popq %rbx
+       popq %rbp
+       ret
+       # sweet jeebus.
+__nmi_pop_fail_end:
+
 
 .globl sysenter_handler;
 .type sysenter_handler, @function;
-# All of the pushq zeros are to keep the trap frame looking the same as when we
-# receive a trap or an interrupt
+
 sysenter_handler:
-       cld
-       pushq $0                                # ss
-       pushq $0                                # rsp
-       pushfq                                  # eflags
-       pushq $0                                # CS == 0 lets the kernel know it was a sysenter        
-       pushq $0                                # eip
-       pushq $0                                # err 
-       pushq $T_SYSCALL                # helps with print_trapframe
-       # pushq %ds
-       # pushq %es
-       pushq %fs
-       pushq %gs
-       # pushal
-       movw $0, %ax;
-       movw %ax, %gs;
-       movw %ax, %fs;
-       movw $GD_KD, %ax
-       movw %ax, %ds
-       movw %ax, %es
-       pushq %rsp
+#ifndef CONFIG_NOFASTCALL_FSBASE
+       # Do a quick TLS / FS base change, never changing stacks.
+       # When rdi has the magic number, rsi has the new base
+       movabs $FASTCALL_SETFSBASE, %rax
+       cmp %rax, %rdi
+       jne normal_syscall      # could profile this and handle the jump differently
+       # need to check rsi, make sure it is canonical (will enfore below ULIM).
+       # need to either do this check, or handle the kernel GP fault on wrmsr.
+       movq %rsi, %rdi
+       shrq $47, %rdi
+       cmp $0, %rdi
+       jne fastcall_pop
+       # need to use cx, dx, and ax for the wrmsr.  dx and ax are free.
+       movq %rcx, %rdi         # save rcx, the retaddr
+       movq %rsi, %rdx
+       movq %rsi, %rax
+       shrq $32, %rdx
+       andl $0xffffffff, %eax
+       movl $MSR_FS_BASE, %ecx
+       wrmsr
+       movq %rdi, %rcx         # restore retaddr
+fastcall_pop:
+       rex.w sysret
+normal_syscall:
+#endif
+       # cld is handled by the SFMASK
+       swapgs                                  # user's GS is now in MSR_KERNEL_GS_BASE
+       movq %gs:0, %rsp
+       # Saving the FPU callee-saved state for now.  Might be able to have the
+       # preempt handler deal with it.
+       pushq $0                                # space for mxcsr, fpucw, and padding0
+       movw $0x1, 0x6(%rsp)    # tf_padding0 = 1, partial context
+       fnstcw 0x4(%rsp)
+       stmxcsr (%rsp)
+       pushq %rdx                      # rsp, saved by userspace
+       pushq %rcx                      # rip, saved by hardware
+       pushq %r15
+       pushq %r14
+       pushq %r13
+       pushq %r12
+       pushq %rbp
+       pushq %rbx
+       pushq $0                        # fsbase space
+       pushq $0                        # gsbase space
        movq $0, %rbp                   # so we can backtrace to this point
+       movq %rsp, %rdx
+       # arg0, rdi: struct sysc*.  arg1, rsi: count.  arg2, rdx: sw_tf
        call sysenter_callwrapper
-       popq %rsp
-       # popal
-       popq %gs
-       popq %fs
-       # popq %es
-       # popq %ds
-       addq $0x10, %rsp                # pop T_SYSCALL and the three zeros
-       popfq                                   # restore EFLAGS (and usually enables interrupts!)
-       movq %rbp, %rcx
-       sti                                             # interrupts are turned off when starting a core
-       sysexit
+       # return via pop_tf, never this path
+sysenter_spin:
+       jmp sysenter_spin
+
+.globl vmexit_handler;
+.type vmexit_handler, @function;
+vmexit_handler:
+       # rflags has all flags = 0, so cli and cld already.
+       # HOST_GS_BASE and RSP is set by the hardware
+       # Set default values.  Most of these will be set in C later.
+       pushq $0                        # guest_pa
+       pushq $0                        # guest_va
+       pushq $0                        # intrinfo2 and 1
+       pushq $0                        # exit_qual + exit_reason
+       pushq $0                        # pad + trap_inject
+       pushq $0                        # flags + guest_pcorid
+       pushq $0                        # cr3
+       pushq $0                        # cr2
+       pushq $0                        # rsp
+       pushq $0                        # rflags
+       pushq $0                        # rip
+       # Save register state
+       pushq %r15
+       pushq %r14
+       pushq %r13
+       pushq %r12
+       pushq %r11
+       pushq %r10
+       pushq %r9
+       pushq %r8
+       pushq %rdi
+       pushq %rsi
+       pushq %rbp
+       pushq %rdx
+       pushq %rcx
+       pushq %rbx
+       pushq %rax
+       movq $0, %rbp                   # so we can backtrace to this point
+       movq %rsp, %rdi
+       call handle_vmexit
+vmexit_spin:
+       jmp vmexit_spin
+
+.globl __asm_entry_points_end
+__asm_entry_points_end: