feat: Implement shutdown and restart
This commit is contained in:
parent
775626379e
commit
d11c7842d4
3
TODO.txt
3
TODO.txt
@ -23,7 +23,8 @@
|
||||
|
||||
|
||||
// BUG
|
||||
// - 在測試RTC中斷時 rtc::get_time() 處卡住
|
||||
// - ACPI 關機 mem 映射問題
|
||||
// - 關機重啟回收記憶體
|
||||
|
||||
// ==================== 高半核地址空間佈局 ====================
|
||||
//
|
||||
|
||||
@ -4,6 +4,7 @@ use x86_64::structures::idt::{InterruptStackFrame, PageFaultErrorCode};
|
||||
use x86_64::VirtAddr;
|
||||
use crate::{drivers, kprintln};
|
||||
use crate::{log_trace, log_debug, log_info, log_warn, log_error, log_fatal};
|
||||
use super::gdt;
|
||||
use crate::hal::{timer, cpu, lapic, rtc};
|
||||
use crate::mm::paging;
|
||||
|
||||
@ -32,21 +33,34 @@ pub extern "x86-interrupt" fn debug_handler(stack_frame: InterruptStackFrame) {
|
||||
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();
|
||||
}
|
||||
@ -56,7 +70,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();
|
||||
}
|
||||
@ -66,7 +85,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();
|
||||
}
|
||||
@ -76,7 +100,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();
|
||||
}
|
||||
@ -90,7 +119,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");
|
||||
}
|
||||
|
||||
@ -102,7 +137,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();
|
||||
}
|
||||
@ -127,11 +168,9 @@ pub extern "x86-interrupt" fn segment_not_present_handler(
|
||||
let index = (error_code >> 3) & 0x1FFF;
|
||||
|
||||
log_fatal!("Segment: {} index {:#x} (external: {})", table, index, is_external);
|
||||
log_fatal!("{:#?}", stack_frame);
|
||||
|
||||
log_fatal!("Current segments:");
|
||||
log_fatal!(" CS: {:#x}", crate::arch::amd64::gdt::kernel_code_selector().0);
|
||||
log_fatal!(" SS: {:#x}", crate::arch::amd64::gdt::kernel_data_selector().0);
|
||||
log_fatal!("CS: {:#x}", gdt::kernel_code_selector().0);
|
||||
log_fatal!("SS: {:#x}", gdt::kernel_data_selector().0);
|
||||
|
||||
loop {
|
||||
cpu::cpu_halt();
|
||||
@ -146,7 +185,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();
|
||||
}
|
||||
@ -160,7 +204,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();
|
||||
}
|
||||
@ -181,7 +230,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()),
|
||||
@ -197,7 +251,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();
|
||||
}
|
||||
@ -211,7 +270,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();
|
||||
}
|
||||
@ -221,15 +285,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();
|
||||
}
|
||||
@ -239,7 +313,12 @@ 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();
|
||||
}
|
||||
@ -247,7 +326,7 @@ pub extern "x86-interrupt" fn virtualization_handler(stack_frame: InterruptStack
|
||||
|
||||
// TODO Timer interrupt, Keyboard interrupt
|
||||
|
||||
pub extern "x86-interrupt" fn keyboard_interrupt_handler(stack_frame: InterruptStackFrame) {
|
||||
pub extern "x86-interrupt" fn keyboard_interrupt_handler(_stack_frame: InterruptStackFrame) {
|
||||
|
||||
unsafe {
|
||||
let mut port = Port::new(0x60);
|
||||
@ -259,7 +338,7 @@ pub extern "x86-interrupt" fn keyboard_interrupt_handler(stack_frame: InterruptS
|
||||
lapic::send_eoi();
|
||||
}
|
||||
|
||||
pub extern "x86-interrupt" fn default_irq_handler(stack_frame: InterruptStackFrame) {
|
||||
pub extern "x86-interrupt" fn default_irq_handler(_stack_frame: InterruptStackFrame) {
|
||||
lapic::send_eoi();
|
||||
log_trace!("Unhandled IRQ");
|
||||
}
|
||||
|
||||
148
kernel/src/hal/acpi/init.rs
Normal file
148
kernel/src/hal/acpi/init.rs
Normal file
@ -0,0 +1,148 @@
|
||||
use acpi::{aml, sdt, 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};
|
||||
use crate::hal::{cpu, io};
|
||||
use super::*;
|
||||
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
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 ACPI power management info...");
|
||||
// if let Some(power_info) = extract_power_info(&platform.tables) {
|
||||
// store_power_info(power_info);
|
||||
// } else {
|
||||
// log_warn!("Could not extract ACPI power info, shutdown may not work");
|
||||
// }
|
||||
|
||||
|
||||
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" });
|
||||
}
|
||||
|
||||
@ -1,13 +1,15 @@
|
||||
use acpi::{aml, AcpiTables, Handle, Handler, PciAddress, PhysicalMapping};
|
||||
use acpi::platform::{AcpiPlatform, interrupt::InterruptModel, PciConfigRegions};
|
||||
pub mod power;
|
||||
pub mod init;
|
||||
|
||||
use acpi::{aml, sdt, AcpiTables, Handle, Handler, PciAddress, PhysicalMapping};
|
||||
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};
|
||||
use crate::hal::{cpu, io};
|
||||
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct CureAcpiHandler {
|
||||
physical_memory_offset: u64,
|
||||
@ -23,6 +25,17 @@ pub struct AcpiInfo {
|
||||
pub io_apics: alloc::vec::Vec<(u64, u8, u32)>, // (address, id, gsi_base)
|
||||
}
|
||||
|
||||
/// ACPI 關機所需的信息
|
||||
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,
|
||||
}
|
||||
|
||||
static mut ACPI_POWER_INFO: Option<AcpiPowerInfo> = None;
|
||||
|
||||
impl CureAcpiHandler {
|
||||
pub const fn new(physical_memory_offset: u64) -> Self {
|
||||
Self {
|
||||
@ -170,7 +183,7 @@ impl Handler for CureAcpiHandler {
|
||||
fn stall(&self, _microseconds: u64) {
|
||||
// TODO: 實作微秒級延遲
|
||||
// 簡單的忙等待實作
|
||||
cpu::cpu_pause(_microseconds * 1000);
|
||||
cpu::cpu_pause(_microseconds * 1000);
|
||||
|
||||
}
|
||||
|
||||
@ -180,10 +193,10 @@ impl Handler for CureAcpiHandler {
|
||||
self.stall(_milliseconds * 1000);
|
||||
}
|
||||
|
||||
fn create_mutex(&self) -> acpi::Handle {
|
||||
fn create_mutex(&self) -> Handle {
|
||||
// TODO: 實作 Mutex
|
||||
// 目前返回一個假的 handle
|
||||
acpi::Handle(0)
|
||||
Handle(0)
|
||||
}
|
||||
|
||||
fn acquire(&self, mutex: Handle, timeout: u16) -> Result<(), aml::AmlError> {
|
||||
@ -192,134 +205,7 @@ impl Handler for CureAcpiHandler {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn release(&self, _handle: acpi::Handle) {
|
||||
fn release(&self, _handle: Handle) {
|
||||
// TODO: 實作 Mutex 釋放
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
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" });
|
||||
}
|
||||
336
kernel/src/hal/acpi/power.rs
Normal file
336
kernel/src/hal/acpi/power.rs
Normal file
@ -0,0 +1,336 @@
|
||||
use acpi::{aml, sdt, AcpiTables, Handle, Handler, PciAddress, PhysicalMapping};
|
||||
use acpi::platform::{AcpiPlatform, interrupt::InterruptModel, PciConfigRegions};
|
||||
use core::mem;
|
||||
use crate::kprintln;
|
||||
use crate::{log_trace, log_debug, log_info, log_warn, log_error, log_fatal};
|
||||
use crate::hal::{cpu, io};
|
||||
use crate::mm::vma;
|
||||
use super::*;
|
||||
|
||||
|
||||
/// Extract shutdown information from ACPI table
|
||||
pub fn extract_power_info(tables: &AcpiTables<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;
|
||||
}
|
||||
};
|
||||
|
||||
log_debug!("FADT found");
|
||||
|
||||
// Get the control block address and DSDT address from FADT
|
||||
unsafe {
|
||||
// Get the raw pointer of FADT to read the fields manually
|
||||
let fadt_ptr = (&*fadt as *const sdt::fadt::Fadt) as *const u8;
|
||||
|
||||
// FADT structure offset
|
||||
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);
|
||||
|
||||
// Read DSDT address
|
||||
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);
|
||||
|
||||
// Parse the _S5 object
|
||||
let (slp_typa, slp_typb) = match parse_s5_object(dsdt_address) {
|
||||
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) -> Option<(u16, u16)> {
|
||||
let dsdt_virt_addr = vma::phys_to_virt(dsdt_phys_addr);
|
||||
|
||||
unsafe {
|
||||
let dsdt_ptr = dsdt_virt_addr.as_ptr::<u8>();
|
||||
|
||||
// DSDT header
|
||||
let signature = core::slice::from_raw_parts(dsdt_ptr, 4);
|
||||
if signature != b"DSDT" {
|
||||
log_error!("Invalid DSDT signature");
|
||||
return None;
|
||||
}
|
||||
|
||||
// Get DSDT length
|
||||
let length = core::ptr::read_unaligned(dsdt_ptr.add(4) as *const u32);
|
||||
log_debug!("DSDT length: {} bytes", length);
|
||||
|
||||
// Search for the "_S5_" string in DSDT
|
||||
let dsdt_data = core::slice::from_raw_parts(dsdt_ptr, length as usize);
|
||||
|
||||
// Byte representation of "_S5_" in AML
|
||||
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);
|
||||
|
||||
// Parse the _S5 package
|
||||
// Typical AML bytecode:
|
||||
// Name(_S5, Package() {...})
|
||||
// Or: 08 5F 53 35 5F 12 [pkg_length] [num_elements] ...
|
||||
|
||||
let mut offset = i + 4;
|
||||
|
||||
// Skip possible NameOp (0x08)
|
||||
if offset < dsdt_data.len() && dsdt_data[offset] == 0x08 {
|
||||
offset += 1;
|
||||
}
|
||||
|
||||
// Find PackageOp (0x12)
|
||||
while offset < dsdt_data.len() && dsdt_data[offset] != 0x12 {
|
||||
offset += 1;
|
||||
if offset - i > 16 {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if offset >= dsdt_data.len() {
|
||||
log_warn!("PackageOp not found after _S5");
|
||||
continue;
|
||||
}
|
||||
|
||||
offset += 1; // 跳過 PackageOp
|
||||
|
||||
// Parse PkgLength
|
||||
let pkg_length = parse_pkg_length(&dsdt_data[offset..]);
|
||||
offset += get_pkg_length_size(&dsdt_data[offset..]);
|
||||
|
||||
// NumElements
|
||||
let num_elements = dsdt_data[offset];
|
||||
offset += 1;
|
||||
|
||||
log_debug!("Package length: {}, elements: {}", pkg_length, num_elements);
|
||||
|
||||
if num_elements < 2 {
|
||||
log_warn!("_S5 package has less than 2 elements");
|
||||
continue;
|
||||
}
|
||||
|
||||
// Extract SLP_TYPa
|
||||
let slp_typa = parse_aml_integer(&dsdt_data[offset..]).unwrap_or(0);
|
||||
offset += get_aml_integer_size(&dsdt_data[offset..]);
|
||||
|
||||
// Extract SLP_TYPb
|
||||
let slp_typb = parse_aml_integer(&dsdt_data[offset..]).unwrap_or(0);
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
/// 解析 AML 整數
|
||||
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
|
||||
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],
|
||||
]))
|
||||
}
|
||||
_ => 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 => 1,
|
||||
0x0A => 2,
|
||||
0x0B => 3,
|
||||
0x0C => 5,
|
||||
0x0E => 9,
|
||||
_ => 1,
|
||||
}
|
||||
}
|
||||
|
||||
/// Perform ACPI shutdown
|
||||
pub fn acpi_shutdown() -> ! {
|
||||
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 fallback_shutdown();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
// 等待關機
|
||||
for _ in 0..1000000 {
|
||||
cpu::cpu_pause(100);
|
||||
}
|
||||
}
|
||||
|
||||
log_error!("ACPI shutdown failed!");
|
||||
fallback_shutdown()
|
||||
}
|
||||
|
||||
/// Backup shutdown method
|
||||
fn fallback_shutdown() -> ! {
|
||||
log_warn!("Using fallback shutdown methods...");
|
||||
|
||||
unsafe {
|
||||
// QEMU
|
||||
io::io_port_ww(0x604, 0x2000);
|
||||
cpu::cpu_pause(10000);
|
||||
|
||||
// Bochs
|
||||
for &c in b"Shutdown" {
|
||||
io::io_port_wb(0x8900, c);
|
||||
}
|
||||
cpu::cpu_pause(10000);
|
||||
|
||||
// VirtualBox
|
||||
io::io_port_ww(0x4004, 0x3400);
|
||||
}
|
||||
|
||||
log_error!("All shutdown methods failed!");
|
||||
|
||||
loop {
|
||||
cpu::cpu_halt();
|
||||
}
|
||||
}
|
||||
|
||||
/// Store ACPI shutdown information
|
||||
pub fn store_power_info(info: AcpiPowerInfo) {
|
||||
unsafe {
|
||||
ACPI_POWER_INFO = Some(info);
|
||||
}
|
||||
log_info!("ACPI power info stored successfully");
|
||||
}
|
||||
@ -4,4 +4,5 @@ pub mod acpi;
|
||||
pub mod rtc;
|
||||
pub mod lapic;
|
||||
pub mod ioapic;
|
||||
pub mod timer;
|
||||
pub mod timer;
|
||||
pub mod power;
|
||||
222
kernel/src/hal/power.rs
Normal file
222
kernel/src/hal/power.rs
Normal file
@ -0,0 +1,222 @@
|
||||
// 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 設備
|
||||
io::io_port_ww(0x604, 0x2000);
|
||||
io::io_port_rl(0x501);
|
||||
}
|
||||
|
||||
ShutdownMethod::BochsExit => {
|
||||
// Bochs 專用關機端口
|
||||
io::io_port_ww(0xB004, 0x2000);
|
||||
}
|
||||
|
||||
ShutdownMethod::VirtualBox => {
|
||||
// VirtualBox 關機端口
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/// 重啟系統
|
||||
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 shutdown method: {:?}", method);
|
||||
try_reboot(*method);
|
||||
|
||||
log_warn!("{:?} shutdown 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_reboot()
|
||||
}
|
||||
|
||||
RebootMethod::BootKBD => {
|
||||
keyboard_controller_reboot()
|
||||
}
|
||||
|
||||
RebootMethod::BootCF9 => {
|
||||
pci_reboot()
|
||||
}
|
||||
RebootMethod::BootEFI => {
|
||||
efi_reboot()
|
||||
}
|
||||
|
||||
RebootMethod::Boot92h => {
|
||||
cpu_reset()
|
||||
}
|
||||
}
|
||||
|
||||
cpu::cpu_pause(10000);
|
||||
}
|
||||
|
||||
fn acpi_reboot() {
|
||||
log_debug!("Trying ACPI reboot...");
|
||||
|
||||
unsafe {
|
||||
// ACPI FADT 的 RESET_REG
|
||||
// 需要從 ACPI 表中讀取實際地址
|
||||
// 這裡使用常見的地址作為示例
|
||||
io::io_port_wb(0xCF9, 0x06);
|
||||
cpu::cpu_pause(10000);
|
||||
}
|
||||
}
|
||||
|
||||
fn keyboard_controller_reboot() {
|
||||
log_debug!("Trying 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);
|
||||
}
|
||||
}
|
||||
|
||||
fn pci_reboot() {
|
||||
log_debug!("Trying 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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fn efi_reboot() {
|
||||
log_debug!("Trying EFI runtime services reboot...");
|
||||
|
||||
// TODO: 實現 EFI ResetSystem 調用
|
||||
}
|
||||
|
||||
fn cpu_reset() {
|
||||
log_debug!("Trying CPU reset via port 92h...");
|
||||
|
||||
unsafe {
|
||||
let mut val = io::io_port_rb(0x92);
|
||||
val &= !0x01; // 清除快速 A20 位
|
||||
val |= 0x01; // 設置重置位
|
||||
io::io_port_wb(0x92, val);
|
||||
|
||||
cpu::cpu_pause(100000);
|
||||
}
|
||||
}
|
||||
@ -38,7 +38,7 @@ static TICK_COUNTER: AtomicU64 = AtomicU64::new(0);
|
||||
// Calibration flag
|
||||
static IS_CALIBRATING: AtomicBool = AtomicBool::new(false);
|
||||
|
||||
static TIMEOUT: AtomicU64 = AtomicU64::new(0);
|
||||
static TIMEOUT: AtomicU64 = AtomicU64::new(100_000_000);
|
||||
|
||||
struct TimerConfig {
|
||||
base_frequency: u32,
|
||||
@ -143,15 +143,14 @@ pub fn init(target_frequency: u32, apic_id: u8) -> bool {
|
||||
|
||||
cpu::cpu_enable_interrupts();
|
||||
|
||||
let mut timeout = 100_000_000;
|
||||
while !unsafe { CALIBRATION.done } && timeout > 0 {
|
||||
let mut remaining = TIMEOUT.load(Ordering::Relaxed);
|
||||
while !unsafe { CALIBRATION.done } && remaining > 0 {
|
||||
cpu::cpu_pause(0);
|
||||
timeout -= 1;
|
||||
remaining -= 1;
|
||||
}
|
||||
|
||||
cpu::cpu_disable_interrupts();
|
||||
|
||||
if timeout == 0 {
|
||||
if remaining == 0 {
|
||||
log_error!("Calibration timeout!");
|
||||
IS_CALIBRATING.store(false, Ordering::SeqCst);
|
||||
return false;
|
||||
|
||||
@ -94,14 +94,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();
|
||||
|
||||
@ -144,8 +140,8 @@ fn _acpi_init(rsdp_addr: Option<u64>, physical_memory_offset: u64) -> Option<acp
|
||||
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");
|
||||
@ -284,9 +280,7 @@ pub fn _kernel_init(boot_info: &'static mut BootInfo) -> ! {
|
||||
_boot_report(&boot_info.memory_regions, physical_memory_offset);
|
||||
|
||||
kprintln!();
|
||||
kprintln!("========================================");
|
||||
kprintln!(" Kernel Initialization Complete! ");
|
||||
kprintln!("========================================");
|
||||
log_info!("System initialization complete!");
|
||||
kprintln!();
|
||||
|
||||
k_main::_kernel_main();
|
||||
|
||||
@ -30,11 +30,7 @@ pub fn _kernel_main() -> ! {
|
||||
if let time = rtc::get_time() {
|
||||
kprintln!("Date/Time: {}", time.format());
|
||||
}
|
||||
|
||||
log_info!("{}", cpu::cpu_rdtscp()) ;
|
||||
kprintln!();
|
||||
|
||||
log_info!("System initialization complete!");
|
||||
|
||||
kprintln!();
|
||||
kprintln!("Type 'help' for available commands");
|
||||
kprintln!();
|
||||
|
||||
@ -1,15 +1,17 @@
|
||||
// kernel/src/shell/commands.rs
|
||||
use crate::{kprintln, tty, hal::{rtc, timer, cpu}, mm::allocator::pmm, log_info};
|
||||
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 - Clear the screen");
|
||||
kprintln!(" time - Display current date and time");
|
||||
kprintln!(" uptime - Show system uptime");
|
||||
kprintln!(" sysinfo - Display system information");
|
||||
kprintln!(" reboot - Reboot the system");
|
||||
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!();
|
||||
}
|
||||
|
||||
@ -51,6 +53,93 @@ pub fn cmd_echo(text: &str) {
|
||||
}
|
||||
}
|
||||
|
||||
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 ===");
|
||||
@ -81,58 +170,17 @@ pub fn cmd_sysinfo() {
|
||||
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);
|
||||
|
||||
log_info!("Disabling CPU interrupts");
|
||||
cpu::cpu_disable_interrupts();
|
||||
|
||||
log_info!("Disabling RTC Timer");
|
||||
rtc::disable_timer();
|
||||
|
||||
unsafe {
|
||||
io_port_wb(0x64, 0xFE);
|
||||
|
||||
log_info!("Complete");
|
||||
|
||||
loop {
|
||||
cpu::cpu_halt();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn cmd_halt() {
|
||||
kprintln!("System Statistics:");
|
||||
|
||||
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!(" Uptime: {}h {}m {}s", hours, minutes, seconds);
|
||||
|
||||
if let Some(stats) = pmm::get_memory_stats() {
|
||||
kprintln!(" Memory Used: {} MiB / {} MiB",
|
||||
stats.used_memory / (1024 * 1024),
|
||||
stats.total_memory / (1024 * 1024));
|
||||
}
|
||||
|
||||
if let time = rtc::get_time() {
|
||||
kprintln!(" Shutdown at: {}", time.format());
|
||||
}
|
||||
|
||||
kprintln!();
|
||||
kprintln!("System halted. Safe to power off.");
|
||||
kprintln!();
|
||||
|
||||
cpu::cpu_disable_interrupts();
|
||||
|
||||
rtc::disable_timer();
|
||||
|
||||
loop {
|
||||
cpu::cpu_halt();
|
||||
}
|
||||
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)
|
||||
}
|
||||
}
|
||||
@ -4,6 +4,7 @@ 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());
|
||||
|
||||
@ -12,7 +13,7 @@ pub fn init() {
|
||||
}
|
||||
|
||||
pub fn show_prompt() {
|
||||
kprint!("cure > ");
|
||||
tty::tty::write_str("cure > ", 0x00FF00);
|
||||
}
|
||||
|
||||
pub fn process_keyboard_char(c: char) {
|
||||
@ -37,23 +38,29 @@ pub fn process_keyboard_char(c: char) {
|
||||
}
|
||||
|
||||
fn execute_command(cmd: &str) {
|
||||
let cmd = cmd.trim();
|
||||
let mut cmd = cmd.trim();
|
||||
|
||||
if cmd.is_empty() {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
match cmd {
|
||||
"help" => commands::cmd_help(),
|
||||
"clear" => commands::cmd_clear(),
|
||||
"clear" | "clr" => commands::cmd_clear(),
|
||||
"time" => commands::cmd_time(),
|
||||
"uptime" => commands::cmd_uptime(),
|
||||
"sysinfo" => commands::cmd_sysinfo(),
|
||||
"sysinfo" | "sys" => commands::cmd_sysinfo(),
|
||||
"meminfo" | "mem" => commands::cmd_meminfo(),
|
||||
"reboot" => commands::cmd_reboot(),
|
||||
"halt" | "shutdown" | "poweroff" => commands::cmd_halt(),
|
||||
"halt" | "shutdown" | "poweroff" => commands::cmd_shutdown(),
|
||||
_ => {
|
||||
kprintln!("Unknown command: '{}'", cmd);
|
||||
kprintln!("Type 'help' for available commands");
|
||||
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