| 1 | /* |
| 2 | * IGD device quirks |
| 3 | * |
| 4 | * Copyright Red Hat, Inc. 2016 |
| 5 | * |
| 6 | * Authors: |
| 7 | * Alex Williamson <alex.williamson@redhat.com> |
| 8 | * |
| 9 | * This work is licensed under the terms of the GNU GPL, version 2. See |
| 10 | * the COPYING file in the top-level directory. |
| 11 | */ |
| 12 | |
| 13 | #include "qemu/osdep.h" |
| 14 | #include "qemu/units.h" |
| 15 | #include "qemu/error-report.h" |
| 16 | #include "qapi/error.h" |
| 17 | #include "qapi/qmp/qerror.h" |
| 18 | #include "hw/core/boards.h" |
| 19 | #include "hw/nvram/fw_cfg.h" |
| 20 | #include "pci.h" |
| 21 | #include "pci-quirks.h" |
| 22 | #include "trace.h" |
| 23 | |
| 24 | /* |
| 25 | * Intel IGD support |
| 26 | * |
| 27 | * Obviously IGD is not a discrete device, this is evidenced not only by it |
| 28 | * being integrated into the CPU, but by the various chipset and BIOS |
| 29 | * dependencies that it brings along with it. Intel is trying to move away |
| 30 | * from this and Broadwell and newer devices can run in what Intel calls |
| 31 | * "Universal Pass-Through" mode, or UPT. Theoretically in UPT mode, nothing |
| 32 | * more is required beyond assigning the IGD device to a VM. There are |
| 33 | * however support limitations to this mode. It only supports IGD as a |
| 34 | * secondary graphics device in the VM and it doesn't officially support any |
| 35 | * physical outputs. |
| 36 | * |
| 37 | * The code here attempts to enable what we'll call legacy mode assignment, |
| 38 | * IGD retains most of the capabilities we expect for it to have on bare |
| 39 | * metal. To enable this mode, the IGD device must be assigned to the VM |
| 40 | * at PCI address 00:02.0, it must have a ROM, it very likely needs VGA |
| 41 | * support, we must have VM BIOS support for reserving and populating some |
| 42 | * of the required tables, and we need to tweak the chipset with revisions |
| 43 | * and IDs and an LPC/ISA bridge device. The intention is to make all of |
| 44 | * this happen automatically by installing the device at the correct VM PCI |
| 45 | * bus address. If any of the conditions are not met, we cross our fingers |
| 46 | * and hope the user knows better. |
| 47 | * |
| 48 | * NB - It is possible to enable physical outputs in UPT mode by supplying |
| 49 | * an OpRegion table. We don't do this by default because the guest driver |
| 50 | * behaves differently if an OpRegion is provided and no monitor is attached |
| 51 | * vs no OpRegion and a monitor being attached or not. Effectively, if a |
| 52 | * headless setup is desired, the OpRegion gets in the way of that. |
| 53 | */ |
| 54 | |
| 55 | /* |
| 56 | * This presumes the device is already known to be an Intel VGA device, so we |
| 57 | * take liberties in which device ID bits match which generation. This should |
| 58 | * not be taken as an indication that all the devices are supported, or even |
| 59 | * supportable, some of them don't even support VT-d. |
| 60 | * See linux:include/drm/i915_pciids.h for IDs. |
| 61 | */ |
| 62 | static int igd_gen(VFIOPCIDevice *vdev) |
| 63 | { |
| 64 | /* |
| 65 | * Device IDs for Broxton/Apollo Lake are 0x0a84, 0x1a84, 0x1a85, 0x5a84 |
| 66 | * and 0x5a85, match bit 11:1 here |
| 67 | * Prefix 0x0a is taken by Haswell, this rule should be matched first. |
| 68 | */ |
| 69 | if ((vdev->device_id & 0xffe) == 0xa84) { |
| 70 | return 9; |
| 71 | } |
| 72 | |
| 73 | switch (vdev->device_id & 0xff00) { |
| 74 | case 0x0100: /* SandyBridge, IvyBridge */ |
| 75 | return 6; |
| 76 | case 0x0400: /* Haswell */ |
| 77 | case 0x0a00: /* Haswell */ |
| 78 | case 0x0c00: /* Haswell */ |
| 79 | case 0x0d00: /* Haswell */ |
| 80 | case 0x0f00: /* Valleyview/Bay Trail */ |
| 81 | return 7; |
| 82 | case 0x1600: /* Broadwell */ |
| 83 | case 0x2200: /* Cherryview */ |
| 84 | return 8; |
| 85 | case 0x1900: /* Skylake */ |
| 86 | case 0x3100: /* Gemini Lake */ |
| 87 | case 0x5900: /* Kaby Lake */ |
| 88 | case 0x3e00: /* Coffee Lake */ |
| 89 | case 0x9B00: /* Comet Lake */ |
| 90 | return 9; |
| 91 | case 0x8A00: /* Ice Lake */ |
| 92 | case 0x4500: /* Elkhart Lake */ |
| 93 | case 0x4E00: /* Jasper Lake */ |
| 94 | return 11; |
| 95 | case 0x9A00: /* Tiger Lake */ |
| 96 | case 0x4C00: /* Rocket Lake */ |
| 97 | case 0x4600: /* Alder Lake */ |
| 98 | case 0xA700: /* Raptor Lake */ |
| 99 | return 12; |
| 100 | } |
| 101 | |
| 102 | /* |
| 103 | * Unfortunately, Intel changes it's specification quite often. This makes |
| 104 | * it impossible to use a suitable default value for unknown devices. |
| 105 | * Return -1 for not applying any generation-specific quirks. |
| 106 | */ |
| 107 | return -1; |
| 108 | } |
| 109 | |
| 110 | #define IGD_ASLS 0xfc /* ASL Storage Register */ |
| 111 | #define IGD_GMCH 0x50 /* Graphics Control Register */ |
| 112 | #define IGD_BDSM 0x5c /* Base Data of Stolen Memory */ |
| 113 | #define IGD_BDSM_GEN11 0xc0 /* Base Data of Stolen Memory of gen 11 and later */ |
| 114 | |
| 115 | #define IGD_GMCH_VGA_DISABLE BIT(1) |
| 116 | #define IGD_GMCH_GEN6_GMS_SHIFT 3 /* SNB_GMCH in i915 */ |
| 117 | #define IGD_GMCH_GEN6_GMS_MASK 0x1f |
| 118 | #define IGD_GMCH_GEN8_GMS_SHIFT 8 /* BDW_GMCH in i915 */ |
| 119 | #define IGD_GMCH_GEN8_GMS_MASK 0xff |
| 120 | |
| 121 | static uint64_t igd_stolen_memory_size(int gen, uint32_t gmch) |
| 122 | { |
| 123 | uint64_t gms; |
| 124 | |
| 125 | if (gen < 8) { |
| 126 | gms = (gmch >> IGD_GMCH_GEN6_GMS_SHIFT) & IGD_GMCH_GEN6_GMS_MASK; |
| 127 | } else { |
| 128 | gms = (gmch >> IGD_GMCH_GEN8_GMS_SHIFT) & IGD_GMCH_GEN8_GMS_MASK; |
| 129 | } |
| 130 | |
| 131 | if (gen < 9) { |
| 132 | return gms * 32 * MiB; |
| 133 | } else { |
| 134 | if (gms < 0xf0) { |
| 135 | return gms * 32 * MiB; |
| 136 | } else { |
| 137 | return (gms - 0xf0 + 1) * 4 * MiB; |
| 138 | } |
| 139 | } |
| 140 | |
| 141 | return 0; |
| 142 | } |
| 143 | |
| 144 | /* |
| 145 | * The OpRegion includes the Video BIOS Table, which seems important for |
| 146 | * telling the driver what sort of outputs it has. Without this, the device |
| 147 | * may work in the guest, but we may not get output. This also requires BIOS |
| 148 | * support to reserve and populate a section of guest memory sufficient for |
| 149 | * the table and to write the base address of that memory to the ASLS register |
| 150 | * of the IGD device. |
| 151 | */ |
| 152 | static bool vfio_pci_igd_opregion_init(VFIOPCIDevice *vdev, |
| 153 | struct vfio_region_info *info, |
| 154 | Error **errp) |
| 155 | { |
| 156 | int ret; |
| 157 | |
| 158 | vdev->igd_opregion = g_malloc0(info->size); |
| 159 | ret = pread(vdev->vbasedev.fd, vdev->igd_opregion, |
| 160 | info->size, info->offset); |
| 161 | if (ret != info->size) { |
| 162 | error_setg(errp, "failed to read IGD OpRegion"); |
| 163 | g_free(vdev->igd_opregion); |
| 164 | vdev->igd_opregion = NULL; |
| 165 | return false; |
| 166 | } |
| 167 | |
| 168 | /* |
| 169 | * Provide fw_cfg with a copy of the OpRegion which the VM firmware is to |
| 170 | * allocate 32bit reserved memory for, copy these contents into, and write |
| 171 | * the reserved memory base address to the device ASLS register at 0xFC. |
| 172 | * Alignment of this reserved region seems flexible, but using a 4k page |
| 173 | * alignment seems to work well. This interface assumes a single IGD |
| 174 | * device, which may be at VM address 00:02.0 in legacy mode or another |
| 175 | * address in UPT mode. |
| 176 | * |
| 177 | * NB, there may be future use cases discovered where the VM should have |
| 178 | * direct interaction with the host OpRegion, in which case the write to |
| 179 | * the ASLS register would trigger MemoryRegion setup to enable that. |
| 180 | */ |
| 181 | fw_cfg_add_file(fw_cfg_find(), "etc/igd-opregion", |
| 182 | vdev->igd_opregion, info->size); |
| 183 | |
| 184 | trace_vfio_pci_igd_opregion_enabled(vdev->vbasedev.name); |
| 185 | |
| 186 | return true; |
| 187 | } |
| 188 | |
| 189 | static bool vfio_pci_igd_opregion_detect(VFIOPCIDevice *vdev, |
| 190 | struct vfio_region_info **opregion) |
| 191 | { |
| 192 | int ret; |
| 193 | |
| 194 | ret = vfio_device_get_region_info_type(&vdev->vbasedev, |
| 195 | VFIO_REGION_TYPE_PCI_VENDOR_TYPE | PCI_VENDOR_ID_INTEL, |
| 196 | VFIO_REGION_SUBTYPE_INTEL_IGD_OPREGION, opregion); |
| 197 | if (ret) { |
| 198 | return false; |
| 199 | } |
| 200 | |
| 201 | /* Hotplugging is not supported for opregion access */ |
| 202 | if (DEVICE(vdev)->hotplugged) { |
| 203 | warn_report("IGD device detected, but OpRegion is not supported " |
| 204 | "on hotplugged device."); |
| 205 | return false; |
| 206 | } |
| 207 | |
| 208 | return true; |
| 209 | } |
| 210 | |
| 211 | /* |
| 212 | * The rather short list of registers that we copy from the host devices. |
| 213 | * The LPC/ISA bridge values are definitely needed to support the vBIOS, the |
| 214 | * host bridge values may or may not be needed depending on the guest OS. |
| 215 | * Since we're only munging revision and subsystem values on the host bridge, |
| 216 | * we don't require our own device. The LPC/ISA bridge needs to be our very |
| 217 | * own though. |
| 218 | */ |
| 219 | typedef struct { |
| 220 | uint8_t offset; |
| 221 | uint8_t len; |
| 222 | } IGDHostInfo; |
| 223 | |
| 224 | static const IGDHostInfo igd_host_bridge_infos[] = { |
| 225 | {PCI_REVISION_ID, 2}, |
| 226 | {PCI_SUBSYSTEM_VENDOR_ID, 2}, |
| 227 | {PCI_SUBSYSTEM_ID, 2}, |
| 228 | }; |
| 229 | |
| 230 | static const IGDHostInfo igd_lpc_bridge_infos[] = { |
| 231 | {PCI_VENDOR_ID, 2}, |
| 232 | {PCI_DEVICE_ID, 2}, |
| 233 | {PCI_REVISION_ID, 2}, |
| 234 | {PCI_SUBSYSTEM_VENDOR_ID, 2}, |
| 235 | {PCI_SUBSYSTEM_ID, 2}, |
| 236 | }; |
| 237 | |
| 238 | static int vfio_pci_igd_copy(VFIOPCIDevice *vdev, PCIDevice *pdev, |
| 239 | struct vfio_region_info *info, |
| 240 | const IGDHostInfo *list, int len) |
| 241 | { |
| 242 | int i, ret; |
| 243 | |
| 244 | for (i = 0; i < len; i++) { |
| 245 | ret = pread(vdev->vbasedev.fd, pdev->config + list[i].offset, |
| 246 | list[i].len, info->offset + list[i].offset); |
| 247 | if (ret != list[i].len) { |
| 248 | error_report("IGD copy failed: %m"); |
| 249 | return -errno; |
| 250 | } |
| 251 | } |
| 252 | |
| 253 | return 0; |
| 254 | } |
| 255 | |
| 256 | /* |
| 257 | * Stuff a few values into the host bridge. |
| 258 | */ |
| 259 | static int vfio_pci_igd_host_init(VFIOPCIDevice *vdev, |
| 260 | struct vfio_region_info *info) |
| 261 | { |
| 262 | PCIDevice *pdev = PCI_DEVICE(vdev); |
| 263 | PCIBus *bus; |
| 264 | PCIDevice *host_bridge; |
| 265 | int ret; |
| 266 | |
| 267 | bus = pci_device_root_bus(pdev); |
| 268 | host_bridge = pci_find_device(bus, 0, PCI_DEVFN(0, 0)); |
| 269 | |
| 270 | if (!host_bridge) { |
| 271 | error_report("Can't find host bridge"); |
| 272 | return -ENODEV; |
| 273 | } |
| 274 | |
| 275 | ret = vfio_pci_igd_copy(vdev, host_bridge, info, igd_host_bridge_infos, |
| 276 | ARRAY_SIZE(igd_host_bridge_infos)); |
| 277 | if (!ret) { |
| 278 | trace_vfio_pci_igd_host_bridge_enabled(vdev->vbasedev.name); |
| 279 | } |
| 280 | |
| 281 | return ret; |
| 282 | } |
| 283 | |
| 284 | /* |
| 285 | * IGD LPC/ISA bridge support code. The vBIOS needs this, but we can't write |
| 286 | * arbitrary values into just any bridge, so we must create our own. We try |
| 287 | * to handle if the user has created it for us, which they might want to do |
| 288 | * to enable multifunction so we don't occupy the whole PCI slot. |
| 289 | */ |
| 290 | static void vfio_pci_igd_lpc_bridge_realize(PCIDevice *pdev, Error **errp) |
| 291 | { |
| 292 | if (pdev->devfn != PCI_DEVFN(0x1f, 0)) { |
| 293 | error_setg(errp, "VFIO dummy ISA/LPC bridge must have address 1f.0"); |
| 294 | } |
| 295 | } |
| 296 | |
| 297 | static void vfio_pci_igd_lpc_bridge_class_init(ObjectClass *klass, |
| 298 | const void *data) |
| 299 | { |
| 300 | DeviceClass *dc = DEVICE_CLASS(klass); |
| 301 | PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); |
| 302 | |
| 303 | set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories); |
| 304 | dc->desc = "VFIO dummy ISA/LPC bridge for IGD assignment"; |
| 305 | dc->hotpluggable = false; |
| 306 | k->realize = vfio_pci_igd_lpc_bridge_realize; |
| 307 | k->class_id = PCI_CLASS_BRIDGE_ISA; |
| 308 | } |
| 309 | |
| 310 | static const TypeInfo vfio_pci_igd_lpc_bridge_info = { |
| 311 | .name = "vfio-pci-igd-lpc-bridge", |
| 312 | .parent = TYPE_PCI_DEVICE, |
| 313 | .class_init = vfio_pci_igd_lpc_bridge_class_init, |
| 314 | .interfaces = (const InterfaceInfo[]) { |
| 315 | { INTERFACE_CONVENTIONAL_PCI_DEVICE }, |
| 316 | { }, |
| 317 | }, |
| 318 | }; |
| 319 | |
| 320 | static void vfio_pci_igd_register_types(void) |
| 321 | { |
| 322 | type_register_static(&vfio_pci_igd_lpc_bridge_info); |
| 323 | } |
| 324 | |
| 325 | type_init(vfio_pci_igd_register_types) |
| 326 | |
| 327 | static int vfio_pci_igd_lpc_init(VFIOPCIDevice *vdev, |
| 328 | struct vfio_region_info *info) |
| 329 | { |
| 330 | PCIDevice *pdev = PCI_DEVICE(vdev); |
| 331 | PCIDevice *lpc_bridge; |
| 332 | int ret; |
| 333 | |
| 334 | lpc_bridge = pci_find_device(pci_device_root_bus(pdev), |
| 335 | 0, PCI_DEVFN(0x1f, 0)); |
| 336 | if (!lpc_bridge) { |
| 337 | lpc_bridge = pci_create_simple(pci_device_root_bus(pdev), |
| 338 | PCI_DEVFN(0x1f, 0), "vfio-pci-igd-lpc-bridge"); |
| 339 | } |
| 340 | |
| 341 | ret = vfio_pci_igd_copy(vdev, lpc_bridge, info, igd_lpc_bridge_infos, |
| 342 | ARRAY_SIZE(igd_lpc_bridge_infos)); |
| 343 | if (!ret) { |
| 344 | trace_vfio_pci_igd_lpc_bridge_enabled(vdev->vbasedev.name); |
| 345 | } |
| 346 | |
| 347 | return ret; |
| 348 | } |
| 349 | |
| 350 | static bool vfio_pci_igd_setup_lpc_bridge(VFIOPCIDevice *vdev, Error **errp) |
| 351 | { |
| 352 | struct vfio_region_info *host = NULL; |
| 353 | struct vfio_region_info *lpc = NULL; |
| 354 | PCIDevice *pdev = PCI_DEVICE(vdev); |
| 355 | PCIDevice *lpc_bridge; |
| 356 | int ret; |
| 357 | |
| 358 | /* |
| 359 | * Copying IDs or creating new devices are not supported on hotplug |
| 360 | */ |
| 361 | if (DEVICE(vdev)->hotplugged) { |
| 362 | error_setg(errp, "IGD LPC is not supported on hotplugged device"); |
| 363 | return false; |
| 364 | } |
| 365 | |
| 366 | /* |
| 367 | * We need to create an LPC/ISA bridge at PCI bus address 00:1f.0 that we |
| 368 | * can stuff host values into, so if there's already one there and it's not |
| 369 | * one we can hack on, this quirk is no-go. Sorry Q35. |
| 370 | */ |
| 371 | lpc_bridge = pci_find_device(pci_device_root_bus(pdev), |
| 372 | 0, PCI_DEVFN(0x1f, 0)); |
| 373 | if (lpc_bridge && !object_dynamic_cast(OBJECT(lpc_bridge), |
| 374 | "vfio-pci-igd-lpc-bridge")) { |
| 375 | error_setg(errp, |
| 376 | "Cannot create LPC bridge due to existing device at 1f.0"); |
| 377 | return false; |
| 378 | } |
| 379 | |
| 380 | /* |
| 381 | * Check whether we have all the vfio device specific regions to |
| 382 | * support LPC quirk (added in Linux v4.6). |
| 383 | */ |
| 384 | ret = vfio_device_get_region_info_type(&vdev->vbasedev, |
| 385 | VFIO_REGION_TYPE_PCI_VENDOR_TYPE | PCI_VENDOR_ID_INTEL, |
| 386 | VFIO_REGION_SUBTYPE_INTEL_IGD_LPC_CFG, &lpc); |
| 387 | if (ret) { |
| 388 | error_setg(errp, "IGD LPC bridge access is not supported by kernel"); |
| 389 | return false; |
| 390 | } |
| 391 | |
| 392 | ret = vfio_device_get_region_info_type(&vdev->vbasedev, |
| 393 | VFIO_REGION_TYPE_PCI_VENDOR_TYPE | PCI_VENDOR_ID_INTEL, |
| 394 | VFIO_REGION_SUBTYPE_INTEL_IGD_HOST_CFG, &host); |
| 395 | if (ret) { |
| 396 | error_setg(errp, "IGD host bridge access is not supported by kernel"); |
| 397 | return false; |
| 398 | } |
| 399 | |
| 400 | /* Create/modify LPC bridge */ |
| 401 | ret = vfio_pci_igd_lpc_init(vdev, lpc); |
| 402 | if (ret) { |
| 403 | error_setg(errp, "Failed to create/modify LPC bridge for IGD"); |
| 404 | return false; |
| 405 | } |
| 406 | |
| 407 | /* Stuff some host values into the VM PCI host bridge */ |
| 408 | ret = vfio_pci_igd_host_init(vdev, host); |
| 409 | if (ret) { |
| 410 | error_setg(errp, "Failed to modify host bridge for IGD"); |
| 411 | return false; |
| 412 | } |
| 413 | |
| 414 | return true; |
| 415 | } |
| 416 | |
| 417 | static bool vfio_pci_igd_override_gms(int gen, uint32_t gms, uint32_t *gmch) |
| 418 | { |
| 419 | bool ret = false; |
| 420 | |
| 421 | if (gen == -1) { |
| 422 | error_report("x-igd-gms is not supported on this device"); |
| 423 | } else if (gen < 8) { |
| 424 | if (gms <= 0x10) { |
| 425 | *gmch &= ~(IGD_GMCH_GEN6_GMS_MASK << IGD_GMCH_GEN6_GMS_SHIFT); |
| 426 | *gmch |= gms << IGD_GMCH_GEN6_GMS_SHIFT; |
| 427 | ret = true; |
| 428 | } else { |
| 429 | error_report(QERR_INVALID_PARAMETER_VALUE, "x-igd-gms", "0~0x10"); |
| 430 | } |
| 431 | } else if (gen == 8) { |
| 432 | if (gms <= 0x40) { |
| 433 | *gmch &= ~(IGD_GMCH_GEN8_GMS_MASK << IGD_GMCH_GEN8_GMS_SHIFT); |
| 434 | *gmch |= gms << IGD_GMCH_GEN8_GMS_SHIFT; |
| 435 | ret = true; |
| 436 | } else { |
| 437 | error_report(QERR_INVALID_PARAMETER_VALUE, "x-igd-gms", "0~0x40"); |
| 438 | } |
| 439 | } else { |
| 440 | /* 0x0 to 0x40: 32MB increments starting at 0MB */ |
| 441 | /* 0xf0 to 0xfe: 4MB increments starting at 4MB */ |
| 442 | if ((gms <= 0x40) || (gms >= 0xf0 && gms <= 0xfe)) { |
| 443 | *gmch &= ~(IGD_GMCH_GEN8_GMS_MASK << IGD_GMCH_GEN8_GMS_SHIFT); |
| 444 | *gmch |= gms << IGD_GMCH_GEN8_GMS_SHIFT; |
| 445 | ret = true; |
| 446 | } else { |
| 447 | error_report(QERR_INVALID_PARAMETER_VALUE, |
| 448 | "x-igd-gms", "0~0x40 or 0xf0~0xfe"); |
| 449 | } |
| 450 | } |
| 451 | |
| 452 | return ret; |
| 453 | } |
| 454 | |
| 455 | #define IGD_GGC_MMIO_OFFSET 0x108040 |
| 456 | #define IGD_BDSM_MMIO_OFFSET 0x1080C0 |
| 457 | |
| 458 | void vfio_probe_igd_bar0_quirk(VFIOPCIDevice *vdev, int nr) |
| 459 | { |
| 460 | VFIOQuirk *ggc_quirk, *bdsm_quirk; |
| 461 | VFIOConfigMirrorQuirk *ggc_mirror, *bdsm_mirror; |
| 462 | int gen; |
| 463 | |
| 464 | if (!vfio_pci_is(vdev, PCI_VENDOR_ID_INTEL, PCI_ANY_ID) || |
| 465 | !vfio_is_base_display(vdev) || nr != 0) { |
| 466 | return; |
| 467 | } |
| 468 | |
| 469 | /* Only on IGD Gen6-12 device needs quirks in BAR 0 */ |
| 470 | gen = igd_gen(vdev); |
| 471 | if (gen < 6) { |
| 472 | return; |
| 473 | } |
| 474 | |
| 475 | if (vdev->igd_gms) { |
| 476 | ggc_quirk = vfio_quirk_alloc(1); |
| 477 | ggc_mirror = ggc_quirk->data = g_malloc0(sizeof(*ggc_mirror)); |
| 478 | ggc_mirror->mem = ggc_quirk->mem; |
| 479 | ggc_mirror->vdev = vdev; |
| 480 | ggc_mirror->bar = nr; |
| 481 | ggc_mirror->offset = IGD_GGC_MMIO_OFFSET; |
| 482 | ggc_mirror->config_offset = IGD_GMCH; |
| 483 | |
| 484 | memory_region_init_io(ggc_mirror->mem, OBJECT(vdev), |
| 485 | &vfio_generic_mirror_quirk, ggc_mirror, |
| 486 | "vfio-igd-ggc-quirk", 2); |
| 487 | memory_region_add_subregion_overlap(vdev->bars[nr].region.mem, |
| 488 | ggc_mirror->offset, ggc_mirror->mem, |
| 489 | 1); |
| 490 | |
| 491 | QLIST_INSERT_HEAD(&vdev->bars[nr].quirks, ggc_quirk, next); |
| 492 | } |
| 493 | |
| 494 | bdsm_quirk = vfio_quirk_alloc(1); |
| 495 | bdsm_mirror = bdsm_quirk->data = g_malloc0(sizeof(*bdsm_mirror)); |
| 496 | bdsm_mirror->mem = bdsm_quirk->mem; |
| 497 | bdsm_mirror->vdev = vdev; |
| 498 | bdsm_mirror->bar = nr; |
| 499 | bdsm_mirror->offset = IGD_BDSM_MMIO_OFFSET; |
| 500 | bdsm_mirror->config_offset = (gen < 11) ? IGD_BDSM : IGD_BDSM_GEN11; |
| 501 | |
| 502 | memory_region_init_io(bdsm_mirror->mem, OBJECT(vdev), |
| 503 | &vfio_generic_mirror_quirk, bdsm_mirror, |
| 504 | "vfio-igd-bdsm-quirk", (gen < 11) ? 4 : 8); |
| 505 | memory_region_add_subregion_overlap(vdev->bars[nr].region.mem, |
| 506 | bdsm_mirror->offset, bdsm_mirror->mem, |
| 507 | 1); |
| 508 | |
| 509 | QLIST_INSERT_HEAD(&vdev->bars[nr].quirks, bdsm_quirk, next); |
| 510 | } |
| 511 | |
| 512 | static bool vfio_pci_igd_config_quirk(VFIOPCIDevice *vdev, Error **errp) |
| 513 | { |
| 514 | struct vfio_region_info *opregion = NULL; |
| 515 | PCIDevice *pdev = PCI_DEVICE(vdev); |
| 516 | int ret, gen; |
| 517 | uint64_t gms_size = 0; |
| 518 | uint64_t *bdsm_size; |
| 519 | uint32_t gmch; |
| 520 | bool legacy_mode_enabled = false; |
| 521 | Error *err = NULL; |
| 522 | |
| 523 | if (!vfio_pci_is(vdev, PCI_VENDOR_ID_INTEL, PCI_ANY_ID) || |
| 524 | !vfio_is_base_display(vdev)) { |
| 525 | return true; |
| 526 | } |
| 527 | |
| 528 | /* IGD device always comes with OpRegion */ |
| 529 | if (!vfio_pci_igd_opregion_detect(vdev, &opregion)) { |
| 530 | return true; |
| 531 | } |
| 532 | info_report("OpRegion detected on Intel display %x.", vdev->device_id); |
| 533 | |
| 534 | gen = igd_gen(vdev); |
| 535 | gmch = vfio_pci_read_config(pdev, IGD_GMCH, 4); |
| 536 | |
| 537 | /* |
| 538 | * For backward compatibility, enable legacy mode when |
| 539 | * - Device geneation is 6 to 9 (including both) |
| 540 | * - IGD exposes itself as VGA controller and claims VGA cycles on host |
| 541 | * - Machine type is i440fx (pc_piix) |
| 542 | * - IGD device is at guest BDF 00:02.0 |
| 543 | * - Not manually disabled by x-igd-legacy-mode=off |
| 544 | */ |
| 545 | if ((vdev->igd_legacy_mode != ON_OFF_AUTO_OFF) && |
| 546 | vfio_is_vga(vdev) && |
| 547 | (gen >= 6 && gen <= 9) && |
| 548 | !(gmch & IGD_GMCH_VGA_DISABLE) && |
| 549 | !strcmp(MACHINE_GET_CLASS(qdev_get_machine())->family, "pc_piix") && |
| 550 | (pdev == pci_find_device(pci_device_root_bus(pdev), |
| 551 | 0, PCI_DEVFN(0x2, 0)))) { |
| 552 | /* |
| 553 | * IGD legacy mode requires: |
| 554 | * - VBIOS in ROM BAR or file |
| 555 | * - VGA IO/MMIO ranges are claimed by IGD |
| 556 | * - OpRegion |
| 557 | * - Same LPC bridge and Host bridge VID/DID/SVID/SSID as host |
| 558 | */ |
| 559 | struct vfio_region_info *rom = NULL; |
| 560 | |
| 561 | legacy_mode_enabled = true; |
| 562 | info_report("IGD legacy mode enabled, " |
| 563 | "use x-igd-legacy-mode=off to disable it if unwanted."); |
| 564 | |
| 565 | /* |
| 566 | * Most of what we're doing here is to enable the ROM to run, so if |
| 567 | * there's no ROM, there's no point in setting up this quirk. |
| 568 | * NB. We only seem to get BIOS ROMs, so UEFI VM would need CSM support. |
| 569 | */ |
| 570 | ret = vfio_device_get_region_info(&vdev->vbasedev, |
| 571 | VFIO_PCI_ROM_REGION_INDEX, &rom); |
| 572 | if ((ret || !rom->size) && !pdev->romfile) { |
| 573 | error_setg(&err, "Device has no ROM"); |
| 574 | goto error; |
| 575 | } |
| 576 | |
| 577 | /* |
| 578 | * If VGA is not already enabled, try to enable it. We shouldn't be |
| 579 | * using legacy mode without VGA. |
| 580 | */ |
| 581 | if (!vdev->vga) { |
| 582 | if (vfio_populate_vga(vdev, &err)) { |
| 583 | vfio_pci_config_register_vga(vdev); |
| 584 | } else { |
| 585 | error_setg(&err, "Unable to enable VGA access"); |
| 586 | goto error; |
| 587 | } |
| 588 | } |
| 589 | |
| 590 | /* Enable OpRegion and LPC bridge quirk */ |
| 591 | vdev->features |= VFIO_FEATURE_ENABLE_IGD_OPREGION; |
| 592 | vdev->features |= VFIO_FEATURE_ENABLE_IGD_LPC; |
| 593 | } else if (vdev->igd_legacy_mode == ON_OFF_AUTO_ON) { |
| 594 | error_setg(&err, |
| 595 | "Machine is not i440fx, assigned BDF is not 00:02.0, " |
| 596 | "or device %04x (gen %d) doesn't support legacy mode", |
| 597 | vdev->device_id, gen); |
| 598 | goto error; |
| 599 | } |
| 600 | |
| 601 | /* Setup OpRegion access */ |
| 602 | if ((vdev->features & VFIO_FEATURE_ENABLE_IGD_OPREGION) && |
| 603 | !vfio_pci_igd_opregion_init(vdev, opregion, errp)) { |
| 604 | goto error; |
| 605 | } |
| 606 | |
| 607 | /* Setup LPC bridge / Host bridge PCI IDs */ |
| 608 | if ((vdev->features & VFIO_FEATURE_ENABLE_IGD_LPC) && |
| 609 | !vfio_pci_igd_setup_lpc_bridge(vdev, errp)) { |
| 610 | goto error; |
| 611 | } |
| 612 | |
| 613 | /* |
| 614 | * ASLS (OpRegion address) is read-only, emulated |
| 615 | * It contains HPA, guest firmware need to reprogram it with GPA. |
| 616 | */ |
| 617 | pci_set_long(pdev->config + IGD_ASLS, 0); |
| 618 | pci_set_long(pdev->wmask + IGD_ASLS, ~0); |
| 619 | pci_set_long(vdev->emulated_config_bits + IGD_ASLS, ~0); |
| 620 | |
| 621 | /* |
| 622 | * Allow user to override dsm size using x-igd-gms option, in multiples of |
| 623 | * 32MiB. This option should only be used when the desired size cannot be |
| 624 | * set from DVMT Pre-Allocated option in host BIOS. |
| 625 | */ |
| 626 | if (vdev->igd_gms) { |
| 627 | if (!vfio_pci_igd_override_gms(gen, vdev->igd_gms, &gmch)) { |
| 628 | return false; |
| 629 | } |
| 630 | |
| 631 | /* GMCH is read-only, emulated */ |
| 632 | pci_set_long(pdev->config + IGD_GMCH, gmch); |
| 633 | pci_set_long(pdev->wmask + IGD_GMCH, 0); |
| 634 | pci_set_long(vdev->emulated_config_bits + IGD_GMCH, ~0); |
| 635 | } |
| 636 | |
| 637 | if (gen > 0) { |
| 638 | gms_size = igd_stolen_memory_size(gen, gmch); |
| 639 | |
| 640 | /* BDSM is read-write, emulated. BIOS needs to be able to write it */ |
| 641 | if (gen < 11) { |
| 642 | pci_set_long(pdev->config + IGD_BDSM, 0); |
| 643 | pci_set_long(pdev->wmask + IGD_BDSM, ~0); |
| 644 | pci_set_long(vdev->emulated_config_bits + IGD_BDSM, ~0); |
| 645 | } else { |
| 646 | pci_set_quad(pdev->config + IGD_BDSM_GEN11, 0); |
| 647 | pci_set_quad(pdev->wmask + IGD_BDSM_GEN11, ~0); |
| 648 | pci_set_quad(vdev->emulated_config_bits + IGD_BDSM_GEN11, ~0); |
| 649 | } |
| 650 | } |
| 651 | |
| 652 | /* |
| 653 | * Request reserved memory for stolen memory via fw_cfg. VM firmware |
| 654 | * must allocate a 1MB aligned reserved memory region below 4GB with |
| 655 | * the requested size (in bytes) for use by the IGD device. The base |
| 656 | * address of this reserved memory region must be written to the |
| 657 | * device BDSM register. |
| 658 | * For newer device without BDSM register, this fw_cfg item is 0. |
| 659 | */ |
| 660 | bdsm_size = g_malloc(sizeof(*bdsm_size)); |
| 661 | *bdsm_size = cpu_to_le64(gms_size); |
| 662 | fw_cfg_add_file(fw_cfg_find(), "etc/igd-bdsm-size", |
| 663 | bdsm_size, sizeof(*bdsm_size)); |
| 664 | |
| 665 | trace_vfio_pci_igd_bdsm_enabled(vdev->vbasedev.name, (gms_size / MiB)); |
| 666 | |
| 667 | return true; |
| 668 | |
| 669 | error: |
| 670 | /* |
| 671 | * When legacy mode is implicity enabled, continue on error, |
| 672 | * to keep compatibility |
| 673 | */ |
| 674 | if (legacy_mode_enabled && (vdev->igd_legacy_mode == ON_OFF_AUTO_AUTO)) { |
| 675 | error_report_err(err); |
| 676 | error_report("IGD legacy mode disabled"); |
| 677 | return true; |
| 678 | } |
| 679 | |
| 680 | error_propagate(errp, err); |
| 681 | return false; |
| 682 | } |
| 683 | |
| 684 | /* |
| 685 | * KVMGT/GVT-g vGPU exposes an emulated OpRegion. So far, users have to specify |
| 686 | * x-igd-opregion=on to enable the access. |
| 687 | * TODO: Check VID/DID and enable opregion access automatically |
| 688 | */ |
| 689 | static bool vfio_pci_kvmgt_config_quirk(VFIOPCIDevice *vdev, Error **errp) |
| 690 | { |
| 691 | struct vfio_region_info *opregion = NULL; |
| 692 | int gen; |
| 693 | |
| 694 | if (!vfio_pci_is(vdev, PCI_VENDOR_ID_INTEL, PCI_ANY_ID) || |
| 695 | !vfio_is_vga(vdev)) { |
| 696 | return true; |
| 697 | } |
| 698 | |
| 699 | /* FIXME: Cherryview is Gen8, but don't support GVT-g */ |
| 700 | gen = igd_gen(vdev); |
| 701 | if (gen != 8 && gen != 9) { |
| 702 | return true; |
| 703 | } |
| 704 | |
| 705 | if (!vfio_pci_igd_opregion_detect(vdev, &opregion)) { |
| 706 | /* Should never reach here, KVMGT always emulates OpRegion */ |
| 707 | return false; |
| 708 | } |
| 709 | |
| 710 | if ((vdev->features & VFIO_FEATURE_ENABLE_IGD_OPREGION) && |
| 711 | !vfio_pci_igd_opregion_init(vdev, opregion, errp)) { |
| 712 | return false; |
| 713 | } |
| 714 | |
| 715 | return true; |
| 716 | } |
| 717 | |
| 718 | bool vfio_probe_igd_config_quirk(VFIOPCIDevice *vdev, Error **errp) |
| 719 | { |
| 720 | /* KVMGT/GVT-g vGPU is exposed as mdev */ |
| 721 | if (vdev->vbasedev.mdev) { |
| 722 | return vfio_pci_kvmgt_config_quirk(vdev, errp); |
| 723 | } |
| 724 | |
| 725 | return vfio_pci_igd_config_quirk(vdev, errp); |
| 726 | } |
| 727 | |
| 728 | /* |
| 729 | * IGD ROM BAR read from kernel is actually the host VBIOS shadow RAM region, |
| 730 | * which contains host modifications. In Gen 6-9 VBIOS, the routine below is |
| 731 | * used to get BDSM value when programming the initial GTT. |
| 732 | * xx xx xx xx v: .long ? # saved value |
| 733 | * 66 53 push %ebx |
| 734 | * 66 2e 83 3e xx xx 00 cmpl $0x0,%cs:v # is saved value empty? |
| 735 | * 74 07 je 1f # if zero, go compute |
| 736 | * 66 2e a1 xx xx mov %cs:v,%eax # else return saved value |
| 737 | * eb 0f jmp 2f |
| 738 | * b8 5e 10 1: mov $0x105e,%ax # dev 00:02.0, offset 5E |
| 739 | * e8 xx xx call pci_read_cfg_word |
| 740 | * 66 c1 e0 10 shl $0x10,%eax # left shift 16 bits |
| 741 | * 66 2e a3 xx xx mov %eax,%cs:v # save the result |
| 742 | * 66 5b 2: pop %ebx |
| 743 | * c3 ret |
| 744 | * When running the VBIOS in guest, saved value still reflects the host stolen |
| 745 | * memory base address, which is not correct in guest. So we need to patch the |
| 746 | * VBIOS to clear the saved value. |
| 747 | * |
| 748 | * The unique 19-byte starts at `cmpl $0,%cs:v` and ends at `mov $0x105e,%ax` |
| 749 | * anchors the match to the routine. Both `cs:` displacements must reference |
| 750 | * the same offset. |
| 751 | */ |
| 752 | static int igd_vbios_find_saved_bdsm(const uint8_t *rom, size_t rom_size, |
| 753 | uint16_t *bdsm_offset) |
| 754 | { |
| 755 | static const uint8_t start[] = { 0x66, 0x2e, 0x83, 0x3e }; |
| 756 | static const uint8_t middle[] = { 0x00, 0x74, 0x07, 0x66, 0x2e, 0xa1 }; |
| 757 | static const uint8_t end[] = { 0xeb, 0x0f, 0xb8, 0x5e, 0x10 }; |
| 758 | uint16_t val; |
| 759 | size_t i; |
| 760 | bool found = false; |
| 761 | |
| 762 | if (rom_size < 19) { |
| 763 | return -ENOENT; |
| 764 | } |
| 765 | |
| 766 | for (i = 0; i + 19 <= rom_size; i++) { |
| 767 | if (memcmp(rom + i, start, sizeof(start)) != 0 || |
| 768 | memcmp(rom + i + 6, middle, sizeof(middle)) != 0 || |
| 769 | memcmp(rom + i + 14, end, sizeof(end)) != 0) { |
| 770 | continue; |
| 771 | } |
| 772 | |
| 773 | /* same saved value address? */ |
| 774 | if (rom[i + 4] != rom[i + 12] || rom[i + 5] != rom[i + 13]) { |
| 775 | continue; |
| 776 | } |
| 777 | |
| 778 | if (found) { |
| 779 | return -EEXIST; |
| 780 | } |
| 781 | |
| 782 | val = rom[i + 4] | ((uint16_t)rom[i + 5] << 8); |
| 783 | if (val + sizeof(uint32_t) <= rom_size) { |
| 784 | *bdsm_offset = val; |
| 785 | found = true; |
| 786 | } |
| 787 | } |
| 788 | |
| 789 | if (!found) { |
| 790 | return -ENOENT; |
| 791 | } |
| 792 | |
| 793 | return 0; |
| 794 | } |
| 795 | |
| 796 | void vfio_igd_legacy_rom_quirk(VFIOPCIDevice *vdev) |
| 797 | { |
| 798 | uint8_t *rom = vdev->rom; |
| 799 | int gen; |
| 800 | uint16_t pcir_offset; |
| 801 | uint16_t bdsm_offset = 0; |
| 802 | uint8_t checksum = 0; |
| 803 | uint32_t i; |
| 804 | |
| 805 | if (!vfio_pci_is(vdev, PCI_VENDOR_ID_INTEL, PCI_ANY_ID) || |
| 806 | !vfio_is_vga(vdev) || !vdev->vga) { |
| 807 | return; |
| 808 | } |
| 809 | |
| 810 | /* Only Gen 6~9 devices have legacy VBIOS as Option ROM */ |
| 811 | gen = igd_gen(vdev); |
| 812 | if (gen < 6 || gen > 9) { |
| 813 | return; |
| 814 | } |
| 815 | |
| 816 | if (pci_get_word(rom) != 0xaa55) { |
| 817 | return; |
| 818 | } |
| 819 | |
| 820 | /* Must be a legacy ROM */ |
| 821 | pcir_offset = pci_get_word(rom + 0x18); |
| 822 | if (pcir_offset + 0x14 >= vdev->rom_size || |
| 823 | memcmp(rom + pcir_offset, "PCIR", 4) || |
| 824 | pci_get_byte(rom + pcir_offset + 0x14) != 0x00) { |
| 825 | return; |
| 826 | } |
| 827 | |
| 828 | /* Search and clear the saved BDSM value */ |
| 829 | if (igd_vbios_find_saved_bdsm(rom, vdev->rom_size, &bdsm_offset)) { |
| 830 | return; |
| 831 | } |
| 832 | memset(rom + bdsm_offset, 0, sizeof(uint32_t)); |
| 833 | |
| 834 | /* Recalculate checksum and patch it. */ |
| 835 | for (i = 0; i < vdev->rom_size; i++) { |
| 836 | checksum += rom[i]; |
| 837 | } |
| 838 | rom[6] -= checksum; |
| 839 | |
| 840 | trace_vfio_pci_igd_vbios_patched(vdev->vbasedev.name); |
| 841 | } |