From 15f59e68d5d10abd49d928a50fb06a289a4d51e7 Mon Sep 17 00:00:00 2001 From: zxq5 Date: Wed, 26 Feb 2025 03:48:11 +0000 Subject: [PATCH] initial apic implementaion (Commented out / NOT WORKING) --- kernel/src/arch/x86_64/interrupts.rs | 9 +- libk/src/drivers/apic/mod.rs | 45 ++++++++ libk/src/drivers/cpu.rs | 26 +++++ libk/src/drivers/mod.rs | 4 + libk/src/drivers/pic.rs | 152 +++++++++++++++++++++++++++ 5 files changed, 233 insertions(+), 3 deletions(-) create mode 100644 libk/src/drivers/apic/mod.rs create mode 100644 libk/src/drivers/cpu.rs create mode 100644 libk/src/drivers/pic.rs diff --git a/kernel/src/arch/x86_64/interrupts.rs b/kernel/src/arch/x86_64/interrupts.rs index 56f13c7..8035918 100644 --- a/kernel/src/arch/x86_64/interrupts.rs +++ b/kernel/src/arch/x86_64/interrupts.rs @@ -1,8 +1,9 @@ +use libk::drivers::apic::enable_apic; +use libk::drivers::pic::ChainedPics; use libk::prelude::*; use x86_64::registers::control::Cr2; use x86_64::structures::idt::{InterruptDescriptorTable, InterruptStackFrame, PageFaultErrorCode}; -use pic8259::ChainedPics; use spin::{Lazy, Mutex}; use super::gdt; @@ -21,8 +22,8 @@ static IDT: Lazy = Lazy::new(|| { .set_handler_fn(general_protection_fault_handler); idt.page_fault.set_handler_fn(page_fault_handler); - idt[InterruptIndex::Timer.as_u8()].set_handler_fn(timer_interrupt_handler); - idt[InterruptIndex::Keyboard.as_u8()].set_handler_fn(keyboard_interrupt_handler); + // idt[InterruptIndex::Timer.as_u8()].set_handler_fn(timer_interrupt_handler); + // idt[InterruptIndex::Keyboard.as_u8()].set_handler_fn(keyboard_interrupt_handler); idt }); @@ -51,6 +52,8 @@ impl InterruptIndex { pub fn init_idt() { IDT.load(); + // enable_apic(); + // TODO: fix apic unsafe { PICS.lock().initialize(); PICS.lock().write_masks(0xfc, 0xff); diff --git a/libk/src/drivers/apic/mod.rs b/libk/src/drivers/apic/mod.rs new file mode 100644 index 0000000..1c59e9e --- /dev/null +++ b/libk/src/drivers/apic/mod.rs @@ -0,0 +1,45 @@ +use core::arch::x86_64::__cpuid; + +use x86_64::{PhysAddr, instructions::port::Port, registers::model_specific::Msr}; + +use super::cpu::model_specific_registers::*; + +const IA32_APIC_BASE_MSR: u32 = 0x1b; +const IA32_APIC_BASE_MSR_BSP: u64 = 0x100; +const IA32_APIC_BASE_MSR_ENABLE: u64 = 0x800; + +const CPUID_FEAT_EDX_APIC: u64 = 1 << 9; // the cpuid instruction will return this flag if it supports APIC + +fn set_apic_base(apic: PhysAddr) { + let rax = (apic.as_u64() & 0xfffff0000) | IA32_APIC_BASE_MSR_ENABLE; + cpu_set_msr(IA32_APIC_BASE_MSR, rax); +} + +fn get_apic_base() -> PhysAddr { + let mut value: u64 = 0; + cpu_get_msr(IA32_APIC_BASE_MSR, &mut value); + PhysAddr::new(value & 0xfffff0000) +} + +fn write_apic_register(reg: u8, value: u32) { + let apic_base = get_apic_base().as_u64(); + let reg_addr = (apic_base & 0xFFFFF0000) + reg as u64; + unsafe { *(reg_addr as *mut u32) = value }; +} + +fn read_apic_register(reg: u8) -> u32 { + let apic_base = get_apic_base().as_u64(); + let reg_addr = (apic_base & 0xFFFFF0000) + reg as u64; + unsafe { *(reg_addr as *const u32) } +} + +pub fn check_apic() -> bool { + let res = unsafe { __cpuid(1) }; + res.edx as u64 & CPUID_FEAT_EDX_APIC != 0 +} + +pub fn enable_apic() { + set_apic_base(get_apic_base()); + + write_apic_register(0xF0, read_apic_register(0xF0) | 0x100); +} diff --git a/libk/src/drivers/cpu.rs b/libk/src/drivers/cpu.rs new file mode 100644 index 0000000..96416dc --- /dev/null +++ b/libk/src/drivers/cpu.rs @@ -0,0 +1,26 @@ +pub mod model_specific_registers { + use core::arch::x86_64::__cpuid; + use spin::Lazy; + use x86_64::registers::model_specific::Msr; + + const CPUID_FLAG_MSR: u32 = 1 << 5; + static EDX: Lazy = Lazy::new(|| unsafe { __cpuid(1).edx }); + + pub fn cpu_has_msr() -> bool { + *EDX & CPUID_FLAG_MSR != 0 + } + + pub fn cpu_get_msr(msr: u32, value: &mut u64) { + let msr = Msr::new(msr); + unsafe { + *value = msr.read(); + } + } + + pub fn cpu_set_msr(msr: u32, value: u64) { + let mut msr = Msr::new(msr); + unsafe { + msr.write(value); + } + } +} diff --git a/libk/src/drivers/mod.rs b/libk/src/drivers/mod.rs index 61ec870..5ed6e5e 100644 --- a/libk/src/drivers/mod.rs +++ b/libk/src/drivers/mod.rs @@ -1,3 +1,7 @@ pub mod io; pub mod kalloc; pub mod scheduling; + +pub mod apic; +pub mod cpu; +pub mod pic; diff --git a/libk/src/drivers/pic.rs b/libk/src/drivers/pic.rs new file mode 100644 index 0000000..9c710f8 --- /dev/null +++ b/libk/src/drivers/pic.rs @@ -0,0 +1,152 @@ +use x86_64::instructions::port::Port; + +const CMD_INIT: u8 = 0x11; +const CMD_END_OF_INT: u8 = 0x20; +const MODE_8086: u8 = 0x01; + +struct Pic { + offset: u8, + data: Port, + command: Port, +} + +impl Pic { + /// Are we in charge of handling the specified interrupt? + /// (Each PIC handles 8 interrupts.) + const fn handles_interrupt(&self, interrupt_id: u8) -> bool { + self.offset <= interrupt_id && interrupt_id < self.offset + 8 + } + + /// Notify us that an interrupt has been handled and that we're ready + /// for more. + unsafe fn end_of_interrupt(&mut self) { + unsafe { self.command.write(CMD_END_OF_INT) }; + } + + /// Reads the interrupt mask of this PIC. + unsafe fn read_mask(&mut self) -> u8 { + unsafe { self.data.read() } + } + + /// Writes the interrupt mask of this PIC. + unsafe fn write_mask(&mut self, mask: u8) { + unsafe { self.data.write(mask) } + } +} + +/// A pair of chained PICs. This is the standard setup on x86. +pub struct ChainedPics { + pics: [Pic; 2], +} + +impl ChainedPics { + pub const unsafe fn new(offset1: u8, offset2: u8) -> Self { + ChainedPics { + pics: [ + Pic { + offset: offset1, + command: Port::new(0x20), + data: Port::new(0x21), + }, + Pic { + offset: offset2, + command: Port::new(0xA0), + data: Port::new(0xA1), + }, + ], + } + } + + /// . + /// + /// # Safety + /// + /// . + pub const unsafe fn new_contiguous(primary_offset: u8) -> Self { + unsafe { Self::new(primary_offset, primary_offset + 8) } + } + + /// Returns the initialize of this [`ChainedPics`]. + /// + /// # Safety + /// + /// . + pub unsafe fn initialize(&mut self) { + unsafe { + let mut wait_port: Port = Port::new(0x80); + let mut wait = || wait_port.write(0); + + // Save our original interrupt masks, because I'm too lazy to + // figure out reasonable values. We'll restore these when we're + // done. + let saved_masks = self.read_masks(); + + // Tell each PIC that we're going to send it a three-byte + // initialization sequence on its data port. + self.pics[0].command.write(CMD_INIT); + wait(); + self.pics[1].command.write(CMD_INIT); + wait(); + + // Byte 1: Set up our base offsets. + self.pics[0].data.write(self.pics[0].offset); + wait(); + self.pics[1].data.write(self.pics[1].offset); + wait(); + + // Byte 2: Configure chaining between PIC1 and PIC2. + self.pics[0].data.write(4); + wait(); + self.pics[1].data.write(2); + wait(); + + // Byte 3: Set our mode. + self.pics[0].data.write(MODE_8086); + wait(); + self.pics[1].data.write(MODE_8086); + wait(); + + // Restore our saved masks. + self.write_masks(saved_masks[0], saved_masks[1]) + } + } + + /// Reads the interrupt masks of both PICs. + pub unsafe fn read_masks(&mut self) -> [u8; 2] { + unsafe { [self.pics[0].read_mask(), self.pics[1].read_mask()] } + } + + /// Writes the interrupt masks of both PICs. + pub unsafe fn write_masks(&mut self, mask1: u8, mask2: u8) { + unsafe { + self.pics[0].write_mask(mask1); + self.pics[1].write_mask(mask2); + } + } + + /// Disables both PICs by masking all interrupts. + pub unsafe fn disable(&mut self) { + unsafe { self.write_masks(u8::MAX, u8::MAX) } + } + + /// Do we handle this interrupt? + pub fn handles_interrupt(&self, interrupt_id: u8) -> bool { + self.pics.iter().any(|p| p.handles_interrupt(interrupt_id)) + } + + /// Figure out which (if any) PICs in our chain need to know about this + /// interrupt. This is tricky, because all interrupts from `pics[1]` + /// get chained through `pics[0]`. + pub unsafe fn notify_end_of_interrupt(&mut self, interrupt_id: u8) { + if self.handles_interrupt(interrupt_id) { + if self.pics[1].handles_interrupt(interrupt_id) { + unsafe { + self.pics[1].end_of_interrupt(); + } + } + unsafe { + self.pics[0].end_of_interrupt(); + } + } + } +}