No. 79 / project of 147 on the ladder

Linux host TTY over SBI getchar

introduces — bidirectional hvc0 console input; host FIFO to SBI getchar; tiny interactive /init

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

P79 makes the Linux console bidirectional. The kernel was already using earlycon=sbi console=hvc0, so output flowed through SBI_CONSOLE_PUTCHAR. Input did not: stage-0 firmware always returned -1 from SBI_CONSOLE_GETCHAR.

The new path is intentionally direct:

host FIFO -> Verilator queue -> chip MMIO FIFO -> SBI getchar -> hvc0 -> /init read(0)

That keeps the proof focused on console transport. No UART device tree node, no Linux driver, no BusyBox yet.

Captured run

The smoke run rebuilt the local Linux 6.12.85 Image with P79’s initramfs, booted to /init, queued four commands, and stopped after the expected PID 1 exit panic.

P79 Linux host TTY ready
type 'help' or 'exit'
p79> help
commands: help hello status exit
p79> status
console=hvc0, input=SBI getchar, host bridge=MMIO FIFO
p79> hello
hello from a Linux userspace tty on the chip
p79> exit
exiting PID 1; Linux should panic after this
Kernel panic - not syncing: Attempted to kill init

Harness accounting:

[harness] === run ended after 59905843 post-load cycles ===
[harness] stopped after kernel panic milestone
[harness] input bytes read=23 delivered=23 queued=0

What changed

layernew behavior
RTL16-entry host input FIFO at 0x10001008/0x1000100c
firmwareSBI_CONSOLE_GETCHAR pops one byte from that FIFO
harnessreads host bytes from captured/input.fifo and waits for host_input_ready
userspaceno-libc /init reads fd 0 and writes fd 1 through Linux syscalls

The host-side queue matters. Scripted input can arrive before Linux gets to userspace; the harness holds those bytes and only hands them to the chip when the 16-entry RTL FIFO has room.

BusyBox

BusyBox is the right next step for a useful Linux environment. P79 does not try to prove BusyBox at the same time as console input. The better sequence is:

  1. Prove stdin reaches Linux userspace. P79 did that.
  2. Package BusyBox as /init or under an initramfs root.
  3. Decide whether to keep HVC-over-SBI or add a real UART/console device model for Linux.

Verification

cd projects/79_linux_host_tty/userspace
make

cd /mnt/MediaVolume/home/jadams/src/linux-rv32-build/linux-6.12.85
./scripts/config --set-str INITRAMFS_SOURCE \
  /mnt/MediaVolume/home/jadams/src/gitlab.daringbit.com/josh/librelane-playground/projects/79_linux_host_tty/userspace/initramfs.cpio
make ARCH=riscv olddefconfig
make ARCH=riscv -j$(nproc) Image

cd /mnt/MediaVolume/home/jadams/src/gitlab.daringbit.com/josh/librelane-playground/projects/79_linux_host_tty/test
make tty-smoke \
  KERNEL_IMAGE=/mnt/MediaVolume/home/jadams/src/linux-rv32-build/linux-6.12.85/arch/riscv/boot/Image

Honest Status

checkstatus
Verilator model buildsPASS
Tiny RV32 /init buildsPASS
Linux boots to /initPASS
Host FIFO bytes reach chip MMIO FIFOPASS
SBI getchar returns queued host bytesPASS
Linux userspace reads stdin from /dev/consolePASS
Scripted commands produce expected stdoutPASS
BusyBox initramfsNOT RUN
F/D architectural complianceNOT RUN
LibreLane hardeningNOT RUN

What just happened?

The homemade-chip Linux console stopped being print-only. A host shell can now send bytes into a running Linux userspace process through the same HVC console Linux already used for output.