@samitouri / QOSamiQemu / commits / 285bc90d5b

arm: virt: add support for WDAT based watchdog

Add WDAT handling for sbsa-gwdt on arm/virt machine. WDAT mode is enabled by 'wdat' option: ex: "-device sbsa-gwdt,wdat=on" When WDAT is enabled: - Build the WDAT ACPI table instead of the GTDT watchdog entry, since they are mutually exclusive due to different timer resolution (WDAT uses 1 kHz vs GTDT's system counter frequency). - Skip FDT watchdog node creation, as the DT-based Linux driver would use the system counter frequency which doesn't match the WDAT-mode 1 kHz clock. 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-8-imammedo@redhat.com>

Igor Mammedov committed Jul 2, 2026 at 16:58 UTC 285bc90d5b31775b7803e50822930a2914bf42de
3 files changed +49 -12
docs/system/arm/virt.rst
+15
@@ -323,6 +323,21 @@ 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 +Two modes are available:
327 +
328 +Native mode (default)
329 + The watchdog is described via the ACPI GTDT table and FDT, using
330 + the system counter frequency. Example::
331 +
332 + -device sbsa-gwdt
333 +
334 +WDAT mode
335 + The watchdog is described via the ACPI WDAT table (no FDT node),
336 + using a 1 kHz timer frequency. WDAT and GTDT watchdog entries are
337 + mutually exclusive. Example::
338 +
339 + -device sbsa-gwdt,wdat=on
340 +
341 Linux guest kernel configuration
342 """"""""""""""""""""""""""""""""
343
hw/arm/virt-acpi-build.c
+26 -8
@@ -65,6 +65,7 @@
65 #include "target/arm/cpu.h"
66 #include "target/arm/multiprocessing.h"
67 #include "hw/watchdog/sbsa_gwdt.h"
68 +#include "hw/acpi/wdat-gwdt.h"
69
70 #include "smmuv3-accel.h"
71 #include "tegra241-cmdqv.h"
@@ -859,7 +860,8 @@ build_srat(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms)
860 * 5.2.25 Generic Timer Description Table (GTDT)
861 */
862 static void
862 -build_gtdt(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms)
863 +build_gtdt(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms,
864 + bool add_watchdog)
865 {
866 /*
867 * Table 5-117 Flag Definitions
@@ -870,7 +872,6 @@ build_gtdt(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms)
872 AcpiTable table = { .sig = "GTDT", .rev = 3, .oem_id = vms->oem_id,
873 .oem_table_id = vms->oem_table_id };
874 uint32_t gtdt_start = table_data->len;
873 - Object *wdt = object_resolve_type_unambiguous(TYPE_WDT_SBSA, NULL);
875
876 acpi_table_begin(&table, table_data);
877
@@ -903,12 +904,12 @@ build_gtdt(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms)
904 build_append_int_noprefix(table_data, 0xFFFFFFFFFFFFFFFF, 8);
905
906 /* Platform Timer Count */
906 - build_append_int_noprefix(table_data, wdt ? 1 : 0, 4);
907 + build_append_int_noprefix(table_data, add_watchdog ? 1 : 0, 4);
908 /* Platform Timer Offset */
909 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);
910 + add_watchdog ? (table_data->len - gtdt_start) +
911 + 4 + 4 + 4 /* len of this & following 2 fields to skip */
912 + : 0, 4);
913
914 if (vms->ns_el2_virt_timer_irq) {
915 /* Virtual EL2 Timer GSIV */
@@ -921,7 +922,7 @@ build_gtdt(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms)
922 }
923
924 /* ACPI 6.5 spec: 5.2.25.2 ARM Generic Watchdog Structure (Table 5-124) */
924 - if (wdt) {
925 + if (add_watchdog) {
926 hwaddr rbase = vms->memmap[VIRT_GWDT_REFRESH].base;
927 hwaddr cbase = vms->memmap[VIRT_GWDT_CONTROL].base;
928 int irq = ARM_SPI_BASE + vms->irqmap[VIRT_GWDT_WS0];
@@ -1332,13 +1333,19 @@ void virt_acpi_build(VirtMachineState *vms, AcpiBuildTables *tables)
1333 VirtMachineClass *vmc = VIRT_MACHINE_GET_CLASS(vms);
1334 GArray *table_offsets;
1335 unsigned dsdt, xsdt;
1336 + bool has_wdat = false;
1337 GArray *tables_blob = tables->table_data;
1338 MachineState *ms = MACHINE(vms);
1339 CPUCoreCaches caches[CPU_MAX_CACHES];
1340 unsigned int num_caches;
1341 + Object *wdt = object_resolve_type_unambiguous(TYPE_WDT_SBSA, NULL);
1342
1343 num_caches = virt_get_caches(vms, caches);
1344
1345 + if (wdt) {
1346 + has_wdat = object_property_get_bool(wdt, "wdat", &error_abort);
1347 + }
1348 +
1349 table_offsets = g_array_new(false, true /* clear */,
1350 sizeof(uint32_t));
1351
@@ -1357,6 +1364,17 @@ void virt_acpi_build(VirtMachineState *vms, AcpiBuildTables *tables)
1364 acpi_add_table(table_offsets, tables_blob);
1365 build_madt(tables_blob, tables->linker, vms);
1366
1367 + acpi_add_table(table_offsets, tables_blob);
1368 + if (wdt && has_wdat) {
1369 + uint64_t freq = object_property_get_uint(wdt, "clock-frequency",
1370 + &error_abort);
1371 + build_gwdt_wdat(tables_blob, tables->linker,
1372 + vms->oem_id, vms->oem_table_id,
1373 + vms->memmap[VIRT_GWDT_REFRESH].base,
1374 + vms->memmap[VIRT_GWDT_CONTROL].base,
1375 + freq);
1376 + }
1377 +
1378 if (!vmc->no_cpu_topology) {
1379 acpi_add_table(table_offsets, tables_blob);
1380 build_pptt(tables_blob, tables->linker, ms, vms->oem_id,
@@ -1364,7 +1382,7 @@ void virt_acpi_build(VirtMachineState *vms, AcpiBuildTables *tables)
1382 }
1383
1384 acpi_add_table(table_offsets, tables_blob);
1367 - build_gtdt(tables_blob, tables->linker, vms);
1385 + build_gtdt(tables_blob, tables->linker, vms, wdt && !has_wdat);
1386
1387 acpi_add_table(table_offsets, tables_blob);
1388 {
hw/arm/virt.c
+8 -4
@@ -3846,10 +3846,12 @@ static void virt_machine_device_pre_plug_cb(HotplugHandler *hotplug_dev,
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);
3849 + if (!object_property_get_bool(OBJECT(dev), "wdat", &error_abort)) {
3850 + uint64_t cntfrq = object_property_get_int(OBJECT(qemu_get_cpu(0)),
3851 + "cntfrq", &error_abort);
3852
3852 - qdev_prop_set_uint64(dev, "clock-frequency", cntfrq);
3853 + qdev_prop_set_uint64(dev, "clock-frequency", cntfrq);
3854 + }
3855 } else if (object_dynamic_cast(OBJECT(dev), TYPE_ARM_SMMUV3)) {
3856 if (vms->legacy_smmuv3_present || vms->iommu == VIRT_IOMMU_VIRTIO) {
3857 error_setg(errp, "virt machine already has %s set. "
@@ -3911,7 +3913,9 @@ static void virt_machine_device_plug_cb(HotplugHandler *hotplug_dev,
3913 sysbus_mmio_map(s, 1, cbase);
3914 sysbus_connect_irq(s, 0, qdev_get_gpio_in(vms->gic, irq));
3915
3914 - create_gwdt_dt_bindings(vms);
3916 + if (!object_property_get_bool(OBJECT(dev), "wdat", &error_abort)) {
3917 + create_gwdt_dt_bindings(vms);
3918 + }
3919 }
3920
3921 if (vms->platform_bus_dev) {