x86: Remove the POLL_CONSOLE hack
[akaros.git] / kern / arch / x86 / trap.h
index bf29eed..ab28349 100644 (file)
@@ -1,7 +1,10 @@
-#ifndef ROS_KERN_ARCH_TRAP_H
+#pragma once
+
 #define ROS_KERN_ARCH_TRAP_H
 
-#include "msr-index.h"
+#include <ros/arch/msr-index.h>
+#include <ros/errno.h>
+#include <arch/fixup.h>
 
 #define NUM_IRQS                                       256
 
@@ -13,7 +16,7 @@
 #define T_OFLOW      4         // overflow
 #define T_BOUND      5         // bounds check
 #define T_ILLOP      6         // illegal opcode
-#define T_DEVICE     7         // device not available 
+#define T_DEVICE     7         // device not available
 #define T_DBLFLT     8         // double fault
 /* #define T_COPROC  9 */      // reserved (not generated by recent processors)
 #define T_TSS       10         // invalid task switch segment
 #define MaxIrqPIC                              15
 #define MaxIdtPIC                              (IdtPIC + MaxIrqPIC)
 
-/* 48-63 are LAPIC vectors */
-#define IdtLAPIC                               (IdtPIC + 16)
-#define IdtLAPIC_LINT0                 (IdtLAPIC + 0)
-#define IdtLAPIC_LINT1                 (IdtLAPIC + 1)
-#define IdtLAPIC_TIMER                 (IdtLAPIC + 2)
-#define IdtLAPIC_ERROR                 (IdtLAPIC + 3)
-#define IdtLAPIC_PCINT                 (IdtLAPIC + 4)
-/* Plan 9 apic note: the spurious vector number must have bits 3-0 0x0f
- * unless the Extended Spurious Vector Enable bit is set in the
- * HyperTransport Transaction Control register.  Plan 9 used 63 (0x3f), but
- * 55 should have worked too (0x37). */
-#define IdtLAPIC_SPURIOUS              (IdtLAPIC + 15) /* Aka 63, 0x3f */
-#define MaxIdtLAPIC                            (IdtLAPIC + 15)
-
-/* T_SYSCALL is defined by the following include (64) */
+/* T_SYSCALL is defined by the following include (48) */
 #include <ros/arch/syscall.h>
 
-/* 65-229 are IOAPIC routing vectors (from IOAPIC to LAPIC) */
+/* 49-223 are IOAPIC routing vectors (from IOAPIC to LAPIC) */
 #define IdtIOAPIC                              (T_SYSCALL + 1)
-#define MaxIdtIOAPIC                   229
-/* 230-255 are OS IPI vectors */
-#define IdtMAX                                 255
+#define MaxIdtIOAPIC                   223
+
+/* 224-239 are OS IPI vectors (0xe0-0xef) */
+/* smp_call_function IPIs, keep in sync with NUM_HANDLER_WRAPPERS.
+ * SMP_CALL0 needs to be 16-aligned (we mask in x86/trap.c).  If you move these,
+ * also change INIT_HANDLER_WRAPPER */
+#define I_SMP_CALL0                            224
+#define I_SMP_CALL1                            (I_SMP_CALL0 + 1)
+#define I_SMP_CALL2                            (I_SMP_CALL0 + 2)
+#define I_SMP_CALL3                            (I_SMP_CALL0 + 3)
+#define I_SMP_CALL4                            (I_SMP_CALL0 + 4)
+#define I_SMP_CALL_LAST                        I_SMP_CALL4
+#define I_TESTING                              237     /* Testing IPI (used in testing.c) */
+#define I_POKE_CORE                            238
+#define I_KERNEL_MSG                   239
+
+/* 240-255 are LAPIC vectors (0xf0-0xff), hightest priority class */
+#define IdtLAPIC                               240
+#define IdtLAPIC_TIMER                 (IdtLAPIC + 0)
+#define IdtLAPIC_THERMAL               (IdtLAPIC + 1)
+#define IdtLAPIC_PCINT                 (IdtLAPIC + 2)
+#define IdtLAPIC_LINT0                 (IdtLAPIC + 3)
+#define IdtLAPIC_LINT1                 (IdtLAPIC + 4)
+#define IdtLAPIC_ERROR                 (IdtLAPIC + 5)
+/* Plan 9 apic note: the spurious vector number must have bits 3-0 0x0f
+ * unless the Extended Spurious Vector Enable bit is set in the
+ * HyperTransport Transaction Control register.  On some intel machines, those
+ * bits are hardwired to 1s (SDM 3-10.9). */
+#define IdtLAPIC_SPURIOUS              (IdtLAPIC + 0xf) /* Aka 255, 0xff */
+#define MaxIdtLAPIC                            (IdtLAPIC + 0xf)
 
