parent
230d5a0287
commit
cea8ccdac9
@ -1,653 +0,0 @@
|
||||
// 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 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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<HandlerFunc>,
|
||||
|
||||
/// When the debug-exception mechanism is enabled, a `#DB` exception can occur under any
|
||||
/// of the following circumstances:
|
||||
///
|
||||
/// <details>
|
||||
///
|
||||
/// - 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).
|
||||
///
|
||||
/// </details>
|
||||
///
|
||||
/// `#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<HandlerFunc>,
|
||||
|
||||
/// 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<HandlerFunc>,
|
||||
|
||||
/// 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<HandlerFunc>,
|
||||
|
||||
/// 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<HandlerFunc>,
|
||||
|
||||
/// 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<HandlerFunc>,
|
||||
|
||||
/// 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.
|
||||
///
|
||||
/// <details><summary>A `#UD` occurs under the following conditions:</summary>
|
||||
///
|
||||
/// - 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.
|
||||
///
|
||||
/// </details>
|
||||
///
|
||||
/// 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<HandlerFunc>,
|
||||
|
||||
/// A device not available exception (`#NM`) occurs under any of the following conditions:
|
||||
///
|
||||
/// <details>
|
||||
///
|
||||
/// - 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`.
|
||||
///
|
||||
/// </details>
|
||||
///
|
||||
/// 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<HandlerFunc>,
|
||||
|
||||
/// A double fault (`#DF`) exception can occur when a second exception occurs during
|
||||
/// the handling of a prior (first) exception or interrupt handler.
|
||||
///
|
||||
/// <details>
|
||||
///
|
||||
/// 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.
|
||||
///
|
||||
/// </details>
|
||||
///
|
||||
/// 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<HandlerFuncWithErrCode>,
|
||||
|
||||
/// 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<HandlerFunc>,
|
||||
|
||||
/// 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<HandlerFuncWithErrCode>,
|
||||
|
||||
/// 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<HandlerFuncWithErrCode>,
|
||||
|
||||
/// 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<HandlerFuncWithErrCode>,
|
||||
|
||||
/// 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<HandlerFuncWithErrCode>,
|
||||
|
||||
/// 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<PageFaultHandlerFunc>,
|
||||
|
||||
/// vector nr. 15
|
||||
reserved_1: IdtEntry<HandlerFunc>,
|
||||
|
||||
/// 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<HandlerFunc>,
|
||||
|
||||
/// 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<HandlerFuncWithErrCode>,
|
||||
|
||||
/// 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<HandlerFunc>,
|
||||
|
||||
/// 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<HandlerFunc>,
|
||||
|
||||
/// vector nr. 20
|
||||
pub virtualization: IdtEntry<HandlerFunc>,
|
||||
|
||||
/// vector nr. 21-29
|
||||
reserved_2: [IdtEntry<HandlerFunc>; 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<HandlerFuncWithErrCode>,
|
||||
|
||||
/// vector nr. 31
|
||||
reserved_3: IdtEntry<HandlerFunc>,
|
||||
|
||||
/// 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<HandlerFunc>; 256 - 32],
|
||||
}
|
||||
|
||||
impl Idt {
|
||||
/// Creates a new IDT filled with non-present entries.
|
||||
pub const fn new() -> Idt {
|
||||
// debug_assert_eq!(mem::size_of::<Self>(), 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::<Self>() - 1) as u16,
|
||||
};
|
||||
|
||||
unsafe { lidt(&ptr) };
|
||||
}
|
||||
}
|
||||
|
||||
impl Index<usize> for Idt {
|
||||
type Output = IdtEntry<HandlerFunc>;
|
||||
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<usize> 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<F> {
|
||||
pointer_low: u16,
|
||||
gdt_selector: u16,
|
||||
options: EntryOptions,
|
||||
pointer_middle: u16,
|
||||
pointer_high: u32,
|
||||
reserved: u32,
|
||||
phantom: PhantomData<F>,
|
||||
}
|
||||
|
||||
/// 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<F> IdtEntry<F> {
|
||||
/// 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,
|
||||
}
|
||||
}
|
Loading…
Reference in new issue