Skip to main content

rustubs/arch/x86_64/interrupt/
idt.rs

1use crate::defs::ExternSyms;
2use crate::defs::HWDefs::*;
3use crate::io::*;
4use crate::ExternSyms::{IDT, IDT_DESCR, VECTORS_START};
5use core::arch::asm;
6use core::slice;
7
8/// initialize the idt: we reserved space for idt in assembly code (and linker
9/// script) we write contents to them now. One purpose is to save binary space.
10/// This also allows more flexibility for some interrupt handlers (e.g. when
11/// they needs a dedicated stack...)
12#[inline(always)]
13pub fn init() {
14	println!("[init] idt: VECTORS_START: 0x{:x}", VECTORS_START as usize);
15
16	let gate_descriptors: &mut [GateDescriptor64] = unsafe {
17		slice::from_raw_parts_mut(IDT as *mut GateDescriptor64, IDT_CAPACITY)
18	};
19
20	// write to idt
21	for i in 0..IDT_VALID {
22		let offset = VECTORS_START as usize + (i * VECTOR_SIZE);
23		gate_descriptors[i].set_default_interrupt(offset as u64);
24	}
25	let offset_inv: usize = VECTORS_START as usize + (IDT_VALID * VECTOR_SIZE);
26	for i in IDT_VALID..IDT_CAPACITY {
27		gate_descriptors[i].set_default_interrupt(offset_inv as u64);
28	}
29
30	// install the syscall gate (int 0x80) with DPL=3
31	gate_descriptors[0x80].set_syscall_gate(ExternSyms::VECTOR_128 as u64);
32
33	// set idtr
34	unsafe { asm! ("lidt [{}]", in(reg) IDT_DESCR) }
35}
36
37/// ```text
38/// <pre>
39/// [0 :15]  addr[0:15]
40/// [16:31]  segment selector: must point to valid code segment in GDT
41/// [32:39]  ist: index into Interrupt Stack Table; only lower 3 bit used,
42///          other bits are reserved to 0
43/// [40:47]  attrs: attributes of the call gate:
44///          [0:3]    - Gate Type: 0xe for interrupt and 0xf for trap
45///          [ 4 ]    - Res0
46///          [5:6]    - DPL: allowed privilege levels (via INT)
47///          [ 7 ]    - Present (Valid)
48/// [48:63] - addr[16:31]
49/// [64:95] - addr[32:63]
50/// ```
51#[repr(C)]
52#[repr(packed)]
53struct GateDescriptor64 {
54	pub offset_1: u16,
55	pub selector: u16,
56	pub ist: u8,
57	pub attrs: u8,
58	pub offset_2: u16,
59	pub offset_3: u32,
60	res0: u32, // [96:127]
61}
62
63// TODO expand interface for idt entry, if needed.
64impl GateDescriptor64 {
65	#[inline(always)]
66	fn set_offset(&mut self, offset: u64) {
67		self.offset_1 = (offset & 0xffff) as u16;
68		self.offset_2 = ((offset & 0xffff0000) >> 16) as u16;
69		self.offset_3 = ((offset & 0xffffffff00000000) >> 32) as u32;
70	}
71	// selector = 0; present; type = interrupt;
72	fn set_default_interrupt(&mut self, offset: u64) {
73		self.set_offset(offset);
74		self.selector = 0x8 * 2;
75		self.attrs = 0x8e;
76		self.ist = 0;
77		self.res0 = 0;
78	}
79	// syscall gate: DPL=3 so ring-3 code can invoke it via `int 0x80`.
80	// attrs = 0xee = present | DPL=3 | interrupt gate
81	fn set_syscall_gate(&mut self, offset: u64) {
82		self.set_offset(offset);
83		self.selector = crate::defs::HWDefs::SEG_KERNEL_CODE;
84		self.attrs = 0xee;
85		self.ist = 0;
86		self.res0 = 0;
87	}
88}