diff options
Diffstat (limited to 'src/bits64/irq.rs')
-rw-r--r-- | src/bits64/irq.rs | 340 |
1 files changed, 340 insertions, 0 deletions
diff --git a/src/bits64/irq.rs b/src/bits64/irq.rs new file mode 100644 index 0000000..a1b6d12 --- /dev/null +++ b/src/bits64/irq.rs @@ -0,0 +1,340 @@ +//! Interrupt description and set-up code. + +use core::fmt; +use super::paging::VAddr; + +/// x86 Exception description (see also Intel Vol. 3a Chapter 6). +#[derive(Debug)] +pub struct InterruptDescription { + pub vector: u8, + pub mnemonic: &'static str, + pub description: &'static str, + pub irqtype: &'static str, + pub source: &'static str, +} + +impl fmt::Display for InterruptDescription { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, + "{} ({}, vec={}) {}", + self.mnemonic, + self.irqtype, + self.vector, + self.description) + } +} + + +/// x86 External Interrupts (1-16). +pub static EXCEPTIONS: [InterruptDescription; 21] = [InterruptDescription { + vector: 0, + mnemonic: "#DE", + description: "Divide Error", + irqtype: "Fault", + source: "DIV and IDIV instructions.", + }, + InterruptDescription { + vector: 1, + mnemonic: "#DB", + description: "Debug", + irqtype: "Fault/ Trap", + source: "Debug condition", + }, + InterruptDescription { + vector: 2, + mnemonic: "NMI", + description: "Nonmaskable Interrupt", + irqtype: "Interrupt", + source: "Nonmaskable external interrupt.", + }, + InterruptDescription { + vector: 3, + mnemonic: "#BP", + description: "Breakpoint", + irqtype: "Trap", + source: "INT 3 instruction.", + }, + InterruptDescription { + vector: 4, + mnemonic: "#OF", + description: "Overflow", + irqtype: "Trap", + source: "INTO instruction.", + }, + InterruptDescription { + vector: 5, + mnemonic: "#BR", + description: "BOUND Range Exceeded", + irqtype: "Fault", + source: "BOUND instruction.", + }, + InterruptDescription { + vector: 6, + mnemonic: "#UD", + description: "Invalid Opcode (Undefined \ + Opcode)", + irqtype: "Fault", + source: "UD2 instruction or reserved \ + opcode.", + }, + InterruptDescription { + vector: 7, + mnemonic: "#NM", + description: "Device Not Available (No \ + Math Coprocessor)", + irqtype: "Fault", + source: "Floating-point or WAIT/FWAIT \ + instruction.", + }, + InterruptDescription { + vector: 8, + mnemonic: "#DF", + description: "Double Fault", + irqtype: "Abort", + source: "Any instruction that can \ + generate an exception, an NMI, \ + or an INTR.", + }, + InterruptDescription { + vector: 9, + mnemonic: "", + description: "Coprocessor Segment Overrun", + irqtype: "Fault", + source: "Floating-point instruction.", + }, + InterruptDescription { + vector: 10, + mnemonic: "#TS", + description: "Invalid TSS", + irqtype: "Fault", + source: "Task switch or TSS access.", + }, + InterruptDescription { + vector: 11, + mnemonic: "#NP", + description: "Segment Not Present", + irqtype: "Fault", + source: "Loading segment registers or \ + accessing system segments.", + }, + InterruptDescription { + vector: 12, + mnemonic: "#SS", + description: "Stack-Segment Fault", + irqtype: "Fault", + source: "Stack operations and SS register \ + loads.", + }, + InterruptDescription { + vector: 13, + mnemonic: "#GP", + description: "General Protection", + irqtype: "Fault", + source: "Any memory reference and other \ + protection checks.", + }, + InterruptDescription { + vector: 14, + mnemonic: "#PF", + description: "Page Fault", + irqtype: "Fault", + source: "Any memory reference.", + }, + InterruptDescription { + vector: 15, + mnemonic: "", + description: "RESERVED", + irqtype: "", + source: "None.", + }, + InterruptDescription { + vector: 16, + mnemonic: "#MF", + description: "x87 FPU Floating-Point", + irqtype: "Fault", + source: "x87 FPU instructions.", + }, + InterruptDescription { + vector: 17, + mnemonic: "#AC", + description: "Alignment Check", + irqtype: "Fault", + source: "Unaligned memory access.", + }, + InterruptDescription { + vector: 18, + mnemonic: "#MC", + description: "Machine Check", + irqtype: "Abort", + source: "Internal machine error.", + }, + InterruptDescription { + vector: 19, + mnemonic: "#XM", + description: "SIMD Floating-Point", + irqtype: "Fault", + source: "SSE SIMD instructions.", + }, + InterruptDescription { + vector: 20, + mnemonic: "#VE", + description: "Virtualization", + irqtype: "Fault", + source: "EPT violation.", + }]; + + +/// Enable Interrupts. +pub unsafe fn enable() { + asm!("sti"); +} + +/// Disable Interrupts. +pub unsafe fn disable() { + asm!("cli"); +} + +/// Generate a software interrupt. +/// This is a macro argument needs to be an immediate. +#[macro_export] +macro_rules! int { + ( $x:expr ) => { + { + asm!("int $0" :: "N" ($x)); + } + }; +} + +/// A struct describing an interrupt gate. See the Intel manual mentioned +/// above for details, specifically, the section "6.14.1 64-Bit Mode IDT" +/// and "Table 3-2. System-Segment and Gate-Descriptor Types". +#[derive(Debug, Clone, Copy)] +#[repr(C, packed)] +pub struct IdtEntry { + /// Lower 16 bits of ISR. + pub base_lo: u16, + /// Segment selector. + pub sel: u16, + /// This must always be zero. + pub res0: u8, + /// Flags. + pub flags: u8, + /// The upper 48 bits of ISR (the last 16 bits must be zero). + pub base_hi: u64, + /// Must be zero. + pub res1: u16, +} + +impl IdtEntry { + /// Create a "missing" IdtEntry. This is a `const` function, so we can + /// call it at compile time to initialize static variables. + /// + /// If the CPU tries to invoke a missing interrupt, it will instead + /// send a General Protection fault (13), with the interrupt number and + /// some other data stored in the error code. + pub const fn missing() -> IdtEntry { + IdtEntry { + base_lo: 0, + sel: 0, + res0: 0, + flags: 0, + base_hi: 0, + res1: 0, + } + } + + /// Create a new IdtEntry pointing at `handler`, which must be a + /// function with interrupt calling conventions. (This must be + /// currently defined in assembly language.) The `gdt_code_selector` + /// value must be the offset of code segment entry in the GDT. + /// + /// Create an interrupt gate with the "Present" flag set, which is the + /// most common case. If you need something else, you can construct it + /// manually. + pub const fn interrupt_gate(gdt_code_selector: u16, handler: VAddr) -> IdtEntry { + IdtEntry { + base_lo: ((handler.as_usize() as u64) & 0xFFFF) as u16, + sel: gdt_code_selector, + res0: 0, + // Bit 7: "Present" flag set. + // Bits 0-4: This is an interrupt gate. + flags: 0b1000_1110, + base_hi: handler.as_usize() as u64 >> 16, + res1: 0, + } + } +} + +bitflags!{ + // Taken from Intel Manual Section 4.7 Page-Fault Exceptions. + pub flags PageFaultError: u32 { + /// 0: The fault was caused by a non-present page. + /// 1: The fault was caused by a page-level protection violation + const PFAULT_ERROR_P = bit!(0), + + /// 0: The access causing the fault was a read. + /// 1: The access causing the fault was a write. + const PFAULT_ERROR_WR = bit!(1), + + /// 0: The access causing the fault originated when the processor + /// was executing in supervisor mode. + /// 1: The access causing the fault originated when the processor + /// was executing in user mode. + const PFAULT_ERROR_US = bit!(2), + + /// 0: The fault was not caused by reserved bit violation. + /// 1: The fault was caused by reserved bits set to 1 in a page directory. + const PFAULT_ERROR_RSVD = bit!(3), + + /// 0: The fault was not caused by an instruction fetch. + /// 1: The fault was caused by an instruction fetch. + const PFAULT_ERROR_ID = bit!(4), + + /// 0: The fault was not by protection keys. + /// 1: There was a protection key violation. + const PFAULT_ERROR_PK = bit!(5), + } +} + +impl fmt::Display for PageFaultError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let p = match self.contains(PFAULT_ERROR_P) { + false => "The fault was caused by a non-present page.", + true => "The fault was caused by a page-level protection violation.", + }; + let wr = match self.contains(PFAULT_ERROR_WR) { + false => "The access causing the fault was a read.", + true => "The access causing the fault was a write.", + }; + let us = match self.contains(PFAULT_ERROR_US) { + false => { + "The access causing the fault originated when the processor was executing in \ + supervisor mode." + } + true => { + "The access causing the fault originated when the processor was executing in user \ + mode." + } + }; + let rsvd = match self.contains(PFAULT_ERROR_RSVD) { + false => "The fault was not caused by reserved bit violation.", + true => "The fault was caused by reserved bits set to 1 in a page directory.", + }; + let id = match self.contains(PFAULT_ERROR_ID) { + false => "The fault was not caused by an instruction fetch.", + true => "The fault was caused by an instruction fetch.", + }; + + write!(f, "{}\n{}\n{}\n{}\n{}", p, wr, us, rsvd, id) + } +} + +#[test] +fn bit_macro() { + assert!(PFAULT_ERROR_PK.bits() == 0b100000); + assert!(PFAULT_ERROR_ID.bits() == 0b10000); + assert!(PFAULT_ERROR_RSVD.bits() == 0b1000); + assert!(PFAULT_ERROR_US.bits() == 0b100); + assert!(PFAULT_ERROR_WR.bits() == 0b10); + assert!(PFAULT_ERROR_P.bits() == 0b1); +} |