const MADT_HEADER_SIZE: usize = 44;
const ACPI_MADT_ENABLED: u32 = 1;
#[cfg(any(target_arch = "x86_64", target_arch = "aarch64"))]
use acpi::sdt::madt::Madt;
#[derive(Clone, Copy, Debug)]
pub struct CpuInfo {
pub physical_id: u32,
#[allow(dead_code)]
pub processor_id: u32,
pub enabled: bool,
}
fn non_empty_cpu_info_iter<I>(mut iter: I) -> Option<impl Iterator<Item = CpuInfo>>
where
I: Iterator<Item = CpuInfo>,
{
let first = iter.next()?;
Some(core::iter::once(first).chain(iter))
}
fn non_empty_enabled_cpu_id_iter<I>(cpu_info: I) -> Option<impl Iterator<Item = usize>>
where
I: Iterator<Item = CpuInfo>,
{
let mut ids = cpu_info
.filter(|info| info.enabled)
.map(|info| info.physical_id as usize);
let first = ids.next()?;
Some(core::iter::once(first).chain(ids))
}
#[cfg(target_arch = "x86_64")]
mod x86_64_impl {
use super::super::tables;
use super::{
ACPI_MADT_ENABLED, CpuInfo, MADT_HEADER_SIZE, Madt, non_empty_cpu_info_iter,
non_empty_enabled_cpu_id_iter,
};
const MADT_TYPE_LOCAL_APIC: u8 = 0;
const MADT_TYPE_LOCAL_X2APIC: u8 = 9;
#[derive(Clone, Copy, Debug)]
#[repr(C, packed)]
struct LocalApicEntry {
entry_type: u8,
length: u8,
processor_id: u8,
apic_id: u8,
flags: u32,
}
#[derive(Clone, Copy, Debug)]
#[repr(C, packed)]
struct LocalX2ApicEntry {
entry_type: u8,
length: u8,
_reserved: u16,
x2apic_id: u32,
flags: u32,
processor_id: u32,
}
const _: () = assert!(core::mem::size_of::<LocalApicEntry>() == 8);
const _: () = assert!(core::mem::size_of::<LocalX2ApicEntry>() == 16);
struct X86CpuIter {
_madt_mapping: acpi::PhysicalMapping<super::super::AcpiHandle, Madt>,
madt_ptr: *const u8,
madt_len: usize,
offset: usize,
}
impl Iterator for X86CpuIter {
type Item = CpuInfo;
fn next(&mut self) -> Option<Self::Item> {
while self.offset + 2 <= self.madt_len {
unsafe {
let entry_type = *self.madt_ptr.add(self.offset);
let entry_len = *self.madt_ptr.add(self.offset + 1) as usize;
if entry_len < 2 || self.offset + entry_len > self.madt_len {
return None;
}
let mut cpu_info = None;
if entry_type == MADT_TYPE_LOCAL_APIC
&& entry_len >= core::mem::size_of::<LocalApicEntry>()
{
let entry = core::ptr::read_unaligned(
self.madt_ptr.add(self.offset) as *const LocalApicEntry
);
cpu_info = Some(CpuInfo {
physical_id: entry.apic_id as u32,
processor_id: entry.processor_id as u32,
enabled: (entry.flags & ACPI_MADT_ENABLED) != 0,
});
} else if entry_type == MADT_TYPE_LOCAL_X2APIC
&& entry_len >= core::mem::size_of::<LocalX2ApicEntry>()
{
let entry = core::ptr::read_unaligned(
self.madt_ptr.add(self.offset) as *const LocalX2ApicEntry
);
cpu_info = Some(CpuInfo {
physical_id: entry.x2apic_id,
processor_id: entry.processor_id,
enabled: (entry.flags & ACPI_MADT_ENABLED) != 0,
});
}
self.offset += entry_len;
if let Some(info) = cpu_info {
return Some(info);
}
}
}
None
}
}
pub fn x86_64_cpu_info() -> Option<impl Iterator<Item = CpuInfo>> {
let tables = tables().ok()?;
let madt_mapping = tables.find_table::<Madt>()?;
let madt_ptr = madt_mapping.virtual_start.as_ptr() as *const u8;
let madt_len = madt_mapping.region_length;
non_empty_cpu_info_iter(X86CpuIter {
_madt_mapping: madt_mapping,
madt_ptr,
madt_len,
offset: MADT_HEADER_SIZE,
})
}
pub fn x86_64_cpu_id_list() -> Option<impl Iterator<Item = usize>> {
non_empty_enabled_cpu_id_iter(x86_64_cpu_info()?)
}
}
#[cfg(target_arch = "x86_64")]
pub use x86_64_impl::*;
#[cfg(target_arch = "aarch64")]
mod aarch64_impl {
use super::super::tables;
use super::{
ACPI_MADT_ENABLED, CpuInfo, MADT_HEADER_SIZE, Madt, non_empty_cpu_info_iter,
non_empty_enabled_cpu_id_iter,
};
const MADT_TYPE_GICC: u8 = 11;
#[derive(Clone, Copy, Debug)]
#[repr(C, packed)]
struct GiccHead {
entry_type: u8,
length: u8,
_reserved: u16,
cpu_interface_number: u32,
uid: u32,
flags: u32,
}
const _: () = assert!(core::mem::size_of::<GiccHead>() == 16);
struct Aarch64CpuIter {
_madt_mapping: acpi::PhysicalMapping<super::super::AcpiHandle, Madt>,
madt_ptr: *const u8,
madt_len: usize,
offset: usize,
}
impl Iterator for Aarch64CpuIter {
type Item = CpuInfo;
fn next(&mut self) -> Option<Self::Item> {
while self.offset + 2 <= self.madt_len {
unsafe {
let entry_type = *self.madt_ptr.add(self.offset);
let entry_len = *self.madt_ptr.add(self.offset + 1) as usize;
if entry_len < 2 || self.offset + entry_len > self.madt_len {
return None;
}
let mut cpu_info = None;
if entry_type == MADT_TYPE_GICC && entry_len >= core::mem::size_of::<GiccHead>()
{
let entry = core::ptr::read_unaligned(
self.madt_ptr.add(self.offset) as *const GiccHead
);
cpu_info = Some(CpuInfo {
physical_id: entry.cpu_interface_number,
processor_id: entry.uid,
enabled: (entry.flags & ACPI_MADT_ENABLED) != 0,
});
}
self.offset += entry_len;
if let Some(info) = cpu_info {
return Some(info);
}
}
}
None
}
}
pub fn aarch64_cpu_info() -> Option<impl Iterator<Item = CpuInfo>> {
let tables = tables().ok()?;
let madt_mapping = tables.find_table::<Madt>()?;
let madt_ptr = madt_mapping.virtual_start.as_ptr() as *const u8;
let madt_len = madt_mapping.region_length;
non_empty_cpu_info_iter(Aarch64CpuIter {
_madt_mapping: madt_mapping,
madt_ptr,
madt_len,
offset: MADT_HEADER_SIZE,
})
}
pub fn aarch64_cpu_id_list() -> Option<impl Iterator<Item = usize>> {
non_empty_enabled_cpu_id_iter(aarch64_cpu_info()?)
}
}
#[cfg(target_arch = "aarch64")]
pub use aarch64_impl::*;
#[cfg(target_arch = "riscv64")]
mod riscv64_impl {
use super::super::tables;
use super::{
ACPI_MADT_ENABLED, CpuInfo, MADT_HEADER_SIZE, non_empty_cpu_info_iter,
non_empty_enabled_cpu_id_iter,
};
use acpi::sdt::madt::Madt;
const MADT_TYPE_RINTC: u8 = 0x18;
#[derive(Clone, Copy, Debug)]
#[repr(C, packed)]
pub struct RintcEntry {
pub entry_type: u8,
pub length: u8,
pub version: u8,
_reserved1: u8,
pub flags: u32,
pub hart_id: u64,
pub uid: u32,
pub ext_intc_id: u32,
pub imsic_addr: u64,
pub imsic_size: u32,
}
impl RintcEntry {
#[inline]
pub fn is_enabled(&self) -> bool {
(self.flags & ACPI_MADT_ENABLED) != 0
}
}
const _: () = assert!(core::mem::size_of::<RintcEntry>() == 36);
struct Riscv64CpuIter {
_madt_mapping: acpi::PhysicalMapping<super::super::AcpiHandle, Madt>,
madt_ptr: *const u8,
madt_len: usize,
offset: usize,
}
impl Iterator for Riscv64CpuIter {
type Item = CpuInfo;
fn next(&mut self) -> Option<Self::Item> {
while self.offset + 2 <= self.madt_len {
unsafe {
let entry_type = *self.madt_ptr.add(self.offset);
let entry_len = *self.madt_ptr.add(self.offset + 1) as usize;
if entry_len < 2 || self.offset + entry_len > self.madt_len {
return None;
}
let mut cpu_info = None;
if entry_type == MADT_TYPE_RINTC
&& entry_len >= core::mem::size_of::<RintcEntry>()
{
let rintc = core::ptr::read_unaligned(
self.madt_ptr.add(self.offset) as *const RintcEntry
);
cpu_info = Some(CpuInfo {
physical_id: rintc.hart_id as u32,
processor_id: rintc.uid,
enabled: rintc.is_enabled(),
});
}
self.offset += entry_len;
if let Some(info) = cpu_info {
return Some(info);
}
}
}
None
}
}
pub fn riscv64_cpu_info() -> Option<impl Iterator<Item = CpuInfo>> {
let tables = tables().ok()?;
let madt_mapping = tables.find_table::<Madt>()?;
let madt_ptr = madt_mapping.virtual_start.as_ptr() as *const u8;
let madt_len = madt_mapping.region_length;
non_empty_cpu_info_iter(Riscv64CpuIter {
_madt_mapping: madt_mapping,
madt_ptr,
madt_len,
offset: MADT_HEADER_SIZE,
})
}
pub fn riscv64_cpu_id_list() -> Option<impl Iterator<Item = usize>> {
non_empty_enabled_cpu_id_iter(riscv64_cpu_info()?)
}
}
#[cfg(target_arch = "riscv64")]
pub use riscv64_impl::*;
#[cfg(target_arch = "loongarch64")]
mod loongarch64_impl {
use super::super::tables;
use super::{
ACPI_MADT_ENABLED, CpuInfo, MADT_HEADER_SIZE, non_empty_cpu_info_iter,
non_empty_enabled_cpu_id_iter,
};
use acpi::sdt::madt::Madt;
const MADT_TYPE_CORE_PIC: u8 = 0x11;
#[derive(Clone, Copy, Debug)]
#[repr(C, packed)]
pub struct CorePicEntry {
pub entry_type: u8,
pub length: u8,
pub version: u8,
pub processor_id: u32,
pub core_id: u32,
pub flags: u32,
}
impl CorePicEntry {
#[inline]
pub fn is_enabled(&self) -> bool {
(self.flags & ACPI_MADT_ENABLED) != 0
}
}
const _: () = assert!(core::mem::size_of::<CorePicEntry>() == 15);
struct LoongArch64CpuIter {
_madt_mapping: acpi::PhysicalMapping<super::super::AcpiHandle, Madt>,
madt_ptr: *const u8,
madt_len: usize,
offset: usize,
}
impl Iterator for LoongArch64CpuIter {
type Item = CpuInfo;
fn next(&mut self) -> Option<Self::Item> {
while self.offset + 2 <= self.madt_len {
unsafe {
let entry_type = *self.madt_ptr.add(self.offset);
let entry_len = *self.madt_ptr.add(self.offset + 1) as usize;
if entry_len < 2 || self.offset + entry_len > self.madt_len {
return None;
}
let mut cpu_info = None;
if entry_type == MADT_TYPE_CORE_PIC
&& entry_len >= core::mem::size_of::<CorePicEntry>()
{
let core_pic = core::ptr::read_unaligned(
self.madt_ptr.add(self.offset) as *const CorePicEntry
);
cpu_info = Some(CpuInfo {
physical_id: core_pic.core_id,
processor_id: core_pic.processor_id,
enabled: core_pic.is_enabled(),
});
}
self.offset += entry_len;
if let Some(info) = cpu_info {
return Some(info);
}
}
}
None
}
}
pub fn loongarch64_cpu_info() -> Option<impl Iterator<Item = CpuInfo>> {
let tables = tables().ok()?;
let madt_mapping = tables.find_table::<Madt>()?;
let madt_ptr = madt_mapping.virtual_start.as_ptr() as *const u8;
let madt_len = madt_mapping.region_length;
non_empty_cpu_info_iter(LoongArch64CpuIter {
_madt_mapping: madt_mapping,
madt_ptr,
madt_len,
offset: MADT_HEADER_SIZE,
})
}
pub fn loongarch64_cpu_id_list() -> Option<impl Iterator<Item = usize>> {
non_empty_enabled_cpu_id_iter(loongarch64_cpu_info()?)
}
}
#[cfg(target_arch = "loongarch64")]
pub use loongarch64_impl::*;
#[cfg(target_arch = "x86_64")]
#[allow(dead_code)]
pub fn cpu_info() -> Option<impl Iterator<Item = CpuInfo>> {
x86_64_cpu_info()
}
#[cfg(target_arch = "aarch64")]
#[allow(dead_code)]
pub fn cpu_info() -> Option<impl Iterator<Item = CpuInfo>> {
aarch64_cpu_info()
}
#[cfg(target_arch = "riscv64")]
#[allow(dead_code)]
pub fn cpu_info() -> Option<impl Iterator<Item = CpuInfo>> {
riscv64_cpu_info()
}
#[cfg(target_arch = "loongarch64")]
#[allow(dead_code)]
pub fn cpu_info() -> Option<impl Iterator<Item = CpuInfo>> {
loongarch64_cpu_info()
}
#[cfg(target_arch = "x86_64")]
pub fn cpu_id_list() -> Option<impl Iterator<Item = usize>> {
x86_64_cpu_id_list()
}
#[cfg(target_arch = "aarch64")]
pub fn cpu_id_list() -> Option<impl Iterator<Item = usize>> {
aarch64_cpu_id_list()
}
#[cfg(target_arch = "riscv64")]
pub fn cpu_id_list() -> Option<impl Iterator<Item = usize>> {
riscv64_cpu_id_list()
}
#[cfg(target_arch = "loongarch64")]
pub fn cpu_id_list() -> Option<impl Iterator<Item = usize>> {
loongarch64_cpu_id_list()
}