#![no_std]
extern crate alloc;
extern crate core;
#[macro_use]
extern crate lazy_static;
extern crate cstr_core;
extern crate log;
mod acpi;
mod cpuid;
use alloc::vec::Vec;
use core::convert::TryInto;
use core::fmt;
use log::debug;
use x86::apic::ApicId;
use acpi::{
process_madt, process_msct, process_srat, IoApic, LocalApic, LocalX2Apic,
MaximumProximityDomainInfo, MaximumSystemCharacteristics, MemoryAffinity,
};
pub type GlobalThreadId = u64;
pub type ThreadId = u64;
pub type CoreId = u64;
pub type PackageId = u64;
pub type NodeId = u64;
#[derive(Eq, PartialEq, Debug, Ord, PartialOrd)]
enum ApicThreadInfo {
Apic(LocalApic),
X2Apic(LocalX2Apic),
}
impl ApicThreadInfo {
fn id(&self) -> ApicId {
match &self {
ApicThreadInfo::Apic(apic) => ApicId::XApic(apic.apic_id),
ApicThreadInfo::X2Apic(x2apic) => ApicId::X2Apic(x2apic.apic_id),
}
}
}
#[derive(Ord, PartialOrd)]
pub struct Thread {
pub id: GlobalThreadId,
pub node_id: Option<NodeId>,
pub package_id: PackageId,
pub core_id: CoreId,
pub thread_id: ThreadId,
apic: ApicThreadInfo,
}
impl PartialEq for Thread {
fn eq(&self, other: &Self) -> bool {
self.id == other.id
}
}
impl Eq for Thread {}
impl fmt::Debug for Thread {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("Thread")
.field("id", &self.id)
.field("apic_id", &self.apic.id())
.field(
"thread/core/package",
&(self.thread_id, self.core_id, self.package_id),
)
.field("numa_node", &self.node_id)
.finish()
}
}
impl Thread {
fn new_with_apic(
global_id: GlobalThreadId,
apic: LocalApic,
node_id: Option<NodeId>,
) -> Thread {
let (thread_id, core_id, package_id) = cpuid::get_topology_from_apic_id(apic.apic_id);
Thread {
id: global_id,
apic: ApicThreadInfo::Apic(apic),
thread_id,
core_id,
package_id,
node_id,
}
}
fn new_with_x2apic(
global_id: GlobalThreadId,
apic: LocalX2Apic,
node_id: Option<NodeId>,
) -> Thread {
let (thread_id, core_id, package_id) = cpuid::get_topology_from_x2apic_id(apic.apic_id);
Thread {
id: global_id,
apic: ApicThreadInfo::X2Apic(apic),
thread_id,
core_id,
package_id,
node_id,
}
}
pub fn apic_id(&self) -> ApicId {
self.apic.id()
}
pub fn siblings(&'static self) -> impl Iterator<Item = &'static Thread> {
MACHINE_TOPOLOGY
.threads()
.filter(move |t| t.package_id == self.package_id && t.core_id == self.core_id)
.filter(move |t| t != &self)
}
pub fn core(&'static self) -> &'static Core {
MACHINE_TOPOLOGY
.cores()
.find(move |core| core.package_id == self.package_id && core.id == self.core_id)
.unwrap()
}
pub fn package(&'static self) -> &'static Package {
MACHINE_TOPOLOGY
.packages()
.find(move |package| package.id == self.package_id)
.unwrap()
}
pub fn node(&'static self) -> Option<&'static Node> {
self.node_id
.and_then(|nid| MACHINE_TOPOLOGY.nodes().find(move |node| node.id == nid))
}
}
#[derive(Eq, PartialEq, Debug, Ord, PartialOrd)]
pub struct Core {
pub node_id: Option<NodeId>,
pub package_id: PackageId,
pub id: CoreId,
}
impl Core {
fn new(node_id: Option<NodeId>, package_id: PackageId, id: CoreId) -> Core {
Core {
node_id,
package_id,
id,
}
}
pub fn siblings(&'static self) -> impl Iterator<Item = &'static Core> {
MACHINE_TOPOLOGY
.cores()
.filter(move |c| c.package_id == self.package_id)
.filter(move |c| c != &self)
}
pub fn threads(&'static self) -> impl Iterator<Item = &'static Thread> {
MACHINE_TOPOLOGY
.threads()
.filter(move |t| t.package_id == self.package_id && t.core_id == self.id)
}
pub fn package(&'static self) -> &'static Package {
MACHINE_TOPOLOGY
.packages()
.find(move |package| package.id == self.package_id)
.unwrap()
}
pub fn node(&'static self) -> Option<&'static Node> {
self.node_id
.and_then(|nid| MACHINE_TOPOLOGY.nodes().find(move |node| node.id == nid))
}
}
#[derive(Eq, PartialEq, Debug, Ord, PartialOrd)]
pub struct Package {
pub id: PackageId,
pub node_id: Option<NodeId>,
}
impl Package {
fn new(id: PackageId, node_id: Option<NodeId>) -> Package {
Package { id, node_id }
}
pub fn siblings(&'static self) -> impl Iterator<Item = &'static Package> {
MACHINE_TOPOLOGY
.packages()
.filter(move |p| p != &self)
}
pub fn threads(&'static self) -> impl Iterator<Item = &'static Thread> {
MACHINE_TOPOLOGY
.threads()
.filter(move |t| t.package_id == self.id)
}
pub fn cores(&'static self) -> impl Iterator<Item = &'static Core> {
MACHINE_TOPOLOGY
.cores()
.filter(move |c| c.package_id == self.id)
}
pub fn node(&'static self) -> Option<&'static Node> {
self.node_id
.and_then(|nid| MACHINE_TOPOLOGY.nodes().find(move |node| node.id == nid))
}
}
#[derive(Eq, PartialEq, Debug, Ord, PartialOrd)]
pub struct Node {
pub id: NodeId,
}
impl Node {
fn new(id: NodeId) -> Node {
Node { id }
}
pub fn siblings(&self) -> impl Iterator<Item = &'static Node> {
MACHINE_TOPOLOGY.nodes()
}
pub fn memory(&'static self) -> impl Iterator<Item = &'static MemoryAffinity> {
MACHINE_TOPOLOGY
.memory_affinity
.iter()
.filter(move |ma| ma.proximity_domain as NodeId == self.id)
}
pub fn threads(&'static self) -> impl Iterator<Item = &'static Thread> {
MACHINE_TOPOLOGY
.threads()
.filter(move |t| t.node_id == Some(self.id))
}
pub fn cores(&'static self) -> impl Iterator<Item = &'static Core> {
MACHINE_TOPOLOGY
.cores()
.filter(move |c| c.node_id == Some(self.id))
}
pub fn packages(&'static self) -> impl Iterator<Item = &'static Package> {
MACHINE_TOPOLOGY
.packages()
.filter(move |p| p.node_id == Some(self.id))
}
}
lazy_static! {
pub static ref MACHINE_TOPOLOGY: MachineInfo = {
let (mut local_apics, mut local_x2apics, ioapics) = process_madt();
let (mut core_affinity, mut x2apic_affinity, memory_affinity) = process_srat();
let (max_proximity_info, prox_domain_info) = process_msct();
local_apics.sort_by(|a, b| a.apic_id.cmp(&b.apic_id));
local_x2apics.sort_by(|a, b| a.apic_id.cmp(&b.apic_id));
core_affinity.sort_by(|a, b| b.apic_id.cmp(&a.apic_id));
x2apic_affinity.sort_by(|a, b| b.x2apic_id.cmp(&a.x2apic_id));
assert!(local_apics.len() == core_affinity.len() || core_affinity.is_empty(),
"Either we have matching entries for core in affinity table or no affinity information at all.");
let mut global_thread_id: GlobalThreadId = 0;
let mut threads = Vec::with_capacity(local_apics.len() + local_x2apics.len());
for local_apic in local_apics {
let mut proximity_domain = None;
if !core_affinity.is_empty() {
let affinity_entry = core_affinity.pop();
if affinity_entry.as_ref().unwrap().apic_id == local_apic.apic_id {
proximity_domain = affinity_entry.as_ref().map(|a| a.proximity_domain as u64);
}
else {
core_affinity.push(affinity_entry.unwrap());
}
}
if proximity_domain.is_none() && !x2apic_affinity.is_empty() {
let affinity_entry = x2apic_affinity.pop();
let x2apic_id: u8 = affinity_entry.as_ref().unwrap().x2apic_id.try_into().unwrap();
if x2apic_id == local_apic.apic_id {
proximity_domain = affinity_entry.as_ref().map(|a| a.proximity_domain as u64);
}
else {
x2apic_affinity.push(affinity_entry.unwrap());
}
}
let t = Thread::new_with_apic(global_thread_id, local_apic, proximity_domain);
debug!("Found {:?}", t);
threads.push(t);
global_thread_id += 1;
}
for local_x2apic in local_x2apics {
let affinity = x2apic_affinity.pop();
if let Some(affinity_entry) = affinity.as_ref() {
assert_eq!(affinity_entry.x2apic_id, local_x2apic.apic_id, "The x2apic_affinity and local_x2apic are not in the same order?");
}
let t = Thread::new_with_x2apic(global_thread_id, local_x2apic, affinity.map(|a| a.proximity_domain as u64));
debug!("Found {:?}", t);
threads.push(t);
global_thread_id += 1;
}
let mut cores: Vec<Core> = threads.iter().map(|t| Core::new(t.node_id, t.package_id, t.core_id)).collect();
cores.sort();
cores.dedup();
let mut packages: Vec<Package> = threads.iter().map(|t| Package::new(t.package_id, t.node_id)).collect();
packages.sort();
packages.dedup();
let mut nodes: Vec<Node> = threads
.iter()
.filter(|t| t.node_id.is_some())
.map(|t| Node::new(t.node_id.unwrap_or(0)))
.collect::<Vec<Node>>();
nodes.sort();
nodes.dedup();
MachineInfo::new(
threads,
cores,
packages,
nodes,
ioapics,
memory_affinity,
max_proximity_info,
prox_domain_info
)
};
}
#[derive(Debug)]
pub struct MachineInfo {
pub threads: Vec<Thread>,
cores: Vec<Core>,
packages: Vec<Package>,
nodes: Vec<Node>,
memory_affinity: Vec<MemoryAffinity>,
io_apics: Vec<IoApic>,
max_proximity_info: MaximumSystemCharacteristics,
proximity_domains: Vec<MaximumProximityDomainInfo>,
}
impl MachineInfo {
fn new(
threads: Vec<Thread>,
cores: Vec<Core>,
packages: Vec<Package>,
nodes: Vec<Node>,
io_apics: Vec<IoApic>,
memory_affinity: Vec<MemoryAffinity>,
max_proximity_info: MaximumSystemCharacteristics,
proximity_domains: Vec<MaximumProximityDomainInfo>,
) -> MachineInfo {
MachineInfo {
threads,
cores,
packages,
nodes,
memory_affinity,
io_apics,
max_proximity_info,
proximity_domains,
}
}
fn determine_apic_id_with_cpuid() -> x86::apic::ApicId {
let cpuid = x86::cpuid::CpuId::new();
let xapic_id: Option<u8> = cpuid
.get_feature_info()
.as_ref()
.map(|finfo| finfo.initial_local_apic_id());
let x2apic_id: Option<u32> = cpuid
.get_extended_topology_info()
.and_then(|mut topiter| topiter.next().as_ref().map(|t| t.x2apic_id()));
match (x2apic_id, xapic_id) {
(None, None) => {
unreachable!("Can't determine APIC ID, bad. (Maybe try fallback on APIC_BASE_MSR")
}
(Some(x2id), None) => ApicId::X2Apic(x2id),
(None, Some(xid)) => ApicId::XApic(xid),
(Some(x2id), Some(xid)) => {
debug_assert!(
(x2id & 0xff) == xid.into(),
"xAPIC ID is first byte of X2APIC ID"
);
if (xid as u32) == x2id {
ApicId::XApic(xid)
} else {
ApicId::X2Apic(x2id)
}
}
}
}
pub fn current_thread(&'static self) -> &'static Thread {
let apic_id = MachineInfo::determine_apic_id_with_cpuid();
self.threads()
.find(move |t| t.apic_id() == apic_id)
.unwrap()
}
pub fn num_threads(&self) -> usize {
self.threads.len()
}
pub fn threads(&'static self) -> impl Iterator<Item = &Thread> {
self.threads.iter()
}
pub fn num_cores(&self) -> usize {
self.cores.len()
}
pub fn cores(&'static self) -> impl Iterator<Item = &Core> {
self.cores.iter()
}
pub fn num_packages(&self) -> usize {
self.packages.len()
}
pub fn packages(&'static self) -> impl Iterator<Item = &Package> {
self.packages.iter()
}
pub fn nodes(&'static self) -> impl Iterator<Item = &Node> {
self.nodes.iter()
}
pub fn num_nodes(&self) -> usize {
self.nodes.len()
}
pub fn io_apics(&'static self) -> impl Iterator<Item = &IoApic> {
self.io_apics.iter()
}
}