SchOS/run.ps1
Nexoniarz 66340e720e Rewrite run.ps1; it was five kinds of stale
run.ps1 is a .ps1 that does not live under tools/, so every sweep that
fixed the other launchers went straight past it:

  - hdb defaulted to build/fs.img, which nothing builds since SchFS
    became the root, so it pointed at a file that does not exist. QEMU
    came up with no usable disk on device 1 and the kernel panicked with
    SCHFS_ROOT -- "no SchFS volume on the root device". That is exactly
    the failure this script exists to prevent.
  - a -SchFS switch built around the old arrangement, where SchFS was a
    secondary volume and needed `make ramroot` to free device 1. There
    is no such arrangement now; every boot is a SchFS boot.
  - `cd /d/DEV/SchOS` hardcoded into the build invocation. It survived
    the Schunix -> SchOS rename by luck and would break on any clone.
    Derived from $PSScriptRoot now.
  - a hardcoded scoop QEMU path, and build\ backslash literals.
  - only -vga std, where the Makefile also attaches virtio-gpu-pci.

The magic check is SchFS-only now and prints the bytes it actually read
when it fails.

SchFS itself is fine: it mounts on 1, 2 and 4 CPUs, and all seven
bootcheck suites pass.
2026-08-09 14:49:47 +02:00

140 lines
5.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.
#
# hda the boot disk: bootblock, stage 2 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.
$hda = Join-Path $root 'schos.img'
$hdb = Join-Path $root 'build/schfs.img'
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 @($hda, $hdb)) {
if (-not (Test-Path $img)) {
throw "missing image: $img (run without -NoBuild, or: make)"
}
}
# 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', "file=$hda,format=raw,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