@samitouri / QOSamiQemu / commits / cf0210df65

amd_iommu: Generate XT interrupts when xt support is enabled

When MMIO 0x18[IntCapXTEn]=1, interrupts originating from the IOMMU itself are sent based on the programming in XT IOMMU Interrupt Control Registers in MMIO 0x170-0x180 instead of the programming in the IOMMU's MSI capability registers. The guest programs these registers with appropriate vector and destination ID instead of writing to PCI MSI capability. Current AMD vIOMMU is capable of generating interrupts only through PCI MSI capability and does not care about xt mode. Because of this AMD vIOMMU cannot generate event log interrupts when the guest has enabled xt mode. Introduce a new flag "intcapxten" which is set when guest writes control register [IntCapXTEn] (bit 51) and use vector and destination field in the XT MMIO register (0x170) to support XT mode. Signed-off-by: Sairaj Kodilkar <sarunkod@amd.com> Reviewed-by: Vasant Hegde <vasant.hegde@amd.com> Reviewed-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: <20260302115130.5903-4-sarunkod@amd.com>

Sairaj Kodilkar committed Mar 2, 2026 at 17:21 UTC cf0210df65aa4f582e444b910c950e2b4a80b194
3 files changed +59 -6
hw/i386/amd_iommu.c
+41 -6
@@ -192,18 +192,38 @@ static void amdvi_assign_andq(AMDVIState *s, hwaddr addr, uint64_t val)
192 amdvi_writeq_raw(s, addr, amdvi_readq(s, addr) & val);
193 }
194
195 +static void amdvi_build_xt_msi_msg(AMDVIState *s, MSIMessage *msg)
196 +{
197 + union mmio_xt_intr xt_reg;
198 + struct X86IOMMUIrq irq;
199 +
200 + xt_reg.val = amdvi_readq(s, AMDVI_MMIO_XT_GEN_INTR);
201 +
202 + irq.vector = xt_reg.vector;
203 + irq.delivery_mode = xt_reg.delivery_mode;
204 + irq.dest_mode = xt_reg.destination_mode;
205 + irq.dest = (xt_reg.destination_hi << 24) | xt_reg.destination_lo;
206 + irq.trigger_mode = 0;
207 + irq.redir_hint = 0;
208 +
209 + x86_iommu_irq_to_msi_message(&irq, msg);
210 +}
211 +
212 static void amdvi_generate_msi_interrupt(AMDVIState *s)
213 {
214 MSIMessage msg = {};
198 - MemTxAttrs attrs = {
199 - .requester_id = pci_requester_id(&s->pci->dev)
200 - };
215
202 - if (msi_enabled(&s->pci->dev)) {
216 + if (s->intcapxten) {
217 + trace_amdvi_generate_msi_interrupt("XT GEN");
218 + amdvi_build_xt_msi_msg(s, &msg);
219 + } else if (msi_enabled(&s->pci->dev)) {
220 + trace_amdvi_generate_msi_interrupt("MSI");
221 msg = msi_get_message(&s->pci->dev, 0);
204 - address_space_stl_le(&address_space_memory, msg.address, msg.data,
205 - attrs, NULL);
222 + } else {
223 + trace_amdvi_generate_msi_interrupt("NO MSI");
224 + return;
225 }
226 + apic_get_class(NULL)->send_msi(&msg);
227 }
228
229 static uint32_t get_next_eventlog_entry(AMDVIState *s)
@@ -1538,6 +1558,7 @@ const char *amdvi_mmio_get_name(hwaddr addr)
1558 MMIO_REG_TO_STRING(AMDVI_MMIO_PPR_BASE);
1559 MMIO_REG_TO_STRING(AMDVI_MMIO_PPR_HEAD);
1560 MMIO_REG_TO_STRING(AMDVI_MMIO_PPR_TAIL);
1561 + MMIO_REG_TO_STRING(AMDVI_MMIO_XT_GEN_INTR);
1562 #undef MMIO_REG_TO_STRING
1563 default:
1564 return "UNHANDLED";
@@ -1581,6 +1602,15 @@ static void amdvi_handle_control_write(AMDVIState *s)
1602 s->ga_enabled = !!(control & AMDVI_MMIO_CONTROL_GAEN);
1603 s->xten = !!(control & AMDVI_MMIO_CONTROL_XTEN) && s->xtsup &&
1604 s->ga_enabled;
1605 + /*
1606 + * IntCapXTEn controls whether IOMMU-originated interrupts are sent based
1607 + * on the information in XT IOMMU Interrupt Control Registers rather than
1608 + * the IOMMU’s MSI capability registers. Therefore it requires IOMMU
1609 + * x2APIC support capabilities (i.e. XTSup=1), but it is independent of
1610 + * whether a driver chooses to enable x2APIC mode for interrupt remapping
1611 + * (i.e. XTEn=1).
1612 + */
1613 + s->intcapxten = !!(control & AMDVI_MMIO_CONTROL_INTCAPXTEN) && s->xtsup;
1614
1615 /* update the flags depending on the control register */
1616 if (s->cmdbuf_enabled) {
@@ -1790,6 +1820,9 @@ static void amdvi_mmio_write(void *opaque, hwaddr addr, uint64_t val,
1820 case AMDVI_MMIO_STATUS:
1821 amdvi_mmio_reg_write(s, size, val, addr);
1822 break;
1823 + case AMDVI_MMIO_XT_GEN_INTR:
1824 + amdvi_mmio_reg_write(s, size, val, addr);
1825 + break;
1826 }
1827 }
1828
@@ -2440,6 +2473,7 @@ static void amdvi_init(AMDVIState *s)
2473 s->enabled = false;
2474 s->cmdbuf_enabled = false;
2475 s->xten = false;
2476 + s->intcapxten = false;
2477
2478 /* reset MMIO */
2479 memset(s->mmior, 0, AMDVI_MMIO_SIZE);
@@ -2510,6 +2544,7 @@ static const VMStateDescription vmstate_xt = {
2544 .minimum_version_id = 1,
2545 .fields = (VMStateField[]) {
2546 VMSTATE_BOOL(xten, AMDVIState),
2547 + VMSTATE_BOOL(intcapxten, AMDVIState),
2548 VMSTATE_END_OF_LIST()
2549 }
2550 };
hw/i386/amd_iommu.h
+17
@@ -53,6 +53,7 @@
53 #define AMDVI_MMIO_EXCL_BASE 0x0020
54 #define AMDVI_MMIO_EXCL_LIMIT 0x0028
55 #define AMDVI_MMIO_EXT_FEATURES 0x0030
56 +#define AMDVI_MMIO_XT_GEN_INTR 0x0170
57 #define AMDVI_MMIO_COMMAND_HEAD 0x2000
58 #define AMDVI_MMIO_COMMAND_TAIL 0x2008
59 #define AMDVI_MMIO_EVENT_HEAD 0x2010
@@ -103,6 +104,7 @@
104 #define AMDVI_MMIO_CONTROL_CMDBUFLEN (1ULL << 12)
105 #define AMDVI_MMIO_CONTROL_GAEN (1ULL << 17)
106 #define AMDVI_MMIO_CONTROL_XTEN (1ULL << 50)
107 +#define AMDVI_MMIO_CONTROL_INTCAPXTEN (1ULL << 51)
108
109 /* MMIO status register bits */
110 #define AMDVI_MMIO_STATUS_CMDBUF_RUN (1 << 4)
@@ -338,6 +340,20 @@ struct irte_ga {
340 union irte_ga_hi hi;
341 };
342
343 +union mmio_xt_intr {
344 + uint64_t val;
345 + struct {
346 + uint64_t rsvd_1:2,
347 + destination_mode:1,
348 + rsvd_2:5,
349 + destination_lo:24,
350 + vector:8,
351 + delivery_mode:1,
352 + rsvd_3:15,
353 + destination_hi:8;
354 + };
355 +};
356 +
357 #define TYPE_AMD_IOMMU_DEVICE "amd-iommu"
358 OBJECT_DECLARE_SIMPLE_TYPE(AMDVIState, AMD_IOMMU_DEVICE)
359
@@ -416,6 +432,7 @@ struct AMDVIState {
432 bool ga_enabled;
433 bool xtsup; /* xtsup=on command line */
434 bool xten; /* guest controlled, x2apic mode enabled */
435 + bool intcapxten; /* guest controlled, IOMMU x2apic interrupts enabled */
436
437 /* DMA address translation */
438 bool dma_remap;
hw/i386/trace-events
+1
@@ -118,6 +118,7 @@ amdvi_ir_intctl(uint8_t val) "int_ctl 0x%"PRIx8
118 amdvi_ir_target_abort(const char *str) "%s"
119 amdvi_ir_delivery_mode(const char *str) "%s"
120 amdvi_ir_irte_ga_val(uint64_t hi, uint64_t lo) "hi 0x%"PRIx64" lo 0x%"PRIx64
121 +amdvi_generate_msi_interrupt(const char *str) "Mode: %s"
122
123 # vmport.c
124 vmport_register(unsigned char command, void *func, void *opaque) "command: 0x%02x func: %p opaque: %p"