Quick start
Invoke either the built binary or, during development, via Cargo:
protoxe-cli <command> [args...] [flags...] # development cargo run -p protoxe-cli -- <command> [args...]
Running with no command executes the built-in demo battery (assembles a stock RK3588 BOM and self-tests UART, IRQ, MMU, etc.) — a quick smoke test that the build works.
Flags may appear anywhere on the line; positional arguments are matched in order after flags are filtered out.
Addresses are hex (0x...); instruction counts accept K/M/G decimal suffixes where noted.
Common flags
Shared by the boot commands that support them.
Expose a GDB remote stub and wait for an attach. Attach with gdb-multiarch -ex 'target remote :1234'. Supports breakpoints, single step, register/memory inspection (MMU-translated), and reverse debugging via monitor record on + bs.
- --gdb → 127.0.0.1:1234
- --gdb=<port> → 127.0.0.1:<port>
- --gdb=:<port> → 0.0.0.0:<port> (all interfaces, e.g. attach from WSL)
- --gdb=<host>:<port> → that endpoint
Serve the JSON control API + web GUI while the guest runs. Default 127.0.0.1:9000. --api=:<port> binds all interfaces.
Record an instruction-coverage audit — which encodings the guest exercised, and which hit Unknown — for the ISA long-tail triage.
Environment variables
| Variable | Purpose |
|---|---|
| PROTOXE_QEMU | Path to qemu-system-aarch64 for the difftest oracle. |
| PROTOXE_RK_DTB=<dtb> | Boot an external device tree on bootrk3588 (drives the model with the real silicon's DTB; memory is clipped to model RAM). |
| PROTOXE_USB_KBD=1 | Enable the demo USB-HID keyboard auto-typer (bootrk3588). |
| PROTOXE_*_LOG | Per-device host-side tracing (built with the std feature). |
Boot real software
bootlinux
Boot an unmodified arm64 Linux kernel to an interactive shell on a QEMU-virt-shaped board (RAM, GICv3, PL011, virtio-blk, RAZ/WI sinks for the rest).
protoxe-cli bootlinux <Image> <dtb> [initramfs] [disk.ext4] [--audit] [--gdb[=port]]
- •
<Image>— the arm64Image(kernel 6.6). - •
<dtb>— the device tree (e.g. a generatedvirt.dtb). - •
[initramfs]— optional initramfs (cpio). - •
[disk.ext4]— optional ext4 image mounted r/w as/dev/vda(virtio-blk).
Supports interactive input (host stdin → PL011 RX IRQ), SMP (N cores from the DTB via PSCI CPU_ON), and a real on-disk ext4 root.
protoxe-cli bootlinux Image virt.dtb initramfs.cpio rootfs.ext4
bootrk3588
Boot Linux on the composed, silicon-validated RK3588 board (the real R1 device map).
protoxe-cli bootrk3588 <Image> [initramfs] [flags...]
| Flag | Effect |
|---|---|
| --smp | Full 4×Cortex-A76 + 4×Cortex-A55 (else a single core). |
| --disk <path> | Back the virtio-mmio transport with an image (→ /dev/vda). |
| --demo-disk | Build a minimal ext2 root in-process and boot off it. |
| --demo-init | Use a built-in demo init. |
| --fast | Run the fast decode-cache backend (bit-exact, ~10% faster on a full boot). |
| --replay-check | Snapshot a boot mid-flight, replay a window, and assert the machine digest matches (deterministic-replay oracle). |
| --audit, --gdb, --api | See common flags. |
PROTOXE_RK_DTB=r1-rk3588s.dtb protoxe-cli bootrk3588 Image --smp
bootoptee
Boot the real OP-TEE OS (Trusted OS) at Secure-EL1; a Rust EL3 monitor drives a Non-secure client through the real OPTEE_SMC ABI.
protoxe-cli bootoptee <tee.bin> [--audit] [--gdb[=port]]
boot (alias bootel2)
Load a bare-metal ELF payload and start the primary core at a chosen exception level (--el <N>), honouring the ELF entry. EL2 emulates an EL3 firmware handoff to a Type-1 hypervisor, EL1 a kernel, EL3 secure firmware.
The diagnostic of choice for bare-metal bring-up at any level: symbolized fault reporting (<symbol+0x..>), a decoded ESR/FAR/register dump for the current EL, stop-on-fault at the root cause, and a stuck/cascade detector. A BRK the payload places on purpose (any immediate except #0x800, reserved for the kernel WARN/BUG path) is a first-class software breakpoint / checkpoint.
bootel2 is the --el 2 shorthand — protoxe-cli bootel2 … is exactly protoxe-cli boot --el 2 ….
protoxe-cli boot [--el N] [--board file] [--load file@addr]... \ [--run] [--steps N] [--fast] [--gdb[=port]] \ [--rewind N] [--mmio-trace[=N][@LO-HI]] \ [--exc-trace[=N][@FILTER]] [--break-on-bug] \ [--to N] [--save f] [--resume f] <payload.elf>
Core options
| --el <N> | Enter at exception level N (0/1/2/3). Default 2. The end-of-run state dump and fault report read the banked ESR/FAR/ELR/SPSR for whichever EL the core is in. |
| --board <file> | Boot on an exact machine from a .board description (e.g. boards/r1-el2.board). Without it, a built-in RK3588-shaped board (512 MiB @ 0x40000000, GICv3, DW-8250 console, broad RAZ/WI sinks) is used. |
| --load <file>@<addr> | Place a flat blob (a Linux Image / dtb / initramfs) at <addr> on the bus, QEMU -device loader,file=,addr= style. Repeatable. |
| --run | Alias --no-stop-on-fault. Do not stop on the payload's own fault; let it handle every exception itself. |
| --steps <N> | Alias --run-insns. Instruction budget; suffix K/M/G (--steps 10G), or 0 for unbounded. Default 200M. |
| --fast | The fast decode-cache backend (bit-exact, ~7× faster); shortens a boot-per-iteration loop. |
See Diagnostic suite for --gdb, --rewind, --mmio-trace, --exc-trace, --break-on-bug.
protoxe-cli bootel2 --board boards/r1-el2.board \ --load linux-guest/Image@0x40800000 \ --load guest.dtb@0x46800000 \ --load initramfs.cpio@0x47000000 \ --steps 10G hypervisor_rk3588.elf
bootriscv
Run a RISC-V (RV64) payload on a virt-shaped board with a console UART. Experimental.
protoxe-cli bootriscv [program.elf | program.bin] \ [--steps N] [--fast] [--gdb[=port]] \ [--rewind N] [--mmio-trace[=N][@LO-HI]]
No argument runs a built-in hello demo that drives the console UART. An ebreak is a software breakpoint that stops the run.
bootriscv hands off to the same shared diagnostic runner as boot, so it inherits every diagnostic described below — symbolized fault reporting, the register dump
(here RISC-V mcause/mtval/mepc/mstatus + x0–x31),
the stuck detector, recent activity, the MMIO access trace, snapshot rewind/trail, and the GDB stub.
Diagnostic suite
The flag set every boot/bootriscv run can opt into. Off by default, zero-overhead when off.
--rewind <N>
Time-travelKeep a single rolling whole-machine snapshot, refreshed every N instructions. On any stop (fault / stuck / step-limit) the machine is restored to that point (≤N before) and, with --gdb, the stub is served there so you single-step forward into the fault; without --gdb, it prints an instruction trail into the stop (the last ~48 EL/PC).
Snapshot-backed replay is the sound form of reverse: a pure undo-log can't un-do device side effects — a UART tx, a GIC ack.
--mmio-trace[=<N>][@<lo>-<hi>]
Device-register ringCapture the most recent N MMIO accesses (default 256) into a ring and print them on a stop: sequence number, direction, size, address annotated with the device it hit (<name+0xoff>), and the value written/read.
Bulk-memory (RAM) traffic is excluded — this is the register stream only. An optional inclusive address window @<lo>-<hi> restricts capture (e.g. --mmio-trace=512@0xFE600000-0xFE60FFFF keeps only GIC-distributor traffic). Accesses the device rejects are flagged !FAULT.
--exc-trace[=<N>][@<filter>]
Ordered exception streamCapture the most recent N (default 64) exceptions/interrupts taken into a ring and print the ordered sequence on a stop: sequence number, target level, kind + decoded class (sync EC=0x.., or IRQ/FIQ with its INTID), the faulting/return address ELR (symbolized) and FAR.
Telemetry gives counts; this gives the order — the tool for an exception-nesting overflow. Optional @<filter> restricts capture to a target EL (EL0..EL3) or kind (irq/fiq/sync/serror).
--break-on-bug
Pre-panic catchStop the run at a kernel BRK #0x800 (the BUG/WARN entry, normally vectored to EL1) before its byte-by-byte panic-print floods the trace. Combine with --exc-trace/--rewind to catch the recursion that led to the panic instead of the post-panic park.
--save <f> · --resume <f> · --to <N>
ReproducersCapture and share machine state at any icount. <command> --to <icount> replays to the exact point of a bug every time — the deterministic-reproducer story.
At the end of a run, boot prints exception/interrupt telemetry: synchronous exceptions by target EL and ESR class, plus IRQ/FIQ/SError counts per EL. A flat/missing line is itself a signal.
Boards as data
protoXE topologies are .board text files — M2 declarative composition. See the examples under boards/ and the format in protoxe-cli/src/board_file.rs.
board
Assemble (validate + build) a machine from a .board description and report it.
protoxe-cli board <file.board> protoxe-cli board boards/r1.board # → assembled board from boards/r1.board: 8 cores, 24 devices
dtbimport
Generate a .board from a flattened device tree — best-effort: known nodes are mapped, unmodelled peripherals are emitted as commented sink suggestions.
protoxe-cli dtbimport <in.dtb|in.dts> [out.board] protoxe-cli dtbimport r1-rk3588s.dtb boards/r1.board
A DTB is an OS-visibility artifact: it carries the device map but not bus latencies, clock trees, or SMMU internals. Great for boot + diagnostics, not cycle-level fidelity.
api
Serve the JSON-over-TCP control API (and web GUI) on a board loaded from a file, so a script or the GUI can drive/recompose the machine live.
protoxe-cli api <board-file> [host:port]
Default endpoint 127.0.0.1:9000.
Run / inspect
run
Load and run a bare ELF on a minimal board (no boot protocol).
protoxe-cli run <file.elf>
(no command) — demo battery
Assembles the stock RK3588 BOM and runs a self-test battery (UART TX, IRQ delivery, MMU translation, …), printing PASS/FAIL — a quick smoke test that the build works.
protoxe-cli
Validation oracles
difftest
The differential tester: drives QEMU (qemu-system-aarch64) paused on its gdbstub, injects instruction streams, and compares architectural state against protoXE in lockstep after every instruction; delta-debugging (ddmin) shrinks any divergence. Needs QEMU (set PROTOXE_QEMU).
protoxe-cli difftest [cases] [insns/case] [seed-hex] [port] [mode]
Positional: cases (default 200), insns per case (16), seed (hex, 0xC0FFEE), gdb port (1234). The mode is a keyword anywhere on the line:
| Mode | What it validates |
|---|---|
| (none) | integer data-processing, load/store (all addressing forms), branches |
| neon | + Advanced SIMD (three-same/diff/misc, shifts, reductions, TBL, LD1-4, crypto) |
| fp | FP arithmetic (f32/f64), FSQRT/FRINT, FP↔int conversions, ARM NaN propagation |
| sys | EL/exception machinery: CPACR FP trap, SVC — compares ESR/ELR/SPSR/EL |
| mmu | page-table walk + data abort — translated loads and FAR/ESR |
| replay | boot-oracle Phase 0: replay a fixed program's per-insn state + MMIO reads |
| irq | boot-oracle Phase 1: GICv3 SGI bring-up, retire-count-pinned injection |
| timer | boot-oracle Phase 2: CNTP physical-timer → PPI 30 |
| boot | boot-oracle Phase 3: unified mini-firmware (UART MMIO + IRQ + ERET resume) |
| el2 | EL2 exception machinery (virtualization=on) |
| el3 | Category-A secure world (secure=on): EL3 entry + SMC routing, OP-TEE SMC ABI |
| el3mem | just the S3 Secure-only memory-isolation scenario |
| optee | just the S4 OP-TEE discovery + CALL_WITH_ARG scenarios |
protoxe-cli difftest 500 32 0x1 1234 neon
cpuverify
Lock-step a CPU-under-test against the golden ARM64 reference model.
protoxe-cli cpuverify [insns] # a stand-in DUT vs the golden model (default 1000 insns) protoxe-cli cpuverify rtl # an RtlCpu (Verilator-shaped DUT) bridge, golden vs buggy protoxe-cli cpuverify rv # a real RV32I core verified against the golden model
rvoracle
A sovereign differential oracle for the RV64IM engine. The signature evidence: 2,000,000 RV64IM instructions, zero divergences.
protoxe-cli rvoracle [cases] [seed] # vs the in-house spec-direct reference protoxe-cli rvoracle qemu [cases] [seed] # vs a real qemu-system-riscv64 (gdbstub)
Authoritative source: docs/protoxe-cli.md in the protoXE repository.
Full technical documentation is available to Design Partners under NDA.