Local APIC
This commit is contained in:
100
nel_os_kernel/src/interrupt/apic.rs
Normal file
100
nel_os_kernel/src/interrupt/apic.rs
Normal file
@ -0,0 +1,100 @@
|
||||
use acpi::PlatformInfo;
|
||||
use alloc::alloc::Global;
|
||||
use spin::{Lazy, Once};
|
||||
use x86_64::instructions::port::Port;
|
||||
|
||||
use crate::interrupt::idt::IRQ_TIMER;
|
||||
|
||||
pub static LAPIC: Once<LocalApic> = Once::new();
|
||||
|
||||
pub fn disable_pic_8259() {
|
||||
unsafe {
|
||||
Port::new(0xa1).write(0xffu8);
|
||||
Port::new(0x21).write(0xffu8);
|
||||
}
|
||||
}
|
||||
|
||||
pub struct LocalApic {
|
||||
pub ptr: *mut u32,
|
||||
}
|
||||
|
||||
unsafe impl Send for LocalApic {}
|
||||
unsafe impl Sync for LocalApic {}
|
||||
|
||||
impl LocalApic {
|
||||
fn new(base: u64) -> Self {
|
||||
LocalApic {
|
||||
ptr: base as *mut u32,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn read(&self, offset: u32) -> u32 {
|
||||
unsafe { self.ptr.add(offset as usize).read_volatile() }
|
||||
}
|
||||
|
||||
pub fn write(&self, offset: u32, value: u32) {
|
||||
unsafe { self.ptr.add(offset as usize).write_volatile(value) }
|
||||
}
|
||||
}
|
||||
|
||||
const SVR: u32 = 0x00f0 / 4;
|
||||
const ENABLE: u32 = 0x100;
|
||||
|
||||
const TDCR: u32 = 0x03e0 / 4;
|
||||
const TICR: u32 = 0x0380 / 4;
|
||||
|
||||
const TIMER: u32 = 0x0320 / 4;
|
||||
const X1: u32 = 0b1011;
|
||||
const PERIODIC: u32 = 0x20000;
|
||||
|
||||
const MASKED: u32 = 0x10000;
|
||||
|
||||
const ICRLO: u32 = 0x0300 / 4;
|
||||
const BCAST: u32 = 0x80000;
|
||||
const INIT: u32 = 0x500;
|
||||
const LEVEL: u32 = 0x8000;
|
||||
const DELIVS: u32 = 0x1000;
|
||||
|
||||
const ICRHI: u32 = 0x0310 / 4;
|
||||
|
||||
const PCINT: u32 = 0x0340 / 4;
|
||||
const LINT0: u32 = 0x0350 / 4;
|
||||
const LINT1: u32 = 0x0360 / 4;
|
||||
|
||||
pub const EOI: u32 = 0x00b0 / 4;
|
||||
|
||||
const TPR: u32 = 0x0080 / 4;
|
||||
|
||||
pub fn init_local_apic(platform_info: PlatformInfo<'_, Global>) {
|
||||
disable_pic_8259();
|
||||
|
||||
let apic_info = match platform_info.interrupt_model {
|
||||
acpi::InterruptModel::Apic(ref apic) => apic,
|
||||
_ => panic!("APIC not found in ACPI tables"),
|
||||
};
|
||||
|
||||
let local_apic_base = apic_info.local_apic_address;
|
||||
let local_apic = LocalApic::new(local_apic_base);
|
||||
LAPIC.call_once(|| LocalApic::new(local_apic_base));
|
||||
|
||||
local_apic.write(SVR, ENABLE | 0xff);
|
||||
local_apic.write(TDCR, X1);
|
||||
local_apic.write(TIMER, PERIODIC | IRQ_TIMER);
|
||||
local_apic.write(TICR, 10000000); //TODO
|
||||
|
||||
local_apic.write(LINT0, MASKED);
|
||||
local_apic.write(LINT1, MASKED);
|
||||
|
||||
if (local_apic.read(0x30 / 4) >> 16) & 0xFF >= 4 {
|
||||
local_apic.write(PCINT, MASKED);
|
||||
}
|
||||
|
||||
local_apic.write(EOI, 0);
|
||||
|
||||
local_apic.write(ICRHI, 0);
|
||||
local_apic.write(ICRLO, BCAST | INIT | LEVEL);
|
||||
|
||||
while local_apic.read(ICRLO) & DELIVS != 0 {}
|
||||
|
||||
local_apic.write(TPR, 0);
|
||||
}
|
@ -1,18 +1,37 @@
|
||||
use lazy_static::lazy_static;
|
||||
use x86_64::structures::idt::{InterruptDescriptorTable, InterruptStackFrame, PageFaultErrorCode};
|
||||
|
||||
use crate::{interrupt::gdt, warn};
|
||||
use crate::{
|
||||
info_w_int,
|
||||
interrupt::{
|
||||
apic::{EOI, LAPIC},
|
||||
gdt,
|
||||
},
|
||||
warn,
|
||||
};
|
||||
|
||||
const PIC_8259_IRQ_OFFSET: u32 = 32;
|
||||
pub const IRQ_TIMER: u32 = PIC_8259_IRQ_OFFSET + 16;
|
||||
|
||||
lazy_static! {
|
||||
static ref IDT: InterruptDescriptorTable = {
|
||||
let mut idt = InterruptDescriptorTable::new();
|
||||
idt.breakpoint.set_handler_fn(breakpoint_handler);
|
||||
idt.breakpoint
|
||||
.set_handler_fn(breakpoint_handler)
|
||||
.disable_interrupts(true);
|
||||
unsafe {
|
||||
idt.double_fault
|
||||
.set_handler_fn(double_fault_handler)
|
||||
.set_stack_index(gdt::DOUBLE_FAULT_IST_INDEX);
|
||||
.set_stack_index(gdt::DOUBLE_FAULT_IST_INDEX)
|
||||
.disable_interrupts(true);
|
||||
}
|
||||
idt.page_fault.set_handler_fn(page_fault_handler);
|
||||
idt.page_fault
|
||||
.set_handler_fn(page_fault_handler)
|
||||
.disable_interrupts(true);
|
||||
idt[IRQ_TIMER as u8]
|
||||
.set_handler_fn(timer_handler)
|
||||
.disable_interrupts(true);
|
||||
|
||||
idt
|
||||
};
|
||||
}
|
||||
@ -44,3 +63,8 @@ extern "x86-interrupt" fn page_fault_handler(
|
||||
Cr2::read().unwrap().as_u64()
|
||||
);
|
||||
}
|
||||
|
||||
extern "x86-interrupt" fn timer_handler(_stack_frame: InterruptStackFrame) {
|
||||
LAPIC.get().unwrap().write(EOI, 0);
|
||||
info_w_int!("Timer interrupt received");
|
||||
}
|
||||
|
@ -1,2 +1,3 @@
|
||||
pub mod apic;
|
||||
pub mod gdt;
|
||||
pub mod idt;
|
||||
|
@ -1,6 +1,7 @@
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
#![feature(abi_x86_interrupt)]
|
||||
#![feature(allocator_api)]
|
||||
|
||||
extern crate alloc;
|
||||
|
||||
@ -24,6 +25,7 @@ use crate::{
|
||||
acpi::KernelAcpiHandler,
|
||||
constant::{KERNEL_STACK_SIZE, PKG_VERSION},
|
||||
graphics::{FrameBuffer, FRAME_BUFFER},
|
||||
interrupt::apic,
|
||||
memory::{allocator, memory::BitmapMemoryTable, paging},
|
||||
};
|
||||
|
||||
@ -144,8 +146,13 @@ pub extern "sysv64" fn main(boot_info: &nel_os_common::BootInfo) {
|
||||
let acpi_tables =
|
||||
unsafe { AcpiTables::from_rsdp(KernelAcpiHandler, boot_info.rsdp as usize) }.unwrap();
|
||||
let platform_info = acpi_tables.platform_info().unwrap();
|
||||
let processor_info = platform_info.processor_info;
|
||||
info!("Processor info: {:#x?}", processor_info);
|
||||
|
||||
apic::init_local_apic(platform_info);
|
||||
info!("Local APIC initialized",);
|
||||
|
||||
x86_64::instructions::interrupts::enable();
|
||||
|
||||
info!("Interrupts enabled");
|
||||
|
||||
hlt_loop();
|
||||
}
|
||||
|
Reference in New Issue
Block a user