Added IDT
This commit is contained in:
parent
db859ddeb8
commit
9c515cfab0
3
tetros/Cargo.lock
generated
3
tetros/Cargo.lock
generated
@ -1,6 +1,6 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
version = 4
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
@ -79,6 +79,7 @@ dependencies = [
|
||||
name = "tetros"
|
||||
version = "1.0.0"
|
||||
dependencies = [
|
||||
"bitflags 2.8.0",
|
||||
"lazy_static",
|
||||
"spin",
|
||||
"uart_16550",
|
||||
|
@ -64,6 +64,7 @@ identity_op = "allow"
|
||||
|
||||
|
||||
[dependencies]
|
||||
bitflags = "2.8.0"
|
||||
spin = "0.9.8"
|
||||
uart_16550 = "0.3.2"
|
||||
|
||||
|
127
tetros/src/idt/entry.rs
Normal file
127
tetros/src/idt/entry.rs
Normal file
@ -0,0 +1,127 @@
|
||||
use core::{
|
||||
fmt::{self},
|
||||
marker::PhantomData,
|
||||
};
|
||||
|
||||
use super::{HandlerFuncType, VirtAddr};
|
||||
|
||||
/// An Interrupt Descriptor Table entry.
|
||||
///
|
||||
/// The generic parameter is some [`HandlerFuncType`], depending on the interrupt vector.
|
||||
///
|
||||
/// For reference, see https://wiki.osdev.org/Interrupt_Descriptor_Table#Gate_Descriptor
|
||||
#[derive(Clone, Copy)]
|
||||
#[repr(C)]
|
||||
pub struct Entry<F> {
|
||||
phantom: PhantomData<F>,
|
||||
|
||||
/// Low 16 bits of the 32-bit ISR pointer
|
||||
pointer_low: u16,
|
||||
|
||||
/// A segment selector that points to a valid code segment in the GDT
|
||||
segment: u16,
|
||||
|
||||
/// Always zero
|
||||
reserved: u8,
|
||||
|
||||
/// Interrupt options.
|
||||
///
|
||||
/// Bits 0-3: gate type. One of:
|
||||
/// - 0101: Task Gate, note that in this case, the Offset value is unused and should be set to zero.
|
||||
/// - 0110: 16-bit Interrupt Gate
|
||||
/// - 0111: 16-bit Trap Gate
|
||||
/// - 1110: 32-bit Interrupt Gate
|
||||
/// - 1111: 32-bit Trap Gate
|
||||
///
|
||||
/// Bit 4: always zero
|
||||
///
|
||||
/// Bits 5-6: DPL, defines cpu privilege level require to INT this interrupt.
|
||||
/// For our purposes, always zero.
|
||||
///
|
||||
/// Bit 7: 1 if present, 0 if not.
|
||||
///
|
||||
/// Note that these appear in reverse order in Rust.
|
||||
/// For example, 0b1000_1110 sets `present = 1` and `type = 1110`.
|
||||
options: u8,
|
||||
|
||||
/// High 16 bits of the 32-bit ISR pointer
|
||||
pointer_high: u16,
|
||||
}
|
||||
|
||||
impl<T> fmt::Debug for Entry<T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("Entry")
|
||||
.field("preset", &self.is_present())
|
||||
.field(
|
||||
"handler_addr",
|
||||
&format_args!("{:#x}", self.handler_addr().unwrap_or(VirtAddr::new(0))),
|
||||
)
|
||||
.field("options", &self.options)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl<F> Entry<F> {
|
||||
/// Create a valid non-present IDT entry.
|
||||
#[inline]
|
||||
pub const fn missing() -> Self {
|
||||
Entry {
|
||||
pointer_low: 0,
|
||||
segment: 0,
|
||||
reserved: 0,
|
||||
options: 0,
|
||||
pointer_high: 0,
|
||||
phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets the handler address for the IDT entry and sets the following defaults:
|
||||
/// - The code selector is the code segment currently active in the CPU
|
||||
/// - The present bit is set
|
||||
/// - Interrupts are disabled on handler invocation
|
||||
/// - The privilege level (DPL) is [`PrivilegeLevel::Ring0`]
|
||||
/// - No IST is configured (existing stack will be used)
|
||||
/// - This is a 32-bit interrupt gate
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// The caller must ensure that `addr` is the address of a valid interrupt handler function,
|
||||
/// and the signature of such a function is correct for the entry type.
|
||||
#[inline]
|
||||
pub unsafe fn set_handler_addr(&mut self, addr: VirtAddr) {
|
||||
let ptr = addr.as_u32();
|
||||
|
||||
self.pointer_low = ptr as u16;
|
||||
self.pointer_high = (ptr >> 16) as u16;
|
||||
self.options = 0b1000_1110; // Present 32-bit interrupt gate
|
||||
|
||||
// SAFETY: The current CS is a valid, long-mode code segment.
|
||||
self.segment = super::util::get_cs();
|
||||
}
|
||||
|
||||
/// True if the "present" bit is set, false otherwise.
|
||||
pub fn is_present(&self) -> bool {
|
||||
return self.options & 0b1000_0000 == 0b1000_0000;
|
||||
}
|
||||
|
||||
/// Get the address of this entry's handler.
|
||||
pub fn handler_addr(&self) -> Option<VirtAddr> {
|
||||
self.is_present().then_some(VirtAddr::new(
|
||||
self.pointer_low as u32 + ((self.pointer_high as u32) << 16),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
impl<F: HandlerFuncType> Entry<F> {
|
||||
/// Sets the handler address for the IDT entry and sets the following defaults:
|
||||
/// - The code selector is the code segment currently active in the CPU
|
||||
/// - The present bit is set
|
||||
/// - Interrupts are disabled on handler invocation
|
||||
/// - The privilege level (DPL) is [`PrivilegeLevel::Ring0`]
|
||||
/// - No IST is configured (existing stack will be used)
|
||||
/// - This is a 32-bit interrupt gate
|
||||
#[inline]
|
||||
pub fn set_handler_fn(&mut self, handler: F) {
|
||||
unsafe { self.set_handler_addr(handler.to_virt_addr()) }
|
||||
}
|
||||
}
|
117
tetros/src/idt/handler.rs
Normal file
117
tetros/src/idt/handler.rs
Normal file
@ -0,0 +1,117 @@
|
||||
use bitflags::bitflags;
|
||||
|
||||
use super::{InterruptStackFrame, VirtAddr};
|
||||
|
||||
bitflags! {
|
||||
/// Describes an page fault error code.
|
||||
///
|
||||
/// This structure is defined by the following manual sections:
|
||||
/// * AMD Volume 2: 8.4.2
|
||||
/// * Intel Volume 3A: 4.7
|
||||
#[repr(transparent)]
|
||||
#[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Clone, Copy)]
|
||||
pub struct 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;
|
||||
|
||||
/// 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;
|
||||
|
||||
/// If this flag is set, it indicates that the page fault was caused by a protection key.
|
||||
const PROTECTION_KEY = 1 << 5;
|
||||
|
||||
/// If this flag is set, it indicates that the page fault was caused by a shadow stack
|
||||
/// access.
|
||||
const SHADOW_STACK = 1 << 6;
|
||||
|
||||
/// If this flag is set, it indicates that the page fault was caused by SGX access-control
|
||||
/// requirements (Intel-only).
|
||||
const SGX = 1 << 15;
|
||||
|
||||
/// If this flag is set, it indicates that the page fault is a result of the processor
|
||||
/// encountering an RMP violation (AMD-only).
|
||||
const RMP = 1 << 31;
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
/// MARK: types
|
||||
///
|
||||
|
||||
/// A handler function for an interrupt or an exception without error code.
|
||||
pub type HandlerFunc = extern "x86-interrupt" fn(InterruptStackFrame);
|
||||
|
||||
/// A handler function for an exception that pushes an error code.
|
||||
pub type HandlerFuncWithErrCode = extern "x86-interrupt" fn(InterruptStackFrame, error_code: u64);
|
||||
|
||||
/// A handler function that must not return, e.g. for a machine check exception.
|
||||
pub type DivergingHandlerFunc = extern "x86-interrupt" fn(InterruptStackFrame) -> !;
|
||||
|
||||
/// A handler function with an error code that must not return, e.g. for a double fault exception.
|
||||
pub type DivergingHandlerFuncWithErrCode =
|
||||
extern "x86-interrupt" fn(InterruptStackFrame, error_code: u64) -> !;
|
||||
|
||||
/// A page fault handler function that pushes a page fault error code.
|
||||
pub type PageFaultHandlerFunc =
|
||||
extern "x86-interrupt" fn(InterruptStackFrame, error_code: PageFaultErrorCode);
|
||||
|
||||
/// A common trait for all handler functions usable in [`Entry`].
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// Implementors have to ensure that `to_virt_addr` returns a valid address.
|
||||
pub unsafe trait HandlerFuncType {
|
||||
/// Get the virtual address of the handler function.
|
||||
fn to_virt_addr(self) -> VirtAddr;
|
||||
}
|
||||
|
||||
unsafe impl HandlerFuncType for HandlerFunc {
|
||||
#[inline]
|
||||
fn to_virt_addr(self) -> VirtAddr {
|
||||
VirtAddr::new(self as u32)
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl HandlerFuncType for HandlerFuncWithErrCode {
|
||||
#[inline]
|
||||
fn to_virt_addr(self) -> VirtAddr {
|
||||
VirtAddr::new(self as u32)
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl HandlerFuncType for DivergingHandlerFunc {
|
||||
#[inline]
|
||||
fn to_virt_addr(self) -> VirtAddr {
|
||||
VirtAddr::new(self as u32)
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl HandlerFuncType for DivergingHandlerFuncWithErrCode {
|
||||
#[inline]
|
||||
fn to_virt_addr(self) -> VirtAddr {
|
||||
VirtAddr::new(self as u32)
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl HandlerFuncType for PageFaultHandlerFunc {
|
||||
#[inline]
|
||||
fn to_virt_addr(self) -> VirtAddr {
|
||||
VirtAddr::new(self as u32)
|
||||
}
|
||||
}
|
22
tetros/src/idt/mod.rs
Normal file
22
tetros/src/idt/mod.rs
Normal file
@ -0,0 +1,22 @@
|
||||
//! IDT structures and routines for 32-bit x86.
|
||||
//!
|
||||
//! Based on code from the `x86_64` crate.
|
||||
//! Many comments are copied verbatim.
|
||||
//! (most notably, in `table.rs`)
|
||||
|
||||
mod virtaddr;
|
||||
pub use virtaddr::*;
|
||||
|
||||
mod entry;
|
||||
pub use entry::*;
|
||||
|
||||
mod table;
|
||||
pub use table::*;
|
||||
|
||||
mod handler;
|
||||
pub use handler::*;
|
||||
|
||||
mod stackframe;
|
||||
pub use stackframe::*;
|
||||
|
||||
mod util;
|
142
tetros/src/idt/stackframe.rs
Normal file
142
tetros/src/idt/stackframe.rs
Normal file
@ -0,0 +1,142 @@
|
||||
use core::{fmt, ops::Deref};
|
||||
|
||||
use super::VirtAddr;
|
||||
|
||||
use bitflags::bitflags;
|
||||
|
||||
bitflags! {
|
||||
/// The EFLAGS register. All bit patterns are valid representations for this type.
|
||||
///
|
||||
/// See https://wiki.osdev.org/CPU_Registers_x86#EFLAGS_Register
|
||||
#[repr(transparent)]
|
||||
#[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Clone, Copy)]
|
||||
pub struct EFlags: u32 {
|
||||
/// Processor feature identification flag.
|
||||
///
|
||||
/// If this flag is modifiable, the CPU supports CPUID.
|
||||
const ID = 1 << 21;
|
||||
|
||||
/// Indicates that an external, maskable interrupt is pending.
|
||||
///
|
||||
/// Used when virtual-8086 mode extensions (CR4.VME) or protected-mode virtual
|
||||
/// interrupts (CR4.PVI) are activated.
|
||||
const VIRTUAL_INTERRUPT_PENDING = 1 << 20;
|
||||
|
||||
/// Virtual image of the INTERRUPT_FLAG bit.
|
||||
///
|
||||
/// Used when virtual-8086 mode extensions (CR4.VME) or protected-mode virtual
|
||||
/// interrupts (CR4.PVI) are activated.
|
||||
const VIRTUAL_INTERRUPT = 1 << 19;
|
||||
|
||||
/// Enable automatic alignment checking if CR0.AM is set. Only works if CPL is 3.
|
||||
const ALIGNMENT_CHECK = 1 << 18;
|
||||
|
||||
/// Enable the virtual-8086 mode.
|
||||
const VIRTUAL_8086_MODE = 1 << 17;
|
||||
|
||||
/// Allows to restart an instruction following an instruction breakpoint.
|
||||
const RESUME_FLAG = 1 << 16;
|
||||
|
||||
/// Used by `iret` in hardware task switch mode to determine if current task is nested.
|
||||
const NESTED_TASK = 1 << 14;
|
||||
|
||||
/// The high bit of the I/O Privilege Level field.
|
||||
///
|
||||
/// Specifies the privilege level required for executing I/O address-space instructions.
|
||||
const IOPL_HIGH = 1 << 13;
|
||||
|
||||
/// The low bit of the I/O Privilege Level field.
|
||||
///
|
||||
/// Specifies the privilege level required for executing I/O address-space instructions.
|
||||
const IOPL_LOW = 1 << 12;
|
||||
|
||||
/// Set by hardware to indicate that the sign bit of the result of the last signed integer
|
||||
/// operation differs from the source operands.
|
||||
const OVERFLOW_FLAG = 1 << 11;
|
||||
|
||||
/// Determines the order in which strings are processed.
|
||||
const DIRECTION_FLAG = 1 << 10;
|
||||
|
||||
/// Enable interrupts.
|
||||
const INTERRUPT_FLAG = 1 << 9;
|
||||
|
||||
/// Enable single-step mode for debugging.
|
||||
const TRAP_FLAG = 1 << 8;
|
||||
|
||||
/// Set by hardware if last arithmetic operation resulted in a negative value.
|
||||
const SIGN_FLAG = 1 << 7;
|
||||
|
||||
/// Set by hardware if last arithmetic operation resulted in a zero value.
|
||||
const ZERO_FLAG = 1 << 6;
|
||||
|
||||
/// Set by hardware if last arithmetic operation generated a carry ouf of bit 3 of the
|
||||
/// result.
|
||||
const AUXILIARY_CARRY_FLAG = 1 << 4;
|
||||
|
||||
/// Set by hardware if last result has an even number of 1 bits (only for some operations).
|
||||
const PARITY_FLAG = 1 << 2;
|
||||
|
||||
/// Set by hardware if last arithmetic operation generated a carry out of the
|
||||
/// most-significant bit of the result.
|
||||
const CARRY_FLAG = 1;
|
||||
}
|
||||
}
|
||||
|
||||
/// Wrapper type for the interrupt stack frame pushed by the CPU.
|
||||
///
|
||||
/// This type derefs to an [`InterruptStackFrameValue`], which allows reading the actual values.
|
||||
///
|
||||
/// This wrapper type ensures that no accidental modification of the interrupt stack frame
|
||||
/// occurs, which can cause undefined behavior (see the [`as_mut`](InterruptStackFrame::as_mut)
|
||||
/// method for more information).
|
||||
#[repr(transparent)]
|
||||
pub struct InterruptStackFrame(InterruptStackFrameValue);
|
||||
|
||||
impl Deref for InterruptStackFrame {
|
||||
type Target = InterruptStackFrameValue;
|
||||
|
||||
#[inline]
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for InterruptStackFrame {
|
||||
#[inline]
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
self.0.fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents the interrupt stack frame pushed by the CPU on interrupt or exception entry.
|
||||
///
|
||||
/// See https://wiki.osdev.org/Interrupt_Service_Routines#x86
|
||||
#[derive(Clone, Copy)]
|
||||
#[repr(C)]
|
||||
pub struct InterruptStackFrameValue {
|
||||
/// 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.
|
||||
pub eip: VirtAddr,
|
||||
|
||||
/// The code segment selector at the time of the interrupt.
|
||||
pub cs: u16,
|
||||
|
||||
/// Padding for CS
|
||||
_reserved1: [u8; 2],
|
||||
|
||||
/// The EFLAGS register before the interrupt handler was invoked.
|
||||
pub cpu_flags: EFlags,
|
||||
}
|
||||
|
||||
impl fmt::Debug for InterruptStackFrameValue {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let mut s = f.debug_struct("InterruptStackFrame");
|
||||
s.field("instruction_pointer", &self.eip);
|
||||
s.field("code_segment", &self.cs);
|
||||
s.field("cpu_flags", &self.cpu_flags);
|
||||
s.finish()
|
||||
}
|
||||
}
|
482
tetros/src/idt/table.rs
Normal file
482
tetros/src/idt/table.rs
Normal file
@ -0,0 +1,482 @@
|
||||
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
|
||||
//
|
||||
|
||||
#[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],
|
||||
}
|
||||
|
||||
//
|
||||
// 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));
|
||||
}
|
||||
}
|
||||
}
|
10
tetros/src/idt/util.rs
Normal file
10
tetros/src/idt/util.rs
Normal file
@ -0,0 +1,10 @@
|
||||
use core::arch::asm;
|
||||
|
||||
/// Get the current value of the CS register
|
||||
pub(super) fn get_cs() -> u16 {
|
||||
let segment: u16;
|
||||
unsafe {
|
||||
asm!("mov {0:x}, cs", out(reg) segment, options(nomem, nostack, preserves_flags));
|
||||
}
|
||||
segment
|
||||
}
|
96
tetros/src/idt/virtaddr.rs
Normal file
96
tetros/src/idt/virtaddr.rs
Normal file
@ -0,0 +1,96 @@
|
||||
use core::{
|
||||
fmt::{self},
|
||||
ops::{Add, AddAssign, Sub, SubAssign},
|
||||
};
|
||||
|
||||
/// A canonical 32-bit virtual memory address.
|
||||
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
#[repr(transparent)]
|
||||
pub struct VirtAddr(u32);
|
||||
|
||||
impl VirtAddr {
|
||||
/// Create a new [`VirtAddr`] from a [`u32`].
|
||||
#[inline]
|
||||
pub const fn new(addr: u32) -> Self {
|
||||
Self(addr)
|
||||
}
|
||||
|
||||
/// Converts this address to a `u32`.
|
||||
#[inline]
|
||||
pub const fn as_u32(self) -> u32 {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for VirtAddr {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_tuple("VirtAddr")
|
||||
.field(&format_args!("{:#x}", self.0))
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Binary for VirtAddr {
|
||||
#[inline]
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
fmt::Binary::fmt(&self.0, f)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::LowerHex for VirtAddr {
|
||||
#[inline]
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
fmt::LowerHex::fmt(&self.0, f)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Octal for VirtAddr {
|
||||
#[inline]
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
fmt::Octal::fmt(&self.0, f)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::UpperHex for VirtAddr {
|
||||
#[inline]
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
fmt::UpperHex::fmt(&self.0, f)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Pointer for VirtAddr {
|
||||
#[inline]
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
fmt::Pointer::fmt(&(self.0 as *const ()), f)
|
||||
}
|
||||
}
|
||||
|
||||
impl Add<u32> for VirtAddr {
|
||||
type Output = Self;
|
||||
#[inline]
|
||||
fn add(self, rhs: u32) -> Self::Output {
|
||||
VirtAddr(self.0.checked_add(rhs).unwrap())
|
||||
}
|
||||
}
|
||||
|
||||
impl AddAssign<u32> for VirtAddr {
|
||||
#[inline]
|
||||
fn add_assign(&mut self, rhs: u32) {
|
||||
*self = *self + rhs;
|
||||
}
|
||||
}
|
||||
|
||||
impl Sub<u32> for VirtAddr {
|
||||
type Output = Self;
|
||||
#[inline]
|
||||
fn sub(self, rhs: u32) -> Self::Output {
|
||||
VirtAddr(self.0.checked_sub(rhs).unwrap())
|
||||
}
|
||||
}
|
||||
|
||||
impl SubAssign<u32> for VirtAddr {
|
||||
#[inline]
|
||||
fn sub_assign(&mut self, rhs: u32) {
|
||||
*self = *self - rhs;
|
||||
}
|
||||
}
|
@ -1,13 +1,19 @@
|
||||
#![no_std]
|
||||
#![feature(int_roundings)]
|
||||
#![feature(lang_items)]
|
||||
#![feature(abi_x86_interrupt)]
|
||||
#![allow(internal_features)]
|
||||
|
||||
use drivers::vga::Vga13h;
|
||||
use os::thunk::ThunkData;
|
||||
use core::arch::asm;
|
||||
use lazy_static::lazy_static;
|
||||
use spin::Mutex;
|
||||
|
||||
use drivers::vga::Vga13h;
|
||||
use idt::{InterruptDescriptorTable, InterruptStackFrame};
|
||||
use os::thunk::ThunkData;
|
||||
use tetrisboard::TetrisBoard;
|
||||
|
||||
mod idt;
|
||||
mod os;
|
||||
mod tetrisboard;
|
||||
|
||||
@ -16,12 +22,25 @@ mod drivers;
|
||||
|
||||
pub(crate) static VGA: Mutex<Vga13h> = Mutex::new(unsafe { Vga13h::new() });
|
||||
|
||||
lazy_static! {
|
||||
static ref IDT: InterruptDescriptorTable = {
|
||||
let mut idt = InterruptDescriptorTable::new();
|
||||
idt.divide_error.set_handler_fn(breakpoint_handler);
|
||||
|
||||
idt
|
||||
};
|
||||
}
|
||||
|
||||
extern "x86-interrupt" fn breakpoint_handler(stack_frame: InterruptStackFrame) {
|
||||
println!("EXCEPTION {:?}", stack_frame);
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn start(
|
||||
_boot_disk: usize,
|
||||
thunk10: extern "C" fn(),
|
||||
_thunk13: extern "C" fn(),
|
||||
thunk15: extern "C" fn(),
|
||||
_thunk15: extern "C" fn(),
|
||||
_thunk16: extern "C" fn(),
|
||||
) -> ! {
|
||||
println!("Entered Rust, serial ready.");
|
||||
@ -41,23 +60,18 @@ pub unsafe extern "C" fn start(
|
||||
data.with(thunk10);
|
||||
}
|
||||
|
||||
{
|
||||
// Initialize OS
|
||||
IDT.load();
|
||||
}
|
||||
|
||||
// Clear screen
|
||||
let mut v = VGA.lock();
|
||||
|
||||
/*
|
||||
let fb = v.get_fb();
|
||||
let mut c: u8 = 0;
|
||||
for x in 0..128 {
|
||||
for y in 0..128 {
|
||||
let idx = Vga13h::pix_idx(x, y);
|
||||
fb[idx] = c;
|
||||
c = c.wrapping_add(1);
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
let t = TetrisBoard::new();
|
||||
t.draw(&mut v);
|
||||
|
||||
asm!("int $0");
|
||||
|
||||
panic!("kernel");
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user