feat: Initialize Local APIC and IO APIC

This commit is contained in:
ParrotXray 2025-10-14 11:04:17 +08:00
parent b277334069
commit 5313892f80
13 changed files with 843 additions and 56 deletions

View File

@ -35,6 +35,9 @@ lazy_static! {
idt.simd_floating_point.set_handler_fn(simd_floating_point_handler);
idt.virtualization.set_handler_fn(virtualization_handler);
// IRQ 32 start
idt[33].set_handler_fn(keyboard_interrupt_handler); // IRQ 1 Keyboard
idt
};
}

View File

@ -3,7 +3,7 @@ use x86_64::structures::idt::{InterruptStackFrame, PageFaultErrorCode};
use x86_64::VirtAddr;
use crate::kprintln;
use crate::{log_trace, log_debug, log_info, log_warn, log_error, log_fatal};
use crate::hal::cpu;
use crate::hal::{cpu, lapic};
use crate::mm::paging;
/// Divide Error (#DE)
@ -224,4 +224,20 @@ pub extern "x86-interrupt" fn virtualization_handler(stack_frame: InterruptStack
}
}
// TODO Timer interrupt, Keyboard interrupt
// TODO Timer interrupt, Keyboard interrupt
pub extern "x86-interrupt" fn keyboard_interrupt_handler(stack_frame: InterruptStackFrame) {
use x86_64::instructions::port::Port;
unsafe {
// 读取键盘扫描码
let mut port = Port::new(0x60);
let scancode: u8 = port.read();
// 传递给键盘驱动处理
crate::drivers::keyboard::handle_scancode(scancode);
}
// 发送 EOI
lapic::send_eoi();
}

View File