+#define IdtMAX                                 255
 
 #define T_DEFAULT   0x0000beef         // catchall
 
 #define FP_CW_RC_MASK                  (3 << FP_CW_RC_SHIFT)
 #define FP_CW_IC                               (1 << 12)
 
-/* IPIs */
-/* Testing IPI (used in testing.c) */
-#define I_TESTING              230
-/* smp_call_function IPIs, keep in sync with NUM_HANDLER_WRAPPERS (and < 16)
- * it's important that this begins with 0xf0.  check i386/trap.c for details. */
-#define I_SMP_CALL0    0xf0 // 240
-#define I_SMP_CALL1    0xf1
-#define I_SMP_CALL2    0xf2
-#define I_SMP_CALL3    0xf3
-#define I_SMP_CALL4    0xf4
-#define I_SMP_CALL_LAST I_SMP_CALL4
-/* Direct/Hardwired IPIs.  Hardwired in trapentry.S */
-#define I_KERNEL_MSG   255
-
 #ifndef __ASSEMBLER__
 
 #ifndef ROS_KERN_TRAP_H
 #include <ros/trapframe.h>
 #include <arch/pci.h>
 #include <arch/pic.h>
-#include <arch/coreid.h>
+#include <arch/topology.h>
+#include <cpu_feat.h>
 #include <arch/io.h>
+#include <stdio.h>
 
 struct irq_handler {
        struct irq_handler *next;
        void (*isr)(struct hw_trapframe *hw_tf, void *data);
        void *data;
+       int apic_vector;
 
        /* all handlers in the chain need to have the same func pointers.  we only
         * really use the first one, and the latter are to catch bugs.  also, we
         * won't be doing a lot of IRQ line sharing */
        bool (*check_spurious)(int);
        void (*eoi)(int);
-       void (*mask)(int);
-       void (*unmask)(int);
-       /* TODO: add a helper to (re)route irqs? */
+       void (*mask)(struct irq_handler *irq_h, int vec);
+       void (*unmask)(struct irq_handler *irq_h, int vec);
+       void (*route_irq)(struct irq_handler *irq_h, int vec, int dest);
 
        int tbdf;
        int dev_irq;
-       int apic_vector;
 
+       void *dev_private;
        char *type;
        #define IRQ_NAME_LEN 26
        char name[IRQ_NAME_LEN];
 };
 
-static bool idt_vec_is_pic(int vec)
-{
-       return (IdtPIC <= vec) && (vec <= MaxIdtPIC);
-}
-
-static bool idt_vec_is_lapic(int vec)
-{
-       return (IdtLAPIC <= vec) && (vec <= MaxIdtLAPIC);
-}
-
 /* The kernel's interrupt descriptor table */
 extern gatedesc_t idt[];
 extern pseudodesc_t idt_pd;
 extern taskstate_t ts;
