SchOS/tools/dispshot.ps1
Nexoniarz cda5908660 Build and tools work on Linux, macOS and BSD as well as Windows
Developed from both Windows and Linux, so nothing in the build should
care which host it runs on. It did, in four places.

Toolchain: one path instead of two. clang is a cross-compiler by
construction, so a single LLVM install targets bare-metal ELF, 16/32-bit
code below 1 MB, and Linux/musl userland from any host -- which is how
the Windows build already worked. The GNU branch beside it had drifted
into passing --no-warn-rwx-segments, a hard error on lld, which is the
default linker on both FreeBSD and macOS; and it required a brewed
x86_64-elf-gcc before anything built on a Mac at all. GNU is kept as a
fallback (USE_GNU=1) but clang+lld is chosen whenever both are present.
Host tools use the host's own cc; only mingw needs naming explicitly.

tools/mkschfs.c included <sys/sysmacros.h> unconditionally on non-mingw
hosts. That header is glibc/musl only -- macOS and the BSDs have neither
the header nor a need for it, since major()/minor() come from
<sys/types.h> there. Now guarded on __linux__.

Ports verified tarballs with `sha256sum -c -`, which does not exist on
macOS (shasum), FreeBSD/NetBSD (sha256), or OpenBSD (cksum -a sha256),
and each spells its check mode differently. tools/sha256check.sh picks
whichever of the five the host has and compares digests itself.

nproc is coreutils, so absent on macOS and BSD; falls through to sysctl
-n hw.ncpu before defaulting to 4.

The PowerShell tools now run under pwsh on any OS: QEMU is found on PATH
(the scoop location is a Windows-only fallback), and path literals use
forward slashes -- Join-Path $root 'build\x' produces a *filename
containing a backslash* on a Unix host. The backslash sweep is limited
to known directory prefixes on purpose: bootcheck sends
printf 'caf\303\251' to the guest to test UTF-8, and a blanket
substitution would have quietly changed what that asserts.

Also fixes six tools left booting build/fs.img, which stopped being
bootable when SchFS became the root filesystem: crashshot, dispshot,
fbshot, splashshot, textshot and ttyshot all take build/schfs.img now,
and the -SchFS switches that used to select it are gone.

Verified on Windows: full build, and bootcheck green. The Linux, macOS
and BSD paths are written from their documented behaviour and are NOT
executed here -- this machine has no WSL and no second host.
2026-08-09 14:24:53 +02:00

141 lines
5.4 KiB
PowerShell

