[vfio-users] How to spoof device (sub)class ID for passthrough devices?

Björn Ruytenberg bjorn at bjornweb.nl
Sun Feb 10 19:01:47 UTC 2019


Hi Alex,

Thanks for your quick response and the patch!

I am looking into passing through a muxless GeForce GPU to a Windows guest.

Having been through several resources, passing through muxed and desktop
cards seems quite straightforward. Either no configuration is necessary,
or exposing the (UEFI GOP) VBIOS through the ACPI _ROM method will do
the trick. From what I gather, the latter will also work with the
proprietary NVIDIA driver on Linux. However, on Windows guests, it will
simply bail out with error 43.

I have been doing some ACPI debugging on Windows (using windbg and QEMU,
which is excellent for this :-)), and it looks like the NVIDIA driver
does several _DSM calls instead. I'm not entirely sure what these
methods do. One method contains a number of magic strings such as
`NVIDIA Certified Optimus Ready Motherboard`, which presumably lets the
driver verify it's not running in a VM.

Rather than trying to (partially) replicate the ACPI table from the host
in the guest, I figured it might be possible to trick the NVIDIA driver
into detecting a muxed/desktop card. For this I'll need to:

  1. Find a VBIOS with a UEFI GOP header from a non-muxless GPU, ideally
one that is the same model (muxed/desktop) or similar (Quadro).
  2. Spoof the PCI sub vendor and sub device id, or patch the VBIOS to
have these match my own card.
  3. Spoof the PCI device class, changing it from 0302 (3D controller,
i.e. muxless card) to 0300 (VGA device).

Now that your patch enables the last, I'll try and see if this works. If
you are interested, I'd be happy to report back the results.


