No. 81 / project of 147 on the ladder

BusyBox PTY console

introduces — Linux devpts; BusyBox ash with a controlling terminal; raw UART screen bridge; lossless host-input handshake

harden statelast run2026-05-05
signoff
  • DRCNOT RUN
  • LVSNOT RUN
  • antennaNOT RUN

P80 got BusyBox running, but the first interactive shell was still a workaround: host input was shoved into ash from a helper process, not through a real terminal. P81 fixes that by creating a Linux PTY inside the initramfs and running /bin/sh -i with the PTY slave as its controlling terminal.

The host still connects with:

nix shell nixpkgs#screen -c screen /tmp/p81-busybox 115200

Now the data path is:

screen -> host PTY -> Verilator queue -> chip MMIO FIFO -> console_sh -> Linux PTY -> BusyBox ash

Why this mattered

Ash expects terminal behavior. Backspace, line editing, Ctrl-C, and arrow-key escape sequences are not just bytes in a pipe; they are interpreted by terminal line discipline and shell editing code. P81 mounts devpts, provides /dev/ptmx, opens a PTY, calls setsid(), uses TIOCSCTTY, and connects ash to the PTY slave.

Two host-side bugs showed up immediately:

  • The Verilator UART logger escaped ANSI bytes as printable \x1b text. Live screen runs now pass +uart_raw, so color and terminal escape sequences reach the host PTY as real bytes.
  • The host-input harness popped queued bytes using host_input_ready after the clock edge. When the 16-byte chip FIFO was full and the guest popped one byte, the host could incorrectly discard the next byte. The harness now samples the handshake before the accepting edge.

Verification

tty-smoke waits for the BusyBox prompt, then sends commands through the host FIFO:

P81-BUSYBOX-SMOKE
P81-UNAME-OK
P81-LS-BIN-OK
P81-BACKSPACE-OK
P81-FILE-OK

screen-smoke attaches a detached screen session to /tmp/p81-busybox and proves the same path through a real terminal client:

P81-SCREEN-INPUT-OK
P81-SCREEN-UNAME-OK
P81-SCREEN-BACKSPACE-OK
P81-SCREEN-TOUCH-OK
screen-file-ok
P81-SCREEN-FILE-OK

Honest Status

checkstatus
Static RV32 musl BusyBox packagePASS
Initramfs with /dev/ptmx and devptsPASS
Linux boots to BusyBox /initPASS
BusyBox ash runs on a PTY with a controlling terminalPASS
FIFO smoke proves uname, ls, touch, cat, and BackspacePASS
screen-smoke proves terminal input through screenPASS
Raw ANSI color reaches the host PTYPASS
LibreLane hardeningNOT RUN

What just happened?

We stopped treating the shell like a command parser hanging off a pipe. This is now a small but real terminal stack: Linux PTY inside the guest, host PTY outside the simulator, and a checked input handshake between them.