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.
149 lines
6 KiB
PowerShell
149 lines
6 KiB
PowerShell
# SchOS launcher (Windows side).
|
|
#
|
|
# Why this exists: without it you hand-type a QEMU command line, and a
|
|
# hand-typed one drifts -- notably onto the wrong disk as hdb, which boots
|
|
# straight into a PSOGM reading "no SchFS volume on the root device".
|
|
#
|
|
# The build runs natively rather than through WSL. WSL2 runs a real Linux
|
|
# kernel inside a Hyper-V VM, so it hard-requires virtualisation, and enabling
|
|
# that puts the Hyper-V hypervisor underneath the whole machine -- which makes
|
|
# VMware run nested on top of it and slows the box down permanently. Not a
|
|
# trade worth making for a build environment.
|
|
#
|
|
# Instead: clang/lld cross-compile the kernel to bare-metal ELF, mingw-w64
|
|
# builds the host tools, and Git Bash supplies make, perl, sed and dd. See the
|
|
# Toolchain section of the Makefile, and docs/building.md.
|
|
#
|
|
# .\run.ps1 build then boot, windowed
|
|
# .\run.ps1 -NoBuild boot whatever is already built
|
|
# .\run.ps1 -Headless no window; serial captured to build/serial.log
|
|
# .\run.ps1 -Gdb wait for a debugger on :26000 before executing
|
|
#
|
|
# There is no -SchFS switch. SchFS is the root filesystem now, not an extra
|
|
# volume to attach, so every boot is a SchFS boot.
|
|
|
|
param(
|
|
[switch]$NoBuild,
|
|
[switch]$Headless,
|
|
[switch]$Gdb,
|
|
[string]$Qemu
|
|
)
|
|
|
|
$ErrorActionPreference = 'Stop'
|
|
$root = $PSScriptRoot
|
|
|
|
if (-not $Qemu) {
|
|
$found = Get-Command 'qemu-system-x86_64.exe' -ErrorAction SilentlyContinue
|
|
if ($found) {
|
|
$Qemu = $found.Source
|
|
} else {
|
|
$Qemu = Join-Path $env:USERPROFILE 'scoop/apps/qemu/current/qemu-system-x86_64.exe'
|
|
}
|
|
}
|
|
if (-not (Test-Path $Qemu)) { throw "QEMU not found: $Qemu (install it, or pass -Qemu <path>)" }
|
|
|
|
# The two images QEMU needs. Keep in sync with the 'qemu' target in the
|
|
# Makefile.
|
|
#
|
|
# esp the staged EFI system partition: the loader and the kernel
|
|
# hdb the root filesystem, mounted as / by forkret()
|
|
#
|
|
# hdb was build/fs.img until SchFS became the root. Nothing builds fs.img on
|
|
# the default path any more, so the old default pointed at a file that no
|
|
# longer exists -- QEMU came up with no usable disk on device 1 and the kernel
|
|
# panicked with SCHFS_ROOT. That is the failure this script is supposed to
|
|
# prevent, so it is worth naming.
|
|
$esp = Join-Path $root 'build/esp'
|
|
$hdb = Join-Path $root 'build/schfs.img'
|
|
$code = Join-Path $root 'build/OVMF_CODE.fd'
|
|
$vars = Join-Path $root 'build/OVMF_VARS.fd'
|
|
|
|
if (-not $NoBuild) {
|
|
Write-Host '==> building (native, no WSL)' -ForegroundColor Cyan
|
|
|
|
# The three toolchain directories, prepended so the build sees them whether
|
|
# or not the current shell happened to inherit them: scoop's shims (make),
|
|
# mingw-w64 (host gcc), and LLVM (clang, ld.lld, llvm-objcopy/objdump).
|
|
$bashCandidates = @(
|
|
(Join-Path $env:ProgramFiles 'Git/bin/bash.exe')
|
|
(Join-Path ${env:ProgramFiles(x86)} 'Git/bin/bash.exe')
|
|
(Join-Path $env:USERPROFILE 'scoop/apps/git/current/bin/bash.exe')
|
|
(Join-Path $env:LOCALAPPDATA 'Programs/Git/bin/bash.exe')
|
|
)
|
|
$bash = $bashCandidates | Where-Object { $_ -and (Test-Path $_) } | Select-Object -First 1
|
|
if (-not $bash) { throw "Git Bash not found; looked in:`n $($bashCandidates -join "`n ")" }
|
|
|
|
$toolPath = @(
|
|
"$env:USERPROFILE\scoop\shims"
|
|
"$env:USERPROFILE\scoop\apps\mingw\current\bin"
|
|
"$env:ProgramFiles\LLVM\bin"
|
|
) -join ';'
|
|
$env:PATH = "$toolPath;$env:PATH"
|
|
|
|
# The repo's own path, not a hardcoded one. This used to say
|
|
# `cd /d/DEV/SchOS`, which survived the Schunix -> SchOS rename only by
|
|
# luck and would break the moment the tree is cloned anywhere else.
|
|
$posixRoot = ($root -replace '\\', '/') -replace '^([A-Za-z]):', '/$1'
|
|
$posixRoot = $posixRoot.Substring(0, 2).ToLower() + $posixRoot.Substring(2)
|
|
|
|
& $bash -lc "cd '$posixRoot' && make"
|
|
if ($LASTEXITCODE -ne 0) { throw "build failed (exit $LASTEXITCODE)" }
|
|
}
|
|
|
|
foreach ($img in @($hdb, (Join-Path $esp 'EFI/BOOT/BOOTX64.EFI'))) {
|
|
if (-not (Test-Path $img)) {
|
|
throw "missing: $img (run without -NoBuild, or: make)"
|
|
}
|
|
}
|
|
foreach ($f in @($code, $vars)) {
|
|
if (-not (Test-Path $f)) { throw "no $f -- run 'make qemu-uefi' once to fetch OVMF" }
|
|
}
|
|
$runVars = Join-Path $root 'build/run-OVMF_VARS.fd'
|
|
Copy-Item -Force $vars $runVars
|
|
|
|
# Guard against the exact failure this script exists to prevent: booting
|
|
# against a stale, empty or wrong-format root disk. The first uberblock sits in
|
|
# block 1 at SCHFS_BLKSIZE 4096, and starts with the magic "SchF" -- stored
|
|
# little-endian, so the bytes on disk read 46 68 63 53.
|
|
$fs = [System.IO.File]::OpenRead($hdb)
|
|
try {
|
|
$buf = New-Object byte[] 4
|
|
$null = $fs.Seek(4096, 'Begin')
|
|
$null = $fs.Read($buf, 0, 4)
|
|
} finally { $fs.Close() }
|
|
|
|
if ($buf[0] -ne 0x46 -or $buf[1] -ne 0x68 -or $buf[2] -ne 0x63 -or $buf[3] -ne 0x53) {
|
|
throw ("$hdb is not a SchFS volume (bad uberblock magic: " +
|
|
('{0:x2} {1:x2} {2:x2} {3:x2}' -f $buf[0], $buf[1], $buf[2], $buf[3]) +
|
|
").`n Rebuild it: rm -f build/schfs.img && make")
|
|
}
|
|
|
|
$qemuArgs = @(
|
|
'-drive', "if=pflash,format=raw,unit=0,readonly=on,file=$code"
|
|
'-drive', "if=pflash,format=raw,unit=1,file=$runVars"
|
|
'-drive', "file=fat:rw:$esp,index=0,media=disk"
|
|
'-drive', "file=$hdb,format=raw,index=1,media=disk"
|
|
'-m', '1G'
|
|
'-smp', '2'
|
|
'-cpu', 'qemu64,+rdtscp'
|
|
'-machine', 'pc-i440fx-9.2'
|
|
)
|
|
|
|
if ($Headless) {
|
|
$log = Join-Path $root 'build/serial.log'
|
|
$qemuArgs += @('-display', 'none', '-serial', "file:$log")
|
|
Write-Host "==> headless; serial -> $log" -ForegroundColor Cyan
|
|
} else {
|
|
# Both display devices, matching QEMU_DISPLAY in the Makefile: the
|
|
# bootloader draws its splash through VBE in 16-bit real mode and there is
|
|
# no virtio path that early, so stdvga carries the splash and the text
|
|
# console while virtio-gpu is what the display class binds.
|
|
$qemuArgs += @('-vga', 'std', '-device', 'virtio-gpu-pci', '-serial', 'stdio')
|
|
}
|
|
|
|
if ($Gdb) {
|
|
$qemuArgs += @('-gdb', 'tcp::26000', '-S')
|
|
Write-Host '==> paused for gdb on localhost:26000' -ForegroundColor Yellow
|
|
}
|
|
|
|
& $Qemu @qemuArgs
|