Skip to content

Commit 83b52ea

Browse files
committed
Add 32-bit IDT types to second stage
1 parent 875f870 commit 83b52ea

File tree

2 files changed

+221
-2
lines changed

2 files changed

+221
-2
lines changed

src/stage_2/src/idt.rs

Lines changed: 214 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,214 @@
1+
use core::marker::PhantomData;
2+
3+
/// An Interrupt Descriptor Table with 32 entries.
4+
#[derive(Clone)]
5+
#[repr(C, align(16))]
6+
pub struct InterruptDescriptorTable {
7+
pub divide_error: Entry<HandlerFunc>,
8+
pub debug: Entry<HandlerFunc>,
9+
pub non_maskable_interrupt: Entry<HandlerFunc>,
10+
pub breakpoint: Entry<HandlerFunc>,
11+
pub overflow: Entry<HandlerFunc>,
12+
pub bound_range_exceeded: Entry<HandlerFunc>,
13+
pub invalid_opcode: Entry<HandlerFunc>,
14+
pub device_not_available: Entry<HandlerFunc>,
15+
pub double_fault: Entry<DivergingHandlerFuncWithErrCode>,
16+
coprocessor_segment_overrun: Entry<HandlerFunc>,
17+
pub invalid_tss: Entry<HandlerFuncWithErrCode>,
18+
pub segment_not_present: Entry<HandlerFuncWithErrCode>,
19+
pub stack_segment_fault: Entry<HandlerFuncWithErrCode>,
20+
pub general_protection_fault: Entry<HandlerFuncWithErrCode>,
21+
pub page_fault: Entry<HandlerFuncWithErrCode>,
22+
reserved_1: Entry<HandlerFunc>,
23+
pub x87_floating_point: Entry<HandlerFunc>,
24+
pub alignment_check: Entry<HandlerFuncWithErrCode>,
25+
pub machine_check: Entry<DivergingHandlerFunc>,
26+
pub simd_floating_point: Entry<HandlerFunc>,
27+
pub virtualization: Entry<HandlerFunc>,
28+
reserved_2: [Entry<HandlerFunc>; 9],
29+
pub security_exception: Entry<HandlerFuncWithErrCode>,
30+
reserved_3: Entry<HandlerFunc>,
31+
}
32+
33+
impl InterruptDescriptorTable {
34+
/// Creates a new IDT filled with non-present entries.
35+
#[inline]
36+
pub const fn new() -> InterruptDescriptorTable {
37+
InterruptDescriptorTable {
38+
divide_error: Entry::missing(),
39+
debug: Entry::missing(),
40+
non_maskable_interrupt: Entry::missing(),
41+
breakpoint: Entry::missing(),
42+
overflow: Entry::missing(),
43+
bound_range_exceeded: Entry::missing(),
44+
invalid_opcode: Entry::missing(),
45+
device_not_available: Entry::missing(),
46+
double_fault: Entry::missing(),
47+
coprocessor_segment_overrun: Entry::missing(),
48+
invalid_tss: Entry::missing(),
49+
segment_not_present: Entry::missing(),
50+
stack_segment_fault: Entry::missing(),
51+
general_protection_fault: Entry::missing(),
52+
page_fault: Entry::missing(),
53+
reserved_1: Entry::missing(),
54+
x87_floating_point: Entry::missing(),
55+
alignment_check: Entry::missing(),
56+
machine_check: Entry::missing(),
57+
simd_floating_point: Entry::missing(),
58+
virtualization: Entry::missing(),
59+
reserved_2: [Entry::missing(); 9],
60+
security_exception: Entry::missing(),
61+
reserved_3: Entry::missing(),
62+
}
63+
}
64+
65+
/// Loads the IDT in the CPU using the `lidt` command.
66+
pub fn load(&'static self) {
67+
unsafe { self.load_unsafe() }
68+
}
69+
70+
/// Loads the IDT in the CPU using the `lidt` command.
71+
///
72+
/// # Safety
73+
///
74+
/// As long as it is the active IDT, you must ensure that:
75+
///
76+
/// - `self` is never destroyed.
77+
/// - `self` always stays at the same memory location. It is recommended to wrap it in
78+
/// a `Box`.
79+
///
80+
pub unsafe fn load_unsafe(&self) {
81+
use core::mem::size_of;
82+
83+
let ptr = DescriptorTablePointer {
84+
base: self as *const _ as u32,
85+
limit: (size_of::<Self>() - 1) as u16,
86+
};
87+
88+
llvm_asm!("lidt ($0)" :: "r" (&ptr) : "memory");
89+
}
90+
}
91+
92+
/// A struct describing a pointer to a descriptor table (GDT / IDT).
93+
/// This is in a format suitable for giving to 'lgdt' or 'lidt'.
94+
#[derive(Debug, Clone, Copy)]
95+
#[repr(C, packed)]
96+
pub struct DescriptorTablePointer {
97+
/// Size of the DT.
98+
pub limit: u16,
99+
/// Pointer to the memory region containing the DT.
100+
pub base: u32,
101+
}
102+
103+
/// An Interrupt Descriptor Table entry.
104+
///
105+
/// The generic parameter can either be `HandlerFunc` or `HandlerFuncWithErrCode`, depending
106+
/// on the interrupt vector.
107+
#[derive(Debug, Clone, Copy, PartialEq)]
108+
#[repr(C)]
109+
pub struct Entry<F> {
110+
offset_low: u16,
111+
gdt_selector: u16,
112+
zero: u8,
113+
options: EntryOptions,
114+
offset_high: u16,
115+
phantom: PhantomData<F>,
116+
}
117+
118+
impl<F> Entry<F> {
119+
/// Creates a non-present IDT entry (but sets the must-be-one bits).
120+
#[inline]
121+
pub const fn missing() -> Self {
122+
Entry {
123+
gdt_selector: 0,
124+
offset_low: 0,
125+
offset_high: 0,
126+
zero: 0,
127+
options: EntryOptions::minimal(),
128+
phantom: PhantomData,
129+
}
130+
}
131+
/// Set the handler address for the IDT entry and sets the present bit.
132+
///
133+
/// For the code selector field, this function uses the code segment selector currently
134+
/// active in the CPU.
135+
///
136+
/// The function returns a mutable reference to the entry's options that allows
137+
/// further customization.
138+
#[inline]
139+
fn set_handler_addr(&mut self, addr: u32) -> &mut EntryOptions {
140+
self.offset_low = addr as u16;
141+
self.offset_high = (addr >> 16) as u16;
142+
143+
let segment: u16;
144+
unsafe { llvm_asm!("mov %cs, $0" : "=r" (segment) ) };
145+
146+
self.gdt_selector = segment;
147+
148+
self.options.set_present(true);
149+
&mut self.options
150+
}
151+
}
152+
153+
macro_rules! impl_set_handler_fn {
154+
($h:ty) => {
155+
impl Entry<$h> {
156+
/// Set the handler function for the IDT entry and sets the present bit.
157+
///
158+
/// For the code selector field, this function uses the code segment selector currently
159+
/// active in the CPU.
160+
///
161+
/// The function returns a mutable reference to the entry's options that allows
162+
/// further customization.
163+
#[inline]
164+
pub fn set_handler_fn(&mut self, handler: $h) -> &mut EntryOptions {
165+
self.set_handler_addr(handler as u32)
166+
}
167+
}
168+
};
169+
}
170+
171+
impl_set_handler_fn!(HandlerFunc);
172+
impl_set_handler_fn!(HandlerFuncWithErrCode);
173+
impl_set_handler_fn!(DivergingHandlerFunc);
174+
impl_set_handler_fn!(DivergingHandlerFuncWithErrCode);
175+
176+
/// Represents the options field of an IDT entry.
177+
#[repr(transparent)]
178+
#[derive(Debug, Clone, Copy, PartialEq)]
179+
pub struct EntryOptions(u8);
180+
181+
impl EntryOptions {
182+
/// Creates a minimal options field with all the must-be-one bits set.
183+
#[inline]
184+
const fn minimal() -> Self {
185+
EntryOptions(0b1110)
186+
}
187+
188+
/// Set or reset the preset bit.
189+
#[inline]
190+
pub fn set_present(&mut self, present: bool) -> &mut Self {
191+
self.0.set_bit(15, present);
192+
self
193+
}
194+
}
195+
196+
/// A handler function for an interrupt or an exception without error code.
197+
pub type HandlerFunc = extern "x86-interrupt" fn(&mut InterruptStackFrame);
198+
/// A handler function for an exception that pushes an error code.
199+
pub type HandlerFuncWithErrCode =
200+
extern "x86-interrupt" fn(&mut InterruptStackFrame, error_code: u64);
201+
/// A handler function that must not return, e.g. for a machine check exception.
202+
pub type DivergingHandlerFunc = extern "x86-interrupt" fn(&mut InterruptStackFrame) -> !;
203+
/// A handler function with an error code that must not return, e.g. for a double fault exception.
204+
pub type DivergingHandlerFuncWithErrCode =
205+
extern "x86-interrupt" fn(&mut InterruptStackFrame, error_code: u64) -> !;
206+
207+
/// Represents the interrupt stack frame pushed by the CPU on interrupt or exception entry.
208+
#[derive(Clone)]
209+
#[repr(C)]
210+
pub struct InterruptStackFrame {
211+
eip: u32,
212+
cs: u32,
213+
eflags: u32,
214+
}

src/stage_2/src/lib.rs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,14 @@
11
#![no_std]
2+
#![feature(abi_x86_interrupt)]
3+
#![feature(const_fn)]
4+
#![feature(llvm_asm)]
25

36
use shared::console::println;
47

8+
mod idt;
9+
510
#[no_mangle]
611
pub fn second_stage() -> u16 {
7-
println(b"Stage 2");
12+
println(b"Stage 2");
813
return 12345;
9-
}
14+
}

0 commit comments

Comments
 (0)