gfx: the console's typeface is replaceable, and the loader is what replaces it
Gate 0's last-but-one open item. Drop a TrueType file at \schos\console.ttf on
the ESP and the console is drawn with it; with no file, the face linked into
the kernel image, which is what an early panic needs and is not a degraded
mode.
**The loader reads it, and that is the decision worth writing down.** The
kernel opening the file once the root filesystem is mounted is the obvious
design. It is also wrong: the console has been drawing since the kernel's
first line, so a font arriving later changes the cell size, which changes the
column count, which re-lays out every terminal -- and blanks them. The kernel
log would vanish off the screen at the moment the root filesystem mounted,
which is exactly the log a machine being brought up for the first time is
being read for. Since the next thing on the list is real hardware, losing that
was not a trade worth making.
So the choice happens before anything is drawn. Three details make it cheap:
- **EfiReservedMemoryType**, not EfiLoaderData. The E820 handoff turns
loader data into usable memory, on the argument that the loader's own
pages are the kernel's the moment it runs -- true of the loader, not true
of a buffer it wants the kernel to keep reading from.
- **Below 4 GB**, because the kernel reaches this through its direct map and
that is how far the direct map goes. A font above it would need an ioremap
the console cannot do before there is a console.
- **Used in place.** No copy, which matters because the kernel has no
contiguous allocator to copy 112 KB into -- the only one is the DMA arena,
and that belongs to devices.
Sized without EFI_FILE_INFO and its GUID: seek past the end and ask where that
landed, which the specification requires to clamp.
Every failure falls back and keeps going -- no file, implausible size, no
memory, short read, unparseable file. The last says so in the log, because a
font that silently did not load looks exactly like a font that did.
Verified by booting with JetBrainsMono-ExtraBold.ttf on the ESP and
screendumping the console: visibly the bold face, against the Regular of a
boot with no file. Three boots each way, no traps and no panics, socktest and
memfdtest green in all six.
Nothing ships a console.ttf: the file would be the file already in the kernel,
and two copies of a font are two things that can disagree.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
df304918f8
commit
d85274dbd9
5 changed files with 215 additions and 5 deletions
|
|
@ -264,6 +264,43 @@ So the text layer fixes three things:
|
|||
changing — glyphs carry 8-bit coverage, so an anti-aliased outline renderer
|
||||
already has somewhere to put its output.
|
||||
|
||||
### The font is replaceable
|
||||
|
||||
The console draws with the TrueType face linked into the kernel image, and
|
||||
will use a different one if the boot volume offers it: `\schos\console.ttf`,
|
||||
or `\console.ttf`, on the ESP.
|
||||
|
||||
```sh
|
||||
cp ports/JetBrains_Mono/static/JetBrainsMono-ExtraBold.ttf \
|
||||
build/esp/schos/console.ttf
|
||||
```
|
||||
|
||||
Nothing ships one, because the file that would be shipped is the file already
|
||||
in the kernel, and two copies of a font are two things that can disagree. What
|
||||
this buys is that the typeface is a property of the *installation* rather than
|
||||
of the build.
|
||||
|
||||
**The loader reads it.** The kernel could open the file itself once the root
|
||||
filesystem is mounted, and that is the obvious design and the wrong one: the
|
||||
console has been drawing since the kernel's first line, so a font that arrives
|
||||
later changes the cell size, which changes the number of columns, which
|
||||
re-lays out every terminal — and blanks them. The kernel log would disappear
|
||||
off the screen at the moment the root filesystem mounted, which is precisely
|
||||
the log a machine being brought up for the first time is being read for.
|
||||
|
||||
So the choice is made before anything is drawn. The loader allocates the pages
|
||||
as `EfiReservedMemoryType` — not `EfiLoaderData`, which the E820 handoff turns
|
||||
into usable memory on the argument that the loader's own pages belong to the
|
||||
kernel the moment it runs; true of the loader, not true of this — and hands
|
||||
over a physical address and a length in `SCHBOOT_FONT_ADDR`. The kernel reads
|
||||
the file where it lies. There is no copy, and no need for the contiguous
|
||||
allocator the kernel does not have.
|
||||
|
||||
Every failure falls back to the built-in font and keeps going: no file, an
|
||||
implausible size, no memory, a short read, or a file that will not parse. The
|
||||
last of those says so in the log, because a font that silently did not load
|
||||
looks exactly like a font that did.
|
||||
|
||||
### The TrueType rasteriser
|
||||
|
||||
`kernel/dev/ttf.c`: `glyf` outlines, composite glyphs with their component
|
||||
|
|
|
|||
|
|
@ -256,9 +256,21 @@ These are the galling ones — the replacement exists, is tested, and is unused.
|
|||
open. The console is a fixed grid of codepoints; `text.c`'s proportional
|
||||
path has no caller. Nothing needs this before the compositor, which is
|
||||
why it is separated from the row above rather than left blocking it.
|
||||
- [ ] **Load a font from disk** and point the console at it. The built-in one
|
||||
is linked in on purpose — an early panic has to be readable — so this is
|
||||
about a *replaceable* font, not about having one.
|
||||
- [x] ~~**Load a font from disk**~~ and point the console at it. The built-in
|
||||
one is linked in on purpose — an early panic has to be readable — so this
|
||||
is about a *replaceable* font, not about having one. Drop a TrueType file
|
||||
at `\schos\console.ttf` on the ESP and the console is drawn with it.
|
||||
|
||||
**The loader reads it, not the kernel**, and that is the whole design
|
||||
decision. The kernel could open the file once the root filesystem is
|
||||
mounted — but the console has been drawing since long before that, so
|
||||
switching then means re-measuring the cell and re-laying out every
|
||||
terminal mid-boot, which wipes the kernel log off the screen at exactly
|
||||
the moment a machine being brought up for the first time needs it. The
|
||||
loader allocates the pages as `EfiReservedMemoryType`, so the E820
|
||||
handoff marks them reserved, the page allocator never offers them, and
|
||||
the kernel uses the file *in place* — no copy, and no need for a
|
||||
contiguous allocator the kernel does not have.
|
||||
- [x] ~~**Interrupt-driven receive.**~~ The e1000's handler calls
|
||||
`netdev_rx_signal()`, which ends the network thread's wait early; the frame
|
||||
is still *pulled* by the thread, because an interrupt handler can neither
|
||||
|
|
|
|||
Loading…
Reference in a new issue