pvmfw: Add irq node to the vcpu stall detector device tree
Specify the interrupt node in the device tree to let the guest
kernel register the PPI for the stall detector.
Bug: 272284118
Test: m pvmfw
Change-Id: Iff5c4bed0036c2fdc810e95ebfdc6c7e1d051271
Signed-off-by: Sebastian Ene <sebastianene@google.com>
diff --git a/pvmfw/platform.dts b/pvmfw/platform.dts
index 2df0768..44834ed 100644
--- a/pvmfw/platform.dts
+++ b/pvmfw/platform.dts
@@ -411,6 +411,7 @@
reg = <0x00 0x3000 0x00 0x1000>;
clock-frequency = <10>;
timeout-sec = <8>;
+ interrupts = <GIC_PPI 0xf PLACEHOLDER>;
};
cpufreq {
diff --git a/pvmfw/src/fdt.rs b/pvmfw/src/fdt.rs
index 84dc14d..11cd5e3 100644
--- a/pvmfw/src/fdt.rs
+++ b/pvmfw/src/fdt.rs
@@ -759,6 +759,83 @@
Ok(SerialInfo { addrs })
}
+#[derive(Default, Debug, PartialEq)]
+struct WdtInfo {
+ addr: u64,
+ size: u64,
+ irq: [u32; WdtInfo::IRQ_CELLS],
+}
+
+impl WdtInfo {
+ const IRQ_CELLS: usize = 3;
+ const IRQ_NR: u32 = 0xf;
+ const ADDR: u64 = 0x3000;
+ const SIZE: u64 = 0x1000;
+ const GIC_PPI: u32 = 1;
+ const IRQ_TYPE_EDGE_RISING: u32 = 1;
+ const GIC_FDT_IRQ_PPI_CPU_SHIFT: u32 = 8;
+ const GIC_FDT_IRQ_PPI_CPU_MASK: u32 = 0xff << Self::GIC_FDT_IRQ_PPI_CPU_SHIFT;
+
+ const fn get_expected(num_cpus: usize) -> Self {
+ Self {
+ addr: Self::ADDR,
+ size: Self::SIZE,
+ irq: [
+ Self::GIC_PPI,
+ Self::IRQ_NR,
+ ((((1 << num_cpus) - 1) << Self::GIC_FDT_IRQ_PPI_CPU_SHIFT)
+ & Self::GIC_FDT_IRQ_PPI_CPU_MASK)
+ | Self::IRQ_TYPE_EDGE_RISING,
+ ],
+ }
+ }
+}
+
+fn read_wdt_info_from(fdt: &Fdt) -> libfdt::Result<WdtInfo> {
+ let mut node_iter = fdt.compatible_nodes(cstr!("qemu,vcpu-stall-detector"))?;
+ let node = node_iter.next().ok_or(FdtError::NotFound)?;
+ let mut ranges = node.reg()?.ok_or(FdtError::NotFound)?;
+
+ let reg = ranges.next().ok_or(FdtError::NotFound)?;
+ let size = reg.size.ok_or(FdtError::NotFound)?;
+ if ranges.next().is_some() {
+ warn!("Discarding extra vmwdt <reg> entries.");
+ }
+
+ let interrupts = node.getprop_cells(cstr!("interrupts"))?.ok_or(FdtError::NotFound)?;
+ let mut chunks = CellChunkIterator::<{ WdtInfo::IRQ_CELLS }>::new(interrupts);
+ let irq = chunks.next().ok_or(FdtError::NotFound)?;
+
+ if chunks.next().is_some() {
+ warn!("Discarding extra vmwdt <interrupts> entries.");
+ }
+
+ Ok(WdtInfo { addr: reg.addr, size, irq })
+}
+
+fn validate_wdt_info(wdt: &WdtInfo, num_cpus: usize) -> Result<(), RebootReason> {
+ if *wdt != WdtInfo::get_expected(num_cpus) {
+ error!("Invalid watchdog timer: {wdt:?}");
+ return Err(RebootReason::InvalidFdt);
+ }
+
+ 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();
+ }
+
+ let mut node = fdt
+ .root_mut()
+ .next_compatible(cstr!("qemu,vcpu-stall-detector"))?
+ .ok_or(libfdt::FdtError::NotFound)?;
+ node.setprop_inplace(cstr!("interrupts"), interrupts.as_bytes())?;
+ Ok(())
+}
+
/// Patch the DT by deleting the ns16550a compatible nodes whose address are unknown
fn patch_serial_info(fdt: &mut Fdt, serial_info: &SerialInfo) -> libfdt::Result<()> {
let name = cstr!("ns16550a");
@@ -863,6 +940,7 @@
let num_cpus: u32 = num_cpus.try_into().unwrap();
let cpu_mask: u32 = (((0x1 << num_cpus) - 1) & 0xff) << 8;
+
for v in value.iter_mut().skip(2).step_by(CELLS_PER_INTERRUPT) {
*v |= cpu_mask;
}
@@ -1053,6 +1131,12 @@
})?;
validate_pci_info(&pci_info, &memory_range)?;
+ let wdt_info = read_wdt_info_from(fdt).map_err(|e| {
+ error!("Failed to read vCPU stall detector info from DT: {e}");
+ RebootReason::InvalidFdt
+ })?;
+ validate_wdt_info(&wdt_info, cpus.len())?;
+
let serial_info = read_serial_info_from(fdt).map_err(|e| {
error!("Failed to read serial info from DT: {e}");
RebootReason::InvalidFdt
@@ -1141,6 +1225,10 @@
error!("Failed to patch pci info to DT: {e}");
RebootReason::InvalidFdt
})?;
+ patch_wdt_info(fdt, info.cpus.len()).map_err(|e| {
+ error!("Failed to patch wdt info to DT: {e}");
+ RebootReason::InvalidFdt
+ })?;
patch_serial_info(fdt, &info.serial_info).map_err(|e| {
error!("Failed to patch serial info to DT: {e}");
RebootReason::InvalidFdt
diff --git a/pvmfw/testdata/test_crosvm_dt_base.dtsi b/pvmfw/testdata/test_crosvm_dt_base.dtsi
index 7d1161a..55f0a14 100644
--- a/pvmfw/testdata/test_crosvm_dt_base.dtsi
+++ b/pvmfw/testdata/test_crosvm_dt_base.dtsi
@@ -141,6 +141,7 @@
reg = <0x00 0x3000 0x00 0x1000>;
clock-frequency = <0x0a>;
timeout-sec = <0x08>;
+ interrupts = <0x01 0xf 0x101>; // <GIC_PPI 0xf IRQ_TYPE_EDGE_RISING>
};
__symbols__ {