Skip to content

feat(perf): T-029 Phase 2 — EL0 syscall round-trip micro-bench (completes T-029) #175

feat(perf): T-029 Phase 2 — EL0 syscall round-trip micro-bench (completes T-029)

feat(perf): T-029 Phase 2 — EL0 syscall round-trip micro-bench (completes T-029) #175

Workflow file for this run

# Tyrne CI — gate for every push and pull request.
#
# Goal: every hygiene check that a contributor is expected to pass
# locally before opening a PR is also enforced here. A red CI blocks
# merge.
#
# What actually gates merge (configured in GitHub branch protection):
# - lint-and-host-test, kernel-build, host-stable-check (fast lane)
# - miri (required, slow)
# The `miri` job runs the host-test suite under Stacked Borrows; it is
# slower (~1–2 min in practice on the current small suite; historically
# budgeted at ~10–15 min) but a Miri regression is a hard stop. The
# `coverage` job is INFORMATIONAL only (it sets `continue-on-error: true`)
# and must NOT be added to the required-checks list until the post-T-011
# flip removes that flag — see docs/guides/ci.md §"Branch protection".
#
# Toolchain note: the kernel needs nightly (inline asm / lang items),
# so the lint-and-host-test, kernel-build, and miri jobs all run the
# pinned nightly ($NIGHTLY_PIN) — the same toolchain rust-toolchain.toml
# selects for in-repo `cargo` invocations. A separate host-stable-check
# job gives a genuine "host crates build clean on stable" signal.
#
# When this pipeline was born (2026-04-23, R6 retrospective work) the
# kernel-build target was aarch64-unknown-none. Any time a new crate or
# target lands, update the job matrix below.
name: CI
on:
push:
branches: [main, development]
pull_request:
branches: [main, development]
# Cancel in-progress runs for the same branch — we only care about
# the latest commit's verdict, not intermediate ones.
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
# Least-privilege for the auto-provisioned GITHUB_TOKEN. This pipeline
# only reads the repository and runs builds; it never writes refs,
# publishes artifacts, or touches issues/PRs. Override per-job only if a
# future job genuinely needs more — none do today.
permissions:
contents: read
env:
CARGO_TERM_COLOR: always
# NOTE: `-D warnings` is intentionally NOT set as a global RUSTFLAGS env
# var. A process-wide RUSTFLAGS REPLACES (does not merge) the per-target
# `[target.aarch64-unknown-none] rustflags` in .cargo/config.toml, which
# would silently drop `panic=abort` + `force-frame-pointers=yes` from the
# kernel build — CI would then compile a different ELF than a local build.
# Deny-warnings is enforced instead by the `-- -D warnings` already passed
# in the host-clippy / kernel-clippy aliases (.cargo/config.toml).
# Pinned nightly for the kernel + miri + coverage jobs. Rolling nightly means a
# miri/llvm-tools regression on the public channel breaks master
# without any commit of ours being the cause. Update this pin
# intentionally (open an issue citing the pin bump), not silently.
# When bumping, also refresh docs/guides/ci.md's "Nightly pinning"
# section. Last bumped: 2026-04-23 (initial pin).
NIGHTLY_PIN: nightly-2026-01-15
jobs:
# ─── Fast lane: fmt + clippy + host tests (pinned nightly) ──────────────
# Expected wall time: ~2 min. Every PR must pass this before anything
# else runs. Runs on the pinned nightly — `rust-toolchain.toml` overrides
# `rustup default` for in-repo cargo anyway, so we select the pin
# explicitly (matching the miri/coverage jobs) instead of pretending to
# run on stable. A genuine stable signal lives in `host-stable-check`.
lint-and-host-test:
name: fmt + clippy + host tests (nightly)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
# CI only reads the repo; do not persist the token into local git
# config (least-privilege; avoids credential reuse by later steps).
persist-credentials: false
- name: Install pinned nightly with rustfmt + clippy
run: |
rustup toolchain install $NIGHTLY_PIN --component rustfmt --component clippy --no-self-update
rustup override set $NIGHTLY_PIN
- name: Cache cargo registry and build
uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3
with:
path: |
~/.cargo/bin
~/.cargo/registry
~/.cargo/git
target
key: ${{ runner.os }}-cargo-${{ env.NIGHTLY_PIN }}-${{ hashFiles('**/Cargo.lock') }}
restore-keys: |
${{ runner.os }}-cargo-${{ env.NIGHTLY_PIN }}-
- name: cargo fmt --check
run: cargo +$NIGHTLY_PIN fmt --all -- --check
- name: cargo host-clippy
run: cargo +$NIGHTLY_PIN host-clippy
- name: cargo host-test
run: cargo +$NIGHTLY_PIN host-test
# ─── Kernel build: aarch64-unknown-none ─────────────────────────────────
# The bare-metal BSP cannot be built as part of the default workspace
# test target (no_std + no_main). Build it explicitly here so the
# kernel ELF and its dependencies stay compilable.
kernel-build:
name: aarch64-unknown-none kernel build (nightly)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
# CI only reads the repo; do not persist the token into local git
# config (least-privilege; avoids credential reuse by later steps).
persist-credentials: false
- name: Install pinned nightly + aarch64 target
run: |
# llvm-tools-preview provides rust-objcopy for the userland build
# step below (ADR-0039); it is pinned in rust-toolchain.toml but this
# explicit install does not read that file, so name it here.
rustup toolchain install $NIGHTLY_PIN --component clippy --component llvm-tools-preview --no-self-update
rustup override set $NIGHTLY_PIN
rustup target add aarch64-unknown-none --toolchain $NIGHTLY_PIN
- name: Cache cargo registry and build
uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3
with:
path: |
~/.cargo/bin
~/.cargo/registry
~/.cargo/git
target
key: ${{ runner.os }}-cargo-aarch64-${{ env.NIGHTLY_PIN }}-${{ hashFiles('**/Cargo.lock') }}
restore-keys: |
${{ runner.os }}-cargo-aarch64-${{ env.NIGHTLY_PIN }}-
- name: build userland image (ADR-0039)
# Produces userland/hello/hello.bin (rust-objcopy of the userland crate)
# which the BSP embeds via include_bytes!; MUST run before kernel-build,
# whose build.rs panics if the .bin is absent.
run: tools/build-userland.sh
- name: cargo kernel-build
run: cargo +$NIGHTLY_PIN kernel-build
- name: cargo kernel-clippy
run: cargo +$NIGHTLY_PIN kernel-clippy
# ─── Host stable check: host crates compile + test on stable ────────────
# A genuine "the host-buildable crates compile and pass tests on stable
# Rust" gate. The kernel image itself needs nightly (inline asm / lang
# items) so the bare-metal BSP is NOT built here — this job runs only the
# workspace default-members (kernel, hal, test-hal), which carry no
# nightly-only features today. If a host crate ever grows a `#![feature]`,
# this job is the one that will (correctly) go red on stable.
#
# Deliberately build + test only — NOT clippy/fmt with `-D warnings`.
# `clippy::pedantic` is `warn` workspace-wide and stable is a rolling
# toolchain: a future stable release can add a pedantic lint that turns a
# `-D warnings` gate red with no code change of ours. Lint/format
# enforcement therefore lives only on the pinned-nightly jobs (which run
# host-clippy + fmt), keeping this gate immune to upstream lint drift.
host-stable-check:
name: host crates on stable
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
# CI only reads the repo; do not persist the token into local git
# config (least-privilege; avoids credential reuse by later steps).
persist-credentials: false
- name: Install Rust stable
run: |
rustup toolchain install stable --no-self-update
- name: Cache cargo registry and build
uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3
with:
path: |
~/.cargo/bin
~/.cargo/registry
~/.cargo/git
target
key: ${{ runner.os }}-cargo-stable-${{ hashFiles('**/Cargo.lock') }}
restore-keys: |
${{ runner.os }}-cargo-stable-
# Explicit `+stable` bypasses the rust-toolchain.toml override so this
# job exercises stable for real. `cargo build` / `host-test` operate on
# default-members (host-buildable crates only); the bare-metal BSP is
# never built here. No `-D warnings` lint/fmt step — see the job header.
- name: cargo +stable build (host crates)
run: cargo +stable build
- name: cargo +stable host-test
run: cargo +stable host-test
# ─── Miri: aliasing validation ──────────────────────────────────────────
# Runs the full host-test suite under Miri's Stacked Borrows checker
# (see ADR-0021 / UNSAFE-2026-0014). Slower than the fast lane
# (~1–2 min in practice; historically budgeted ~10–15 min) and requires
# nightly, so it runs as its own job. A
# Miri regression is a hard stop.
miri:
name: miri (Stacked Borrows)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
# CI only reads the repo; do not persist the token into local git
# config (least-privilege; avoids credential reuse by later steps).
persist-credentials: false
- name: Install pinned nightly with miri
run: |
rustup toolchain install $NIGHTLY_PIN --component miri --no-self-update
rustup override set $NIGHTLY_PIN
- name: Cache cargo registry and miri target
uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3
with:
path: |
~/.cargo/bin
~/.cargo/registry
~/.cargo/git
target
key: ${{ runner.os }}-miri-${{ env.NIGHTLY_PIN }}-${{ hashFiles('**/Cargo.lock') }}
restore-keys: |
${{ runner.os }}-miri-${{ env.NIGHTLY_PIN }}-
- name: cargo miri setup
run: cargo +$NIGHTLY_PIN miri setup
- name: cargo miri test (excl. BSP)
run: cargo +$NIGHTLY_PIN miri test --workspace --exclude tyrne-bsp-qemu-virt
# ─── Coverage: informational ────────────────────────────────────────────
# Produces an lcov report. Does NOT fail on a coverage drop today
# (no agreed floor yet); it exists to make trends visible. When the
# workspace settles around its post-T-011 shape, flip this job to
# enforce a floor (≥ 94 % regions or similar).
coverage:
name: coverage (informational)
runs-on: ubuntu-latest
continue-on-error: true
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
# CI only reads the repo; do not persist the token into local git
# config (least-privilege; avoids credential reuse by later steps).
persist-credentials: false
- name: Install pinned nightly with llvm-tools
run: |
rustup toolchain install $NIGHTLY_PIN --component llvm-tools-preview --no-self-update
rustup override set $NIGHTLY_PIN
- name: Install pinned cargo-llvm-cov via prebuilt-binary action
# taiki-e/install-action downloads a prebuilt cargo-llvm-cov
# binary instead of compiling from source. Two wins versus the
# earlier `cargo install --locked`: (1) the version is pinned
# explicitly on the `tool:` line below, so an upstream
# cargo-llvm-cov release cannot silently change the coverage
# numbers; (2) it's a download rather than a 1-2 min compile,
# so the cache-miss path is fast.
# The cargo-llvm-cov version pin is independent of NIGHTLY_PIN —
# the two can be bumped together or independently per
# docs/guides/ci.md "Nightly pinning".
uses: taiki-e/install-action@e0eafa9a0d485c37f97c0f7beb930a58a2facbac # v2.79.4
with:
tool: cargo-llvm-cov@0.6.16
- name: Cache cargo registry and llvm-cov target
uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3
with:
# ~/.cargo/bin is intentionally omitted here (unlike the sibling
# jobs): cargo-llvm-cov is installed by taiki-e/install-action, not
# `cargo install`, so nothing useful lands in ~/.cargo/bin to cache.
path: |
~/.cargo/registry
~/.cargo/git
target
key: ${{ runner.os }}-llvmcov-${{ env.NIGHTLY_PIN }}-${{ hashFiles('**/Cargo.lock') }}
restore-keys: |
${{ runner.os }}-llvmcov-${{ env.NIGHTLY_PIN }}-
- name: cargo llvm-cov --summary-only
# Exclude the bare-metal crates: the BSP is no_std/no_main (cannot host-
# build) and the userland crates (tyrne-user / hello) host-compile only
# as aarch64-gated stubs (ADR-0039) — coverage of never-run stubs is
# meaningless and would only dilute the metric.
run: cargo llvm-cov --workspace --exclude tyrne-bsp-qemu-virt --exclude tyrne-user --exclude tyrne-userland-hello --summary-only