# Boot SchOS, run the display self-test, and capture the result as an image.
#
# The display class cannot be checked without a display: the assertion is "did
# the right pixels reach the screen", and the only instrument for that is a
# screendump. This drives it through `echo d > /proc/sysrq` and takes a picture.
#
# What the pattern proves: four solid quadrants in theme colours (a stride error
# would slant the bands, a bpp error would change the colours), a white frame one
# pixel inside the edge (an off-by-one in the present rectangle would clip it),
# and the fact that every pixel was drawn into RAM before a single byte reached
# the framebuffer -- which is what double buffering means.
#
# QEMU's monitor is on its own socket so the serial console stays free for the
# shell. screendump writes a PPM; the PNG conversion is optional and skipped if
# ImageMagick is not around.
#
# tools/crashshot.ps1 crash the ramdisk-root build
param(
[int]$BootSeconds = 20,
[string]$Out = "build/dispshot.ppm",
[string]$Qemu = (Find-Qemu)
)
# QEMU, on whichever host this is.
#
# PowerShell 7 runs on Linux, macOS and the BSDs as well as Windows, so these
# scripts are usable from all of them -- but the old default was a hardcoded
# scoop path under $env:USERPROFILE, which exists on exactly one of those. PATH
# first, because a package-managed qemu is on it everywhere; the scoop location
# is only a fallback for the Windows install that does not add itself to PATH.
function Find-Qemu {
$exe = if ($IsWindows -or $env:OS -eq 'Windows_NT') { 'qemu-system-x86_64.exe' }
else { 'qemu-system-x86_64' }
$onPath = Get-Command $exe -ErrorAction SilentlyContinue
if ($onPath) { return $onPath.Source }
if ($env:USERPROFILE) {
$scoop = Join-Path (Join-Path $env:USERPROFILE 'scoop/apps/qemu/current') $exe
if (Test-Path $scoop) { return $scoop }
}
foreach ($p in @('/usr/bin', '/usr/local/bin', '/opt/homebrew/bin')) {
$c = Join-Path $p $exe
if (Test-Path $c) { return $c }
}
throw "qemu-system-x86_64 not found on PATH. Install QEMU, or pass -Qemu <path>."
}
$ErrorActionPreference = 'Stop'
$root = Split-Path $PSScriptRoot -Parent
$hda = Join-Path $root 'schos.img'
# The root filesystem. SchFS is the only root there is: the kernel mounts
# device 1 in forkret() and panics if it is not a SchFS volume, so this is
# not a choice and there is no -SchFS switch any more. It used to default to
# build/fs.img, which stopped being bootable the moment SchFS became root.
$hdb = Join-Path $root 'build/schfs.img'
$log = Join-Path $root 'build/crashshot.log'
$ppm = Join-Path $root $Out
$mon = Join-Path $root 'build/qemu-mon.sock'
Remove-Item -Force $log, $ppm -ErrorAction SilentlyContinue
# The monitor goes to a TCP port rather than a unix socket: PowerShell can open
# a TCP client without any extra machinery, and QEMU on Windows has no unix
# sockets anyway.
$monPort = 55555
$psi = New-Object System.Diagnostics.ProcessStartInfo
$psi.FileName = $Qemu
$psi.Arguments = @(
"-drive file=`"$hda`",format=raw,index=0,media=disk"
"-drive file=`"$hdb`",format=raw,index=1,media=disk"
'-m 1G -smp 1 -cpu qemu64,+rdtscp -machine pc-i440fx-9.2'
'-display none'
"-chardev stdio,id=c0,logfile=`"$log`""
'-serial chardev:c0'
"-monitor tcp:127.0.0.1:$monPort,server,nowait"
) -join ' '
$psi.RedirectStandardInput = $true
$psi.UseShellExecute = $false
Write-Host "==> booting" -ForegroundColor Cyan
$p = [System.Diagnostics.Process]::Start($psi)
function Send-Line($text) {
foreach ($ch in ($text + "`n").ToCharArray()) {
$p.StandardInput.Write($ch)
$p.StandardInput.Flush()
Start-Sleep -Milliseconds 30 # serial has no flow control
}
}
try {
Start-Sleep -Seconds $BootSeconds
# Show the device list first, so the transcript records what was bound.
Write-Host " > listing devices" -ForegroundColor DarkGray
Send-Line 'cat /proc/devices'
Start-Sleep -Seconds 3
Write-Host " > echo d > /proc/sysrq" -ForegroundColor DarkGray
Send-Line 'echo d > /proc/sysrq'
Start-Sleep -Seconds 4
Write-Host "==> capturing the framebuffer" -ForegroundColor Cyan
$client = New-Object System.Net.Sockets.TcpClient('127.0.0.1', $monPort)
$stream = $client.GetStream()
$writer = New-Object System.IO.StreamWriter($stream)
$writer.AutoFlush = $true
Start-Sleep -Milliseconds 500
# QEMU on Windows wants a native path here, not the one PowerShell shows.
$writer.WriteLine("screendump $ppm")
Start-Sleep -Seconds 2
$writer.WriteLine('quit')
Start-Sleep -Milliseconds 500
$client.Close()
} finally {
if (-not $p.HasExited) { $p.Kill() }
Start-Sleep -Milliseconds 800
}
if (Test-Path $log) {
$text = Get-Content $log -Raw
Write-Host "==> serial transcript" -ForegroundColor Cyan
# Only the crash report, not the whole boot.
$idx = $text.IndexOf('display: presented')
if ($idx -ge 0) { Write-Host $text.Substring($idx) } else { Write-Host $text }
}
if (Test-Path $ppm) {
$size = (Get-Item $ppm).Length
Write-Host "==> $ppm ($size bytes)" -ForegroundColor Green
$png = [System.IO.Path]::ChangeExtension($ppm, '.png')
$magick = Get-Command magick -ErrorAction SilentlyContinue
if ($magick) {
& magick $ppm $png
Write-Host "==> $png" -ForegroundColor Green
}
} else {
Write-Host "FAIL: no screendump was produced" -ForegroundColor Red
exit 1
}