-/* Mapping of irq -> PCI device (TODO: make this PCI-agnostic) */
-extern struct pci_device *irq_pci_map[NUM_IRQS];
-int bus_irq_enable(struct irq_handler *irq_h); /* ioapic.c */
+int bus_irq_setup(struct irq_handler *irq_h);  /* ioapic.c */
 extern const char *x86_trapname(int trapno);
 extern void sysenter_handler(void);
-void backtrace_kframe(struct hw_trapframe *hw_tf);
 
 /* Defined and set up in in arch/init.c, used for XMM initialization */
 extern struct ancillary_state x86_default_fpu;
 
 static inline void save_fp_state(struct ancillary_state *silly)
 {
-       asm volatile("fxsave %0" : : "m"(*silly));
+       uint64_t x86_default_xcr0 = __proc_global_info.x86_default_xcr0;
+       uint32_t eax, edx;
+
+       /* PLEASE NOTE:
+        * AMD CPUs ignore the FOP/FIP/FDP fields when there is
+        * no pending exception. When you are on AMD, we zero these fields in the
+        * ancillary_state argument before saving. This way, if you are on AMD and
+        * re-using an ancillary_state memory region, an old save's information
+        * won't leak into your new data. The side-effect of this is that you can't
+        * trust these fields to report accurate information on AMD unless an
+        * exception was pending. Granted, AMD says that only exception handlers
+        * should care about FOP/FIP/FDP, so that's probably okay.
+        *
+        * You should also note that on newer Intel 64 processors, while the value
+        * of the FOP is always saved and restored, it contains the opcode of the
+        * most recent x87 FPU instruction that triggered an unmasked exception,
+        * rather than simply the most recent opcode. Some older Xeons and P4s had
+        * the fopcode compatibility mode feature, which you could use to make the
+        * FOP update on every x87 non-control instruction, but that has been
+        * eliminated in newer hardware.
+        *
+        */
+       if (cpu_has_feat(CPU_FEAT_X86_VENDOR_AMD)) {
+               silly->fp_head_64d.fop      = 0x0;
+               silly->fp_head_64d.fpu_ip   = 0x0;
+               silly->fp_head_64d.cs       = 0x0;
+               silly->fp_head_64d.padding1 = 0x0; // padding1 is FIP or rsvd, proc dep.
+               silly->fp_head_64d.fpu_dp   = 0x0;
+               silly->fp_head_64d.ds       = 0x0;
+               silly->fp_head_64d.padding2 = 0x0; // padding2 is FDP or rsvd, proc dep.
+       }
+
+
+       if (cpu_has_feat(CPU_FEAT_X86_XSAVEOPT)) {
+               edx = x86_default_xcr0 >> 32;
+               eax = x86_default_xcr0;
+               asm volatile("xsaveopt64 %0" : : "m"(*silly), "a"(eax), "d"(edx));
+       } else if (cpu_has_feat(CPU_FEAT_X86_XSAVE)) {
+               edx = x86_default_xcr0 >> 32;
+               eax = x86_default_xcr0;
+               asm volatile("xsave64 %0" : : "m"(*silly), "a"(eax), "d"(edx));
+       } else {
+               asm volatile("fxsave64 %0" : : "m"(*silly));
+       }
 }
 
