pvmfw: Introduce CpuInfo for DT parsing
Refactor the code to add support for per-vCPU DT information. The struct
is currently empty and will be used by future CLs.
Similarly to the way UARTs are handled, discard extra CPU nodes with a
warning instead of aborting the boot.
Check at build time that gic_patched_size() won't panic when unwrapped.
Test: m pvmfw
Change-Id: I5f985c6afca9fc909d12f61857ab12ca9eee0ecf
diff --git a/pvmfw/src/fdt.rs b/pvmfw/src/fdt.rs
index d2aad61..e0927c8 100644
--- a/pvmfw/src/fdt.rs
+++ b/pvmfw/src/fdt.rs
@@ -41,6 +41,7 @@
use log::error;
use log::info;
use log::warn;
+use static_assertions::const_assert;
use tinyvec::ArrayVec;
use vmbase::fdt::SwiotlbInfo;
use vmbase::layout::{crosvm::MEM_START, MAX_VIRT_ADDR};
@@ -172,33 +173,40 @@
.setprop_inplace(cstr!("reg"), [addr.to_be(), size.to_be()].as_bytes())
}
-/// Read the number of CPUs from DT
-fn read_num_cpus_from(fdt: &Fdt) -> libfdt::Result<usize> {
- Ok(fdt.compatible_nodes(cstr!("arm,arm-v8"))?.count())
-}
+#[derive(Debug, Default)]
+struct CpuInfo {}
-/// Validate number of CPUs
-fn validate_num_cpus(num_cpus: usize) -> Result<(), FdtValidationError> {
- if num_cpus == 0 || DeviceTreeInfo::gic_patched_size(num_cpus).is_none() {
- Err(FdtValidationError::InvalidCpuCount(num_cpus))
- } else {
- Ok(())
+fn read_cpu_info_from(fdt: &Fdt) -> libfdt::Result<ArrayVec<[CpuInfo; DeviceTreeInfo::MAX_CPUS]>> {
+ let mut cpus = ArrayVec::new();
+
+ let mut cpu_nodes = fdt.compatible_nodes(cstr!("arm,arm-v8"))?;
+ for _cpu in cpu_nodes.by_ref().take(cpus.capacity()) {
+ let info = CpuInfo {};
+ cpus.push(info);
}
+ if cpu_nodes.next().is_some() {
+ warn!("DT has more than {} CPU nodes: discarding extra nodes.", cpus.capacity());
+ }
+
+ Ok(cpus)
}
-/// Patch DT by keeping `num_cpus` number of arm,arm-v8 compatible nodes, and pruning the rest.
-fn patch_num_cpus(fdt: &mut Fdt, num_cpus: usize) -> libfdt::Result<()> {
- let cpu = cstr!("arm,arm-v8");
- let mut next = fdt.root_mut()?.next_compatible(cpu)?;
- for _ in 0..num_cpus {
- next = if let Some(current) = next {
- current.next_compatible(cpu)?
- } else {
- return Err(FdtError::NoSpace);
- };
+fn validate_cpu_info(cpus: &[CpuInfo]) -> Result<(), FdtValidationError> {
+ if cpus.is_empty() {
+ return Err(FdtValidationError::InvalidCpuCount(0));
+ }
+
+ Ok(())
+}
+
+fn patch_cpus(fdt: &mut Fdt, cpus: &[CpuInfo]) -> libfdt::Result<()> {
+ const COMPAT: &CStr = cstr!("arm,arm-v8");
+ let mut next = fdt.root_mut()?.next_compatible(COMPAT)?;
+ for _cpu in cpus {
+ next = next.ok_or(FdtError::NoSpace)?.next_compatible(COMPAT)?;
}
while let Some(current) = next {
- next = current.delete_and_next_compatible(cpu)?;
+ next = current.delete_and_next_compatible(COMPAT)?;
}
Ok(())
}
@@ -582,7 +590,8 @@
let mut range1 = ranges.next().ok_or(FdtError::NotFound)?;
let addr = range0.addr;
- // `validate_num_cpus()` checked that this wouldn't panic
+ // `read_cpu_info_from()` guarantees that we have at most MAX_CPUS.
+ const_assert!(DeviceTreeInfo::gic_patched_size(DeviceTreeInfo::MAX_CPUS).is_some());
let size = u64::try_from(DeviceTreeInfo::gic_patched_size(num_cpus).unwrap()).unwrap();
// range1 is just below range0
@@ -628,7 +637,7 @@
pub initrd_range: Option<Range<usize>>,
pub memory_range: Range<usize>,
bootargs: Option<CString>,
- num_cpus: usize,
+ cpus: ArrayVec<[CpuInfo; DeviceTreeInfo::MAX_CPUS]>,
pci_info: PciInfo,
serial_info: SerialInfo,
pub swiotlb_info: SwiotlbInfo,
@@ -637,7 +646,9 @@
}
impl DeviceTreeInfo {
- fn gic_patched_size(num_cpus: usize) -> Option<usize> {
+ const MAX_CPUS: usize = 16;
+
+ const fn gic_patched_size(num_cpus: usize) -> Option<usize> {
const GIC_REDIST_SIZE_PER_CPU: usize = 32 * SIZE_4KB;
GIC_REDIST_SIZE_PER_CPU.checked_mul(num_cpus)
@@ -733,12 +744,12 @@
RebootReason::InvalidFdt
})?;
- let num_cpus = read_num_cpus_from(fdt).map_err(|e| {
- error!("Failed to read num cpus from DT: {e}");
+ let cpus = read_cpu_info_from(fdt).map_err(|e| {
+ error!("Failed to read CPU info from DT: {e}");
RebootReason::InvalidFdt
})?;
- validate_num_cpus(num_cpus).map_err(|e| {
- error!("Failed to validate num cpus from DT: {e}");
+ validate_cpu_info(&cpus).map_err(|e| {
+ error!("Failed to validate CPU info from DT: {e}");
RebootReason::InvalidFdt
})?;
@@ -786,7 +797,7 @@
initrd_range,
memory_range,
bootargs,
- num_cpus,
+ cpus,
pci_info,
serial_info,
swiotlb_info,
@@ -812,7 +823,7 @@
RebootReason::InvalidFdt
})?;
}
- patch_num_cpus(fdt, info.num_cpus).map_err(|e| {
+ patch_cpus(fdt, &info.cpus).map_err(|e| {
error!("Failed to patch cpus to DT: {e}");
RebootReason::InvalidFdt
})?;
@@ -828,11 +839,11 @@
error!("Failed to patch swiotlb info to DT: {e}");
RebootReason::InvalidFdt
})?;
- patch_gic(fdt, info.num_cpus).map_err(|e| {
+ patch_gic(fdt, info.cpus.len()).map_err(|e| {
error!("Failed to patch gic info to DT: {e}");
RebootReason::InvalidFdt
})?;
- patch_timer(fdt, info.num_cpus).map_err(|e| {
+ patch_timer(fdt, info.cpus.len()).map_err(|e| {
error!("Failed to patch timer info to DT: {e}");
RebootReason::InvalidFdt
})?;