From: Igor Mammedov <[email protected]> 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 <[email protected]> Reviewed-by: Eric Auger <[email protected]> Reviewed-by: Michael S. Tsirkin <[email protected]> Signed-off-by: Michael S. Tsirkin <[email protected]> Message-ID: <[email protected]> --- docs/system/arm/virt.rst | 9 ++++++++ include/hw/arm/virt.h | 3 +++ hw/arm/virt-acpi-build.c | 29 ++++++++++++++++++++++++-- hw/arm/virt.c | 44 ++++++++++++++++++++++++++++++++++++++++ hw/core/sysbus-fdt.c | 2 ++ hw/watchdog/sbsa_gwdt.c | 2 ++ hw/arm/Kconfig | 1 + 7 files changed, 88 insertions(+), 2 deletions(-) diff --git a/docs/system/arm/virt.rst b/docs/system/arm/virt.rst index ae0be35d57..bf5a9c8f6c 100644 --- a/docs/system/arm/virt.rst +++ b/docs/system/arm/virt.rst @@ -39,6 +39,7 @@ The virt board supports: - A PL061 GPIO controller - An optional machine-wide SMMUv3 IOMMU - User-creatable SMMUv3 devices (see below for example) +- An optional SBSA Generic Watchdog Timer (see below) - hotpluggable DIMMs - hotpluggable NVDIMMs - An MSI controller (GICv2m or ITS). @@ -314,6 +315,14 @@ User-creatable SMMUv3 devices bypassing QEMU and improving throughput for workloads that issue many invalidations. Without it, every invalidation command traps into QEMU. +SBSA Generic Watchdog +""""""""""""""""""""" + +The SBSA Generic Watchdog Timer (GWDT) can be added to the virt machine +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. + Linux guest kernel configuration """""""""""""""""""""""""""""""" diff --git a/include/hw/arm/virt.h b/include/hw/arm/virt.h index 171d44c644..22e66d1a11 100644 --- a/include/hw/arm/virt.h +++ b/include/hw/arm/virt.h @@ -97,6 +97,9 @@ enum { VIRT_NVDIMM_ACPI, VIRT_PVTIME, VIRT_ACPI_PCIHP, + VIRT_GWDT_WS0, + VIRT_GWDT_REFRESH, + VIRT_GWDT_CONTROL, VIRT_LOWMEMMAP_LAST, }; diff --git a/hw/arm/virt-acpi-build.c b/hw/arm/virt-acpi-build.c index 99490aa7b1..f5b3b4ce48 100644 --- a/hw/arm/virt-acpi-build.c +++ b/hw/arm/virt-acpi-build.c @@ -64,6 +64,7 @@ #include "hw/virtio/virtio-acpi.h" #include "target/arm/cpu.h" #include "target/arm/multiprocessing.h" +#include "hw/watchdog/sbsa_gwdt.h" #include "smmuv3-accel.h" #include "tegra241-cmdqv.h" @@ -868,6 +869,8 @@ build_gtdt(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms) const uint32_t irqflags = 0; /* Interrupt is Level triggered */ 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); @@ -898,10 +901,15 @@ build_gtdt(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms) build_append_int_noprefix(table_data, irqflags, 4); /* CntReadBase Physical address */ build_append_int_noprefix(table_data, 0xFFFFFFFFFFFFFFFF, 8); + /* Platform Timer Count */ - build_append_int_noprefix(table_data, 0, 4); + build_append_int_noprefix(table_data, wdt ? 1 : 0, 4); /* Platform Timer Offset */ - build_append_int_noprefix(table_data, 0, 4); + 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); + if (vms->ns_el2_virt_timer_irq) { /* Virtual EL2 Timer GSIV */ 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) build_append_int_noprefix(table_data, 0, 4); build_append_int_noprefix(table_data, 0, 4); } + + /* ACPI 6.5 spec: 5.2.25.2 ARM Generic Watchdog Structure (Table 5-124) */ + if (wdt) { + 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]; + + build_append_int_noprefix(table_data, 1 /* Type: Watchdog GT */, 1); + build_append_int_noprefix(table_data, 28 /* Length */, 2); + build_append_int_noprefix(table_data, 0, 1); /* Reserved */ + /* RefreshFrame Physical Address */ + build_append_int_noprefix(table_data, rbase, 8); + /* WatchdogControlFrame Physical Address */ + build_append_int_noprefix(table_data, cbase, 8); + build_append_int_noprefix(table_data, irq, 4); /* Watchdog Timer GSIV */ + build_append_int_noprefix(table_data, 0, 4); /* Watchdog Timer Flags */ + } acpi_table_end(linker, &table); } diff --git a/hw/arm/virt.c b/hw/arm/virt.c index d8d27f2ef6..eabc5274d5 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c @@ -95,6 +95,7 @@ #include "hw/cxl/cxl.h" #include "hw/cxl/cxl_host.h" #include "qemu/guest-random.h" +#include "hw/watchdog/sbsa_gwdt.h" static GlobalProperty arm_virt_compat_defaults[] = { { TYPE_VIRTIO_IOMMU_PCI, "aw-bits", "48" }, @@ -214,6 +215,8 @@ static const MemMapEntry base_memmap[] = { /* ...repeating for a total of NUM_VIRTIO_TRANSPORTS, each of that size */ [VIRT_PLATFORM_BUS] = { 0x0c000000, 0x02000000 }, [VIRT_SECURE_MEM] = { 0x0e000000, 0x01000000 }, + [VIRT_GWDT_REFRESH] = { 0x0f000000, 0x00001000 }, + [VIRT_GWDT_CONTROL] = { 0x0f001000, 0x00001000 }, [VIRT_PCIE_MMIO] = { 0x10000000, 0x2eff0000 }, [VIRT_PCIE_PIO] = { 0x3eff0000, 0x00010000 }, [VIRT_PCIE_ECAM] = { 0x3f000000, 0x01000000 }, @@ -264,6 +267,7 @@ static const int a15irqmap[] = { [VIRT_GPIO] = 7, [VIRT_UART1] = 8, [VIRT_ACPI_GED] = 9, + [VIRT_GWDT_WS0] = 10, [VIRT_MMIO] = 16, /* ...to 16 + NUM_VIRTIO_TRANSPORTS - 1 */ [VIRT_GIC_V2M] = 48, /* ...to 48 + NUM_GICV2M_SPIS - 1 */ [VIRT_SMMU] = 74, /* ...to 74 + NUM_SMMU_IRQS - 1 */ @@ -2085,6 +2089,27 @@ static void create_smmu(const VirtMachineState *vms, PCIBus *bus) create_smmuv3_dt_bindings(vms, base, size, irq); } +static void create_gwdt_dt_bindings(VirtMachineState *vms) +{ + MachineState *ms = MACHINE(vms); + hwaddr rbase = vms->memmap[VIRT_GWDT_REFRESH].base; + hwaddr cbase = vms->memmap[VIRT_GWDT_CONTROL].base; + int irq = vms->irqmap[VIRT_GWDT_WS0]; + char *nodename = g_strdup_printf("/watchdog@%" PRIx64, cbase); + + qemu_fdt_add_subnode(ms->fdt, nodename); + qemu_fdt_setprop_string(ms->fdt, nodename, + "compatible", "arm,sbsa-gwdt"); + qemu_fdt_setprop_sized_cells(ms->fdt, nodename, "reg", + 2, cbase, 2, SBSA_GWDT_CMMIO_SIZE, + 2, rbase, 2, SBSA_GWDT_RMMIO_SIZE); + qemu_fdt_setprop_cells(ms->fdt, nodename, "interrupts", + GIC_FDT_IRQ_TYPE_SPI, irq, + GIC_FDT_IRQ_FLAGS_LEVEL_HI); + qemu_fdt_setprop_cell(ms->fdt, nodename, "timeout-sec", 30); + g_free(nodename); +} + static void create_virtio_iommu_dt_bindings(VirtMachineState *vms) { const char compat[] = "virtio,pci-iommu\0pci1af4,1057"; @@ -3820,6 +3845,11 @@ static void virt_machine_device_pre_plug_cb(HotplugHandler *hotplug_dev, qlist_append_str(reserved_regions, resv_prop_str); 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); + + 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. " @@ -3871,6 +3901,19 @@ static void virt_machine_device_plug_cb(HotplugHandler *hotplug_dev, { VirtMachineState *vms = VIRT_MACHINE(hotplug_dev); + if (object_dynamic_cast(OBJECT(dev), TYPE_WDT_SBSA)) { + SysBusDevice *s = SYS_BUS_DEVICE(dev); + hwaddr rbase = vms->memmap[VIRT_GWDT_REFRESH].base; + hwaddr cbase = vms->memmap[VIRT_GWDT_CONTROL].base; + int irq = vms->irqmap[VIRT_GWDT_WS0]; + + sysbus_mmio_map(s, 0, rbase); + sysbus_mmio_map(s, 1, cbase); + sysbus_connect_irq(s, 0, qdev_get_gpio_in(vms->gic, irq)); + + create_gwdt_dt_bindings(vms); + } + if (vms->platform_bus_dev) { MachineClass *mc = MACHINE_GET_CLASS(vms); @@ -4123,6 +4166,7 @@ static void virt_machine_class_init(ObjectClass *oc, const void *data) machine_class_allow_dynamic_sysbus_dev(mc, TYPE_RAMFB_DEVICE); machine_class_allow_dynamic_sysbus_dev(mc, TYPE_UEFI_VARS_SYSBUS); machine_class_allow_dynamic_sysbus_dev(mc, TYPE_ARM_SMMUV3); + machine_class_allow_dynamic_sysbus_dev(mc, TYPE_WDT_SBSA); #ifdef CONFIG_TPM machine_class_allow_dynamic_sysbus_dev(mc, TYPE_TPM_TIS_SYSBUS); #endif diff --git a/hw/core/sysbus-fdt.c b/hw/core/sysbus-fdt.c index 89d0c46445..12238570b5 100644 --- a/hw/core/sysbus-fdt.c +++ b/hw/core/sysbus-fdt.c @@ -36,6 +36,7 @@ #include "hw/display/ramfb.h" #include "hw/uefi/var-service-api.h" #include "hw/arm/fdt.h" +#include "hw/watchdog/sbsa_gwdt.h" /* * internal struct that contains the information to create dynamic @@ -140,6 +141,7 @@ static const BindingEntry bindings[] = { TYPE_BINDING(TYPE_ARM_SMMUV3, no_fdt_node), TYPE_BINDING(TYPE_RAMFB_DEVICE, no_fdt_node), TYPE_BINDING(TYPE_UEFI_VARS_SYSBUS, add_uefi_vars_node), + TYPE_BINDING(TYPE_WDT_SBSA, no_fdt_node), TYPE_BINDING("", NULL), /* last element */ }; diff --git a/hw/watchdog/sbsa_gwdt.c b/hw/watchdog/sbsa_gwdt.c index acb970e8b3..49fdd723c2 100644 --- a/hw/watchdog/sbsa_gwdt.c +++ b/hw/watchdog/sbsa_gwdt.c @@ -285,6 +285,8 @@ static void wdt_sbsa_gwdt_class_init(ObjectClass *klass, const void *data) dc->realize = wdt_sbsa_gwdt_realize; device_class_set_legacy_reset(dc, wdt_sbsa_gwdt_reset); dc->hotpluggable = false; + /* requires machine-specific wiring (MMIO/IRQ/FDT) at plug time */ + dc->user_creatable = true; set_bit(DEVICE_CATEGORY_WATCHDOG, dc->categories); dc->vmsd = &vmstate_sbsa_gwdt; dc->desc = "SBSA-compliant generic watchdog device"; diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig index 5f5c4899ad..f9a225c19b 100644 --- a/hw/arm/Kconfig +++ b/hw/arm/Kconfig @@ -36,6 +36,7 @@ config ARM_VIRT select VIRTIO_MEM_SUPPORTED select ACPI_CXL select ACPI_HMAT + select WDT_SBSA config CUBIEBOARD bool -- MST
