Enabling X2APIC
[akaros.git] / kern / arch / x86 / ros / mmu64.h
index 1742f72..3ebd79e 100644 (file)
@@ -1,5 +1,4 @@
-#ifndef ROS_INC_ARCH_MMU64_H
-#define ROS_INC_ARCH_MMU64_H
+#pragma once
 
 #ifndef ROS_INC_ARCH_MMU_H
 #error "Do not include include ros/arch/mmu64.h directly"
@@ -7,8 +6,15 @@
 
 #ifndef __ASSEMBLER__
 #include <ros/common.h>
-typedef unsigned long pte_t;
-typedef unsigned long pde_t;
+typedef unsigned long kpte_t;
+typedef unsigned long epte_t;
+
+typedef kpte_t* pte_t;
+
+typedef struct x86_pgdir {
+       kpte_t  *kpte;
+       uint64_t eptp;
+} pgdir_t;
 #endif
 
 /* Virtual memory map:                                  Virt Addresses
@@ -25,14 +31,14 @@ typedef unsigned long pde_t;
  *                     |                              |                     |
  * KERN_LOAD_ADDR -->  +------------------------------+ 0xffffffffc0000000 -+
  *                     |                              |
- *                     |          Local APIC          | RW/--  PGSIZE
+ *                     |            IOAPIC            | RW/--  APIC_SIZE (1MB)
  *                     |                              |
- *    LAPIC_BASE  -->  +------------------------------+ 0xffffffffbffff000
+ *   IOAPIC_BASE  -->  +------------------------------+ 0xffffffffbff00000
  *                     |                              |
- *                     |            IOAPIC            | RW/--  PGSIZE
+ *                     |                              | RW/--  APIC_SIZE (1MB)
  *                     |                              |
- *  IOAPIC_BASE,  -->  +------------------------------+ 0xffffffffbfffe000
- *  KERN_DYN_TOP       |   Kernel Dynamic Mappings    |
+ *   KERN_DYN_TOP -->  +------------------------------+ 0xffffffffbfe00000
+ *                     |   Kernel Dynamic Mappings    |
  *                     |              .               |
  *                     :              .               :
  *                     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ RW/--
@@ -94,7 +100,7 @@ typedef unsigned long pde_t;
  *    USTACKTOP        |                              |
  *                     |      Normal User Stack       | RW/RW 256 * PGSIZE
  *                     |                              |
- *                     +------------------------------+ 0x00007f7fffbfb000
+ *                     +------------------------------+ 0x00007f7fffaff000
  *                     |                              |
  *                     |        Empty Memory          |
  *                     |                              |
@@ -111,6 +117,17 @@ typedef unsigned long pde_t;
  *                     |     Program Data & Heap      |
  *                     |                              |
  *                     +------------------------------+ 0x0000000000400000
+ *                     .                              .
+ *                     .                              .
+ *                     |~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~|
+ *                     |                              |
+ *                     |       ld.so (Dynamic)        |
+ *                     |                              |
+ * MMAP_LD_FIXED_VA    +------------------------------+ 0x0000000000100000
+ *                     |                              |
+ *                     |                              |
+ *                     |                              |
+ * MMAP_LOWEST_VA      +------------------------------+ 0x0000000000001000
  *                     |                              |
  *                     |       Empty Memory (*)       |
  *                     |                              |
@@ -134,8 +151,8 @@ typedef unsigned long pde_t;
  * the top 1 GB. */
 #define KERN_LOAD_ADDR  0xffffffffc0000000
 /* Static kernel mappings */
-#define LAPIC_BASE             (KERN_LOAD_ADDR - PGSIZE)
-#define IOAPIC_BASE            (LAPIC_BASE - PGSIZE)
+#define APIC_SIZE              0x100000
+#define IOAPIC_BASE            (KERN_LOAD_ADDR - APIC_SIZE)
 /* All arches must define this, which is the lower limit of their static
  * mappings, and where the dynamic mappings will start. */
 #define KERN_DYN_TOP   IOAPIC_BASE
@@ -157,7 +174,9 @@ typedef unsigned long pde_t;
 #define VPT_TOP                        0xffffff0000000000
 #define VPT                            (VPT_TOP - PML4_PTE_REACH)
 /* Helper to return the current outer pgdir via the VPT mapping. */