@ -0,0 +1,184 @@
// kernel/src/drivers/keyboard.rs
use spin::Mutex;
use crate::{kprintln, log_debug, log_info};
/// Keyboard scancode to ASCII mapping table (US keyboard layout)
static SCANCODE_TO_ASCII: [u8; 128] = [
0, 27, b'1', b'2', b'3', b'4', b'5', b'6', // 0x00-0x07
b'7', b'8', b'9', b'0', b'-', b'=', 8, b'\t', // 0x08-0x0F (8=Backspace)
b'q', b'w', b'e', b'r', b't', b'y', b'u', b'i', // 0x10-0x17
b'o', b'p', b'[', b']', b'\n', 0, b'a', b's', // 0x18-0x1F (Ctrl)
b'd', b'f', b'g', b'h', b'j', b'k', b'l', b';', // 0x20-0x27
b'\'',b'`', 0, b'\\',b'z', b'x', b'c', b'v', // 0x28-0x2F (LShift)
b'b', b'n', b'm', b',', b'.', b'/', 0, b'*', // 0x30-0x37 (RShift)
0, b' ', 0, 0, 0, 0, 0, 0, // 0x38-0x3F (Alt, CapsLock, F1-F5)
0, 0, 0, 0, 0, 0, 0, 0, // 0x40-0x47 (F6-F10, NumLock, ScrollLock)
0, 0, 0, 0, 0, 0, 0, 0, // 0x48-0x4F (Home, Up, PgUp, -, Left, ...)
0, 0, 0, 0, 0, 0, 0, 0, // 0x50-0x57
0, 0, 0, 0, 0, 0, 0, 0, // 0x58-0x5F
0, 0, 0, 0, 0, 0, 0, 0, // 0x60-0x67
0, 0, 0, 0, 0, 0, 0, 0, // 0x68-0x6F
0, 0, 0, 0, 0, 0, 0, 0, // 0x70-0x77
0, 0, 0, 0, 0, 0, 0, 0, // 0x78-0x7F
];
/// Character mapping when Shift key is pressed
static SCANCODE_TO_ASCII_SHIFT: [u8; 128] = [
0, 27, b'!', b'@', b'#', b'$', b'%', b'^', // 0x00-0x07
b'&', b'*', b'(', b')', b'_', b'+', 8, b'\t', // 0x08-0x0F
b'Q', b'W', b'E', b'R', b'T', b'Y', b'U', b'I', // 0x10-0x17
b'O', b'P', b'{', b'}', b'\n', 0, b'A', b'S', // 0x18-0x1F
b'D', b'F', b'G', b'H', b'J', b'K', b'L', b':', // 0x20-0x27
b'"', b'~', 0, b'|', b'Z', b'X', b'C', b'V', // 0x28-0x2F
b'B', b'N', b'M', b'<', b'>', b'?', 0, b'*', // 0x30-0x37
0, b' ', 0, 0, 0, 0, 0, 0, // 0x38-0x3F
0, 0, 0, 0, 0, 0, 0, 0, // 0x40-0x47
0, 0, 0, 0, 0, 0, 0, 0, // 0x48-0x4F
0, 0, 0, 0, 0, 0, 0, 0, // 0x50-0x57
0, 0, 0, 0, 0, 0, 0, 0, // 0x58-0x5F
0, 0, 0, 0, 0, 0, 0, 0, // 0x60-0x67
0, 0, 0, 0, 0, 0, 0, 0, // 0x68-0x6F
0, 0, 0, 0, 0, 0, 0, 0, // 0x70-0x77
0, 0, 0, 0, 0, 0, 0, 0, // 0x78-0x7F
];
/// Numpad scancode mapping
static NUMPAD_SCANCODE_TO_ASCII: [u8; 128] = {
let mut map = [0u8; 128];
map[0x47] = b'7';
map[0x48] = b'8';
map[0x49] = b'9';
map[0x4B] = b'4';
map[0x4C] = b'5';
map[0x4D] = b'6';
map[0x4F] = b'1';
map[0x50] = b'2';
map[0x51] = b'3';
map[0x52] = b'0';
map[0x53] = b'.';
map[0x4A] = b'-';
map[0x4E] = b'+';
map[0x37] = b'*';
map[0x35] = b'/';
map
};
/// Keyboard state
struct KeyboardState {
shift_pressed: bool,
ctrl_pressed: bool,
alt_pressed: bool,
caps_lock: bool,
num_lock: bool,
}
impl KeyboardState {
const fn new() -> Self {
Self {
shift_pressed: false,
ctrl_pressed: false,
alt_pressed: false,
caps_lock: false,
num_lock: true, // Usually enabled by default
}
}
}
static KEYBOARD_STATE: Mutex<KeyboardState> = Mutex::new(KeyboardState::new());
/// Handle keyboard scancode
pub fn handle_scancode(raw: u8) {
let mut state = KEYBOARD_STATE.lock();
let key_released = (raw & 0x80) != 0;
let scancode = raw & 0x7F;
// Handle modifier keys
match scancode {
0x2A | 0x36 => { state.shift_pressed = !key_released; return; } // Shift
0x1D => { state.ctrl_pressed = !key_released; return; } // Ctrl
0x38 => { state.alt_pressed = !key_released; return; } // Alt
0x3A => { if !key_released { state.caps_lock = !state.caps_lock; } return; } // Caps Lock
0x45 => { if !key_released { state.num_lock = !state.num_lock; } return; } // Num Lock
_ => {}
}
if key_released { return; }
// When NumLock is off: Numpad outputs arrow or control keys
if !state.num_lock {
match scancode {
0x47 => { kprintln!("[Home]"); return; }
0x48 => { kprintln!("[Up]"); return; }
0x49 => { kprintln!("[PgUp]"); return; }
0x4B => { kprintln!("[Left]"); return; }
0x4C => { kprintln!("[Center]"); return; }
0x4D => { kprintln!("[Right]"); return; }
0x4F => { kprintln!("[End]"); return; }
0x50 => { kprintln!("[Down]"); return; }
0x51 => { kprintln!("[PgDn]"); return; }
0x52 => { kprintln!("[Insert]"); return; }
0x53 => { kprintln!("[Delete]"); return; }
_ => {}
}
}
// Handle numpad output based on NumLock state
let ascii = if state.num_lock && NUMPAD_SCANCODE_TO_ASCII[scancode as usize] != 0 {
NUMPAD_SCANCODE_TO_ASCII[scancode as usize]
} else if state.shift_pressed {
SCANCODE_TO_ASCII_SHIFT[scancode as usize]
} else {
SCANCODE_TO_ASCII[scancode as usize]
};
if ascii == 0 {
return; // Unmapped key
}
// Handle Caps Lock (affects letters only)
let ascii = if state.caps_lock && ascii.is_ascii_alphabetic() {
if state.shift_pressed {
ascii.to_ascii_lowercase()
} else {
ascii.to_ascii_uppercase()
}
} else {
ascii
};
// Handle Ctrl combinations
if state.ctrl_pressed {
match ascii {
b'c' | b'C' => { kprintln!("^C"); return; }
b'd' | b'D' => { kprintln!("^D"); return; }
b'l' | b'L' => {
crate::tty::tty::clear(0x000000);
return;
}
_ => {}
}
}
// Output character
print_char(ascii);
}
/// Print a character to the screen
fn print_char(c: u8) {
use crate::kprint;
if c == b'\n' {
kprintln!();
} else if c == 8 {
// TODO: Implement backspace functionality
kprint!("\x08");
} else if c.is_ascii_graphic() || c == b' ' {
kprint!("{}", c as char);
}
}
/// Initialize keyboard driver
pub fn init() {
log_info!("Keyboard driver initialized");
}

View File

