From 077155f8359fdc9f54c8620fae9a658649bd29cd Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sat, 17 Aug 2024 22:17:45 -0400 Subject: [PATCH] Add windows vm to FreeBSD on laptop. --- .../environments/home/host_vars/homeserver | 1 - .../environments/laptop/host_vars/odofreebsd | 2 +- ansible/roles/base/files/watch_freebsd | 2 +- ansible/roles/bhyve/defaults/main.yaml | 1 - .../bhyve/files/bhyve_netgraph_bridge.bash | 4 +- .../files/launch_windows.bash | 285 ++++++++++++++++++ ansible/roles/framework_laptop/files/windows | 46 +++ ansible/roles/framework_laptop/meta/main.yaml | 3 + .../roles/framework_laptop/tasks/freebsd.yaml | 21 ++ .../roles/framework_laptop/tasks/linux.yaml | 8 + .../roles/jail_nat_dhcp/files/kea-dhcp4.conf | 1 + ansible/roles/kubernetes/files/kshell | 2 +- 12 files changed, 370 insertions(+), 6 deletions(-) create mode 100644 ansible/roles/framework_laptop/files/launch_windows.bash create mode 100644 ansible/roles/framework_laptop/files/windows create mode 100644 ansible/roles/framework_laptop/meta/main.yaml diff --git a/ansible/environments/home/host_vars/homeserver b/ansible/environments/home/host_vars/homeserver index 1e8a27a..785b6b1 100644 --- a/ansible/environments/home/host_vars/homeserver +++ b/ansible/environments/home/host_vars/homeserver @@ -77,7 +77,6 @@ jail_list: # - name: mumbledb # mount: /var/db/murmur bhyve_dataset: zmass/encrypted/vm -bhyve_list: [] bhyve_canmount: "on" bhyve_bemount: "on" wireguard_directory: homeserver diff --git a/ansible/environments/laptop/host_vars/odofreebsd b/ansible/environments/laptop/host_vars/odofreebsd index 322d499..9112ecf 100644 --- a/ansible/environments/laptop/host_vars/odofreebsd +++ b/ansible/environments/laptop/host_vars/odofreebsd @@ -49,7 +49,7 @@ jail_list: conf: src: nat_dhcp bhyve_dataset: zroot/freebsd/current/vm -bhyve_list: [] +bhyve_bemount: off # efi_dev: /dev/gpt/EFI efi_dev: /dev/diskid/DISK-SJB7N717610407Q0Hp1 sway_conf_files: diff --git a/ansible/roles/base/files/watch_freebsd b/ansible/roles/base/files/watch_freebsd index 6b56000..b2ac06b 100644 --- a/ansible/roles/base/files/watch_freebsd +++ b/ansible/roles/base/files/watch_freebsd @@ -10,7 +10,7 @@ DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" function cleanup { switch_to_main_screen } -for sig in EXIT INT QUIT HUP TERM; do +for sig in EXIT; do trap "set +e; cleanup; exit" "$sig" done diff --git a/ansible/roles/bhyve/defaults/main.yaml b/ansible/roles/bhyve/defaults/main.yaml index d7cab58..1838e9c 100644 --- a/ansible/roles/bhyve/defaults/main.yaml +++ b/ansible/roles/bhyve/defaults/main.yaml @@ -1,2 +1 @@ bhyve_mountpoint: "/vm" -bhyve_list: [] diff --git a/ansible/roles/bhyve/files/bhyve_netgraph_bridge.bash b/ansible/roles/bhyve/files/bhyve_netgraph_bridge.bash index 693b295..e44dd7d 100644 --- a/ansible/roles/bhyve/files/bhyve_netgraph_bridge.bash +++ b/ansible/roles/bhyve/files/bhyve_netgraph_bridge.bash @@ -47,7 +47,7 @@ function cleanup { done } vms=() -for sig in EXIT INT QUIT HUP TERM; do +for sig in EXIT; do trap "set +e; sleep 10; cleanup" "$sig" done @@ -154,6 +154,8 @@ function start_vm { -c $CPU_CORES \ -m $MEMORY \ -H \ + -P \ + -o 'rtc.use_localtime=false' \ -s 0,hostbridge \ -s "4,nvme,/dev/zvol/${zfs_path}/disk0" \ -s 30,xhci,tablet \ diff --git a/ansible/roles/framework_laptop/files/launch_windows.bash b/ansible/roles/framework_laptop/files/launch_windows.bash new file mode 100644 index 0000000..26a79c5 --- /dev/null +++ b/ansible/roles/framework_laptop/files/launch_windows.bash @@ -0,0 +1,285 @@ +#!/usr/local/bin/bash +# +set -euo pipefail +IFS=$'\n\t' +DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" + +# Share a host directory to the guest via 9pfs. +# +# Inside the VM run: +# mount -t virtfs -o trans=virtio sharename /some/vm/path +# mount -t 9p -o cache=mmap -o msize=512000 sharename /mnt/9p +# mount -t 9p -o trans=virtio,cache=mmap,msize=512000 sharename /path/to/mountpoint +# bhyve_options="-s 28,virtio-9p,sharename=/" + +# Enable Sound +# bhyve_options="-s 16,hda,play=/dev/dsp,rec=/dev/dsp" + +# Example usage: +# +# doas bhyve_netgraph_bridge create-disk zdata/vm/poudriere /vm/poudriere 10 +# doas bhyve_netgraph_bridge start poudriere zdata/vm/poudriere /vm/poudriere /vm/iso/FreeBSD-13.2-RELEASE-amd64-bootonly.iso +# doas bhyve_netgraph_bridge start poudriere zdata/vm/poudriere /vm/poudriere + +: ${VERBOSE:="NO"} # or YES +: ${CPU_CORES:="1"} +: ${MEMORY:="1G"} +: ${NETWORK:="NAT"} # or RAW or BOTH +: ${IP_RANGE:="10.215.1.1/24"} # Ignored for RAW networks +: ${INTERFACE_NAME:="jail_nat"} # or the external interface like lagg0 for RAW networks +: ${BRIDGE_NAME:="bridge_$INTERFACE_NAME"} # or bridge_raw for RAW networks +: ${VNC_ENABLE:="NO"} +: ${VNC_LISTEN:="127.0.0.1:5900"} +: ${VNC_WIDTH:="1920"} +: ${VNC_HEIGHT:="1080"} + +if [ "$VERBOSE" = "YES" ]; then + set -x +fi + +############## Setup ######################### + +function cleanup { + for vm in "${vms[@]}"; do + log "Destroying bhyve vm $vm" + bhyvectl "--vm=$vm" --destroy + log "Destroyed bhyve vm $vm" + done +} +vms=() +for sig in EXIT; do + trap "set +e; sleep 10; cleanup" "$sig" +done + +function die { + local status_code="$1" + shift + (>&2 echo "${@}") + exit "$status_code" +} + +function log { + (>&2 echo "${@}") +} + +############## Program ######################### + +function main { + local cmd="$1" + shift 1 + if [ "$cmd" = "create-disk" ]; then + create_disk "${@}" + elif [ "$cmd" = "start" ]; then + start_vm "${@}" + else + die 1 "Unrecognized command $cmd" + fi +} + +function create_disk { + local zfs_path="$1" + local mount_path="$2" + local gigabytes="$3" + zfs create -o "mountpoint=$mount_path" "$zfs_path" + cp /usr/local/share/edk2-bhyve/BHYVE_UEFI_VARS.fd "${mount_path}/" + tee "${mount_path}/settings" <&2 echo "Not using any network.") + else + die 1 "Unrecognized NETWORK type $NETWORK" + fi + + + # -H release the CPU when guest issues HLT instruction. Otherwise 100% of core will be consumed. + # -s 3,ahci-cd,/vm/.iso/archlinux-2023.04.01-x86_64.iso \ + # -s 29,fbuf,tcp=0.0.0.0:5900,w=1920,h=1080,wait \ + # -s 29,fbuf,tcp=0.0.0.0:5900,w=1920,h=1080 \ + + # TODO: Look into using nmdm instead of stdio for serial console + if [ -n "$mount_cd" ]; then + additional_args+=("-s" "5,ahci-cd,$mount_cd") + fi + if [ "$VNC_ENABLE" = "YES" ]; then + additional_args+=("-s" "29,fbuf,tcp=$VNC_LISTEN,w=$VNC_WIDTH,h=$VNC_HEIGHT,wait") + fi + vms+=("$name") + # Removes CPU_CORES because windows must be a single CPU in bhyve + # -c $CPU_CORES \ + # We need tpm + # -l "tpm,passthru,/dev/tpm0" \ + # -S \ + while true; do + set -x + set +e + bhyve \ + -D \ + -c sockets=1,cores=2,threads=2 \ + -m $MEMORY \ + -H \ + -w \ + -o 'rtc.use_localtime=false' \ + -s 0,hostbridge \ + -s "4,nvme,/dev/zvol/${zfs_path}/disk0" \ + -s 16,hda,play=/dev/dsp,rec=/dev/dsp \ + -s 30,xhci,tablet \ + -s 31,lpc -l com1,stdio \ + -l "bootrom,/usr/local/share/uefi-firmware/BHYVE_UEFI.fd,${mount_path}/BHYVE_UEFI_VARS.fd" \ + -U '5a63bcd1-5cb4-4401-8a6f-d4042fb928a6' \ + "${additional_args[@]}" \ + "$name" + local exit_code=$? + set -e + set +x + if [ $exit_code -eq 0 ]; then + echo "Rebooting." + sleep 5 + elif [ $exit_code -eq 1 ]; then + echo "Powered off." + break + elif [ $exit_code -eq 2 ]; then + echo "Halted." + break + elif [ $exit_code -eq 3 ]; then + echo "Triple fault." + break + elif [ $exit_code -eq 4 ]; then + echo "Exited due to an error." + break + fi + done +} + +function detect_available_link { + local bridge_name="$1" + local linknum=1 + while true; do + local link_name="link${linknum}" + if ! ng_exists "${bridge_name}:${link_name}"; then + echo "$link_name" + return + fi + linknum=$((linknum + 1)) + if [ "$linknum" -gt 90 ]; then + (>&2 echo "No available links on bridge $bridge_name") + exit 1 + fi + done +} + +function assert_bridge { + local host_interface_name="$1" + local bridge_name="$2" + local ip_range="$3" + + if ! ng_exists "${bridge_name}:"; then + ngctl -d -f - </dev/null 2>&1 +} + +function calculate_mac_address { + local name="$1" + local source + source=$(md5 -r -s "$name" | awk '{print $1}') + echo "06:${source:0:2}:${source:2:2}:${source:4:2}:${source:6:2}:${source:8:2}" +} + +function find_available_port { + local start_port="$1" + local port="$start_port" + while true; do + sockstat -P tcp -p 443 + port=$((port + 1)) + done +} + +function ngctlcat { + if [ "$VERBOSE" = "YES" ]; then + tee /dev/tty | ngctl -d -f - + else + ngctl -d -f - + fi +} + + +main "${@}" diff --git a/ansible/roles/framework_laptop/files/windows b/ansible/roles/framework_laptop/files/windows new file mode 100644 index 0000000..bc3b2e8 --- /dev/null +++ b/ansible/roles/framework_laptop/files/windows @@ -0,0 +1,46 @@ +#!/bin/sh +# +# REQUIRE: LOGIN +# PROVIDE: windows +# KEYWORD: shutdown + +. /etc/rc.subr +name=windows +rcvar=${name}_enable +start_cmd="${name}_start" +stop_cmd="${name}_stop" +status_cmd="${name}_status" +load_rc_config $name + +tmux_name="windows" + +windows_start() { + /usr/local/bin/tmux new-session -d -s "$tmux_name" "/usr/bin/env VNC_ENABLE=YES VNC_LISTEN=0.0.0.0:5900 /usr/local/bin/bash /usr/local/bin/launch_windows start windows zroot/freebsd/current/vm/windows /vm/windows /vm/.iso/Win11_23H2_English_x64v2.iso" +} + +windows_status() { + if /usr/local/bin/tmux has-session -t $tmux_name 2>/dev/null; then + echo "$tmux_name is running." + else + echo "$tmux_name is not running." + return 1 + fi +} + +windows_stop() { + /usr/local/bin/tmux has-session -t $tmux_name 2>/dev/null && ( + /usr/local/bin/tmux kill-session -t $tmux_name + sleep 10 + bhyvectl --vm=windows --destroy + # kill `cat /var/run/windows.pid` + ) + windows_wait_for_end +} + +windows_wait_for_end() { + while /usr/local/bin/tmux has-session -t $tmux_name 2>dev/null; do + sleep 1 + done +} + +run_rc_command "$1" diff --git a/ansible/roles/framework_laptop/meta/main.yaml b/ansible/roles/framework_laptop/meta/main.yaml new file mode 100644 index 0000000..4fc8499 --- /dev/null +++ b/ansible/roles/framework_laptop/meta/main.yaml @@ -0,0 +1,3 @@ +dependencies: + - role: bhyve + when: 'os_flavor == "freebsd"' diff --git a/ansible/roles/framework_laptop/tasks/freebsd.yaml b/ansible/roles/framework_laptop/tasks/freebsd.yaml index fb5f5f5..316f365 100644 --- a/ansible/roles/framework_laptop/tasks/freebsd.yaml +++ b/ansible/roles/framework_laptop/tasks/freebsd.yaml @@ -7,3 +7,24 @@ group: wheel loop: - disable_wifi_powersave + +- name: Install scripts + copy: + src: "files/{{ item.src }}" + dest: "{{ item.dest }}" + mode: 0755 + owner: root + group: wheel + loop: + - src: launch_windows.bash + dest: /usr/local/bin/launch_windows + +- name: Install rc script + copy: + src: "files/{{ item.src }}" + dest: "/usr/local/etc/rc.d/{{ item.dest|default(item.src) }}" + owner: root + group: wheel + mode: 0755 + loop: + - src: windows diff --git a/ansible/roles/framework_laptop/tasks/linux.yaml b/ansible/roles/framework_laptop/tasks/linux.yaml index 2367007..1605b66 100644 --- a/ansible/roles/framework_laptop/tasks/linux.yaml +++ b/ansible/roles/framework_laptop/tasks/linux.yaml @@ -65,3 +65,11 @@ loop: - gpe10-boot.service - gpe10-sleep.service +# install swtpm +# install edk2-ovmf for /usr/share/ovmf/OVMF.fd +# install qemu-system-x86 + +# doas qemu-system-x86_64 -cdrom /vm/.iso/Win11_23H2_English_x64v2.iso -cpu Skylake-Client-v3 -enable-kvm -m 8192 —device chardev,socket,id=chrtpm,path=/tmp/emulated_tpm/swtpm-sock -tpmdev emulator,id=tpm0,chardev=chrtpm -device tpm-tis,tpmdev=tpm0 -smp 2 -device intel-hda -device hda-duplex -usb -nic user,ipv6=off,model=rtl8139,mac=84:1b:77:c9:03:a6 -bios /usr/share/edk2/x64/OVMF.fd -drive file=/dev/zvol/zroot/freebsd/current/vm/windows/disk0,format=raw,media=disk,if=none,id=nvm -device nvme,drive=nvm,serial=foo,opt_io_size=4096,min_io_size=4096,logical_block_size=4096,physical_block_size=4096 + +# doas mkdir /tmp/emulated_tpm +# doas swtpm socket --tpmstate dir=/tmp/emulated_tpm --ctrl type=unixio,path=/tmp/emulated_tpm/swtpm-sock --log level=20 --tpm2 diff --git a/ansible/roles/jail_nat_dhcp/files/kea-dhcp4.conf b/ansible/roles/jail_nat_dhcp/files/kea-dhcp4.conf index 895df95..4b85320 100644 --- a/ansible/roles/jail_nat_dhcp/files/kea-dhcp4.conf +++ b/ansible/roles/jail_nat_dhcp/files/kea-dhcp4.conf @@ -6,6 +6,7 @@ "subnet4": [ { "subnet": "10.215.1.0/24", + "id": 1, "pools": [ { "pool": "10.215.1.10-10.215.1.200" } ], "option-data": [ { diff --git a/ansible/roles/kubernetes/files/kshell b/ansible/roles/kubernetes/files/kshell index 07bd55d..f1dadfe 100644 --- a/ansible/roles/kubernetes/files/kshell +++ b/ansible/roles/kubernetes/files/kshell @@ -13,7 +13,7 @@ function cleanup { done } pods=() -for sig in EXIT INT QUIT HUP TERM; do +for sig in EXIT; do trap "set +e; cleanup" "$sig" done