boot: the BIOS loader is gone
1449 lines of boot/bios/, tools/sign.pl and tools/padstage2.pl, plus the build rules, the disk image and the ISO entry that existed to carry them. Everything since roughly 2012 boots UEFI, and CSM -- the firmware-side BIOS emulation that lets a legacy loader run on such a machine at all -- has been going away since Intel dropped support for it in 2020. So this is not a path being retired early; it is one whose remaining hardware is already the exception. The cost was never the code, it was the tax on everything near it. Stage 2 had a 65-sector ceiling it overflowed silently -- 25440 bytes against 24576 -- and because the kernel is written at sector 1 + STAGE2_SECTORS, an overflowing loader was overwritten by the very kernel it was loading. The resolution list could not be shared with UEFI either: VBE mode setting is INT 10h, so matching it needed unreal mode, which is the riskiest code in the tree for a feature that already worked elsewhere. What replaces schos.img is the staged ESP, handed to QEMU as a virtual FAT volume. That is a development arrangement and not an installable disk; a real GPT image with a real FAT partition is the installer's job, and until it exists nothing here writes a disk a machine boots unaided. Secure Boot is untouched too -- the loader is unsigned. The ISO keeps one El Torito entry, the FAT image holding loader and kernel. It never booted from a USB stick via BIOS anyway: the firmware presents the stick as a hard disk while stage2main.c read the kernel back over ATAPI. The 32-bit multiboot entry in entry.S now has no caller and is left in place. Deleting kernel entry code is its own change with its own way of failing silently, and it should not ride along with this one. Ported with it: run.ps1, the six screenshot tools, and schcom_test.sh, each with its own OVMF varstore copy so no run inherits another's UEFI variables. Two things this shook out. OVMF now belongs in `all`: nothing boots without firmware, `make clean` wipes build/ including the copy fetched from the host, and a clean followed by a build left every run script throwing. And it cannot be listed as a prerequisite at the top of the file -- make expands prerequisites when it reads the line, and $(OVMF_CODE) is defined further down, so naming it there appends nothing and says nothing. Verified from a clean tree: 110 [ok] and no failures on the full suite, the ISO boots with no hard disk attached, schfs-check passes, schcom's end-to-end pixel test passes, and fbshot captures a real 1280x800 frame.
This commit is contained in:
parent
0007853baa
commit
651899220c
23 changed files with 317 additions and 1826 deletions
|
|
@ -3,62 +3,37 @@
|
|||
#
|
||||
# tools/posix/mkiso.sh [output.iso]
|
||||
#
|
||||
# The disc carries two El Torito boot entries and a real directory tree:
|
||||
# The disc carries one El Torito boot entry and a real directory tree:
|
||||
#
|
||||
# /boot/schos.bin BIOS: cdboot + stage2 + kernel, loaded no-emulation
|
||||
# /boot/efiboot.img UEFI: a FAT image holding EFI/BOOT/BOOTX64.EFI
|
||||
# /boot/efiboot.img a FAT image holding the loader and the kernel
|
||||
# /EFI/BOOT/BOOTX64.EFI the same loader, visible as a file on the disc
|
||||
# /bin /etc /usr ... the staged sysroot, as an ordinary hierarchy
|
||||
#
|
||||
# --- BIOS entry -------------------------------------------------------------
|
||||
# --- Why only one entry -----------------------------------------------------
|
||||
#
|
||||
# El Torito's "no emulation" mode, which is the only one of the three that fits
|
||||
# how SchOS actually loads itself.
|
||||
# There was a second, for BIOS: cdboot and stage2 concatenated with the kernel
|
||||
# and loaded by El Torito in no-emulation mode. The BIOS loader is gone, so the
|
||||
# entry that started it is too.
|
||||
#
|
||||
# floppy emulation caps the image at 2.88 MB; the ramdisk-root kernel
|
||||
# alone is bigger than that.
|
||||
# hard disk emulation presents the image through the BIOS's INT 13h, and
|
||||
# SchOS's loader does not use INT 13h -- it drives
|
||||
# the IDE controller's registers directly, which
|
||||
# cannot reach a BIOS-emulated drive.
|
||||
# no emulation the BIOS loads a chosen run of sectors at 0x7C00
|
||||
# and gets out of the way. That is this.
|
||||
# What UEFI wants is not that mechanism at all. Firmware does not run
|
||||
# no-emulation boot code; it looks for a FAT filesystem containing
|
||||
# EFI/BOOT/BOOTX64.EFI and hands that volume to the application it finds. So
|
||||
# the entry is a FAT image built with mtools, and it carries the kernel as well
|
||||
# as the loader -- BOOTX64.EFI opens \schos\kernel on the volume it was itself
|
||||
# loaded from, and for a UEFI boot that volume is this FAT image, not the ISO
|
||||
# 9660 tree around it. Without the kernel in here the loader comes up and halts
|
||||
# with EFI_NOT_FOUND, which is what it did.
|
||||
#
|
||||
# So the layout below is deliberate: -boot-load-size is 1 + STAGE2_SECTORS,
|
||||
# which makes the BIOS place both boot/cdboot and the whole of stage2 in memory
|
||||
# before anything of ours runs. That puts stage2 at 0x7E00 -- byte for byte the
|
||||
# arrangement bootasm.S produces on a hard disk. From there the only difference
|
||||
# is that stage2main.c reads the kernel back with ATAPI instead of ATA.
|
||||
#
|
||||
# STAGE2_SECTORS comes from the environment, set by the Makefile, because this
|
||||
# same number also lives in boot/bios/bootasm.S, boot/bios/stage2main.c and the dd offsets
|
||||
# in schos.img's recipe. It was previously written out by hand in three
|
||||
# separate spots in this file alone, and raising it for the boot logo is what
|
||||
# turned that from a latent hazard into a broken image.
|
||||
#
|
||||
# NOT using -boot-info-table on purpose: it patches 56 bytes over offset 8 of
|
||||
# the boot image, which is the middle of cdboot's code. stage2main.c finds
|
||||
# where it was loaded from by reading the El Torito boot catalog instead.
|
||||
#
|
||||
# --- UEFI entry -------------------------------------------------------------
|
||||
#
|
||||
# UEFI firmware does not run El Torito no-emulation code. It wants a FAT
|
||||
# filesystem containing EFI/BOOT/BOOTX64.EFI, offered as a second boot entry.
|
||||
# So one is built with mtools and added with -eltorito-alt-boot. The same
|
||||
# loader is also copied into the ISO tree at /EFI/BOOT/BOOTX64.EFI, because
|
||||
# The loader is also copied into the ISO tree at /EFI/BOOT/BOOTX64.EFI, because
|
||||
# some firmware looks there directly when booting from a USB stick.
|
||||
#
|
||||
# --- Booting from a USB stick -----------------------------------------------
|
||||
#
|
||||
# -isohybrid-gpt-basdat adds a GPT that marks the FAT image as a basic data
|
||||
# partition, so writing this .iso to a stick with dd produces something UEFI
|
||||
# will boot.
|
||||
#
|
||||
# That covers UEFI only, and deliberately. The classic BIOS isohybrid trick
|
||||
# needs syslinux's isohdpfx.bin, and even with it the BIOS path here would not
|
||||
# work: on a stick the firmware presents the medium as a hard disk, while
|
||||
# stage2main.c reads the kernel back over ATAPI. Booting BIOS off USB is what
|
||||
# build/usbstick.img is for.
|
||||
# will boot. That was already the only arrangement that worked from a stick:
|
||||
# the firmware presents the medium as a hard disk, while the BIOS loader read
|
||||
# the kernel back over ATAPI, so it never booted this way even when it existed.
|
||||
#
|
||||
# --- Rock Ridge and Joliet --------------------------------------------------
|
||||
#
|
||||
|
|
@ -76,34 +51,29 @@ set -e
|
|||
|
||||
BUILD=${BUILD:-build}
|
||||
OUT=${1:-schos.iso}
|
||||
STAGE2_SECTORS=${STAGE2_SECTORS:-48}
|
||||
STAGE2_BYTES=$((STAGE2_SECTORS * 512))
|
||||
LOAD_SIZE=$((1 + STAGE2_SECTORS))
|
||||
STAGING="$BUILD/iso"
|
||||
SYSROOT=${SYSROOT:-sysroot}
|
||||
|
||||
CDBOOT="$BUILD/boot/cdboot"
|
||||
STAGE2="$BUILD/boot/stage2"
|
||||
KERNEL="$BUILD/kernel/kernel"
|
||||
EFIAPP="$BUILD/uefi/BOOTX64.EFI"
|
||||
|
||||
for f in "$CDBOOT" "$STAGE2" "$KERNEL"; do
|
||||
[ -f "$f" ] || { echo "mkiso: missing $f -- run make first" >&2; exit 1; }
|
||||
done
|
||||
[ -f "$KERNEL" ] || { echo "mkiso: missing $KERNEL -- run make first" >&2; exit 1; }
|
||||
[ -f "$EFIAPP" ] || { echo "mkiso: missing $EFIAPP -- run 'make uefi' first" >&2; exit 1; }
|
||||
|
||||
# The three pieces must be contiguous and at these exact sizes: stage2main.c's
|
||||
# readseg() finds the kernel at sector (1 + STAGE2_SECTORS) of the boot image,
|
||||
# so a short stage2 would silently shift the kernel out from under it.
|
||||
cdboot_sz=$(wc -c < "$CDBOOT")
|
||||
stage2_sz=$(wc -c < "$STAGE2")
|
||||
[ "$cdboot_sz" -eq 512 ] || { echo "mkiso: cdboot is $cdboot_sz bytes, expected 512" >&2; exit 1; }
|
||||
[ "$stage2_sz" -eq "$STAGE2_BYTES" ] || { echo "mkiso: stage2 is $stage2_sz bytes, expected $STAGE2_BYTES (STAGE2_SECTORS=$STAGE2_SECTORS)" >&2; exit 1; }
|
||||
# mtools is not optional any more. It used to be: without it the disc fell back
|
||||
# to being BIOS-only, which was a degraded disc rather than no disc. There is no
|
||||
# other entry to fall back to now, so a missing mformat means the ISO would not
|
||||
# boot anything, and failing here says so instead of shipping it.
|
||||
if ! command -v mformat >/dev/null 2>&1 || ! command -v mcopy >/dev/null 2>&1; then
|
||||
echo "mkiso: need mtools (mformat, mcopy) to build the EFI system partition" >&2
|
||||
echo " Fedora: sudo dnf install mtools" >&2
|
||||
echo " Debian/Ubuntu: apt install mtools" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
rm -rf "$STAGING"
|
||||
mkdir -p "$STAGING/boot" "$STAGING/EFI/BOOT"
|
||||
|
||||
cat "$CDBOOT" "$STAGE2" "$KERNEL" > "$STAGING/boot/schos.bin"
|
||||
cp "$EFIAPP" "$STAGING/EFI/BOOT/BOOTX64.EFI"
|
||||
|
||||
# The FAT image for the UEFI El Torito entry.
|
||||
|
|
@ -113,10 +83,8 @@ cp "$EFIAPP" "$STAGING/EFI/BOOT/BOOTX64.EFI"
|
|||
# this FAT image -- not the ISO 9660 tree around it. Without the kernel in here
|
||||
# the loader comes up and halts with EFI_NOT_FOUND, which is what it did.
|
||||
#
|
||||
# That means the kernel is stored twice on the disc: once inside boot/schos.bin
|
||||
# for the BIOS entry, once here for the UEFI one. The alternative is teaching
|
||||
# the loader to go looking for other volumes, which is a real feature and not a
|
||||
# packaging detail -- worth doing deliberately rather than to save a copy.
|
||||
# The ISO 9660 tree still carries the sysroot for a running system to read; the
|
||||
# loader's own two files live in here because that is the volume it is handed.
|
||||
#
|
||||
# Sized from what actually goes in plus slack, rather than fixed: a hardcoded
|
||||
# size is a thing that breaks quietly the first time the kernel grows.
|
||||
|
|
@ -125,8 +93,8 @@ payload_kb=$(( ( $(wc -c < "$EFIAPP") + $(wc -c < "$KERNEL") + 1023 ) / 1024 ))
|
|||
img_kb=$(( ( (payload_kb + 512 + 31) / 32 ) * 32 ))
|
||||
[ "$img_kb" -lt 512 ] && img_kb=512
|
||||
|
||||
if command -v mformat >/dev/null 2>&1 && command -v mcopy >/dev/null 2>&1; then
|
||||
rm -f "$EFI_IMG"
|
||||
rm -f "$EFI_IMG"
|
||||
if true; then
|
||||
# Create the file first and format it with an explicit geometry.
|
||||
#
|
||||
# Not `mformat -f`: that takes one of the standard floppy capacities
|
||||
|
|
@ -139,12 +107,6 @@ if command -v mformat >/dev/null 2>&1 && command -v mcopy >/dev/null 2>&1; then
|
|||
mmd -i "$EFI_IMG" ::/EFI ::/EFI/BOOT ::/schos
|
||||
mcopy -i "$EFI_IMG" "$EFIAPP" ::/EFI/BOOT/BOOTX64.EFI
|
||||
mcopy -i "$EFI_IMG" "$KERNEL" ::/schos/kernel
|
||||
HAVE_EFI=1
|
||||
else
|
||||
echo "mkiso: warning: mtools not found, building a BIOS-only disc" >&2
|
||||
echo " Fedora: sudo dnf install mtools" >&2
|
||||
rm -f "$EFI_IMG"
|
||||
HAVE_EFI=0
|
||||
fi
|
||||
|
||||
# The sysroot, as an ordinary hierarchy. Copied rather than linked so that
|
||||
|
|
@ -175,12 +137,6 @@ else
|
|||
exit 1
|
||||
fi
|
||||
|
||||
if [ "$HAVE_EFI" = 1 ]; then
|
||||
EFI_ARGS="-eltorito-alt-boot -e boot/efiboot.img -no-emul-boot $HYBRID"
|
||||
else
|
||||
EFI_ARGS=""
|
||||
fi
|
||||
|
||||
# shellcheck disable=SC2086 # EFI_ARGS is a deliberate word list
|
||||
$MKISOFS \
|
||||
-quiet \
|
||||
|
|
@ -188,18 +144,12 @@ $MKISOFS \
|
|||
-A "SchOS" \
|
||||
-R -J -joliet-long \
|
||||
-cache-inodes \
|
||||
-b boot/schos.bin \
|
||||
-e boot/efiboot.img \
|
||||
-no-emul-boot \
|
||||
-boot-load-size "$LOAD_SIZE" \
|
||||
$EFI_ARGS \
|
||||
$HYBRID \
|
||||
-o "$OUT" \
|
||||
"$STAGING"
|
||||
|
||||
echo "==> $OUT ($(wc -c < "$OUT") bytes)"
|
||||
if [ "$HAVE_EFI" = 1 ]; then
|
||||
echo " BIOS: qemu-system-x86_64 -cdrom $OUT -boot d"
|
||||
echo " UEFI: qemu-system-x86_64 -bios OVMF_CODE.fd -cdrom $OUT"
|
||||
echo " USB: dd if=$OUT of=/dev/sdX bs=4M (UEFI only -- see the header)"
|
||||
else
|
||||
echo " qemu-system-x86_64 -cdrom $OUT -boot d"
|
||||
fi
|
||||
echo " UEFI: qemu-system-x86_64 -bios OVMF_CODE.fd -cdrom $OUT"
|
||||
echo " USB: dd if=$OUT of=/dev/sdX bs=4M"
|
||||
|
|
|
|||
|
|
@ -17,6 +17,10 @@ LOG=$BUILD/schcom-test.log
|
|||
PPM=${1:-$BUILD/schcom.ppm}
|
||||
rm -f "$LOG" "$PPM"
|
||||
|
||||
# The firmware varstore is written by OVMF, so this run gets its own copy
|
||||
# rather than sharing the pristine one every other run starts from.
|
||||
cp -f build/OVMF_VARS.fd build/schcom-OVMF_VARS.fd
|
||||
|
||||
PORT=$(python3 - <<'EOF'
|
||||
import socket
|
||||
s=socket.socket(); s.bind(('127.0.0.1',0)); print(s.getsockname()[1]); s.close()
|
||||
|
|
@ -32,7 +36,11 @@ EOF
|
|||
sleep 40
|
||||
) | timeout -k 5 120 qemu-system-x86_64 \
|
||||
-machine pc-i440fx-9.2 -cpu qemu64,+rdtscp \
|
||||
-hda schos.img -hdb build/schfs.img -m 1G -smp 1 \
|
||||
-drive if=pflash,format=raw,unit=0,readonly=on,file=build/OVMF_CODE.fd \
|
||||
-drive if=pflash,format=raw,unit=1,file=build/schcom-OVMF_VARS.fd \
|
||||
-drive file=fat:rw:build/esp,index=0,media=disk \
|
||||
-drive file=build/schfs.img,format=raw,index=1,media=disk \
|
||||
-m 1G -smp 1 \
|
||||
-display none -vga std \
|
||||
-monitor tcp:127.0.0.1:$PORT,server,nowait \
|
||||
-chardev stdio,id=c0,logfile="$LOG" -serial chardev:c0 \
|
||||
|
|
|
|||
Loading…
Reference in a new issue