import: upstream ACPI SRAT/SLIT parsing modules (cb88ed59)
Import upstream kernel commit cb88ed59 'Parse ACPI tables for NUMA
information without allocating from the heap'. Adds new files:
- src/acpi/slit.rs (SLIT table parsing)
- src/acpi/srat/mod.rs (SRAT table framework)
- src/acpi/srat/x86.rs (x86 SRAT implementation)
- src/acpi/srat/aarch64.rs (aarch64 SRAT stub)
These modules are not yet wired into our acpi/mod.rs module tree.
Our fork uses a userspace numad daemon for NUMA discovery, while
upstream uses kernel-side parsing. The files are available for
future convergence work.
Existing files (numa.rs, startup/mod.rs, start.rs) retain our Red
Bear implementation — the upstream refactoring (numa::init() →
numa::dump_info()) does not apply to our different init path.
This commit is contained in:
@@ -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<A: Arch>(&self, allocator: &mut BumpAllocator<A>) -> &'static mut [u8] {
|
||||
unsafe { slice::from_raw_parts_mut(self.address as *mut u8, (self.no * self.no) as usize) }
|
||||
}
|
||||
}
|
||||
|
||||
pub fn init<A: Arch>(allocator: &mut BumpAllocator<A>, 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
use crate::acpi::srat::Srat;
|
||||
|
||||
pub fn init_srat(
|
||||
allocator: &mut BumpAllocator<A>,
|
||||
srat: &Srat,
|
||||
) -> (&'static [u32], &'static [u32], &'static [NumaMemory]) {
|
||||
// todo
|
||||
}
|
||||
@@ -0,0 +1,176 @@
|
||||
//! See <https://uefi.org/htmlspecs/ACPI_Spec_6_4_html/05_ACPI_Software_Programming_Model/ACPI_Software_Programming_Model.html#system-resource-affinity-table-srat>
|
||||
|
||||
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<A: Arch>(
|
||||
allocator: &mut BumpAllocator<A>,
|
||||
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<Self::Item> {
|
||||
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::<LegacyProcessorLocalAffinity>() + 2);
|
||||
*(entry.add(2) as *const LegacyProcessorLocalAffinity)
|
||||
}),
|
||||
|
||||
1 => SratEntry::MemoryAffinity(unsafe {
|
||||
assert!(entry_len as usize == size_of::<MemoryAffinity>() + 10);
|
||||
*(entry.add(2) as *const MemoryAffinity)
|
||||
}),
|
||||
2 => SratEntry::ProcessorLocalAffinity(unsafe {
|
||||
assert!(entry_len as usize == size_of::<ProcessorLocalAffinity>() + 8);
|
||||
*(entry.add(4) as *const ProcessorLocalAffinity)
|
||||
}),
|
||||
3 => SratEntry::GiccAffinity(unsafe {
|
||||
assert!(entry_len as usize == size_of::<GiccAffinity>() + 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)
|
||||
}
|
||||
}
|
||||
@@ -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<A: Arch>(
|
||||
allocator: &mut BumpAllocator<A>,
|
||||
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::<u32>()) / 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::<A>::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::<A>::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)
|
||||
}
|
||||
Reference in New Issue
Block a user