Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 6 additions & 4 deletions .claude/skills/qemu-boot/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,12 @@ macOS host, so you must bound the run externally.
`source .env` and hard-fail if it's missing. If absent, copy
`.env.example` to `.env` (empty is fine) before proceeding.

2. **Rebuild.** Run `./make-image.sh` in the repo root. This fetches OVMF
prebuilts (first run only) and builds the loader + kernel into `out/esp`.
Treat a failure here as a build error, not a boot error — report it and
stop; don't proceed to boot a stale image.
2. **Rebuild.** Run `./make-image.sh` in the repo root. It uses the
vendored OVMF firmware under `third_party/ovmf` (committed to the repo,
no network fetch — it errors out if the blobs are missing rather than
downloading them) and builds the loader + kernel into `out/esp`. Treat a
failure here as a build error, not a boot error — report it and stop;
don't proceed to boot a stale image.

3. **Determine the expected success signature before booting.** Read the
current [src/kmain.rs](../../../src/kmain.rs) to see what `kernel_entry`
Expand Down
45 changes: 35 additions & 10 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ file covers knowledge which agents need but can't easily infer from reading the
This is a learning project, so expect lots of brainstorming, experimentation,
trial-and-error, and exploration of existing tools and practices. I'm also
learning how to use Claude Code in this existing project, so keep that in mind.
Claude Code-specific configuration (output style, skills, subagents under
`.claude/`) is indexed in [CLAUDE.md](CLAUDE.md).

## Long-term goal

Expand Down Expand Up @@ -40,10 +42,14 @@ Eventually, a C toolchain necessary. However, it is helpful much sooner:

## Project status

Last updated 2026-07-21. The project just finished migrating its boot
process from Multiboot2/GRUB to a custom UEFI loader. The loader
(`loader/`) now successfully parses the kernel ELF, maps its segments, sets
up paging, and jumps into `kernel_entry` in [src/kmain.rs](src/kmain.rs).
Last updated 2026-07-26. The project migrated its boot process from
Multiboot2/GRUB to a custom UEFI loader, and has since moved the whole
workspace to Rust **edition 2024** and centralized its unsafe/safety lints
in `[workspace.lints]` (`unsafe_op_in_unsafe_fn` and the clippy safety lints
— see `Cargo.toml`), which is what the clippy CI gate below enforces. The
loader (`loader/`) now successfully parses the kernel ELF, maps its
segments, sets up paging, and jumps into `kernel_entry` in
[src/kmain.rs](src/kmain.rs).

