pvmfw: Handle absence of vcpu-stall-detector to avoid boot failure

In some VM configurations, the `qemu,vcpu-stall-detector` may not be
used. This change ensures that the absence of a compatible vCPU stall
detector does not cause a boot failure.

When no compatible node is found, the process continues without
attempting to patch the `interrupts` property.
This allows for smoother boot operations in VMs that do not use
qemu,vcpu-stall-detector.

Test: m pvmfw_img
Bug: 385802423
Change-Id: I8094f782345cbe8f4d7a13a81ca44aee72764540
Signed-off-by: Sreenad Menon <quic_sreemeno@quicinc.com>
diff --git a/guest/pvmfw/src/fdt.rs b/guest/pvmfw/src/fdt.rs
index 8adf8e5..4853e85 100644
--- a/guest/pvmfw/src/fdt.rs
+++ b/guest/pvmfw/src/fdt.rs
@@ -874,9 +874,13 @@
     }
 }
 
-fn read_wdt_info_from(fdt: &Fdt) -> libfdt::Result<WdtInfo> {
+fn read_wdt_info_from(fdt: &Fdt) -> libfdt::Result<Option<WdtInfo>> {
     let mut node_iter = fdt.compatible_nodes(c"qemu,vcpu-stall-detector")?;
-    let node = node_iter.next().ok_or(FdtError::NotFound)?;
+    let Some(node) = node_iter.next() else {
+        // Some VMs may not have qemu,vcpu-stall-detector compatible watchdogs.
+        // Do not treat it as error.
+        return Ok(None);
+    };
     let mut ranges = node.reg()?.ok_or(FdtError::NotFound)?;
 
     let reg = ranges.next().ok_or(FdtError::NotFound)?;
@@ -893,7 +897,7 @@
         warn!("Discarding extra vmwdt <interrupts> entries.");
     }
 
-    Ok(WdtInfo { addr: reg.addr, size, irq })
+    Ok(Some(WdtInfo { addr: reg.addr, size, irq }))
 }
 
 fn validate_wdt_info(wdt: &WdtInfo, num_cpus: usize) -> Result<(), RebootReason> {
@@ -905,18 +909,25 @@
     Ok(())
 }
 
-fn patch_wdt_info(fdt: &mut Fdt, num_cpus: usize) -> libfdt::Result<()> {
-    let mut interrupts = WdtInfo::get_expected(num_cpus).irq;
-    for v in interrupts.iter_mut() {
-        *v = v.to_be();
-    }
-
+fn patch_wdt_info(
+    fdt: &mut Fdt,
+    wdt_info: &Option<WdtInfo>,
+    num_cpus: usize,
+) -> libfdt::Result<()> {
     let mut node = fdt
         .root_mut()
         .next_compatible(c"qemu,vcpu-stall-detector")?
         .ok_or(libfdt::FdtError::NotFound)?;
-    node.setprop_inplace(c"interrupts", interrupts.as_bytes())?;
-    Ok(())
+
+    if wdt_info.is_some() {
+        let mut interrupts = WdtInfo::get_expected(num_cpus).irq;
+        for v in interrupts.iter_mut() {
+            *v = v.to_be();
+        }
+        node.setprop_inplace(c"interrupts", interrupts.as_bytes())
+    } else {
+        node.nop()
+    }
 }
 
 /// Patch the DT by deleting the ns16550a compatible nodes whose address are unknown
@@ -1088,6 +1099,7 @@
     untrusted_props: BTreeMap<CString, Vec<u8>>,
     vm_ref_dt_props_info: BTreeMap<CString, Vec<u8>>,
     vcpufreq_info: Option<VcpufreqInfo>,
+    wdt_info: Option<WdtInfo>,
 }
 
 impl DeviceTreeInfo {
@@ -1218,7 +1230,9 @@
         error!("Failed to read vCPU stall detector info from DT: {e}");
         RebootReason::InvalidFdt
     })?;
-    validate_wdt_info(&wdt_info, cpus.len())?;
+    if let Some(ref info) = wdt_info {
+        validate_wdt_info(info, cpus.len())?;
+    }
 
     let serial_info = read_serial_info_from(fdt).map_err(|e| {
         error!("Failed to read serial info from DT: {e}");
@@ -1284,6 +1298,7 @@
         untrusted_props,
         vm_ref_dt_props_info,
         vcpufreq_info,
+        wdt_info,
     })
 }
 
@@ -1316,7 +1331,7 @@
         error!("Failed to patch pci info to DT: {e}");
         RebootReason::InvalidFdt
     })?;
-    patch_wdt_info(fdt, info.cpus.len()).map_err(|e| {
+    patch_wdt_info(fdt, &info.wdt_info, info.cpus.len()).map_err(|e| {
         error!("Failed to patch wdt info to DT: {e}");
         RebootReason::InvalidFdt
     })?;