485 lines
21 KiB
Rust
485 lines
21 KiB
Rust
use core::{arch::asm, mem::size_of};
|
|
|
|
use super::{
|
|
handler::HandlerFunc, DivergingHandlerFunc, DivergingHandlerFuncWithErrCode, Entry,
|
|
HandlerFuncWithErrCode, PageFaultHandlerFunc,
|
|
};
|
|
|
|
// TODO: comments
|
|
#[repr(C, packed(2))]
|
|
struct Idtr {
|
|
size: u16,
|
|
offset: u32,
|
|
}
|
|
|
|
//
|
|
// MARK: idt
|
|
//
|
|
|
|
// spell:off
|
|
#[derive(Clone, Debug)]
|
|
#[repr(C)]
|
|
#[repr(align(8))]
|
|
pub struct InterruptDescriptorTable {
|
|
/// A divide error (`#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_error: Entry<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: Entry<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: Entry<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: Entry<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: Entry<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: Entry<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: Entry<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: Entry<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: Entry<DivergingHandlerFuncWithErrCode>,
|
|
|
|
/// 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: Entry<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 `#TS` exception is 10.
|
|
pub invalid_tss: Entry<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: Entry<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: Entry<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: Entry<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: Entry<PageFaultHandlerFunc>,
|
|
|
|
/// vector nr. 15
|
|
reserved_1: Entry<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: Entry<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: Entry<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: Entry<DivergingHandlerFunc>,
|
|
|
|
/// 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: Entry<HandlerFunc>,
|
|
|
|
/// vector nr. 20
|
|
pub virtualization: Entry<HandlerFunc>,
|
|
|
|
/// A #CP exception is generated when shadow stacks are enabled and mismatch
|
|
/// scenarios are detected (possible error code cases below).
|
|
///
|
|
/// The error code is the #CP error code, for each of the following situations:
|
|
/// - A RET (near) instruction encountered a return address mismatch.
|
|
/// - A RET (far) instruction encountered a return address mismatch.
|
|
/// - A RSTORSSP instruction encountered an invalid shadow stack restore token.
|
|
/// - A SETSSBY instruction encountered an invalid supervisor shadow stack token.
|
|
/// - A missing ENDBRANCH instruction if indirect branch tracking is enabled.
|
|
///
|
|
/// vector nr. 21
|
|
pub cp_protection_exception: Entry<HandlerFuncWithErrCode>,
|
|
|
|
/// vector nr. 22-27
|
|
reserved_2: [Entry<HandlerFunc>; 6],
|
|
|
|
/// The Hypervisor Injection Exception (`#HV`) is injected by a hypervisor
|
|
/// as a doorbell to inform an `SEV-SNP` enabled guest running with the
|
|
/// `Restricted Injection` feature of events to be processed.
|
|
///
|
|
/// `SEV-SNP` stands for the _"Secure Nested Paging"_ feature of the _"AMD
|
|
/// Secure Encrypted Virtualization"_ technology. The `Restricted
|
|
/// Injection` feature disables all hypervisor-based interrupt queuing
|
|
/// and event injection of all vectors except #HV.
|
|
///
|
|
/// The `#HV` exception is a benign exception and can only be injected as
|
|
/// an exception and without an error code. `SEV-SNP` enabled guests are
|
|
/// expected to communicate with the hypervisor about events via a
|
|
/// software-managed para-virtualization interface.
|
|
///
|
|
/// The vector number of the ``#HV`` exception is 28.
|
|
pub hv_injection_exception: Entry<HandlerFunc>,
|
|
|
|
/// The VMM Communication Exception (`#VC`) is always generated by hardware when an `SEV-ES`
|
|
/// enabled guest is running and an `NAE` event occurs.
|
|
///
|
|
/// `SEV-ES` stands for the _"Encrypted State"_ feature of the _"AMD Secure Encrypted Virtualization"_
|
|
/// technology. `NAE` stands for an _"Non-Automatic Exit"_, which is an `VMEXIT` event that requires
|
|
/// hypervisor emulation. See
|
|
/// [this whitepaper](https://www.amd.com/system/files/TechDocs/Protecting%20VM%20Register%20State%20with%20SEV-ES.pdf)
|
|
/// for an overview of the `SEV-ES` feature.
|
|
///
|
|
/// The `#VC` exception is a precise, contributory, fault-type exception utilizing exception vector 29.
|
|
/// This exception cannot be masked. The error code of the `#VC` exception is equal
|
|
/// to the `#VMEXIT` code of the event that caused the `NAE`.
|
|
///
|
|
/// In response to a `#VC` exception, a typical flow would involve the guest handler inspecting the error
|
|
/// code to determine the cause of the exception and deciding what register state must be copied to the
|
|
/// `GHCB` (_"Guest Hypervisor Communication Block"_) for the event to be handled. The handler
|
|
/// should then execute the `VMGEXIT` instruction to
|
|
/// create an `AE` and invoke the hypervisor. After a later `VMRUN`, guest execution will resume after the
|
|
/// `VMGEXIT` instruction where the handler can view the results from the hypervisor and copy state from
|
|
/// the `GHCB` back to its internal state as needed.
|
|
///
|
|
/// Note that it is inadvisable for the hypervisor to set the `VMCB` (_"Virtual Machine Control Block"_)
|
|
/// intercept bit for the `#VC` exception as
|
|
/// this would prevent proper handling of `NAE`s by the guest. Similarly, the hypervisor should avoid
|
|
/// setting intercept bits for events that would occur in the `#VC` handler (such as `IRET`).
|
|
///
|
|
/// The vector number of the ``#VC`` exception is 29.
|
|
pub vmm_communication_exception: Entry<HandlerFuncWithErrCode>,
|
|
|
|
/// 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: Entry<HandlerFuncWithErrCode>,
|
|
|
|
/// vector nr. 31
|
|
reserved_3: Entry<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.
|
|
interrupts: [Entry<HandlerFunc>; 256 - 32],
|
|
}
|
|
// spell:on
|
|
|
|
//
|
|
// MARK: impl
|
|
//
|
|
|
|
impl InterruptDescriptorTable {
|
|
/// Creates a new IDT filled with non-present entries.
|
|
#[inline]
|
|
pub const fn new() -> InterruptDescriptorTable {
|
|
InterruptDescriptorTable {
|
|
divide_error: Entry::missing(),
|
|
debug: Entry::missing(),
|
|
non_maskable_interrupt: Entry::missing(),
|
|
breakpoint: Entry::missing(),
|
|
overflow: Entry::missing(),
|
|
bound_range_exceeded: Entry::missing(),
|
|
invalid_opcode: Entry::missing(),
|
|
device_not_available: Entry::missing(),
|
|
double_fault: Entry::missing(),
|
|
coprocessor_segment_overrun: Entry::missing(),
|
|
invalid_tss: Entry::missing(),
|
|
segment_not_present: Entry::missing(),
|
|
stack_segment_fault: Entry::missing(),
|
|
general_protection_fault: Entry::missing(),
|
|
page_fault: Entry::missing(),
|
|
reserved_1: Entry::missing(),
|
|
x87_floating_point: Entry::missing(),
|
|
alignment_check: Entry::missing(),
|
|
machine_check: Entry::missing(),
|
|
simd_floating_point: Entry::missing(),
|
|
virtualization: Entry::missing(),
|
|
cp_protection_exception: Entry::missing(),
|
|
reserved_2: [Entry::missing(); 6],
|
|
hv_injection_exception: Entry::missing(),
|
|
vmm_communication_exception: Entry::missing(),
|
|
security_exception: Entry::missing(),
|
|
reserved_3: Entry::missing(),
|
|
interrupts: [Entry::missing(); 256 - 32],
|
|
}
|
|
}
|
|
|
|
/// Loads the IDT in the CPU using the `lidt` command.
|
|
#[inline]
|
|
pub fn load(&'static self) {
|
|
unsafe { self.load_unsafe() }
|
|
}
|
|
|
|
/// Loads the IDT in the CPU using the `lidt` command.
|
|
///
|
|
/// # Safety
|
|
///
|
|
/// As long as it is the active IDT, you must ensure that:
|
|
///
|
|
/// - `self` is never destroyed.
|
|
/// - `self` always stays at the same memory location.
|
|
/// It is recommended to wrap it in a `Box`.
|
|
#[inline]
|
|
pub unsafe fn load_unsafe(&self) {
|
|
let idtr = {
|
|
Idtr {
|
|
size: (size_of::<InterruptDescriptorTable>() - 1) as u16,
|
|
offset: self as *const _ as u32,
|
|
}
|
|
};
|
|
|
|
unsafe {
|
|
asm!("lidt [{}]", in(reg) &idtr, options(readonly, nostack, preserves_flags));
|
|
}
|
|
}
|
|
}
|