From 4db9673e2aeffc1b8a56325f2c9d070b3adb9a78 Mon Sep 17 00:00:00 2001 From: Jeremy Soller Date: Thu, 31 Oct 2024 10:53:58 -0600 Subject: [PATCH] Support SPCR and clean up device memory allocation --- src/acpi/gtdt.rs | 7 +- src/acpi/hpet.rs | 71 ++++++++--------- src/acpi/madt/arch/aarch64.rs | 31 ++------ src/acpi/madt/arch/x86.rs | 5 +- src/acpi/mod.rs | 15 ++++ src/acpi/spcr.rs | 117 +++++++++++++++++++++++++++++ src/arch/aarch64/start.rs | 15 ---- src/arch/x86_shared/device/hpet.rs | 36 ++++----- src/arch/x86_shared/time.rs | 6 +- src/memory/mod.rs | 24 +++++- 10 files changed, 218 insertions(+), 109 deletions(-) create mode 100644 src/acpi/spcr.rs diff --git a/src/acpi/gtdt.rs b/src/acpi/gtdt.rs index 6b25501e41..218c942c8f 100644 --- a/src/acpi/gtdt.rs +++ b/src/acpi/gtdt.rs @@ -1,5 +1,5 @@ use alloc::boxed::Box; -use core::{mem, ptr}; +use core::mem; use super::{find_sdt, sdt::Sdt}; use crate::{ @@ -58,10 +58,9 @@ impl Gtdt { unsafe { IRQ_CHIP.irq_enable(gsiv as u32) }; } - pub fn new(sdt: &'static Sdt) -> Option { + pub fn new(sdt: &'static Sdt) -> Option<&'static Gtdt> { if &sdt.signature == b"GTDT" && sdt.length as usize >= mem::size_of::() { - let s = unsafe { ptr::read((sdt as *const Sdt) as *const Gtdt) }; - Some(s) + Some(unsafe { &*((sdt as *const Sdt) as *const Gtdt) }) } else { None } diff --git a/src/acpi/hpet.rs b/src/acpi/hpet.rs index cc583a1b5e..c3c05c45a0 100644 --- a/src/acpi/hpet.rs +++ b/src/acpi/hpet.rs @@ -2,22 +2,9 @@ use core::{mem, ptr}; use core::ptr::{read_volatile, write_volatile}; -use crate::{ - memory::{Frame, KernelMapper}, - paging::{entry::EntryFlags, PageFlags, PhysicalAddress}, -}; +use crate::memory::{map_device_memory, PhysicalAddress, PAGE_SIZE}; -use super::{find_sdt, sdt::Sdt, ACPI_TABLE}; - -#[repr(C, packed)] -#[derive(Clone, Copy, Debug, Default)] -pub struct GenericAddressStructure { - _address_space: u8, - _bit_width: u8, - _bit_offset: u8, - _access_size: u8, - pub address: u64, -} +use super::{find_sdt, sdt::Sdt, GenericAddressStructure, ACPI_TABLE}; #[repr(C, packed)] #[derive(Clone, Copy, Debug)] @@ -56,8 +43,16 @@ impl Hpet { pub fn new(sdt: &'static Sdt) -> Option { if &sdt.signature == b"HPET" && sdt.length as usize >= mem::size_of::() { let s = unsafe { ptr::read((sdt as *const Sdt) as *const Hpet) }; - unsafe { s.base_address.init(&mut KernelMapper::lock()) }; - Some(s) + if s.base_address.address_space == 0 { + unsafe { s.map() }; + Some(s) + } else { + log::warn!( + "HPET has unsupported address space {}", + s.base_address.address_space + ); + None + } } else { None } @@ -66,14 +61,18 @@ impl Hpet { //TODO: x86 use assumes only one HPET and only one GenericAddressStructure #[cfg(target_arch = "x86")] -impl GenericAddressStructure { - pub unsafe fn init(&self, mapper: &mut KernelMapper) { - use crate::paging::{Page, VirtualAddress}; +impl Hpet { + pub unsafe fn map(&self) { + use crate::{ + memory::{Frame, KernelMapper}, + paging::{entry::EntryFlags, Page, VirtualAddress}, + }; + use rmm::PageFlags; - let frame = Frame::containing(PhysicalAddress::new(self.address as usize)); + let frame = Frame::containing(PhysicalAddress::new(self.base_address.address as usize)); let page = Page::containing_address(VirtualAddress::new(crate::HPET_OFFSET)); - mapper + KernelMapper::lock() .get_mut() .expect( "KernelMapper locked re-entrant while mapping memory for GenericAddressStructure", @@ -99,31 +98,23 @@ impl GenericAddressStructure { } #[cfg(not(target_arch = "x86"))] -impl GenericAddressStructure { - pub unsafe fn init(&self, mapper: &mut KernelMapper) { - let frame = Frame::containing(PhysicalAddress::new(self.address as usize)); - let (_, result) = mapper - .get_mut() - .expect( - "KernelMapper locked re-entrant while mapping memory for GenericAddressStructure", - ) - .map_linearly( - frame.base(), - PageFlags::new() - .write(true) - .custom_flag(EntryFlags::NO_CACHE.bits(), true), - ) - .expect("failed to map memory for GenericAddressStructure"); - result.flush(); +impl Hpet { + pub unsafe fn map(&self) { + map_device_memory( + PhysicalAddress::new(self.base_address.address as usize), + PAGE_SIZE, + ); } pub unsafe fn read_u64(&self, offset: usize) -> u64 { - read_volatile((self.address as usize + offset + crate::PHYS_OFFSET) as *const u64) + read_volatile( + (self.base_address.address as usize + offset + crate::PHYS_OFFSET) as *const u64, + ) } pub unsafe fn write_u64(&mut self, offset: usize, value: u64) { write_volatile( - (self.address as usize + offset + crate::PHYS_OFFSET) as *mut u64, + (self.base_address.address as usize + offset + crate::PHYS_OFFSET) as *mut u64, value, ); } diff --git a/src/acpi/madt/arch/aarch64.rs b/src/acpi/madt/arch/aarch64.rs index b879004d52..dc460b08a5 100644 --- a/src/acpi/madt/arch/aarch64.rs +++ b/src/acpi/madt/arch/aarch64.rs @@ -1,5 +1,4 @@ use alloc::{boxed::Box, vec::Vec}; -use rmm::{Arch, PageFlags}; use super::{Madt, MadtEntry}; use crate::{ @@ -7,28 +6,10 @@ use crate::{ gic::{GenericInterruptController, GicCpuIf, GicDistIf}, gicv3::{GicV3, GicV3CpuIf}, }, - dtb::irqchip::{InterruptController, IrqChipItem, IRQ_CHIP}, - memory::{Frame, KernelMapper, PhysicalAddress}, - paging::{entry::EntryFlags, RmmA}, + dtb::irqchip::{IrqChipItem, IRQ_CHIP}, + memory::{map_device_memory, PhysicalAddress, PAGE_SIZE}, }; -unsafe fn map_gic_page(phys: PhysicalAddress) { - let frame = Frame::containing(phys); - let (_, result) = KernelMapper::lock() - .get_mut() - .expect("KernelMapper locked re-entrant while mapping memory for GIC") - .map_linearly( - frame.base(), - PageFlags::new() - .write(true) - .custom_flag(EntryFlags::NO_CACHE.bits(), true), - ) - .expect("failed to map memory for GIC"); - result.flush(); -} - -fn add_irqchip(irqchip: Box) {} - pub(super) fn init(madt: Madt) { let mut gicd_opt = None; let mut giccs = Vec::new(); @@ -55,8 +36,8 @@ pub(super) fn init(madt: Madt) { let mut gic_dist_if = GicDistIf::default(); unsafe { let phys = PhysicalAddress::new(gicd.physical_base_address as usize); - map_gic_page(phys); - gic_dist_if.init(RmmA::phys_to_virt(phys).data()); + let virt = map_device_memory(phys, PAGE_SIZE); + gic_dist_if.init(virt.data()); }; log::info!("{:#x?}", gic_dist_if); match gicd.gic_version { @@ -65,8 +46,8 @@ pub(super) fn init(madt: Madt) { let mut gic_cpu_if = GicCpuIf::default(); unsafe { let phys = PhysicalAddress::new(gicc.physical_base_address as usize); - map_gic_page(phys); - gic_cpu_if.init(RmmA::phys_to_virt(phys).data()) + let virt = map_device_memory(phys, PAGE_SIZE); + gic_cpu_if.init(virt.data()) }; log::info!("{:#x?}", gic_cpu_if); let gic = GenericInterruptController { diff --git a/src/acpi/madt/arch/x86.rs b/src/acpi/madt/arch/x86.rs index 508c146003..d97ea62543 100644 --- a/src/acpi/madt/arch/x86.rs +++ b/src/acpi/madt/arch/x86.rs @@ -89,7 +89,10 @@ pub(super) fn init(madt: Madt) { }; AP_READY.store(false, Ordering::SeqCst); - print!(" AP {} APIC {}:", ap_local_apic.processor, ap_local_apic.id); + print!( + " AP {} APIC {}:", + ap_local_apic.processor, ap_local_apic.id + ); // Send INIT IPI { diff --git a/src/acpi/mod.rs b/src/acpi/mod.rs index 42d685f0c1..465f7e79d8 100644 --- a/src/acpi/mod.rs +++ b/src/acpi/mod.rs @@ -23,6 +23,8 @@ mod rsdp; mod rsdt; mod rxsdt; pub mod sdt; +#[cfg(target_arch = "aarch64")] +mod spcr; mod xsdt; unsafe fn map_linearly(addr: PhysicalAddress, len: usize, mapper: &mut crate::paging::PageMapper) { @@ -64,6 +66,16 @@ pub fn get_sdt(sdt_address: usize, mapper: &mut KernelMapper) -> &'static Sdt { sdt } +#[repr(C, packed)] +#[derive(Clone, Copy, Debug, Default)] +pub struct GenericAddressStructure { + pub address_space: u8, + pub bit_width: u8, + pub bit_offset: u8, + pub access_size: u8, + pub address: u64, +} + pub enum RxsdtEnum { Rsdt(Rsdt), Xsdt(Xsdt), @@ -145,6 +157,9 @@ pub unsafe fn init(already_supplied_rsdp: Option<*const u8>) { } } + //TODO: support this on any arch + #[cfg(target_arch = "aarch64")] + spcr::Spcr::init(); // TODO: Enumerate processors in userspace, and then provide an ACPI-independent interface // to initialize enumerated processors to userspace? Madt::init(); diff --git a/src/acpi/spcr.rs b/src/acpi/spcr.rs new file mode 100644 index 0000000000..51cf179352 --- /dev/null +++ b/src/acpi/spcr.rs @@ -0,0 +1,117 @@ +use core::mem; + +use super::{find_sdt, sdt::Sdt, GenericAddressStructure}; +use crate::{ + device::{ + serial::{SerialKind, COM1}, + uart_pl011, + }, + memory::{map_device_memory, PhysicalAddress, PAGE_SIZE}, +}; + +#[derive(Clone, Copy, Debug)] +#[repr(C, packed)] +pub struct Spcr { + pub header: Sdt, + pub interface_type: u8, + _reserved: [u8; 3], + pub base_address: GenericAddressStructure, + pub interrupt_type: u8, + pub irq: u8, + pub gsiv: u32, + pub configured_baud_rate: u8, + pub parity: u8, + pub stop_bits: u8, + pub flow_control: u8, + pub terminal_type: u8, + pub language: u8, + pub pci_device_id: u16, + pub pci_vendor_id: u16, + pub pci_bus: u8, + pub pci_device: u8, + pub pci_function: u8, + pub pci_flags: u32, + pub pci_segment: u8, + /*TODO: these fields are optional based on the table revision + pub uart_clock_frequency: u32, + pub precise_baud_rate: u32, + pub namespace_string_length: u16, + pub namespace_string_offset: u16, + */ + // namespace_string +} + +impl Spcr { + pub fn init() { + let spcr_sdt = find_sdt("SPCR"); + let spcr = if spcr_sdt.len() == 1 { + match Spcr::new(spcr_sdt[0]) { + Some(spcr) => spcr, + None => { + log::warn!("Failed to parse SPCR"); + return; + } + } + } else { + log::warn!("Unable to find SPCR"); + return; + }; + + if spcr.base_address.address == 0 { + // Serial disabled + return; + } + + if spcr.header.revision >= 2 { + match spcr.interface_type { + 3 => { + // PL011 + if spcr.base_address.address_space == 0 + && spcr.base_address.bit_width == 32 + && spcr.base_address.bit_offset == 0 + && spcr.base_address.access_size == 3 + { + let virt = unsafe { + map_device_memory( + PhysicalAddress::new(spcr.base_address.address as usize), + PAGE_SIZE, + ) + }; + let serial_port = uart_pl011::SerialPort::new(virt.data(), false); + *COM1.lock() = Some(SerialKind::Pl011(serial_port)) + } else { + log::warn!( + "SPCR unsuppoted address for PL011 {:#x?}", + spcr.base_address + ); + } + } + //TODO: support more types! + unsupported => { + log::warn!( + "SPCR revision {} unsupported interface type {}", + spcr.header.revision, + unsupported + ); + } + } + } else if spcr.header.revision == 1 { + match spcr.interface_type { + //TODO: support more types! + unsupported => { + log::warn!("SPCR revision 1 unsupported interface type {}", unsupported); + } + } + } else { + log::warn!("SPCR unsupported revision {}", spcr.header.revision); + } + } + + pub fn new(sdt: &'static Sdt) -> Option<&'static Spcr> { + if &sdt.signature == b"SPCR" && sdt.length as usize >= mem::size_of::() { + Some(unsafe { &*((sdt as *const Sdt) as *const Spcr) }) + } else { + None + } + } +} diff --git a/src/arch/aarch64/start.rs b/src/arch/aarch64/start.rs index 5314d97646..ec0641bc39 100644 --- a/src/arch/aarch64/start.rs +++ b/src/arch/aarch64/start.rs @@ -102,18 +102,6 @@ pub unsafe extern "C" fn kstart(args_ptr: *const KernelArgs) -> ! { // Try to find serial port prior to logging if let Ok(dtb) = &dtb_res { device::serial::init_early(dtb); - } else { - /* - //TODO: This is for QEMU debugging when using ACPI - use crate::device::{ - serial::{SerialKind, COM1}, - uart_pl011, - }; - let mut serial_port = - uart_pl011::SerialPort::new(crate::PHYS_OFFSET + 0x9000000, false); - serial_port.init(false); - *COM1.lock() = Some(SerialKind::Pl011(serial_port)) - */ } // Initialize logger @@ -174,9 +162,6 @@ pub unsafe extern "C" fn kstart(args_ptr: *const KernelArgs) -> ! { register_bootloader_areas(args.areas_base, args.areas_size); if let Ok(dtb) = &dtb_res { register_dev_memory_ranges(dtb); - } else { - //TODO: THIS IS JUST FOR QEMU SERIAL WHEN ACPI - register_memory_region(0x9000000, 0x1000, BootloaderMemoryKind::Device); } register_memory_region( diff --git a/src/arch/x86_shared/device/hpet.rs b/src/arch/x86_shared/device/hpet.rs index b52fe87a16..9b57103951 100644 --- a/src/arch/x86_shared/device/hpet.rs +++ b/src/arch/x86_shared/device/hpet.rs @@ -25,13 +25,12 @@ pub unsafe fn init(hpet: &mut Hpet) -> bool { // Disable HPET { - let mut config_word = hpet.base_address.read_u64(GENERAL_CONFIG_OFFSET); + let mut config_word = hpet.read_u64(GENERAL_CONFIG_OFFSET); config_word &= !(LEG_RT_CNF | ENABLE_CNF); - hpet.base_address - .write_u64(GENERAL_CONFIG_OFFSET, config_word); + hpet.write_u64(GENERAL_CONFIG_OFFSET, config_word); } - let capability = hpet.base_address.read_u64(CAPABILITY_OFFSET); + let capability = hpet.read_u64(CAPABILITY_OFFSET); if capability & LEG_RT_CAP == 0 { log::warn!("HPET missing capability LEG_RT_CAP"); return false; @@ -40,29 +39,26 @@ pub unsafe fn init(hpet: &mut Hpet) -> bool { let period_fs = capability >> 32; let divisor = (pit::RATE as u64 * 1_000_000) / period_fs; - let t0_capabilities = hpet.base_address.read_u64(T0_CONFIG_CAPABILITY_OFFSET); + let t0_capabilities = hpet.read_u64(T0_CONFIG_CAPABILITY_OFFSET); if t0_capabilities & PER_INT_CAP == 0 { log::warn!("HPET T0 missing capability PER_INT_CAP"); return false; } - let counter = hpet.base_address.read_u64(MAIN_COUNTER_OFFSET); + let counter = hpet.read_u64(MAIN_COUNTER_OFFSET); let t0_config_word: u64 = TN_VAL_SET_CNF | TN_TYPE_CNF | TN_INT_ENB_CNF; - hpet.base_address - .write_u64(T0_CONFIG_CAPABILITY_OFFSET, t0_config_word); + hpet.write_u64(T0_CONFIG_CAPABILITY_OFFSET, t0_config_word); // set accumulator value - hpet.base_address - .write_u64(T0_COMPARATOR_OFFSET, counter + divisor); + hpet.write_u64(T0_COMPARATOR_OFFSET, counter + divisor); // set interval - hpet.base_address.write_u64(T0_COMPARATOR_OFFSET, divisor); + hpet.write_u64(T0_COMPARATOR_OFFSET, divisor); // Enable interrupts from the HPET { - let mut config_word: u64 = hpet.base_address.read_u64(GENERAL_CONFIG_OFFSET); + let mut config_word: u64 = hpet.read_u64(GENERAL_CONFIG_OFFSET); config_word |= LEG_RT_CNF | ENABLE_CNF; - hpet.base_address - .write_u64(GENERAL_CONFIG_OFFSET, config_word); + hpet.write_u64(GENERAL_CONFIG_OFFSET, config_word); } println!("HPET After Init"); @@ -74,7 +70,7 @@ pub unsafe fn init(hpet: &mut Hpet) -> bool { pub unsafe fn debug(hpet: &mut Hpet) { println!("HPET @ {:#x}", { hpet.base_address.address }); - let capability = hpet.base_address.read_u64(CAPABILITY_OFFSET); + let capability = hpet.read_u64(CAPABILITY_OFFSET); { println!(" caps: {:#x}", capability); println!(" clock period: {}", (capability >> 32) as u32); @@ -88,16 +84,16 @@ pub unsafe fn debug(hpet: &mut Hpet) { println!(" revision: {}", capability as u8); } - let config_word = hpet.base_address.read_u64(GENERAL_CONFIG_OFFSET); + let config_word = hpet.read_u64(GENERAL_CONFIG_OFFSET); println!(" config: {:#x}", config_word); - let interrupt_status = hpet.base_address.read_u64(GENERAL_INTERRUPT_OFFSET); + let interrupt_status = hpet.read_u64(GENERAL_INTERRUPT_OFFSET); println!(" interrupt status: {:#x}", interrupt_status); - let counter = hpet.base_address.read_u64(MAIN_COUNTER_OFFSET); + let counter = hpet.read_u64(MAIN_COUNTER_OFFSET); println!(" counter: {:#x}", counter); - let t0_capabilities = hpet.base_address.read_u64(T0_CONFIG_CAPABILITY_OFFSET); + let t0_capabilities = hpet.read_u64(T0_CONFIG_CAPABILITY_OFFSET); println!(" T0 caps: {:#x}", t0_capabilities); println!( " interrupt routing: {:#x}", @@ -105,6 +101,6 @@ pub unsafe fn debug(hpet: &mut Hpet) { ); println!(" flags: {:#x}", t0_capabilities as u16); - let t0_comparator = hpet.base_address.read_u64(T0_COMPARATOR_OFFSET); + let t0_comparator = hpet.read_u64(T0_COMPARATOR_OFFSET); println!(" T0 comparator: {:#x}", t0_comparator); } diff --git a/src/arch/x86_shared/time.rs b/src/arch/x86_shared/time.rs index 3a02bf5935..d953a9c6db 100644 --- a/src/arch/x86_shared/time.rs +++ b/src/arch/x86_shared/time.rs @@ -19,11 +19,11 @@ fn hpet_or_pit() -> u128 { //TODO: improve performance // Current count - let counter = unsafe { hpet.base_address.read_u64(hpet::MAIN_COUNTER_OFFSET) }; + let counter = unsafe { hpet.read_u64(hpet::MAIN_COUNTER_OFFSET) }; // Comparator holds next interrupt count - let comparator = unsafe { hpet.base_address.read_u64(hpet::T0_COMPARATOR_OFFSET) }; + let comparator = unsafe { hpet.read_u64(hpet::T0_COMPARATOR_OFFSET) }; // Get period in femtoseconds - let capability = unsafe { hpet.base_address.read_u64(hpet::CAPABILITY_OFFSET) }; + let capability = unsafe { hpet.read_u64(hpet::CAPABILITY_OFFSET) }; // There seems to be a bug in qemu on macos that causes the calculation to produce 0 for // period_fs and hence a divide by zero calculating the divisor - workaround it while we diff --git a/src/memory/mod.rs b/src/memory/mod.rs index f60947b9c4..0ab2b16052 100644 --- a/src/memory/mod.rs +++ b/src/memory/mod.rs @@ -20,7 +20,7 @@ use crate::{ memory::{AccessMode, PfError}, }, kernel_executable_offsets::{__usercopy_end, __usercopy_start}, - paging::Page, + paging::{entry::EntryFlags, Page, PageFlags}, syscall::error::{Error, ENOMEM}, }; use rmm::{BumpAllocator, FrameAllocator, FrameCount, FrameUsage, TableKind, VirtualAddress}; @@ -233,6 +233,28 @@ pub unsafe fn deallocate_frame(frame: Frame) { deallocate_p2frame(frame, 0) } +// Helper function for quickly mapping device memory +pub unsafe fn map_device_memory(addr: PhysicalAddress, len: usize) -> VirtualAddress { + let mut mapper_lock = KernelMapper::lock(); + let mapper = mapper_lock + .get_mut() + .expect("KernelMapper mapper locked re-entrant in map_device_memory"); + let base = PhysicalAddress::new(crate::paging::round_down_pages(addr.data())); + let aligned_len = crate::paging::round_up_pages(len + (addr.data() - base.data())); + for page_idx in 0..aligned_len / crate::memory::PAGE_SIZE { + let (_, flush) = mapper + .map_linearly( + base.add(page_idx * crate::memory::PAGE_SIZE), + PageFlags::new() + .write(true) + .custom_flag(EntryFlags::NO_CACHE.bits(), true), + ) + .expect("failed to linearly map SDT"); + flush.flush(); + } + RmmA::phys_to_virt(addr) +} + const ORDER_COUNT: u32 = 11; const MAX_ORDER: u32 = ORDER_COUNT - 1;