Nothing to see here, move along meow
1use x2apic::lapic::{LocalApicBuilder, TimerDivide, TimerMode, xapic_base};
2
3use super::idt::{ERROR_VECTOR, SPURIOUS_VECTOR, TIMER_VECTOR};
4
5const LAPIC_EOI_OFFSET: u64 = 0xB0;
6const IA32_X2APIC_EOI: u32 = 0x80B;
7
8struct ApicConfig {
9 virt_base: u64,
10 x2apic: bool,
11}
12
13static APIC_CONFIG: spin::Once<ApicConfig> = spin::Once::new();
14
15const PIC1_DATA: u16 = 0x21;
16const PIC2_DATA: u16 = 0xA1;
17const PIC1_CMD: u16 = 0x20;
18const PIC2_CMD: u16 = 0xA0;
19
20fn disable_legacy_pic() {
21 unsafe {
22 x86_64::instructions::port::Port::<u8>::new(PIC1_CMD).write(0x11);
23 x86_64::instructions::port::Port::<u8>::new(PIC2_CMD).write(0x11);
24 x86_64::instructions::port::Port::<u8>::new(PIC1_DATA).write(0x20);
25 x86_64::instructions::port::Port::<u8>::new(PIC2_DATA).write(0x28);
26 x86_64::instructions::port::Port::<u8>::new(PIC1_DATA).write(0x04);
27 x86_64::instructions::port::Port::<u8>::new(PIC2_DATA).write(0x02);
28 x86_64::instructions::port::Port::<u8>::new(PIC1_DATA).write(0x01);
29 x86_64::instructions::port::Port::<u8>::new(PIC2_DATA).write(0x01);
30 x86_64::instructions::port::Port::<u8>::new(PIC1_DATA).write(0xFF);
31 x86_64::instructions::port::Port::<u8>::new(PIC2_DATA).write(0xFF);
32 }
33}
34
35pub fn init(hhdm_offset: u64) {
36 disable_legacy_pic();
37
38 let apic_base = unsafe { xapic_base() };
39 let apic_virt = apic_base + hhdm_offset;
40
41 let mut lapic = LocalApicBuilder::new()
42 .timer_vector(TIMER_VECTOR.as_usize())
43 .error_vector(ERROR_VECTOR.as_usize())
44 .spurious_vector(SPURIOUS_VECTOR.as_usize())
45 .timer_mode(TimerMode::Periodic)
46 .timer_divide(TimerDivide::Div16)
47 .timer_initial(500_000)
48 .set_xapic_base(apic_virt)
49 .build()
50 .expect("Failed to build Local APIC");
51
52 unsafe { lapic.enable() };
53
54 let base_msr = unsafe { x86_64::registers::model_specific::Msr::new(0x1B).read() };
55 let is_x2apic = (base_msr & (1 << 10)) != 0;
56
57 APIC_CONFIG.call_once(|| ApicConfig {
58 virt_base: apic_virt,
59 x2apic: is_x2apic,
60 });
61
62 let pc = super::syscall::this_cpu();
63 unsafe { core::ptr::addr_of_mut!((*pc).lapic_base).write(apic_virt) };
64}
65
66pub fn tick() {
67 let pc = super::syscall::this_cpu();
68 unsafe {
69 let count =
70 core::sync::atomic::AtomicU64::from_ptr(core::ptr::addr_of_mut!((*pc).tick_count));
71 count.fetch_add(1, core::sync::atomic::Ordering::Relaxed);
72 }
73}
74
75#[allow(dead_code)]
76pub fn tick_count() -> u64 {
77 let pc = super::syscall::this_cpu();
78 unsafe {
79 let count =
80 core::sync::atomic::AtomicU64::from_ptr(core::ptr::addr_of_mut!((*pc).tick_count));
81 count.load(core::sync::atomic::Ordering::Relaxed)
82 }
83}
84
85#[allow(dead_code)]
86pub fn is_x2apic() -> bool {
87 APIC_CONFIG.get().expect("APIC not initialized").x2apic
88}
89
90const LAPIC_ESR_OFFSET: u64 = 0x280;
91const IA32_X2APIC_ESR: u32 = 0x828;
92
93pub unsafe fn read_and_clear_esr() -> u32 {
94 let config = APIC_CONFIG
95 .get()
96 .expect("read_and_clear_esr called before APIC init");
97 if config.x2apic {
98 unsafe {
99 x86_64::registers::model_specific::Msr::new(IA32_X2APIC_ESR).write(0);
100 x86_64::registers::model_specific::Msr::new(IA32_X2APIC_ESR).read() as u32
101 }
102 } else {
103 unsafe {
104 core::ptr::write_volatile((config.virt_base + LAPIC_ESR_OFFSET) as *mut u32, 0);
105 core::ptr::read_volatile((config.virt_base + LAPIC_ESR_OFFSET) as *const u32)
106 }
107 }
108}
109
110pub unsafe fn end_of_interrupt() {
111 let config = APIC_CONFIG
112 .get()
113 .expect("end_of_interrupt called before APIC init");
114 if config.x2apic {
115 unsafe {
116 x86_64::registers::model_specific::Msr::new(IA32_X2APIC_EOI).write(0);
117 }
118 } else {
119 unsafe {
120 core::ptr::write_volatile((config.virt_base + LAPIC_EOI_OFFSET) as *mut u32, 0);
121 }
122 }
123}