use super::{AsNode, FallibleNode, NodeChildrenIter};
use crate::{
cell_collector::{BuildCellCollector, CellCollector, CollectCellsError},
parsing::{aligned::AlignedParser, NoPanic, Panic, ParserWithMode},
properties::{
cells::{AddressCells, CellSizes},
values::StringList,
PHandle,
},
FdtError,
};
pub struct Cpus<'a, P: ParserWithMode<'a> = (AlignedParser<'a>, Panic)> {
pub(crate) node: FallibleNode<'a, P>,
}
impl<'a, P: ParserWithMode<'a>> Cpus<'a, P> {
#[track_caller]
pub fn cell_sizes(&self) -> P::Output<CellSizes> {
P::to_output(
self.node.property().and_then(|p| p.ok_or(FdtError::MissingRequiredProperty("#address-cells/#size-cells"))),
)
}
#[track_caller]
pub fn common_timebase_frequency(&self) -> P::Output<Option<u64>> {
P::to_output(crate::tryblock!({
match self.node.properties()?.find("timebase-frequency")? {
Some(prop) => match prop.value.len() {
4 => Ok(Some(u64::from(prop.as_value::<u32>()?))),
8 => Ok(Some(prop.as_value::<u64>()?)),
_ => Err(FdtError::InvalidPropertyValue),
},
None => Ok(None),
}
}))
}
#[track_caller]
pub fn common_clock_frequency(&self) -> P::Output<Option<u64>> {
P::to_output(crate::tryblock!({
match self.node.properties()?.find("clock-frequency")? {
Some(prop) => match prop.value.len() {
4 => Ok(Some(u64::from(prop.as_value::<u32>()?))),
8 => Ok(Some(prop.as_value::<u64>()?)),
_ => Err(FdtError::InvalidPropertyValue),
},
None => Ok(None),
}
}))
}
pub fn topology(&self) -> P::Output<Option<CpuTopology<'a, P>>> {
P::to_output(crate::tryblock!({
match self.node.children()?.find("cpu-map")? {
Some(node) => Ok(Some(CpuTopology { node })),
None => Ok(None),
}
}))
}
pub fn iter(&self) -> P::Output<CpusIter<'a, P>> {
P::to_output(crate::tryblock!({
Ok(CpusIter { children: self.node.children()?.iter().filter(filter_cpus::<P>) })
}))
}
}
impl<'a, P: ParserWithMode<'a>> AsNode<'a, P> for Cpus<'a, P> {
fn as_node(&self) -> super::Node<'a, P> {
self.node.alt()
}
}
fn filter_cpus<'a, P: ParserWithMode<'a>>(node: &Result<FallibleNode<'a, P>, FdtError>) -> bool {
match node {
Ok(node) => matches!(node.name().map(|n| n.name), Ok("cpu")),
_ => true,
}
}
#[allow(missing_docs)]
pub struct CpusIter<'a, P: ParserWithMode<'a> = (AlignedParser<'a>, Panic)> {
#[allow(clippy::type_complexity)]
children: core::iter::Filter<
NodeChildrenIter<'a, (P::Parser, NoPanic)>,
fn(&Result<FallibleNode<'a, P>, FdtError>) -> bool,
>,
}
impl<'a, P: ParserWithMode<'a>> Iterator for CpusIter<'a, P> {
type Item = P::Output<Cpu<'a, P>>;
#[track_caller]
fn next(&mut self) -> Option<Self::Item> {
match self.children.next()? {
Ok(node) => Some(P::to_output(Ok(Cpu { node }))),
Err(e) => Some(P::to_output(Err(e))),
}
}
}
#[derive(Debug, Clone, Copy)]
pub struct Cpu<'a, P: ParserWithMode<'a> = (AlignedParser<'a>, Panic)> {
pub(crate) node: FallibleNode<'a, P>,
}
impl<'a, P: ParserWithMode<'a>> Cpu<'a, P> {
#[inline]
#[track_caller]
#[doc(alias = "ids")]
pub fn reg<C: CellCollector>(self) -> P::Output<CpuIds<'a, C>> {
P::to_output(crate::tryblock!({
let Some(reg) = self.node.properties()?.find("reg")? else {
return Err(FdtError::MissingRequiredProperty("reg"));
};
if reg.value.is_empty() {
return Err(FdtError::InvalidPropertyValue);
}
let Some(address_cells) = self.node.parent().unwrap().property::<AddressCells>()? else {
return Err(FdtError::MissingRequiredProperty("#address-cells"));
};
Ok(CpuIds { reg: reg.value, address_cells: address_cells.0, _collector: core::marker::PhantomData })
}))
}
#[inline]
#[track_caller]
pub fn clock_frequency(self) -> P::Output<u64> {
P::to_output(crate::tryblock!({
match self.node.properties()?.find("clock-frequency")? {
Some(prop) => match prop.value.len() {
4 => Ok(u64::from(prop.as_value::<u32>()?)),
8 => Ok(prop.as_value::<u64>()?),
_ => Err(FdtError::InvalidPropertyValue),
},
None => {
let prop = self
.node
.parent()
.unwrap()
.properties()?
.find("clock-frequency")?
.ok_or(FdtError::MissingRequiredProperty("clock-frequency"))?;
match prop.value.len() {
4 => Ok(u64::from(prop.as_value::<u32>()?)),
8 => Ok(prop.as_value::<u64>()?),
_ => Err(FdtError::InvalidPropertyValue),
}
}
}
}))
}
#[inline]
#[track_caller]
pub fn timebase_frequency(self) -> P::Output<u64> {
P::to_output(crate::tryblock!({
match self.node.properties()?.find("timebase-frequency")? {
Some(prop) => match prop.value.len() {
4 => Ok(u64::from(prop.as_value::<u32>()?)),
8 => Ok(prop.as_value::<u64>()?),
_ => Err(FdtError::InvalidPropertyValue),
},
None => {
let prop = self
.node
.parent()
.unwrap()
.properties()?
.find("timebase-frequency")?
.ok_or(FdtError::MissingRequiredProperty("timebase-frequency"))?;
match prop.value.len() {
4 => Ok(u64::from(prop.as_value::<u32>()?)),
8 => Ok(prop.as_value::<u64>()?),
_ => Err(FdtError::InvalidPropertyValue),
}
}
}
}))
}
#[inline]
#[track_caller]
pub fn status(&self) -> P::Output<Option<CpuStatus<'a>>> {
P::to_output(crate::tryblock!({
let Some(status) = self.node.properties()?.find("status")? else {
return Ok(None);
};
Ok(Some(CpuStatus(status.as_value()?)))
}))
}
#[inline]
#[track_caller]
pub fn enable_method(&self) -> P::Output<Option<CpuEnableMethods<'a>>> {
P::to_output(crate::tryblock!({
let Some(status) = self.node.properties()?.find("enable-method")? else {
return Ok(None);
};
let s: &'a str = status.as_value()?;
if s.is_empty() {
return Err(FdtError::InvalidPropertyValue);
}
Ok(Some(CpuEnableMethods(s.into())))
}))
}
#[inline]
#[track_caller]
pub fn mmu_type(&self) -> P::Output<Option<&'a str>> {
P::to_output(self.node.properties().and_then(|p| {
p.find("mmu-type").and_then(|p| match p {
Some(p) => Ok(Some(p.as_value()?)),
None => Ok(None),
})
}))
}
#[inline]
#[track_caller]
pub fn tlb_split(&self) -> P::Output<bool> {
P::to_output(self.node.properties().and_then(|p| p.find("tlb-split").map(|p| p.is_some())))
}
#[inline]
#[track_caller]
pub fn tlb_size(&self) -> P::Output<Option<u32>> {
P::to_output(self.node.properties().and_then(|p| {
p.find("tlb-size").and_then(|p| match p {
Some(p) => Ok(Some(p.as_value()?)),
None => Ok(None),
})
}))
}
#[inline]
#[track_caller]
pub fn tlb_sets(&self) -> P::Output<Option<u32>> {
P::to_output(self.node.properties().and_then(|p| {
p.find("tlb-sets").and_then(|p| match p {
Some(p) => Ok(Some(p.as_value()?)),
None => Ok(None),
})
}))
}
#[inline]
#[track_caller]
pub fn d_tlb_size(&self) -> P::Output<Option<u32>> {
P::to_output(self.node.properties().and_then(|p| {
p.find("d-tlb-size").and_then(|p| match p {
Some(p) => Ok(Some(p.as_value()?)),
None => Ok(None),
})
}))
}
#[inline]
#[track_caller]
pub fn d_tlb_sets(&self) -> P::Output<Option<u32>> {
P::to_output(self.node.properties().and_then(|p| {
p.find("d-tlb-sets").and_then(|p| match p {
Some(p) => Ok(Some(p.as_value()?)),
None => Ok(None),
})
}))
}
#[inline]
#[track_caller]
pub fn i_tlb_size(&self) -> P::Output<Option<u32>> {
P::to_output(self.node.properties().and_then(|p| {
p.find("i-tlb-size").and_then(|p| match p {
Some(p) => Ok(Some(p.as_value()?)),
None => Ok(None),
})
}))
}
#[inline]
#[track_caller]
pub fn i_tlb_sets(&self) -> P::Output<Option<u32>> {
P::to_output(self.node.properties().and_then(|p| {
p.find("i-tlb-sets").and_then(|p| match p {
Some(p) => Ok(Some(p.as_value()?)),
None => Ok(None),
})
}))
}
}
impl<'a, P: ParserWithMode<'a>> AsNode<'a, P> for Cpu<'a, P> {
fn as_node(&self) -> super::Node<'a, P> {
self.node.alt()
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[repr(transparent)]
pub struct CpuStatus<'a>(&'a str);
impl<'a> CpuStatus<'a> {
pub const OKAY: Self = Self("okay");
pub const DISABLED: Self = Self("disabled");
pub const FAIL: Self = Self("fail");
pub fn new(status: &'a str) -> Self {
Self(status)
}
pub fn is_okay(self) -> bool {
self == Self::OKAY
}
pub fn is_disabled(self) -> bool {
self == Self::DISABLED
}
pub fn is_failed(self) -> bool {
self == Self::FAIL
}
}
impl<'a> PartialEq<str> for CpuStatus<'a> {
fn eq(&self, other: &str) -> bool {
self.0 == other
}
}
#[derive(Debug, Clone)]
pub struct CpuEnableMethods<'a>(StringList<'a>);
impl<'a> CpuEnableMethods<'a> {
pub fn iter(&self) -> CpuEnableMethodsIter<'a> {
CpuEnableMethodsIter(self.0.clone())
}
pub fn first(&self) -> CpuEnableMethod<'a> {
self.iter().next().unwrap()
}
}
impl<'a> IntoIterator for CpuEnableMethods<'a> {
type IntoIter = CpuEnableMethodsIter<'a>;
type Item = CpuEnableMethod<'a>;
fn into_iter(self) -> Self::IntoIter {
CpuEnableMethodsIter(self.0)
}
}
pub struct CpuEnableMethodsIter<'a>(StringList<'a>);
impl<'a> Iterator for CpuEnableMethodsIter<'a> {
type Item = CpuEnableMethod<'a>;
fn next(&mut self) -> Option<Self::Item> {
match self.0.next()? {
"spin-table" => Some(CpuEnableMethod::SpinTable),
other => {
let (vendor, method) = other.split_once(',').unwrap_or((other, ""));
Some(CpuEnableMethod::VendorMethod { vendor, method })
}
}
}
}
pub enum CpuEnableMethod<'a> {
SpinTable,
VendorMethod {
vendor: &'a str,
method: &'a str,
},
}
pub struct CpuIds<'a, C: CellCollector> {
reg: &'a [u8],
address_cells: usize,
_collector: core::marker::PhantomData<*mut C>,
}
impl<'a, C: CellCollector> CpuIds<'a, C> {
pub fn first(&self) -> Result<C::Output, CollectCellsError> {
self.iter().next().unwrap()
}
pub fn iter(&self) -> CpuIdsIter<'a, C> {
CpuIdsIter { reg: self.reg, address_cells: self.address_cells, _collector: core::marker::PhantomData }
}
}
impl<C: CellCollector> Copy for CpuIds<'_, C> {}
impl<C: CellCollector> Clone for CpuIds<'_, C> {
fn clone(&self) -> Self {
*self
}
}
impl<'a, C: CellCollector> core::fmt::Debug for CpuIds<'a, C> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("CpuIds")
.field("reg", &self.reg)
.field("address_cells", &self.address_cells)
.finish_non_exhaustive()
}
}
#[allow(missing_docs)]
pub struct CpuIdsIter<'a, C: CellCollector> {
reg: &'a [u8],
address_cells: usize,
_collector: core::marker::PhantomData<*mut C>,
}
impl<'a, C: CellCollector> core::fmt::Debug for CpuIdsIter<'a, C> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("CpuIds")
.field("reg", &self.reg)
.field("address_cells", &self.address_cells)
.finish_non_exhaustive()
}
}
impl<C: CellCollector> Clone for CpuIdsIter<'_, C> {
fn clone(&self) -> Self {
Self { address_cells: self.address_cells, reg: self.reg, _collector: core::marker::PhantomData }
}
}
impl<'a, C: CellCollector> Iterator for CpuIdsIter<'a, C> {
type Item = Result<C::Output, CollectCellsError>;
fn next(&mut self) -> Option<Self::Item> {
let (this_cell, rest) = self.reg.split_at_checked(self.address_cells * 4)?;
self.reg = rest;
let mut collector = <C as CellCollector>::Builder::default();
for cell in this_cell.chunks_exact(4) {
if let Err(e) = collector.push(u32::from_be_bytes(cell.try_into().unwrap())) {
return Some(Err(e));
}
}
Some(Ok(C::map(collector.finish())))
}
}
pub struct CpuTopology<'a, P: ParserWithMode<'a> = (AlignedParser<'a>, Panic)> {
node: FallibleNode<'a, P>,
}
impl<'a, P: ParserWithMode<'a>> CpuTopology<'a, P> {
pub fn sockets(&self) -> P::Output<CpuSocketIter<'a, P>> {
P::to_output(crate::tryblock!({
Ok(CpuSocketIter { children: self.node.children()?.iter().filter(filter_sockets::<P>) })
}))
}
pub fn clusters(&self) -> P::Output<CpuClusterIter<'a, P>> {
P::to_output(crate::tryblock!({
Ok(CpuClusterIter { children: self.node.children()?.iter().filter(filter_clusters::<P>) })
}))
}
}
fn filter_sockets<'a, P: ParserWithMode<'a>>(node: &Result<FallibleNode<'a, P>, FdtError>) -> bool {
match node {
Ok(node) => matches!(node.name().map(|n| n.name), Ok(n) if n.starts_with("socket")),
_ => true,
}
}
#[allow(missing_docs)]
pub struct CpuSocketIter<'a, P: ParserWithMode<'a> = (AlignedParser<'a>, Panic)> {
#[allow(clippy::type_complexity)]
children: core::iter::Filter<
NodeChildrenIter<'a, (P::Parser, NoPanic)>,
fn(&Result<FallibleNode<'a, P>, FdtError>) -> bool,
>,
}
impl<'a, P: ParserWithMode<'a>> Iterator for CpuSocketIter<'a, P> {
type Item = P::Output<CpuSocket<'a, P>>;
fn next(&mut self) -> Option<Self::Item> {
match self.children.next()? {
Ok(node) => Some(P::to_output(Ok(CpuSocket { node }))),
Err(e) => Some(P::to_output(Err(e))),
}
}
}
fn filter_clusters<'a, P: ParserWithMode<'a>>(node: &Result<FallibleNode<'a, P>, FdtError>) -> bool {
match node {
Ok(node) => matches!(node.name().map(|n| n.name), Ok(n) if n.starts_with("cluster")),
_ => true,
}
}
#[allow(missing_docs)]
pub struct CpuClusterIter<'a, P: ParserWithMode<'a> = (AlignedParser<'a>, Panic)> {
#[allow(clippy::type_complexity)]
children: core::iter::Filter<
NodeChildrenIter<'a, (P::Parser, NoPanic)>,
fn(&Result<FallibleNode<'a, P>, FdtError>) -> bool,
>,
}
impl<'a, P: ParserWithMode<'a>> Iterator for CpuClusterIter<'a, P> {
type Item = P::Output<CpuCluster<'a, P>>;
#[track_caller]
fn next(&mut self) -> Option<Self::Item> {
match self.children.next()? {
Ok(node) => Some(P::to_output(Ok(CpuCluster { node }))),
Err(e) => Some(P::to_output(Err(e))),
}
}
}
pub struct CpuSocket<'a, P: ParserWithMode<'a> = (AlignedParser<'a>, Panic)> {
node: FallibleNode<'a, P>,
}
impl<'a, P: ParserWithMode<'a>> CpuSocket<'a, P> {
pub fn id(&self) -> P::Output<usize> {
P::to_output(crate::tryblock!({
match self.node.name()?.name.trim_start_matches("socket").parse() {
Ok(id) => Ok(id),
Err(_) => Err(FdtError::InvalidNodeName),
}
}))
}
pub fn clusters(&self) -> P::Output<CpuClusterIter<'a, P>> {
P::to_output(crate::tryblock!({
Ok(CpuClusterIter { children: self.node.children()?.iter().filter(filter_clusters::<P>) })
}))
}
}
pub struct CpuCluster<'a, P: ParserWithMode<'a> = (AlignedParser<'a>, Panic)> {
node: FallibleNode<'a, P>,
}
impl<'a, P: ParserWithMode<'a>> CpuCluster<'a, P> {
pub fn id(&self) -> P::Output<usize> {
P::to_output(crate::tryblock!({
match self.node.name()?.name.trim_start_matches("cluster").parse() {
Ok(id) => Ok(id),
Err(_) => Err(FdtError::InvalidNodeName),
}
}))
}
pub fn cores(&self) -> P::Output<CpuCoreIter<'a, P>> {
P::to_output(crate::tryblock!({
Ok(CpuCoreIter { children: self.node.children()?.iter().filter(filter_cores::<P>) })
}))
}
}
fn filter_cores<'a, P: ParserWithMode<'a>>(node: &Result<FallibleNode<'a, P>, FdtError>) -> bool {
match node {
Ok(node) => matches!(node.name().map(|n| n.name), Ok(n) if n.starts_with("core")),
_ => true,
}
}
#[allow(missing_docs)]
pub struct CpuCoreIter<'a, P: ParserWithMode<'a> = (AlignedParser<'a>, Panic)> {
#[allow(clippy::type_complexity)]
children: core::iter::Filter<
NodeChildrenIter<'a, (P::Parser, NoPanic)>,
fn(&Result<FallibleNode<'a, P>, FdtError>) -> bool,
>,
}
impl<'a, P: ParserWithMode<'a>> Iterator for CpuCoreIter<'a, P> {
type Item = P::Output<CpuCore<'a, P>>;
#[track_caller]
fn next(&mut self) -> Option<Self::Item> {
match self.children.next()? {
Ok(node) => Some(P::to_output(Ok(CpuCore { node }))),
Err(e) => Some(P::to_output(Err(e))),
}
}
}
pub struct CpuCore<'a, P: ParserWithMode<'a> = (AlignedParser<'a>, Panic)> {
node: FallibleNode<'a, P>,
}
impl<'a, P: ParserWithMode<'a>> CpuCore<'a, P> {
pub fn id(&self) -> P::Output<usize> {
P::to_output(crate::tryblock!({
match self.node.name()?.name.trim_start_matches("core").parse() {
Ok(id) => Ok(id),
Err(_) => Err(FdtError::InvalidNodeName),
}
}))
}
pub fn cpu(&self) -> P::Output<Option<Cpu<'a, P>>> {
P::to_output(crate::tryblock!({
let phandle = match self.node.properties()?.find("cpu")? {
Some(property) => PHandle::new(property.as_value::<u32>()?),
None => return Ok(None),
};
Ok(Some(Cpu {
node: self
.node
.make_root()?
.resolve_phandle(phandle)?
.ok_or(FdtError::MissingPHandleNode(phandle.as_u32()))?,
}))
}))
}
pub fn threads(&self) -> P::Output<CpuThreadIter<'a, P>> {
P::to_output(crate::tryblock!({
Ok(CpuThreadIter { children: self.node.children()?.iter().filter(filter_threads::<P>) })
}))
}
}
fn filter_threads<'a, P: ParserWithMode<'a>>(node: &Result<FallibleNode<'a, P>, FdtError>) -> bool {
match node {
Ok(node) => matches!(node.name().map(|n| n.name), Ok(n) if n.starts_with("thread")),
_ => true,
}
}
#[allow(missing_docs)]
pub struct CpuThreadIter<'a, P: ParserWithMode<'a> = (AlignedParser<'a>, Panic)> {
#[allow(clippy::type_complexity)]
children: core::iter::Filter<
NodeChildrenIter<'a, (P::Parser, NoPanic)>,
fn(&Result<FallibleNode<'a, P>, FdtError>) -> bool,
>,
}
impl<'a, P: ParserWithMode<'a>> Iterator for CpuThreadIter<'a, P> {
type Item = P::Output<CpuThread<'a, P>>;
#[track_caller]
fn next(&mut self) -> Option<Self::Item> {
match self.children.next()? {
Ok(node) => Some(P::to_output(Ok(CpuThread { node }))),
Err(e) => Some(P::to_output(Err(e))),
}
}
}
pub struct CpuThread<'a, P: ParserWithMode<'a> = (AlignedParser<'a>, Panic)> {
node: FallibleNode<'a, P>,
}
impl<'a, P: ParserWithMode<'a>> CpuThread<'a, P> {
pub fn id(&self) -> P::Output<usize> {
P::to_output(crate::tryblock!({
match self.node.name()?.name.trim_start_matches("socket").parse() {
Ok(id) => Ok(id),
Err(_) => Err(FdtError::InvalidNodeName),
}
}))
}
pub fn cpu(&self) -> P::Output<Cpu<'a, P>> {
P::to_output(crate::tryblock!({
let phandle = match self.node.properties()?.find("cpu")? {
Some(property) => PHandle::new(property.as_value::<u32>()?),
None => return Err(FdtError::MissingRequiredProperty("cpu")),
};
self.node
.make_root()?
.resolve_phandle(phandle)?
.map(|node| Cpu { node })
.ok_or(FdtError::MissingPHandleNode(phandle.as_u32()))
}))
}
}