blob: 051b9c636133926a285c714e21856ee36f75d0fd [file] [log] [blame]
#pragma once
#define ROS_KERN_ARCH_TRAP_H
#include <ros/arch/msr-index.h>
#include <ros/errno.h>
#include <arch/fixup.h>
#define NUM_IRQS 256
/* 0-31 are hardware traps */
#define T_DIVIDE 0 // divide error
#define T_DEBUG 1 // debug exception
#define T_NMI 2 // non-maskable interrupt
#define T_BRKPT 3 // breakpoint
#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_DBLFLT 8 // double fault
/* #define T_COPROC 9 */ // reserved (not generated by recent processors)
#define T_TSS 10 // invalid task switch segment
#define T_SEGNP 11 // segment not present
#define T_STACK 12 // stack exception
#define T_GPFLT 13 // genernal protection fault
#define T_PGFLT 14 // page fault
/* #define T_RES 15 */ // reserved
#define T_FPERR 16 // floating point error
#define T_ALIGN 17 // aligment check
#define T_MCHK 18 // machine check
#define T_SIMDERR 19 // SIMD floating point error
/* 32-47 are PIC/8259 IRQ vectors */
#define IdtPIC 32
#define IrqCLOCK 0
#define IrqKBD 1
#define IrqUART1 3
#define IrqUART0 4
#define IrqPCMCIA 5
#define IrqFLOPPY 6
#define IrqLPT 7
#define IrqAUX 12 /* PS/2 port */
#define IrqIRQ13 13 /* coprocessor on 386 */
#define IrqATA0 14
#define IrqATA1 15
#define MaxIrqPIC 15
#define MaxIdtPIC (IdtPIC + MaxIrqPIC)
/* T_SYSCALL is defined by the following include (48) */
#include <ros/arch/syscall.h>
/* 49-223 are IOAPIC routing vectors (from IOAPIC to LAPIC) */
#define IdtIOAPIC (T_SYSCALL + 1)
#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 236 /* Testing IPI (used in testing.c) */
#define I_POKE_GUEST 237
#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
/* Floating point constants */
#define FP_EXCP_IE (1 << 0) /* invalid op */
#define FP_EXCP_DE (1 << 1) /* denormalized op */
#define FP_EXCP_ZE (1 << 2) /* div by zero */
#define FP_EXCP_OE (1 << 3) /* numeric overflow */
#define FP_EXCP_UE (1 << 4) /* numeric underflow */
#define FP_EXCP_PE (1 << 5) /* precision */
#define FP_SW_SF (1 << 6) /* stack fault */
#define FP_SW_ES (1 << 7) /* error summary status */
#define FP_SW_C0 (1 << 8) /* condition codes */
#define FP_SW_C1 (1 << 9)
#define FP_SW_C2 (1 << 10)
#define FP_SW_C3 (1 << 14)
#define FP_CW_TOP_SHIFT (11)
#define FP_CW_TOP_MASK (7 << FP_CW_TOP_SHIFT)
#define FP_CW_PC_SHIFT (8)
#define FP_CW_PC_MASK (3 << FP_CW_PC_SHIFT)
#define FP_CW_RC_SHIFT (10)
#define FP_CW_RC_MASK (3 << FP_CW_RC_SHIFT)
#define FP_CW_IC (1 << 12)
#ifndef __ASSEMBLER__
#ifndef ROS_KERN_TRAP_H
#error "Do not include include arch/trap.h directly"
#endif
#include <ros/common.h>
#include <arch/mmu.h>
#include <ros/trapframe.h>
#include <arch/pci.h>
#include <arch/pic.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)(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;
void *dev_private;
char *type;
#define IRQ_NAME_LEN 26
char name[IRQ_NAME_LEN];
};
/* The kernel's interrupt descriptor table */
extern gatedesc_t idt[];
extern pseudodesc_t idt_pd;
extern taskstate_t ts;
int bus_irq_setup(struct irq_handler *irq_h); /* ioapic.c */
extern const char *x86_trapname(int trapno);
extern void sysenter_handler(void);
/* 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)
{
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;
// padding1 is FIP or rsvd, proc dep.
silly->fp_head_64d.padding1 = 0x0;
silly->fp_head_64d.fpu_dp = 0x0;
silly->fp_head_64d.ds = 0x0;
// padding2 is FDP or rsvd, proc dep.
silly->fp_head_64d.padding2 = 0x0;
}
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));
}
}
static inline void init_fp_state(void);
static inline void restore_fp_state(struct ancillary_state *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!\n");
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 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.
*
* 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);
}
static inline void __attribute__((always_inline))
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;
#include <arch/trap64.h>
#endif /* !__ASSEMBLER__ */