-#define VPD (VPT + (VPT >> 9) + (VPT >> 18) + (VPT >> 27))
+#define VPD (VPT + ((VPT & 0x0000ffffffffffff) >> 9) +                         \
+                   ((VPT & 0x0000ffffffffffff) >> 18) +                        \
+                   ((VPT & 0x0000ffffffffffff) >> 27))
 #define vpd VPD
 
 /* Top of the kernel virtual mapping area (KERNBASE) */
@@ -199,6 +218,7 @@ typedef unsigned long pde_t;
 #define PML3_SHIFT             30
 #define PML2_SHIFT             21
 #define PML1_SHIFT             12
+#define BITS_PER_PML   9
 
 /* PTE reach is the amount of VM an entry can map, either as a jumbo or as
  * further page tables.  I'd like to write these as shifts, but I can't please
@@ -214,11 +234,13 @@ typedef unsigned long pde_t;
 #define PML2_REACH             (PML3_PTE_REACH)
 #define PML1_REACH             (PML2_PTE_REACH)
 
-/* PMLx(la) gives the 9 bits specifying the la's entry in PML x */
-#define PML4(la)               (((uintptr_t)(la) >> PML4_SHIFT) & 0x1ff)
-#define PML3(la)               (((uintptr_t)(la) >> PML3_SHIFT) & 0x1ff)
-#define PML2(la)               (((uintptr_t)(la) >> PML2_SHIFT) & 0x1ff)
-#define PML1(la)               (((uintptr_t)(la) >> PML1_SHIFT) & 0x1ff)
+/* PMLx(la, shift) gives the 9 bits specifying the la's entry in the PML
+ * corresponding to shift.  PMLn(la) gives the 9 bits for PML4, etc. */
+#define PMLx(la, shift)        (((uintptr_t)(la) >> (shift)) & 0x1ff)
+#define PML4(la)               PMLx(la, PML4_SHIFT)
+#define PML3(la)               PMLx(la, PML3_SHIFT)
+#define PML2(la)               PMLx(la, PML2_SHIFT)
+#define PML1(la)               PMLx(la, PML1_SHIFT)
 
 /* Common kernel helpers */
 #define PGSHIFT                        PML1_SHIFT
@@ -226,31 +248,11 @@ typedef unsigned long pde_t;
 #define LA2PPN(la)             ((uintptr_t)(la) >> PGSHIFT)
 #define PTE2PPN(pte)   LA2PPN(pte)
 #define PGOFF(la)              ((uintptr_t)(la) & (PGSIZE - 1))
+#define NPTENTRIES             512
 
-/* construct PTE from PPN and flags */
-#define PTE(ppn, flags) ((ppn) << PGSHIFT | PGOFF(flags))
-
-/* construct linear address from indexes and offset */
-#define PGADDR(p4, p3, p2, p1, o) ((void*)(((p4) << PML4_SHIFT) |              \
-                                           ((p3) << PML3_SHIFT) |              \
-                                           ((p2) << PML2_SHIFT) |              \
-                                           ((p1) << PML1_SHIFT) |(o)))
-
-/* These are used in older code, referring to the outer-most page table */
-#define PDX(la)                        PML4(la)
-#define NPDENTRIES             512
 /* This is used in places (procinfo) meaning "size of smallest jumbo page" */
 #define PTSIZE PML2_PTE_REACH
 
-
-/* TODO: not sure if we'll need these - limited to 64bit code */
-/* this only gives us the L1 PML */
-#define PTX(la)                ((((uintptr_t) (la)) >> 12) & 0x1ff)
-#define JPGOFF(la)     (((uintptr_t) (la)) & 0x001FFFFF)
-#define NPTENTRIES             512
-#define JPGSIZE PTSIZE
-
-
 /* Page table/directory entry flags. */
 
 /* Some things to be careful of:  Global and PAT only apply to the last PTE in
@@ -269,17 +271,21 @@ typedef unsigned long pde_t;
 #define PTE_PAT                        0x080   /* Page attribute table */
 #define PTE_G                  0x100   /* Global Page */
 #define PTE_JPAT               0x800   /* Jumbo PAT */
+#define PTE_NOCACHE            (PTE_PWT | PTE_PCD)
 
 /* Permissions fields and common access modes.  These should be read as 'just
  * kernel or user too' and 'RO or RW'.  USER_RO means read-only for everyone. */
