Sixty-five headers sat side by side in include/, so `#include "irq.h"` said nothing about where irq.h was or what it belonged to, and errno.h, stat.h and fcntl.h at the top shadowed the system's for any host tool built with -I./include. They are in folders now -- arch/ core/ fs/ dev/ gfx/ net/ uapi/ (and ldls/ for the Linux driver layer) -- and every include names the folder, as Linux's do: "core/proc.h". Done mechanically and checked the hard way: the kernel built from this tree disassembles identically to the one built from the tree before it. The full image boots, and userland is the same set of programs. Paths in docs and comments follow; docs/guides/layout.md has a table of what is where, and tools/fix-includes.py rewrites old-style includes from the folders in the tree, for code written before this. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01S1giNvrHTrPMPw7V6x1kQ4
71 lines
4 KiB
Markdown
71 lines
4 KiB
Markdown
---
|
||
name: schunix-qr-encoder
|
||
description: "kernel/core/qr.c — the crash-report QR encoder, how it's verified, and the segno padding bug that is NOT ours."
|
||
metadata:
|
||
node_type: memory
|
||
type: project
|
||
originSessionId: 9d8a2aef-bcf0-47a0-96c6-f706efab4a95
|
||
modified: 2026-08-08T16:22:01.468Z
|
||
---
|
||
|
||
Built 2026-08-08. `kernel/core/qr.c` + `include/gfx/qr.h`, the Phase 8 crash channel
|
||
(see [[schunix-crash-screen]] and `docs/plan/roadmap.md`).
|
||
|
||
**Wired into the crash screen the same day.** `panic.c` builds a compact payload
|
||
and paints a white card on the right of PSOGM. Verified end-to-end with
|
||
`make crashshot && make crashqr`, which decodes the symbol out of the captured
|
||
framebuffer — not out of the encoder. Payload format `SXPSOGM1`: magic, reason,
|
||
`c<cpu> p<pid> t<ticks> <name>`, `b<kernel base>`, then call-chain frames as
|
||
**offsets from the kernel base** (8 hex digits, not 16 — that is what makes a
|
||
useful number of frames fit in 134 bytes). The reason is written against a
|
||
buffer end held back by 34 bytes, so a long message costs frames but never all
|
||
of them. Deliberately not a URL: it encodes the report itself and reaches
|
||
nothing.
|
||
|
||
**Scope, deliberately narrow:** byte mode only, EC level L only, versions 1–6
|
||
only (max 134 bytes). Version 7 is where QR starts needing an 18-bit
|
||
version-information block in two extra places; version 10 is where the character
|
||
count grows to 16 bits and the alignment grid becomes 2-D. Stopping at 6 avoids
|
||
both. If a payload ever needs more, shorten the report rather than add the
|
||
tables. Freestanding: no allocation, no libc beyond memset/memmove, GF(256)
|
||
multiply by shift-and-add so there is no table init to depend on — all of which
|
||
matters because the caller is a panic handler.
|
||
|
||
**How it is verified — this is the important part.** A wrong QR still looks
|
||
exactly like a QR, so "it renders" proves nothing.
|
||
|
||
- `make qrtest` — self-checks (table arithmetic, RS generator polynomials,
|
||
finder/timing placement) and writes `build/qr.png` plus a terminal rendering
|
||
a phone can read straight off the screen. `-q` suppresses the ANSI art,
|
||
`-m TEXT` dumps the module matrix, `-f TEXT` dumps the function-pattern map.
|
||
- `make qrcheck` (`tools/qrcheck.py`) — the real oracle. Decodes with OpenCV,
|
||
*and* compares module-for-module against segno. Needs
|
||
`pip install --target <dir> opencv-python-headless numpy segno` and
|
||
PYTHONPATH; not wired into `make`.
|
||
|
||
**Two traps found doing this, both worth remembering:**
|
||
|
||
1. **segno adds a spurious zero pad codeword to every byte-mode symbol.** Its
|
||
`write_padding_bits()` does `extend([0] * (8 - (length % 8)))`, which emits a
|
||
whole byte when the stream is already codeword-aligned — and in byte mode it
|
||
always is (8L+16 bits after the terminator). ISO/IEC 18004 makes it
|
||
conditional and segno quotes that exact sentence above the line. Harmless
|
||
(decoders read `count` bytes and ignore padding) but it means the two
|
||
encoders **cannot** agree bit-for-bit on any padded payload. qrcheck compares
|
||
full matrices only for exact-fill payloads and decodes the rest. Our
|
||
behaviour is correct; do not "fix" it to match.
|
||
2. **OpenCV's QRCodeDetector fails on perfectly valid symbols**, depending on
|
||
scale and quiet-zone width. qrcheck tries six renderings, and if one still
|
||
fails it re-tests segno's symbol for the same payload — if that fails too it
|
||
reports `[detector]`, not `[FAIL]`. Without that step the check produces
|
||
false failures, which is worse than not running it.
|
||
|
||
**Debugging technique that cracked it:** dump the module matrix *and* the
|
||
function map, run the zigzag backwards in Python to recover the codewords from
|
||
both encoders, and diff those. "First differing codeword index 18" localised it
|
||
instantly; comparing matrices only ever said "108 of 441 modules differ". Note
|
||
the mask must be read from the format bits and un-applied first — un-masking
|
||
with the wrong mask makes your own output look like noise, which cost a round.
|
||
|
||
Windows note: `subprocess` cannot launch a relative path with forward slashes;
|
||
`os.path.abspath` it.
|