Skip to content

Commit 5d540b4

Browse files
rucodersesantosclaude
committed
pkg/xen-tools: refactor iGPU QEMU patch for q35 passthrough
Rework vfio_probe_igd_bar4_quirk() for correct iGPU passthrough on q35: - Move Intel VGA check to function top; exit early if not iGPU - Accept any gen >= 0 (Sandy Bridge through future devices); the old gen != 6 && gen != 8 check incorrectly blocked gen 9-12 devices - Read GMCH and emulate it unconditionally before the BDF/LPC checks, so UPT-mode guests on q35 also see a clean value - Write etc/igd-bdsm-size to fw_cfg before the BDF/LPC checks so OVMF's IgdAssignmentDxe can allocate guest stolen memory on q35 (the "Sorry Q35" path never reaches the old write location in legacy mode) - Emulate BDSM (PCI config 0x5C) to 0 before the BDF/LPC checks so IgdAssignmentDxe's idempotency guard (skip if BDSM != 0) is not falsely triggered by the host physical address - Remove duplicate fw_cfg, GMCH, and BDSM writes from the legacy path to prevent a QEMU crash from a duplicate fw_cfg key on i440fx Signed-off-by: Mikhail Malyshev <mike.malyshev@gmail.com> Co-Authored-By: sesantos <sergioliveirasantos@gmail.com> Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 9a2b752 commit 5d540b4

File tree

1 file changed

+145
-33
lines changed

1 file changed

+145
-33
lines changed
Lines changed: 145 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,26 @@
1-
From 9eb4883e57224db64588d724d5764cdccb164c7d Mon Sep 17 00:00:00 2001
2-
From: Petr Fedchenkov <giggsoff@gmail.com>
3-
Date: Mon, 11 Oct 2021 17:30:15 +0300
4-
Subject: [PATCH] Revert "Revert "vfio/pci-quirks.c: Disable stolen memory for
5-
igd VFIO"" and adopts it to the new qemu with moved vfio_probe_igd_bar4_quirk
6-
into igd.c.
1+
From 22e1ac33571bbb70740bb181e4729018b6402442 Mon Sep 17 00:00:00 2001
2+
From: Mikhail Malyshev <mike.malyshev@gmail.com>
3+
Date: Fri, 27 Mar 2026 23:26:25 +0000
4+
Subject: [PATCH 1/2] vfio/igd: q35 iGPU passthrough support
5+
(GMCH/BDSM/bdsm-size)
76

8-
This reverts commit 93587e3af3a259deac89c12863d93653d69d22b8.
97
---
10-
tools/qemu-xen/hw/vfio/igd.c | 64 ++++++++++++++++++++++++++++++---------------------
11-
1 file changed, 38 insertions(+), 26 deletions(-)
8+
hw/vfio/igd.c | 133 ++++++++++++++++++++++++++++++++++----------------
9+
1 file changed, 91 insertions(+), 42 deletions(-)
1210

