feat: ACPI reboot implementation
This commit is contained in:
parent
687c59f467
commit
8bae31852d
3
TODO.txt
3
TODO.txt
@ -22,8 +22,7 @@
|
||||
// - 用戶態/內核態切換
|
||||
|
||||
|
||||
// BUG
|
||||
// - ACPI 關機 mem 映射問題
|
||||
// 5. 回收
|
||||
// - 關機重啟回收記憶體
|
||||
|
||||
// ==================== 高半核地址空間佈局 ====================
|
||||
|
||||
@ -3,9 +3,10 @@ use x86_64::instructions::port::Port;
|
||||
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 crate::{log_debug, log_error, log_fatal, log_info, log_trace, log_warn};
|
||||
use super::gdt;
|
||||
use crate::hal::{timer, cpu, lapic, rtc};
|
||||
use crate::hal::{cpu, rtc, timer};
|
||||
use crate::hal::apic::lapic;
|
||||
use crate::mm::paging;
|
||||
|
||||
/// Divide Error (#DE)
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
use super::power::{extract_power_info, store_power_info};
|
||||
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;
|
||||
@ -126,6 +126,14 @@ pub fn init(rsdp_addr: u64, physical_memory_offset: u64) -> Option<AcpiInfo> {
|
||||
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 {
|
||||
|
||||
@ -29,6 +29,16 @@ pub struct AcpiPowerInfo {
|
||||
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 {
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
use super::{AcpiPowerInfo, CureAcpiHandler, ACPI_POWER_INFO};
|
||||
use super::{AcpiPowerInfo, CureAcpiHandler, ResetRegister, ACPI_POWER_INFO, ACPI_RESET_REG};
|
||||
use crate::hal::{cpu, io};
|
||||
use crate::kprintln;
|
||||
use crate::mm::vma;
|
||||
@ -83,10 +83,7 @@ fn parse_s5_object(dsdt_phys_addr: u64, handler: &CureAcpiHandler) -> Option<(u1
|
||||
|
||||
// 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>(),
|
||||
)
|
||||
handler.map_physical_region::<SdtHeader>(dsdt_phys_addr as usize, size_of::<SdtHeader>())
|
||||
};
|
||||
|
||||
if dsdt_header_mapping.signature != Signature::DSDT {
|
||||
@ -108,7 +105,7 @@ fn parse_s5_object(dsdt_phys_addr: u64, handler: &CureAcpiHandler) -> Option<(u1
|
||||
let dsdt_ptr = dsdt_mapping.virtual_start.as_ptr();
|
||||
let dsdt_data = core::slice::from_raw_parts(dsdt_ptr, dsdt_length);
|
||||
|
||||
// 在 DSDT 中搜索 "_S5_"
|
||||
// Search DSDT for "_S5_"
|
||||
let s5_name = b"_S5_";
|
||||
|
||||
for i in 0..(dsdt_data.len() - 4) {
|
||||
@ -121,7 +118,7 @@ fn parse_s5_object(dsdt_phys_addr: u64, handler: &CureAcpiHandler) -> Option<(u1
|
||||
let mut offset = i + 4; // Skip "_S5_"
|
||||
|
||||
// Skip any intermediate bytes and go straight to PackageOp
|
||||
let search_limit = offset + 8;
|
||||
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);
|
||||
@ -248,15 +245,16 @@ fn get_pkg_length_size(data: &[u8]) -> usize {
|
||||
1 + byte_count
|
||||
}
|
||||
|
||||
/// 解析 AML 整數
|
||||
/// 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
|
||||
0x00 => Some(0), // ZeroOp
|
||||
0x01 => Some(1), // OneOp
|
||||
0xFF => Some(0xFFFFFFFF), // OnesOp
|
||||
0x0A => {
|
||||
// BytePrefix
|
||||
if data.len() < 2 {
|
||||
@ -287,7 +285,10 @@ fn parse_aml_integer(data: &[u8]) -> Option<u64> {
|
||||
data[1], data[2], data[3], data[4], data[5], data[6], data[7], data[8],
|
||||
]))
|
||||
}
|
||||
_ => None,
|
||||
_ => {
|
||||
log_warn!("Unknown AML integer prefix: {:#02x}", data[0]);
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -298,17 +299,17 @@ fn get_aml_integer_size(data: &[u8]) -> usize {
|
||||
}
|
||||
|
||||
match data[0] {
|
||||
0x00 | 0x01 => 1,
|
||||
0x0A => 2,
|
||||
0x0B => 3,
|
||||
0x0C => 5,
|
||||
0x0E => 9,
|
||||
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() -> ! {
|
||||
pub fn acpi_shutdown() -> bool {
|
||||
log_info!("Attempting ACPI shutdown...");
|
||||
|
||||
let power_info = unsafe {
|
||||
@ -316,7 +317,7 @@ pub fn acpi_shutdown() -> ! {
|
||||
Some(info) => info,
|
||||
None => {
|
||||
log_error!("ACPI power info not initialized!");
|
||||
return fallback_shutdown();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
};
|
||||
@ -344,40 +345,11 @@ pub fn acpi_shutdown() -> ! {
|
||||
io::io_port_ww(power_info.pm1b_control_block as u16, slp_cmd_b);
|
||||
}
|
||||
|
||||
// 等待關機
|
||||
for _ in 0..1000000 {
|
||||
cpu::cpu_pause(100);
|
||||
}
|
||||
cpu::cpu_pause(10000);
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
/// Store ACPI shutdown information
|
||||
@ -387,3 +359,181 @@ pub fn store_power_info(info: AcpiPowerInfo) {
|
||||
}
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,106 +1,8 @@
|
||||
// kernel/src/hal/ioapic.rs - 無鎖設計
|
||||
// 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;
|
||||
use crate::{log_trace, log_debug, log_info, log_warn, log_error};
|
||||
|
||||
/// 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,
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/// Initialize a single IO APIC (using mapped virtual addresses)
|
||||
///
|
||||
@ -305,4 +207,4 @@ pub fn print_info() {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,68 +1,11 @@
|
||||
// kernel/src/hal/lapic.rs
|
||||
|
||||
use x86_64::VirtAddr;
|
||||
use crate::{log_trace, log_debug, log_info, log_warn, log_error};
|
||||
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;
|
||||
|
||||
/// 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,
|
||||
}
|
||||
}
|
||||
}
|
||||
/// Read APIC registers
|
||||
///
|
||||
/// # Safety
|
||||
@ -124,10 +67,10 @@ pub unsafe fn write_apic_reg_raw(offset: u32, value: u32) -> bool {
|
||||
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);
|
||||
|
||||
// 讀取 APIC 信息
|
||||
// Read APIC information
|
||||
let id_reg = read_apic_reg(ApicRegister::Id).unwrap();
|
||||
let version_reg = read_apic_reg(ApicRegister::Version).unwrap();
|
||||
|
||||
@ -181,9 +124,7 @@ pub fn send_eoi() {
|
||||
/// Get the Local APIC ID
|
||||
#[inline]
|
||||
pub fn get_apic_id() -> Option<u32> {
|
||||
unsafe {
|
||||
Some(APIC_INFO.id)
|
||||
}
|
||||
unsafe { Some(APIC_INFO.id) }
|
||||
}
|
||||
|
||||
/// Get the Local APIC base virtual address
|
||||
@ -220,7 +161,6 @@ pub fn get_max_lvt() -> Option<u32> {
|
||||
///
|
||||
/// 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
|
||||
@ -253,4 +193,4 @@ pub fn print_info() {
|
||||
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);
|
||||
}
|
||||
}
|
||||
@ -2,7 +2,6 @@ pub mod io;
|
||||
pub mod cpu;
|
||||
pub mod acpi;
|
||||
pub mod rtc;
|
||||
pub mod lapic;
|
||||
pub mod ioapic;
|
||||
pub mod timer;
|
||||
pub mod power;
|
||||
pub mod power;
|
||||
pub mod apic;
|
||||
@ -71,18 +71,18 @@ fn try_shutdown(method: ShutdownMethod) {
|
||||
}
|
||||
|
||||
ShutdownMethod::QemuExit => {
|
||||
// QEMU isa-debug-exit 設備
|
||||
// QEMU isa-debug-exit device
|
||||
io::io_port_ww(0x604, 0x2000);
|
||||
io::io_port_rl(0x501);
|
||||
}
|
||||
|
||||
ShutdownMethod::BochsExit => {
|
||||
// Bochs 專用關機端口
|
||||
// Bochs dedicated shutdown port
|
||||
io::io_port_ww(0xB004, 0x2000);
|
||||
}
|
||||
|
||||
ShutdownMethod::VirtualBox => {
|
||||
// VirtualBox 關機端口
|
||||
// VirtualBox shutdown port
|
||||
io::io_port_ww(0x4004, 0x3400);
|
||||
}
|
||||
|
||||
@ -100,7 +100,7 @@ fn try_shutdown(method: ShutdownMethod) {
|
||||
}
|
||||
}
|
||||
|
||||
/// 重啟系統
|
||||
/// Restart the system
|
||||
pub fn reboot() -> ! {
|
||||
log_info!("Rebooting system...");
|
||||
|
||||
@ -119,10 +119,10 @@ pub fn reboot() -> ! {
|
||||
];
|
||||
|
||||
for method in methods.iter() {
|
||||
log_debug!("Trying shutdown method: {:?}", method);
|
||||
log_debug!("Trying reboot method: {:?}", method);
|
||||
try_reboot(*method);
|
||||
|
||||
log_warn!("{:?} shutdown failed", method);
|
||||
log_warn!("{:?} reboot failed", method);
|
||||
cpu::cpu_pause(1000);
|
||||
}
|
||||
|
||||
@ -137,42 +137,30 @@ pub fn reboot() -> ! {
|
||||
fn try_reboot(method: RebootMethod) {
|
||||
match method {
|
||||
RebootMethod::BootACPI => {
|
||||
acpi_reboot()
|
||||
acpi::power::acpi_reset_reg_reboot();
|
||||
}
|
||||
|
||||
RebootMethod::BootKBD => {
|
||||
keyboard_controller_reboot()
|
||||
keyboard_controller_reboot();
|
||||
}
|
||||
|
||||
RebootMethod::BootCF9 => {
|
||||
pci_reboot()
|
||||
pci_reboot();
|
||||
}
|
||||
RebootMethod::BootEFI => {
|
||||
efi_reboot()
|
||||
efi_reboot();
|
||||
}
|
||||
|
||||
RebootMethod::Boot92h => {
|
||||
cpu_reset()
|
||||
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...");
|
||||
fn keyboard_controller_reboot() -> bool {
|
||||
log_debug!("keyboard controller reboot...");
|
||||
|
||||
unsafe {
|
||||
for _ in 0..1000 {
|
||||
@ -186,10 +174,12 @@ fn keyboard_controller_reboot() {
|
||||
|
||||
cpu::cpu_pause(100000);
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
fn pci_reboot() {
|
||||
log_debug!("Trying PCI reboot...");
|
||||
fn pci_reboot() -> bool {
|
||||
log_debug!("PCI reboot...");
|
||||
|
||||
unsafe {
|
||||
let mut val = io::io_port_rb(0xCF9) & !0x06;
|
||||
@ -199,24 +189,29 @@ fn pci_reboot() {
|
||||
|
||||
cpu::cpu_pause(100000);
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
|
||||
fn efi_reboot() {
|
||||
fn efi_reboot() -> bool {
|
||||
log_debug!("Trying EFI runtime services reboot...");
|
||||
|
||||
// TODO: 實現 EFI ResetSystem 調用
|
||||
false
|
||||
}
|
||||
|
||||
fn cpu_reset() {
|
||||
log_debug!("Trying CPU reset via port 92h...");
|
||||
fn cpu_reset() -> bool {
|
||||
log_debug!("CPU reset via port 92h...");
|
||||
|
||||
unsafe {
|
||||
let mut val = io::io_port_rb(0x92);
|
||||
val &= !0x01; // 清除快速 A20 位
|
||||
val |= 0x01; // 設置重置位
|
||||
val &= !0x01; // Clear Fast A20 Bit
|
||||
val |= 0x01; // Set the reset bit
|
||||
io::io_port_wb(0x92, val);
|
||||
|
||||
cpu::cpu_pause(100000);
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
@ -1,8 +1,9 @@
|
||||
// kernel/src/hal/timer
|
||||
|
||||
use crate::hal::{lapic, rtc, cpu, ioapic};
|
||||
use core::sync::atomic::{AtomicU64, AtomicBool, Ordering};
|
||||
use crate::{log_info, log_debug, log_warn, log_error};
|
||||
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;
|
||||
|
||||
@ -3,7 +3,7 @@ use bootloader_api::BootInfo;
|
||||
use bootloader_api::info::MemoryRegionKind;
|
||||
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;
|
||||
@ -12,7 +12,8 @@ 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::drivers::keyboard;
|
||||
use crate::hal::{acpi, timer, cpu, ioapic, lapic, rtc};
|
||||
use crate::hal::{acpi, cpu, rtc, timer};
|
||||
use crate::hal::apic::{ioapic, lapic};
|
||||
use crate::hal::cpu::cpu_enable_interrupts;
|
||||
|
||||
fn _logger_init() {
|
||||
@ -294,5 +295,5 @@ pub fn _kernel_init(boot_info: &'static mut BootInfo) -> ! {
|
||||
pub fn kernel_emergency_cleanup() {
|
||||
log_error!("Emergency cleanup triggered");
|
||||
// 在 panic 前調用,做最後的清理工作
|
||||
// 比如刷新緩衝區、保存日誌等
|
||||
// 刷新緩衝區、保存日誌等
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user