diff --git a/src/acpi/slit.rs b/src/acpi/slit.rs new file mode 100644 index 0000000000..03c91597da --- /dev/null +++ b/src/acpi/slit.rs @@ -0,0 +1,44 @@ +use crate::{ + acpi::{rxsdt::Rxsdt, sdt::Sdt, RXSDT_ENUM}, + find_one_sdt, + memory::{round_up_pages, PAGE_SIZE}, + numa::{self}, +}; +use core::{ops::Add, slice}; +use hashbrown::HashMap; +use rmm::{Arch, BumpAllocator, FrameAllocator, FrameCount}; +use spin::once::Once; + +#[derive(Debug)] +pub struct Slit { + sdt: &'static Sdt, + no: u64, + address: *const u8, +} + +impl Slit { + pub fn new(sdt: &'static Sdt) -> Self { + Self { + sdt, + no: unsafe { *(sdt.data_address() as *const u64) }, + address: (sdt.data_address() + 8) as *const u8, + } + } + pub fn init(&self, allocator: &mut BumpAllocator) -> &'static mut [u8] { + unsafe { slice::from_raw_parts_mut(self.address as *mut u8, (self.no * self.no) as usize) } + } +} + +pub fn init(allocator: &mut BumpAllocator, distances: &Once<&'static [u8]>) { + if let Some(rxsdt) = RXSDT_ENUM.get() { + for sdt_addr in rxsdt.iter() { + let sdt = + unsafe { &*(crate::memory::RmmA::phys_to_virt(sdt_addr).data() as *const Sdt) }; + if &sdt.signature == b"SLIT" { + let slit = Slit::new(sdt); + distances.call_once(|| slit.init(allocator)); + return; + } + } + } +} diff --git a/src/acpi/srat/aarch64.rs b/src/acpi/srat/aarch64.rs new file mode 100644 index 0000000000..e6b4e6363e --- /dev/null +++ b/src/acpi/srat/aarch64.rs @@ -0,0 +1,8 @@ +use crate::acpi::srat::Srat; + +pub fn init_srat( + allocator: &mut BumpAllocator, + srat: &Srat, +) -> (&'static [u32], &'static [u32], &'static [NumaMemory]) { + // todo +} diff --git a/src/acpi/srat/mod.rs b/src/acpi/srat/mod.rs new file mode 100644 index 0000000000..d613f2c1aa --- /dev/null +++ b/src/acpi/srat/mod.rs @@ -0,0 +1,176 @@ +//! See + +use hashbrown::HashMap; +use rmm::{Arch, BumpAllocator}; +use spin::once::Once; + +use crate::{ + acpi::{find_sdt, get_sdt_signature, rxsdt::Rxsdt, sdt::Sdt, srat, RXSDT_ENUM}, + find_one_sdt, memory, + numa::NumaMemory, +}; + +#[cfg(target_arch = "aarch64")] +#[path = "aarch64.rs"] +mod arch; + +#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +#[path = "x86.rs"] +mod arch; + +#[repr(C, packed)] +pub struct Srat { + sdt: &'static Sdt, + entries: *const u8, +} + +pub fn init( + allocator: &mut BumpAllocator, + map: &Once<&'static [u32]>, + cpus: &Once<&'static [u32]>, + mem: &Once<&'static [NumaMemory]>, +) { + if let Some(rxsdt) = RXSDT_ENUM.get() { + for sdt_addr in rxsdt.iter() { + let sdt = unsafe { &*(memory::RmmA::phys_to_virt(sdt_addr).data() as *const Sdt) }; + if &sdt.signature == b"SRAT" { + let (a, b, c) = arch::init_srat(allocator, &Srat::new(sdt)); + map.call_once(|| a); + cpus.call_once(|| b); + mem.call_once(|| c); + return; + } + } + } +} + +impl Srat { + pub fn new(sdt: &'static Sdt) -> Self { + Self { + sdt, + entries: (sdt.data_address() + 12) as *const u8, + } + } +} + +impl<'a> IntoIterator for &'a Srat { + type Item = SratEntry; + + type IntoIter = SratIter<'a>; + + fn into_iter(self) -> Self::IntoIter { + SratIter { i: 0, srat: self } + } +} + +pub struct SratIter<'a> { + i: u32, + srat: &'a Srat, +} + +impl<'a> Iterator for SratIter<'a> { + type Item = SratEntry; + + fn next(&mut self) -> Option { + while self.i < self.srat.sdt.data_len() as u32 { + let entry = unsafe { self.srat.entries.add(self.i as usize) }; + let entry_len = unsafe { *self.srat.entries.add(self.i as usize + 1) }; + + let entry = Some(match unsafe { *entry } { + 0 => SratEntry::LegacyProcessorLocalAffinity(unsafe { + assert!(entry_len as usize == size_of::() + 2); + *(entry.add(2) as *const LegacyProcessorLocalAffinity) + }), + + 1 => SratEntry::MemoryAffinity(unsafe { + assert!(entry_len as usize == size_of::() + 10); + *(entry.add(2) as *const MemoryAffinity) + }), + 2 => SratEntry::ProcessorLocalAffinity(unsafe { + assert!(entry_len as usize == size_of::() + 8); + *(entry.add(4) as *const ProcessorLocalAffinity) + }), + 3 => SratEntry::GiccAffinity(unsafe { + assert!(entry_len as usize == size_of::() + 2); + *(entry.add(2) as *const GiccAffinity) + }), + // ignore GIC ITS Affinity and Generic Initiator Affinity + _ => { + self.i += entry_len as u32; + continue; + } + }); + self.i += entry_len as u32; + return entry; + } + None + } +} + +#[derive(Debug, Clone, Copy)] +pub enum SratEntry { + LegacyProcessorLocalAffinity(LegacyProcessorLocalAffinity), + MemoryAffinity(MemoryAffinity), + ProcessorLocalAffinity(ProcessorLocalAffinity), + GiccAffinity(GiccAffinity), + // unimplemented: Gic Its Affinity and Generic Initiator Affinity + // our current focus is only on memory and cpus +} + +#[repr(C, packed)] +#[derive(Clone, Copy, Debug)] +/// For legacy xAPIC systems +struct LegacyProcessorLocalAffinity { + proximity_domain_low: u8, + apic_id: u8, + flags: u32, + sapic_id: u8, + proximity_domain_high: [u8; 3], + clock_domain: u32, +} + +#[repr(C, packed)] +#[derive(Clone, Copy, Debug)] +struct MemoryAffinity { + proximity_domain: u32, + _reserved0: u16, + base_address_low: u32, + base_address_high: u32, + length_low: u32, + length_high: u32, + _reserved1: u32, + flags: u32, +} + +#[repr(C, packed)] +#[derive(Clone, Copy, Debug)] +/// For x2APIC systems +struct ProcessorLocalAffinity { + proximity_domain: u32, + x2apic_id: u32, + flags: u32, + clock_domain: u32, +} + +#[repr(C, packed)] +#[derive(Clone, Copy, Debug)] +struct GiccAffinity { + proximity_domain: u32, + processor_uid: u32, + flags: u32, + clock_domain: u32, +} + +#[inline(always)] +pub(crate) fn to_usize(low: u32, high: u32) -> usize { + #[cfg(target_pointer_width = "32")] + return low as usize; + + #[cfg(target_pointer_width = "64")] + { + let mut low_and_high = [0u8; 8]; + low_and_high[0..=3].copy_from_slice(low.to_le_bytes().as_slice()); + low_and_high[4..=7].copy_from_slice(high.to_le_bytes().as_slice()); + usize::from_le_bytes(low_and_high) + } +} diff --git a/src/acpi/srat/x86.rs b/src/acpi/srat/x86.rs new file mode 100644 index 0000000000..e65e71c4be --- /dev/null +++ b/src/acpi/srat/x86.rs @@ -0,0 +1,164 @@ +use core::{iter, slice}; + +use hashbrown::HashMap; +use rmm::{Arch, BumpAllocator, FrameAllocator}; + +use crate::{ + acpi::srat::{to_usize, Srat, SratEntry}, + cpu_set, + memory::{self, PAGE_SIZE}, + numa::{self, NumaMemory}, +}; + +#[inline(always)] +fn to_single_int(high: &[u8; 3], low: u8) -> u32 { + let mut high_and_low = [0u8; 4]; + high_and_low[0] = low; + (high_and_low[1], high_and_low[2], high_and_low[3]) = (high[0], high[1], high[2]); + u32::from_le_bytes(high_and_low) +} + +pub fn init_srat( + allocator: &mut BumpAllocator, + srat: &Srat, +) -> (&'static [u32], &'static [u32], &'static [NumaMemory]) { + let dom_node_map = allocator + .allocate(rmm::FrameCount::new( + memory::round_up_pages(numa::MAX_DOMAINS * size_of::()) / PAGE_SIZE, + )) + .expect("Failed to allocate memory for storing NUMA info"); + let mut mapper = unsafe { rmm::PageMapper::current(rmm::TableKind::Kernel, allocator) }; + let mut flags = rmm::PageFlags::::new(); + let flags = flags.write(true); + let dom_node_map_ptr = unsafe { + let (va, flush) = mapper + .map_linearly(dom_node_map, flags) + .expect("Failed to map NUMA info pages"); + flush.flush(); + va.data() as *mut u32 + }; + // Occupies 512 bytes (1/8th of a page) + let dom_node_map: &'static mut [u32] = + unsafe { slice::from_raw_parts_mut(dom_node_map_ptr, numa::MAX_DOMAINS) }; + dom_node_map.fill(u32::MAX); + + let mut cpu_count = 0; + let mut memory_count = 0; + + srat.into_iter().for_each(|e| match e { + SratEntry::LegacyProcessorLocalAffinity(legacy_processor_local_affinity) => cpu_count += 1, + SratEntry::MemoryAffinity(memory_affinity) => memory_count += 1, + SratEntry::ProcessorLocalAffinity(processor_local_affinity) => todo!(), + _ => (), + }); + + assert!( + memory_count <= numa::MAX_DOMAINS, + "Found {} memory blocks while only a maximum of {} are supported", + memory_count, + numa::MAX_DOMAINS + ); + + assert!( + cpu_count <= cpu_set::MAX_CPU_COUNT, + "Found more number of CPUs than supported" + ); + + // occupies 512 bytes (1/8th of a page) + let cpus: &'static mut [u32] = unsafe { + slice::from_raw_parts_mut( + dom_node_map_ptr.add(numa::MAX_DOMAINS) as *mut u32, + numa::MAX_DOMAINS, + ) + }; + + cpus.fill(u32::MAX); + + // total occupied till now: 1024 bytes, remaining 3072 bytes, can accomodate 128 memory entries + + let memories: &'static mut [NumaMemory] = unsafe { + slice::from_raw_parts_mut( + cpus.as_ptr().add(numa::MAX_DOMAINS) as *mut NumaMemory, + numa::MAX_DOMAINS, + ) + }; + + memories.fill(NumaMemory { + start: 0, + length: 0, + dom: 0, + _pad: [0; 4], + }); + + for affinity in srat { + match affinity { + SratEntry::LegacyProcessorLocalAffinity(legacy_processor_local_affinity) => { + if legacy_processor_local_affinity.flags & 1 == 0 { + // processor disabled + continue; + } + let dom = to_single_int( + &legacy_processor_local_affinity.proximity_domain_high, + legacy_processor_local_affinity.proximity_domain_low, + ); + if dom_node_map[dom as usize] == u32::MAX { + let node_id = numa::assign_node_id(true); + dom_node_map[dom as usize] = node_id as u32; + } + cpus[legacy_processor_local_affinity.apic_id as usize] = dom_node_map[dom as usize]; + } + SratEntry::MemoryAffinity(memory_affinity) => { + if memory_affinity.flags & 1 == 0 { + // memory is not enabled + continue; + } + if memory_affinity.flags & (1 << 1) != 0 { + // memory is hot-pluggable + continue; + } + let dom = memory_affinity.proximity_domain; + if memory_affinity.length_low == 0 { + continue; + } + let start = to_usize( + memory_affinity.base_address_low, + memory_affinity.base_address_high, + ); + let length = to_usize(memory_affinity.length_low, memory_affinity.length_high); + if dom_node_map[dom as usize] == u32::MAX { + let node_id = numa::assign_node_id(true); + dom_node_map[dom as usize] = node_id as u32; + } + memories[dom_node_map[dom as usize] as usize] = numa::NumaMemory { + start, + length, + dom: dom_node_map[dom as usize], + _pad: [0u8; 4], + }; + } + SratEntry::ProcessorLocalAffinity(processor_local_affinity) => { + if processor_local_affinity.flags & 1 == 0 { + // processor disabled + continue; + } + let dom = processor_local_affinity.proximity_domain; + if dom_node_map[dom as usize] == u32::MAX { + let node_id = numa::assign_node_id(true); + dom_node_map[dom as usize] = node_id as u32; + } + cpus[dom_node_map[dom as usize] as usize] = + processor_local_affinity.proximity_domain; + } + _ => continue, + } + } + let mut flags = rmm::PageFlags::::new(); + let flags = flags.write(false); + let flush = unsafe { + mapper + .remap(rmm::VirtualAddress::new(dom_node_map_ptr.addr()), flags) + .expect("Unable to make NUMA info page read-only") + }; + flush.flush(); + (dom_node_map, cpus, memories) +}