From 08900d56c8471e50d5e4da8c26bab6acdebaf010 Mon Sep 17 00:00:00 2001 From: Jeremy Soller Date: Tue, 16 Aug 2016 18:04:15 -0600 Subject: [PATCH] Changes to allow for detection and init of ASPs --- Makefile | 2 +- arch/x86_64/src/acpi/local_apic.rs | 51 +++++++++++ arch/x86_64/src/acpi/madt.rs | 133 +++++++++++++++++++++++++++++ arch/x86_64/src/acpi/mod.rs | 88 ++++++++++++++----- arch/x86_64/src/acpi/rsdt.rs | 20 ++--- arch/x86_64/src/acpi/sdt.rs | 8 +- arch/x86_64/src/acpi/xsdt.rs | 20 ++--- arch/x86_64/src/idt.rs | 23 ++++- arch/x86_64/src/lib.rs | 46 ++++++++++ 9 files changed, 343 insertions(+), 48 deletions(-) create mode 100644 arch/x86_64/src/acpi/local_apic.rs create mode 100644 arch/x86_64/src/acpi/madt.rs diff --git a/Makefile b/Makefile index 993d224..ebd42bc 100644 --- a/Makefile +++ b/Makefile @@ -10,7 +10,7 @@ bochs: build/harddrive.bin bochs -f bochs.$(ARCH) qemu: build/harddrive.bin - qemu-system-$(ARCH) -enable-kvm -cpu host -machine q35 \ + qemu-system-$(ARCH) -enable-kvm -cpu host -smp 4 -machine q35 \ -serial mon:stdio -drive file=$<,format=raw,index=0,media=disk \ -nographic -d guest_errors #-device intel-iommu diff --git a/arch/x86_64/src/acpi/local_apic.rs b/arch/x86_64/src/acpi/local_apic.rs new file mode 100644 index 0000000..a9ca626 --- /dev/null +++ b/arch/x86_64/src/acpi/local_apic.rs @@ -0,0 +1,51 @@ +use x86::msr::*; + +bitflags! { + pub flags LocalApicIcr: u64 { + const ICR_VECTOR = 0xFF, + + const ICR_FIXED = 0b000 << 8, + const ICR_SMI = 0b010 << 8, + const ICR_NMI = 0b100 << 8, + const ICR_INIT = 0b101 << 8, + const ICR_START = 0b110 << 8, + + const ICR_PHYSICAL = 0 << 11, + const ICR_LOGICAL = 1 << 11, + + const ICR_DEASSERT = 0 << 14, + const ICR_ASSERT = 1 << 14, + + const ICR_EDGE = 0 << 15, + const ICR_LEVEL = 1 << 15, + + const ICR_DESTINATION = 0b1111 << 56, + } +} + +/// Local APIC +#[repr(packed)] +pub struct LocalApic; + +impl LocalApic { + pub fn new() -> Self { + unsafe { wrmsr(IA32_APIC_BASE, rdmsr(IA32_APIC_BASE) | 1 << 10) } + LocalApic + } + + pub fn id(&self) -> u32 { + unsafe { rdmsr(IA32_X2APIC_APICID) as u32 } + } + + pub fn version(&self) -> u32 { + unsafe { rdmsr(IA32_X2APIC_VERSION) as u32 } + } + + pub fn icr(&self) -> u64 { + unsafe { rdmsr(IA32_X2APIC_ICR) } + } + + pub fn set_icr(&mut self, value: u64) { + unsafe { wrmsr(IA32_X2APIC_ICR, value) } + } +} diff --git a/arch/x86_64/src/acpi/madt.rs b/arch/x86_64/src/acpi/madt.rs new file mode 100644 index 0000000..bd1a5d2 --- /dev/null +++ b/arch/x86_64/src/acpi/madt.rs @@ -0,0 +1,133 @@ +use core::mem; + +use super::sdt::Sdt; + +/// The Multiple APIC Descriptor Table +#[derive(Debug)] +pub struct Madt { + sdt: &'static Sdt, + pub local_address: u32, + pub flags: u32 +} + +impl Madt { + pub fn new(sdt: &'static Sdt) -> Option { + if &sdt.signature == b"APIC" && sdt.data_len() >= 8 { //Not valid if no local address and flags + let local_address = unsafe { *(sdt.data_address() as *const u32) }; + let flags = unsafe { *(sdt.data_address() as *const u32).offset(1) }; + + Some(Madt { + sdt: sdt, + local_address: local_address, + flags: flags + }) + } else { + None + } + } + + pub fn iter(&self) -> MadtIter { + MadtIter { + sdt: self.sdt, + i: 8 // Skip local controller address and flags + } + } +} + +/// + +/// MADT Local APIC +#[derive(Debug)] +#[repr(packed)] +pub struct MadtLocalApic { + /// Processor ID + pub processor: u8, + /// Local APIC ID + pub id: u8, + /// Flags. 1 means that the processor is enabled + pub flags: u32 +} + +/// MADT I/O APIC +#[derive(Debug)] +#[repr(packed)] +pub struct MadtIoApic { + /// I/O APIC ID + pub id: u8, + /// reserved + reserved: u8, + /// I/O APIC address + pub address: u32, + /// Global system interrupt base + pub gsi_base: u32 +} + +/// MADT Interrupt Source Override +#[derive(Debug)] +#[repr(packed)] +pub struct MadtIntSrcOverride { + /// Bus Source + pub bus_source: u8, + /// IRQ Source + pub irq_source: u8, + /// Global system interrupt base + pub gsi_base: u32, + /// Flags + pub flags: u16 +} + +/// MADT Entries +#[derive(Debug)] +pub enum MadtEntry { + LocalApic(&'static MadtLocalApic), + InvalidLocalApic(usize), + IoApic(&'static MadtIoApic), + InvalidIoApic(usize), + IntSrcOverride(&'static MadtIntSrcOverride), + InvalidIntSrcOverride(usize), + Unknown +} + +pub struct MadtIter { + sdt: &'static Sdt, + i: usize +} + +impl Iterator for MadtIter { + type Item = MadtEntry; + fn next(&mut self) -> Option { + if self.i + 1 < self.sdt.data_len() { + let entry_type = unsafe { *(self.sdt.data_address() as *const u8).offset(self.i as isize) }; + let entry_len = unsafe { *(self.sdt.data_address() as *const u8).offset(self.i as isize + 1) } as usize; + + if self.i + entry_len <= self.sdt.data_len() { + let item = match entry_type { + 0 => if entry_len == mem::size_of::() + 2 { + MadtEntry::LocalApic(unsafe { &*((self.sdt.data_address() + self.i + 2) as *const MadtLocalApic) }) + } else { + MadtEntry::InvalidLocalApic(entry_len) + }, + 1 => if entry_len == mem::size_of::() + 2 { + MadtEntry::IoApic(unsafe { &*((self.sdt.data_address() + self.i + 2) as *const MadtIoApic) }) + } else { + MadtEntry::InvalidIoApic(entry_len) + }, + 2 => if entry_len == mem::size_of::() + 2 { + MadtEntry::IntSrcOverride(unsafe { &*((self.sdt.data_address() + self.i + 2) as *const MadtIntSrcOverride) }) + } else { + MadtEntry::InvalidIntSrcOverride(entry_len) + }, + _ => MadtEntry::Unknown + }; + + self.i += entry_len; + + Some(item) + } else { + None + } + } else { + None + } + } +} diff --git a/arch/x86_64/src/acpi/mod.rs b/arch/x86_64/src/acpi/mod.rs index 9614efe..f35f351 100644 --- a/arch/x86_64/src/acpi/mod.rs +++ b/arch/x86_64/src/acpi/mod.rs @@ -4,18 +4,74 @@ use memory::{Frame, FrameAllocator}; use paging::{entry, ActivePageTable, Page, PhysicalAddress, VirtualAddress}; -use self::rsdt::RSDT; -use self::sdt::SDT; -use self::xsdt::XSDT; +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; +pub fn init_sdt(sdt: &'static Sdt) { + 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 { + { + 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); + } + { + let icr = 0x00004600 | (asp_local_apic.id as u64) << 32; + println!(" Sending SIPI to {}: {:>016X} {:?}", asp_local_apic.id, icr, LocalApicIcr::from_bits(icr)); + local_apic.set_icr(icr); + } + } else { + println!(" CPU Disabled"); + } + }, + _ => () + } + } + }else { + println!(" {:?}", sdt); + } +} + /// Parse the ACPI tables to gather CPU, interrupt, and timer information pub unsafe fn init(allocator: &mut A, active_table: &mut ActivePageTable) -> Option where A: FrameAllocator { + // Stupidity of enormous proportion. Write the halt opcode to the 0'th physical address + // so that START IPI's can halt the processor + { + if active_table.translate_page(Page::containing_address(VirtualAddress::new(0))).is_none() { + active_table.identity_map(Frame::containing_address(PhysicalAddress::new(0)), entry::PRESENT | entry::WRITABLE, allocator); + } + unsafe { *(0 as *mut u8) = 0xF4 }; + } + let start_addr = 0xE0000; let end_addr = 0xFFFFF; @@ -34,12 +90,12 @@ pub unsafe fn init(allocator: &mut A, active_table: &mut ActivePageTable) -> if let Some(rsdp) = RSDP::search(start_addr, end_addr) { println!("{:?}", rsdp); - let mut get_sdt = |sdt_address: usize| -> &'static SDT { + let mut get_sdt = |sdt_address: usize| -> &'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, allocator); } - unsafe { &*(sdt_address as *const SDT) } + &*(sdt_address as *const Sdt) }; let rxsdt = get_sdt(rsdp.sdt_address()); @@ -48,25 +104,13 @@ pub unsafe fn init(allocator: &mut A, active_table: &mut ActivePageTable) -> print!("{}", c as char); } println!(":"); - if let Some(rsdt) = RSDT::new(rxsdt) { - println!("{:?}", rsdt); + if let Some(rsdt) = Rsdt::new(rxsdt) { for sdt_address in rsdt.iter() { - let sdt = get_sdt(sdt_address); - for &c in sdt.signature.iter() { - print!("{}", c as char); - } - println!(":"); - println!("{:?}", sdt); + init_sdt(get_sdt(sdt_address)); } - } else if let Some(xsdt) = XSDT::new(rxsdt) { - println!("{:?}", xsdt); + } else if let Some(xsdt) = Xsdt::new(rxsdt) { for sdt_address in xsdt.iter() { - let sdt = get_sdt(sdt_address); - for &c in sdt.signature.iter() { - print!("{}", c as char); - } - println!(":"); - println!("{:?}", sdt); + init_sdt(get_sdt(sdt_address)); } } else { println!("UNKNOWN RSDT OR XSDT SIGNATURE"); @@ -99,7 +143,7 @@ impl RSDP { /// Search for the RSDP pub fn search(start_addr: usize, end_addr: usize) -> Option { for i in 0 .. (end_addr + 1 - start_addr)/16 { - let mut rsdp = unsafe { &*((start_addr + i * 16) as *const RSDP) }; + let rsdp = unsafe { &*((start_addr + i * 16) as *const RSDP) }; if &rsdp.signature == b"RSD PTR " { return Some(*rsdp); } diff --git a/arch/x86_64/src/acpi/rsdt.rs b/arch/x86_64/src/acpi/rsdt.rs index 3c2d018..fa391c0 100644 --- a/arch/x86_64/src/acpi/rsdt.rs +++ b/arch/x86_64/src/acpi/rsdt.rs @@ -1,33 +1,33 @@ use core::mem; -use super::sdt::SDT; +use super::sdt::Sdt; #[derive(Debug)] -pub struct RSDT(&'static SDT); +pub struct Rsdt(&'static Sdt); -impl RSDT { - pub fn new(sdt: &'static SDT) -> Option { +impl Rsdt { + pub fn new(sdt: &'static Sdt) -> Option { if &sdt.signature == b"RSDT" { - Some(RSDT(sdt)) + Some(Rsdt(sdt)) } else { None } } - pub fn iter(&self) -> RSDTIter { - RSDTIter { + pub fn iter(&self) -> RsdtIter { + RsdtIter { sdt: self.0, i: 0 } } } -pub struct RSDTIter { - sdt: &'static SDT, +pub struct RsdtIter { + sdt: &'static Sdt, i: usize } -impl Iterator for RSDTIter { +impl Iterator for RsdtIter { type Item = usize; fn next(&mut self) -> Option { if self.i < self.sdt.data_len()/mem::size_of::() { diff --git a/arch/x86_64/src/acpi/sdt.rs b/arch/x86_64/src/acpi/sdt.rs index 15584f5..0d8cedd 100644 --- a/arch/x86_64/src/acpi/sdt.rs +++ b/arch/x86_64/src/acpi/sdt.rs @@ -2,7 +2,7 @@ use core::mem; #[derive(Copy, Clone, Debug)] #[repr(packed)] -pub struct SDT { +pub struct Sdt { pub signature: [u8; 4], pub length: u32, pub revision: u8, @@ -14,16 +14,16 @@ pub struct SDT { pub creator_revision: u32 } -impl SDT { +impl Sdt { /// Get the address of this tables data pub fn data_address(&'static self) -> usize { - self as *const _ as usize + mem::size_of::() + self as *const _ as usize + mem::size_of::() } /// Get the length of this tables data pub fn data_len(&'static self) -> usize { let total_size = self.length as usize; - let header_size = mem::size_of::(); + let header_size = mem::size_of::(); if total_size >= header_size { total_size - header_size } else { diff --git a/arch/x86_64/src/acpi/xsdt.rs b/arch/x86_64/src/acpi/xsdt.rs index 3d2e9f2..5ec6036 100644 --- a/arch/x86_64/src/acpi/xsdt.rs +++ b/arch/x86_64/src/acpi/xsdt.rs @@ -1,33 +1,33 @@ use core::mem; -use super::sdt::SDT; +use super::sdt::Sdt; #[derive(Debug)] -pub struct XSDT(&'static SDT); +pub struct Xsdt(&'static Sdt); -impl XSDT { - pub fn new(sdt: &'static SDT) -> Option { +impl Xsdt { + pub fn new(sdt: &'static Sdt) -> Option { if &sdt.signature == b"XSDT" { - Some(XSDT(sdt)) + Some(Xsdt(sdt)) } else { None } } - pub fn iter(&self) -> XSDTIter { - XSDTIter { + pub fn iter(&self) -> XsdtIter { + XsdtIter { sdt: self.0, i: 0 } } } -pub struct XSDTIter { - sdt: &'static SDT, +pub struct XsdtIter { + sdt: &'static Sdt, i: usize } -impl Iterator for XSDTIter { +impl Iterator for XsdtIter { type Item = usize; fn next(&mut self) -> Option { if self.i < self.sdt.data_len()/mem::size_of::() { diff --git a/arch/x86_64/src/idt.rs b/arch/x86_64/src/idt.rs index 318242e..2d45561 100644 --- a/arch/x86_64/src/idt.rs +++ b/arch/x86_64/src/idt.rs @@ -1,5 +1,7 @@ use core::mem; +use interrupt::halt; + pub static mut IDTR: IdtDescriptor = IdtDescriptor { size: 0, offset: 0 @@ -12,6 +14,8 @@ pub unsafe fn init() { entry.set_flags(IDT_PRESENT | IDT_RING_0 | IDT_INTERRUPT); entry.set_offset(8, exception as usize); } + IDT[13].set_offset(8, protection_fault as usize); + IDT[14].set_offset(8, page_fault as usize); for entry in IDT[32..].iter_mut() { entry.set_flags(IDT_PRESENT | IDT_RING_0 | IDT_INTERRUPT); entry.set_offset(8, blank as usize); @@ -26,7 +30,24 @@ interrupt!(blank, { }); interrupt!(exception, { - panic!("EXCEPTION"); + println!("EXCEPTION"); + loop { + halt(); + } +}); + +interrupt_error!(protection_fault, { + println!("PROTECTION FAULT"); + loop { + halt(); + } +}); + +interrupt_error!(page_fault, { + println!("PAGE FAULT"); + loop { + halt(); + } }); bitflags! { diff --git a/arch/x86_64/src/lib.rs b/arch/x86_64/src/lib.rs index 96f4142..6fde0ae 100644 --- a/arch/x86_64/src/lib.rs +++ b/arch/x86_64/src/lib.rs @@ -39,6 +39,7 @@ macro_rules! interrupt { ($name:ident, $func:block) => { #[naked] pub unsafe extern fn $name () { + #[inline(never)] unsafe fn inner() { $func } @@ -76,6 +77,51 @@ macro_rules! interrupt { }; } +#[macro_export] +macro_rules! interrupt_error { + ($name:ident, $func:block) => { + #[naked] + pub unsafe extern fn $name () { + #[inline(never)] + unsafe fn inner() { + $func + } + + asm!("xchg bx, bx" : : : : "intel", "volatile"); + + // Push scratch registers, grab stack pointer + asm!("push rax + push rcx + push rdx + push rdi + push rsi + push r8 + push r9 + push r10 + push r11" + : : : : "intel", "volatile"); + + + // Call inner rust function + inner(); + + // Pop scratch registers, error code, and return + asm!("pop r11 + pop r10 + pop r9 + pop r8 + pop rsi + pop rdi + pop rdx + pop rcx + pop rax + add rsp, 8 + iretq" + : : : : "intel", "volatile"); + } + }; +} + /// ACPI table parsing pub mod acpi;