# 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 )" } # 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