Skip to content
Merged
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
4 changes: 4 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 8 additions & 0 deletions NOTICE
Original file line number Diff line number Diff line change
Expand Up @@ -428,6 +428,14 @@ kernel/microcode/intel-ucode/* — Intel microcode, redistributable unmodified
sha256 03efb1491c7e899feb2665fa299363e64035e5444c1b8bc1f6ebed30de964e12)
SPDX-License-Identifier: LicenseRef-Intel-Microcode

The kernel embeds 06-8c-01 and loads it on the CPU it names; 06-cc-02 is
`toyos-microcode`'s test input. The licence permits redistribution in binary
form, unmodified, on three conditions: the copyright notice and the
disclaimer are reproduced with the distribution, which every x86-64 image does
as /system/share/licenses/Intel-microcode-license.txt; Intel's name endorses
nothing derived from it; and no reverse engineering, decompilation or
disassembly.


tests/cavp/ — NIST's SHA-2 test vectors, CAVP SHAVS
----------------------------------------------------
Expand Down
11 changes: 11 additions & 0 deletions build/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -803,6 +803,13 @@ pub const TRUST_ROOTS_LICENCE: (&str, &[u8]) = (
include_bytes!("../licenses/CDLA-Permissive-2.0-webpki-root-certs.txt"),
);

/// Where on ROOT the licence of the Intel microcode an x86-64 kernel embeds
/// is, and its text: the licence asks that a redistribution carry it.
pub const MICROCODE_LICENCE: (&str, &[u8]) = (
"share/licenses/Intel-microcode-license.txt",
include_bytes!("../licenses/Intel-microcode-license.txt"),
);

/// The Mozilla root program's authorities as `webpki-root-certs` publishes
/// them, each certificate a PEM block in the published order.
pub fn trust_roots() -> Vec<u8> {
Expand Down Expand Up @@ -837,6 +844,10 @@ fn build_and_assemble(

root_files.push((TRUST_ROOTS.to_string(), trust_roots()));
root_files.push((TRUST_ROOTS_LICENCE.0.to_string(), TRUST_ROOTS_LICENCE.1.to_vec()));
match arch {
Arch::X86_64 => root_files.push((MICROCODE_LICENCE.0.to_string(), MICROCODE_LICENCE.1.to_vec())),
Arch::Aarch64 => {}
}

// Extra files (test binaries, shared libs)
for (name, data) in extra_files {
Expand Down
28 changes: 23 additions & 5 deletions build/licence.rs
Original file line number Diff line number Diff line change
Expand Up @@ -87,13 +87,20 @@ const FONTS_ONLY: &[&str] = &["OFL-1.1"];
/// CDLA-Permissive-2.0 for the Mozilla roots.
const DATA_ONLY: &[&str] = &["CDLA-Permissive-2.0"];

/// What a licence is judged as: [`FONTS_ONLY`] passes only for a font and
/// [`DATA_ONLY`] only for the trust roots.
/// Allowed for a [`Terms::Firmware`] row, and nowhere else: the owner's
/// ruling that vendor firmware a device or CPU verifies by its maker's
/// signature may ship, pinned and unmodified (root `CLAUDE.md`).
const FIRMWARE_ONLY: &[&str] = &["LicenseRef-Intel-Microcode"];

/// What a licence is judged as: [`FONTS_ONLY`] passes only for a font,
/// [`DATA_ONLY`] only for the trust roots and [`FIRMWARE_ONLY`] only for
/// vendor firmware.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
enum Scope {
Code,
Font,
Data,
Firmware,
}

impl Scope {
Expand All @@ -103,6 +110,7 @@ impl Scope {
Scope::Code => false,
Scope::Font => FONTS_ONLY.contains(&id),
Scope::Data => DATA_ONLY.contains(&id),
Scope::Firmware => FIRMWARE_ONLY.contains(&id),
}
}
}
Expand All @@ -119,12 +127,15 @@ pub enum Terms {
/// A font's glyphs, or a raster of them, under an SPDX expression: the one
/// place [`FONTS_ONLY`] passes.
Font(&'static str),
/// Vendor firmware a device or CPU verifies by its maker's signature,
/// under an SPDX expression: the one place [`FIRMWARE_ONLY`] passes.
Firmware(&'static str),
}

impl Terms {
fn expr(self) -> &'static str {
match self {
Terms::Spdx(e) | Terms::Font(e) => e,
Terms::Spdx(e) | Terms::Font(e) | Terms::Firmware(e) => e,
}
}
}
Expand Down Expand Up @@ -577,13 +588,13 @@ pub const COMMITTED_FILES: &[(&str, &str, &str, Terms)] = &[
"kernel/microcode/intel-ucode/06-8c-01",
"efe83e312b90f7fe4b8f75260087edf03e048d2f4f80caef2ef631c842714bb3",
"NOTICE",
Terms::Spdx("LicenseRef-Intel-Microcode"),
Terms::Firmware("LicenseRef-Intel-Microcode"),
),
(
"kernel/microcode/intel-ucode/06-cc-02",
"4e43bb4d23c3638f8c16967d61e8f6456ae0da2ddd1da82ab579a50715147bb1",
"NOTICE",
Terms::Spdx("LicenseRef-Intel-Microcode"),
Terms::Firmware("LicenseRef-Intel-Microcode"),
),
];

Expand Down Expand Up @@ -1136,6 +1147,7 @@ fn judge_files(ledger: &[Row], tracked: &[String], shipping: &Shipping, report:
let scope = match terms {
Terms::Spdx(_) => Scope::Code,
Terms::Font(_) => Scope::Font,
Terms::Firmware(_) => Scope::Firmware,
};
report.judge(Finding::at(Kind::File, path, terms.expr(), String::new()), scope);
}
Expand Down Expand Up @@ -1493,6 +1505,12 @@ mod tests {
assert!(verdict_of("CDLA-Permissive-2.0").is_some(), "CDLA passed outside data");
assert!(judge_licence("CDLA-Permissive-2.0", Scope::Font).is_some(), "CDLA passed for a font");
assert_eq!(judge_licence("CDLA-Permissive-2.0", Scope::Data), None);
for firmware in FIRMWARE_ONLY {
assert!(verdict_of(firmware).is_some(), "{firmware} passed outside firmware");
assert!(judge_licence(firmware, Scope::Font).is_some(), "{firmware} passed for a font");
assert_eq!(judge_licence(firmware, Scope::Firmware), None);
}
assert!(judge_licence("OFL-1.1", Scope::Firmware).is_some(), "OFL passed as firmware");
assert_eq!(verdict_of("MPL-2.0"), None);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
---
status: open
kind: tooling
opened: 2026-10-13
---

# A metal readback does not prove which image wrote its log

`read_readback` (`tests/common/metal.rs`) judges a readback directory's
`kernel.log` without holding the log's `supervisor: build <tree> clean` line
to the tree doing the judging, so a judge from one tree passes or reds a boot
of another's image and says nothing. And `--metal --metal-readback <dir>`
from a tree whose images the directory does not hold stages that tree's
images into it, overwriting the `image.img` that was booted, so the directory
no longer holds the image that wrote its log.

**Evidence.** #886's round-2 `t14-reseal` arm, booted from tree `b7a7b062a`
(image sha256 `c6d44fe8…`): a first judging from the head tree `e2b398cb6`
staged that tree's image beside the readback and judged nothing (exit 2,
https://github.com/ToyOSOrg/evidence/blob/f17cea3bdae1bda4586220fc7b9f551d9cecff49/r2/readings/t14-reseal/judge-from-e2b398cb6-tree.log);
the judging from `b7a7b062a` in a second checkout, over a copy of the
readback, rebuilt and wrote its `image.img` (`55bec413…`), never booted and
2,388,453 bytes from the booted one
(https://github.com/ToyOSOrg/ToyOS/pull/886#issuecomment-6135201083). That
the log came from `b7a7b062a` was told by reading its `supervisor: build`
line; no check did.

Owner: the orchestrator.

**Exit**: a judge from a tree other than the one a readback's log names
refuses the readback by name, and judging a readback directory never
writes its `image.img`.
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
---
status: open
kind: defect
opened: 2026-10-12
---

# A panic before the reset register is read holds the machine for good

On x86-64 the kernel learns its reset from the FADT in `power::init_reset`,
which `arch::boot::interrupts` calls, well after `arch::boot::after_console`
(`kernel/src/main.rs`). A panic before it finds
`power::can_reboot` false, and `panic_reboot::arm` returns `Bound::Held`:
the panel is held until somebody cuts the power. On the AMD bench every panic
before `clock::init` (`arch::boot::clock`) is held as well, whatever the
reset: its CPU states no counter frequency (CPUID leaves 0x15 and 0x16), so
`panic_reboot::deadline` has nothing to time the bound with and `arm` takes
its "no clock" arm.

Everything `after_console` runs is in that window, the BSP's microcode load
among them. `issues/a-fatal-event-stands-down-both-bounds-and-may-leave-nothing-to-end-the-machine.md`
covers a later window, where a reset is known and both bounds stand down.

**Evidence.** #886's two negative-control arms panicked at the microcode
read-back inside `after_console`, and both benches had to be power-cycled by
hand: the T14 armed with `microcode-reseal` and the AMD bench with its
trigger write removed. The AMD bench's log partition, read after the power
cycle, carries the failed read-back; the T14's cannot tell the panic from a
fault at the trigger
(https://github.com/ToyOSOrg/ToyOS/pull/886#issuecomment-6131006022).

Owner: the orchestrator.

**Exit**: a T14 boot and an AMD bench boot armed with `test-early-panic`,
which panics right after `after_console`, each reset themselves with nobody
at the machine, and the boot after it reports the panic.
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ firmware's memory-overwrite request, by the owner's "Keep crash records"
- `issues/user-pointer-checks-have-no-spectre-v1-fence-and-smap-is-optional.md`
- `issues/indirect-branches-and-returns-run-without-thunks.md`
- `issues/tsx-stays-as-firmware-left-it.md`
- `issues/the-kernel-loads-no-cpu-microcode.md`
- `issues/the-kernel-loads-no-amd-microcode.md`
- `issues/no-user-address-is-drawn-per-spawn.md`
- `issues/the-kernel-has-no-stack-protector.md`
- `issues/no-kernel-address-is-drawn-per-boot.md`
Expand Down
48 changes: 48 additions & 0 deletions issues/the-kernel-loads-no-amd-microcode.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
---
status: open
kind: defect
opened: 2026-09-29
---

# The kernel loads no AMD microcode

The kernel is to load CPU microcode signed by the CPU's maker, pinned by
version and hash the way vendor device firmware is (owner, 2026-09-30).
It loads Intel's (`toyos-microcode`, `kernel/src/arch/x86_64/microcode.rs`)
and no AMD CPU's: on one, every CPU's line says ToyOS has no AMD microcode
loader, and the `microcode` metal row holds the AMD bench to that line.

**Met** by #886: on the T14 every CPU runs Intel's 0xbe for 0x806c1
platform 7, `current` or `loaded`; the T14 armed with `microcode-reseal`
completes its boot and reds the row on cpu0's `refused` line; every guest
loads nothing, for its CPU reports a hypervisor or a vendor ToyOS has no
loader for.

**AMD.** AMD documents MSR0000_008B (55922) and not the patch loader, so
the loader rests on AMD's own parser, `amd_ucode_info`. Root `CLAUDE.md`
("Zero legacy") lets that source be read only clean-room: one agent writes
from it a specification of facts citing no implementation, and another
builds from that specification alone. #886's loader was not built that way
and was taken out.

Owner: the orchestrator.

**Exit**: the kernel loads, early on every AMD CPU, the newest patch the
embedded container holds for it, and a CPU that does not take one says so
and runs what it reads back:

- the loader is built clean-room: one agent reads `amd_ucode_info` and
writes from it a specification of facts of the container format citing no
implementation, keeping the provenance in its own evidence, and another
builds the loader from that specification alone, handed to it in its
brief and never committed;
- a patch is matched through the container's equivalence table, CPUID.01H:EAX
to processor ID and processor ID to patch, and a malformed table is refused
by name; a host test whose table maps 0x00860f01 to a processor ID other
than the one in a patch whose ID decodes to 0x00860f01 expects no match;
- every behaviour neither AMD's documents nor that specification states is
named undocumented in the module header with the measurement behind it,
or is not in the loader;
- the `microcode` row is green on the AMD bench with every CPU running the
container's patch, and the bench with its trigger write removed completes
its boot and reds the row on a `refused` line.
77 changes: 0 additions & 77 deletions issues/the-kernel-loads-no-cpu-microcode.md

This file was deleted.

1 change: 1 addition & 0 deletions kernel/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -490,6 +490,7 @@ toyos-gicv3 = { path = "gicv3" }
toyos-gpt = { path = "../lib/gpt" }
toyos-hda = { path = "../lib/hda" }
toyos-its = { path = "its" }
toyos-microcode = { path = "microcode" }
toyos-pci = { path = "pci" }
toyos-phys = { path = "phys" }
toyos-smmu = { path = "smmu" }
Expand Down
5 changes: 4 additions & 1 deletion kernel/microcode/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,11 @@

[package]
name = "toyos-microcode"
description = "Intel microcode update files validated and matched to a CPU as the SDM's Microcode Update Facilities define them, pure."
description = "Intel microcode update files validated and matched to a CPU as the SDM's Microcode Update Facilities define them, whether a CPU loads at all, and the file the kernel embeds, pure."
version = "0.1.0"
edition = "2021"
license = "MIT OR Apache-2.0"
publish = false

[dependencies]
toyos-cpuvuln = { path = "../cpuvuln" }
Loading