Compare commits

...

No commits in common. "master" and "master-old" have entirely different histories.

24 changed files with 338 additions and 183 deletions

26
.editorconfig Normal file
View file

@ -0,0 +1,26 @@
root = true
[**]
end_of_line = lf
insert_final_newline = true
charset = utf-8
indent_style = space
indent_size = 4
trim_trailing_whitespace = true
max_line_length = 72
[{**.c}]
indent_size = 8
[{Makefile,makefile,**.mk,makefile**,Makefile**}]
indent_style = tab
indent_size = 2
[{**.py,**.rb,**.bash,**.sh,**.ksh}]
indent_size = 4
[{**.css}]
indent_size = 2
[{**.json,**.yaml,**.Dockerfile}]
indent_size = 2

8
.gitignore vendored Normal file
View file

@ -0,0 +1,8 @@
*.iso
*.rom
default.conf
base.conf
hardware.conf
win10.conf
*.qcow2
debian.conf

View file

@ -1,15 +0,0 @@
TARGET ?= /usr/local
ARCH != uname -m
SRCS := qemu-vm qemu-vm-*
install: check_deps $(SRCS)
install -Dm 0755 --owner=root --group=root -t $(TARGET)/bin $(SRCS)
check_deps:
@command -v qemu-system-$(ARCH)
@command -v qemu-affinity
@command -v swtpm
@command -v dnsmasq
.PHONY: install check_deps

12
base.conf.tmpl Normal file
View file

@ -0,0 +1,12 @@
-boot order=dc
-machine type=q35,accel=kvm,kernel_irqchip=on
-smp $NUM_THREADS,sockets=1,cores=$NUM_PROCESSORS,threads=$NUM_THREADS_PER_CORE
-enable-kvm
-cpu host,kvm=on,topoext,tsc_deadline,tsc_adjust,hv_relaxed,hv_vapic,hv_spinlocks=0x1fff,hv_vpindex,hv_runtime,hv_crash,hv_time,hv_synic,hv_stimer,hv_tlbflush,hv_ipi,hv_vendor_id=null,hv_reset,hv_frequencies,hv_reenlightenment,hv_evmcs,hv_stimer_direct
-m 12G
-mem-prealloc -mem-path /dev/hugepages
-parallel none
-serial none
-rtc clock=host,base=localtime,driftfix=none
-usb
-vga none -nographic

5
base.sh Executable file
View file

@ -0,0 +1,5 @@
#!/usr/bin/env bash
export NUM_THREADS_PER_CORE=$(./cpus processors_per_core)
export NUM_THREADS=$(./cpus decompress_seq "$(./cpus compute_vm $NUM_PROCESSORS)" | wc -w)

5
config.conf Executable file
View file

@ -0,0 +1,5 @@
#!/usr/bin/env bash
export PIDFILE=/run/qemu_ex.pid
export MONITOR=/tmp/qemu_ex.sock
export NUM_PROCESSORS=4

View file

@ -1,4 +1,4 @@
#!/bin/sh
#!/usr/bin/env sh
all_processors() {
grep -E '(processor|core id)' /proc/cpuinfo | while : ; do
@ -114,4 +114,8 @@ processors_per_core() {
echo $((processor_count/core_count))
}
"$@"

2
debian.conf.tmpl Normal file
View file

@ -0,0 +1,2 @@
-drive file=debian.qcow2,if=virtio,cache=off

5
debian.desktop Normal file
View file

@ -0,0 +1,5 @@
[Desktop Entry]
Name=Debian VM
Exec=/opt/vm/start debian
Type=Application
Categories=Utility;

4
default.conf.tmpl Normal file
View file

@ -0,0 +1,4 @@
-netdev tap,id=net0,br=$BR_NAME,ifname=$TAP_NAME,script=no,downscript=no
-device e1000,netdev=net0
-audiodev pa,id=snd0,server=unix:/run/user/$MYUID/pulse/native
-device intel-hda -device hda-duplex,audiodev=snd0

6
default.sh Executable file
View file

@ -0,0 +1,6 @@
#!/usr/bin/env bash
source <(sudo cat "$NET_CONF_FILE")
export BR_NAME="$bridge_name"
export TAP_NAME="$tap_name"
export MYUID="$(id -u)"

15
hardware.conf.tmpl Normal file
View file

@ -0,0 +1,15 @@
-device usb-host,hostbus=1,hostport=12.1
-device usb-host,hostbus=1,hostport=12.2
-device usb-host,hostbus=1,hostport=12.3
-device usb-host,hostbus=1,hostport=12.4
-device usb-host,hostbus=1,hostport=11.4
-device vfio-pci,host=$GPU_ID,multifunction=on,id=gpu,romfile=$GPU_ROM
-device vfio-pci,host=$AUDIO_ID,id=audio
-chardev socket,id=chrtpm,path=$TPM_PATH/swtpm-sock
-tpmdev emulator,id=tpm0,chardev=chrtpm
-device tpm-tis,tpmdev=tpm0
-object input-linux,id=kbd11,evdev=/dev/input/by-id/usb-Wooting_WootingTwo_WOOT_001_A01B1852W021H00067-event-if04
-object input-linux,id=kbd12,evdev=/dev/input/by-id/usb-Wooting_WootingTwo_WOOT_001_A01B1852W021H00067-if03-event-kbd,grab_all=on,repeat=on
-object input-linux,id=mouse11,evdev=/dev/input/by-id/usb-Mad_Catz_Global_MADCATZ_R.A.T._8+_gaming_mouse-event-if01
-object input-linux,id=mouse12,evdev=/dev/input/by-id/usb-Mad_Catz_Global_MADCATZ_R.A.T._8+_gaming_mouse-event-mouse
-object input-linux,id=mouse13,evdev=/dev/input/by-id/usb-Mad_Catz_Global_MADCATZ_R.A.T._8+_gaming_mouse-if01-event-kbd,grab_all=on,repeat=on

10
hardware.sh Executable file
View file

@ -0,0 +1,10 @@
#!/usr/bin/env bash
export GPU_ROM=/opt/vm/1080ti_asus.rom
export GPU_ID='0000:65:00.0'
export AUDIO_ID='0000:65:00.1'
export TPM_PATH="/opt/vm/tpm/"
mkdir -p "$TPM_PATH$(mktemp --directory)"
swtpm socket --tpm2 --tpmstate dir="$TPM_PATH" --ctrl type=unixio,path="$TPM_PATH/swtpm-sock" &
disown -h "$!"

View file

@ -1,13 +1,18 @@
#!/bin/bash
#!/usr/bin/env bash
set -euo pipefail
BASE_BRIDGE_NAME=br-q
BASE_TAP_NAME=tap-q
randstr() {
dd if=/dev/urandom count=1 bs=3 2>/dev/null | xxd -p -g 0
}
default_route() {
ip route | awk '/^default/ && (NR==1) {print $5}'
}
find_next_subnet() {
@ -89,7 +94,7 @@ nft_rev() {
nft delete rule $table $chain handle \$handle
"
else
echo "Warning: Don't know how to reverse 'nft $*'" 1>&2
echo "Warning: Don't know how to reverse 'nft $@'" 1>&2
nft "$@"
return 0
fi
@ -168,11 +173,9 @@ create() {
ip addr
dnsmasq -d --interface="$bridge_name" --bind-interface --dhcp-range="$dhcp_range" &
dnsmasq_pid="$!"
{
echo "bridge_name='$bridge_name'"
echo "tap_name='$tap_name'"
echo "dnsmasq_pid='$dnsmasq_pid'"
} >> "$NET_CONF_FILE"
echo "bridge_name='$bridge_name'" >> "$NET_CONF_FILE"
echo "tap_name='$tap_name'" >> "$NET_CONF_FILE"
echo "dnsmasq_pid='$dnsmasq_pid'" >> "$NET_CONF_FILE"
disown -h "$dnsmasq_pid"
echo "nft_ruleset='$(nft -s list ruleset)'" >> "$NET_CONF_FILE"
@ -215,7 +218,7 @@ if [[ -z "$NET_CONF_FILE" ]]; then
exit 1
fi
if [[ "$EUID" -ne 0 ]]; then
if [[ "EUID" -ne 0 ]]; then
echo "Please run as root" >&2
exit 2
fi

View file

@ -1,4 +1,4 @@
#!/bin/bash
#!/usr/bin/env bash
set -euo pipefail
@ -9,24 +9,23 @@ if [[ "${BASE_PATH: -1}" = '/' ]]; then
BASE_PATH="${BASE_PATH::${#BASE_PATH}-1}"
fi
_="${SUDO:=sudo}"
vfio_override_device() {
local pci_id
pci_id="$1"
if [[ -e "$BASE_PATH/devices/$pci_id" ]]; then
echo "vfio-pci" | $SUDO tee \
"$BASE_PATH/devices/$pci_id/driver_override" > /dev/null
fi
}
vfio_rebind_device() {
local pci_id
local override
pci_id="$1"
override="$2"
if [[ "$override" != 'true' ]]; then
override=false
fi
if [[ -e "$BASE_PATH/devices/$pci_id" ]]; then
if "$override"; then
echo "vfio-pci" | sudo tee \
"$BASE_PATH/devices/$pci_id/driver_override" > /dev/null
fi
[[ -e "$BASE_PATH/devices/$pci_id/driver/unbind" ]] \
&& echo "$pci_id" | $SUDO tee \
&& echo "$pci_id" | sudo tee \
"$BASE_PATH/devices/$pci_id/driver/unbind" >/dev/null
echo "$pci_id" | $SUDO tee \
echo "$pci_id" | sudo tee \
"$BASE_PATH/drivers/vfio-pci/bind" > /dev/null
fi
}

8
pre-start.sh Executable file
View file

@ -0,0 +1,8 @@
#!/usr/bin/env bash
if [[ -z "$DEBUG" ]] \
&& [[ -e /sys/bus/platform/devices/efi-framebuffer.0/driver ]]; then
echo efi-framebuffer.0 | sudo tee \
'/sys/bus/platform/devices/efi-framebuffer.0/driver/unbind'
fi

123
qemu-vm
View file

@ -1,123 +0,0 @@
#!/bin/bash
set -eo pipefail
SUDO=sudo
$SUDO true
if [[ -z "$VMNAME" ]]; then
echo "VMNAME not specified, aborting" >&2
exit 1
fi
set -u
# create config directory
_="${XDG_CONFIG_HOME:=$HOME/.config}"
CONFIG_PATH="$XDG_CONFIG_HOME/qemu-vm"
CONFIG_PATH_ARGUMENTS="$CONFIG_PATH/arguments.d"
CONFIG_PATH_ARGUMENTS_SPECIFIC="$CONFIG_PATH/$VMNAME/arguments.d"
# load config
[[ -e "$CONFIG_PATH/config.conf" ]] && source "$CONFIG_PATH/config.conf"
[[ -e "$CONFIG_PATH/config-$VMNAME.conf" ]] && source "$CONFIG_PATH/config-$VMNAME.conf"
[[ -e "$CONFIG_PATH/pre-config" ]] && SUDO="$SUDO" "$CONFIG_PATH/pre-config"
[[ -e "$CONFIG_PATH/pre-config-$VMNAME" ]] && SUDO="$SUDO" "$CONFIG_PATH/pre-config-$VMNAME"
# efi variables
EFI_FIRMWARE=/usr/share/ovmf/x64/OVMF_CODE.secboot.4m.fd
EFI_VARS="$($SUDO mktemp)"
$SUDO cp /usr/share/ovmf/x64/OVMF_VARS.4m.fd "$EFI_VARS"
# setup network
NET_CONF_FILE="$($SUDO mktemp)"
NET_CONF_FILE="$NET_CONF_FILE" "$SUDO" --preserve-env=PATH,NET_CONF_FILE qemu-vm-net create || true
# rebind devices
old_IFS="$IFS"
IFS=$'\n'
for device_override in $(cat "$CONFIG_PATH/vfio-devices.txt" || true) $(sh "$CONFIG_PATH/vfio-devices.sh") $(cat "$CONFIG_PATH/vfio-devices-$VMNAME.txt" || true) $(sh "$CONFIG_PATH/vfio-devices-$VMNAME.sh"); do
device_override="${device_override%%#*}"
device="$(awk '{print $1}' <<< "$device_override")"
override="$(awk '{print $2}' <<< "$device_override")"
if [[ "$device" = '' ]] || [[ "$override" = '' ]]; then
continue
fi
[[ "$override" != 'true' ]] && continue
$SUDO --preserve-env=SUDO,PATH qemu-vm-pci vfio_override_device "$device"
done
$SUDO modprobe vfio-pci
for device_override in $(cat "$CONFIG_PATH/vfio-devices.txt" || true) $(sh "$CONFIG_PATH/vfio-devices.sh") $(cat "$CONFIG_PATH/vfio-devices-$VMNAME.txt" || true) $(sh "$CONFIG_PATH/vfio-devices-$VMNAME.sh"); do
device_override="${device_override%%#*}"
device="$(awk '{print $1}' <<< "$device_override")"
override="$(awk '{print $2}' <<< "$device_override")"
if [[ "$device" = '' ]] || [[ "$override" = '' ]]; then
continue
fi
$SUDO --preserve-env=SUDO,PATH qemu-vm-pci vfio_rebind_device "$device" "$override"
done
IFS="$old_IFS"
# memory backend
MEM_PATH="$(SUDO="$SUDO" qemu-vm-mem init "$NUM_MEM")"
# generate arguments
t="$(mktemp -d)"
for f in "$CONFIG_PATH_ARGUMENTS/"*.sh "$CONFIG_PATH_ARGUMENTS_SPECIFIC/"*.sh; do
(
export SUDO
export MEM_PATH
source "$f"
newfile="$(basename "$f" .sh)"
envsubst < "${f%.sh}" > "$t/$newfile"
)
done
for f in "$CONFIG_PATH_ARGUMENTS/"*.conf "$CONFIG_PATH_ARGUMENTS_SPECIFIC/"*.conf; do
[[ -e "$f.sh" ]] && continue
cp "$f" "$t/"
done
qemu_arguments=()
for f in "$t/"*.conf; do
read -ra _qemu_arguments -d '' < "$f" || true
qemu_arguments+=("${_qemu_arguments[@]}")
done
unset _qemu_arguments
# pre-run callbacks
[[ -e "$CONFIG_PATH/pre-run" ]] && SUDO="$SUDO" "$CONFIG_PATH/pre-run"
[[ -e "$CONFIG_PATH/pre-run-$VMNAME" ]] && SUDO="$SUDO" "$CONFIG_PATH/pre-run-$VMNAME"
# run and set affinity
set -x
$SUDO qemu-system-$(uname -m) \
-name "$VMNAME,process=$VMNAME,debug-threads=on" \
-monitor unix:"$MONITOR",server,nowait \
-drive if=pflash,format=raw,readonly=on,file="$EFI_FIRMWARE" \
-drive if=pflash,format=raw,file="$EFI_VARS" \
"${qemu_arguments[@]}" &
set +x
qemu_pid=
while [[ "$qemu_pid" = '' ]]; do
qemu_pid="$(pidof qemu-system-$(uname -m) || true)"
sleep .1
done
while :; do
sleep .1
$SUDO qemu-affinity \
-k $(qemu-vm-cpus decompress_seq "$(qemu-vm-cpus compute_vm $NUM_PROCESSORS)") \
-i *:$(qemu-vm-cpus compute_vm $NUM_PROCESSORS) \
-- $qemu_pid && break
done
echo 'Startup complete'
tail --pid="$qemu_pid" -f /dev/null
echo 'Stop stopped'
SUDO="$SUDO" qemu-vm-mem restore "$MEM_PATH"
NET_CONF_FILE="$NET_CONF_FILE" $SUDO --preserve-env=PATH,NET_CONF_FILE qemu-vm-net delete
[[ -e "$CONFIG_PATH/post-run" ]] && SUDO="$SUDO" "$CONFIG_PATH/post-run"
[[ -e "$CONFIG_PATH/post-run-$VMNAME" ]] && SUDO="$SUDO" "$CONFIG_PATH/post-run-$VMNAME"

View file

@ -1,19 +0,0 @@
#!/bin/bash
set -euo pipefail
HUGEPAGES_PATH=/sys/kernel/mm/hugepages/hugepages-1048576kB/nr_hugepages
init() {
echo "$1" | $SUDO tee "$HUGEPAGES_PATH" >/dev/null
d="$($SUDO mktemp -d)"
$SUDO mount -t hugetlbfs -o pagesize=1024M hugetlbfs "$d"
echo "$d"
}
restore() {
$SUDO umount "$1"
echo 0 | $SUDO tee "$HUGEPAGES_PATH" >/dev/null
}
"$@"

View file

@ -1,4 +0,0 @@
qemu-full
qemu-affinity
swtpm
dnsmasq

181
start Executable file
View file

@ -0,0 +1,181 @@
#!/usr/bin/env bash
set -eo pipefail
if [[ ! -t 1 ]]; then
if [[ -z "$TERMINAL" ]]; then
TERMINAL=i3-sensible-terminal
fi
"$TERMINAL" -e "$0" "$@"
exit $?
fi
if [[ -n "$1" ]] && [[ -z "$VMNAME" ]]; then
VMNAME="$1"
fi
if [[ -z "$VMNAME" ]]; then
echo "VMNAME not specified, aborting" >&2
exit 1
fi
if [[ -z "$DEBUG" ]]; then
DEBUG=
fi
# Sudo prompt
if [[ -n "$SUDO" ]]; then
true
elif [[ -t 1 ]]; then
SUDO=sudo
else
export SUDO_ASKPASS=/usr/lib/ssh/ssh-askpass
SUDO='sudo --askpass'
fi
if ! which "$SUDO" >/dev/null 2>&1; then
SUDO=sudo
fi
set -u
cd "$(dirname "$0")"
$SUDO true
NET_CONF_FILE="$($SUDO mktemp)"
# create bridge
NET_CONF_FILE="$NET_CONF_FILE" $SUDO --preserve-env=NET_CONF_FILE \
./net create
# rebind devices
old_IFS="$IFS"
IFS=$'\n'
for device_override in $(< vfio_devices.txt); do
device="$(echo "$device_override" | awk '{print $1}')"
override="$(echo "$device_override" | awk '{print $2}')"
$SUDO ./pci vfio_rebind_device "$device" "$override"
done
IFS="$old_IFS"
# efi variables
EFI_VARS="$(mktemp)"
cp /usr/share/ovmf/x64/OVMF_VARS.fd "$EFI_VARS"
EFI_FIRMWARE=/usr/share/ovmf/x64/OVMF_CODE.secboot.fd
source config.conf
base_path=base
default_path=default
hardware_path=hardware
specific_path="$VMNAME"
o_base_path="$(mktemp)"
o_default_path="$(mktemp)"
o_hardware_path="$(mktemp)"
o_specific_path="$(mktemp)"
if [[ -e "$base_path.conf.tmpl" ]]; then
(
# shellcheck disable=SC1090
[[ -e "$base_path.sh" ]] && source "$base_path.sh"
envsubst < "$base_path.conf.tmpl" > "$o_base_path"
)
else
cp "$base_path.conf" "$o_base_path"
fi
if [[ -e "$default_path.conf.tmpl" ]]; then
(
# shellcheck disable=SC1090
[[ -e "$default_path.sh" ]] && source "$default_path.sh"
envsubst < "$default_path.conf.tmpl" > "$o_default_path"
)
else
cp "$default_path.conf" "$o_default_path"
fi
if [[ -e "$hardware_path.conf.tmpl" ]]; then
(
# shellcheck disable=SC1090
[[ -e "$hardware_path.sh" ]] && source "$hardware_path.sh"
envsubst < "$hardware_path.conf.tmpl" > "$o_hardware_path"
)
else
cp "$hardware_path.conf" "$o_hardware_path"
fi
if [[ -e "$specific_path.conf.tmpl" ]]; then
(
# shellcheck disable=SC1090
[[ -e "$specific_path.sh" ]] && source "$specific_path.sh"
envsubst < "$specific_path.conf.tmpl" > "$o_specific_path"
)
else
cp "$specific_path.conf" "$o_specific_path"
fi
if [[ -e "pre-start.sh" ]]; then
./pre-start.sh
fi
if [[ -e "pre-start-$VMNAME.sh" ]]; then
./"pre-start-$VMNAME.sh"
fi
# run qemu
base_arguments=()
default_arguments=()
hardware_arguments=()
specific_arguments=()
# mask EOF
read -ra base_arguments -d '' < "$o_base_path" || true
read -ra default_arguments -d '' < "$o_default_path" || true
read -ra hardware_arguments -d '' < "$o_hardware_path" || true
read -ra specific_arguments -d '' < "$o_specific_path" || true
# preven host from using vm cpus
set -x
$SUDO qemu-system-x86_64 \
-name "$VMNAME,process=VMNAME,debug-threads=on" \
-daemonize -pidfile "$PIDFILE" \
-monitor unix:"$MONITOR",server,nowait \
-drive if=pflash,format=raw,readonly=on,file="$EFI_FIRMWARE" \
-drive if=pflash,format=raw,file="$EFI_VARS" \
"${base_arguments[@]}" \
"${default_arguments[@]}" \
"${hardware_arguments[@]}" \
"${specific_arguments[@]}" || true
set +x
$SUDO cat "$PIDFILE"
$SUDO qemu-affinity \
-k $(./cpus decompress_seq "$(./cpus compute_vm $NUM_PROCESSORS)") \
-i *:$(./cpus compute_vm $NUM_PROCESSORS) \
-- $($SUDO cat "$PIDFILE")
host_cpus="$(./cpus compute_host $NUM_PROCESSORS)"
# $SUDO systemctl set-property --runtime -- user.slice AllowedCPUs="$host_cpus"
# $SUDO systemctl set-property --runtime -- system.slice AllowedCPUs="$host_cpus"
# $SUDO systemctl set-property --runtime -- init.slice AllowedCPUs="$host_cpus"
# undo_file=/tmp/vfio-isolate.undo.bin
# vfio-isolate \
# -u "$undo_file" \
# drop-caches \
# cpuset-modify --cpus C$host_cpus /system.slice \
# cpuset-modify --cpus C$host_cpus /user.slice
while [ -f "$PIDFILE" ]; do
sleep 1
done
host_cpus="$(./cpus compute_all)"
# vfio-isolate \
# restore "$undo_file"
# $SUDO systemctl set-property --runtime -- user.slice AllowedCPUs="$host_cpus"
# $SUDO systemctl set-property --runtime -- system.slice AllowedCPUs="$host_cpus"
# $SUDO systemctl set-property --runtime -- init.slice AllowedCPUs="$host_cpus"
# to power down the machine
# echo system_powerdown | socat - UNIX-CONNECT:"$MONITOR"
NET_CONF_FILE="$NET_CONF_FILE" $SUDO --preserve-env=NET_CONF_FILE \
./net delete

4
vfio_devices.txt Normal file
View file

@ -0,0 +1,4 @@
0000:02:00.0 false
0000:07:00.0 false
0000:65:00.0 false
0000:65:00.1 false

10
win10.conf.tmpl Normal file
View file

@ -0,0 +1,10 @@
-drive file=virtio-win-0.1.185.iso,media=cdrom
-drive file=Win10_21H1_EnglishInternational_x64.iso,media=cdrom
-device vfio-pci,host=$SSD_ID,id=sdd
-device vfio-pci,host=$SATA_ID,id=sata
-device ivshmem-plain,memdev=ivshmem,bus=pcie.0
-object memory-backend-file,id=ivshmem,share=on,mem-path=/dev/shm/looking-glass,size=64M
-device virtio-serial-pci
-chardev spicevmc,id=vdagent,name=vdagent
-device virtserialport,chardev=vdagent,name=com.redhat.spice.0

4
win10.sh Executable file
View file

@ -0,0 +1,4 @@
#!/usr/bin/env bash
export SSD_ID='0000:02:00.0'
export SATA_ID='0000:07:00.0'

5
win10vm.desktop Normal file
View file

@ -0,0 +1,5 @@
[Desktop Entry]
Name=Windows 10 VM
Exec=/opt/vm/start win10
Type=Application
Categories=Game;Utility;