feat(cpuid): automate the passthrough toggle around enabled guests
This commit is contained in:
68
scripts/libvirt-hook-cpuid-passthrough.sh
Executable file
68
scripts/libvirt-hook-cpuid-passthrough.sh
Executable file
@@ -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
|
||||
Reference in New Issue
Block a user