diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..ab88bfa --- /dev/null +++ b/.editorconfig @@ -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 diff --git a/.gitignore b/.gitignore index e170ac1..41f481e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,6 @@ *.iso *.rom +default.conf +base.conf +hardware.conf +win10.conf diff --git a/base.conf.tmpl b/base.conf.tmpl new file mode 100644 index 0000000..b661521 --- /dev/null +++ b/base.conf.tmpl @@ -0,0 +1,12 @@ +-boot order=dc +-machine type=q35,accel=kvm,kernel_irqchip=on +-smp 10,sockets=1,cores=5,threads=2 +-enable-kvm +-cpu host,kvm=on,topoext,tsc_deadline,tsc_adjust,l3-cache,hv_vendor_id=null,hv_vpindex,hv_runtime,hv_synic,hv_stimer,hv_reset,hv_frequencies,hv_tlbflush,hv_reenlightenment,hv_ipi,hv_time,hv_relaxed,hv_vapic,hv_spinlocks=0x1fff +-m 12G +-mem-prealloc -mem-path /dev/hugepages +-vga none -nographic +-parallel none +-serial none +-rtc clock=host,base=localtime,driftfix=none +-usb diff --git a/default.conf.tmpl b/default.conf.tmpl new file mode 100644 index 0000000..cb92a99 --- /dev/null +++ b/default.conf.tmpl @@ -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 diff --git a/default.sh b/default.sh new file mode 100755 index 0000000..53584c7 --- /dev/null +++ b/default.sh @@ -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)" diff --git a/hardware.conf.tmpl b/hardware.conf.tmpl new file mode 100644 index 0000000..1ad559c --- /dev/null +++ b/hardware.conf.tmpl @@ -0,0 +1,6 @@ +-device usb-host,hostbus=1,hostport=6.1 +-device usb-host,hostbus=1,hostport=6.2 +-device usb-host,hostbus=1,hostport=6.3 +-device usb-host,hostbus=1,hostport=6.4 +-device vfio-pci,host=$GPU_ID,multifunction=on,id=gpu,romfile=$GPU_ROM +-device vfio-pci,host=$AUDIO_ID,id=audio diff --git a/hardware.sh b/hardware.sh new file mode 100755 index 0000000..73ef72f --- /dev/null +++ b/hardware.sh @@ -0,0 +1,6 @@ +#!/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' + diff --git a/net b/net new file mode 100755 index 0000000..2ec058c --- /dev/null +++ b/net @@ -0,0 +1,101 @@ +#!/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=4 | xxd -p -g 0 +} + +default_route() { + ip route | grep '^default' | sed -n 's/.*dev \([^ ]*\).*/\1/p' +} + +find_next_subnet() { + local i + for i in {20..254}; do + if ip route | grep -q "^172\.$i\."; then + true + else + break + fi + done + echo "172.$i" +} + +create() { + local next_subnet + + local bridge_name + local dhcp_subnet + local dhcp_range + local tap_name + local dnsmasq_pid + + next_subnet="$(find_next_subnet)" + dhcp_subnet="$next_subnet.0.1/16" + dhcp_range="$next_subnet.0.2,$next_subnet.255.254" + + bridge_name="$BASE_BRIDGE_NAME-$(randstr)" + tap_name="$BASE_TAP_NAME-$(randstr)" + + echo > "$NET_CONF_FILE" + + ip link add name "$bridge_name" type bridge + echo "bridge_name='$bridge_name'" >> "$NET_CONF_FILE" + ip addr add "$dhcp_subnet" dev "$bridge_name" + ip link set dev "$bridge_name" up + ip tuntap add "$tap_name" mode tap + echo "tap_name='$tap_name'" >> "$NET_CONF_FILE" + ip link set "$tap_name" up + ip link set dev "$tap_name" master "$bridge_name" + dnsmasq -k --interface="$bridge_name" --bind-interface \ + --dhcp-range="$dhcp_range" & + dnsmasq_pid="$!" + echo "dnsmasq_pid='$dnsmasq_pid'" >> "$NET_CONF_FILE" + disown -h "$dnsmasq_pid" + + echo "nft_ruleset='$(nft -s list ruleset)'" >> "$NET_CONF_FILE" + + # dhcp + nft add rule ip filter INPUT udp dport 67 accept + nft add rule ip filter INPUT tcp dport 67 accept + # dns + nft add rule ip filter INPUT udp dport 53 accept + nft add rule ip filter INPUT tcp dport 53 accept + + # forward bridge + nft add rule ip filter FORWARD iifname "$bridge_name" \ + counter packets 0 bytes 0 accept + nft add rule ip filter FORWARD oifname "$bridge_name" \ + counter packets 0 bytes 0 accept + nft add rule ip nat POSTROUTING oifname "$(default_route)" \ + counter masquerade +} + +delete() { + source "$NET_CONF_FILE" + + kill "$dnsmasq_pid" + ip link del "$tap_name" + ip link del "$bridge_name" + nft flush ruleset + nft -f - <<< "$nft_ruleset" + rm "$NET_CONF_FILE" +} + +if [[ -z "$NET_CONF_FILE" ]]; then + echo Please specify the configuration file path \ + with NET_CONF_FILE >&2 + exit 1 +fi + +if [[ "EUID" -ne 0 ]]; then + echo "Please run as root" >&2 + exit 2 +fi + +"$@" diff --git a/pre-start.sh b/pre-start.sh new file mode 100755 index 0000000..b899e2c --- /dev/null +++ b/pre-start.sh @@ -0,0 +1,6 @@ +#!/usr/bin/env bash + +if [[ -z "$DEBUG" ]]; then + echo efi-framebuffer.0 | sudo tee \ + '/sys/bus/platform/devices/efi-framebuffer.0/driver/unbind' || true +fi diff --git a/start b/start new file mode 100755 index 0000000..6780340 --- /dev/null +++ b/start @@ -0,0 +1,98 @@ +#!/usr/bin/env bash + +set -eo pipefail + +if [[ -z "$VMNAME" ]]; then + echo "VMNAME not specified, aborting" >&2 + exit 1 +fi + +if [[ -z "$DEBUG" ]]; then + DEBUG= +fi + +set -u + +SUDO=sudo + +$SUDO true + +NET_CONF_FILE="$(sudo mktemp)" + +# create bridge +NET_CONF_FILE="$NET_CONF_FILE" $DEBUG $SUDO --preserve-env=NET_CONF_FILE \ + ./net create + +# rebind devices +for device in $(< vfio_devices.txt); do + $DEBUG $SUDO ./pci vfio_rebind_device "$device" +done + +# efi variables +EFI_VARS="$(mktemp)" +cp /usr/share/ovmf/x64/OVMF_VARS.fd "$EFI_VARS" +EFI_FIRMWARE=/usr/share/ovmf/x64/OVMF_CODE.fd + +base_path=base +default_path=default +hardware_path=hardware +specific_path="$VMNAME" +if [[ -e "$base_path.conf.tmpl" ]]; then + ( + # shellcheck disable=SC1090 + [[ -e "$base_path.sh" ]] && source "$base_path.sh" + envsubst < "$base_path.conf.tmpl" > "$base_path.conf" + ) +fi +if [[ -e "$default_path.conf.tmpl" ]]; then + ( + # shellcheck disable=SC1090 + [[ -e "$default_path.sh" ]] && source "$default_path.sh" + envsubst < "$default_path.conf.tmpl" > "$default_path.conf" + ) +fi +if [[ -e "$hardware_path.conf.tmpl" ]]; then + ( + # shellcheck disable=SC1090 + [[ -e "$hardware_path.sh" ]] && source "$hardware_path.sh" + envsubst < "$hardware_path.conf.tmpl" > "$hardware_path.conf" + ) +fi +if [[ -e "$specific_path.conf.tmpl" ]]; then + ( + # shellcheck disable=SC1090 + [[ -e "$specific_path.sh" ]] && source "$specific_path.sh" + envsubst < "$specific_path.conf.tmpl" > "$specific_path.conf" + ) +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 '' < "$base_path.conf" || true +read -ra default_arguments -d '' < "$default_path.conf" || true +read -ra hardware_arguments -d '' < "$hardware_path.conf" || true +read -ra specific_arguments -d '' < "$specific_path.conf" || true +$DEBUG $SUDO nice --adjustment=-20 taskset --cpu-list '1-5,7-11' \ + qemu-system-x86_64 \ + -name "$VMNAME,process=VMNAME" \ + -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[@]}" + +NET_CONF_FILE="$NET_CONF_FILE" $DEBUG $SUDO --preserve-env=NET_CONF_FILE \ + ./net delete diff --git a/vfio_devices.txt b/vfio_devices.txt new file mode 100644 index 0000000..4be96e3 --- /dev/null +++ b/vfio_devices.txt @@ -0,0 +1,4 @@ +0000:02:00.0 +0000:07:00.0 +0000:65:00.0 +0000:65:00.1 diff --git a/win10.conf.tmpl b/win10.conf.tmpl new file mode 100644 index 0000000..19436c5 --- /dev/null +++ b/win10.conf.tmpl @@ -0,0 +1,5 @@ +-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 + diff --git a/win10.sh b/win10.sh new file mode 100755 index 0000000..cba15cb --- /dev/null +++ b/win10.sh @@ -0,0 +1,4 @@ +#!/usr/bin/env bash + +export SSD_ID='0000:02:00.0' +export SATA_ID='0000:07:00.0'