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
     })?;