uefi: copy the kernel home and jump from a page it cannot reach
The staged segments were copied home after ExitBootServices by the loader itself -- on code, a stack and a copy list the firmware had placed wherever it liked. On an HP all-in-one some of that was in the kernel's range: "Placing the kernel", then a triple fault and a reset. The copy and the jump now run from one page below the kernel's lowest address, allocated as loader code before the firmware exits: a short position-independent routine, the copy list, and a stack. It switches to the loader's page tables and its own stack before copying anything. Called sysv_abi, because UEFI code is the Microsoft convention and the first try loaded the framebuffer address into rsp. The framebuffer address handed over in rbx is 64-bit now too; the kernel already read the full one from the hand-off block. Also write.sh: writes schos.iso to a USB stick, after showing the device and asking for its name to be typed back; refuses partitions, the system disk and non-removable disks; reads the result back. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01S1giNvrHTrPMPw7V6x1kQ4
This commit is contained in:
parent
a4fbdb7437
commit
726091000e
2 changed files with 239 additions and 45 deletions
140
write.sh
Normal file
140
write.sh
Normal file
|
|
@ -0,0 +1,140 @@
|
|||
#!/bin/sh
|
||||
# Write schos.iso to a USB stick.
|
||||
#
|
||||
# ./write.sh sdc or ./write.sh /dev/sdc
|
||||
# ./write.sh sdc other.iso to write a different image
|
||||
#
|
||||
# Shows what is on the device, asks you to type the device's name back, then
|
||||
# asks for your password and writes. Everything on the device is destroyed;
|
||||
# there is no undo.
|
||||
#
|
||||
# Linux only: it reads the device list from lsblk. It refuses a partition
|
||||
# (sdc1 -- the stick is written whole), anything holding a mounted system
|
||||
# path, and anything that is not a removable or USB disk unless you add
|
||||
# --any-disk, because the one mistake this script exists to stop is writing
|
||||
# over the disk you are running from.
|
||||
|
||||
set -eu
|
||||
|
||||
if [ -t 1 ]; then
|
||||
R=$(printf '\033[1;31m'); G=$(printf '\033[1;32m')
|
||||
B=$(printf '\033[1m'); D=$(printf '\033[2m'); N=$(printf '\033[0m')
|
||||
else
|
||||
R='' G='' B='' D='' N=''
|
||||
fi
|
||||
|
||||
die() { printf '%swrite.sh:%s %s\n' "$R" "$N" "$*" >&2; exit 1; }
|
||||
|
||||
usage() {
|
||||
echo "usage: ./write.sh <device> [image.iso] [--any-disk]"
|
||||
echo " ./write.sh sdc writes schos.iso to /dev/sdc"
|
||||
echo
|
||||
echo "Removable disks on this machine:"
|
||||
lsblk -d -n -o NAME,SIZE,TRAN,RM,MODEL 2>/dev/null |
|
||||
awk '$3 == "usb" || $4 == "1" { printf " /dev/%-8s %8s %s\n", $1, $2, substr($0, index($0,$5)) }'
|
||||
exit 2
|
||||
}
|
||||
|
||||
ANY=0
|
||||
DEV=
|
||||
ISO=
|
||||
for a in "$@"; do
|
||||
case "$a" in
|
||||
--any-disk) ANY=1 ;;
|
||||
-h|--help) usage ;;
|
||||
*) if [ -z "$DEV" ]; then DEV=$a; elif [ -z "$ISO" ]; then ISO=$a; else usage; fi ;;
|
||||
esac
|
||||
done
|
||||
[ -n "$DEV" ] || usage
|
||||
|
||||
HERE=$(cd "$(dirname "$0")" && pwd)
|
||||
[ -n "$ISO" ] || ISO=$HERE/schos.iso
|
||||
|
||||
command -v lsblk >/dev/null 2>&1 || die "needs lsblk (util-linux); this script is Linux only"
|
||||
|
||||
# --- the image --------------------------------------------------------------
|
||||
|
||||
[ -f "$ISO" ] || die "no image at $ISO -- run 'make iso' first"
|
||||
ISO_BYTES=$(wc -c < "$ISO" | tr -d ' ')
|
||||
ISO_DATE=$(date -r "$ISO" '+%Y-%m-%d %H:%M' 2>/dev/null || echo '?')
|
||||
|
||||
# --- the device -------------------------------------------------------------
|
||||
|
||||
case "$DEV" in
|
||||
/dev/*) ;;
|
||||
*) DEV=/dev/$DEV ;;
|
||||
esac
|
||||
[ -b "$DEV" ] || die "$DEV is not a block device"
|
||||
|
||||
NAME=$(basename "$DEV")
|
||||
TYPE=$(lsblk -d -n -o TYPE "$DEV")
|
||||
[ "$TYPE" = disk ] || die "$DEV is a $TYPE, not a whole disk -- give the disk (sdc), not a partition (sdc1)"
|
||||
|
||||
SIZE_BYTES=$(lsblk -d -n -b -o SIZE "$DEV" | tr -d ' ')
|
||||
SIZE=$(lsblk -d -n -o SIZE "$DEV" | tr -d ' ')
|
||||
TRAN=$(lsblk -d -n -o TRAN "$DEV" | tr -d ' ')
|
||||
RM=$(lsblk -d -n -o RM "$DEV" | tr -d ' ')
|
||||
MODEL=$(lsblk -d -n -o MODEL "$DEV" | sed 's/ *$//')
|
||||
|
||||
# The disk this system runs from, or anything with a system path on it.
|
||||
for mp in $(lsblk -n -o MOUNTPOINTS "$DEV" 2>/dev/null); do
|
||||
case "$mp" in
|
||||
/|/boot|/boot/*|/efi|/home|/nix|/nix/*|/usr|/var|\[SWAP\])
|
||||
die "$DEV holds $mp -- that is this computer's own disk, not a stick" ;;
|
||||
esac
|
||||
done
|
||||
|
||||
if [ "$TRAN" != usb ] && [ "$RM" != 1 ] && [ "$ANY" != 1 ]; then
|
||||
die "$DEV ($MODEL, $SIZE, ${TRAN:-no transport}) is not a USB or removable disk.
|
||||
If you are sure, run it again with --any-disk."
|
||||
fi
|
||||
|
||||
[ "$SIZE_BYTES" -ge "$ISO_BYTES" ] || die "$DEV is $SIZE, smaller than the image"
|
||||
|
||||
# --- show, and ask ----------------------------------------------------------
|
||||
|
||||
echo
|
||||
echo "${B}Image${N} $ISO"
|
||||
echo " $((ISO_BYTES / 1048576)) MB, built $ISO_DATE"
|
||||
echo
|
||||
echo "${B}Device${N} $DEV ${B}$MODEL${N} $SIZE ${D}(${TRAN:-?}, removable=$RM)${N}"
|
||||
lsblk -n -o NAME,SIZE,FSTYPE,LABEL,MOUNTPOINTS "$DEV" | sed '1d; s/^/ /'
|
||||
USED=$(lsblk -n -o NAME "$DEV" | sed 1d | wc -l | tr -d ' ')
|
||||
[ "$USED" -gt 0 ] || echo " ${D}(no partitions)${N}"
|
||||
echo
|
||||
echo "${R}Everything on $DEV will be destroyed. This cannot be undone.${N}"
|
||||
printf 'Type %s%s%s to write, anything else to stop: ' "$B" "$NAME" "$N"
|
||||
read -r answer
|
||||
[ "$answer" = "$NAME" ] || { echo "Stopped. Nothing was written."; exit 1; }
|
||||
|
||||
# --- write ------------------------------------------------------------------
|
||||
|
||||
echo
|
||||
echo "Your password is needed to write to the device."
|
||||
sudo -v || die "no permission; nothing was written"
|
||||
|
||||
# Unmount whatever the desktop mounted, or the kernel keeps writing its own
|
||||
# cached view of the old filesystem over the new image afterwards.
|
||||
for part in $(lsblk -n -l -o NAME,MOUNTPOINTS "$DEV" | awk 'NF > 1 { print $1 }'); do
|
||||
echo "Unmounting /dev/$part"
|
||||
sudo umount "/dev/$part" || die "could not unmount /dev/$part; nothing was written"
|
||||
done
|
||||
|
||||
echo "Writing..."
|
||||
sudo dd if="$ISO" of="$DEV" bs=4M conv=fsync oflag=direct status=progress
|
||||
sync
|
||||
|
||||
# --- check ------------------------------------------------------------------
|
||||
|
||||
# Read it back. A stick that takes the write and keeps none of it is common
|
||||
# enough among cheap ones to be worth thirty seconds.
|
||||
echo "Checking what was written..."
|
||||
if sudo cmp -n "$ISO_BYTES" "$ISO" "$DEV"; then
|
||||
echo "${G}Done.${N} $DEV holds $(basename "$ISO") and can be removed."
|
||||
else
|
||||
die "the device does not read back what was written -- the stick may be failing"
|
||||
fi
|
||||
|
||||
# The kernel re-reads the partition table so the new layout shows up here
|
||||
# too; harmless if it cannot.
|
||||
sudo partprobe "$DEV" 2>/dev/null || sudo blockdev --rereadpt "$DEV" 2>/dev/null || true
|
||||
Loading…
Reference in a new issue