Nexoniarz is the sole author and SchOS was never published under Apache-2.0, so this is a straight relicence. LICENSE is the FSF GPLv2 text, every first-party SPDX header moves to GPL-2.0-or-later, and NOTICE, README.md, docs/licensing.md and the site say so. projects/schfs-linux follows the rest of the tree. The GPL gives copyleft: a fork has to ship its source. It also lifts the old "Linux drivers, never" rule, since GPL-2.0-only code combines with GPL-2.0-or-later. "Or later" leaves room for GPLv3, but one binary still has to pick one version, so docs/licensing.md now keeps Apache-2.0 and GPLv3 code out of the kernel to keep Linux code possible there. BSD 4-clause code goes from "avoid" to "no", because the advertising clause is GPL-incompatible. LDLS still ships no .ko. That boundary is now project policy rather than a legal requirement, and ldls.md, devices.md and licensing.md section 6 say so. refmod declares license=GPL, and ldls-check passes (27 checks). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
93 lines
4.4 KiB
Markdown
93 lines
4.4 KiB
Markdown
<!-- SPDX-License-Identifier: GPL-2.0-or-later -->
|
|
# Booting SchOS
|
|
|
|
One firmware path:
|
|
|
|
```
|
|
boot/
|
|
uefi/ the loader: a 64-bit PE application
|
|
common/ the user interface, shared with nothing else now
|
|
```
|
|
|
|
There were two. `boot/bios/` held an MBR, an El Torito CD stub and a real-mode
|
|
stage 2, and it was a separate directory rather than `#ifdef`s because the two
|
|
shared almost nothing: a BIOS loader lives in 16-bit real mode and talks to the
|
|
firmware through software interrupts, while UEFI hands a loader long mode with
|
|
paging already on and a table of function pointers to call.
|
|
|
|
That asymmetry is what removed it. Everything since roughly 2012 boots UEFI,
|
|
CSM — the firmware-side BIOS emulation that let legacy loaders run at all — has
|
|
been going away since Intel dropped support for it in 2020, and the cost of the
|
|
second path was not the code but the tax on everything that touched it: stage 2
|
|
had a 65-sector ceiling that it silently overflowed, and the resolution menu
|
|
could not be shared because VBE mode setting is `INT 10h` and needs real mode.
|
|
|
|
The contract below survives it. It was written to let a second loader exist, and
|
|
it is worth keeping in that shape: it is what the kernel expects, stated once,
|
|
rather than a description of whoever happens to be calling.
|
|
|
|
## What the loader must hand the kernel
|
|
|
|
Whichever path runs, the kernel expects the same things. This is the contract;
|
|
a second loader is correct exactly when it satisfies it.
|
|
|
|
| | Where | Notes |
|
|
|---|---|---|
|
|
| Kernel image | loaded per its ELF program headers | entry at `entry64` |
|
|
| CPU state | see below -- the two paths differ here | |
|
|
| Memory map | `E820_MAP_ADDR`, 24-byte entries, count at `E820_COUNT_ADDR` | `kernel/arch/x86_64/e820.c` reads it |
|
|
| Framebuffer | `VBE_INFO_ADDR` | address, pitch, width, height, depth |
|
|
| Verbose flag | `BOOT_VERBOSE_FLAG_ADDR` | set by the F8 menu |
|
|
|
|
The addresses are defined once, in `include/memlayout.h` and `include/schboot.h`.
|
|
The kernel does not care which firmware produced them.
|
|
|
|
## The entry point
|
|
|
|
UEFI hands a loader long mode with paging already on. Dropping back to
|
|
protected mode only to climb out again is a mode switch whose failure mode is a
|
|
triple fault with no console, so the kernel publishes an entry that takes the
|
|
machine as it already is.
|
|
`kernel/arch/x86_64/entry.S` carries a `sch64_header` — magic `SCH64BOO`,
|
|
8-byte aligned within the first 16 KB, followed by a 64-bit physical address —
|
|
found by scanning. `include/schboot.h` holds the magic so loader and kernel
|
|
cannot disagree.
|
|
|
|
The 32-bit multiboot entry is still in `entry.S` and now has no caller: the
|
|
BIOS loader was the only thing that used it. It is left in place rather than
|
|
removed in the same change that removed the loader, because deleting kernel
|
|
entry code is its own piece of work with its own way of failing silently.
|
|
|
|
What that entry expects:
|
|
|
|
- long mode, interrupts off
|
|
- CR3 pointing at tables that identity-map the low 4 GB **and** map the same
|
|
4 GB at `0xffff800000000000`. `entry64low`'s first instruction is a `movabs`
|
|
to a high address, so identity alone triple faults immediately.
|
|
- RBX = the framebuffer physical address, and never 1 — that value marks an
|
|
application processor in `entry64high`.
|
|
|
|
The kernel loads its own GDT at that entry (`entry64uefi`), rather than the
|
|
loader doing it. A loader arriving in long mode is still on the firmware's
|
|
descriptors, and the first thing the kernel does that depends on its own
|
|
selectors takes a `#GP` against a GDT the firmware no longer owns — it
|
|
surfaced as `unexpected trap 13 ... cs=38`, a long way from the cause.
|
|
|
|
`e820.c` reads a *BIOS* memory map. UEFI hands out a different structure
|
|
(`EFI_MEMORY_DESCRIPTOR`, from `GetMemoryMap`), so the UEFI loader has to
|
|
translate it into the same 24-byte entries rather than inventing a second
|
|
format for the kernel to learn.
|
|
|
|
## What booting looks like now
|
|
|
|
`make esp` stages `EFI/BOOT/BOOTX64.EFI` and `schos/kernel`, and QEMU is
|
|
pointed at OVMF with that directory as a virtual FAT volume — `make qemu`.
|
|
`make iso` builds a disc with a single El Torito entry pointing at a FAT image
|
|
holding the same two files.
|
|
|
|
Neither is an installable disk. A real GPT image with a real FAT partition is
|
|
the installer's job, and until it exists there is nothing here that writes to a
|
|
disk a machine could boot from unaided.
|
|
|
|
Secure Boot is not addressed either: the loader is unsigned, so a machine
|
|
enforcing it will refuse to run this.
|