On 09-02-19 18:02, Alex Williamson wrote:
> On Sat, 9 Feb 2019 14:38:35 +0100
> Björn Ruytenberg <bjorn at bjornweb.nl> wrote:
> 
>> Hi,
>>
>> I am looking for a way to spoof the PCI Device (sub)class ID for
>> passthrough devices. The following returns a nice list of options (on
>> QEMU 2.11.1), but I can't seem to find one that does just that:
>>
>> $ qemu-system-x86_64 -device vfio-pci,? 2>&1 | grep "x-*"
>> vfio-pci.x-pci-sub-device-id=uint32
>> vfio-pci.x-no-kvm-msi=bool
>> vfio-pci.x-pcie-lnksta-dllla=bool (on/off)
>> vfio-pci.x-igd-opregion=bool (on/off)
>> vfio-pci.x-vga=bool (on/off)
>> vfio-pci.x-pci-vendor-id=uint32
>> vfio-pci.x-req=bool (on/off)
>> vfio-pci.x-igd-gms=uint32
>> vfio-pci.x-no-kvm-intx=bool
>> vfio-pci.x-pci-device-id=uint32
>> vfio-pci.host=str (Address (bus/device/function) of the host device,
>> example: 04:10.0)
>> vfio-pci.x-no-kvm-msix=bool
>> vfio-pci.x-intx-mmap-timeout-ms=uint32
>> vfio-pci.bootindex=int32
>> vfio-pci.x-pcie-extcap-init=bool (on/off)
>> vfio-pci.addr=int32 (Slot and optional function number, example: 06.0 or 06)
>> vfio-pci.x-pci-sub-vendor-id=uint32
>> vfio-pci.x-nv-gpudirect-clique=uint4 (NVIDIA GPUDirect Clique ID (0 - 15))
>> vfio-pci.x-no-mmap=bool
>>
>>
>> Is it possible to spoof PCI device class ID? Otherwise, any pointers
>> where it can be patched (if possible) will be much appreciated!
> 
> Nobody has had a use for this yet, so it doesn't exist.  I think the
> below accomplishes it (compile tested only).  It's meant to allow
> specifying the entire class code register, so you'll need to specify
> the base class and programming interface even if you only want to
> change the sub-class.  For instance, to change a VGA device to a 3D
> controller you'd specify:
> 
>    x-pci-class-code=0x030200
> 
> Let us know if it works.  I'd also be curious to understand your use
> case to know if this is something we want upstream, ie. is this an
> experiment or does it result in something useful?  Thanks,
> 
> Alex
> 
> diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c
> index dd12f363915d..63c4cbc09a26 100644
> --- a/hw/vfio/pci.c
> +++ b/hw/vfio/pci.c
> @@ -2966,6 +2966,17 @@ static void vfio_realize(PCIDevice *pdev, Error **errp)
>                                                 vdev->sub_device_id);
>       }
>   
> +    if (vdev->class_code != ~0) {
> +        if (vdev->class_code & ~0xffffffU) {
> +            error_setg(errp, "invalid PCI class code provided");
> +            goto error;
> +        }
> +        vfio_add_emulated_long(vdev, PCI_REVISION_ID,
> +                               vdev->class_code << 8, ~0xff);
> +        trace_vfio_pci_emulated_class_code(vdev->vbasedev.name,
> +                                           vdev->class_code);
> +    }
> +
>       /* QEMU can change multi-function devices to single function, or reverse */
>       vdev->emulated_config_bits[PCI_HEADER_TYPE] =
>                                                 PCI_HEADER_TYPE_MULTI_FUNCTION;
> @@ -3208,6 +3219,7 @@ static Property vfio_pci_dev_properties[] = {
>                          sub_vendor_id, PCI_ANY_ID),
>       DEFINE_PROP_UINT32("x-pci-sub-device-id", VFIOPCIDevice,
>                          sub_device_id, PCI_ANY_ID),
> +    DEFINE_PROP_UINT32("x-pci-class-code", VFIOPCIDevice, class_code, ~0),
>       DEFINE_PROP_UINT32("x-igd-gms", VFIOPCIDevice, igd_gms, 0),
>       DEFINE_PROP_UNSIGNED_NODEFAULT("x-nv-gpudirect-clique", VFIOPCIDevice,
>                                      nv_gpudirect_clique,
> diff --git a/hw/vfio/pci.h b/hw/vfio/pci.h
> index b1ae4c07549a..900880e9dc9b 100644
> --- a/hw/vfio/pci.h
> +++ b/hw/vfio/pci.h
> @@ -140,6 +140,7 @@ typedef struct VFIOPCIDevice {
>       uint32_t device_id;
>       uint32_t sub_vendor_id;
>       uint32_t sub_device_id;
> +    uint32_t class_code;
>       uint32_t features;
>   #define VFIO_FEATURE_ENABLE_VGA_BIT 0
>   #define VFIO_FEATURE_ENABLE_VGA (1 << VFIO_FEATURE_ENABLE_VGA_BIT)
> diff --git a/hw/vfio/trace-events b/hw/vfio/trace-events
> index f41ca96160bf..809406b860d4 100644
> --- a/hw/vfio/trace-events
> +++ b/hw/vfio/trace-events
> @@ -48,6 +48,7 @@ vfio_pci_emulated_vendor_id(const char *name, uint16_t val) "%s 0x%04x"
>   vfio_pci_emulated_device_id(const char *name, uint16_t val) "%s 0x%04x"
>   vfio_pci_emulated_sub_vendor_id(const char *name, uint16_t val) "%s 0x%04x"
>   vfio_pci_emulated_sub_device_id(const char *name, uint16_t val) "%s 0x%04x"
> +vfio_pci_emulated_class_code(const char *name, uint32_t val) "%s 0x%06x"
>   
>   # hw/vfio/pci-quirks.c
>   vfio_quirk_rom_blacklisted(const char *name, uint16_t vid, uint16_t did) "%s %04x:%04x"
> 

-- 
Kind regards,
Björn Ruytenberg
https://bjornweb.nl




More information about the vfio-users mailing list