diff options
Diffstat (limited to 'kernel/src')
-rw-r--r-- | kernel/src/interrupts/apic.rs | 121 | ||||
-rw-r--r-- | kernel/src/interrupts/mod.rs | 1 | ||||
-rw-r--r-- | kernel/src/lib.rs | 1 | ||||
-rw-r--r-- | kernel/src/main.rs | 4 |
4 files changed, 127 insertions, 0 deletions
diff --git a/kernel/src/interrupts/apic.rs b/kernel/src/interrupts/apic.rs new file mode 100644 index 0000000..519caf2 --- /dev/null +++ b/kernel/src/interrupts/apic.rs @@ -0,0 +1,121 @@ +use x86_64::registers::model_specific::Msr; + +#[cfg(target_arch = "x86_64")] +const APIC_FLAG: u64 = 0x0800; +const APIC_BASE_MSR: u32 = 0x1b; + +pub fn is_x2apic() -> bool { + let info: u32; + unsafe { + asm!("cpuid" : "={ecx}" (info) : "{eax}" (1) : "memory"); + } + info & (1 << 21) != 0 +} + +pub fn is_apic() -> bool { + let info: u32; + unsafe { + asm!("cpuid" : "={edx}" (info) : "{eax}" (1) : "memory"); + } + info & (1 << 9) != 0 +} + +pub fn set_apic_base(v: u64) { + unsafe { Msr::new(APIC_BASE_MSR).write(v) } +} + +#[repr(C)] +pub struct InterruptCommand { + low: u32, + _rsv: [u32; 3], + high: u32 +} + +#[repr(isize)] +pub enum ApicRegister { + ApicId=8, + ApicVer=12, + TaskPriority=32, + ArbitrationPriority=36, + ProcessorPriority=40, + EndOfInterrupt=44, + LogicalDst=52, + DstFmt=56, + SpuriousInterruptVec=60, + InService=64, + TriggerMode=96, + InterruptRequest=128, + ErrorStatus=160, + InterruptCommand=192, + InterruptCommandHigh=196, + LvtTimer=200, + LvtThermalSensor=204, + LvtPerformanceMonitor=208, + LvtLint1=212, + LvtError=220, + TimerInitialCount=224, + TimerCurrentCount=228, + TimerDivideConfig=248, + ExtApicFeature=256, + ExtApicControl=260, + SpecificEndOfInterrupt=264, + InterruptEnable=288, + ExtInterruptLocalVectorTable=320, +} + +#[repr(C)] +pub struct ApicRegisters { + regs32: *mut [u32; 1024], +} + +impl ApicRegisters { + fn from_base_apic(base_apic: u64) -> Self { + Self { regs32: + ((base_apic >> 12) & 0xfffffffff) as *mut [u32; 1024] + } + } + + fn ptr(&self) -> *const u32 { self.regs32 as *const u32 } + fn ptr_mut(&mut self) -> *mut u32 { self.regs32 as *mut u32 } + + pub fn get(&self, register: ApicRegister) -> u32 { + unsafe { self.ptr().offset(register as isize).read_volatile() } + } + + pub fn set(&mut self, register: ApicRegister, val: u32) { + unsafe { self.ptr_mut().offset(register as isize).write_volatile(val) } + } + + pub fn set_interrupt_command(&mut self, dst: u8, v: u8) { + let (dst, v): (u32, u32) = (dst.into(), v.into()); + self.set(ApicRegister::InterruptCommandHigh, dst << 24); + self.set(ApicRegister::InterruptCommand, v) + } +} + +pub struct Apic { + base_apic: u64, + regs: ApicRegisters, +} + +impl Apic { + pub fn new() -> Option<Self> { + use core::fmt::Write; + let mut stdout = crate::io::vga_text::OStream::new(); + stdout.clear(); + + if !is_apic() { return None; } + let mut base_apic = unsafe { Msr::new(APIC_BASE_MSR).read() }; + let mut regs = ApicRegisters::from_base_apic(base_apic); + + let spurious = regs.get(ApicRegister::SpuriousInterruptVec) | 0x1ff; + + unsafe { Msr::new(APIC_BASE_MSR).write(base_apic | APIC_FLAG) }; + + regs.set(ApicRegister::SpuriousInterruptVec, spurious); + + Some(Self { + base_apic, regs + }) + } +} diff --git a/kernel/src/interrupts/mod.rs b/kernel/src/interrupts/mod.rs index 076c61c..bc313b8 100644 --- a/kernel/src/interrupts/mod.rs +++ b/kernel/src/interrupts/mod.rs @@ -1,3 +1,4 @@ +pub mod apic; pub mod exceptions; pub mod gdt; pub mod idt; diff --git a/kernel/src/lib.rs b/kernel/src/lib.rs index 8ab660d..adf14b4 100644 --- a/kernel/src/lib.rs +++ b/kernel/src/lib.rs @@ -3,6 +3,7 @@ #![feature(custom_test_frameworks)] #![feature(abi_x86_interrupt)] #![feature(panic_info_message)] +#![feature(asm)] #![test_runner(crate::testing::serial_test_runner)] #![reexport_test_harness_main = "test_main"] #![no_std] diff --git a/kernel/src/main.rs b/kernel/src/main.rs index bb1dcec..1613292 100644 --- a/kernel/src/main.rs +++ b/kernel/src/main.rs @@ -3,6 +3,7 @@ #![feature(custom_test_frameworks)] #![feature(abi_x86_interrupt)] #![feature(panic_info_message)] +#![feature(asm)] #![test_runner(crate::testing::test_runner)] #![no_std] @@ -18,6 +19,9 @@ pub use io::{qemu, serial, vga_text}; #[no_mangle] extern "C" fn _start() -> ! { kernel::init(); + + let apic = interrupts::apic::Apic::new(); + if cfg!(test) { qemu::exit_qemu(qemu::QemuExitCode::Success); } |