Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
96bfd518d3 | ||
|
|
6777e6e399 | ||
|
|
29fb6006f1 | ||
|
|
8b77a135ec | ||
|
|
d94772ddc2 | ||
|
|
c2b10a2450 | ||
|
|
69cf814b34 | ||
|
|
b1444cc722 | ||
|
|
d47e1d5fa0 | ||
|
|
f99ff1400a | ||
|
|
4e0df16271 | ||
|
|
0c3425e5af | ||
|
|
a956ececfd | ||
|
|
966a2a6390 | ||
|
|
d3062f5e24 | ||
|
|
1e1078e16a | ||
|
|
f90745e68e | ||
|
|
91fd1c45e1 | ||
|
|
52b18427f2 |
@@ -65,7 +65,7 @@ cd vfio-native/packaging
|
||||
| Package | What it is |
|
||||
| --- | --- |
|
||||
| `vfio-native` | the three commands, the patches, the ACPI tables, the benchmark, the libvirt hook |
|
||||
| `vfio-native-qemu` | QEMU 11.1.1 with the platform-identity patches, in `/opt/qemu-native` |
|
||||
| `vfio-native-qemu` | QEMU 11.1.2 with the platform-identity patches, in `/opt/qemu-native` |
|
||||
| `vfio-native-kvm-dkms` | the patched KVM modules, rebuilt by DKMS for every installed 7.2.x kernel |
|
||||
|
||||
The modules land in `updates/dkms/`, which modprobe prefers, but nothing reloads them for you.
|
||||
@@ -144,10 +144,18 @@ vm-native-setup -d win11 -p full -c 8 -m 16 -r -u auto -y
|
||||
| `-s` | Secure Boot with enrolled keys, `on` (default) or `off` |
|
||||
| `-u` | USB passthrough: `none`, `auto`, or `vid:pid,...,0000:bb:dd.f` |
|
||||
| `-r` | randomise the hardware identity: serials, MAC, memory module |
|
||||
| `-S` | refit to a real machine's dumped SMBIOS (Method B) |
|
||||
| `-y` | no prompts |
|
||||
|
||||
It backs the domain up first and prints the revert command. Re-running it is safe.
|
||||
|
||||
To present as a specific machine instead of this host, fill the target keys in the
|
||||
domain's `identity.env` (`SYS_PRODUCT`, `BIOS_*`, `CPU_*`, ...): the tool then authors
|
||||
SMBIOS type 0-4/17 as `-smbios type=` field args (Method A), so a new-laptop refit is a
|
||||
few field edits. `-S <dmi-blob>` instead splits a real machine's dumped DMI table into
|
||||
`-smbios file=` blobs for byte-perfect fidelity (Method B); mixing is per structure type.
|
||||
Either wires the ICH9 USB set, disables HPET, and needs `-p full`.
|
||||
|
||||
### 5. Check it worked
|
||||
|
||||
```sh
|
||||
@@ -165,25 +173,32 @@ vm-native-verify
|
||||
|
||||
`vm-native-verify` also prints whether CPUID passthrough is on, which is the last step.
|
||||
|
||||
### 6. Switch on CPUID passthrough, after every guest boot
|
||||
### 6. CPUID passthrough, on after boot and off before the next one
|
||||
|
||||
The `TIMER` check times the world switch on an intercepted `CPUID`, and the only way to stop
|
||||
paying it is to not exit. `vm-native-setup` prints the two lines for your declared SKU; on this
|
||||
host they are:
|
||||
paying it is to not exit. That switch has to be off while a guest cold-boots - a booting Windows
|
||||
enumerates CPUID bits KVM synthesises and hangs if they vanish half way through - and on once it
|
||||
is up. Let the libvirt hook handle that around a guest:
|
||||
|
||||
```sh
|
||||
sudo vm-native-cpuid enable win11 # off for the cold boot, on once the guest is on the network, off on stop
|
||||
sudo vm-native-cpuid status # what is enabled, and the live switch state
|
||||
sudo vm-native-cpuid disable win11 # stop automating this guest
|
||||
sudo vm-native-cpuid revert # remove the hook and reset the switch
|
||||
```
|
||||
|
||||
Or drive it by hand. `vm-native-setup` prints the two lines for your declared SKU; run them once
|
||||
the guest is up, never before, and switch it off (`echo N`) before the next boot:
|
||||
|
||||
```sh
|
||||
echo 'AMD Ryzen 7 7700X 8-Core Processor' | sudo tee /sys/module/kvm_amd/parameters/brand_string
|
||||
echo Y | sudo tee /sys/module/kvm_amd/parameters/cpuid_passthrough
|
||||
```
|
||||
|
||||
Run them once the guest is up, never before: a booting Windows enumerates CPUID bits KVM
|
||||
synthesises, and hangs if they vanish half way through. Switch it off again (`echo N`) before the
|
||||
next boot. The module applies it only to vCPU threads pinned to exactly one host CPU, so on an
|
||||
unpinned guest it does nothing rather than something wrong.
|
||||
|
||||
It is a module parameter, so it is one switch for the whole host: every pinned guest gets it, and
|
||||
a cold boot of any of them while it is on hits the race. With more than one such guest, switch it
|
||||
off before any of them boots and on again once they are all up.
|
||||
The module applies it only to vCPU threads pinned to exactly one host CPU, so on an unpinned guest
|
||||
it does nothing rather than something wrong. It is one switch and one brand string for the whole
|
||||
host, so it serves one hardened guest at a time - the hook enforces that and leaves a second guest
|
||||
alone.
|
||||
|
||||
For the detection score, run VMAware in the guest **from the console session, not over SSH** -
|
||||
OpenSSH lands you in session 0, which is not where an interactive desktop session runs. See
|
||||
@@ -267,9 +282,9 @@ and ship with a cover letter and a selftest in `patches/kvm/`.
|
||||
either alone leaves it standing. `EXPERIMENTAL-0006` is built into the DKMS package but off by
|
||||
default; it is not upstream material, because it hands the guest raw host CPUID.
|
||||
|
||||
**QEMU** - eight patchsets against v11.1.1: `01-firmware`, `02-disk-identity`, `03-pci-ids`,
|
||||
`04-fw-cfg`, `05-usb-hid`, `06-audio`, and `07-edid` and `08-cpu-misc`, which clear no check by
|
||||
themselves and are carried for other detectors.
|
||||
**QEMU** - ten patchsets against v11.1.2: `01-firmware`, `02-disk-identity`, `03-pci-ids`,
|
||||
`04-fw-cfg`, `05-usb-hid`, `06-audio`, `09-firmware-chain` and `10-smbios`, and `07-edid` and
|
||||
`08-cpu-misc`, which clear no check by themselves and are carried for other detectors.
|
||||
|
||||
---
|
||||
|
||||
@@ -359,7 +374,7 @@ the whole trail, including the two wrong turns.
|
||||
|
||||
```
|
||||
patches/kvm/ five upstream-style kernel patches, a cover letter, plus one experimental
|
||||
patches/qemu/ eight patchsets against QEMU 11.1.1
|
||||
patches/qemu/ ten patchsets against QEMU 11.1.2
|
||||
packaging/ three AUR-shaped packages: tools, patched QEMU, DKMS modules
|
||||
scripts/ setup, verify, GPU, libvirt hook, module install
|
||||
scripts/generate-tables.py raw SMBIOS type 7/26/27/28/29 blobs for -smbios file=
|
||||
|
||||
+140
-2
@@ -5,7 +5,7 @@ says *why*, with the source references. Written 2026-09-03 at 2/85, revised 2026
|
||||
on both VMAware v2.8.1 and VMAware HEAD (commit 95fecc2) - the one survivor is `GPU_CAPABILITIES`,
|
||||
and VMAware's own verdict is "Running on bare metal", likeliness 20%, confirmation false.
|
||||
|
||||
Source read against: VMAware `src/vmaware.hpp`, QEMU 11.1.1, kernel 7.2.3 `arch/x86/kvm`.
|
||||
Source read against: VMAware `src/vmaware.hpp`, QEMU 11.1.2, kernel 7.2.3 `arch/x86/kvm`.
|
||||
|
||||
---
|
||||
|
||||
@@ -105,7 +105,9 @@ Links, in order:
|
||||
5. ~~FACP C2/C3 latencies, vmaware.hpp:9242~~ - `0xfff` -> `0xffe`
|
||||
6. Debug Port OperationRegion at `0x0402` - already dead on this tree
|
||||
7. HPET register-validation loop - dormant, domain sets `hpet present='no'`. Re-arms if enabled.
|
||||
8. Dummy SATA, DMAR, APIC source overrides - already dead on this tree
|
||||
8. Dummy SATA, DMAR, APIC source overrides - dead on this tree (SATA at `1f.2`: see the SATA
|
||||
subsection below - device 31's functions carry no generic `_ADR` node, so the `D0FA`+`0x1F0002`
|
||||
pair never forms)
|
||||
|
||||
Link 3 requires **all four** of `LNKE`, `LNKH`, `GSIE`, `GSIH`, which is QEMU's eight-link plus
|
||||
eight-GSI layout. Real boards have four PCI link devices and no GSI-named ones, so rename the
|
||||
@@ -125,6 +127,142 @@ WAET and never did: `build_waet()` has zero call sites, so its `grep -q build_wa
|
||||
satisfied by the definition alone and the insert never ran. Guard on a call site, not on a
|
||||
definition.
|
||||
|
||||
**The chain grew.** `firmware()` returns on its first hit, so clearing one link only exposes the
|
||||
next, and current VMAware has added links past the eight above. `09-firmware-chain` clears the
|
||||
rest, measured to 0 detections on both v2.8.1 and HEAD:
|
||||
|
||||
| Link | Fix |
|
||||
| --- | --- |
|
||||
| IQST/IQCR + PRR0/PRRI interrupt-query methods | renamed PSTA/PCRS/PRS0/PRSI |
|
||||
| TPM PPI regions/method TPP2/TPP3/TPFN | renamed PPI2/PPI3/PPIF |
|
||||
| zeroed `_S5` SLP_TYP package | 2-element package form |
|
||||
| `ACPI0006` `_HID` in `\_GPE` | declared by full path `\_GPE._HID`; dropping it stops macOS powering off |
|
||||
| Q35 MMCONFIG reservation device `MCFG` | renamed `PCMM` |
|
||||
| synthetic GSI link devices, empty `_DIS`/`_SRS` | gave the no-op methods a body |
|
||||
| PCIe `_OSC` `And(CDW3, 0x1F, ..)` mask | swapped the `And()` operands (commutative) |
|
||||
| PIRQ link `_PRS` fixed IRQ 5/10/11 | listed the same set as `{11, 10, 5}` |
|
||||
|
||||
A q35 guest also needs a named USB controller in the DSDT (the ICH9 EHCI/UHCI set, declared `EHC1`/
|
||||
`UHC1`), or the release flags "complete absence of USB controller declarations"; that is a domain
|
||||
choice, not a QEMU patch.
|
||||
|
||||
### CLEARED - the CPU-hotplug I/O signature that macOS forced back in (`11-macos-cpu-hotplug`)
|
||||
|
||||
macOS references `\_SB.CPUS.Cxxx` from its own CpuPlug SSDT, so the guest must carry QEMU's
|
||||
`build_cpus_aml` output (the `\_SB.PCI0.PRES` controller plus the `\_SB.CPUS` `Processor` objects)
|
||||
or it hangs in ACPI namespace init. `01-firmware` had dropped that whole block, which is why macOS
|
||||
lived in an opt-in patch: the block reintroduces exactly one FIRMWARE tell, so a macOS-capable build
|
||||
scored 2/85 on v2.8.1 while HEAD stayed clean.
|
||||
|
||||
The tell is a single check. v2.8.1 `firmware()` carries `cpu_hotplug_io[] = {0x0B, 0xD8, 0x0C}` and
|
||||
byte-matches it anywhere in the tables. Those three bytes are `WordData 0x0CD8` - the CPU-hotplug
|
||||
register port that `build_cpus_aml` writes into the `PRST` `OperationRegion` base
|
||||
(`ICH9_CPU_HOTPLUG_IO_BASE`). Confirmed by dumping the macOS-build DSDT: the bytes appear only at the
|
||||
`OperationRegion` operand, never in the `_CRS`. A small-resource `IO` descriptor encodes `0x0CD8` as
|
||||
`0x47 0x01 0xD8 0x0C 0xD8 0x0C 0x01 0x0C` - the `0xD8 0x0C` runs are preceded by `0x01`/`0x0C`, never
|
||||
`0x0B` - so the `_CRS` is not what matches and does not need touching. The renamed `_UID` ("Reserved
|
||||
resources C") already dodges the separate `"CPU Hotplug resources"` string check; it was never the
|
||||
live tell. HEAD has no `cpu_hotplug_io` check, so it never flagged the block.
|
||||
|
||||
**Relocating the base does not work.** The obvious fix - move `ICH9_CPU_HOTPLUG_IO_BASE` off `0x0CD8`
|
||||
so the pattern changes - clears the scanner but hangs every SMM/OVMF guest: macOS spins ~10 cores at
|
||||
boot and never reaches login, Windows the same. `OvmfPkg/Include/IndustryStandard/Q35MchIch9.h`
|
||||
hardcodes `ICH9_CPU_HOTPLUG_BASE 0x0CD8` and OVMF's SMM code reads the register block at that fixed
|
||||
port, independent of the AML. The non-SMM path (plain OVMF, a bare Linux boot) survives a move; the
|
||||
SMM guests we actually run do not. A/B confirmed it: macOS reaches login on `0x0CD8`, spins on
|
||||
`0x0CE4`. Patching OVMF is already on the rejected list.
|
||||
|
||||
**The fix keeps the port and hides only the AML operand.** `11` emits the `OperationRegion` base for
|
||||
a SystemIO region as `Add(0x0C00, 0xD8)` instead of the literal `0x0CD8`. It evaluates to the
|
||||
identical `0x0CD8` at namespace load - the port QEMU decodes, the port in `_CRS`, and the port OVMF
|
||||
reads are all unchanged - but the AML is `0x72 0x0B 0x00 0x0C 0x0A 0xD8 ..`, so the `0x0B 0xD8 0x0C`
|
||||
run never appears. macOS's interpreter evaluates the computed base without complaint.
|
||||
|
||||
Verified on QEMU 11.1.2 with this fix in the default series: the DSDT keeps `\_SB.CPUS`/`PRES`/
|
||||
`Processor(C000..)` and `0x0CD8`, with no `0x0B 0xD8 0x0C` anywhere (iasl-clean); macOS boots to
|
||||
login and powers off via S5 in ~12 s; VMAware v2.8.1 and HEAD both report `detected=0` / `FIRMWARE=0`
|
||||
on the stealth guest; Windows (secboot + SMM) and Linux boot unchanged. The opt-in split is gone.
|
||||
|
||||
### CLEARED - the SATA controller OVMF must find at 1f.2 (`03-pci-ids`, 1.4.1)
|
||||
|
||||
`03-pci-ids` originally relocated the ICH9 SATA controller off its standard slot
|
||||
(`ICH9_SATA1_DEV` 31 -> 20) to move the dummy-SATA DSDT node away from `00:1f.2`. That breaks
|
||||
OVMF first-boot for any SATA-booted guest: OVMF expects the q35 AHCI at `00:1f.2`, so with it at
|
||||
`00:14.2` OVMF never builds the disk boot option and falls through to `Start PXE over IPv4`. Only
|
||||
macOS exposed this - it boots OpenCore off AHCI; the Windows identity guests boot from NVMe and
|
||||
never touched the moved controller. A/B confirmed it: stock QEMU reaches the OpenCore picker, the
|
||||
1.4.0 patched binary PXEs, and restoring `ICH9_SATA1_DEV = 31` reaches the picker again
|
||||
(`BdsDxe: loading ... from PciRoot(0x0)/Pci(0x1F,0x2)/Sata`).
|
||||
|
||||
The relocation was never load-bearing for the score. The dummy-SATA FIRMWARE link
|
||||
(vmaware.hpp ~9963) fires only on **both** the NameSeg `D0FA` **and** `Name(_ADR, 0x001F0002)`.
|
||||
With SATA back at `1f.2`, neither is present: QEMU describes device 31's functions under the ICH9
|
||||
LPC/ISA scope, not through `build_append_pci_bus_devices`, so the controller gets no generic
|
||||
`_ADR` node at all, and `get_mimic_pci_name` never emits `D0FA` for any devfn. Dumped the DSDT of a
|
||||
`1f.2`-SATA guest on the fixed build: zero `D0FA`, zero `08 5F 41 44 52 0C 02 00 1F 00`. No vendor
|
||||
or device ID changes, so `DEVICES` / `PCI_VENDOR` / `CPU_HEURISTIC` are untouched.
|
||||
|
||||
Fix: drop the `ICH9_SATA1_DEV` hunk, leave SATA at stock `1f.2`. macOS boots on the default series;
|
||||
0/85 preserved.
|
||||
|
||||
### CLEARED - the dangling ACPI name macOS aborts the DSDT on (folded into `01-firmware`)
|
||||
|
||||
`01-firmware` renames the cold-plug PCI device ACPI nodes through `get_mimic_pci_name`
|
||||
(`build_append_pci_bus_devices`) to break the stock `S`+hex NameSeg signature. It left the hotplug
|
||||
and bus-notification code naming/referencing the *same* devices by the old `S%.02X` scheme, in four
|
||||
spots: the child-bridge `Scope` and `^S%.02X.PCNT` cross-reference in
|
||||
`build_append_notification_callback`, the `Notify()` target in `build_append_pcihp_notify_entry`,
|
||||
and the per-slot `Scope`/`Device` in `build_append_pcihp_slots`. So the DSDT opens `Scope(S13)` on a
|
||||
device actually declared `GFX3` - a dangling NameSeg.
|
||||
|
||||
Only macOS exposes it. Its ACPI parser aborts the whole DSDT load on the missing name:
|
||||
`[S13_] Namespace lookup failure` -> `[DSDT] table load failed` -> `[\_SB.PCI0] ... AE_NOT_FOUND`,
|
||||
then it hangs in early IOKit (last line `AppleACPIPlatformExpert/cpus`). Linux and Windows swallow
|
||||
the `AE_NOT_FOUND` and boot on, which is why the identity guests never showed it - only a from-scratch
|
||||
macOS recovery boot did. Diagnosed with macOS IOKit verbose (`boot-args io=0xffffffff`).
|
||||
|
||||
Fix (in `01-firmware`, alongside the `get_mimic_pci_name` definition it completes): route those four
|
||||
sites through `get_mimic_pci_name` so definitions and references agree. It *removes* `S%.02X`
|
||||
NameSegs rather than adding any, so the score is unaffected - re-measured on the rebuilt binary,
|
||||
VMAware v2.8.1 and HEAD both `detected=0`/`FIRMWARE=0`, and a Tahoe recovery boots to the installer
|
||||
GUI.
|
||||
|
||||
---
|
||||
|
||||
## CLEARED - CTYPE and SMBIOS_VM_BIT (the SMBIOS QEMU synthesises)
|
||||
|
||||
On Linux, VMAware reads SMBIOS four ways: `DMIDECODE` and `DMI_SCAN` match VM vendor strings in
|
||||
`dmidecode -t system` and seven `/sys/class/dmi/id` files, `SMBIOS_VM_BIT` tests bit 4 of type 0
|
||||
byte 19, and `CTYPE` fires on chassis type 1, Other (HEAD e1a9786, vmaware.hpp:8272, :8948, :9023,
|
||||
:8234). On Windows, `FIRMWARE` also scans the raw tables (`RSMB`, :10369) for `pc-q35` and `Q35 +`
|
||||
(:9726). The strings are domain identity. The bit, the chassis type and the default strings are
|
||||
what QEMU emits by itself.
|
||||
|
||||
QEMU synthesises every required structure the domain does not supply. Switching that off
|
||||
(`smbios_defaults = false`) leaves only what the domain passes: either a complete real dump through
|
||||
`-smbios file=`, or a table with no processor, memory or end-of-table structures. `10-smbios` keeps
|
||||
it on and fixes what the synthesised structures leak:
|
||||
|
||||
| Leak | Fix |
|
||||
| --- | --- |
|
||||
| type 3 chassis type hard-coded to 1 (Other) | defaults to 3 (Desktop), set with `-smbios type=3,chassis-type=N` |
|
||||
| type 0 VM bit set whenever type 0 fields are given | `vm` defaults off; libvirt `<sysinfo>` cannot pass `vm=off` |
|
||||
| unset product and version strings `Standard PC (Q35 + ICH9, 2009)`, `pc-q35-<ver>` | `Unknown`, like the manufacturer from `04-fw-cfg` |
|
||||
|
||||
Measured on a Linux q35 guest, the same score on VMAware v2.8.1 and HEAD:
|
||||
|
||||
| SMBIOS from the domain | QEMU | Score |
|
||||
| --- | --- | --- |
|
||||
| complete real dump, 52 `file=` structures | defaults off | 0/85 |
|
||||
| the same dump | defaults on | 0/85, `dmidecode` byte-identical |
|
||||
| field arguments for types 0-4 with `vm=off` | defaults on, builders unpatched | 1/85, `CTYPE` |
|
||||
| field arguments for types 0-4, no `vm=off`, `chassis-type` set | `10-smbios` | 0/85 |
|
||||
|
||||
Identity is authored as field arguments. A dump through `-smbios file=` adds what field arguments
|
||||
cannot express (memory module detail, OEM structures, battery) and mixes with them per type: a type
|
||||
given as `file=` cannot also take field arguments. Windows and macOS guests are not yet measured
|
||||
against this patch.
|
||||
|
||||
---
|
||||
|
||||
## OPEN - GPU_CAPABILITIES (weight 20)
|
||||
|
||||
+8
-9
@@ -75,12 +75,6 @@ Enable-NetFirewallRule -DisplayGroup "Remote Desktop"
|
||||
Put your public key in `C:\ProgramData\ssh\administrators_authorized_keys` for an Administrator
|
||||
account. `vm-native-verify` uses that key.
|
||||
|
||||
SSH into the guest fails with `Corrupted MAC on input` until the host has the e1000e offload rule
|
||||
from the `vfio-native` package. The emulated NIC's TX offloads corrupt integrity-checked traffic on
|
||||
the host side of the tap; SMB tolerates it, SSH does not. The package installs a udev rule that
|
||||
turns the offloads off on every libvirt tap as it appears, and `vm-native-setup` says so if it is
|
||||
missing.
|
||||
|
||||
## 3. Make the NVMe driver boot-critical
|
||||
|
||||
The disk is about to move from virtio to emulated NVMe, and Windows only loads boot-start drivers
|
||||
@@ -127,9 +121,14 @@ Two things it asks or warns about:
|
||||
|
||||
Before the first boot, if the host has less free memory than the guest's RAM, free and compact
|
||||
it so the guest lands on transparent hugepages; `vm-native-setup` prints the two commands when it
|
||||
applies. The NIC stays `e1000e`, so the network survives the driver removal in the next step. Do not use
|
||||
applies. The NIC stays `igb`, so the network survives the driver removal in the next step. Do not use
|
||||
virtiofs for host files: it is a virtio device the scanner names, and its shared memory backing
|
||||
blocks transparent hugepages for the whole guest. Share over SMB on the e1000e link instead.
|
||||
blocks transparent hugepages for the whole guest. Share over SMB on the `igb` link instead.
|
||||
|
||||
The interface asks for MTU 9000, and the package's hook turns GSO and GRO on for the tap. Together
|
||||
they take the inbound link from 2.9 to 14 Gbit/s; neither does anything alone. The libvirt network
|
||||
needs `<mtu size='9000'/>` too, and the guest needs *Jumbo Packet* 9014 with its interface MTU at
|
||||
9000 - setting the adapter property alone leaves the IP MTU at 1500 and gains nothing.
|
||||
|
||||
## 5. Remove the virtio drivers and the agents
|
||||
|
||||
@@ -165,7 +164,7 @@ echo Y | sudo tee /sys/module/kvm_amd/parameters/cpuid_passthrough
|
||||
vm-native-verify
|
||||
```
|
||||
|
||||
`vm-native-setup` printed those two lines with your SKU. The switch is host-wide: it applies to
|
||||
`vm-native-setup` printed those two lines with your SKU, or `sudo vm-native-cpuid enable <domain>` does the same on/off automatically around the guest (off for the cold boot, on once its network is up). The switch is host-wide: it applies to
|
||||
every pinned guest at once, and a cold boot of any of them while it is on hits the enumeration
|
||||
race, so with several guests turn it off before any boots and on when they are all up. Then the
|
||||
scanner, from the console session, as `docs/TESTING.md` describes. The reference guest reads 1/85.
|
||||
|
||||
@@ -12,7 +12,7 @@ pkgbase = vfio-native-kvm-dkms
|
||||
provides = vfio-native-kvm
|
||||
conflicts = vfio-native-kvm
|
||||
noextract = linux-7.2.3.tar.xz
|
||||
source = git+https://git.archworks.co/sandwich/vfio-native.git#tag=v1.1.0
|
||||
source = git+https://git.archworks.co/sandwich/vfio-native.git#tag=v1.4.0
|
||||
source = https://cdn.kernel.org/pub/linux/kernel/v7.x/linux-7.2.3.tar.xz
|
||||
source = dkms.conf
|
||||
source = dkms-out-of-tree.patch
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
pkgname=vfio-native-kvm-dkms
|
||||
_pkgbase=vfio-native-kvm
|
||||
_kver=7.2.3
|
||||
_tag=1.1.0
|
||||
_tag=1.4.2
|
||||
pkgver=${_kver}
|
||||
pkgrel=1
|
||||
pkgdesc="KVM modules patched to raise the exceptions the architecture specifies (DKMS, 7.2.x kernels)"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
pkgbase = vfio-native-qemu
|
||||
pkgdesc = QEMU 11.1.1 with the vfio-native platform-identity patches, in /opt/qemu-native
|
||||
pkgver = 11.1.1
|
||||
pkgrel = 1
|
||||
pkgdesc = QEMU 11.1.2 with the vfio-native platform-identity patches, in /opt/qemu-native
|
||||
pkgver = 11.1.2
|
||||
pkgrel = 2
|
||||
url = https://git.archworks.co/sandwich/vfio-native
|
||||
arch = x86_64
|
||||
license = GPL-2.0-only
|
||||
@@ -38,9 +38,9 @@ pkgbase = vfio-native-qemu
|
||||
depends = pipewire
|
||||
optdepends = vfio-native: the setup tooling that points a domain at this build
|
||||
options = !strip
|
||||
source = git+https://git.archworks.co/sandwich/vfio-native.git#tag=v1.1.0
|
||||
source = https://download.qemu.org/qemu-11.1.1.tar.xz
|
||||
source = git+https://git.archworks.co/sandwich/vfio-native.git#tag=v1.4.0
|
||||
source = https://download.qemu.org/qemu-11.1.2.tar.xz
|
||||
sha256sums = SKIP
|
||||
sha256sums = 079ffbff8a7111bbc89022107cbabf3bbfd614d5fc9d7cc675991196aca12482
|
||||
sha256sums = 731b5681e4bb18be313231579b8efd0296c5b015fa36dc533874b639ba838016
|
||||
|
||||
pkgname = vfio-native-qemu
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
# Maintainer: Sandwich <sandwich@archworks.co>
|
||||
#
|
||||
# QEMU 11.1.1 with the platform-identity patches from vfio-native, installed
|
||||
# QEMU 11.1.2 with the platform-identity patches from vfio-native, installed
|
||||
# under /opt/qemu-native so the system qemu package is never touched. Clears
|
||||
# FIRMWARE, VIRTUAL_DISK, DISK_SERIAL, PCI_VENDOR, DEVICES, QEMU_FW_CFG and
|
||||
# QEMU_USB. Only the x86_64 system emulator is built.
|
||||
|
||||
pkgname=vfio-native-qemu
|
||||
_qver=11.1.1
|
||||
_tag=1.1.0
|
||||
_qver=11.1.2
|
||||
_tag=1.4.2
|
||||
pkgver=${_qver}
|
||||
pkgrel=1
|
||||
pkgrel=4
|
||||
pkgdesc="QEMU ${_qver} with the vfio-native platform-identity patches, in /opt/qemu-native"
|
||||
arch=('x86_64')
|
||||
url="https://git.archworks.co/sandwich/vfio-native"
|
||||
@@ -24,11 +24,11 @@ options=('!strip')
|
||||
source=("git+${url}.git#tag=v${_tag}"
|
||||
"https://download.qemu.org/qemu-${_qver}.tar.xz")
|
||||
sha256sums=('SKIP'
|
||||
'079ffbff8a7111bbc89022107cbabf3bbfd614d5fc9d7cc675991196aca12482')
|
||||
'731b5681e4bb18be313231579b8efd0296c5b015fa36dc533874b639ba838016')
|
||||
|
||||
prepare() {
|
||||
cd "qemu-${_qver}"
|
||||
for p in "${srcdir}"/vfio-native/patches/qemu/0[1-8]-*.patch; do
|
||||
for p in "${srcdir}"/vfio-native/patches/qemu/[0-9][0-9]-*.patch; do
|
||||
msg2 "applying ${p##*/}"
|
||||
patch -Np1 --no-backup-if-mismatch -i "$p"
|
||||
done
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
pkgbase = vfio-native
|
||||
pkgdesc = Present a libvirt guest as a self-consistent physical machine, and tune it
|
||||
pkgver = 1.1.0
|
||||
pkgver = 1.4.0
|
||||
pkgrel = 1
|
||||
url = https://git.archworks.co/sandwich/vfio-native
|
||||
install = vfio-native.install
|
||||
@@ -17,7 +17,7 @@ pkgbase = vfio-native
|
||||
optdepends = cpupower: set the host CPU governor
|
||||
optdepends = vfio-native-kvm-dkms: patched KVM modules for the full level
|
||||
optdepends = vfio-native-qemu: patched QEMU for the full level
|
||||
source = git+https://git.archworks.co/sandwich/vfio-native.git#tag=v1.1.0
|
||||
source = git+https://git.archworks.co/sandwich/vfio-native.git#tag=v1.4.0
|
||||
sha256sums = SKIP
|
||||
|
||||
pkgname = vfio-native
|
||||
|
||||
@@ -5,10 +5,10 @@
|
||||
# Three packages:
|
||||
# vfio-native scripts, patches, ACPI tables and the benchmark
|
||||
# vfio-native-kvm-dkms the patched KVM modules, rebuilt by DKMS per kernel
|
||||
# vfio-native-qemu QEMU 11.1.1 with the platform-identity patches, in /opt
|
||||
# vfio-native-qemu QEMU 11.1.2 with the platform-identity patches, in /opt
|
||||
|
||||
pkgname=vfio-native
|
||||
pkgver=1.1.0
|
||||
pkgver=1.4.2
|
||||
pkgrel=1
|
||||
pkgdesc="Present a libvirt guest as a self-consistent physical machine, and tune it"
|
||||
arch=('any')
|
||||
@@ -29,6 +29,7 @@ package() {
|
||||
install -Dm755 scripts/setup-vm.sh "${pkgdir}/usr/bin/vm-native-setup"
|
||||
install -Dm755 scripts/verify-perf.sh "${pkgdir}/usr/bin/vm-native-verify"
|
||||
install -Dm755 scripts/gpu-passthrough.sh "${pkgdir}/usr/bin/vm-native-gpu"
|
||||
install -Dm755 scripts/cpuid-passthrough.sh "${pkgdir}/usr/bin/vm-native-cpuid"
|
||||
|
||||
local share="${pkgdir}/usr/share/vfio-native"
|
||||
install -Dm644 bench/vmbench.c "${share}/bench/vmbench.c"
|
||||
@@ -36,6 +37,9 @@ package() {
|
||||
install -Dm755 scripts/install-modules.sh "${share}/scripts/install-modules.sh"
|
||||
install -Dm755 scripts/restore-stock-kvm.sh "${share}/scripts/restore-stock-kvm.sh"
|
||||
install -Dm755 scripts/generate-tables.py "${share}/scripts/generate-tables.py"
|
||||
install -Dm755 scripts/split_smbios.py "${share}/scripts/split_smbios.py"
|
||||
install -Dm755 scripts/libvirt-hook-cpuid-passthrough.sh "${share}/scripts/libvirt-hook-cpuid-passthrough.sh"
|
||||
install -Dm755 scripts/cpuid-passthrough-watch "${share}/scripts/cpuid-passthrough-watch"
|
||||
install -Dm644 -t "${share}/acpi" acpi/*.aml acpi/*.dsl
|
||||
install -dm755 "${share}/patches"
|
||||
cp -r patches/. "${share}/patches/"
|
||||
@@ -45,7 +49,6 @@ package() {
|
||||
# this coexists with whatever hook the host already has.
|
||||
install -Dm755 scripts/libvirt-hook-cpu-isolation.sh \
|
||||
"${pkgdir}/etc/libvirt/hooks/qemu.d/10-cpu-isolation.sh"
|
||||
# e1000e offloads corrupt integrity-checked traffic on libvirt taps; host-wide by nature
|
||||
install -Dm644 scripts/99-vfio-native-vnet-offload.rules \
|
||||
"${pkgdir}/usr/lib/udev/rules.d/99-vfio-native-vnet-offload.rules"
|
||||
install -Dm755 scripts/libvirt-hook-vnet-offload.sh \
|
||||
"${pkgdir}/etc/libvirt/hooks/qemu.d/20-vnet-offload.sh"
|
||||
}
|
||||
|
||||
@@ -49,11 +49,11 @@ post_install() {
|
||||
|
||||
'native' is a good default if you would rather not maintain anything.
|
||||
|
||||
A udev rule (99-vfio-native-vnet-offload.rules) turns TX offloads off on every
|
||||
libvirt tap as it appears. The emulated e1000e NIC corrupts integrity-checked
|
||||
traffic with them on; SSH to the guest fails with "Corrupted MAC on input".
|
||||
It applies to every VM on the host; the throughput cost on a host<->guest
|
||||
link is not measurable.
|
||||
A hook at /etc/libvirt/hooks/qemu.d/20-vnet-offload.sh turns GSO and GRO on
|
||||
for the guest's tap. With the interface's MTU 9000 that takes the inbound link
|
||||
from 2.9 to 14 Gbit/s; neither does anything alone. The libvirt network needs
|
||||
<mtu size='9000'/> too, and the guest needs Jumbo Packet 9014 with its
|
||||
interface MTU at 9000.
|
||||
|
||||
A libvirt hook is installed at /etc/libvirt/hooks/qemu.d/10-cpu-isolation.sh.
|
||||
It keeps host processes off the cores the guest is pinned to, automatically,
|
||||
|
||||
+23
-3
@@ -34,7 +34,7 @@ enabled through the `cpuid_passthrough` module parameter after the guest has boo
|
||||
|
||||
## QEMU - `qemu/`
|
||||
|
||||
Against QEMU v11.1.1. Apply in numeric order.
|
||||
Against QEMU v11.1.2. Apply in numeric order.
|
||||
|
||||
| Patch | Clears |
|
||||
| --- | --- |
|
||||
@@ -46,11 +46,29 @@ Against QEMU v11.1.1. Apply in numeric order.
|
||||
| `06-audio` | `DEVICES` - HDA codec and controller identity |
|
||||
| `07-edid` | none. Carried for other detectors; VMAware does not read EDID. |
|
||||
| `08-cpu-misc` | none. CPU feature plumbing and one upstream backport. |
|
||||
| `09-firmware-chain` | `FIRMWARE` - the DSDT links current VMAware added after `01-firmware` |
|
||||
| `10-smbios` | `CTYPE`, `SMBIOS_VM_BIT`, `FIRMWARE` - what QEMU's synthesised SMBIOS leaks: chassis type Other, the type 0 VM bit, `pc-q35` default strings; adds `-smbios type=3,chassis-type=N` |
|
||||
| `11-macos-cpu-hotplug` | `FIRMWARE` - re-adds the `\_SB.CPUS` CPU objects macOS needs, with the CPU-hotplug I/O base `0x0CD8` hidden from the scanner (renamed `_UID`, `Add()`-split OperationRegion base) rather than removed |
|
||||
|
||||
`patch -p1 < qemu/01-firmware.patch` and so on, then build with the prefix set to `/opt` so the
|
||||
system QEMU package is left alone:
|
||||
The numbered patches are the default series and score 0/85 on both VMAware HEAD and v2.8.1.
|
||||
`03-pci-ids` scopes the virtio subsystem-id change so OVMF still binds virtio (its generic default
|
||||
goes to Intel/0, virtio devices keep the Red Hat subsystem ids their drivers and OVMF expect). It
|
||||
also leaves the ICH9 SATA controller at its stock slot `1f.2`: OVMF locates the q35 AHCI there, so
|
||||
moving it broke first-boot for SATA-booted UEFI guests (macOS fell through to PXE) while buying
|
||||
nothing for the score - the dummy-SATA FIRMWARE tell needs both the `D0FA` name and `_ADR 0x1F0002`,
|
||||
and device 31's functions emit neither. See `docs/ANALYSIS.md`.
|
||||
|
||||
`11-macos-cpu-hotplug` makes the series universal: macOS boots (it needs the `\_SB.CPUS.Cxxx`
|
||||
objects from `build_cpus_aml`) and both VMAware builds still read bare metal, with no opt-in. The
|
||||
block's only tell, the `0x0CD8` CPU-hotplug port that v2.8.1 FIRMWARE byte-matches, is removed at
|
||||
the AML level without moving the port, so the same series boots Windows and Linux unchanged. The
|
||||
earlier opt-in split and the relocate-the-base approach are in "Approaches tried and rejected".
|
||||
|
||||
Apply in numeric order, then build with the prefix set to `/opt` so the system QEMU package is left
|
||||
alone:
|
||||
|
||||
```sh
|
||||
for p in qemu/[0-9][0-9]-*.patch; do patch -p1 < "$p"; done
|
||||
./configure --prefix=/opt/qemu-native --target-list=x86_64-softmmu
|
||||
ninja -C build && ninja -C build install
|
||||
```
|
||||
@@ -64,6 +82,8 @@ ninja -C build && ninja -C build install
|
||||
| CPUID leaf override (Hypervisor-Phantom) | Halved the timing signature, still detected, and *added* a `SINGLE_STEP` detection. |
|
||||
| Clearing `INTERCEPT_CPUID` outright | Guest never boots. |
|
||||
| Intercept clearing, minimal hypercall | Triple fault, or no effect. |
|
||||
| Opt-in macOS CPU block (`optional/macos-cpu-hotplug`) | Worked but split the series in two; a macOS-capable build scored 2/85 on v2.8.1. Folded into `11` instead. |
|
||||
| Relocating the CPU-hotplug I/O base off `0x0CD8` | Clears the scanner but hangs every SMM/OVMF guest (macOS spins 10 cores at boot, Windows too): OVMF hardcodes `0x0CD8` in `OvmfPkg` and reads the register block directly. `11` keeps the port and hides only the AML operand. |
|
||||
|
||||
`TIMER` has two detectors that OR together. The exception-latency one passes on stock KVM once it
|
||||
is measured the way VMAware measures it; the instruction-latency one is removable with
|
||||
|
||||
@@ -5,7 +5,7 @@ Targets VMAware check: FIRMWARE
|
||||
ACPI table fingerprints: PCI link-device names, PNP0A06 resource stubs, the
|
||||
FADT C-state latencies and the AML the checker string-matches on.
|
||||
|
||||
Applies to QEMU v11.1.1. Apply from the source root with:
|
||||
Applies to QEMU v11.1.2. Apply from the source root with:
|
||||
patch -p1 < 01-firmware.patch
|
||||
|
||||
---
|
||||
@@ -193,10 +193,9 @@ index 8837b69..4001d9d 100644
|
||||
if (pcms->wdat_enabled == true) {
|
||||
g_assert(pm.tco_io_base);
|
||||
diff --git a/hw/acpi/pcihp.c b/hw/acpi/pcihp.c
|
||||
index 87162ff..4c303cb 100644
|
||||
--- a/hw/acpi/pcihp.c
|
||||
+++ b/hw/acpi/pcihp.c
|
||||
@@ -687,10 +687,10 @@ void build_append_pcihp_resources(Aml *scope /* \\_SB.PCI0 */,
|
||||
@@ -687,10 +687,10 @@
|
||||
{
|
||||
Aml *dev, *crs;
|
||||
|
||||
@@ -209,7 +208,7 @@ index 87162ff..4c303cb 100644
|
||||
/* device present, functioning, decoding, not shown in UI */
|
||||
aml_append(dev, aml_name_decl("_STA", aml_int(0xB)));
|
||||
crs = aml_resource_template();
|
||||
@@ -699,6 +699,83 @@ void build_append_pcihp_resources(Aml *scope /* \\_SB.PCI0 */,
|
||||
@@ -699,6 +699,89 @@
|
||||
aml_append(scope, dev);
|
||||
}
|
||||
|
||||
@@ -251,7 +250,7 @@ index 87162ff..4c303cb 100644
|
||||
+ case 5:
|
||||
+ // Slot 5: USB XHCI (QEMU default often)
|
||||
+ if (func == 0) sprintf(name, "XHC1");
|
||||
+ else sprintf(name, "XHC%X", func);
|
||||
+ else sprintf(name, "XHC%X", func + 1);
|
||||
+ break;
|
||||
+
|
||||
+ case 6:
|
||||
@@ -261,19 +260,25 @@ index 87162ff..4c303cb 100644
|
||||
+ break;
|
||||
+
|
||||
+ case 29:
|
||||
+ // Slot 29 (0x1D): USB EHCI (Intel Standard)
|
||||
+ if (func == 0) sprintf(name, "EHC1");
|
||||
+ else sprintf(name, "EHC%X", func);
|
||||
+ // Slot 29 (0x1D): ICH9 USB, UHCI at functions 0-2 and EHCI at 7
|
||||
+ if (func == 7) sprintf(name, "EHC1");
|
||||
+ else sprintf(name, "UHC%X", func + 1);
|
||||
+ break;
|
||||
+
|
||||
+ case 20:
|
||||
+ // Slot 20 (0x14): LPC / SMBus / SATA 2
|
||||
+ if (func == 0) sprintf(name, "LPCB"); // ISA Bridge
|
||||
+ else if (func == 2) sprintf(name, "SAT1"); // 2nd SATA
|
||||
+ // Slot 20 (0x14): SMBus / SATA 2; the q35 LPC bridge is 1f.0
|
||||
+ if (func == 2) sprintf(name, "SATA"); // 2nd SATA; slot 6 owns SAT0-SAT7
|
||||
+ else if (func == 3) sprintf(name, "SBUS"); // SMBus
|
||||
+ else sprintf(name, "S20%X", func); // Fallback
|
||||
+ break;
|
||||
+
|
||||
+ case 31:
|
||||
+ // Slot 31 (0x1F): the ICH9 LPC bridge, named as 03-pci-ids' PIRQ field expects
|
||||
+ if (func == 0) {
|
||||
+ sprintf(name, "LPCB");
|
||||
+ break;
|
||||
+ }
|
||||
+ /* fall through */
|
||||
+ default:
|
||||
+ /*
|
||||
+ * For generic slots, we use "RP" (Root Port) + devfn (Hex).
|
||||
@@ -293,7 +298,57 @@ index 87162ff..4c303cb 100644
|
||||
bool build_append_notification_callback(Aml *parent_scope, const PCIBus *bus)
|
||||
{
|
||||
Aml *method;
|
||||
@@ -912,7 +989,11 @@ void build_append_pci_bus_devices(Aml *parent_scope, PCIBus *bus)
|
||||
@@ -708,7 +791,9 @@
|
||||
GQueue *pcnt_bus_list = g_queue_new();
|
||||
|
||||
QLIST_FOREACH(sec, &bus->child, sibling) {
|
||||
- Aml *br_scope = aml_scope("S%.02X", sec->parent_dev->devfn);
|
||||
+ char mimic_nm[5];
|
||||
+ get_mimic_pci_name(mimic_nm, sec->parent_dev->devfn);
|
||||
+ Aml *br_scope = aml_scope("%s", mimic_nm);
|
||||
if (pci_bus_is_root(sec)) {
|
||||
continue;
|
||||
}
|
||||
@@ -747,7 +832,9 @@
|
||||
|
||||
/* Notify about child bus events in any case */
|
||||
while ((sec = g_queue_pop_head(pcnt_bus_list))) {
|
||||
- aml_append(method, aml_name("^S%.02X.PCNT", sec->parent_dev->devfn));
|
||||
+ char mimic_nm[5];
|
||||
+ get_mimic_pci_name(mimic_nm, sec->parent_dev->devfn);
|
||||
+ aml_append(method, aml_name("^%s.PCNT", mimic_nm));
|
||||
}
|
||||
|
||||
aml_append(parent_scope, method);
|
||||
@@ -802,9 +889,11 @@
|
||||
{
|
||||
Aml *if_ctx;
|
||||
int32_t devfn = PCI_DEVFN(slot, 0);
|
||||
+ char mimic_nm[5];
|
||||
|
||||
+ get_mimic_pci_name(mimic_nm, devfn);
|
||||
if_ctx = aml_if(aml_and(aml_arg(0), aml_int(0x1U << slot), NULL));
|
||||
- aml_append(if_ctx, aml_notify(aml_name("S%.02X", devfn), aml_arg(1)));
|
||||
+ aml_append(if_ctx, aml_notify(aml_name("%s", mimic_nm), aml_arg(1)));
|
||||
aml_append(method, if_ctx);
|
||||
}
|
||||
|
||||
@@ -867,10 +956,12 @@
|
||||
continue;
|
||||
}
|
||||
|
||||
+ char mimic_nm[5];
|
||||
+ get_mimic_pci_name(mimic_nm, devfn);
|
||||
if (bus->devices[devfn]) {
|
||||
- dev = aml_scope("S%.02X", devfn);
|
||||
+ dev = aml_scope("%s", mimic_nm);
|
||||
} else {
|
||||
- dev = aml_device("S%.02X", devfn);
|
||||
+ dev = aml_device("%s", mimic_nm);
|
||||
aml_append(dev, aml_name_decl("_ADR", aml_int(adr)));
|
||||
}
|
||||
|
||||
@@ -912,7 +1003,11 @@
|
||||
}
|
||||
|
||||
/* start to compose PCI device descriptor */
|
||||
|
||||
@@ -5,7 +5,7 @@ Targets VMAware check: VIRTUAL_DISK / DISK_SERIAL
|
||||
Storage that announces itself as emulated: 'QEMU HARDDISK', 'QEMU DVD-ROM',
|
||||
empty serials and the QEMU vendor string in SCSI inquiry data.
|
||||
|
||||
Applies to QEMU v11.1.1. Apply from the source root with:
|
||||
Applies to QEMU v11.1.2. Apply from the source root with:
|
||||
patch -p1 < 02-disk-identity.patch
|
||||
|
||||
---
|
||||
@@ -13,7 +13,7 @@ diff --git a/hw/scsi/scsi-disk.c b/hw/scsi/scsi-disk.c
|
||||
index 1b0cce1..0e8ded4 100644
|
||||
--- a/hw/scsi/scsi-disk.c
|
||||
+++ b/hw/scsi/scsi-disk.c
|
||||
@@ -2551,7 +2551,7 @@ static void scsi_realize(SCSIDevice *dev, Error **errp)
|
||||
@@ -2556,7 +2556,7 @@ static void scsi_realize(SCSIDevice *dev, Error **errp)
|
||||
s->version = g_strdup(QEMU_HW_VERSION);
|
||||
}
|
||||
if (!s->vendor) {
|
||||
@@ -22,7 +22,7 @@ index 1b0cce1..0e8ded4 100644
|
||||
}
|
||||
if (s->serial && strlen(s->serial) > MAX_SERIAL_LEN) {
|
||||
error_setg(errp, "The serial number can't be longer than %d characters",
|
||||
@@ -2615,7 +2615,7 @@ static void scsi_hd_realize(SCSIDevice *dev, Error **errp)
|
||||
@@ -2620,7 +2620,7 @@ static void scsi_hd_realize(SCSIDevice *dev, Error **errp)
|
||||
s->qdev.blocksize = s->qdev.conf.logical_block_size;
|
||||
s->qdev.type = TYPE_DISK;
|
||||
if (!s->product) {
|
||||
@@ -31,7 +31,7 @@ index 1b0cce1..0e8ded4 100644
|
||||
}
|
||||
scsi_realize(&s->qdev, errp);
|
||||
}
|
||||
@@ -2642,7 +2642,7 @@ static void scsi_cd_realize(SCSIDevice *dev, Error **errp)
|
||||
@@ -2647,7 +2647,7 @@ static void scsi_cd_realize(SCSIDevice *dev, Error **errp)
|
||||
s->qdev.type = TYPE_ROM;
|
||||
s->features |= 1 << SCSI_DISK_F_REMOVABLE;
|
||||
if (!s->product) {
|
||||
@@ -59,7 +59,7 @@ diff --git a/hw/ide/core.c b/hw/ide/core.c
|
||||
index fb9bf11..7f44de2 100644
|
||||
--- a/hw/ide/core.c
|
||||
+++ b/hw/ide/core.c
|
||||
@@ -2640,21 +2640,20 @@ int ide_init_drive(IDEState *s, IDEDevice *dev, IDEDriveKind kind, Error **errp)
|
||||
@@ -2648,21 +2648,20 @@ int ide_init_drive(IDEState *s, IDEDevice *dev, IDEDriveKind kind, Error **errp)
|
||||
if (dev->serial) {
|
||||
pstrcpy(s->drive_serial_str, sizeof(s->drive_serial_str), dev->serial);
|
||||
} else {
|
||||
|
||||
@@ -5,7 +5,10 @@ Targets VMAware check: PCI_VENDOR / DEVICES
|
||||
PCI vendor, device and subsystem IDs that read as Red Hat / QEMU, and the
|
||||
ICH9 southbridge layout OVMF expects to find at 1f:0.
|
||||
|
||||
Applies to QEMU v11.1.1. Apply from the source root with:
|
||||
Virtio devices keep Red Hat's subsystem IDs: their vendor ID names them
|
||||
anyway, and OVMF and the guest virtio drivers only bind with them.
|
||||
|
||||
Applies to QEMU v11.1.2. Apply from the source root with:
|
||||
patch -p1 < 03-pci-ids.patch
|
||||
|
||||
---
|
||||
@@ -24,19 +27,6 @@ index f2448e9..4eea405 100644
|
||||
#define PCI_DEVICE_ID_QEMU_IPMI 0x1112
|
||||
|
||||
/* VMWare (0x15ad) */
|
||||
@@ -77,9 +77,9 @@ extern bool pci_available;
|
||||
#define PCI_DEVICE_ID_INTEL_82801IR 0x2922
|
||||
|
||||
/* Red Hat / Qumranet (for QEMU) -- see pci-ids.txt */
|
||||
-#define PCI_VENDOR_ID_REDHAT_QUMRANET 0x1af4
|
||||
-#define PCI_SUBVENDOR_ID_REDHAT_QUMRANET 0x1af4
|
||||
-#define PCI_SUBDEVICE_ID_QEMU 0x1100
|
||||
+#define PCI_VENDOR_ID_REDHAT_QUMRANET 0x1af4 // VirtIO devices | IVSHMEM Driver | Looking Glass
|
||||
+#define PCI_SUBVENDOR_ID_REDHAT_QUMRANET 0x8086 // VirtIO devices | IVSHMEM Driver | Looking Glass
|
||||
+#define PCI_SUBDEVICE_ID_QEMU 0x0000 // VirtIO devices | IVSHMEM Driver | Looking Glass
|
||||
|
||||
/* legacy virtio-pci devices */
|
||||
#define PCI_DEVICE_ID_VIRTIO_NET 0x1000
|
||||
@@ -100,21 +100,21 @@ extern bool pci_available;
|
||||
*/
|
||||
#define PCI_DEVICE_ID_VIRTIO_10_BASE 0x1040
|
||||
@@ -100,15 +90,6 @@ index 2c35dd0..2b6a39c 100644
|
||||
#define ICH9_LPC_NB_PIRQS 8 /* PCI A-H */
|
||||
|
||||
#define ICH9_LPC_PMBASE 0x40
|
||||
@@ -177,7 +177,7 @@ struct ICH9LPCState {
|
||||
#define ICH9_GPIO_GSI "gsi"
|
||||
|
||||
/* D31:F2 SATA Controller #1 */
|
||||
-#define ICH9_SATA1_DEV 31
|
||||
+#define ICH9_SATA1_DEV 20
|
||||
#define ICH9_SATA1_FUNC 2
|
||||
|
||||
/* D31:F0 power management I/O registers
|
||||
@@ -213,7 +213,7 @@ struct ICH9LPCState {
|
||||
/* D31:F3 SMBus controller */
|
||||
#define TYPE_ICH9_SMB_DEVICE "ICH9-SMB"
|
||||
@@ -166,16 +147,32 @@ index e2aab3d..9131b5d 100644
|
||||
dc->user_creatable = true;
|
||||
vc->get_id = device_vmstate_if_get_id;
|
||||
rc->get_state = device_get_reset_state;
|
||||
diff --git a/hw/i386/pc.c b/hw/i386/pc.c
|
||||
index f064aa2..cfdbbc1 100644
|
||||
--- a/hw/i386/pc.c
|
||||
+++ b/hw/i386/pc.c
|
||||
@@ -1689,7 +1689,7 @@ static void pc_machine_class_init(ObjectClass *oc, const void *data)
|
||||
diff --git a/hw/pci/pci.c b/hw/pci/pci.c
|
||||
index d319160..7206e3d 100644
|
||||
--- a/hw/pci/pci.c
|
||||
+++ b/hw/pci/pci.c
|
||||
@@ -355,8 +355,8 @@ static void pci_irq_handler(void *opaque, int irq_num, int level);
|
||||
static void pci_add_option_rom(PCIDevice *pdev, bool is_default_rom, Error **);
|
||||
static void pci_del_option_rom(PCIDevice *pdev);
|
||||
|
||||
pcmc->pci_enabled = true;
|
||||
pcmc->has_acpi_build = true;
|
||||
- pcmc->smbios_defaults = true;
|
||||
+ pcmc->smbios_defaults = false;
|
||||
pcmc->gigabyte_align = true;
|
||||
pcmc->has_reserved_memory = true;
|
||||
pcmc->enforce_amd_1tb_hole = true;
|
||||
-static uint16_t pci_default_sub_vendor_id = PCI_SUBVENDOR_ID_REDHAT_QUMRANET;
|
||||
-static uint16_t pci_default_sub_device_id = PCI_SUBDEVICE_ID_QEMU;
|
||||
+static uint16_t pci_default_sub_vendor_id = PCI_VENDOR_ID_INTEL;
|
||||
+static uint16_t pci_default_sub_device_id = 0;
|
||||
|
||||
PCIHostStateList pci_host_bridges;
|
||||
|
||||
diff --git a/hw/virtio/virtio-pci.c b/hw/virtio/virtio-pci.c
|
||||
index 6f5db5f..a7e07b8 100644
|
||||
--- a/hw/virtio/virtio-pci.c
|
||||
+++ b/hw/virtio/virtio-pci.c
|
||||
@@ -2029,6 +2029,9 @@ static void virtio_pci_device_plugged(DeviceState *d, Error **errp)
|
||||
modern = virtio_pci_modern(proxy);
|
||||
|
||||
config = proxy->pci_dev.config;
|
||||
+ /* virtio drivers match Red Hat's subsystem IDs; OVMF needs a device ID >= 0x40 */
|
||||
+ pci_set_word(config + PCI_SUBSYSTEM_VENDOR_ID, PCI_SUBVENDOR_ID_REDHAT_QUMRANET);
|
||||
+ pci_set_word(config + PCI_SUBSYSTEM_ID, PCI_SUBDEVICE_ID_QEMU);
|
||||
if (proxy->class_code) {
|
||||
pci_config_set_class(config, proxy->class_code);
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ Targets VMAware check: QEMU_FW_CFG
|
||||
|
||||
The fw_cfg device signature, which names QEMU literally.
|
||||
|
||||
Applies to QEMU v11.1.1. Apply from the source root with:
|
||||
Applies to QEMU v11.1.2. Apply from the source root with:
|
||||
patch -p1 < 04-fw-cfg.patch
|
||||
|
||||
---
|
||||
|
||||
@@ -4,7 +4,7 @@ Targets VMAware check: QEMU_USB
|
||||
|
||||
USB HID descriptors carrying QEMU vendor strings and product IDs.
|
||||
|
||||
Applies to QEMU v11.1.1. Apply from the source root with:
|
||||
Applies to QEMU v11.1.2. Apply from the source root with:
|
||||
patch -p1 < 05-usb-hid.patch
|
||||
|
||||
---
|
||||
|
||||
@@ -4,7 +4,7 @@ Targets VMAware check: DEVICES
|
||||
|
||||
Intel HDA codec and controller identity strings.
|
||||
|
||||
Applies to QEMU v11.1.1. Apply from the source root with:
|
||||
Applies to QEMU v11.1.2. Apply from the source root with:
|
||||
patch -p1 < 06-audio.patch
|
||||
|
||||
---
|
||||
|
||||
@@ -5,7 +5,7 @@ Targets VMAware check: (display identity)
|
||||
Generated EDID vendor and model. Note VMAware does not read EDID - this is
|
||||
carried for other detectors, not for the score.
|
||||
|
||||
Applies to QEMU v11.1.1. Apply from the source root with:
|
||||
Applies to QEMU v11.1.2. Apply from the source root with:
|
||||
patch -p1 < 07-edid.patch
|
||||
|
||||
---
|
||||
|
||||
@@ -4,7 +4,7 @@ Targets VMAware check: (CPU model plumbing)
|
||||
|
||||
CPU feature-word plumbing and an upstream backport; not aimed at a check.
|
||||
|
||||
Applies to QEMU v11.1.1. Apply from the source root with:
|
||||
Applies to QEMU v11.1.2. Apply from the source root with:
|
||||
patch -p1 < 08-cpu-misc.patch
|
||||
|
||||
---
|
||||
|
||||
@@ -0,0 +1,239 @@
|
||||
Subject: [PATCH] acpi: clear the remaining VMAware FIRMWARE chain links
|
||||
|
||||
Targets VMAware check: FIRMWARE
|
||||
|
||||
01-firmware cleared the FIRMWARE links known when it was written. Current
|
||||
VMAware adds more DSDT fingerprints to the same chain, and firmware() returns
|
||||
on the first match, so the technique keeps firing until every link is gone.
|
||||
This clears the rest, taking a q35 guest to 0 detections on both the v2.8.1
|
||||
release and current HEAD. Each change is a rename or a byte-neutral rewrite of
|
||||
emitted AML; none alters ACPI semantics.
|
||||
|
||||
Links cleared:
|
||||
- PCI interrupt-link query methods IQST/IQCR and the PRR0/PRRI resource
|
||||
template/field -> PSTA/PCRS/PRS0/PRSI
|
||||
- TPM Physical Presence Interface regions/method TPP2/TPP3/TPFN -> PPI2/PPI3/PPIF
|
||||
- zeroed _S5 SLP_TYP: emit the 2-element package form
|
||||
- ACPI0006 _HID in \_GPE: declared by its full path \_GPE._HID; the
|
||||
object stays, because macOS will not power off without it
|
||||
- Q35 MMCONFIG reservation device MCFG -> PCMM
|
||||
- synthetic GSI link devices: give the no-op _DIS/_SRS a body so the
|
||||
empty-stub descriptor no longer matches
|
||||
- PCIe _OSC capability mask: And() operands swapped (commutative)
|
||||
- PIRQ link _PRS: list the identical IRQ set as {11, 10, 5} so the fixed
|
||||
5/10/11 descriptor differs
|
||||
|
||||
Applies to QEMU v11.1.2, on top of 01-firmware. From the source root:
|
||||
patch -p1 < 09-firmware-chain.patch
|
||||
|
||||
diff --git a/hw/acpi/pci.c b/hw/acpi/pci.c
|
||||
index 8c7ed1047..0d6a95d67 100644
|
||||
--- a/hw/acpi/pci.c
|
||||
+++ b/hw/acpi/pci.c
|
||||
@@ -326,8 +326,8 @@ Aml *build_pci_host_bridge_osc_method(bool enable_native_pcie_hotplug)
|
||||
* Allow SHPC (PCI bridges can have SHPC controller)
|
||||
* Disable PCIe Native Hot-plug if ACPI PCI Hot-plug is enabled.
|
||||
*/
|
||||
- aml_append(if_ctx, aml_and(a_ctrl,
|
||||
- aml_int(0x1E | (enable_native_pcie_hotplug ? 0x1 : 0x0)), a_ctrl));
|
||||
+ aml_append(if_ctx, aml_and(
|
||||
+ aml_int(0x1E | (enable_native_pcie_hotplug ? 0x1 : 0x0)), a_ctrl, a_ctrl));
|
||||
|
||||
if_ctx2 = aml_if(aml_lnot(aml_equal(aml_arg(1), aml_int(1))));
|
||||
/* Unknown revision */
|
||||
diff --git a/hw/acpi/tpm.c b/hw/acpi/tpm.c
|
||||
index e70377598..d7c234cc5 100644
|
||||
--- a/hw/acpi/tpm.c
|
||||
+++ b/hw/acpi/tpm.c
|
||||
@@ -39,10 +39,10 @@ void tpm_build_ppi_acpi(TPMIf *tpm, Aml *dev, hwaddr ppi_base)
|
||||
* the PPI code and parameter (PPRQ, PPRM) to the firmware.
|
||||
*/
|
||||
aml_append(dev,
|
||||
- aml_operation_region("TPP2", AML_SYSTEM_MEMORY,
|
||||
+ aml_operation_region("PPI2", AML_SYSTEM_MEMORY,
|
||||
aml_int(ppi_base + 0x100),
|
||||
0x5A));
|
||||
- field = aml_field("TPP2", AML_ANY_ACC, AML_NOLOCK, AML_PRESERVE);
|
||||
+ field = aml_field("PPI2", AML_ANY_ACC, AML_NOLOCK, AML_PRESERVE);
|
||||
aml_append(field, aml_named_field("PPIN", 8));
|
||||
aml_append(field, aml_named_field("PPIP", 32));
|
||||
aml_append(field, aml_named_field("PPRP", 32));
|
||||
@@ -55,11 +55,11 @@ void tpm_build_ppi_acpi(TPMIf *tpm, Aml *dev, hwaddr ppi_base)
|
||||
|
||||
aml_append(dev,
|
||||
aml_operation_region(
|
||||
- "TPP3", AML_SYSTEM_MEMORY,
|
||||
+ "PPI3", AML_SYSTEM_MEMORY,
|
||||
aml_int(ppi_base +
|
||||
0x15a /* movv, docs/specs/tpm.rst */),
|
||||
0x1));
|
||||
- field = aml_field("TPP3", AML_BYTE_ACC, AML_NOLOCK, AML_PRESERVE);
|
||||
+ field = aml_field("PPI3", AML_BYTE_ACC, AML_NOLOCK, AML_PRESERVE);
|
||||
aml_append(field, aml_named_field("MOVV", 8));
|
||||
aml_append(dev, field);
|
||||
|
||||
@@ -67,7 +67,7 @@ void tpm_build_ppi_acpi(TPMIf *tpm, Aml *dev, hwaddr ppi_base)
|
||||
* DerefOf in Windows is broken with SYSTEM_MEMORY. Use a dynamic
|
||||
* operation region inside of a method for getting FUNC[op].
|
||||
*/
|
||||
- method = aml_method("TPFN", 1, AML_SERIALIZED);
|
||||
+ method = aml_method("PPIF", 1, AML_SERIALIZED);
|
||||
{
|
||||
Aml *op = aml_arg(0);
|
||||
ifctx = aml_if(aml_lgreater_equal(op, aml_int(0x100)));
|
||||
@@ -164,7 +164,7 @@ void tpm_build_ppi_acpi(TPMIf *tpm, Aml *dev, hwaddr ppi_base)
|
||||
|
||||
/* get opcode flags */
|
||||
aml_append(ifctx2,
|
||||
- aml_store(aml_call1("TPFN", op), op_flags));
|
||||
+ aml_store(aml_call1("PPIF", op), op_flags));
|
||||
|
||||
/* if func[opcode] & TPM_PPI_FUNC_NOT_IMPLEMENTED */
|
||||
ifctx3 = aml_if(
|
||||
@@ -325,7 +325,7 @@ void tpm_build_ppi_acpi(TPMIf *tpm, Aml *dev, hwaddr ppi_base)
|
||||
op));
|
||||
|
||||
/* get opcode flags */
|
||||
- aml_append(ifctx2, aml_store(aml_call1("TPFN", op),
|
||||
+ aml_append(ifctx2, aml_store(aml_call1("PPIF", op),
|
||||
op_flags));
|
||||
/* if func[opcode] & TPM_PPI_FUNC_NOT_IMPLEMENTED */
|
||||
ifctx3 = aml_if(
|
||||
@@ -397,7 +397,7 @@ void tpm_build_ppi_acpi(TPMIf *tpm, Aml *dev, hwaddr ppi_base)
|
||||
op));
|
||||
|
||||
/* get opcode flags */
|
||||
- aml_append(ifctx2, aml_store(aml_call1("TPFN", op),
|
||||
+ aml_append(ifctx2, aml_store(aml_call1("PPIF", op),
|
||||
op_flags));
|
||||
/* return confirmation status code */
|
||||
aml_append(ifctx2,
|
||||
diff --git a/hw/i386/acpi-build.c b/hw/i386/acpi-build.c
|
||||
index 4001d9d69..087d5f778 100644
|
||||
--- a/hw/i386/acpi-build.c
|
||||
+++ b/hw/i386/acpi-build.c
|
||||
@@ -515,7 +515,7 @@ static Aml *build_link_dev(const char *name, uint8_t uid, Aml *reg)
|
||||
Aml *dev;
|
||||
Aml *crs;
|
||||
Aml *method;
|
||||
- uint32_t irqs[] = {5, 10, 11};
|
||||
+ uint32_t irqs[] = {11, 10, 5};
|
||||
|
||||
dev = aml_device("%s", name);
|
||||
aml_append(dev, aml_name_decl("_HID", aml_eisaid("PNP0C0F")));
|
||||
@@ -527,7 +527,7 @@ static Aml *build_link_dev(const char *name, uint8_t uid, Aml *reg)
|
||||
aml_append(dev, aml_name_decl("_PRS", crs));
|
||||
|
||||
method = aml_method("_STA", 0, AML_NOTSERIALIZED);
|
||||
- aml_append(method, aml_return(aml_call1("IQST", reg)));
|
||||
+ aml_append(method, aml_return(aml_call1("PSTA", reg)));
|
||||
aml_append(dev, method);
|
||||
|
||||
method = aml_method("_DIS", 0, AML_NOTSERIALIZED);
|
||||
@@ -535,12 +535,12 @@ static Aml *build_link_dev(const char *name, uint8_t uid, Aml *reg)
|
||||
aml_append(dev, method);
|
||||
|
||||
method = aml_method("_CRS", 0, AML_NOTSERIALIZED);
|
||||
- aml_append(method, aml_return(aml_call1("IQCR", reg)));
|
||||
+ aml_append(method, aml_return(aml_call1("PCRS", reg)));
|
||||
aml_append(dev, method);
|
||||
|
||||
method = aml_method("_SRS", 1, AML_NOTSERIALIZED);
|
||||
- aml_append(method, aml_create_dword_field(aml_arg(0), aml_int(5), "PRRI"));
|
||||
- aml_append(method, aml_store(aml_name("PRRI"), reg));
|
||||
+ aml_append(method, aml_create_dword_field(aml_arg(0), aml_int(5), "PRSI"));
|
||||
+ aml_append(method, aml_store(aml_name("PRSI"), reg));
|
||||
aml_append(dev, method);
|
||||
|
||||
return dev;
|
||||
@@ -569,9 +569,11 @@ static Aml *build_gsi_link_dev(const char *name, uint8_t uid, uint8_t gsi)
|
||||
* _DIS can be no-op because the interrupt cannot be disabled.
|
||||
*/
|
||||
method = aml_method("_DIS", 0, AML_NOTSERIALIZED);
|
||||
+ aml_append(method, aml_store(aml_int(0), aml_local(0)));
|
||||
aml_append(dev, method);
|
||||
|
||||
method = aml_method("_SRS", 1, AML_NOTSERIALIZED);
|
||||
+ aml_append(method, aml_store(aml_int(0), aml_local(0)));
|
||||
aml_append(dev, method);
|
||||
|
||||
return dev;
|
||||
@@ -582,28 +584,28 @@ static Aml *build_iqcr_method(bool is_piix4)
|
||||
{
|
||||
Aml *if_ctx;
|
||||
uint32_t irqs;
|
||||
- Aml *method = aml_method("IQCR", 1, AML_SERIALIZED);
|
||||
+ Aml *method = aml_method("PCRS", 1, AML_SERIALIZED);
|
||||
Aml *crs = aml_resource_template();
|
||||
|
||||
irqs = 0;
|
||||
aml_append(crs, aml_interrupt(AML_CONSUMER, AML_LEVEL,
|
||||
AML_ACTIVE_HIGH, AML_SHARED, &irqs, 1));
|
||||
- aml_append(method, aml_name_decl("PRR0", crs));
|
||||
+ aml_append(method, aml_name_decl("PRS0", crs));
|
||||
|
||||
aml_append(method,
|
||||
- aml_create_dword_field(aml_name("PRR0"), aml_int(5), "PRRI"));
|
||||
+ aml_create_dword_field(aml_name("PRS0"), aml_int(5), "PRSI"));
|
||||
|
||||
if (is_piix4) {
|
||||
if_ctx = aml_if(aml_lless(aml_arg(0), aml_int(0x80)));
|
||||
- aml_append(if_ctx, aml_store(aml_arg(0), aml_name("PRRI")));
|
||||
+ aml_append(if_ctx, aml_store(aml_arg(0), aml_name("PRSI")));
|
||||
aml_append(method, if_ctx);
|
||||
} else {
|
||||
aml_append(method,
|
||||
aml_store(aml_and(aml_arg(0), aml_int(0xF), NULL),
|
||||
- aml_name("PRRI")));
|
||||
+ aml_name("PRSI")));
|
||||
}
|
||||
|
||||
- aml_append(method, aml_return(aml_name("PRR0")));
|
||||
+ aml_append(method, aml_return(aml_name("PRS0")));
|
||||
return method;
|
||||
}
|
||||
|
||||
@@ -611,7 +613,7 @@ static Aml *build_iqcr_method(bool is_piix4)
|
||||
static Aml *build_irq_status_method(void)
|
||||
{
|
||||
Aml *if_ctx;
|
||||
- Aml *method = aml_method("IQST", 1, AML_NOTSERIALIZED);
|
||||
+ Aml *method = aml_method("PSTA", 1, AML_NOTSERIALIZED);
|
||||
|
||||
if_ctx = aml_if(aml_and(aml_int(0x80), aml_arg(0), NULL));
|
||||
aml_append(if_ctx, aml_return(aml_int(0x09)));
|
||||
@@ -801,7 +803,7 @@ static Aml *build_q35_dram_controller(const AcpiMcfgInfo *mcfg)
|
||||
Aml *resource_template;
|
||||
|
||||
/* DRAM controller */
|
||||
- dev = aml_device("MCFG");
|
||||
+ dev = aml_device("PCMM");
|
||||
aml_append(dev, aml_name_decl("_HID", aml_string("PNP0C01")));
|
||||
|
||||
resource_template = aml_resource_template();
|
||||
@@ -986,7 +988,11 @@ build_dsdt(GArray *table_data, BIOSLinker *linker,
|
||||
|
||||
scope = aml_scope("_GPE");
|
||||
{
|
||||
- aml_append(scope, aml_name_decl("_HID", aml_string("ACPI0006")));
|
||||
+ /*
|
||||
+ * macOS needs the GPE block device to reach S5; naming it by full
|
||||
+ * path keeps it without the byte pattern VMAware matches.
|
||||
+ */
|
||||
+ aml_append(scope, aml_name_decl("\\_GPE._HID", aml_string("ACPI0006")));
|
||||
if (machine->nvdimms_state->is_enabled) {
|
||||
method = aml_method("_E04", 0, AML_NOTSERIALIZED);
|
||||
aml_append(method, aml_notify(aml_name("\\_SB.NVDR"),
|
||||
@@ -1193,11 +1199,9 @@ build_dsdt(GArray *table_data, BIOSLinker *linker,
|
||||
aml_append(scope, aml_name_decl("_S4", pkg));
|
||||
}
|
||||
|
||||
- pkg = aml_package(4);
|
||||
+ pkg = aml_package(2);
|
||||
aml_append(pkg, aml_int(0)); /* PM1a_CNT.SLP_TYP */
|
||||
aml_append(pkg, aml_int(0)); /* PM1b_CNT.SLP_TYP not impl. */
|
||||
- aml_append(pkg, aml_int(0)); /* reserved */
|
||||
- aml_append(pkg, aml_int(0)); /* reserved */
|
||||
aml_append(scope, aml_name_decl("_S5", pkg));
|
||||
aml_append(dsdt, scope);
|
||||
|
||||
@@ -0,0 +1,97 @@
|
||||
Subject: [PATCH] smbios: CTYPE / SMBIOS_VM_BIT / FIRMWARE
|
||||
|
||||
Targets VMAware check: CTYPE / SMBIOS_VM_BIT / FIRMWARE
|
||||
|
||||
QEMU keeps synthesising every required SMBIOS structure the domain does not
|
||||
supply, so identity can be set with -smbios type=N field arguments and real
|
||||
structures passed with -smbios file= only where they are wanted. What QEMU
|
||||
fills in by itself must not read as a VM:
|
||||
|
||||
- type 3 chassis type was hard-coded to 1 (Other), which CTYPE flags. It
|
||||
defaults to 3 (Desktop) and is set with type=3,chassis-type=N.
|
||||
- the type 0 "virtual machine" characteristics bit now defaults off, so a
|
||||
type 0 from libvirt <sysinfo>, which cannot pass vm=off, stays clean.
|
||||
- unset product and version strings in types 1-4 default to "Unknown"
|
||||
instead of the machine description and name ("Standard PC (Q35 + ICH9,
|
||||
2009)", "pc-q35-<ver>"), which FIRMWARE matches when it scans SMBIOS on
|
||||
Windows.
|
||||
|
||||
Applies to QEMU v11.1.2, on top of 04-fw-cfg. Apply from the source root with:
|
||||
patch -p1 < 10-smbios.patch
|
||||
|
||||
---
|
||||
diff --git a/hw/i386/fw_cfg.c b/hw/i386/fw_cfg.c
|
||||
index e5fddbc..bfc193c 100644
|
||||
--- a/hw/i386/fw_cfg.c
|
||||
+++ b/hw/i386/fw_cfg.c
|
||||
@@ -71,13 +71,12 @@ void fw_cfg_build_smbios(PCMachineState *pcms, FWCfgState *fw_cfg,
|
||||
unsigned i, array_count;
|
||||
MachineState *ms = MACHINE(pcms);
|
||||
PCMachineClass *pcmc = PC_MACHINE_GET_CLASS(pcms);
|
||||
- MachineClass *mc = MACHINE_GET_CLASS(pcms);
|
||||
X86CPU *cpu = X86_CPU(ms->possible_cpus->cpus[0].cpu);
|
||||
int nr_e820;
|
||||
|
||||
if (pcmc->smbios_defaults) {
|
||||
/* These values are guest ABI, do not change */
|
||||
- smbios_set_defaults("Unknown", mc->desc, mc->name);
|
||||
+ smbios_set_defaults("Unknown", "Unknown", "Unknown");
|
||||
}
|
||||
|
||||
/* tell smbios about cpuid version and features */
|
||||
diff --git a/hw/smbios/smbios.c b/hw/smbios/smbios.c
|
||||
index 7d71418..c557069 100644
|
||||
--- a/hw/smbios/smbios.c
|
||||
+++ b/hw/smbios/smbios.c
|
||||
@@ -62,7 +62,8 @@ static struct {
|
||||
|
||||
static struct {
|
||||
const char *manufacturer, *version, *serial, *asset, *sku;
|
||||
-} type3;
|
||||
+ uint64_t chassis_type;
|
||||
+} type3 = { .chassis_type = 0x03 }; /* Desktop: QEMU's "Other" reads as a VM */
|
||||
|
||||
/*
|
||||
* SVVP requires max_speed and current_speed to be set and not being
|
||||
@@ -282,6 +283,10 @@ static const QemuOptDesc qemu_smbios_type3_opts[] = {
|
||||
.name = "sku",
|
||||
.type = QEMU_OPT_STRING,
|
||||
.help = "SKU number",
|
||||
+ },{
|
||||
+ .name = "chassis-type",
|
||||
+ .type = QEMU_OPT_NUMBER,
|
||||
+ .help = "chassis type",
|
||||
},
|
||||
{ /* end of list */ }
|
||||
};
|
||||
@@ -656,7 +661,7 @@ static void smbios_build_type_3_table(void)
|
||||
SMBIOS_BUILD_TABLE_PRE(3, T3_BASE, true); /* required */
|
||||
|
||||
SMBIOS_TABLE_SET_STR(3, manufacturer_str, type3.manufacturer);
|
||||
- t->type = 0x01; /* Other */
|
||||
+ t->type = type3.chassis_type;
|
||||
SMBIOS_TABLE_SET_STR(3, version_str, type3.version);
|
||||
SMBIOS_TABLE_SET_STR(3, serial_number_str, type3.serial);
|
||||
SMBIOS_TABLE_SET_STR(3, asset_tag_number_str, type3.asset);
|
||||
@@ -1413,7 +1418,7 @@ void smbios_entry_add(QemuOpts *opts, Error **errp)
|
||||
save_opt(&smbios_type0.version, opts, "version");
|
||||
save_opt(&smbios_type0.date, opts, "date");
|
||||
smbios_type0.uefi = qemu_opt_get_bool(opts, "uefi", false);
|
||||
- smbios_type0.vm = qemu_opt_get_bool(opts, "vm", true);
|
||||
+ smbios_type0.vm = qemu_opt_get_bool(opts, "vm", false);
|
||||
|
||||
val = qemu_opt_get(opts, "release");
|
||||
if (val) {
|
||||
@@ -1465,6 +1470,12 @@ void smbios_entry_add(QemuOpts *opts, Error **errp)
|
||||
save_opt(&type3.serial, opts, "serial");
|
||||
save_opt(&type3.asset, opts, "asset");
|
||||
save_opt(&type3.sku, opts, "sku");
|
||||
+ type3.chassis_type = qemu_opt_get_number(opts, "chassis-type",
|
||||
+ type3.chassis_type);
|
||||
+ if (type3.chassis_type > UINT8_MAX) {
|
||||
+ error_setg(errp, "SMBIOS chassis type is too large (> %d)",
|
||||
+ UINT8_MAX);
|
||||
+ }
|
||||
return;
|
||||
case 4:
|
||||
if (!qemu_opts_validate(opts, qemu_smbios_type4_opts, errp)) {
|
||||
@@ -0,0 +1,91 @@
|
||||
Subject: [PATCH] acpi: keep the CPU objects macOS needs without the FIRMWARE tell
|
||||
|
||||
Targets VMAware check: FIRMWARE (CPU hotplug I/O signature)
|
||||
|
||||
macOS references \_SB.CPUS.Cxxx from its own CpuPlug SSDT, so the guest needs
|
||||
QEMU's build_cpus_aml output (the \_SB.PCI0.PRES controller and the \_SB.CPUS
|
||||
Processor objects) or it hangs in ACPI namespace init. 01-firmware dropped that
|
||||
block because build_cpus_aml emits the CPU-hotplug register region at I/O port
|
||||
0x0CD8, which VMAware v2.8.1 FIRMWARE byte-matches as the WordData sequence
|
||||
0x0B 0xD8 0x0C and flips to a QEMU verdict. HEAD has no such check.
|
||||
|
||||
This re-adds the block for every guest (no opt-in) and removes that one tell at
|
||||
the source rather than removing the whole block:
|
||||
|
||||
- the controller _UID is renamed from "CPU Hotplug resources" to "Reserved
|
||||
resources C", clearing the FIRMWARE string match the same way 01-firmware
|
||||
renamed the other reserved-resource devices.
|
||||
|
||||
- the PRST OperationRegion base is emitted as Add(0x0C00, 0xD8) for a SystemIO
|
||||
base. It evaluates to the identical 0x0CD8 at namespace load, so the port
|
||||
QEMU decodes and the port OVMF's SMM code reads directly are both unchanged.
|
||||
Relocating the base instead was tried and breaks every SMM/OVMF guest (macOS
|
||||
hangs at boot, Windows too), because OVMF hardcodes 0x0CD8. Splitting the
|
||||
operand keeps 0x0CD8 live but never places the three matched bytes next to
|
||||
each other in the AML.
|
||||
|
||||
The _CRS IO descriptor keeps 0x0CD8 untouched: a small-resource IO descriptor
|
||||
encodes its base as 0xD8 0x0C preceded by the 0x47/0x01 descriptor bytes, never
|
||||
by 0x0B, so it is not the sequence the scanner matches.
|
||||
|
||||
Verified against QEMU 11.1.2: macOS boots to login and powers off via S5;
|
||||
VMAware v2.8.1 and HEAD both read bare metal with FIRMWARE clear on the stealth
|
||||
guest; Windows and Linux boot unchanged.
|
||||
|
||||
Applies to QEMU v11.1.2, on top of 01-firmware. From the source root:
|
||||
patch -p1 < 11-macos-cpu-hotplug.patch
|
||||
|
||||
--- a/hw/i386/acpi-build.c
|
||||
+++ b/hw/i386/acpi-build.c
|
||||
@@ -1002,6 +1002,16 @@
|
||||
}
|
||||
aml_append(dsdt, scope);
|
||||
|
||||
+ {
|
||||
+ CPUHotplugFeatures opts = {
|
||||
+ .acpi_1_compatible = true,
|
||||
+ .smi_path = pm->smi_on_cpuhp ? "\\_SB.PCI0.SMI0.SMIC" : NULL,
|
||||
+ .fw_unplugs_cpu = pm->smi_on_cpu_unplug,
|
||||
+ };
|
||||
+ build_cpus_aml(dsdt, machine, opts, pc_madt_cpu_entry,
|
||||
+ pm->cpu_hp_io_base, "\\_SB.PCI0", "\\_GPE._E02",
|
||||
+ AML_SYSTEM_IO);
|
||||
+ }
|
||||
|
||||
crs_range_set_init(&crs_range_set);
|
||||
bus = PC_MACHINE(machine)->pcibus;
|
||||
--- a/hw/acpi/cpu.c
|
||||
+++ b/hw/acpi/cpu.c
|
||||
@@ -363,7 +363,7 @@
|
||||
aml_append(cpu_ctrl_dev,
|
||||
aml_name_decl("_HID", aml_eisaid("PNP0A06")));
|
||||
aml_append(cpu_ctrl_dev,
|
||||
- aml_name_decl("_UID", aml_string("CPU Hotplug resources")));
|
||||
+ aml_name_decl("_UID", aml_string("Reserved resources C")));
|
||||
aml_append(cpu_ctrl_dev, aml_mutex(CPU_LOCK, 0));
|
||||
|
||||
assert((rs == AML_SYSTEM_IO) || (rs == AML_SYSTEM_MEMORY));
|
||||
@@ -379,9 +379,21 @@
|
||||
|
||||
aml_append(cpu_ctrl_dev, aml_name_decl("_CRS", crs));
|
||||
|
||||
- /* declare CPU hotplug MMIO region with related access fields */
|
||||
+ /*
|
||||
+ * Declare the CPU hotplug register block. The region is at the same
|
||||
+ * fixed base QEMU decodes and firmware (OVMF SMM) reads directly, so
|
||||
+ * that is unchanged. Only the base operand's AML encoding is split
|
||||
+ * across an Add() for a SystemIO base: emitting the 16-bit port as a
|
||||
+ * single WordData leaks the exact three-byte sequence a VM-detection
|
||||
+ * scanner byte-matches on. The Add() evaluates to the identical base
|
||||
+ * at namespace load but never places those bytes contiguously.
|
||||
+ */
|
||||
+ Aml *region_base = (rs == AML_SYSTEM_IO)
|
||||
+ ? aml_add(aml_int(base_addr & 0xff00),
|
||||
+ aml_int(base_addr & 0x00ff), NULL)
|
||||
+ : aml_int(base_addr);
|
||||
aml_append(cpu_ctrl_dev,
|
||||
- aml_operation_region("PRST", rs, aml_int(base_addr),
|
||||
+ aml_operation_region("PRST", rs, region_base,
|
||||
ACPI_CPU_HOTPLUG_REG_LEN));
|
||||
|
||||
field = aml_field("PRST", AML_BYTE_ACC, AML_NOLOCK,
|
||||
@@ -1,8 +0,0 @@
|
||||
# vfio-native: the emulated e1000e NIC's TX checksum and segmentation offloads
|
||||
# corrupt packets on the host side of a libvirt tap. SMB tolerates it; SSH fails
|
||||
# with "Corrupted MAC on input" and any integrity-checked protocol breaks.
|
||||
# Measured on a Zen 4 host with QEMU 11.1.1. Disabling the offloads on every
|
||||
# libvirt tap as it appears fixes it; on a host<->guest link the throughput
|
||||
# cost is not measurable. libvirt's <driver><host .../> attributes are ignored
|
||||
# for e1000e, and a libvirt hook must not call virsh, hence udev.
|
||||
ACTION=="add", SUBSYSTEM=="net", KERNEL=="vnet*", RUN+="/usr/bin/ethtool -K %k tx off gso off gro off tso off"
|
||||
Executable
+71
@@ -0,0 +1,71 @@
|
||||
#!/bin/bash
|
||||
# Keeps the kvm_amd cpuid_passthrough switch correct across a guest's whole life,
|
||||
# in-guest reboots included.
|
||||
#
|
||||
# The switch must be OFF (N) while the guest enumerates CPUID at boot or Windows
|
||||
# hangs, and ON (Y) once it is up, where it clears the timer detection. The libvirt
|
||||
# hook only fires at VM start and stop, so a guest-initiated reboot would otherwise
|
||||
# re-enumerate with the switch still Y and hang. This watcher drops it to N on every
|
||||
# QEMU RESET and raises it again once the guest's NIC is back up.
|
||||
#
|
||||
# Readiness signal: the guest tap's rx_packets counter growing past a baseline. It
|
||||
# only moves once the guest NIC driver has loaded, well past CPU enumeration, and it
|
||||
# needs nothing enabled inside the guest. The counter is cumulative and does NOT
|
||||
# reset on an in-guest reboot, so readiness is growth past the value captured at the
|
||||
# reset, not an absolute threshold.
|
||||
#
|
||||
# Launched as a transient systemd unit by the cpuid-passthrough hook, so it is free
|
||||
# to call virsh (the hook itself must not - that deadlocks libvirtd).
|
||||
|
||||
DOMAIN=$1
|
||||
BRAND=$2
|
||||
PARAM=/sys/module/kvm_amd/parameters
|
||||
V="virsh -c qemu:///system"
|
||||
|
||||
running() { [ "$($V domstate "$DOMAIN" 2>/dev/null)" = running ]; }
|
||||
rx() { cat "$RX" 2>/dev/null || echo 0; }
|
||||
|
||||
set_N() { echo N > "$PARAM/cpuid_passthrough"; }
|
||||
set_Y() { printf '%s' "$BRAND" > "$PARAM/brand_string"; echo Y > "$PARAM/cpuid_passthrough"; }
|
||||
|
||||
# Wait until the tap rx counter grows at least 4 past $1 (guest NIC driver up again).
|
||||
# The iteration floor keeps a stray pre-OS packet (a UEFI netboot attempt) from
|
||||
# tripping the flip before the guest is even past its interrupt and timer setup.
|
||||
wait_net_up() {
|
||||
local base=$1 i=0
|
||||
for _ in $(seq 1 100); do # ~300 s cap, then raise anyway if still up
|
||||
i=$((i + 1))
|
||||
running || return 1
|
||||
[ "$i" -ge 4 ] && [ -n "$tap" ] && [ -r "$RX" ] &&
|
||||
[ "$(rx)" -ge "$((base + 4))" ] && return 0
|
||||
sleep 3
|
||||
done
|
||||
return 0
|
||||
}
|
||||
|
||||
# The hook arms this watcher at prepare, before QEMU starts, so the domain is not
|
||||
# yet running and its tap does not exist. Wait for the domain, then read the tap;
|
||||
# otherwise the first running check in wait_net_up bails and the flip never happens.
|
||||
for _ in $(seq 1 120); do running && break; sleep 1; done
|
||||
running || exit 0
|
||||
tap=$($V domiflist "$DOMAIN" 2>/dev/null | awk '$1 ~ /^(vnet|tap|macvtap)/ {print $1; exit}')
|
||||
RX="/sys/class/net/$tap/statistics/rx_packets"
|
||||
|
||||
# initial cold boot: wait for the network, then harden
|
||||
wait_net_up 0
|
||||
running || exit 0
|
||||
set_Y
|
||||
logger -t vfio-cpuid "$DOMAIN network up: cpuid_passthrough=Y"
|
||||
|
||||
# every in-guest reboot fires a QEMU RESET: drop to N for the re-enumeration, then
|
||||
# raise it again once the guest's NIC is back. --loop streams one line per reset.
|
||||
$V qemu-monitor-event --domain "$DOMAIN" --event RESET --loop 2>/dev/null | while read -r _; do
|
||||
running || continue
|
||||
base=$(rx)
|
||||
set_N
|
||||
logger -t vfio-cpuid "$DOMAIN reset: cpuid_passthrough=N for re-enumeration"
|
||||
wait_net_up "$base" || continue
|
||||
running || continue
|
||||
set_Y
|
||||
logger -t vfio-cpuid "$DOMAIN back up: cpuid_passthrough=Y"
|
||||
done
|
||||
Executable
+77
@@ -0,0 +1,77 @@
|
||||
#!/bin/bash
|
||||
# vm-native-cpuid - manage the automatic cpuid_passthrough toggle for hardened guests.
|
||||
#
|
||||
# vm-native-cpuid enable <domain> apply the automatic cpuid passthrough to <domain>
|
||||
# vm-native-cpuid disable <domain> stop applying it to <domain>
|
||||
# vm-native-cpuid status show the config and the live module state
|
||||
# vm-native-cpuid revert remove the hook, watcher and config; reset the module
|
||||
#
|
||||
# The switch and brand string are one global kvm_amd parameter, so it serves one
|
||||
# hardened guest at a time. It is off until you enable it here - no domain is
|
||||
# touched otherwise. Around an enabled guest the libvirt hook forces the switch off
|
||||
# for the cold boot, a watcher flips it on once the guest's network is up, and it
|
||||
# goes off again on stop.
|
||||
|
||||
set -uo pipefail
|
||||
|
||||
CONF=/etc/vfio-native/cpuid-passthrough.conf
|
||||
HOOK=/etc/libvirt/hooks/qemu.d/30-cpuid-passthrough.sh
|
||||
WATCH=/usr/lib/vfio-native/cpuid-passthrough-watch
|
||||
PARAM=/sys/module/kvm_amd/parameters
|
||||
SELF=$(cd "$(dirname "$0")/.." 2>/dev/null && pwd)
|
||||
|
||||
die() { echo "$*" >&2; exit 1; }
|
||||
need_root() { [ "$(id -u)" = 0 ] || die "run this as root"; }
|
||||
|
||||
load() { ENABLED=yes; DOMAINS=""; [ -r "$CONF" ] && . "$CONF"; }
|
||||
save() { mkdir -p "$(dirname "$CONF")"; printf 'ENABLED=%s\nDOMAINS="%s"\n' "$ENABLED" "$DOMAINS" > "$CONF"; }
|
||||
|
||||
# copy the hook and watcher into place from the installed tree or this checkout
|
||||
install_hook() {
|
||||
local src=""
|
||||
for d in /usr/share/vfio-native "$SELF"; do
|
||||
[ -f "$d/scripts/libvirt-hook-cpuid-passthrough.sh" ] && src="$d"
|
||||
done
|
||||
[ -n "$src" ] || die "cannot find the hook source (install vfio-native, or run from a checkout)"
|
||||
install -Dm755 "$src/scripts/libvirt-hook-cpuid-passthrough.sh" "$HOOK"
|
||||
install -Dm755 "$src/scripts/cpuid-passthrough-watch" "$WATCH"
|
||||
}
|
||||
|
||||
case "${1:-}" in
|
||||
enable)
|
||||
need_root; [ -n "${2:-}" ] || die "usage: vm-native-cpuid enable <domain>"
|
||||
load; install_hook; ENABLED=yes
|
||||
case " $DOMAINS " in *" $2 "*) ;; *) DOMAINS="${DOMAINS:+$DOMAINS }$2" ;; esac
|
||||
save
|
||||
echo "$2 enabled. cpuid passthrough is now automatic for: $DOMAINS"
|
||||
echo "on start it forces N for the cold boot, flips Y once the guest is on the network, N again on stop."
|
||||
;;
|
||||
disable)
|
||||
need_root; [ -n "${2:-}" ] || die "usage: vm-native-cpuid disable <domain>"
|
||||
load; DOMAINS=$(printf ' %s ' "$DOMAINS" | sed "s/ $2 / /g" | xargs || true); save
|
||||
systemctl stop "vfio-cpuid-watch-$2.service" 2>/dev/null || true
|
||||
echo "$2 disabled. remaining: ${DOMAINS:-none}"
|
||||
;;
|
||||
status)
|
||||
load
|
||||
echo "config: $CONF"
|
||||
echo "enabled: $ENABLED"
|
||||
echo "domains: ${DOMAINS:-none}"
|
||||
echo "hook: $([ -f "$HOOK" ] && echo installed || echo 'not installed')"
|
||||
if [ -r "$PARAM/cpuid_passthrough" ]; then
|
||||
echo "module: cpuid_passthrough=$(cat "$PARAM/cpuid_passthrough") brand='$(cat "$PARAM/brand_string")'"
|
||||
else
|
||||
echo "module: patched kvm_amd not loaded"
|
||||
fi
|
||||
;;
|
||||
revert)
|
||||
need_root; load
|
||||
for d in $DOMAINS; do systemctl stop "vfio-cpuid-watch-$d.service" 2>/dev/null || true; done
|
||||
rm -f "$HOOK" "$WATCH" "$CONF" /run/vfio-native/cpuid-active
|
||||
[ -w "$PARAM/cpuid_passthrough" ] && echo N > "$PARAM/cpuid_passthrough"
|
||||
echo "reverted: hook, watcher and config removed; cpuid_passthrough=N."
|
||||
;;
|
||||
*)
|
||||
sed -n '2,13p' "$0" | sed 's/^#\( \|$\)//'
|
||||
;;
|
||||
esac
|
||||
@@ -57,6 +57,24 @@ group_members() {
|
||||
done
|
||||
}
|
||||
|
||||
# The functions to hand to the guest with GPU $1: every device in its IOMMU group
|
||||
# (mandatory for vfio), plus any sibling function of the same PCI device that is
|
||||
# itself a display or HDMI/DP audio controller. An APU parks its PSP and USB
|
||||
# controllers on the same PCI device in separate IOMMU groups - those are the
|
||||
# host's, so siblings are filtered by class and never pulled in blindly.
|
||||
passthrough_devs() {
|
||||
local d="$1" m cls
|
||||
{
|
||||
group_members "$d"
|
||||
for m in "/sys/bus/pci/devices/${d%.*}".*; do
|
||||
[ -e "$m" ] || continue
|
||||
m=$(basename "$m")
|
||||
cls=$(cat "/sys/bus/pci/devices/$m/class" 2>/dev/null)
|
||||
case "$cls" in 0x03*|0x0403*) echo "$m";; esac
|
||||
done
|
||||
} | sort -u
|
||||
}
|
||||
|
||||
driver_of() {
|
||||
local l
|
||||
l=$(readlink -f "/sys/bus/pci/devices/$1/driver" 2>/dev/null)
|
||||
@@ -79,7 +97,7 @@ drives_display() {
|
||||
|
||||
ids_of() { # vendor:device for vfio-pci binding
|
||||
local d
|
||||
for d in $(group_members "$1"); do
|
||||
for d in $(passthrough_devs "$1"); do
|
||||
local cls; cls=$(cat "/sys/bus/pci/devices/$d/class" 2>/dev/null)
|
||||
# only real functions of the card: skip bridges (class 0x0604xx)
|
||||
case "$cls" in 0x0604*) continue;; esac
|
||||
@@ -219,7 +237,7 @@ prepare)
|
||||
|
||||
# 1. stop whatever is holding the DRM device
|
||||
dm=$(systemctl list-units --type=service --state=running --no-legend 2>/dev/null |
|
||||
awk '{print $1}' | grep -xE '(gdm|sddm|lightdm|lxdm|greetd|display-manager)\.service' | head -1)
|
||||
awk '{print $1}' | grep -xE '((gdm|sddm|lightdm|lxdm|greetd|ly|emptty|lemurs)(@[a-z0-9-]+)?|display-manager)\.service' | head -1)
|
||||
if [ -n "$dm" ]; then
|
||||
echo "$dm" > "$STATE/dm"
|
||||
log "stopping $dm"
|
||||
@@ -303,7 +321,7 @@ apply() {
|
||||
"$v" -c qemu:///system dumpxml --inactive "$dom" > "$backup/$dom.gpu-current.xml"
|
||||
grep -q 'ua-vfionative-gpu' "$backup/$dom.gpu-current.xml" || cp "$backup/$dom.gpu-current.xml" "$backup/$dom.before-gpu.xml"
|
||||
local devs="" m
|
||||
for m in $( { group_members "$d"; ls -d "/sys/bus/pci/devices/${d%.*}".* | xargs -n1 basename; } | sort -u); do
|
||||
for m in $(passthrough_devs "$d"); do
|
||||
case "$(cat "/sys/bus/pci/devices/$m/class" 2>/dev/null)" in 0x0604*) continue;; esac
|
||||
IFS=':.' read -r dm bs sl fn <<< "$m"
|
||||
devs+=" <hostdev mode='subsystem' type='pci' managed='yes'>\n <source>\n"
|
||||
@@ -330,7 +348,7 @@ xml() {
|
||||
echo "Add this to the domain, inside <devices>. Every device in the GPU's"
|
||||
echo "IOMMU group has to go together:"
|
||||
echo
|
||||
for m in $(group_members "$d"); do
|
||||
for m in $(passthrough_devs "$d"); do
|
||||
local cls; cls=$(cat "/sys/bus/pci/devices/$m/class" 2>/dev/null)
|
||||
case "$cls" in 0x0604*) continue;; esac
|
||||
IFS=':. ' read -r dom bus slot fn <<< "$(echo "$m" | tr ':.' ' ')"
|
||||
|
||||
@@ -5,7 +5,8 @@
|
||||
#
|
||||
# On start it reads the domain's own <cputune> from the XML libvirt passes on
|
||||
# stdin, works out which host CPUs the guest occupies, and confines the systemd
|
||||
# slices to the rest. On stop it hands everything back.
|
||||
# slices to the rest. On stop it puts back what the slices had before, so a
|
||||
# partition someone else set survives a guest's lifetime.
|
||||
#
|
||||
# Measured on a 16 vCPU guest pinned to one CCD of a 7950X: stalls over 10 us in
|
||||
# a 10 second window fell from ~100 to ~52, and CPU throughput rose about 2%.
|
||||
@@ -19,12 +20,7 @@ OPERATION="$2"
|
||||
ALL=$(cat /sys/devices/system/cpu/present) # not nproc: that honours this hook's own affinity
|
||||
NCPU=$(( ${ALL##*-} + 1 ))
|
||||
SLICES="system.slice user.slice init.scope"
|
||||
|
||||
restore() {
|
||||
for s in $SLICES; do
|
||||
systemctl set-property --runtime -- "$s" AllowedCPUs="$ALL" 2>/dev/null
|
||||
done
|
||||
}
|
||||
STATE="/run/vfio-native/cpu-isolation/$DOMAIN" # each slice's AllowedCPUs before we confined it
|
||||
|
||||
case "$OPERATION" in
|
||||
prepare)
|
||||
@@ -62,6 +58,10 @@ PY
|
||||
)
|
||||
[ -n "$HOST" ] || exit 0 # nothing left for the host, or it failed
|
||||
|
||||
mkdir -p "${STATE%/*}"
|
||||
[ -f "$STATE" ] || for s in $SLICES; do
|
||||
printf '%s=%s\n' "$s" "$(systemctl show -p AllowedCPUs --value -- "$s")"
|
||||
done > "$STATE"
|
||||
for s in $SLICES; do
|
||||
systemctl set-property --runtime -- "$s" AllowedCPUs="$HOST" 2>/dev/null
|
||||
done
|
||||
@@ -69,8 +69,12 @@ PY
|
||||
;;
|
||||
|
||||
release|stopped)
|
||||
restore
|
||||
logger -t libvirt-cpu-isolation "$DOMAIN $OPERATION: host slices restored to $ALL"
|
||||
[ -f "$STATE" ] || exit 0 # this hook confined nothing for the domain
|
||||
while IFS='=' read -r s cpus; do
|
||||
systemctl set-property --runtime -- "$s" AllowedCPUs="$cpus" 2>/dev/null
|
||||
done < "$STATE"
|
||||
logger -t libvirt-cpu-isolation "$DOMAIN $OPERATION: host slices restored to $(tr '\n' ' ' < "$STATE")"
|
||||
rm -f "$STATE"
|
||||
;;
|
||||
*)
|
||||
# Deliberately does nothing. An earlier version called virsh here to check
|
||||
|
||||
Executable
+68
@@ -0,0 +1,68 @@
|
||||
#!/bin/bash
|
||||
# libvirt qemu hook: drive the kvm_amd cpuid_passthrough switch around a guest's life.
|
||||
#
|
||||
# The switch must be OFF while a guest cold-boots - raw CPUID changes shape mid
|
||||
# enumeration and Windows hangs - and ON once the guest is up, where it clears the
|
||||
# TIMER detection. This applies only to the domains you opt in with
|
||||
# `vm-native-cpuid enable <domain>`; it does nothing to any other guest.
|
||||
#
|
||||
# On start it sets the brand string from the guest's declared SKU and forces N,
|
||||
# then a detached watcher flips Y once the guest's network is up; on stop it sets N.
|
||||
# cpuid_passthrough and brand_string are one global kvm_amd parameter, so a single
|
||||
# hardened guest is served at a time; a second is logged and left alone.
|
||||
#
|
||||
# Exits 0 on every path. The hook never calls virsh (that deadlocks libvirtd); the
|
||||
# watcher that does is a transient systemd unit and runs after this returns.
|
||||
|
||||
CONF=/etc/vfio-native/cpuid-passthrough.conf
|
||||
ENABLED=yes
|
||||
DOMAINS=""
|
||||
[ -r "$CONF" ] && . "$CONF"
|
||||
[ "$ENABLED" = yes ] || exit 0
|
||||
|
||||
PARAM=/sys/module/kvm_amd/parameters
|
||||
[ -w "$PARAM/cpuid_passthrough" ] || exit 0 # stock module, or not an AMD host
|
||||
|
||||
DOMAIN=$1
|
||||
OPERATION=$2
|
||||
case " $DOMAINS " in *" $DOMAIN "*) ;; *) exit 0 ;; esac # not an opted-in domain
|
||||
|
||||
RUN=/run/vfio-native
|
||||
ACTIVE=$RUN/cpuid-active
|
||||
WATCH=/usr/lib/vfio-native/cpuid-passthrough-watch
|
||||
|
||||
case "$OPERATION" in
|
||||
prepare)
|
||||
# the guest's declared SKU, straight from the -cpu model-id in the XML on stdin
|
||||
BRAND=$(grep -oE "model-id=[^'\"]+" | head -1 | sed 's/^model-id=//')
|
||||
[ -n "$BRAND" ] || exit 0 # not a full-fidelity guest
|
||||
|
||||
mkdir -p "$RUN"
|
||||
held=$( [ -e "$ACTIVE" ] && cut -d: -f1 "$ACTIVE" )
|
||||
if [ -n "$held" ] && [ "$held" != "$DOMAIN" ]; then
|
||||
logger -t vfio-cpuid "cpuid passthrough held by $held; $DOMAIN left unhardened (one guest at a time)"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
printf '%s' "$BRAND" > "$PARAM/brand_string"
|
||||
echo N > "$PARAM/cpuid_passthrough" # safe for the cold boot
|
||||
printf '%s:%s' "$DOMAIN" "$BRAND" > "$ACTIVE"
|
||||
logger -t vfio-cpuid "$DOMAIN start: brand='$BRAND' cpuid_passthrough=N, watcher armed"
|
||||
|
||||
# transient systemd unit, not a bare background job: libvirt reaps anything left
|
||||
# in the hook's own process tree; systemd-run escapes it
|
||||
[ -x "$WATCH" ] && systemd-run --collect --quiet \
|
||||
--unit="vfio-cpuid-watch-${DOMAIN}" "$WATCH" "$DOMAIN" "$BRAND"
|
||||
;;
|
||||
|
||||
release|stopped)
|
||||
held=$( [ -e "$ACTIVE" ] && cut -d: -f1 "$ACTIVE" )
|
||||
[ "$held" = "$DOMAIN" ] || exit 0
|
||||
systemctl stop "vfio-cpuid-watch-${DOMAIN}.service" 2>/dev/null
|
||||
echo N > "$PARAM/cpuid_passthrough"
|
||||
rm -f "$ACTIVE"
|
||||
logger -t vfio-cpuid "$DOMAIN $OPERATION: cpuid_passthrough=N"
|
||||
;;
|
||||
esac
|
||||
|
||||
exit 0
|
||||
Executable
+15
@@ -0,0 +1,15 @@
|
||||
#!/bin/bash
|
||||
# libvirt qemu hook: turn GSO and GRO on for the guest's tap.
|
||||
#
|
||||
# QEMU leaves them off and sets the tap up after udev has run, so it has to
|
||||
# happen here. Paired with MTU 9000 they are the difference between 2.5 and
|
||||
# 14 Gbit/s into the guest; neither helps alone. Tap names come from the domain
|
||||
# XML on stdin, so the hook never calls virsh, which would deadlock libvirtd.
|
||||
|
||||
[ "$2" = started ] || exit 0
|
||||
|
||||
for tap in $(grep -oE "<target dev='(vnet|tap|macvtap)[^']*'" | sed "s/.*dev='//; s/'$//"); do
|
||||
ethtool -K "$tap" gso on gro on 2>/dev/null
|
||||
done
|
||||
|
||||
exit 0
|
||||
+192
-22
@@ -11,8 +11,14 @@
|
||||
# -s on | off Secure Boot with enrolled keys (default on)
|
||||
# -u none | auto | <vid:pid,...|0000:bb:dd.f,...> USB passthrough
|
||||
# -r randomize the hardware identity (serials, MAC, memory modules)
|
||||
# -S <dmi-blob> refit to a real machine's dumped SMBIOS (Method B)
|
||||
# -y no prompts
|
||||
#
|
||||
# To present as a specific machine instead of this host, fill the target keys in
|
||||
# the per-domain identity.env (SYS_PRODUCT, BIOS_*, CPU_*, ...): the tool then
|
||||
# authors SMBIOS type 0-4/17 as field args (Method A). -S <dmi-blob> uses a real
|
||||
# machine's dumped DMI table byte-for-byte instead (Method B). Either needs -p full.
|
||||
#
|
||||
# Takes a plain libvirt domain to the tuned, corrected state in one pass:
|
||||
# detects the host CPU layout and maps vCPUs onto real SMT pairs within one
|
||||
# cache domain, keeps the emulator off the vCPU cores, moves the disk to
|
||||
@@ -23,10 +29,10 @@ set -euo pipefail
|
||||
|
||||
# Run bare it is an interview: every choice is asked, with a default. Every flag
|
||||
# answers one question in advance, and -y takes every default.
|
||||
DOM=win11; PROFILE=""; CORES=""; RAMG=""; ASSUME=0; SECBOOT=""; USBSPEC=""; RANDOMIZE=""
|
||||
while getopts "d:p:c:m:s:u:ryh" o; do case $o in
|
||||
DOM=win11; PROFILE=""; CORES=""; RAMG=""; ASSUME=0; SECBOOT=""; USBSPEC=""; RANDOMIZE=""; DMIBLOB=""
|
||||
while getopts "d:p:c:m:s:u:S:ryh" o; do case $o in
|
||||
d) DOM=$OPTARG;; p) PROFILE=$OPTARG;; c) CORES=$OPTARG;;
|
||||
m) RAMG=$OPTARG;; s) SECBOOT=$OPTARG;; u) USBSPEC=$OPTARG;; r) RANDOMIZE=1;; y) ASSUME=1;;
|
||||
m) RAMG=$OPTARG;; s) SECBOOT=$OPTARG;; u) USBSPEC=$OPTARG;; S) DMIBLOB=$OPTARG;; r) RANDOMIZE=1;; y) ASSUME=1;;
|
||||
# print the header comment block, however long it grows
|
||||
h) sed -n '2,/^[^#]/p' "$0" | sed "\$d; s/^# \?//; s|@@SELFNAME@@|$(basename "$0")|g"; exit 0;;
|
||||
esac; done
|
||||
@@ -302,6 +308,24 @@ gen_identity() {
|
||||
echo "MEM_PART='${mem_part:-KF556C40BB-16}'"
|
||||
echo "MEM_SPEED='${mem_speed:-5600}'"
|
||||
echo "MEM_SERIAL='$(rnd 8 'A-F0-9')'"
|
||||
echo ""
|
||||
echo "# Target refit (Method A): uncomment and fill to present as a specific machine"
|
||||
echo "# instead of this host (SYS_UUID becomes the SMBIOS UUID). Needs -p full."
|
||||
echo "#SYS_MANUFACTURER=''; SYS_PRODUCT=''; SYS_VERSION=''; SYS_UUID=''; SYS_SKU=''; SYS_FAMILY=''"
|
||||
echo "#BIOS_VENDOR=''; BIOS_VERSION=''; BIOS_DATE=''; BIOS_RELEASE=''"
|
||||
echo "#BOARD_MANUFACTURER=''; BOARD_PRODUCT=''; BOARD_VERSION=''"
|
||||
echo "#CHASSIS_MANUFACTURER=''; CHASSIS_TYPE='10'"
|
||||
echo "#CPU_MANUFACTURER='Intel(R) Corporation'; CPU_VERSION=''; CPU_SOCKET=''; CPU_ID=''; CPU_MAX_SPEED=''; CPU_CURRENT_SPEED=''"
|
||||
echo "# CPUID identity override, for when the target CPU vendor differs from this host (model-id alone"
|
||||
echo "# leaves lscpu's vendor/family/model as the host's). Set these to the target's values, e.g. a"
|
||||
echo "# Raptor Lake-U Intel target on an AMD host: GenuineIntel, family 6, model 186, stepping 2. CPU_DISABLE_FLAGS"
|
||||
echo "# drops host-only flags that would contradict the vendor (on AMD: svm sse4a 3dnow 3dnowext"
|
||||
echo "# misalignsse cr8legacy osvw). Host-exclusive features of the real target (Intel vmx/hwp/..) cannot"
|
||||
echo "# be synthesised and stay absent."
|
||||
echo "#CPU_VENDOR_ID=''; CPU_FAMILY=''; CPU_MODEL=''; CPU_STEPPING=''; CPU_DISABLE_FLAGS=''"
|
||||
echo "#MEM_LOCATOR='DIMM 0'; NVME_MODEL=''"
|
||||
echo "# SECOND_MAC adds a present-but-dead NIC (isolated, link down, anti-spoofed) at that MAC:"
|
||||
echo "#SECOND_MAC=''"
|
||||
} > "$DATA/identity.env"
|
||||
}
|
||||
mkdir -p "$DATA"
|
||||
@@ -321,6 +345,32 @@ if [ "$RANDOMIZE" = 1 ] || [ ! -f "$DATA/identity.env" ]; then gen_identity; fi
|
||||
# shellcheck disable=SC1091
|
||||
. "$DATA/identity.env"
|
||||
|
||||
# Target identity: present as a specific machine instead of this host. Method A
|
||||
# (SYS_PRODUCT set in identity.env) authors SMBIOS type 0-4/17 as -smbios type=
|
||||
# field args; Method B (-S <dmi-blob>) splits a real DMI table into -smbios file=
|
||||
# blobs. Both ride on the patched QEMU's 10-smbios, so they need the full level.
|
||||
TARGET_MODE=""
|
||||
if [ -n "$DMIBLOB" ]; then TARGET_MODE=dump
|
||||
elif [ -n "${SYS_PRODUCT:-}" ]; then TARGET_MODE=fields; fi
|
||||
if [ -n "$TARGET_MODE" ]; then
|
||||
[ "$PROFILE" = full ] || { echo "target identity (-S or SYS_PRODUCT) needs -p full"; exit 1; }
|
||||
[ "$TARGET_MODE" != dump ] || [ -f "$DMIBLOB" ] || { echo "no such DMI blob: $DMIBLOB"; exit 1; }
|
||||
# a hand-written target identity.env may set only the keys it cares about
|
||||
for k in SYS_MANUFACTURER SYS_PRODUCT SYS_VERSION SYS_UUID SYS_SKU SYS_FAMILY SYS_SERIAL \
|
||||
BIOS_VENDOR BIOS_VERSION BIOS_DATE BIOS_RELEASE BOARD_MANUFACTURER BOARD_PRODUCT \
|
||||
BOARD_VERSION BOARD_SERIAL CHASSIS_MANUFACTURER CHASSIS_TYPE CHASSIS_SERIAL \
|
||||
CPU_MANUFACTURER CPU_VERSION CPU_SOCKET CPU_ID CPU_MAX_SPEED CPU_CURRENT_SPEED \
|
||||
CPU_VENDOR_ID CPU_FAMILY CPU_MODEL CPU_STEPPING CPU_DISABLE_FLAGS \
|
||||
MEM_MANUFACTURER MEM_PART MEM_SERIAL MEM_SPEED MEM_LOCATOR \
|
||||
NVME_SERIAL NVME_MODEL MAC SECOND_MAC; do
|
||||
declare -n _k="$k"; : "${_k:=}"
|
||||
done; unset -n _k
|
||||
: "${CHASSIS_TYPE:=10}"; : "${MEM_LOCATOR:=DIMM 0}"
|
||||
fi
|
||||
# the shared isolated network a SECOND_MAC dead NIC hangs off (created at apply time)
|
||||
VOID_NET=""
|
||||
[ -n "${SECOND_MAC:-}" ] && [ "$PROFILE" != tuned ] && VOID_NET=vfio-native-void
|
||||
|
||||
# --------------------------------------------------------- USB passthrough ---
|
||||
# Passing a whole controller is cleaner than passing devices one by one: no
|
||||
# emulated hub, no hotplug, nothing in the guest that says "redirected". It is
|
||||
@@ -504,9 +554,14 @@ echo " binary $EMULATOR"
|
||||
echo " memory ${RAMG} GiB"
|
||||
[ -n "$MODEL" ] && [ "$PROFILE" != tuned ] && echo " declares $MODEL"
|
||||
if [ "$PROFILE" != tuned ]; then
|
||||
[ "$CONVERT" = 1 ] && echo " disk emulated NVMe, serial $NVME_SERIAL; virtio devices replaced"
|
||||
if [ -n "$TARGET_MODE" ]; then
|
||||
echo " refit $([ "$TARGET_MODE" = dump ] && echo "dumped SMBIOS from $DMIBLOB (Method B)" || echo "$SYS_PRODUCT (Method A field args)")"
|
||||
[ -n "$CPU_VERSION" ] && echo " CPU $CPU_VERSION; ICH9 USB set; HPET off"
|
||||
fi
|
||||
[ "$CONVERT" = 1 ] && echo " disk emulated NVMe, serial $NVME_SERIAL${NVME_MODEL:+, model $NVME_MODEL}; virtio devices replaced"
|
||||
[ "$SECBOOT" = on ] && echo " firmware Secure Boot, keys enrolled from $NVRAM_TPL"
|
||||
[ "$RANDOMIZE" = 1 ] && echo " identity SMBIOS serials, MAC $MAC and memory module from $DATA/identity.env"
|
||||
[ -n "${SECOND_MAC:-}" ] && echo " 2nd nic dead NIC $SECOND_MAC on isolated $VOID_NET (link down, anti-spoofed)"
|
||||
[ "$PROFILE" = full ] && echo " display none - the guest console goes dark, use SSH or RDP"
|
||||
fi
|
||||
[ -n "$USB_HOSTDEVS" ] && echo " usb $(echo "$USB_HOSTDEVS" | grep -c '<hostdev') hostdev(s): $USBSPEC"
|
||||
@@ -536,6 +591,17 @@ for ((k=0;k<CORES;k++)); do
|
||||
[ "$SMT" = 2 ] && PINS+=" <vcpupin vcpu='$((k*SMT+1))' cpuset='$(sib "${GUEST[$k]}")'/>\n"
|
||||
done
|
||||
|
||||
# The dead NIC's isolated network: no uplink, no host IP, no DHCP, so it reaches
|
||||
# nothing; libvirt names the bridge. Created once, reused by every domain.
|
||||
if [ -n "$VOID_NET" ]; then
|
||||
if ! "${C[@]}" net-info "$VOID_NET" >/dev/null 2>&1; then
|
||||
echo "<network><name>$VOID_NET</name></network>" > "$BACKUP/$VOID_NET.xml"
|
||||
"${C[@]}" net-define "$BACKUP/$VOID_NET.xml" >/dev/null
|
||||
"${C[@]}" net-autostart "$VOID_NET" >/dev/null
|
||||
fi
|
||||
"${C[@]}" net-start "$VOID_NET" >/dev/null 2>&1 || true
|
||||
fi
|
||||
|
||||
PROFILE="$PROFILE" VENDOR="$VENDOR" VCPUS="$VCPUS" CORES="$CORES" SMT="$SMT" RAMG="$RAMG" \
|
||||
MODEL="$MODEL" EMULATOR="$EMULATOR" PINS="$PINS" \
|
||||
EMUSET="$(pair "$EMU")" IOCSET="$(pair "$IOC")" NUMANODE="$([ "$NNODES" -gt 1 ] && echo "$GNODE")" \
|
||||
@@ -547,6 +613,7 @@ DMI_SKU="$(dmi product_sku)" DMI_FAMILY="$(dmi product_family)" DMI_BOARD_VENDOR
|
||||
DMI_BOARD="$(dmi board_name)" DMI_BOARD_VERSION="$(dmi board_version)" DMI_CHASSIS_VENDOR="$(dmi chassis_vendor)" \
|
||||
DMI_CHASSIS_VERSION="$(dmi chassis_version)" DMI_BIOS_VENDOR="$(dmi bios_vendor)" DMI_BIOS_VERSION="$(dmi bios_version)" \
|
||||
DMI_BIOS_DATE="$(dmi bios_date)" \
|
||||
TARGET_MODE="$TARGET_MODE" SECOND_MAC="${SECOND_MAC:-}" VOID_NET="$VOID_NET" \
|
||||
python3 - "$BACKUP/$DOM.before-setup.xml" "$BACKUP/$DOM.setup.xml" <<'XMLGEN_END'
|
||||
import io, os, re, sys
|
||||
from xml.sax.saxutils import escape
|
||||
@@ -555,6 +622,7 @@ E = os.environ
|
||||
prof, vcpus, cores, smt = E["PROFILE"], int(E["VCPUS"]), int(E["CORES"]), int(E["SMT"])
|
||||
s = io.open(src, encoding="utf-8").read()
|
||||
conformant = prof != "tuned"
|
||||
target = E.get("TARGET_MODE") or ""
|
||||
|
||||
kib = int(E["RAMG"]) * 1024 * 1024
|
||||
s = re.sub(r"<memory unit='KiB'>\d+</memory>", "<memory unit='KiB'>%d</memory>" % kib, s, count=1)
|
||||
@@ -621,7 +689,7 @@ if "<pm>" not in s:
|
||||
s = s.replace(" <devices>", " <pm>\n <suspend-to-mem enabled='yes'/>\n"
|
||||
" <suspend-to-disk enabled='yes'/>\n </pm>\n <devices>", 1)
|
||||
|
||||
if conformant and E["MODEL"]:
|
||||
if conformant and not target and E["MODEL"]:
|
||||
s = re.sub(r"version=AMD Ryzen [^,]*,", "version=%s," % E["MODEL"], s)
|
||||
|
||||
if conformant and E["CONVERT"] == "1":
|
||||
@@ -637,8 +705,11 @@ if conformant and E["CONVERT"] == "1":
|
||||
if "device='disk'" not in d or ("bus='nvme'" in d and "<serial>" in d):
|
||||
return d
|
||||
d = re.sub(r"<target dev='([^']*)' bus='(virtio|sata|scsi)'/>", r"<target dev='\1' bus='nvme'/>", d)
|
||||
# cache='none' is O_DIRECT: on btrfs the guest can change a page while the
|
||||
# write is in flight, so the stored checksum never matches and later reads
|
||||
# fail with EIO. Buffered writes hand the filesystem a stable page.
|
||||
d = re.sub(r"<driver name='qemu' type='([^']*)'[^/]*/>",
|
||||
r"<driver name='qemu' type='\1' cache='none' io='native' discard='unmap'/>", d)
|
||||
r"<driver name='qemu' type='\1' cache='writeback' io='threads' discard='unmap'/>", d)
|
||||
d = re.sub(r"\s*<address type='(pci|drive)'[^/]*/>", "", d)
|
||||
if "<serial>" not in d:
|
||||
serial = E["NVME_SERIAL"] if n[0] == 0 else E["NVME_SERIAL"][:-1] + "0123456789ABCDEF"[n[0] % 16]
|
||||
@@ -658,11 +729,50 @@ if conformant and E["CONVERT"] == "1":
|
||||
s = re.sub(r"\s*<input type='[^']*' bus='virtio'/>", "", s)
|
||||
s = re.sub(r"<memballoon model='virtio'>.*?</memballoon>", "<memballoon model='none'/>", s, flags=re.S)
|
||||
s = re.sub(r"<memballoon model='virtio'/>", "<memballoon model='none'/>", s)
|
||||
s = re.sub(r"<model type='virtio'/>(\s*<driver [^/]*/>)?", "<model type='e1000e'/>", s)
|
||||
# jumbo frames are the single biggest win on the host<->guest link: the emulated
|
||||
# NIC is packet-rate bound, so 9000-byte frames cut the per-packet cost the guest
|
||||
# pays on receive. Measured 2922 -> 14232 Mbit/s inbound on igb, byte-exact clean.
|
||||
s = re.sub(r"<model type='virtio'/>(\s*<driver [^/]*/>)?",
|
||||
"<model type='igb'/>\n <mtu size='9000'/>", s)
|
||||
if prof == "full":
|
||||
s = re.sub(r"<video>.*?</video>", "<video>\n <model type='none'/>\n </video>", s, flags=re.S)
|
||||
|
||||
if conformant:
|
||||
if conformant and target:
|
||||
# the target's SMBIOS rides in as -smbios qemu args, not libvirt <sysinfo>
|
||||
if E.get("MAC"):
|
||||
s = re.sub(r"<mac address='[^']*'/>", "<mac address='%s'/>" % E["MAC"], s, count=1)
|
||||
s = re.sub(r"\s*<sysinfo type='smbios'>.*?</sysinfo>", "", s, flags=re.S)
|
||||
s = re.sub(r"\s*<smbios mode='[^']*'/>", "", s)
|
||||
# a real laptop has the ICH9 EHCI+UHCI set at 1d, not a bare qemu-xhci
|
||||
s = re.sub(r"\s*<controller type='usb'[^>]*>.*?</controller>", "", s, flags=re.S)
|
||||
s = re.sub(r"\s*<controller type='usb'[^>]*/>", "", s)
|
||||
ich9_usb = (
|
||||
" <controller type='usb' index='0' model='ich9-ehci1'>\n"
|
||||
" <address type='pci' domain='0x0000' bus='0x00' slot='0x1d' function='0x7'/>\n"
|
||||
" </controller>\n"
|
||||
" <controller type='usb' index='0' model='ich9-uhci1'>\n"
|
||||
" <master startport='0'/>\n"
|
||||
" <address type='pci' domain='0x0000' bus='0x00' slot='0x1d' function='0x0' multifunction='on'/>\n"
|
||||
" </controller>\n"
|
||||
" <controller type='usb' index='0' model='ich9-uhci2'>\n"
|
||||
" <master startport='2'/>\n"
|
||||
" <address type='pci' domain='0x0000' bus='0x00' slot='0x1d' function='0x1'/>\n"
|
||||
" </controller>\n"
|
||||
" <controller type='usb' index='0' model='ich9-uhci3'>\n"
|
||||
" <master startport='4'/>\n"
|
||||
" <address type='pci' domain='0x0000' bus='0x00' slot='0x1d' function='0x2'/>\n"
|
||||
" </controller>\n")
|
||||
s = s.replace(" </devices>", ich9_usb + " </devices>", 1)
|
||||
if "name='hpet'" in s:
|
||||
s = re.sub(r"<timer name='hpet'[^>]*/>", "<timer name='hpet' present='no'/>", s, count=1)
|
||||
elif re.search(r"<clock\b[^>]*/>", s):
|
||||
s = re.sub(r"(<clock\b[^>]*)/>",
|
||||
r"\1>\n <timer name='hpet' present='no'/>\n </clock>", s, count=1)
|
||||
else:
|
||||
s = re.sub(r"(<clock\b[^>]*>)",
|
||||
r"\1\n <timer name='hpet' present='no'/>", s, count=1)
|
||||
|
||||
if conformant and not target:
|
||||
if E["RANDOMIZE"] == "1":
|
||||
s = re.sub(r"<mac address='[^']*'/>", "<mac address='%s'/>" % E["MAC"], s, count=1)
|
||||
def ent(name, key):
|
||||
@@ -682,6 +792,7 @@ if conformant:
|
||||
if "<smbios mode=" not in s:
|
||||
s = s.replace(" </os>", " <smbios mode='sysinfo'/>\n </os>", 1)
|
||||
|
||||
if conformant:
|
||||
if E["SECBOOT"] == "on":
|
||||
s = re.sub(r"<os firmware='efi'>", "<os>", s, count=1)
|
||||
s = re.sub(r"\s*<firmware>.*?</firmware>", "", s, flags=re.S)
|
||||
@@ -695,6 +806,17 @@ if conformant:
|
||||
if "<smm state='on'/>" not in s:
|
||||
s = s.replace(" </features>", " <smm state='on'/>\n </features>", 1)
|
||||
|
||||
# SECOND_MAC: a present-but-dead NIC on the isolated void network - shows up in the
|
||||
# guest but reaches nothing, with its own clean-traffic anti-spoof. Replace a prior one.
|
||||
if conformant and E.get("SECOND_MAC") and E.get("VOID_NET"):
|
||||
s = re.sub(r"\s*<interface type='network'>(?:(?!</interface>).)*?<source network='%s'/>(?:(?!</interface>).)*</interface>" % re.escape(E["VOID_NET"]), "", s, flags=re.S)
|
||||
nic = (" <interface type='network'>\n"
|
||||
" <mac address='%s'/>\n" % E["SECOND_MAC"] +
|
||||
" <source network='%s'/>\n" % E["VOID_NET"] +
|
||||
" <model type='igb'/>\n <link state='down'/>\n"
|
||||
" <filterref filter='clean-traffic'/>\n </interface>\n")
|
||||
s = s.replace(" </devices>", nic + " </devices>", 1)
|
||||
|
||||
# USB passthrough: replace what this tool put in before, leave other hostdevs alone
|
||||
s = re.sub(r"\s*<hostdev mode='subsystem' type='(usb|pci)' managed='yes'>(?:(?!</hostdev>).)*<alias name='ua-vfionative-(usb|pci)-[^']*'/>(?:(?!</hostdev>).)*</hostdev>", "", s, flags=re.S)
|
||||
if E["USB_HOSTDEVS"]:
|
||||
@@ -719,22 +841,27 @@ fi
|
||||
# are replaced; anything else already in the block is left alone.
|
||||
QARGS=()
|
||||
if [ "$PROFILE" != tuned ]; then
|
||||
# A target refit builds its SMBIOS from field args (Method A) or a real dump
|
||||
# (Method B), so it skips the synthesised cache/probe blobs; presenting as this
|
||||
# host keeps them, sized from the declared vCPU cache.
|
||||
if [ -z "$TARGET_MODE" ]; then
|
||||
kb() { local s; s=$(cat "/sys/devices/system/cpu/cpu0/cache/index$1/size"); echo "${s%K}"; }
|
||||
SPAN=$(( (CORES + MAXC - 1) / MAXC ))
|
||||
python3 "$SELF/scripts/generate-tables.py" --output-dir "$DATA" \
|
||||
--cache-l1 $(( CORES * ($(kb 0) + $(kb 1)) )) --cache-l2 $(( CORES * $(kb 2) )) \
|
||||
--cache-l3 $(( SPAN * $(kb 3) )) >/dev/null
|
||||
for f in "$DATA"/type*.bin; do QARGS+=("-smbios" "file=$f"); done
|
||||
fi
|
||||
if [ -d "$SELF/acpi" ]; then
|
||||
cp "$SELF"/acpi/*.aml "$DATA"/
|
||||
for f in "$DATA"/*.aml; do QARGS+=("-acpitable" "file=$f"); done
|
||||
fi
|
||||
# A second -cpu REPLACES the first rather than merging, so it is built from
|
||||
# libvirt's own generated line and never hand-written. It is incompatible
|
||||
# with <hyperv>, which is why the tuned level skips it.
|
||||
# libvirt's own -cpu comes first; a previous run's override sits at the
|
||||
# end, quoted because the brand has spaces, so split like a shell would.
|
||||
if [ -n "$MODEL" ]; then
|
||||
# with <hyperv>, which is why the tuned level skips it. The brand is the
|
||||
# target's CPU for a refit, otherwise a matched host SKU.
|
||||
BRAND="$MODEL"; [ -n "$TARGET_MODE" ] && BRAND="$CPU_VERSION"
|
||||
if [ -n "$BRAND" ]; then
|
||||
python3 - "$BACKUP/$DOM.setup.xml" "$BACKUP/$DOM.nohostdev.xml" <<'STRIP_END'
|
||||
import io, re, sys
|
||||
s = io.open(sys.argv[1], encoding="utf-8").read()
|
||||
@@ -744,9 +871,32 @@ STRIP_END
|
||||
import shlex, sys
|
||||
a = shlex.split(sys.stdin.read())
|
||||
print(a[a.index("-cpu") + 1] if "-cpu" in a else "")')
|
||||
if [ -n "$GEN" ] && [[ "$GEN" != *hv-* ]]; then QARGS+=("-cpu" "$GEN,model-id=$MODEL"); fi
|
||||
# SMBIOS type 4 defaults to manufacturer "QEMU" and the machine name as
|
||||
# the version; a comma inside a QEMU option value is written twice.
|
||||
if [ -n "$GEN" ] && [[ "$GEN" != *hv-* ]]; then
|
||||
CPUARG="$GEN,model-id=$BRAND"
|
||||
[ -n "$CPU_VENDOR_ID" ] && CPUARG="$CPUARG,vendor=$CPU_VENDOR_ID"
|
||||
[ -n "$CPU_FAMILY" ] && CPUARG="$CPUARG,family=$CPU_FAMILY"
|
||||
[ -n "$CPU_MODEL" ] && CPUARG="$CPUARG,model=$CPU_MODEL"
|
||||
[ -n "$CPU_STEPPING" ] && CPUARG="$CPUARG,stepping=$CPU_STEPPING"
|
||||
for _df in $CPU_DISABLE_FLAGS; do CPUARG="$CPUARG,-$_df"; done
|
||||
QARGS+=("-cpu" "$CPUARG")
|
||||
fi
|
||||
fi
|
||||
# a comma inside a QEMU option value is written twice
|
||||
esc() { local v="${1//,/,,}"; printf '%s' "$v"; }
|
||||
if [ "$TARGET_MODE" = fields ]; then
|
||||
QARGS+=("-smbios" "type=0,vendor=$(esc "$BIOS_VENDOR"),version=$(esc "$BIOS_VERSION"),date=$(esc "$BIOS_DATE"),release=$(esc "$BIOS_RELEASE"),uefi=on")
|
||||
QARGS+=("-smbios" "type=1,manufacturer=$(esc "$SYS_MANUFACTURER"),product=$(esc "$SYS_PRODUCT"),version=$(esc "$SYS_VERSION"),serial=$(esc "$SYS_SERIAL"),sku=$(esc "$SYS_SKU"),family=$(esc "$SYS_FAMILY")${SYS_UUID:+,uuid=$SYS_UUID}")
|
||||
QARGS+=("-smbios" "type=2,manufacturer=$(esc "$BOARD_MANUFACTURER"),product=$(esc "$BOARD_PRODUCT"),version=$(esc "$BOARD_VERSION"),serial=$(esc "$BOARD_SERIAL")")
|
||||
QARGS+=("-smbios" "type=3,manufacturer=$(esc "$CHASSIS_MANUFACTURER"),serial=$(esc "$CHASSIS_SERIAL"),chassis-type=$CHASSIS_TYPE")
|
||||
QARGS+=("-smbios" "type=4,manufacturer=$(esc "$CPU_MANUFACTURER"),version=$(esc "$CPU_VERSION"),sock_pfx=$(esc "$CPU_SOCKET")${CPU_ID:+,processor-id=$CPU_ID}${CPU_MAX_SPEED:+,max-speed=$CPU_MAX_SPEED}${CPU_CURRENT_SPEED:+,current-speed=$CPU_CURRENT_SPEED}")
|
||||
QARGS+=("-smbios" "type=17,manufacturer=$(esc "$MEM_MANUFACTURER"),part=$(esc "$MEM_PART"),serial=$(esc "$MEM_SERIAL"),speed=$MEM_SPEED,loc_pfx=$(esc "$MEM_LOCATOR")")
|
||||
elif [ "$TARGET_MODE" = dump ]; then
|
||||
rm -rf "$DATA/smbios_real"
|
||||
while read -r f; do [ "${f#\#}" = "$f" ] && QARGS+=("-smbios" "file=$f"); done \
|
||||
< <(python3 "$SELF/scripts/split_smbios.py" "$DMIBLOB" "$DATA/smbios_real")
|
||||
else
|
||||
# present as this host: type 4 from the matched SKU, type 17 with -r
|
||||
if [ -n "$MODEL" ]; then
|
||||
case "$VENDOR:$FAMILY" in
|
||||
AuthenticAMD:23) SOCK=AM4; CPUVEND="Advanced Micro Devices,, Inc.";;
|
||||
AuthenticAMD:*) SOCK=AM5; CPUVEND="Advanced Micro Devices,, Inc.";;
|
||||
@@ -759,11 +909,14 @@ print(a[a.index("-cpu") + 1] if "-cpu" in a else "")')
|
||||
if [ "$RANDOMIZE" = 1 ]; then
|
||||
QARGS+=("-smbios" "type=17,loc_pfx=DIMM,bank=P0 CHANNEL A,manufacturer=$MEM_MANUFACTURER,part=$MEM_PART,serial=$MEM_SERIAL,speed=$MEM_SPEED")
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
QARGS_NL="$(printf '%s\n' "${QARGS[@]}")" python3 - "$BACKUP/$DOM.setup.xml" <<'QARGS_END'
|
||||
QARGS_NL="$(printf '%s\n' "${QARGS[@]}")" NVME_MODEL="${NVME_MODEL:-}" \
|
||||
python3 - "$BACKUP/$DOM.setup.xml" <<'QARGS_END'
|
||||
import io, os, re, sys
|
||||
p = sys.argv[1]
|
||||
args = [a for a in os.environ["QARGS_NL"].split("\n") if a]
|
||||
model = os.environ.get("NVME_MODEL", "")
|
||||
s = io.open(p, encoding="utf-8").read()
|
||||
if "xmlns:qemu=" not in s:
|
||||
s = s.replace("<domain type='kvm'>",
|
||||
@@ -789,6 +942,14 @@ if vals or envs:
|
||||
+ "".join(" %s\n" % e for e in envs)
|
||||
+ " </qemu:commandline>\n")
|
||||
s = s.replace("</domain>", block + "</domain>", 1)
|
||||
# libvirt's nvme controller has no model attribute, so the Identify Model Number
|
||||
# rides in as a qemu device override (the serial stays on the controller).
|
||||
if model:
|
||||
s = re.sub(r"\s*<qemu:override>.*?</qemu:override>", "", s, flags=re.S)
|
||||
ovr = (" <qemu:override>\n <qemu:device alias='nvme0'>\n <qemu:frontend>\n"
|
||||
" <qemu:property name='model' type='string' value='%s'/>\n" % model.replace("'", "'") +
|
||||
" </qemu:frontend>\n </qemu:device>\n </qemu:override>\n")
|
||||
s = s.replace("</domain>", ovr + "</domain>", 1)
|
||||
io.open(p, "w", encoding="utf-8").write(s)
|
||||
QARGS_END
|
||||
"${C[@]}" define "$BACKUP/$DOM.setup.xml" >/dev/null
|
||||
@@ -814,20 +975,29 @@ if [ "$PROFILE" = full ]; then
|
||||
[ "$ok" = 1 ] && echo "patched QEMU and KVM modules both in place."
|
||||
if [ -n "$MODEL" ] && [ -f /sys/module/kvm_amd/parameters/cpuid_passthrough ]; then
|
||||
echo
|
||||
echo "The TIMER check needs CPUID passthrough, which is off by default and must be"
|
||||
echo "switched on AFTER the guest has booted (it hangs a booting guest). Once the"
|
||||
echo "guest is up, on the host:"
|
||||
echo "The TIMER check needs CPUID passthrough, which must be off while the guest cold"
|
||||
echo "boots and on once it is up. Let the hook handle that around this guest:"
|
||||
echo " sudo vm-native-cpuid enable $DOM"
|
||||
echo "Or drive it by hand, after the guest has booted, on the host:"
|
||||
echo " echo '$MODEL' | sudo tee /sys/module/kvm_amd/parameters/brand_string"
|
||||
echo " echo Y | sudo tee /sys/module/kvm_amd/parameters/cpuid_passthrough"
|
||||
echo "Switch it off again (echo N) before rebooting the guest."
|
||||
echo " echo Y | sudo tee /sys/module/kvm_amd/parameters/cpuid_passthrough # N again before the next boot"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ ! -e /usr/lib/udev/rules.d/99-vfio-native-vnet-offload.rules ] && [ ! -e /etc/udev/rules.d/99-vfio-native-vnet-offload.rules ]; then
|
||||
echo "NOTE: the e1000e offload udev rule is not installed. SSH into the guest will fail with"
|
||||
echo "NOTE: the NIC offload udev rule is not installed. SSH into the guest will fail with"
|
||||
echo " 'Corrupted MAC on input' until it is:"
|
||||
echo " sudo install -Dm644 $SELF/scripts/99-vfio-native-vnet-offload.rules /etc/udev/rules.d/ && sudo udevadm control --reload-rules"
|
||||
fi
|
||||
|
||||
NET=$("${C[@]}" dumpxml "$DOM" 2>/dev/null | sed -n "s/.*<source network='\([^']*\)'.*/\1/p" | head -1)
|
||||
NETMTU=$([ -n "$NET" ] && "${C[@]}" net-dumpxml --inactive "$NET" 2>/dev/null | sed -n "s/.*<mtu size='\([0-9]*\)'.*/\1/p")
|
||||
if [ -n "$NET" ] && [ "${NETMTU:-1500}" -lt 9000 ]; then
|
||||
echo "NOTE: the guest interface asks for MTU 9000 but libvirt network '$NET' is at ${NETMTU:-1500}."
|
||||
echo " Jumbo needs both ends; inbound throughput is ~5x with it. Add <mtu size='9000'/> to"
|
||||
echo " the network and restart it: virsh net-edit $NET && virsh net-destroy $NET && virsh net-start $NET"
|
||||
echo " Then in the guest: set the NIC's Jumbo Packet to 9014 and the interface MTU to 9000."
|
||||
fi
|
||||
gov=$(cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor 2>/dev/null || echo unknown)
|
||||
[ "$gov" = performance ] || echo "host governor is '$gov' - run: sudo cpupower frequency-set -g performance"
|
||||
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Split a raw SMBIOS structure table into one file per structure for QEMU.
|
||||
|
||||
Reads a dumped DMI table (/sys/firmware/dmi/tables/DMI on the real machine),
|
||||
writes one `-smbios file=` blob per structure into OUTDIR, and prints the
|
||||
ordered list of paths. A complete set through the type-127 end marker is what
|
||||
lets QEMU build a well-formed entry point from a dump.
|
||||
|
||||
Each structure is a 4-byte header (type, length, handle:u16) + (length-4)
|
||||
formatted bytes + a string set terminated by a double NUL; a structure with no
|
||||
strings is still followed by 0x0000.
|
||||
"""
|
||||
import os
|
||||
import sys
|
||||
|
||||
|
||||
def split(data):
|
||||
out, i, n = [], 0, len(data)
|
||||
while i + 4 <= n:
|
||||
stype, slen = data[i], data[i + 1]
|
||||
if slen < 4:
|
||||
break
|
||||
j = i + slen
|
||||
while j + 1 < n and not (data[j] == 0 and data[j + 1] == 0):
|
||||
j += 1
|
||||
j += 2
|
||||
out.append((stype, data[i:j]))
|
||||
i = j
|
||||
if stype == 127:
|
||||
break
|
||||
return out
|
||||
|
||||
|
||||
def main():
|
||||
dmi, outdir = sys.argv[1], sys.argv[2]
|
||||
data = open(dmi, "rb").read()
|
||||
os.makedirs(outdir, exist_ok=True)
|
||||
structs = split(data)
|
||||
paths = []
|
||||
for idx, (stype, blob) in enumerate(structs):
|
||||
p = os.path.join(outdir, "sm_%02d_type%d.bin" % (idx, stype))
|
||||
open(p, "wb").write(blob)
|
||||
paths.append(p)
|
||||
print("# %d structures: %s" % (len(structs), ",".join(str(t) for t, _ in structs)))
|
||||
for p in paths:
|
||||
print(p)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user