diff --git a/Cargo.lock b/Cargo.lock
index 1e18b1d023b..7c192d6e676 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1096,6 +1096,10 @@ dependencies = [
name = "toyos-libc-copies"
version = "0.1.0"
+[[package]]
+name = "toyos-llvmbar"
+version = "0.1.0"
+
[[package]]
name = "toyos-logstream"
version = "0.1.0"
diff --git a/Cargo.toml b/Cargo.toml
index 661c2ad735b..7a4dda16e5b 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -36,6 +36,7 @@ members = [
"toyos-inspect",
"toyos-keymap",
"toyos-ld",
+ "toyos-llvmbar",
"toyos-libc-copies",
"toyos-logstream",
"toyos-manifest",
diff --git a/issues/build/toyos-builds-itself.md b/issues/build/toyos-builds-itself.md
index 8bf082ce50d..a12af500877 100644
--- a/issues/build/toyos-builds-itself.md
+++ b/issues/build/toyos-builds-itself.md
@@ -53,6 +53,94 @@ enough for an LLVM build tree
memory beyond what 2 MiB process pages allow
(`issues/kernel/process-memory-is-2-mib-pages-and-that-caps-the-process-count.md`).
+**The bar: Linux on this T14 building the same LLVM with a clang-built
+clang+lld against libc++.** It is a recipe, and a ToyOS run matches every
+element of it. `toyos-llvmbar` holds its lines (`t14/block.txt`), the steps
+the T14 runs (`t14/`), the power envelope and the judge, whose module header
+is the rule a sample is valid by.
+
+- *Source*: `rust-lang/llvm-project` at
+ `52ed14fcd56afc30f9cccd8ca8ce237c2eef7e04`. A ToyOS run's source, for
+ the building compiler and the build alike, differs from it by nothing
+ but the fork's commits over that base (`forks.toml`, `[llvm-project]`).
+- *Building compiler*: clang+lld at `52ed14fc`, Release,
+ `LLVM_ENABLE_LTO=OFF`, `LLVM_BUILD_INSTRUMENTED=OFF`, no profile data,
+ built by clang+lld at `52ed14fc` with the same configuration and flags:
+ the `s2` lines. Stage 1 is only how the first clang exists: gcc, which
+ has no ToyOS target, builds it with the `s1` lines
+ (`gcc (Ubuntu 13.3.0-6ubuntu2~24.04.1) 13.3.0`). For ToyOS it is M2's
+ cross build of the same source and configuration, whose lines M2 owes.
+- *C++ library*: libc++ with libc++abi from the same source, built in each
+ stage's build by that stage's own clang (`runtimes`). The `s2` tools link
+ it statically: `s2`'s `clang-22` and `lld` name no C++ shared object and
+ reference no `GLIBCXX` symbol. `s2`'s libc++ headers are
+ `_LIBCPP_VERSION` 220108.
+- *Build*: that clang+lld builds clang+lld from the same source with the
+ `s3` lines, configured afresh; cmake 3.28.3, ninja 1.11.1.
+- *Timed span*: the last line's `ninja` alone.
+- *Warm*: a span's configure line starts as soon as a complete `s3`
+ `ninja` line exits, and its `ninja` as soon as that configure exits; the
+ build reads no block from a block device during the span (`time -v`'s
+ file system inputs 0).
+- *Power envelope*: `toyos-llvmbar`'s `ENVELOPE` and `ENVELOPE_EVERY_CPU`,
+ every element with its value; `LINUX` and `LINUX_EVERY_CPU` are how Linux
+ reaches them.
+- *Machine*: i5-1135G7, 8 threads, 16476082176 B RAM; microcode revision
+ `0xbe` on every CPU; Ubuntu 24.04.4 with the kernel
+ `issues/hardware/linuxs-readings-of-the-t14-and-the-tcg-model-are-not-committed.md`
+ pins, 6.8.0-142-generic; ext4 on LVM.
+- *Mitigations*: that issue's T14 capture is the one read of them. A sample
+ reads the kernel, the vulnerabilities and the microcode at its start and
+ its end with the capture's own commands, and the judge refuses it unless
+ they are the capture's.
+
+The bar is the best of three valid samples, and it is not set: no sample is
+valid by the judge.
+
+Owed once the T14 answers again
+(`issues/hardware/a-t14-measurement-has-no-way-back-when-the-t14-stops-answering.md`),
+and Ubuntu is wiped only after the capture and the bar are both committed:
+
+- the capture on `main`, whose `/proc/version`, vulnerabilities and
+ `/proc/cpuinfo` text is the judge's second argument;
+- `t14/driver.sh` under `t14/sampler.sh`, run until `toyos-llvmbar` exits
+ 0, and the bar, the BIOS version and the kernel command line it prints
+ recorded here;
+- *Flags*, of `s2` and `s3`: the `CMAKE_*_FLAGS` and linker flags the
+ run's `*-cache.txt` hold, and the compile line `s3-commands.txt` holds
+ for `clang/tools/driver/driver.cpp` and the link line for `clang`, which
+ show whether `$LIBCXX` added `-stdlib=libc++` and `-static-libstdc++`;
+- `s2-commands.txt` and `s3-commands.txt` compared: they may differ only in
+ the build directory.
+
+*Exit*: a ToyOS build of the source above with the `s3` configuration and
+flags, by a clang+lld built by the recipe above, on this T14, warm, whose
+`ninja` exits 0 at or under the bar, read back as the judge reads a Linux
+sample: the envelope at its cadence from a start read to an end read, and at
+the span's start and end the microcode, the BIOS version (SMBIOS type 0)
+equal to the Linux sample's, and for each entry the capture shows as a
+mitigation the line
+`issues/kernel/each-boot-prints-the-vulnerabilities-lines-linux-prints.md`
+requires, with its mechanism in force on every CPU:
+
+- `gather_data_sampling`: `IA32_MCU_OPT_CTRL` (0x123) `GDS_MITG_DIS` clear
+ (`issues/kernel/spec-ctrl-and-gds-stay-as-firmware-left-them.md`);
+- `spectre_v2`: `IA32_SPEC_CTRL` (0x48) at the capture's read with SSBD
+ masked (the same issue);
+- `indirect_target_selection`: the live thunk bodies equal to the body
+ `issues/kernel/a-pure-function-decides-a-cpus-speculation-mitigations-as-linux-does.md`
+ selects (`issues/kernel/indirect-branches-and-returns-run-without-thunks.md`);
+- `spectre_v1`: CR4.SMAP set
+ (`issues/kernel/user-pointer-checks-have-no-spectre-v1-fence-and-smap-is-optional.md`);
+- `spec_store_bypass`: where the capture shows Linux's prctl mode, SSBD is
+ set only in a process that asked, so parity is SSBD set in exactly the
+ build's processes that ask Linux, read on the switch into each
+ (`issues/kernel/no-program-runs-with-speculative-store-bypass-disabled.md`);
+ which of them ask is owed.
+
+An entry the capture shows as a mitigation that this list does not name holds
+the exit until it is named here.
+
**What M2 must do to delete toyos-ld.** It is frozen and links nothing the host
builds; what keeps it is that it is the one linker a ToyOS process can run,
shipped as `/system/bin/toyos-ld` by `system.toml`'s `[programs]` row and named
diff --git a/issues/hardware/a-t14-measurement-has-no-way-back-when-the-t14-stops-answering.md b/issues/hardware/a-t14-measurement-has-no-way-back-when-the-t14-stops-answering.md
new file mode 100644
index 00000000000..6db17500788
--- /dev/null
+++ b/issues/hardware/a-t14-measurement-has-no-way-back-when-the-t14-stops-answering.md
@@ -0,0 +1,20 @@
+---
+status: open
+kind: tooling
+opened: 2026-09-28
+---
+
+# A T14 measurement has no way back when the T14 stops answering
+
+A measurement under the T14's Ubuntu, such as the LLVM bar in
+`issues/build/toyos-builds-itself.md`, is driven and read over ssh to
+`t14@192.168.1.46`. At about 19:20Z on 2026-09-28, during the bar's `s3-2`
+span, the T14 stopped answering ping and ssh, so the run's state, and whether
+its driver and root sampler still run, cannot be read. Which of the T14's
+links that address is on is unread; `ip route get` to the development Mac,
+run on the T14, reads it.
+
+Owner: the orchestrator, which runs the T14
+(`issues/hardware/the-t14-answers-only-through-a-usb-stick.md`). Exit: a second
+path to the T14 under Ubuntu, independent of the link that address is on, that
+the orchestrator reaches when that link is down.
diff --git a/toyos-llvmbar/Cargo.toml b/toyos-llvmbar/Cargo.toml
new file mode 100644
index 00000000000..48daa3ffef9
--- /dev/null
+++ b/toyos-llvmbar/Cargo.toml
@@ -0,0 +1,13 @@
+# A member of the host workspace (root `Cargo.toml`): the T14 LLVM bar's steps
+# and its judge.
+
+[package]
+name = "toyos-llvmbar"
+description = "The T14 LLVM bar: the steps its Linux run takes on the T14, and the judge of the files they write."
+version = "0.1.0"
+edition = "2021"
+license = "MIT OR Apache-2.0"
+publish = false
+
+[lints.rust]
+warnings = "deny"
diff --git a/toyos-llvmbar/src/lib.rs b/toyos-llvmbar/src/lib.rs
new file mode 100644
index 00000000000..cee142ba1a0
--- /dev/null
+++ b/toyos-llvmbar/src/lib.rs
@@ -0,0 +1,597 @@
+//! The judge of the T14 LLVM bar (`issues/build/toyos-builds-itself.md`).
+//!
+//! On the T14, `t14/sampler.sh
` runs as root and `t14/driver.sh ` as
+//! the build's user, `` holding `t14/block.txt`. The judge reads what they
+//! leave there, and the T14 capture of
+//! `issues/hardware/linuxs-readings-of-the-t14-and-the-tcg-model-are-not-committed.md`:
+//! its `/proc/version`, vulnerabilities and `/proc/cpuinfo` text.
+//!
+//! A stage-3 span `s3-`, `k >= 1`, is a valid sample when:
+//! - `samples-s3-.log` opens with a `read=start` row and closes with a
+//! `read=end` row, every row between is `read=timed`, and every row is
+//! `phase=s3-`;
+//! - every row reads every element of [`ENVELOPE`], [`ENVELOPE_EVERY_CPU`],
+//! [`LINUX`] and [`LINUX_EVERY_CPU`] at its value, and [`MICROCODE`] on every
+//! CPU;
+//! - consecutive rows start at most [`MAX_GAP_MS`] apart;
+//! - `s3--time.txt`, GNU `time -v`'s, reads exit status 0 and file system
+//! inputs 0;
+//! - `s3--machine-start.txt` and `s3--machine-end.txt` both read kernel
+//! [`KERNEL`], one command line, one BIOS version and one boot, and the
+//! capture's `/proc/version`, vulnerabilities and microcode lines;
+//! - it is warm: `s3-` exited 0, and its end read precedes this span's
+//! start read.
+//!
+//! The bar is the shortest wall of the valid samples once there are
+//! [`SAMPLES`] of them.
+
+use std::collections::BTreeMap;
+
+/// The T14's logical CPUs, every one of which a per-CPU element is read on.
+pub const CPUS: usize = 8;
+/// How many valid samples the bar is the best of.
+pub const SAMPLES: usize = 3;
+/// The longest a span may go between the starts of two reads.
+pub const MAX_GAP_MS: u64 = 61_000;
+/// The kernel the capture pins.
+pub const KERNEL: &str = "6.8.0-142-generic";
+/// The microcode revision, `IA32_BIOS_SIGN_ID` (0x8B) bits 63:32, on every CPU.
+pub const MICROCODE: u32 = 0xbe;
+
+/// The power envelope's package elements, a ToyOS run's as much as Linux's:
+/// the sampler's key and the value it reads.
+pub const ENVELOPE: &[(&str, &str)] = &[
+ ("ac", "1"),
+ ("platform_profile", "performance"),
+ // MSR_PKG_POWER_LIMIT: PL1 64 W over 28 s, PL2 64 W over 2.44 ms, both
+ // enabled, unlocked.
+ ("msr_610", "0042820000dd8200"),
+ // The package limit through MMIO, MCHBAR + 0x59A0: PL1 20 W over 28 s, PL2
+ // 64 W over 2.44 ms.
+ ("mmio_59a0", "0042820000dd80a0"),
+ // MSR_VR_CURRENT_CONFIG: the peak limit, 121 W.
+ ("msr_601", "00000000000003c8"),
+ // IA32_HWP_REQUEST_PKG.
+ ("msr_772", "000000008000ff01"),
+];
+
+/// The power envelope's per-CPU elements, each read on every CPU.
+pub const ENVELOPE_EVERY_CPU: &[(&str, &str)] = &[
+ // IA32_PM_ENABLE: HWP on.
+ ("msr_770", "0000000000000001"),
+ // IA32_HWP_REQUEST: minimum 4, maximum 42, desired 0, EPP 128, activity
+ // window 0, package control off.
+ ("msr_774", "0000000080002a04"),
+ // IA32_ENERGY_PERF_BIAS.
+ ("msr_1b0", "0000000000000006"),
+ // IA32_MISC_ENABLE bit 38 clear: turbo enabled.
+ ("msr_1a0_b38", "0"),
+];
+
+/// How Linux reaches the envelope, read back beside it: powercap's limits in
+/// µW (PL1, PL2, peak) and windows in µs (PL1, PL2) for the MSR and the MMIO
+/// package limit, and intel_pstate.
+pub const LINUX: &[(&str, &str)] = &[
+ ("rapl_msr", "64000000,64000000,121000000,27983872,2440"),
+ ("rapl_mmio", "20000000,64000000,121000000,27983872,2440"),
+ ("pstate", "active"),
+ ("no_turbo", "0"),
+ ("hwp_dynamic_boost", "0"),
+];
+
+/// Linux's per-CPU cpufreq settings, each read on every CPU.
+pub const LINUX_EVERY_CPU: &[(&str, &str)] = &[
+ ("governor", "powersave"),
+ ("epp", "balance_performance"),
+ ("min_khz", "400000"),
+ ("max_khz", "4200000"),
+];
+
+const VULNERABILITY: &str = "/sys/devices/system/cpu/vulnerabilities/";
+const BIOS: &str = "/sys/class/dmi/id/bios_version:";
+const BOOT: &str = "/proc/sys/kernel/random/boot_id:";
+
+/// Every key a sampler row must carry and the value it must read.
+pub fn wanted() -> Vec<(&'static str, String)> {
+ let every_cpu = |v: &str| [v; CPUS].join(",");
+ let mut want: Vec<(&'static str, String)> = ENVELOPE
+ .iter()
+ .chain(LINUX)
+ .map(|&(k, v)| (k, v.to_string()))
+ .collect();
+ want.extend(
+ ENVELOPE_EVERY_CPU
+ .iter()
+ .chain(LINUX_EVERY_CPU)
+ .map(|&(k, v)| (k, every_cpu(v))),
+ );
+ want.push(("msr_8b", every_cpu(&format!("{MICROCODE:08x}00000000"))));
+ want
+}
+
+/// What Linux's T14 capture read that a span's machine reads must repeat.
+#[derive(Debug)]
+pub struct Capture {
+ version: String,
+ vulnerabilities: Vec,
+ microcode: Vec,
+}
+
+impl Capture {
+ /// Takes the `/proc/version`, vulnerabilities and microcode lines out of
+ /// the capture's text, whatever else it holds.
+ pub fn parse(text: &str) -> Result {
+ let versions: Vec<&str> = text
+ .lines()
+ .filter(|l| l.starts_with("Linux version "))
+ .collect();
+ let [version] = versions[..] else {
+ return Err(format!(
+ "the capture holds {} /proc/version lines, not 1",
+ versions.len()
+ ));
+ };
+ let mut vulnerabilities: Vec = text
+ .lines()
+ .filter(|l| l.starts_with(VULNERABILITY))
+ .map(String::from)
+ .collect();
+ vulnerabilities.sort();
+ if vulnerabilities.is_empty() {
+ return Err("the capture holds no vulnerabilities line".into());
+ }
+ let microcode: Vec = text.lines().filter_map(microcode).collect();
+ if microcode.len() != CPUS {
+ return Err(format!(
+ "the capture holds {} microcode lines, not {CPUS}",
+ microcode.len()
+ ));
+ }
+ Ok(Capture {
+ version: version.into(),
+ vulnerabilities,
+ microcode,
+ })
+ }
+}
+
+/// One machine read, as `driver.sh`'s `machine` writes it.
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub struct Machine {
+ pub version: String,
+ pub cmdline: String,
+ pub bios: String,
+ boot: String,
+ vulnerabilities: Vec,
+ microcode: Vec,
+}
+
+impl Machine {
+ pub fn parse(text: &str) -> Result {
+ let mut lines = text.lines();
+ let version = lines
+ .next()
+ .filter(|l| l.starts_with("Linux version "))
+ .ok_or("its first line is not /proc/version")?;
+ let cmdline = lines
+ .next()
+ .filter(|l| !l.is_empty())
+ .ok_or("its second line is not /proc/cmdline")?;
+ let (mut vulnerabilities, mut microcodes, mut bios, mut boot) =
+ (Vec::new(), Vec::new(), None, None);
+ for line in lines {
+ if line.starts_with(VULNERABILITY) {
+ vulnerabilities.push(line.to_string());
+ } else if let Some(m) = microcode(line) {
+ microcodes.push(m);
+ } else if let Some(b) = line.strip_prefix(BIOS) {
+ once(&mut bios, b, "BIOS version")?;
+ } else if let Some(b) = line.strip_prefix(BOOT) {
+ once(&mut boot, b, "boot_id")?;
+ } else {
+ return Err(format!("it holds a line nothing reads: {line:?}"));
+ }
+ }
+ vulnerabilities.sort();
+ if vulnerabilities.is_empty() {
+ return Err("it holds no vulnerabilities line".into());
+ }
+ if microcodes.len() != CPUS {
+ return Err(format!(
+ "it holds {} microcode lines, not {CPUS}",
+ microcodes.len()
+ ));
+ }
+ Ok(Machine {
+ version: version.into(),
+ cmdline: cmdline.into(),
+ bios: bios.ok_or("it holds no BIOS version")?,
+ boot: boot.ok_or("it holds no boot_id")?,
+ vulnerabilities,
+ microcode: microcodes,
+ })
+ }
+}
+
+fn once(slot: &mut Option, value: &str, what: &str) -> Result<(), String> {
+ match slot.replace(value.to_string()) {
+ None if !value.is_empty() => Ok(()),
+ None => Err(format!("its {what} is empty")),
+ Some(_) => Err(format!("it holds two {what} lines")),
+ }
+}
+
+/// The value of a `/proc/cpuinfo` `microcode` line.
+fn microcode(line: &str) -> Option {
+ let value = line
+ .strip_prefix("microcode")?
+ .trim_start()
+ .strip_prefix(':')?;
+ Some(value.trim().to_string())
+}
+
+/// Milliseconds since the epoch of a `date -u +%FT%T.%3NZ` stamp.
+pub fn utc_ms(stamp: &str) -> Result {
+ let bad = || format!("{stamp:?} is not a UTC stamp");
+ let b = stamp.as_bytes();
+ let separators = [
+ (4, b'-'),
+ (7, b'-'),
+ (10, b'T'),
+ (13, b':'),
+ (16, b':'),
+ (19, b'.'),
+ (23, b'Z'),
+ ];
+ if b.len() != 24 || separators.iter().any(|&(i, c)| b[i] != c) {
+ return Err(bad());
+ }
+ let n = |from: usize, to: usize| -> Result {
+ let digits = &stamp[from..to];
+ if digits.bytes().all(|c| c.is_ascii_digit()) {
+ digits.parse().map_err(|_| bad())
+ } else {
+ Err(bad())
+ }
+ };
+ let (y, m, d) = (n(0, 4)?, n(5, 7)?, n(8, 10)?);
+ let (hh, mm, ss, ms) = (n(11, 13)?, n(14, 16)?, n(17, 19)?, n(20, 23)?);
+ if !(1..=12).contains(&m) || !(1..=31).contains(&d) || hh > 23 || mm > 59 || ss > 60 {
+ return Err(bad());
+ }
+ // Days from 1970-01-01 in the proleptic Gregorian calendar, the year
+ // taken to start in March so the leap day ends it.
+ let y = if m <= 2 { y - 1 } else { y };
+ let era = y.div_euclid(400);
+ let yoe = y - era * 400;
+ let doy = (153 * ((m + 9) % 12) + 2) / 5 + d - 1;
+ let days = era * 146_097 + yoe * 365 + yoe / 4 - yoe / 100 + doy - 719_468;
+ u64::try_from(((days * 24 + hh) * 60 + mm) * 60 + ss)
+ .map(|s| s * 1000 + ms as u64)
+ .map_err(|_| bad())
+}
+
+/// Centiseconds of GNU time's `Elapsed (wall clock)` value, `m:ss.cc` or
+/// `h:mm:ss.cc`.
+pub fn wall_cs(value: &str) -> Result {
+ let bad = || format!("{value:?} is not a GNU time wall clock");
+ let (whole, cc) = value.split_once('.').ok_or_else(bad)?;
+ let numbers: Vec = whole
+ .split(':')
+ .chain([cc])
+ .map(|p| {
+ if !p.is_empty() && p.bytes().all(|c| c.is_ascii_digit()) {
+ p.parse().ok()
+ } else {
+ None
+ }
+ })
+ .collect::