qemu-spoof: OEM-brand emulated PCI subsystem ids + class-bound nvme vendor:device Realize-time overrides in do_pci_register_device (inert without a spoof-seed): 1. Subsystem id (SVID/SSID) of every non-bridge emulated device -> an OEM-branded value. QEMU stamps a constant 1b36:1100 on all of them (a fleet fingerprint); the subsystem id is not used for driver binding so this is safe. Red Hat/virtio (0x1af4) is skipped -- legacy-virtio encodes the device type in the subsystem id. 2. The qemu-nvme controller (1b36:0010) vendor:device -> the real vendor:device of the spoofed NVMe model brand (Samsung/WD/Kioxia/...), with a matching subsystem. Safe because NVMe binds by class code, not id; runs before nvme_init_ctrl so the IDENTIFY vid/ssvid stay coherent. virtio/GPU vendor:device are NOT touched (their id IS the driver-binding contract). vfio passthrough overwrites with real hw ids. diff --git a/hw/pci/pci.c b/hw/pci/pci.c index 2c3657d..c6eb0ef 100644 --- a/hw/pci/pci.c +++ b/hw/pci/pci.c @@ -23,6 +23,7 @@ */ #include "qemu/osdep.h" +#include "hw/misc/spoof.h" #include "qemu/datadir.h" #include "qemu/units.h" #include "hw/core/irq.h" @@ -1417,6 +1418,44 @@ static PCIDevice *do_pci_register_device(PCIDevice *pci_dev, assert(!pc->subsystem_vendor_id); assert(!pc->subsystem_id); } + + /* + * qemu-spoof: OEM-brand the subsystem id of emulated devices. Real boards + * carry the vendor's SSID; QEMU stamps a constant 1b36:1100 default on every + * device, which is itself a fleet fingerprint. The subsystem id does not take + * part in driver binding, so this is safe -- EXCEPT for Red Hat/virtio + * (0x1af4), where legacy-virtio encodes the device type in the subsystem id; + * skip those (virtio is de-fingerprinted by device choice, not id spoofing). + * vfio passthrough overwrites this later with the real hardware SSID. Inert + * unless a spoof-seed is set. + */ + if (spoof_enabled() && !is_bridge && + pci_get_word(pci_dev->config + PCI_VENDOR_ID) != 0x1af4) { + const char *role = object_get_typename(OBJECT(pci_dev)); + pci_set_word(pci_dev->config + PCI_SUBSYSTEM_VENDOR_ID, + spoof_pci_subvendor( + pci_get_word(pci_dev->config + PCI_SUBSYSTEM_VENDOR_ID))); + pci_set_word(pci_dev->config + PCI_SUBSYSTEM_ID, + spoof_pci_subdevice(role, + pci_get_word(pci_dev->config + PCI_SUBSYSTEM_ID))); + } + /* + * qemu-spoof: class-bound device ids may be set to a REAL vendor:device safely, + * because the guest binds these by class code, not by id. qemu-nvme (Red Hat + * 1b36:0010) -> the vendor:device of the spoofed NVMe model's brand, with a + * matching subsystem (a real NVMe controller's SSVID equals its VID). This runs + * before nvme_init_ctrl, so the NVMe IDENTIFY vid/ssvid stay coherent. virtio / + * GPU ids are NOT touched here (their id IS the driver-binding contract). + */ + if (spoof_enabled() && + !strcmp(object_get_typename(OBJECT(pci_dev)), "nvme")) { + uint16_t vid = spoof_nvme_pci_vendor(pci_get_word(pci_dev->config + PCI_VENDOR_ID)); + uint16_t did = spoof_nvme_pci_device(pci_get_word(pci_dev->config + PCI_DEVICE_ID)); + pci_config_set_vendor_id(pci_dev->config, vid); + pci_config_set_device_id(pci_dev->config, did); + pci_set_word(pci_dev->config + PCI_SUBSYSTEM_VENDOR_ID, vid); + pci_set_word(pci_dev->config + PCI_SUBSYSTEM_ID, did); + } pci_init_cmask(pci_dev); pci_init_wmask(pci_dev); pci_init_w1cmask(pci_dev);