Merge changes I2c6c196f,Ia70a7354 into main
* changes:
pvmfw: Fix up cpu-map DT patching
pvmfw: Parse cpu-map DT node for the guest
diff --git a/pvmfw/platform.dts b/pvmfw/platform.dts
index 569d20a..275a1c9 100644
--- a/pvmfw/platform.dts
+++ b/pvmfw/platform.dts
@@ -52,7 +52,35 @@
cpus {
#address-cells = <1>;
#size-cells = <0>;
- cpu@0 {
+
+ cpu-map {
+ cluster0 {
+ core0 { cpu = <PLACEHOLDER>; };
+ core1 { cpu = <PLACEHOLDER>; };
+ core2 { cpu = <PLACEHOLDER>; };
+ core3 { cpu = <PLACEHOLDER>; };
+ core4 { cpu = <PLACEHOLDER>; };
+ core5 { cpu = <PLACEHOLDER>; };
+ };
+ cluster1 {
+ core0 { cpu = <PLACEHOLDER>; };
+ core1 { cpu = <PLACEHOLDER>; };
+ core2 { cpu = <PLACEHOLDER>; };
+ core3 { cpu = <PLACEHOLDER>; };
+ core4 { cpu = <PLACEHOLDER>; };
+ core5 { cpu = <PLACEHOLDER>; };
+ };
+ cluster2 {
+ core0 { cpu = <PLACEHOLDER>; };
+ core1 { cpu = <PLACEHOLDER>; };
+ core2 { cpu = <PLACEHOLDER>; };
+ core3 { cpu = <PLACEHOLDER>; };
+ core4 { cpu = <PLACEHOLDER>; };
+ core5 { cpu = <PLACEHOLDER>; };
+ };
+ };
+
+ cpu0: cpu@0 {
device_type = "cpu";
compatible = "arm,arm-v8";
enable-method = "psci";
@@ -84,7 +112,7 @@
opp20 { opp-hz = <PLACEHOLDER2>; };
};
};
- cpu@1 {
+ cpu1: cpu@1 {
device_type = "cpu";
compatible = "arm,arm-v8";
enable-method = "psci";
@@ -116,7 +144,7 @@
opp20 { opp-hz = <PLACEHOLDER2>; };
};
};
- cpu@2 {
+ cpu2: cpu@2 {
device_type = "cpu";
compatible = "arm,arm-v8";
enable-method = "psci";
@@ -148,7 +176,7 @@
opp20 { opp-hz = <PLACEHOLDER2>; };
};
};
- cpu@3 {
+ cpu3: cpu@3 {
device_type = "cpu";
compatible = "arm,arm-v8";
enable-method = "psci";
@@ -180,7 +208,7 @@
opp20 { opp-hz = <PLACEHOLDER2>; };
};
};
- cpu@4 {
+ cpu4: cpu@4 {
device_type = "cpu";
compatible = "arm,arm-v8";
enable-method = "psci";
@@ -212,7 +240,7 @@
opp20 { opp-hz = <PLACEHOLDER2>; };
};
};
- cpu@5 {
+ cpu5: cpu@5 {
device_type = "cpu";
compatible = "arm,arm-v8";
enable-method = "psci";
@@ -244,7 +272,7 @@
opp20 { opp-hz = <PLACEHOLDER2>; };
};
};
- cpu@6 {
+ cpu6: cpu@6 {
device_type = "cpu";
compatible = "arm,arm-v8";
enable-method = "psci";
@@ -276,7 +304,7 @@
opp20 { opp-hz = <PLACEHOLDER2>; };
};
};
- cpu@7 {
+ cpu7: cpu@7 {
device_type = "cpu";
compatible = "arm,arm-v8";
enable-method = "psci";
@@ -308,7 +336,7 @@
opp20 { opp-hz = <PLACEHOLDER2>; };
};
};
- cpu@8 {
+ cpu8: cpu@8 {
device_type = "cpu";
compatible = "arm,arm-v8";
enable-method = "psci";
@@ -340,7 +368,7 @@
opp20 { opp-hz = <PLACEHOLDER2>; };
};
};
- cpu@9 {
+ cpu9: cpu@9 {
device_type = "cpu";
compatible = "arm,arm-v8";
enable-method = "psci";
@@ -372,7 +400,7 @@
opp20 { opp-hz = <PLACEHOLDER2>; };
};
};
- cpu@10 {
+ cpu10: cpu@10 {
device_type = "cpu";
compatible = "arm,arm-v8";
enable-method = "psci";
@@ -404,7 +432,7 @@
opp20 { opp-hz = <PLACEHOLDER2>; };
};
};
- cpu@11 {
+ cpu11: cpu@11 {
device_type = "cpu";
compatible = "arm,arm-v8";
enable-method = "psci";
@@ -436,7 +464,7 @@
opp20 { opp-hz = <PLACEHOLDER2>; };
};
};
- cpu@12 {
+ cpu12: cpu@12 {
device_type = "cpu";
compatible = "arm,arm-v8";
enable-method = "psci";
@@ -468,7 +496,7 @@
opp20 { opp-hz = <PLACEHOLDER2>; };
};
};
- cpu@13 {
+ cpu13: cpu@13 {
device_type = "cpu";
compatible = "arm,arm-v8";
enable-method = "psci";
@@ -500,7 +528,7 @@
opp20 { opp-hz = <PLACEHOLDER2>; };
};
};
- cpu@14 {
+ cpu14: cpu@14 {
device_type = "cpu";
compatible = "arm,arm-v8";
enable-method = "psci";
@@ -532,7 +560,7 @@
opp20 { opp-hz = <PLACEHOLDER2>; };
};
};
- cpu@15 {
+ cpu15: cpu@15 {
device_type = "cpu";
compatible = "arm,arm-v8";
enable-method = "psci";
diff --git a/pvmfw/src/fdt.rs b/pvmfw/src/fdt.rs
index ba0a992..bd47ed7 100644
--- a/pvmfw/src/fdt.rs
+++ b/pvmfw/src/fdt.rs
@@ -21,6 +21,7 @@
use crate::RebootReason;
use alloc::collections::BTreeMap;
use alloc::ffi::CString;
+use alloc::format;
use alloc::vec::Vec;
use core::cmp::max;
use core::cmp::min;
@@ -37,6 +38,7 @@
use libfdt::FdtError;
use libfdt::FdtNode;
use libfdt::FdtNodeMut;
+use libfdt::Phandle;
use log::debug;
use log::error;
use log::info;
@@ -200,10 +202,63 @@
Ok(table)
}
-fn read_cpu_info_from(fdt: &Fdt) -> libfdt::Result<ArrayVec<[CpuInfo; DeviceTreeInfo::MAX_CPUS]>> {
+#[derive(Debug, Default)]
+struct ClusterTopology {
+ // TODO: Support multi-level clusters & threads.
+ cores: [Option<usize>; ClusterTopology::MAX_CORES_PER_CLUSTER],
+}
+
+impl ClusterTopology {
+ const MAX_CORES_PER_CLUSTER: usize = 6;
+}
+
+#[derive(Debug, Default)]
+struct CpuTopology {
+ // TODO: Support sockets.
+ clusters: [Option<ClusterTopology>; CpuTopology::MAX_CLUSTERS],
+}
+
+impl CpuTopology {
+ const MAX_CLUSTERS: usize = 3;
+}
+
+fn read_cpu_map_from(fdt: &Fdt) -> libfdt::Result<Option<BTreeMap<Phandle, (usize, usize)>>> {
+ let Some(cpu_map) = fdt.node(cstr!("/cpus/cpu-map"))? else {
+ return Ok(None);
+ };
+
+ let mut topology = BTreeMap::new();
+ for n in 0..CpuTopology::MAX_CLUSTERS {
+ let name = CString::new(format!("cluster{n}")).unwrap();
+ let Some(cluster) = cpu_map.subnode(&name)? else {
+ break;
+ };
+ for m in 0..ClusterTopology::MAX_CORES_PER_CLUSTER {
+ let name = CString::new(format!("core{m}")).unwrap();
+ let Some(core) = cluster.subnode(&name)? else {
+ break;
+ };
+ let cpu = core.getprop_u32(cstr!("cpu"))?.ok_or(FdtError::NotFound)?;
+ let prev = topology.insert(cpu.try_into()?, (n, m));
+ if prev.is_some() {
+ return Err(FdtError::BadValue);
+ }
+ }
+ }
+
+ Ok(Some(topology))
+}
+
+fn read_cpu_info_from(
+ fdt: &Fdt,
+) -> libfdt::Result<(ArrayVec<[CpuInfo; DeviceTreeInfo::MAX_CPUS]>, Option<CpuTopology>)> {
let mut cpus = ArrayVec::new();
+
+ let cpu_map = read_cpu_map_from(fdt)?;
+ let mut topology: CpuTopology = Default::default();
+
let mut cpu_nodes = fdt.compatible_nodes(cstr!("arm,arm-v8"))?;
- for cpu in cpu_nodes.by_ref().take(cpus.capacity()) {
+ for (idx, cpu) in cpu_nodes.by_ref().take(cpus.capacity()).enumerate() {
let cpu_capacity = cpu.getprop_u32(cstr!("capacity-dmips-mhz"))?;
let opp_phandle = cpu.getprop_u32(cstr!("operating-points-v2"))?;
let opptable_info = if let Some(phandle) = opp_phandle {
@@ -215,12 +270,23 @@
};
let info = CpuInfo { opptable_info, cpu_capacity };
cpus.push(info);
+
+ if let Some(ref cpu_map) = cpu_map {
+ let phandle = cpu.get_phandle()?.ok_or(FdtError::NotFound)?;
+ let (cluster, core_idx) = cpu_map.get(&phandle).ok_or(FdtError::BadValue)?;
+ let cluster = topology.clusters[*cluster].get_or_insert(Default::default());
+ if cluster.cores[*core_idx].is_some() {
+ return Err(FdtError::BadValue);
+ }
+ cluster.cores[*core_idx] = Some(idx);
+ }
}
+
if cpu_nodes.next().is_some() {
warn!("DT has more than {} CPU nodes: discarding extra nodes.", cpus.capacity());
}
- Ok(cpus)
+ Ok((cpus, cpu_map.map(|_| topology)))
}
fn validate_cpu_info(cpus: &[CpuInfo]) -> Result<(), FdtValidationError> {
@@ -304,10 +370,17 @@
Ok(node)
}
-fn patch_cpus(fdt: &mut Fdt, cpus: &[CpuInfo]) -> libfdt::Result<()> {
+fn patch_cpus(
+ fdt: &mut Fdt,
+ cpus: &[CpuInfo],
+ topology: &Option<CpuTopology>,
+) -> libfdt::Result<()> {
const COMPAT: &CStr = cstr!("arm,arm-v8");
+ let mut cpu_phandles = Vec::new();
for (idx, cpu) in cpus.iter().enumerate() {
let mut cur = get_nth_compatible(fdt, idx, COMPAT)?.ok_or(FdtError::NoSpace)?;
+ let phandle = cur.as_node().get_phandle()?.unwrap();
+ cpu_phandles.push(phandle);
if let Some(cpu_capacity) = cpu.cpu_capacity {
cur.setprop_inplace(cstr!("capacity-dmips-mhz"), &cpu_capacity.to_be_bytes())?;
}
@@ -317,6 +390,33 @@
while let Some(current) = next {
next = current.delete_and_next_compatible(COMPAT)?;
}
+
+ if let Some(topology) = topology {
+ for (n, cluster) in topology.clusters.iter().enumerate() {
+ let path = CString::new(format!("/cpus/cpu-map/cluster{n}")).unwrap();
+ let cluster_node = fdt.node_mut(&path)?.unwrap();
+ if let Some(cluster) = cluster {
+ let mut iter = cluster_node.first_subnode()?;
+ for core in cluster.cores {
+ let mut core_node = iter.unwrap();
+ iter = if let Some(core_idx) = core {
+ let phandle = *cpu_phandles.get(core_idx).unwrap();
+ let value = u32::from(phandle).to_be_bytes();
+ core_node.setprop_inplace(cstr!("cpu"), &value)?;
+ core_node.next_subnode()?
+ } else {
+ core_node.delete_and_next_subnode()?
+ };
+ }
+ assert!(iter.is_none());
+ } else {
+ cluster_node.nop()?;
+ }
+ }
+ } else {
+ fdt.node_mut(cstr!("/cpus/cpu-map"))?.unwrap().nop()?;
+ }
+
Ok(())
}
@@ -759,6 +859,7 @@
pub memory_range: Range<usize>,
bootargs: Option<CString>,
cpus: ArrayVec<[CpuInfo; DeviceTreeInfo::MAX_CPUS]>,
+ cpu_topology: Option<CpuTopology>,
pci_info: PciInfo,
serial_info: SerialInfo,
pub swiotlb_info: SwiotlbInfo,
@@ -868,7 +969,7 @@
RebootReason::InvalidFdt
})?;
- let cpus = read_cpu_info_from(fdt).map_err(|e| {
+ let (cpus, cpu_topology) = read_cpu_info_from(fdt).map_err(|e| {
error!("Failed to read CPU info from DT: {e}");
RebootReason::InvalidFdt
})?;
@@ -933,6 +1034,7 @@
memory_range,
bootargs,
cpus,
+ cpu_topology,
pci_info,
serial_info,
swiotlb_info,
@@ -959,7 +1061,7 @@
RebootReason::InvalidFdt
})?;
}
- patch_cpus(fdt, &info.cpus).map_err(|e| {
+ patch_cpus(fdt, &info.cpus, &info.cpu_topology).map_err(|e| {
error!("Failed to patch cpus to DT: {e}");
RebootReason::InvalidFdt
})?;