1use crate::process::ProcessContext;
2
3use self::{
4 gdt::{GlobalDescriptorTablePointer, SegmentSelector},
5 idt::InterruptDescriptorTablePointer,
6};
7
8pub mod gdt;
9pub mod idt;
10pub mod interrupts;
11
12const MAX_CPUS: usize = 8;
13
14pub mod flags {
15 pub const IF: u64 = 1 << 9;
16}
17
18#[allow(dead_code)]
19pub mod msr {
20 pub const APIC_BASE: u32 = 0x1b;
21 pub const EFER: u32 = 0xc0000080;
22
23 pub unsafe fn read(reg: u32) -> u64 {
24 let (eax, edx): (u32, u32);
25 core::arch::asm!("rdmsr", in("ecx") reg, out("eax") eax, out("edx") edx, options(readonly, nostack, preserves_flags));
26 ((edx as u64) << 32) | (eax as u64)
27 }
28
29 pub unsafe fn write(reg: u32, val: u64) {
30 let eax = val as u32;
31 let edx = (val >> 32) as u32;
32 core::arch::asm!("wrmsr", in("ecx") reg, in("eax") eax, in("edx") edx, options(readonly, nostack, preserves_flags));
33 }
34}
35
36#[allow(dead_code)]
37pub mod cpuid {
38 pub const FN_FEAT: u32 = 1;
39
40 pub const FEAT_EDX_TSC: u32 = 1 << 4;
41 pub const FEAT_EDX_APIC: u32 = 1 << 9;
42
43 #[macro_export]
44 macro_rules! cpuid {
45 ($rax:expr) => {
46 ::core::arch::x86_64::__cpuid_count($rax, 0)
47 };
48 ($rax:expr, $rcx:expr) => {
49 ::core::arch::x86_64::__cpuid_count($rax, $rcx)
50 };
51 }
52 #[allow(unused_imports)]
53 pub use cpuid;
54}
55
56static mut CPUS: [Cpu; MAX_CPUS] = [Cpu::empty(); MAX_CPUS];
57
58#[derive(Debug, Clone, Copy)]
59pub struct Cpu {
60 pub id: usize,
62 apic_id: u8,
63 old_interrupt_enable: bool,
64 n_cli: usize,
66
67 pub context: Option<ProcessContext>,
73 pub process_id: u64,
75 pub scheduling: bool,
76}
77
78impl Cpu {
79 const fn empty() -> Self {
80 Self {
81 id: 0,
82 apic_id: 0,
83 old_interrupt_enable: false,
84 n_cli: 0,
85 context: None,
86 process_id: 0,
87 scheduling: false,
88 }
89 }
90
91 fn init(&mut self, id: usize, apic_id: u8) {
92 self.id = id;
93 self.apic_id = apic_id;
94 }
95
96 pub fn push_cli(&mut self) {
97 if self.n_cli == 0 {
98 let rflags = unsafe { rflags() };
99 let old_interrupt_flag = rflags & flags::IF != 0;
100 unsafe { clear_interrupts() };
101 self.old_interrupt_enable = old_interrupt_flag;
102 }
103 let rflags = unsafe { rflags() };
105 assert!(self.n_cli < usize::MAX);
106 assert_eq!(rflags & flags::IF, 0);
107 self.n_cli += 1;
108 }
109
110 pub fn pop_cli(&mut self) {
111 let rflags = unsafe { rflags() };
112 assert!(self.n_cli > 0);
113 assert_eq!(rflags & flags::IF, 0);
114
115 self.n_cli -= 1;
116 if self.n_cli == 0 && self.old_interrupt_enable {
117 unsafe { set_interrupts() };
118 }
119 }
120
121 pub fn interrupts_disabled(&self) -> bool {
122 unsafe { rflags() & flags::IF == 0 }
123 }
124
125 pub fn n_cli(&self) -> usize {
126 self.n_cli
127 }
128}
129
130pub fn cpu() -> &'static mut Cpu {
131 unsafe { &mut CPUS[0] }
133}
134
135pub unsafe fn rflags() -> u64 {
136 let rflags: u64;
137 core::arch::asm!("pushfq; pop {0:r}", out(reg) rflags, options(nostack, preserves_flags));
138 rflags
139}
140
141unsafe fn outb(port: u16, val: u8) {
142 core::arch::asm!("out dx, al", in("al") val, in("dx") port, options(readonly, nostack, preserves_flags));
143}
144
145unsafe fn inb(port: u16) -> u8 {
146 let val: u8;
147 core::arch::asm!("in al, dx", out("al") val, in("dx") port, options(readonly, nostack, preserves_flags));
148 val
149}
150
151unsafe fn inw(port: u16) -> u16 {
152 let val: u16;
153 core::arch::asm!("in ax, dx", out("ax") val, in("dx") port, options(readonly, nostack, preserves_flags));
154 val
155}
156
157unsafe fn outw(port: u16, val: u16) {
158 core::arch::asm!("out dx, ax", in("ax") val, in("dx") port, options(readonly, nostack, preserves_flags));
159}
160
161unsafe fn outd(port: u16, val: u32) {
162 core::arch::asm!("out dx, eax", in("eax") val, in("dx") port, options(readonly, nostack, preserves_flags));
163}
164
165unsafe fn ind(port: u16) -> u32 {
166 let val: u32;
167 core::arch::asm!("in eax, dx", out("eax") val, in("dx") port, options(readonly, nostack, preserves_flags));
168 val
169}
170
171pub trait IoPortInt {
172 fn io_out(port: u16, val: Self);
173 fn io_in(port: u16) -> Self;
174}
175
176impl IoPortInt for u8 {
177 fn io_out(port: u16, val: Self) {
178 unsafe { outb(port, val) }
179 }
180 fn io_in(port: u16) -> Self {
181 unsafe { inb(port) }
182 }
183}
184
185impl IoPortInt for u16 {
186 fn io_out(port: u16, val: Self) {
187 unsafe { outw(port, val) }
188 }
189 fn io_in(port: u16) -> Self {
190 unsafe { inw(port) }
191 }
192}
193
194impl IoPortInt for u32 {
195 fn io_out(port: u16, val: Self) {
196 unsafe { outd(port, val) }
197 }
198 fn io_in(port: u16) -> Self {
199 unsafe { ind(port) }
200 }
201}
202
203pub unsafe fn io_out<T: IoPortInt>(port: u16, val: T) {
204 T::io_out(port, val);
205}
206
207pub unsafe fn io_in<T: IoPortInt>(port: u16) -> T {
208 T::io_in(port)
209}
210
211pub unsafe fn clear_interrupts() {
212 core::arch::asm!("cli", options(nomem, nostack, preserves_flags));
213}
214
215pub unsafe fn set_interrupts() {
216 core::arch::asm!("sti", options(nomem, nostack, preserves_flags));
217}
218
219#[allow(dead_code)]
220pub unsafe fn get_cr0() -> u64 {
221 let cr0: u64;
222 core::arch::asm!("mov {0:r}, cr0", out(reg) cr0, options(readonly, nostack, preserves_flags));
223 cr0
224}
225
226#[allow(dead_code)]
227pub unsafe fn set_cr0(cr0: u64) {
228 core::arch::asm!("mov cr0, rax", in("rax") cr0, options(nomem, nostack, preserves_flags));
229}
230
231pub unsafe fn set_cr3(cr3: u64) {
232 core::arch::asm!("mov cr3, rax", in("rax") cr3, options(nomem, nostack, preserves_flags));
233}
234
235pub unsafe fn get_cr3() -> u64 {
236 let cr3: u64;
237 core::arch::asm!("mov {0:r}, cr3", out(reg) cr3, options(readonly, nostack, preserves_flags));
238 cr3
239}
240
241#[allow(dead_code)]
242pub unsafe fn get_cr4() -> u64 {
243 let cr4: u64;
244 core::arch::asm!("mov {0:r}, cr4", out(reg) cr4, options(readonly, nostack, preserves_flags));
245 cr4
246}
247
248#[allow(dead_code)]
249pub unsafe fn set_cr4(cr4: u64) {
250 core::arch::asm!("mov cr4, rax", in("rax") cr4, options(nomem, nostack, preserves_flags));
251}
252
253unsafe fn lgdt(gdtr: &GlobalDescriptorTablePointer) {
255 core::arch::asm!("lgdt [rax]", in("rax") gdtr, options(readonly, nostack, preserves_flags));
256}
257
258unsafe fn lidt(ldtr: &InterruptDescriptorTablePointer) {
260 core::arch::asm!("lidt [rax]", in("rax") ldtr, options(readonly, nostack, preserves_flags));
261}
262
263unsafe fn ltr(tr: SegmentSelector) {
264 core::arch::asm!("ltr ax", in("ax") tr.0, options(nomem, nostack, preserves_flags));
265}
266
267unsafe fn set_cs(cs: SegmentSelector) {
268 core::arch::asm!(
269 "push {0:r}",
270 "lea {tmp}, [rip + 2f]",
273 "push {tmp}",
274 "retfq",
275 "2:",
276 in(reg) cs.0, tmp=lateout(reg) _, options(preserves_flags));
277}
278
279unsafe fn set_data_segments(ds: SegmentSelector) {
280 core::arch::asm!(
281 "mov ds, {0:r}",
282 "mov es, {0:r}",
283 "mov ss, {0:r}",
284 "mov fs, {0:r}",
285 "mov gs, {0:r}",
286 in(reg) ds.0, options(preserves_flags));
287}
288
289fn get_cs() -> u16 {
290 let cs: u16;
291 unsafe {
292 core::arch::asm!("mov {0:r}, cs", out(reg) cs, options(readonly, nostack, preserves_flags));
293 }
294 cs
295}
296
297pub unsafe fn halt() {
298 core::arch::asm!("hlt", options(nomem, nostack, preserves_flags));
299}
300
301pub unsafe fn invalidate_tlp(virtual_address: u64) {
302 core::arch::asm!("invlpg [{0}]", in(reg) virtual_address);
303}
304
305#[allow(dead_code)]
306pub unsafe fn read_tsc() -> u64 {
307 let (low, high): (u32, u32);
308 core::arch::asm!("rdtsc", out("eax") low, out("edx") high, options(nomem, nostack, preserves_flags));
309 ((high as u64) << 32) | (low as u64)
310}
311
312#[macro_export]
313macro_rules! rip {
314 () => {
315 {
316 let rip: u64;
317 unsafe {
318 core::arch::asm!("lea {0:r}, [rip]", out(reg) rip, options(nomem, nostack, preserves_flags));
319 }
320 rip
321 }
322 };
323}
324#[allow(unused_imports)]
325pub use rip;
326
327#[macro_export]
328macro_rules! rbp {
329 () => {
330 {
331 let rbp: u64;
332 unsafe {
333 core::arch::asm!("mov {0:r}, rbp", out(reg) rbp, options(nomem, nostack, preserves_flags));
334 }
335 rbp
336 }
337 };
338}
339#[allow(unused_imports)]
340pub use rbp;
341
342#[macro_export]
343macro_rules! rsp {
344 () => {
345 {
346 let rsp: u64;
347 unsafe {
348 core::arch::asm!("mov {0:r}, rsp", out(reg) rsp, options(nomem, nostack, preserves_flags));
349 }
350 rsp
351 }
352 };
353}
354#[allow(unused_imports)]
355pub use rsp;