commit
2197f20134
7
TODO.txt
7
TODO.txt
@ -2,9 +2,7 @@
|
||||
// - IDT (中斷描述符表) - OK
|
||||
// - 中斷處理函數 - OK
|
||||
// - ACPI 初始化 - OK
|
||||
// - PIC/APIC 初始化
|
||||
|
||||
// - 滾動 - OK
|
||||
// - PIC/APIC 初始化 - OK
|
||||
|
||||
// 2. 內存管理
|
||||
// - 物理內存分配器 - OK
|
||||
@ -22,6 +20,9 @@
|
||||
// - 用戶態/內核態切換
|
||||
|
||||
|
||||
// 5. 回收
|
||||
// - 關機重啟回收記憶體
|
||||
|
||||
// ==================== 高半核地址空間佈局 ====================
|
||||
//
|
||||
// 0x0000_0000_0000 ┌─────────────────────┐
|
||||
|
||||
@ -14,25 +14,25 @@ lazy_static! {
|
||||
static ref TSS: TaskStateSegment = {
|
||||
let mut tss = TaskStateSegment::new();
|
||||
|
||||
unsafe { tss.interrupt_stack_table[DOUBLE_FAULT_IST_INDEX as usize] = {
|
||||
const STACK_SIZE: usize = 4096 * 5; // 20KB
|
||||
static mut STACK: [u8; STACK_SIZE] = [0; STACK_SIZE];
|
||||
// Double Fault Stack
|
||||
unsafe {
|
||||
tss.interrupt_stack_table[DOUBLE_FAULT_IST_INDEX as usize] = {
|
||||
const STACK_SIZE: usize = 4096 * 5;
|
||||
static mut STACK: [u8; STACK_SIZE] = [0; STACK_SIZE];
|
||||
let stack_start = VirtAddr::new(&raw const STACK as u64);
|
||||
stack_start + STACK_SIZE as u64
|
||||
};
|
||||
}
|
||||
|
||||
let stack_start = VirtAddr::new(&raw const STACK as *const _ as u64);
|
||||
let stack_end = stack_start + STACK_SIZE as u64;
|
||||
|
||||
stack_end
|
||||
}; }
|
||||
|
||||
unsafe { tss.privilege_stack_table[0] = {
|
||||
const STACK_SIZE: usize = 4096 * 5; // 20KB
|
||||
static mut STACK: [u8; STACK_SIZE] = [0; STACK_SIZE];
|
||||
|
||||
let stack_start = VirtAddr::new(&raw const STACK as *const _ as u64);
|
||||
let stack_end = stack_start + STACK_SIZE as u64;
|
||||
|
||||
stack_end
|
||||
}; }
|
||||
// Privilege Stack
|
||||
unsafe {
|
||||
tss.privilege_stack_table[0] = {
|
||||
const STACK_SIZE: usize = 4096 * 5;
|
||||
static mut STACK: [u8; STACK_SIZE] = [0; STACK_SIZE];
|
||||
let stack_start = VirtAddr::new(&raw const STACK as u64);
|
||||
stack_start + STACK_SIZE as u64
|
||||
};
|
||||
}
|
||||
|
||||
tss
|
||||
};
|
||||
@ -42,21 +42,21 @@ lazy_static! {
|
||||
static ref GDT: (GlobalDescriptorTable, Selectors) = {
|
||||
let mut gdt = GlobalDescriptorTable::new();
|
||||
|
||||
// 0x00: Null
|
||||
// 0x00: Null Descriptor (必須)
|
||||
|
||||
// 0x08 ring 0
|
||||
// 0x08: Kernel Code (ring 0, executable)
|
||||
let kernel_code_selector = gdt.append(Descriptor::kernel_code_segment());
|
||||
|
||||
// 0x10 ring 0
|
||||
// 0x10: Kernel Data (ring 0, writable)
|
||||
let kernel_data_selector = gdt.append(Descriptor::kernel_data_segment());
|
||||
|
||||
// 0x18 ring 3
|
||||
let user_code_selector = gdt.append(Descriptor::user_code_segment());
|
||||
|
||||
// 0x20 ring 3
|
||||
// 0x18: User Data (ring 3, writable) - 注意順序
|
||||
let user_data_selector = gdt.append(Descriptor::user_data_segment());
|
||||
|
||||
// 0x28 Task seg
|
||||
// 0x20: User Code (ring 3, executable)
|
||||
let user_code_selector = gdt.append(Descriptor::user_code_segment());
|
||||
|
||||
// 0x28: TSS (佔用 2 個條目)
|
||||
let tss_selector = gdt.append(Descriptor::tss_segment(&TSS));
|
||||
|
||||
(
|
||||
@ -101,11 +101,10 @@ pub fn kernel_data_selector() -> SegmentSelector {
|
||||
GDT.1.kernel_data_selector
|
||||
}
|
||||
|
||||
// 初始化 GDT
|
||||
// initialization GDT
|
||||
pub fn init() {
|
||||
|
||||
GDT.0.load();
|
||||
let gdt_addr = &GDT.0 as *const _ as u64;
|
||||
log_debug!("GDT address: {:#018x}", gdt_addr);
|
||||
|
||||
unsafe {
|
||||
CS::set_reg(GDT.1.kernel_code_selector);
|
||||
@ -114,9 +113,11 @@ pub fn init() {
|
||||
ES::set_reg(GDT.1.kernel_data_selector);
|
||||
SS::set_reg(GDT.1.kernel_data_selector);
|
||||
|
||||
// 加載 TSS
|
||||
load_tss(GDT.1.tss_selector);
|
||||
}
|
||||
|
||||
log_debug!("GDT loaded at {:#018x}", &GDT.0 as *const _ as u64);
|
||||
log_debug!("TSS loaded, selector: {:#x}", GDT.1.tss_selector.0);
|
||||
}
|
||||
|
||||
pub fn print_info() {
|
||||
|
||||
@ -35,6 +35,19 @@ lazy_static! {
|
||||
idt.simd_floating_point.set_handler_fn(simd_floating_point_handler);
|
||||
idt.virtualization.set_handler_fn(virtualization_handler);
|
||||
|
||||
// IRQ 0 (32)
|
||||
idt[32].set_handler_fn(apic_timer_handler);
|
||||
|
||||
// IRQ 1 (33) - Keyboard
|
||||
idt[33].set_handler_fn(keyboard_interrupt_handler); // IRQ 1 Keyboard
|
||||
|
||||
idt[40].set_handler_fn(rtc_interrupt_handler); // IRQ 8
|
||||
|
||||
for i in 34..=47 {
|
||||
if i != 40 && i != 32 {
|
||||
idt[i].set_handler_fn(default_irq_handler);
|
||||
}
|
||||
}
|
||||
idt
|
||||
};
|
||||
}
|
||||
@ -44,6 +57,6 @@ pub fn init() {
|
||||
}
|
||||
|
||||
pub fn print_info() {
|
||||
log_info!(" IDT loaded and active");
|
||||
log_info!(" Exception handlers registered");
|
||||
log_info!("IDT loaded and active");
|
||||
log_info!("Exception handlers registered");
|
||||
}
|
||||
@ -1,9 +1,12 @@
|
||||
use x86_64::instructions::port::Port;
|
||||
// kernel/src/kernel/asm/amd64/isr
|
||||
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::{drivers, kprintln};
|
||||
use crate::{log_debug, log_error, log_fatal, log_info, log_trace, log_warn};
|
||||
use super::gdt;
|
||||
use crate::hal::{cpu, rtc, timer};
|
||||
use crate::hal::apic::lapic;
|
||||
use crate::mm::paging;
|
||||
|
||||
/// Divide Error (#DE)
|
||||
@ -20,28 +23,45 @@ pub extern "x86-interrupt" fn divide_error_handler(stack_frame: InterruptStackFr
|
||||
pub extern "x86-interrupt" fn debug_handler(stack_frame: InterruptStackFrame) {
|
||||
kprintln!();
|
||||
log_debug!("EXCEPTION: DEBUG (#DB)");
|
||||
log_debug!("{:#?}", stack_frame);
|
||||
log_debug!("instruction pointer: {:#x}", stack_frame.instruction_pointer.as_u64());
|
||||
log_debug!("code segment: index: {:#?}, rpl: {:#?}", stack_frame.code_segment.index(), stack_frame.code_segment.rpl());
|
||||
log_debug!("cpu flags: {:#x}", stack_frame.cpu_flags.bits());
|
||||
log_debug!("stack pointer: {:#x}", stack_frame.stack_pointer.as_u64());
|
||||
log_debug!("stack segment: index: {:#?}, rpl: {:#?}", stack_frame.stack_segment.index(), stack_frame.stack_segment.rpl());
|
||||
}
|
||||
|
||||
/// Non-Maskable Interrupt (NMI)
|
||||
pub extern "x86-interrupt" fn nmi_handler(stack_frame: InterruptStackFrame) {
|
||||
kprintln!();
|
||||
log_fatal!("EXCEPTION: NON-MASKABLE INTERRUPT (NMI)");
|
||||
log_fatal!("{:#?}", stack_frame);
|
||||
log_fatal!("instruction pointer: {:#x}", stack_frame.instruction_pointer.as_u64());
|
||||
log_fatal!("code segment: index: {:#?}, rpl: {:#?}", stack_frame.code_segment.index(), stack_frame.code_segment.rpl());
|
||||
log_fatal!("cpu flags: {:#x}", stack_frame.cpu_flags.bits());
|
||||
log_fatal!("stack pointer: {:#x}", stack_frame.stack_pointer.as_u64());
|
||||
log_fatal!("stack segment: index: {:#?}, rpl: {:#?}", stack_frame.stack_segment.index(), stack_frame.stack_segment.rpl());
|
||||
}
|
||||
|
||||
/// Breakpoint (#BP)
|
||||
pub extern "x86-interrupt" fn breakpoint_handler(stack_frame: InterruptStackFrame) {
|
||||
kprintln!();
|
||||
log_debug!("EXCEPTION: BREAKPOINT (#BP)");
|
||||
log_debug!("{:#?}", stack_frame);
|
||||
log_debug!("instruction pointer: {:#x}", stack_frame.instruction_pointer.as_u64());
|
||||
log_debug!("code segment: index: {:#?}, rpl: {:#?}", stack_frame.code_segment.index(), stack_frame.code_segment.rpl());
|
||||
log_debug!("cpu flags: {:#x}", stack_frame.cpu_flags.bits());
|
||||
log_debug!("stack pointer: {:#x}", stack_frame.stack_pointer.as_u64());
|
||||
log_debug!("stack segment: index: {:#?}, rpl: {:#?}", stack_frame.stack_segment.index(), stack_frame.stack_segment.rpl());
|
||||
}
|
||||
|
||||
/// Overflow (#OF)
|
||||
pub extern "x86-interrupt" fn overflow_handler(stack_frame: InterruptStackFrame) {
|
||||
kprintln!();
|
||||
log_error!("EXCEPTION: OVERFLOW (#OF)");
|
||||
log_error!("{:#?}", stack_frame);
|
||||
log_error!("instruction pointer: {:#x}", stack_frame.instruction_pointer.as_u64());
|
||||
log_error!("code segment: index: {:#?}, rpl: {:#?}", stack_frame.code_segment.index(), stack_frame.code_segment.rpl());
|
||||
log_error!("cpu flags: {:#x}", stack_frame.cpu_flags.bits());
|
||||
log_error!("stack pointer: {:#x}", stack_frame.stack_pointer.as_u64());
|
||||
log_error!("stack segment: index: {:#?}, rpl: {:#?}", stack_frame.stack_segment.index(), stack_frame.stack_segment.rpl());
|
||||
|
||||
loop {
|
||||
cpu::cpu_halt();
|
||||
}
|
||||
@ -51,7 +71,12 @@ pub extern "x86-interrupt" fn overflow_handler(stack_frame: InterruptStackFrame)
|
||||
pub extern "x86-interrupt" fn bound_range_handler(stack_frame: InterruptStackFrame) {
|
||||
kprintln!();
|
||||
log_error!("EXCEPTION: BOUND RANGE EXCEEDED (#BR)");
|
||||
log_error!("{:#?}", stack_frame);
|
||||
log_error!("instruction pointer: {:#x}", stack_frame.instruction_pointer.as_u64());
|
||||
log_error!("code segment: index: {:#?}, rpl: {:#?}", stack_frame.code_segment.index(), stack_frame.code_segment.rpl());
|
||||
log_error!("cpu flags: {:#x}", stack_frame.cpu_flags.bits());
|
||||
log_error!("stack pointer: {:#x}", stack_frame.stack_pointer.as_u64());
|
||||
log_error!("stack segment: index: {:#?}, rpl: {:#?}", stack_frame.stack_segment.index(), stack_frame.stack_segment.rpl());
|
||||
|
||||
loop {
|
||||
cpu::cpu_halt();
|
||||
}
|
||||
@ -61,7 +86,12 @@ pub extern "x86-interrupt" fn bound_range_handler(stack_frame: InterruptStackFra
|
||||
pub extern "x86-interrupt" fn invalid_opcode_handler(stack_frame: InterruptStackFrame) {
|
||||
kprintln!();
|
||||
log_error!("EXCEPTION: INVALID OPCODE (#UD)");
|
||||
log_error!("{:#?}", stack_frame);
|
||||
log_error!("instruction pointer: {:#x}", stack_frame.instruction_pointer.as_u64());
|
||||
log_error!("code segment: index: {:#?}, rpl: {:#?}", stack_frame.code_segment.index(), stack_frame.code_segment.rpl());
|
||||
log_error!("cpu flags: {:#x}", stack_frame.cpu_flags.bits());
|
||||
log_error!("stack pointer: {:#x}", stack_frame.stack_pointer.as_u64());
|
||||
log_error!("stack segment: index: {:#?}, rpl: {:#?}", stack_frame.stack_segment.index(), stack_frame.stack_segment.rpl());
|
||||
|
||||
loop {
|
||||
cpu::cpu_halt();
|
||||
}
|
||||
@ -71,7 +101,12 @@ pub extern "x86-interrupt" fn invalid_opcode_handler(stack_frame: InterruptStack
|
||||
pub extern "x86-interrupt" fn device_not_available_handler(stack_frame: InterruptStackFrame) {
|
||||
kprintln!();
|
||||
log_error!("EXCEPTION: DEVICE NOT AVAILABLE (#NM)");
|
||||
log_error!("{:#?}", stack_frame);
|
||||
log_error!("instruction pointer: {:#x}", stack_frame.instruction_pointer.as_u64());
|
||||
log_error!("code segment: index: {:#?}, rpl: {:#?}", stack_frame.code_segment.index(), stack_frame.code_segment.rpl());
|
||||
log_error!("cpu flags: {:#x}", stack_frame.cpu_flags.bits());
|
||||
log_error!("stack pointer: {:#x}", stack_frame.stack_pointer.as_u64());
|
||||
log_error!("stack segment: index: {:#?}, rpl: {:#?}", stack_frame.stack_segment.index(), stack_frame.stack_segment.rpl());
|
||||
|
||||
loop {
|
||||
cpu::cpu_halt();
|
||||
}
|
||||
@ -85,7 +120,13 @@ pub extern "x86-interrupt" fn double_fault_handler(
|
||||
kprintln!();
|
||||
log_fatal!("EXCEPTION: DOUBLE FAULT (#DF)");
|
||||
log_fatal!("Error Code: {:#x}", error_code);
|
||||
log_fatal!("{:#?}", stack_frame);
|
||||
|
||||
log_fatal!("instruction pointer: {:#x}", stack_frame.instruction_pointer.as_u64());
|
||||
log_fatal!("code segment: index: {:#?}, rpl: {:#?}", stack_frame.code_segment.index(), stack_frame.code_segment.rpl());
|
||||
log_fatal!("cpu flags: {:#x}", stack_frame.cpu_flags.bits());
|
||||
log_fatal!("stack pointer: {:#x}", stack_frame.stack_pointer.as_u64());
|
||||
log_fatal!("stack segment: index: {:#?}, rpl: {:#?}", stack_frame.stack_segment.index(), stack_frame.stack_segment.rpl());
|
||||
|
||||
panic!("DOUBLE FAULT - System cannot continue");
|
||||
}
|
||||
|
||||
@ -97,7 +138,13 @@ pub extern "x86-interrupt" fn invalid_tss_handler(
|
||||
kprintln!();
|
||||
log_fatal!("EXCEPTION: INVALID TSS (#TS)");
|
||||
log_fatal!("Error Code: {:#x}", error_code);
|
||||
log_fatal!("{:#?}", stack_frame);
|
||||
|
||||
log_fatal!("instruction pointer: {:#x}", stack_frame.instruction_pointer.as_u64());
|
||||
log_fatal!("code segment: index: {:#?}, rpl: {:#?}", stack_frame.code_segment.index(), stack_frame.code_segment.rpl());
|
||||
log_fatal!("cpu flags: {:#x}", stack_frame.cpu_flags.bits());
|
||||
log_fatal!("stack pointer: {:#x}", stack_frame.stack_pointer.as_u64());
|
||||
log_fatal!("stack segment: index: {:#?}, rpl: {:#?}", stack_frame.stack_segment.index(), stack_frame.stack_segment.rpl());
|
||||
|
||||
loop {
|
||||
cpu::cpu_halt();
|
||||
}
|
||||
@ -111,7 +158,21 @@ pub extern "x86-interrupt" fn segment_not_present_handler(
|
||||
kprintln!();
|
||||
log_fatal!("EXCEPTION: SEGMENT NOT PRESENT (#NP)");
|
||||
log_fatal!("Error Code: {:#x}", error_code);
|
||||
log_fatal!("{:#?}", stack_frame);
|
||||
log_fatal!("instruction pointer: {:#x}", stack_frame.instruction_pointer.as_u64());
|
||||
log_fatal!("code segment: index: {:#?}, rpl: {:#?}", stack_frame.code_segment.index(), stack_frame.code_segment.rpl());
|
||||
log_fatal!("cpu flags: {:#x}", stack_frame.cpu_flags.bits());
|
||||
log_fatal!("stack pointer: {:#x}", stack_frame.stack_pointer.as_u64());
|
||||
log_fatal!("stack segment: index: {:#?}, rpl: {:#?}", stack_frame.stack_segment.index(), stack_frame.stack_segment.rpl());
|
||||
|
||||
let is_external = (error_code & 0x01) != 0;
|
||||
let table = if (error_code & 0x02) != 0 { "IDT" } else { "GDT" };
|
||||
let index = (error_code >> 3) & 0x1FFF;
|
||||
|
||||
log_fatal!("Segment: {} index {:#x} (external: {})", table, index, is_external);
|
||||
|
||||
log_fatal!("CS: {:#x}", gdt::kernel_code_selector().0);
|
||||
log_fatal!("SS: {:#x}", gdt::kernel_data_selector().0);
|
||||
|
||||
loop {
|
||||
cpu::cpu_halt();
|
||||
}
|
||||
@ -125,7 +186,12 @@ pub extern "x86-interrupt" fn stack_segment_fault_handler(
|
||||
kprintln!();
|
||||
log_fatal!("EXCEPTION: STACK SEGMENT FAULT (#SS)");
|
||||
log_fatal!("Error Code: {:#x}", error_code);
|
||||
log_fatal!("{:#?}", stack_frame);
|
||||
log_fatal!("instruction pointer: {:#x}", stack_frame.instruction_pointer.as_u64());
|
||||
log_fatal!("code segment: index: {:#?}, rpl: {:#?}", stack_frame.code_segment.index(), stack_frame.code_segment.rpl());
|
||||
log_fatal!("cpu flags: {:#x}", stack_frame.cpu_flags.bits());
|
||||
log_fatal!("stack pointer: {:#x}", stack_frame.stack_pointer.as_u64());
|
||||
log_fatal!("stack segment: index: {:#?}, rpl: {:#?}", stack_frame.stack_segment.index(), stack_frame.stack_segment.rpl());
|
||||
|
||||
loop {
|
||||
cpu::cpu_halt();
|
||||
}
|
||||
@ -139,7 +205,12 @@ pub extern "x86-interrupt" fn general_protection_fault_handler(
|
||||
kprintln!();
|
||||
log_fatal!("EXCEPTION: GENERAL PROTECTION FAULT (#GP)");
|
||||
log_fatal!("Error Code: {:#x}", error_code);
|
||||
log_fatal!("{:#?}", stack_frame);
|
||||
log_fatal!("instruction pointer: {:#x}", stack_frame.instruction_pointer.as_u64());
|
||||
log_fatal!("code segment: index: {:#?}, rpl: {:#?}", stack_frame.code_segment.index(), stack_frame.code_segment.rpl());
|
||||
log_fatal!("cpu flags: {:#x}", stack_frame.cpu_flags.bits());
|
||||
log_fatal!("stack pointer: {:#x}", stack_frame.stack_pointer.as_u64());
|
||||
log_fatal!("stack segment: index: {:#?}, rpl: {:#?}", stack_frame.stack_segment.index(), stack_frame.stack_segment.rpl());
|
||||
|
||||
loop {
|
||||
cpu::cpu_halt();
|
||||
}
|
||||
@ -160,7 +231,12 @@ pub extern "x86-interrupt" fn page_fault_handler(
|
||||
log_fatal!("User: {}", error_code.contains(PageFaultErrorCode::USER_MODE));
|
||||
log_fatal!("Reserved Write: {}", error_code.contains(PageFaultErrorCode::MALFORMED_TABLE));
|
||||
log_fatal!("Instruction Fetch: {}", error_code.contains(PageFaultErrorCode::INSTRUCTION_FETCH));
|
||||
log_fatal!("{:#?}", stack_frame);
|
||||
|
||||
log_fatal!("instruction pointer: {:#x}", stack_frame.instruction_pointer.as_u64());
|
||||
log_fatal!("code segment: index: {:#?}, rpl: {:#?}", stack_frame.code_segment.index(), stack_frame.code_segment.rpl());
|
||||
log_fatal!("cpu flags: {:#x}", stack_frame.cpu_flags.bits());
|
||||
log_fatal!("stack pointer: {:#x}", stack_frame.stack_pointer.as_u64());
|
||||
log_fatal!("stack segment: index: {:#?}, rpl: {:#?}", stack_frame.stack_segment.index(), stack_frame.stack_segment.rpl());
|
||||
|
||||
paging::handle_page_fault(
|
||||
VirtAddr::new(cpu::cpu_r_cr2()),
|
||||
@ -176,7 +252,12 @@ pub extern "x86-interrupt" fn page_fault_handler(
|
||||
pub extern "x86-interrupt" fn x87_floating_point_handler(stack_frame: InterruptStackFrame) {
|
||||
kprintln!();
|
||||
log_error!("EXCEPTION: x87 FLOATING POINT (#MF)");
|
||||
log_error!("{:#?}", stack_frame);
|
||||
log_error!("instruction pointer: {:#x}", stack_frame.instruction_pointer.as_u64());
|
||||
log_error!("code segment: index: {:#?}, rpl: {:#?}", stack_frame.code_segment.index(), stack_frame.code_segment.rpl());
|
||||
log_error!("cpu flags: {:#x}", stack_frame.cpu_flags.bits());
|
||||
log_error!("stack pointer: {:#x}", stack_frame.stack_pointer.as_u64());
|
||||
log_error!("stack segment: index: {:#?}, rpl: {:#?}", stack_frame.stack_segment.index(), stack_frame.stack_segment.rpl());
|
||||
|
||||
loop {
|
||||
cpu::cpu_halt();
|
||||
}
|
||||
@ -190,7 +271,12 @@ pub extern "x86-interrupt" fn alignment_check_handler(
|
||||
kprintln!();
|
||||
log_error!("EXCEPTION: ALIGNMENT CHECK (#AC)");
|
||||
log_error!("Error Code: {:#x}", error_code);
|
||||
log_error!("{:#?}", stack_frame);
|
||||
log_error!("instruction pointer: {:#x}", stack_frame.instruction_pointer.as_u64());
|
||||
log_error!("code segment: index: {:#?}, rpl: {:#?}", stack_frame.code_segment.index(), stack_frame.code_segment.rpl());
|
||||
log_error!("cpu flags: {:#x}", stack_frame.cpu_flags.bits());
|
||||
log_error!("stack pointer: {:#x}", stack_frame.stack_pointer.as_u64());
|
||||
log_error!("stack segment: index: {:#?}, rpl: {:#?}", stack_frame.stack_segment.index(), stack_frame.stack_segment.rpl());
|
||||
|
||||
loop {
|
||||
cpu::cpu_halt();
|
||||
}
|
||||
@ -200,15 +286,25 @@ pub extern "x86-interrupt" fn alignment_check_handler(
|
||||
pub extern "x86-interrupt" fn machine_check_handler(stack_frame: InterruptStackFrame) -> ! {
|
||||
kprintln!();
|
||||
log_error!("EXCEPTION: MACHINE CHECK (#MC)");
|
||||
log_error!("{:#?}", stack_frame);
|
||||
log_error!("instruction pointer: {:#x}", stack_frame.instruction_pointer.as_u64());
|
||||
log_error!("code segment: index: {:#?}, rpl: {:#?}", stack_frame.code_segment.index(), stack_frame.code_segment.rpl());
|
||||
log_error!("cpu flags: {:#x}", stack_frame.cpu_flags.bits());
|
||||
log_error!("stack pointer: {:#x}", stack_frame.stack_pointer.as_u64());
|
||||
log_error!("stack segment: index: {:#?}, rpl: {:#?}", stack_frame.stack_segment.index(), stack_frame.stack_segment.rpl());
|
||||
|
||||
panic!("MACHINE CHECK - System cannot continue");
|
||||
}
|
||||
|
||||
/// SIMD Floating-Point Exception (#XM/#XF)
|
||||
pub extern "x86-interrupt" fn simd_floating_point_handler(stack_frame: InterruptStackFrame) {
|
||||
kprintln!();
|
||||
kprintln!("EXCEPTION: SIMD FLOATING POINT (#XM/#XF)");
|
||||
kprintln!("{:#?}", stack_frame);
|
||||
log_error!("EXCEPTION: SIMD FLOATING POINT (#XM/#XF)");
|
||||
log_error!("instruction pointer: {:#x}", stack_frame.instruction_pointer.as_u64());
|
||||
log_error!("code segment: index: {:#?}, rpl: {:#?}", stack_frame.code_segment.index(), stack_frame.code_segment.rpl());
|
||||
log_error!("cpu flags: {:#x}", stack_frame.cpu_flags.bits());
|
||||
log_error!("stack pointer: {:#x}", stack_frame.stack_pointer.as_u64());
|
||||
log_error!("stack segment: index: {:#?}, rpl: {:#?}", stack_frame.stack_segment.index(), stack_frame.stack_segment.rpl());
|
||||
|
||||
loop {
|
||||
cpu::cpu_halt();
|
||||
}
|
||||
@ -218,10 +314,57 @@ pub extern "x86-interrupt" fn simd_floating_point_handler(stack_frame: Interrupt
|
||||
pub extern "x86-interrupt" fn virtualization_handler(stack_frame: InterruptStackFrame) {
|
||||
kprintln!();
|
||||
log_warn!("EXCEPTION: VIRTUALIZATION (#VE)");
|
||||
log_warn!("{:#?}", stack_frame);
|
||||
log_warn!("instruction pointer: {:#x}", stack_frame.instruction_pointer.as_u64());
|
||||
log_warn!("code segment: index: {:#?}, rpl: {:#?}", stack_frame.code_segment.index(), stack_frame.code_segment.rpl());
|
||||
log_warn!("cpu flags: {:#x}", stack_frame.cpu_flags.bits());
|
||||
log_warn!("stack pointer: {:#x}", stack_frame.stack_pointer.as_u64());
|
||||
log_warn!("stack segment: index: {:#?}, rpl: {:#?}", stack_frame.stack_segment.index(), stack_frame.stack_segment.rpl());
|
||||
|
||||
loop {
|
||||
cpu::cpu_halt();
|
||||
}
|
||||
}
|
||||
|
||||
// TODO Timer interrupt, Keyboard interrupt
|
||||
// TODO Timer interrupt, Keyboard interrupt
|
||||
|
||||
pub extern "x86-interrupt" fn keyboard_interrupt_handler(_stack_frame: InterruptStackFrame) {
|
||||
|
||||
unsafe {
|
||||
let mut port = Port::new(0x60);
|
||||
let scancode: u8 = port.read();
|
||||
|
||||
drivers::keyboard::handle_scancode(scancode);
|
||||
}
|
||||
|
||||
lapic::send_eoi();
|
||||
}
|
||||
|
||||
pub extern "x86-interrupt" fn default_irq_handler(_stack_frame: InterruptStackFrame) {
|
||||
lapic::send_eoi();
|
||||
log_trace!("Unhandled IRQ");
|
||||
}
|
||||
|
||||
|
||||
pub extern "x86-interrupt" fn apic_timer_handler(_stack_frame: InterruptStackFrame) {
|
||||
|
||||
// log_info!("Processing of APIC Timer Calibration Phase");
|
||||
if timer::is_calibrating() {
|
||||
timer::apic_calibration_handler();
|
||||
} else {
|
||||
timer::timer_tick_handler();
|
||||
}
|
||||
|
||||
lapic::send_eoi();
|
||||
}
|
||||
|
||||
pub extern "x86-interrupt" fn rtc_interrupt_handler(_stack_frame: InterruptStackFrame) {
|
||||
rtc::handle_interrupt();
|
||||
|
||||
// log_info!("Processing of APIC Timer Calibration Phase");
|
||||
if timer::is_calibrating() {
|
||||
timer::rtc_calibration_handler();
|
||||
}
|
||||
|
||||
lapic::send_eoi();
|
||||
}
|
||||
|
||||
|
||||
217
kernel/src/drivers/keyboard.rs
Normal file
217
kernel/src/drivers/keyboard.rs
Normal file
@ -0,0 +1,217 @@
|
||||
// kernel/src/drivers/keyboard.rs
|
||||
use spin::Mutex;
|
||||
use crate::{kprintln, log_debug, log_info, shell, tty};
|
||||
|
||||
/// 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, Numpad *)
|
||||
0, b' ', 0, 0, 0, 0, 0, 0, // 0x38-0x3F
|
||||
0, 0, 0, 0, 0, 0, 0, b'7', // 0x40-0x47 (Numpad 7)
|
||||
b'8', b'9', b'-', b'4', b'5', b'6', b'+', b'1', // 0x48-0x4F (Numpad)
|
||||
b'2', b'3', b'0', b'.', 0, 0, 0, 0, // 0x50-0x57 (Numpad)
|
||||
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, b'7', // 0x40-0x47
|
||||
b'8', b'9', b'-', b'4', b'5', b'6', b'+', b'1', // 0x48-0x4F
|
||||
b'2', b'3', b'0', b'.', 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
|
||||
];
|
||||
|
||||
/// Keyboard state
|
||||
struct KeyboardState {
|
||||
shift_pressed: bool,
|
||||
ctrl_pressed: bool,
|
||||
alt_pressed: bool,
|
||||
caps_lock: bool,
|
||||
num_lock: bool,
|
||||
e0_prefix: bool,
|
||||
}
|
||||
|
||||
impl KeyboardState {
|
||||
const fn new() -> Self {
|
||||
Self {
|
||||
shift_pressed: false,
|
||||
ctrl_pressed: false,
|
||||
alt_pressed: false,
|
||||
caps_lock: false,
|
||||
num_lock: true,
|
||||
e0_prefix: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static KEYBOARD_STATE: Mutex<KeyboardState> = Mutex::new(KeyboardState::new());
|
||||
|
||||
/// Handle keyboard scancode
|
||||
pub fn handle_scancode(raw: u8) {
|
||||
let mut state = KEYBOARD_STATE.lock();
|
||||
|
||||
if raw == 0xE0 {
|
||||
state.e0_prefix = true;
|
||||
return;
|
||||
}
|
||||
|
||||
let key_released = (raw & 0x80) != 0;
|
||||
let scancode = raw & 0x7F;
|
||||
let is_extended = state.e0_prefix;
|
||||
state.e0_prefix = false;
|
||||
|
||||
// Process the extended key
|
||||
if is_extended {
|
||||
if key_released {
|
||||
return;
|
||||
}
|
||||
|
||||
match scancode {
|
||||
0x1C => print_char(b'\n'), // Numpad Enter
|
||||
0x35 => print_char(b'/'), // Numpad /
|
||||
0x47 => kprintln!("[Home]"),
|
||||
0x48 => kprintln!("[Up]"),
|
||||
0x49 => kprintln!("[PgUp]"),
|
||||
0x4B => kprintln!("[Left]"),
|
||||
0x4D => kprintln!("[Right]"),
|
||||
0x4F => kprintln!("[End]"),
|
||||
0x50 => kprintln!("[Down]"),
|
||||
0x51 => kprintln!("[PgDn]"),
|
||||
0x52 => kprintln!("[Insert]"),
|
||||
0x53 => kprintln!("[Delete]"),
|
||||
|
||||
// 0x19 => { kprintln!("[Next Track]");}
|
||||
// 0x10 => { kprintln!("[Prev Track]");}
|
||||
// 0x24 => { kprintln!("[Stop]");}
|
||||
// 0x22 => { kprintln!("[Play/Pause]");}
|
||||
// 0x20 => { kprintln!("[Mute]");}
|
||||
// 0x30 => { kprintln!("[Volume Up]");}
|
||||
// 0x2E => { kprintln!("[Volume Down]");}
|
||||
_ => {}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Modifier keys
|
||||
match scancode {
|
||||
0x2A | 0x36 => { state.shift_pressed = !key_released; return; }
|
||||
0x1D => { state.ctrl_pressed = !key_released; return; }
|
||||
0x38 => { state.alt_pressed = !key_released; return; }
|
||||
0x3A => { if !key_released { state.caps_lock = !state.caps_lock; } return; }
|
||||
0x45 => { if !key_released { state.num_lock = !state.num_lock; } return; }
|
||||
_ => {}
|
||||
}
|
||||
|
||||
if key_released {
|
||||
return;
|
||||
}
|
||||
|
||||
// F1-F12
|
||||
match scancode {
|
||||
0x3B => { kprintln!("[F1]"); return; }
|
||||
0x3C => { kprintln!("[F2]"); return; }
|
||||
0x3D => { kprintln!("[F3]"); return; }
|
||||
0x3E => { kprintln!("[F4]"); return; }
|
||||
0x3F => { kprintln!("[F5]"); return; }
|
||||
0x40 => { kprintln!("[F6]"); return; }
|
||||
0x41 => { kprintln!("[F7]"); return; }
|
||||
0x42 => { kprintln!("[F8]"); return; }
|
||||
0x43 => { kprintln!("[F9]"); return; }
|
||||
0x44 => { kprintln!("[F10]"); return; }
|
||||
0x57 => { kprintln!("[F11]"); return; }
|
||||
0x58 => { kprintln!("[F12]"); return; }
|
||||
_ => {}
|
||||
}
|
||||
|
||||
// Esc 鍵
|
||||
if scancode == 0x01 {
|
||||
kprintln!("[Esc]");
|
||||
return;
|
||||
}
|
||||
|
||||
// Arrow keys when NumLock is OFF (0x47-0x53)
|
||||
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; }
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
let ascii = if state.shift_pressed {
|
||||
SCANCODE_TO_ASCII_SHIFT[scancode as usize]
|
||||
} else {
|
||||
SCANCODE_TO_ASCII[scancode as usize]
|
||||
};
|
||||
|
||||
if ascii == 0 {
|
||||
return;
|
||||
}
|
||||
|
||||
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
|
||||
};
|
||||
|
||||
if state.ctrl_pressed {
|
||||
match ascii {
|
||||
b'c' | b'C' => { kprintln!("^C"); return; }
|
||||
b'd' | b'D' => { kprintln!("^D"); return; }
|
||||
b'l' | b'L' => { tty::tty::clear(0x000000); return; }
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
print_char(ascii);
|
||||
}
|
||||
|
||||
/// Print a character to the screen
|
||||
fn print_char(c: u8) {
|
||||
if c == b'\n' {
|
||||
shell::process_keyboard_char('\n');
|
||||
} else if c == 8 { // Backspace
|
||||
shell::process_keyboard_char('\x08');
|
||||
} else if c.is_ascii_graphic() || c == b' ' {
|
||||
shell::process_keyboard_char(c as char);
|
||||
}
|
||||
}
|
||||
|
||||
/// Initialize keyboard driver
|
||||
pub fn init() {
|
||||
log_info!("Keyboard driver initialized");
|
||||
}
|
||||
1
kernel/src/drivers/mod.rs
Normal file
1
kernel/src/drivers/mod.rs
Normal file
@ -0,0 +1 @@
|
||||
pub mod keyboard;
|
||||
@ -1,314 +0,0 @@
|
||||
use acpi::{aml, AcpiTables, Handle, Handler, PciAddress, PhysicalMapping};
|
||||
use acpi::platform::{AcpiPlatform, interrupt::InterruptModel, PciConfigRegions};
|
||||
use acpi::sdt::hpet::HpetInfo;
|
||||
use acpi::rsdp::Rsdp;
|
||||
use core::ptr::NonNull;
|
||||
use core::mem;
|
||||
use crate::kprintln;
|
||||
use crate::{log_trace, log_debug, log_info, log_warn, log_error, log_fatal};
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct CureAcpiHandler {
|
||||
physical_memory_offset: u64,
|
||||
}
|
||||
|
||||
impl CureAcpiHandler {
|
||||
pub const fn new(physical_memory_offset: u64) -> Self {
|
||||
Self {
|
||||
physical_memory_offset,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Handler for CureAcpiHandler {
|
||||
unsafe fn map_physical_region<T>(
|
||||
&self,
|
||||
physical_address: usize,
|
||||
size: usize,
|
||||
) -> PhysicalMapping<Self, T> {
|
||||
// Bootloader 已經映射了所有物理記憶體
|
||||
let virtual_address = physical_address as u64 + self.physical_memory_offset;
|
||||
let virtual_start = NonNull::new((virtual_address) as *mut T).unwrap();
|
||||
|
||||
PhysicalMapping {
|
||||
physical_start: physical_address,
|
||||
virtual_start,
|
||||
region_length: size,
|
||||
mapped_length: size,
|
||||
handler: *self,
|
||||
}
|
||||
}
|
||||
|
||||
fn unmap_physical_region<T>(region: &PhysicalMapping<Self, T>) {
|
||||
//
|
||||
}
|
||||
|
||||
fn read_u8(&self, address: usize) -> u8 {
|
||||
unsafe {
|
||||
let ptr = self.map_physical_region::<u8>(address, 1);
|
||||
core::ptr::read_volatile(ptr.virtual_start.as_ptr())
|
||||
}
|
||||
}
|
||||
|
||||
fn read_u16(&self, address: usize) -> u16 {
|
||||
unsafe {
|
||||
let ptr = self.map_physical_region::<u16>(address, 2);
|
||||
core::ptr::read_volatile(ptr.virtual_start.as_ptr())
|
||||
}
|
||||
}
|
||||
|
||||
fn read_u32(&self, address: usize) -> u32 {
|
||||
unsafe {
|
||||
let ptr = self.map_physical_region::<u32>(address, 4);
|
||||
core::ptr::read_volatile(ptr.virtual_start.as_ptr())
|
||||
}
|
||||
}
|
||||
|
||||
fn read_u64(&self, address: usize) -> u64 {
|
||||
unsafe {
|
||||
let ptr = self.map_physical_region::<u64>(address, 8);
|
||||
core::ptr::read_volatile(ptr.virtual_start.as_ptr())
|
||||
}
|
||||
}
|
||||
|
||||
fn write_u8(&self, address: usize, value: u8) {
|
||||
unsafe {
|
||||
let ptr = self.map_physical_region::<u8>(address, 1);
|
||||
core::ptr::write_volatile(ptr.virtual_start.as_ptr(), value);
|
||||
}
|
||||
}
|
||||
|
||||
fn write_u16(&self, address: usize, value: u16) {
|
||||
unsafe {
|
||||
let ptr = self.map_physical_region::<u16>(address, 2);
|
||||
core::ptr::write_volatile(ptr.virtual_start.as_ptr(), value);
|
||||
}
|
||||
}
|
||||
|
||||
fn write_u32(&self, address: usize, value: u32) {
|
||||
unsafe {
|
||||
let ptr = self.map_physical_region::<u32>(address, 4);
|
||||
core::ptr::write_volatile(ptr.virtual_start.as_ptr(), value);
|
||||
}
|
||||
}
|
||||
|
||||
fn write_u64(&self, address: usize, value: u64) {
|
||||
unsafe {
|
||||
let ptr = self.map_physical_region::<u64>(address, 8);
|
||||
core::ptr::write_volatile(ptr.virtual_start.as_ptr(), value);
|
||||
}
|
||||
}
|
||||
|
||||
fn read_io_u8(&self, port: u16) -> u8 {
|
||||
unsafe { crate::hal::io::io_port_rb(port) }
|
||||
}
|
||||
|
||||
fn read_io_u16(&self, port: u16) -> u16 {
|
||||
unsafe { crate::hal::io::io_port_rw(port) }
|
||||
}
|
||||
|
||||
fn read_io_u32(&self, port: u16) -> u32 {
|
||||
unsafe { crate::hal::io::io_port_rl(port) }
|
||||
}
|
||||
|
||||
fn write_io_u8(&self, port: u16, value: u8) {
|
||||
unsafe { crate::hal::io::io_port_wb(port, value) };
|
||||
}
|
||||
|
||||
fn write_io_u16(&self, port: u16, value: u16) {
|
||||
unsafe { crate::hal::io::io_port_ww(port, value) };
|
||||
}
|
||||
|
||||
fn write_io_u32(&self, port: u16, value: u32) {
|
||||
unsafe { crate::hal::io::io_port_wl(port, value) };
|
||||
}
|
||||
|
||||
fn read_pci_u8(&self, address: PciAddress, offset: u16) -> u8 {
|
||||
// TODO: 實作 PCI 配置空間讀取
|
||||
0xFF
|
||||
}
|
||||
|
||||
fn read_pci_u16(&self, address: PciAddress, offset: u16) -> u16 {
|
||||
// TODO: 實作 PCI 配置空間讀取
|
||||
0xFFFF
|
||||
}
|
||||
|
||||
fn read_pci_u32(&self, address: PciAddress, offset: u16) -> u32 {
|
||||
// TODO: 實作 PCI 配置空間讀取
|
||||
0xFFFFFFFF
|
||||
}
|
||||
|
||||
fn write_pci_u8(&self, address: PciAddress, offset: u16, value: u8) {
|
||||
// TODO: 實作 PCI 配置空間寫入
|
||||
}
|
||||
|
||||
fn write_pci_u16(&self, address: PciAddress, offset: u16, value: u16) {
|
||||
// TODO: 實作 PCI 配置空間寫入
|
||||
}
|
||||
|
||||
fn write_pci_u32(&self, address: PciAddress, offset: u16, value: u32) {
|
||||
// TODO: 實作 PCI 配置空間寫入
|
||||
}
|
||||
|
||||
fn nanos_since_boot(&self) -> u64 {
|
||||
// TODO: 實作高精度計時器 (需要 HPET 或 TSC)
|
||||
// 目前返回 0
|
||||
0
|
||||
}
|
||||
|
||||
fn stall(&self, _microseconds: u64) {
|
||||
// TODO: 實作微秒級延遲
|
||||
// 簡單的忙等待實作
|
||||
for _ in 0..(_microseconds * 1000) {
|
||||
crate::hal::cpu::cpu_pause();
|
||||
}
|
||||
}
|
||||
|
||||
fn sleep(&self, _milliseconds: u64) {
|
||||
// TODO: 實作毫秒級睡眠
|
||||
// 簡單的忙等待實作
|
||||
self.stall(_milliseconds * 1000);
|
||||
}
|
||||
|
||||
fn create_mutex(&self) -> acpi::Handle {
|
||||
// TODO: 實作 Mutex
|
||||
// 目前返回一個假的 handle
|
||||
acpi::Handle(0)
|
||||
}
|
||||
|
||||
fn acquire(&self, mutex: Handle, timeout: u16) -> Result<(), aml::AmlError> {
|
||||
// TODO: 實作 Mutex 獲取
|
||||
// 暫時直接返回成功
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn release(&self, _handle: acpi::Handle) {
|
||||
// TODO: 實作 Mutex 釋放
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
let rsdp_mapping = unsafe {
|
||||
handler.map_physical_region::<Rsdp>(rsdp_addr as usize, mem::size_of::<Rsdp>())
|
||||
};
|
||||
let revision = rsdp_mapping.revision();
|
||||
log_info!("ACPI Revision: {}", revision);
|
||||
|
||||
let tables = unsafe {
|
||||
match AcpiTables::from_rsdp(handler, rsdp_addr as usize) {
|
||||
Ok(tables) => tables,
|
||||
Err(e) => {
|
||||
log_error!("Failed to parse ACPI tables: {:?}", e);
|
||||
return None;
|
||||
}
|
||||
}
|
||||
};
|
||||
// kprintln!(" ACPI Revision: {}", tables.rsdp_revision);
|
||||
|
||||
let platform = match AcpiPlatform::new(tables, handler) {
|
||||
Ok(platform) => platform,
|
||||
Err(e) => {
|
||||
log_error!("Failed to create ACPI platform: {:?}", e);
|
||||
return None;
|
||||
}
|
||||
};
|
||||
|
||||
log_info!("Power Profile: {:?}", platform.power_profile);
|
||||
|
||||
let (boot_processor, cpu_count) = if let Some(proc_info) = &platform.processor_info {
|
||||
let boot_proc = Some(proc_info.boot_processor.processor_uid);
|
||||
let cpu_cnt = proc_info.application_processors.len() + 1;
|
||||
|
||||
log_info!("Boot Processor UID: {:?}", boot_proc);
|
||||
log_info!("Total CPU Count: {}", cpu_cnt);
|
||||
|
||||
(boot_proc, cpu_cnt)
|
||||
} else {
|
||||
log_warn!("No processor info found");
|
||||
(None, 0)
|
||||
};
|
||||
|
||||
// 檢查中斷模型
|
||||
let has_apic = 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());
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
true
|
||||
}
|
||||
InterruptModel::Unknown => {
|
||||
log_warn!("Interrupt Model: Unknown (not APIC)");
|
||||
false
|
||||
}
|
||||
_ => {
|
||||
log_warn!("Interrupt Model: Other");
|
||||
false
|
||||
}
|
||||
};
|
||||
|
||||
let has_hpet = match HpetInfo::new(&platform.tables) {
|
||||
Ok(hpet) => {
|
||||
log_info!("Base Address: {:#x}", hpet.base_address);
|
||||
log_info!("Hardware Rev: {}", hpet.hardware_rev);
|
||||
log_info!("Comparator Count: {}", hpet.num_comparators);
|
||||
log_info!("Counter Size: {} bit", if hpet.main_counter_is_64bits { 64 } else { 32 });
|
||||
log_info!("Legacy IRQ Capable: {}", hpet.legacy_irq_capable);
|
||||
log_info!("PCI Vendor ID: {:#x}", hpet.pci_vendor_id);
|
||||
true
|
||||
}
|
||||
Err(_) => {
|
||||
log_warn!("HPET: Not available");
|
||||
false
|
||||
}
|
||||
};
|
||||
|
||||
if let Ok(mcfg) = PciConfigRegions::new(&platform.tables) {
|
||||
for (i, entry) in mcfg.regions.iter().enumerate() {
|
||||
let segment_group = entry.pci_segment_group;
|
||||
let base_addr = entry.base_address;
|
||||
let bus_start = entry.bus_number_start;
|
||||
let bus_end = entry.bus_number_end;
|
||||
|
||||
log_info!("Entry {}: Segment Group {}", i, segment_group);
|
||||
log_info!("Base Address: {:#x}", base_addr);
|
||||
log_info!("Bus Range: {}-{}", bus_start, bus_end);
|
||||
}
|
||||
}
|
||||
|
||||
log_info!("ACPI initialized successfully!");
|
||||
|
||||
Some(AcpiInfo {
|
||||
revision,
|
||||
boot_processor,
|
||||
cpu_count,
|
||||
has_apic,
|
||||
has_hpet,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn print_info(info: &AcpiInfo) {
|
||||
kprintln!();
|
||||
log_info!("Revision: ACPI {}.0", info.revision);
|
||||
log_info!("CPUs: {} processor(s)", info.cpu_count);
|
||||
if let Some(boot_proc) = info.boot_processor {
|
||||
log_info!("Boot Processor: UID {}", boot_proc);
|
||||
}
|
||||
log_info!("APIC: {}", if info.has_apic { "Available " } else { "Not available" });
|
||||
log_info!("HPET: {}", if info.has_hpet { "Available " } else { "Not available" });
|
||||
}
|
||||
173
kernel/src/hal/acpi/init.rs
Normal file
173
kernel/src/hal/acpi/init.rs
Normal file
@ -0,0 +1,173 @@
|
||||
use super::power::{extract_power_info, extract_reset_reg, store_power_info, store_reset_reg};
|
||||
use super::{AcpiInfo, CureAcpiHandler};
|
||||
use crate::hal::{cpu, io};
|
||||
use crate::kprintln;
|
||||
use crate::{log_debug, log_error, log_fatal, log_info, log_trace, log_warn};
|
||||
use acpi::platform::{interrupt::InterruptModel, AcpiPlatform, PciConfigRegions};
|
||||
use acpi::rsdp::Rsdp;
|
||||
use acpi::sdt::hpet::HpetInfo;
|
||||
use acpi::{aml, sdt, AcpiTables, Handle, Handler, PciAddress, PhysicalMapping};
|
||||
use core::{mem, ptr::NonNull};
|
||||
|
||||
pub fn init(rsdp_addr: u64, physical_memory_offset: u64) -> Option<AcpiInfo> {
|
||||
let handler = CureAcpiHandler::new(physical_memory_offset);
|
||||
|
||||
let rsdp_mapping =
|
||||
unsafe { handler.map_physical_region::<Rsdp>(rsdp_addr as usize, size_of::<Rsdp>()) };
|
||||
let revision = rsdp_mapping.revision();
|
||||
log_info!("ACPI Revision: {}", revision);
|
||||
|
||||
let tables = unsafe {
|
||||
match AcpiTables::from_rsdp(handler, rsdp_addr as usize) {
|
||||
Ok(tables) => tables,
|
||||
Err(e) => {
|
||||
log_error!("Failed to parse ACPI tables: {:?}", e);
|
||||
return None;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let platform = match AcpiPlatform::new(tables, handler) {
|
||||
Ok(platform) => platform,
|
||||
Err(e) => {
|
||||
log_error!("Failed to create ACPI platform: {:?}", e);
|
||||
return None;
|
||||
}
|
||||
};
|
||||
|
||||
log_info!("Power Profile: {:?}", platform.power_profile);
|
||||
|
||||
let (boot_processor, cpu_count) = if let Some(proc_info) = &platform.processor_info {
|
||||
let boot_proc = Some(proc_info.boot_processor.processor_uid);
|
||||
let cpu_cnt = proc_info.application_processors.len() + 1;
|
||||
|
||||
log_info!("Boot Processor UID: {:?}", boot_proc);
|
||||
log_info!("Total CPU Count: {}", cpu_cnt);
|
||||
|
||||
(boot_proc, cpu_cnt)
|
||||
} else {
|
||||
log_warn!("No processor info found");
|
||||
(None, 0)
|
||||
};
|
||||
|
||||
// Check interrupt mode
|
||||
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, Some(apic.local_apic_address as u64), io_apics)
|
||||
}
|
||||
InterruptModel::Unknown => {
|
||||
log_warn!("Interrupt Model: Unknown (not APIC)");
|
||||
(false, None, alloc::vec::Vec::new())
|
||||
}
|
||||
_ => {
|
||||
log_warn!("Interrupt Model: Other");
|
||||
(false, None, alloc::vec::Vec::new())
|
||||
}
|
||||
};
|
||||
|
||||
let has_hpet = match HpetInfo::new(&platform.tables) {
|
||||
Ok(hpet) => {
|
||||
log_info!("Base Address: {:#x}", hpet.base_address);
|
||||
log_info!("Hardware Rev: {}", hpet.hardware_rev);
|
||||
log_info!("Comparator Count: {}", hpet.num_comparators);
|
||||
log_info!(
|
||||
"Counter Size: {} bit",
|
||||
if hpet.main_counter_is_64bits { 64 } else { 32 }
|
||||
);
|
||||
log_info!("Legacy IRQ Capable: {}", hpet.legacy_irq_capable);
|
||||
log_info!("PCI Vendor ID: {:#x}", hpet.pci_vendor_id);
|
||||
true
|
||||
}
|
||||
Err(_) => {
|
||||
log_warn!("HPET: Not available");
|
||||
false
|
||||
}
|
||||
};
|
||||
|
||||
if let Ok(mcfg) = PciConfigRegions::new(&platform.tables) {
|
||||
for (i, entry) in mcfg.regions.iter().enumerate() {
|
||||
let segment_group = entry.pci_segment_group;
|
||||
let base_addr = entry.base_address;
|
||||
let bus_start = entry.bus_number_start;
|
||||
let bus_end = entry.bus_number_end;
|
||||
|
||||
log_info!("Entry {}: Segment Group {}", i, segment_group);
|
||||
log_info!("Base Address: {:#x}", base_addr);
|
||||
log_info!("Bus Range: {}-{}", bus_start, bus_end);
|
||||
}
|
||||
}
|
||||
|
||||
kprintln!();
|
||||
log_info!("Extracting power management information...");
|
||||
if let Some(power_info) = extract_power_info(&platform.tables, &handler) {
|
||||
store_power_info(power_info);
|
||||
} else {
|
||||
log_warn!("Could not extract ACPI power info");
|
||||
}
|
||||
|
||||
kprintln!();
|
||||
log_info!("Extracting reset register information...");
|
||||
if let Some(reset_reg) = extract_reset_reg(&platform.tables) {
|
||||
store_reset_reg(reset_reg);
|
||||
} else {
|
||||
log_warn!("Could not extract ACPI reset register");
|
||||
}
|
||||
|
||||
log_info!("ACPI initialized successfully!");
|
||||
|
||||
Some(AcpiInfo {
|
||||
revision,
|
||||
boot_processor,
|
||||
cpu_count,
|
||||
has_apic,
|
||||
has_hpet,
|
||||
local_apic_address: local_apic_addr,
|
||||
io_apics: io_apics_info,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn print_info(info: &AcpiInfo) {
|
||||
kprintln!();
|
||||
log_info!("Revision: ACPI {}.0", info.revision);
|
||||
log_info!("CPUs: {} processor(s)", info.cpu_count);
|
||||
if let Some(boot_proc) = info.boot_processor {
|
||||
log_info!("Boot Processor: UID {}", boot_proc);
|
||||
}
|
||||
log_info!(
|
||||
"APIC: {}",
|
||||
if info.has_apic {
|
||||
"Available "
|
||||
} else {
|
||||
"Not available"
|
||||
}
|
||||
);
|
||||
log_info!(
|
||||
"HPET: {}",
|
||||
if info.has_hpet {
|
||||
"Available "
|
||||
} else {
|
||||
"Not available"
|
||||
}
|
||||
);
|
||||
}
|
||||
215
kernel/src/hal/acpi/mod.rs
Normal file
215
kernel/src/hal/acpi/mod.rs
Normal file
@ -0,0 +1,215 @@
|
||||
pub mod init;
|
||||
pub mod power;
|
||||
|
||||
use crate::hal::{cpu, io};
|
||||
use acpi::{aml, Handle, Handler, PciAddress, PhysicalMapping};
|
||||
use core::ptr::NonNull;
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
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)
|
||||
}
|
||||
|
||||
/// Information required for ACPI shutdown
|
||||
pub struct AcpiPowerInfo {
|
||||
pub pm1a_control_block: u32,
|
||||
pub pm1b_control_block: u32,
|
||||
pub slp_typa: u16,
|
||||
pub slp_typb: u16,
|
||||
pub slp_en: u16,
|
||||
}
|
||||
|
||||
/// ACPI 重置寄存器信息
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct ResetRegister {
|
||||
pub address_space: u8, // 0=SystemMemory, 1=SystemIO, 2=PciConfig
|
||||
pub address: u64,
|
||||
pub value: u8,
|
||||
}
|
||||
|
||||
static mut ACPI_RESET_REG: Option<ResetRegister> = None;
|
||||
|
||||
static mut ACPI_POWER_INFO: Option<AcpiPowerInfo> = None;
|
||||
|
||||
impl CureAcpiHandler {
|
||||
pub const fn new(physical_memory_offset: u64) -> Self {
|
||||
Self {
|
||||
physical_memory_offset,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Handler for CureAcpiHandler {
|
||||
unsafe fn map_physical_region<T>(
|
||||
&self,
|
||||
physical_address: usize,
|
||||
size: usize,
|
||||
) -> PhysicalMapping<Self, T> {
|
||||
// Bootloader has mapped all physical memory
|
||||
let virtual_address = physical_address as u64 + self.physical_memory_offset;
|
||||
let virtual_start = NonNull::new((virtual_address) as *mut T).unwrap();
|
||||
|
||||
PhysicalMapping {
|
||||
physical_start: physical_address,
|
||||
virtual_start,
|
||||
region_length: size,
|
||||
mapped_length: size,
|
||||
handler: *self,
|
||||
}
|
||||
}
|
||||
|
||||
fn unmap_physical_region<T>(region: &PhysicalMapping<Self, T>) {
|
||||
//
|
||||
}
|
||||
|
||||
fn read_u8(&self, address: usize) -> u8 {
|
||||
unsafe {
|
||||
let ptr = self.map_physical_region::<u8>(address, 1);
|
||||
core::ptr::read_volatile(ptr.virtual_start.as_ptr())
|
||||
}
|
||||
}
|
||||
|
||||
fn read_u16(&self, address: usize) -> u16 {
|
||||
unsafe {
|
||||
let ptr = self.map_physical_region::<u16>(address, 2);
|
||||
core::ptr::read_volatile(ptr.virtual_start.as_ptr())
|
||||
}
|
||||
}
|
||||
|
||||
fn read_u32(&self, address: usize) -> u32 {
|
||||
unsafe {
|
||||
let ptr = self.map_physical_region::<u32>(address, 4);
|
||||
core::ptr::read_volatile(ptr.virtual_start.as_ptr())
|
||||
}
|
||||
}
|
||||
|
||||
fn read_u64(&self, address: usize) -> u64 {
|
||||
unsafe {
|
||||
let ptr = self.map_physical_region::<u64>(address, 8);
|
||||
core::ptr::read_volatile(ptr.virtual_start.as_ptr())
|
||||
}
|
||||
}
|
||||
|
||||
fn write_u8(&self, address: usize, value: u8) {
|
||||
unsafe {
|
||||
let ptr = self.map_physical_region::<u8>(address, 1);
|
||||
core::ptr::write_volatile(ptr.virtual_start.as_ptr(), value);
|
||||
}
|
||||
}
|
||||
|
||||
fn write_u16(&self, address: usize, value: u16) {
|
||||
unsafe {
|
||||
let ptr = self.map_physical_region::<u16>(address, 2);
|
||||
core::ptr::write_volatile(ptr.virtual_start.as_ptr(), value);
|
||||
}
|
||||
}
|
||||
|
||||
fn write_u32(&self, address: usize, value: u32) {
|
||||
unsafe {
|
||||
let ptr = self.map_physical_region::<u32>(address, 4);
|
||||
core::ptr::write_volatile(ptr.virtual_start.as_ptr(), value);
|
||||
}
|
||||
}
|
||||
|
||||
fn write_u64(&self, address: usize, value: u64) {
|
||||
unsafe {
|
||||
let ptr = self.map_physical_region::<u64>(address, 8);
|
||||
core::ptr::write_volatile(ptr.virtual_start.as_ptr(), value);
|
||||
}
|
||||
}
|
||||
|
||||
fn read_io_u8(&self, port: u16) -> u8 {
|
||||
unsafe { io::io_port_rb(port) }
|
||||
}
|
||||
|
||||
fn read_io_u16(&self, port: u16) -> u16 {
|
||||
unsafe { io::io_port_rw(port) }
|
||||
}
|
||||
|
||||
fn read_io_u32(&self, port: u16) -> u32 {
|
||||
unsafe { io::io_port_rl(port) }
|
||||
}
|
||||
|
||||
fn write_io_u8(&self, port: u16, value: u8) {
|
||||
unsafe { io::io_port_wb(port, value) };
|
||||
}
|
||||
|
||||
fn write_io_u16(&self, port: u16, value: u16) {
|
||||
unsafe { io::io_port_ww(port, value) };
|
||||
}
|
||||
|
||||
fn write_io_u32(&self, port: u16, value: u32) {
|
||||
unsafe { io::io_port_wl(port, value) };
|
||||
}
|
||||
|
||||
fn read_pci_u8(&self, address: PciAddress, offset: u16) -> u8 {
|
||||
// TODO: 實作 PCI 配置空間讀取
|
||||
0xFF
|
||||
}
|
||||
|
||||
fn read_pci_u16(&self, address: PciAddress, offset: u16) -> u16 {
|
||||
// TODO: 實作 PCI 配置空間讀取
|
||||
0xFFFF
|
||||
}
|
||||
|
||||
fn read_pci_u32(&self, address: PciAddress, offset: u16) -> u32 {
|
||||
// TODO: 實作 PCI 配置空間讀取
|
||||
0xFFFFFFFF
|
||||
}
|
||||
|
||||
fn write_pci_u8(&self, address: PciAddress, offset: u16, value: u8) {
|
||||
// TODO: 實作 PCI 配置空間寫入
|
||||
}
|
||||
|
||||
fn write_pci_u16(&self, address: PciAddress, offset: u16, value: u16) {
|
||||
// TODO: 實作 PCI 配置空間寫入
|
||||
}
|
||||
|
||||
fn write_pci_u32(&self, address: PciAddress, offset: u16, value: u32) {
|
||||
// TODO: 實作 PCI 配置空間寫入
|
||||
}
|
||||
|
||||
fn nanos_since_boot(&self) -> u64 {
|
||||
// TODO: 實作高精度計時器 (需要 HPET 或 TSC)
|
||||
// 目前返回 0
|
||||
0
|
||||
}
|
||||
|
||||
fn stall(&self, _microseconds: u64) {
|
||||
// TODO: 實作微秒級延遲
|
||||
// 簡單的忙等待實作
|
||||
cpu::cpu_pause(_microseconds * 1000);
|
||||
}
|
||||
|
||||
fn sleep(&self, _milliseconds: u64) {
|
||||
// TODO: 實作毫秒級睡眠
|
||||
// 簡單的忙等待實作
|
||||
self.stall(_milliseconds * 1000);
|
||||
}
|
||||
|
||||
fn create_mutex(&self) -> Handle {
|
||||
// TODO: 實作 Mutex
|
||||
// 目前返回一個假的 handle
|
||||
Handle(0)
|
||||
}
|
||||
|
||||
fn acquire(&self, mutex: Handle, timeout: u16) -> Result<(), aml::AmlError> {
|
||||
// TODO: 實作 Mutex 獲取
|
||||
// 暫時直接返回成功
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn release(&self, _handle: Handle) {
|
||||
// TODO: 實作 Mutex 釋放
|
||||
}
|
||||
}
|
||||
539
kernel/src/hal/acpi/power.rs
Normal file
539
kernel/src/hal/acpi/power.rs
Normal file
@ -0,0 +1,539 @@
|
||||
use super::{AcpiPowerInfo, CureAcpiHandler, ResetRegister, ACPI_POWER_INFO, ACPI_RESET_REG};
|
||||
use crate::hal::{cpu, io};
|
||||
use crate::kprintln;
|
||||
use crate::mm::vma;
|
||||
use crate::{log_debug, log_error, log_fatal, log_info, log_trace, log_warn};
|
||||
use acpi::platform::{interrupt::InterruptModel, AcpiPlatform, PciConfigRegions};
|
||||
use acpi::sdt::{SdtHeader, Signature};
|
||||
use acpi::{aml, sdt, AcpiTables, Handle, Handler, PciAddress, PhysicalMapping};
|
||||
use core::mem;
|
||||
|
||||
/// Extract shutdown information from ACPI table
|
||||
pub fn extract_power_info(
|
||||
tables: &AcpiTables<CureAcpiHandler>,
|
||||
handler: &CureAcpiHandler,
|
||||
) -> Option<AcpiPowerInfo> {
|
||||
log_info!("Extracting ACPI power management info...");
|
||||
|
||||
let fadt = match tables.find_table::<sdt::fadt::Fadt>() {
|
||||
Some(fadt) => fadt,
|
||||
None => {
|
||||
log_error!("Failed to find FADT");
|
||||
return None;
|
||||
}
|
||||
};
|
||||
|
||||
unsafe {
|
||||
let fadt_ptr = (&*fadt as *const sdt::fadt::Fadt) as *const u8;
|
||||
|
||||
let pm1a_control_block = core::ptr::read_unaligned(fadt_ptr.add(64) as *const u32);
|
||||
let pm1b_control_block = core::ptr::read_unaligned(fadt_ptr.add(68) as *const u32);
|
||||
|
||||
let fadt_revision = core::ptr::read_unaligned(fadt_ptr.add(8) as *const u8);
|
||||
let dsdt_address = if fadt_revision >= 2 {
|
||||
let x_dsdt = core::ptr::read_unaligned(fadt_ptr.add(140) as *const u64);
|
||||
if x_dsdt != 0 {
|
||||
x_dsdt
|
||||
} else {
|
||||
core::ptr::read_unaligned(fadt_ptr.add(40) as *const u32) as u64
|
||||
}
|
||||
} else {
|
||||
core::ptr::read_unaligned(fadt_ptr.add(40) as *const u32) as u64
|
||||
};
|
||||
|
||||
log_info!("PM1a Control Block: {:#x}", pm1a_control_block);
|
||||
if pm1b_control_block != 0 {
|
||||
log_info!("PM1b Control Block: {:#x}", pm1b_control_block);
|
||||
}
|
||||
log_debug!("DSDT address: {:#x}", dsdt_address);
|
||||
|
||||
let (slp_typa, slp_typb) = match parse_s5_object(dsdt_address, handler) {
|
||||
Some(values) => values,
|
||||
None => {
|
||||
log_warn!("Could not parse _S5 object, using default values");
|
||||
(5, 5)
|
||||
}
|
||||
};
|
||||
|
||||
log_info!("SLP_TYPa: {:#x}", slp_typa);
|
||||
log_info!("SLP_TYPb: {:#x}", slp_typb);
|
||||
|
||||
Some(AcpiPowerInfo {
|
||||
pm1a_control_block,
|
||||
pm1b_control_block,
|
||||
slp_typa,
|
||||
slp_typb,
|
||||
slp_en: 1 << 13,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Parse the _S5 object in the DSDT
|
||||
///
|
||||
/// AML bytecode format for the _S5 object:
|
||||
/// ```
|
||||
/// Name(_S5, Package() {
|
||||
/// SLP_TYPa, // Type A for entering the S5 state
|
||||
/// SLP_TYPb, // Type B for entering the S5 state
|
||||
/// ...
|
||||
/// })
|
||||
/// ```
|
||||
fn parse_s5_object(dsdt_phys_addr: u64, handler: &CureAcpiHandler) -> Option<(u16, u16)> {
|
||||
log_debug!("Parsing DSDT at physical address {:#x}", dsdt_phys_addr);
|
||||
|
||||
// Map the DSDT header first to get the length
|
||||
let dsdt_header_mapping = unsafe {
|
||||
handler.map_physical_region::<SdtHeader>(dsdt_phys_addr as usize, size_of::<SdtHeader>())
|
||||
};
|
||||
|
||||
if dsdt_header_mapping.signature != Signature::DSDT {
|
||||
log_error!("Invalid DSDT signature");
|
||||
return None;
|
||||
}
|
||||
|
||||
let dsdt_length = dsdt_header_mapping.length as usize;
|
||||
log_debug!("DSDT length: {} bytes", dsdt_length);
|
||||
|
||||
// Release the header mapping
|
||||
drop(dsdt_header_mapping);
|
||||
|
||||
// 映射完整的 DSDT
|
||||
let dsdt_mapping =
|
||||
unsafe { handler.map_physical_region::<u8>(dsdt_phys_addr as usize, dsdt_length) };
|
||||
|
||||
unsafe {
|
||||
let dsdt_ptr = dsdt_mapping.virtual_start.as_ptr();
|
||||
let dsdt_data = core::slice::from_raw_parts(dsdt_ptr, dsdt_length);
|
||||
|
||||
// Search DSDT for "_S5_"
|
||||
let s5_name = b"_S5_";
|
||||
|
||||
for i in 0..(dsdt_data.len() - 4) {
|
||||
if &dsdt_data[i..i + 4] == s5_name {
|
||||
log_debug!("Found _S5 at offset {:#x}", i);
|
||||
|
||||
let debug_range = i..core::cmp::min(i + 32, dsdt_data.len());
|
||||
log_debug!("_S5 region bytes: {:02x?}", &dsdt_data[debug_range]);
|
||||
|
||||
let mut offset = i + 4; // Skip "_S5_"
|
||||
|
||||
// Skip any intermediate bytes and go straight to PackageOp
|
||||
let search_limit = offset + 16;
|
||||
while offset < search_limit && offset < dsdt_data.len() {
|
||||
if dsdt_data[offset] == 0x12 {
|
||||
log_debug!("Found PackageOp at offset {:#x}", offset);
|
||||
break;
|
||||
}
|
||||
offset += 1;
|
||||
}
|
||||
|
||||
if offset >= dsdt_data.len() || dsdt_data[offset] != 0x12 {
|
||||
log_warn!("PackageOp not found after _S5");
|
||||
continue;
|
||||
}
|
||||
|
||||
offset += 1; // Skip PackageOp (0x12)
|
||||
|
||||
log_debug!("Found _S5 at offset {:#x}", i);
|
||||
log_debug!("Found _S5 at offset {:#x}", i);
|
||||
|
||||
// Parse PkgLength
|
||||
let pkg_length = parse_pkg_length(&dsdt_data[offset..]);
|
||||
let pkg_length_size = get_pkg_length_size(&dsdt_data[offset..]);
|
||||
log_debug!("PkgLength: {}, size: {}", pkg_length, pkg_length_size);
|
||||
offset += pkg_length_size;
|
||||
|
||||
// NumElements
|
||||
if offset >= dsdt_data.len() {
|
||||
log_warn!("Unexpected end of data");
|
||||
continue;
|
||||
}
|
||||
let num_elements = dsdt_data[offset];
|
||||
offset += 1;
|
||||
|
||||
log_debug!("Package elements: {}", num_elements);
|
||||
|
||||
if num_elements < 2 {
|
||||
log_warn!("_S5 package has less than 2 elements");
|
||||
continue;
|
||||
}
|
||||
|
||||
log_debug!(
|
||||
"Current offset: {:#x}, next bytes: {:02x?}",
|
||||
offset,
|
||||
&dsdt_data[offset..core::cmp::min(offset + 8, dsdt_data.len())]
|
||||
);
|
||||
|
||||
// Extract SLP_TYPa
|
||||
log_debug!(
|
||||
"Reading SLP_TYPa at offset {:#x}, byte: {:#x}",
|
||||
offset,
|
||||
dsdt_data[offset]
|
||||
);
|
||||
let slp_typa = parse_aml_integer(&dsdt_data[offset..]).unwrap_or(0);
|
||||
log_debug!("SLP_TYPa parsed: {:#x}", slp_typa);
|
||||
let typa_size = get_aml_integer_size(&dsdt_data[offset..]);
|
||||
offset += typa_size;
|
||||
|
||||
// Extract SLP_TYPb
|
||||
log_debug!(
|
||||
"Reading SLP_TYPb at offset {:#x}, byte: {:#x}",
|
||||
offset,
|
||||
dsdt_data[offset]
|
||||
);
|
||||
let slp_typb = parse_aml_integer(&dsdt_data[offset..]).unwrap_or(0);
|
||||
log_debug!("SLP_TYPb parsed: {:#x}", slp_typb);
|
||||
|
||||
log_info!(
|
||||
"Parsed _S5: SLP_TYPa={:#x}, SLP_TYPb={:#x}",
|
||||
slp_typa,
|
||||
slp_typb
|
||||
);
|
||||
|
||||
return Some((slp_typa as u16, slp_typb as u16));
|
||||
}
|
||||
}
|
||||
log_error!("_S5 object not found in DSDT");
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Parse AML packet length
|
||||
fn parse_pkg_length(data: &[u8]) -> usize {
|
||||
if data.is_empty() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
let lead_byte = data[0];
|
||||
let byte_count = (lead_byte >> 6) as usize;
|
||||
|
||||
match byte_count {
|
||||
0 => (lead_byte & 0x3F) as usize,
|
||||
1 => {
|
||||
if data.len() < 2 {
|
||||
return 0;
|
||||
}
|
||||
((lead_byte & 0x0F) as usize) | ((data[1] as usize) << 4)
|
||||
}
|
||||
2 => {
|
||||
if data.len() < 3 {
|
||||
return 0;
|
||||
}
|
||||
((lead_byte & 0x0F) as usize) | ((data[1] as usize) << 4) | ((data[2] as usize) << 12)
|
||||
}
|
||||
3 => {
|
||||
if data.len() < 4 {
|
||||
return 0;
|
||||
}
|
||||
((lead_byte & 0x0F) as usize)
|
||||
| ((data[1] as usize) << 4)
|
||||
| ((data[2] as usize) << 12)
|
||||
| ((data[3] as usize) << 20)
|
||||
}
|
||||
_ => 0,
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the number of bytes encoded by the packet length
|
||||
fn get_pkg_length_size(data: &[u8]) -> usize {
|
||||
if data.is_empty() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
let lead_byte = data[0];
|
||||
let byte_count = (lead_byte >> 6) as usize;
|
||||
1 + byte_count
|
||||
}
|
||||
|
||||
/// Parse AML integer
|
||||
fn parse_aml_integer(data: &[u8]) -> Option<u64> {
|
||||
if data.is_empty() {
|
||||
return None;
|
||||
}
|
||||
|
||||
match data[0] {
|
||||
0x00 => Some(0), // ZeroOp
|
||||
0x01 => Some(1), // OneOp
|
||||
0xFF => Some(0xFFFFFFFF), // OnesOp
|
||||
0x0A => {
|
||||
// BytePrefix
|
||||
if data.len() < 2 {
|
||||
return None;
|
||||
}
|
||||
Some(data[1] as u64)
|
||||
}
|
||||
0x0B => {
|
||||
// WordPrefix
|
||||
if data.len() < 3 {
|
||||
return None;
|
||||
}
|
||||
Some(u16::from_le_bytes([data[1], data[2]]) as u64)
|
||||
}
|
||||
0x0C => {
|
||||
// DWordPrefix
|
||||
if data.len() < 5 {
|
||||
return None;
|
||||
}
|
||||
Some(u32::from_le_bytes([data[1], data[2], data[3], data[4]]) as u64)
|
||||
}
|
||||
0x0E => {
|
||||
// QWordPrefix
|
||||
if data.len() < 9 {
|
||||
return None;
|
||||
}
|
||||
Some(u64::from_le_bytes([
|
||||
data[1], data[2], data[3], data[4], data[5], data[6], data[7], data[8],
|
||||
]))
|
||||
}
|
||||
_ => {
|
||||
log_warn!("Unknown AML integer prefix: {:#02x}", data[0]);
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the byte size of the AML integer
|
||||
fn get_aml_integer_size(data: &[u8]) -> usize {
|
||||
if data.is_empty() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
match data[0] {
|
||||
0x00 | 0x01 | 0xFF => 1, // Zero, One, Ones
|
||||
0x0A => 2, // Byte
|
||||
0x0B => 3, // Word
|
||||
0x0C => 5, // DWord
|
||||
0x0E => 9, // QWord
|
||||
_ => 1,
|
||||
}
|
||||
}
|
||||
|
||||
/// Perform ACPI shutdown
|
||||
pub fn acpi_shutdown() -> bool {
|
||||
log_info!("Attempting ACPI shutdown...");
|
||||
|
||||
let power_info = unsafe {
|
||||
match &ACPI_POWER_INFO {
|
||||
Some(info) => info,
|
||||
None => {
|
||||
log_error!("ACPI power info not initialized!");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
unsafe {
|
||||
let slp_cmd_a = (power_info.slp_typa << 10) | power_info.slp_en;
|
||||
|
||||
log_info!(
|
||||
"Writing {:#x} to PM1a_CNT ({:#x})",
|
||||
slp_cmd_a,
|
||||
power_info.pm1a_control_block
|
||||
);
|
||||
|
||||
// Write to PM1a control register
|
||||
io::io_port_ww(power_info.pm1a_control_block as u16, slp_cmd_a);
|
||||
|
||||
// If PM1b exists, also write
|
||||
if power_info.pm1b_control_block != 0 {
|
||||
let slp_cmd_b = (power_info.slp_typb << 10) | power_info.slp_en;
|
||||
log_info!(
|
||||
"Writing {:#x} to PM1b_CNT ({:#x})",
|
||||
slp_cmd_b,
|
||||
power_info.pm1b_control_block
|
||||
);
|
||||
io::io_port_ww(power_info.pm1b_control_block as u16, slp_cmd_b);
|
||||
}
|
||||
|
||||
cpu::cpu_pause(10000);
|
||||
}
|
||||
|
||||
log_error!("ACPI shutdown failed!");
|
||||
false
|
||||
}
|
||||
|
||||
/// Store ACPI shutdown information
|
||||
pub fn store_power_info(info: AcpiPowerInfo) {
|
||||
unsafe {
|
||||
ACPI_POWER_INFO = Some(info);
|
||||
}
|
||||
log_info!("ACPI power info stored successfully");
|
||||
}
|
||||
|
||||
/// Extract reset register information from FADT
|
||||
pub fn extract_reset_reg(tables: &AcpiTables<CureAcpiHandler>) -> Option<ResetRegister> {
|
||||
log_info!("Extracting ACPI reset register info...");
|
||||
|
||||
let fadt = match tables.find_table::<sdt::fadt::Fadt>() {
|
||||
Some(fadt) => fadt,
|
||||
None => {
|
||||
log_error!("Failed to find FADT");
|
||||
return None;
|
||||
}
|
||||
};
|
||||
|
||||
unsafe {
|
||||
let fadt_ptr = (&*fadt as *const sdt::fadt::Fadt) as *const u8;
|
||||
|
||||
let fadt_revision = core::ptr::read_unaligned(fadt_ptr.add(8) as *const u8);
|
||||
|
||||
if fadt_revision < 2 {
|
||||
log_warn!("FADT revision {} does not support RESET_REG", fadt_revision);
|
||||
return None;
|
||||
}
|
||||
|
||||
// 讀取 Flags (offset 112 in FADT)
|
||||
let flags = core::ptr::read_unaligned(fadt_ptr.add(112) as *const u32);
|
||||
let reset_reg_supported = (flags & (1 << 10)) != 0;
|
||||
|
||||
if !reset_reg_supported {
|
||||
log_warn!("RESET_REG not supported (FADT flags bit 10 not set)");
|
||||
return None;
|
||||
}
|
||||
|
||||
// RESET_REG is at offset 116 in the FADT
|
||||
// Generic Address Structure format:
|
||||
// +0: Address Space ID (1 byte)
|
||||
// +1: Register Bit Width (1 byte)
|
||||
// +2: Register Bit Offset (1 byte)
|
||||
// +3: Access Size (1 byte)
|
||||
// +4: Address (8 bytes)
|
||||
|
||||
let reset_reg_offset = 116;
|
||||
|
||||
let address_space = core::ptr::read_unaligned(fadt_ptr.add(reset_reg_offset) as *const u8);
|
||||
let bit_width = core::ptr::read_unaligned(fadt_ptr.add(reset_reg_offset + 1) as *const u8);
|
||||
let bit_offset = core::ptr::read_unaligned(fadt_ptr.add(reset_reg_offset + 2) as *const u8);
|
||||
let address = core::ptr::read_unaligned(fadt_ptr.add(reset_reg_offset + 4) as *const u64);
|
||||
|
||||
// RESET_VALUE is after RESET_REG (offset 128)
|
||||
let reset_value = core::ptr::read_unaligned(fadt_ptr.add(128) as *const u8);
|
||||
|
||||
if bit_width != 8 || bit_offset != 0 {
|
||||
log_warn!(
|
||||
"Invalid RESET_REG configuration: width={}, offset={}",
|
||||
bit_width,
|
||||
bit_offset
|
||||
);
|
||||
return None;
|
||||
}
|
||||
|
||||
if address_space > 2 {
|
||||
log_warn!("Invalid address space ID: {}", address_space);
|
||||
return None;
|
||||
}
|
||||
|
||||
log_info!("RESET_REG found:");
|
||||
log_info!(
|
||||
" Address Space: {} ({})",
|
||||
address_space,
|
||||
match address_space {
|
||||
0 => "System Memory",
|
||||
1 => "System I/O",
|
||||
2 => "PCI Config",
|
||||
_ => "Unknown",
|
||||
}
|
||||
);
|
||||
log_info!("Address: {:#x}", address);
|
||||
log_info!("Reset Value: {:#x}", reset_value);
|
||||
|
||||
Some(ResetRegister {
|
||||
address_space,
|
||||
address,
|
||||
value: reset_value,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub fn store_reset_reg(reset_reg: ResetRegister) {
|
||||
unsafe {
|
||||
ACPI_RESET_REG = Some(reset_reg);
|
||||
}
|
||||
log_info!("ACPI reset register info stored");
|
||||
}
|
||||
|
||||
/// Restart using ACPI RESET_REG
|
||||
pub fn acpi_reset_reg_reboot() -> bool {
|
||||
unsafe {
|
||||
let reset_reg = match &ACPI_RESET_REG {
|
||||
Some(reg) => reg,
|
||||
None => {
|
||||
log_debug!("ACPI RESET_REG not available");
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
log_info!("Using ACPI RESET_REG for reboot");
|
||||
log_info!(
|
||||
" Space: {}, Address: {:#x}, Value: {:#x}",
|
||||
reset_reg.address_space,
|
||||
reset_reg.address,
|
||||
reset_reg.value
|
||||
);
|
||||
|
||||
match reset_reg.address_space {
|
||||
// System I/O
|
||||
1 => {
|
||||
log_debug!(
|
||||
"Writing {:#x} to I/O port {:#x}",
|
||||
reset_reg.value,
|
||||
reset_reg.address
|
||||
);
|
||||
io::io_port_wb(reset_reg.address as u16, reset_reg.value);
|
||||
true
|
||||
}
|
||||
|
||||
// System Memory
|
||||
0 => {
|
||||
log_debug!(
|
||||
"Writing {:#x} to memory address {:#x}",
|
||||
reset_reg.value,
|
||||
reset_reg.address
|
||||
);
|
||||
|
||||
let virt_addr = vma::phys_to_virt(reset_reg.address);
|
||||
let ptr = virt_addr.as_mut_ptr::<u8>();
|
||||
core::ptr::write_volatile(ptr, reset_reg.value);
|
||||
true
|
||||
}
|
||||
|
||||
// PCI Config Space
|
||||
2 => {
|
||||
log_debug!(
|
||||
"Writing {:#x} to PCI config space {:#x}",
|
||||
reset_reg.value,
|
||||
reset_reg.address
|
||||
);
|
||||
|
||||
// PCI address encoding (ACPI format):
|
||||
// Bits 63-32: Reserved
|
||||
// Bits 31-16: Bus Number
|
||||
// Bits 15-11: Device Number
|
||||
// Bits 10-8: Function Number
|
||||
// Bits 7-0: Register Offset
|
||||
|
||||
let bus = ((reset_reg.address >> 16) & 0xFFFF) as u8;
|
||||
let device = ((reset_reg.address >> 11) & 0x1F) as u8;
|
||||
let function = ((reset_reg.address >> 8) & 0x7) as u8;
|
||||
let offset = (reset_reg.address & 0xFF) as u8;
|
||||
|
||||
log_debug!(
|
||||
"PCI Bus={}, Dev={}, Func={}, Offset={:#x}",
|
||||
bus,
|
||||
device,
|
||||
function,
|
||||
offset
|
||||
);
|
||||
|
||||
// TODO: 調用你的 PCI 配置空間寫入函數
|
||||
// pci_config_write_byte(bus, device, function, offset, reset_reg.value);
|
||||
false
|
||||
}
|
||||
|
||||
_ => {
|
||||
log_error!("Unknown address space: {}", reset_reg.address_space);
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
210
kernel/src/hal/apic/ioapic.rs
Normal file
210
kernel/src/hal/apic/ioapic.rs
Normal file
@ -0,0 +1,210 @@
|
||||
// kernel/src/hal/ioapic.rs
|
||||
|
||||
use super::{redir_flags, reg, IoApicInfo, IO_APICS, IO_APIC_COUNT, MAX_IOAPICS};
|
||||
use crate::{log_debug, log_error, log_info, log_trace, log_warn};
|
||||
use x86_64::VirtAddr;
|
||||
|
||||
/// Initialize a single IO APIC (using mapped virtual addresses)
|
||||
///
|
||||
/// # Safety
|
||||
/// Must be called after mapping the IO APIC memory.
|
||||
pub unsafe fn init_single_ioapic(base_vaddr: VirtAddr, id: u8, gsi_base: u32) {
|
||||
if IO_APIC_COUNT >= MAX_IOAPICS {
|
||||
log_error!("Too many IO APICs! Maximum {} supported", MAX_IOAPICS);
|
||||
return;
|
||||
}
|
||||
|
||||
log_debug!("Initializing IO APIC {} at {:#x}", id, base_vaddr.as_u64());
|
||||
|
||||
// Create a temporary structure to read the version information
|
||||
let temp_info = IoApicInfo {
|
||||
base_vaddr,
|
||||
id,
|
||||
gsi_base,
|
||||
max_redirection_entries: 0,
|
||||
};
|
||||
|
||||
// Read version information to get the maximum number of entries
|
||||
let version = temp_info.read(reg::VER);
|
||||
let max_entries = ((version >> 16) & 0xFF) as u8 + 1;
|
||||
|
||||
// 創建完整的信息結構
|
||||
let info = IoApicInfo {
|
||||
base_vaddr,
|
||||
id,
|
||||
gsi_base,
|
||||
max_redirection_entries: max_entries,
|
||||
};
|
||||
|
||||
// Mask all interrupts
|
||||
for irq in 0..max_entries {
|
||||
info.write_redirection_entry(irq, redir_flags::MASKED);
|
||||
}
|
||||
|
||||
// Store to global array
|
||||
IO_APICS[IO_APIC_COUNT] = Some(info);
|
||||
IO_APIC_COUNT += 1;
|
||||
|
||||
log_info!("IO APIC {} initialized, {} entries", id, max_entries);
|
||||
print_single_ioapic_info(&info);
|
||||
}
|
||||
|
||||
/// Initialize all IO APICs (from physical addresses)
|
||||
///
|
||||
/// # Safety
|
||||
/// The physical addresses must be correctly mapped.
|
||||
pub unsafe fn init_io_apics(io_apics: &[(x86_64::PhysAddr, u8, u32)]) {
|
||||
log_info!("Initializing {} IO APIC(s)...", io_apics.len());
|
||||
|
||||
for (paddr, id, gsi_base) in io_apics {
|
||||
let vaddr = crate::mm::vma::phys_to_virt(paddr.as_u64());
|
||||
init_single_ioapic(vaddr, *id, *gsi_base);
|
||||
}
|
||||
|
||||
log_info!("All IO APICs initialized");
|
||||
}
|
||||
|
||||
/// 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 fn set_irq_redirect(
|
||||
irq: u8,
|
||||
vector: u8,
|
||||
dest_apic_id: u8,
|
||||
level_triggered: bool,
|
||||
active_low: bool,
|
||||
) {
|
||||
unsafe {
|
||||
// Find the IO APIC responsible for this IRQ
|
||||
for i in 0..IO_APIC_COUNT {
|
||||
if let Some(ioapic) = &IO_APICS[i] {
|
||||
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;
|
||||
|
||||
// Build redirection entry
|
||||
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;
|
||||
}
|
||||
|
||||
// Set polarity
|
||||
if active_low {
|
||||
entry |= redir_flags::POLARITY_LOW;
|
||||
}
|
||||
|
||||
// Set the transfer mode to Fixed
|
||||
entry |= redir_flags::DELIVERY_FIXED;
|
||||
|
||||
// Set the target mode to Physical
|
||||
entry |= redir_flags::DEST_PHYSICAL;
|
||||
|
||||
// Set the vector number
|
||||
entry |= vector as u64;
|
||||
|
||||
// Write to the redirection table (unmask)
|
||||
ioapic.write_redirection_entry(local_irq, entry);
|
||||
|
||||
log_info!(
|
||||
"IO APIC {} IRQ {} -> Vector {} (APIC {}, {}, {})",
|
||||
ioapic.id,
|
||||
irq,
|
||||
vector,
|
||||
dest_apic_id,
|
||||
if level_triggered { "level" } else { "edge" },
|
||||
if active_low { "low" } else { "high" }
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
log_warn!("No IO APIC found for IRQ {}", irq);
|
||||
}
|
||||
}
|
||||
|
||||
/// Mask IRQ
|
||||
pub fn mask_irq(irq: u8) {
|
||||
unsafe {
|
||||
for i in 0..IO_APIC_COUNT {
|
||||
if let Some(ioapic) = &IO_APICS[i] {
|
||||
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;
|
||||
let mut entry = ioapic.read_redirection_entry(local_irq);
|
||||
entry |= redir_flags::MASKED;
|
||||
ioapic.write_redirection_entry(local_irq, entry);
|
||||
|
||||
log_debug!("IO APIC {} IRQ {} masked", ioapic.id, irq);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Unmask IRQ
|
||||
pub fn unmask_irq(irq: u8) {
|
||||
unsafe {
|
||||
for i in 0..IO_APIC_COUNT {
|
||||
if let Some(ioapic) = &IO_APICS[i] {
|
||||
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;
|
||||
let mut entry = ioapic.read_redirection_entry(local_irq);
|
||||
entry &= !redir_flags::MASKED;
|
||||
ioapic.write_redirection_entry(local_irq, entry);
|
||||
|
||||
log_debug!("IO APIC {} IRQ {} unmasked", ioapic.id, irq);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Print single IO APIC information
|
||||
fn print_single_ioapic_info(info: &IoApicInfo) {
|
||||
unsafe {
|
||||
let id = info.read(reg::ID) >> 24;
|
||||
let version = info.read(reg::VER);
|
||||
let apic_ver = version & 0xFF;
|
||||
|
||||
log_info!("ID: {}", id);
|
||||
log_info!("Version: {:#x}", apic_ver);
|
||||
log_info!("Max Entries: {}", info.max_redirection_entries);
|
||||
log_info!("GSI Base: {}", info.gsi_base);
|
||||
}
|
||||
}
|
||||
|
||||
/// Print all IO APIC information
|
||||
pub fn print_info() {
|
||||
unsafe {
|
||||
log_info!("=== IO APIC Information ===");
|
||||
log_info!("Total IO APICs: {}", IO_APIC_COUNT);
|
||||
|
||||
for i in 0..IO_APIC_COUNT {
|
||||
if let Some(info) = &IO_APICS[i] {
|
||||
log_info!("IO APIC {}:", i);
|
||||
print_single_ioapic_info(info);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
196
kernel/src/hal/apic/lapic.rs
Normal file
196
kernel/src/hal/apic/lapic.rs
Normal file
@ -0,0 +1,196 @@
|
||||
// kernel/src/hal/lapic.rs
|
||||
|
||||
use super::{flags, ApicInfo, ApicRegister, APIC_INFO, LOCAL_APIC_BASE};
|
||||
use crate::hal::io;
|
||||
use crate::mm::vma;
|
||||
use crate::{log_debug, log_error, log_info, log_trace, log_warn};
|
||||
use x86_64::VirtAddr;
|
||||
|
||||
/// Read APIC registers
|
||||
///
|
||||
/// # Safety
|
||||
/// The caller must ensure the APIC is initialized.
|
||||
#[inline]
|
||||
pub unsafe fn read_apic_reg(reg: ApicRegister) -> Option<u32> {
|
||||
LOCAL_APIC_BASE.map(|base| {
|
||||
let addr = base.as_u64() + reg as u64;
|
||||
core::ptr::read_volatile(addr as *const u32)
|
||||
})
|
||||
}
|
||||
|
||||
/// Write to APIC registers
|
||||
///
|
||||
/// # Safety
|
||||
/// The caller must ensure the APIC is initialized.
|
||||
#[inline]
|
||||
pub unsafe fn write_apic_reg(reg: ApicRegister, value: u32) -> bool {
|
||||
if let Some(base) = LOCAL_APIC_BASE {
|
||||
let addr = base.as_u64() + reg as u64;
|
||||
core::ptr::write_volatile(addr as *mut u32, value);
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
/// Directly read the APIC register using an offset (for apic_timer)
|
||||
///
|
||||
/// # Safety
|
||||
/// The caller must ensure the APIC is initialized and the offset is valid.
|
||||
#[inline]
|
||||
pub unsafe fn read_apic_reg_raw(offset: u32) -> Option<u32> {
|
||||
LOCAL_APIC_BASE.map(|base| {
|
||||
let addr = base.as_u64() + offset as u64;
|
||||
core::ptr::read_volatile(addr as *const u32)
|
||||
})
|
||||
}
|
||||
|
||||
/// Directly read the APIC register using an offset (for apic_timer)
|
||||
///
|
||||
/// # Safety
|
||||
/// The caller must ensure the APIC is initialized and the offset is valid.
|
||||
#[inline]
|
||||
pub unsafe fn write_apic_reg_raw(offset: u32, value: u32) -> bool {
|
||||
if let Some(base) = LOCAL_APIC_BASE {
|
||||
let addr = base.as_u64() + offset as u64;
|
||||
core::ptr::write_volatile(addr as *mut u32, value);
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
/// Initialize the Local APIC (using mapped virtual addresses)
|
||||
///
|
||||
/// # Safety
|
||||
/// Must be called after paging is initialized and APIC memory is mapped.
|
||||
pub unsafe fn init_local_apic_with_vaddr(base_vaddr: VirtAddr) {
|
||||
log_debug!("Initializing Local APIC at {:#x}", base_vaddr.as_u64());
|
||||
|
||||
// Storage base address
|
||||
LOCAL_APIC_BASE = Some(base_vaddr);
|
||||
|
||||
// Read APIC information
|
||||
let id_reg = read_apic_reg(ApicRegister::Id).unwrap();
|
||||
let version_reg = read_apic_reg(ApicRegister::Version).unwrap();
|
||||
|
||||
APIC_INFO = ApicInfo {
|
||||
id: id_reg >> 24,
|
||||
version: version_reg & 0xFF,
|
||||
max_lvt: (version_reg >> 16) & 0xFF,
|
||||
};
|
||||
|
||||
// Enable APIC (via Spurious Interrupt Vector Register)
|
||||
let spurious = flags::APIC_SW_ENABLE | 0xFF;
|
||||
write_apic_reg(ApicRegister::SpuriousInterruptVector, spurious);
|
||||
|
||||
// Set task priority to 0 (accept all interrupts)
|
||||
write_apic_reg(ApicRegister::TaskPriority, 0);
|
||||
|
||||
// Configure LVT entries - default to full mask
|
||||
write_apic_reg(ApicRegister::LvtTimer, flags::LVT_MASKED);
|
||||
write_apic_reg(ApicRegister::LvtLint0, flags::LVT_MASKED);
|
||||
write_apic_reg(ApicRegister::LvtLint1, flags::LVT_MASKED);
|
||||
write_apic_reg(ApicRegister::LvtError, flags::LVT_MASKED);
|
||||
write_apic_reg(ApicRegister::LvtPerformanceCounter, flags::LVT_MASKED);
|
||||
write_apic_reg(ApicRegister::LvtThermalSensor, flags::LVT_MASKED);
|
||||
|
||||
log_info!("Local APIC initialized");
|
||||
print_info();
|
||||
}
|
||||
|
||||
/// Initialize Local APIC (from physical address, needs to be mapped first)
|
||||
///
|
||||
/// # Safety
|
||||
/// The physical address must be a valid APIC base address.
|
||||
pub unsafe fn init_local_apic(base_paddr: x86_64::PhysAddr) {
|
||||
let base_vaddr = vma::phys_to_virt(base_paddr.as_u64());
|
||||
init_local_apic_with_vaddr(base_vaddr);
|
||||
}
|
||||
|
||||
/// Send EOI (End of Interrupt)
|
||||
///
|
||||
/// This is the most frequently called function and must be extremely fast.
|
||||
#[inline]
|
||||
pub fn send_eoi() {
|
||||
unsafe {
|
||||
if let Some(base) = LOCAL_APIC_BASE {
|
||||
let addr = base.as_u64() + ApicRegister::Eoi as u64;
|
||||
core::ptr::write_volatile(addr as *mut u32, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the Local APIC ID
|
||||
#[inline]
|
||||
pub fn get_apic_id() -> Option<u32> {
|
||||
unsafe { Some(APIC_INFO.id) }
|
||||
}
|
||||
|
||||
/// Get the Local APIC base virtual address
|
||||
#[inline]
|
||||
pub fn get_base_vaddr() -> Option<VirtAddr> {
|
||||
unsafe { LOCAL_APIC_BASE }
|
||||
}
|
||||
|
||||
/// Get APIC version
|
||||
#[inline]
|
||||
pub fn get_version() -> Option<u32> {
|
||||
unsafe {
|
||||
if LOCAL_APIC_BASE.is_some() {
|
||||
Some(APIC_INFO.version)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the maximum number of LVT entries
|
||||
#[inline]
|
||||
pub fn get_max_lvt() -> Option<u32> {
|
||||
unsafe {
|
||||
if LOCAL_APIC_BASE.is_some() {
|
||||
Some(APIC_INFO.max_lvt)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Disable legacy 8259 PIC
|
||||
///
|
||||
/// Must be called before using the APIC to avoid conflicts
|
||||
pub fn disable_legacy_pic() {
|
||||
unsafe {
|
||||
// Master PIC
|
||||
io::io_port_wb(0x20, 0x11); // ICW1: initialization
|
||||
io::io_port_wb(0x21, 0x20); // ICW2: Interrupt vector offset (32-39)
|
||||
io::io_port_wb(0x21, 0x04); // ICW3: Tell the Master PIC Slave to be on IRQ2
|
||||
io::io_port_wb(0x21, 0x01); // ICW4: 8086 mode
|
||||
|
||||
// Slave PIC
|
||||
io::io_port_wb(0xA0, 0x11); // ICW1: initialization
|
||||
io::io_port_wb(0xA1, 0x28); // ICW2: Interrupt vector offset (40-47)
|
||||
io::io_port_wb(0xA1, 0x02); // ICW3: Tell the Slave PIC to connect to Master IRQ2
|
||||
io::io_port_wb(0xA1, 0x01); // ICW4: 8086 mode
|
||||
|
||||
// Mask all IRQs (disable PIC)
|
||||
io::io_port_wb(0x21, 0xFF);
|
||||
io::io_port_wb(0xA1, 0xFF);
|
||||
}
|
||||
|
||||
log_info!("Legacy 8259 PIC disabled");
|
||||
}
|
||||
|
||||
/// Print APIC information
|
||||
pub fn print_info() {
|
||||
unsafe {
|
||||
if LOCAL_APIC_BASE.is_some() {
|
||||
log_info!("Local APIC ID: {}", APIC_INFO.id);
|
||||
log_info!("Local APIC Version: {:#x}", APIC_INFO.version);
|
||||
log_info!("Max LVT Entry: {}", APIC_INFO.max_lvt);
|
||||
} else {
|
||||
log_warn!("Local APIC not initialized");
|
||||
}
|
||||
}
|
||||
}
|
||||
163
kernel/src/hal/apic/mod.rs
Normal file
163
kernel/src/hal/apic/mod.rs
Normal file
@ -0,0 +1,163 @@
|
||||
use x86_64::VirtAddr;
|
||||
use crate::log_warn;
|
||||
|
||||
pub mod ioapic;
|
||||
pub mod lapic;
|
||||
|
||||
/// 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;
|
||||
}
|
||||
|
||||
// Supports up to 8 IO APICs
|
||||
const MAX_IOAPICS: usize = 8;
|
||||
|
||||
//IO APIC information array (read-only after initialization)
|
||||
static mut IO_APICS: [Option<IoApicInfo>; MAX_IOAPICS] = [None; MAX_IOAPICS];
|
||||
static mut IO_APIC_COUNT: usize = 0;
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
struct IoApicInfo {
|
||||
base_vaddr: VirtAddr,
|
||||
id: u8,
|
||||
gsi_base: u32,
|
||||
max_redirection_entries: u8,
|
||||
}
|
||||
|
||||
/// Local APIC register offset
|
||||
#[repr(u32)]
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
#[allow(dead_code)]
|
||||
pub 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)]
|
||||
pub 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 const LVT_TIMER_ONESHOT: u32 = 0 << 17;
|
||||
}
|
||||
|
||||
// Local APIC base address (read-only after initialization)
|
||||
static mut LOCAL_APIC_BASE: Option<VirtAddr> = None;
|
||||
|
||||
// APIC information (read-only after initialization)
|
||||
static mut APIC_INFO: ApicInfo = ApicInfo::new();
|
||||
|
||||
struct ApicInfo {
|
||||
id: u32,
|
||||
version: u32,
|
||||
max_lvt: u32,
|
||||
}
|
||||
|
||||
impl ApicInfo {
|
||||
const fn new() -> Self {
|
||||
Self {
|
||||
id: 0,
|
||||
version: 0,
|
||||
max_lvt: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl IoApicInfo {
|
||||
/// Read IO APIC register
|
||||
#[inline]
|
||||
unsafe fn read(&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)
|
||||
}
|
||||
|
||||
/// Write to IO APIC register
|
||||
#[inline]
|
||||
unsafe fn write(&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 the redirection table entry
|
||||
#[inline]
|
||||
unsafe fn read_redirection_entry(&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
|
||||
#[inline]
|
||||
unsafe fn write_redirection_entry(&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);
|
||||
}
|
||||
}
|
||||
@ -3,8 +3,7 @@ use x86_64::registers::control::{Cr0, Cr0Flags, Cr2, Cr3, Cr4, Cr4Flags};
|
||||
use x86_64::instructions::{interrupts, hlt};
|
||||
use core::arch::asm;
|
||||
use raw_cpuid::CpuId;
|
||||
use x86_64::{PhysAddr, structures::paging::PhysFrame};
|
||||
use x86_64::VirtAddr;
|
||||
use x86_64::{PhysAddr, structures::paging::PhysFrame, registers};
|
||||
|
||||
/// 64-bit register type
|
||||
#[allow(dead_code)]
|
||||
@ -197,15 +196,15 @@ pub fn cpu_get_brand(brand_out: &mut [u8]) -> &str {
|
||||
/// The timestamp count value
|
||||
#[allow(dead_code)]
|
||||
#[inline]
|
||||
pub fn cpu_rdtsc() -> u64 {
|
||||
pub fn cpu_rdtscp() -> u64 {
|
||||
unsafe {
|
||||
let low: u32;
|
||||
let high: u32;
|
||||
asm!(
|
||||
"rdtsc",
|
||||
"rdtscp",
|
||||
out("eax") low,
|
||||
out("edx") high,
|
||||
options(nomem, nostack, preserves_flags)
|
||||
options(nomem, nostack, preserves_flags, att_syntax)
|
||||
);
|
||||
((high as u64) << 32) | (low as u64)
|
||||
}
|
||||
@ -214,8 +213,19 @@ pub fn cpu_rdtsc() -> u64 {
|
||||
/// Execute CPU pause instruction (reduce power consumption)
|
||||
#[allow(dead_code)]
|
||||
#[inline]
|
||||
pub fn cpu_pause() {
|
||||
core::hint::spin_loop();
|
||||
pub fn cpu_pause(ms: u64) {
|
||||
if ms == 0 {
|
||||
core::hint::spin_loop();
|
||||
return;
|
||||
}
|
||||
|
||||
let start = cpu_rdtscp();
|
||||
let target = start + ms * 1000;
|
||||
|
||||
while cpu_rdtscp() < target {
|
||||
core::hint::spin_loop();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// Stop CPU execution until the next interrupt occurs
|
||||
@ -276,12 +286,12 @@ where
|
||||
#[allow(dead_code)]
|
||||
#[inline]
|
||||
pub fn cpu_breakpoint() {
|
||||
x86_64::instructions::interrupts::int3();
|
||||
interrupts::int3();
|
||||
}
|
||||
|
||||
/// Read the RFLAGS register
|
||||
#[allow(dead_code)]
|
||||
#[inline]
|
||||
pub fn cpu_read_flags() -> u64 {
|
||||
x86_64::registers::rflags::read().bits()
|
||||
registers::rflags::read().bits()
|
||||
}
|
||||
@ -1,4 +1,7 @@
|
||||
pub mod io;
|
||||
pub mod cpu;
|
||||
pub mod acpi;
|
||||
mod rtc;
|
||||
pub mod rtc;
|
||||
pub mod timer;
|
||||
pub mod power;
|
||||
pub mod apic;
|
||||
217
kernel/src/hal/power.rs
Normal file
217
kernel/src/hal/power.rs
Normal file
@ -0,0 +1,217 @@
|
||||
// kernel/src/hal/power.rs
|
||||
|
||||
use crate::hal::{io, cpu, rtc};
|
||||
use crate::{log_info, log_debug, log_warn, log_error};
|
||||
use super::acpi;
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum PowerState {
|
||||
S0, // Working
|
||||
S1, // Sleep
|
||||
S3, // Suspend to RAM
|
||||
S4, // Suspend to Disk
|
||||
S5, // Soft Off
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
pub enum ShutdownMethod {
|
||||
Acpi,
|
||||
QemuExit,
|
||||
BochsExit,
|
||||
VirtualBox,
|
||||
Apm,
|
||||
}
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
pub enum RebootMethod {
|
||||
BootACPI,
|
||||
BootEFI,
|
||||
BootKBD,
|
||||
BootCF9,
|
||||
Boot92h,
|
||||
}
|
||||
|
||||
pub fn shutdown() -> ! {
|
||||
log_info!("Initiating system shutdown...");
|
||||
|
||||
log_info!("Disabling CPU interrupts");
|
||||
cpu::cpu_disable_interrupts();
|
||||
|
||||
log_info!("Shutting down the RTC Timer");
|
||||
rtc::disable_timer();
|
||||
|
||||
let methods = [
|
||||
ShutdownMethod::Acpi,
|
||||
ShutdownMethod::QemuExit,
|
||||
ShutdownMethod::BochsExit,
|
||||
ShutdownMethod::VirtualBox,
|
||||
ShutdownMethod::Apm,
|
||||
];
|
||||
|
||||
for method in methods.iter() {
|
||||
log_debug!("Trying shutdown method: {:?}", method);
|
||||
try_shutdown(*method);
|
||||
|
||||
log_warn!("{:?} shutdown failed", method);
|
||||
cpu::cpu_pause(1000);
|
||||
}
|
||||
|
||||
log_error!("All shutdown methods failed!");
|
||||
log_error!("System halted. Please power off manually.");
|
||||
|
||||
loop {
|
||||
cpu::cpu_halt();
|
||||
}
|
||||
}
|
||||
|
||||
fn try_shutdown(method: ShutdownMethod) {
|
||||
unsafe {
|
||||
match method {
|
||||
ShutdownMethod::Acpi => {
|
||||
acpi::power::acpi_shutdown();
|
||||
}
|
||||
|
||||
ShutdownMethod::QemuExit => {
|
||||
// QEMU isa-debug-exit device
|
||||
io::io_port_ww(0x604, 0x2000);
|
||||
io::io_port_rl(0x501);
|
||||
}
|
||||
|
||||
ShutdownMethod::BochsExit => {
|
||||
// Bochs dedicated shutdown port
|
||||
io::io_port_ww(0xB004, 0x2000);
|
||||
}
|
||||
|
||||
ShutdownMethod::VirtualBox => {
|
||||
// VirtualBox shutdown port
|
||||
io::io_port_ww(0x4004, 0x3400);
|
||||
}
|
||||
|
||||
ShutdownMethod::Apm => {
|
||||
// APM (Advanced Power Management) BIOS
|
||||
// APM version 1.0
|
||||
io::io_port_wb(0x8900, 0x53);
|
||||
io::io_port_wb(0x8900, 0x00);
|
||||
io::io_port_wb(0x8900, 0x01);
|
||||
io::io_port_wb(0x8900, 0x53);
|
||||
}
|
||||
}
|
||||
|
||||
cpu::cpu_pause(10000);
|
||||
}
|
||||
}
|
||||
|
||||
/// Restart the system
|
||||
pub fn reboot() -> ! {
|
||||
log_info!("Rebooting system...");
|
||||
|
||||
log_info!("Disabling CPU interrupts");
|
||||
cpu::cpu_disable_interrupts();
|
||||
|
||||
log_info!("Shutting down the RTC Timer");
|
||||
rtc::disable_timer();
|
||||
|
||||
let methods = [
|
||||
RebootMethod::BootACPI,
|
||||
RebootMethod::BootEFI,
|
||||
RebootMethod::BootKBD,
|
||||
RebootMethod::BootCF9,
|
||||
RebootMethod::Boot92h,
|
||||
];
|
||||
|
||||
for method in methods.iter() {
|
||||
log_debug!("Trying reboot method: {:?}", method);
|
||||
try_reboot(*method);
|
||||
|
||||
log_warn!("{:?} reboot failed", method);
|
||||
cpu::cpu_pause(1000);
|
||||
}
|
||||
|
||||
log_error!("All reboot methods failed!");
|
||||
log_error!("System halted. Please power off manually.");
|
||||
|
||||
loop {
|
||||
cpu::cpu_halt();
|
||||
}
|
||||
}
|
||||
|
||||
fn try_reboot(method: RebootMethod) {
|
||||
match method {
|
||||
RebootMethod::BootACPI => {
|
||||
acpi::power::acpi_reset_reg_reboot();
|
||||
}
|
||||
|
||||
RebootMethod::BootKBD => {
|
||||
keyboard_controller_reboot();
|
||||
}
|
||||
|
||||
RebootMethod::BootCF9 => {
|
||||
pci_reboot();
|
||||
}
|
||||
RebootMethod::BootEFI => {
|
||||
efi_reboot();
|
||||
}
|
||||
|
||||
RebootMethod::Boot92h => {
|
||||
cpu_reset();
|
||||
}
|
||||
}
|
||||
|
||||
cpu::cpu_pause(10000);
|
||||
}
|
||||
|
||||
fn keyboard_controller_reboot() -> bool {
|
||||
log_debug!("keyboard controller reboot...");
|
||||
|
||||
unsafe {
|
||||
for _ in 0..1000 {
|
||||
if (io::io_port_rb(0x64) & 0x02) == 0 {
|
||||
break;
|
||||
}
|
||||
cpu::cpu_pause(10);
|
||||
}
|
||||
|
||||
io::io_port_wb(0x64, 0xFE);
|
||||
|
||||
cpu::cpu_pause(100000);
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
fn pci_reboot() -> bool {
|
||||
log_debug!("PCI reboot...");
|
||||
|
||||
unsafe {
|
||||
let mut val = io::io_port_rb(0xCF9) & !0x06;
|
||||
io::io_port_wb(0xCF9, val | 0x02);
|
||||
cpu::cpu_pause(1000);
|
||||
io::io_port_wb(0xCF9, val | 0x06);
|
||||
|
||||
cpu::cpu_pause(100000);
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
|
||||
fn efi_reboot() -> bool {
|
||||
log_debug!("Trying EFI runtime services reboot...");
|
||||
|
||||
// TODO: 實現 EFI ResetSystem 調用
|
||||
false
|
||||
}
|
||||
|
||||
fn cpu_reset() -> bool {
|
||||
log_debug!("CPU reset via port 92h...");
|
||||
|
||||
unsafe {
|
||||
let mut val = io::io_port_rb(0x92);
|
||||
val &= !0x01; // Clear Fast A20 Bit
|
||||
val |= 0x01; // Set the reset bit
|
||||
io::io_port_wb(0x92, val);
|
||||
|
||||
cpu::cpu_pause(100000);
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
@ -0,0 +1,299 @@
|
||||
// kernel/src/hal/rtc.rs
|
||||
use crate::hal::io::{io_port_rb, io_port_wb};
|
||||
use core::sync::atomic::{AtomicU64, Ordering};
|
||||
use crate::log_info;
|
||||
|
||||
const RTC_INDEX_PORT: u16 = 0x70;
|
||||
const RTC_TARGET_PORT: u16 = 0x71;
|
||||
const WITH_NMI_DISABLED: u8 = 0x80;
|
||||
|
||||
const RTC_REG_SEC: u8 = 0x00;
|
||||
const RTC_REG_MIN: u8 = 0x02;
|
||||
const RTC_REG_HRS: u8 = 0x04;
|
||||
const RTC_REG_WDY: u8 = 0x06;
|
||||
const RTC_REG_DAY: u8 = 0x07;
|
||||
const RTC_REG_MTH: u8 = 0x08;
|
||||
const RTC_REG_YRS: u8 = 0x09;
|
||||
|
||||
const RTC_REG_A: u8 = 0x0A;
|
||||
const RTC_REG_B: u8 = 0x0B;
|
||||
const RTC_REG_C: u8 = 0x0C;
|
||||
|
||||
const RTC_UPDATE_IN_PROGRESS: u8 = 0x80;
|
||||
const RTC_BIN_ENCODED_BIT: u8 = 0x04;
|
||||
const RTC_24HRS_ENCODED_BIT: u8 = 0x02;
|
||||
|
||||
const RTC_TIMER_ON: u8 = 0x40;
|
||||
const RTC_FREQUENCY_1024HZ: u8 = 0b110;
|
||||
const RTC_DIVIDER_33KHZ: u8 = 0b010 << 4;
|
||||
|
||||
const RTC_CURRENT_CENTURY: u16 = 2000;
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub struct DateTime {
|
||||
pub year: u16,
|
||||
pub month: u8,
|
||||
pub day: u8,
|
||||
pub weekday: u8,
|
||||
pub hour: u8,
|
||||
pub minute: u8,
|
||||
pub second: u8,
|
||||
}
|
||||
|
||||
impl DateTime {
|
||||
pub fn format(&self) -> alloc::string::String {
|
||||
alloc::format!(
|
||||
"{:04}-{:02}-{:02} {:02}:{:02}:{:02}",
|
||||
self.year, self.month, self.day,
|
||||
self.hour, self.minute, self.second
|
||||
)
|
||||
}
|
||||
|
||||
pub fn weekday_name(&self) -> &'static str {
|
||||
match self.weekday {
|
||||
1 => "Sunday",
|
||||
2 => "Monday",
|
||||
3 => "Tuesday",
|
||||
4 => "Wednesday",
|
||||
5 => "Thursday",
|
||||
6 => "Friday",
|
||||
7 => "Saturday",
|
||||
_ => "Unknown",
|
||||
}
|
||||
}
|
||||
|
||||
pub fn month_name(&self) -> &'static str {
|
||||
match self.month {
|
||||
1 => "January",
|
||||
2 => "February",
|
||||
3 => "March",
|
||||
4 => "April",
|
||||
5 => "May",
|
||||
6 => "June",
|
||||
7 => "July",
|
||||
8 => "August",
|
||||
9 => "September",
|
||||
10 => "October",
|
||||
11 => "November",
|
||||
12 => "December",
|
||||
_ => "Unknown",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// RTC Configuration
|
||||
static mut RTC_CONFIG: RtcConfig = RtcConfig::new();
|
||||
|
||||
// Tick Counter
|
||||
static RTC_TICK_COUNT: AtomicU64 = AtomicU64::new(0);
|
||||
|
||||
struct RtcConfig {
|
||||
binary_mode: bool,
|
||||
hour_24_mode: bool,
|
||||
}
|
||||
|
||||
impl RtcConfig {
|
||||
const fn new() -> Self {
|
||||
Self {
|
||||
binary_mode: false,
|
||||
hour_24_mode: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Read CMOS register
|
||||
#[inline]
|
||||
pub unsafe fn read_register(reg: u8) -> u8 {
|
||||
io_port_wb(RTC_INDEX_PORT, reg | WITH_NMI_DISABLED);
|
||||
io_port_rb(RTC_TARGET_PORT)
|
||||
}
|
||||
|
||||
/// Write to CMOS register
|
||||
#[inline]
|
||||
pub unsafe fn write_register(reg: u8, value: u8) {
|
||||
io_port_wb(RTC_INDEX_PORT, reg | WITH_NMI_DISABLED);
|
||||
io_port_wb(RTC_TARGET_PORT, value);
|
||||
}
|
||||
|
||||
/// Check if RTC is updating
|
||||
#[inline]
|
||||
unsafe fn is_updating() -> bool {
|
||||
(read_register(RTC_REG_A) & RTC_UPDATE_IN_PROGRESS) != 0
|
||||
}
|
||||
|
||||
/// Wait for RTC update to complete
|
||||
unsafe fn wait_for_update() {
|
||||
while is_updating() {
|
||||
core::hint::spin_loop();
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert BCD to binary
|
||||
#[inline]
|
||||
fn bcd_to_binary(bcd: u8) -> u8 {
|
||||
(bcd & 0x0F) + ((bcd >> 4) * 10)
|
||||
}
|
||||
|
||||
pub fn init() {
|
||||
unsafe {
|
||||
// Read configuration
|
||||
let status_b = read_register(RTC_REG_B);
|
||||
RTC_CONFIG.binary_mode = (status_b & RTC_BIN_ENCODED_BIT) != 0;
|
||||
RTC_CONFIG.hour_24_mode = (status_b & RTC_24HRS_ENCODED_BIT) != 0;
|
||||
|
||||
// Configuring frequencies and dividers
|
||||
let mut reg_a = read_register(RTC_REG_A);
|
||||
reg_a = (reg_a & 0xF0) | RTC_DIVIDER_33KHZ | RTC_FREQUENCY_1024HZ;
|
||||
write_register(RTC_REG_A, reg_a);
|
||||
|
||||
// 清除中斷
|
||||
read_register(RTC_REG_C);
|
||||
|
||||
// 確保 timer 關閉
|
||||
disable_timer();
|
||||
|
||||
// 再次清除
|
||||
read_register(RTC_REG_C);
|
||||
}
|
||||
|
||||
log_info!("RTC initialized (lock-free design)");
|
||||
}
|
||||
|
||||
pub fn get_time() -> DateTime {
|
||||
unsafe {
|
||||
loop {
|
||||
// Wait for RTC to be ready
|
||||
wait_for_update();
|
||||
|
||||
// Read twice quickly
|
||||
let time1 = read_time_raw();
|
||||
let time2 = read_time_raw();
|
||||
|
||||
// If the two reads are consistent, return the result
|
||||
if time1 == time2 {
|
||||
return time1;
|
||||
}
|
||||
|
||||
// Retry if inconsistent (maybe just rollover in seconds)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Directly read the RTC register
|
||||
unsafe fn read_time_raw() -> DateTime {
|
||||
let config = &RTC_CONFIG;
|
||||
|
||||
let mut second = read_register(RTC_REG_SEC);
|
||||
let mut minute = read_register(RTC_REG_MIN);
|
||||
let mut hour = read_register(RTC_REG_HRS);
|
||||
let mut day = read_register(RTC_REG_DAY);
|
||||
let mut month = read_register(RTC_REG_MTH);
|
||||
let mut year = read_register(RTC_REG_YRS);
|
||||
let weekday = read_register(RTC_REG_WDY);
|
||||
|
||||
if !config.binary_mode {
|
||||
second = bcd_to_binary(second);
|
||||
minute = bcd_to_binary(minute);
|
||||
day = bcd_to_binary(day);
|
||||
month = bcd_to_binary(month);
|
||||
year = bcd_to_binary(year);
|
||||
}
|
||||
|
||||
let pm_bit = hour & 0x80;
|
||||
hour = if config.binary_mode {
|
||||
hour & 0x7F
|
||||
} else {
|
||||
bcd_to_binary(hour & 0x7F)
|
||||
};
|
||||
|
||||
if !config.hour_24_mode && pm_bit != 0 {
|
||||
hour = (hour + 12) % 24;
|
||||
}
|
||||
|
||||
DateTime {
|
||||
year: RTC_CURRENT_CENTURY + year as u16,
|
||||
month,
|
||||
day,
|
||||
weekday,
|
||||
hour,
|
||||
minute,
|
||||
second,
|
||||
}
|
||||
}
|
||||
|
||||
/// Enable RTC periodic interrupt (1024Hz)
|
||||
pub fn enable_timer() {
|
||||
unsafe {
|
||||
// Close first
|
||||
disable_timer();
|
||||
read_register(RTC_REG_C);
|
||||
|
||||
// Setting the frequency
|
||||
let mut reg_a = read_register(RTC_REG_A);
|
||||
reg_a = (reg_a & 0xF0) | RTC_DIVIDER_33KHZ | RTC_FREQUENCY_1024HZ;
|
||||
write_register(RTC_REG_A, reg_a);
|
||||
|
||||
// Enable periodic interrupts
|
||||
let mut reg_b = read_register(RTC_REG_B);
|
||||
reg_b |= RTC_TIMER_ON;
|
||||
write_register(RTC_REG_B, reg_b);
|
||||
|
||||
// Clear interrupt flag
|
||||
read_register(RTC_REG_C);
|
||||
}
|
||||
|
||||
log_info!("RTC timer enabled at 1024Hz");
|
||||
}
|
||||
|
||||
/// Disable RTC periodic interrupt
|
||||
pub fn disable_timer() {
|
||||
unsafe {
|
||||
let mut reg_b = read_register(RTC_REG_B);
|
||||
reg_b &= !RTC_TIMER_ON;
|
||||
write_register(RTC_REG_B, reg_b);
|
||||
|
||||
read_register(RTC_REG_C);
|
||||
}
|
||||
}
|
||||
|
||||
/// RTC interrupt handler
|
||||
///
|
||||
/// This is the only function called in interrupt context.
|
||||
/// Clears the interrupt flag
|
||||
/// Increments the tick counter
|
||||
#[inline]
|
||||
pub fn handle_interrupt() {
|
||||
unsafe {
|
||||
read_register(RTC_REG_C);
|
||||
}
|
||||
|
||||
RTC_TICK_COUNT.fetch_add(1, Ordering::Relaxed);
|
||||
}
|
||||
|
||||
/// Get RTC tick count (lock-free)
|
||||
#[inline]
|
||||
pub fn get_tick_count() -> u64 {
|
||||
RTC_TICK_COUNT.load(Ordering::Relaxed)
|
||||
}
|
||||
|
||||
/// Reset tick count
|
||||
#[inline]
|
||||
pub fn reset_tick_count() {
|
||||
RTC_TICK_COUNT.store(0, Ordering::Relaxed);
|
||||
}
|
||||
|
||||
/// Print current time information
|
||||
pub fn print_info() {
|
||||
let time = get_time();
|
||||
|
||||
log_info!(
|
||||
"{}, {} {}, {} - {:02}:{:02}:{:02}",
|
||||
time.weekday_name(),
|
||||
time.month_name(),
|
||||
time.day,
|
||||
time.year,
|
||||
time.hour,
|
||||
time.minute,
|
||||
time.second
|
||||
);
|
||||
}
|
||||
344
kernel/src/hal/timer.rs
Normal file
344
kernel/src/hal/timer.rs
Normal file
@ -0,0 +1,344 @@
|
||||
// kernel/src/hal/timer
|
||||
|
||||
use crate::hal::{cpu, rtc};
|
||||
use core::sync::atomic::{AtomicBool, AtomicU64, Ordering};
|
||||
use crate::{log_debug, log_error, log_info, log_warn};
|
||||
use crate::hal::apic::{ioapic, lapic};
|
||||
|
||||
const APIC_CALIBRATION_CONST: u32 = 0x100000;
|
||||
const RTC_BASE_FREQUENCY: u32 = 1024;
|
||||
|
||||
// APIC Timer register offset
|
||||
const APIC_LVT_TIMER: u32 = 0x320;
|
||||
const APIC_TIMER_ICR: u32 = 0x380;
|
||||
const APIC_TIMER_DCR: u32 = 0x3E0;
|
||||
|
||||
/// APIC Timer divider
|
||||
#[repr(u32)]
|
||||
#[allow(dead_code)]
|
||||
pub enum ApicTimerDivider {
|
||||
Div1 = 0b1011,
|
||||
Div2 = 0b0000,
|
||||
Div4 = 0b0001,
|
||||
Div8 = 0b0010,
|
||||
Div16 = 0b0011,
|
||||
Div32 = 0b1000,
|
||||
Div64 = 0b1001,
|
||||
Div128 = 0b1010,
|
||||
}
|
||||
|
||||
// Timer configuration
|
||||
static mut TIMER_CONFIG: TimerConfig = TimerConfig::new();
|
||||
|
||||
// Calibration status
|
||||
static mut CALIBRATION: CalibrationState = CalibrationState::new();
|
||||
|
||||
// Runtime counter
|
||||
static TICK_COUNTER: AtomicU64 = AtomicU64::new(0);
|
||||
|
||||
// Calibration flag
|
||||
static IS_CALIBRATING: AtomicBool = AtomicBool::new(false);
|
||||
|
||||
static TIMEOUT: AtomicU64 = AtomicU64::new(100_000_000);
|
||||
|
||||
struct TimerConfig {
|
||||
base_frequency: u32,
|
||||
running_frequency: u32,
|
||||
tick_interval: u32,
|
||||
initialized: bool,
|
||||
}
|
||||
|
||||
impl TimerConfig {
|
||||
const fn new() -> Self {
|
||||
Self {
|
||||
base_frequency: 0,
|
||||
running_frequency: 0,
|
||||
tick_interval: 0,
|
||||
initialized: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct CalibrationState {
|
||||
rtc_ticks: u64,
|
||||
done: bool,
|
||||
frequency: u64,
|
||||
}
|
||||
|
||||
impl CalibrationState {
|
||||
const fn new() -> Self {
|
||||
Self {
|
||||
rtc_ticks: 0,
|
||||
done: false,
|
||||
frequency: 0,
|
||||
}
|
||||
}
|
||||
|
||||
fn reset(&mut self) {
|
||||
self.rtc_ticks = 0;
|
||||
self.done = false;
|
||||
self.frequency = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/// Check if calibration is in progress (lock-free)
|
||||
#[inline]
|
||||
pub fn is_calibrating() -> bool {
|
||||
IS_CALIBRATING.load(Ordering::Relaxed)
|
||||
}
|
||||
|
||||
/// Initialize and calibrate the APIC Timer
|
||||
///
|
||||
/// # Parameters
|
||||
/// - `target_frequency`: Target frequency (Hz), recommended range: 100-1000
|
||||
/// - `apic_id`: APIC ID of the current CPU
|
||||
///
|
||||
/// # Returns
|
||||
/// Whether initialization was successful
|
||||
pub fn init(target_frequency: u32, apic_id: u8) -> bool {
|
||||
if lapic::get_base_vaddr().is_none() {
|
||||
log_error!("LAPIC not initialized!");
|
||||
return false;
|
||||
}
|
||||
|
||||
unsafe {
|
||||
CALIBRATION.reset();
|
||||
}
|
||||
|
||||
IS_CALIBRATING.store(true, Ordering::SeqCst);
|
||||
|
||||
cpu::cpu_disable_interrupts();
|
||||
|
||||
log_debug!("Setting up APIC Timer for calibration...");
|
||||
|
||||
unsafe {
|
||||
// Configure LVT Timer: one-shot mode, vector 32, masked
|
||||
lapic::write_apic_reg_raw(APIC_LVT_TIMER, 32 | (1 << 16));
|
||||
|
||||
// Set the divider to 64
|
||||
lapic::write_apic_reg_raw(APIC_TIMER_DCR, ApicTimerDivider::Div64 as u32);
|
||||
}
|
||||
|
||||
log_debug!("Configuring interrupts...");
|
||||
|
||||
// Configure RTC interrupt (IRQ 8 -> Vector 40)
|
||||
ioapic::set_irq_redirect(8, 40, apic_id, false, false);
|
||||
ioapic::unmask_irq(8);
|
||||
|
||||
log_info!("Starting calibration...");
|
||||
|
||||
rtc::reset_tick_count();
|
||||
rtc::enable_timer();
|
||||
|
||||
cpu::cpu_pause(1000);
|
||||
|
||||
unsafe {
|
||||
// Unmask APIC Timer
|
||||
lapic::write_apic_reg_raw(APIC_LVT_TIMER, 32);
|
||||
|
||||
// Write the initial count value and start counting down
|
||||
lapic::write_apic_reg_raw(APIC_TIMER_ICR, APIC_CALIBRATION_CONST);
|
||||
}
|
||||
|
||||
log_debug!("Waiting for calibration...");
|
||||
|
||||
cpu::cpu_enable_interrupts();
|
||||
|
||||
let mut remaining = TIMEOUT.load(Ordering::Relaxed);
|
||||
while !unsafe { CALIBRATION.done } && remaining > 0 {
|
||||
cpu::cpu_pause(0);
|
||||
remaining -= 1;
|
||||
}
|
||||
cpu::cpu_disable_interrupts();
|
||||
|
||||
if remaining == 0 {
|
||||
log_error!("Calibration timeout!");
|
||||
IS_CALIBRATING.store(false, Ordering::SeqCst);
|
||||
return false;
|
||||
}
|
||||
|
||||
let base_frequency = unsafe { CALIBRATION.frequency as u32 };
|
||||
let rtc_ticks = unsafe { CALIBRATION.rtc_ticks };
|
||||
|
||||
if base_frequency == 0 {
|
||||
log_error!("Calibration failed (freq = 0)!");
|
||||
IS_CALIBRATING.store(false, Ordering::SeqCst);
|
||||
return false;
|
||||
}
|
||||
|
||||
log_info!("Calibration complete!");
|
||||
log_info!("RTC ticks: {}", rtc_ticks);
|
||||
log_info!("Base frequency: {} Hz", base_frequency);
|
||||
log_info!("Bus speed: ~{} MHz", base_frequency * 64 / 1_000_000);
|
||||
|
||||
// Calculating the tick interval
|
||||
let tick_interval = base_frequency / target_frequency;
|
||||
|
||||
log_info!("Configuring periodic timer...");
|
||||
log_info!("Target: {} Hz", target_frequency);
|
||||
log_info!("Interval: {}", tick_interval);
|
||||
|
||||
unsafe {
|
||||
TIMER_CONFIG = TimerConfig {
|
||||
base_frequency,
|
||||
running_frequency: target_frequency,
|
||||
tick_interval,
|
||||
initialized: true,
|
||||
};
|
||||
}
|
||||
|
||||
unsafe {
|
||||
// Configure for periodic mode: periodic bit | vector 32
|
||||
lapic::write_apic_reg_raw(APIC_LVT_TIMER, (1 << 17) | 32);
|
||||
|
||||
// Set the count value
|
||||
lapic::write_apic_reg_raw(APIC_TIMER_ICR, tick_interval);
|
||||
}
|
||||
|
||||
// Mark calibration completed
|
||||
IS_CALIBRATING.store(false, Ordering::SeqCst);
|
||||
|
||||
// Ensure all writes are complete
|
||||
core::sync::atomic::fence(Ordering::SeqCst);
|
||||
|
||||
log_info!("APIC Timer ready at {} Hz", target_frequency);
|
||||
log_info!("APIC Timer started successfully!");
|
||||
|
||||
cpu::cpu_enable_interrupts();
|
||||
|
||||
true
|
||||
}
|
||||
|
||||
/// RTC interrupt handling (calibration phase)
|
||||
///
|
||||
/// Only called during calibration, only counts
|
||||
#[inline]
|
||||
pub fn rtc_calibration_handler() {
|
||||
unsafe {
|
||||
CALIBRATION.rtc_ticks += 1;
|
||||
}
|
||||
}
|
||||
|
||||
/// APIC Timer Interrupt Handling (Calibration Phase)
|
||||
///
|
||||
/// Calculate frequency and mark completion
|
||||
pub fn apic_calibration_handler() {
|
||||
let rtc_ticks = unsafe { CALIBRATION.rtc_ticks };
|
||||
|
||||
if rtc_ticks == 0 {
|
||||
log_warn!("APIC Timer fired but RTC = 0!");
|
||||
unsafe {
|
||||
CALIBRATION.done = true;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// 計算頻率: base_freq = (CONST / ticks) * RTC_FREQ
|
||||
let base_frequency = ((APIC_CALIBRATION_CONST as u64) * (RTC_BASE_FREQUENCY as u64))
|
||||
/ rtc_ticks;
|
||||
|
||||
log_debug!("Calibration: {} ticks -> {} Hz", rtc_ticks, base_frequency);
|
||||
|
||||
unsafe {
|
||||
CALIBRATION.frequency = base_frequency;
|
||||
CALIBRATION.done = true;
|
||||
}
|
||||
|
||||
// 停止 RTC
|
||||
rtc::disable_timer();
|
||||
}
|
||||
|
||||
/// APIC Timer Periodic Tick Processing (Run Phase)
|
||||
///
|
||||
/// Counts only, does nothing else
|
||||
#[inline]
|
||||
pub fn timer_tick_handler() {
|
||||
TICK_COUNTER.fetch_add(1, Ordering::Relaxed);
|
||||
}
|
||||
|
||||
/// Get timer information
|
||||
pub fn get_info() -> Option<(u32, u32, u64)> {
|
||||
unsafe {
|
||||
if TIMER_CONFIG.initialized {
|
||||
Some((
|
||||
TIMER_CONFIG.base_frequency,
|
||||
TIMER_CONFIG.running_frequency,
|
||||
TICK_COUNTER.load(Ordering::Relaxed)
|
||||
))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the total number of ticks (lock-free)
|
||||
#[inline]
|
||||
pub fn get_tick_count() -> u64 {
|
||||
TICK_COUNTER.load(Ordering::Relaxed)
|
||||
}
|
||||
|
||||
/// Reset the tick counter
|
||||
#[inline]
|
||||
pub fn reset_tick_count() {
|
||||
TICK_COUNTER.store(0, Ordering::Relaxed);
|
||||
}
|
||||
|
||||
/// Get the basic frequency
|
||||
#[inline]
|
||||
pub fn get_base_frequency() -> Option<u32> {
|
||||
unsafe {
|
||||
if TIMER_CONFIG.initialized {
|
||||
Some(TIMER_CONFIG.base_frequency)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the running frequency
|
||||
#[inline]
|
||||
pub fn get_running_frequency() -> Option<u32> {
|
||||
unsafe {
|
||||
if TIMER_CONFIG.initialized {
|
||||
Some(TIMER_CONFIG.running_frequency)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Wait for the specified number of milliseconds (busy wait)
|
||||
pub fn busy_wait_ms(ms: u64) {
|
||||
if let Some(freq) = get_running_frequency() {
|
||||
let ticks_to_wait = (ms * freq as u64) / 1000;
|
||||
let start = get_tick_count();
|
||||
|
||||
while get_tick_count() - start < ticks_to_wait {
|
||||
cpu::cpu_pause(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Wait for the specified number of microseconds (busy wait)
|
||||
pub fn busy_wait_us(us: u64) {
|
||||
if let Some(freq) = get_running_frequency() {
|
||||
let ticks_to_wait = (us * freq as u64) / 1_000_000;
|
||||
let start = get_tick_count();
|
||||
|
||||
while get_tick_count() - start < ticks_to_wait {
|
||||
cpu::cpu_pause(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Print timer information
|
||||
pub fn print_info() {
|
||||
if let Some((base_freq, running_freq, ticks)) = get_info() {
|
||||
log_info!("Base Frequency: {} Hz", base_freq);
|
||||
log_info!("Running Frequency: {} Hz", running_freq);
|
||||
log_info!("Total Ticks: {}", ticks);
|
||||
log_info!("Bus Speed: ~{} MHz", base_freq * 64 / 1_000_000);
|
||||
} else {
|
||||
log_warn!("APIC Timer not initialized");
|
||||
}
|
||||
}
|
||||
@ -1,9 +1,9 @@
|
||||
// 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::mm::{allocator::{frame, heap, pmm}, paging, vma, vmm};
|
||||
use crate::arch::amd64::{gdt, idt};
|
||||
use crate::tty::tty;
|
||||
use crate::kprintln;
|
||||
@ -11,7 +11,10 @@ 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;
|
||||
use crate::drivers::keyboard;
|
||||
use crate::hal::{acpi, cpu, rtc, timer};
|
||||
use crate::hal::apic::{ioapic, lapic};
|
||||
use crate::hal::cpu::cpu_enable_interrupts;
|
||||
|
||||
fn _logger_init() {
|
||||
init(
|
||||
@ -92,14 +95,10 @@ fn _memory_init(
|
||||
fn _display_init(framebuffer: &'static mut bootloader_api::info::FrameBuffer) {
|
||||
tty::init(framebuffer);
|
||||
tty::clear(0x000000);
|
||||
|
||||
kprintln!("========================================");
|
||||
kprintln!(" CureOS Kernel v0.1.0" );
|
||||
kprintln!("========================================");
|
||||
kprintln!();
|
||||
}
|
||||
|
||||
fn _boot_report(memory_regions: &bootloader_api::info::MemoryRegions, physical_memory_offset: u64) {
|
||||
kprintln!();
|
||||
log_info!("GDT initialized");
|
||||
gdt::print_info();
|
||||
|
||||
@ -132,32 +131,127 @@ 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 {
|
||||
log_debug!("RSDP Address: {:#x}", rsdp);
|
||||
|
||||
if let Some(acpi_info) = acpi::init(rsdp, physical_memory_offset) {
|
||||
acpi::print_info(&acpi_info);
|
||||
if let Some(acpi_info) = acpi::init::init(rsdp, physical_memory_offset) {
|
||||
acpi::init::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());
|
||||
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();
|
||||
|
||||
if let Some(apic_id) = lapic::get_apic_id() {
|
||||
log_info!("Setting up APIC Timer...");
|
||||
log_info!("Current CPU APIC ID: {}", apic_id);
|
||||
|
||||
if timer::init(100, apic_id as u8) {
|
||||
log_info!("APIC Timer initialized successfully!");
|
||||
|
||||
// 顯示信息
|
||||
if let Some((base, running, ticks)) = timer::get_info() {
|
||||
log_info!("Base freq: {} Hz", base);
|
||||
log_info!("Running at: {} Hz", running);
|
||||
log_info!("Current ticks: {}", ticks);
|
||||
}
|
||||
} else {
|
||||
log_error!("Failed to initialize APIC Timer!");
|
||||
}
|
||||
|
||||
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)
|
||||
);
|
||||
|
||||
log_info!("Configuring hardware interrupts...");
|
||||
cpu_enable_interrupts();
|
||||
|
||||
} else {
|
||||
log_error!("APIC not available, cannot enable keyboard");
|
||||
}
|
||||
|
||||
log_debug!("Cleanup completed");
|
||||
}
|
||||
|
||||
@ -180,19 +274,14 @@ 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();
|
||||
|
||||
_test_memory_management(&mut mapper, &mut frame_allocator);
|
||||
|
||||
kprintln!();
|
||||
kprintln!("========================================");
|
||||
kprintln!(" Kernel Initialization Complete! ");
|
||||
kprintln!("========================================");
|
||||
log_info!("System initialization complete!");
|
||||
kprintln!();
|
||||
|
||||
k_main::_kernel_main();
|
||||
@ -206,68 +295,5 @@ pub fn _kernel_init(boot_info: &'static mut BootInfo) -> ! {
|
||||
pub fn kernel_emergency_cleanup() {
|
||||
log_error!("Emergency cleanup triggered");
|
||||
// 在 panic 前調用,做最後的清理工作
|
||||
// 比如刷新緩衝區、保存日誌等
|
||||
}
|
||||
|
||||
fn _test_memory_management(
|
||||
mapper: &mut OffsetPageTable,
|
||||
frame_allocator: &mut frame::BootInfoFrameAllocator
|
||||
) {
|
||||
kprintln!();
|
||||
log_info!("Testing Memory Management System...");
|
||||
|
||||
// Physical memory allocation
|
||||
log_debug!("Test 1: Physical frame allocation");
|
||||
if let Some(frame) = pmm::allocate_frame() {
|
||||
log_debug!(" Allocated frame at: {:#x}", frame.start_address().as_u64());
|
||||
pmm::deallocate_frame(frame);
|
||||
log_debug!(" Deallocated frame");
|
||||
}
|
||||
|
||||
// Virtual memory allocation
|
||||
log_debug!("Test 2: Virtual memory allocation (kmalloc)");
|
||||
if let Some(vaddr) = malloc::kmalloc(8192, mapper, frame_allocator) {
|
||||
log_debug!(" Allocated 8KB at: {:#x}", vaddr.as_u64());
|
||||
|
||||
// Test Write
|
||||
unsafe {
|
||||
let ptr = vaddr.as_mut_ptr::<u64>();
|
||||
*ptr = 0xDEADBEEF;
|
||||
log_debug!(" Written test value: {:#x}", *ptr);
|
||||
}
|
||||
|
||||
malloc::kfree(vaddr, 8192, mapper, frame_allocator);
|
||||
log_debug!(" Freed memory");
|
||||
}
|
||||
|
||||
// Page table mapping
|
||||
log_debug!("Test 3: Page table mapping");
|
||||
let test_vaddr = VirtAddr::new(0x5000_0000_0000);
|
||||
let test_page = Page::containing_address(test_vaddr);
|
||||
|
||||
if let Some(test_frame) = pmm::allocate_frame() {
|
||||
if paging::PageTableManager::map_page(
|
||||
test_page,
|
||||
test_frame,
|
||||
paging::kernel_data(),
|
||||
mapper,
|
||||
frame_allocator
|
||||
).is_ok() {
|
||||
log_debug!(" Mapped page {:#x} to frame {:#x}",
|
||||
test_vaddr.as_u64(), test_frame.start_address().as_u64());
|
||||
|
||||
// Testing Address Translation
|
||||
if let Some(phys) = paging::PageTableManager::translate_addr(test_vaddr, mapper) {
|
||||
log_debug!(" Translation check: {:#x} -> {:#x}", test_vaddr.as_u64(), phys.as_u64());
|
||||
}
|
||||
|
||||
// Unmap
|
||||
if paging::PageTableManager::unmap_page(test_page, mapper).is_ok() {
|
||||
log_debug!(" Unmapped page");
|
||||
}
|
||||
}
|
||||
pmm::deallocate_frame(test_frame);
|
||||
}
|
||||
|
||||
log_info!("Memory tests completed!");
|
||||
// 刷新緩衝區、保存日誌等
|
||||
}
|
||||
@ -1,56 +1,62 @@
|
||||
// kernel/src/k_main.rs (Updated with Logger Demo)
|
||||
use crate::hal::cpu;
|
||||
use crate::kprintln;
|
||||
// kernel/src/kernel/k_main.rs
|
||||
use crate::hal::{timer, cpu, rtc};
|
||||
use crate::{kprint, kprintln, shell};
|
||||
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!();
|
||||
kprintln!("=== Welcome to CureOS! ===");
|
||||
kprintln!();
|
||||
|
||||
let mut brand_buf = [0u8; 64];
|
||||
let mut model_buf = [0u8; 16];
|
||||
log_info!("CPU: {} ({})",
|
||||
cpu::cpu_get_brand(&mut brand_buf),
|
||||
cpu::cpu_get_model(&mut model_buf)
|
||||
);
|
||||
let mut brand = [0u8; 64];
|
||||
let cpu_brand = cpu::cpu_get_brand(&mut brand);
|
||||
kprintln!("CPU: {}", cpu_brand);
|
||||
|
||||
if let Some(stats) = pmm::get_memory_stats() {
|
||||
kprintln!("Memory: {} MiB / {} MiB free",
|
||||
stats.free_memory / (1024 * 1024),
|
||||
stats.total_memory / (1024 * 1024));
|
||||
kprintln!(" {}% used",
|
||||
(stats.used_memory * 100) / stats.total_memory);
|
||||
}
|
||||
|
||||
if let Some((base_freq, running_freq, ticks)) = timer::get_info() {
|
||||
kprintln!("Timer: {} Hz (base: {} Hz)", running_freq, base_freq);
|
||||
kprintln!("Ticks: {}", ticks);
|
||||
}
|
||||
|
||||
if let time = rtc::get_time() {
|
||||
kprintln!("Date/Time: {}", time.format());
|
||||
}
|
||||
|
||||
kprintln!();
|
||||
log_debug!("CR0: 0x{:016x}", cpu::cpu_r_cr0());
|
||||
log_debug!("CR2: 0x{:016x}", cpu::cpu_r_cr2());
|
||||
log_debug!("CR3: 0x{:016x}", cpu::cpu_r_cr3());
|
||||
log_debug!("CR4: 0x{:016x}", cpu::cpu_r_cr4());
|
||||
|
||||
vma::print_info();
|
||||
let vmm_stats = vmm::get_vmm_stats();
|
||||
vmm_stats.print();
|
||||
|
||||
// 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!");
|
||||
|
||||
kprintln!("Type 'help' for available commands");
|
||||
kprintln!();
|
||||
log_warn!("Entering idle loop");
|
||||
|
||||
shell::init();
|
||||
|
||||
loop {
|
||||
cpu::cpu_halt();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn test_apic_timer() {
|
||||
|
||||
log_info!("=== APIC Timer Test ===");
|
||||
|
||||
let start_ticks = timer::get_tick_count();
|
||||
|
||||
cpu::cpu_pause(1000);
|
||||
|
||||
let end_ticks = timer::get_tick_count();
|
||||
let elapsed = end_ticks - start_ticks;
|
||||
|
||||
log_info!("Elapsed ticks: {}", elapsed);
|
||||
|
||||
if let Some((_, freq, _)) = timer::get_info() {
|
||||
log_info!("Expected ~{} ticks/sec", freq);
|
||||
log_info!("Actual rate: {} Hz", elapsed);
|
||||
}
|
||||
}
|
||||
|
||||
@ -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)*)));
|
||||
}
|
||||
|
||||
|
||||
@ -21,6 +21,8 @@ pub mod arch;
|
||||
pub mod mm;
|
||||
pub mod tty;
|
||||
pub mod kernel;
|
||||
pub mod drivers;
|
||||
pub mod shell;
|
||||
|
||||
use hal::cpu;
|
||||
const CONFIG: BootloaderConfig = {
|
||||
|
||||
@ -141,32 +141,31 @@ pub fn get_region_name(addr: VirtAddr) -> &'static str {
|
||||
|
||||
/// Print hhk address space layout
|
||||
pub fn print_info() {
|
||||
log_info!("Higher Half Kernel Memory Layout:");
|
||||
log_info!(" User Space: {:#018x} - {:#018x}",
|
||||
log_info!("User Space: {:#018x} - {:#018x}",
|
||||
0x0u64,
|
||||
0x0000_7FFF_FFFF_FFFFu64
|
||||
);
|
||||
log_info!(" Physical Map: {:#018x} - {:#018x}",
|
||||
log_info!("Physical Map: {:#018x} - {:#018x}",
|
||||
PHYS_MEM_OFFSET,
|
||||
HIGHER_HALF_BASE - 1
|
||||
);
|
||||
log_info!(" Kernel Base: {:#018x}", HIGHER_HALF_BASE);
|
||||
log_info!(" Heap: {:#018x} - {:#018x} ({} KiB)",
|
||||
log_info!("Kernel Base: {:#018x}", HIGHER_HALF_BASE);
|
||||
log_info!("Heap: {:#018x} - {:#018x} ({} KiB)",
|
||||
HEAP_START.as_u64(),
|
||||
HEAP_START.as_u64() + HEAP_SIZE as u64,
|
||||
HEAP_SIZE / 1024
|
||||
);
|
||||
log_info!(" Dynamic: {:#018x} - {:#018x} ({} MiB)",
|
||||
log_info!("Dynamic: {:#018x} - {:#018x} ({} MiB)",
|
||||
KERNEL_DYNAMIC_START.as_u64(),
|
||||
KERNEL_DYNAMIC_END.as_u64(),
|
||||
KERNEL_DYNAMIC_SIZE / (1024 * 1024)
|
||||
);
|
||||
log_info!(" Kernel Stack: {:#018x} - {:#018x} ({} MiB)",
|
||||
log_info!("Kernel Stack: {:#018x} - {:#018x} ({} MiB)",
|
||||
KERNEL_STACK_START.as_u64(),
|
||||
KERNEL_STACK_END.as_u64(),
|
||||
KERNEL_STACK_SIZE / (1024 * 1024)
|
||||
);
|
||||
log_info!(" Device Mapping: {:#018x} - {:#018x} ({} MiB)",
|
||||
log_info!("Device Mapping: {:#018x} - {:#018x} ({} MiB)",
|
||||
DEVICE_MAPPING_START.as_u64(),
|
||||
DEVICE_MAPPING_END.as_u64(),
|
||||
DEVICE_MAPPING_SIZE / (1024 * 1024)
|
||||
|
||||
@ -21,7 +21,6 @@ struct MemoryBlock {
|
||||
page_count: usize,
|
||||
}
|
||||
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct VmmStats {
|
||||
pub next_vaddr: VirtAddr,
|
||||
@ -35,18 +34,17 @@ pub struct VmmStats {
|
||||
|
||||
impl VmmStats {
|
||||
pub fn print(&self) {
|
||||
log_info!("Virtual Memory Statistics:");
|
||||
log_info!(" Allocated: {} pages ({} KiB) in {} blocks",
|
||||
log_info!("Allocated: {} pages ({} KiB) in {} blocks",
|
||||
self.allocated_pages,
|
||||
self.allocated_pages * 4,
|
||||
self.allocated_blocks_count
|
||||
);
|
||||
log_info!(" Free: {} pages ({} KiB) in {} blocks",
|
||||
log_info!("Free: {} pages ({} KiB) in {} blocks",
|
||||
self.free_pages,
|
||||
self.free_pages * 4,
|
||||
self.free_blocks_count
|
||||
);
|
||||
log_info!(" New Usage: {} pages ({} KiB)",
|
||||
log_info!("New Usage: {} pages ({} KiB)",
|
||||
self.used_from_new,
|
||||
self.used_from_new * 4
|
||||
);
|
||||
|
||||
186
kernel/src/shell/commands.rs
Normal file
186
kernel/src/shell/commands.rs
Normal file
@ -0,0 +1,186 @@
|
||||
// kernel/src/shell/commands.rs
|
||||
use crate::{kprintln, tty, hal::{rtc, timer, cpu}, log_info};
|
||||
use crate::hal::io::io_port_wb;
|
||||
use crate::hal::power;
|
||||
use crate::mm::{vma, vmm, allocator::pmm};
|
||||
/// help 命令
|
||||
pub fn cmd_help() {
|
||||
kprintln!(" help - Show this help message");
|
||||
kprintln!(" clear | clr - Clear the screen");
|
||||
kprintln!(" time - Display current date and time");
|
||||
kprintln!(" uptime - Show system uptime");
|
||||
kprintln!(" sysinfo | sys - Display system information");
|
||||
kprintln!(" meminfo | mem - Display memory information");
|
||||
kprintln!(" reboot - Reboot the system");
|
||||
kprintln!();
|
||||
}
|
||||
|
||||
|
||||
pub fn cmd_clear() {
|
||||
tty::tty::clear(0x000000);
|
||||
}
|
||||
|
||||
pub fn cmd_time() {
|
||||
if let time = rtc::get_time() {
|
||||
kprintln!("Current time: {}", time.format());
|
||||
kprintln!("{}, {} {}, {}",
|
||||
time.weekday_name(),
|
||||
time.month_name(),
|
||||
time.day,
|
||||
time.year
|
||||
);
|
||||
} else {
|
||||
kprintln!("Error: Unable to read RTC");
|
||||
}
|
||||
}
|
||||
|
||||
pub fn cmd_uptime() {
|
||||
let ticks = timer::get_tick_count();
|
||||
let total_seconds = ticks / 100;
|
||||
let hours = total_seconds / 3600;
|
||||
let minutes = (total_seconds % 3600) / 60;
|
||||
let seconds = total_seconds % 60;
|
||||
|
||||
kprintln!("System uptime: {}h {}m {}s ({} ticks)",
|
||||
hours, minutes, seconds, ticks);
|
||||
}
|
||||
|
||||
pub fn cmd_echo(text: &str) {
|
||||
if text.is_empty() {
|
||||
kprintln!();
|
||||
} else {
|
||||
kprintln!("{}", text);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn cmd_meminfo() {
|
||||
kprintln!();
|
||||
kprintln!("=== Memory Layout ===");
|
||||
kprintln!();
|
||||
|
||||
// 高半核地址空間布局
|
||||
kprintln!("Virtual Memory Layout:");
|
||||
kprintln!(" User Space: {:#018x} - {:#018x}",
|
||||
0x0u64,
|
||||
0x0000_7FFF_FFFF_FFFFu64
|
||||
);
|
||||
kprintln!(" (Non-canonical): {:#018x} - {:#018x}",
|
||||
0x0000_8000_0000_0000u64,
|
||||
0xFFFF_7FFF_FFFF_FFFFu64
|
||||
);
|
||||
kprintln!(" Physical Map: {:#018x} - {:#018x}",
|
||||
vma::PHYS_MEM_OFFSET,
|
||||
vma::HIGHER_HALF_BASE - 1
|
||||
);
|
||||
kprintln!(" Kernel Base: {:#018x}", vma::HIGHER_HALF_BASE);
|
||||
kprintln!(" Kernel Heap: {:#018x} - {:#018x} ({} KiB)",
|
||||
vma::HEAP_START.as_u64(),
|
||||
vma::HEAP_START.as_u64() + vma::HEAP_SIZE as u64,
|
||||
vma::HEAP_SIZE / 1024
|
||||
);
|
||||
kprintln!(" Kernel Dynamic: {:#018x} - {:#018x} ({} MiB)",
|
||||
vma::KERNEL_DYNAMIC_START.as_u64(),
|
||||
vma::KERNEL_DYNAMIC_END.as_u64(),
|
||||
vma::KERNEL_DYNAMIC_SIZE / (1024 * 1024)
|
||||
);
|
||||
kprintln!(" Kernel Stack: {:#018x} - {:#018x} ({} MiB)",
|
||||
vma::KERNEL_STACK_START.as_u64(),
|
||||
vma::KERNEL_STACK_END.as_u64(),
|
||||
vma::KERNEL_STACK_SIZE / (1024 * 1024)
|
||||
);
|
||||
kprintln!(" Device Mapping: {:#018x} - {:#018x} ({} MiB)",
|
||||
vma::DEVICE_MAPPING_START.as_u64(),
|
||||
vma::DEVICE_MAPPING_END.as_u64(),
|
||||
vma::DEVICE_MAPPING_SIZE / (1024 * 1024)
|
||||
);
|
||||
|
||||
kprintln!();
|
||||
kprintln!("Physical Memory (PMM):");
|
||||
|
||||
if let Some(stats) = pmm::get_memory_stats() {
|
||||
kprintln!(" Total: {} MiB ({} frames)",
|
||||
stats.total_memory / (1024 * 1024),
|
||||
stats.total_frames
|
||||
);
|
||||
kprintln!(" Used: {} MiB ({} frames)",
|
||||
stats.used_memory / (1024 * 1024),
|
||||
stats.allocated_frames
|
||||
);
|
||||
kprintln!(" Free: {} MiB ({} frames)",
|
||||
stats.free_memory / (1024 * 1024),
|
||||
stats.free_frames
|
||||
);
|
||||
kprintln!(" Usage: {}%",
|
||||
(stats.used_memory * 100) / stats.total_memory
|
||||
);
|
||||
} else {
|
||||
kprintln!(" (PMM not initialized)");
|
||||
}
|
||||
|
||||
kprintln!();
|
||||
kprintln!("Virtual Memory Manager (VMM):");
|
||||
|
||||
let vmm_stats = vmm::get_vmm_stats();
|
||||
kprintln!(" Next Address: {:#018x}", vmm_stats.next_vaddr.as_u64());
|
||||
kprintln!(" Allocated: {} pages ({} KiB) in {} blocks",
|
||||
vmm_stats.allocated_pages,
|
||||
vmm_stats.allocated_pages * 4,
|
||||
vmm_stats.allocated_blocks_count
|
||||
);
|
||||
kprintln!(" Free: {} pages ({} KiB) in {} blocks",
|
||||
vmm_stats.free_pages,
|
||||
vmm_stats.free_pages * 4,
|
||||
vmm_stats.free_blocks_count
|
||||
);
|
||||
kprintln!(" New Usage: {} pages ({} KiB)",
|
||||
vmm_stats.used_from_new,
|
||||
vmm_stats.used_from_new * 4
|
||||
);
|
||||
|
||||
kprintln!();
|
||||
}
|
||||
|
||||
pub fn cmd_sysinfo() {
|
||||
kprintln!();
|
||||
kprintln!("=== System Information ===");
|
||||
kprintln!();
|
||||
|
||||
let mut brand = [0u8; 64];
|
||||
let cpu_brand = cpu::cpu_get_brand(&mut brand);
|
||||
kprintln!("CPU: {}", cpu_brand);
|
||||
|
||||
// 記憶體信息
|
||||
if let Some(stats) = pmm::get_memory_stats() {
|
||||
kprintln!("Memory: {} MiB / {} MiB free",
|
||||
stats.free_memory / (1024 * 1024),
|
||||
stats.total_memory / (1024 * 1024));
|
||||
kprintln!(" {}% used",
|
||||
(stats.used_memory * 100) / stats.total_memory);
|
||||
}
|
||||
|
||||
if let Some((base_freq, running_freq, ticks)) = timer::get_info() {
|
||||
kprintln!("Timer: {} Hz (base: {} Hz)", running_freq, base_freq);
|
||||
kprintln!("Ticks: {}", ticks);
|
||||
}
|
||||
|
||||
if let time = rtc::get_time() {
|
||||
kprintln!("Date/Time: {}", time.format());
|
||||
}
|
||||
|
||||
kprintln!();
|
||||
}
|
||||
|
||||
pub fn cmd_shutdown() {
|
||||
kprintln!("Shutdown system...");
|
||||
cpu::cpu_pause(1000);
|
||||
tty::tty::clear(0x000000);
|
||||
power::shutdown();
|
||||
}
|
||||
|
||||
pub fn cmd_reboot() {
|
||||
kprintln!("Rebooting system...");
|
||||
cpu::cpu_pause(1000);
|
||||
tty::tty::clear(0x000000);
|
||||
|
||||
power::reboot();
|
||||
}
|
||||
138
kernel/src/shell/math.rs
Normal file
138
kernel/src/shell/math.rs
Normal file
@ -0,0 +1,138 @@
|
||||
use alloc::string::String;
|
||||
use alloc::vec::Vec;
|
||||
use crate::kprint;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum CalcError {
|
||||
UnexpectedChar(char),
|
||||
UnexpectedEnd,
|
||||
DivisionByZero,
|
||||
InvalidNumber,
|
||||
}
|
||||
|
||||
pub fn eval_expression(expr: &str) -> Result<f64, CalcError> {
|
||||
let mut parser = Parser::new(expr);
|
||||
let result = parser.parse_expr()?;
|
||||
parser.skip_whitespace();
|
||||
if parser.pos < parser.chars.len() {
|
||||
Err(CalcError::UnexpectedChar(parser.chars[parser.pos]))
|
||||
} else {
|
||||
Ok(result)
|
||||
}
|
||||
}
|
||||
|
||||
struct Parser<'a> {
|
||||
chars: Vec<char>,
|
||||
pos: usize,
|
||||
_expr: &'a str,
|
||||
}
|
||||
|
||||
impl<'a> Parser<'a> {
|
||||
fn new(expr: &'a str) -> Self {
|
||||
Self {
|
||||
chars: expr.chars().collect(),
|
||||
pos: 0,
|
||||
_expr: expr,
|
||||
}
|
||||
}
|
||||
|
||||
fn peek(&self) -> Option<char> {
|
||||
self.chars.get(self.pos).cloned()
|
||||
}
|
||||
|
||||
fn next(&mut self) -> Option<char> {
|
||||
let c = self.chars.get(self.pos).cloned();
|
||||
if c.is_some() {
|
||||
self.pos += 1;
|
||||
}
|
||||
c
|
||||
}
|
||||
|
||||
fn skip_whitespace(&mut self) {
|
||||
while let Some(c) = self.peek() {
|
||||
if c.is_whitespace() {
|
||||
self.pos += 1;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
fn parse_number(&mut self) -> Result<f64, CalcError> {
|
||||
self.skip_whitespace();
|
||||
let start = self.pos;
|
||||
while let Some(c) = self.peek() {
|
||||
if c.is_ascii_digit() || c == '.' {
|
||||
self.pos += 1;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if start == self.pos {
|
||||
return Err(CalcError::InvalidNumber);
|
||||
}
|
||||
let s: String = self.chars[start..self.pos].iter().collect();
|
||||
s.parse::<f64>().map_err(|_| CalcError::InvalidNumber)
|
||||
}
|
||||
|
||||
fn parse_factor(&mut self) -> Result<f64, CalcError> {
|
||||
self.skip_whitespace();
|
||||
match self.peek() {
|
||||
Some('(') => {
|
||||
self.next();
|
||||
let val = self.parse_expr()?;
|
||||
self.skip_whitespace();
|
||||
if self.next() != Some(')') {
|
||||
return Err(CalcError::UnexpectedEnd);
|
||||
}
|
||||
Ok(val)
|
||||
}
|
||||
Some('-') => {
|
||||
self.next();
|
||||
Ok(-self.parse_factor()?)
|
||||
}
|
||||
_ => self.parse_number(),
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_term(&mut self) -> Result<f64, CalcError> {
|
||||
let mut val = self.parse_factor()?;
|
||||
loop {
|
||||
self.skip_whitespace();
|
||||
match self.peek() {
|
||||
Some('*') => {
|
||||
self.next();
|
||||
val *= self.parse_factor()?;
|
||||
}
|
||||
Some('/') => {
|
||||
self.next();
|
||||
let rhs = self.parse_factor()?;
|
||||
if rhs == 0.0 {
|
||||
return Err(CalcError::DivisionByZero);
|
||||
}
|
||||
val /= rhs;
|
||||
}
|
||||
_ => break,
|
||||
}
|
||||
}
|
||||
Ok(val)
|
||||
}
|
||||
|
||||
fn parse_expr(&mut self) -> Result<f64, CalcError> {
|
||||
let mut val = self.parse_term()?;
|
||||
loop {
|
||||
self.skip_whitespace();
|
||||
match self.peek() {
|
||||
Some('+') => {
|
||||
self.next();
|
||||
val += self.parse_term()?;
|
||||
}
|
||||
Some('-') => {
|
||||
self.next();
|
||||
val -= self.parse_term()?;
|
||||
}
|
||||
_ => break,
|
||||
}
|
||||
}
|
||||
Ok(val)
|
||||
}
|
||||
}
|
||||
66
kernel/src/shell/mod.rs
Normal file
66
kernel/src/shell/mod.rs
Normal file
@ -0,0 +1,66 @@
|
||||
// kernel/src/shell/mod.rs
|
||||
use alloc::string::String;
|
||||
use spin::Mutex;
|
||||
use crate::{kprint, kprintln, tty, hal::{rtc, timer}};
|
||||
|
||||
pub mod commands;
|
||||
pub mod math;
|
||||
|
||||
static COMMAND_BUFFER: Mutex<String> = Mutex::new(String::new());
|
||||
|
||||
pub fn init() {
|
||||
show_prompt();
|
||||
}
|
||||
|
||||
pub fn show_prompt() {
|
||||
tty::tty::write_str("cure > ", 0x00FF00);
|
||||
}
|
||||
|
||||
pub fn process_keyboard_char(c: char) {
|
||||
let mut buffer = COMMAND_BUFFER.lock();
|
||||
|
||||
if c == '\n' {
|
||||
let cmd = buffer.clone();
|
||||
buffer.clear();
|
||||
|
||||
kprintln!();
|
||||
execute_command(&cmd);
|
||||
show_prompt();
|
||||
} else if c == '\x08' {
|
||||
if !buffer.is_empty() {
|
||||
buffer.pop();
|
||||
kprint!("\x08 \x08");
|
||||
}
|
||||
} else if c.is_ascii_graphic() || c == ' ' {
|
||||
buffer.push(c);
|
||||
kprint!("{}", c);
|
||||
}
|
||||
}
|
||||
|
||||
fn execute_command(cmd: &str) {
|
||||
let mut cmd = cmd.trim();
|
||||
|
||||
if cmd.is_empty() {
|
||||
return;
|
||||
}
|
||||
|
||||
match cmd {
|
||||
"help" => commands::cmd_help(),
|
||||
"clear" | "clr" => commands::cmd_clear(),
|
||||
"time" => commands::cmd_time(),
|
||||
"uptime" => commands::cmd_uptime(),
|
||||
"sysinfo" | "sys" => commands::cmd_sysinfo(),
|
||||
"meminfo" | "mem" => commands::cmd_meminfo(),
|
||||
"reboot" => commands::cmd_reboot(),
|
||||
"halt" | "shutdown" | "poweroff" => commands::cmd_shutdown(),
|
||||
_ => {
|
||||
match math::eval_expression(cmd) {
|
||||
Ok(result) => kprintln!("{}", result),
|
||||
Err(_) => {
|
||||
kprintln!("Unknown command: '{}'", cmd);
|
||||
kprintln!("Type 'help' for available commands");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -128,6 +128,28 @@ impl TTYState {
|
||||
return;
|
||||
}
|
||||
|
||||
if c == '\x08' {
|
||||
if self.cursor_x >= CHAR_WIDTH {
|
||||
self.cursor_x -= CHAR_WIDTH;
|
||||
|
||||
for y in 0..CHAR_HEIGHT {
|
||||
for x in 0..CHAR_WIDTH {
|
||||
self.draw_pixel(self.cursor_x + x, self.cursor_y + y, 0x000000);
|
||||
}
|
||||
}
|
||||
} else if self.cursor_y >= CHAR_HEIGHT {
|
||||
self.cursor_y -= CHAR_HEIGHT;
|
||||
self.cursor_x = (self.info.width / CHAR_WIDTH - 1) * CHAR_WIDTH;
|
||||
|
||||
for y in 0..CHAR_HEIGHT {
|
||||
for x in 0..CHAR_WIDTH {
|
||||
self.draw_pixel(self.cursor_x + x, self.cursor_y + y, 0x000000);
|
||||
}
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if c == '\n' {
|
||||
self.cursor_x = 0;
|
||||
self.cursor_y += CHAR_HEIGHT;
|
||||
@ -160,6 +182,9 @@ impl TTYState {
|
||||
for x in 0..8 {
|
||||
if (row >> (7 - x)) & 1 == 1 {
|
||||
self.draw_pixel(cursor_x + x, cursor_y + y, color);
|
||||
} else {
|
||||
// 同時清除背景
|
||||
self.draw_pixel(cursor_x + x, cursor_y + y, 0x000000);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user