From da6de394e4f3c8968806b674879e8f6d344a31b0 Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Sat, 18 Apr 2020 11:28:11 +0200 Subject: [PATCH 01/13] Add a new backwards-compatible v2 boot protocol. --- src/acpi/mod.rs | 5 +-- src/acpi/rsdp.rs | 54 ++++++++++++++++++++++++++++++- src/arch/x86_64/paging/mod.rs | 16 ++++++++++ src/arch/x86_64/start.rs | 60 ++++++++++++++++++++++++++++++++--- 4 files changed, 128 insertions(+), 7 deletions(-) diff --git a/src/acpi/mod.rs b/src/acpi/mod.rs index 1686835..f0f8245 100644 --- a/src/acpi/mod.rs +++ b/src/acpi/mod.rs @@ -114,7 +114,7 @@ fn init_namespace() { } /// Parse the ACPI tables to gather CPU, interrupt, and timer information -pub unsafe fn init(active_table: &mut ActivePageTable) { +pub unsafe fn init(active_table: &mut ActivePageTable, already_supplied_rsdps: Option<(u64, u64)>) { { let mut sdt_ptrs = SDT_POINTERS.write(); *sdt_ptrs = Some(BTreeMap::new()); @@ -126,7 +126,8 @@ pub unsafe fn init(active_table: &mut ActivePageTable) { } // Search for RSDP - if let Some(rsdp) = RSDP::get_rsdp(active_table) { + if let Some(rsdp) = RSDP::get_rsdp(active_table, already_supplied_rsdps) { + println!("RSDP: {:?}", rsdp); let rxsdt = get_sdt(rsdp.sdt_address(), active_table); for &c in rxsdt.signature.iter() { diff --git a/src/acpi/rsdp.rs b/src/acpi/rsdp.rs index 6ad61d8..c457ef1 100644 --- a/src/acpi/rsdp.rs +++ b/src/acpi/rsdp.rs @@ -1,3 +1,5 @@ +use core::convert::TryFrom; + use crate::memory::Frame; use crate::paging::{ActivePageTable, Page, PhysicalAddress, VirtualAddress}; use crate::paging::entry::EntryFlags; @@ -18,8 +20,58 @@ pub struct RSDP { } impl RSDP { + fn is_acpi_1_0(&self) -> bool { + self.revision == 0 + } + fn is_acpi_2_0(&self) -> bool { + self.revision == 2 + } + fn get_already_supplied_rsdps(area: &[u8]) -> Option { + // the bootloader has already checked all the checksums for us, but we still need to + // double-check. + struct Iter<'a> { + buf: &'a [u8], + } + impl<'a> Iterator for Iter<'a> { + type Item = &'a [u8]; + + fn next(&mut self) -> Option { + let length = <[u8; 4]>::try_from(&self.buf[..4]).ok()?; + if (4 + length as usize) > self.buf.len() { return None } + self.buf = self.buf[4 + length..]; + Ok(length) + } + } + fn slice_to_rsdp(slice: &[u8]) -> Option<&RSDP> { + let ptr = slice.as_ptr() as usize; + + if slice.len() >= mem::size_of::() && ptr & (!0x7) == ptr { + let rsdp = unsafe { &*(slice.as_ptr() as *const RSDP) }; + // TODO: Validate + Some(rsdp) + } else { None } + } + + // first, find an RDSP for ACPI 2.0 + if let Some(rdsp_2_0) = Iter { buf: area }.filter_map(slice_to_rsdp).filter(|rsdp| rsdp.is_acpi_2_0()) { + return Some(rsdp_2_0); + } + + // secondly, find an RDSP for ACPI 1.0 + if let Some(rdsp_1_0) = Iter { buf: area }.filter_map(slice_to_rsdp).filter(|rsdp| rsdp.is_acpi_1_0()) { + return Some(rsdp_1_0); + } + } + pub fn get_rsdp(active_table: &mut ActivePageTable, already_supplied_rsdps: Option<(u64, u64)>) -> Option { + if let Some((base, size)) = already_supplied_rsdps { + let area = core::slice::from_raw_parts(base as usize as *const u8, size as usize); + Self::get_already_supplied_rsdps(area) + } else { + Self::get_rsdp_by_searching(active_table) + } + } /// Search for the RSDP - pub fn get_rsdp(active_table: &mut ActivePageTable) -> Option { + pub fn get_rsdp_by_searching(active_table: &mut ActivePageTable) -> Option { let start_addr = 0xE_0000; let end_addr = 0xF_FFFF; diff --git a/src/arch/x86_64/paging/mod.rs b/src/arch/x86_64/paging/mod.rs index c4acb93..f748d97 100644 --- a/src/arch/x86_64/paging/mod.rs +++ b/src/arch/x86_64/paging/mod.rs @@ -125,6 +125,7 @@ pub unsafe fn init( kernel_end: usize, stack_start: usize, stack_end: usize, + other_ro_ranges: &[(usize, usize)], // base + length ) -> (ActivePageTable, usize) { extern "C" { /// The starting byte of the text (code) data segment. @@ -266,6 +267,21 @@ pub unsafe fn init( result.ignore(); } } + // Map all other necessary address ranges coming from the bootloader. + // The address ranges may overlap, but this is not a problem since they have the same + // flags. + { + for (range_start, range_size) in other_ro_ranges { + let start_phys_addr = Frame::containing_address(PhysicalAddress::new((range_start / 4096) * 4096 - crate::KERNEL_OFFSET)); + let end_phys_addr = Frame::containing_address(PhysicalAddress::new(((range_start + range_size + 4095) / 4096) * 4096 - crate::KERNEL_OFFSET)); + + for frame in Frame::range_inclusive(start_phys_addr, end_phys_addr) { + let page = Page::containing_address(VirtualAddress::new(crate::KERNEL_OFFSET + frame.start_address().get())); + let result = mapper.map_to(page, frame, EntryFlags::PRESENT | EntryFlags::GLOBAL | EntryFlags::NO_EXECUTE); + result.ignore(); + } + } + } }); // This switches the active table, which is setup by the bootloader, to a correct table diff --git a/src/arch/x86_64/start.rs b/src/arch/x86_64/start.rs index 226c9a2..4cb7333 100644 --- a/src/arch/x86_64/start.rs +++ b/src/arch/x86_64/start.rs @@ -3,7 +3,7 @@ /// It must create the IDT with the correct entries, those entries are /// defined in other files inside of the `arch` module -use core::slice; +use core::{mem, slice}; use core::sync::atomic::{AtomicBool, AtomicUsize, Ordering}; use crate::allocator; @@ -47,6 +47,37 @@ pub struct KernelArgs { env_size: u64, } +/// Extended kernel args. +/// +/// While the base kernel args are sufficient for basic initialization of most things, information +/// like the memory map and the ACPI RSDPS (on UEFI), might have to be passed som other way. +/// +/// If the envs begin with b"\x7f=V2", the pointer to `KernelArgs` can be assumed to also point to a +/// complete `KernelArgsExt`. +#[repr(packed)] +pub struct KernelArgsExt { + /// The base compatible kernel args. + base: KernelArgs, + /// The revision of the KernelArgsExt struct. Only 0 at the moment. + proto_rev: u16, + /// The total length of the kernel args struct. + length: u16, + /// Some flags. All flags are currently reserved and shall be 0. + flags: u32, + /// The base 64-bit pointer to an array of saved RSDPs. It's up to the kernel (and possibly + /// userspace), to decide which RSDP to use. The buffer will be a linked list containing a + /// 32-bit relative (to this field) next, and the actual struct afterwards. + /// + /// This field can be NULL, and if so, the system has not booted with UEFI or in some other way + /// retrieved the RSDPs. The kernel or a userspace driver will thus try searching the BIOS + /// memory instead. On UEFI systems, this is not guaranteed to actually work. + acpi_rsdps_base: u64, + /// The size of the RSDPs region. + acpi_rsdps_size: u64, + // TODO: Include things like memory map + // TODO: Generalized tag structure, like with GRUB. +} + /// The entry to Rust, all things must be initialized #[no_mangle] pub unsafe extern fn kstart(args_ptr: *const KernelArgs) -> ! { @@ -73,6 +104,27 @@ pub unsafe extern fn kstart(args_ptr: *const KernelArgs) -> ! { println!("Stack: {:X}:{:X}", stack_base, stack_base + stack_size); println!("Env: {:X}:{:X}", env_base, env_base + env_size); + let mut env = slice::from_raw_parts(env_base as *const u8, env_size); + + // This is kind of a hack, only used for an extended KernelArgs struct. + let extended_kargs = if &env[..4] == b"\x7f=V2" { + println!("Using EXT kernel args."); + env = &env[4..]; + let ext_kargs = &*(args_ptr as *const KernelArgsExt); + + if (ext_kargs.length as usize) < mem::size_of::() { + None + } else { + Some(ext_kargs) + } + } else { None }; + + let ext_mem_ranges = if let Some(ref xargs) = extended_kargs { + Some([(xargs.acpi_rsdps_base as usize, xargs.acpi_rsdps_size as usize)]) + } else { + None + }; + // Set up GDT before paging gdt::init(); @@ -83,7 +135,7 @@ pub unsafe extern fn kstart(args_ptr: *const KernelArgs) -> ! { memory::init(0, kernel_base + ((kernel_size + 4095)/4096) * 4096); // Initialize paging - let (mut active_table, tcb_offset) = paging::init(0, kernel_base, kernel_base + kernel_size, stack_base, stack_base + stack_size); + let (mut active_table, tcb_offset) = paging::init(0, kernel_base, kernel_base + kernel_size, stack_base, stack_base + stack_size, ext_mem_ranges.as_ref().map(|arr| &arr[..]).unwrap_or(&[])); // Set up GDT after paging with TLS gdt::init_paging(tcb_offset, stack_base + stack_size); @@ -124,7 +176,7 @@ pub unsafe extern fn kstart(args_ptr: *const KernelArgs) -> ! { // Read ACPI tables, starts APs #[cfg(feature = "acpi")] - acpi::init(&mut active_table); + acpi::init(&mut active_table, extended_kargs.as_ref().map(|kargs| (kargs.acpi_rsdps_base, kargs.acpi_rsdps_size))); // Initialize all of the non-core devices not otherwise needed to complete initialization device::init_noncore(); @@ -138,7 +190,7 @@ pub unsafe extern fn kstart(args_ptr: *const KernelArgs) -> ! { BSP_READY.store(true, Ordering::SeqCst); - slice::from_raw_parts(env_base as *const u8, env_size) + env }; crate::kmain(CPU_COUNT.load(Ordering::SeqCst), env); From f0b5d517932a803f7a2c3d73711b55d4d9f665c8 Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Sat, 18 Apr 2020 16:36:18 +0200 Subject: [PATCH 02/13] Use the I/O APIC when applicable. --- src/acpi/madt.rs | 8 +- src/acpi/mod.rs | 2 +- src/acpi/rsdp.rs | 32 ++- src/arch/x86_64/device/ioapic.rs | 382 +++++++++++++++++++++++++++++++ src/arch/x86_64/device/mod.rs | 7 +- src/arch/x86_64/device/pic.rs | 9 + src/arch/x86_64/interrupt/irq.rs | 121 ++++++++-- src/arch/x86_64/start.rs | 5 +- 8 files changed, 527 insertions(+), 39 deletions(-) create mode 100644 src/arch/x86_64/device/ioapic.rs diff --git a/src/acpi/madt.rs b/src/acpi/madt.rs index d890489..6774358 100644 --- a/src/acpi/madt.rs +++ b/src/acpi/madt.rs @@ -15,13 +15,16 @@ use crate::interrupt; use crate::start::{kstart_ap, CPU_COUNT, AP_READY}; /// The Multiple APIC Descriptor Table -#[derive(Debug)] +#[derive(Clone, Copy, Debug)] pub struct Madt { sdt: &'static Sdt, pub local_address: u32, pub flags: u32 } +pub static mut MADT: Option = None; +pub const FLAG_PCAT: u32 = 1; + impl Madt { pub fn init(active_table: &mut ActivePageTable) { let madt_sdt = find_sdt("APIC"); @@ -34,6 +37,9 @@ impl Madt { }; if let Some(madt) = madt { + // safe because no APs have been started yet. + unsafe { MADT = Some(madt) }; + println!(" APIC: {:>08X}: {}", madt.local_address, madt.flags); let local_apic = unsafe { &mut LOCAL_APIC }; diff --git a/src/acpi/mod.rs b/src/acpi/mod.rs index f0f8245..7d79c7c 100644 --- a/src/acpi/mod.rs +++ b/src/acpi/mod.rs @@ -31,7 +31,7 @@ use self::aml::{parse_aml_table, AmlError, AmlValue}; pub mod hpet; mod dmar; mod fadt; -mod madt; +pub mod madt; mod rsdt; mod sdt; mod xsdt; diff --git a/src/acpi/rsdp.rs b/src/acpi/rsdp.rs index c457ef1..fa244fd 100644 --- a/src/acpi/rsdp.rs +++ b/src/acpi/rsdp.rs @@ -1,4 +1,5 @@ use core::convert::TryFrom; +use core::mem; use crate::memory::Frame; use crate::paging::{ActivePageTable, Page, PhysicalAddress, VirtualAddress}; @@ -36,10 +37,17 @@ impl RSDP { type Item = &'a [u8]; fn next(&mut self) -> Option { - let length = <[u8; 4]>::try_from(&self.buf[..4]).ok()?; + if self.buf.len() < 4 { return None } + + let length_bytes = <[u8; 4]>::try_from(&self.buf[..4]).ok()?; + let length = u32::from_ne_bytes(length_bytes) as usize; + if (4 + length as usize) > self.buf.len() { return None } - self.buf = self.buf[4 + length..]; - Ok(length) + + let buf = &self.buf[4..4 + length]; + self.buf = &self.buf[4 + length..]; + + Some(buf) } } fn slice_to_rsdp(slice: &[u8]) -> Option<&RSDP> { @@ -52,20 +60,22 @@ impl RSDP { } else { None } } - // first, find an RDSP for ACPI 2.0 - if let Some(rdsp_2_0) = Iter { buf: area }.filter_map(slice_to_rsdp).filter(|rsdp| rsdp.is_acpi_2_0()) { - return Some(rsdp_2_0); + // first, find an RSDP for ACPI 2.0 + if let Some(rsdp_2_0) = (Iter { buf: area }.filter_map(slice_to_rsdp).find(|rsdp| rsdp.is_acpi_2_0())) { + return Some(*rsdp_2_0); } - // secondly, find an RDSP for ACPI 1.0 - if let Some(rdsp_1_0) = Iter { buf: area }.filter_map(slice_to_rsdp).filter(|rsdp| rsdp.is_acpi_1_0()) { - return Some(rsdp_1_0); + // secondly, find an RSDP for ACPI 1.0 + if let Some(rsdp_1_0) = (Iter { buf: area }.filter_map(slice_to_rsdp).find(|rsdp| rsdp.is_acpi_1_0())) { + return Some(*rsdp_1_0); } + + None } pub fn get_rsdp(active_table: &mut ActivePageTable, already_supplied_rsdps: Option<(u64, u64)>) -> Option { if let Some((base, size)) = already_supplied_rsdps { - let area = core::slice::from_raw_parts(base as usize as *const u8, size as usize); - Self::get_already_supplied_rsdps(area) + let area = unsafe { core::slice::from_raw_parts(base as usize as *const u8, size as usize) }; + Self::get_already_supplied_rsdps(area).or_else(|| Self::get_rsdp_by_searching(active_table)) } else { Self::get_rsdp_by_searching(active_table) } diff --git a/src/arch/x86_64/device/ioapic.rs b/src/arch/x86_64/device/ioapic.rs new file mode 100644 index 0000000..2897ed9 --- /dev/null +++ b/src/arch/x86_64/device/ioapic.rs @@ -0,0 +1,382 @@ +use core::{fmt, ptr}; + +use alloc::vec::Vec; +use spin::Mutex; + +#[cfg(feature = "acpi")] +use crate::acpi::madt::{self, Madt, MadtEntry, MadtIoApic, MadtIntSrcOverride}; + +use crate::arch::interrupt::irq; +use crate::memory::Frame; +use crate::paging::{ActivePageTable, entry::EntryFlags, Page, PhysicalAddress, VirtualAddress}; +use crate::syscall::io::Mmio; + +use super::pic; + +pub struct IoApicRegs { + pointer: *const u32, +} +impl IoApicRegs { + fn ioregsel(&self) -> *const u32 { + self.pointer + } + fn iowin(&self) -> *const u32 { + // offset 0x10 + unsafe { self.pointer.offset(4) } + } + fn read_ioregsel(&self) -> u32 { + unsafe { ptr::read_volatile::(self.ioregsel()) } + } + fn write_ioregsel(&mut self, value: u32) { + unsafe { ptr::write_volatile::(self.ioregsel() as *mut u32, value) } + } + fn read_iowin(&self) -> u32 { + unsafe { ptr::read_volatile::(self.iowin()) } + } + fn write_iowin(&mut self, value: u32) { + unsafe { ptr::write_volatile::(self.iowin() as *mut u32, value) } + } + fn read_reg(&mut self, reg: u8) -> u32 { + self.write_ioregsel(reg.into()); + self.read_iowin() + } + fn write_reg(&mut self, reg: u8, value: u32) { + self.write_ioregsel(reg.into()); + self.write_iowin(value); + } + pub fn read_ioapicid(&mut self) -> u32 { + self.read_reg(0x00) + } + pub fn write_ioapicid(&mut self, value: u32) { + self.write_reg(0x00, value); + } + pub fn read_ioapicver(&mut self) -> u32 { + self.read_reg(0x01) + } + pub fn read_ioapicarb(&mut self) -> u32 { + self.read_reg(0x02) + } + pub fn read_ioredtbl(&mut self, idx: u8) -> u64 { + assert!(idx < 24); + let lo = self.read_reg(0x10 + idx * 2); + let hi = self.read_reg(0x10 + idx * 2 + 1); + + u64::from(lo) | (u64::from(hi) << 32) + } + pub fn write_ioredtbl(&mut self, idx: u8, value: u64) { + assert!(idx < 24); + + let lo = value as u32; + let hi = (value >> 32) as u32; + + self.write_reg(0x10 + idx * 2, lo); + self.write_reg(0x10 + idx * 2 + 1, hi); + } + + pub fn max_redirection_table_entries(&mut self) -> u8 { + let ver = self.read_ioapicver(); + ((ver & 0x00FF_0000) >> 16) as u8 + } + pub fn id(&mut self) -> u8 { + let id_reg = self.read_ioapicid(); + ((id_reg & 0x0F00_0000) >> 24) as u8 + } +} +pub struct IoApic { + regs: Mutex, + gsi_start: u32, + count: u8, +} +impl IoApic { + pub fn new(regs_base: *const u32, gsi_start: u32) -> Self { + let mut regs = IoApicRegs { pointer: regs_base }; + let count = regs.max_redirection_table_entries(); + + Self { + regs: Mutex::new(regs), + gsi_start, + count, + } + } + /// Map an interrupt vector to a physical local APIC ID of a processor (thus physical mode). + pub fn map(&self, idx: u8, info: MapInfo) { + self.regs.lock().write_ioredtbl(idx, info.as_raw()) + } + pub fn set_mask(&self, gsi: u32, mask: bool) { + let idx = (gsi - self.gsi_start) as u8; + let mut guard = self.regs.lock(); + + let mut reg = guard.read_ioredtbl(idx); + reg &= !(1 << 16); + reg |= u64::from(mask) << 16; + guard.write_ioredtbl(idx, reg); + } +} +#[repr(u8)] +#[derive(Clone, Copy, Debug)] +pub enum ApicTriggerMode { + Edge = 0, + Level = 1, +} +#[repr(u8)] +#[derive(Clone, Copy, Debug)] +pub enum ApicPolarity { + ActiveHigh = 0, + ActiveLow = 1, +} +#[repr(u8)] +#[derive(Clone, Copy, Debug)] +pub enum DestinationMode { + Physical = 0, + Logical = 1, +} +#[repr(u8)] +#[derive(Clone, Copy, Debug)] +pub enum DeliveryMode { + Fixed = 0b000, + LowestPriority = 0b001, + Smi = 0b010, + Nmi = 0b100, + Init = 0b101, + ExtInt = 0b111, +} + +#[derive(Clone, Copy, Debug)] +pub struct MapInfo { + pub dest: u8, + pub mask: bool, + pub trigger_mode: ApicTriggerMode, + pub polarity: ApicPolarity, + pub dest_mode: DestinationMode, + pub delivery_mode: DeliveryMode, + pub vector: u8, +} + +impl MapInfo { + pub fn as_raw(&self) -> u64 { + assert!(self.vector >= 0x20); + assert!(self.vector <= 0xFE); + + // TODO: Check for reserved fields. + + (u64::from(self.dest) << 56) + | (u64::from(self.mask) << 16) + | ((self.trigger_mode as u64) << 15) + | ((self.polarity as u64) << 13) + | ((self.dest_mode as u64) << 11) + | ((self.delivery_mode as u64) << 8) + | u64::from(self.vector) + } +} + +impl fmt::Debug for IoApic { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + struct RedirTable<'a>(&'a Mutex); + + impl<'a> fmt::Debug for RedirTable<'a> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let mut guard = self.0.lock(); + + let count = guard.max_redirection_table_entries(); + f.debug_list().entries((0..count).map(|i| guard.read_ioredtbl(i))).finish() + } + } + + f.debug_struct("IoApic") + .field("redir_table", &RedirTable(&self.regs)) + .field("gsi_start", &self.gsi_start) + .field("count", &self.count) + .finish() + } +} + +#[derive(Clone, Copy, Debug)] +pub enum TriggerMode { + ConformsToSpecs, + Edge, + Level, +} + +#[derive(Clone, Copy, Debug)] +pub enum Polarity { + ConformsToSpecs, + ActiveHigh, + ActiveLow, +} + +#[derive(Clone, Copy, Debug)] +pub struct Override { + bus_irq: u8, + gsi: u32, + + trigger_mode: TriggerMode, + polarity: Polarity, +} + +// static mut because only the AP initializes the I/O Apic, and when that is done, it's solely +// accessed immutably. +static mut IOAPICS: Option> = None; + +// static mut for the same reason as above +static mut SRC_OVERRIDES: Option> = None; + +pub fn ioapics() -> &'static [IoApic] { + unsafe { + IOAPICS.as_ref().map_or(&[], |vector| &vector[..]) + } +} +pub fn src_overrides() -> &'static [Override] { + unsafe { + SRC_OVERRIDES.as_ref().map_or(&[], |vector| &vector[..]) + } +} + +#[cfg(feature = "acpi")] +pub unsafe fn handle_ioapic(active_table: &mut ActivePageTable, madt_ioapic: &'static MadtIoApic) { + // map the I/O APIC registers + let frame = Frame::containing_address(PhysicalAddress::new(madt_ioapic.address as usize)); + let page = Page::containing_address(VirtualAddress::new(madt_ioapic.address as usize + crate::KERNEL_OFFSET)); + + assert_eq!(active_table.translate_page(page), None); + + let result = active_table.map_to(page, frame, EntryFlags::PRESENT | EntryFlags::GLOBAL | EntryFlags::WRITABLE | EntryFlags::NO_CACHE); + result.flush(active_table); + + let ioapic_registers = page.start_address().get() as *const u32; + let mut ioapic = IoApic::new(ioapic_registers, madt_ioapic.gsi_base); + + assert_eq!(ioapic.regs.lock().id(), madt_ioapic.id, "mismatched ACPI MADT I/O APIC ID, and the ID reported by the I/O APIC"); + + IOAPICS.get_or_insert_with(Vec::new).push(ioapic); +} +#[cfg(feature = "acpi")] +pub unsafe fn handle_src_override(src_override: &'static MadtIntSrcOverride) { + let flags = src_override.flags; + + let polarity_raw = (flags & 0x0003) as u8; + let trigger_mode_raw = ((flags & 0x000C) >> 2) as u8; + + let polarity = match polarity_raw { + 0b00 => Polarity::ConformsToSpecs, + 0b01 => Polarity::ActiveHigh, + 0b10 => return, // reserved + 0b11 => Polarity::ActiveLow, + + _ => unreachable!(), + }; + + let trigger_mode = match trigger_mode_raw { + 0b00 => TriggerMode::ConformsToSpecs, + 0b01 => TriggerMode::Edge, + 0b10 => return, // reserved + 0b11 => TriggerMode::Level, + _ => unreachable!(), + }; + + let over = Override { + bus_irq: src_override.irq_source, + gsi: src_override.gsi_base, + polarity, + trigger_mode, + }; + SRC_OVERRIDES.get_or_insert_with(Vec::new).push(over); +} + +pub unsafe fn init(active_table: &mut ActivePageTable) { + let mut bsp_apic_id = x86::cpuid::CpuId::new().get_feature_info().unwrap().initial_local_apic_id(); // TODO + + // search the madt for all IOAPICs. + #[cfg(feature = "acpi")] + { + let madt: &'static Madt = match madt::MADT.as_ref() { + Some(m) => m, + // TODO: Parse MP tables too. + None => return, + }; + if madt.flags & madt::FLAG_PCAT != 0 { + pic::disable(); + } + + // find all I/O APICs (usually one). + + for entry in madt.iter() { + match entry { + MadtEntry::IoApic(ioapic) => handle_ioapic(active_table, ioapic), + MadtEntry::IntSrcOverride(src_override) => handle_src_override(src_override), + _ => (), + } + } + } + println!("I/O APICs: {:?}, overrides: {:?}", ioapics(), src_overrides()); + + // map the legacy PC-compatible IRQs (0-15) to 32-47, just like we did with 8259 PIC (if it + // wouldn't have been disabled due to this I/O APIC) + for legacy_irq in 0..=15 { + let (gsi, trigger_mode, polarity) = match get_override(legacy_irq) { + Some(over) => (over.gsi, over.trigger_mode, over.polarity), + None => { + if src_overrides().iter().any(|over| over.gsi == u32::from(legacy_irq) && over.bus_irq != legacy_irq) && !src_overrides().iter().any(|over| over.bus_irq == legacy_irq) { + // there's an IRQ conflict, making this legacy IRQ inaccessible. + continue; + } + (legacy_irq.into(), TriggerMode::ConformsToSpecs, Polarity::ConformsToSpecs) + } + }; + let apic = match find_ioapic(gsi) { + Some(ioapic) => ioapic, + None => { + println!("Unable to find a suitable APIC for legacy IRQ {} (GSI {}). It will not be mapped.", legacy_irq, gsi); + continue; + } + }; + let redir_tbl_index = (gsi - apic.gsi_start) as u8; + + let map_info = MapInfo { + // only send to the BSP + dest: bsp_apic_id, + dest_mode: DestinationMode::Physical, + delivery_mode: DeliveryMode::Fixed, + mask: false, + polarity: match polarity { + Polarity::ActiveHigh => ApicPolarity::ActiveHigh, + Polarity::ActiveLow => ApicPolarity::ActiveLow, + Polarity::ConformsToSpecs => ApicPolarity::ActiveHigh, + }, + trigger_mode: match trigger_mode { + TriggerMode::Edge => ApicTriggerMode::Edge, + TriggerMode::Level => ApicTriggerMode::Level, + TriggerMode::ConformsToSpecs => ApicTriggerMode::Edge, + }, + vector: 32 + legacy_irq, + }; + apic.map(redir_tbl_index, map_info); + } + println!("I/O APICs: {:?}, overrides: {:?}", ioapics(), src_overrides()); + irq::set_irq_method(irq::IrqMethod::Apic); +} +fn get_override(irq: u8) -> Option<&'static Override> { + src_overrides().iter().find(|over| over.bus_irq == irq) +} +fn resolve(irq: u8) -> u32 { + get_override(irq).map_or(u32::from(irq), |over| over.gsi) +} +fn find_ioapic(gsi: u32) -> Option<&'static IoApic> { + ioapics().iter().find(|apic| gsi >= apic.gsi_start && gsi < apic.gsi_start + u32::from(apic.count)) +} + +pub unsafe fn mask(irq: u8) { + let gsi = resolve(irq); + let apic = match find_ioapic(gsi) { + Some(a) => a, + None => return, + }; + apic.set_mask(gsi, true); +} +pub unsafe fn unmask(irq: u8) { + let gsi = resolve(irq); + let apic = match find_ioapic(gsi) { + Some(a) => a, + None => return, + }; + apic.set_mask(gsi, false); +} diff --git a/src/arch/x86_64/device/mod.rs b/src/arch/x86_64/device/mod.rs index 3c87652..656d2b2 100644 --- a/src/arch/x86_64/device/mod.rs +++ b/src/arch/x86_64/device/mod.rs @@ -1,6 +1,7 @@ use crate::paging::ActivePageTable; pub mod cpu; +pub mod ioapic; pub mod local_apic; pub mod pic; pub mod pit; @@ -9,10 +10,14 @@ pub mod serial; #[cfg(feature = "acpi")] pub mod hpet; -pub unsafe fn init(active_table: &mut ActivePageTable){ +pub unsafe fn init(active_table: &mut ActivePageTable) { pic::init(); local_apic::init(active_table); } +pub unsafe fn init_after_acpi(active_table: &mut ActivePageTable) { + // this will disable the IOAPIC if needed. + ioapic::init(active_table); +} #[cfg(feature = "acpi")] unsafe fn init_hpet() -> bool { diff --git a/src/arch/x86_64/device/pic.rs b/src/arch/x86_64/device/pic.rs index 88f5709..2518550 100644 --- a/src/arch/x86_64/device/pic.rs +++ b/src/arch/x86_64/device/pic.rs @@ -1,4 +1,5 @@ use crate::syscall::io::{Io, Pio}; +use crate::arch::interrupt::irq; pub static mut MASTER: Pic = Pic::new(0x20); pub static mut SLAVE: Pic = Pic::new(0xA0); @@ -27,6 +28,14 @@ pub unsafe fn init() { // Ack remaining interrupts MASTER.ack(); SLAVE.ack(); + + // probably already set to PIC, but double-check + irq::set_irq_method(irq::IrqMethod::Pic); +} + +pub unsafe fn disable() { + MASTER.data.write(0xFF); + SLAVE.data.write(0xFF); } pub struct Pic { diff --git a/src/arch/x86_64/interrupt/irq.rs b/src/arch/x86_64/interrupt/irq.rs index 72c2972..9b4ba74 100644 --- a/src/arch/x86_64/interrupt/irq.rs +++ b/src/arch/x86_64/interrupt/irq.rs @@ -1,7 +1,7 @@ use core::sync::atomic::{AtomicUsize, Ordering}; use crate::context::timeout; -use crate::device::{local_apic, pic}; +use crate::device::{local_apic, ioapic, pic}; use crate::device::serial::{COM1, COM2}; use crate::ipi::{ipi, IpiKind, IpiTarget}; use crate::scheme::debug::debug_input; @@ -11,38 +11,99 @@ use crate::{context, ptrace, time}; #[thread_local] pub static PIT_TICKS: AtomicUsize = AtomicUsize::new(0); +#[repr(u8)] +pub enum IrqMethod { + Pic = 0, + Apic = 1, +} + +static IRQ_METHOD: AtomicUsize = AtomicUsize::new(IrqMethod::Pic as usize); + +pub fn set_irq_method(method: IrqMethod) { + IRQ_METHOD.store(method as usize, core::sync::atomic::Ordering::Release); +} + +fn irq_method() -> IrqMethod { + let raw = IRQ_METHOD.load(core::sync::atomic::Ordering::Acquire); + + match raw { + 0 => IrqMethod::Pic, + 1 => IrqMethod::Apic, + _ => unreachable!(), + } +} + extern { + // triggers irq scheme fn irq_trigger(irq: u8); } +/// Notify the IRQ scheme that an IRQ has been registered. This should mask the IRQ until the +/// scheme user unmasks it ("acknowledges" it). unsafe fn trigger(irq: u8) { - - if irq < 16 { - if irq >= 8 { - pic::SLAVE.mask_set(irq - 8); - pic::MASTER.ack(); - pic::SLAVE.ack(); - } else { - pic::MASTER.mask_set(irq); - pic::MASTER.ack(); - } + match irq_method() { + IrqMethod::Pic => if irq < 16 { pic_mask(irq) }, + IrqMethod::Apic => ioapic_mask(irq), } - irq_trigger(irq); } +/// Unmask the IRQ. This is called from the IRQ scheme, which does this when a user process has +/// processed the IRQ. +pub unsafe fn acknowledge(irq: usize) { + match irq_method() { + IrqMethod::Pic => if irq < 16 { pic_unmask(irq) }, + IrqMethod::Apic => ioapic_unmask(irq), + } +} +/// Sends an end-of-interrupt, so that the interrupt controller can go on to the next one. +pub unsafe fn eoi(irq: u8) { + match irq_method() { + IrqMethod::Pic => if irq < 16 { pic_eoi(irq) }, + IrqMethod::Apic => lapic_eoi(), + } +} + +unsafe fn pic_mask(irq: u8) { + debug_assert!(irq < 16); + + if irq >= 8 { + pic::SLAVE.mask_set(irq - 8); + } else { + pic::MASTER.mask_set(irq); + } +} + +unsafe fn ioapic_mask(irq: u8) { + ioapic::mask(irq); +} + + +unsafe fn pic_eoi(irq: u8) { + debug_assert!(irq < 16); + + if irq >= 8 { + pic::MASTER.ack(); + pic::SLAVE.ack(); + } else { + pic::MASTER.ack(); + } +} unsafe fn lapic_eoi() { local_apic::LOCAL_APIC.eoi() } -pub unsafe fn acknowledge(irq: usize) { - if irq < 16 { - if irq >= 8 { - pic::SLAVE.mask_clear(irq as u8 - 8); - } else { - pic::MASTER.mask_clear(irq as u8); - } +unsafe fn pic_unmask(irq: usize) { + debug_assert!(irq < 16); + + if irq >= 8 { + pic::SLAVE.mask_clear(irq as u8 - 8); + } else { + pic::MASTER.mask_clear(irq as u8); } } +unsafe fn ioapic_unmask(irq: usize) { + ioapic::unmask(irq as u8); +} interrupt_stack!(pit, stack, { // Saves CPU time by not sending IRQ event irq_trigger(0); @@ -56,7 +117,7 @@ interrupt_stack!(pit, stack, { offset.0 += sum / 1_000_000_000; } - pic::MASTER.ack(); + eoi(0); // Wake up other CPUs ipi(IpiKind::Pit, IpiTarget::Other); @@ -72,68 +133,80 @@ interrupt_stack!(pit, stack, { interrupt!(keyboard, { trigger(1); + eoi(1); }); interrupt!(cascade, { // No need to do any operations on cascade - pic::MASTER.ack(); + eoi(2); }); interrupt!(com2, { while let Some(c) = COM2.lock().receive() { debug_input(c); } - pic::MASTER.ack(); + eoi(3); }); interrupt!(com1, { while let Some(c) = COM1.lock().receive() { debug_input(c); } - pic::MASTER.ack(); + eoi(4); }); interrupt!(lpt2, { + eoi(5); trigger(5); }); interrupt!(floppy, { + eoi(6); trigger(6); }); interrupt!(lpt1, { + eoi(7); trigger(7); }); interrupt!(rtc, { + eoi(8); trigger(8); }); interrupt!(pci1, { + eoi(9); trigger(9); }); interrupt!(pci2, { + eoi(10); trigger(10); }); interrupt!(pci3, { + eoi(11); trigger(11); }); interrupt!(mouse, { + eoi(12); trigger(12); }); interrupt!(fpu, { + eoi(13); trigger(13); }); interrupt!(ata1, { + eoi(14); trigger(14); }); interrupt!(ata2, { + eoi(15); trigger(15); }); interrupt!(lapic_timer, { @@ -154,7 +227,7 @@ interrupt!(calib_pit, { offset.0 += sum / 1_000_000_000; } - pic::MASTER.ack(); + eoi(0); }); // XXX: This would look way prettier using const generics. diff --git a/src/arch/x86_64/start.rs b/src/arch/x86_64/start.rs index 4cb7333..12afd59 100644 --- a/src/arch/x86_64/start.rs +++ b/src/arch/x86_64/start.rs @@ -176,7 +176,10 @@ pub unsafe extern fn kstart(args_ptr: *const KernelArgs) -> ! { // Read ACPI tables, starts APs #[cfg(feature = "acpi")] - acpi::init(&mut active_table, extended_kargs.as_ref().map(|kargs| (kargs.acpi_rsdps_base, kargs.acpi_rsdps_size))); + { + acpi::init(&mut active_table, extended_kargs.as_ref().map(|kargs| (kargs.acpi_rsdps_base, kargs.acpi_rsdps_size))); + device::init_after_acpi(&mut active_table); + } // Initialize all of the non-core devices not otherwise needed to complete initialization device::init_noncore(); From de4b66150d84bb59238a312677740516d7bf2d81 Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Sat, 18 Apr 2020 17:06:45 +0200 Subject: [PATCH 03/13] Remove unnecessary kernel args. --- src/arch/x86_64/start.rs | 46 ++++++---------------------------------- 1 file changed, 6 insertions(+), 40 deletions(-) diff --git a/src/arch/x86_64/start.rs b/src/arch/x86_64/start.rs index 12afd59..86d9cbc 100644 --- a/src/arch/x86_64/start.rs +++ b/src/arch/x86_64/start.rs @@ -45,37 +45,17 @@ pub struct KernelArgs { stack_size: u64, env_base: u64, env_size: u64, -} -/// Extended kernel args. -/// -/// While the base kernel args are sufficient for basic initialization of most things, information -/// like the memory map and the ACPI RSDPS (on UEFI), might have to be passed som other way. -/// -/// If the envs begin with b"\x7f=V2", the pointer to `KernelArgs` can be assumed to also point to a -/// complete `KernelArgsExt`. -#[repr(packed)] -pub struct KernelArgsExt { - /// The base compatible kernel args. - base: KernelArgs, - /// The revision of the KernelArgsExt struct. Only 0 at the moment. - proto_rev: u16, - /// The total length of the kernel args struct. - length: u16, - /// Some flags. All flags are currently reserved and shall be 0. - flags: u32, /// The base 64-bit pointer to an array of saved RSDPs. It's up to the kernel (and possibly /// userspace), to decide which RSDP to use. The buffer will be a linked list containing a /// 32-bit relative (to this field) next, and the actual struct afterwards. /// /// This field can be NULL, and if so, the system has not booted with UEFI or in some other way /// retrieved the RSDPs. The kernel or a userspace driver will thus try searching the BIOS - /// memory instead. On UEFI systems, this is not guaranteed to actually work. + /// memory instead. On UEFI systems, searching is not guaranteed to actually work though. acpi_rsdps_base: u64, /// The size of the RSDPs region. acpi_rsdps_size: u64, - // TODO: Include things like memory map - // TODO: Generalized tag structure, like with GRUB. } /// The entry to Rust, all things must be initialized @@ -103,24 +83,10 @@ pub unsafe extern fn kstart(args_ptr: *const KernelArgs) -> ! { println!("Kernel: {:X}:{:X}", kernel_base, kernel_base + kernel_size); println!("Stack: {:X}:{:X}", stack_base, stack_base + stack_size); println!("Env: {:X}:{:X}", env_base, env_base + env_size); + println!("RSDPs: {:X}:{:X}", args.acpi_rsdps_base, args.acpi_rsdps_base + args.acpi_rsdps_size); - let mut env = slice::from_raw_parts(env_base as *const u8, env_size); - - // This is kind of a hack, only used for an extended KernelArgs struct. - let extended_kargs = if &env[..4] == b"\x7f=V2" { - println!("Using EXT kernel args."); - env = &env[4..]; - let ext_kargs = &*(args_ptr as *const KernelArgsExt); - - if (ext_kargs.length as usize) < mem::size_of::() { - None - } else { - Some(ext_kargs) - } - } else { None }; - - let ext_mem_ranges = if let Some(ref xargs) = extended_kargs { - Some([(xargs.acpi_rsdps_base as usize, xargs.acpi_rsdps_size as usize)]) + let ext_mem_ranges = if args.acpi_rsdps_base != 0 && args.acpi_rsdps_size > 0 { + Some([(args.acpi_rsdps_base as usize, args.acpi_rsdps_size as usize)]) } else { None }; @@ -177,7 +143,7 @@ pub unsafe extern fn kstart(args_ptr: *const KernelArgs) -> ! { // Read ACPI tables, starts APs #[cfg(feature = "acpi")] { - acpi::init(&mut active_table, extended_kargs.as_ref().map(|kargs| (kargs.acpi_rsdps_base, kargs.acpi_rsdps_size))); + acpi::init(&mut active_table, if args.acpi_rsdps_base != 0 && args.acpi_rsdps_size > 0 { Some((args.acpi_rsdps_base, args.acpi_rsdps_size)) } else { None }); device::init_after_acpi(&mut active_table); } @@ -193,7 +159,7 @@ pub unsafe extern fn kstart(args_ptr: *const KernelArgs) -> ! { BSP_READY.store(true, Ordering::SeqCst); - env + slice::from_raw_parts(env_base as *const u8, env_size) }; crate::kmain(CPU_COUNT.load(Ordering::SeqCst), env); From 5490de9fd26db43610e4f0a891d27479aea83dcc Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Sat, 18 Apr 2020 17:33:03 +0200 Subject: [PATCH 04/13] Fix a page fault. --- src/arch/x86_64/start.rs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/arch/x86_64/start.rs b/src/arch/x86_64/start.rs index 86d9cbc..11f8404 100644 --- a/src/arch/x86_64/start.rs +++ b/src/arch/x86_64/start.rs @@ -70,6 +70,8 @@ pub unsafe extern fn kstart(args_ptr: *const KernelArgs) -> ! { let stack_size = args.stack_size as usize; let env_base = args.env_base as usize; let env_size = args.env_size as usize; + let acpi_rsdps_base = args.acpi_rsdps_base; + let acpi_rsdps_size = args.acpi_rsdps_size; // BSS should already be zero { @@ -83,10 +85,10 @@ pub unsafe extern fn kstart(args_ptr: *const KernelArgs) -> ! { println!("Kernel: {:X}:{:X}", kernel_base, kernel_base + kernel_size); println!("Stack: {:X}:{:X}", stack_base, stack_base + stack_size); println!("Env: {:X}:{:X}", env_base, env_base + env_size); - println!("RSDPs: {:X}:{:X}", args.acpi_rsdps_base, args.acpi_rsdps_base + args.acpi_rsdps_size); + println!("RSDPs: {:X}:{:X}", acpi_rsdps_base, acpi_rsdps_base + acpi_rsdps_size); let ext_mem_ranges = if args.acpi_rsdps_base != 0 && args.acpi_rsdps_size > 0 { - Some([(args.acpi_rsdps_base as usize, args.acpi_rsdps_size as usize)]) + Some([(acpi_rsdps_base as usize, acpi_rsdps_size as usize)]) } else { None }; @@ -143,7 +145,7 @@ pub unsafe extern fn kstart(args_ptr: *const KernelArgs) -> ! { // Read ACPI tables, starts APs #[cfg(feature = "acpi")] { - acpi::init(&mut active_table, if args.acpi_rsdps_base != 0 && args.acpi_rsdps_size > 0 { Some((args.acpi_rsdps_base, args.acpi_rsdps_size)) } else { None }); + acpi::init(&mut active_table, if acpi_rsdps_base != 0 && acpi_rsdps_size > 0 { Some((acpi_rsdps_base, acpi_rsdps_size)) } else { None }); device::init_after_acpi(&mut active_table); } From 290098b5a4124e1d9a52479648e1cd92c61ec3df Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Sat, 18 Apr 2020 18:21:04 +0200 Subject: [PATCH 05/13] impl Debug for AmlValue. --- Cargo.toml | 2 +- src/acpi/aml/namespace.rs | 27 ++++++++++++--------------- src/arch/x86_64/device/ioapic.rs | 5 +++++ 3 files changed, 18 insertions(+), 16 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index d750a8c..56e9d96 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -31,7 +31,7 @@ version = "0.29.0" default-features = false [features] -default = ["serial_debug"] +default = ["serial_debug", "acpi"] acpi = [] doc = [] graphical_debug = [] diff --git a/src/acpi/aml/namespace.rs b/src/acpi/aml/namespace.rs index c420c9d..8716398 100644 --- a/src/acpi/aml/namespace.rs +++ b/src/acpi/aml/namespace.rs @@ -14,7 +14,7 @@ use super::AmlError; use crate::acpi::{SdtSignature, get_signature_from_index, get_index_from_signature}; -#[derive(Clone)] +#[derive(Clone, Debug)] pub enum FieldSelector { Region(String), Bank { @@ -28,7 +28,7 @@ pub enum FieldSelector { } } -#[derive(Clone)] +#[derive(Clone, Debug)] pub enum ObjectReference { ArgObj(u8), LocalObj(u8), @@ -36,7 +36,7 @@ pub enum ObjectReference { Index(Box, Box) } -#[derive(Clone)] +#[derive(Clone, Debug)] pub struct Method { pub arg_count: u8, pub serialized: bool, @@ -44,14 +44,14 @@ pub struct Method { pub term_list: Vec } -#[derive(Clone)] +#[derive(Clone, Debug)] pub struct BufferField { pub source_buf: Box, pub index: Box, pub length: Box } -#[derive(Clone)] +#[derive(Clone, Debug)] pub struct FieldUnit { pub selector: FieldSelector, pub connection: Box, @@ -60,19 +60,19 @@ pub struct FieldUnit { pub length: usize } -#[derive(Clone)] +#[derive(Clone, Debug)] pub struct Device { pub obj_list: Vec, pub notify_methods: BTreeMap> } -#[derive(Clone)] +#[derive(Clone, Debug)] pub struct ThermalZone { pub obj_list: Vec, pub notify_methods: BTreeMap> } -#[derive(Clone)] +#[derive(Clone, Debug)] pub struct Processor { pub proc_id: u8, pub p_blk: Option, @@ -80,7 +80,7 @@ pub struct Processor { pub notify_methods: BTreeMap> } -#[derive(Clone)] +#[derive(Clone, Debug)] pub struct OperationRegion { pub region: RegionSpace, pub offset: Box, @@ -89,13 +89,14 @@ pub struct OperationRegion { pub accessed_by: Option } -#[derive(Clone)] +#[derive(Clone, Debug)] pub struct PowerResource { pub system_level: u8, pub resource_order: u16, pub obj_list: Vec } +#[derive(Debug)] pub struct Accessor { pub read: fn(usize) -> u64, pub write: fn(usize, u64) @@ -110,7 +111,7 @@ impl Clone for Accessor { } } -#[derive(Clone)] +#[derive(Clone, Debug)] pub enum AmlValue { None, Uninitialized, @@ -136,10 +137,6 @@ pub enum AmlValue { ThermalZone(ThermalZone) } -impl Debug for AmlValue { - fn fmt(&self, _f: &mut Formatter) -> Result<(), Error> { Ok(()) } -} - impl AmlValue { pub fn get_type_string(&self) -> String { match *self { diff --git a/src/arch/x86_64/device/ioapic.rs b/src/arch/x86_64/device/ioapic.rs index 2897ed9..d296678 100644 --- a/src/arch/x86_64/device/ioapic.rs +++ b/src/arch/x86_64/device/ioapic.rs @@ -306,6 +306,11 @@ pub unsafe fn init(active_table: &mut ActivePageTable) { _ => (), } } + + let namespace_guard = crate::acpi::ACPI_TABLE.namespace.read(); + for (k, v) in namespace_guard.as_ref().unwrap().iter() { + println!("{} = {:?}", k, v); + } } println!("I/O APICs: {:?}, overrides: {:?}", ioapics(), src_overrides()); From dc3452650cfb593de80b64eac0d2bfba4339f569 Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Sat, 18 Apr 2020 23:17:37 +0200 Subject: [PATCH 06/13] Execute AML code after IOAPIC init, which tells the firmware that the I/O APIC is used rather than the 8259 PIC. --- src/acpi/mod.rs | 2 +- src/arch/x86_64/device/ioapic.rs | 19 ++++++++++++++++++- 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/src/acpi/mod.rs b/src/acpi/mod.rs index 7d79c7c..96f50a1 100644 --- a/src/acpi/mod.rs +++ b/src/acpi/mod.rs @@ -35,7 +35,7 @@ pub mod madt; mod rsdt; mod sdt; mod xsdt; -mod aml; +pub mod aml; mod rxsdt; mod rsdp; diff --git a/src/arch/x86_64/device/ioapic.rs b/src/arch/x86_64/device/ioapic.rs index d296678..ecee0e8 100644 --- a/src/arch/x86_64/device/ioapic.rs +++ b/src/arch/x86_64/device/ioapic.rs @@ -306,11 +306,12 @@ pub unsafe fn init(active_table: &mut ActivePageTable) { _ => (), } } - +/* let namespace_guard = crate::acpi::ACPI_TABLE.namespace.read(); for (k, v) in namespace_guard.as_ref().unwrap().iter() { println!("{} = {:?}", k, v); } +*/ } println!("I/O APICs: {:?}, overrides: {:?}", ioapics(), src_overrides()); @@ -358,6 +359,22 @@ pub unsafe fn init(active_table: &mut ActivePageTable) { } println!("I/O APICs: {:?}, overrides: {:?}", ioapics(), src_overrides()); irq::set_irq_method(irq::IrqMethod::Apic); + + // tell the firmware that we're using APIC rather than the default 8259 PIC. + #[cfg(feature = "acpi")] + { + let method = { + let namespace_guard = crate::acpi::ACPI_TABLE.namespace.read(); + if let Some(value) = namespace_guard.as_ref().unwrap().get("\\_PIC") { + value.get_as_method().ok() + } else { + None + } + }; + if let Some(m) = method { + m.execute("\\_PIC".into(), vec!(crate::acpi::aml::AmlValue::Integer(1))); + } + } } fn get_override(irq: u8) -> Option<&'static Override> { src_overrides().iter().find(|over| over.bus_irq == irq) From d1ece2c811a689a2671bcd54fb3c30a506822152 Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Sun, 12 Apr 2020 00:50:47 +0200 Subject: [PATCH 07/13] Add a basic acpi: scheme, currently only for MCFG. --- src/acpi/mod.rs | 6 +- src/scheme/acpi.rs | 362 +++++++++++++++++++++++++++++++++++++++++++++ src/scheme/mod.rs | 10 ++ 3 files changed, 375 insertions(+), 3 deletions(-) create mode 100644 src/scheme/acpi.rs diff --git a/src/acpi/mod.rs b/src/acpi/mod.rs index 96f50a1..5f507b2 100644 --- a/src/acpi/mod.rs +++ b/src/acpi/mod.rs @@ -33,7 +33,7 @@ mod dmar; mod fadt; pub mod madt; mod rsdt; -mod sdt; +pub mod sdt; mod xsdt; pub mod aml; mod rxsdt; @@ -42,7 +42,7 @@ mod rsdp; const TRAMPOLINE: usize = 0x7E00; const AP_STARTUP: usize = TRAMPOLINE + 512; -fn get_sdt(sdt_address: usize, active_table: &mut ActivePageTable) -> &'static Sdt { +pub fn get_sdt(sdt_address: usize, active_table: &mut ActivePageTable) -> &'static Sdt { { let page = Page::containing_address(VirtualAddress::new(sdt_address)); if active_table.translate_page(page).is_none() { @@ -193,7 +193,7 @@ pub fn set_global_s_state(state: u8) { } } -type SdtSignature = (String, [u8; 6], [u8; 8]); +pub type SdtSignature = (String, [u8; 6], [u8; 8]); pub static SDT_POINTERS: RwLock>> = RwLock::new(None); pub static SDT_ORDER: RwLock>> = RwLock::new(None); diff --git a/src/scheme/acpi.rs b/src/scheme/acpi.rs new file mode 100644 index 0000000..93a9a19 --- /dev/null +++ b/src/scheme/acpi.rs @@ -0,0 +1,362 @@ +use core::convert::{TryFrom, TryInto}; +use core::sync::atomic::{self, AtomicUsize}; +use core::fmt::Write; +use core::str; + +use alloc::vec::Vec; +use alloc::collections::BTreeMap; + +use syscall::scheme::Scheme; +use syscall::data::Stat; +use syscall::flag::{O_DIRECTORY, O_STAT, O_ACCMODE, O_WRONLY, O_RDWR}; +use syscall::error::{EACCES, EBADF, EBADFD, EISDIR, EINVAL, EIO, ENOENT, ENOTDIR}; +use syscall::{MODE_DIR, MODE_FILE, SEEK_CUR, SEEK_END, SEEK_SET}; +use syscall::{Error, Result}; + +use spin::{Mutex, RwLock}; + +use crate::acpi::SdtSignature; +use crate::acpi::sdt::Sdt; +use crate::paging::ActivePageTable; + +#[derive(Clone, Copy)] +struct PhysSlice { + phys_ptr: usize, + len: usize, + /// These appear to be identity mapped, so this is technically not needed. + virt: usize, +} + +/// A scheme used to access ACPI tables needed for some drivers to function (e.g. pcid with the +/// PCIe "MCFG" table). +/// +/// # Layout +/// * `/tables` +/// * _can be listed to retrieve the available tables_ +/// * e.g. MCFG-- +/// * _maybe_ the MADT, in case some userspace driver takes care of the I/O APIC. +/// * _perhaps_ some interface for e.g. power management. +pub struct AcpiScheme { + handles: RwLock>>, + tables: Vec<(SdtSignature, PhysSlice)>, + next_fd: AtomicUsize, +} + +const TOPLEVEL_DIR_CONTENTS: &[u8] = b"tables\n"; +const ALLOWED_TABLES: &[[u8; 4]] = &[*b"MCFG"]; + +// XXX: Why can't core also have something like std::io::Take? It's not even real I/O! +/// An internal wrapper struct that limits the number of bytes that can be fmt-written, in order to +/// properly return the length when reading directories etc. The bytes that cannot be written will +/// be discarded. +struct Take<'a> { + buf: &'a mut [u8], + offset: usize, +} + +impl Take<'_> { + pub fn write_to_buf<'a>(buf: &'a mut [u8]) -> Take<'a> { + Take { + offset: 0, + buf, + } + } + pub fn bytes_currently_written(&self) -> usize { + self.offset + } +} + +impl<'a> core::fmt::Write for Take<'a> +{ + fn write_str(&mut self, string: &str) -> core::fmt::Result { + if self.offset > self.buf.len() { return Ok(()) } + + let string_bytes = string.as_bytes(); + let max = core::cmp::min(string_bytes.len() + self.offset, self.buf.len()) - self.offset; + self.buf[self.offset..self.offset + max].copy_from_slice(&string_bytes[..max]); + self.offset += max; + Ok(()) + } +} + +enum Handle { + TopLevel(usize), // seek offset + Tables(usize), // seek offset + + Table { + name: [u8; 4], + oem_id: [u8; 6], + oem_table_id: [u8; 8], + + offset: usize, // seek offset + }, +} + +impl AcpiScheme { + fn get_tables() -> Vec<(SdtSignature, PhysSlice)> { + let mut active_table = unsafe { ActivePageTable::new() }; + + let mut tables = Vec::new(); + + for allowed_tbl_name in ALLOWED_TABLES.iter() { + use crate::acpi::{find_sdt, get_sdt, get_sdt_signature}; + + // it appears that the SDTs are identity mapped, in which case we can just call get_sdt + // whenever we need to and use the slice as if it was physical. + + let table_name_str = str::from_utf8(allowed_tbl_name).expect("ACPI table name wasn't correct UTF-8"); + + for sdt in find_sdt(table_name_str) { + let virt = get_sdt(sdt as *const Sdt as usize, &mut active_table) as *const Sdt as usize; + let signature = get_sdt_signature(sdt); + let sdt_pointer = sdt as *const Sdt as usize; + let len = sdt.length as usize; + assert_eq!(virt, sdt_pointer); + tables.push((signature, PhysSlice { phys_ptr: sdt_pointer, len, virt })); + } + } + tables + } + pub fn new() -> Self { + Self { + handles: RwLock::new(BTreeMap::new()), + tables: Self::get_tables(), + next_fd: AtomicUsize::new(0), + } + } + fn lookup_signature_index(&self, name: [u8; 4], oem_id: [u8; 6], oem_table_id: [u8; 8]) -> Option { + self.tables.iter().position(|((sig_name, sig_oem_id, sig_oem_table_id), _)| sig_name.as_bytes() == &name && sig_oem_id == &oem_id && sig_oem_table_id == &oem_table_id) + } + fn lookup_signature(&self, name: [u8; 4], oem_id: [u8; 6], oem_table_id: [u8; 8]) -> Option { + Some(self.tables[self.lookup_signature_index(name, oem_id, oem_table_id)?].1) + } +} + +fn parse_table_filename(filename: &[u8]) -> Option<([u8; 4], [u8; 6], [u8; 8])> { + // the table identifier takes the form: + // 1. a four byte table name, like 'APIC' (MADT) or 'MCFG'. + // 2. a dash followed by 12 hexadecimal digits (6 bytes when decoded) composing the OEM ID. + // 3. another dash followed by 16 hex digits (8 bytes), composing the OEM Table ID. + // hence, the table is 4 + 1 + 12 + 1 + 16 = 34 bytes long. + if filename.len() != 34 { return None } + let mut table_identifier = [0u8; 34]; + table_identifier.copy_from_slice(filename); + + let table_name = &table_identifier[..4]; + if table_identifier[4] != b'-' { return None } + let oem_id_hex = &table_identifier[5..17]; + if table_identifier[17] != b'-' { return None } + let oem_table_id_hex = &table_identifier[18..34]; + + let oem_id_hex_str = str::from_utf8(oem_id_hex).ok()?; + let oem_table_id_hex_str = str::from_utf8(oem_table_id_hex).ok()?; + + let mut oem_id = [0u8; 6]; + + for index in 0..oem_id.len() { + oem_id[index] = u8::from_str_radix(&oem_id_hex_str[index * 2..(index + 1) * 2], 16).ok()?; + } + + let mut oem_table_id = [0u8; 8]; + + for index in 0..oem_table_id.len() { + oem_table_id[index] = u8::from_str_radix(&oem_table_id_hex_str[index * 2..(index + 1) * 2], 16).ok()?; + } + + Some((table_name.try_into().unwrap(), oem_id, oem_table_id)) +} +fn serialize_table_filename(buffer: &mut [u8], (table_name, oem_id, oem_table_id): ([u8; 4], [u8; 6], [u8; 8])) -> usize { + let mut wrapper = Take::write_to_buf(buffer); + write!(wrapper, "{}-", str::from_utf8(&table_name).expect("Acpi table id wasn't valid UTF-8")).unwrap(); + for b in &oem_id { + write!(wrapper, "{:2x}", b).unwrap(); + } + write!(wrapper, "-").unwrap(); + for b in &oem_table_id { + write!(wrapper, "{:2x}", b).unwrap(); + } + wrapper.bytes_currently_written() +} + +impl Scheme for AcpiScheme { + fn open(&self, path: &[u8], flags: usize, opener_uid: u32, _opener_gid: u32) -> Result { + if opener_uid != 0 { return Err(Error::new(EACCES)) } + + let path_str = str::from_utf8(path).or(Err(Error::new(ENOENT)))?; + let path_str = path_str.trim_start_matches('/'); + + // TODO: Use some kind of component iterator. + + let new_handle = if path_str.starts_with("tables") { + let subpath = (&path_str[6..]).trim_start_matches('/'); + + if subpath.is_empty() { + // List of ACPI tables + if (flags & O_DIRECTORY == 0 && flags & O_STAT == 0) || (flags & O_ACCMODE == O_WRONLY || flags & O_ACCMODE == O_RDWR) { + return Err(Error::new(EISDIR)); + } + Handle::Tables(0) + } else { + if (flags & O_DIRECTORY != 0 && flags & O_STAT == 0) { + return Err(Error::new(ENOTDIR)); + } + if flags & O_ACCMODE == O_WRONLY || flags & O_ACCMODE == O_RDWR { + return Err(Error::new(EINVAL)); + } + let (name, oem_id, oem_table_id) = parse_table_filename(subpath.as_bytes()).ok_or(Error::new(ENOENT))?; + + if self.lookup_signature_index(name, oem_id, oem_table_id).is_none() { + return Err(Error::new(ENOENT)); + } + Handle::Table { + name, + oem_id, + oem_table_id, + offset: 0, + } + } + } else if path.is_empty() { + // Top-level + if (flags & O_DIRECTORY == 0 && flags & O_STAT == 0) || (flags & O_ACCMODE == O_WRONLY || flags & O_ACCMODE == O_RDWR) { + return Err(Error::new(EISDIR)); + } + Handle::TopLevel(0) + } else { + return Err(Error::new(ENOENT)); + }; + let new_fd = self.next_fd.fetch_add(1, atomic::Ordering::SeqCst); + self.handles.write().insert(new_fd, Mutex::new(new_handle)); + Ok(new_fd) + } + fn fpath(&self, id: usize, buf: &mut [u8]) -> Result { + let handles_guard = self.handles.read(); + let handle = handles_guard.get(&id).ok_or(Error::new(EBADF))?.lock(); + + Ok(match &*handle { + &Handle::TopLevel(_) => { + let path = b"acpi:"; + let max = core::cmp::min(buf.len(), path.len()); + buf[..max].copy_from_slice(&path[..]); + max + } + &Handle::Tables(_) => { + let path = b"acpi:tables"; + let max = core::cmp::min(buf.len(), path.len()); + buf[..max].copy_from_slice(&path[..]); + max + } + &Handle::Table { name, oem_id, oem_table_id, .. } => { + let base_path = b"acpi:tables/"; + let base_max = core::cmp::min(buf.len(), base_path.len()); + buf[..base_max].copy_from_slice(&base_path[..]); + serialize_table_filename(&mut buf[base_max..], (name, oem_id, oem_table_id)) + } + }) + } + fn fstat(&self, id: usize, stat: &mut Stat) -> Result { + let handles_guard = self.handles.read(); + let handle = handles_guard.get(&id).ok_or(Error::new(EBADF))?.lock(); + + match &*handle { + &Handle::TopLevel(_) => { + stat.st_mode = MODE_DIR; + stat.st_size = TOPLEVEL_DIR_CONTENTS.len() as u64; + } + &Handle::Tables(_) => { + stat.st_mode = MODE_DIR; + stat.st_size = (self.tables.len() * 35) as u64; // fixed size of 34 bytes for the file names, plus a newline + } + &Handle::Table { name, oem_id, oem_table_id, .. } => { + let len = self.lookup_signature(name, oem_id, oem_table_id).ok_or(Error::new(EBADFD))?.len; + + stat.st_mode = MODE_FILE; + stat.st_size = len as u64; + } + } + Ok(0) + } + fn seek(&self, id: usize, pos: usize, whence: usize) -> Result { + let handles_guard = self.handles.read(); + let mut handle = handles_guard.get(&id).ok_or(Error::new(EBADF))?.lock(); + + let (cur_offset, length) = match &*handle { + &Handle::TopLevel(offset) => (offset, TOPLEVEL_DIR_CONTENTS.len()), + &Handle::Tables(offset) => (offset, self.tables.len() * 35), + &Handle::Table { name, oem_id, oem_table_id, offset } => (offset, self.lookup_signature(name, oem_id, oem_table_id).ok_or(Error::new(EBADFD))?.len), + }; + let new_offset = match whence { + SEEK_CUR => core::cmp::min(cur_offset + pos, length), + SEEK_END => core::cmp::min(length + pos, length), + SEEK_SET => core::cmp::min(length, pos), + _ => return Err(Error::new(EINVAL)), + }; + match &mut *handle { + &mut Handle::Table { ref mut offset, .. } | &mut Handle::Tables(ref mut offset) | &mut Handle::TopLevel(ref mut offset) => *offset = new_offset, + } + Ok(new_offset) + } + fn read(&self, id: usize, mut buf: &mut [u8]) -> Result { + let handles_guard = self.handles.read(); + let mut handle = handles_guard.get(&id).ok_or(Error::new(EBADF))?.lock(); + + match &mut *handle { + &mut Handle::TopLevel(ref mut offset) => { + let max_bytes_to_read = core::cmp::min(buf.len(), TOPLEVEL_DIR_CONTENTS.len()); + let bytes_to_read = core::cmp::max(max_bytes_to_read, *offset) - *offset; + buf[..bytes_to_read].copy_from_slice(&TOPLEVEL_DIR_CONTENTS[*offset..*offset + bytes_to_read]); + *offset += bytes_to_read; + Ok(bytes_to_read) + } + &mut Handle::Tables(ref mut offset) => { + if *offset >= self.tables.len() * 35 { + return Ok(0); + } + // one really good thing with fixed size filenames, is that no index has to be + // stored anywhere! + let base_table_index = *offset / 35; + let mut bytes_to_skip = *offset % 35; + let mut bytes_read = 0; + + for index in base_table_index..self.tables.len() { + let &(ref name_string, oem_id, oem_table_id) = &self.tables[index].0; + let signature = (name_string.as_bytes().try_into().or(Err(Error::new(EIO)))?, oem_id, oem_table_id); + + let mut src_buf = [0u8; 35]; + serialize_table_filename(&mut src_buf[..34], signature); + src_buf[34] = b'\n'; + + let max_bytes_to_read = core::cmp::min(buf.len(), src_buf.len()); + let bytes_to_read = core::cmp::max(max_bytes_to_read, bytes_to_skip) - bytes_to_skip; + buf[..bytes_to_read].copy_from_slice(&src_buf[..bytes_to_read]); + bytes_read += bytes_to_read; + bytes_to_skip = 0; + buf = &mut buf[..bytes_to_read]; + } + *offset += bytes_read; + Ok(bytes_read) + } + &mut Handle::Table { + name, + oem_id, + oem_table_id, + ref mut offset, + } => { + let index = self.lookup_signature_index(name, oem_id, oem_table_id).ok_or(Error::new(EBADFD))?; + let (_, PhysSlice { phys_ptr, len, virt: old_virt }) = self.tables[index]; + assert_eq!(phys_ptr, old_virt); + let new_virt = crate::acpi::get_sdt(phys_ptr, unsafe { &mut ActivePageTable::new() }) as *const Sdt as usize; + + let table_contents = unsafe { core::slice::from_raw_parts(new_virt as *const u8, len) }; + + let max_bytes_to_read = core::cmp::min(buf.len(), table_contents.len()); + let bytes_to_read = core::cmp::max(max_bytes_to_read, *offset) - *offset; + buf[..bytes_to_read].copy_from_slice(&table_contents[*offset..*offset + bytes_to_read]); + *offset += bytes_to_read; + Ok(bytes_to_read) + } + } + } + fn write(&self, id: usize, buf: &[u8]) -> Result { + Err(Error::new(EBADF)) + } +} diff --git a/src/scheme/mod.rs b/src/scheme/mod.rs index e539de9..5f92107 100644 --- a/src/scheme/mod.rs +++ b/src/scheme/mod.rs @@ -16,6 +16,9 @@ use spin::{Once, RwLock, RwLockReadGuard, RwLockWriteGuard}; use crate::syscall::error::*; use crate::syscall::scheme::Scheme; +#[cfg(feature = "acpi")] +use self::acpi::AcpiScheme; + use self::debug::DebugScheme; use self::event::EventScheme; use self::initfs::InitFsScheme; @@ -28,6 +31,10 @@ use self::root::RootScheme; use self::sys::SysScheme; use self::time::TimeScheme; +/// When compiled with the "acpi" feature - `acpi:` - allows drivers to read a limited set of ACPI tables. +#[cfg(feature = "acpi")] +pub mod acpi; + /// `debug:` - provides access to serial console pub mod debug; @@ -136,6 +143,9 @@ impl SchemeList { let ns = self.new_ns(); // These schemes should only be available on the root + #[cfg(feature = "acpi")] { + self.insert(ns, Box::new(*b"acpi"), |_| Arc::new(AcpiScheme::new())).unwrap(); + } self.insert(ns, Box::new(*b"debug"), |scheme_id| Arc::new(DebugScheme::new(scheme_id))).unwrap(); self.insert(ns, Box::new(*b"initfs"), |_| Arc::new(InitFsScheme::new())).unwrap(); self.insert(ns, Box::new(*b"irq"), |scheme_id| Arc::new(IrqScheme::new(scheme_id))).unwrap(); From 45fe0406253026604d68775fbcbd423549a7cb28 Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Sun, 12 Apr 2020 18:32:21 +0200 Subject: [PATCH 08/13] rustfmt. --- src/scheme/acpi.rs | 184 +++++++++++++++++++++++++++++++++------------ 1 file changed, 138 insertions(+), 46 deletions(-) diff --git a/src/scheme/acpi.rs b/src/scheme/acpi.rs index 93a9a19..b11739b 100644 --- a/src/scheme/acpi.rs +++ b/src/scheme/acpi.rs @@ -1,22 +1,22 @@ use core::convert::{TryFrom, TryInto}; -use core::sync::atomic::{self, AtomicUsize}; use core::fmt::Write; use core::str; +use core::sync::atomic::{self, AtomicUsize}; -use alloc::vec::Vec; use alloc::collections::BTreeMap; +use alloc::vec::Vec; -use syscall::scheme::Scheme; use syscall::data::Stat; -use syscall::flag::{O_DIRECTORY, O_STAT, O_ACCMODE, O_WRONLY, O_RDWR}; -use syscall::error::{EACCES, EBADF, EBADFD, EISDIR, EINVAL, EIO, ENOENT, ENOTDIR}; -use syscall::{MODE_DIR, MODE_FILE, SEEK_CUR, SEEK_END, SEEK_SET}; +use syscall::error::{EACCES, EBADF, EBADFD, EINVAL, EIO, EISDIR, ENOENT, ENOTDIR}; +use syscall::flag::{O_ACCMODE, O_DIRECTORY, O_RDWR, O_STAT, O_WRONLY}; +use syscall::scheme::Scheme; use syscall::{Error, Result}; +use syscall::{MODE_DIR, MODE_FILE, SEEK_CUR, SEEK_END, SEEK_SET}; use spin::{Mutex, RwLock}; -use crate::acpi::SdtSignature; use crate::acpi::sdt::Sdt; +use crate::acpi::SdtSignature; use crate::paging::ActivePageTable; #[derive(Clone, Copy)] @@ -56,20 +56,18 @@ struct Take<'a> { impl Take<'_> { pub fn write_to_buf<'a>(buf: &'a mut [u8]) -> Take<'a> { - Take { - offset: 0, - buf, - } + Take { offset: 0, buf } } pub fn bytes_currently_written(&self) -> usize { self.offset } } -impl<'a> core::fmt::Write for Take<'a> -{ +impl<'a> core::fmt::Write for Take<'a> { fn write_str(&mut self, string: &str) -> core::fmt::Result { - if self.offset > self.buf.len() { return Ok(()) } + if self.offset > self.buf.len() { + return Ok(()); + } let string_bytes = string.as_bytes(); let max = core::cmp::min(string_bytes.len() + self.offset, self.buf.len()) - self.offset; @@ -80,8 +78,8 @@ impl<'a> core::fmt::Write for Take<'a> } enum Handle { - TopLevel(usize), // seek offset - Tables(usize), // seek offset + TopLevel(usize), // seek offset + Tables(usize), // seek offset Table { name: [u8; 4], @@ -104,15 +102,24 @@ impl AcpiScheme { // it appears that the SDTs are identity mapped, in which case we can just call get_sdt // whenever we need to and use the slice as if it was physical. - let table_name_str = str::from_utf8(allowed_tbl_name).expect("ACPI table name wasn't correct UTF-8"); + let table_name_str = + str::from_utf8(allowed_tbl_name).expect("ACPI table name wasn't correct UTF-8"); for sdt in find_sdt(table_name_str) { - let virt = get_sdt(sdt as *const Sdt as usize, &mut active_table) as *const Sdt as usize; + let virt = + get_sdt(sdt as *const Sdt as usize, &mut active_table) as *const Sdt as usize; let signature = get_sdt_signature(sdt); let sdt_pointer = sdt as *const Sdt as usize; let len = sdt.length as usize; assert_eq!(virt, sdt_pointer); - tables.push((signature, PhysSlice { phys_ptr: sdt_pointer, len, virt })); + tables.push(( + signature, + PhysSlice { + phys_ptr: sdt_pointer, + len, + virt, + }, + )); } } tables @@ -124,10 +131,26 @@ impl AcpiScheme { next_fd: AtomicUsize::new(0), } } - fn lookup_signature_index(&self, name: [u8; 4], oem_id: [u8; 6], oem_table_id: [u8; 8]) -> Option { - self.tables.iter().position(|((sig_name, sig_oem_id, sig_oem_table_id), _)| sig_name.as_bytes() == &name && sig_oem_id == &oem_id && sig_oem_table_id == &oem_table_id) + fn lookup_signature_index( + &self, + name: [u8; 4], + oem_id: [u8; 6], + oem_table_id: [u8; 8], + ) -> Option { + self.tables + .iter() + .position(|((sig_name, sig_oem_id, sig_oem_table_id), _)| { + sig_name.as_bytes() == &name + && sig_oem_id == &oem_id + && sig_oem_table_id == &oem_table_id + }) } - fn lookup_signature(&self, name: [u8; 4], oem_id: [u8; 6], oem_table_id: [u8; 8]) -> Option { + fn lookup_signature( + &self, + name: [u8; 4], + oem_id: [u8; 6], + oem_table_id: [u8; 8], + ) -> Option { Some(self.tables[self.lookup_signature_index(name, oem_id, oem_table_id)?].1) } } @@ -138,14 +161,20 @@ fn parse_table_filename(filename: &[u8]) -> Option<([u8; 4], [u8; 6], [u8; 8])> // 2. a dash followed by 12 hexadecimal digits (6 bytes when decoded) composing the OEM ID. // 3. another dash followed by 16 hex digits (8 bytes), composing the OEM Table ID. // hence, the table is 4 + 1 + 12 + 1 + 16 = 34 bytes long. - if filename.len() != 34 { return None } + if filename.len() != 34 { + return None; + } let mut table_identifier = [0u8; 34]; table_identifier.copy_from_slice(filename); let table_name = &table_identifier[..4]; - if table_identifier[4] != b'-' { return None } + if table_identifier[4] != b'-' { + return None; + } let oem_id_hex = &table_identifier[5..17]; - if table_identifier[17] != b'-' { return None } + if table_identifier[17] != b'-' { + return None; + } let oem_table_id_hex = &table_identifier[18..34]; let oem_id_hex_str = str::from_utf8(oem_id_hex).ok()?; @@ -160,14 +189,23 @@ fn parse_table_filename(filename: &[u8]) -> Option<([u8; 4], [u8; 6], [u8; 8])> let mut oem_table_id = [0u8; 8]; for index in 0..oem_table_id.len() { - oem_table_id[index] = u8::from_str_radix(&oem_table_id_hex_str[index * 2..(index + 1) * 2], 16).ok()?; + oem_table_id[index] = + u8::from_str_radix(&oem_table_id_hex_str[index * 2..(index + 1) * 2], 16).ok()?; } Some((table_name.try_into().unwrap(), oem_id, oem_table_id)) } -fn serialize_table_filename(buffer: &mut [u8], (table_name, oem_id, oem_table_id): ([u8; 4], [u8; 6], [u8; 8])) -> usize { +fn serialize_table_filename( + buffer: &mut [u8], + (table_name, oem_id, oem_table_id): ([u8; 4], [u8; 6], [u8; 8]), +) -> usize { let mut wrapper = Take::write_to_buf(buffer); - write!(wrapper, "{}-", str::from_utf8(&table_name).expect("Acpi table id wasn't valid UTF-8")).unwrap(); + write!( + wrapper, + "{}-", + str::from_utf8(&table_name).expect("Acpi table id wasn't valid UTF-8") + ) + .unwrap(); for b in &oem_id { write!(wrapper, "{:2x}", b).unwrap(); } @@ -180,7 +218,9 @@ fn serialize_table_filename(buffer: &mut [u8], (table_name, oem_id, oem_table_id impl Scheme for AcpiScheme { fn open(&self, path: &[u8], flags: usize, opener_uid: u32, _opener_gid: u32) -> Result { - if opener_uid != 0 { return Err(Error::new(EACCES)) } + if opener_uid != 0 { + return Err(Error::new(EACCES)); + } let path_str = str::from_utf8(path).or(Err(Error::new(ENOENT)))?; let path_str = path_str.trim_start_matches('/'); @@ -192,7 +232,9 @@ impl Scheme for AcpiScheme { if subpath.is_empty() { // List of ACPI tables - if (flags & O_DIRECTORY == 0 && flags & O_STAT == 0) || (flags & O_ACCMODE == O_WRONLY || flags & O_ACCMODE == O_RDWR) { + if (flags & O_DIRECTORY == 0 && flags & O_STAT == 0) + || (flags & O_ACCMODE == O_WRONLY || flags & O_ACCMODE == O_RDWR) + { return Err(Error::new(EISDIR)); } Handle::Tables(0) @@ -203,9 +245,13 @@ impl Scheme for AcpiScheme { if flags & O_ACCMODE == O_WRONLY || flags & O_ACCMODE == O_RDWR { return Err(Error::new(EINVAL)); } - let (name, oem_id, oem_table_id) = parse_table_filename(subpath.as_bytes()).ok_or(Error::new(ENOENT))?; + let (name, oem_id, oem_table_id) = + parse_table_filename(subpath.as_bytes()).ok_or(Error::new(ENOENT))?; - if self.lookup_signature_index(name, oem_id, oem_table_id).is_none() { + if self + .lookup_signature_index(name, oem_id, oem_table_id) + .is_none() + { return Err(Error::new(ENOENT)); } Handle::Table { @@ -217,7 +263,9 @@ impl Scheme for AcpiScheme { } } else if path.is_empty() { // Top-level - if (flags & O_DIRECTORY == 0 && flags & O_STAT == 0) || (flags & O_ACCMODE == O_WRONLY || flags & O_ACCMODE == O_RDWR) { + if (flags & O_DIRECTORY == 0 && flags & O_STAT == 0) + || (flags & O_ACCMODE == O_WRONLY || flags & O_ACCMODE == O_RDWR) + { return Err(Error::new(EISDIR)); } Handle::TopLevel(0) @@ -245,7 +293,12 @@ impl Scheme for AcpiScheme { buf[..max].copy_from_slice(&path[..]); max } - &Handle::Table { name, oem_id, oem_table_id, .. } => { + &Handle::Table { + name, + oem_id, + oem_table_id, + .. + } => { let base_path = b"acpi:tables/"; let base_max = core::cmp::min(buf.len(), base_path.len()); buf[..base_max].copy_from_slice(&base_path[..]); @@ -266,8 +319,16 @@ impl Scheme for AcpiScheme { stat.st_mode = MODE_DIR; stat.st_size = (self.tables.len() * 35) as u64; // fixed size of 34 bytes for the file names, plus a newline } - &Handle::Table { name, oem_id, oem_table_id, .. } => { - let len = self.lookup_signature(name, oem_id, oem_table_id).ok_or(Error::new(EBADFD))?.len; + &Handle::Table { + name, + oem_id, + oem_table_id, + .. + } => { + let len = self + .lookup_signature(name, oem_id, oem_table_id) + .ok_or(Error::new(EBADFD))? + .len; stat.st_mode = MODE_FILE; stat.st_size = len as u64; @@ -282,7 +343,17 @@ impl Scheme for AcpiScheme { let (cur_offset, length) = match &*handle { &Handle::TopLevel(offset) => (offset, TOPLEVEL_DIR_CONTENTS.len()), &Handle::Tables(offset) => (offset, self.tables.len() * 35), - &Handle::Table { name, oem_id, oem_table_id, offset } => (offset, self.lookup_signature(name, oem_id, oem_table_id).ok_or(Error::new(EBADFD))?.len), + &Handle::Table { + name, + oem_id, + oem_table_id, + offset, + } => ( + offset, + self.lookup_signature(name, oem_id, oem_table_id) + .ok_or(Error::new(EBADFD))? + .len, + ), }; let new_offset = match whence { SEEK_CUR => core::cmp::min(cur_offset + pos, length), @@ -291,7 +362,9 @@ impl Scheme for AcpiScheme { _ => return Err(Error::new(EINVAL)), }; match &mut *handle { - &mut Handle::Table { ref mut offset, .. } | &mut Handle::Tables(ref mut offset) | &mut Handle::TopLevel(ref mut offset) => *offset = new_offset, + &mut Handle::Table { ref mut offset, .. } + | &mut Handle::Tables(ref mut offset) + | &mut Handle::TopLevel(ref mut offset) => *offset = new_offset, } Ok(new_offset) } @@ -303,7 +376,8 @@ impl Scheme for AcpiScheme { &mut Handle::TopLevel(ref mut offset) => { let max_bytes_to_read = core::cmp::min(buf.len(), TOPLEVEL_DIR_CONTENTS.len()); let bytes_to_read = core::cmp::max(max_bytes_to_read, *offset) - *offset; - buf[..bytes_to_read].copy_from_slice(&TOPLEVEL_DIR_CONTENTS[*offset..*offset + bytes_to_read]); + buf[..bytes_to_read] + .copy_from_slice(&TOPLEVEL_DIR_CONTENTS[*offset..*offset + bytes_to_read]); *offset += bytes_to_read; Ok(bytes_to_read) } @@ -319,14 +393,19 @@ impl Scheme for AcpiScheme { for index in base_table_index..self.tables.len() { let &(ref name_string, oem_id, oem_table_id) = &self.tables[index].0; - let signature = (name_string.as_bytes().try_into().or(Err(Error::new(EIO)))?, oem_id, oem_table_id); + let signature = ( + name_string.as_bytes().try_into().or(Err(Error::new(EIO)))?, + oem_id, + oem_table_id, + ); let mut src_buf = [0u8; 35]; serialize_table_filename(&mut src_buf[..34], signature); src_buf[34] = b'\n'; let max_bytes_to_read = core::cmp::min(buf.len(), src_buf.len()); - let bytes_to_read = core::cmp::max(max_bytes_to_read, bytes_to_skip) - bytes_to_skip; + let bytes_to_read = + core::cmp::max(max_bytes_to_read, bytes_to_skip) - bytes_to_skip; buf[..bytes_to_read].copy_from_slice(&src_buf[..bytes_to_read]); bytes_read += bytes_to_read; bytes_to_skip = 0; @@ -341,16 +420,29 @@ impl Scheme for AcpiScheme { oem_table_id, ref mut offset, } => { - let index = self.lookup_signature_index(name, oem_id, oem_table_id).ok_or(Error::new(EBADFD))?; - let (_, PhysSlice { phys_ptr, len, virt: old_virt }) = self.tables[index]; + let index = self + .lookup_signature_index(name, oem_id, oem_table_id) + .ok_or(Error::new(EBADFD))?; + let ( + _, + PhysSlice { + phys_ptr, + len, + virt: old_virt, + }, + ) = self.tables[index]; assert_eq!(phys_ptr, old_virt); - let new_virt = crate::acpi::get_sdt(phys_ptr, unsafe { &mut ActivePageTable::new() }) as *const Sdt as usize; + let new_virt = + crate::acpi::get_sdt(phys_ptr, unsafe { &mut ActivePageTable::new() }) + as *const Sdt as usize; - let table_contents = unsafe { core::slice::from_raw_parts(new_virt as *const u8, len) }; + let table_contents = + unsafe { core::slice::from_raw_parts(new_virt as *const u8, len) }; let max_bytes_to_read = core::cmp::min(buf.len(), table_contents.len()); let bytes_to_read = core::cmp::max(max_bytes_to_read, *offset) - *offset; - buf[..bytes_to_read].copy_from_slice(&table_contents[*offset..*offset + bytes_to_read]); + buf[..bytes_to_read] + .copy_from_slice(&table_contents[*offset..*offset + bytes_to_read]); *offset += bytes_to_read; Ok(bytes_to_read) } From 8c351e076843fbe408de88185f5af6035a92b28d Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Sat, 18 Apr 2020 16:36:18 +0200 Subject: [PATCH 09/13] Use the I/O APIC when applicable. --- src/arch/x86_64/device/ioapic.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/arch/x86_64/device/ioapic.rs b/src/arch/x86_64/device/ioapic.rs index ecee0e8..8c5c930 100644 --- a/src/arch/x86_64/device/ioapic.rs +++ b/src/arch/x86_64/device/ioapic.rs @@ -306,12 +306,15 @@ pub unsafe fn init(active_table: &mut ActivePageTable) { _ => (), } } +<<<<<<< HEAD /* let namespace_guard = crate::acpi::ACPI_TABLE.namespace.read(); for (k, v) in namespace_guard.as_ref().unwrap().iter() { println!("{} = {:?}", k, v); } */ +======= +>>>>>>> c723256... Use the I/O APIC when applicable. } println!("I/O APICs: {:?}, overrides: {:?}", ioapics(), src_overrides()); From 02ca8edfc5a61bcac3ee6ee0517f67d42e0b0896 Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Sat, 18 Apr 2020 23:17:37 +0200 Subject: [PATCH 10/13] Execute AML code after IOAPIC init, which tells the firmware that the I/O APIC is used rather than the 8259 PIC. --- src/arch/x86_64/device/ioapic.rs | 9 --------- 1 file changed, 9 deletions(-) diff --git a/src/arch/x86_64/device/ioapic.rs b/src/arch/x86_64/device/ioapic.rs index 8c5c930..99f299e 100644 --- a/src/arch/x86_64/device/ioapic.rs +++ b/src/arch/x86_64/device/ioapic.rs @@ -306,15 +306,6 @@ pub unsafe fn init(active_table: &mut ActivePageTable) { _ => (), } } -<<<<<<< HEAD -/* - let namespace_guard = crate::acpi::ACPI_TABLE.namespace.read(); - for (k, v) in namespace_guard.as_ref().unwrap().iter() { - println!("{} = {:?}", k, v); - } -*/ -======= ->>>>>>> c723256... Use the I/O APIC when applicable. } println!("I/O APICs: {:?}, overrides: {:?}", ioapics(), src_overrides()); From 3bc4b9a69158b1e377891c1b33544fae539286b1 Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Sun, 19 Apr 2020 09:38:36 +0200 Subject: [PATCH 11/13] Allow the MADT to be read from userspace, and fix a typo that prevented multiple tables from being listed correctly. --- src/scheme/acpi.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/scheme/acpi.rs b/src/scheme/acpi.rs index b11739b..12eb2d7 100644 --- a/src/scheme/acpi.rs +++ b/src/scheme/acpi.rs @@ -43,7 +43,7 @@ pub struct AcpiScheme { } const TOPLEVEL_DIR_CONTENTS: &[u8] = b"tables\n"; -const ALLOWED_TABLES: &[[u8; 4]] = &[*b"MCFG"]; +const ALLOWED_TABLES: &[[u8; 4]] = &[*b"MCFG", *b"APIC"]; // XXX: Why can't core also have something like std::io::Take? It's not even real I/O! /// An internal wrapper struct that limits the number of bytes that can be fmt-written, in order to @@ -409,7 +409,7 @@ impl Scheme for AcpiScheme { buf[..bytes_to_read].copy_from_slice(&src_buf[..bytes_to_read]); bytes_read += bytes_to_read; bytes_to_skip = 0; - buf = &mut buf[..bytes_to_read]; + buf = &mut buf[bytes_to_read..]; } *offset += bytes_read; Ok(bytes_read) From 00312bdf32026b5045d634dc9993b122716ddd57 Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Sun, 19 Apr 2020 13:21:59 +0200 Subject: [PATCH 12/13] Revert to old default-features. --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 56e9d96..d750a8c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -31,7 +31,7 @@ version = "0.29.0" default-features = false [features] -default = ["serial_debug", "acpi"] +default = ["serial_debug"] acpi = [] doc = [] graphical_debug = [] From 94134751190ff05bb72d86298838fd128406a305 Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Sun, 19 Apr 2020 13:25:43 +0200 Subject: [PATCH 13/13] Don't use the I/O APIC by default, since this would require pcid to know the _PRT (PCI routing table) to use and map the interrupt pins to the correct IRQs. xhcid is unaffected by this though, since it uses MSI-X. All ACPI handling will be done in userspace before the infrastructure necessary would make sense (I don't think adding serde to the kernel would be optimal, and how else would all of the ACPI namespace be parsed?). --- src/arch/x86_64/device/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/arch/x86_64/device/mod.rs b/src/arch/x86_64/device/mod.rs index 656d2b2..0c3f4de 100644 --- a/src/arch/x86_64/device/mod.rs +++ b/src/arch/x86_64/device/mod.rs @@ -16,7 +16,7 @@ pub unsafe fn init(active_table: &mut ActivePageTable) { } pub unsafe fn init_after_acpi(active_table: &mut ActivePageTable) { // this will disable the IOAPIC if needed. - ioapic::init(active_table); + //ioapic::init(active_table); } #[cfg(feature = "acpi")]