diff --git a/Makefile b/Makefile index 8827c62..5723e0b 100644 --- a/Makefile +++ b/Makefile @@ -31,29 +31,29 @@ distclean: test: disk echo 'running $< in qemu' - ./run.sh '$<' | tee serial.log - grep -xF 'Found GPT disk: 01234567-ABCD-0123-ABCD-0123456789AB' < serial.log > /dev/null - grep -xF 'Found ESP partition: AAAAAAAA-BBBB-CCCC-DDDD-EEEEEEEEEEEE' < serial.log > /dev/null - grep -xF 'Using ESP partiton @00000800' < serial.log > /dev/null + ./run_vm.sh -n '$<' | tee serial.log + grep -F 'Found GPT disk: 01234567-ABCD-0123-ABCD-0123456789AB' < serial.log > /dev/null + grep -F 'Found ESP partition: AAAAAAAA-BBBB-CCCC-DDDD-EEEEEEEEEEEE' < serial.log > /dev/null + grep -F 'Using ESP partiton @00000800' < serial.log > /dev/null grep -F 'hello from the initrd' < serial.log > /dev/null uki_test: uki.efi uki_disk echo 'running $< as EFI binary' - ./run_efi.sh '$<' | tee serial.log + ./run_vm.sh -n --efi --kernel '$<' | tee serial.log grep -F 'hello from the initrd' < serial.log > /dev/null echo 'running $< as kernel bzImage' - ./run_kernel.sh '$<' | tee serial.log + ./run_vm.sh -n --kernel '$<' | tee serial.log grep -F 'hello from the initrd' < serial.log > /dev/null echo 'running $(word 2,$^) in qemu' - ./run.sh '$(word 2,$^)' | tee serial.log - grep -xF 'Found GPT disk: 01234567-ABCD-0123-ABCD-0123456789AB' < serial.log > /dev/null - grep -xF 'Found ESP partition: AAAAAAAA-BBBB-CCCC-DDDD-EEEEEEEEEEEE' < serial.log > /dev/null - grep -xF 'Using ESP partiton @00000800' < serial.log > /dev/null + ./run_vm.sh -n '$(word 2,$^)' | tee serial.log + grep -F 'Found GPT disk: 01234567-ABCD-0123-ABCD-0123456789AB' < serial.log > /dev/null + grep -F 'Found ESP partition: AAAAAAAA-BBBB-CCCC-DDDD-EEEEEEEEEEEE' < serial.log > /dev/null + grep -F 'Using ESP partiton @00000800' < serial.log > /dev/null grep -F 'hello from the initrd' < serial.log > /dev/null -kexec_test: kexec.cpio +kexec_test: kernel kexec.cpio echo 'running $< as initrd in qemu' - ./run_init.sh kernel '$<' rdinit=/mini_kexec | tee serial.log + ./run_vm.sh -n --kernel '$<' --initrd '$(word 2,$^)' --append rdinit=/mini_kexec | tee serial.log grep -F 'kexec_core: Starting new kernel' < serial.log > /dev/null grep -F 'hello from the initrd' < serial.log > /dev/null diff --git a/make_demo.sh b/make_demo.sh index 3e3486e..337bd43 100755 --- a/make_demo.sh +++ b/make_demo.sh @@ -27,11 +27,16 @@ mount -t proc proc /proc mount -t sysfs sysfs /sys mount -t tmpfs -o mode=0755 none /run mount -t tmpfs none /tmp -mount -t vfat /dev/sda1 /efi +mount -t vfat -o ro /dev/sda1 /efi + +ln -s /proc/self/fd /dev/fd +ln -s /proc/self/fd/0 /dev/stdin +ln -s /proc/self/fd/1 /dev/stdout +ln -s /proc/self/fd/2 /dev/stderr echo 4 > /proc/sys/kernel/printk -sh +exec setsid -c /bin/sh -i EOF cat > "$tmpdir/rootfs/etc/passwd" << EOF @@ -82,7 +87,7 @@ mcopy -i "$disk@@2048s" - ::/loader/entries/example-00.conf << EOF title Demo version 0.1 linux /kernel -options root=/dev/sda2 ro +options loglevel=6 root=/dev/sda2 ro EOF mdir -i "$disk@@2048s" -/ :: diff --git a/make_demo_uki.sh b/make_demo_uki.sh index b0b2443..4bf82fe 100755 --- a/make_demo_uki.sh +++ b/make_demo_uki.sh @@ -29,11 +29,16 @@ mount -t sysfs sysfs /sys mount -t devtmpfs devtmpfs /dev mount -t tmpfs -o mode=0755 none /run mount -t tmpfs none /tmp -mount -t vfat /dev/sda1 /efi +mount -t vfat -o ro /dev/sda1 /efi + +ln -s /proc/self/fd /dev/fd +ln -s /proc/self/fd/0 /dev/stdin +ln -s /proc/self/fd/1 /dev/stdout +ln -s /proc/self/fd/2 /dev/stderr echo 4 > /proc/sys/kernel/printk -sh +exec setsid -c /bin/sh -i EOF cat > "$tmpdir/rootfs/etc/passwd" << EOF @@ -51,7 +56,7 @@ chmod a+x "$tmpdir/rootfs/sbin/init" (cd "$tmpdir/rootfs" && find . | cpio -o -H newc) > "$tmpdir/rootfs.cpio" -ukify build --stub /usr/lib/systemd/boot/efi/linuxx64.efi.stub --linux "$kernel" --initrd "$tmpdir/rootfs.cpio" --cmdline "rdinit=/sbin/init" --os-release $'PRETTY_NAME=Demo\nVERSION=0.1\n' -o "$tmpdir/uki_base.efi" +ukify build --stub /usr/lib/systemd/boot/efi/linuxx64.efi.stub --linux "$kernel" --initrd "$tmpdir/rootfs.cpio" --cmdline "loglevel=6 rdinit=/sbin/init" --os-release $'PRETTY_NAME=Demo\nVERSION=0.1\n' -o "$tmpdir/uki_base.efi" ./pe_inject.py "$tmpdir/uki_base.efi" "$kernel_stub" "$pe_loader" "$tmpdir/uki.efi" truncate -s 0 "$disk" diff --git a/run.sh b/run.sh deleted file mode 100755 index 8f1e8a4..0000000 --- a/run.sh +++ /dev/null @@ -1,6 +0,0 @@ -#!/usr/bin/env bash - -set -eufo pipefail - -qemu-system-x86_64 -machine pc -cpu qemu64 -accel tcg -m 1024 -nodefaults -no-reboot -nographic -serial stdio -drive file="$1",format=raw \ -| stdbuf -i0 -o0 sed 's/\x1b[\[0-9;?=]*[a-zA-Z]//g;s/\t/ /g;s/[^[:print:]]//g' diff --git a/run_demo_efi.sh b/run_demo_efi.sh deleted file mode 100755 index d483a17..0000000 --- a/run_demo_efi.sh +++ /dev/null @@ -1,7 +0,0 @@ -#!/usr/bin/env bash - -qemu-system-x86_64 \ - -machine q35 -cpu qemu64 -accel tcg -m 1024 \ - -nodefaults -no-reboot -nographic -serial mon:stdio \ - -drive if=pflash,unit=0,file=/usr/share/OVMF/OVMF_CODE_4M.fd,readonly=on \ - -drive file="$1",format=raw diff --git a/run_demo_legacy.sh b/run_demo_legacy.sh deleted file mode 100755 index 8fe9422..0000000 --- a/run_demo_legacy.sh +++ /dev/null @@ -1,6 +0,0 @@ -#!/usr/bin/env bash - -qemu-system-x86_64 \ - -machine pc -cpu qemu64 -accel tcg -m 1024 \ - -nodefaults -no-reboot -nographic -serial mon:stdio \ - -drive file="$1",format=raw diff --git a/run_efi.sh b/run_efi.sh deleted file mode 100755 index 9b1461b..0000000 --- a/run_efi.sh +++ /dev/null @@ -1,6 +0,0 @@ -#!/usr/bin/env bash - -set -eufo pipefail - -qemu-system-x86_64 -machine q35 -cpu qemu64 -accel tcg -m 1024 -nodefaults -no-reboot -nographic -serial stdio -drive if=pflash,unit=0,file=/usr/share/OVMF/OVMF_CODE_4M.fd,readonly=on -kernel "$1" \ -| stdbuf -i0 -o0 sed 's/\x1b[\[0-9;?=]*[a-zA-Z]//g;s/\t/ /g;s/[^[:print:]]//g' diff --git a/run_init.sh b/run_init.sh deleted file mode 100755 index badc38e..0000000 --- a/run_init.sh +++ /dev/null @@ -1,6 +0,0 @@ -#!/usr/bin/env bash - -set -eufo pipefail - -qemu-system-x86_64 -machine pc -cpu qemu64 -accel tcg -m 4096 -nodefaults -no-reboot -nographic -serial stdio -kernel "$1" -initrd "$2" -append "$3" \ -| stdbuf -i0 -o0 sed 's/\x1b[\[0-9;?=]*[a-zA-Z]//g;s/\t/ /g;s/[^[:print:]]//g' diff --git a/run_kernel.sh b/run_kernel.sh deleted file mode 100755 index a7e07f4..0000000 --- a/run_kernel.sh +++ /dev/null @@ -1,6 +0,0 @@ -#!/usr/bin/env bash - -set -eufo pipefail - -qemu-system-x86_64 -machine pc -cpu qemu64 -accel tcg -m 1024 -nodefaults -no-reboot -nographic -serial stdio -kernel "$1" \ -| stdbuf -i0 -o0 sed 's/\x1b[\[0-9;?=]*[a-zA-Z]//g;s/\t/ /g;s/[^[:print:]]//g' diff --git a/run_vm.sh b/run_vm.sh new file mode 100755 index 0000000..3bbb7ed --- /dev/null +++ b/run_vm.sh @@ -0,0 +1,35 @@ +#!/usr/bin/env bash + +set -euo pipefail + +machine="pc" +serial="mon:stdio" +qemu_opts=() + +while [[ $# -gt 0 ]]; do + case "$1" in + --efi) + machine="q35"; qemu_opts+=(-drive if=pflash,unit=0,file=/usr/share/OVMF/OVMF_CODE_4M.fd,readonly=on); shift;; + --legacy) + machine="pc"; shift;; + -n|--no-monitor) + serial="stdio"; shift;; + --kernel) + qemu_opts+=(-kernel "$2"); shift 2;; + --initrd) + qemu_opts+=(-initrd "$2"); shift 2;; + --append) + qemu_opts+=(-append "$2"); shift 2;; + *) + break;; + esac +done + +for disk in "$@"; do + qemu_opts+=(-drive "file=$disk,format=raw") +done + +exec ./term_emu.py qemu-system-x86_64 \ + -machine "$machine" -cpu qemu64 -accel tcg -m 1024 \ + -nodefaults -no-reboot -nographic -serial "$serial" \ + "${qemu_opts[@]}" diff --git a/term_emu.py b/term_emu.py new file mode 100755 index 0000000..df1376f --- /dev/null +++ b/term_emu.py @@ -0,0 +1,102 @@ +#!/usr/bin/env python3 + +import errno +import os +import pty +import selectors +import sys +import termios +import tty + +class EscSeqFilter: + def __init__(self): + self.state = "TXT" + + def feed(self, data): + out = bytearray() + for ch in data: + if self.state == "TXT": + if ch == 0x1B: + self.state = "ESC" + elif 0x20 <= ch <= 0x7F or ch == 0x0A or ch == 0x0D or ch == 0x08: + out.append(ch) + elif self.state == "ESC": + if ch == ord("["): + self.state = "CSI" + elif ch == ord("]"): + self.state = "OSC" + elif ch == ord("P"): + self.state = "DCS" + else: + self.state = "TXT" + elif self.state == "CSI": + if 0x40 <= ch <= 0x7E: + self.state = "TXT" + elif self.state == "OSC": + if ch == 0x07: + self.state = "TXT" + elif ch == 0x1B: + self.state = "OSC_ESC" + elif self.state == "OSC_ESC": + if ch == ord("\\"): + self.state = "TXT" + else: + self.state = "OSC" + elif self.state == "DCS": + if ch == 0x1B: + self.state = "DCS_ESC" + elif self.state == "DCS_ESC": + if ch == ord("\\"): + self.state = "TXT" + else: + self.state = "DCS" + return bytes(out) + +stdin_fd = sys.stdin.fileno() +stdout_fd = sys.stdout.fileno() + +try: + old_tios = termios.tcgetattr(stdin_fd) + tty.setraw(stdin_fd) +except Exception: + pass + +pid, master_fd = pty.fork() +if pid == 0: + os.execvp(sys.argv[1], sys.argv[1:]) + os._exit(127) + +sel = selectors.DefaultSelector() +try: + sel.register(stdin_fd, selectors.EVENT_READ) +except Exception: + pass +sel.register(master_fd, selectors.EVENT_READ) + +esc_filter = EscSeqFilter() + +try: + while True: + for key, _ in sel.select(): + if key.fd == stdin_fd: + data = os.read(stdin_fd, 65536) + if not data: + os.close(master_fd); raise SystemExit + os.write(master_fd, data) + else: + try: + data = os.read(master_fd, 65536) + except OSError as e: + if e.errno == errno.EIO: + os.waitpid(pid, 0); raise SystemExit + else: + raise + if not data: + os.waitpid(pid, 0); raise SystemExit + data = esc_filter.feed(data) + os.write(stdout_fd, data) +finally: + try: + termios.tcsetattr(stdin_fd, termios.TCSADRAIN, old_tios) + except Exception: + pass