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.
This commit is contained in:
parent
c84fc223a0
commit
66340e720e
2 changed files with 106 additions and 46 deletions
|
|
@ -56,3 +56,36 @@ not a theoretical one.
|
||||||
written from documented behaviour and have **never been executed** — this
|
written from documented behaviour and have **never been executed** — this
|
||||||
machine has no WSL and no second host. Windows is the only host where the build
|
machine has no WSL and no second host. Windows is the only host where the build
|
||||||
and `tools/bootcheck.ps1` have actually been run green.
|
and `tools/bootcheck.ps1` have actually been run green.
|
||||||
|
|
||||||
|
|
||||||
|
## run.ps1 is at the repo root, so tool sweeps miss it
|
||||||
|
|
||||||
|
It is a `.ps1` but it does **not** live under `tools/`, and it went stale in
|
||||||
|
five ways at once while every tool beside it was fixed: hdb defaulting to
|
||||||
|
`build/fs.img` (which nothing builds any more, so the file simply does not
|
||||||
|
exist), a `-SchFS` switch built around the old arrangement where SchFS was a
|
||||||
|
secondary volume, a hardcoded QEMU path, `build\` backslash literals, and
|
||||||
|
`cd /d/DEV/SchOS` hardcoded into the build invocation -- which survived the
|
||||||
|
Schunix -> SchOS rename by luck and would break on any clone. Rewritten
|
||||||
|
2026-08-09. **When sweeping the tools, check the repo root too.**
|
||||||
|
|
||||||
|
## The stale-image trap bit again, and it looks exactly like a filesystem bug
|
||||||
|
|
||||||
|
A leftover headless QEMU holds `schos.img` and `build/schfs.img` open. On
|
||||||
|
Windows that makes `rm` fail with *Device or resource busy*, so `make` stops
|
||||||
|
**after** the kernel has relinked -- leaving a stale `schos.img` that boots the
|
||||||
|
previous kernel. The symptom this time was a PSOGM reading
|
||||||
|
`schfs root: no SchFS volume on the root device`, which reads as SchFS being
|
||||||
|
broken and is not: SchFS mounts cleanly on 1, 2 and 4 CPUs.
|
||||||
|
|
||||||
|
`kill %1` from Git Bash does not reliably kill a Windows QEMU. Use:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
pwsh -NoProfile -Command "Get-Process qemu-system-x86_64 -ErrorAction SilentlyContinue | Stop-Process -Force"
|
||||||
|
```
|
||||||
|
|
||||||
|
A tell: `ls -l schos.img` showing anything other than **5,120,000 bytes**
|
||||||
|
(`dd count=10000`) means the image was not rewritten and is stale.
|
||||||
|
|
||||||
|
**Rule: before believing a boot failure, confirm no QEMU is running and that
|
||||||
|
schos.img was written by the build you think it was.**
|
||||||
|
|
|
||||||
117
run.ps1
117
run.ps1
|
|
@ -1,43 +1,60 @@
|
||||||
# SchOS launcher (Windows side).
|
# SchOS launcher (Windows side).
|
||||||
#
|
#
|
||||||
# Why this exists: without it you have to hand-type a QEMU command line, and a
|
# Why this exists: without it you hand-type a QEMU command line, and a
|
||||||
# hand-typed one drifts -- notably onto a stale fs.img, which boots to
|
# hand-typed one drifts -- notably onto the wrong disk as hdb, which boots
|
||||||
# "fsinit: invalid file system" because the wrong disk is attached as hdb.
|
# straight into a PSOGM reading "no SchFS volume on the root device".
|
||||||
#
|
#
|
||||||
# The build no longer goes through WSL. WSL2 runs a real Linux kernel inside a
|
# The build runs natively rather than through WSL. WSL2 runs a real Linux
|
||||||
# Hyper-V VM, so it hard-requires virtualisation, and enabling that puts the
|
# kernel inside a Hyper-V VM, so it hard-requires virtualisation, and enabling
|
||||||
# Hyper-V hypervisor underneath the whole machine -- which makes VMware run
|
# that puts the Hyper-V hypervisor underneath the whole machine -- which makes
|
||||||
# nested on top of it and slows the box down permanently. Not a trade worth
|
# VMware run nested on top of it and slows the box down permanently. Not a
|
||||||
# making for a build environment.
|
# trade worth making for a build environment.
|
||||||
#
|
#
|
||||||
# Instead it builds natively: clang/lld cross-compile the kernel to bare-metal
|
# Instead: clang/lld cross-compile the kernel to bare-metal ELF, mingw-w64
|
||||||
# ELF, mingw-w64 builds the host tools, and Git Bash supplies make, perl, sed
|
# builds the host tools, and Git Bash supplies make, perl, sed and dd. See the
|
||||||
# and dd. See the Toolchain section of the Makefile.
|
# Toolchain section of the Makefile, and docs/building.md.
|
||||||
#
|
#
|
||||||
# .\run.ps1 build (native) then boot, windowed
|
# .\run.ps1 build then boot, windowed
|
||||||
# .\run.ps1 -NoBuild boot whatever is already built
|
# .\run.ps1 -NoBuild boot whatever is already built
|
||||||
# .\run.ps1 -Headless no window; serial captured to build\serial.log
|
# .\run.ps1 -Headless no window; serial captured to build/serial.log
|
||||||
# .\run.ps1 -Gdb wait for a debugger on :26000 before executing
|
# .\run.ps1 -Gdb wait for a debugger on :26000 before executing
|
||||||
|
#
|
||||||
# .\run.ps1 -SchFS boot the ramdisk-root kernel with a SchFS volume
|
# There is no -SchFS switch. SchFS is the root filesystem now, not an extra
|
||||||
# attached as hdb; then run cat /proc/schfs
|
# volume to attach, so every boot is a SchFS boot.
|
||||||
|
|
||||||
param(
|
param(
|
||||||
[switch]$NoBuild,
|
[switch]$NoBuild,
|
||||||
[switch]$Headless,
|
[switch]$Headless,
|
||||||
[switch]$Gdb,
|
[switch]$Gdb,
|
||||||
[switch]$SchFS,
|
[string]$Qemu
|
||||||
[string]$Qemu = "$env:USERPROFILE\scoop\apps\qemu\current\qemu-system-x86_64.exe"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
$ErrorActionPreference = 'Stop'
|
$ErrorActionPreference = 'Stop'
|
||||||
$root = $PSScriptRoot
|
$root = $PSScriptRoot
|
||||||
|
|
||||||
# The two images QEMU needs. These paths are the single source of truth --
|
if (-not $Qemu) {
|
||||||
# keep them in sync with the 'qemu' and 'qemu-schfs' targets in the Makefile.
|
$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'
|
$hda = Join-Path $root 'schos.img'
|
||||||
$hdb = if ($SchFS) { Join-Path $root 'build\schfs.img' }
|
$hdb = Join-Path $root 'build/schfs.img'
|
||||||
else { Join-Path $root 'build\fs.img' }
|
|
||||||
|
|
||||||
if (-not $NoBuild) {
|
if (-not $NoBuild) {
|
||||||
Write-Host '==> building (native, no WSL)' -ForegroundColor Cyan
|
Write-Host '==> building (native, no WSL)' -ForegroundColor Cyan
|
||||||
|
|
@ -45,8 +62,15 @@ if (-not $NoBuild) {
|
||||||
# The three toolchain directories, prepended so the build sees them whether
|
# The three toolchain directories, prepended so the build sees them whether
|
||||||
# or not the current shell happened to inherit them: scoop's shims (make),
|
# 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).
|
# mingw-w64 (host gcc), and LLVM (clang, ld.lld, llvm-objcopy/objdump).
|
||||||
$bash = Join-Path $env:ProgramFiles 'Git\bin\bash.exe'
|
$bashCandidates = @(
|
||||||
if (-not (Test-Path $bash)) { throw "Git Bash not found at $bash" }
|
(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 = @(
|
$toolPath = @(
|
||||||
"$env:USERPROFILE\scoop\shims"
|
"$env:USERPROFILE\scoop\shims"
|
||||||
"$env:USERPROFILE\scoop\apps\mingw\current\bin"
|
"$env:USERPROFILE\scoop\apps\mingw\current\bin"
|
||||||
|
|
@ -54,38 +78,37 @@ if (-not $NoBuild) {
|
||||||
) -join ';'
|
) -join ';'
|
||||||
$env:PATH = "$toolPath;$env:PATH"
|
$env:PATH = "$toolPath;$env:PATH"
|
||||||
|
|
||||||
# -SchFS needs the ramdisk-root kernel: the root filesystem then comes out
|
# The repo's own path, not a hardcoded one. This used to say
|
||||||
# of memory, which leaves device 1 free for the SchFS volume. Booting the
|
# `cd /d/DEV/SchOS`, which survived the Schunix -> SchOS rename only by
|
||||||
# normal kernel with a SchFS disk as hdb would leave it with no root.
|
# luck and would break the moment the tree is cloned anywhere else.
|
||||||
$target = if ($SchFS) { 'make ramroot && make build/schfs.img' } else { 'make' }
|
$posixRoot = ($root -replace '\\', '/') -replace '^([A-Za-z]):', '/$1'
|
||||||
& $bash -lc "cd /d/DEV/SchOS && $target"
|
$posixRoot = $posixRoot.Substring(0, 2).ToLower() + $posixRoot.Substring(2)
|
||||||
|
|
||||||
|
& $bash -lc "cd '$posixRoot' && make"
|
||||||
if ($LASTEXITCODE -ne 0) { throw "build failed (exit $LASTEXITCODE)" }
|
if ($LASTEXITCODE -ne 0) { throw "build failed (exit $LASTEXITCODE)" }
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach ($img in @($hda, $hdb)) {
|
foreach ($img in @($hda, $hdb)) {
|
||||||
if (-not (Test-Path $img)) { throw "missing image: $img (run without -NoBuild)" }
|
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
|
# Guard against the exact failure this script exists to prevent: booting
|
||||||
# a stale or empty hdb. Each format has its own magic in its own place.
|
# 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)
|
$fs = [System.IO.File]::OpenRead($hdb)
|
||||||
try {
|
try {
|
||||||
$buf = New-Object byte[] 4
|
$buf = New-Object byte[] 4
|
||||||
# v1: superblock in block 1 with BSIZE 1024, FSMAGIC 0x10203040.
|
$null = $fs.Seek(4096, 'Begin')
|
||||||
# SchFS: first uberblock in block 1 with SCHFS_BLKSIZE 4096, magic "SchF".
|
|
||||||
$null = $fs.Seek($(if ($SchFS) { 4096 } else { 1024 }), 'Begin')
|
|
||||||
$null = $fs.Read($buf, 0, 4)
|
$null = $fs.Read($buf, 0, 4)
|
||||||
} finally { $fs.Close() }
|
} finally { $fs.Close() }
|
||||||
|
|
||||||
if ($SchFS) {
|
|
||||||
# 0x53636846 little-endian on disk: 46 68 63 53 ("FhcS" byte order).
|
|
||||||
if ($buf[0] -ne 0x46 -or $buf[1] -ne 0x68 -or $buf[2] -ne 0x63 -or $buf[3] -ne 0x53) {
|
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 valid SchFS image (bad uberblock magic). Rebuild it: make build/schfs.img"
|
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]) +
|
||||||
} else {
|
").`n Rebuild it: rm -f build/schfs.img && make")
|
||||||
if ($buf[0] -ne 0x40 -or $buf[1] -ne 0x30 -or $buf[2] -ne 0x20 -or $buf[3] -ne 0x10) {
|
|
||||||
throw "$hdb is not a valid SCHFS image (bad superblock magic). Rebuild it: rm -f build/fs.img && make"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$qemuArgs = @(
|
$qemuArgs = @(
|
||||||
|
|
@ -98,11 +121,15 @@ $qemuArgs = @(
|
||||||
)
|
)
|
||||||
|
|
||||||
if ($Headless) {
|
if ($Headless) {
|
||||||
$log = Join-Path $root 'build\serial.log'
|
$log = Join-Path $root 'build/serial.log'
|
||||||
$qemuArgs += @('-display', 'none', '-serial', "file:$log")
|
$qemuArgs += @('-display', 'none', '-serial', "file:$log")
|
||||||
Write-Host "==> headless; serial -> $log" -ForegroundColor Cyan
|
Write-Host "==> headless; serial -> $log" -ForegroundColor Cyan
|
||||||
} else {
|
} else {
|
||||||
$qemuArgs += @('-vga', 'std', '-serial', 'stdio')
|
# 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) {
|
if ($Gdb) {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue