diff --git a/issues/a-namespace-is-a-kernel-object-and-service-lookup-is-kept-out-of-the-kernel.md b/issues/a-namespace-is-a-kernel-object-and-service-lookup-is-kept-out-of-the-kernel.md new file mode 100644 index 00000000000..c82c3e4441b --- /dev/null +++ b/issues/a-namespace-is-a-kernel-object-and-service-lookup-is-kept-out-of-the-kernel.md @@ -0,0 +1,30 @@ +--- +status: open +kind: finding +opened: 2026-11-11 +--- + +# A namespace is a kernel object, and service lookup is kept out of the kernel + +An open design question, the orchestrator's to resolve. Found by the +inventory in +`issues/the-kernel-is-small-interrupts-post-and-threads-wait.md`. + +The kernel holds a table of service names to connectors per namespace +(`kernel/src/object/namespace.rs`) and three calls over it, +`SYS_NAMESPACE_BUILD`, `SYS_NAMESPACE_OPEN` and `SYS_NAMESPACE_NAMES` +(`kernel/src/syscall/ipc.rs`), about 215 lines: it copies name text of up +to 64 KiB (`SYS_NAMESPACE_OPEN`, through `user_str`), compares it and lists +it. The design keeps a namespace as an immutable kernel object and, in its +statement of what stays out of the kernel, names service lookup. Both +cannot be the whole truth of this code. + +The two readings: the object is the mechanism and "lookup" means a global +registry, which the kernel has none of, so nothing moves; or name text is +userland's, a process is endowed connectors and its parent's library maps +names to them, and the kernel's table and three calls go. The second is an +inference, not a design anyone wrote. + +**Exit**: one reading is chosen and the design says it in one place; under +the first this file is deleted, under the second it becomes a stage of the +track. diff --git a/issues/the-kernel-decides-who-is-asked-to-quit-and-who-is-killed.md b/issues/the-kernel-decides-who-is-asked-to-quit-and-who-is-killed.md new file mode 100644 index 00000000000..2a002a7e8ca --- /dev/null +++ b/issues/the-kernel-decides-who-is-asked-to-quit-and-who-is-killed.md @@ -0,0 +1,26 @@ +--- +status: open +kind: finding +opened: 2026-11-11 +--- + +# The kernel decides who is asked to quit and who is killed + +An open design question, the orchestrator's to resolve, and an inference: +raised by the review of #1208, of the inventory in +`issues/the-kernel-is-small-interrupts-post-and-threads-wait.md`. + +`SYS_PROCESS_QUIT` walks the subtree in the kernel, puts the reason in the +notice of each process that listens and kills each that does not +(`kernel/proclife/src/quit.rs`, 216 lines; `quit_process` in +`kernel/src/process.rs`; `kernel/src/object/notice.rs`, 61). The design +reads both ways: quitting gracefully is the parent's job, and interrupt, +hang-up and terminate requests are native events a program receives. Under +the first a parent would ask its children over its own protocol and the +kernel would keep only the kill; under the second the kernel's walk is the +mechanism those events need. +`issues/a-childs-end-is-an-event-and-a-parent-takes-its-children-down.md` +built it as its stage 6. + +**Exit**: the design says which reading holds; this file is deleted, or the +walk's removal becomes a stage of the track. diff --git a/issues/the-kernel-is-small-interrupts-post-and-threads-wait.md b/issues/the-kernel-is-small-interrupts-post-and-threads-wait.md index 3404660c79c..bdc61b600ec 100644 --- a/issues/the-kernel-is-small-interrupts-post-and-threads-wait.md +++ b/issues/the-kernel-is-small-interrupts-post-and-threads-wait.md @@ -327,6 +327,127 @@ Beside that order, each waiting on something outside it: `a_stream_truncated_at_any_offset_resyncs_within_two_packets`. **Done** (#899). +## What the kernel still holds + +An inventory at `9349705ce` (main, 2026-11-10): `git ls-files +'kernel/src/*.rs'` is 215 files and 55,378 lines by `wc -l`. Each file is +counted once, in the area of its main responsibility. "Kept" is the design +read against the code; "inferred" marks a reading the design does not +state. Nothing here is ruled, and the ranking is a proposal. + +| Area | Files | Lines | Kept by the design | +|---|---|---|---| +| Memory: page tables, address spaces, frames, heap, TLB, user copies, shared memory | 27 | 7,975 | yes | +| CPU bring-up, per-CPU state, control registers, microcode, FPU state, the boot handoff | 24 | 6,304 | yes | +| IOMMU: VT-d, AMD-Vi, SMMUv3, domains, faults | 19 | 6,126 | yes | +| Objects, handles and IPC: pipes, messages, ports, namespaces, events, inboxes, the watch | 22 | 5,751 | yes, but the TTY mark (below) | +| Device claims: PCI enumeration, config space, MSI, BARs, hot-plug, `isa` rows, the framebuffer | 18 | 5,585 | yes, but the OpRegion's lookup (below) | +| Interrupt controllers, vectors, traps | 10 | 3,970 | yes | +| Log ring, console UART, panic console, panic capture, own symbols | 17 | 3,517 | yes | +| Scheduling: the core's driver, context switch, idle states, jobs, preemption | 20 | 3,160 | yes | +| Firmware tables, ACPI mode, reset and power-off, the machine's stop, what the loader named | 12 | 3,099 | yes for the tables, the `acpi` row and power; the rest below | +| Processes, threads, spawn | 8 | 2,736 | yes, less the userland text a spawn carries (below) | +| Clocks: TSC and generic timer, the wall clock, typed durations | 7 | 1,847 | the clock yes; the CMOS driver is a recorded defect (below) | +| Counters, the diary, IRQ census, mask windows, boot deadline, hard lockup, chipset watchdog | 14 | 1,844 | counters and diary yes; the three bounds are unnamed and inferred to be the kernel's, since they bound the kernel | +| Paged objects and their pager calls | 2 | 1,325 | yes | +| Syscall entry and argument decode | 3 | 806 | yes | +| Test-only: actuators, `SYS_DEBUG`, the IOMMU test device, the log storm | 6 | 668 | behind `boot-actuators` or `test-actuators` | +| Locks, seqlock, hasher, random | 6 | 665 | yes | + +The kernel also links the crates beside it, whose lines carry their host +tests: `kernel/pure` (49 files, 26,108, the scheduler core 15,570 of it), +`kernel/proclife` (3,550), `kernel/pci` (2,211), `kernel/smmu` (1,463), +`kernel/its` (985), `kernel/amdvi` (902) and three under 200. `kernel/sim` +(10,242), `kernel/loom` (6,088) and `kernel/copies` (73) are harnesses no +kernel links. + +Observed: no filesystem, storage, input, network, audio or USB driver, no +ELF or AML parser and no virtio is left in `kernel/src`. What it parses is +firmware's static tables (`toyos-acpi`), the build's own table +(`toyos-kimage`), the vendor's microcode container, and, below, a UEFI +device path. + +### Userland's, by the design, and still here + +Sizes are of `kernel/src` and read off the functions, so estimates but for +the first. + +| What | Size | Dependents | Owner in the design, and what blocks it | +|---|---|---|---| +| A child's arguments and environment | about 140, by #1200's count | std, libc, `toyos::startup` | the parent's startup block: #1200 | +| Endowment labels: a blob per process for its life, answered by `SYS_ENDOWMENTS`, of which the kernel compares three (`JOB_LABEL`, `SELF_LABEL`, `QUIT_LABEL`) and writes three more for the supervisor | ~130 | `toyos::endow`, the supervisor's table | the startup block (inferred); needs the child's handle numbers known to its parent and the three compared labels as typed spawn fields; after #1200 | +| The TTY mark on a pipe end (`SYS_MARK_TTY`, `FileType::Tty`) | ~40 | std's spawn, libc's `isatty` and stdio, std's stdio | a fact about a stream that only libc and std read; a label or the block could carry it (inferred); after the labels | +| The main thread's stack: 8 MiB and a guard the kernel chooses and reserves | ~30 | `SYS_STACK_INFO`, which only a guest test calls | its creator's, since the design gives a thread's stack a caller-chosen size; after #1200, when a child's stack starts empty | +| What the loader named: partition GUIDs by role, in the inventory, and the boot sources `SYS_BOOT_SOURCES` answers, whose device paths `loaded.rs` parses (`toyos-devicepath`) only to refuse the loader's own bytes | ~95, and the kernel's link to a 324-line parser | the supervisor, `toyos::syscap`, the manifest's inventory reader | the design gives the kernel a typed description of what was loaded and keeps a partition GUID out of it; passing the description on unread, as a read-only boot image as ROOT is, fits both (inferred) and waits on no other change | +| The IGD OpRegion's lookup: a firmware window is a granted privilege the design keeps, but `SYS_DEVICE_FIRMWARE_WINDOW` finds it by reading Intel graphics' `ASLS` | ~90 and ~30 of `pcidev` | tgl-display, the supervisor, the manifest | the lookup is device knowledge; a window the ACPI service names for a claim would be generic (inferred); needs a design | +| The supervisor's last words, userland text written into the kernel's ring (`SYS_LOG_LAST_WORDS`) | ~20 | the supervisor | the design keeps userland output out of the kernel's ring and names no exception; this is one | + +Changes of the design, each inferred and none of them work until resolved: + +- **The program's path**, up to 64 KiB of text per process (~40 lines), + which the kernel writes into a killed program's report as its file. The + design reports a killed program by file, offset and build-id, so removing + the path changes that. It needs a frame named by build-id alone and + something in userland that maps a build-id to a file; neither is built. +- **Namespaces**: + `issues/a-namespace-is-a-kernel-object-and-service-lookup-is-kept-out-of-the-kernel.md`. +- **The stop's sweep of userland threads**: + `issues/the-kernel-stops-every-userland-thread-at-a-stop-the-supervisor-orders.md`. +- **The quit's walk**: + `issues/the-kernel-decides-who-is-asked-to-quit-and-who-is-killed.md`. + +Already a defect with its exit: the x86-64 CMOS driver and its IRQ8 (285 +lines), `issues/x86-64-reads-the-cmos-for-a-wall-clock-the-loader-already-hands-it.md`. + +### The next removals, ranked (a proposal) + +By mechanisms eliminated: + +1. #1200: `SYS_GET_ENV`, the environment kept per process, the kernel's + write onto a child's stack. +2. Endowment labels, after #1200: `SYS_ENDOWMENTS`, the per-process blob + and its charge, and label text the kernel compares. +3. What the loader named, independent of the others: `SYS_BOOT_SOURCES`, + the inventory's partition records, the device-path parser. +4. The TTY mark, with or after 2: `SYS_MARK_TTY`. It meets + `issues/a-received-handle-has-no-knowable-type.md`, which wants + `SYS_FSTAT` to answer more, so the two are designed together. +5. The main thread's stack, after #1200: `SYS_STACK_INFO` and the kernel's + two stack constants. + +By lines: none is large. The five ranked are about 435 of 55,378; with the +OpRegion's lookup (~120, which needs a design) and the last words (~20), +the table's rows are about 575. What the design gives userland is nearly +out, and a larger number needs one of the design changes above. + +### Dead or nearly so, recorded and not deleted + +- `SYS_STACK_INFO` is called by `tests/guest/rust-tests/src/bin/std_threads.rs` + alone, and `SYS_VM_DECOMMIT` by two guest tests alone (`mmap_stress`, + `starveling`). +- `KernelArgs::boot_partition_start_lba` and `boot_partition_blocks` are + read by one log line (`kernel/src/main.rs`), and `wall_clock_*` by no + x86-64 code. +- `KernelStack::heap` (`kernel/src/loader/start.rs`) charges every thread + two strings at 64 KiB and a page, "as a rename's paths". No rename is + left: a spawn's path and arguments are the two strings today. After #1200 + no call copies two, and one string of that bound remains for as long as + a spawn's path or `SYS_NAMESPACE_OPEN`'s name, which nothing bounds below + 64 KiB, is copied; the page is the namespace names'. +- `FileType::File`, `Directory` and `Symlink` are answered by no kernel + path: the file protocol's clients (`sdk/toyos/src/files/owner.rs`, + `sdk/libc/src/posix_io.rs`, `sdk/std/sys/fs.rs`) reuse the kernel ABI's + enum for answers the kernel never gives. The kernel's `Stat` answers + `mtime`, `volume` and `node` as zero for every object + (`issues/fstats-answer-is-declared-twice.md` owns its two declarations). +- Stale text: `kernel/Cargo.toml`'s description says "filesystem"; + `kernel/CLAUDE.md`'s first caveat is about a filesystem on the idle path; + `quiesce.rs`'s header speaks of the block layer and a USB stop, neither + of which the kernel has; `UserImage`'s doc comment + (`kernel/src/process.rs`) says "the file the kernel opened", and the + kernel opens none; `loaded.rs`'s header says it "interprets no path" + above a function that parses one. + ## Standing No device wait, spin or poll happens while holding a spinlock or inside a diff --git a/issues/the-kernel-stops-every-userland-thread-at-a-stop-the-supervisor-orders.md b/issues/the-kernel-stops-every-userland-thread-at-a-stop-the-supervisor-orders.md new file mode 100644 index 00000000000..efdd46ec1c6 --- /dev/null +++ b/issues/the-kernel-stops-every-userland-thread-at-a-stop-the-supervisor-orders.md @@ -0,0 +1,31 @@ +--- +status: open +kind: finding +opened: 2026-11-11 +--- + +# The kernel stops every userland thread at a stop the supervisor orders + +An open design question, the orchestrator's to resolve. Found by the +inventory in +`issues/the-kernel-is-small-interrupts-post-and-threads-wait.md`. + +`SYS_SHUTDOWN` and `SYS_REBOOT` first stop every userland thread at its +next return to Ring 3 and wait for them inside a budget +(`kernel/src/quiesce.rs`, 216 lines, deciding by `toyos-quiesce`, 294, and +about 110 of `kernel/src/syscall/machine.rs`), then log the census and end +the machine. The supervisor owns the orderly stop and has already ended or +flushed what it started before it asks +(`issues/the-supervisor-is-host-tested-and-owns-the-stop.md`). The design +names the power-off as the kernel's and does not name this stop of +threads. + +What is not established is what the kernel's stop holds that the +supervisor's does not: the census is of a machine that has stopped, and the +console wire is taken from its holder. Whether a supervisor that ends its +subtree before the call makes the kernel's sweep redundant is an inference +nobody has checked against the stop's tests. + +**Exit**: what the kernel's sweep establishes that the supervisor's stop +cannot is written at `kernel/src/quiesce.rs`'s header and this file +deleted, or the sweep is removed as a stage of the track. diff --git a/issues/x86-64-reads-the-cmos-for-a-wall-clock-the-loader-already-hands-it.md b/issues/x86-64-reads-the-cmos-for-a-wall-clock-the-loader-already-hands-it.md index ecc6bae2a83..1f4cc83e719 100644 --- a/issues/x86-64-reads-the-cmos-for-a-wall-clock-the-loader-already-hands-it.md +++ b/issues/x86-64-reads-the-cmos-for-a-wall-clock-the-loader-already-hands-it.md @@ -16,6 +16,13 @@ two readers of one clock, and on x86-64 the field is written and never read. Whether the T14's firmware answers `GetTime` from the same CMOS is not measured. +The wall clock is not the CMOS driver's only reader: x86-64's clock takes +the RTC's update-ended edges, on IRQ8, as the third reference that tells a +TSC step from a stall of the chipset's counters +(`kernel/src/arch/x86_64/clocksource.rs`, `rtc::last_edge`). So the exit +below deletes the wall-clock read and the century register, and +`arch/x86_64/rtc.rs` whole only if that reference goes or moves with it. + Exit condition: x86-64 anchors on the loader's reading, and `arch/x86_64/rtc.rs`, `acpi::rtc_century_register` and their callers are deleted with it, against a metal row reading the T14's wall clock; or a