1311
diff --git a/tools/qemu-xen/hw/vfio/igd.c b/tools/qemu-xen/hw/vfio/igd.c
14-
index 64e332746b..573cd53803 100644
12+
index d320d032a7..cb37f8ce08 100644
1513
--- a/tools/qemu-xen/hw/vfio/igd.c
1614
+++ b/tools/qemu-xen/hw/vfio/igd.c
17-
@@ -377,14 +377,45 @@ void vfio_probe_igd_bar4_quirk(VFIOPCIDevice *vdev, int nr)
15+
@@ -375,19 +375,98 @@ void vfio_probe_igd_bar4_quirk(VFIOPCIDevice *vdev, int nr)
16+
VFIOIGDQuirk *igd;
17+
PCIDevice *lpc_bridge;
18+
int i, ret, ggms_mb, gms_mb = 0, gen;
19+
- uint64_t *bdsm_size;
20+
uint32_t gmch;
1821
uint16_t cmd_orig, cmd;
1922
Error *err = NULL;
20-
23+
2124
+ /* This must be an Intel VGA device. */
2225
+ if (!vfio_pci_is(vdev, PCI_VENDOR_ID_INTEL, PCI_ANY_ID) ||
2326
+ !vfio_is_vga(vdev) || nr != 4 ) {
@@ -29,16 +32,16 @@ index 64e332746b..573cd53803 100644
2932
- * consider enabling legacy mode. The vBIOS has dependencies on the
3033
- * PCI bus address.
3134
+ * IGD is not a standard, they like to change their specs often. We
32-
+ * only attempt to support back to SandBridge and we hope that newer
33-
+ * devices maintain compatibility with generation 8.
35+
+ * support Sandy Bridge (gen 6) through Raptor Lake (gen 12) and assume
36+
+ * future devices maintain compatibility with the gen 8 GMCH layout.
3437
*/
3538
- if (!vfio_pci_is(vdev, PCI_VENDOR_ID_INTEL, PCI_ANY_ID) ||
3639
- !vfio_is_vga(vdev) || nr != 4 ||
3740
- &vdev->pdev != pci_find_device(pci_device_root_bus(&vdev->pdev),
3841
+ gen = igd_gen(vdev);
39-
+ if (gen != 6 && gen != 8) {
40-
+ error_report("IGD device %s is unsupported by IGD quirks, "
41-
+ "try SandyBridge or newer", vdev->vbasedev.name);
42+
+ if (gen < 0) {
43+
+ error_report("IGD device %s has unknown generation, skipping IGD quirks",
44+
+ vdev->vbasedev.name);
4245
+ return;
4346
+ }
4447
+
@@ -51,6 +54,40 @@ index 64e332746b..573cd53803 100644
5154
+ * this below.
5255
+ */
5356
+ gmch = vfio_pci_read_config(&vdev->pdev, IGD_GMCH, 4);
57+
+
58+
+ /*
59+
+ * Write the GMS-based stolen memory size to fw_cfg so that OVMF's
60+
+ * IgdAssignmentDxe can allocate guest stolen memory and set BDSM
61+
+ * (PCI config 0x5C). We do this before the BDF and LPC bridge checks
62+
+ * so the entry is available on q35 where the legacy-mode path never
63+
+ * runs ("Sorry Q35").
64+
+ *
65+
+ * GMS codes 1-16 map to 32-512 MB (code * 32 MB).
66+
+ */
67+
+ {
68+
+ uint32_t gms = (gmch >> (gen < 8 ? 3 : 8)) & (gen < 8 ? 0x1f : 0xff);
69+
+ uint64_t bdsm_bytes = (uint64_t)gms * 32 * MiB;
70+
+ error_report("IGD bdsm-size: device %04x gen=%d gmch=0x%08x "
71+
+ "gms=%u bdsm_bytes=%" PRIu64,
72+
+ vdev->device_id, gen, gmch, gms, bdsm_bytes);
73+
+ if (gms) {
74+
+ FWCfgState *fw_cfg = fw_cfg_find();
75+
+ if (fw_cfg) {
76+
+ uint64_t *bdsm_size = g_malloc(sizeof(*bdsm_size));
77+
+ *bdsm_size = cpu_to_le64(bdsm_bytes);
78+
+ fw_cfg_add_file(fw_cfg, "etc/igd-bdsm-size",
79+
+ bdsm_size, sizeof(*bdsm_size));
80+
+ error_report("IGD bdsm-size: wrote etc/igd-bdsm-size = %"
81+
+ PRIu64 " bytes to fw_cfg", bdsm_bytes);
82+
+ } else {
83+
+ error_report("IGD bdsm-size: fw_cfg not found, skipping");
84+
+ }
85+
+ } else {
86+
+ error_report("IGD bdsm-size: gms=0, no stolen memory "
87+
+ "reported by host GMCH");
88+
+ }
89+
+ }
90+
+
5491
+ gmch &= ~((gen < 8 ? 0x1f : 0xff) << (gen < 8 ? 3 : 8));
5592
+
5693
+ /* GMCH is read-only, emulated */
@@ -59,17 +96,32 @@ index 64e332746b..573cd53803 100644
5996
+ pci_set_long(vdev->emulated_config_bits + IGD_GMCH, ~0);
6097
+
6198
+ /*
99+
+ * BDSM is read-write, emulated. Initialize to 0 here (before the BDF
100+
+ * and LPC bridge checks) so that:
101+
+ * 1. On q35 ("Sorry Q35"), where the legacy path never runs, OVMF's
102+
+ * IgdAssignmentDxe sees BDSM=0 and proceeds to allocate guest
103+
+ * stolen memory, writing the guest PA back here.
104+
+ * 2. The guest driver reads the guest PA written by OVMF, not the
105+
+ * host PA (which would cause DMA into the wrong guest memory).
106+
+ * 3. IgdAssignmentDxe's idempotency check ("if BDSM != 0, skip")
107+
+ * is not falsely triggered by the host's physical address.
108+
+ */
109+
+ pci_set_long(vdev->pdev.config + IGD_BDSM, 0);
110+
+ pci_set_long(vdev->pdev.wmask + IGD_BDSM, ~0);
111+
+ pci_set_long(vdev->emulated_config_bits + IGD_BDSM, ~0);
112+
+
113+
+ /*
62114
+ * This must be at address 00:02.0 for us to even onsider enabling
63115
+ * legacy mode. The vBIOS has dependencies on the PCI bus address.
64116
+ */
65117
+ if (&vdev->pdev != pci_find_device(pci_device_root_bus(&vdev->pdev),
66118
0, PCI_DEVFN(0x2, 0))) {
67119
return;
68120
}
69-
@@ -403,18 +434,6 @@ void vfio_probe_igd_bar4_quirk(VFIOPCIDevice *vdev, int nr)
121+
@@ -406,18 +485,6 @@ void vfio_probe_igd_bar4_quirk(VFIOPCIDevice *vdev, int nr)
70122
return;
71123
}
72-
124+
73125
- /*
74126
- * IGD is not a standard, they like to change their specs often. We
75127
- * only attempt to support back to SandBridge and we hope that newer
@@ -85,21 +137,21 @@ index 64e332746b..573cd53803 100644
85137
/*
86138
* Most of what we're doing here is to enable the ROM to run, so if
87139
* there's no ROM, there's no point in setting up this quirk.
88-
@@ -470,8 +489,6 @@ void vfio_probe_igd_bar4_quirk(VFIOPCIDevice *vdev, int nr)
89-
goto out;
140+
@@ -473,8 +540,6 @@ void vfio_probe_igd_bar4_quirk(VFIOPCIDevice *vdev, int nr)
141+
return;
90142
}
91-
143+
92144
- gmch = vfio_pci_read_config(&vdev->pdev, IGD_GMCH, 4);
93145
-
94146
/*
95147
* If IGD VGA Disable is clear (expected) and VGA is not already enabled,
96148
* try to enable it. Probably shouldn't be using legacy mode without VGA,
97-
@@ -540,12 +557,12 @@ void vfio_probe_igd_bar4_quirk(VFIOPCIDevice *vdev, int nr)
149+
@@ -542,12 +607,12 @@ void vfio_probe_igd_bar4_quirk(VFIOPCIDevice *vdev, int nr)
98150
* when IVD (IGD VGA Disable) is clear, but the claim is that it's unused,
99151
* so let's not waste VM memory for it.
100152
*/
101153
- gmch &= ~((gen < 8 ? 0x1f : 0xff) << (gen < 8 ? 3 : 8));
102-
154+
103155
if (vdev->igd_gms) {
104156
if (vdev->igd_gms <= 0x10) {
105157
gms_mb = vdev->igd_gms * 32;
@@ -108,17 +160,77 @@ index 64e332746b..573cd53803 100644
108160
} else {
109161
error_report("Unsupported IGD GMS value 0x%x", vdev->igd_gms);
110162
vdev->igd_gms = 0;
111-
@@ -565,11 +582,6 @@ void vfio_probe_igd_bar4_quirk(VFIOPCIDevice *vdev, int nr)
112-
fw_cfg_add_file(fw_cfg_find(), "etc/igd-bdsm-size",
113-
bdsm_size, sizeof(*bdsm_size));
114-
163+
@@ -555,27 +620,11 @@ void vfio_probe_igd_bar4_quirk(VFIOPCIDevice *vdev, int nr)
164+
}
165+
166+
/*
167+
- * Request reserved memory for stolen memory via fw_cfg. VM firmware
168+
- * must allocate a 1MB aligned reserved memory region below 4GB with
169+
- * the requested size (in bytes) for use by the Intel PCI class VGA
170+
- * device at VM address 00:02.0. The base address of this reserved
171+
- * memory region must be written to the device BDSM register at PCI
172+
- * config offset 0x5C.
173+
+ * etc/igd-bdsm-size is written before the BDF check (above) so that it
174+
+ * is available on q35 machines where the legacy path never runs.
175+
+ * Do not write it again here to avoid a duplicate fw_cfg key.
176+
+ * BDSM emulation is also set up before the BDF check.
177+
*/
178+
- bdsm_size = g_malloc(sizeof(*bdsm_size));
179+
- *bdsm_size = cpu_to_le64((ggms_mb + gms_mb) * MiB);
180+
- fw_cfg_add_file(fw_cfg_find(), "etc/igd-bdsm-size",
181+
- bdsm_size, sizeof(*bdsm_size));
182+
-
115183
- /* GMCH is read-only, emulated */
116184
- pci_set_long(vdev->pdev.config + IGD_GMCH, gmch);
117185
- pci_set_long(vdev->pdev.wmask + IGD_GMCH, 0);
118186
- pci_set_long(vdev->emulated_config_bits + IGD_GMCH, ~0);
119187
-
120-
/* BDSM is read-write, emulated. The BIOS needs to be able to write it */
121-
pci_set_long(vdev->pdev.config + IGD_BDSM, 0);
122-
pci_set_long(vdev->pdev.wmask + IGD_BDSM, ~0);
123-
--
124-
2.30.2
188+
- /* BDSM is read-write, emulated. The BIOS needs to be able to write it */
189+
- pci_set_long(vdev->pdev.config + IGD_BDSM, 0);
190+
- pci_set_long(vdev->pdev.wmask + IGD_BDSM, ~0);
191+
- pci_set_long(vdev->emulated_config_bits + IGD_BDSM, ~0);
192+
193+
/*
194+
* This IOBAR gives us access to GTTADR, which allows us to write to
195+
--
196+
2.43.0
197+
198+
199+
From 77fe327406163bd4acfb1fccb1fc37d17b458ef3 Mon Sep 17 00:00:00 2001
200+
From: Mikhail Malyshev <mike.malyshev@gmail.com>
201+
Date: Sat, 28 Mar 2026 00:25:44 +0000
202+
Subject: [PATCH 2/2] vfio/igd: fix GMS stolen memory size for gen9+ 0xf0-0xff
203+
codes
204+
205+
---
206+
hw/vfio/igd.c | 11 +++++++++--
207+
1 file changed, 9 insertions(+), 2 deletions(-)
208+
209+
diff --git a/tools/qemu-xen/hw/vfio/igd.c b/tools/qemu-xen/hw/vfio/igd.c
210+
index cb37f8ce08..921cddfa16 100644
211+
--- a/tools/qemu-xen/hw/vfio/igd.c
212+
+++ b/tools/qemu-xen/hw/vfio/igd.c
213+
@@ -414,11 +414,18 @@ void vfio_probe_igd_bar4_quirk(VFIOPCIDevice *vdev, int nr)
214+
* so the entry is available on q35 where the legacy-mode path never
215+
* runs ("Sorry Q35").
216+
*
217+
- * GMS codes 1-16 map to 32-512 MB (code * 32 MB).
218+
+ * GMS encoding (gen >= 9): codes 0x01-0xef map to N*32 MB; codes
219+
+ * 0xf0-0xff map to (N-0xf0+1)*4 MB (4 MB granularity, Atom SKUs).
220+
+ * For gen < 9 the field is at most 5 bits so only 0x01-0x10 are valid.
221+
*/
222+
{
223+
uint32_t gms = (gmch >> (gen < 8 ? 3 : 8)) & (gen < 8 ? 0x1f : 0xff);
224+
- uint64_t bdsm_bytes = (uint64_t)gms * 32 * MiB;
225+
+ uint64_t bdsm_bytes;
226+
+ if (gen < 9 || gms < 0xf0) {
227+
+ bdsm_bytes = (uint64_t)gms * 32 * MiB;
228+
+ } else {
229+
+ bdsm_bytes = (uint64_t)(gms - 0xf0 + 1) * 4 * MiB;
230+
+ }
231+
error_report("IGD bdsm-size: device %04x gen=%d gmch=0x%08x "
232+
"gms=%u bdsm_bytes=%" PRIu64,
233+
vdev->device_id, gen, gmch, gms, bdsm_bytes);
234+
--
235+
2.43.0
236+

0 commit comments

Comments
 (0)