//! # ACPI //! Code to parse the ACPI tables use core::intrinsics::{atomic_load, atomic_store}; use interrupt; use memory::{allocate_frame, Frame}; use paging::{entry, ActivePageTable, Page, PhysicalAddress, VirtualAddress}; use start::kstart_ap; use self::local_apic::{LocalApic, LocalApicIcr}; use self::madt::{Madt, MadtEntry}; use self::rsdt::Rsdt; use self::sdt::Sdt; use self::xsdt::Xsdt; pub mod local_apic; pub mod madt; pub mod rsdt; pub mod sdt; pub mod xsdt; const TRAMPOLINE: usize = 0x7E00; const AP_STARTUP: usize = 0x8000; pub fn init_sdt(sdt: &'static Sdt, active_table: &mut ActivePageTable) { print!(" "); for &c in sdt.signature.iter() { print!("{}", c as char); } println!(":"); if let Some(madt) = Madt::new(sdt) { println!(" {:>016X}: {}", madt.local_address, madt.flags); let mut local_apic = LocalApic::new(); let me = local_apic.id() as u8; for madt_entry in madt.iter() { println!(" {:?}", madt_entry); match madt_entry { MadtEntry::LocalApic(asp_local_apic) => if asp_local_apic.id == me { println!(" This is my local APIC"); } else { if asp_local_apic.flags & 1 == 1 { // Map trampoline { if active_table.translate_page(Page::containing_address(VirtualAddress::new(TRAMPOLINE))).is_none() { active_table.identity_map(Frame::containing_address(PhysicalAddress::new(TRAMPOLINE)), entry::PRESENT | entry::WRITABLE); } } // Allocate a stack // TODO: Allocate contiguous let stack_start = allocate_frame().expect("no more frames").start_address().get(); for _i in 0..62 { allocate_frame().expect("no more frames"); } let stack_end = allocate_frame().expect("no more frames").start_address().get() + 4096; let ap_ready = TRAMPOLINE as *mut u64; let ap_stack_start = unsafe { ap_ready.offset(1) }; let ap_stack_end = unsafe { ap_ready.offset(2) }; let ap_code = unsafe { ap_ready.offset(3) }; // Set the ap_ready to 0, volatile unsafe { atomic_store(ap_ready, 0) }; unsafe { atomic_store(ap_stack_start, stack_start as u64) }; unsafe { atomic_store(ap_stack_end, stack_end as u64) }; unsafe { atomic_store(ap_code, kstart_ap as u64) }; // Send INIT IPI { let icr = 0x00004500 | (asp_local_apic.id as u64) << 32; println!(" Sending IPI to {}: {:>016X} {:?}", asp_local_apic.id, icr, LocalApicIcr::from_bits(icr)); local_apic.set_icr(icr); } // Send START IPI { let ap_segment = (AP_STARTUP >> 12) & 0xFF; let icr = 0x00004600 | ((asp_local_apic.id as u64) << 32) | ap_segment as u64; //Start at 0x0800:0000 => 0x8000. Hopefully the bootloader code is still there println!(" Sending SIPI to {}: {:>016X} {:?}", asp_local_apic.id, icr, LocalApicIcr::from_bits(icr)); local_apic.set_icr(icr); } // Wait for trampoline ready println!(" Waiting for AP {}", asp_local_apic.id); while unsafe { atomic_load(ap_ready) } == 0 { interrupt::pause(); } println!(" AP {} is ready!", asp_local_apic.id); } else { println!(" CPU Disabled"); } }, _ => () } } }else { println!(" {:?}", sdt); } } /// Parse the ACPI tables to gather CPU, interrupt, and timer information pub unsafe fn init(active_table: &mut ActivePageTable) -> Option<Acpi> { let start_addr = 0xE0000; let end_addr = 0xFFFFF; // Map all of the ACPI table space { let start_frame = Frame::containing_address(PhysicalAddress::new(start_addr)); let end_frame = Frame::containing_address(PhysicalAddress::new(end_addr)); for frame in Frame::range_inclusive(start_frame, end_frame) { if active_table.translate_page(Page::containing_address(VirtualAddress::new(frame.start_address().get()))).is_none() { active_table.identity_map(frame, entry::PRESENT | entry::NO_EXECUTE); } } } // Search for RSDP if let Some(rsdp) = RSDP::search(start_addr, end_addr) { println!("{:?}", rsdp); let get_sdt = |sdt_address: usize, active_table: &mut ActivePageTable| -> &'static Sdt { if active_table.translate_page(Page::containing_address(VirtualAddress::new(sdt_address))).is_none() { let sdt_frame = Frame::containing_address(PhysicalAddress::new(sdt_address)); active_table.identity_map(sdt_frame, entry::PRESENT | entry::NO_EXECUTE); } &*(sdt_address as *const Sdt) }; let rxsdt = get_sdt(rsdp.sdt_address(), active_table); for &c in rxsdt.signature.iter() { print!("{}", c as char); } println!(":"); if let Some(rsdt) = Rsdt::new(rxsdt) { for sdt_address in rsdt.iter() { let sdt = get_sdt(sdt_address, active_table); init_sdt(sdt, active_table); } } else if let Some(xsdt) = Xsdt::new(rxsdt) { for sdt_address in xsdt.iter() { let sdt = get_sdt(sdt_address, active_table); init_sdt(sdt, active_table); } } else { println!("UNKNOWN RSDT OR XSDT SIGNATURE"); } } else { println!("NO RSDP FOUND"); } None } pub struct Acpi; /// RSDP #[derive(Copy, Clone, Debug)] #[repr(packed)] pub struct RSDP { signature: [u8; 8], checksum: u8, oemid: [u8; 6], revision: u8, rsdt_address: u32, length: u32, xsdt_address: u64, extended_checksum: u8, reserved: [u8; 3] } impl RSDP { /// Search for the RSDP pub fn search(start_addr: usize, end_addr: usize) -> Option<RSDP> { for i in 0 .. (end_addr + 1 - start_addr)/16 { let rsdp = unsafe { &*((start_addr + i * 16) as *const RSDP) }; if &rsdp.signature == b"RSD PTR " { return Some(*rsdp); } } None } /// Get the RSDT or XSDT address pub fn sdt_address(&self) -> usize { if self.revision >= 2 { self.xsdt_address as usize } else { self.rsdt_address as usize } } }