**`kernel_entry` has been re-wired for the new UEFI handoff.** It sets up
the GDT and IDT, initializes the frame allocator, and hands off into
Expand All @@ -57,9 +63,16 @@ and manual QEMU boots) landed in `.github/workflows/ci.yml` (PR #25):
three jobs — `cheap` (host unit tests + per-crate compile checks),
`expensive` (Miri), and `smoke` (full image build + a headless QEMU boot,
gated on a new `src/qemu.rs` isa-debug-exit pass/fail signal, see
"Verifying changes" below). GDB-over-QEMU-stub debugging is still real but
slow for interactive local work, so CI — not local runs — is what now
exercises everything, including a full boot, on every push/PR. Clippy **is**
"Verifying changes" below). The `expensive` job is path-gated (PR #32): it
runs Miri only when a Miri input changed (`shared/**`, `Cargo.lock`,
`Cargo.toml`, `.cargo/config.toml`, `rust-toolchain`, or the workflow
itself) and otherwise reports a fast green no-op, so it never blocks a
docs-only or kernel-only PR; a daily `schedule:` run (cron `0 6 * * *`)
forces the full Miri suite regardless of paths, catching nightly drift the
path filter can't see. GDB-over-QEMU-stub debugging is still real but slow
for interactive local work, so CI — not local runs — is what now exercises
everything: a full boot on every push/PR, and Miri on every push/PR that
touches a Miri input (plus the daily scheduled run). Clippy **is**
now gated: the `cheap` job runs `kclippy`/`sclippy`/`lclippy`/`iclippy`/
`tclippy`, covering every workspace package. What it enforces is the safety
lints denied in `[workspace.lints.clippy]` (`undocumented_unsafe_blocks`,
Expand Down Expand Up @@ -148,7 +161,10 @@ Cargo aliases (see `.cargo/config.toml` for the full definitions):
* **mkimage**: builds a GRUB/xorriso ISO. Leftover from the pre-UEFI boot
flow — **not** invoked by `make-image.sh` anymore; don't assume it's part
of the live build path.
* **buildutil**: helpers shared between build scripts and mkimage.
* **buildutil**: host-side helper lib (e.g. `run_and_check`) consumed
**only** by `mkimage` (per `Cargo.toml` and a source grep). Despite the
"build scripts" framing, the live `make-image.sh` path does not use it —
like `mkimage`, it's effectively a pre-UEFI leftover.
* **fetch-prebuilts**: manual updater that downloads prebuilt OVMF firmware and
refreshes the vendored copy in `third_party/ovmf` (not part of the normal
build; needs network).
Expand Down Expand Up @@ -199,8 +215,13 @@ of running them all by hand every time. Locally, in order of preference:
**Not part of the standard local PR loop.** CI runs Miri as its own
`expensive` job (see `.github/workflows/ci.yml`), so don't run
`cargo smiri` locally as a matter of course before shipping a PR — rely
on that CI job instead. Do run it locally when the specific change
actually hinges on it (e.g. you're touching the unsafe page-table
on that CI job instead. That job is path-gated (PR #32): it runs Miri
only when a Miri input changed (`shared/**` plus a few build files — see
"Project status" for the full list), which covers every change Miri can
actually catch, since Miri only exercises `shared`; a daily scheduled
run additionally forces it to catch nightly drift. Do run it locally
when the specific change actually hinges on it (e.g. you're touching the
unsafe page-table
pointer walks in [shared/src/memory/paging.rs](shared/src/memory/paging.rs)
and want fast local iteration), but a slow/pending local Miri run should
never block otherwise-ready work from being committed or opened as a PR.
Expand All @@ -219,6 +240,10 @@ of running them all by hand every time. Locally, in order of preference:

### Booting headlessly and bounding the run

Claude Code automates this procedure as the `qemu-boot` skill
(`.claude/skills/qemu-boot/SKILL.md`); it mirrors the steps below, so keep the
two in sync if either changes.

Rebuild first (`./make-image.sh`), then run headless and capture output.
QEMU never exits on its own after the kernel halts, and there's **no
`timeout`/`gtimeout` on macOS**. A `perl -e 'alarm N; exec ...'` wrapper
Expand Down
22 changes: 21 additions & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,23 @@
@AGENTS.md

This file is for Claude-specific notes only; there are none yet.
## Claude-specific tooling

Everything general lives in [AGENTS.md](AGENTS.md); this section indexes the
Claude Code-specific configuration checked into `.claude/`:

* **Output style — `Terse`** (`.claude/output-styles/terse.md`). Enabled
project-wide via `.claude/settings.json` (`"outputStyle": "Terse"`), so it
is the default for sessions in this repo: minimum output, reasoning budget
unchanged.
* **Skill — `qemu-boot`** (`.claude/skills/qemu-boot/SKILL.md`). Automates the
"Booting headlessly and bounding the run" procedure in AGENTS.md: rebuilds
the image, boots it in QEMU headlessly under an external `pkill` watchdog,
and distinguishes a real hang from a silent triple-fault reset from a normal
panic. Prefer it over re-deriving the boot dance by hand.
* **Subagent — `source-grounded-explorer`**
(`.claude/agents/source-grounded-explorer.md`). For open-ended
architectural/strategic questions about the kernel; it reads the actual
source before answering and returns a labeled option menu with a
recommendation. Not for concrete, already-decided implementation tasks.

Keep this list in sync when `.claude/` tooling is added, removed, or renamed.
Loading