@ -0,0 +1 @@
pub mod keyboard;

View File

@ -12,6 +12,16 @@ pub struct CureAcpiHandler {
physical_memory_offset: u64,
}
pub struct AcpiInfo {
pub revision: u8,
pub boot_processor: Option<u32>,
pub cpu_count: usize,
pub has_apic: bool,
pub has_hpet: bool,
pub local_apic_address: Option<u64>,
pub io_apics: alloc::vec::Vec<(u64, u8, u32)>, // (address, id, gsi_base)
}
impl CureAcpiHandler {
pub const fn new(physical_memory_offset: u64) -> Self {
Self {
@ -187,14 +197,6 @@ impl Handler for CureAcpiHandler {
}
}
pub struct AcpiInfo {
pub revision: u8,
pub boot_processor: Option<u32>,
pub cpu_count: usize,
pub has_apic: bool,
pub has_hpet: bool,
}
pub fn init(rsdp_addr: u64, physical_memory_offset: u64) -> Option<AcpiInfo> {
let handler = CureAcpiHandler::new(physical_memory_offset);
@ -214,7 +216,6 @@ pub fn init(rsdp_addr: u64, physical_memory_offset: u64) -> Option<AcpiInfo> {
}
}
};
// kprintln!(" ACPI Revision: {}", tables.rsdp_revision);
let platform = match AcpiPlatform::new(tables, handler) {
Ok(platform) => platform,
@ -240,25 +241,33 @@ pub fn init(rsdp_addr: u64, physical_memory_offset: u64) -> Option<AcpiInfo> {
};
// 檢查中斷模型
let has_apic = match &platform.interrupt_model {
let (has_apic, local_apic_addr, io_apics_info) = match &platform.interrupt_model {
InterruptModel::Apic(apic) => {
log_info!("Local APIC Address: {:#x}", apic.local_apic_address);
log_info!("IO APICs: {} controller(s)", apic.io_apics.len());
let mut io_apics = alloc::vec::Vec::new();
for (i, io_apic) in apic.io_apics.iter().enumerate() {
log_info!("IO APIC {}: ID={}, Address={:#x}, GSI Base={}",
i, io_apic.id, io_apic.address, io_apic.global_system_interrupt_base);
io_apics.push((
io_apic.address as u64,
io_apic.id,
io_apic.global_system_interrupt_base,
));
}
true
(true, Some(apic.local_apic_address as u64), io_apics)
}
InterruptModel::Unknown => {
log_warn!("Interrupt Model: Unknown (not APIC)");
false
(false, None, alloc::vec::Vec::new())
}
_ => {
log_warn!("Interrupt Model: Other");
false
(false, None, alloc::vec::Vec::new())
}
};
@ -299,6 +308,8 @@ pub fn init(rsdp_addr: u64, physical_memory_offset: u64) -> Option<AcpiInfo> {
cpu_count,
has_apic,
has_hpet,
local_apic_address: local_apic_addr,
io_apics: io_apics_info,
})
}

View File

View File

@ -0,0 +1,306 @@
// kernel/src/hal/ioapic.rs
use x86_64::{PhysAddr, VirtAddr};
use spin::Mutex;
use alloc::vec::Vec;
use crate::{log_trace, log_debug, log_info, log_warn, log_error};
use crate::mm::vma;
/// IO APIC register selector
const IOREGSEL: u32 = 0x00;
const IOWIN: u32 = 0x10;
/// IO APIC register index
#[allow(dead_code)]
mod reg {
pub const ID: u8 = 0x00;
pub const VER: u8 = 0x01;
pub const ARB: u8 = 0x02;
pub const REDTBL_BASE: u8 = 0x10;
}
/// Redirection Entry flag
#[allow(dead_code)]
mod redir_flags {
pub const MASKED: u64 = 1 << 16;
pub const TRIGGER_LEVEL: u64 = 1 << 15;
pub const TRIGGER_EDGE: u64 = 0;
pub const POLARITY_LOW: u64 = 1 << 13;
pub const POLARITY_HIGH: u64 = 0;
pub const DEST_LOGICAL: u64 = 1 << 11;
pub const DEST_PHYSICAL: u64 = 0;
pub const DELIVERY_FIXED: u64 = 0 << 8;
pub const DELIVERY_LOWEST: u64 = 1 << 8;
}
pub struct IoApic {
base_vaddr: VirtAddr,
id: u8,
gsi_base: u32,
max_redirection_entries: u8,
}
static IO_APICS: Mutex<Vec<IoApic>> = Mutex::new(Vec::new());
impl IoApic {
/// Create IO APIC from physical address
pub unsafe fn new(base_paddr: PhysAddr, id: u8, gsi_base: u32) -> Self {
let base_vaddr = vma::phys_to_virt(base_paddr.as_u64());
log_debug!("IO APIC {} physical base: {:#x}", id, base_paddr.as_u64());
log_debug!("IO APIC {} virtual base: {:#x}", id, base_vaddr.as_u64());
log_debug!("IO APIC {} GSI base: {}", id, gsi_base);
let mut ioapic = Self {
base_vaddr,
id,
gsi_base,
max_redirection_entries: 0,
};
// Read version information to get the maximum number of redirect entries
let version = ioapic.read(reg::VER);
ioapic.max_redirection_entries = ((version >> 16) & 0xFF) as u8 + 1;
ioapic
}
/// Read IO APIC registers
unsafe fn read(&mut self, reg: u8) -> u32 {
let regsel_addr = self.base_vaddr.as_u64() + IOREGSEL as u64;
let win_addr = self.base_vaddr.as_u64() + IOWIN as u64;
core::ptr::write_volatile(regsel_addr as *mut u32, reg as u32);
core::ptr::read_volatile(win_addr as *const u32)
}
/// Writing to IO APIC registers
unsafe fn write(&mut self, reg: u8, value: u32) {
let regsel_addr = self.base_vaddr.as_u64() + IOREGSEL as u64;
let win_addr = self.base_vaddr.as_u64() + IOWIN as u64;
core::ptr::write_volatile(regsel_addr as *mut u32, reg as u32);
core::ptr::write_volatile(win_addr as *mut u32, value);
}
/// Read redirection table entries
unsafe fn read_redirection_entry(&mut self, irq: u8) -> u64 {
if irq >= self.max_redirection_entries {
log_warn!("IRQ {} out of range for IO APIC {}", irq, self.id);
return 0;
}
let low_reg = reg::REDTBL_BASE + (irq * 2);
let high_reg = low_reg + 1;
let low = self.read(low_reg) as u64;
let high = self.read(high_reg) as u64;
(high << 32) | low
}
/// Write redirection table entry
unsafe fn write_redirection_entry(&mut self, irq: u8, entry: u64) {
if irq >= self.max_redirection_entries {
log_warn!("IRQ {} out of range for IO APIC {}", irq, self.id);
return;
}
let low_reg = reg::REDTBL_BASE + (irq * 2);
let high_reg = low_reg + 1;
let low = entry as u32;
let high = (entry >> 32) as u32;
self.write(high_reg, high);
self.write(low_reg, low);
}
/// Initialize the IO APIC
pub unsafe fn init(&mut self) {
// Mask all interrupts
for irq in 0..self.max_redirection_entries {
let entry = redir_flags::MASKED;
self.write_redirection_entry(irq, entry);
}
log_info!("IO APIC {} initialized, {} entries", self.id, self.max_redirection_entries);
}
/// Configure IRQ redirection
///
/// # Parameters
/// - `irq`: IRQ number (0-23)
/// - `vector`: Interrupt vector number (32-255)
/// - `dest_apic_id`: Destination Local APIC ID
/// - `level_triggered`: true = level triggered, false = edge triggered
/// - `active_low`: true = active low, false = active high
pub unsafe fn set_irq_redirect(
&mut self,
irq: u8,
vector: u8,
dest_apic_id: u8,
level_triggered: bool,
active_low: bool,
) {
let mut entry: u64 = 0;
// Set target APIC ID (bits 56-63)
entry |= (dest_apic_id as u64) << 56;
// Set the trigger mode
if level_triggered {
entry |= redir_flags::TRIGGER_LEVEL;
}
// Setting Polarity
if active_low {
entry |= redir_flags::POLARITY_LOW;
}
// Set the delivery mode to Fixed
entry |= redir_flags::DELIVERY_FIXED;
// Set the target mode to Physical
entry |= redir_flags::DEST_PHYSICAL;
// Set vector number
entry |= vector as u64;
// Write redirection table (unmask)
self.write_redirection_entry(irq, entry);
log_info!(
"IO APIC {} IRQ {} -> Vector {} (APIC {}, {}, {})",
self.id,
irq,
vector,
dest_apic_id,
if level_triggered { "level" } else { "edge" },
if active_low { "low" } else { "high" }
);
}
/// masked IRQ
pub unsafe fn mask_irq(&mut self, irq: u8) {
let mut entry = self.read_redirection_entry(irq);
entry |= redir_flags::MASKED;
self.write_redirection_entry(irq, entry);
log_debug!("IO APIC {} IRQ {} masked", self.id, irq);
}
/// unmasked IRQ
pub unsafe fn unmask_irq(&mut self, irq: u8) {
let mut entry = self.read_redirection_entry(irq);
entry &= !redir_flags::MASKED;
self.write_redirection_entry(irq, entry);
log_debug!("IO APIC {} IRQ {} unmasked", self.id, irq);
}
/// Print IO APIC information
pub unsafe fn print_info(&mut self) {
let id = self.read(reg::ID) >> 24;
let version = self.read(reg::VER);
let apic_ver = version & 0xFF;
log_info!("IO APIC {} ID: {}", self.id, id);
log_info!("IO APIC {} Version: {:#x}", self.id, apic_ver);
log_info!("IO APIC {} Max Redirection Entries: {}", self.id, self.max_redirection_entries);
log_info!("IO APIC {} GSI Base: {}", self.id, self.gsi_base);
}
}
/// Initialize a single IO APIC (using mapped virtual address)
pub unsafe fn init_single_ioapic(base_vaddr: VirtAddr, id: u8, gsi_base: u32) {
let mut ioapic = IoApic {
base_vaddr,
id,
gsi_base,
max_redirection_entries: 0,
};
// Read version information to get the maximum number of redirect entries
let version = ioapic.read(reg::VER);
ioapic.max_redirection_entries = ((version >> 16) & 0xFF) as u8 + 1;
ioapic.init();
ioapic.print_info();
IO_APICS.lock().push(ioapic);
}
/// Initialize all IO APICs (from physical address, need to be mapped first)
pub unsafe fn init_io_apics(io_apics: &[(PhysAddr, u8, u32)]) {
let mut apics = IO_APICS.lock();
for (paddr, id, gsi_base) in io_apics {
let mut ioapic = IoApic::new(*paddr, *id, *gsi_base);
ioapic.init();
ioapic.print_info();
apics.push(ioapic);
}
log_info!("All IO APICs initialized");
}
/// Configuring IRQ Redirection
pub fn set_irq_redirect(
irq: u8,
vector: u8,
dest_apic_id: u8,
level_triggered: bool,
active_low: bool,
) {
unsafe {
let mut apics = IO_APICS.lock();
// Find the IO APIC responsible for this IRQ
for ioapic in apics.iter_mut() {
let gsi_start = ioapic.gsi_base as u8;
let gsi_end = gsi_start + ioapic.max_redirection_entries;
if irq >= gsi_start && irq < gsi_end {
let local_irq = irq - gsi_start;
ioapic.set_irq_redirect(local_irq, vector, dest_apic_id, level_triggered, active_low);
return;
}
}
log_warn!("No IO APIC found for IRQ {}", irq);
}
}
/// Block IRQ
pub fn mask_irq(irq: u8) {
unsafe {
let mut apics = IO_APICS.lock();
for ioapic in apics.iter_mut() {
let gsi_start = ioapic.gsi_base as u8;
let gsi_end = gsi_start + ioapic.max_redirection_entries;
if irq >= gsi_start && irq < gsi_end {
let local_irq = irq - gsi_start;
ioapic.mask_irq(local_irq);
return;
}
}
}
}
/// Unmask IRQ
pub fn unmask_irq(irq: u8) {
unsafe {
let mut apics = IO_APICS.lock();
for ioapic in apics.iter_mut() {
let gsi_start = ioapic.gsi_base as u8;
let gsi_end = gsi_start + ioapic.max_redirection_entries;
if irq >= gsi_start && irq < gsi_end {
let local_irq = irq - gsi_start;
ioapic.unmask_irq(local_irq);
return;
}
}
}
}

190
kernel/src/hal/lapic.rs Normal file
View File

@ -0,0 +1,190 @@
// kernel/src/hal/lapic.rs
use x86_64::{PhysAddr, VirtAddr};
use spin::Mutex;
use crate::{log_trace, log_debug, log_info, log_warn, log_error};
use crate::mm::vma;
/// Local APIC register offset
#[repr(u32)]
#[derive(Debug, Clone, Copy)]
#[allow(dead_code)]
enum ApicRegister {
Id = 0x20,
Version = 0x30,
TaskPriority = 0x80,
ProcessorPriority = 0xA0,
Eoi = 0xB0,
LogicalDestination = 0xD0,
DestinationFormat = 0xE0,
SpuriousInterruptVector = 0xF0,
ErrorStatus = 0x280,
LvtTimer = 0x320,
LvtThermalSensor = 0x330,
LvtPerformanceCounter = 0x340,
LvtLint0 = 0x350,
LvtLint1 = 0x360,
LvtError = 0x370,
TimerInitialCount = 0x380,
TimerCurrentCount = 0x390,
TimerDivideConfig = 0x3E0,
}
/// APIC configuration flags
#[allow(dead_code)]
mod flags {
pub const APIC_ENABLE: u32 = 0x100;
pub const APIC_SW_ENABLE: u32 = 0x100;
pub const APIC_SPURIOUS_ALL: u32 = 0xFF;
pub const LVT_MASKED: u32 = 1 << 16;
pub const LVT_TIMER_PERIODIC: u32 = 1 << 17;
}
pub struct LocalApic {
base_vaddr: VirtAddr,
}
static LOCAL_APIC: Mutex<Option<LocalApic>> = Mutex::new(None);
impl LocalApic {
/// Create Local APIC from physical address
pub unsafe fn new(base_paddr: PhysAddr) -> Self {
let base_vaddr = vma::phys_to_virt(base_paddr.as_u64());
log_debug!("Local APIC physical base: {:#x}", base_paddr.as_u64());
log_debug!("Local APIC virtual base: {:#x}", base_vaddr.as_u64());
Self { base_vaddr }
}
/// Read APIC registers
unsafe fn read(&self, reg: ApicRegister) -> u32 {
let addr = self.base_vaddr.as_u64() + reg as u64;
core::ptr::read_volatile(addr as *const u32)
}
/// Write to APIC register
unsafe fn write(&mut self, reg: ApicRegister, value: u32) {
let addr = self.base_vaddr.as_u64() + reg as u64;
core::ptr::write_volatile(addr as *mut u32, value);
}
/// Initialize Local APIC
pub unsafe fn init(&mut self) {
// Enable APIC (via Spurious Interrupt Vector Register)
let spurious = flags::APIC_SW_ENABLE | 0xFF; // IRQ 0xFF 作为 spurious vector
self.write(ApicRegister::SpuriousInterruptVector, spurious);
// Set task priority to 0 (accept all interrupts)
self.write(ApicRegister::TaskPriority, 0);
// Configure LVT entries - mask all local interrupts by default
self.write(ApicRegister::LvtTimer, flags::LVT_MASKED);
self.write(ApicRegister::LvtLint0, flags::LVT_MASKED);
self.write(ApicRegister::LvtLint1, flags::LVT_MASKED);
self.write(ApicRegister::LvtError, flags::LVT_MASKED);
self.write(ApicRegister::LvtPerformanceCounter, flags::LVT_MASKED);
self.write(ApicRegister::LvtThermalSensor, flags::LVT_MASKED);
log_info!("Local APIC initialized");
}
/// Obtaining the APIC ID
pub unsafe fn id(&self) -> u32 {
self.read(ApicRegister::Id) >> 24
}
/// Get the APIC version
pub unsafe fn version(&self) -> u32 {
self.read(ApicRegister::Version)
}
/// Send EOI (End of Interrupt)
pub unsafe fn send_eoi(&mut self) {
self.write(ApicRegister::Eoi, 0);
}
/// Configuring Timers
pub unsafe fn setup_timer(&mut self, vector: u8, divide_config: u32, initial_count: u32) {
// Setting the crossover
self.write(ApicRegister::TimerDivideConfig, divide_config);
// Setting the LVT Timer (periodic mode)
let lvt = flags::LVT_TIMER_PERIODIC | (vector as u32);
self.write(ApicRegister::LvtTimer, lvt);
// Set the initial count
self.write(ApicRegister::TimerInitialCount, initial_count);
log_info!("Local APIC timer configured: vector={}, count={}", vector, initial_count);
}
pub unsafe fn print_info(&self) {
let id = self.id();
let version = self.version();
let max_lvt = (version >> 16) & 0xFF;
let apic_version = version & 0xFF;
log_info!("Local APIC ID: {}", id);
log_info!("Local APIC Version: {:#x}", apic_version);
log_info!("Max LVT Entry: {}", max_lvt);
}
}
/// Initialize Local APIC (using mapped virtual address)
pub unsafe fn init_local_apic_with_vaddr(base_vaddr: VirtAddr) {
let mut apic = LocalApic { base_vaddr };
apic.init();
apic.print_info();
*LOCAL_APIC.lock() = Some(apic);
}
/// Initialize Local APIC (from physical address, needs to be mapped first)
pub unsafe fn init_local_apic(base_paddr: PhysAddr) {
let base_vaddr = vma::phys_to_virt(base_paddr.as_u64());
init_local_apic_with_vaddr(base_vaddr);
}
/// Send EOI
pub fn send_eoi() {
unsafe {
if let Some(apic) = LOCAL_APIC.lock().as_mut() {
apic.send_eoi();
}
}
}
/// Get the Local APIC ID
pub fn get_apic_id() -> Option<u32> {
unsafe {
LOCAL_APIC.lock().as_ref().map(|apic| apic.id())
}
}
/// Disable legacy 8259 PIC
/// This function should be called before using the APIC to avoid conflicts.
pub fn disable_legacy_pic() {
use crate::hal::io::io_port_wb;
unsafe {
// Remap PIC to unused interrupt vector
// Master PIC
io_port_wb(0x20, 0x11); // ICW1: initialization
io_port_wb(0x21, 0x20); // ICW2: Interrupt vector offset (32-39)
io_port_wb(0x21, 0x04); // ICW3: Tell the Master PIC Slave to be on IRQ2
io_port_wb(0x21, 0x01); // ICW4: 8086 mode
// Slave PIC
io_port_wb(0xA0, 0x11); // ICW1: initialization
io_port_wb(0xA1, 0x28); // ICW2: Interrupt vector offset (40-47)
io_port_wb(0xA1, 0x02); // ICW3: Tell the Slave PIC to connect to Master IRQ2
io_port_wb(0xA1, 0x01); // ICW4: 8086 mode
// Block all IRQs (disable PIC)
io_port_wb(0x21, 0xFF);
io_port_wb(0xA1, 0xFF);
}
log_info!("Legacy 8259 PIC disabled");
}

View File

@ -2,5 +2,5 @@ pub mod io;
pub mod cpu;
pub mod acpi;
pub mod rtc;
pub mod apic;
mod ioapic;
pub mod lapic;
pub mod ioapic;

View File

@ -1,7 +1,7 @@
// kernel/src/k_init.rs (Updated with Logger)
// kernel/src/kernel/k_init.rs
use bootloader_api::BootInfo;
use bootloader_api::info::MemoryRegionKind;
use x86_64::structures::paging::{OffsetPageTable, Page };
use x86_64::structures::paging::OffsetPageTable;
use x86_64::{PhysAddr, VirtAddr};
use crate::mm::{allocator::{heap, frame, pmm}, vmm, paging, vma};
use crate::arch::amd64::{gdt, idt};
@ -11,7 +11,8 @@ use crate::kernel::k_main;
use crate::klibc::logger::{init, LogLevel, LoggerConfig};
use crate::klibc::malloc;
use crate::{log_debug, log_error, log_info, log_trace, log_warn};
use crate::hal::{acpi, rtc};
use crate::drivers::keyboard;
use crate::hal::{acpi, lapic, rtc};
fn _logger_init() {
init(
@ -132,13 +133,11 @@ fn _boot_report(memory_regions: &bootloader_api::info::MemoryRegions, physical_m
log_info!("Heap Start: {:#x}", vma::HEAP_START);
log_info!("Heap Size: {} KiB", vma::HEAP_SIZE / 1024);
if let Some(stats) = pmm::get_memory_stats() {
log_info!("Total Memory: {} MiB", stats.total_memory / (1024 * 1024));
log_info!("Free Memory: {} MiB", stats.free_memory / (1024 * 1024));
}
vma::print_info();
vmm::get_vmm_stats().print();
}
fn _acpi_init(rsdp_addr: Option<u64>, physical_memory_offset: u64) {
fn _acpi_init(rsdp_addr: Option<u64>, physical_memory_offset: u64) -> Option<acpi::AcpiInfo> {
kprintln!();
if let Some(rsdp) = rsdp_addr {
@ -146,20 +145,82 @@ fn _acpi_init(rsdp_addr: Option<u64>, physical_memory_offset: u64) {
if let Some(acpi_info) = acpi::init(rsdp, physical_memory_offset) {
acpi::print_info(&acpi_info);
return Some(acpi_info);
} else {
log_warn!("ACPI initialization failed");
}
} else {
log_warn!("RSDP not provided by bootloader");
}
None
}
fn _post_init() {
fn _post_init(
acpi_info: Option<&acpi::AcpiInfo>,
mapper: &mut OffsetPageTable,
frame_allocator: &mut frame::BootInfoFrameAllocator,
) {
kprintln!();
log_info!("Post Initialization");
// TODO: 釋放 bootloader 佔用的內存
// 初始化 APIC/IOAPIC (如果有的話)
if let Some(info) = acpi_info {
if info.has_apic {
lapic::disable_legacy_pic();
unsafe {
if let Some(local_apic_addr) = info.local_apic_address {
log_info!("Initializing Local APIC...");
log_debug!("Mapping Local APIC physical address: {:#x}", local_apic_addr);
if let Some(vaddr) = vmm::map_device_memory(
PhysAddr::new(local_apic_addr),
4096,
mapper,
frame_allocator,
) {
log_debug!("Local APIC mapped to virtual address: {:#x}", vaddr.as_u64());
lapic::init_local_apic_with_vaddr(vaddr);
if let Some(apic_id) = lapic::get_apic_id() {
log_info!("Current Local APIC ID: {}", apic_id);
}
} else {
log_error!("Failed to map Local APIC memory");
}
}
if !info.io_apics.is_empty() {
log_info!("Initializing IO APICs...");
for (paddr, id, gsi_base) in &info.io_apics {
log_debug!("Mapping IO APIC {} at physical address: {:#x}", id, paddr);
if let Some(vaddr) = vmm::map_device_memory(
PhysAddr::new(*paddr),
4096,
mapper,
frame_allocator,
) {
log_debug!("IO APIC {} mapped to virtual address: {:#x}", id, vaddr.as_u64());
crate::hal::ioapic::init_single_ioapic(vaddr, *id, *gsi_base);
} else {
log_error!("Failed to map IO APIC {} memory", id);
}
}
log_info!("All IO APICs initialized");
}
}
} else {
log_warn!("APIC not available, using legacy PIC (not implemented yet)");
}
}
rtc::init();
keyboard::init();
log_debug!("Cleanup completed");
}
@ -182,13 +243,12 @@ pub fn _kernel_init(boot_info: &'static mut BootInfo) -> ! {
&boot_info.memory_regions,
physical_memory_offset
);
let acpi_info = _acpi_init(rsdp_addr, physical_memory_offset);
_post_init(acpi_info.as_ref(), &mut mapper, &mut frame_allocator);
_boot_report(&boot_info.memory_regions, physical_memory_offset);
_acpi_init(rsdp_addr, physical_memory_offset);
_post_init();
kprintln!();
kprintln!("========================================");
kprintln!(" Kernel Initialization Complete! ");

View File

@ -1,8 +1,9 @@
// kernel/src/k_main.rs
// kernel/src/kernel/k_main.rs
use crate::hal::{cpu, rtc};
use crate::kprintln;
use crate::{log_trace, log_debug, log_info, log_warn, log_error, log_fatal};
use crate::mm::{vma, vmm};
use crate::mm::allocator::pmm;
pub fn _kernel_main() -> ! {
kprintln!();
@ -18,8 +19,10 @@ pub fn _kernel_main() -> ! {
kprintln!();
vma::print_info();
vmm::get_vmm_stats().print();
if let Some(stats) = pmm::get_memory_stats() {
log_info!("Total Memory: {} MiB", stats.total_memory / (1024 * 1024));
log_info!("Free Memory: {} MiB", stats.free_memory / (1024 * 1024));
}
kprintln!();
@ -32,33 +35,45 @@ pub fn _kernel_main() -> ! {
log_debug!("CR3: 0x{:016x}", cpu::cpu_r_cr3());
log_debug!("CR4: 0x{:016x}", cpu::cpu_r_cr4());
kprintln!();
if let Some(apic_id) = crate::hal::lapic::get_apic_id() {
log_info!("Configuring hardware interrupts...");
log_info!("Current CPU APIC ID: {}", apic_id);
// kprintln!();
// kprintln!("=== Logger Level Demonstration ===");
// kprintln!();
//
// log_trace!("TRACE: This is a trace message (lowest priority)");
// log_debug!("DEBUG: Detailed debugging information");
// log_info!("INFO: General information about system operation");
// log_warn!("WARN: Warning message - something might be wrong");
// log_error!("ERROR: Error occurred but system can continue");
// log_fatal!("FATAL: Critical error (highest priority)");
//
// kprintln!();
//
// log_info!("Starting system services...");
// log_debug!("Loading drivers...");
// log_trace!(" Scanning PCI bus");
// log_trace!("Initializing USB controller");
// log_debug!("Drivers loaded successfully");
//
// log_info!("System is ready!");
log_info!("Setting up keyboard interrupt (IRQ 1 -> Vector 33)");
crate::hal::ioapic::set_irq_redirect(
1, // IRQ number (keyboard)
33, // Interrupt vector number
apic_id as u8, // APIC ID of target CPU
false, // Edge triggered (false = edge, true = level)
false // Active high (false = high, true = low)
);
// Unmask IRQ 1 (enable keyboard interrupt)
crate::hal::ioapic::unmask_irq(1);
log_info!("Keyboard interrupt unmasked");
// Enable CPU interrupts
cpu::cpu_enable_interrupts();
log_info!("CPU interrupts enabled");
kprintln!();
log_info!("Interrupt system ready!");
} else {
log_error!("APIC not available, cannot enable keyboard");
}
kprintln!();
log_warn!("Entering idle loop");
log_info!("System initialization complete!");
log_warn!("Entering idle loop...");
kprintln!();
loop {
// Until the next interrupt occurs
cpu::cpu_halt();
}
}

View File

@ -18,7 +18,7 @@ pub fn _print(args: fmt::Arguments) {
}
#[macro_export]
macro_rules! print {
macro_rules! kprint {
($($arg:tt)*) => ($crate::klibc::print::_print(format_args!($($arg)*)));
}

View File

@ -21,6 +21,7 @@ pub mod arch;
pub mod mm;
pub mod tty;
pub mod kernel;
pub mod drivers;
use hal::cpu;
const CONFIG: BootloaderConfig = {