-#define PTE_PERM               (PTE_W | PTE_U)
-#define PTE_KERN_RW            PTE_W           // Kernel Read/Write
-#define PTE_KERN_RO            0               // Kernel Read-Only
-#define PTE_USER_RW            (PTE_W | PTE_U) // Kernel/User Read/Write
-#define PTE_USER_RO            PTE_U           // Kernel/User Read-Only
+#define PTE_PERM               (PTE_W | PTE_U | PTE_P)
+#define PTE_KERN_RW            (PTE_W | PTE_P)
+#define PTE_KERN_RO            PTE_P
+#define PTE_USER_RW            (PTE_W | PTE_U | PTE_P)
+#define PTE_USER_RO            (PTE_U | PTE_P)
+#define PTE_NONE               0
 
 /* The PTE/translation part of a PTE/virtual(linear) address.  It's used
- * frequently to be the page address of a virtual address. */
+ * frequently to be the page address of a virtual address.  Note this doesn't
+ * work on jumbo PTEs due to the reserved bits.  Jumbo's don't have a PTE_ADDR
+ * in them - just a base address of wherever they point. */
 #define PTE_ADDR(pte)  ((physaddr_t) (pte) & ~(PGSIZE - 1))
 /* More meaningful macro, same as PTE_ADDR */
 #define PG_ADDR(la)    ((uintptr_t)(la) & ~(PGSIZE - 1))
@@ -287,21 +293,25 @@ typedef unsigned long pde_t;
 /* we must guarantee that for any PTE, exactly one of the following is true */
 #define PAGE_PRESENT(pte) ((pte) & PTE_P)
 #define PAGE_UNMAPPED(pte) ((pte) == 0)
-#define PAGE_PAGED_OUT(pte) (!PAGE_PRESENT(pte) && !PAGE_UNMAPPED(pte))
+#define PAGE_PAGED_OUT(pte) (0)        /* Need to use an OS reserved PTE bit */
 
 
 /* **************************************** */
 /* Segmentation */
-// XXX 64b: these all need redone
 
-// Global descriptor numbers
-#define GD_NULL   0x00     // NULL descriptor
-#define GD_KT     0x08     // kernel text
-#define GD_KD     0x10     // kernel data
-#define GD_UT     0x18     // user text
-#define GD_UD     0x20     // user data
-#define GD_TSS    0x28     // Task segment selector
-#define GD_LDT    0x30     // local descriptor table
+/* Global descriptor numbers */
+#define GD_NULL                        0x00    /* NULL descriptor */
+#define GD_KT                  0x08    /* kernel text */
+#define GD_KD                  0x10    /* kernel data */
+/* syscall/sysret wants UD before UT, but KT before KD.  it really wants UT32,
+ * UD, UT64.  anyways... */
+#define GD_UD                  0x18    /* user data */
+#define GD_UT                  0x20    /* user text */
+#define GD_TSS                 0x28    /* Task segment selector */
+#define GD_TSS2                        0x30    /* Placeholder, TSS is 2-descriptors wide */
+/* These two aren't in the GDT yet (might never be) */
+#define GD_LDT                 0x38    /* Local descriptor table */
+#define GD_LDT2                        0x40    /* Placeholder */
 
 #ifdef __ASSEMBLER__
 
@@ -321,11 +331,23 @@ typedef unsigned long pde_t;
 
 /* 64 bit data segment.  These are pretty much completely ignored (except if we
  * use them for fs/gs, or compatibility mode */
-#define SEG_DATA_64                                                         \
-       .word 0, 0;                                                             \
+#define SEG_DATA_64(dpl)                                                    \
+       .word 0xffff, 0;                                                        \
        .byte 0;                                                                \
-       .byte 0x90;                                                             \
-       .word 0;
+       .byte (0x92 | ((dpl) << 5));                                            \
+       .byte 0x8f;                                                             \
+       .byte 0;
+
+/* System segments (TSS/LDT) are twice as long as usual (16 bytes). */
+#define SEG_SYS_64(type, base, lim, dpl)                                       \
+       .word ((lim) & 0xffff);                                                    \
+       .word ((base) & 0xffff);                                                   \
+       .byte (((base) >> 16) & 0xff);                                             \
+       .byte ((1 << 7) | ((dpl) << 5) | (type));                                  \
+       .byte (((1/*g*/) << 7) | (((lim) >> 16) & 0xf));                           \
+       .byte (((base) >> 24) & 0xff);                                             \
+       .quad ((base) >> 32);                                                      \
+       .quad 0;
 
 /* Default segment (32 bit style).  Would work for fs/gs, if needed */
 #define SEG(type, base, lim)                                                \
