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>
This commit is contained in:
Igor Mammedov
2026-07-02 16:58:50 +02:00
committed by Michael S. Tsirkin
parent beadd272c1
commit 285bc90d5b
3 changed files with 49 additions and 12 deletions

View File

@@ -323,6 +323,21 @@ using ``-device sbsa-gwdt``. It is only supported on the virt machine,
which wires up statically assigned MMIO regions and IRQs via
machine-specific plug handlers.
Two modes are available:
Native mode (default)
The watchdog is described via the ACPI GTDT table and FDT, using
the system counter frequency. Example::
-device sbsa-gwdt
WDAT mode
The watchdog is described via the ACPI WDAT table (no FDT node),
using a 1 kHz timer frequency. WDAT and GTDT watchdog entries are
mutually exclusive. Example::
-device sbsa-gwdt,wdat=on
Linux guest kernel configuration
""""""""""""""""""""""""""""""""

View File

@@ -65,6 +65,7 @@
#include "target/arm/cpu.h"
#include "target/arm/multiprocessing.h"
#include "hw/watchdog/sbsa_gwdt.h"
#include "hw/acpi/wdat-gwdt.h"
#include "smmuv3-accel.h"
#include "tegra241-cmdqv.h"
@@ -859,7 +860,8 @@ build_srat(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms)
* 5.2.25 Generic Timer Description Table (GTDT)
*/
static void
build_gtdt(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms)
build_gtdt(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms,
bool add_watchdog)
{
/*
* Table 5-117 Flag Definitions
@@ -870,7 +872,6 @@ build_gtdt(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms)
AcpiTable table = { .sig = "GTDT", .rev = 3, .oem_id = vms->oem_id,
.oem_table_id = vms->oem_table_id };
uint32_t gtdt_start = table_data->len;
Object *wdt = object_resolve_type_unambiguous(TYPE_WDT_SBSA, NULL);
acpi_table_begin(&table, table_data);
@@ -903,12 +904,12 @@ build_gtdt(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms)
build_append_int_noprefix(table_data, 0xFFFFFFFFFFFFFFFF, 8);
/* Platform Timer Count */
build_append_int_noprefix(table_data, wdt ? 1 : 0, 4);
build_append_int_noprefix(table_data, add_watchdog ? 1 : 0, 4);
/* Platform Timer Offset */
build_append_int_noprefix(table_data,
wdt ? (table_data->len - gtdt_start) +
4 + 4 + 4 /* len of this & following 2 fields to skip */
: 0, 4);
add_watchdog ? (table_data->len - gtdt_start) +
4 + 4 + 4 /* len of this & following 2 fields to skip */
: 0, 4);
if (vms->ns_el2_virt_timer_irq) {
/* Virtual EL2 Timer GSIV */
@@ -921,7 +922,7 @@ build_gtdt(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms)
}
/* ACPI 6.5 spec: 5.2.25.2 ARM Generic Watchdog Structure (Table 5-124) */
if (wdt) {
if (add_watchdog) {
hwaddr rbase = vms->memmap[VIRT_GWDT_REFRESH].base;
hwaddr cbase = vms->memmap[VIRT_GWDT_CONTROL].base;
int irq = ARM_SPI_BASE + vms->irqmap[VIRT_GWDT_WS0];
@@ -1332,13 +1333,19 @@ void virt_acpi_build(VirtMachineState *vms, AcpiBuildTables *tables)
VirtMachineClass *vmc = VIRT_MACHINE_GET_CLASS(vms);
GArray *table_offsets;
unsigned dsdt, xsdt;
bool has_wdat = false;
GArray *tables_blob = tables->table_data;
MachineState *ms = MACHINE(vms);
CPUCoreCaches caches[CPU_MAX_CACHES];
unsigned int num_caches;
Object *wdt = object_resolve_type_unambiguous(TYPE_WDT_SBSA, NULL);
num_caches = virt_get_caches(vms, caches);
if (wdt) {
has_wdat = object_property_get_bool(wdt, "wdat", &error_abort);
}
table_offsets = g_array_new(false, true /* clear */,
sizeof(uint32_t));
@@ -1357,6 +1364,17 @@ void virt_acpi_build(VirtMachineState *vms, AcpiBuildTables *tables)
acpi_add_table(table_offsets, tables_blob);
build_madt(tables_blob, tables->linker, vms);
acpi_add_table(table_offsets, tables_blob);
if (wdt && has_wdat) {
uint64_t freq = object_property_get_uint(wdt, "clock-frequency",
&error_abort);
build_gwdt_wdat(tables_blob, tables->linker,
vms->oem_id, vms->oem_table_id,
vms->memmap[VIRT_GWDT_REFRESH].base,
vms->memmap[VIRT_GWDT_CONTROL].base,
freq);
}
if (!vmc->no_cpu_topology) {
acpi_add_table(table_offsets, tables_blob);
build_pptt(tables_blob, tables->linker, ms, vms->oem_id,
@@ -1364,7 +1382,7 @@ void virt_acpi_build(VirtMachineState *vms, AcpiBuildTables *tables)
}
acpi_add_table(table_offsets, tables_blob);
build_gtdt(tables_blob, tables->linker, vms);
build_gtdt(tables_blob, tables->linker, vms, wdt && !has_wdat);
acpi_add_table(table_offsets, tables_blob);
{

View File

@@ -3846,10 +3846,12 @@ static void virt_machine_device_pre_plug_cb(HotplugHandler *hotplug_dev,
qdev_prop_set_array(dev, "reserved-regions", reserved_regions);
g_free(resv_prop_str);
} else if (object_dynamic_cast(OBJECT(dev), TYPE_WDT_SBSA)) {
uint64_t cntfrq = object_property_get_int(OBJECT(qemu_get_cpu(0)),
"cntfrq", &error_abort);
if (!object_property_get_bool(OBJECT(dev), "wdat", &error_abort)) {
uint64_t cntfrq = object_property_get_int(OBJECT(qemu_get_cpu(0)),
"cntfrq", &error_abort);
qdev_prop_set_uint64(dev, "clock-frequency", cntfrq);
qdev_prop_set_uint64(dev, "clock-frequency", cntfrq);
}
} else if (object_dynamic_cast(OBJECT(dev), TYPE_ARM_SMMUV3)) {
if (vms->legacy_smmuv3_present || vms->iommu == VIRT_IOMMU_VIRTIO) {
error_setg(errp, "virt machine already has %s set. "
@@ -3911,7 +3913,9 @@ static void virt_machine_device_plug_cb(HotplugHandler *hotplug_dev,
sysbus_mmio_map(s, 1, cbase);
sysbus_connect_irq(s, 0, qdev_get_gpio_in(vms->gic, irq));
create_gwdt_dt_bindings(vms);
if (!object_property_get_bool(OBJECT(dev), "wdat", &error_abort)) {
create_gwdt_dt_bindings(vms);
}
}
if (vms->platform_bus_dev) {