diff --git a/src/arch/x86_64/idt.rs b/src/arch/x86_64/idt.rs new file mode 100644 index 0000000..c71e642 --- /dev/null +++ b/src/arch/x86_64/idt.rs @@ -0,0 +1,653 @@ +// Copyright 2017 Philipp Oppermann. See the README.md +// file at the top-level directory of this distribution. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Provides types for the Interrupt Descriptor Table and its entries. + +use core::fmt; +use core::marker::PhantomData; +use core::mem; +use core::ops::{Index, IndexMut}; +use bit_field::BitField; +use x86_64::{PrivilegeLevel, VirtualAddress}; + +/// An Interrupt Descriptor Table with 256 entries. +/// +/// The field descriptions are taken from the +/// [AMD64 manual volume 2](https://support.amd.com/TechDocs/24593.pdf) +/// (with slight modifications). +#[repr(C)] +pub struct Idt { + /// A divide by zero exception (`#DE`) occurs when the denominator of a DIV instruction or + /// an IDIV instruction is 0. A `#DE` also occurs if the result is too large to be + /// represented in the destination. + /// + /// The saved instruction pointer points to the instruction that caused the `#DE`. + /// + /// The vector number of the `#DE` exception is 0. + pub divide_by_zero: IdtEntry, + + /// When the debug-exception mechanism is enabled, a `#DB` exception can occur under any + /// of the following circumstances: + /// + ///
+ /// + /// - Instruction execution. + /// - Instruction single stepping. + /// - Data read. + /// - Data write. + /// - I/O read. + /// - I/O write. + /// - Task switch. + /// - Debug-register access, or general detect fault (debug register access when DR7.GD=1). + /// - Executing the INT1 instruction (opcode 0F1h). + /// + ///
+ /// + /// `#DB` conditions are enabled and disabled using the debug-control register, `DR7` + /// and `RFLAGS.TF`. + /// + /// In the following cases, the saved instruction pointer points to the instruction that + /// caused the `#DB`: + /// + /// - Instruction execution. + /// - Invalid debug-register access, or general detect. + /// + /// In all other cases, the instruction that caused the `#DB` is completed, and the saved + /// instruction pointer points to the instruction after the one that caused the `#DB`. + /// + /// The vector number of the `#DB` exception is 1. + pub debug: IdtEntry, + + /// An non maskable interrupt exception (NMI) occurs as a result of system logic + /// signaling a non-maskable interrupt to the processor. + /// + /// The processor recognizes an NMI at an instruction boundary. + /// The saved instruction pointer points to the instruction immediately following the + /// boundary where the NMI was recognized. + /// + /// The vector number of the NMI exception is 2. + pub non_maskable_interrupt: IdtEntry, + + /// A breakpoint (`#BP`) exception occurs when an `INT3` instruction is executed. The + /// `INT3` is normally used by debug software to set instruction breakpoints by replacing + /// + /// The saved instruction pointer points to the byte after the `INT3` instruction. + /// + /// The vector number of the `#BP` exception is 3. + pub breakpoint: IdtEntry, + + /// An overflow exception (`#OF`) occurs as a result of executing an `INTO` instruction + /// while the overflow bit in `RFLAGS` is set to 1. + /// + /// The saved instruction pointer points to the instruction following the `INTO` + /// instruction that caused the `#OF`. + /// + /// The vector number of the `#OF` exception is 4. + pub overflow: IdtEntry, + + /// A bound-range exception (`#BR`) exception can occur as a result of executing + /// the `BOUND` instruction. The `BOUND` instruction compares an array index (first + /// operand) with the lower bounds and upper bounds of an array (second operand). + /// If the array index is not within the array boundary, the `#BR` occurs. + /// + /// The saved instruction pointer points to the `BOUND` instruction that caused the `#BR`. + /// + /// The vector number of the `#BR` exception is 5. + pub bound_range_exceeded: IdtEntry, + + /// An invalid opcode exception (`#UD`) occurs when an attempt is made to execute an + /// invalid or undefined opcode. The validity of an opcode often depends on the + /// processor operating mode. + /// + ///
A `#UD` occurs under the following conditions: + /// + /// - Execution of any reserved or undefined opcode in any mode. + /// - Execution of the `UD2` instruction. + /// - Use of the `LOCK` prefix on an instruction that cannot be locked. + /// - Use of the `LOCK` prefix on a lockable instruction with a non-memory target location. + /// - Execution of an instruction with an invalid-operand type. + /// - Execution of the `SYSENTER` or `SYSEXIT` instructions in long mode. + /// - Execution of any of the following instructions in 64-bit mode: `AAA`, `AAD`, + /// `AAM`, `AAS`, `BOUND`, `CALL` (opcode 9A), `DAA`, `DAS`, `DEC`, `INC`, `INTO`, + /// `JMP` (opcode EA), `LDS`, `LES`, `POP` (`DS`, `ES`, `SS`), `POPA`, `PUSH` (`CS`, + /// `DS`, `ES`, `SS`), `PUSHA`, `SALC`. + /// - Execution of the `ARPL`, `LAR`, `LLDT`, `LSL`, `LTR`, `SLDT`, `STR`, `VERR`, or + /// `VERW` instructions when protected mode is not enabled, or when virtual-8086 mode + /// is enabled. + /// - Execution of any legacy SSE instruction when `CR4.OSFXSR` is cleared to 0. + /// - Execution of any SSE instruction (uses `YMM`/`XMM` registers), or 64-bit media + /// instruction (uses `MMXTM` registers) when `CR0.EM` = 1. + /// - Execution of any SSE floating-point instruction (uses `YMM`/`XMM` registers) that + /// causes a numeric exception when `CR4.OSXMMEXCPT` = 0. + /// - Use of the `DR4` or `DR5` debug registers when `CR4.DE` = 1. + /// - Execution of `RSM` when not in `SMM` mode. + /// + ///
+ /// + /// The saved instruction pointer points to the instruction that caused the `#UD`. + /// + /// The vector number of the `#UD` exception is 6. + pub invalid_opcode: IdtEntry, + + /// A device not available exception (`#NM`) occurs under any of the following conditions: + /// + ///
+ /// + /// - An `FWAIT`/`WAIT` instruction is executed when `CR0.MP=1` and `CR0.TS=1`. + /// - Any x87 instruction other than `FWAIT` is executed when `CR0.EM=1`. + /// - Any x87 instruction is executed when `CR0.TS=1`. The `CR0.MP` bit controls whether the + /// `FWAIT`/`WAIT` instruction causes an `#NM` exception when `TS=1`. + /// - Any 128-bit or 64-bit media instruction when `CR0.TS=1`. + /// + ///
+ /// + /// The saved instruction pointer points to the instruction that caused the `#NM`. + /// + /// The vector number of the `#NM` exception is 7. + pub device_not_available: IdtEntry, + + /// A double fault (`#DF`) exception can occur when a second exception occurs during + /// the handling of a prior (first) exception or interrupt handler. + /// + ///
+ /// + /// Usually, the first and second exceptions can be handled sequentially without + /// resulting in a `#DF`. In this case, the first exception is considered _benign_, as + /// it does not harm the ability of the processor to handle the second exception. In some + /// cases, however, the first exception adversely affects the ability of the processor to + /// handle the second exception. These exceptions contribute to the occurrence of a `#DF`, + /// and are called _contributory exceptions_. The following exceptions are contributory: + /// + /// - Invalid-TSS Exception + /// - Segment-Not-Present Exception + /// - Stack Exception + /// - General-Protection Exception + /// + /// A double-fault exception occurs in the following cases: + /// + /// - If a contributory exception is followed by another contributory exception. + /// - If a divide-by-zero exception is followed by a contributory exception. + /// - If a page fault is followed by another page fault or a contributory exception. + /// + /// If a third interrupting event occurs while transferring control to the `#DF` handler, + /// the processor shuts down. + /// + ///
+ /// + /// The returned error code is always zero. The saved instruction pointer is undefined, + /// and the program cannot be restarted. + /// + /// The vector number of the `#DF` exception is 8. + pub double_fault: IdtEntry, + + /// This interrupt vector is reserved. It is for a discontinued exception originally used + /// by processors that supported external x87-instruction coprocessors. On those processors, + /// the exception condition is caused by an invalid-segment or invalid-page access on an + /// x87-instruction coprocessor-instruction operand. On current processors, this condition + /// causes a general-protection exception to occur. + coprocessor_segment_overrun: IdtEntry, + + /// An invalid TSS exception (`#TS`) occurs only as a result of a control transfer through + /// a gate descriptor that results in an invalid stack-segment reference using an `SS` + /// selector in the TSS. + /// + /// The returned error code is the `SS` segment selector. The saved instruction pointer + /// points to the control-transfer instruction that caused the `#TS`. + /// + /// The vector number of the `#DF` exception is 10. + pub invalid_tss: IdtEntry, + + /// An segment-not-present exception (`#NP`) occurs when an attempt is made to load a + /// segment or gate with a clear present bit. + /// + /// The returned error code is the segment-selector index of the segment descriptor + /// causing the `#NP` exception. The saved instruction pointer points to the instruction + /// that loaded the segment selector resulting in the `#NP`. + /// + /// The vector number of the `#NP` exception is 11. + pub segment_not_present: IdtEntry, + + /// An stack segment exception (`#SS`) can occur in the following situations: + /// + /// - Implied stack references in which the stack address is not in canonical + /// form. Implied stack references include all push and pop instructions, and any + /// instruction using `RSP` or `RBP` as a base register. + /// - Attempting to load a stack-segment selector that references a segment descriptor + /// containing a clear present bit. + /// - Any stack access that fails the stack-limit check. + /// + /// The returned error code depends on the cause of the `#SS`. If the cause is a cleared + /// present bit, the error code is the corresponding segment selector. Otherwise, the + /// error code is zero. The saved instruction pointer points to the instruction that + /// caused the `#SS`. + /// + /// The vector number of the `#NP` exception is 12. + pub stack_segment_fault: IdtEntry, + + /// A general protection fault (`#GP`) can occur in various situations. Common causes include: + /// + /// - Executing a privileged instruction while `CPL > 0`. + /// - Writing a 1 into any register field that is reserved, must be zero (MBZ). + /// - Attempting to execute an SSE instruction specifying an unaligned memory operand. + /// - Loading a non-canonical base address into the `GDTR` or `IDTR`. + /// - Using WRMSR to write a read-only MSR. + /// - Any long-mode consistency-check violation. + /// + /// The returned error code is a segment selector, if the cause of the `#GP` is + /// segment-related, and zero otherwise. The saved instruction pointer points to + /// the instruction that caused the `#GP`. + /// + /// The vector number of the `#GP` exception is 13. + pub general_protection_fault: IdtEntry, + + /// A page fault (`#PF`) can occur during a memory access in any of the following situations: + /// + /// - A page-translation-table entry or physical page involved in translating the memory + /// access is not present in physical memory. This is indicated by a cleared present + /// bit in the translation-table entry. + /// - An attempt is made by the processor to load the instruction TLB with a translation + /// for a non-executable page. + /// - The memory access fails the paging-protection checks (user/supervisor, read/write, + /// or both). + /// - A reserved bit in one of the page-translation-table entries is set to 1. A `#PF` + /// occurs for this reason only when `CR4.PSE=1` or `CR4.PAE=1`. + /// + /// The virtual (linear) address that caused the `#PF` is stored in the `CR2` register. + /// The saved instruction pointer points to the instruction that caused the `#PF`. + /// + /// The page-fault error code is described by the + /// [`PageFaultErrorCode`](struct.PageFaultErrorCode.html) struct. + /// + /// The vector number of the `#PF` exception is 14. + pub page_fault: IdtEntry, + + /// vector nr. 15 + reserved_1: IdtEntry, + + /// The x87 Floating-Point Exception-Pending exception (`#MF`) is used to handle unmasked x87 + /// floating-point exceptions. In 64-bit mode, the x87 floating point unit is not used + /// anymore, so this exception is only relevant when executing programs in the 32-bit + /// compatibility mode. + /// + /// The vector number of the `#MF` exception is 16. + pub x87_floating_point: IdtEntry, + + /// An alignment check exception (`#AC`) occurs when an unaligned-memory data reference + /// is performed while alignment checking is enabled. An `#AC` can occur only when CPL=3. + /// + /// The returned error code is always zero. The saved instruction pointer points to the + /// instruction that caused the `#AC`. + /// + /// The vector number of the `#AC` exception is 17. + pub alignment_check: IdtEntry, + + /// The machine check exception (`#MC`) is model specific. Processor implementations + /// are not required to support the `#MC` exception, and those implementations that do + /// support `#MC` can vary in how the `#MC` exception mechanism works. + /// + /// There is no reliable way to restart the program. + /// + /// The vector number of the `#MC` exception is 18. + pub machine_check: IdtEntry, + + /// The SIMD Floating-Point Exception (`#XF`) is used to handle unmasked SSE + /// floating-point exceptions. The SSE floating-point exceptions reported by + /// the `#XF` exception are (including mnemonics): + /// + /// - IE: Invalid-operation exception (also called #I). + /// - DE: Denormalized-operand exception (also called #D). + /// - ZE: Zero-divide exception (also called #Z). + /// - OE: Overflow exception (also called #O). + /// - UE: Underflow exception (also called #U). + /// - PE: Precision exception (also called #P or inexact-result exception). + /// + /// The saved instruction pointer points to the instruction that caused the `#XF`. + /// + /// The vector number of the `#XF` exception is 19. + pub simd_floating_point: IdtEntry, + + /// vector nr. 20 + pub virtualization: IdtEntry, + + /// vector nr. 21-29 + reserved_2: [IdtEntry; 9], + + /// The Security Exception (`#SX`) signals security-sensitive events that occur while + /// executing the VMM, in the form of an exception so that the VMM may take appropriate + /// action. (A VMM would typically intercept comparable sensitive events in the guest.) + /// In the current implementation, the only use of the `#SX` is to redirect external INITs + /// into an exception so that the VMM may — among other possibilities. + /// + /// The only error code currently defined is 1, and indicates redirection of INIT has occurred. + /// + /// The vector number of the ``#SX`` exception is 30. + pub security_exception: IdtEntry, + + /// vector nr. 31 + reserved_3: IdtEntry, + + /// User-defined interrupts can be initiated either by system logic or software. They occur + /// when: + /// + /// - System logic signals an external interrupt request to the processor. The signaling + /// mechanism and the method of communicating the interrupt vector to the processor are + /// implementation dependent. + /// - Software executes an `INTn` instruction. The `INTn` instruction operand provides + /// the interrupt vector number. + /// + /// Both methods can be used to initiate an interrupt into vectors 0 through 255. However, + /// because vectors 0 through 31 are defined or reserved by the AMD64 architecture, + /// software should not use vectors in this range for purposes other than their defined use. + /// + /// The saved instruction pointer depends on the interrupt source: + /// + /// - External interrupts are recognized on instruction boundaries. The saved instruction + /// pointer points to the instruction immediately following the boundary where the + /// external interrupt was recognized. + /// - If the interrupt occurs as a result of executing the INTn instruction, the saved + /// instruction pointer points to the instruction after the INTn. + pub interrupts: [IdtEntry; 256 - 32], +} + +impl Idt { + /// Creates a new IDT filled with non-present entries. + pub const fn new() -> Idt { + // debug_assert_eq!(mem::size_of::(), 256 * 16); + Idt { + divide_by_zero: IdtEntry::missing(), + debug: IdtEntry::missing(), + non_maskable_interrupt: IdtEntry::missing(), + breakpoint: IdtEntry::missing(), + overflow: IdtEntry::missing(), + bound_range_exceeded: IdtEntry::missing(), + invalid_opcode: IdtEntry::missing(), + device_not_available: IdtEntry::missing(), + double_fault: IdtEntry::missing(), + coprocessor_segment_overrun: IdtEntry::missing(), + invalid_tss: IdtEntry::missing(), + segment_not_present: IdtEntry::missing(), + stack_segment_fault: IdtEntry::missing(), + general_protection_fault: IdtEntry::missing(), + page_fault: IdtEntry::missing(), + reserved_1: IdtEntry::missing(), + x87_floating_point: IdtEntry::missing(), + alignment_check: IdtEntry::missing(), + machine_check: IdtEntry::missing(), + simd_floating_point: IdtEntry::missing(), + virtualization: IdtEntry::missing(), + reserved_2: [IdtEntry::missing(); 9], + security_exception: IdtEntry::missing(), + reserved_3: IdtEntry::missing(), + interrupts: [IdtEntry::missing(); 256 - 32], + } + } + + /// Loads the IDT in the CPU using the `lidt` command. + pub fn load(&'static self) { + use x86_64::instructions::tables::{DescriptorTablePointer, lidt}; + use core::mem::size_of; + + let ptr = DescriptorTablePointer { + base: self as *const _ as u64, + limit: (size_of::() - 1) as u16, + }; + + unsafe { lidt(&ptr) }; + } +} + +impl Index for Idt { + type Output = IdtEntry; + fn index(&self, index: usize) -> &Self::Output { + match index { + 0 => &self.divide_by_zero, + 1 => &self.debug, + 2 => &self.non_maskable_interrupt, + 3 => &self.breakpoint, + 4 => &self.overflow, + 5 => &self.bound_range_exceeded, + 6 => &self.invalid_opcode, + 7 => &self.device_not_available, + 9 => &self.coprocessor_segment_overrun, + 16 => &self.x87_floating_point, + 18 => &self.machine_check, + 19 => &self.simd_floating_point, + 20 => &self.virtualization, + i @ 32...255 => &self.interrupts[i - 32], + i @ 15 | i @ 31 | i @ 21...29 => panic!("entry {} is reserved", i), + i @ 8 | i @ 10...14 | i @ 17 | i @ 30 => { + panic!("entry {} is an exception with error code", i) + } + i => panic!("no entry with index {}", i), + } + } +} + +impl IndexMut for Idt { + fn index_mut(&mut self, index: usize) -> &mut Self::Output { + match index { + 0 => &mut self.divide_by_zero, + 1 => &mut self.debug, + 2 => &mut self.non_maskable_interrupt, + 3 => &mut self.breakpoint, + 4 => &mut self.overflow, + 5 => &mut self.bound_range_exceeded, + 6 => &mut self.invalid_opcode, + 7 => &mut self.device_not_available, + 9 => &mut self.coprocessor_segment_overrun, + 16 => &mut self.x87_floating_point, + 18 => &mut self.machine_check, + 19 => &mut self.simd_floating_point, + 20 => &mut self.virtualization, + i @ 32...255 => &mut self.interrupts[i - 32], + i @ 15 | i @ 31 | i @ 21...29 => panic!("entry {} is reserved", i), + i @ 8 | i @ 10...14 | i @ 17 | i @ 30 => { + panic!("entry {} is an exception with error code", i) + } + i => panic!("no entry with index {}", i), + } + } +} + +/// An Interrupt Descriptor Table entry. +/// +/// The generic parameter can either be `HandlerFunc` or `HandlerFuncWithErrCode`, depending +/// on the interrupt vector. +#[derive(Debug, Clone, Copy)] +#[repr(C, packed)] +pub struct IdtEntry { + pointer_low: u16, + gdt_selector: u16, + options: EntryOptions, + pointer_middle: u16, + pointer_high: u32, + reserved: u32, + phantom: PhantomData, +} + +/// A handler function for an interrupt or an exception without error code. +pub type HandlerFunc = extern "x86-interrupt" fn(&mut ExceptionStackFrame); +/// A handler function for an exception that pushes an error code. +pub type HandlerFuncWithErrCode = extern "x86-interrupt" fn(&mut ExceptionStackFrame, error_code: u64); +/// A page fault handler function that pushes a page fault error code. +pub type PageFaultHandlerFunc = extern "x86-interrupt" fn(&mut ExceptionStackFrame, error_code: PageFaultErrorCode); + +impl IdtEntry { + /// Creates a non-present IDT entry (but sets the must-be-one bits). + const fn missing() -> Self { + IdtEntry { + gdt_selector: 0, + pointer_low: 0, + pointer_middle: 0, + pointer_high: 0, + options: EntryOptions::minimal(), + reserved: 0, + phantom: PhantomData, + } + } + + /// Set the handler address for the IDT entry and sets the present bit. + /// + /// For the code selector field, this function uses the code segment selector currently + /// active in the CPU. + /// + /// The function returns a mutable reference to the entry's options that allows + /// further customization. + fn set_handler_addr(&mut self, addr: u64) -> &mut EntryOptions { + use x86_64::instructions::segmentation; + + self.pointer_low = addr as u16; + self.pointer_middle = (addr >> 16) as u16; + self.pointer_high = (addr >> 32) as u32; + + self.gdt_selector = segmentation::cs().0; + + self.options.set_present(true); + &mut self.options + } +} + +macro_rules! impl_set_handler_fn { + ($h:ty) => { + impl IdtEntry<$h> { + /// Set the handler function for the IDT entry and sets the present bit. + /// + /// For the code selector field, this function uses the code segment selector currently + /// active in the CPU. + /// + /// The function returns a mutable reference to the entry's options that allows + /// further customization. + pub fn set_handler_fn(&mut self, handler: $h) -> &mut EntryOptions { + self.set_handler_addr(handler as u64) + } + } + } +} + +impl_set_handler_fn!(HandlerFunc); +impl_set_handler_fn!(HandlerFuncWithErrCode); +impl_set_handler_fn!(PageFaultHandlerFunc); + +/// Represents the options field of an IDT entry. +#[derive(Debug, Clone, Copy)] +pub struct EntryOptions(u16); + +impl EntryOptions { + /// Creates a minimal options field with all the must-be-one bits set. + const fn minimal() -> Self { + // options.set_bits(9..12, 0b111); // 'must-be-one' bits + EntryOptions(0b111_000_000_000) + } + + /// Set or reset the preset bit. + pub fn set_present(&mut self, present: bool) -> &mut Self { + self.0.set_bit(15, present); + self + } + + /// Let the CPU disable hardware interrupts when the handler is invoked. By default, + /// interrupts are disabled on handler invocation. + pub fn disable_interrupts(&mut self, disable: bool) -> &mut Self { + self.0.set_bit(8, !disable); + self + } + + /// Set the required privilege level (DPL) for invoking the handler. The DPL can be 0, 1, 2, + /// or 3, the default is 0. If CPL < DPL, a general protection fault occurs. + /// + /// This function panics for a DPL > 3. + pub fn set_privilege_level(&mut self, dpl: PrivilegeLevel) -> &mut Self { + self.0.set_bits(13..15, dpl as u16); + self + } + + /// Assigns a Interrupt Stack Table (IST) stack to this handler. The CPU will then always + /// switch to the specified stack before the handler is invoked. This allows kernels to + /// recover from corrupt stack pointers (e.g., on kernel stack overflow). + /// + /// An IST stack is specified by an IST index between 0 and 6 (inclusive). Using the same + /// stack for multiple interrupts can be dangerous when nested interrupts are possible. + /// + /// This function panics if the index is not in the range 0..7. + /// + /// ## Safety + /// This function is unsafe because the caller must ensure that the passed stack index is + /// valid and not used by other interrupts. Otherwise, memory safety violations are possible. + pub unsafe fn set_stack_index(&mut self, index: u16) -> &mut Self { + // The hardware IST index starts at 1, but our software IST index + // starts at 0. Therefore we need to add 1 here. + self.0.set_bits(0..3, index + 1); + self + } +} + +/// Represents the exception stack frame pushed by the CPU on exception entry. +#[repr(C)] +pub struct ExceptionStackFrame { + /// This value points to the instruction that should be executed when the interrupt + /// handler returns. For most interrupts, this value points to the instruction immediately + /// following the last executed instruction. However, for some exceptions (e.g., page faults), + /// this value points to the faulting instruction, so that the instruction is restarted on + /// return. See the documentation of the `Idt` fields for more details. + pub instruction_pointer: VirtualAddress, + /// The code segment selector, padded with zeros. + pub code_segment: u64, + /// The flags register before the interrupt handler was invoked. + pub cpu_flags: u64, + /// The stack pointer at the time of the interrupt. + pub stack_pointer: VirtualAddress, + /// The stack segment descriptor at the time of the interrupt (often zero in 64-bit mode). + pub stack_segment: u64, +} + +impl fmt::Debug for ExceptionStackFrame { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + struct Hex(u64); + impl fmt::Debug for Hex { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{:#x}", self.0) + } + } + + let mut s = f.debug_struct("ExceptionStackFrame"); + s.field("instruction_pointer", &self.instruction_pointer); + s.field("code_segment", &self.code_segment); + s.field("cpu_flags", &Hex(self.cpu_flags)); + s.field("stack_pointer", &self.stack_pointer); + s.field("stack_segment", &self.stack_segment); + s.finish() + } +} + +bitflags! { + /// Describes an page fault error code. + pub flags PageFaultErrorCode: u64 { + /// If this flag is set, the page fault was caused by a page-protection violation, + /// else the page fault was caused by a not-present page. + const PROTECTION_VIOLATION = 1 << 0, + + /// If this flag is set, the memory access that caused the page fault was a write. + /// Else the access that caused the page fault is a memory read. This bit does not + /// necessarily indicate the cause of the page fault was a read or write violation. + const CAUSED_BY_WRITE = 1 << 1, + + /// If this flag is set, an access in user mode (CPL=3) caused the page fault. Else + /// an access in supervisor mode (CPL=0, 1, or 2) caused the page fault. This bit + /// does not necessarily indicate the cause of the page fault was a privilege violation. + const USER_MODE = 1 << 2, + + /// If this flag is set, the page fault is a result of the processor reading a 1 from + /// a reserved field within a page-translation-table entry. + const MALFORMED_TABLE = 1 << 3, + + /// If this flag is set, it indicates that the access that caused the page fault was an + /// instruction fetch. + const INSTRUCTION_FETCH = 1 << 4, + } +} \ No newline at end of file diff --git a/src/arch/x86_64/mod.rs b/src/arch/x86_64/mod.rs index a6322a3..f3203c1 100644 --- a/src/arch/x86_64/mod.rs +++ b/src/arch/x86_64/mod.rs @@ -1,5 +1,6 @@ pub mod driver; pub mod cpu; +pub mod idt; pub fn init() { cpu::enable_nxe_bit(); diff --git a/src/interrupts/mod.rs b/src/interrupts/mod.rs index 5a2324d..23a7b97 100644 --- a/src/interrupts/mod.rs +++ b/src/interrupts/mod.rs @@ -1,28 +1,25 @@ use x86_64::VirtualAddress; -use x86_64::structures::idt::{Idt, ExceptionStackFrame}; +use arch::idt::{Idt, ExceptionStackFrame}; use x86_64::structures::tss::TaskStateSegment; -use memory::MemoryController; +use memory::{MemoryController, as_ref_in_real}; use spin::Once; mod gdt; -lazy_static! { - static ref IDT: Idt = { - let mut idt = Idt::new(); - idt.breakpoint.set_handler_fn(breakpoint_handler); - unsafe { - idt.double_fault.set_handler_fn(double_fault_handler) - .set_stack_index(DOUBLE_FAULT_IST_INDEX as u16); - } - idt - }; -} +static mut IDT: Idt = Idt::new(); static TSS: Once = Once::new(); static GDT: Once = Once::new(); const DOUBLE_FAULT_IST_INDEX: usize = 0; +pub unsafe fn init_idt() { + IDT.breakpoint.set_handler_fn(*as_ref_in_real(&breakpoint_handler)); + IDT.double_fault.set_handler_fn(*as_ref_in_real(&double_fault_handler)); + // .set_stack_index(DOUBLE_FAULT_IST_INDEX as u16); + IDT.load(); +} + pub fn init(memory_controller: &mut MemoryController) { use x86_64::structures::gdt::SegmentSelector; use x86_64::instructions::segmentation::set_cs; @@ -55,7 +52,7 @@ pub fn init(memory_controller: &mut MemoryController) { load_tss(tss_selector); } - IDT.load(); + // IDT.load(); } extern "x86-interrupt" fn breakpoint_handler( diff --git a/src/lib.rs b/src/lib.rs index 31d161e..b30f33a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -48,6 +48,8 @@ mod arch; pub extern "C" fn rust_main(multiboot_information_address: usize) { // ATTENTION: we have a very small stack and no guard page println!("Hello World{}", "!"); + unsafe{ interrupts::init_idt(); } + unsafe{ let a = *(0xdeadbeaf as *const u8); } // double fault let boot_info = unsafe { multiboot2::load(multiboot_information_address) }; arch::init(); diff --git a/src/memory/mod.rs b/src/memory/mod.rs index 16e15a8..6c58db0 100644 --- a/src/memory/mod.rs +++ b/src/memory/mod.rs @@ -141,3 +141,16 @@ impl MemoryController { size_in_pages) } } + + +pub unsafe fn as_mut_in_real (obj: &mut T) -> &mut T { + let va = obj as *mut T as VirtualAddress; + let pa = PhysicalAddress::from_kernel_virtual(va).0; + &mut *(pa as *mut T) +} + +pub unsafe fn as_ref_in_real (obj: &T) -> &T { + let va = obj as *const T as VirtualAddress; + let pa = PhysicalAddress::from_kernel_virtual(va).0; + &*(pa as *const T) +} \ No newline at end of file