Files
qemu-spoof/patches/0018-pci-subsystem-id.patch
T
lirent 3268fe11e1 spoof: class-bound nvme PCI vendor:device (extend 0018)
Override the qemu-nvme controller vendor:device (Red Hat 1b36:0010) to a real NVMe
vendor matched to the spoofed model brand (Samsung/WD/Kioxia/Kingston/Intel/SMI/
hynix/Micron/Phison), with a coherent subsystem. Safe: NVMe binds by class code,
not id, and this runs before nvme_init_ctrl so the IDENTIFY vid/ssvid stay aligned.
virtio/GPU vendor:device untouched (load-bearing). Inert without a seed.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-11 22:35:47 +03:00

70 lines
3.6 KiB
Diff

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);