-/* TODO: this can trigger a GP fault if MXCSR reserved bits are set.  Callers
- * will need to handle intercepting the kernel fault. */
+static inline void init_fp_state(void);
 static inline void restore_fp_state(struct ancillary_state *silly)
 {
-       asm volatile("fxrstor %0" : : "m"(*silly));
+       uint64_t x86_default_xcr0 = __proc_global_info.x86_default_xcr0;
+       int err = 0;
+       uint32_t eax, edx;
+
+       /*
+        * Since AMD CPUs ignore the FOP/FIP/FDP fields when there is
+        * no pending exception, we clear those fields before restoring
+        * when we are both on AMD and there is no pending exception in
+        * the ancillary_state argument to restore_fp_state.
+        * If there is a pending exception in the ancillary_state,
+        * these fields will be written to the FPU upon executing
+        * a restore instruction, and there is nothing to worry about.
+        *
+        * See CVE-2006-1056 and CVE-2013-2076 on cve.mitre.org.
+        *
+        * We check for a pending exception by checking FSW.ES (bit 7)
+        *
+        * FNINIT clears FIP and FDP and, even though it is technically a
+        * control instruction, it clears FOP because it is initializing the FPU.
+        *
+        * NOTE: This might not be the most efficient way to do things, and
+        *       could be an optimization target for context switch performance
+        *       on AMD processors in the future.
+        */
+       if (!(silly->fp_head_64d.fsw & 0x80)
+               && cpu_has_feat(CPU_FEAT_X86_VENDOR_AMD))
+               asm volatile ("fninit;");
+
+       if (cpu_has_feat(CPU_FEAT_X86_XSAVE)) {
+               edx = x86_default_xcr0 >> 32;
+               eax = x86_default_xcr0;
+               asm volatile(ASM_STAC               ";"
+                            "1: xrstor64 %1         ;"
+                            "2: " ASM_CLAC "        ;"
+                            ".section .fixup, \"ax\";"
+                            "3: mov %4, %0          ;"
+                            "   jmp 2b              ;"
+                            ".previous              ;"
+                            _ASM_EXTABLE(1b, 3b)
+                            : "=r" (err)
+                            : "m"(*silly), "a"(eax), "d"(edx),
+                              "i" (-EINVAL), "0" (err));
+       } else {
+               asm volatile(ASM_STAC               ";"
+                            "1: fxrstor64 %1         ;"
+                            "2: " ASM_CLAC "        ;"
+                            ".section .fixup, \"ax\";"
+                            "3: mov %2, %0          ;"
+                            "   jmp 2b              ;"
+                            ".previous              ;"
+                            _ASM_EXTABLE(1b, 3b)
+                            : "=r" (err)
+                            : "m"(*silly),
+                              "i" (-EINVAL), "0" (err));
+       }
+
+
+       if (err) {
+               printk("Error restoring fp state!");
+               printk("Likely a bad ancillary_state argument.\n");
+               printk("Re-initializing fp state to default due to error.\n");
+               init_fp_state();
+       }
 }
 
-/* A regular fninit will only initialize the x87 header part of the FPU, not the
- * st(n) (MMX) registers, the XMM registers, or the MXCSR state.  So to init,
- * we'll just keep around a copy of the default FPU state, which we grabbed
- * during boot, and can copy that over.
+/* A regular fninit only initializes the control, status, tag, ip,
+ * and data pointer registers. Since it leaves the data registers,
+ * MXCSR, etc. unchanged, we use init_fp_state to restore a default
+ * state that we save at boot time.
  *
- * Alternatively, we can fninit, ldmxcsr with the default value, and 0 out all
- * of the registers manually. */
+ * If you're looking for a workout, you could also fninit, ldmxcsr with
+ * a default value, and 0 all the registers by hand.
+ */
 static inline void init_fp_state(void)
 {
        restore_fp_state(&x86_default_fpu);
@@ -197,14 +295,16 @@ set_stack_pointer(uintptr_t sp)
        asm volatile("mov %0,%%"X86_REG_SP"" : : "r"(sp) : "memory", X86_REG_SP);
 }
 
+static inline void __attribute__((always_inline))
+set_frame_pointer(uintptr_t fp)
+{
+       /* note we can't list BP as a clobber - the compiler will flip out.  makes
+        * me wonder if clobbering SP above makes a difference (probably not) */
+       asm volatile("mov %0,%%"X86_REG_BP"" : : "r"(fp) : "memory");
+}
+
 extern segdesc_t *gdt;
 
-#ifdef CONFIG_X86_64
 #include <arch/trap64.h>
-#else
-#include <arch/trap32.h>
-#endif
 
 #endif /* !__ASSEMBLER__ */
-
-#endif /* !ROS_INC_ARCH_TRAP_H */