* [PULL 01/44] amd_iommu: Fix opcode reported in invalid command handling
2026-07-05 14:36 [PULL 00/44] pci, vhost, virtio, iommu: features, fixes, cleanups Michael S. Tsirkin
@ 2026-07-05 14:34 ` Michael S. Tsirkin
2026-07-05 14:34 ` [PULL 02/44] amd_iommu: Return int from page walk status helpers Michael S. Tsirkin
` (43 subsequent siblings)
44 siblings, 0 replies; 46+ messages in thread
From: Michael S. Tsirkin @ 2026-07-05 14:34 UTC (permalink / raw)
To: qemu-devel
Cc: Peter Maydell, David Hoppenbrouwers, Sairaj Kodilkar,
Igor Mammedov, Alejandro Jimenez, Paolo Bonzini,
Richard Henderson
From: David Hoppenbrouwers <qemu@demindiro.com>
According to the AMD I/O Virtualization Technology (IOMMU) Specification
(Rev 3.10), Section 2.4 Commands, the Generic Command Buffer Entry Format
encodes the opcode in bits [63:60] of the command buffer.
When handling illegal opcodes, the traces for unhandled commands and event
log info extract the opcode from an incorrect offset in the command buffer.
Fix this issue to avoid potential confusion with mismatched opcodes in
traces and unlikely errors in guest event processing.
Fixes: d29a09ca68428 ("hw/i386: Introduce AMD IOMMU")
Signed-off-by: David Hoppenbrouwers <qemu@demindiro.com>
Reviewed-by: Sairaj Kodilkar <sarunkod@amd.com>
Acked-by: Igor Mammedov <imammedo@redhat.com>
Signed-off-by: Alejandro Jimenez <alejandro.j.jimenez@oracle.com>
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
Message-ID: <20260630220806.1758748-2-alejandro.j.jimenez@oracle.com>
---
hw/i386/amd_iommu.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/hw/i386/amd_iommu.c b/hw/i386/amd_iommu.c
index 79216fb305..05b7c638f4 100644
--- a/hw/i386/amd_iommu.c
+++ b/hw/i386/amd_iommu.c
@@ -1509,9 +1509,9 @@ static void amdvi_cmdbuf_exec(AMDVIState *s)
amdvi_inval_all(s, cmd);
break;
default:
- trace_amdvi_unhandled_command(extract64(cmd[1], 60, 4));
+ trace_amdvi_unhandled_command(extract64(cmd[0], 60, 4));
/* log illegal command */
- amdvi_log_illegalcom_error(s, extract64(cmd[1], 60, 4),
+ amdvi_log_illegalcom_error(s, extract64(cmd[0], 60, 4),
s->cmdbuf + s->cmdbuf_head);
}
}
--
MST
^ permalink raw reply related [flat|nested] 46+ messages in thread
* [PULL 02/44] amd_iommu: Return int from page walk status helpers
2026-07-05 14:36 [PULL 00/44] pci, vhost, virtio, iommu: features, fixes, cleanups Michael S. Tsirkin
2026-07-05 14:34 ` [PULL 01/44] amd_iommu: Fix opcode reported in invalid command handling Michael S. Tsirkin
@ 2026-07-05 14:34 ` Michael S. Tsirkin
2026-07-05 14:34 ` [PULL 03/44] amd_iommu: Decode XT interrupt control register without bitfields Michael S. Tsirkin
` (42 subsequent siblings)
44 siblings, 0 replies; 46+ messages in thread
From: Michael S. Tsirkin @ 2026-07-05 14:34 UTC (permalink / raw)
To: qemu-devel
Cc: Peter Maydell, Alejandro Jimenez, Philippe Mathieu-Daudé,
Sairaj Kodilkar, Paolo Bonzini, Richard Henderson
From: Alejandro Jimenez <alejandro.j.jimenez@oracle.com>
fetch_pte() returns a status code 0 on success, and (small) negative values
on failure. The PTE value itself is returned via an output parameter.
amdvi_get_top_pt_level_and_perms() follows the same return convention.
Both functions currently return uint64_t, which means any negative error
values are returned as unsigned and then converted back to int by the
callers. This does not cause any issues in the current implementation, but
Coverity flags the type mismatch and potential overflow.
Make both helpers return int, so the type matches what the return variable
is (0 on success, small negative value on failure), and also the type used
by all callers to store their return values.
No functional changes are intended.
Fixes: a1c97c395729 ("amd_iommu: Sync shadow page tables on page invalidation")
Fixes: 786550e2d38a ("amd_iommu: Follow root pointer before page walk and use 1-based levels")
Reported-by: Peter Maydell <peter.maydell@linaro.org>
Suggested-by: Peter Maydell <peter.maydell@linaro.org>
Signed-off-by: Alejandro Jimenez <alejandro.j.jimenez@oracle.com>
Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
Reviewed-by: Philippe Mathieu-Daudé <philmd@oss.qualcomm.com>
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
Message-ID: <20260630220806.1758748-3-alejandro.j.jimenez@oracle.com>
---
hw/i386/amd_iommu.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/hw/i386/amd_iommu.c b/hw/i386/amd_iommu.c
index 05b7c638f4..a0835a20d7 100644
--- a/hw/i386/amd_iommu.c
+++ b/hw/i386/amd_iommu.c
@@ -659,7 +659,7 @@ static uint64_t large_pte_page_size(uint64_t pte)
* - IOVA exceeds the address width supported by DTE[Mode]
* In all such cases a page walk must be aborted.
*/
-static uint64_t amdvi_get_top_pt_level_and_perms(hwaddr address, uint64_t dte,
+static int amdvi_get_top_pt_level_and_perms(hwaddr address, uint64_t dte,
uint8_t *top_level,
IOMMUAccessFlags *dte_perms)
{
@@ -702,7 +702,7 @@ static uint64_t amdvi_get_top_pt_level_and_perms(hwaddr address, uint64_t dte,
* page table walk. This means that the DTE has valid data, but one of the
* lower level entries in the Page Table could not be read.
*/
-static uint64_t fetch_pte(AMDVIAddressSpace *as, hwaddr address, uint64_t dte,
+static int fetch_pte(AMDVIAddressSpace *as, hwaddr address, uint64_t dte,
uint64_t *pte, hwaddr *page_size)
{
uint64_t pte_addr;
--
MST
^ permalink raw reply related [flat|nested] 46+ messages in thread
* [PULL 03/44] amd_iommu: Decode XT interrupt control register without bitfields
2026-07-05 14:36 [PULL 00/44] pci, vhost, virtio, iommu: features, fixes, cleanups Michael S. Tsirkin
2026-07-05 14:34 ` [PULL 01/44] amd_iommu: Fix opcode reported in invalid command handling Michael S. Tsirkin
2026-07-05 14:34 ` [PULL 02/44] amd_iommu: Return int from page walk status helpers Michael S. Tsirkin
@ 2026-07-05 14:34 ` Michael S. Tsirkin
2026-07-05 14:34 ` [PULL 04/44] amd_iommu: Decode IRTEs " Michael S. Tsirkin
` (41 subsequent siblings)
44 siblings, 0 replies; 46+ messages in thread
From: Michael S. Tsirkin @ 2026-07-05 14:34 UTC (permalink / raw)
To: qemu-devel
Cc: Peter Maydell, Alejandro Jimenez, Philippe Mathieu-Daudé,
Sairaj Kodilkar, Paolo Bonzini, Richard Henderson
From: Alejandro Jimenez <alejandro.j.jimenez@oracle.com>
The XT IOMMU General Interrupt Control Register is a guest-visible MMIO
register. Decoding it with bitfields depends on host bitfield layout and is
not portable to big-endian hosts.
Fix this by removing union mmio_xt_intr and explicitly extracting fields
with FIELD_EX64() from the full register value returned by amdvi_readq(),
which has already been converted to host endianness.
Using a designated initializer for X86IOMMUIrq also ensures fields not
provided by the XT register (e.g. msi_addr_last_bits) are initialized before
x86_iommu_irq_to_msi_message() uses them.
CID: 1660056
Fixes: cf0210df65aa ("amd_iommu: Generate XT interrupts when xt support is enabled")
Reported-by: Peter Maydell <peter.maydell@linaro.org>
Suggested-by: Peter Maydell <peter.maydell@linaro.org>
Signed-off-by: Alejandro Jimenez <alejandro.j.jimenez@oracle.com>
Reviewed-by: Philippe Mathieu-Daudé <philmd@oss.qualcomm.com>
Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
Message-ID: <20260630220806.1758748-4-alejandro.j.jimenez@oracle.com>
---
hw/i386/amd_iommu.h | 14 --------------
hw/i386/amd_iommu.c | 28 ++++++++++++++++++----------
2 files changed, 18 insertions(+), 24 deletions(-)
diff --git a/hw/i386/amd_iommu.h b/hw/i386/amd_iommu.h
index 3cab04a6d4..ca4440a4c1 100644
--- a/hw/i386/amd_iommu.h
+++ b/hw/i386/amd_iommu.h
@@ -340,20 +340,6 @@ struct irte_ga {
union irte_ga_hi hi;
};
-union mmio_xt_intr {
- uint64_t val;
- struct {
- uint64_t rsvd_1:2,
- destination_mode:1,
- rsvd_2:5,
- destination_lo:24,
- vector:8,
- delivery_mode:1,
- rsvd_3:15,
- destination_hi:8;
- };
-};
-
#define TYPE_AMD_IOMMU_DEVICE "amd-iommu"
OBJECT_DECLARE_SIMPLE_TYPE(AMDVIState, AMD_IOMMU_DEVICE)
diff --git a/hw/i386/amd_iommu.c b/hw/i386/amd_iommu.c
index a0835a20d7..291cb368a9 100644
--- a/hw/i386/amd_iommu.c
+++ b/hw/i386/amd_iommu.c
@@ -34,6 +34,7 @@
#include "hw/core/qdev-properties.h"
#include "kvm/kvm_i386.h"
#include "qemu/iova-tree.h"
+#include "hw/core/registerfields.h"
struct AMDVIAddressSpace {
PCIBus *bus; /* PCIBus (for bus number) */
@@ -88,6 +89,13 @@ typedef struct AMDVIIOTLBKey {
uint16_t devid;
} AMDVIIOTLBKey;
+/* XT IOMMU General Interrupt Control Register layout */
+FIELD(AMDVI_XT_GEN_INTR, DEST_MODE, 2, 1)
+FIELD(AMDVI_XT_GEN_INTR, DEST_LO, 8, 24)
+FIELD(AMDVI_XT_GEN_INTR, VECTOR, 32, 8)
+FIELD(AMDVI_XT_GEN_INTR, DELIVERY_MODE, 40, 1)
+FIELD(AMDVI_XT_GEN_INTR, DEST_HI, 56, 8)
+
uint64_t amdvi_extended_feature_register(AMDVIState *s)
{
uint64_t feature = AMDVI_DEFAULT_EXT_FEATURES;
@@ -194,17 +202,17 @@ static void amdvi_assign_andq(AMDVIState *s, hwaddr addr, uint64_t val)
static void amdvi_build_xt_msi_msg(AMDVIState *s, MSIMessage *msg)
{
- union mmio_xt_intr xt_reg;
- struct X86IOMMUIrq irq;
+ uint64_t xt_reg = amdvi_readq(s, AMDVI_MMIO_XT_GEN_INTR);
- xt_reg.val = amdvi_readq(s, AMDVI_MMIO_XT_GEN_INTR);
-
- irq.vector = xt_reg.vector;
- irq.delivery_mode = xt_reg.delivery_mode;
- irq.dest_mode = xt_reg.destination_mode;
- irq.dest = (xt_reg.destination_hi << 24) | xt_reg.destination_lo;
- irq.trigger_mode = 0;
- irq.redir_hint = 0;
+ X86IOMMUIrq irq = {
+ .vector = FIELD_EX64(xt_reg, AMDVI_XT_GEN_INTR, VECTOR),
+ .delivery_mode = FIELD_EX64(xt_reg, AMDVI_XT_GEN_INTR, DELIVERY_MODE),
+ .dest_mode = FIELD_EX64(xt_reg, AMDVI_XT_GEN_INTR, DEST_MODE),
+ .dest = (FIELD_EX64(xt_reg, AMDVI_XT_GEN_INTR, DEST_HI) << 24) |
+ FIELD_EX64(xt_reg, AMDVI_XT_GEN_INTR, DEST_LO),
+ .trigger_mode = 0,
+ .redir_hint = 0,
+ };
x86_iommu_irq_to_msi_message(&irq, msg);
}
--
MST
^ permalink raw reply related [flat|nested] 46+ messages in thread
* [PULL 04/44] amd_iommu: Decode IRTEs without bitfields
2026-07-05 14:36 [PULL 00/44] pci, vhost, virtio, iommu: features, fixes, cleanups Michael S. Tsirkin
` (2 preceding siblings ...)
2026-07-05 14:34 ` [PULL 03/44] amd_iommu: Decode XT interrupt control register without bitfields Michael S. Tsirkin
@ 2026-07-05 14:34 ` Michael S. Tsirkin
2026-07-05 14:34 ` [PULL 05/44] amd_iommu: Fix endianness handling for command buffer entries Michael S. Tsirkin
` (40 subsequent siblings)
44 siblings, 0 replies; 46+ messages in thread
From: Michael S. Tsirkin @ 2026-07-05 14:34 UTC (permalink / raw)
To: qemu-devel
Cc: Peter Maydell, Alejandro Jimenez, Philippe Mathieu-Daudé,
Sairaj Kodilkar, Paolo Bonzini, Richard Henderson
From: Alejandro Jimenez <alejandro.j.jimenez@oracle.com>
Interrupt remapping table entries are data stored in guest memory in
little-endian format. Decoding them with bitfields depends on host bitfield
layout and the value returned from dma_memory_read() is not portable to
big-endian hosts.
Replace the legacy and GA IRTE bitfield definitions with explicit FIELD()
definitions. Convert the guest memory values returned from dma_memory_read()
with le32_to_cpu() or le64_to_cpu(), then extract relevant fields using
FIELD_EX32() or FIELD_EX64() as appropriate to match the IRTE format.
Fixes: b44159fe0078 ("x86_iommu/amd: Add interrupt remap support when VAPIC is not enabled")
Fixes: 135f866e609c ("x86_iommu/amd: Add interrupt remap support when VAPIC is enabled")
Reported-by: Peter Maydell <peter.maydell@linaro.org>
Suggested-by: Peter Maydell <peter.maydell@linaro.org>
Signed-off-by: Alejandro Jimenez <alejandro.j.jimenez@oracle.com>
Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
Reviewed-by: Philippe Mathieu-Daudé <philmd@oss.qualcomm.com>
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
Message-ID: <20260630220806.1758748-5-alejandro.j.jimenez@oracle.com>
---
hw/i386/amd_iommu.h | 49 ------------------------
hw/i386/amd_iommu.c | 93 +++++++++++++++++++++++++++++++++------------
2 files changed, 69 insertions(+), 73 deletions(-)
diff --git a/hw/i386/amd_iommu.h b/hw/i386/amd_iommu.h
index ca4440a4c1..687691ec1c 100644
--- a/hw/i386/amd_iommu.h
+++ b/hw/i386/amd_iommu.h
@@ -291,55 +291,6 @@
#define AMDVI_DEV_LINT0_PASS_MASK (1ULL << 62)
#define AMDVI_DEV_LINT1_PASS_MASK (1ULL << 63)
-/* Interrupt remapping table fields (Guest VAPIC not enabled) */
-union irte {
- uint32_t val;
- struct {
- uint32_t valid:1,
- no_fault:1,
- int_type:3,
- rq_eoi:1,
- dm:1,
- guest_mode:1,
- destination:8,
- vector:8,
- rsvd:8;
- } fields;
-};
-
-/* Interrupt remapping table fields (Guest VAPIC is enabled) */
-union irte_ga_lo {
- uint64_t val;
-
- /* For int remapping */
- struct {
- uint64_t valid:1,
- no_fault:1,
- /* ------ */
- int_type:3,
- rq_eoi:1,
- dm:1,
- /* ------ */
- guest_mode:1,
- destination:24,
- rsvd_1:32;
- } fields_remap;
-};
-
-union irte_ga_hi {
- uint64_t val;
- struct {
- uint64_t vector:8,
- rsvd_2:48,
- destination_hi:8;
- } fields;
-};
-
-struct irte_ga {
- union irte_ga_lo lo;
- union irte_ga_hi hi;
-};
-
#define TYPE_AMD_IOMMU_DEVICE "amd-iommu"
OBJECT_DECLARE_SIMPLE_TYPE(AMDVIState, AMD_IOMMU_DEVICE)
diff --git a/hw/i386/amd_iommu.c b/hw/i386/amd_iommu.c
index 291cb368a9..c7bf21b762 100644
--- a/hw/i386/amd_iommu.c
+++ b/hw/i386/amd_iommu.c
@@ -89,6 +89,11 @@ typedef struct AMDVIIOTLBKey {
uint16_t devid;
} AMDVIIOTLBKey;
+typedef struct AMDVIIrteGA {
+ uint64_t ga_lo;
+ uint64_t ga_hi;
+} AMDVIIrteGA;
+
/* XT IOMMU General Interrupt Control Register layout */
FIELD(AMDVI_XT_GEN_INTR, DEST_MODE, 2, 1)
FIELD(AMDVI_XT_GEN_INTR, DEST_LO, 8, 24)
@@ -96,6 +101,37 @@ FIELD(AMDVI_XT_GEN_INTR, VECTOR, 32, 8)
FIELD(AMDVI_XT_GEN_INTR, DELIVERY_MODE, 40, 1)
FIELD(AMDVI_XT_GEN_INTR, DEST_HI, 56, 8)
+/* Interrupt Remapping Table Fields Formats */
+
+/* Basic 32-bit IRTE layout (GAEn=0) */
+FIELD(AMDVI_IRTE, VALID, 0, 1)
+FIELD(AMDVI_IRTE, SUP_IOPF, 1, 1)
+FIELD(AMDVI_IRTE, INT_TYPE, 2, 3)
+FIELD(AMDVI_IRTE, RQ_EOI, 5, 1)
+FIELD(AMDVI_IRTE, DM, 6, 1)
+FIELD(AMDVI_IRTE, GUEST_MODE, 7, 1)
+FIELD(AMDVI_IRTE, DESTINATION, 8, 8)
+FIELD(AMDVI_IRTE, VECTOR, 16, 8)
+
+/* 128-bit IRTE layout (GAEn=1) */
+FIELD(AMDVI_IRTE_GA_LO, VALID, 0, 1)
+FIELD(AMDVI_IRTE_GA_LO, SUP_IOPF, 1, 1)
+FIELD(AMDVI_IRTE_GA_LO, INT_TYPE, 2, 3)
+FIELD(AMDVI_IRTE_GA_LO, RQ_EOI, 5, 1)
+FIELD(AMDVI_IRTE_GA_LO, DM, 6, 1)
+FIELD(AMDVI_IRTE_GA_LO, GUEST_MODE, 7, 1)
+/*
+ * In the 128-bit IRTE format, XT mode uses IRTE_GA_LOW.Destination[23:0]
+ * together with IRTE_GA_HI.DestinationHi[7:0] to construct a 32-bit x2APIC
+ * destination.
+ * Without XTEn (i.e. when x2APIC support is not enabled), only
+ * IRTE_GA_LOW.Destination[7:0] is used.
+ */
+FIELD(AMDVI_IRTE_GA_LO, DESTINATION, 8, 24)
+
+FIELD(AMDVI_IRTE_GA_HI, VECTOR, 0, 8)
+FIELD(AMDVI_IRTE_GA_HI, DESTINATION_HI, 56, 8)
+
uint64_t amdvi_extended_feature_register(AMDVIState *s)
{
uint64_t feature = AMDVI_DEFAULT_EXT_FEATURES;
@@ -1983,7 +2019,7 @@ static IOMMUTLBEntry amdvi_translate(IOMMUMemoryRegion *iommu, hwaddr addr,
}
static int amdvi_get_irte(AMDVIState *s, MSIMessage *origin, uint64_t *dte,
- union irte *irte, uint16_t devid)
+ uint32_t *irte, uint16_t devid)
{
uint64_t irte_root, offset;
@@ -1998,7 +2034,8 @@ static int amdvi_get_irte(AMDVIState *s, MSIMessage *origin, uint64_t *dte,
return -AMDVI_IR_GET_IRTE;
}
- trace_amdvi_ir_irte_val(irte->val);
+ *irte = le32_to_cpu(*irte);
+ trace_amdvi_ir_irte_val(*irte);
return 0;
}
@@ -2010,8 +2047,9 @@ static int amdvi_int_remap_legacy(AMDVIState *iommu,
X86IOMMUIrq *irq,
uint16_t sid)
{
+ uint8_t int_type;
+ uint32_t irte;
int ret;
- union irte irte;
/* get interrupt remapping table */
ret = amdvi_get_irte(iommu, origin, dte, &irte, sid);
@@ -2019,32 +2057,33 @@ static int amdvi_int_remap_legacy(AMDVIState *iommu,
return ret;
}
- if (!irte.fields.valid) {
+ if (!FIELD_EX32(irte, AMDVI_IRTE, VALID)) {
trace_amdvi_ir_target_abort("RemapEn is disabled");
return -AMDVI_IR_TARGET_ABORT;
}
- if (irte.fields.guest_mode) {
+ if (FIELD_EX32(irte, AMDVI_IRTE, GUEST_MODE)) {
error_report_once("guest mode is not zero");
return -AMDVI_IR_ERR;
}
- if (irte.fields.int_type > AMDVI_IOAPIC_INT_TYPE_ARBITRATED) {
+ int_type = FIELD_EX32(irte, AMDVI_IRTE, INT_TYPE);
+ if (int_type > AMDVI_IOAPIC_INT_TYPE_ARBITRATED) {
error_report_once("reserved int_type");
return -AMDVI_IR_ERR;
}
- irq->delivery_mode = irte.fields.int_type;
- irq->vector = irte.fields.vector;
- irq->dest_mode = irte.fields.dm;
- irq->redir_hint = irte.fields.rq_eoi;
- irq->dest = irte.fields.destination;
+ irq->delivery_mode = int_type;
+ irq->vector = FIELD_EX32(irte, AMDVI_IRTE, VECTOR);
+ irq->dest_mode = FIELD_EX32(irte, AMDVI_IRTE, DM);
+ irq->redir_hint = FIELD_EX32(irte, AMDVI_IRTE, RQ_EOI);
+ irq->dest = FIELD_EX32(irte, AMDVI_IRTE, DESTINATION);
return 0;
}
static int amdvi_get_irte_ga(AMDVIState *s, MSIMessage *origin, uint64_t *dte,
- struct irte_ga *irte, uint16_t devid)
+ AMDVIIrteGA *irte, uint16_t devid)
{
uint64_t irte_root, offset;
@@ -2058,7 +2097,9 @@ static int amdvi_get_irte_ga(AMDVIState *s, MSIMessage *origin, uint64_t *dte,
return -AMDVI_IR_GET_IRTE;
}
- trace_amdvi_ir_irte_ga_val(irte->hi.val, irte->lo.val);
+ irte->ga_lo = le64_to_cpu(irte->ga_lo);
+ irte->ga_hi = le64_to_cpu(irte->ga_hi);
+ trace_amdvi_ir_irte_ga_val(irte->ga_hi, irte->ga_lo);
return 0;
}
@@ -2069,8 +2110,9 @@ static int amdvi_int_remap_ga(AMDVIState *iommu,
X86IOMMUIrq *irq,
uint16_t sid)
{
+ AMDVIIrteGA irte;
+ uint8_t int_type;
int ret;
- struct irte_ga irte;
/* get interrupt remapping table */
ret = amdvi_get_irte_ga(iommu, origin, dte, &irte, sid);
@@ -2078,30 +2120,33 @@ static int amdvi_int_remap_ga(AMDVIState *iommu,
return ret;
}
- if (!irte.lo.fields_remap.valid) {
+ if (!FIELD_EX64(irte.ga_lo, AMDVI_IRTE_GA_LO, VALID)) {
trace_amdvi_ir_target_abort("RemapEn is disabled");
return -AMDVI_IR_TARGET_ABORT;
}
- if (irte.lo.fields_remap.guest_mode) {
+ if (FIELD_EX64(irte.ga_lo, AMDVI_IRTE_GA_LO, GUEST_MODE)) {
error_report_once("guest mode is not zero");
return -AMDVI_IR_ERR;
}
- if (irte.lo.fields_remap.int_type > AMDVI_IOAPIC_INT_TYPE_ARBITRATED) {
+ int_type = FIELD_EX64(irte.ga_lo, AMDVI_IRTE_GA_LO, INT_TYPE);
+ if (int_type > AMDVI_IOAPIC_INT_TYPE_ARBITRATED) {
error_report_once("reserved int_type is set");
return -AMDVI_IR_ERR;
}
- irq->delivery_mode = irte.lo.fields_remap.int_type;
- irq->vector = irte.hi.fields.vector;
- irq->dest_mode = irte.lo.fields_remap.dm;
- irq->redir_hint = irte.lo.fields_remap.rq_eoi;
+ irq->delivery_mode = int_type;
+ irq->vector = FIELD_EX64(irte.ga_hi, AMDVI_IRTE_GA_HI, VECTOR);
+ irq->dest_mode = FIELD_EX64(irte.ga_lo, AMDVI_IRTE_GA_LO, DM);
+ irq->redir_hint = FIELD_EX64(irte.ga_lo, AMDVI_IRTE_GA_LO, RQ_EOI);
if (iommu->xten) {
- irq->dest = irte.lo.fields_remap.destination |
- (irte.hi.fields.destination_hi << 24);
+ irq->dest = FIELD_EX64(irte.ga_lo, AMDVI_IRTE_GA_LO, DESTINATION) |
+ (FIELD_EX64(irte.ga_hi, AMDVI_IRTE_GA_HI, DESTINATION_HI)
+ << 24);
} else {
- irq->dest = irte.lo.fields_remap.destination & 0xff;
+ irq->dest = FIELD_EX64(irte.ga_lo, AMDVI_IRTE_GA_LO, DESTINATION) &
+ 0xff;
}
return 0;
--
MST
^ permalink raw reply related [flat|nested] 46+ messages in thread
* [PULL 05/44] amd_iommu: Fix endianness handling for command buffer entries
2026-07-05 14:36 [PULL 00/44] pci, vhost, virtio, iommu: features, fixes, cleanups Michael S. Tsirkin
` (3 preceding siblings ...)
2026-07-05 14:34 ` [PULL 04/44] amd_iommu: Decode IRTEs " Michael S. Tsirkin
@ 2026-07-05 14:34 ` Michael S. Tsirkin
2026-07-05 14:34 ` [PULL 06/44] hw/mem: add sp-mem device for Specific Purpose Memory Michael S. Tsirkin
` (39 subsequent siblings)
44 siblings, 0 replies; 46+ messages in thread
From: Michael S. Tsirkin @ 2026-07-05 14:34 UTC (permalink / raw)
To: qemu-devel
Cc: Peter Maydell, Alejandro Jimenez, Philippe Mathieu-Daudé,
Sairaj Kodilkar, Paolo Bonzini, Richard Henderson
From: Alejandro Jimenez <alejandro.j.jimenez@oracle.com>
AMD IOMMU command buffer entries are stored in guest memory in little-endian
format. Convert command buffer with le64_to_cpu() after dma_memory_read(),
so that command handlers can all operate using host native endianness.
Remove the cpu_to_le*() conversions from command handlers, since the values
are used internally by device emulation and do not need translation.
Conversion is only necessary when reading or writing to guest memory e.g.
writing completion-wait data and event log entries.
The flow for command buffer handling is:
- Retrieve command buffer (cmd[]) from guest memory (via dma_memory_read())
- Convert command buffer to host endianness (via le64_to_cpu())
- All handlers decode fields from cmd[] in host-endian format
- All emulation code uses decoded values in host-endian format
- Use cpu_to_le*() when writing back data to guest memory
Fixes: d29a09ca6842 ("hw/i386: Introduce AMD IOMMU")
Signed-off-by: Alejandro Jimenez <alejandro.j.jimenez@oracle.com>
Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
Reviewed-by: Philippe Mathieu-Daudé <philmd@oss.qualcomm.com>
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
Message-ID: <20260630220806.1758748-6-alejandro.j.jimenez@oracle.com>
---
hw/i386/amd_iommu.c | 39 +++++++++++++++++++++++++++++----------
1 file changed, 29 insertions(+), 10 deletions(-)
diff --git a/hw/i386/amd_iommu.c b/hw/i386/amd_iommu.c
index c7bf21b762..90252c52af 100644
--- a/hw/i386/amd_iommu.c
+++ b/hw/i386/amd_iommu.c
@@ -278,6 +278,7 @@ static uint32_t get_next_eventlog_entry(AMDVIState *s)
static void amdvi_log_event(AMDVIState *s, uint64_t *evt)
{
+ uint64_t le_evt[2];
uint32_t evtlog_tail_next;
/* event logging not enabled */
@@ -298,8 +299,14 @@ static void amdvi_log_event(AMDVIState *s, uint64_t *evt)
return;
}
+ /*
+ * Convert event buffer to little-endian before writing it to guest memory.
+ */
+ le_evt[0] = cpu_to_le64(evt[0]);
+ le_evt[1] = cpu_to_le64(evt[1]);
+
if (dma_memory_write(&address_space_memory, s->evtlog + s->evtlog_tail,
- evt, AMDVI_EVENT_LEN, MEMTXATTRS_UNSPECIFIED)) {
+ le_evt, AMDVI_EVENT_LEN, MEMTXATTRS_UNSPECIFIED)) {
trace_amdvi_evntlog_fail(s->evtlog, s->evtlog_tail);
}
@@ -545,15 +552,18 @@ static void amdvi_update_iotlb(AMDVIState *s, uint16_t devid,
static void amdvi_completion_wait(AMDVIState *s, uint64_t *cmd)
{
/* pad the last 3 bits */
- hwaddr addr = cpu_to_le64(extract64(cmd[0], 3, 49)) << 3;
- uint64_t data = cpu_to_le64(cmd[1]);
+ hwaddr addr = extract64(cmd[0], 3, 49) << 3;
+ uint64_t data = cmd[1];
+
+ /* Format the data to be written to guest memory as little-endian */
+ uint64_t le_data = cpu_to_le64(data);
if (extract64(cmd[0], 52, 8)) {
amdvi_log_illegalcom_error(s, extract64(cmd[0], 60, 4),
s->cmdbuf + s->cmdbuf_head);
}
if (extract64(cmd[0], 0, 1)) {
- if (dma_memory_write(&address_space_memory, addr, &data,
+ if (dma_memory_write(&address_space_memory, addr, &le_data,
AMDVI_COMPLETION_DATA_SIZE,
MEMTXATTRS_UNSPECIFIED)) {
trace_amdvi_completion_wait_fail(addr);
@@ -1281,7 +1291,7 @@ static void amdvi_update_addr_translation_mode(AMDVIState *s, uint16_t devid)
/* log error without aborting since linux seems to be using reserved bits */
static void amdvi_inval_devtab_entry(AMDVIState *s, uint64_t *cmd)
{
- uint16_t devid = cpu_to_le16((uint16_t)extract64(cmd[0], 0, 16));
+ uint16_t devid = extract64(cmd[0], 0, 16);
trace_amdvi_devtab_inval(PCI_BUS_NUM(devid), PCI_SLOT(devid),
PCI_FUNC(devid));
@@ -1448,9 +1458,9 @@ static void amdvi_sync_domain(AMDVIState *s, uint16_t domid, uint64_t addr,
/* we don't have devid - we can't remove pages by address */
static void amdvi_inval_pages(AMDVIState *s, uint64_t *cmd)
{
- uint16_t domid = cpu_to_le16((uint16_t)extract64(cmd[0], 32, 16));
- uint64_t addr = cpu_to_le64(extract64(cmd[1], 12, 52)) << 12;
- uint16_t flags = cpu_to_le16((uint16_t)extract64(cmd[1], 0, 3));
+ uint16_t domid = extract64(cmd[0], 32, 16);
+ uint64_t addr = extract64(cmd[1], 12, 52) << 12;
+ uint16_t flags = extract64(cmd[1], 0, 3);
if (extract64(cmd[0], 20, 12) || extract64(cmd[0], 48, 12) ||
extract64(cmd[1], 3, 9)) {
@@ -1497,7 +1507,7 @@ static void amdvi_inval_inttable(AMDVIState *s, uint64_t *cmd)
static void iommu_inval_iotlb(AMDVIState *s, uint64_t *cmd)
{
- uint16_t devid = cpu_to_le16(extract64(cmd[0], 0, 16));
+ uint16_t devid = extract64(cmd[0], 0, 16);
if (extract64(cmd[1], 1, 1) || extract64(cmd[1], 3, 1) ||
extract64(cmd[1], 6, 6)) {
amdvi_log_illegalcom_error(s, extract64(cmd[0], 60, 4),
@@ -1509,7 +1519,7 @@ static void iommu_inval_iotlb(AMDVIState *s, uint64_t *cmd)
g_hash_table_foreach_remove(s->iotlb, amdvi_iotlb_remove_by_devid,
&devid);
} else {
- amdvi_iotlb_remove_page(s, cpu_to_le64(extract64(cmd[1], 12, 52)) << 12,
+ amdvi_iotlb_remove_page(s, extract64(cmd[1], 12, 52) << 12,
devid);
}
trace_amdvi_iotlb_inval();
@@ -1527,6 +1537,15 @@ static void amdvi_cmdbuf_exec(AMDVIState *s)
return;
}
+ /*
+ * Commands in guest memory are little-endian. Convert once after reading
+ * so that command handlers can decode values in host native endianness.
+ * Convert back to little-endian only when writing data to guest memory via
+ * dma_memory_write().
+ */
+ cmd[0] = le64_to_cpu(cmd[0]);
+ cmd[1] = le64_to_cpu(cmd[1]);
+
switch (extract64(cmd[0], 60, 4)) {
case AMDVI_CMD_COMPLETION_WAIT:
amdvi_completion_wait(s, cmd);
--
MST
^ permalink raw reply related [flat|nested] 46+ messages in thread
* [PULL 06/44] hw/mem: add sp-mem device for Specific Purpose Memory
2026-07-05 14:36 [PULL 00/44] pci, vhost, virtio, iommu: features, fixes, cleanups Michael S. Tsirkin
` (4 preceding siblings ...)
2026-07-05 14:34 ` [PULL 05/44] amd_iommu: Fix endianness handling for command buffer entries Michael S. Tsirkin
@ 2026-07-05 14:34 ` Michael S. Tsirkin
2026-07-05 14:34 ` [PULL 07/44] qapi, hmp: introspection for the sp-mem device Michael S. Tsirkin
` (38 subsequent siblings)
44 siblings, 0 replies; 46+ messages in thread
From: Michael S. Tsirkin @ 2026-07-05 14:34 UTC (permalink / raw)
To: qemu-devel
Cc: Peter Maydell, fanhuang, Igor Mammedov, David Hildenbrand (Arm),
Paolo Bonzini
From: fanhuang <FangSheng.Huang@amd.com>
Introduce a TYPE_MEMORY_DEVICE subclass `sp-mem` for boot-time
SOFT_RESERVED memory exposed to the guest with a per-device NUMA
proximity domain.
The device targets accelerator memory (HBM and similar) that the
firmware hands to the guest OS as SOFT_RESERVED memory, so a driver
in the guest -- rather than the kernel's general allocator -- owns
the range.
Usage:
-object memory-backend-ram,id=spm0,size=$SIZE
-numa node,nodeid=$N
-device sp-mem,id=dev0,memdev=spm0,node=$N[,addr=$GPA]
The device is boot-time only (no hotplug).
Signed-off-by: FangSheng Huang <FangSheng.Huang@amd.com>
Reviewed-by: Igor Mammedov <imammedo@redhat.com>
Reviewed-by: David Hildenbrand (Arm) <david@kernel.org>
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
Message-ID: <20260623075051.3797975-2-FangSheng.Huang@amd.com>
---
include/hw/mem/sp-mem.h | 33 ++++++++++++
hw/mem/sp-mem.c | 109 ++++++++++++++++++++++++++++++++++++++++
hw/mem/Kconfig | 4 ++
hw/mem/meson.build | 1 +
4 files changed, 147 insertions(+)
create mode 100644 include/hw/mem/sp-mem.h
create mode 100644 hw/mem/sp-mem.c
diff --git a/include/hw/mem/sp-mem.h b/include/hw/mem/sp-mem.h
new file mode 100644
index 0000000000..a8951b49e6
--- /dev/null
+++ b/include/hw/mem/sp-mem.h
@@ -0,0 +1,33 @@
+/*
+ * Specific Purpose Memory (SPM) device
+ *
+ * TYPE_MEMORY_DEVICE subclass for boot-time-only memory exposed to the
+ * guest as an E820 SOFT_RESERVED range with a SRAT memory-affinity entry.
+ *
+ * Copyright (c) 2026 Advanced Micro Devices, Inc.
+ *
+ * Authors:
+ * FangSheng Huang <FangSheng.Huang@amd.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef QEMU_SP_MEM_H
+#define QEMU_SP_MEM_H
+
+#include "hw/core/qdev.h"
+#include "qom/object.h"
+
+#define TYPE_SP_MEM "sp-mem"
+
+OBJECT_DECLARE_SIMPLE_TYPE(SpMemDevice, SP_MEM)
+
+struct SpMemDevice {
+ DeviceState parent_obj;
+
+ HostMemoryBackend *hostmem;
+ uint32_t node;
+ uint64_t addr;
+};
+
+#endif /* QEMU_SP_MEM_H */
diff --git a/hw/mem/sp-mem.c b/hw/mem/sp-mem.c
new file mode 100644
index 0000000000..d088222f54
--- /dev/null
+++ b/hw/mem/sp-mem.c
@@ -0,0 +1,109 @@
+/*
+ * Specific Purpose Memory (SPM) device
+ *
+ * Copyright (c) 2026 Advanced Micro Devices, Inc.
+ *
+ * Authors:
+ * FangSheng Huang <FangSheng.Huang@amd.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/module.h"
+#include "qapi/error.h"
+#include "hw/core/qdev-properties.h"
+#include "hw/core/qdev.h"
+#include "hw/mem/sp-mem.h"
+#include "hw/mem/memory-device.h"
+#include "system/hostmem.h"
+
+#define SP_MEM_MEMDEV_PROP "memdev"
+#define SP_MEM_NODE_PROP "node"
+#define SP_MEM_ADDR_PROP "addr"
+
+static const Property sp_mem_properties[] = {
+ DEFINE_PROP_LINK(SP_MEM_MEMDEV_PROP, SpMemDevice, hostmem,
+ TYPE_MEMORY_BACKEND, HostMemoryBackend *),
+ DEFINE_PROP_UINT32(SP_MEM_NODE_PROP, SpMemDevice, node, 0),
+ DEFINE_PROP_UINT64(SP_MEM_ADDR_PROP, SpMemDevice, addr, 0),
+};
+
+static uint64_t sp_mem_get_addr(const MemoryDeviceState *md)
+{
+ return object_property_get_uint(OBJECT(md), SP_MEM_ADDR_PROP,
+ &error_abort);
+}
+
+static void sp_mem_set_addr(MemoryDeviceState *md, uint64_t addr,
+ Error **errp)
+{
+ object_property_set_uint(OBJECT(md), SP_MEM_ADDR_PROP, addr, errp);
+}
+
+static MemoryRegion *sp_mem_get_memory_region(MemoryDeviceState *md,
+ Error **errp)
+{
+ SpMemDevice *spm = SP_MEM(md);
+
+ if (!spm->hostmem) {
+ error_setg(errp, "'%s' property must be set", SP_MEM_MEMDEV_PROP);
+ return NULL;
+ }
+ return host_memory_backend_get_memory(spm->hostmem);
+}
+
+static void sp_mem_realize(DeviceState *dev, Error **errp)
+{
+ SpMemDevice *spm = SP_MEM(dev);
+
+ if (!spm->hostmem) {
+ error_setg(errp, "'%s' property is required", SP_MEM_MEMDEV_PROP);
+ return;
+ }
+ if (host_memory_backend_is_mapped(spm->hostmem)) {
+ error_setg(errp, "memory backend '%s' is already in use",
+ object_get_canonical_path_component(OBJECT(spm->hostmem)));
+ return;
+ }
+ host_memory_backend_set_mapped(spm->hostmem, true);
+}
+
+static void sp_mem_unrealize(DeviceState *dev)
+{
+ SpMemDevice *spm = SP_MEM(dev);
+
+ host_memory_backend_set_mapped(spm->hostmem, false);
+}
+
+static void sp_mem_class_init(ObjectClass *oc, const void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(oc);
+ MemoryDeviceClass *mdc = MEMORY_DEVICE_CLASS(oc);
+
+ dc->desc = "SPM (Specific Purpose Memory) device";
+ dc->hotpluggable = false;
+ dc->realize = sp_mem_realize;
+ dc->unrealize = sp_mem_unrealize;
+ device_class_set_props(dc, sp_mem_properties);
+
+ mdc->get_addr = sp_mem_get_addr;
+ mdc->set_addr = sp_mem_set_addr;
+ mdc->get_memory_region = sp_mem_get_memory_region;
+ mdc->get_plugged_size = memory_device_get_region_size;
+}
+
+static const TypeInfo sp_mem_types[] = {
+ {
+ .name = TYPE_SP_MEM,
+ .parent = TYPE_DEVICE,
+ .class_init = sp_mem_class_init,
+ .instance_size = sizeof(SpMemDevice),
+ .interfaces = (InterfaceInfo[]) {
+ { TYPE_MEMORY_DEVICE },
+ { }
+ },
+ },
+};
+
+DEFINE_TYPES(sp_mem_types)
diff --git a/hw/mem/Kconfig b/hw/mem/Kconfig
index 73c5ae8ad9..39ddb36710 100644
--- a/hw/mem/Kconfig
+++ b/hw/mem/Kconfig
@@ -16,3 +16,7 @@ config CXL_MEM_DEVICE
bool
default y if CXL
select MEM_DEVICE
+
+config SP_MEM
+ bool
+ select MEM_DEVICE
diff --git a/hw/mem/meson.build b/hw/mem/meson.build
index 8c2beeb7d4..f410d75475 100644
--- a/hw/mem/meson.build
+++ b/hw/mem/meson.build
@@ -4,6 +4,7 @@ mem_ss.add(when: 'CONFIG_DIMM', if_true: files('pc-dimm.c'))
mem_ss.add(when: 'CONFIG_NPCM7XX', if_true: files('npcm7xx_mc.c'))
mem_ss.add(when: 'CONFIG_NVDIMM', if_true: files('nvdimm.c'))
mem_ss.add(when: 'CONFIG_CXL_MEM_DEVICE', if_true: files('cxl_type3.c'))
+mem_ss.add(when: 'CONFIG_SP_MEM', if_true: files('sp-mem.c'))
stub_ss.add(files('cxl_type3_stubs.c'))
stub_ss.add(files('memory-device-stubs.c'))
--
MST
^ permalink raw reply related [flat|nested] 46+ messages in thread
* [PULL 07/44] qapi, hmp: introspection for the sp-mem device
2026-07-05 14:36 [PULL 00/44] pci, vhost, virtio, iommu: features, fixes, cleanups Michael S. Tsirkin
` (5 preceding siblings ...)
2026-07-05 14:34 ` [PULL 06/44] hw/mem: add sp-mem device for Specific Purpose Memory Michael S. Tsirkin
@ 2026-07-05 14:34 ` Michael S. Tsirkin
2026-07-05 14:34 ` [PULL 08/44] i386/acpi-build: partition device_memory SRAT umbrella for sp-mem Michael S. Tsirkin
` (37 subsequent siblings)
44 siblings, 0 replies; 46+ messages in thread
From: Michael S. Tsirkin @ 2026-07-05 14:34 UTC (permalink / raw)
To: qemu-devel
Cc: Peter Maydell, fanhuang, Igor Mammedov, David Hildenbrand (Arm),
Philippe Mathieu-Daudé, Zhao Liu, Eric Blake,
Markus Armbruster
From: fanhuang <FangSheng.Huang@amd.com>
Add a SpMemDeviceInfo variant to MemoryDeviceInfo so `query-memory-devices`
reports each sp-mem instance (id, addr, size, node, memdev), and print it
from HMP `info memory-devices`.
Signed-off-by: FangSheng Huang <FangSheng.Huang@amd.com>
Reviewed-by: Igor Mammedov <imammedo@redhat.com>
Reviewed-by: David Hildenbrand (Arm) <david@kernel.org>
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
Message-ID: <20260623075051.3797975-3-FangSheng.Huang@amd.com>
---
qapi/machine.json | 43 ++++++++++++++++++++++++++++++++++++--
hw/core/machine-hmp-cmds.c | 11 ++++++++++
hw/mem/sp-mem.c | 19 +++++++++++++++++
3 files changed, 71 insertions(+), 2 deletions(-)
diff --git a/qapi/machine.json b/qapi/machine.json
index 685e4e29b8..9b2248038f 100644
--- a/qapi/machine.json
+++ b/qapi/machine.json
@@ -1413,6 +1413,32 @@
}
}
+##
+# @SpMemDeviceInfo:
+#
+# sp-mem device state information
+#
+# @id: device's ID
+#
+# @addr: physical address, where device is mapped
+#
+# @size: size of memory that the device provides, in bytes
+#
+# @node: NUMA proximity domain to which the device is assigned
+#
+# @memdev: memory backend linked with device
+#
+# Since: 11.1
+##
+{ 'struct': 'SpMemDeviceInfo',
+ 'data': { '*id': 'str',
+ 'addr': 'size',
+ 'size': 'size',
+ 'node': 'int',
+ 'memdev': 'str'
+ }
+}
+
##
# @MemoryDeviceInfoKind:
#
@@ -1426,11 +1452,13 @@
#
# @hv-balloon: since 8.2.
#
+# @sp-mem: since 11.1.
+#
# Since: 2.1
##
{ 'enum': 'MemoryDeviceInfoKind',
'data': [ 'dimm', 'nvdimm', 'virtio-pmem', 'virtio-mem', 'sgx-epc',
- 'hv-balloon' ] }
+ 'hv-balloon', 'sp-mem' ] }
##
# @PCDIMMDeviceInfoWrapper:
@@ -1482,6 +1510,16 @@
{ 'struct': 'HvBalloonDeviceInfoWrapper',
'data': { 'data': 'HvBalloonDeviceInfo' } }
+##
+# @SpMemDeviceInfoWrapper:
+#
+# @data: sp-mem device state information
+#
+# Since: 11.1
+##
+{ 'struct': 'SpMemDeviceInfoWrapper',
+ 'data': { 'data': 'SpMemDeviceInfo' } }
+
##
# @MemoryDeviceInfo:
#
@@ -1499,7 +1537,8 @@
'virtio-pmem': 'VirtioPMEMDeviceInfoWrapper',
'virtio-mem': 'VirtioMEMDeviceInfoWrapper',
'sgx-epc': 'SgxEPCDeviceInfoWrapper',
- 'hv-balloon': 'HvBalloonDeviceInfoWrapper'
+ 'hv-balloon': 'HvBalloonDeviceInfoWrapper',
+ 'sp-mem': 'SpMemDeviceInfoWrapper'
}
}
diff --git a/hw/core/machine-hmp-cmds.c b/hw/core/machine-hmp-cmds.c
index 46846f741a..686304bafa 100644
--- a/hw/core/machine-hmp-cmds.c
+++ b/hw/core/machine-hmp-cmds.c
@@ -279,6 +279,7 @@ void hmp_info_memory_devices(Monitor *mon, const QDict *qdict)
PCDIMMDeviceInfo *di;
SgxEPCDeviceInfo *se;
HvBalloonDeviceInfo *hi;
+ SpMemDeviceInfo *spmi;
for (info = info_list; info; info = info->next) {
value = info->value;
@@ -350,6 +351,16 @@ void hmp_info_memory_devices(Monitor *mon, const QDict *qdict)
monitor_printf(mon, " memdev: %s\n", hi->memdev);
}
break;
+ case MEMORY_DEVICE_INFO_KIND_SP_MEM:
+ spmi = value->u.sp_mem.data;
+ monitor_printf(mon, "Memory device [%s]: \"%s\"\n",
+ MemoryDeviceInfoKind_str(value->type),
+ spmi->id ? spmi->id : "");
+ monitor_printf(mon, " addr: 0x%" PRIx64 "\n", spmi->addr);
+ monitor_printf(mon, " node: %" PRId64 "\n", spmi->node);
+ monitor_printf(mon, " size: %" PRIu64 "\n", spmi->size);
+ monitor_printf(mon, " memdev: %s\n", spmi->memdev);
+ break;
default:
g_assert_not_reached();
}
diff --git a/hw/mem/sp-mem.c b/hw/mem/sp-mem.c
index d088222f54..962d0f937e 100644
--- a/hw/mem/sp-mem.c
+++ b/hw/mem/sp-mem.c
@@ -53,6 +53,24 @@ static MemoryRegion *sp_mem_get_memory_region(MemoryDeviceState *md,
return host_memory_backend_get_memory(spm->hostmem);
}
+static void sp_mem_fill_device_info(const MemoryDeviceState *md,
+ MemoryDeviceInfo *info)
+{
+ SpMemDeviceInfo *di = g_new0(SpMemDeviceInfo, 1);
+ SpMemDevice *spm = SP_MEM(md);
+ DeviceState *dev = DEVICE(md);
+
+ di->id = dev->id ? g_strdup(dev->id) : NULL;
+ di->addr = spm->addr;
+ di->size = memory_region_size(
+ host_memory_backend_get_memory(spm->hostmem));
+ di->node = spm->node;
+ di->memdev = object_get_canonical_path(OBJECT(spm->hostmem));
+
+ info->u.sp_mem.data = di;
+ info->type = MEMORY_DEVICE_INFO_KIND_SP_MEM;
+}
+
static void sp_mem_realize(DeviceState *dev, Error **errp)
{
SpMemDevice *spm = SP_MEM(dev);
@@ -91,6 +109,7 @@ static void sp_mem_class_init(ObjectClass *oc, const void *data)
mdc->set_addr = sp_mem_set_addr;
mdc->get_memory_region = sp_mem_get_memory_region;
mdc->get_plugged_size = memory_device_get_region_size;
+ mdc->fill_device_info = sp_mem_fill_device_info;
}
static const TypeInfo sp_mem_types[] = {
--
MST
^ permalink raw reply related [flat|nested] 46+ messages in thread
* [PULL 08/44] i386/acpi-build: partition device_memory SRAT umbrella for sp-mem
2026-07-05 14:36 [PULL 00/44] pci, vhost, virtio, iommu: features, fixes, cleanups Michael S. Tsirkin
` (6 preceding siblings ...)
2026-07-05 14:34 ` [PULL 07/44] qapi, hmp: introspection for the sp-mem device Michael S. Tsirkin
@ 2026-07-05 14:34 ` Michael S. Tsirkin
2026-07-05 14:35 ` [PULL 09/44] hw/i386: hook sp-mem into the pc machine plug path Michael S. Tsirkin
` (36 subsequent siblings)
44 siblings, 0 replies; 46+ messages in thread
From: Michael S. Tsirkin @ 2026-07-05 14:34 UTC (permalink / raw)
To: qemu-devel
Cc: Peter Maydell, fanhuang, Igor Mammedov, Ani Sinha, Paolo Bonzini,
Richard Henderson
From: fanhuang <FangSheng.Huang@amd.com>
Restructure the device_memory SRAT umbrella entry into a per-kind
partition: each TYPE_SP_MEM device gets an ENABLED entry at its own
proximity_domain; the remaining sub-ranges get HOTPLUGGABLE | ENABLED
placeholders at the highest PXM, preserving the existing umbrella
convention.
Signed-off-by: FangSheng Huang <FangSheng.Huang@amd.com>
Reviewed-by: Igor Mammedov <imammedo@redhat.com>
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
Message-ID: <20260623075051.3797975-4-FangSheng.Huang@amd.com>
---
hw/i386/acpi-build.c | 96 ++++++++++++++++++++++++++++++++++++++++++--
1 file changed, 92 insertions(+), 4 deletions(-)
diff --git a/hw/i386/acpi-build.c b/hw/i386/acpi-build.c
index 2ee061558c..8837b69687 100644
--- a/hw/i386/acpi-build.c
+++ b/hw/i386/acpi-build.c
@@ -52,6 +52,7 @@
#include "migration/vmstate.h"
#include "hw/mem/memory-device.h"
#include "hw/mem/nvdimm.h"
+#include "hw/mem/sp-mem.h"
#include "system/numa.h"
#include "system/reset.h"
#include "hw/hyperv/vmbus-bridge.h"
@@ -1351,6 +1352,96 @@ build_tpm_tcpa(GArray *table_data, BIOSLinker *linker, GArray *tcpalog,
}
#endif
+typedef struct {
+ uint64_t addr;
+ uint64_t size;
+ uint32_t node;
+} SpMemRange;
+
+static int sp_mem_collect_ranges_cb(Object *obj, void *opaque)
+{
+ GArray *ranges = opaque;
+ SpMemDevice *spm;
+ MemoryDeviceClass *mdc;
+ SpMemRange r;
+
+ if (!object_dynamic_cast(obj, TYPE_SP_MEM)) {
+ return 0;
+ }
+ spm = SP_MEM(obj);
+ mdc = MEMORY_DEVICE_GET_CLASS(MEMORY_DEVICE(spm));
+ r.addr = mdc->get_addr(MEMORY_DEVICE(spm));
+ r.size = memory_region_size(
+ host_memory_backend_get_memory(spm->hostmem));
+ r.node = spm->node;
+ g_array_append_val(ranges, r);
+ return 0;
+}
+
+static gint sp_mem_range_compare(gconstpointer a, gconstpointer b)
+{
+ const SpMemRange *range_a = a;
+ const SpMemRange *range_b = b;
+
+ if (range_a->addr < range_b->addr) {
+ return -1;
+ }
+ if (range_a->addr > range_b->addr) {
+ return 1;
+ }
+ return 0;
+}
+
+/*
+ * Emit SRAT memory-affinity entries covering the device_memory region.
+ *
+ * For each plugged TYPE_SP_MEM device, emit an ENABLED entry at the
+ * device's own proximity_domain. All remaining sub-ranges (gaps
+ * between sp-mem devices, leading and trailing padding, and ranges
+ * occupied by other memory devices) are covered by HOTPLUGGABLE |
+ * ENABLED placeholder entries at PXM = nb_numa_nodes - 1.
+ */
+static void build_srat_device_memory(GArray *table_data, MachineState *ms)
+{
+ g_autoptr(GArray) ranges = g_array_new(FALSE, TRUE, sizeof(SpMemRange));
+ uint32_t hotplug_pxm = ms->numa_state->num_nodes - 1;
+ uint64_t region_start, region_end;
+ guint i;
+
+ region_start = ms->device_memory->base;
+ region_end = region_start + memory_region_size(&ms->device_memory->mr);
+
+ object_child_foreach_recursive(qdev_get_machine(),
+ sp_mem_collect_ranges_cb, ranges);
+ g_array_sort(ranges, sp_mem_range_compare);
+
+ for (i = 0; i < ranges->len; i++) {
+ SpMemRange *r = &g_array_index(ranges, SpMemRange, i);
+
+ if (region_start < r->addr) {
+ build_srat_memory(table_data, region_start, r->addr - region_start,
+ hotplug_pxm,
+ MEM_AFFINITY_HOTPLUGGABLE |
+ MEM_AFFINITY_ENABLED);
+ }
+ build_srat_memory(table_data, r->addr, r->size, r->node,
+ MEM_AFFINITY_ENABLED);
+ region_start = r->addr + r->size;
+ }
+
+ /*
+ * Cover the rest of the device_memory window that no sp-mem device
+ * occupies. Keeping it HOTPLUGGABLE preserves the umbrella entry's
+ * role for future pc-dimm / virtio-mem hot-add into this window.
+ */
+ if (region_start < region_end) {
+ build_srat_memory(table_data, region_start, region_end - region_start,
+ hotplug_pxm,
+ MEM_AFFINITY_HOTPLUGGABLE |
+ MEM_AFFINITY_ENABLED);
+ }
+}
+
#define HOLE_640K_START (640 * KiB)
#define HOLE_640K_END (1 * MiB)
@@ -1487,10 +1578,7 @@ build_srat(GArray *table_data, BIOSLinker *linker, MachineState *machine)
* providing _PXM method if necessary.
*/
if (machine->device_memory) {
- build_srat_memory(table_data, machine->device_memory->base,
- memory_region_size(&machine->device_memory->mr),
- nb_numa_nodes - 1,
- MEM_AFFINITY_HOTPLUGGABLE | MEM_AFFINITY_ENABLED);
+ build_srat_device_memory(table_data, machine);
}
acpi_table_end(linker, &table);
--
MST
^ permalink raw reply related [flat|nested] 46+ messages in thread
* [PULL 09/44] hw/i386: hook sp-mem into the pc machine plug path
2026-07-05 14:36 [PULL 00/44] pci, vhost, virtio, iommu: features, fixes, cleanups Michael S. Tsirkin
` (7 preceding siblings ...)
2026-07-05 14:34 ` [PULL 08/44] i386/acpi-build: partition device_memory SRAT umbrella for sp-mem Michael S. Tsirkin
@ 2026-07-05 14:35 ` Michael S. Tsirkin
2026-07-05 14:35 ` [PULL 10/44] MAINTAINERS: cover sp-mem under Memory devices, add R: tag Michael S. Tsirkin
` (35 subsequent siblings)
44 siblings, 0 replies; 46+ messages in thread
From: Michael S. Tsirkin @ 2026-07-05 14:35 UTC (permalink / raw)
To: qemu-devel
Cc: Peter Maydell, fanhuang, Igor Mammedov, David Hildenbrand (Arm),
Paolo Bonzini, Richard Henderson
From: fanhuang <FangSheng.Huang@amd.com>
Add the pc machine hookup for TYPE_SP_MEM so each sp-mem instance is
placed by the memory-device framework and reported to the guest as
E820_SOFT_RESERVED.
Signed-off-by: FangSheng Huang <FangSheng.Huang@amd.com>
Reviewed-by: Igor Mammedov <imammedo@redhat.com>
Reviewed-by: David Hildenbrand (Arm) <david@kernel.org>
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
Message-ID: <20260623075051.3797975-5-FangSheng.Huang@amd.com>
---
hw/i386/e820_memory_layout.h | 11 ++++++-----
hw/i386/pc.c | 36 ++++++++++++++++++++++++++++++++++++
hw/i386/Kconfig | 2 ++
3 files changed, 44 insertions(+), 5 deletions(-)
diff --git a/hw/i386/e820_memory_layout.h b/hw/i386/e820_memory_layout.h
index b50acfa201..6ef169db9c 100644
--- a/hw/i386/e820_memory_layout.h
+++ b/hw/i386/e820_memory_layout.h
@@ -10,11 +10,12 @@
#define HW_I386_E820_MEMORY_LAYOUT_H
/* e820 types */
-#define E820_RAM 1
-#define E820_RESERVED 2
-#define E820_ACPI 3
-#define E820_NVS 4
-#define E820_UNUSABLE 5
+#define E820_RAM 1
+#define E820_RESERVED 2
+#define E820_ACPI 3
+#define E820_NVS 4
+#define E820_UNUSABLE 5
+#define E820_SOFT_RESERVED 0xefffffff
struct e820_entry {
uint64_t address;
diff --git a/hw/i386/pc.c b/hw/i386/pc.c
index 73a625327c..2bacece249 100644
--- a/hw/i386/pc.c
+++ b/hw/i386/pc.c
@@ -63,6 +63,7 @@
#include "hw/i386/kvm/xen_gnttab.h"
#include "hw/i386/kvm/xen_xenstore.h"
#include "hw/mem/memory-device.h"
+#include "hw/mem/sp-mem.h"
#include "e820_memory_layout.h"
#include "trace.h"
#include "sev.h"
@@ -1285,11 +1286,43 @@ static void pc_hv_balloon_plug(HotplugHandler *hotplug_dev,
memory_device_plug(MEMORY_DEVICE(dev), MACHINE(hotplug_dev));
}
+static void pc_sp_mem_pre_plug(HotplugHandler *hotplug_dev,
+ DeviceState *dev, Error **errp)
+{
+ MachineState *ms = MACHINE(hotplug_dev);
+ SpMemDevice *spm = SP_MEM(dev);
+
+ if (ms->numa_state && spm->node >= ms->numa_state->num_nodes) {
+ error_setg(errp,
+ "'node' property value %" PRIu32
+ " exceeds the number of NUMA nodes (%d)",
+ spm->node, ms->numa_state->num_nodes);
+ return;
+ }
+ memory_device_pre_plug(MEMORY_DEVICE(dev), ms, errp);
+}
+
+static void pc_sp_mem_plug(HotplugHandler *hotplug_dev,
+ DeviceState *dev, Error **errp)
+{
+ SpMemDevice *spm = SP_MEM(dev);
+ MemoryDeviceClass *mdc = MEMORY_DEVICE_GET_CLASS(MEMORY_DEVICE(dev));
+ uint64_t addr, size;
+
+ memory_device_plug(MEMORY_DEVICE(dev), MACHINE(hotplug_dev));
+
+ addr = mdc->get_addr(MEMORY_DEVICE(dev));
+ size = memory_region_size(host_memory_backend_get_memory(spm->hostmem));
+ e820_add_entry(addr, size, E820_SOFT_RESERVED);
+}
+
static void pc_machine_device_pre_plug_cb(HotplugHandler *hotplug_dev,
DeviceState *dev, Error **errp)
{
if (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) {
pc_memory_pre_plug(hotplug_dev, dev, errp);
+ } else if (object_dynamic_cast(OBJECT(dev), TYPE_SP_MEM)) {
+ pc_sp_mem_pre_plug(hotplug_dev, dev, errp);
} else if (object_dynamic_cast(OBJECT(dev), TYPE_CPU)) {
x86_cpu_pre_plug(hotplug_dev, dev, errp);
} else if (object_dynamic_cast(OBJECT(dev), TYPE_VIRTIO_MD_PCI)) {
@@ -1326,6 +1359,8 @@ static void pc_machine_device_plug_cb(HotplugHandler *hotplug_dev,
{
if (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) {
pc_memory_plug(hotplug_dev, dev, errp);
+ } else if (object_dynamic_cast(OBJECT(dev), TYPE_SP_MEM)) {
+ pc_sp_mem_plug(hotplug_dev, dev, errp);
} else if (object_dynamic_cast(OBJECT(dev), TYPE_CPU)) {
x86_cpu_plug(hotplug_dev, dev, errp);
} else if (object_dynamic_cast(OBJECT(dev), TYPE_VIRTIO_MD_PCI)) {
@@ -1370,6 +1405,7 @@ static HotplugHandler *pc_get_hotplug_handler(MachineState *machine,
DeviceState *dev)
{
if (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM) ||
+ object_dynamic_cast(OBJECT(dev), TYPE_SP_MEM) ||
object_dynamic_cast(OBJECT(dev), TYPE_CPU) ||
object_dynamic_cast(OBJECT(dev), TYPE_VIRTIO_MD_PCI) ||
object_dynamic_cast(OBJECT(dev), TYPE_VIRTIO_IOMMU_PCI) ||
diff --git a/hw/i386/Kconfig b/hw/i386/Kconfig
index 12473acaa7..e27d8816e5 100644
--- a/hw/i386/Kconfig
+++ b/hw/i386/Kconfig
@@ -84,6 +84,7 @@ config I440FX
select PCI_I440FX
select PIIX
select DIMM
+ select SP_MEM
select SMBIOS
select SMBIOS_LEGACY
select FW_CFG_DMA
@@ -113,6 +114,7 @@ config Q35
select LPC_ICH9
select AHCI_ICH9
select DIMM
+ select SP_MEM
select SMBIOS
select FW_CFG_DMA
--
MST
^ permalink raw reply related [flat|nested] 46+ messages in thread
* [PULL 10/44] MAINTAINERS: cover sp-mem under Memory devices, add R: tag
2026-07-05 14:36 [PULL 00/44] pci, vhost, virtio, iommu: features, fixes, cleanups Michael S. Tsirkin
` (8 preceding siblings ...)
2026-07-05 14:35 ` [PULL 09/44] hw/i386: hook sp-mem into the pc machine plug path Michael S. Tsirkin
@ 2026-07-05 14:35 ` Michael S. Tsirkin
2026-07-05 14:35 ` [PULL 11/44] tests/acpi: add empty expected blobs for sp-mem SRAT test Michael S. Tsirkin
` (34 subsequent siblings)
44 siblings, 0 replies; 46+ messages in thread
From: Michael S. Tsirkin @ 2026-07-05 14:35 UTC (permalink / raw)
To: qemu-devel
Cc: Peter Maydell, fanhuang, Igor Mammedov, David Hildenbrand,
Philippe Mathieu-Daudé, Thomas Huth, Pierrick Bouvier,
Markus Armbruster
From: fanhuang <FangSheng.Huang@amd.com>
Signed-off-by: FangSheng Huang <FangSheng.Huang@amd.com>
Acked-by: Igor Mammedov <imammedo@redhat.com>
Acked-by: David Hildenbrand <david@kernel.org>
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
Acked-by: David Hildenbrand (Arm) <david@kernel.org>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
Message-ID: <20260623075051.3797975-6-FangSheng.Huang@amd.com>
---
MAINTAINERS | 3 +++
1 file changed, 3 insertions(+)
diff --git a/MAINTAINERS b/MAINTAINERS
index 97dcc78ded..5ee7c1f601 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -3417,13 +3417,16 @@ F: tests/unit/test-ram-discard-manager-stubs.c
Memory devices
M: David Hildenbrand <david@kernel.org>
M: Igor Mammedov <imammedo@redhat.com>
+R: FangSheng Huang <FangSheng.Huang@amd.com>
S: Supported
F: hw/mem/memory-device*.c
F: hw/mem/nvdimm.c
F: hw/mem/pc-dimm.c
+F: hw/mem/sp-mem.c
F: include/hw/mem/memory-device.h
F: include/hw/mem/nvdimm.h
F: include/hw/mem/pc-dimm.h
+F: include/hw/mem/sp-mem.h
F: docs/nvdimm.txt
SPICE
--
MST
^ permalink raw reply related [flat|nested] 46+ messages in thread
* [PULL 11/44] tests/acpi: add empty expected blobs for sp-mem SRAT test
2026-07-05 14:36 [PULL 00/44] pci, vhost, virtio, iommu: features, fixes, cleanups Michael S. Tsirkin
` (9 preceding siblings ...)
2026-07-05 14:35 ` [PULL 10/44] MAINTAINERS: cover sp-mem under Memory devices, add R: tag Michael S. Tsirkin
@ 2026-07-05 14:35 ` Michael S. Tsirkin
2026-07-05 14:35 ` [PULL 12/44] tests/acpi: add bios-tables-test case for sp-mem Michael S. Tsirkin
` (33 subsequent siblings)
44 siblings, 0 replies; 46+ messages in thread
From: Michael S. Tsirkin @ 2026-07-05 14:35 UTC (permalink / raw)
To: qemu-devel; +Cc: Peter Maydell, fanhuang, Igor Mammedov, Ani Sinha
From: fanhuang <FangSheng.Huang@amd.com>
Add empty SRAT.spmem and DSDT.spmem stubs and list them in
bios-tables-test-allowed-diff.h.
Signed-off-by: FangSheng Huang <FangSheng.Huang@amd.com>
Acked-by: Igor Mammedov <imammedo@redhat.com>
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
Message-ID: <20260623075051.3797975-7-FangSheng.Huang@amd.com>
---
tests/qtest/bios-tables-test-allowed-diff.h | 2 ++
tests/data/acpi/x86/q35/DSDT.spmem | 0
tests/data/acpi/x86/q35/SRAT.spmem | 0
3 files changed, 2 insertions(+)
create mode 100644 tests/data/acpi/x86/q35/DSDT.spmem
create mode 100644 tests/data/acpi/x86/q35/SRAT.spmem
diff --git a/tests/qtest/bios-tables-test-allowed-diff.h b/tests/qtest/bios-tables-test-allowed-diff.h
index dfb8523c8b..188003fa90 100644
--- a/tests/qtest/bios-tables-test-allowed-diff.h
+++ b/tests/qtest/bios-tables-test-allowed-diff.h
@@ -1 +1,3 @@
/* List of comma-separated changed AML files to ignore */
+"tests/data/acpi/x86/q35/DSDT.spmem",
+"tests/data/acpi/x86/q35/SRAT.spmem",
diff --git a/tests/data/acpi/x86/q35/DSDT.spmem b/tests/data/acpi/x86/q35/DSDT.spmem
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/tests/data/acpi/x86/q35/SRAT.spmem b/tests/data/acpi/x86/q35/SRAT.spmem
new file mode 100644
index 0000000000..e69de29bb2
--
MST
^ permalink raw reply related [flat|nested] 46+ messages in thread
* [PULL 12/44] tests/acpi: add bios-tables-test case for sp-mem
2026-07-05 14:36 [PULL 00/44] pci, vhost, virtio, iommu: features, fixes, cleanups Michael S. Tsirkin
` (10 preceding siblings ...)
2026-07-05 14:35 ` [PULL 11/44] tests/acpi: add empty expected blobs for sp-mem SRAT test Michael S. Tsirkin
@ 2026-07-05 14:35 ` Michael S. Tsirkin
2026-07-05 14:35 ` [PULL 13/44] tests/acpi: generate expected blobs for sp-mem SRAT test Michael S. Tsirkin
` (32 subsequent siblings)
44 siblings, 0 replies; 46+ messages in thread
From: Michael S. Tsirkin @ 2026-07-05 14:35 UTC (permalink / raw)
To: qemu-devel; +Cc: Peter Maydell, fanhuang, Igor Mammedov, Ani Sinha
From: fanhuang <FangSheng.Huang@amd.com>
Add a q35 bios-tables-test case that boots two sp-mem devices on
distinct NUMA nodes within the device_memory window, exercising the
per-kind SRAT partition (per-device ENABLED entries plus HOTPLUGGABLE
placeholders for the remaining sub-ranges).
Signed-off-by: FangSheng Huang <FangSheng.Huang@amd.com>
Reviewed-by: Igor Mammedov <imammedo@redhat.com>
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
Message-ID: <20260623075051.3797975-8-FangSheng.Huang@amd.com>
---
tests/qtest/bios-tables-test.c | 21 +++++++++++++++++++++
1 file changed, 21 insertions(+)
diff --git a/tests/qtest/bios-tables-test.c b/tests/qtest/bios-tables-test.c
index af6d9b5136..45abd8bd8c 100644
--- a/tests/qtest/bios-tables-test.c
+++ b/tests/qtest/bios-tables-test.c
@@ -1416,6 +1416,26 @@ static void test_acpi_q35_tcg_numamem(void)
free_test_data(&data);
}
+static void test_acpi_q35_tcg_sp_mem(void)
+{
+ test_data data = {};
+
+ data.machine = MACHINE_Q35;
+ data.arch = "x86",
+ data.variant = ".spmem";
+ test_acpi_one(" -m 128M,slots=4,maxmem=1G"
+ " -object memory-backend-ram,id=ram0,size=128M"
+ " -numa node,nodeid=0,memdev=ram0"
+ " -numa node,nodeid=1"
+ " -numa node,nodeid=2"
+ " -object memory-backend-ram,id=spm0,size=128M"
+ " -object memory-backend-ram,id=spm1,size=128M"
+ " -device sp-mem,id=sp0,memdev=spm0,node=1"
+ " -device sp-mem,id=sp1,memdev=spm1,node=2",
+ &data);
+ free_test_data(&data);
+}
+
static void test_acpi_q35_kvm_xapic(void)
{
test_data data = {};
@@ -2807,6 +2827,7 @@ int main(int argc, char *argv[])
if (strcmp(arch, "i386")) {
qtest_add_func("acpi/q35/memhp", test_acpi_q35_tcg_memhp);
qtest_add_func("acpi/q35/dimmpxm", test_acpi_q35_tcg_dimm_pxm);
+ qtest_add_func("acpi/q35/sp-mem", test_acpi_q35_tcg_sp_mem);
qtest_add_func("acpi/q35/acpihmat",
test_acpi_q35_tcg_acpi_hmat);
qtest_add_func("acpi/q35/mmio64", test_acpi_q35_tcg_mmio64);
--
MST
^ permalink raw reply related [flat|nested] 46+ messages in thread
* [PULL 13/44] tests/acpi: generate expected blobs for sp-mem SRAT test
2026-07-05 14:36 [PULL 00/44] pci, vhost, virtio, iommu: features, fixes, cleanups Michael S. Tsirkin
` (11 preceding siblings ...)
2026-07-05 14:35 ` [PULL 12/44] tests/acpi: add bios-tables-test case for sp-mem Michael S. Tsirkin
@ 2026-07-05 14:35 ` Michael S. Tsirkin
2026-07-05 14:35 ` [PULL 14/44] tests/qtest: add e820 fw_cfg test Michael S. Tsirkin
` (31 subsequent siblings)
44 siblings, 0 replies; 46+ messages in thread
From: Michael S. Tsirkin @ 2026-07-05 14:35 UTC (permalink / raw)
To: qemu-devel; +Cc: Peter Maydell, fanhuang, Igor Mammedov, Ani Sinha
From: fanhuang <FangSheng.Huang@amd.com>
Populate the expected ACPI blobs for the sp-mem test and clear the
allowed-diff list.
SRAT memory-affinity entries for the device_memory window (q35,
-m 128M,maxmem=1G, sp0 on node 1 and sp1 on node 2, each 128M):
Proximity Domain : 1 Base : 0x100000000 Length : 0x08000000 (Enabled)
Proximity Domain : 2 Base : 0x108000000 Length : 0x08000000 (Enabled)
Proximity Domain : 2 Base : 0x110000000 Length : 0x128000000 (Hot Pluggable)
Each sp-mem device gets an ENABLED entry at its own proximity domain;
the remaining device_memory window is covered by a HOTPLUGGABLE
placeholder at the highest proximity domain.
(DSDT.spmem differs from the base only by the memory-hotplug AML
enabled by -m,maxmem.)
Signed-off-by: FangSheng Huang <FangSheng.Huang@amd.com>
Acked-by: Igor Mammedov <imammedo@redhat.com>
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
Message-ID: <20260623075051.3797975-9-FangSheng.Huang@amd.com>
---
tests/qtest/bios-tables-test-allowed-diff.h | 2 --
tests/data/acpi/x86/q35/DSDT.spmem | Bin 0 -> 9910 bytes
tests/data/acpi/x86/q35/SRAT.spmem | Bin 0 -> 384 bytes
3 files changed, 2 deletions(-)
diff --git a/tests/qtest/bios-tables-test-allowed-diff.h b/tests/qtest/bios-tables-test-allowed-diff.h
index 188003fa90..dfb8523c8b 100644
--- a/tests/qtest/bios-tables-test-allowed-diff.h
+++ b/tests/qtest/bios-tables-test-allowed-diff.h
@@ -1,3 +1 @@
/* List of comma-separated changed AML files to ignore */
-"tests/data/acpi/x86/q35/DSDT.spmem",
-"tests/data/acpi/x86/q35/SRAT.spmem",
diff --git a/tests/data/acpi/x86/q35/DSDT.spmem b/tests/data/acpi/x86/q35/DSDT.spmem
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..7e6f850e7a25aaa3cb7abc0ff2aeab4ca7fdb716 100644
GIT binary patch
literal 9910
zcmchdPiz~<y~k(xPs`y_T9T_jmSu}@(xgq=gpyo0cz^mr?k>rdxYU|U*(%n+t|Y6K
zoIGUV#1Bye0}>6iu1_yvUA%W=0v33;*Hf=G(93%_-x{E&9(u{ODC$#K{APEh`B4;5
zA6gx#nc4aNe)HL#neTF!x}$f0{Td<kUTsrrI_287t(D8=G9`pS8U8C~>JD*_^;*ND
zxkQS(Cpli$YmJ(uep|0?HRyjG_`mS|kACXYoq+6qp*-E)2_8N6cL{WQup2N21!be;
zIW^DinKQO#RwS=!>T>ltlO#_IOG(MwGIgT1ro2r@OFemyX!`AyK|9?)D3qAuTEAcW
z%kt8@zqtKT_1VAv?uSp_V<}C)z53)mCTaTZwE#Z+PkeUOA1I&o59vdY^J6Q6(r0~s
zIBZkkNZ>=u=W^Htp;m_PsT*lScdY#2M*kiO3Q0=*!QyA(0G@yE|NrQG_s7<!^Iut)
zeydqSe=zm*kR^tGz)l|fgbe+`QvV*C3X16j$rC?#^!R;3c7mPopxb*#?^HETp10d}
zMXRh3$<sPcVVN!cbe<3yPC5MjYBU=CCMqjOL}|~y1t$+qoO^6*PM7AM$xL!j6sTyn
zW^R>j&D6{8v16=zH1~fpOSvaGsA#o{v0iqMYi+ATbI(H=RJ2-q%PQNZzFl@{t)*=c
z^&7TktS8)8pf(x>2hbxpqK5=xxUWO=kf?7~O?`V(FNhVuhadIzRh`-VeCnB(I-z7I
zfF%3`K6?B<CA)*g&mQ)7Nq<)!jYcE#aEI<Z+&Q9b>g)s$zQ@o0m@p_N5^xbeJyQp}
za7BVVdF)d%^d;NW>O_@2+tg@q05zp|i^3UKw6FMVwL4wAxbfj}F_+4XMx@o=Fi2RY
zt@cKp6lG{<iB@}~5m%C}_C}Kw^C@{Wq69Sc(;BE6q&OWO<GSWq6_iHQH^G&S(;6t_
zu8B}-M9Ku=$~a4sE8`I)xiapW43+grnGBV2mK0aUBS>*&+%**{<H4swWt=6AIMawT
zjXBeyGR~4goEgNK!JHY)IfXc<5a$%;oWh(E;*=1lggGV5nMIsg#F@pMS<J~0CqtYJ
zb27}CL!3FpnZukp%qb&I8F9*(Q^uTm#F<B&dCZx|oYRPN8gWiz&S}g!gE(gp=M3hY
z!JG=>R1l|vITg%VK%51{S-_kH%vnU7MZ{UeoJGu8LYyVUS;Cwp%&8(y6>+MVQ^lOK
zh;tTk&SK74%sGcR=Md){=A6Tv^N4dEan57TdCa+hI2RD-0_I%6oR_$=8ts~wxUv>)
zc9%k>c2<S`R*(8FBIP1dE@I_km@`&hM#{@bc^NA&W91T3E+OR-RxV-X6{Ng^lvl9w
z3RYg_%6Rv^%9ZgRe>GIbd;HZ<8SnASDCaWDxr}oz$2q}3tAc?x9P`vb8;*Hqpbf`7
zGth=(#z`pwO>AwHfGQpzB@A@2D^kKh8KWh*GPbq^SH@kzK*O%wiNQdHEJ?16N08)7
z(LlqlTnPpmj)>cRiYwz0q_{FZxnQ8e;K4uz&NSjoBTg_-ffEc=;LIS-4B`X>6*$2_
z1<onNIfXdEKm|@PP=QlIoD$*$0~I*IKn2b$;>;pWFi?RL3{>D`h?5~sFi?RL3{>FE
zA<i7)1OpW~!9WF08F9*p6AV<~1OpW~^N2H#IKe;#PB2h`a~g3@BTg_-ffEc=;G996
zGl&xmRNw>y6*v{dsUS`;P=ON+RNyQi&H~~D0~I*IKn2bs;w&OgFi?RL3{>DOA<h!w
z1OpW~!9WF06>+MF6AV<~1OpW~XA$Qt;sgT~IKe;#&N;+6hd9AN1x_$ffpZ>l&Ld7R
zP=ON+RN!1doC}B(3{>C*0~I(gaiw6O4aXD>wBeYdfi@g7w!uZDT;w@pB^aon1OpY6
zmyz-^Qi6dBN-$7CxrCHUNC^fiD8WDl<rSp7f|OvOf)Wf=P+sNAc#przmGK@A1}dCe
zFi_##E~A{wC?^=GkP{4)=TsLxyVE5DxzE3>4&e*w=Nb4{RZHegr%MOfPuTGR8M5q<
z<<#ruO|6ra>`tdrm}a@3Wk{AkTsR_6ppB*NPR9&BIbbscN<(OLd#$Nfd!rgZn~`V2
zCo+;}cbvi;EB$PmWRqz^2qT2#2q8yAo$;J*l?-7BcIReQEqJ==kcUua$Gl|*9&tbq
zt;6f{p3`yYlY?jskRIBj?6tA%^d+*_gzPn*y*8G;c0u-<+p~Z@d^#gnV|JE!iS(L~
zUgPPtvGm#n>9r`mJ_ZlZ#Qx@UuM6pQo?ahIuV0W}kJ20C=~FL}-Vo9oJiRfN-nbyW
z5v8w>r%%5``nr(5&ePY&($_CYUysr^#?xnBB7H+h-{9#RW9b_gq;Ev&o8#%HULt){
zNZ;h?n`7yl7o=~xJuB64%<)yvlrVj~c_@6jhmR|9o0{QEUd`}z9fccT#;kzD6()oW
z+a`uPw=Rn~Tya9Uux(<vbL*^x!<8n43)?1!JGX9B98R4OE^M0^?%X<<#o=ZrgbUjy
zhC4T}=Ww{W3E{%FiQ&%8(|H_jenPmg4Tn=KDSRPdNKM=5#<QPBuL0cSrrouOOvv-E
zb0p=SC{QomGTM6C?&w}wy}_L)fEGW1rkx-AOz{Wur+uIF{oH6YB!lTs`#a?4J2Lsh
zpVLD!WHaFmF0kpJWZ?n(^RGz?d`wZjh8<qVd{wi}N~LmxC>ynKh#T2EZztm?@OH;=
z;?cq$v1RIc&E6_EKlmtk|G|&S_xC^i;KBX9{SS#tHM_MDEt))I4f>`0rA*}`;vU-_
zc#8j}%w+fEn^38evSu4?s+nzvY8~qq)r|EDeAFpPYMP}>?nx1PCN;-sl}TCCtKG8O
ztG{!Hza7x*t#Disz)A%&hwoO>YC-GPe~=ik{sH-kR;g4D)n-O>y49@v%AL1UA{&eu
z9{!IvJ4P#j*CGA!12F1<&G_!?=Z>^ChQ4a0f#l^9(eY5mdSzmK)m+pZtu0-EA!%*n
zb}=!MG-JJ0OpF4u7tp(3Y|i%Y(tz9zs1NTN$o?1Y+<*3c`gD&5#1Hnyvvc<GM$OU(
z^H0ZH^=fzTmweu_QGcI}0($qC!K6-uSI?bExb(r^V;J)ed9V{ue?lji8qmpDZ|#!@
z`vLVI?7@pSBNe`R1NAQ1gX_IVgFSa;GS0a(Y_^>TyAY_Bdb+zGJmMMRKrHz;M(J3$
z9_&uOQdU;mbh`2dS8DQvJ2y(r(u2bZr!!!Q@mA=1Z@f{jb|;R`ee``;7U4@bRffGq
z8o~l0l4n)Bg&dQJL<rnOtJB`5)6D@4IXJv0dG@wN)XR6?f-S@|Dyt;gEnr2$<aB#h
zTG&9WrX3m(e+yWx+#um5V(tI?2ltQ42j}*YaL?i!2w{Yg1=v8EHoVUxj4immMmO97
ztfnozUp$Mfo0e6+yZ;}Duq#QH-tNHb!MB8S17m`SNvl(9m90*#S++VYD7Bhp%ii|N
z?#Z_&^skb4nPoU0wP5aAFgGoD1L>Z;I-z&cf}P9SG;WoxO-C!MCBBCbvV9m6fekw6
zQj5QftY$1nYYWE$;=8ZFvC-&@e|hSIBJd&Lpwthuck4g!X~2i_`P@M5hr@wZmJImd
z?)%htUxy$=s(#0UJy-}E{PzLj0dsxM6mDv~x35+gEUn{Y;~~U-C98AedSQ)Kp1etj
zoQz)Dp8kCeZ!6)2W%L(SM@Qrd2-&n{wp(GbXqoM1SbVKz#I0|%jJS2qs%}Sp7p>}c
zGl0tkA}F*VPym+;1W;%}ko(XK1<*o;9|rY6eF|-G{65qvZ!GXe`q60%lv;4CHPEN}
z&`cz&v$lHr&iWJ?P=OK<Io{eTW0u+Wo<M&#ZB@6U;#|vUdjVvI!vP;gb=wPIoNy?*
zLLfUW-ng#5YT4Ts9lrnp%+C0t`sz2WO{beW*Xa-@U2qR(T^LfmW*O_1#JP@q`i5QH
zo|UoeN+n#Km5(c6sNv5R<m34E8YTtY0>od0VNOML*cOH`W?nDArjdlvLG|G9o;u@!
z!NV|kOO$TqG#&gp;P0Mp^)%h;KO)`QX}Yz)YdZBhto!JG2QK0v-1<-`t{e_>{R5VO
z@@iO4LV4{_*u&T2Jsh@F*oYKsHr!*wU$4NgHr8|O*N^igA@jA(=RR%F7xD|4N{{n|
zhD+lwG9fSI(|VpSl0WgEdqhizkr6zb=6?x-eh4CsMx+0Y4*d^0@@zEv&DY_PXWu`!
z_q>}37vjIclN#|);&<n6PfImyBm5Q4m<vI>a1SfN4$X3c1(h@93{{SZ+gnfBO}oP<
z=~xh3Gbh{+pO5uSekRj}Bcgok4jf(cikTFp1j^UFqLg9@mf*GT!Vv&b{`u@OxV^XP
zx3%!=QvRl4ZB;6K7LSiuNh@i%>sIQQ@4%g|(q800rRn9IO2w1Bs@@@P?+vH&PFRt_
z=T*sbR&+Ath1IyQ1_igbp*r=m&dYE=Z+31bn9pQIOYtP`$v?fFgf!3-j#7BaCE1;u
zHTclrLzhU|?%Xt>T!)Vad^9DucYUL#d-3v5Cc`rqeR)}6vkmGKpHSMMcgbBs>9qub
z3-YS9xpk_VDIt}_(%0a}L~d`jWp`KN*s1X5gQC6)%Qxu9$&ZtgbnTc&Ueh+MQ%#!k
zRg5(SHr=2vk}r}(dPU*4D7|lVy4vY*a?x!Jj~$SJQ0W?>FrFEwUX4zC0?s8g)S`wY
QStg1MH&$xKGb$_OuW}xhhyVZp
literal 0
HcmV?d00001
diff --git a/tests/data/acpi/x86/q35/SRAT.spmem b/tests/data/acpi/x86/q35/SRAT.spmem
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..9ecd266eda68d035f72f170cb1346b97da623c28 100644
GIT binary patch
literal 384
zcmWFzatvu;WME+AbMklg2v%^42yj+VP*7lGU|;~TK{N=%fdDF>Q3Eam1za!&iavxo
y0jT^3c9=qB^AYOs1L~_oxB|-JfVlu6!@$6(!2}cLAX1$GR2|#~W|#>K3=9Att_cAE
literal 0
HcmV?d00001
--
MST
^ permalink raw reply related [flat|nested] 46+ messages in thread
* [PULL 14/44] tests/qtest: add e820 fw_cfg test
2026-07-05 14:36 [PULL 00/44] pci, vhost, virtio, iommu: features, fixes, cleanups Michael S. Tsirkin
` (12 preceding siblings ...)
2026-07-05 14:35 ` [PULL 13/44] tests/acpi: generate expected blobs for sp-mem SRAT test Michael S. Tsirkin
@ 2026-07-05 14:35 ` Michael S. Tsirkin
2026-07-05 14:35 ` [PULL 15/44] tests/qtest: cover sp-mem SOFT_RESERVED e820 entry Michael S. Tsirkin
` (30 subsequent siblings)
44 siblings, 0 replies; 46+ messages in thread
From: Michael S. Tsirkin @ 2026-07-05 14:35 UTC (permalink / raw)
To: qemu-devel
Cc: Peter Maydell, fanhuang, Igor Mammedov, Fabiano Rosas,
Laurent Vivier, Paolo Bonzini
From: fanhuang <FangSheng.Huang@amd.com>
Add a qtest that reads the "etc/e820" fw_cfg table and checks its
structural invariants: the file is a whole number of e820 entries and
every entry has a non-zero length. The baseline q35 case asserts the
guest sees RAM and, with no sp-mem device, no SOFT_RESERVED range.
Signed-off-by: FangSheng Huang <FangSheng.Huang@amd.com>
Acked-by: Igor Mammedov <imammedo@redhat.com>
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
Message-ID: <20260623075051.3797975-10-FangSheng.Huang@amd.com>
---
tests/qtest/e820-test.c | 95 +++++++++++++++++++++++++++++++++++++++++
tests/qtest/meson.build | 1 +
2 files changed, 96 insertions(+)
create mode 100644 tests/qtest/e820-test.c
diff --git a/tests/qtest/e820-test.c b/tests/qtest/e820-test.c
new file mode 100644
index 0000000000..1db0744c08
--- /dev/null
+++ b/tests/qtest/e820-test.c
@@ -0,0 +1,95 @@
+/*
+ * qtest e820 fw_cfg test case
+ *
+ * Validate the "etc/e820" fw_cfg table that QEMU hands to the firmware.
+ *
+ * Copyright (c) 2026 Advanced Micro Devices, Inc.
+ *
+ * Authors:
+ * FangSheng Huang <FangSheng.Huang@amd.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "qemu/osdep.h"
+
+#include "libqtest.h"
+#include "libqos/fw_cfg.h"
+#include "qemu/bswap.h"
+
+/* e820 entry layout and types (cf. hw/i386/e820_memory_layout.h) */
+#define E820_RAM 1
+#define E820_SOFT_RESERVED 0xefffffff
+
+struct e820_entry {
+ uint64_t address;
+ uint64_t length;
+ uint32_t type;
+} QEMU_PACKED;
+
+#define E820_MAX_ENTRIES 128
+
+/*
+ * Read and structurally validate "etc/e820": the file is a packed array
+ * of struct e820_entry, so its size must be a whole multiple of the entry
+ * size and every entry must have a non-zero length. Returns the entry
+ * count and fills @table.
+ */
+static size_t get_e820_table(QFWCFG *fw_cfg, struct e820_entry *table)
+{
+ size_t filesize, n, i;
+
+ filesize = qfw_cfg_get_file(fw_cfg, "etc/e820", table,
+ E820_MAX_ENTRIES * sizeof(*table));
+ g_assert_cmpint(filesize, >, 0);
+ g_assert_cmpint(filesize % sizeof(struct e820_entry), ==, 0);
+
+ n = filesize / sizeof(struct e820_entry);
+ g_assert_cmpint(n, <=, E820_MAX_ENTRIES);
+
+ for (i = 0; i < n; i++) {
+ g_assert_cmpint(le64_to_cpu(table[i].length), >, 0);
+ }
+
+ return n;
+}
+
+static void test_e820_basic(void)
+{
+ struct e820_entry table[E820_MAX_ENTRIES];
+ QFWCFG *fw_cfg;
+ QTestState *s;
+ size_t n, i;
+ bool found_ram = false, found_soft_reserved = false;
+
+ s = qtest_init("-machine q35 -m 256M");
+ fw_cfg = pc_fw_cfg_init(s);
+
+ n = get_e820_table(fw_cfg, table);
+ for (i = 0; i < n; i++) {
+ switch (le32_to_cpu(table[i].type)) {
+ case E820_RAM:
+ found_ram = true;
+ break;
+ case E820_SOFT_RESERVED:
+ found_soft_reserved = true;
+ break;
+ }
+ }
+
+ /* baseline: RAM present, no SOFT_RESERVED range */
+ g_assert_true(found_ram);
+ g_assert_false(found_soft_reserved);
+
+ pc_fw_cfg_uninit(fw_cfg);
+ qtest_quit(s);
+}
+
+int main(int argc, char **argv)
+{
+ g_test_init(&argc, &argv, NULL);
+
+ qtest_add_func("e820/basic", test_e820_basic);
+
+ return g_test_run();
+}
diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build
index 5e18b947c7..93175baa87 100644
--- a/tests/qtest/meson.build
+++ b/tests/qtest/meson.build
@@ -58,6 +58,7 @@ qtests_i386 = \
(config_all_devices.has_key('CONFIG_AHCI_ICH9') ? ['tco-test'] : []) + \
(config_all_devices.has_key('CONFIG_FDC_ISA') ? ['fdc-test'] : []) + \
(config_all_devices.has_key('CONFIG_I440FX') ? ['fw_cfg-test'] : []) + \
+ (config_all_devices.has_key('CONFIG_Q35') ? ['e820-test'] : []) + \
(config_all_devices.has_key('CONFIG_FW_CFG_DMA') ? ['vmcoreinfo-test'] : []) + \
(config_all_devices.has_key('CONFIG_Q35') ? ['dump-test'] : []) + \
(config_all_devices.has_key('CONFIG_I440FX') ? ['i440fx-test'] : []) + \
--
MST
^ permalink raw reply related [flat|nested] 46+ messages in thread
* [PULL 15/44] tests/qtest: cover sp-mem SOFT_RESERVED e820 entry
2026-07-05 14:36 [PULL 00/44] pci, vhost, virtio, iommu: features, fixes, cleanups Michael S. Tsirkin
` (13 preceding siblings ...)
2026-07-05 14:35 ` [PULL 14/44] tests/qtest: add e820 fw_cfg test Michael S. Tsirkin
@ 2026-07-05 14:35 ` Michael S. Tsirkin
2026-07-05 14:35 ` [PULL 16/44] intel_iommu: Correctly set pt bit in extended capability register Michael S. Tsirkin
` (29 subsequent siblings)
44 siblings, 0 replies; 46+ messages in thread
From: Michael S. Tsirkin @ 2026-07-05 14:35 UTC (permalink / raw)
To: qemu-devel
Cc: Peter Maydell, fanhuang, Igor Mammedov, Fabiano Rosas,
Laurent Vivier, Paolo Bonzini
From: fanhuang <FangSheng.Huang@amd.com>
Boot one sp-mem device and assert the guest's e820 table gains exactly
one E820_SOFT_RESERVED range whose length matches the device's backend
size.
Signed-off-by: FangSheng Huang <FangSheng.Huang@amd.com>
Acked-by: Igor Mammedov <imammedo@redhat.com>
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
Message-ID: <20260623075051.3797975-11-FangSheng.Huang@amd.com>
---
tests/qtest/e820-test.c | 34 ++++++++++++++++++++++++++++++++++
1 file changed, 34 insertions(+)
diff --git a/tests/qtest/e820-test.c b/tests/qtest/e820-test.c
index 1db0744c08..aafa3c1aa2 100644
--- a/tests/qtest/e820-test.c
+++ b/tests/qtest/e820-test.c
@@ -16,6 +16,7 @@
#include "libqtest.h"
#include "libqos/fw_cfg.h"
#include "qemu/bswap.h"
+#include "qemu/units.h"
/* e820 entry layout and types (cf. hw/i386/e820_memory_layout.h) */
#define E820_RAM 1
@@ -85,11 +86,44 @@ static void test_e820_basic(void)
qtest_quit(s);
}
+static void test_e820_sp_mem(void)
+{
+ struct e820_entry table[E820_MAX_ENTRIES];
+ QFWCFG *fw_cfg;
+ QTestState *s;
+ size_t n, i;
+ int soft_reserved = 0;
+ uint64_t soft_reserved_len = 0;
+
+ s = qtest_init("-machine q35 -m 256M,slots=2,maxmem=2G "
+ "-object memory-backend-ram,id=ram0,size=256M "
+ "-numa node,nodeid=0,memdev=ram0 "
+ "-object memory-backend-ram,id=spm0,size=128M "
+ "-device sp-mem,id=sp0,memdev=spm0,node=0");
+ fw_cfg = pc_fw_cfg_init(s);
+
+ n = get_e820_table(fw_cfg, table);
+ for (i = 0; i < n; i++) {
+ if (le32_to_cpu(table[i].type) == E820_SOFT_RESERVED) {
+ soft_reserved++;
+ soft_reserved_len = le64_to_cpu(table[i].length);
+ }
+ }
+
+ /* exactly one SOFT_RESERVED range, sized to the backend */
+ g_assert_cmpint(soft_reserved, ==, 1);
+ g_assert_cmpint(soft_reserved_len, ==, 128 * MiB);
+
+ pc_fw_cfg_uninit(fw_cfg);
+ qtest_quit(s);
+}
+
int main(int argc, char **argv)
{
g_test_init(&argc, &argv, NULL);
qtest_add_func("e820/basic", test_e820_basic);
+ qtest_add_func("e820/sp-mem", test_e820_sp_mem);
return g_test_run();
}
--
MST
^ permalink raw reply related [flat|nested] 46+ messages in thread
* [PULL 16/44] intel_iommu: Correctly set pt bit in extended capability register
2026-07-05 14:36 [PULL 00/44] pci, vhost, virtio, iommu: features, fixes, cleanups Michael S. Tsirkin
` (14 preceding siblings ...)
2026-07-05 14:35 ` [PULL 15/44] tests/qtest: cover sp-mem SOFT_RESERVED e820 entry Michael S. Tsirkin
@ 2026-07-05 14:35 ` Michael S. Tsirkin
2026-07-05 14:35 ` [PULL 17/44] vhost-user.rst: clarify when rings are started Michael S. Tsirkin
` (28 subsequent siblings)
44 siblings, 0 replies; 46+ messages in thread
From: Michael S. Tsirkin @ 2026-07-05 14:35 UTC (permalink / raw)
To: qemu-devel
Cc: Peter Maydell, no92, Clement Mathieu--Drif, Yi Liu, Jason Wang,
Paolo Bonzini, Richard Henderson
From: no92 <leo@managarm.org>
With the changes in c7b2e22bd957, the `pt` bit was set in the (wrong)
capability register, instead of the (correct) extended capability
register.
Fixes: c7b2e22bd957 ("hw/i386/x86-iommu: Remove X86IOMMUState::pt_supported field")
Signed-off-by: no92 <leo@managarm.org>
Reviewed-by: Clement Mathieu--Drif <clement.mathieu--drif@bull.com>
Reviewed-by: Yi Liu <yi.l.liu@intel.com>
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
Message-ID: <20260624103933.1793586-3-leo@managarm.org>
---
hw/i386/intel_iommu.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/hw/i386/intel_iommu.c b/hw/i386/intel_iommu.c
index 744cdfd2e6..d1af7a3135 100644
--- a/hw/i386/intel_iommu.c
+++ b/hw/i386/intel_iommu.c
@@ -4988,7 +4988,7 @@ static void vtd_cap_init(IntelIOMMUState *s)
{
X86IOMMUState *x86_iommu = X86_IOMMU_DEVICE(s);
- s->cap = VTD_CAP_FRO | VTD_CAP_NFR | VTD_CAP_ND | VTD_ECAP_PT |
+ s->cap = VTD_CAP_FRO | VTD_CAP_NFR | VTD_CAP_ND |
VTD_CAP_MAMV | VTD_CAP_PSI | VTD_CAP_SSLPS | VTD_CAP_DRAIN |
VTD_CAP_ESRTPS | VTD_CAP_MGAW(s->aw_bits);
if (x86_iommu->dma_translation) {
@@ -4999,7 +4999,7 @@ static void vtd_cap_init(IntelIOMMUState *s)
s->cap |= VTD_CAP_SAGAW_48bit;
}
}
- s->ecap = VTD_ECAP_QI | VTD_ECAP_IRO;
+ s->ecap = VTD_ECAP_QI | VTD_ECAP_IRO | VTD_ECAP_PT;
if (x86_iommu_ir_supported(x86_iommu)) {
s->ecap |= VTD_ECAP_IR | VTD_ECAP_MHMV;
--
MST
^ permalink raw reply related [flat|nested] 46+ messages in thread
* [PULL 17/44] vhost-user.rst: clarify when rings are started
2026-07-05 14:36 [PULL 00/44] pci, vhost, virtio, iommu: features, fixes, cleanups Michael S. Tsirkin
` (15 preceding siblings ...)
2026-07-05 14:35 ` [PULL 16/44] intel_iommu: Correctly set pt bit in extended capability register Michael S. Tsirkin
@ 2026-07-05 14:35 ` Michael S. Tsirkin
2026-07-05 14:35 ` [PULL 18/44] libvhost-user: look for available vq buffers upon SET_VRING_KICK Michael S. Tsirkin
` (27 subsequent siblings)
44 siblings, 0 replies; 46+ messages in thread
From: Michael S. Tsirkin @ 2026-07-05 14:35 UTC (permalink / raw)
To: qemu-devel
Cc: Peter Maydell, Stefan Hajnoczi, Jorge Moreira,
Michael S . Tsirkin, Stefano Garzarella, Pierrick Bouvier
From: Stefan Hajnoczi <stefanha@redhat.com>
Jorge Moreira <jemoreira@google.com> pointed out that the ring state
machine is underspecified. In the discussion that followed, we
discovered that the spec says one thing and implementations do something
else. This patch updates the spec to reflect how things are actually
implemented across widely-used front-ends and back-ends including QEMU,
crosvm, rust-vmm, and DPDK. Do this while taking care not to make any
other existing implementations non-compliant by changing the spec.
The spec says rings are started when a kick is received but the
implementations actually start rings when VHOST_USER_SET_VRING_KICK is
received.
Reconcile this as follows:
- Clarify that a ring can be stopped and then started again. The
back-end must resume processing available requests when the ring is
restarted.
- Update the spec to say rings are started when
VHOST_USER_SET_VRING_KICK is received.
- Ensure compatibility by saying front-ends SHOULD inject a kick in case
the back-end strictly implemented the old spec.
- Avoid future back-end dependencies on injected kicks by saying that
back-ends SHOULD NOT expect a kick to start rings.
This way implementors have clarity on how things work while still
allowing compatibility for existing implementations.
Reported-by: Jorge Moreira <jemoreira@google.com>
Cc: "Michael S . Tsirkin" <mst@redhat.com>
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
Reviewed-by: Stefano Garzarella <sgarzare@redhat.com>
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
Message-ID: <20260604201029.250450-2-stefanha@redhat.com>
---
docs/interop/vhost-user.rst | 26 ++++++++++++++++++++------
1 file changed, 20 insertions(+), 6 deletions(-)
diff --git a/docs/interop/vhost-user.rst b/docs/interop/vhost-user.rst
index c83ae2accb..a704f3cb53 100644
--- a/docs/interop/vhost-user.rst
+++ b/docs/interop/vhost-user.rst
@@ -518,12 +518,26 @@ Rings have two independent states: started/stopped, and enabled/disabled.
* started and enabled: The back-end must process the ring normally, i.e.
process all requests and execute them.
-Each ring is initialized in a stopped and disabled state. The back-end
-must start a ring upon receiving a kick (that is, detecting that file
-descriptor is readable) on the descriptor specified by
-``VHOST_USER_SET_VRING_KICK`` or receiving the in-band message
-``VHOST_USER_VRING_KICK`` if negotiated, and stop a ring upon receiving
-``VHOST_USER_GET_VRING_BASE``.
+Each ring is initialized in a stopped and disabled state. Rings are started
+with ``VHOST_USER_SET_VRING_KICK`` (or ``VHOST_USER_VRING_KICK`` if
+``VHOST_USER_PROTOCOL_F_INBAND_NOTIFICATIONS`` is negotiated) and stopped with
+``VHOST_USER_GET_VRING_BASE``. A stopped ring enters the started state again
+with ``VHOST_USER_SET_VRING_KICK`` (or ``VHOST_USER_VRING_KICK`` if
+``VHOST_USER_PROTOCOL_F_INBAND_NOTIFICATIONS`` is negotiated) and the back-end
+resumes processing requests.
+
+Note that previous versions of this specification stated that rings start when
+the back-end receives a kick (that is, detecting that file descriptor is
+readable) on the descriptor specified by ``VHOST_USER_SET_VRING_KICK`` or
+receiving the in-band message ``VHOST_USER_VRING_KICK`` if negotiated.
+Widely-used front-ends and back-ends did not implement this behavior and it
+complicates poll mode back-ends that do not rely on the kick file descriptor.
+
+For compatibility with back-ends that implemented the start on kick behavior,
+front-ends SHOULD inject a kick after ``VHOST_USER_SET_VRING_KICK``. This
+ensures that the back-end processes any available requests in the ring.
+Back-ends SHOULD NOT rely on receiving a kick after
+``VHOST_USER_SET_VRING_KICK``.
Rings can be enabled or disabled by ``VHOST_USER_SET_VRING_ENABLE``.
--
MST
^ permalink raw reply related [flat|nested] 46+ messages in thread
* [PULL 18/44] libvhost-user: look for available vq buffers upon SET_VRING_KICK
2026-07-05 14:36 [PULL 00/44] pci, vhost, virtio, iommu: features, fixes, cleanups Michael S. Tsirkin
` (16 preceding siblings ...)
2026-07-05 14:35 ` [PULL 17/44] vhost-user.rst: clarify when rings are started Michael S. Tsirkin
@ 2026-07-05 14:35 ` Michael S. Tsirkin
2026-07-05 14:35 ` [PULL 19/44] vhost-user: inject kick after SET_VRING_KICK Michael S. Tsirkin
` (26 subsequent siblings)
44 siblings, 0 replies; 46+ messages in thread
From: Michael S. Tsirkin @ 2026-07-05 14:35 UTC (permalink / raw)
To: qemu-devel; +Cc: Peter Maydell, Stefan Hajnoczi, Stefano Garzarella
From: Stefan Hajnoczi <stefanha@redhat.com>
When a vring is started the back-end must look for available vq buffers
and process them. This scenario can happen if the back-end is stopped
with unprocessed available buffers and then started again.
The inflight I/O tracking code already did this, but it should also be
done when inflight I/O tracking is not enabled.
Move the code and make it robust in case of EINTR or EAGAIN.
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
Reviewed-by: Stefano Garzarella <sgarzare@redhat.com>
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
Message-ID: <20260604201029.250450-3-stefanha@redhat.com>
---
subprojects/libvhost-user/libvhost-user.c | 19 ++++++++++++++-----
1 file changed, 14 insertions(+), 5 deletions(-)
diff --git a/subprojects/libvhost-user/libvhost-user.c b/subprojects/libvhost-user/libvhost-user.c
index d2df50e3d0..2c35bddd6f 100644
--- a/subprojects/libvhost-user/libvhost-user.c
+++ b/subprojects/libvhost-user/libvhost-user.c
@@ -1390,11 +1390,6 @@ vu_check_queue_inflights(VuDev *dev, VuVirtq *vq)
vq->counter = vq->resubmit_list[0].counter + 1;
}
- /* in case of I/O hang after reconnecting */
- if (eventfd_write(vq->kick_fd, 1)) {
- return -1;
- }
-
return 0;
}
@@ -1436,6 +1431,20 @@ vu_set_vring_kick_exec(VuDev *dev, VhostUserMsg *vmsg)
vu_panic(dev, "Failed to check inflights for vq: %d\n", index);
}
+ /* Inject a kick to look for available vq buffers */
+ if (dev->vq[index].kick_fd != -1) {
+ int ret;
+
+ do {
+ ret = eventfd_write(dev->vq[index].kick_fd, 1);
+ } while (ret != 0 && errno == EINTR);
+
+ if (ret != 0 && errno != EAGAIN /* already readable */) {
+ vu_panic(dev, "Failed to inject kick during SET_VRING_KICK "
+ "on vq: %d with error: %m\n", index);
+ }
+ }
+
return false;
}
--
MST
^ permalink raw reply related [flat|nested] 46+ messages in thread
* [PULL 19/44] vhost-user: inject kick after SET_VRING_KICK
2026-07-05 14:36 [PULL 00/44] pci, vhost, virtio, iommu: features, fixes, cleanups Michael S. Tsirkin
` (17 preceding siblings ...)
2026-07-05 14:35 ` [PULL 18/44] libvhost-user: look for available vq buffers upon SET_VRING_KICK Michael S. Tsirkin
@ 2026-07-05 14:35 ` Michael S. Tsirkin
2026-07-05 14:35 ` [PULL 20/44] hw/char/virtio-serial-bus: fix guest-triggerable OOM in control_out() Michael S. Tsirkin
` (25 subsequent siblings)
44 siblings, 0 replies; 46+ messages in thread
From: Michael S. Tsirkin @ 2026-07-05 14:35 UTC (permalink / raw)
To: qemu-devel; +Cc: Peter Maydell, Stefan Hajnoczi, Stefano Garzarella
From: Stefan Hajnoczi <stefanha@redhat.com>
The vhost-user specification was updated to say that front-ends should
inject a kick after SET_VRING_KICK in case the back-end implements the
old spec wording which said vrings start when a kick is received. Do
this in QEMU's front-end.
An example scenario where this behavior helps: the back-end fails to
check if the vring has available buffers when SET_VRING_KICK is received
and the front-end stopped and then restarted the vring. In the case the
back-end may not notice the available buffers unless the front-end
injects a kick.
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
Reviewed-by: Stefano Garzarella <sgarzare@redhat.com>
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
Message-ID: <20260604201029.250450-4-stefanha@redhat.com>
---
hw/virtio/vhost-user.c | 24 +++++++++++++++++++++++-
1 file changed, 23 insertions(+), 1 deletion(-)
diff --git a/hw/virtio/vhost-user.c b/hw/virtio/vhost-user.c
index d627351f45..517cc4ca71 100644
--- a/hw/virtio/vhost-user.c
+++ b/hw/virtio/vhost-user.c
@@ -1487,7 +1487,29 @@ static int vhost_set_vring_file(struct vhost_dev *dev,
static int vhost_user_set_vring_kick(struct vhost_dev *dev,
struct vhost_vring_file *file)
{
- return vhost_set_vring_file(dev, VHOST_USER_SET_VRING_KICK, file);
+ int ret = vhost_set_vring_file(dev, VHOST_USER_SET_VRING_KICK, file);
+ if (ret < 0) {
+ return ret;
+ }
+
+ /*
+ * Inject a kick in case the back-end only starts vring processing upon
+ * receiving a kick. The spec suggests this to improve compatibility.
+ */
+ if (file->fd != -1) {
+ uint64_t val = 1;
+ ssize_t nwritten;
+
+ do {
+ nwritten = write(file->fd, &val, sizeof(val));
+ } while (nwritten < 0 && errno == EINTR);
+
+ if (nwritten < 0 && errno != EAGAIN /* back-end can already read */) {
+ return -errno;
+ }
+ }
+
+ return 0;
}
static int vhost_user_set_vring_call(struct vhost_dev *dev,
--
MST
^ permalink raw reply related [flat|nested] 46+ messages in thread
* [PULL 20/44] hw/char/virtio-serial-bus: fix guest-triggerable OOM in control_out()
2026-07-05 14:36 [PULL 00/44] pci, vhost, virtio, iommu: features, fixes, cleanups Michael S. Tsirkin
` (18 preceding siblings ...)
2026-07-05 14:35 ` [PULL 19/44] vhost-user: inject kick after SET_VRING_KICK Michael S. Tsirkin
@ 2026-07-05 14:35 ` Michael S. Tsirkin
2026-07-05 14:35 ` [PULL 21/44] tests/qtest/libqos: share Intel IOMMU test setup helpers Michael S. Tsirkin
` (24 subsequent siblings)
44 siblings, 0 replies; 46+ messages in thread
From: Michael S. Tsirkin @ 2026-07-05 14:35 UTC (permalink / raw)
To: qemu-devel
Cc: Peter Maydell, Laurent Vivier, qemu-stable, Amit Shah,
Marc-André Lureau, Paolo Bonzini
From: Laurent Vivier <lvivier@redhat.com>
A malicious guest can craft virtqueue descriptors with arbitrary lengths.
control_out() calls iov_size() on the guest-supplied scatter-gather list
and passes the result directly to g_malloc(), allowing a guest to force
QEMU to attempt multi-gigabyte allocations and crash the host process.
Fix this by copying at most sizeof(struct virtio_console_control) into a
stack-local variable instead of allocating a buffer sized by the guest.
handle_control_message() only accesses the fixed-size id, event, and
value fields, so no data beyond the struct was ever needed.
Cc: qemu-stable@nongnu.org
Resolves: https://gitlab.com/qemu-project/qemu/-/issues/3585
Signed-off-by: Laurent Vivier <lvivier@redhat.com>
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
Message-ID: <20260622161144.2883799-1-lvivier@redhat.com>
---
hw/char/virtio-serial-bus.c | 34 +++++++---------------------------
1 file changed, 7 insertions(+), 27 deletions(-)
diff --git a/hw/char/virtio-serial-bus.c b/hw/char/virtio-serial-bus.c
index cd234dc6db..c1973f0248 100644
--- a/hw/char/virtio-serial-bus.c
+++ b/hw/char/virtio-serial-bus.c
@@ -344,22 +344,16 @@ void virtio_serial_throttle_port(VirtIOSerialPort *port, bool throttle)
}
/* Guest wants to notify us of some event */
-static void handle_control_message(VirtIOSerial *vser, void *buf, size_t len)
+static void handle_control_message(VirtIOSerial *vser,
+ struct virtio_console_control *gcpkt)
{
VirtIODevice *vdev = VIRTIO_DEVICE(vser);
struct VirtIOSerialPort *port;
VirtIOSerialPortClass *vsc;
- struct virtio_console_control cpkt, *gcpkt;
+ struct virtio_console_control cpkt;
uint8_t *buffer;
size_t buffer_len;
- gcpkt = buf;
-
- if (len < sizeof(cpkt)) {
- /* The guest sent an invalid control packet */
- return;
- }
-
cpkt.event = virtio_lduw_p(vdev, &gcpkt->event);
cpkt.value = virtio_lduw_p(vdev, &gcpkt->value);
@@ -457,41 +451,27 @@ static void control_in(VirtIODevice *vdev, VirtQueue *vq)
static void control_out(VirtIODevice *vdev, VirtQueue *vq)
{
+ struct virtio_console_control cpkt;
VirtQueueElement *elem;
VirtIOSerial *vser;
- uint8_t *buf;
size_t len;
vser = VIRTIO_SERIAL(vdev);
- len = 0;
- buf = NULL;
for (;;) {
- size_t cur_len;
-
elem = virtqueue_pop(vq, sizeof(VirtQueueElement));
if (!elem) {
break;
}
- cur_len = iov_size(elem->out_sg, elem->out_num);
- /*
- * Allocate a new buf only if we didn't have one previously or
- * if the size of the buf differs
- */
- if (cur_len > len) {
- g_free(buf);
-
- buf = g_malloc(cur_len);
- len = cur_len;
+ len = iov_to_buf(elem->out_sg, elem->out_num, 0, &cpkt, sizeof(cpkt));
+ if (len == sizeof(cpkt)) {
+ handle_control_message(vser, &cpkt);
}
- iov_to_buf(elem->out_sg, elem->out_num, 0, buf, cur_len);
- handle_control_message(vser, buf, cur_len);
virtqueue_push(vq, elem, 0);
g_free(elem);
}
- g_free(buf);
virtio_notify(vdev, vq);
}
--
MST
^ permalink raw reply related [flat|nested] 46+ messages in thread
* [PULL 21/44] tests/qtest/libqos: share Intel IOMMU test setup helpers
2026-07-05 14:36 [PULL 00/44] pci, vhost, virtio, iommu: features, fixes, cleanups Michael S. Tsirkin
` (19 preceding siblings ...)
2026-07-05 14:35 ` [PULL 20/44] hw/char/virtio-serial-bus: fix guest-triggerable OOM in control_out() Michael S. Tsirkin
@ 2026-07-05 14:35 ` Michael S. Tsirkin
2026-07-05 14:35 ` [PULL 22/44] tests/qtest/libqos: add Intel IOMMU invalidation helpers Michael S. Tsirkin
` (23 subsequent siblings)
44 siblings, 0 replies; 46+ messages in thread
From: Michael S. Tsirkin @ 2026-07-05 14:35 UTC (permalink / raw)
To: qemu-devel
Cc: Peter Maydell, Junjie Cao, Jason Wang, Yi Liu,
Clément Mathieu--Drif, Fabiano Rosas, Laurent Vivier,
Paolo Bonzini, Tao Tang
From: Junjie Cao <junjie.cao@intel.com>
iommu-intel-test.c keeps the iommu-testdev PCI setup (save_fn(),
setup_qtest_pci_device()) and the VT-d command-line / capability
helpers (qvtd_iommu_args(), qvtd_check_caps()) as file-local statics.
A second Intel IOMMU test would have to copy them, which defeats the
purpose of the shared qos-intel-iommu module.
Move them into qos-intel-iommu so sibling tests can reuse them:
save_fn() becomes qvtd_save_pci_dev() and setup_qtest_pci_device()
becomes qvtd_setup_qtest_pci_device(); qvtd_iommu_args() and
qvtd_check_caps() keep their names.
No functional change: iommu-intel-test now calls the public
qvtd_setup_qtest_pci_device() instead of its file-local copy.
Signed-off-by: Junjie Cao <junjie.cao@intel.com>
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
Message-ID: <20260703072200.463082-2-junjie.cao@intel.com>
---
tests/qtest/libqos/qos-intel-iommu.h | 23 ++++++++
tests/qtest/iommu-intel-test.c | 78 +---------------------------
tests/qtest/libqos/qos-intel-iommu.c | 77 +++++++++++++++++++++++++++
3 files changed, 101 insertions(+), 77 deletions(-)
diff --git a/tests/qtest/libqos/qos-intel-iommu.h b/tests/qtest/libqos/qos-intel-iommu.h
index c6cacc5c3f..dd6f920464 100644
--- a/tests/qtest/libqos/qos-intel-iommu.h
+++ b/tests/qtest/libqos/qos-intel-iommu.h
@@ -182,4 +182,27 @@ void qvtd_run_translation_case(QTestState *qts, QPCIDevice *dev,
QPCIBar bar, uint64_t iommu_base,
const QVTDTestConfig *cfg);
+/*
+ * qvtd_iommu_args - Build the -device intel-iommu command-line fragment
+ * for the requested translation mode.
+ */
+const char *qvtd_iommu_args(QVTDTransMode mode);
+
+/*
+ * qvtd_check_caps - Check whether the running QEMU exposes the ECAP bits
+ * required by @mode. Calls g_test_skip() and returns
+ * false when a required capability is missing.
+ */
+bool qvtd_check_caps(QTestState *qts, QVTDTransMode mode);
+
+/*
+ * qvtd_setup_qtest_pci_device - Create a QPCIBus, locate the iommu-testdev
+ * PCI function, enable it and map BAR0.
+ *
+ * On success, *pcibus and *bar are populated and the returned QPCIDevice
+ * must be released by the caller via g_free().
+ */
+QPCIDevice *qvtd_setup_qtest_pci_device(QTestState *qts, QPCIBus **pcibus,
+ QPCIBar *bar);
+
#endif /* QTEST_LIBQOS_INTEL_IOMMU_H */
diff --git a/tests/qtest/iommu-intel-test.c b/tests/qtest/iommu-intel-test.c
index 703bbfef73..c2744def6f 100644
--- a/tests/qtest/iommu-intel-test.c
+++ b/tests/qtest/iommu-intel-test.c
@@ -24,82 +24,6 @@ static uint64_t intel_iommu_expected_gpa(uint64_t iova)
return (QVTD_PT_VAL & VTD_PAGE_MASK_4K) + (iova & 0xfff);
}
-static void save_fn(QPCIDevice *dev, int devfn, void *data)
-{
- QPCIDevice **pdev = (QPCIDevice **) data;
-
- *pdev = dev;
-}
-
-static QPCIDevice *setup_qtest_pci_device(QTestState *qts, QPCIBus **pcibus,
- QPCIBar *bar)
-{
- QPCIDevice *dev = NULL;
-
- *pcibus = qpci_new_pc(qts, NULL);
- g_assert(*pcibus != NULL);
-
- qpci_device_foreach(*pcibus, IOMMU_TESTDEV_VENDOR_ID,
- IOMMU_TESTDEV_DEVICE_ID, save_fn, &dev);
-
- g_assert(dev);
- qpci_device_enable(dev);
- *bar = qpci_iomap(dev, 0, NULL);
- g_assert_false(bar->is_io);
-
- return dev;
-}
-
-static const char *qvtd_iommu_args(QVTDTransMode mode)
-{
- switch (mode) {
- case QVTD_TM_SCALABLE_FLT:
- return "-device intel-iommu,scalable-mode=on,fsts=on ";
- case QVTD_TM_SCALABLE_PT:
- case QVTD_TM_SCALABLE_SLT:
- return "-device intel-iommu,scalable-mode=on ";
- default:
- return "-device intel-iommu ";
- }
-}
-
-static bool qvtd_check_caps(QTestState *qts, QVTDTransMode mode)
-{
- uint64_t ecap = qtest_readq(qts,
- Q35_HOST_BRIDGE_IOMMU_ADDR + DMAR_ECAP_REG);
-
- /* All scalable modes require SMTS */
- if (qvtd_is_scalable(mode) && !(ecap & VTD_ECAP_SMTS)) {
- g_test_skip("ECAP.SMTS not supported");
- return false;
- }
-
- switch (mode) {
- case QVTD_TM_SCALABLE_PT:
- if (!(ecap & VTD_ECAP_PT)) {
- g_test_skip("ECAP.PT not supported");
- return false;
- }
- break;
- case QVTD_TM_SCALABLE_SLT:
- if (!(ecap & VTD_ECAP_SSTS)) {
- g_test_skip("ECAP.SSTS not supported");
- return false;
- }
- break;
- case QVTD_TM_SCALABLE_FLT:
- if (!(ecap & VTD_ECAP_FSTS)) {
- g_test_skip("ECAP.FSTS not supported");
- return false;
- }
- break;
- default:
- break;
- }
-
- return true;
-}
-
static void run_intel_iommu_translation(const QVTDTestConfig *cfg)
{
QTestState *qts;
@@ -124,7 +48,7 @@ static void run_intel_iommu_translation(const QVTDTestConfig *cfg)
}
/* Setup and configure IOMMU-testdev PCI device */
- dev = setup_qtest_pci_device(qts, &pcibus, &bar);
+ dev = qvtd_setup_qtest_pci_device(qts, &pcibus, &bar);
g_assert(dev);
g_test_message("### Intel IOMMU translation mode=%d ###", cfg->trans_mode);
diff --git a/tests/qtest/libqos/qos-intel-iommu.c b/tests/qtest/libqos/qos-intel-iommu.c
index f8ca4c871b..4095ac0917 100644
--- a/tests/qtest/libqos/qos-intel-iommu.c
+++ b/tests/qtest/libqos/qos-intel-iommu.c
@@ -11,6 +11,7 @@
#include "qemu/osdep.h"
#include "hw/i386/intel_iommu_internal.h"
#include "tests/qtest/libqos/pci.h"
+#include "tests/qtest/libqos/pci-pc.h"
#include "qos-iommu-testdev.h"
#include "qos-intel-iommu.h"
@@ -452,3 +453,79 @@ void qvtd_run_translation_case(QTestState *qts, QPCIDevice *dev,
}
}
}
+
+const char *qvtd_iommu_args(QVTDTransMode mode)
+{
+ switch (mode) {
+ case QVTD_TM_SCALABLE_FLT:
+ return "-device intel-iommu,scalable-mode=on,fsts=on ";
+ case QVTD_TM_SCALABLE_PT:
+ case QVTD_TM_SCALABLE_SLT:
+ return "-device intel-iommu,scalable-mode=on ";
+ default:
+ return "-device intel-iommu ";
+ }
+}
+
+bool qvtd_check_caps(QTestState *qts, QVTDTransMode mode)
+{
+ uint64_t ecap = qtest_readq(qts,
+ Q35_HOST_BRIDGE_IOMMU_ADDR + DMAR_ECAP_REG);
+
+ /* All scalable modes require SMTS */
+ if (qvtd_is_scalable(mode) && !(ecap & VTD_ECAP_SMTS)) {
+ g_test_skip("ECAP.SMTS not supported");
+ return false;
+ }
+
+ switch (mode) {
+ case QVTD_TM_SCALABLE_PT:
+ if (!(ecap & VTD_ECAP_PT)) {
+ g_test_skip("ECAP.PT not supported");
+ return false;
+ }
+ break;
+ case QVTD_TM_SCALABLE_SLT:
+ if (!(ecap & VTD_ECAP_SSTS)) {
+ g_test_skip("ECAP.SSTS not supported");
+ return false;
+ }
+ break;
+ case QVTD_TM_SCALABLE_FLT:
+ if (!(ecap & VTD_ECAP_FSTS)) {
+ g_test_skip("ECAP.FSTS not supported");
+ return false;
+ }
+ break;
+ default:
+ break;
+ }
+
+ return true;
+}
+
+static void qvtd_save_pci_dev(QPCIDevice *dev, int devfn, void *data)
+{
+ QPCIDevice **pdev = (QPCIDevice **)data;
+
+ *pdev = dev;
+}
+
+QPCIDevice *qvtd_setup_qtest_pci_device(QTestState *qts, QPCIBus **pcibus,
+ QPCIBar *bar)
+{
+ QPCIDevice *dev = NULL;
+
+ *pcibus = qpci_new_pc(qts, NULL);
+ g_assert(*pcibus != NULL);
+
+ qpci_device_foreach(*pcibus, IOMMU_TESTDEV_VENDOR_ID,
+ IOMMU_TESTDEV_DEVICE_ID, qvtd_save_pci_dev, &dev);
+
+ g_assert(dev);
+ qpci_device_enable(dev);
+ *bar = qpci_iomap(dev, 0, NULL);
+ g_assert_false(bar->is_io);
+
+ return dev;
+}
--
MST
^ permalink raw reply related [flat|nested] 46+ messages in thread
* [PULL 22/44] tests/qtest/libqos: add Intel IOMMU invalidation helpers
2026-07-05 14:36 [PULL 00/44] pci, vhost, virtio, iommu: features, fixes, cleanups Michael S. Tsirkin
` (20 preceding siblings ...)
2026-07-05 14:35 ` [PULL 21/44] tests/qtest/libqos: share Intel IOMMU test setup helpers Michael S. Tsirkin
@ 2026-07-05 14:35 ` Michael S. Tsirkin
2026-07-05 14:35 ` [PULL 23/44] tests/qtest: add IOTLB invalidation test for Intel IOMMU Michael S. Tsirkin
` (22 subsequent siblings)
44 siblings, 0 replies; 46+ messages in thread
From: Michael S. Tsirkin @ 2026-07-05 14:35 UTC (permalink / raw)
To: qemu-devel
Cc: Peter Maydell, Junjie Cao, Tao Tang, Fabiano Rosas,
Laurent Vivier, Paolo Bonzini
From: Junjie Cao <junjie.cao@intel.com>
Add the building blocks a queued-invalidation test needs on top of the
existing translation helpers:
- qvtd_leaf_pte_addr() / qvtd_make_leaf_pte() let a test locate and
rewrite the leaf PTE built by qvtd_setup_translation_tables() without
re-deriving the page-table index or leaf attributes by hand. The
attributes reuse qvtd_get_fl_pte_attrs()/qvtd_get_pte_attrs() so the
first- and second-level leaf formats stay defined in one place.
- qvtd_submit_iotlb_global_inv() / _domain_inv() / _page_inv() write an
IOTLB Invalidation Descriptor (global / domain-selective /
page-selective) into the Invalidation Queue and advance IQT_REG.
- qvtd_submit_inv_wait_and_poll() submits an Invalidation Wait
Descriptor with Status Write and polls the status word with a bounded
retry loop, asserting on timeout.
No caller yet; used by the IOTLB invalidation test that follows.
Signed-off-by: Junjie Cao <junjie.cao@intel.com>
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
Message-ID: <20260703072200.463082-3-junjie.cao@intel.com>
---
tests/qtest/libqos/qos-intel-iommu.h | 51 +++++++++++++
tests/qtest/libqos/qos-intel-iommu.c | 108 +++++++++++++++++++++++++++
2 files changed, 159 insertions(+)
diff --git a/tests/qtest/libqos/qos-intel-iommu.h b/tests/qtest/libqos/qos-intel-iommu.h
index dd6f920464..3a97d14786 100644
--- a/tests/qtest/libqos/qos-intel-iommu.h
+++ b/tests/qtest/libqos/qos-intel-iommu.h
@@ -40,6 +40,16 @@
*/
#define QVTD_IQ_BASE (QVTD_MEM_BASE + 0x00020000)
#define QVTD_IQ_QS 0 /* QS=0 → 256 entries */
+#define QVTD_IQ_DESC_SIZE 16 /* 128-bit descriptor (iq_dw=0) */
+#define QVTD_IQT_SHIFT 4 /* IQT_REG[18:4] is the tail index */
+
+/*
+ * Invalidation Wait Descriptor with Status Write (VT-d 6.5.2.8).
+ * The IOMMU writes QVTD_INV_WAIT_DATA to QVTD_INV_WAIT_ADDR after all
+ * preceding invalidation descriptors complete.
+ */
+#define QVTD_INV_WAIT_ADDR (QVTD_MEM_BASE + 0x00040000)
+#define QVTD_INV_WAIT_DATA 0x1u
/*
* Fault Event MSI configuration.
@@ -205,4 +215,45 @@ bool qvtd_check_caps(QTestState *qts, QVTDTransMode mode);
QPCIDevice *qvtd_setup_qtest_pci_device(QTestState *qts, QPCIBus **pcibus,
QPCIBar *bar);
+/*
+ * qvtd_leaf_pte_addr - Address of the leaf (L1) PTE for @iova, assuming
+ * the hierarchy was built by
+ * qvtd_setup_translation_tables() with 4 KB leaves.
+ */
+uint64_t qvtd_leaf_pte_addr(uint64_t iova);
+
+/*
+ * qvtd_make_leaf_pte - Build a leaf PTE value mapping @pa, for the
+ * 4-level / 4 KB-leaf layout used by
+ * qvtd_setup_translation_tables().
+ * Selects FLT or SLT/legacy attributes from @mode.
+ */
+uint64_t qvtd_make_leaf_pte(uint64_t pa, QVTDTransMode mode);
+
+/*
+ * qvtd_submit_inv_wait_and_poll - Submit an Invalidation Wait Descriptor
+ * with Status Write and poll until the
+ * IOMMU writes the expected status data.
+ *
+ * Asserts on timeout. Returns the new tail index.
+ */
+uint32_t qvtd_submit_inv_wait_and_poll(QTestState *qts, uint64_t iommu_base,
+ uint32_t tail);
+
+/*
+ * qvtd_submit_iotlb_global_inv - global IOTLB invalidation.
+ * qvtd_submit_iotlb_domain_inv - domain-selective IOTLB invalidation.
+ * qvtd_submit_iotlb_page_inv - page-selective IOTLB invalidation;
+ * @addr must be 4 KB aligned. @am
+ * selects the address mask per VT-d
+ * 6.5.2.4 (am=0 → single 4 KB page).
+ */
+uint32_t qvtd_submit_iotlb_global_inv(QTestState *qts, uint64_t iommu_base,
+ uint32_t tail);
+uint32_t qvtd_submit_iotlb_domain_inv(QTestState *qts, uint64_t iommu_base,
+ uint16_t domain_id, uint32_t tail);
+uint32_t qvtd_submit_iotlb_page_inv(QTestState *qts, uint64_t iommu_base,
+ uint16_t domain_id, uint64_t addr,
+ uint8_t am, uint32_t tail);
+
#endif /* QTEST_LIBQOS_INTEL_IOMMU_H */
diff --git a/tests/qtest/libqos/qos-intel-iommu.c b/tests/qtest/libqos/qos-intel-iommu.c
index 4095ac0917..c793ec899c 100644
--- a/tests/qtest/libqos/qos-intel-iommu.c
+++ b/tests/qtest/libqos/qos-intel-iommu.c
@@ -15,6 +15,10 @@
#include "qos-iommu-testdev.h"
#include "qos-intel-iommu.h"
+/* Bounded poll for the invalidation-wait Status Write. */
+#define QVTD_INV_WAIT_POLL_MAX_ITERS 1000
+#define QVTD_INV_WAIT_POLL_INTERVAL_US 1000
+
#define QVTD_AW_48BIT_ENCODING 2
uint32_t qvtd_expected_dma_result(QVTDTestContext *ctx)
@@ -232,6 +236,30 @@ static uint64_t qvtd_get_fl_pte_attrs(bool is_leaf)
return attrs;
}
+uint64_t qvtd_leaf_pte_addr(uint64_t iova)
+{
+ return qvtd_get_table_addr(QVTD_PT_L1_BASE, 1, iova);
+}
+
+uint64_t qvtd_make_leaf_pte(uint64_t pa, QVTDTransMode mode)
+{
+ uint64_t attrs;
+
+ /*
+ * Reuse the same leaf attributes qvtd_setup_translation_tables() writes,
+ * so a rewritten PTE stays consistent with the initial mapping. US=1 in
+ * the first-level format is required because the PASID entry runs with
+ * SRE=0, which makes every DMA appear as a user-mode access
+ * (VT-d 3.6.2 / 9.9).
+ */
+ if (mode == QVTD_TM_SCALABLE_FLT) {
+ attrs = qvtd_get_fl_pte_attrs(true);
+ } else {
+ attrs = qvtd_get_pte_attrs();
+ }
+ return (pa & VTD_PAGE_MASK_4K) | attrs;
+}
+
void qvtd_setup_translation_tables(QTestState *qts, uint64_t iova,
QVTDTransMode mode)
{
@@ -529,3 +557,83 @@ QPCIDevice *qvtd_setup_qtest_pci_device(QTestState *qts, QPCIBus **pcibus,
return dev;
}
+
+/*
+ * Write a 128-bit invalidation descriptor at the current tail and advance
+ * IQT_REG. Internal helper for the IOTLB / wait variants below.
+ */
+static uint32_t qvtd_submit_inv_desc(QTestState *qts, uint64_t iommu_base,
+ uint64_t desc_lo, uint64_t desc_hi,
+ uint32_t tail)
+{
+ uint64_t desc_addr = QVTD_IQ_BASE + (uint64_t)tail * QVTD_IQ_DESC_SIZE;
+
+ qtest_writeq(qts, desc_addr, desc_lo);
+ qtest_writeq(qts, desc_addr + 8, desc_hi);
+ tail++;
+
+ qtest_writeq(qts, iommu_base + DMAR_IQT_REG,
+ (uint64_t)tail << QVTD_IQT_SHIFT);
+ return tail;
+}
+
+uint32_t qvtd_submit_inv_wait_and_poll(QTestState *qts, uint64_t iommu_base,
+ uint32_t tail)
+{
+ uint64_t lo, hi;
+ uint32_t status = 0;
+ int i;
+
+ qtest_writel(qts, QVTD_INV_WAIT_ADDR, 0);
+
+ lo = VTD_INV_DESC_WAIT | VTD_INV_DESC_WAIT_SW |
+ ((uint64_t)QVTD_INV_WAIT_DATA << VTD_INV_DESC_WAIT_DATA_SHIFT);
+ hi = QVTD_INV_WAIT_ADDR;
+
+ tail = qvtd_submit_inv_desc(qts, iommu_base, lo, hi, tail);
+
+ for (i = 0; i < QVTD_INV_WAIT_POLL_MAX_ITERS; i++) {
+ status = qtest_readl(qts, QVTD_INV_WAIT_ADDR);
+ if (status == QVTD_INV_WAIT_DATA) {
+ return tail;
+ }
+ g_usleep(QVTD_INV_WAIT_POLL_INTERVAL_US);
+ }
+
+ g_assert_cmphex(status, ==, QVTD_INV_WAIT_DATA);
+ return tail;
+}
+
+uint32_t qvtd_submit_iotlb_global_inv(QTestState *qts, uint64_t iommu_base,
+ uint32_t tail)
+{
+ uint64_t lo = VTD_INV_DESC_IOTLB | VTD_INV_DESC_IOTLB_GLOBAL;
+
+ return qvtd_submit_inv_desc(qts, iommu_base, lo, 0, tail);
+}
+
+uint32_t qvtd_submit_iotlb_domain_inv(QTestState *qts, uint64_t iommu_base,
+ uint16_t domain_id, uint32_t tail)
+{
+ uint64_t lo = VTD_INV_DESC_IOTLB | VTD_INV_DESC_IOTLB_DOMAIN |
+ ((uint64_t)domain_id << 16);
+
+ return qvtd_submit_inv_desc(qts, iommu_base, lo, 0, tail);
+}
+
+uint32_t qvtd_submit_iotlb_page_inv(QTestState *qts, uint64_t iommu_base,
+ uint16_t domain_id, uint64_t addr,
+ uint8_t am, uint32_t tail)
+{
+ uint64_t lo = VTD_INV_DESC_IOTLB | VTD_INV_DESC_IOTLB_PAGE |
+ ((uint64_t)domain_id << 16);
+ /*
+ * AM selects the invalidation range per VT-d 6.5.2.4:
+ * am=0 → 4 KB, am=9 → 2 MB, am=18 → 1 GB.
+ * IH (hi[6]) is left clear, requesting full invalidation
+ * including non-leaf paging-structure caches.
+ */
+ uint64_t hi = (addr & ~0xfffULL) | (am & 0x3fULL);
+
+ return qvtd_submit_inv_desc(qts, iommu_base, lo, hi, tail);
+}
--
MST
^ permalink raw reply related [flat|nested] 46+ messages in thread
* [PULL 23/44] tests/qtest: add IOTLB invalidation test for Intel IOMMU
2026-07-05 14:36 [PULL 00/44] pci, vhost, virtio, iommu: features, fixes, cleanups Michael S. Tsirkin
` (21 preceding siblings ...)
2026-07-05 14:35 ` [PULL 22/44] tests/qtest/libqos: add Intel IOMMU invalidation helpers Michael S. Tsirkin
@ 2026-07-05 14:35 ` Michael S. Tsirkin
2026-07-05 14:35 ` [PULL 24/44] vhost-user-base: free virtqueue array during cleanup Michael S. Tsirkin
` (21 subsequent siblings)
44 siblings, 0 replies; 46+ messages in thread
From: Michael S. Tsirkin @ 2026-07-05 14:35 UTC (permalink / raw)
To: qemu-devel
Cc: Peter Maydell, Junjie Cao, Jason Wang, Yi Liu,
Clément Mathieu--Drif, Fabiano Rosas, Laurent Vivier,
Paolo Bonzini
From: Junjie Cao <junjie.cao@intel.com>
Nothing in tree exercises IOTLB invalidation for any emulated vIOMMU:
the existing iommu-testdev tests only check one-shot translation, so a
regression that failed to flush a stale IOTLB entry would go unnoticed.
Add a test that drives the queued-invalidation path end to end
(vtd_process_inv_desc -> vtd_process_iotlb_desc ->
vtd_iotlb_{global,domain,page}_invalidate). For each
{legacy, scalable-slt, scalable-flt} x {global, domain, page}
combination it:
1. maps IOVA -> PA_A and DMAs, populating the IOTLB;
2. rewrites the leaf PTE to PA_B *without* invalidating and DMAs
again, asserting the stale entry is still served (MISMATCH);
3. submits the IOTLB invalidation plus a wait descriptor, then DMAs
and asserts the fresh page walk now reaches PA_B.
Step 2 makes the flush observable: it fails loudly if the IOTLB
is not actually caching the first translation.
For scalable first-level (flt), QEMU keeps first- and second-level
mappings in a single IOTLB that the legacy VTD_INV_DESC_IOTLB descriptor
flushes for every level, so the test uses that descriptor across all
modes. PASID-selective invalidation (VTD_INV_DESC_PIOTLB, vtd_piotlb_*)
is a separate path and is left for a follow-up.
It also adds three page-selective cases that cache a second page and check
its fate after invalidating the first: for second-level (legacy, scalable-slt)
the second page survives, while for first-level (scalable-flt) QEMU flushes all
first-stage entries of the domain, so it does not. This distinguishes a
page-selective flush from a domain-wide or global one.
Signed-off-by: Junjie Cao <junjie.cao@intel.com>
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
Message-ID: <20260703072200.463082-4-junjie.cao@intel.com>
---
MAINTAINERS | 1 +
tests/qtest/iommu-intel-inv-test.c | 346 +++++++++++++++++++++++++++++
tests/qtest/meson.build | 2 +-
3 files changed, 348 insertions(+), 1 deletion(-)
create mode 100644 tests/qtest/iommu-intel-inv-test.c
diff --git a/MAINTAINERS b/MAINTAINERS
index 5ee7c1f601..cbdfce4866 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -4106,6 +4106,7 @@ F: include/hw/i386/intel_iommu.h
F: tests/functional/x86_64/test_intel_iommu.py
F: tests/qtest/intel-iommu-test.c
F: tests/qtest/iommu-intel-test.c
+F: tests/qtest/iommu-intel-inv-test.c
AMD-Vi Emulation
M: Alejandro Jimenez <alejandro.j.jimenez@oracle.com>
diff --git a/tests/qtest/iommu-intel-inv-test.c b/tests/qtest/iommu-intel-inv-test.c
new file mode 100644
index 0000000000..1467eef647
--- /dev/null
+++ b/tests/qtest/iommu-intel-inv-test.c
@@ -0,0 +1,346 @@
+/*
+ * QTest for Intel IOMMU (VT-d) IOTLB invalidation via Invalidation Queue
+ *
+ * Validates that IOTLB invalidation descriptors submitted through the
+ * queued invalidation interface correctly flush cached translations,
+ * forcing the IOMMU to re-walk page tables on subsequent DMA.
+ *
+ * Copyright (c) 2026 Intel Corporation.
+ *
+ * Author: Junjie Cao <junjie.cao@intel.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "qemu/osdep.h"
+#include "libqtest.h"
+#include "libqos/pci.h"
+#include "libqos/pci-pc.h"
+#include "hw/i386/intel_iommu_internal.h"
+#include "hw/misc/iommu-testdev.h"
+#include "libqos/qos-intel-iommu.h"
+#include "libqos/qos-iommu-testdev.h"
+
+#define DMA_LEN 4
+
+/*
+ * Second DMA target page, chosen to fall well outside any address used by
+ * qos-intel-iommu's fixed structure layout.
+ */
+#define QVTD_PT_VAL_B (QVTD_MEM_BASE + 0x00200000)
+
+/*
+ * A second IOVA/target page for the page-selectivity test. QVTD_IOVA_2 is
+ * QVTD_IOVA + 4K: it shares the L4/L3/L2 walk built by
+ * qvtd_build_translation() and differs only in the leaf (L1) slot, so mapping
+ * it costs one extra leaf PTE. QVTD_PT_VAL_2 is its distinct target page.
+ */
+#define QVTD_IOVA_2 (QVTD_IOVA + 0x1000)
+#define QVTD_PT_VAL_2 (QVTD_MEM_BASE + 0x00300000)
+
+typedef enum {
+ IOTLB_INV_GLOBAL,
+ IOTLB_INV_DOMAIN,
+ IOTLB_INV_PAGE,
+} IOTLBInvGranularity;
+
+/*
+ * Core invalidation test, parameterized by translation mode and
+ * invalidation granularity.
+ *
+ * The iommu-testdev device performs DMA writes via the IOMMU (using the
+ * IOVA) and verifies by reading back from the expected GPA directly. If
+ * the IOTLB is stale, the DMA write lands at the old PA while readback
+ * uses the GPA we supply, causing a mismatch (ITD_DMA_ERR_MISMATCH).
+ *
+ * Test sequence:
+ * 1. Setup translation: IOVA -> PA_A
+ * 2. DMA(gpa=PA_A) -> success (IOTLB populates cache)
+ * 3. Modify PTE: IOVA -> PA_B (no invalidation)
+ * 4. DMA(gpa=PA_B) -> MISMATCH (stale IOTLB directs write to PA_A)
+ * 5. Issue IOTLB invalidation + wait
+ * 6. DMA(gpa=PA_B) -> success (cache flushed, fresh page walk)
+ *
+ * Phase 4 depends on QEMU's IOTLB caching the Phase 1 translation; if a
+ * future change makes IOTLB caching lazy this assertion would no longer
+ * exercise the stale-cache path.
+ */
+static void run_iotlb_inv_test(QVTDTransMode mode, IOTLBInvGranularity gran)
+{
+ QTestState *qts;
+ QPCIBus *pcibus;
+ QPCIDevice *dev;
+ QPCIBar bar;
+ uint32_t tail = 0;
+ uint32_t result;
+ uint64_t pa_a, pa_b;
+
+ if (!qtest_has_machine("q35")) {
+ g_test_skip("q35 machine not available");
+ return;
+ }
+
+ qts = qtest_initf("-machine q35 -smp 1 -m 512 -net none "
+ "%s -device iommu-testdev",
+ qvtd_iommu_args(mode));
+
+ if (!qvtd_check_caps(qts, mode)) {
+ qtest_quit(qts);
+ return;
+ }
+
+ dev = qvtd_setup_qtest_pci_device(qts, &pcibus, &bar);
+
+ /*
+ * The IOMMU translates an IOVA to a page base, then the page offset
+ * from the IOVA is added. So GPA = page_base + (IOVA & 0xfff).
+ */
+ pa_a = (QVTD_PT_VAL & VTD_PAGE_MASK_4K) + (QVTD_IOVA & 0xfff);
+ pa_b = (QVTD_PT_VAL_B & VTD_PAGE_MASK_4K) + (QVTD_IOVA & 0xfff);
+
+ /* --- Phase 1: Setup and initial DMA (populates IOTLB) --- */
+ qvtd_build_translation(qts, mode, dev->devfn);
+ qvtd_program_regs(qts, Q35_HOST_BRIDGE_IOMMU_ADDR, mode);
+
+ qtest_memset(qts, pa_a, 0, DMA_LEN);
+ qtest_memset(qts, pa_b, 0, DMA_LEN);
+
+ result = qos_iommu_testdev_trigger_dma(dev, bar, QVTD_IOVA, pa_a,
+ DMA_LEN, 0);
+ g_assert_cmpuint(result, ==, 0);
+
+ /* --- Phase 2: Modify PTE without invalidation -> stale IOTLB --- */
+ qtest_writeq(qts, qvtd_leaf_pte_addr(QVTD_IOVA),
+ qvtd_make_leaf_pte(QVTD_PT_VAL_B & VTD_PAGE_MASK_4K, mode));
+ qtest_memset(qts, pa_a, 0, DMA_LEN);
+ qtest_memset(qts, pa_b, 0, DMA_LEN);
+
+ result = qos_iommu_testdev_trigger_dma(dev, bar, QVTD_IOVA, pa_b,
+ DMA_LEN, 0);
+ g_assert_cmpuint(result, ==, ITD_DMA_ERR_MISMATCH);
+
+ /* --- Phase 3: Invalidate IOTLB -> fresh page walk succeeds --- */
+ switch (gran) {
+ case IOTLB_INV_GLOBAL:
+ tail = qvtd_submit_iotlb_global_inv(qts, Q35_HOST_BRIDGE_IOMMU_ADDR,
+ tail);
+ break;
+ case IOTLB_INV_DOMAIN:
+ tail = qvtd_submit_iotlb_domain_inv(qts, Q35_HOST_BRIDGE_IOMMU_ADDR,
+ QVTD_DOMAIN_ID, tail);
+ break;
+ case IOTLB_INV_PAGE:
+ tail = qvtd_submit_iotlb_page_inv(qts, Q35_HOST_BRIDGE_IOMMU_ADDR,
+ QVTD_DOMAIN_ID, QVTD_IOVA, 0,
+ tail);
+ break;
+ }
+ tail = qvtd_submit_inv_wait_and_poll(qts, Q35_HOST_BRIDGE_IOMMU_ADDR,
+ tail);
+
+ qtest_memset(qts, pa_a, 0, DMA_LEN);
+ qtest_memset(qts, pa_b, 0, DMA_LEN);
+
+ result = qos_iommu_testdev_trigger_dma(dev, bar, QVTD_IOVA, pa_b,
+ DMA_LEN, 0);
+ g_assert_cmpuint(result, ==, 0);
+
+ g_free(dev);
+ qpci_free_pc(pcibus);
+ qtest_quit(qts);
+}
+
+/*
+ * Page-selectivity test: verify that a page-selective invalidation flushes
+ * the named page and touches other cached pages only as far as the model
+ * intends. run_iotlb_inv_test() caches a single entry, so it cannot tell a
+ * page-selective flush from a domain-wide or global one; this test caches two
+ * pages in the same domain and checks the second one's fate.
+ *
+ * The expected fate of the second page depends on the translation level:
+ *
+ * - second-level (legacy / scalable-slt): a page-selective descriptor
+ * evicts only the matching gfn, so IOVA_2 survives.
+ * - first-level (scalable-flt): QEMU invalidates all first-stage entries of
+ * the domain on a page-selective descriptor (vtd_hash_remove_by_page()
+ * returns true for any pgtt==FST entry of the domain, matching the VT-d
+ * spec for first-stage IOTLB invalidation), so IOVA_2 is flushed too.
+ *
+ * Method: map IOVA -> PA_A and IOVA_2 -> PA_A_2, DMA both to populate two
+ * IOTLB entries, rewrite both leaf PTEs to PA_B* without invalidating, then
+ * page-invalidate IOVA only. IOVA always re-walks to PA_B. For IOVA_2 we
+ * verify the DMA against its *original* page PA_A_2: if the entry survived,
+ * the stale cache still serves PA_A_2 (success); if it was flushed, the fresh
+ * walk reaches PA_B_2 and mismatches PA_A_2. So a survived entry gives
+ * success and a flushed entry gives MISMATCH, and we assert whichever the
+ * mode requires -- catching both an over-matching second-level flush and a
+ * regression that stopped flushing first-stage entries domain-wide.
+ */
+static void run_page_selectivity_test(QVTDTransMode mode)
+{
+ QTestState *qts;
+ QPCIBus *pcibus;
+ QPCIDevice *dev;
+ QPCIBar bar;
+ uint32_t tail = 0;
+ uint32_t result;
+ uint64_t pa_a, pa_b, pa_a2, pa_b2;
+ bool fl_domain_wide = (mode == QVTD_TM_SCALABLE_FLT);
+
+ if (!qtest_has_machine("q35")) {
+ g_test_skip("q35 machine not available");
+ return;
+ }
+
+ qts = qtest_initf("-machine q35 -smp 1 -m 512 -net none "
+ "%s -device iommu-testdev",
+ qvtd_iommu_args(mode));
+
+ if (!qvtd_check_caps(qts, mode)) {
+ qtest_quit(qts);
+ return;
+ }
+
+ dev = qvtd_setup_qtest_pci_device(qts, &pcibus, &bar);
+
+ pa_a = (QVTD_PT_VAL & VTD_PAGE_MASK_4K) + (QVTD_IOVA & 0xfff);
+ pa_b = (QVTD_PT_VAL_B & VTD_PAGE_MASK_4K) + (QVTD_IOVA & 0xfff);
+ pa_a2 = (QVTD_PT_VAL_2 & VTD_PAGE_MASK_4K) + (QVTD_IOVA_2 & 0xfff);
+ pa_b2 = (QVTD_PT_VAL_B & VTD_PAGE_MASK_4K) + (QVTD_IOVA_2 & 0xfff);
+
+ /* --- Setup: IOVA -> PA_A (built by helper) and IOVA_2 -> PA_A_2 --- */
+ qvtd_build_translation(qts, mode, dev->devfn);
+ qtest_writeq(qts, qvtd_leaf_pte_addr(QVTD_IOVA_2),
+ qvtd_make_leaf_pte(QVTD_PT_VAL_2 & VTD_PAGE_MASK_4K, mode));
+ qvtd_program_regs(qts, Q35_HOST_BRIDGE_IOMMU_ADDR, mode);
+
+ /* Populate both IOTLB entries. */
+ qtest_memset(qts, pa_a, 0, DMA_LEN);
+ qtest_memset(qts, pa_a2, 0, DMA_LEN);
+ result = qos_iommu_testdev_trigger_dma(dev, bar, QVTD_IOVA, pa_a,
+ DMA_LEN, 0);
+ g_assert_cmpuint(result, ==, 0);
+ result = qos_iommu_testdev_trigger_dma(dev, bar, QVTD_IOVA_2, pa_a2,
+ DMA_LEN, 0);
+ g_assert_cmpuint(result, ==, 0);
+
+ /* Rewrite both leaf PTEs to PA_B* without invalidating. */
+ qtest_writeq(qts, qvtd_leaf_pte_addr(QVTD_IOVA),
+ qvtd_make_leaf_pte(QVTD_PT_VAL_B & VTD_PAGE_MASK_4K, mode));
+ qtest_writeq(qts, qvtd_leaf_pte_addr(QVTD_IOVA_2),
+ qvtd_make_leaf_pte(QVTD_PT_VAL_B & VTD_PAGE_MASK_4K, mode));
+
+ /* Page-selective invalidation of IOVA only. */
+ tail = qvtd_submit_iotlb_page_inv(qts, Q35_HOST_BRIDGE_IOMMU_ADDR,
+ QVTD_DOMAIN_ID, QVTD_IOVA, 0, tail);
+ tail = qvtd_submit_inv_wait_and_poll(qts, Q35_HOST_BRIDGE_IOMMU_ADDR,
+ tail);
+
+ /* IOVA was flushed: fresh walk reaches PA_B. */
+ qtest_memset(qts, pa_a, 0, DMA_LEN);
+ qtest_memset(qts, pa_b, 0, DMA_LEN);
+ result = qos_iommu_testdev_trigger_dma(dev, bar, QVTD_IOVA, pa_b,
+ DMA_LEN, 0);
+ g_assert_cmpuint(result, ==, 0);
+
+ /*
+ * IOVA_2's fate, verified against its original page PA_A_2:
+ * - second-level: entry survives, stale cache serves PA_A_2 -> success;
+ * - first-level: entry was flushed domain-wide, fresh walk reaches
+ * PA_B_2 -> MISMATCH against PA_A_2.
+ */
+ qtest_memset(qts, pa_a2, 0, DMA_LEN);
+ qtest_memset(qts, pa_b2, 0, DMA_LEN);
+ result = qos_iommu_testdev_trigger_dma(dev, bar, QVTD_IOVA_2, pa_a2,
+ DMA_LEN, 0);
+ if (fl_domain_wide) {
+ g_assert_cmpuint(result, ==, ITD_DMA_ERR_MISMATCH);
+ } else {
+ g_assert_cmpuint(result, ==, 0);
+ }
+
+ g_free(dev);
+ qpci_free_pc(pcibus);
+ qtest_quit(qts);
+}
+
+/*
+ * scalable-flt is covered here even though, per the VT-d spec, first-level
+ * mappings are invalidated with the PASID-based descriptor
+ * (VTD_INV_DESC_PIOTLB). QEMU keeps first- and second-level mappings in a
+ * single IOTLB that the legacy VTD_INV_DESC_IOTLB descriptor flushes for
+ * every level, so this test drives that descriptor across all modes.
+ * PASID-selective (PIOTLB) invalidation is a separate path, left for a
+ * follow-up.
+ */
+static const struct {
+ const char *name;
+ QVTDTransMode mode;
+} trans_modes[] = {
+ { "legacy", QVTD_TM_LEGACY_TRANS },
+ { "scalable-slt", QVTD_TM_SCALABLE_SLT },
+ { "scalable-flt", QVTD_TM_SCALABLE_FLT },
+};
+
+static const struct {
+ const char *name;
+ IOTLBInvGranularity gran;
+} granularities[] = {
+ { "global", IOTLB_INV_GLOBAL },
+ { "domain", IOTLB_INV_DOMAIN },
+ { "page", IOTLB_INV_PAGE },
+};
+
+typedef struct {
+ QVTDTransMode mode;
+ IOTLBInvGranularity gran;
+} TestCase;
+
+static void test_iotlb_inv(const void *opaque)
+{
+ const TestCase *tc = opaque;
+
+ run_iotlb_inv_test(tc->mode, tc->gran);
+}
+
+static void test_page_selectivity(const void *opaque)
+{
+ const QVTDTransMode *mode = opaque;
+
+ run_page_selectivity_test(*mode);
+}
+
+int main(int argc, char **argv)
+{
+ g_test_init(&argc, &argv, NULL);
+
+ for (size_t m = 0; m < ARRAY_SIZE(trans_modes); m++) {
+ for (size_t g = 0; g < ARRAY_SIZE(granularities); g++) {
+ TestCase *tc = g_new(TestCase, 1);
+ char *path;
+
+ tc->mode = trans_modes[m].mode;
+ tc->gran = granularities[g].gran;
+
+ path = g_strdup_printf("/iommu-testdev/intel/iotlb-inv/%s-%s",
+ granularities[g].name,
+ trans_modes[m].name);
+ qtest_add_data_func_full(path, tc, test_iotlb_inv, g_free);
+ g_free(path);
+ }
+ }
+
+ for (size_t m = 0; m < ARRAY_SIZE(trans_modes); m++) {
+ QVTDTransMode *mode = g_new(QVTDTransMode, 1);
+ char *path;
+
+ *mode = trans_modes[m].mode;
+ path = g_strdup_printf(
+ "/iommu-testdev/intel/iotlb-inv/page-selective/%s",
+ trans_modes[m].name);
+ qtest_add_data_func_full(path, mode, test_page_selectivity, g_free);
+ g_free(path);
+ }
+
+ return g_test_run();
+}
diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build
index 93175baa87..56ff860e21 100644
--- a/tests/qtest/meson.build
+++ b/tests/qtest/meson.build
@@ -100,7 +100,7 @@ qtests_i386 = \
(config_all_devices.has_key('CONFIG_AMD_IOMMU') ? ['amd-iommu-test'] : []) + \
(config_all_devices.has_key('CONFIG_VTD') ? ['intel-iommu-test'] : []) + \
(config_all_devices.has_key('CONFIG_VTD') and
- config_all_devices.has_key('CONFIG_IOMMU_TESTDEV') ? ['iommu-intel-test'] : []) + \
+ config_all_devices.has_key('CONFIG_IOMMU_TESTDEV') ? ['iommu-intel-test', 'iommu-intel-inv-test'] : []) + \
(host_os != 'windows' and \
config_all_devices.has_key('CONFIG_ACPI_ERST') ? ['erst-test'] : []) + \
(config_all_devices.has_key('CONFIG_PCIE_PORT') and \
--
MST
^ permalink raw reply related [flat|nested] 46+ messages in thread
* [PULL 24/44] vhost-user-base: free virtqueue array during cleanup
2026-07-05 14:36 [PULL 00/44] pci, vhost, virtio, iommu: features, fixes, cleanups Michael S. Tsirkin
` (22 preceding siblings ...)
2026-07-05 14:35 ` [PULL 23/44] tests/qtest: add IOTLB invalidation test for Intel IOMMU Michael S. Tsirkin
@ 2026-07-05 14:35 ` Michael S. Tsirkin
2026-07-05 14:35 ` [PULL 25/44] vhost-user-base: clean up vhost_dev on realize failure Michael S. Tsirkin
` (20 subsequent siblings)
44 siblings, 0 replies; 46+ messages in thread
From: Michael S. Tsirkin @ 2026-07-05 14:35 UTC (permalink / raw)
To: qemu-devel
Cc: Peter Maydell, GuoHan Zhao, Alex Bennée, Stefano Garzarella
From: GuoHan Zhao <zhaoguohan@kylinos.cn>
vhost-user-base stores the VirtQueue pointers in a GPtrArray, but its
cleanup helper only deletes the VirtQueues and leaves the array itself
allocated.
Free the GPtrArray after deleting the queues and clear the pointer so
cleanup remains safe if the error path reaches it with no queues to
release.
Fixes: 6275989647ef (virtio: split into vhost-user-base and vhost-user-device)
Signed-off-by: GuoHan Zhao <zhaoguohan@kylinos.cn>
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
Message-ID: <20260629092619.2607275-1-zhaoguohan@kylinos.cn>
---
hw/virtio/vhost-user-base.c | 10 +++++++---
1 file changed, 7 insertions(+), 3 deletions(-)
diff --git a/hw/virtio/vhost-user-base.c b/hw/virtio/vhost-user-base.c
index 39b5e637fc..6ac523f9eb 100644
--- a/hw/virtio/vhost-user-base.c
+++ b/hw/virtio/vhost-user-base.c
@@ -193,9 +193,13 @@ static void do_vhost_user_cleanup(VirtIODevice *vdev, VHostUserBase *vub)
{
vhost_user_cleanup(&vub->vhost_user);
- for (int i = 0; i < vub->num_vqs; i++) {
- VirtQueue *vq = g_ptr_array_index(vub->vqs, i);
- virtio_delete_queue(vq);
+ if (vub->vqs) {
+ for (int i = 0; i < vub->num_vqs; i++) {
+ VirtQueue *vq = g_ptr_array_index(vub->vqs, i);
+ virtio_delete_queue(vq);
+ }
+ g_ptr_array_free(vub->vqs, true);
+ vub->vqs = NULL;
}
virtio_cleanup(vdev);
--
MST
^ permalink raw reply related [flat|nested] 46+ messages in thread
* [PULL 25/44] vhost-user-base: clean up vhost_dev on realize failure
2026-07-05 14:36 [PULL 00/44] pci, vhost, virtio, iommu: features, fixes, cleanups Michael S. Tsirkin
` (23 preceding siblings ...)
2026-07-05 14:35 ` [PULL 24/44] vhost-user-base: free virtqueue array during cleanup Michael S. Tsirkin
@ 2026-07-05 14:35 ` Michael S. Tsirkin
2026-07-05 14:35 ` [PULL 26/44] vdpa: fix use-after-free of vqs in vhost_vdpa_device_unrealize Michael S. Tsirkin
` (19 subsequent siblings)
44 siblings, 0 replies; 46+ messages in thread
From: Michael S. Tsirkin @ 2026-07-05 14:35 UTC (permalink / raw)
To: qemu-devel
Cc: Peter Maydell, GuoHan Zhao, Alex Bennée, Stefano Garzarella
From: GuoHan Zhao <zhaoguohan@kylinos.cn>
Failures after vhost_dev_init() currently skip vhost_dev_cleanup(),
leaking initialized vhost_dev state.
Add a separate unwind label for those paths. Keep a copy of
vhost_dev.vqs so the array can still be freed after vhost_dev_cleanup()
clears struct vhost_dev.
Fixes: 6608dca74ecf (vhost-user-device: Add shared memory BAR)
Signed-off-by: GuoHan Zhao <zhaoguohan@kylinos.cn>
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
Message-ID: <20260630012058.2663259-1-zhaoguohan@kylinos.cn>
---
hw/virtio/vhost-user-base.c | 11 ++++++++---
1 file changed, 8 insertions(+), 3 deletions(-)
diff --git a/hw/virtio/vhost-user-base.c b/hw/virtio/vhost-user-base.c
index 6ac523f9eb..478ec68f09 100644
--- a/hw/virtio/vhost-user-base.c
+++ b/hw/virtio/vhost-user-base.c
@@ -287,6 +287,7 @@ static void vub_device_realize(DeviceState *dev, Error **errp)
VirtIODevice *vdev = VIRTIO_DEVICE(dev);
VHostUserBase *vub = VHOST_USER_BASE(dev);
uint64_t memory_sizes[VIRTIO_MAX_SHMEM_REGIONS];
+ struct vhost_virtqueue *vhost_vqs = NULL;
int i, ret, nregions, regions_processed = 0;
if (!vub->chardev.chr) {
@@ -338,6 +339,7 @@ static void vub_device_realize(DeviceState *dev, Error **errp)
vub->vhost_dev.nvqs = vub->num_vqs;
vub->vhost_dev.vqs = g_new0(struct vhost_virtqueue, vub->vhost_dev.nvqs);
+ vhost_vqs = vub->vhost_dev.vqs;
/* connect to backend */
ret = vhost_dev_init(&vub->vhost_dev, &vub->vhost_user,
@@ -353,7 +355,7 @@ static void vub_device_realize(DeviceState *dev, Error **errp)
errp);
if (ret < 0) {
- goto err;
+ goto err_vhost_dev;
}
for (i = 0; i < VIRTIO_MAX_SHMEM_REGIONS && regions_processed < nregions; i++) {
@@ -368,14 +370,14 @@ static void vub_device_realize(DeviceState *dev, Error **errp)
errp);
if (ret < 0) {
- goto err;
+ goto err_vhost_dev;
}
}
if (memory_sizes[i] % qemu_real_host_page_size() != 0) {
error_setg(errp, "Shared memory %d size must be a multiple of "
"the host page size", i);
- goto err;
+ goto err_vhost_dev;
}
virtio_new_shmem_region(vdev, i, memory_sizes[i]);
@@ -385,7 +387,10 @@ static void vub_device_realize(DeviceState *dev, Error **errp)
qemu_chr_fe_set_handlers(&vub->chardev, NULL, NULL, vub_event, NULL,
dev, NULL, true);
return;
+err_vhost_dev:
+ vhost_dev_cleanup(&vub->vhost_dev);
err:
+ g_free(vhost_vqs);
do_vhost_user_cleanup(vdev, vub);
}
--
MST
^ permalink raw reply related [flat|nested] 46+ messages in thread
* [PULL 26/44] vdpa: fix use-after-free of vqs in vhost_vdpa_device_unrealize
2026-07-05 14:36 [PULL 00/44] pci, vhost, virtio, iommu: features, fixes, cleanups Michael S. Tsirkin
` (24 preceding siblings ...)
2026-07-05 14:35 ` [PULL 25/44] vhost-user-base: clean up vhost_dev on realize failure Michael S. Tsirkin
@ 2026-07-05 14:35 ` Michael S. Tsirkin
2026-07-05 14:35 ` [PULL 27/44] arm: sbsa_gwdt: fixup default "clock-frequency" Michael S. Tsirkin
` (18 subsequent siblings)
44 siblings, 0 replies; 46+ messages in thread
From: Michael S. Tsirkin @ 2026-07-05 14:35 UTC (permalink / raw)
To: qemu-devel
Cc: Peter Maydell, lizhaoxin04, Miao Kezhan, Paolo Bonzini,
Vladimir Sementsov-Ogievskiy, Markus Armbruster
From: lizhaoxin04 <lizhaoxin04@baidu.com>
vhost_vdpa_device_unrealize() frees s->dev.vqs before
vhost_dev_cleanup(), but vhost_dev_cleanup() still accesses hdev->vqs
while tearing down virtqueues. This leads to a use-after-free and may
crash QEMU with SIGSEGV during vDPA hot-unplug.
Save the vqs pointer in a local variable, call vhost_dev_cleanup(),
and free it afterward. This matches the cleanup pattern used by
vhost-scsi.
Fixes: b430a2bd23 ("vdpa: add vdpa-dev support")
Co-developed-by: Miao Kezhan <miaokezhan@baidu.com>
Signed-off-by: Li Zhaoxin <lizhaoxin04@baidu.com>
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
Message-ID: <20260622100145.18924-1-lizhaoxin04@baidu.com>
---
hw/virtio/vdpa-dev.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/hw/virtio/vdpa-dev.c b/hw/virtio/vdpa-dev.c
index 94188d37bb..089a77f4d0 100644
--- a/hw/virtio/vdpa-dev.c
+++ b/hw/virtio/vdpa-dev.c
@@ -172,6 +172,7 @@ static void vhost_vdpa_device_unrealize(DeviceState *dev)
{
VirtIODevice *vdev = VIRTIO_DEVICE(dev);
VhostVdpaDevice *s = VHOST_VDPA_DEVICE(vdev);
+ struct vhost_virtqueue *vqs = s->dev.vqs;
int i;
virtio_set_status(vdev, 0);
@@ -183,8 +184,8 @@ static void vhost_vdpa_device_unrealize(DeviceState *dev)
virtio_cleanup(vdev);
g_free(s->config);
- g_free(s->dev.vqs);
vhost_dev_cleanup(&s->dev);
+ g_free(vqs);
g_free(s->vdpa.shared);
qemu_close(s->vhostfd);
s->vhostfd = -1;
--
MST
^ permalink raw reply related [flat|nested] 46+ messages in thread
* [PULL 27/44] arm: sbsa_gwdt: fixup default "clock-frequency"
2026-07-05 14:36 [PULL 00/44] pci, vhost, virtio, iommu: features, fixes, cleanups Michael S. Tsirkin
` (25 preceding siblings ...)
2026-07-05 14:35 ` [PULL 26/44] vdpa: fix use-after-free of vqs in vhost_vdpa_device_unrealize Michael S. Tsirkin
@ 2026-07-05 14:35 ` Michael S. Tsirkin
2026-07-05 14:35 ` [PULL 28/44] arm: add tracing events to sbsa_gwdt Michael S. Tsirkin
` (17 subsequent siblings)
44 siblings, 0 replies; 46+ messages in thread
From: Michael S. Tsirkin @ 2026-07-05 14:35 UTC (permalink / raw)
To: qemu-devel
Cc: Peter Maydell, Igor Mammedov, Eric Auger, Leif Lindholm, qemu-arm
From: Igor Mammedov <imammedo@redhat.com>
Comment about keeping legacy freq, is wrong to begin with
(should be 62.5MHz), but that value also doesn't make
sense anymore as the watchdog is used only by un-versioned
SBSA board and the later has hard-coded it to 1GHz.
Other potential user (arm/virt) also has system clock at 1GHz.
Drop misleading comment about legacy and set default to 1GHz
to match both boards. (not that it does matter, since users are
setting frequency to match CPU's one explicitly)
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-2-imammedo@redhat.com>
---
hw/watchdog/sbsa_gwdt.c | 5 ++---
1 file changed, 2 insertions(+), 3 deletions(-)
diff --git a/hw/watchdog/sbsa_gwdt.c b/hw/watchdog/sbsa_gwdt.c
index 7ade5c6f18..b739a3ce3c 100644
--- a/hw/watchdog/sbsa_gwdt.c
+++ b/hw/watchdog/sbsa_gwdt.c
@@ -264,11 +264,10 @@ static void wdt_sbsa_gwdt_realize(DeviceState *dev, Error **errp)
static const Property wdt_sbsa_gwdt_props[] = {
/*
* Timer frequency in Hz. This must match the frequency used by
- * the CPU's generic timer. Default 62.5Hz matches QEMU's legacy
- * CPU timer frequency default.
+ * the CPU's generic timer.
*/
DEFINE_PROP_UINT64("clock-frequency", struct SBSA_GWDTState, freq,
- 62500000),
+ 1000000000),
};
static void wdt_sbsa_gwdt_class_init(ObjectClass *klass, const void *data)
--
MST
^ permalink raw reply related [flat|nested] 46+ messages in thread
* [PULL 28/44] arm: add tracing events to sbsa_gwdt
2026-07-05 14:36 [PULL 00/44] pci, vhost, virtio, iommu: features, fixes, cleanups Michael S. Tsirkin
` (26 preceding siblings ...)
2026-07-05 14:35 ` [PULL 27/44] arm: sbsa_gwdt: fixup default "clock-frequency" Michael S. Tsirkin
@ 2026-07-05 14:35 ` Michael S. Tsirkin
2026-07-05 14:35 ` [PULL 29/44] arm: sbsa_gwdt: rename device type to sbsa-gwdt Michael S. Tsirkin
` (16 subsequent siblings)
44 siblings, 0 replies; 46+ messages in thread
From: Michael S. Tsirkin @ 2026-07-05 14:35 UTC (permalink / raw)
To: qemu-devel
Cc: Peter Maydell, Igor Mammedov, Ani Sinha, Eric Auger,
Leif Lindholm, qemu-arm
From: Igor Mammedov <imammedo@redhat.com>
Signed-off-by: Igor Mammedov <imammedo@redhat.com>
Reviewed-by: Ani Sinha <anisinha@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-3-imammedo@redhat.com>
---
hw/watchdog/sbsa_gwdt.c | 8 ++++++++
hw/watchdog/trace-events | 9 +++++++++
2 files changed, 17 insertions(+)
diff --git a/hw/watchdog/sbsa_gwdt.c b/hw/watchdog/sbsa_gwdt.c
index b739a3ce3c..acb970e8b3 100644
--- a/hw/watchdog/sbsa_gwdt.c
+++ b/hw/watchdog/sbsa_gwdt.c
@@ -24,6 +24,7 @@
#include "migration/vmstate.h"
#include "qemu/log.h"
#include "qemu/module.h"
+#include "trace.h"
static const VMStateDescription vmstate_sbsa_gwdt = {
.name = "sbsa-gwdt",
@@ -62,6 +63,7 @@ static uint64_t sbsa_gwdt_rread(void *opaque, hwaddr addr, unsigned int size)
qemu_log_mask(LOG_GUEST_ERROR, "bad address in refresh frame read :"
" 0x%x\n", (int)addr);
}
+ trace_sbsa_gwdt_refresh_read(addr, ret);
return ret;
}
@@ -93,6 +95,7 @@ static uint64_t sbsa_gwdt_read(void *opaque, hwaddr addr, unsigned int size)
qemu_log_mask(LOG_GUEST_ERROR, "bad address in control frame read :"
" 0x%x\n", (int)addr);
}
+ trace_sbsa_gwdt_control_read(addr, ret);
return ret;
}
@@ -127,6 +130,7 @@ static void sbsa_gwdt_rwrite(void *opaque, hwaddr offset, uint64_t data,
unsigned size) {
SBSA_GWDTState *s = SBSA_GWDT(opaque);
+ trace_sbsa_gwdt_refresh_write(offset, data);
if (offset == SBSA_GWDT_WRR) {
s->wcs &= ~(SBSA_GWDT_WCS_WS0 | SBSA_GWDT_WCS_WS1);
@@ -141,6 +145,7 @@ static void sbsa_gwdt_write(void *opaque, hwaddr offset, uint64_t data,
unsigned size) {
SBSA_GWDTState *s = SBSA_GWDT(opaque);
+ trace_sbsa_gwdt_control_write(offset, data);
switch (offset) {
case SBSA_GWDT_WCS:
s->wcs = data & SBSA_GWDT_WCS_EN;
@@ -180,6 +185,7 @@ static void wdt_sbsa_gwdt_reset(DeviceState *dev)
{
SBSA_GWDTState *s = SBSA_GWDT(dev);
+ trace_sbsa_gwdt_reset();
timer_del(s->timer);
s->wcs = 0;
@@ -196,10 +202,12 @@ static void sbsa_gwdt_timer_sysinterrupt(void *opaque)
if (!(s->wcs & SBSA_GWDT_WCS_WS0)) {
s->wcs |= SBSA_GWDT_WCS_WS0;
+ trace_sbsa_gwdt_ws0_asserted();
sbsa_gwdt_update_timer(s, TIMEOUT_REFRESH);
qemu_set_irq(s->irq, 1);
} else {
s->wcs |= SBSA_GWDT_WCS_WS1;
+ trace_sbsa_gwdt_ws1_asserted();
qemu_log_mask(CPU_LOG_RESET, "Watchdog timer expired.\n");
/*
* Reset the watchdog only if the guest gets notified about
diff --git a/hw/watchdog/trace-events b/hw/watchdog/trace-events
index d85b3ca769..b388f7b250 100644
--- a/hw/watchdog/trace-events
+++ b/hw/watchdog/trace-events
@@ -42,3 +42,12 @@ k230_wdt_interrupt(void) "K230 WDT interrupt"
k230_wdt_reset(void) "K230 WDT system reset"
k230_wdt_restart(void) "K230 WDT restart"
k230_wdt_reset_device(void) "K230 WDT device reset"
+
+#sbsa_gwdt.c
+sbsa_gwdt_refresh_read(uint64_t addr, uint32_t value) "[0x%" PRIx64 "] -> 0x%" PRIx32
+sbsa_gwdt_refresh_write(uint64_t addr, uint64_t value) "[0x%" PRIx64 "] <- 0x%" PRIx64
+sbsa_gwdt_control_read(uint64_t addr, uint32_t value) "[0x%" PRIx64 "] -> 0x%" PRIx32
+sbsa_gwdt_control_write(uint64_t addr, uint64_t value) "[0x%" PRIx64 "] <- 0x%" PRIx64
+sbsa_gwdt_ws0_asserted(void) "WS0 signal is asserted"
+sbsa_gwdt_ws1_asserted(void) "WS1 signal is asserted"
+sbsa_gwdt_reset(void) "reset watchdog to default state"
--
MST
^ permalink raw reply related [flat|nested] 46+ messages in thread
* [PULL 29/44] arm: sbsa_gwdt: rename device type to sbsa-gwdt
2026-07-05 14:36 [PULL 00/44] pci, vhost, virtio, iommu: features, fixes, cleanups Michael S. Tsirkin
` (27 preceding siblings ...)
2026-07-05 14:35 ` [PULL 28/44] arm: add tracing events to sbsa_gwdt Michael S. Tsirkin
@ 2026-07-05 14:35 ` Michael S. Tsirkin
2026-07-05 14:35 ` [PULL 30/44] arm: virt: create sbsa-gwdt watchdog Michael S. Tsirkin
` (15 subsequent siblings)
44 siblings, 0 replies; 46+ messages in thread
From: Michael S. Tsirkin @ 2026-07-05 14:35 UTC (permalink / raw)
To: qemu-devel
Cc: Peter Maydell, Igor Mammedov, Eric Auger, Leif Lindholm, qemu-arm
From: Igor Mammedov <imammedo@redhat.com>
Use hyphenated name to follow QEMU device naming convention.
Migration compatibility is preserved since the vmstate name
is already "sbsa-gwdt".
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-4-imammedo@redhat.com>
---
include/hw/watchdog/sbsa_gwdt.h | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/include/hw/watchdog/sbsa_gwdt.h b/include/hw/watchdog/sbsa_gwdt.h
index 307a4f291a..8941dcff9e 100644
--- a/include/hw/watchdog/sbsa_gwdt.h
+++ b/include/hw/watchdog/sbsa_gwdt.h
@@ -16,7 +16,7 @@
#include "hw/core/sysbus.h"
#include "hw/core/irq.h"
-#define TYPE_WDT_SBSA "sbsa_gwdt"
+#define TYPE_WDT_SBSA "sbsa-gwdt"
#define SBSA_GWDT(obj) \
OBJECT_CHECK(SBSA_GWDTState, (obj), TYPE_WDT_SBSA)
#define SBSA_GWDT_CLASS(klass) \
--
MST
^ permalink raw reply related [flat|nested] 46+ messages in thread
* [PULL 30/44] arm: virt: create sbsa-gwdt watchdog
2026-07-05 14:36 [PULL 00/44] pci, vhost, virtio, iommu: features, fixes, cleanups Michael S. Tsirkin
` (28 preceding siblings ...)
2026-07-05 14:35 ` [PULL 29/44] arm: sbsa_gwdt: rename device type to sbsa-gwdt Michael S. Tsirkin
@ 2026-07-05 14:35 ` Michael S. Tsirkin
2026-07-05 14:35 ` [PULL 31/44] arm: sbsa-gwdt: add 'wdat' option Michael S. Tsirkin
` (14 subsequent siblings)
44 siblings, 0 replies; 46+ messages in thread
From: Michael S. Tsirkin @ 2026-07-05 14:35 UTC (permalink / raw)
To: qemu-devel
Cc: Peter Maydell, Igor Mammedov, Eric Auger, Pierrick Bouvier,
Paolo Bonzini, Ani Sinha, Leif Lindholm, qemu-arm
From: Igor Mammedov <imammedo@redhat.com>
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>
---
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
^ permalink raw reply related [flat|nested] 46+ messages in thread
* [PULL 31/44] arm: sbsa-gwdt: add 'wdat' option
2026-07-05 14:36 [PULL 00/44] pci, vhost, virtio, iommu: features, fixes, cleanups Michael S. Tsirkin
` (29 preceding siblings ...)
2026-07-05 14:35 ` [PULL 30/44] arm: virt: create sbsa-gwdt watchdog Michael S. Tsirkin
@ 2026-07-05 14:35 ` Michael S. Tsirkin
2026-07-05 14:35 ` [PULL 32/44] acpi: introduce WDAT table for GWDT Michael S. Tsirkin
` (13 subsequent siblings)
44 siblings, 0 replies; 46+ messages in thread
From: Michael S. Tsirkin @ 2026-07-05 14:35 UTC (permalink / raw)
To: qemu-devel
Cc: Peter Maydell, Igor Mammedov, Eric Auger, Leif Lindholm, qemu-arm
From: Igor Mammedov <imammedo@redhat.com>
it will be used by arm/virt board, to pick WDAT compatible watchdog impl.
and act as switch over to WDAT ACPI table vesus default GTDT ACPI table.
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-6-imammedo@redhat.com>
---
include/hw/watchdog/sbsa_gwdt.h | 1 +
hw/watchdog/sbsa_gwdt.c | 9 +++++++++
2 files changed, 10 insertions(+)
diff --git a/include/hw/watchdog/sbsa_gwdt.h b/include/hw/watchdog/sbsa_gwdt.h
index 8941dcff9e..5ad7c7a798 100644
--- a/include/hw/watchdog/sbsa_gwdt.h
+++ b/include/hw/watchdog/sbsa_gwdt.h
@@ -73,6 +73,7 @@ typedef struct SBSA_GWDTState {
uint32_t woru;
uint32_t wcvl;
uint32_t wcvu;
+ bool wdat;
} SBSA_GWDTState;
#endif /* WDT_SBSA_GWDT_H */
diff --git a/hw/watchdog/sbsa_gwdt.c b/hw/watchdog/sbsa_gwdt.c
index 49fdd723c2..330a74798a 100644
--- a/hw/watchdog/sbsa_gwdt.c
+++ b/hw/watchdog/sbsa_gwdt.c
@@ -265,6 +265,14 @@ static void wdt_sbsa_gwdt_realize(DeviceState *dev, Error **errp)
sysbus_init_irq(sbd, &s->irq);
+ /*
+ * WDAT spec: "The clock interval that the WDT uses must be
+ * greater than or equal to 1 millisecond."
+ */
+ if (s->wdat) {
+ s->freq = 1000;
+ }
+
s->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, sbsa_gwdt_timer_sysinterrupt,
dev);
}
@@ -276,6 +284,7 @@ static const Property wdt_sbsa_gwdt_props[] = {
*/
DEFINE_PROP_UINT64("clock-frequency", struct SBSA_GWDTState, freq,
1000000000),
+ DEFINE_PROP_BOOL("wdat", struct SBSA_GWDTState, wdat, false),
};
static void wdt_sbsa_gwdt_class_init(ObjectClass *klass, const void *data)
--
MST
^ permalink raw reply related [flat|nested] 46+ messages in thread
* [PULL 32/44] acpi: introduce WDAT table for GWDT
2026-07-05 14:36 [PULL 00/44] pci, vhost, virtio, iommu: features, fixes, cleanups Michael S. Tsirkin
` (30 preceding siblings ...)
2026-07-05 14:35 ` [PULL 31/44] arm: sbsa-gwdt: add 'wdat' option Michael S. Tsirkin
@ 2026-07-05 14:35 ` Michael S. Tsirkin
2026-07-05 14:36 ` [PULL 33/44] arm: virt: add support for WDAT based watchdog Michael S. Tsirkin
` (12 subsequent siblings)
44 siblings, 0 replies; 46+ messages in thread
From: Michael S. Tsirkin @ 2026-07-05 14:35 UTC (permalink / raw)
To: qemu-devel; +Cc: Peter Maydell, Igor Mammedov, Eric Auger, Ani Sinha
From: Igor Mammedov <imammedo@redhat.com>
Add build_gwdt_wdat() to generate a Watchdog Action Table
designed for SBSA Generic Watchdog Timer.
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-7-imammedo@redhat.com>
---
include/hw/acpi/wdat-gwdt.h | 19 +++++++
hw/acpi/wdat-gwdt-stub.c | 16 ++++++
hw/acpi/wdat-gwdt.c | 99 +++++++++++++++++++++++++++++++++++++
hw/acpi/meson.build | 2 +
4 files changed, 136 insertions(+)
create mode 100644 include/hw/acpi/wdat-gwdt.h
create mode 100644 hw/acpi/wdat-gwdt-stub.c
create mode 100644 hw/acpi/wdat-gwdt.c
diff --git a/include/hw/acpi/wdat-gwdt.h b/include/hw/acpi/wdat-gwdt.h
new file mode 100644
index 0000000000..42339e031e
--- /dev/null
+++ b/include/hw/acpi/wdat-gwdt.h
@@ -0,0 +1,19 @@
+/*
+ * GWDT Watchdog Action Table (WDAT) definition
+ *
+ * Copyright Red Hat, Inc. 2026
+ * Author(s): Igor Mammedov <imammedo@redhat.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+#ifndef QEMU_HW_ACPI_WDAT_GWDT_H
+#define QEMU_HW_ACPI_WDAT_GWDT_H
+
+#include "hw/acpi/aml-build.h"
+#include "hw/watchdog/sbsa_gwdt.h"
+
+void build_gwdt_wdat(GArray *table_data, BIOSLinker *linker, const char *oem_id,
+ const char *oem_table_id, uint64_t rbase, uint64_t cbase,
+ uint64_t freq);
+
+#endif /* QEMU_HW_ACPI_WDAT_GWDT_H */
diff --git a/hw/acpi/wdat-gwdt-stub.c b/hw/acpi/wdat-gwdt-stub.c
new file mode 100644
index 0000000000..4d43783f70
--- /dev/null
+++ b/hw/acpi/wdat-gwdt-stub.c
@@ -0,0 +1,16 @@
+/*
+ * Copyright Red Hat, Inc. 2026
+ * Author(s): Igor Mammedov <imammedo@redhat.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "qemu/osdep.h"
+#include "hw/acpi/wdat-gwdt.h"
+
+void build_gwdt_wdat(GArray *table_data, BIOSLinker *linker, const char *oem_id,
+ const char *oem_table_id, uint64_t rbase, uint64_t cbase,
+ uint64_t freq)
+{
+ g_assert_not_reached();
+}
diff --git a/hw/acpi/wdat-gwdt.c b/hw/acpi/wdat-gwdt.c
new file mode 100644
index 0000000000..b30566ef6a
--- /dev/null
+++ b/hw/acpi/wdat-gwdt.c
@@ -0,0 +1,99 @@
+/*
+ * SBSA GWDT Watchdog Action Table (WDAT)
+ *
+ * Copyright Red Hat, Inc. 2026
+ * Author(s): Igor Mammedov <imammedo@redhat.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "qemu/osdep.h"
+#include "hw/acpi/aml-build.h"
+#include "hw/acpi/wdat-gwdt.h"
+#include "hw/acpi/wdat.h"
+#include "hw/watchdog/sbsa_gwdt.h"
+
+#define GWDT_REG(base, reg_offset, reg_width) { \
+ .space_id = AML_AS_SYSTEM_MEMORY, \
+ .address = base + reg_offset, .bit_width = reg_width, \
+ .access_width = AML_DWORD_ACC };
+
+/*
+ * "Hardware Watchdog Timers Design Specification"
+ * https://uefi.org/acpi 'Watchdog Action Table (WDAT)'
+ */
+void build_gwdt_wdat(GArray *table_data, BIOSLinker *linker, const char *oem_id,
+ const char *oem_table_id, uint64_t rbase, uint64_t cbase,
+ uint64_t freq)
+{
+ AcpiTable table = { .sig = "WDAT", .rev = 1, .oem_id = oem_id,
+ .oem_table_id = oem_table_id };
+
+ struct AcpiGenericAddress wrr = GWDT_REG(rbase, 0x0, 32);
+ struct AcpiGenericAddress wor_l = GWDT_REG(cbase, SBSA_GWDT_WOR, 32);
+ struct AcpiGenericAddress wcs = GWDT_REG(cbase, SBSA_GWDT_WCS, 32);
+
+ acpi_table_begin(&table, table_data);
+ build_append_int_noprefix(table_data, 0x20, 4); /* Watchdog Header Length */
+ /*
+ * PCI location fields are set to 0xff to indicate
+ * that the watchdog is not a PCI device.
+ */
+ build_append_int_noprefix(table_data, 0xff, 2); /* PCI Segment */
+ build_append_int_noprefix(table_data, 0xff, 1); /* PCI Bus Number */
+ build_append_int_noprefix(table_data, 0xff, 1); /* PCI Device Number */
+ build_append_int_noprefix(table_data, 0xff, 1); /* PCI Function Number */
+ build_append_int_noprefix(table_data, 0, 3); /* Reserved */
+ /*
+ * WDAT spec: "The clock interval that the WDT uses must be
+ * greater than or equal to 1 millisecond."
+ */
+ g_assert(freq <= 1000);
+ /* Timer Period, ms */
+ build_append_int_noprefix(table_data, 1000 / freq, 4);
+ /*
+ * WDAT spec: "The time-out period before the WDT fires is recommended
+ * to be at least 5 minutes."
+ * Set max count to 10min and min count to 5sec.
+ */
+ build_append_int_noprefix(table_data, 600 * freq, 4); /* Maximum Count */
+ build_append_int_noprefix(table_data, 5 * freq, 4); /* Minimum Count */
+ /*
+ * WATCHDOG_ENABLED | WATCHDOG_STOPPED_IN_SLEEP_STATE
+ */
+ build_append_int_noprefix(table_data, 0x81, 1); /* Watchdog Flags */
+ build_append_int_noprefix(table_data, 0, 3); /* Reserved */
+ /*
+ * watchdog instruction entries
+ */
+ build_append_int_noprefix(table_data, 8, 4);
+ /* Action table: WCS (control/status) register actions */
+ build_append_wdat_ins(table_data, WDAT_ACTION_QUERY_RUNNING_STATE,
+ WDAT_INS_READ_VALUE,
+ wcs, 0x1, 0x1);
+ build_append_wdat_ins(table_data, WDAT_ACTION_SET_RUNNING_STATE,
+ WDAT_INS_WRITE_VALUE | WDAT_INS_PRESERVE_REGISTER,
+ wcs, 1, 0x00000001);
+ build_append_wdat_ins(table_data, WDAT_ACTION_QUERY_STOPPED_STATE,
+ WDAT_INS_READ_VALUE,
+ wcs, 0x0, 0x00000001);
+ build_append_wdat_ins(table_data, WDAT_ACTION_SET_STOPPED_STATE,
+ WDAT_INS_WRITE_VALUE | WDAT_INS_PRESERVE_REGISTER,
+ wcs, 0x0, 0x00000001);
+ build_append_wdat_ins(table_data, WDAT_ACTION_QUERY_WATCHDOG_STATUS,
+ WDAT_INS_READ_VALUE,
+ wcs, 0x4, 0x00000004);
+ /* WOR (offset) and WRR (refresh) register actions */
+ build_append_wdat_ins(table_data, WDAT_ACTION_SET_COUNTDOWN_PERIOD,
+ WDAT_INS_WRITE_COUNTDOWN,
+ wor_l, 0, 0xffffffff);
+ /* WRR: any write refreshes the watchdog, value is ignored */
+ build_append_wdat_ins(table_data, WDAT_ACTION_RESET,
+ WDAT_INS_WRITE_VALUE,
+ wrr, 0x1, 0x1);
+ build_append_wdat_ins(table_data, WDAT_ACTION_SET_WATCHDOG_STATUS,
+ WDAT_INS_WRITE_VALUE | WDAT_INS_PRESERVE_REGISTER,
+ wrr, 0x4, 0x4);
+
+ acpi_table_end(linker, &table);
+}
diff --git a/hw/acpi/meson.build b/hw/acpi/meson.build
index 0c7bfb278a..09ad488a04 100644
--- a/hw/acpi/meson.build
+++ b/hw/acpi/meson.build
@@ -31,11 +31,13 @@ acpi_ss.add(when: 'CONFIG_ACPI_ERST', if_true: files('erst.c'))
acpi_ss.add(when: 'CONFIG_IPMI', if_true: files('ipmi.c'))
stub_ss.add(files('ipmi-stub.c'))
stub_ss.add(files('acpi-x86-stub.c'))
+acpi_ss.add(when: 'CONFIG_WDT_SBSA', if_true: files('wdat-gwdt.c'))
if have_tpm
acpi_ss.add(files('tpm.c'))
endif
stub_ss.add(files('acpi-stub.c', 'aml-build-stub.c', 'ghes-stub.c'))
stub_ss.add(files('pci-bridge-stub.c'))
+stub_ss.add(files('wdat-gwdt-stub.c'))
system_ss.add_all(when: 'CONFIG_ACPI', if_true: acpi_ss)
system_ss.add(when: 'CONFIG_GHES_CPER', if_true: files('ghes_cper.c'))
stub_ss.add(files('ghes_cper_stub.c'))
--
MST
^ permalink raw reply related [flat|nested] 46+ messages in thread
* [PULL 33/44] arm: virt: add support for WDAT based watchdog
2026-07-05 14:36 [PULL 00/44] pci, vhost, virtio, iommu: features, fixes, cleanups Michael S. Tsirkin
` (31 preceding siblings ...)
2026-07-05 14:35 ` [PULL 32/44] acpi: introduce WDAT table for GWDT Michael S. Tsirkin
@ 2026-07-05 14:36 ` Michael S. Tsirkin
2026-07-05 14:36 ` [PULL 34/44] tests: acpi: arm/virt: whitelist new WDAT table Michael S. Tsirkin
` (11 subsequent siblings)
44 siblings, 0 replies; 46+ messages in thread
From: Michael S. Tsirkin @ 2026-07-05 14:36 UTC (permalink / raw)
To: qemu-devel
Cc: Peter Maydell, Igor Mammedov, Eric Auger, Pierrick Bouvier,
Ani Sinha, qemu-arm
From: Igor Mammedov <imammedo@redhat.com>
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>
---
docs/system/arm/virt.rst | 15 +++++++++++++++
hw/arm/virt-acpi-build.c | 34 ++++++++++++++++++++++++++--------
hw/arm/virt.c | 12 ++++++++----
3 files changed, 49 insertions(+), 12 deletions(-)
diff --git a/docs/system/arm/virt.rst b/docs/system/arm/virt.rst
index bf5a9c8f6c..5f6dd17978 100644
--- a/docs/system/arm/virt.rst
+++ b/docs/system/arm/virt.rst
@@ -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
""""""""""""""""""""""""""""""""
diff --git a/hw/arm/virt-acpi-build.c b/hw/arm/virt-acpi-build.c
index f5b3b4ce48..f6386088b6 100644
--- a/hw/arm/virt-acpi-build.c
+++ b/hw/arm/virt-acpi-build.c
@@ -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);
{
diff --git a/hw/arm/virt.c b/hw/arm/virt.c
index eabc5274d5..fb916c341b 100644
--- a/hw/arm/virt.c
+++ b/hw/arm/virt.c
@@ -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) {
--
MST
^ permalink raw reply related [flat|nested] 46+ messages in thread
* [PULL 34/44] tests: acpi: arm/virt: whitelist new WDAT table
2026-07-05 14:36 [PULL 00/44] pci, vhost, virtio, iommu: features, fixes, cleanups Michael S. Tsirkin
` (32 preceding siblings ...)
2026-07-05 14:36 ` [PULL 33/44] arm: virt: add support for WDAT based watchdog Michael S. Tsirkin
@ 2026-07-05 14:36 ` Michael S. Tsirkin
2026-07-05 14:36 ` [PULL 35/44] tests: acpi: arm/virt: add WDAT table test case Michael S. Tsirkin
` (10 subsequent siblings)
44 siblings, 0 replies; 46+ messages in thread
From: Michael S. Tsirkin @ 2026-07-05 14:36 UTC (permalink / raw)
To: qemu-devel; +Cc: Peter Maydell, Igor Mammedov, Eric Auger, Ani Sinha
From: Igor Mammedov <imammedo@redhat.com>
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-9-imammedo@redhat.com>
---
tests/qtest/bios-tables-test-allowed-diff.h | 1 +
tests/data/acpi/aarch64/virt/WDAT.wdat | 0
2 files changed, 1 insertion(+)
create mode 100644 tests/data/acpi/aarch64/virt/WDAT.wdat
diff --git a/tests/qtest/bios-tables-test-allowed-diff.h b/tests/qtest/bios-tables-test-allowed-diff.h
index dfb8523c8b..135186b40b 100644
--- a/tests/qtest/bios-tables-test-allowed-diff.h
+++ b/tests/qtest/bios-tables-test-allowed-diff.h
@@ -1 +1,2 @@
/* List of comma-separated changed AML files to ignore */
+"tests/data/acpi/aarch64/virt/WDAT.wdat",
diff --git a/tests/data/acpi/aarch64/virt/WDAT.wdat b/tests/data/acpi/aarch64/virt/WDAT.wdat
new file mode 100644
index 0000000000..e69de29bb2
--
MST
^ permalink raw reply related [flat|nested] 46+ messages in thread
* [PULL 35/44] tests: acpi: arm/virt: add WDAT table test case
2026-07-05 14:36 [PULL 00/44] pci, vhost, virtio, iommu: features, fixes, cleanups Michael S. Tsirkin
` (33 preceding siblings ...)
2026-07-05 14:36 ` [PULL 34/44] tests: acpi: arm/virt: whitelist new WDAT table Michael S. Tsirkin
@ 2026-07-05 14:36 ` Michael S. Tsirkin
2026-07-05 14:36 ` [PULL 36/44] tests: acpi: arm/virt: update expected WDAT blob Michael S. Tsirkin
` (9 subsequent siblings)
44 siblings, 0 replies; 46+ messages in thread
From: Michael S. Tsirkin @ 2026-07-05 14:36 UTC (permalink / raw)
To: qemu-devel; +Cc: Peter Maydell, Igor Mammedov, Eric Auger, Ani Sinha
From: Igor Mammedov <imammedo@redhat.com>
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-10-imammedo@redhat.com>
---
tests/qtest/bios-tables-test.c | 21 +++++++++++++++++++++
1 file changed, 21 insertions(+)
diff --git a/tests/qtest/bios-tables-test.c b/tests/qtest/bios-tables-test.c
index 45abd8bd8c..cee3dff31f 100644
--- a/tests/qtest/bios-tables-test.c
+++ b/tests/qtest/bios-tables-test.c
@@ -2295,6 +2295,25 @@ static void test_acpi_aarch64_virt_tcg_msi_gicv2m(void)
free_test_data(&data);
}
+static void test_acpi_aarch64_virt_tcg_wdat(void)
+{
+ test_data data = {
+ .machine = "virt",
+ .arch = "aarch64",
+ .variant = ".wdat",
+ .tcg_only = true,
+ .uefi_fl1 = "pc-bios/edk2-aarch64-code.fd",
+ .uefi_fl2 = "pc-bios/edk2-arm-vars.fd",
+ .cd = "tests/data/uefi-boot-images/bios-tables-test.aarch64.iso.qcow2",
+ .ram_start = 0x40000000ULL,
+ .scan_len = 128ULL * MiB,
+ };
+
+ test_acpi_one("-cpu cortex-a57 "
+ "-device sbsa-gwdt,wdat=on", &data);
+ free_test_data(&data);
+}
+
static void test_acpi_q35_viot(void)
{
test_data data = {
@@ -2914,6 +2933,8 @@ int main(int argc, char *argv[])
qtest_add_func("acpi/virt/smmuv3-dev",
test_acpi_aarch64_virt_smmuv3_dev);
}
+ qtest_add_func("acpi/virt/acpi-watchdog",
+ test_acpi_aarch64_virt_tcg_wdat);
}
} else if (strcmp(arch, "riscv64") == 0) {
if (has_tcg && qtest_has_device("virtio-blk-pci")) {
--
MST
^ permalink raw reply related [flat|nested] 46+ messages in thread
* [PULL 36/44] tests: acpi: arm/virt: update expected WDAT blob
2026-07-05 14:36 [PULL 00/44] pci, vhost, virtio, iommu: features, fixes, cleanups Michael S. Tsirkin
` (34 preceding siblings ...)
2026-07-05 14:36 ` [PULL 35/44] tests: acpi: arm/virt: add WDAT table test case Michael S. Tsirkin
@ 2026-07-05 14:36 ` Michael S. Tsirkin
2026-07-05 14:36 ` [PULL 37/44] tests: acpi: arm/virt: whitelist GTDT table Michael S. Tsirkin
` (8 subsequent siblings)
44 siblings, 0 replies; 46+ messages in thread
From: Michael S. Tsirkin @ 2026-07-05 14:36 UTC (permalink / raw)
To: qemu-devel; +Cc: Peter Maydell, Igor Mammedov, Eric Auger, Ani Sinha
From: Igor Mammedov <imammedo@redhat.com>
replace blank table with a new one:
[000h 0000 004h] Signature : "WDAT" [Watchdog Action Table]
[004h 0004 004h] Table Length : 00000104
[008h 0008 001h] Revision : 01
[009h 0009 001h] Checksum : 3B
[00Ah 0010 006h] Oem ID : "BOCHS "
[010h 0016 008h] Oem Table ID : "BXPC "
[018h 0024 004h] Oem Revision : 00000001
[01Ch 0028 004h] Asl Compiler ID : "BXPC"
[020h 0032 004h] Asl Compiler Revision : 00000001
[024h 0036 004h] Header Length : 00000020
[028h 0040 002h] PCI Segment : 00FF
[02Ah 0042 001h] PCI Bus : FF
[02Bh 0043 001h] PCI Device : FF
[02Ch 0044 001h] PCI Function : FF
[02Dh 0045 003h] Reserved : 000000
[030h 0048 004h] Timer Period : 00000001
[034h 0052 004h] Max Count : 000927C0
[038h 0056 004h] Min Count : 00001388
[03Ch 0060 001h] Flags (decoded below) : 81
Enabled : 1
Stopped When Asleep : 1
[03Dh 0061 003h] Reserved : 000000
[040h 0064 004h] Watchdog Entry Count : 00000008
[044h 0068 001h] Watchdog Action : 08
[045h 0069 001h] Instruction : 00
[046h 0070 002h] Reserved : 0000
[048h 0072 00Ch] Register Region : [Generic Address Structure]
[048h 0072 001h] Space ID : 00 [SystemMemory]
[049h 0073 001h] Bit Width : 20
[04Ah 0074 001h] Bit Offset : 00
[04Bh 0075 001h] Encoded Access Width : 03 [DWord Access:32]
[04Ch 0076 008h] Address : 000000000C001000
[054h 0084 004h] Value : 00000001
[058h 0088 004h] Register Mask : 00000001
[05Ch 0092 001h] Watchdog Action : 01
[05Dh 0093 001h] Instruction : 02
[05Eh 0094 002h] Reserved : 0000
[060h 0096 00Ch] Register Region : [Generic Address Structure]
[060h 0096 001h] Space ID : 00 [SystemMemory]
[061h 0097 001h] Bit Width : 20
[062h 0098 001h] Bit Offset : 00
[063h 0099 001h] Encoded Access Width : 03 [DWord Access:32]
[064h 0100 008h] Address : 000000000C000000
[06Ch 0108 004h] Value : 00000001
[070h 0112 004h] Register Mask : 00000007
[074h 0116 001h] Watchdog Action : 06
[075h 0117 001h] Instruction : 03
[076h 0118 002h] Reserved : 0000
[078h 0120 00Ch] Register Region : [Generic Address Structure]
[078h 0120 001h] Space ID : 00 [SystemMemory]
[079h 0121 001h] Bit Width : 20
[07Ah 0122 001h] Bit Offset : 00
[07Bh 0123 001h] Encoded Access Width : 03 [DWord Access:32]
[07Ch 0124 008h] Address : 000000000C001008
[084h 0132 004h] Value : 00000000
[088h 0136 004h] Register Mask : FFFFFFFF
[08Ch 0140 001h] Watchdog Action : 09
[08Dh 0141 001h] Instruction : 82
[08Eh 0142 002h] Reserved : 0000
[090h 0144 00Ch] Register Region : [Generic Address Structure]
[090h 0144 001h] Space ID : 00 [SystemMemory]
[091h 0145 001h] Bit Width : 20
[092h 0146 001h] Bit Offset : 00
[093h 0147 001h] Encoded Access Width : 03 [DWord Access:32]
[094h 0148 008h] Address : 000000000C001000
[09Ch 0156 004h] Value : 00000001
[0A0h 0160 004h] Register Mask : 00000001
[0A4h 0164 001h] Watchdog Action : 0A
[0A5h 0165 001h] Instruction : 00
[0A6h 0166 002h] Reserved : 0000
[0A8h 0168 00Ch] Register Region : [Generic Address Structure]
[0A8h 0168 001h] Space ID : 00 [SystemMemory]
[0A9h 0169 001h] Bit Width : 20
[0AAh 0170 001h] Bit Offset : 00
[0ABh 0171 001h] Encoded Access Width : 03 [DWord Access:32]
[0ACh 0172 008h] Address : 000000000C001000
[0B4h 0180 004h] Value : 00000000
[0B8h 0184 004h] Register Mask : 00000001
[0BCh 0188 001h] Watchdog Action : 0B
[0BDh 0189 001h] Instruction : 82
[0BEh 0190 002h] Reserved : 0000
[0C0h 0192 00Ch] Register Region : [Generic Address Structure]
[0C0h 0192 001h] Space ID : 00 [SystemMemory]
[0C1h 0193 001h] Bit Width : 20
[0C2h 0194 001h] Bit Offset : 00
[0C3h 0195 001h] Encoded Access Width : 03 [DWord Access:32]
[0C4h 0196 008h] Address : 000000000C001000
[0CCh 0204 004h] Value : 00000000
[0D0h 0208 004h] Register Mask : 00000001
[0D4h 0212 001h] Watchdog Action : 20
[0D5h 0213 001h] Instruction : 00
[0D6h 0214 002h] Reserved : 0000
[0D8h 0216 00Ch] Register Region : [Generic Address Structure]
[0D8h 0216 001h] Space ID : 00 [SystemMemory]
[0D9h 0217 001h] Bit Width : 20
[0DAh 0218 001h] Bit Offset : 00
[0DBh 0219 001h] Encoded Access Width : 03 [DWord Access:32]
[0DCh 0220 008h] Address : 000000000C001000
[0E4h 0228 004h] Value : 00000004
[0E8h 0232 004h] Register Mask : 00000004
[0ECh 0236 001h] Watchdog Action : 21
[0EDh 0237 001h] Instruction : 82
[0EEh 0238 002h] Reserved : 0000
[0F0h 0240 00Ch] Register Region : [Generic Address Structure]
[0F0h 0240 001h] Space ID : 00 [SystemMemory]
[0F1h 0241 001h] Bit Width : 20
[0F2h 0242 001h] Bit Offset : 00
[0F3h 0243 001h] Encoded Access Width : 03 [DWord Access:32]
[0F4h 0244 008h] Address : 000000000C000000
[0FCh 0252 004h] Value : 00000004
[100h 0256 004h] Register Mask : 00000004
Raw Table Data: Length 260 (0x104)
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-11-imammedo@redhat.com>
---
tests/qtest/bios-tables-test-allowed-diff.h | 1 -
tests/data/acpi/aarch64/virt/WDAT.wdat | Bin 0 -> 260 bytes
2 files changed, 1 deletion(-)
diff --git a/tests/qtest/bios-tables-test-allowed-diff.h b/tests/qtest/bios-tables-test-allowed-diff.h
index 135186b40b..dfb8523c8b 100644
--- a/tests/qtest/bios-tables-test-allowed-diff.h
+++ b/tests/qtest/bios-tables-test-allowed-diff.h
@@ -1,2 +1 @@
/* List of comma-separated changed AML files to ignore */
-"tests/data/acpi/aarch64/virt/WDAT.wdat",
diff --git a/tests/data/acpi/aarch64/virt/WDAT.wdat b/tests/data/acpi/aarch64/virt/WDAT.wdat
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..d5adacf9114637d8803d08906ff1e29ac61f5fe0 100644
GIT binary patch
literal 260
zcmZXOK@NgI3`O4vF`x@byg*$Mjv=^kCkZBU9awQJZ&p(1B&bcAwm<K;ecx}dG(f1E
zXZz@-8M;<PBmmZ16+lr{z~)btGhbi?GEanrk^*Op%cs@+r=JYk@zHbs;iGQ%_k0R#
Y9+OSa{H}$nYB)I0_dnOT-}Sg>0T&h&0RR91
literal 0
HcmV?d00001
--
MST
^ permalink raw reply related [flat|nested] 46+ messages in thread
* [PULL 37/44] tests: acpi: arm/virt: whitelist GTDT table
2026-07-05 14:36 [PULL 00/44] pci, vhost, virtio, iommu: features, fixes, cleanups Michael S. Tsirkin
` (35 preceding siblings ...)
2026-07-05 14:36 ` [PULL 36/44] tests: acpi: arm/virt: update expected WDAT blob Michael S. Tsirkin
@ 2026-07-05 14:36 ` Michael S. Tsirkin
2026-07-05 14:36 ` [PULL 38/44] tests: acpi: arm/virt: add GTDT watchdog table test case Michael S. Tsirkin
` (7 subsequent siblings)
44 siblings, 0 replies; 46+ messages in thread
From: Michael S. Tsirkin @ 2026-07-05 14:36 UTC (permalink / raw)
To: qemu-devel; +Cc: Peter Maydell, Igor Mammedov, Eric Auger, Ani Sinha
From: Igor Mammedov <imammedo@redhat.com>
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-12-imammedo@redhat.com>
---
tests/qtest/bios-tables-test-allowed-diff.h | 1 +
1 file changed, 1 insertion(+)
diff --git a/tests/qtest/bios-tables-test-allowed-diff.h b/tests/qtest/bios-tables-test-allowed-diff.h
index dfb8523c8b..3e9be28cc4 100644
--- a/tests/qtest/bios-tables-test-allowed-diff.h
+++ b/tests/qtest/bios-tables-test-allowed-diff.h
@@ -1 +1,2 @@
/* List of comma-separated changed AML files to ignore */
+"tests/data/acpi/aarch64/virt/GTDT",
--
MST
^ permalink raw reply related [flat|nested] 46+ messages in thread
* [PULL 38/44] tests: acpi: arm/virt: add GTDT watchdog table test case
2026-07-05 14:36 [PULL 00/44] pci, vhost, virtio, iommu: features, fixes, cleanups Michael S. Tsirkin
` (36 preceding siblings ...)
2026-07-05 14:36 ` [PULL 37/44] tests: acpi: arm/virt: whitelist GTDT table Michael S. Tsirkin
@ 2026-07-05 14:36 ` Michael S. Tsirkin
2026-07-05 14:36 ` [PULL 39/44] tests: acpi: arm/virt: update expected GTDT blob Michael S. Tsirkin
` (6 subsequent siblings)
44 siblings, 0 replies; 46+ messages in thread
From: Michael S. Tsirkin @ 2026-07-05 14:36 UTC (permalink / raw)
To: qemu-devel; +Cc: Peter Maydell, Igor Mammedov, Eric Auger, Ani Sinha
From: Igor Mammedov <imammedo@redhat.com>
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-13-imammedo@redhat.com>
---
tests/qtest/bios-tables-test.c | 20 ++++++++++++++++++++
1 file changed, 20 insertions(+)
diff --git a/tests/qtest/bios-tables-test.c b/tests/qtest/bios-tables-test.c
index cee3dff31f..5cc526510a 100644
--- a/tests/qtest/bios-tables-test.c
+++ b/tests/qtest/bios-tables-test.c
@@ -2314,6 +2314,24 @@ static void test_acpi_aarch64_virt_tcg_wdat(void)
free_test_data(&data);
}
+static void test_acpi_aarch64_virt_tcg_gtdt_wd(void)
+{
+ test_data data = {
+ .machine = "virt",
+ .arch = "aarch64",
+ .variant = ".gwdt",
+ .tcg_only = true,
+ .uefi_fl1 = "pc-bios/edk2-aarch64-code.fd",
+ .uefi_fl2 = "pc-bios/edk2-arm-vars.fd",
+ .cd = "tests/data/uefi-boot-images/bios-tables-test.aarch64.iso.qcow2",
+ .ram_start = 0x40000000ULL,
+ .scan_len = 128ULL * MiB,
+ };
+
+ test_acpi_one("-cpu cortex-a57 -device sbsa-gwdt", &data);
+ free_test_data(&data);
+}
+
static void test_acpi_q35_viot(void)
{
test_data data = {
@@ -2935,6 +2953,8 @@ int main(int argc, char *argv[])
}
qtest_add_func("acpi/virt/acpi-watchdog",
test_acpi_aarch64_virt_tcg_wdat);
+ qtest_add_func("acpi/virt/gwdt-watchdog",
+ test_acpi_aarch64_virt_tcg_gtdt_wd);
}
} else if (strcmp(arch, "riscv64") == 0) {
if (has_tcg && qtest_has_device("virtio-blk-pci")) {
--
MST
^ permalink raw reply related [flat|nested] 46+ messages in thread
* [PULL 39/44] tests: acpi: arm/virt: update expected GTDT blob
2026-07-05 14:36 [PULL 00/44] pci, vhost, virtio, iommu: features, fixes, cleanups Michael S. Tsirkin
` (37 preceding siblings ...)
2026-07-05 14:36 ` [PULL 38/44] tests: acpi: arm/virt: add GTDT watchdog table test case Michael S. Tsirkin
@ 2026-07-05 14:36 ` Michael S. Tsirkin
2026-07-05 14:36 ` [PULL 40/44] vhost-user: Guarantee that memory regions do not overlap Michael S. Tsirkin
` (5 subsequent siblings)
44 siblings, 0 replies; 46+ messages in thread
From: Michael S. Tsirkin @ 2026-07-05 14:36 UTC (permalink / raw)
To: qemu-devel; +Cc: Peter Maydell, Igor Mammedov, Eric Auger, Ani Sinha
From: Igor Mammedov <imammedo@redhat.com>
Expected diff from base GTDT is an addition of watchdog
timer block:
[000h 0000 004h] Signature : "GTDT" [Generic Timer Description Table]
-[004h 0004 004h] Table Length : 00000068
+[004h 0004 004h] Table Length : 00000084
[008h 0008 001h] Revision : 03
-[009h 0009 001h] Checksum : 93
+[009h 0009 001h] Checksum : 39
[00Ah 0010 006h] Oem ID : "BOCHS "
[010h 0016 008h] Oem Table ID : "BXPC "
[018h 0024 004h] Oem Revision : 00000001
@@ -48,17 +48,30 @@
Always On : 0
[050h 0080 008h] Counter Read Block Address : FFFFFFFFFFFFFFFF
-[058h 0088 004h] Platform Timer Count : 00000000
-[05Ch 0092 004h] Platform Timer Offset : 00000000
+[058h 0088 004h] Platform Timer Count : 00000001
+[05Ch 0092 004h] Platform Timer Offset : 00000068
[060h 0096 004h] Virtual EL2 Timer GSIV : 00000000
[064h 0100 004h] Virtual EL2 Timer Flags : 00000000
-Raw Table Data: Length 104 (0x68)
+[068h 0104 001h] Subtable Type : 01 [Generic Watchdog Timer]
+[069h 0105 002h] Length : 001C
+[06Bh 0107 001h] Reserved : 00
+[06Ch 0108 008h] Refresh Frame Address : 000000000C000000
+[074h 0116 008h] Control Frame Address : 000000000C001000
+[07Ch 0124 004h] Timer Interrupt : 00000090
+[080h 0128 004h] Timer Flags (decoded below) : 00000000
+ Trigger Mode : 0
+ Polarity : 0
+ Security : 0
+
+Raw Table Data: Length 132 (0x84)
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-14-imammedo@redhat.com>
---
tests/qtest/bios-tables-test-allowed-diff.h | 1 -
tests/data/acpi/aarch64/virt/GTDT.gwdt | Bin 0 -> 132 bytes
2 files changed, 1 deletion(-)
create mode 100644 tests/data/acpi/aarch64/virt/GTDT.gwdt
diff --git a/tests/qtest/bios-tables-test-allowed-diff.h b/tests/qtest/bios-tables-test-allowed-diff.h
index 3e9be28cc4..dfb8523c8b 100644
--- a/tests/qtest/bios-tables-test-allowed-diff.h
+++ b/tests/qtest/bios-tables-test-allowed-diff.h
@@ -1,2 +1 @@
/* List of comma-separated changed AML files to ignore */
-"tests/data/acpi/aarch64/virt/GTDT",
diff --git a/tests/data/acpi/aarch64/virt/GTDT.gwdt b/tests/data/acpi/aarch64/virt/GTDT.gwdt
new file mode 100644
index 0000000000000000000000000000000000000000..44e6f2b18d2848f595a8c69bdb54a59e1becaffd
GIT binary patch
literal 132
zcmXwwyA6Oq3`5@udZ_s+HDlZ%K?f37Fam?H1k-{Nf(yUca^^hc3n=cmre&9SY)M2U
h0D3DQ7eKED3_uCAo*U=C)xXm_Rp<<rdHr#;057;f7y$qP
literal 0
HcmV?d00001
--
MST
^ permalink raw reply related [flat|nested] 46+ messages in thread
* [PULL 40/44] vhost-user: Guarantee that memory regions do not overlap
2026-07-05 14:36 [PULL 00/44] pci, vhost, virtio, iommu: features, fixes, cleanups Michael S. Tsirkin
` (38 preceding siblings ...)
2026-07-05 14:36 ` [PULL 39/44] tests: acpi: arm/virt: update expected GTDT blob Michael S. Tsirkin
@ 2026-07-05 14:36 ` Michael S. Tsirkin
2026-07-05 14:36 ` [PULL 41/44] hw/virtio-crypto: enforce max akcipher key length Michael S. Tsirkin
` (4 subsequent siblings)
44 siblings, 0 replies; 46+ messages in thread
From: Michael S. Tsirkin @ 2026-07-05 14:36 UTC (permalink / raw)
To: qemu-devel
Cc: Peter Maydell, Demi Marie Obenour, Stefano Garzarella,
Pierrick Bouvier
From: Demi Marie Obenour <demiobenour@gmail.com>
Otherwise there would be an ambiguity problem. Suppose that:
1. There is a region from [0x40000, 0x50000) with mmap offset 0x500000.
2. There is a region from [0x48000, 0x58000) with mmap offset 0xA00000.
A request has address 0x44000. Which mmap offset should be used?
This problem appears with both guest and user addresses.
Signed-off-by: Demi Marie Obenour <demiobenour@gmail.com>
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
Message-ID: <20260522-vhost-user-dev-v1-1-b31646cf19b8@gmail.com>
---
docs/interop/vhost-user.rst | 21 +++++++++++++++++++++
1 file changed, 21 insertions(+)
diff --git a/docs/interop/vhost-user.rst b/docs/interop/vhost-user.rst
index a704f3cb53..ae8c7ed995 100644
--- a/docs/interop/vhost-user.rst
+++ b/docs/interop/vhost-user.rst
@@ -214,6 +214,18 @@ fields at the end.
:domid: a 32-bit Xen hypervisor specific domain id.
+For all memory regions active at a given time:
+
+- ``[guest address, guest address + size)`` of one memory region never overlaps
+ the ``[guest address, guest address + size)`` of another memory region.
+
+- ``[user address, user address + size)`` of one memory region never overlaps
+ the ``[user address, user address + size)`` of another memory region.
+
+Violating any of these is a bug in the front-end. This ensures that a guest
+address or user address always refers to at most one location in memory.
+The front-end must remove a region before it can add an overlapping one.
+
Single memory region description
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -745,6 +757,15 @@ The front-end sends a list of vhost memory regions to the back-end using the
``VHOST_USER_SET_MEM_TABLE`` message. Each region has two base
addresses: a guest address and a user address.
+Memory regions can be added via the ``VHOST_USER_ADD_MEM_REG`` message. They
+can be removed via the ``VHOST_USER_REM_MEM_REG`` message. These messages can
+only be used if the ``VHOST_USER_PROTOCOL_F_CONFIGURE_MEM_SLOTS`` protocol
+feature has been successfully negotiated.
+
+Guest addresses are physical addresses in the guest. User addresses are
+arbitrary opaque values, though they typically refer to userspace addresses in
+the client process.
+
Messages contain guest addresses and/or user addresses to reference locations
within the shared memory. The mapping of these addresses works as follows.
--
MST
^ permalink raw reply related [flat|nested] 46+ messages in thread
* [PULL 41/44] hw/virtio-crypto: enforce max akcipher key length
2026-07-05 14:36 [PULL 00/44] pci, vhost, virtio, iommu: features, fixes, cleanups Michael S. Tsirkin
` (39 preceding siblings ...)
2026-07-05 14:36 ` [PULL 40/44] vhost-user: Guarantee that memory regions do not overlap Michael S. Tsirkin
@ 2026-07-05 14:36 ` Michael S. Tsirkin
2026-07-05 14:36 ` [PULL 42/44] vhost-user-scmi: free vhost virtqueue array on cleanup Michael S. Tsirkin
` (3 subsequent siblings)
44 siblings, 0 replies; 46+ messages in thread
From: Michael S. Tsirkin @ 2026-07-05 14:36 UTC (permalink / raw)
To: qemu-devel; +Cc: Peter Maydell, helei, Gonglei (Arei)
From: helei <lhestz@163.com>
enforce the global max_size boundary for akcipher session creation
Signed-off-by: helei <lhestz@163.com>
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
Message-ID: <20260616123251.26446-2-lhestz@163.com>
---
hw/virtio/virtio-crypto.c | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/hw/virtio/virtio-crypto.c b/hw/virtio/virtio-crypto.c
index 6fceb39681..79e2acb56c 100644
--- a/hw/virtio/virtio-crypto.c
+++ b/hw/virtio/virtio-crypto.c
@@ -216,6 +216,12 @@ virtio_crypto_create_asym_session(VirtIOCrypto *vcrypto,
return -VIRTIO_CRYPTO_NOTSUPP;
}
+ if (unlikely(keylen > vcrypto->conf.max_size)) {
+ error_report("virtio-crypto length of akcipher key is too large: %u",
+ keylen);
+ return -VIRTIO_CRYPTO_ERR;
+ }
+
if (keylen) {
asym_info->key = g_malloc(keylen);
if (iov_to_buf(iov, out_num, 0, asym_info->key, keylen) != keylen) {
--
MST
^ permalink raw reply related [flat|nested] 46+ messages in thread
* [PULL 42/44] vhost-user-scmi: free vhost virtqueue array on cleanup
2026-07-05 14:36 [PULL 00/44] pci, vhost, virtio, iommu: features, fixes, cleanups Michael S. Tsirkin
` (40 preceding siblings ...)
2026-07-05 14:36 ` [PULL 41/44] hw/virtio-crypto: enforce max akcipher key length Michael S. Tsirkin
@ 2026-07-05 14:36 ` Michael S. Tsirkin
2026-07-05 14:36 ` [PULL 43/44] vhost-user-blk: add seg-max-adjust flag Michael S. Tsirkin
` (2 subsequent siblings)
44 siblings, 0 replies; 46+ messages in thread
From: Michael S. Tsirkin @ 2026-07-05 14:36 UTC (permalink / raw)
To: qemu-devel; +Cc: Peter Maydell, GuoHan Zhao, Milan Zamazal, Stefano Garzarella
From: GuoHan Zhao <zhaoguohan@kylinos.cn>
vhost-user-scmi allocates vhost_dev.vqs during realize, but the
cleanup helper frees scmi->vhost_dev.vqs after vhost_dev_cleanup() has
cleared struct vhost_dev. This turns the free into g_free(NULL), leaking
the allocated vhost virtqueue array.
Keep a copy of the vhost_dev.vqs pointer across vhost_dev_cleanup() and
free that saved pointer from the common cleanup helper.
Fixes: a5dab090e142 (hw/virtio: Add boilerplate for vhost-user-scmi device)
Signed-off-by: GuoHan Zhao <zhaoguohan@kylinos.cn>
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
Message-ID: <20260630072728.3025097-1-zhaoguohan@kylinos.cn>
---
hw/virtio/vhost-user-scmi.c | 12 ++++++++----
1 file changed, 8 insertions(+), 4 deletions(-)
diff --git a/hw/virtio/vhost-user-scmi.c b/hw/virtio/vhost-user-scmi.c
index 9470f68c1f..02dc088ea9 100644
--- a/hw/virtio/vhost-user-scmi.c
+++ b/hw/virtio/vhost-user-scmi.c
@@ -220,11 +220,12 @@ static void vu_scmi_event(void *opaque, QEMUChrEvent event)
}
}
-static void do_vhost_user_cleanup(VirtIODevice *vdev, VHostUserSCMI *scmi)
+static void do_vhost_user_cleanup(VirtIODevice *vdev, VHostUserSCMI *scmi,
+ struct vhost_virtqueue *vhost_vqs)
{
virtio_delete_queue(scmi->cmd_vq);
virtio_delete_queue(scmi->event_vq);
- g_free(scmi->vhost_dev.vqs);
+ g_free(vhost_vqs);
virtio_cleanup(vdev);
vhost_user_cleanup(&scmi->vhost_user);
}
@@ -233,6 +234,7 @@ static void vu_scmi_device_realize(DeviceState *dev, Error **errp)
{
VirtIODevice *vdev = VIRTIO_DEVICE(dev);
VHostUserSCMI *scmi = VHOST_USER_SCMI(dev);
+ struct vhost_virtqueue *vhost_vqs;
int ret;
if (!scmi->chardev.chr) {
@@ -252,13 +254,14 @@ static void vu_scmi_device_realize(DeviceState *dev, Error **errp)
scmi->event_vq = virtio_add_queue(vdev, 256, vu_scmi_handle_output);
scmi->vhost_dev.nvqs = 2;
scmi->vhost_dev.vqs = g_new0(struct vhost_virtqueue, scmi->vhost_dev.nvqs);
+ vhost_vqs = scmi->vhost_dev.vqs;
ret = vhost_dev_init(&scmi->vhost_dev, &scmi->vhost_user,
VHOST_BACKEND_TYPE_USER, 0, errp);
if (ret < 0) {
error_setg_errno(errp, -ret,
"vhost-user-scmi: vhost_dev_init() failed");
- do_vhost_user_cleanup(vdev, scmi);
+ do_vhost_user_cleanup(vdev, scmi, vhost_vqs);
return;
}
@@ -270,10 +273,11 @@ static void vu_scmi_device_unrealize(DeviceState *dev)
{
VirtIODevice *vdev = VIRTIO_DEVICE(dev);
VHostUserSCMI *scmi = VHOST_USER_SCMI(dev);
+ struct vhost_virtqueue *vhost_vqs = scmi->vhost_dev.vqs;
vu_scmi_set_status(vdev, 0);
vhost_dev_cleanup(&scmi->vhost_dev);
- do_vhost_user_cleanup(vdev, scmi);
+ do_vhost_user_cleanup(vdev, scmi, vhost_vqs);
}
static const VMStateDescription vu_scmi_vmstate = {
--
MST
^ permalink raw reply related [flat|nested] 46+ messages in thread
* [PULL 43/44] vhost-user-blk: add seg-max-adjust flag
2026-07-05 14:36 [PULL 00/44] pci, vhost, virtio, iommu: features, fixes, cleanups Michael S. Tsirkin
` (41 preceding siblings ...)
2026-07-05 14:36 ` [PULL 42/44] vhost-user-scmi: free vhost virtqueue array on cleanup Michael S. Tsirkin
@ 2026-07-05 14:36 ` Michael S. Tsirkin
2026-07-05 14:36 ` [PULL 44/44] virtio-net: validate RSS indirections_len in post_load Michael S. Tsirkin
2026-07-07 5:04 ` [PULL 00/44] pci, vhost, virtio, iommu: features, fixes, cleanups Stefan Hajnoczi
44 siblings, 0 replies; 46+ messages in thread
From: Michael S. Tsirkin @ 2026-07-05 14:36 UTC (permalink / raw)
To: qemu-devel
Cc: Peter Maydell, Sergei Heifetz, Raphael Norwitz,
Stefano Garzarella, Kevin Wolf, Hanna Reitz, qemu-block
From: Sergei Heifetz <heifetz@yandex-team.com>
The virtio specification is not completely clear about seg_max and its
relationship with queue_size. Some drivers (for example, the modern
Linux kernel driver) rely on seg_max to set the maximum number of
segments used in a request. If seg_max is set larger than queue_size,
such a driver might overwhelm a virtqueue by trying to send more
segments than it can handle. As a result, it either hangs or faults.
One might argue that it is the vhost-user server's responsibility to set
a valid seg_max value. However, due to the issue described in the
previous paragraph, this value should generally depend on queue_size.
That's why it may be necessary to control it on QEMU's side.
This patch adds the seg-max-adjust flag. A flag with the same name
already exists for virtio-blk (and it exists to solve the same problem,
except that here we have a vhost-user server to consult).
If seg-max-adjust is set, the final seg_max is the minimum of the value
provided by the vhost-user server and (queue_size - 2). It is not
enabled by default.
Signed-off-by: Sergei Heifetz <heifetz@yandex-team.com>
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
Message-ID: <20260401230548.136541-1-heifetz@yandex-team.com>
---
include/hw/virtio/vhost-user-blk.h | 1 +
hw/block/vhost-user-blk.c | 12 ++++++++++++
2 files changed, 13 insertions(+)
diff --git a/include/hw/virtio/vhost-user-blk.h b/include/hw/virtio/vhost-user-blk.h
index 1e41a2bcdf..dee848cfd8 100644
--- a/include/hw/virtio/vhost-user-blk.h
+++ b/include/hw/virtio/vhost-user-blk.h
@@ -34,6 +34,7 @@ struct VHostUserBlk {
struct virtio_blk_config blkcfg;
uint16_t num_queues;
uint32_t queue_size;
+ bool seg_max_adjust;
struct vhost_dev dev;
struct vhost_inflight *inflight;
VhostUserState vhost_user;
diff --git a/hw/block/vhost-user-blk.c b/hw/block/vhost-user-blk.c
index b2e46eb3f9..2e5b3ae1b1 100644
--- a/hw/block/vhost-user-blk.c
+++ b/hw/block/vhost-user-blk.c
@@ -66,6 +66,12 @@ static void vhost_user_blk_update_config(VirtIODevice *vdev, uint8_t *config)
/* Our num_queues overrides the device backend */
virtio_stw_p(vdev, &s->blkcfg.num_queues, s->num_queues);
+ if (s->seg_max_adjust) {
+ uint32_t seg_max = MIN(s->blkcfg.seg_max, s->queue_size - 2);
+
+ virtio_stl_p(vdev, &s->blkcfg.seg_max, seg_max);
+ }
+
memcpy(config, &s->blkcfg, vdev->config_len);
}
@@ -489,6 +495,10 @@ static void vhost_user_blk_device_realize(DeviceState *dev, Error **errp)
error_setg(errp, "queue size must be non-zero");
return;
}
+ if (s->queue_size < 4 && s->seg_max_adjust) {
+ error_setg(errp, "queue size must be >= 4 when seg-max-adjust is set");
+ return;
+ }
if (s->queue_size > VIRTQUEUE_MAX_SIZE) {
error_setg(errp, "queue size must not exceed %d",
VIRTQUEUE_MAX_SIZE);
@@ -624,6 +634,8 @@ static const Property vhost_user_blk_properties[] = {
DEFINE_PROP_UINT16("num-queues", VHostUserBlk, num_queues,
VHOST_USER_BLK_AUTO_NUM_QUEUES),
DEFINE_PROP_UINT32("queue-size", VHostUserBlk, queue_size, 128),
+ DEFINE_PROP_BOOL("seg-max-adjust", VHostUserBlk, seg_max_adjust,
+ false),
DEFINE_PROP_BIT64("config-wce", VHostUserBlk, parent_obj.host_features,
VIRTIO_BLK_F_CONFIG_WCE, true),
DEFINE_PROP_BIT64("discard", VHostUserBlk, parent_obj.host_features,
--
MST
^ permalink raw reply related [flat|nested] 46+ messages in thread
* [PULL 44/44] virtio-net: validate RSS indirections_len in post_load
2026-07-05 14:36 [PULL 00/44] pci, vhost, virtio, iommu: features, fixes, cleanups Michael S. Tsirkin
` (42 preceding siblings ...)
2026-07-05 14:36 ` [PULL 43/44] vhost-user-blk: add seg-max-adjust flag Michael S. Tsirkin
@ 2026-07-05 14:36 ` Michael S. Tsirkin
2026-07-07 5:04 ` [PULL 00/44] pci, vhost, virtio, iommu: features, fixes, cleanups Stefan Hajnoczi
44 siblings, 0 replies; 46+ messages in thread
From: Michael S. Tsirkin @ 2026-07-05 14:36 UTC (permalink / raw)
To: qemu-devel; +Cc: Peter Maydell, Junjie Cao, qemu-stable, Jason Wang
From: Junjie Cao <junjie.cao@intel.com>
virtio_net_handle_rss() enforces that indirections_len is a non-zero
power of two no larger than VIRTIO_NET_RSS_MAX_TABLE_LEN, but
virtio_net_rss_post_load() applies none of these checks to values
restored from the migration stream.
A corrupted save file or crafted migration stream can set
indirections_len to 0. Even if it also clears redirect,
virtio_load() calls set_features_nocheck() after the device vmstate
(including the RSS subsection and its post_load) has already been
loaded, re-deriving redirect from the negotiated guest features.
When VIRTIO_NET_F_RSS was negotiated, redirect is set back to true
regardless of the migration stream value. The receive path then
computes
hash & (indirections_len - 1) /* wraps to 0xFFFFFFFF via int promotion */
and uses the result to index into indirections_table, which was not
allocated by the VMState loader when the element count is zero (see
vmstate_handle_alloc()), resulting in a NULL pointer dereference that
crashes QEMU:
#0 virtio_net_process_rss ../hw/net/virtio-net.c:1901
#1 virtio_net_receive_rcu ../hw/net/virtio-net.c:1921
#2 virtio_net_do_receive ../hw/net/virtio-net.c:2061
#3 nc_sendv_compat ../net/net.c:823
#4 qemu_deliver_packet_iov ../net/net.c:870
The RSS subsection is only loaded when rss_data.enabled is true (via
virtio_net_rss_needed()), and the command path always produces
indirections_len in {1, 2, 4, …, 128}, so an unconditional check
cannot reject a legitimate migration stream.
Factor the validation into virtio_net_rss_indirections_len_valid()
and call it from both virtio_net_handle_rss() and
virtio_net_rss_post_load().
Fixes: e41b711485e5 ("virtio-net: add migration support for RSS and hash report")
Cc: qemu-stable@nongnu.org
Signed-off-by: Junjie Cao <junjie.cao@intel.com>
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
Message-ID: <20260324060100.1997-1-junjie.cao@intel.com>
---
hw/net/virtio-net.c | 21 ++++++++++++++-------
1 file changed, 14 insertions(+), 7 deletions(-)
diff --git a/hw/net/virtio-net.c b/hw/net/virtio-net.c
index 2a5d642a64..f0e3beb290 100644
--- a/hw/net/virtio-net.c
+++ b/hw/net/virtio-net.c
@@ -1374,6 +1374,11 @@ static void virtio_net_unload_ebpf(VirtIONet *n)
ebpf_rss_unload(&n->ebpf_rss);
}
+static bool virtio_net_rss_indirections_len_valid(uint16_t len)
+{
+ return is_power_of_2(len) && len <= VIRTIO_NET_RSS_MAX_TABLE_LEN;
+}
+
static uint16_t virtio_net_handle_rss(VirtIONet *n,
struct iovec *iov,
unsigned int iov_cnt,
@@ -1411,14 +1416,9 @@ static uint16_t virtio_net_handle_rss(VirtIONet *n,
if (!do_rss) {
n->rss_data.indirections_len = 0;
}
- if (n->rss_data.indirections_len >= VIRTIO_NET_RSS_MAX_TABLE_LEN) {
- err_msg = "Too large indirection table";
- err_value = n->rss_data.indirections_len;
- goto error;
- }
n->rss_data.indirections_len++;
- if (!is_power_of_2(n->rss_data.indirections_len)) {
- err_msg = "Invalid size of indirection table";
+ if (!virtio_net_rss_indirections_len_valid(n->rss_data.indirections_len)) {
+ err_msg = "Invalid indirection table length";
err_value = n->rss_data.indirections_len;
goto error;
}
@@ -3427,6 +3427,13 @@ static int virtio_net_rss_post_load(void *opaque, int version_id)
n->rss_data.supported_hash_types = VIRTIO_NET_RSS_SUPPORTED_HASHES;
}
+ if (!virtio_net_rss_indirections_len_valid(n->rss_data.indirections_len)) {
+ error_report("virtio-net: saved image has invalid RSS "
+ "indirections_len: %u",
+ n->rss_data.indirections_len);
+ return -EINVAL;
+ }
+
return 0;
}
--
MST
^ permalink raw reply related [flat|nested] 46+ messages in thread
* [PULL 00/44] pci, vhost, virtio, iommu: features, fixes, cleanups
@ 2026-07-05 14:36 Michael S. Tsirkin
2026-07-05 14:34 ` [PULL 01/44] amd_iommu: Fix opcode reported in invalid command handling Michael S. Tsirkin
` (44 more replies)
0 siblings, 45 replies; 46+ messages in thread
From: Michael S. Tsirkin @ 2026-07-05 14:36 UTC (permalink / raw)
To: qemu-devel; +Cc: Peter Maydell, stefanha
I hope I didn't miss any features for the freeze. If yours
is missing, pls holler.
There are some (old) bugs that have been reported and will
I hope get fixed before the release, I will focus on that
after the freeze.
The following changes since commit 7f2007d1924565c7a38b2c6ba01ebc1a85db0a49:
Merge tag 'pull-qobject-2026-07-02' of https://repo.or.cz/qemu/armbru into staging (2026-07-03 07:42:51 +0200)
are available in the Git repository at:
https://git.kernel.org/pub/scm/virt/kvm/mst/qemu.git tags/for_upstream
for you to fetch changes up to 32a90850e1e4222091df07b4b46d5f46a8b90d24:
virtio-net: validate RSS indirections_len in post_load (2026-07-05 09:06:13 -0400)
----------------------------------------------------------------
pci, vhost, virtio, iommu: features, fixes, cleanups
A new sp-mem device
New tests for vtd
New seg-max-adjust flag for vhost-user-blk
Watchdog support for arm/virt
Fixes, cleanups all over the place.
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
----------------------------------------------------------------
Alejandro Jimenez (4):
amd_iommu: Return int from page walk status helpers
amd_iommu: Decode XT interrupt control register without bitfields
amd_iommu: Decode IRTEs without bitfields
amd_iommu: Fix endianness handling for command buffer entries
David Hoppenbrouwers (1):
amd_iommu: Fix opcode reported in invalid command handling
Demi Marie Obenour (1):
vhost-user: Guarantee that memory regions do not overlap
GuoHan Zhao (3):
vhost-user-base: free virtqueue array during cleanup
vhost-user-base: clean up vhost_dev on realize failure
vhost-user-scmi: free vhost virtqueue array on cleanup
Igor Mammedov (13):
arm: sbsa_gwdt: fixup default "clock-frequency"
arm: add tracing events to sbsa_gwdt
arm: sbsa_gwdt: rename device type to sbsa-gwdt
arm: virt: create sbsa-gwdt watchdog
arm: sbsa-gwdt: add 'wdat' option
acpi: introduce WDAT table for GWDT
arm: virt: add support for WDAT based watchdog
tests: acpi: arm/virt: whitelist new WDAT table
tests: acpi: arm/virt: add WDAT table test case
tests: acpi: arm/virt: update expected WDAT blob
tests: acpi: arm/virt: whitelist GTDT table
tests: acpi: arm/virt: add GTDT watchdog table test case
tests: acpi: arm/virt: update expected GTDT blob
Junjie Cao (4):
tests/qtest/libqos: share Intel IOMMU test setup helpers
tests/qtest/libqos: add Intel IOMMU invalidation helpers
tests/qtest: add IOTLB invalidation test for Intel IOMMU
virtio-net: validate RSS indirections_len in post_load
Laurent Vivier (1):
hw/char/virtio-serial-bus: fix guest-triggerable OOM in control_out()
Sergei Heifetz (1):
vhost-user-blk: add seg-max-adjust flag
Stefan Hajnoczi (3):
vhost-user.rst: clarify when rings are started
libvhost-user: look for available vq buffers upon SET_VRING_KICK
vhost-user: inject kick after SET_VRING_KICK
fanhuang (10):
hw/mem: add sp-mem device for Specific Purpose Memory
qapi, hmp: introspection for the sp-mem device
i386/acpi-build: partition device_memory SRAT umbrella for sp-mem
hw/i386: hook sp-mem into the pc machine plug path
MAINTAINERS: cover sp-mem under Memory devices, add R: tag
tests/acpi: add empty expected blobs for sp-mem SRAT test
tests/acpi: add bios-tables-test case for sp-mem
tests/acpi: generate expected blobs for sp-mem SRAT test
tests/qtest: add e820 fw_cfg test
tests/qtest: cover sp-mem SOFT_RESERVED e820 entry
helei (1):
hw/virtio-crypto: enforce max akcipher key length
lizhaoxin04 (1):
vdpa: fix use-after-free of vqs in vhost_vdpa_device_unrealize
no92 (1):
intel_iommu: Correctly set pt bit in extended capability register
MAINTAINERS | 4 +
docs/interop/vhost-user.rst | 47 +++-
docs/system/arm/virt.rst | 24 +++
qapi/machine.json | 43 +++-
hw/i386/amd_iommu.h | 63 ------
hw/i386/e820_memory_layout.h | 11 +-
include/hw/acpi/wdat-gwdt.h | 19 ++
include/hw/arm/virt.h | 3 +
include/hw/mem/sp-mem.h | 33 +++
include/hw/virtio/vhost-user-blk.h | 1 +
include/hw/watchdog/sbsa_gwdt.h | 3 +-
tests/qtest/libqos/qos-intel-iommu.h | 74 +++++++
hw/acpi/wdat-gwdt-stub.c | 16 ++
hw/acpi/wdat-gwdt.c | 99 +++++++++
hw/arm/virt-acpi-build.c | 51 ++++-
hw/arm/virt.c | 48 +++++
hw/block/vhost-user-blk.c | 12 ++
hw/char/virtio-serial-bus.c | 34 +--
hw/core/machine-hmp-cmds.c | 11 +
hw/core/sysbus-fdt.c | 2 +
hw/i386/acpi-build.c | 96 ++++++++-
hw/i386/amd_iommu.c | 168 ++++++++++-----
hw/i386/intel_iommu.c | 4 +-
hw/i386/pc.c | 36 ++++
hw/mem/sp-mem.c | 128 +++++++++++
hw/net/virtio-net.c | 21 +-
hw/virtio/vdpa-dev.c | 3 +-
hw/virtio/vhost-user-base.c | 21 +-
hw/virtio/vhost-user-scmi.c | 12 +-
hw/virtio/vhost-user.c | 24 ++-
hw/virtio/virtio-crypto.c | 6 +
hw/watchdog/sbsa_gwdt.c | 24 ++-
subprojects/libvhost-user/libvhost-user.c | 19 +-
tests/qtest/bios-tables-test.c | 62 ++++++
tests/qtest/e820-test.c | 129 +++++++++++
tests/qtest/iommu-intel-inv-test.c | 346 ++++++++++++++++++++++++++++++
tests/qtest/iommu-intel-test.c | 78 +------
tests/qtest/libqos/qos-intel-iommu.c | 185 ++++++++++++++++
hw/acpi/meson.build | 2 +
hw/arm/Kconfig | 1 +
hw/i386/Kconfig | 2 +
hw/mem/Kconfig | 4 +
hw/mem/meson.build | 1 +
hw/watchdog/trace-events | 9 +
tests/data/acpi/aarch64/virt/GTDT.gwdt | Bin 0 -> 132 bytes
tests/data/acpi/aarch64/virt/WDAT.wdat | Bin 0 -> 260 bytes
tests/data/acpi/x86/q35/DSDT.spmem | Bin 0 -> 9910 bytes
tests/data/acpi/x86/q35/SRAT.spmem | Bin 0 -> 384 bytes
tests/qtest/meson.build | 3 +-
49 files changed, 1715 insertions(+), 267 deletions(-)
create mode 100644 include/hw/acpi/wdat-gwdt.h
create mode 100644 include/hw/mem/sp-mem.h
create mode 100644 hw/acpi/wdat-gwdt-stub.c
create mode 100644 hw/acpi/wdat-gwdt.c
create mode 100644 hw/mem/sp-mem.c
create mode 100644 tests/qtest/e820-test.c
create mode 100644 tests/qtest/iommu-intel-inv-test.c
create mode 100644 tests/data/acpi/aarch64/virt/GTDT.gwdt
create mode 100644 tests/data/acpi/aarch64/virt/WDAT.wdat
create mode 100644 tests/data/acpi/x86/q35/DSDT.spmem
create mode 100644 tests/data/acpi/x86/q35/SRAT.spmem
^ permalink raw reply [flat|nested] 46+ messages in thread
* Re: [PULL 00/44] pci, vhost, virtio, iommu: features, fixes, cleanups
2026-07-05 14:36 [PULL 00/44] pci, vhost, virtio, iommu: features, fixes, cleanups Michael S. Tsirkin
` (43 preceding siblings ...)
2026-07-05 14:36 ` [PULL 44/44] virtio-net: validate RSS indirections_len in post_load Michael S. Tsirkin
@ 2026-07-07 5:04 ` Stefan Hajnoczi
44 siblings, 0 replies; 46+ messages in thread
From: Stefan Hajnoczi @ 2026-07-07 5:04 UTC (permalink / raw)
To: Michael S. Tsirkin; +Cc: qemu-devel, Peter Maydell, stefanha
[-- Attachment #1: Type: text/plain, Size: 116 bytes --]
Applied, thanks.
Please update the changelog at https://wiki.qemu.org/ChangeLog/11.1 for any user-visible changes.
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]
^ permalink raw reply [flat|nested] 46+ messages in thread
end of thread, other threads:[~2026-07-07 5:04 UTC | newest]
Thread overview: 46+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-05 14:36 [PULL 00/44] pci, vhost, virtio, iommu: features, fixes, cleanups Michael S. Tsirkin
2026-07-05 14:34 ` [PULL 01/44] amd_iommu: Fix opcode reported in invalid command handling Michael S. Tsirkin
2026-07-05 14:34 ` [PULL 02/44] amd_iommu: Return int from page walk status helpers Michael S. Tsirkin
2026-07-05 14:34 ` [PULL 03/44] amd_iommu: Decode XT interrupt control register without bitfields Michael S. Tsirkin
2026-07-05 14:34 ` [PULL 04/44] amd_iommu: Decode IRTEs " Michael S. Tsirkin
2026-07-05 14:34 ` [PULL 05/44] amd_iommu: Fix endianness handling for command buffer entries Michael S. Tsirkin
2026-07-05 14:34 ` [PULL 06/44] hw/mem: add sp-mem device for Specific Purpose Memory Michael S. Tsirkin
2026-07-05 14:34 ` [PULL 07/44] qapi, hmp: introspection for the sp-mem device Michael S. Tsirkin
2026-07-05 14:34 ` [PULL 08/44] i386/acpi-build: partition device_memory SRAT umbrella for sp-mem Michael S. Tsirkin
2026-07-05 14:35 ` [PULL 09/44] hw/i386: hook sp-mem into the pc machine plug path Michael S. Tsirkin
2026-07-05 14:35 ` [PULL 10/44] MAINTAINERS: cover sp-mem under Memory devices, add R: tag Michael S. Tsirkin
2026-07-05 14:35 ` [PULL 11/44] tests/acpi: add empty expected blobs for sp-mem SRAT test Michael S. Tsirkin
2026-07-05 14:35 ` [PULL 12/44] tests/acpi: add bios-tables-test case for sp-mem Michael S. Tsirkin
2026-07-05 14:35 ` [PULL 13/44] tests/acpi: generate expected blobs for sp-mem SRAT test Michael S. Tsirkin
2026-07-05 14:35 ` [PULL 14/44] tests/qtest: add e820 fw_cfg test Michael S. Tsirkin
2026-07-05 14:35 ` [PULL 15/44] tests/qtest: cover sp-mem SOFT_RESERVED e820 entry Michael S. Tsirkin
2026-07-05 14:35 ` [PULL 16/44] intel_iommu: Correctly set pt bit in extended capability register Michael S. Tsirkin
2026-07-05 14:35 ` [PULL 17/44] vhost-user.rst: clarify when rings are started Michael S. Tsirkin
2026-07-05 14:35 ` [PULL 18/44] libvhost-user: look for available vq buffers upon SET_VRING_KICK Michael S. Tsirkin
2026-07-05 14:35 ` [PULL 19/44] vhost-user: inject kick after SET_VRING_KICK Michael S. Tsirkin
2026-07-05 14:35 ` [PULL 20/44] hw/char/virtio-serial-bus: fix guest-triggerable OOM in control_out() Michael S. Tsirkin
2026-07-05 14:35 ` [PULL 21/44] tests/qtest/libqos: share Intel IOMMU test setup helpers Michael S. Tsirkin
2026-07-05 14:35 ` [PULL 22/44] tests/qtest/libqos: add Intel IOMMU invalidation helpers Michael S. Tsirkin
2026-07-05 14:35 ` [PULL 23/44] tests/qtest: add IOTLB invalidation test for Intel IOMMU Michael S. Tsirkin
2026-07-05 14:35 ` [PULL 24/44] vhost-user-base: free virtqueue array during cleanup Michael S. Tsirkin
2026-07-05 14:35 ` [PULL 25/44] vhost-user-base: clean up vhost_dev on realize failure Michael S. Tsirkin
2026-07-05 14:35 ` [PULL 26/44] vdpa: fix use-after-free of vqs in vhost_vdpa_device_unrealize Michael S. Tsirkin
2026-07-05 14:35 ` [PULL 27/44] arm: sbsa_gwdt: fixup default "clock-frequency" Michael S. Tsirkin
2026-07-05 14:35 ` [PULL 28/44] arm: add tracing events to sbsa_gwdt Michael S. Tsirkin
2026-07-05 14:35 ` [PULL 29/44] arm: sbsa_gwdt: rename device type to sbsa-gwdt Michael S. Tsirkin
2026-07-05 14:35 ` [PULL 30/44] arm: virt: create sbsa-gwdt watchdog Michael S. Tsirkin
2026-07-05 14:35 ` [PULL 31/44] arm: sbsa-gwdt: add 'wdat' option Michael S. Tsirkin
2026-07-05 14:35 ` [PULL 32/44] acpi: introduce WDAT table for GWDT Michael S. Tsirkin
2026-07-05 14:36 ` [PULL 33/44] arm: virt: add support for WDAT based watchdog Michael S. Tsirkin
2026-07-05 14:36 ` [PULL 34/44] tests: acpi: arm/virt: whitelist new WDAT table Michael S. Tsirkin
2026-07-05 14:36 ` [PULL 35/44] tests: acpi: arm/virt: add WDAT table test case Michael S. Tsirkin
2026-07-05 14:36 ` [PULL 36/44] tests: acpi: arm/virt: update expected WDAT blob Michael S. Tsirkin
2026-07-05 14:36 ` [PULL 37/44] tests: acpi: arm/virt: whitelist GTDT table Michael S. Tsirkin
2026-07-05 14:36 ` [PULL 38/44] tests: acpi: arm/virt: add GTDT watchdog table test case Michael S. Tsirkin
2026-07-05 14:36 ` [PULL 39/44] tests: acpi: arm/virt: update expected GTDT blob Michael S. Tsirkin
2026-07-05 14:36 ` [PULL 40/44] vhost-user: Guarantee that memory regions do not overlap Michael S. Tsirkin
2026-07-05 14:36 ` [PULL 41/44] hw/virtio-crypto: enforce max akcipher key length Michael S. Tsirkin
2026-07-05 14:36 ` [PULL 42/44] vhost-user-scmi: free vhost virtqueue array on cleanup Michael S. Tsirkin
2026-07-05 14:36 ` [PULL 43/44] vhost-user-blk: add seg-max-adjust flag Michael S. Tsirkin
2026-07-05 14:36 ` [PULL 44/44] virtio-net: validate RSS indirections_len in post_load Michael S. Tsirkin
2026-07-07 5:04 ` [PULL 00/44] pci, vhost, virtio, iommu: features, fixes, cleanups Stefan Hajnoczi
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.