diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0f8316b..5d796d9 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -2,7 +2,6 @@ name: Kernel CI on: push: - branches: [main] pull_request: branches: [main] workflow_dispatch: @@ -15,87 +14,272 @@ permissions: contents: read jobs: - x86_64: - name: x86_64 freestanding boot + tinycore-x86_64: + name: Tiny Core CorePure64 system health runs-on: ubuntu-latest timeout-minutes: 15 steps: - - name: Checkout kernel - uses: actions/checkout@v7 - - - name: Checkout Flow compiler + - name: Checkout Flow kernel integration uses: actions/checkout@v7 - with: - repository: flooooooooooow/flow - path: flow - - name: Set up Flow Python environment - uses: ./flow/.github/actions/setup-python + - name: Validate diagnostic tooling + run: | + python3 -m py_compile diagnostics/check_boot.py diagnostics/compare_health.py + python3 - <<'PY' + import json + from pathlib import Path + contract = json.loads(Path('diagnostics/boot-sequence.json').read_text()) + assert contract['schema'] == 2 + ids = [s['id'] for s in contract['sequence']] + assert len(ids) == len(set(ids)) + assert ids[0] == 'kernel' + assert ids[-1] == 'complete' + PY - - name: Install freestanding and boot-test toolchain + - name: Install build and boot-test tools run: | sudo apt-get update sudo apt-get install -y --no-install-recommends \ + binutils \ clang \ + cpio \ + curl \ + file \ lld \ - grub-common \ - grub-pc-bin \ - xorriso \ qemu-system-x86 - - name: Build freestanding x86_64 ELF + - name: Cache Tiny Core base + uses: actions/cache@v4 + with: + path: /tmp/tinycore + key: tinycore-corepure64-17.1-linux-6.18.35 + + - name: Fetch and verify Tiny Core Linux base env: - FLOW: ${{ github.workspace }}/flow/flow + TC_MAJOR: 17.x + TC_VERSION: '17.1' + TC_KERNEL: 6.18.35-tinycore64 + run: | + bash tinycore/fetch.sh /tmp/tinycore + test -s /tmp/tinycore/vmlinuz64 + test -s /tmp/tinycore/corepure64.gz + test -s /tmp/tinycore/manifest.txt + file /tmp/tinycore/vmlinuz64 + gzip -t /tmp/tinycore/corepure64.gz + grep -Fx 'tinycore_version=17.1' /tmp/tinycore/manifest.txt + grep -Fx 'kernel_version=6.18.35-tinycore64' /tmp/tinycore/manifest.txt + cat /tmp/tinycore/manifest.txt + + - name: Boot stock Tiny Core kernel under QEMU + run: | + set +e + timeout 20s qemu-system-x86_64 \ + -machine accel=tcg \ + -cpu max \ + -m 256M \ + -kernel /tmp/tinycore/vmlinuz64 \ + -initrd /tmp/tinycore/corepure64.gz \ + -append 'console=ttyS0' \ + -serial file:/tmp/tinycore-serial.log \ + -display none \ + -no-reboot \ + -no-shutdown + rc=$? + set -e + + cat /tmp/tinycore-serial.log + if [[ "$rc" -ne 0 && "$rc" -ne 124 ]]; then + echo "QEMU exited unexpectedly with status $rc" >&2 + exit "$rc" + fi + grep -F 'Linux version 6.18.35-tinycore64' /tmp/tinycore-serial.log + if grep -Eqi 'Kernel panic|not syncing|BUG:|Oops:|general protection fault|Unable to mount root fs|No working init found|Attempted to kill init' /tmp/tinycore-serial.log; then + echo 'fatal signature detected in stock Tiny Core boot' >&2 + exit 1 + fi + + - name: Checkout Flow compiler externally + run: | + git clone --depth 1 https://github.com/flooooooooooow/flow.git /tmp/flow + test -x /tmp/flow/flow + test ! -e ./flow + + - name: Build libc-free Flow hello service + env: + FLOW: /tmp/flow/flow FLOW_HOST: python - PYTHONPATH: ${{ github.workspace }}/flow/src + PYTHONPATH: /tmp/flow/src run: | - chmod +x "$FLOW" x86_64/build.sh - bash x86_64/build.sh /tmp/flow-kernel-build - test -s /tmp/flow-kernel-build/flow-kernel.elf - grub-file --is-x86-multiboot2 /tmp/flow-kernel-build/flow-kernel.elf - llvm-readelf -h /tmp/flow-kernel-build/flow-kernel.elf - llvm-nm /tmp/flow-kernel-build/flow-kernel.elf | grep -F 'flow_export_kernel_main' - - - name: Build bootable GRUB image + chmod +x /tmp/flow/flow examples/build-hello.sh + bash examples/build-hello.sh /tmp/flow-hello-build + test -x /tmp/flow-hello-build/flow-hello + file /tmp/flow-hello-build/flow-hello + ! readelf -l /tmp/flow-hello-build/flow-hello | grep -F 'Requesting program interpreter' + ! nm -u /tmp/flow-hello-build/flow-hello | grep . + + - name: Add diagnostics and Flow service to CorePure64 run: | - mkdir -p /tmp/flow-kernel-iso/boot/grub - cp /tmp/flow-kernel-build/flow-kernel.elf /tmp/flow-kernel-iso/boot/flow-kernel.elf - cp x86_64/grub.cfg /tmp/flow-kernel-iso/boot/grub/grub.cfg - grub-mkrescue -o /tmp/flow-kernel.iso /tmp/flow-kernel-iso - test -s /tmp/flow-kernel.iso + mkdir -p /tmp/flow-overlay/opt + cp /tmp/flow-hello-build/flow-hello /tmp/flow-overlay/opt/flow-hello + cp diagnostics/init.sh /tmp/flow-overlay/opt/flow-diag-init + cp diagnostics/reboot_init.sh /tmp/flow-overlay/opt/flow-reboot-init + cp diagnostics/poweroff_init.sh /tmp/flow-overlay/opt/flow-poweroff-init + chmod 0755 \ + /tmp/flow-overlay/opt/flow-hello \ + /tmp/flow-overlay/opt/flow-diag-init \ + /tmp/flow-overlay/opt/flow-reboot-init \ + /tmp/flow-overlay/opt/flow-poweroff-init + + gzip -dc /tmp/tinycore/corepure64.gz > /tmp/flow-corepure64.cpio + ( + cd /tmp/flow-overlay + find . -print0 | cpio --null -o --format=newc --owner=0:0 + ) >> /tmp/flow-corepure64.cpio + gzip -9 < /tmp/flow-corepure64.cpio > /tmp/flow-corepure64.gz + gzip -t /tmp/flow-corepure64.gz - - name: Boot smoke under QEMU + - name: Run ordered Linux system-health sequence run: | set +e - timeout 10s qemu-system-x86_64 \ + timeout 30s qemu-system-x86_64 \ -machine accel=tcg \ - -m 128M \ - -cdrom /tmp/flow-kernel.iso \ - -serial file:/tmp/flow-kernel-serial.log \ + -cpu max \ + -m 256M \ + -kernel /tmp/tinycore/vmlinuz64 \ + -initrd /tmp/flow-corepure64.gz \ + -append 'console=ttyS0 rdinit=/opt/flow-diag-init' \ + -serial file:/tmp/flow-diagnostics-serial.log \ -display none \ -no-reboot \ -no-shutdown rc=$? set -e - cat /tmp/flow-kernel-serial.log + cat /tmp/flow-diagnostics-serial.log if [[ "$rc" -ne 0 && "$rc" -ne 124 ]]; then echo "QEMU exited unexpectedly with status $rc" >&2 exit "$rc" fi - grep -F 'Flow kernel: entry' /tmp/flow-kernel-serial.log - grep -F 'Flow kernel: boot contract accepted' /tmp/flow-kernel-serial.log + python3 diagnostics/check_boot.py \ + /tmp/flow-diagnostics-serial.log \ + --report /tmp/boot-health.json + cp /tmp/boot-health.json /tmp/boot-health-baseline-candidate.json + + - name: Compare health with committed baseline + run: | + python3 diagnostics/compare_health.py \ + /tmp/boot-health.json \ + diagnostics/baseline-health.json \ + --report /tmp/boot-health-regression.json + + - name: Verify reboot lifecycle path + run: | + set +e + timeout 20s qemu-system-x86_64 \ + -machine accel=tcg \ + -cpu max \ + -m 256M \ + -kernel /tmp/tinycore/vmlinuz64 \ + -initrd /tmp/flow-corepure64.gz \ + -append 'console=ttyS0 rdinit=/opt/flow-reboot-init' \ + -serial file:/tmp/flow-reboot-serial.log \ + -display none \ + -no-reboot + rc=$? + set -e + + cat /tmp/flow-reboot-serial.log + grep -F 'FLOW_LIFECYCLE REBOOT_START' /tmp/flow-reboot-serial.log + grep -F 'FLOW_LIFECYCLE REBOOT_REQUESTED' /tmp/flow-reboot-serial.log + if grep -Fq 'FLOW_LIFECYCLE REBOOT_FAILED' /tmp/flow-reboot-serial.log; then + echo 'guest reboot syscall returned unexpectedly' >&2 + exit 1 + fi + if [[ "$rc" -ne 0 ]]; then + echo "reboot lifecycle QEMU exited with unexpected status $rc" >&2 + exit "$rc" + fi + + - name: Verify poweroff lifecycle path + run: | + set +e + timeout 20s qemu-system-x86_64 \ + -machine accel=tcg \ + -cpu max \ + -m 256M \ + -kernel /tmp/tinycore/vmlinuz64 \ + -initrd /tmp/flow-corepure64.gz \ + -append 'console=ttyS0 rdinit=/opt/flow-poweroff-init' \ + -serial file:/tmp/flow-poweroff-serial.log \ + -display none \ + -no-reboot + rc=$? + set -e + + cat /tmp/flow-poweroff-serial.log + grep -F 'FLOW_LIFECYCLE POWEROFF_START' /tmp/flow-poweroff-serial.log + grep -F 'FLOW_LIFECYCLE POWEROFF_REQUESTED' /tmp/flow-poweroff-serial.log + if grep -Fq 'FLOW_LIFECYCLE POWEROFF_FAILED' /tmp/flow-poweroff-serial.log; then + echo 'guest poweroff syscall returned unexpectedly' >&2 + exit 1 + fi + if [[ "$rc" -ne 0 ]]; then + echo "poweroff lifecycle QEMU exited with unexpected status $rc" >&2 + exit "$rc" + fi + + - name: Probe upstream eBPF and BTF kernel configuration metadata + continue-on-error: true + run: | + CONFIG=/tmp/tinycore/config-6.18.35-tinycore64 + urls=( + 'https://repo.tinycorelinux.net/17.x/x86_64/release/src/kernel/config-6.18.35-tinycore64' + 'https://www.tinycorelinux.net/17.x/x86_64/release/src/kernel/config-6.18.35-tinycore64' + 'https://ftp.icm.edu.pl/packages/linux-tinycorelinux/17.x/x86_64/release/src/kernel/config-6.18.35-tinycore64' + ) + found=0 + for url in "${urls[@]}"; do + if curl --fail --location --connect-timeout 8 --max-time 30 --output "$CONFIG.part" "$url"; then + mv "$CONFIG.part" "$CONFIG" + echo "kernel_config_source=$url" | tee /tmp/tinycore/config-source.txt + found=1 + break + fi + rm -f "$CONFIG.part" + done + if [[ "$found" -ne 1 ]]; then + echo 'Kernel config metadata unavailable; live boot/system-health validation remains authoritative.' >&2 + exit 1 + fi + + grep -E '^(CONFIG_BPF|CONFIG_BPF_SYSCALL|CONFIG_BPF_JIT|CONFIG_KPROBES|CONFIG_BPF_EVENTS|CONFIG_DEBUG_INFO_BTF)=' \ + "$CONFIG" | tee /tmp/tinycore/bpf-config.txt + grep -Fx 'CONFIG_BPF=y' /tmp/tinycore/bpf-config.txt + grep -Fx 'CONFIG_BPF_SYSCALL=y' /tmp/tinycore/bpf-config.txt + grep -Fx 'CONFIG_BPF_JIT=y' /tmp/tinycore/bpf-config.txt + grep -Fx 'CONFIG_KPROBES=y' /tmp/tinycore/bpf-config.txt + grep -Fx 'CONFIG_BPF_EVENTS=y' /tmp/tinycore/bpf-config.txt + grep -Fx 'CONFIG_DEBUG_INFO_BTF=y' /tmp/tinycore/bpf-config.txt - - name: Upload kernel artifacts + - name: Upload system-health evidence if: always() uses: actions/upload-artifact@v4 with: - name: flow-kernel-x86_64 + name: tinycore-x86_64-system-health path: | - /tmp/flow-kernel-build/flow-kernel.elf - /tmp/flow-kernel.iso - /tmp/flow-kernel-serial.log + /tmp/tinycore-serial.log + /tmp/flow-diagnostics-serial.log + /tmp/flow-reboot-serial.log + /tmp/flow-poweroff-serial.log + /tmp/boot-health.json + /tmp/boot-health-baseline-candidate.json + /tmp/boot-health-regression.json + /tmp/tinycore/manifest.txt + /tmp/tinycore/config-6.18.35-tinycore64 + /tmp/tinycore/config-source.txt + /tmp/tinycore/bpf-config.txt + /tmp/flow-hello-build/flow-hello if-no-files-found: warn - retention-days: 7 + retention-days: 14 diff --git a/.github/workflows/diagnostics.yml b/.github/workflows/diagnostics.yml new file mode 100644 index 0000000..61b282c --- /dev/null +++ b/.github/workflows/diagnostics.yml @@ -0,0 +1,25 @@ +name: Diagnostics Unit Tests + +on: + push: + paths: + - 'diagnostics/**' + - '.github/workflows/diagnostics.yml' + pull_request: + paths: + - 'diagnostics/**' + - '.github/workflows/diagnostics.yml' + workflow_dispatch: + +permissions: + contents: read + +jobs: + diagnostics: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v7 + - name: Compile diagnostics tooling + run: python3 -m py_compile diagnostics/check_boot.py diagnostics/compare_health.py diagnostics/test_health.py + - name: Validate health protocol + run: python3 -m unittest diagnostics.test_health -v diff --git a/.github/workflows/pages.yml b/.github/workflows/pages.yml new file mode 100644 index 0000000..ac47bde --- /dev/null +++ b/.github/workflows/pages.yml @@ -0,0 +1,40 @@ +name: Pages + +on: + push: + branches: [main] + paths: + - 'site/**' + - '.github/workflows/pages.yml' + workflow_dispatch: + +permissions: + contents: read + pages: write + id-token: write + +concurrency: + group: pages + cancel-in-progress: true + +jobs: + deploy: + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v7 + + - name: Configure Pages + uses: actions/configure-pages@v5 + + - name: Upload site + uses: actions/upload-pages-artifact@v3 + with: + path: site + + - name: Deploy + id: deployment + uses: actions/deploy-pages@v4 diff --git a/README.md b/README.md index 233d51f..1398000 100644 --- a/README.md +++ b/README.md @@ -1,30 +1,71 @@ # Flow Kernel -A freestanding kernel written in Flow. +[![Kernel CI](https://github.com/flooooooooooow/flow-kernel/actions/workflows/ci.yml/badge.svg)](https://github.com/flooooooooooow/flow-kernel/actions/workflows/ci.yml) +[![Diagnostics](https://github.com/flooooooooooow/flow-kernel/actions/workflows/diagnostics.yml/badge.svg)](https://github.com/flooooooooooow/flow-kernel/actions/workflows/diagnostics.yml) +[![GitHub Pages](https://github.com/flooooooooooow/flow-kernel/actions/workflows/pages.yml/badge.svg)](https://github.com/flooooooooooow/flow-kernel/actions/workflows/pages.yml) -The first target is x86_64 booted through Multiboot2. Flow owns the kernel entry contract and policy-facing primitives; the architecture layer is restricted to CPU operations that cannot yet be expressed portably in Flow. +Flow systems integration on top of a deliberately tiny Linux base. -The current base boots in 32-bit Multiboot2 mode, establishes an identity-mapped 1 GiB long-mode address space with 2 MiB pages, enters x86_64 long mode, installs a 64 KiB kernel stack, and calls the stable Flow C ABI entry `flow_export_kernel_main`. The Flow entry validates the Multiboot2 contract and reports boot state over COM1 serial. +`flow-kernel` no longer implements its own bootloader, page tables, scheduler, interrupt subsystem, or virtual-memory manager. Those are Linux responsibilities. The base target is Tiny Core Linux CorePure64: a minimal command-line Linux system that gives Flow a mature x86_64 kernel, drivers, networking, processes, namespaces, cgroups, perf and the native Linux eBPF surface without dragging in a conventional desktop distribution. -## Build +## Architecture -The kernel consumes the Flow compiler as an external dependency. Set `FLOW` to the Flow driver you want to use, or place a sibling checkout at `../flow`. +```text +Linux kernel + ↑ +Tiny Core CorePure64 userspace + ↑ +Flow system services / kernel-facing components + ↑ +Flow eBPF, XDP, tracing and driver experiments +``` + +Tiny Core is the substrate, not a fork. We consume its `vmlinuz64` and `corepure64.gz` release artifacts directly. + +## Repository boundary + +`flooooooooooow/flow` owns language syntax, parser/type-system behaviour, generic compiler infrastructure and reusable target/backend machinery. `flow-kernel` owns Linux-specific ABI bindings, kernel-facing Flow libraries, eBPF program APIs and examples, loaders/control-plane code, Tiny Core packaging, kernel integration tests and systems benchmarks. Changes needed in the Flow compiler should be implemented upstream rather than copied into this repository. + +## Fetch the Tiny Core base + +The default tracks Tiny Core CorePure64 17.1 with Linux `6.18.35-tinycore64`. The fetch script tries configured public mirrors, verifies Tiny Core's published MD5 sidecars, and records a manifest containing the exact version, source mirror and checksums used. ```bash -git clone https://github.com/flooooooooooow/flow.git ../flow -FLOW=../flow/flow bash x86_64/build.sh +bash tinycore/fetch.sh ``` -The build requires Flow's normal transpiler dependencies plus `clang` and `ld.lld`. If `grub-file` is installed, the resulting ELF is also validated as Multiboot2. +Artifacts are placed under `build/tinycore/`. -## Boot +## Boot it ```bash -FLOW=../flow/flow bash x86_64/run.sh +bash tinycore/run.sh +``` + +This boots the Tiny Core Linux kernel and initramfs directly in QEMU with the serial console attached to the terminal. No GRUB image and no Flow-owned architecture bootstrap are involved. + +## Deterministic system-health sequence + +A successful boot is not treated as a single boolean. CI runs an ordered diagnostic PID 1 and validates the system as a state machine: + +```text +kernel → initramfs → PID 1 → procfs → sysfs → devices → writable state +→ CPU → memory → timer → RNG → processes → signals → pipes → filesystem +→ block devices → network → DNS → namespaces → cgroups → BPF → Flow → complete ``` -That additionally requires `grub-mkrescue` and `qemu-system-x86_64`. Successful boot reaches the serial message `Flow kernel: boot contract accepted`. +Every guest stage emits a stable serial marker and monotonic timestamp. The host verifier rejects missing/out-of-order required stages, kernel panic/oops/BUG/rootfs/init-failure signatures, and non-monotonic timing. Environment-dependent checks such as DNS, block-device presence and cgroups can report advisory degradation without being confused with boot failure. + +The generated `boot-health.json` records the overall health state, last known-good stage, per-stage timings, failures/degradation, and evidence such as kernel release, CPU count, RAM, entropy, block devices and network interfaces. A separate QEMU lifecycle probe verifies the guest reboot path. See [`diagnostics/README.md`](diagnostics/README.md). + +## Verification + +CI caches the Tiny Core base, revalidates the published checksum sidecars, records the exact source/version/checksums, captures serial boot logs, exercises the full system-health sequence, verifies a libc-free Flow executable inside the guest, validates reboot behaviour, and archives the resulting health/evidence reports. The eBPF/BTF feature set is checked from the real Tiny Core kernel config when that metadata is available instead of inferred from the Linux version. + +## Flow compiler + +Flow remains a separate dependency. Kernel-facing Flow programs in this repository should compile against Linux ABIs or to eBPF; the language/compiler belongs in `flooooooooooow/flow` and is checked out independently in CI. ## Roadmap -The next layers are Multiboot2 memory-map ingestion, a physical page allocator, interrupt/exception tables, timer-driven scheduling, syscall entry, virtual memory ownership, and then the eBPF verifier/interpreter/JIT hooks. eBPF should consume explicit kernel hook surfaces rather than becoming part of the boot substrate. +The next work is deliberately Linux-native: finish the Flow-to-eBPF verifier path, BTF-aware bindings, maps, verifier-safe helpers, tracepoint/kprobe hooks, XDP, TC hooks and eventually CO-RE-style relocatable programs. User-space Flow services can remain tiny and run directly on CorePure64. diff --git a/diagnostics/README.md b/diagnostics/README.md new file mode 100644 index 0000000..4d89748 --- /dev/null +++ b/diagnostics/README.md @@ -0,0 +1,85 @@ +# Linux system-health diagnostics + +`flow-kernel` treats boot as an ordered, testable state machine rather than a single QEMU smoke test. + +## Sequence + +The machine-readable contract is `boot-sequence.json`. The current mandatory path is: + +```text +artifact integrity +→ kernel entry +→ initramfs unpack +→ diagnostic PID 1 +→ procfs +→ sysfs +→ core devices +→ writable userspace +→ CPU +→ memory +→ monotonic clock/timer +→ RNG +→ process creation/wait +→ signal delivery +→ pipe IPC +→ filesystem semantics +→ block-device discovery (advisory) +→ loopback/network stack +→ DNS resolver configuration (advisory) +→ namespaces +→ cgroups (advisory) +→ BPF capability evidence +→ libc-free Flow execution +→ complete +``` + +Required stages fail the job. Advisory stages may produce `WARN`; the system report becomes `degraded` but the boot remains valid. + +## Guest protocol + +`init.sh` runs as initramfs PID 1 and emits stable serial records: + +```text +FLOW_DIAG 080 CLOCK_TIMER OK t_ms=1432 +FLOW_EVIDENCE cpu_count=1 +FLOW_EVIDENCE mem_total_kb=209072 +FLOW_DIAG SUMMARY HEALTHY t_ms=1720 +``` + +The sequence numbers are part of the test protocol. New checks should be inserted deliberately rather than relying on incidental kernel log ordering. + +## Host verification + +`check_boot.py` verifies: + +- required stages exist; +- stages occur in contract order; +- diagnostic timestamps are monotonic; +- fatal kernel signatures such as panic, oops, BUG, rootfs failure, init death or diagnostic failure are absent; +- advisory warnings are retained as degradation rather than hidden; +- guest evidence is copied into `boot-health.json`; +- the last known-good stage is always recorded. + +Run locally with: + +```bash +python3 diagnostics/check_boot.py serial.log --report boot-health.json +``` + +## Regression comparison + +`compare_health.py` compares a current report with a known-good report. It detects stages that disappeared, previously healthy stages that regressed, and large stage-timing regressions. Timing checks include an absolute slack so tiny early-boot measurements do not create noisy percentage regressions. + +```bash +python3 diagnostics/compare_health.py current.json baseline.json --report regression.json +``` + +A known-good CI artifact can be promoted to the repository baseline after it is reviewed. The comparison code is independent of how the baseline is stored. + +## Lifecycle + +`reboot_init.sh` is a separate PID-1 lifecycle probe. QEMU runs it with `-no-reboot`; a successful forced reboot therefore terminates the VM rather than silently starting another boot. This keeps reboot-path validation independent from the main health sequence. + +## Design rule + +A diagnostic should test one invariant and emit one stable result. External Internet reachability is not a boot prerequisite: DNS configuration and similar environment-dependent checks are advisory unless a later test profile explicitly requires networking. diff --git a/diagnostics/baseline-health.json b/diagnostics/baseline-health.json new file mode 100644 index 0000000..52807f8 --- /dev/null +++ b/diagnostics/baseline-health.json @@ -0,0 +1,53 @@ +{ + "schema": 2, + "baseline": { + "source_run": 32044879323, + "source_commit": "72ac38844223db3ac9f9c9aa0894d4203b368be6", + "tinycore_version": "17.1", + "kernel_release": "6.18.35-tinycore64", + "profile": "qemu-tcg-x86_64-256m" + }, + "ok": true, + "health": "degraded", + "last_good_stage": "complete", + "total_time_ms": 3740, + "evidence": { + "kernel_release": "6.18.35-tinycore64", + "cpu_count": "1", + "mem_total_kb": "236212", + "uptime_seconds": "3", + "entropy_available": "256", + "block_devices": "ram0,ram1,ram2,ram3,ram4,ram5,ram6,ram7,sr0,zram0", + "net_interfaces": "dummy0,lo,tunl0", + "dns_configured": "false", + "cgroup_capable": "true", + "bpf_capable": "true" + }, + "stages": [ + {"id":"kernel","severity":"required","status":"OK","ok":true,"degraded":false,"time_ms":null}, + {"id":"initramfs","severity":"required","status":"OK","ok":true,"degraded":false,"time_ms":null}, + {"id":"pid1","severity":"required","status":"OK","ok":true,"degraded":false,"time_ms":0}, + {"id":"procfs","severity":"required","status":"OK","ok":true,"degraded":false,"time_ms":1730}, + {"id":"sysfs","severity":"required","status":"OK","ok":true,"degraded":false,"time_ms":1770}, + {"id":"devices","severity":"required","status":"OK","ok":true,"degraded":false,"time_ms":1800}, + {"id":"write","severity":"required","status":"OK","ok":true,"degraded":false,"time_ms":1850}, + {"id":"cpu","severity":"required","status":"OK","ok":true,"degraded":false,"time_ms":1900}, + {"id":"memory","severity":"required","status":"OK","ok":true,"degraded":false,"time_ms":1940}, + {"id":"clock","severity":"required","status":"OK","ok":true,"degraded":false,"time_ms":3020}, + {"id":"rng","severity":"required","status":"OK","ok":true,"degraded":false,"time_ms":3120}, + {"id":"process","severity":"required","status":"OK","ok":true,"degraded":false,"time_ms":3150}, + {"id":"signal","severity":"required","status":"OK","ok":true,"degraded":false,"time_ms":3170}, + {"id":"pipe","severity":"required","status":"OK","ok":true,"degraded":false,"time_ms":3220}, + {"id":"filesystem","severity":"required","status":"OK","ok":true,"degraded":false,"time_ms":3350}, + {"id":"block","severity":"advisory","status":"OK","ok":true,"degraded":false,"time_ms":3440}, + {"id":"network","severity":"required","status":"OK","ok":true,"degraded":false,"time_ms":3520}, + {"id":"dns","severity":"advisory","status":"WARN","ok":true,"degraded":true,"time_ms":3560}, + {"id":"namespace","severity":"required","status":"OK","ok":true,"degraded":false,"time_ms":3580}, + {"id":"cgroup","severity":"advisory","status":"OK","ok":true,"degraded":false,"time_ms":3630}, + {"id":"bpf","severity":"required","status":"OK","ok":true,"degraded":false,"time_ms":3670}, + {"id":"flow","severity":"required","status":"OK","ok":true,"degraded":false,"time_ms":3700}, + {"id":"complete","severity":"required","status":"OK","ok":true,"degraded":false,"time_ms":3720} + ], + "degraded": ["advisory stage degraded: dns"], + "failures": [] +} diff --git a/diagnostics/boot-sequence.json b/diagnostics/boot-sequence.json new file mode 100644 index 0000000..8fec0af --- /dev/null +++ b/diagnostics/boot-sequence.json @@ -0,0 +1,46 @@ +{ + "schema": 2, + "kernel_version": "6.18.35-tinycore64", + "sequence": [ + {"id":"kernel","kind":"log","marker":"Linux version 6.18.35-tinycore64","severity":"required","description":"Linux kernel entered and serial console is alive"}, + {"id":"initramfs","kind":"log","marker":"Trying to unpack rootfs image as initramfs","severity":"required","description":"Kernel reached initramfs unpack"}, + {"id":"pid1","kind":"diag","seq":"010","name":"PID1_START","severity":"required","description":"Diagnostic init is executing as PID 1"}, + {"id":"procfs","kind":"diag","seq":"020","name":"PROCFS","severity":"required","description":"procfs mounted and PID 1 is visible"}, + {"id":"sysfs","kind":"diag","seq":"030","name":"SYSFS","severity":"required","description":"sysfs mounted and kernel device model is visible"}, + {"id":"devices","kind":"diag","seq":"040","name":"DEVICES","severity":"required","description":"core character devices and sysfs classes are available"}, + {"id":"write","kind":"diag","seq":"050","name":"WRITE_TEST","severity":"required","description":"early userspace can create and verify writable state"}, + {"id":"cpu","kind":"diag","seq":"060","name":"CPU","severity":"required","description":"at least one CPU is enumerated"}, + {"id":"memory","kind":"diag","seq":"070","name":"MEMORY","severity":"required","description":"memory accounting is present and non-zero"}, + {"id":"clock","kind":"diag","seq":"080","name":"CLOCK_TIMER","severity":"required","description":"monotonic uptime advances across a sleep"}, + {"id":"rng","kind":"diag","seq":"090","name":"RNG","severity":"required","description":"kernel random source can produce bytes"}, + {"id":"process","kind":"diag","seq":"100","name":"PROCESS","severity":"required","description":"process creation and wait status work"}, + {"id":"signal","kind":"diag","seq":"110","name":"SIGNAL","severity":"required","description":"signal delivery terminates a child process"}, + {"id":"pipe","kind":"diag","seq":"120","name":"PIPE_IPC","severity":"required","description":"pipe-based IPC transfers data"}, + {"id":"filesystem","kind":"diag","seq":"130","name":"FILESYSTEM","severity":"required","description":"create/read/write/rename/link filesystem operations work"}, + {"id":"block","kind":"diag","seq":"140","name":"BLOCK","severity":"advisory","description":"kernel exposes at least one block-device class entry"}, + {"id":"network","kind":"diag","seq":"150","name":"NETWORK","severity":"required","description":"loopback interface and network procfs state exist"}, + {"id":"dns","kind":"diag","seq":"160","name":"DNS","severity":"advisory","description":"resolver configuration contains a nameserver"}, + {"id":"namespace","kind":"diag","seq":"170","name":"NAMESPACES","severity":"required","description":"Linux namespace handles are exposed"}, + {"id":"cgroup","kind":"diag","seq":"180","name":"CGROUP","severity":"advisory","description":"cgroup filesystem capability is present"}, + {"id":"bpf","kind":"diag","seq":"190","name":"BPF","severity":"required","description":"kernel exposes BPF syscall/filesystem capability evidence"}, + {"id":"flow","kind":"diag","seq":"200","name":"FLOW_EXEC","severity":"required","description":"libc-free Flow executable ran successfully"}, + {"id":"complete","kind":"diag","seq":"210","name":"COMPLETE","severity":"required","description":"all required diagnostic stages completed in order"} + ], + "fatal_patterns": [ + "Kernel panic", + "not syncing", + "BUG:", + "Oops:", + "general protection fault", + "segfault at", + "Unable to mount root fs", + "No working init found", + "Attempted to kill init", + "FLOW_DIAG FAIL" + ], + "timing_policy": { + "warn_regression_ratio": 1.5, + "fail_regression_ratio": 3.0, + "absolute_slack_ms": 250 + } +} diff --git a/diagnostics/check_boot.py b/diagnostics/check_boot.py new file mode 100644 index 0000000..3e0b0ca --- /dev/null +++ b/diagnostics/check_boot.py @@ -0,0 +1,139 @@ +#!/usr/bin/env python3 +from __future__ import annotations + +import argparse +import json +import re +import sys +from pathlib import Path + +DIAG_RE = re.compile( + r"FLOW_DIAG\s+(?P\d{3})\s+(?P[A-Z0-9_]+)\s+" + r"(?POK|WARN)\s+t_ms=(?P