@samitouri / QOSamiQemu / commits / 2f27572eb5

arm: virt: create sbsa-gwdt watchdog

Allow to use SBSA generic watchdog with virt machine type. (includes conditional generation of corresponding FDT and ACPI GTDT descriptors) Use '-device sbsa-gwdt' to command line to enable it. Instead of using dynamic sysbus infra to wire up MMIO/IRQ/FDT, statically assign resources in machine's mem/irq maps and wire them up at device (pre_)plug handlers. It's similar to dynamic sysbus wiring, modulo resources are nailed down statically, and wiring is limited to virt machine only. (Benefit is that tests don't break anymore on rebase due to address being stable) Tested with Fedora 43: FDT: -M virt,acpi=off -device sbsa-gwdt ACPI: -M virt -device sbsa-gwdt Note: Windows sees GTDT, initializes watchdog but instead pinging WRR it sets/advances WOR to way too large value, so it's never going to trigger watchdog reboot (it's Windows driver issue though). Signed-off-by: Igor Mammedov <imammedo@redhat.com> Reviewed-by: Eric Auger <eric.auger@redhat.com> Reviewed-by: Michael S. Tsirkin <mst@redhat.com> Signed-off-by: Michael S. Tsirkin <mst@redhat.com> Message-ID: <20260702145856.1539572-5-imammedo@redhat.com>

Igor Mammedov committed Jul 2, 2026 at 16:58 UTC 2f27572eb5ca08c71cdd872d018ea5ad8791dce4
7 files changed +88 -2
docs/system/arm/virt.rst
+9
@@ -39,6 +39,7 @@ The virt board supports:
39 - A PL061 GPIO controller
40 - An optional machine-wide SMMUv3 IOMMU
41 - User-creatable SMMUv3 devices (see below for example)
42 +- An optional SBSA Generic Watchdog Timer (see below)
43 - hotpluggable DIMMs
44 - hotpluggable NVDIMMs
45 - An MSI controller (GICv2m or ITS).
@@ -314,6 +315,14 @@ User-creatable SMMUv3 devices
315 bypassing QEMU and improving throughput for workloads that issue many
316 invalidations. Without it, every invalidation command traps into QEMU.
317
318 +SBSA Generic Watchdog
319 +"""""""""""""""""""""
320 +
321 +The SBSA Generic Watchdog Timer (GWDT) can be added to the virt machine
322 +using ``-device sbsa-gwdt``. It is only supported on the virt machine,
323 +which wires up statically assigned MMIO regions and IRQs via
324 +machine-specific plug handlers.
325 +
326 Linux guest kernel configuration
327 """"""""""""""""""""""""""""""""
328
hw/arm/Kconfig
+1
@@ -36,6 +36,7 @@ config ARM_VIRT
36 select VIRTIO_MEM_SUPPORTED
37 select ACPI_CXL
38 select ACPI_HMAT
39 + select WDT_SBSA
40
41 config CUBIEBOARD
42 bool
hw/arm/virt-acpi-build.c
+27 -2
@@ -64,6 +64,7 @@
64 #include "hw/virtio/virtio-acpi.h"
65 #include "target/arm/cpu.h"
66 #include "target/arm/multiprocessing.h"
67 +#include "hw/watchdog/sbsa_gwdt.h"
68
69 #include "smmuv3-accel.h"
70 #include "tegra241-cmdqv.h"
@@ -868,6 +869,8 @@ build_gtdt(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms)
869 const uint32_t irqflags = 0; /* Interrupt is Level triggered */
870 AcpiTable table = { .sig = "GTDT", .rev = 3, .oem_id = vms->oem_id,
871 .oem_table_id = vms->oem_table_id };
872 + uint32_t gtdt_start = table_data->len;
873 + Object *wdt = object_resolve_type_unambiguous(TYPE_WDT_SBSA, NULL);
874
875 acpi_table_begin(&table, table_data);
876
@@ -898,10 +901,15 @@ build_gtdt(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms)
901 build_append_int_noprefix(table_data, irqflags, 4);
902 /* CntReadBase Physical address */
903 build_append_int_noprefix(table_data, 0xFFFFFFFFFFFFFFFF, 8);
904 +
905 /* Platform Timer Count */
902 - build_append_int_noprefix(table_data, 0, 4);
906 + build_append_int_noprefix(table_data, wdt ? 1 : 0, 4);
907 /* Platform Timer Offset */
904 - build_append_int_noprefix(table_data, 0, 4);
908 + build_append_int_noprefix(table_data,
909 + wdt ? (table_data->len - gtdt_start) +
910 + 4 + 4 + 4 /* len of this & following 2 fields to skip */
911 + : 0, 4);
912 +
913 if (vms->ns_el2_virt_timer_irq) {
914 /* Virtual EL2 Timer GSIV */
915 build_append_int_noprefix(table_data, ARCH_TIMER_NS_EL2_VIRT_IRQ, 4);
@@ -911,6 +919,23 @@ build_gtdt(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms)
919 build_append_int_noprefix(table_data, 0, 4);
920 build_append_int_noprefix(table_data, 0, 4);
921 }
922 +
923 + /* ACPI 6.5 spec: 5.2.25.2 ARM Generic Watchdog Structure (Table 5-124) */
924 + if (wdt) {
925 + hwaddr rbase = vms->memmap[VIRT_GWDT_REFRESH].base;
926 + hwaddr cbase = vms->memmap[VIRT_GWDT_CONTROL].base;
927 + int irq = ARM_SPI_BASE + vms->irqmap[VIRT_GWDT_WS0];
928 +
929 + build_append_int_noprefix(table_data, 1 /* Type: Watchdog GT */, 1);
930 + build_append_int_noprefix(table_data, 28 /* Length */, 2);
931 + build_append_int_noprefix(table_data, 0, 1); /* Reserved */
932 + /* RefreshFrame Physical Address */
933 + build_append_int_noprefix(table_data, rbase, 8);
934 + /* WatchdogControlFrame Physical Address */
935 + build_append_int_noprefix(table_data, cbase, 8);
936 + build_append_int_noprefix(table_data, irq, 4); /* Watchdog Timer GSIV */
937 + build_append_int_noprefix(table_data, 0, 4); /* Watchdog Timer Flags */
938 + }
939 acpi_table_end(linker, &table);
940 }
941
hw/arm/virt.c
+44
@@ -95,6 +95,7 @@
95 #include "hw/cxl/cxl.h"
96 #include "hw/cxl/cxl_host.h"
97 #include "qemu/guest-random.h"
98 +#include "hw/watchdog/sbsa_gwdt.h"
99
100 static GlobalProperty arm_virt_compat_defaults[] = {
101 { TYPE_VIRTIO_IOMMU_PCI, "aw-bits", "48" },
@@ -214,6 +215,8 @@ static const MemMapEntry base_memmap[] = {
215 /* ...repeating for a total of NUM_VIRTIO_TRANSPORTS, each of that size */
216 [VIRT_PLATFORM_BUS] = { 0x0c000000, 0x02000000 },
217 [VIRT_SECURE_MEM] = { 0x0e000000, 0x01000000 },
218 + [VIRT_GWDT_REFRESH] = { 0x0f000000, 0x00001000 },
219 + [VIRT_GWDT_CONTROL] = { 0x0f001000, 0x00001000 },
220 [VIRT_PCIE_MMIO] = { 0x10000000, 0x2eff0000 },
221 [VIRT_PCIE_PIO] = { 0x3eff0000, 0x00010000 },
222 [VIRT_PCIE_ECAM] = { 0x3f000000, 0x01000000 },
@@ -264,6 +267,7 @@ static const int a15irqmap[] = {
267 [VIRT_GPIO] = 7,
268 [VIRT_UART1] = 8,
269 [VIRT_ACPI_GED] = 9,
270 + [VIRT_GWDT_WS0] = 10,
271 [VIRT_MMIO] = 16, /* ...to 16 + NUM_VIRTIO_TRANSPORTS - 1 */
272 [VIRT_GIC_V2M] = 48, /* ...to 48 + NUM_GICV2M_SPIS - 1 */
273 [VIRT_SMMU] = 74, /* ...to 74 + NUM_SMMU_IRQS - 1 */
@@ -2085,6 +2089,27 @@ static void create_smmu(const VirtMachineState *vms, PCIBus *bus)
2089 create_smmuv3_dt_bindings(vms, base, size, irq);
2090 }
2091
2092 +static void create_gwdt_dt_bindings(VirtMachineState *vms)
2093 +{
2094 + MachineState *ms = MACHINE(vms);
2095 + hwaddr rbase = vms->memmap[VIRT_GWDT_REFRESH].base;
2096 + hwaddr cbase = vms->memmap[VIRT_GWDT_CONTROL].base;
2097 + int irq = vms->irqmap[VIRT_GWDT_WS0];
2098 + char *nodename = g_strdup_printf("/watchdog@%" PRIx64, cbase);
2099 +
2100 + qemu_fdt_add_subnode(ms->fdt, nodename);
2101 + qemu_fdt_setprop_string(ms->fdt, nodename,
2102 + "compatible", "arm,sbsa-gwdt");
2103 + qemu_fdt_setprop_sized_cells(ms->fdt, nodename, "reg",
2104 + 2, cbase, 2, SBSA_GWDT_CMMIO_SIZE,
2105 + 2, rbase, 2, SBSA_GWDT_RMMIO_SIZE);
2106 + qemu_fdt_setprop_cells(ms->fdt, nodename, "interrupts",
2107 + GIC_FDT_IRQ_TYPE_SPI, irq,
2108 + GIC_FDT_IRQ_FLAGS_LEVEL_HI);
2109 + qemu_fdt_setprop_cell(ms->fdt, nodename, "timeout-sec", 30);
2110 + g_free(nodename);
2111 +}
2112 +
2113 static void create_virtio_iommu_dt_bindings(VirtMachineState *vms)
2114 {
2115 const char compat[] = "virtio,pci-iommu\0pci1af4,1057";
@@ -3820,6 +3845,11 @@ static void virt_machine_device_pre_plug_cb(HotplugHandler *hotplug_dev,
3845 qlist_append_str(reserved_regions, resv_prop_str);
3846 qdev_prop_set_array(dev, "reserved-regions", reserved_regions);
3847 g_free(resv_prop_str);
3848 + } else if (object_dynamic_cast(OBJECT(dev), TYPE_WDT_SBSA)) {
3849 + uint64_t cntfrq = object_property_get_int(OBJECT(qemu_get_cpu(0)),
3850 + "cntfrq", &error_abort);
3851 +
3852 + qdev_prop_set_uint64(dev, "clock-frequency", cntfrq);
3853 } else if (object_dynamic_cast(OBJECT(dev), TYPE_ARM_SMMUV3)) {
3854 if (vms->legacy_smmuv3_present || vms->iommu == VIRT_IOMMU_VIRTIO) {
3855 error_setg(errp, "virt machine already has %s set. "
@@ -3871,6 +3901,19 @@ static void virt_machine_device_plug_cb(HotplugHandler *hotplug_dev,
3901 {
3902 VirtMachineState *vms = VIRT_MACHINE(hotplug_dev);
3903
3904 + if (object_dynamic_cast(OBJECT(dev), TYPE_WDT_SBSA)) {
3905 + SysBusDevice *s = SYS_BUS_DEVICE(dev);
3906 + hwaddr rbase = vms->memmap[VIRT_GWDT_REFRESH].base;
3907 + hwaddr cbase = vms->memmap[VIRT_GWDT_CONTROL].base;
3908 + int irq = vms->irqmap[VIRT_GWDT_WS0];
3909 +
3910 + sysbus_mmio_map(s, 0, rbase);
3911 + sysbus_mmio_map(s, 1, cbase);
3912 + sysbus_connect_irq(s, 0, qdev_get_gpio_in(vms->gic, irq));
3913 +
3914 + create_gwdt_dt_bindings(vms);
3915 + }
3916 +
3917 if (vms->platform_bus_dev) {
3918 MachineClass *mc = MACHINE_GET_CLASS(vms);
3919
@@ -4123,6 +4166,7 @@ static void virt_machine_class_init(ObjectClass *oc, const void *data)
4166 machine_class_allow_dynamic_sysbus_dev(mc, TYPE_RAMFB_DEVICE);
4167 machine_class_allow_dynamic_sysbus_dev(mc, TYPE_UEFI_VARS_SYSBUS);
4168 machine_class_allow_dynamic_sysbus_dev(mc, TYPE_ARM_SMMUV3);
4169 + machine_class_allow_dynamic_sysbus_dev(mc, TYPE_WDT_SBSA);
4170 #ifdef CONFIG_TPM
4171 machine_class_allow_dynamic_sysbus_dev(mc, TYPE_TPM_TIS_SYSBUS);
4172 #endif
hw/core/sysbus-fdt.c
+2
@@ -36,6 +36,7 @@
36 #include "hw/display/ramfb.h"
37 #include "hw/uefi/var-service-api.h"
38 #include "hw/arm/fdt.h"
39 +#include "hw/watchdog/sbsa_gwdt.h"
40
41 /*
42 * internal struct that contains the information to create dynamic
@@ -140,6 +141,7 @@ static const BindingEntry bindings[] = {
141 TYPE_BINDING(TYPE_ARM_SMMUV3, no_fdt_node),
142 TYPE_BINDING(TYPE_RAMFB_DEVICE, no_fdt_node),
143 TYPE_BINDING(TYPE_UEFI_VARS_SYSBUS, add_uefi_vars_node),
144 + TYPE_BINDING(TYPE_WDT_SBSA, no_fdt_node),
145 TYPE_BINDING("", NULL), /* last element */
146 };
147
hw/watchdog/sbsa_gwdt.c
+2
@@ -285,6 +285,8 @@ static void wdt_sbsa_gwdt_class_init(ObjectClass *klass, const void *data)
285 dc->realize = wdt_sbsa_gwdt_realize;
286 device_class_set_legacy_reset(dc, wdt_sbsa_gwdt_reset);
287 dc->hotpluggable = false;
288 + /* requires machine-specific wiring (MMIO/IRQ/FDT) at plug time */
289 + dc->user_creatable = true;
290 set_bit(DEVICE_CATEGORY_WATCHDOG, dc->categories);
291 dc->vmsd = &vmstate_sbsa_gwdt;
292 dc->desc = "SBSA-compliant generic watchdog device";
include/hw/arm/virt.h
+3
@@ -97,6 +97,9 @@ enum {
97 VIRT_NVDIMM_ACPI,
98 VIRT_PVTIME,
99 VIRT_ACPI_PCIHP,
100 + VIRT_GWDT_WS0,
101 + VIRT_GWDT_REFRESH,
102 + VIRT_GWDT_CONTROL,
103 VIRT_LOWMEMMAP_LAST,
104 };
105