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 <[email protected]>
---
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