@@ -338,8 +360,7 @@ typedef unsigned long pde_t;
 
 #else  // not __ASSEMBLER__
 
-/* TODO: consider removing this, if we're just using one asm GDT */
-// Segment Descriptors
+/* Legacy Segment Descriptor (used for 64 bit data and code) */
 typedef struct Segdesc {
        unsigned sd_lim_15_0 : 16;  // Low bits of segment limit
        unsigned sd_base_15_0 : 16; // Low bits of segment base address
@@ -355,144 +376,106 @@ typedef struct Segdesc {
        unsigned sd_g : 1;          // Granularity: limit scaled by 4K when set
        unsigned sd_base_31_24 : 8; // High bits of segment base address
 } segdesc_t;
-// Null segment
-#define SEG_NULL       { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }
-// Segment that is loadable but faults when used
-#define SEG_FAULT      { 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0 }
-// Normal segment
-#define SEG(type, base, lim, dpl)                                                                      \
-{ ((lim) >> 12) & 0xffff, (base) & 0xffff, ((base) >> 16) & 0xff,      \
-    type, 1, dpl, 1, (unsigned) (lim) >> 28, 0, 0, 1, 1,                       \
-    (unsigned) (base) >> 24 }
-// System segment (LDT)
-#define SEG_SYS(type, base, lim, dpl)                                                                  \
-{ ((lim) >> 12) & 0xffff, (base) & 0xffff, ((base) >> 16) & 0xff,      \
-    type, 0, dpl, 1, (unsigned) (lim) >> 28, 0, 0, 1, 1,                       \
-    (unsigned) (base) >> 24 }
-
-#define SEG16(type, base, lim, dpl)                                                            \
-{ (lim) & 0xffff, (base) & 0xffff, ((base) >> 16) & 0xff,                      \
-    type, 1, dpl, 1, (unsigned) (lim) >> 16, 0, 0, 1, 0,                       \
-    (unsigned) (base) >> 24 }
-
-#define SEG16ROINIT(seg,type,base,lim,dpl) \
-       {\
-               (seg).sd_lim_15_0 = SINIT((lim) & 0xffff);\
-               (seg).sd_base_15_0 = SINIT((uintptr_t)(base)&0xffff);\
-               (seg).sd_base_23_16 = SINIT(((uintptr_t)(base)>>16)&0xff);\
-               (seg).sd_type = SINIT(type);\
-               (seg).sd_s = SINIT(1);\
-               (seg).sd_dpl = SINIT(dpl);\
-               (seg).sd_p = SINIT(1);\
-               (seg).sd_lim_19_16 = SINIT((unsigned)(lim)>>16);\
-               (seg).sd_avl = SINIT(0);\
-               (seg).sd_rsv1 = SINIT(0);\
-               (seg).sd_db = SINIT(1);\
-               (seg).sd_g = SINIT(0);\
-               (seg).sd_base_31_24 = SINIT((uintptr_t)(base)>> 24);\
-       }
-
-// Task state segment format (as described by the Pentium architecture book)
-typedef struct Taskstate {
-       uintptr_t ts_link;      // Old ts selector
-       uintptr_t ts_esp0;      // Stack pointers and segment selectors
-       uint16_t ts_ss0;        //   after an increase in privilege level
-       uint16_t ts_padding1;
-       uintptr_t ts_esp1;
-       uint16_t ts_ss1;
-       uint16_t ts_padding2;
-       uintptr_t ts_esp2;
-       uint16_t ts_ss2;
-       uint16_t ts_padding3;
-       physaddr_t ts_cr3;      // Page directory base
-       uintptr_t ts_eip;       // Saved state from last task switch
-       uintptr_t ts_eflags;
-       uintptr_t ts_eax;       // More saved state (registers)
-       uintptr_t ts_ecx;
-       uintptr_t ts_edx;
-       uintptr_t ts_ebx;
-       uintptr_t ts_esp;
-       uintptr_t ts_ebp;
-       uintptr_t ts_esi;
-       uintptr_t ts_edi;
-       uint16_t ts_es;         // Even more saved state (segment selectors)
-       uint16_t ts_padding4;
-       uint16_t ts_cs;
-       uint16_t ts_padding5;
-       uint16_t ts_ss;
-       uint16_t ts_padding6;
-       uint16_t ts_ds;
-       uint16_t ts_padding7;
-       uint16_t ts_fs;
-       uint16_t ts_padding8;
-       uint16_t ts_gs;
-       uint16_t ts_padding9;
-       uint16_t ts_ldt;
-       uint16_t ts_padding10;
-       uint16_t ts_t;          // Trap on task switch
-       uint16_t ts_iomb;       // I/O map base address
-} taskstate_t;
-
-// Gate descriptors for interrupts and traps
+
+/* Lower half is similar (more ignored, etc)  to a legacy system descriptor */
+struct x86_sysseg64 {
+       unsigned sd_lim_15_0 : 16;      /* Low bits of segment limit */
+       unsigned sd_base_15_0 : 16;     /* Low bits of segment base address */
+       unsigned sd_base_23_16 : 8;     /* Middle bits of segment base address */
+       unsigned sd_type : 4;           /* Segment type (see STS_ constants) */
+       unsigned sd_s : 1;                      /* 0 = system, 1 = application */
+       unsigned sd_dpl : 2;            /* Descriptor Privilege Level */
+       unsigned sd_p : 1;                      /* Present */
+       unsigned sd_lim_19_16 : 4;      /* High bits of segment limit */
+       unsigned sd_avl : 1;            /* Unused (available for software use) */
+       unsigned sd_rsv2 : 2;           /* Reserved */
+       unsigned sd_g : 1;                      /* Granularity: limit scaled by 4K when set */
+       unsigned sd_base_31_24 : 8;     /* 24-31 bits of segment base address */
+       unsigned sd_base_63_32;         /* top 32 bits of the base */
+       unsigned sd_reserved;           /* some parts must be zero, just zero it all */
+};
+typedef struct x86_sysseg64 syssegdesc_t;
+
+/* G(ranularity) determines if the limit is shifted */
+#define __SEG_SYS64(type, base, lim, dpl, g)                                   \
+{ ((lim) >> ((g) * 12)) & 0xffff, (base) & 0xffff, ((base) >> 16) & 0xff,      \
+    type, 0, dpl, 1, (unsigned) (lim) >> 28, 0, 0, (g),                        \
+    ((unsigned) (base) >> 24) & 0xff,                                          \
+    ((unsigned long) (base) >> 32), 0 }
+
+/* Normal system segment descriptor (LDT or TSS). (limit is scaled by 4k) */
+#define SEG_SYS64(type, base, lim, dpl)                                        \
+        __SEG_SYS64(type, base, lim, dpl, 1)
+
+/* Smaller system segment descriptor (LDT or TSS). */
+#define SEG_SYS64_SMALL(type, base, lim, dpl)                                  \
+        __SEG_SYS64(type, base, lim, dpl, 0)
+
+#define SEG_SYS_SMALL(type, base, lim, dpl) \
+        SEG_SYS64_SMALL(type, base, lim, dpl)
+
+/* 64 bit task state segment (AMD 2:12.2.5) */
+typedef struct taskstate {
+       uint32_t                                        ts_rsv1;        /* reserved / ignored */
+       uint64_t                                        ts_rsp0;        /* stack ptr in ring 0 */
+       uint64_t                                        ts_rsp1;        /* stack ptr in ring 1 */
+       uint64_t                                        ts_rsp2;        /* stack ptr in ring 2 */
+       uint64_t                                        ts_rsv2;        /* reserved / ignored */
+       uint64_t                                        ts_ist1;        /* IST stacks: unconditional rsp */
+       uint64_t                                        ts_ist2;        /* check AMD 2:8.9.4 for info */
+       uint64_t                                        ts_ist3;
+       uint64_t                                        ts_ist4;
+       uint64_t                                        ts_ist5;
+       uint64_t                                        ts_ist6;
+       uint64_t                                        ts_ist7;
+       uint64_t                                        ts_rsv3;        /* reserved / ignored */
+       uint16_t                                        ts_rsv4;        /* reserved / ignored */
+       uint16_t                                        ts_iobm;        /* IO base map (offset) */
+} __attribute__((packed)) taskstate_t;
+
+/* 64 bit gate descriptors for interrupts and traps */
 typedef struct Gatedesc {
-       unsigned gd_off_15_0 : 16;   // low 16 bits of offset in segment
-       unsigned gd_ss : 16;         // segment selector
-       unsigned gd_args : 5;        // # args, 0 for interrupt/trap gates
-       unsigned gd_rsv1 : 3;        // reserved(should be zero I guess)
-       unsigned gd_type : 4;        // type(STS_{TG,IG32,TG32})
-       unsigned gd_s : 1;           // must be 0 (system)
-       unsigned gd_dpl : 2;         // DPL - highest ring allowed to use this
-       unsigned gd_p : 1;           // Present
-       unsigned gd_off_31_16 : 16;  // high bits of offset in segment
+       unsigned gd_off_15_0 : 16;      /* low 16 bits of offset in segment */
+       unsigned gd_ss : 16;            /* segment selector */
+       unsigned gd_ist : 3;            /* interrupt stack table selector (0 = none) */
+       unsigned gd_rsv1 : 5;           /* ignored */
+       unsigned gd_type : 4;           /* type(STS_{TG,IG32,TG32}) */
+       unsigned gd_s : 1;                      /* must be 0 (system) */
+       unsigned gd_dpl : 2;            /* DPL - highest ring allowed to use this */
+       unsigned gd_p : 1;                      /* Present */
+       unsigned gd_off_31_16 : 16;     /* 16-31 bits of offset in segment */
+       unsigned gd_off_63_32;          /* top 32 bits of offset */
+       unsigned gd_rsv2;                       /* reserved / unsused */
 } gatedesc_t;
 
-// Set up a normal interrupt/trap gate descriptor.
-// - istrap: 1 for a trap (= exception) gate, 0 for an interrupt gate.
-//   - interrupt gates automatically disable interrupts (cli)
-// - sel: Code segment selector for interrupt/trap handler
-// - off: Offset in code segment for interrupt/trap handler
-// - dpl: Descriptor Privilege Level -
-//       the privilege level required for software to invoke
-//       this interrupt/trap gate explicitly using an int instruction.
-#define SETGATE(gate, istrap, sel, off, dpl)                   \
-{                                                              \
-       (gate).gd_off_15_0 = (uintptr_t) (off) & 0xffff;                \
-       (gate).gd_ss = (sel);                                   \
-       (gate).gd_args = 0;                                     \
-       (gate).gd_rsv1 = 0;                                     \
-       (gate).gd_type = (istrap) ? STS_TG32 : STS_IG32;        \
-       (gate).gd_s = 0;                                        \
-       (gate).gd_dpl = (dpl);                                  \
-       (gate).gd_p = 1;                                        \
-       (gate).gd_off_31_16 = (uintptr_t) (off) >> 16;          \
-}
-
-#define ROSETGATE(gate, istrap, sel, off, dpl)                 \
-{                                                              \
-       (gate).gd_off_15_0 = SINIT((uintptr_t) (off) & 0xffff);         \
-       (gate).gd_ss = SINIT(sel);                                      \
-       (gate).gd_args = SINIT(0);                                      \
-       (gate).gd_rsv1 = SINIT(0);                                      \
-       (gate).gd_type = SINIT((istrap) ? STS_TG32 : STS_IG32); \
-       (gate).gd_s = SINIT(0);                                 \
-       (gate).gd_dpl = SINIT(dpl);                                     \
-       (gate).gd_p = SINIT(1);                                 \
-       (gate).gd_off_31_16 = SINIT((uintptr_t) (off) >> 16);           \
+/* Set up an IST-capable 64 bit interrupt/trap gate descriptor.  IST selects a
+ * stack pointer from the interrupt-stack table (in TSS) that will be loaded
+ * unconditionally when we hit this gate  - regardless of privelege change. */
+#define SETGATE64(gate, istrap, sel, off, dpl, ist)                            \
+{                                                                              \
+       (gate).gd_off_15_0 = (uintptr_t) (off) & 0xffff;                           \
+       (gate).gd_ss = (sel);                                                      \
+       (gate).gd_ist = (ist);                                                     \
+       (gate).gd_rsv1 = 0;                                                        \
+       (gate).gd_type = (istrap) ? STS_TG32 : STS_IG32;                           \
+       (gate).gd_s = 0;                                                           \
+       (gate).gd_dpl = (dpl);                                                     \
+       (gate).gd_p = 1;                                                           \
+       (gate).gd_off_31_16 = (uintptr_t) (off) >> 16;                             \
+       (gate).gd_off_63_32 = (uintptr_t) (off) >> 32;                             \
+       (gate).gd_rsv2 = 0;                                                        \
 }
 
-// Set up a call gate descriptor.
-#define SETCALLGATE(gate, ss, off, dpl)                        \
-{                                                              \
-       (gate).gd_off_15_0 = (uintptr_t) (off) & 0xffff;                \
-       (gate).gd_ss = (ss);                                    \
-       (gate).gd_args = 0;                                     \
-       (gate).gd_rsv1 = 0;                                     \
-       (gate).gd_type = STS_CG32;                              \
-       (gate).gd_s = 0;                                        \
-       (gate).gd_dpl = (dpl);                                  \
-       (gate).gd_p = 1;                                        \
-       (gate).gd_off_31_16 = (uintptr_t) (off) >> 16;          \
-}
+/* Set up a normal, 64 bit interrupt/trap gate descriptor.
+ * - istrap: 1 for a trap (= exception) gate, 0 for an interrupt gate.
+ *   - interrupt gates automatically disable interrupts (cli)
+ * - sel: Code segment selector for interrupt/trap handler
+ * - off: Offset in code segment for interrupt/trap handler (address)
+ * - dpl: Descriptor Privilege Level -
+ *       the privilege level required for software to invoke
+ *       this interrupt/trap gate explicitly using an int instruction. */
+#define SETGATE(gate, istrap, sel, off, dpl)                                   \
+        SETGATE64(gate, istrap, sel, off, dpl, 0)
 
 // Pseudo-descriptors used for LGDT, LLDT and LIDT instructions.
 typedef struct Pseudodesc {
@@ -500,9 +483,6 @@ typedef struct Pseudodesc {
        uintptr_t pd_base;              // Base address
 } __attribute__ ((packed)) pseudodesc_t;
 
-extern segdesc_t (COUNT(SEG_COUNT) RO gdt)[];
-extern pseudodesc_t gdt_pd;
-
 #endif /* !__ASSEMBLER__ */
 
 // Application segment type bits
@@ -513,21 +493,18 @@ extern pseudodesc_t gdt_pd;
 #define STA_R          0x2         // Readable (executable segments)
 #define STA_A          0x1         // Accessed
 
-// System segment type bits
-#define STS_T16A       0x1         // Available 16-bit TSS
-#define STS_LDT                0x2         // Local Descriptor Table
-#define STS_T16B       0x3         // Busy 16-bit TSS
-#define STS_CG16       0x4         // 16-bit Call Gate
-#define STS_TG         0x5         // Task Gate / Coum Transmitions
-#define STS_IG16       0x6         // 16-bit Interrupt Gate
-#define STS_TG16       0x7         // 16-bit Trap Gate
-#define STS_T32A       0x9         // Available 32-bit TSS
-#define STS_T32B       0xB         // Busy 32-bit TSS
-#define STS_CG32       0xC         // 32-bit Call Gate
-#define STS_IG32       0xE         // 32-bit Interrupt Gate
-#define STS_TG32       0xF         // 32-bit Trap Gate
-
-#define SEG_COUNT      7               // Number of segments in the steady state
+/* System segment type bits.  All other types are reserved/illegal.  The '32' is
+ * mostly a legacy naming - the bits work for both 64 and 32. */
+#define STS_LDT                0x2             /* 64-bit Local Descriptor Table  */
+#define STS_T32A       0x9             /* Available 64-bit TSS */
+#define STS_T32B       0xB             /* Busy 64-bit TSS */
+#define STS_CG32       0xC             /* 64-bit Call Gate */
+#define STS_IG32       0xE             /* 64-bit Interrupt Gate */
+#define STS_TG32       0xF             /* 64-bit Trap Gate */
+
+#define SEG_COUNT      7               /* Number of GDT segments */
+/* TODO: Probably won't use this */
 #define LDT_SIZE       (8192 * sizeof(segdesc_t))
 
-#endif /* ROS_INC_ARCH_MMU64_H */
+/* TLS 'syscall', coupled to trapentry64.S.  Needed a non-canon 'addr' */
+#define FASTCALL_SETFSBASE 0xf0f0000000000001