[PATCH] hw/i386: Fix VTD_ECAP_PT set in wrong register in vtd_cap_init()

Fengyuan Yu posted 1 patch 3 weeks, 4 days ago
Patches applied successfully (tree, apply log)
git fetch https://github.com/patchew-project/qemu tags/patchew/a3c74c60e05f6a71138dcb7f39f015b17e065654.1773324526.git.15fengyuan@gmail.com
Maintainers: "Michael S. Tsirkin" <mst@redhat.com>, Jason Wang <jasowang@redhat.com>, Yi Liu <yi.l.liu@intel.com>, "Clément Mathieu--Drif" <clement.mathieu--drif@bull.com>, Marcel Apfelbaum <marcel.apfelbaum@gmail.com>, Paolo Bonzini <pbonzini@redhat.com>, Richard Henderson <richard.henderson@linaro.org>, Eduardo Habkost <eduardo@habkost.net>
hw/i386/intel_iommu.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
[PATCH] hw/i386: Fix VTD_ECAP_PT set in wrong register in vtd_cap_init()
Posted by Fengyuan Yu 3 weeks, 4 days ago
VTD_ECAP_PT (bit 6, Pass Through Support) was incorrectly OR'd into
s->cap (Capability Register) instead of s->ecap (Extended Capability
Register) in vtd_cap_init().

Per VT-d spec Section 11.4.3, PT is bit 6 of the Extended Capability
Register, indicating hardware support for pass-through translation in
context-entries and scalable-mode PASID-table entries.

This caused vtd_pe_type_check() to always reject PGTT=4 (pass-through)
in scalable mode, since it correctly checks s->ecap & VTD_ECAP_PT,
which was never set.

Move VTD_ECAP_PT from s->cap to s->ecap initialization to fix
scalable-mode pass-through translation.

Reproduce:
$ ./check-vtd-ecap-pt.sh ./build/qemu-system-x86_64

Before fix: CAP bit 6: 1, ECAP bit 6: 0
After fix:  CAP bit 6: 0, ECAP bit 6: 1

```sh
#!/bin/bash
#
# check-vtd-ecap-pt.sh
# Check VTD_ECAP_PT (bit 6) in CAP/ECAP registers of emulated Intel IOMMU.
#
# Q35 IOMMU MMIO base = 0xfed90000 (VT-d spec Section 11.4)
#   CAP  register offset = 0x08 → address 0xfed90008
#   ECAP register offset = 0x10 → address 0xfed90010
#

QEMU="${1:-./build/qemu-system-x86_64}"

OUTPUT=$(echo '{"execute": "qmp_capabilities"}
{"execute": "human-monitor-command", "arguments": {"command-line": "xp/1gx 0xfed90008"}}
{"execute": "human-monitor-command", "arguments": {"command-line": "xp/1gx 0xfed90010"}}' \
| timeout 5 "$QEMU" -machine q35 \
    -device intel-iommu,x-scalable-mode=on \
    -display none -qmp stdio -nodefaults 2>&1)

CAP=$(echo "$OUTPUT"  | grep -oP 'fed90008: \K0x\w+')
ECAP=$(echo "$OUTPUT" | grep -oP 'fed90010: \K0x\w+')

echo " CAP ($CAP)  bit 6: $(( (CAP >> 6) & 1 ))"
echo "ECAP ($ECAP)  bit 6: $(( (ECAP >> 6) & 1 ))"
```

Signed-off-by: Fengyuan Yu <15fengyuan@gmail.com>
---
 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 f395fa248c..7b2cead8f8 100644
--- a/hw/i386/intel_iommu.c
+++ b/hw/i386/intel_iommu.c
@@ -4998,7 +4998,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) {
@@ -5009,7 +5009,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;
-- 
2.39.5