Skip to content

Instantly share code, notes, and snippets.

@etnt
Created May 20, 2026 17:20
Show Gist options
  • Select an option

  • Save etnt/65f2b171f38cd81b4b446d9f3803df70 to your computer and use it in GitHub Desktop.

Select an option

Save etnt/65f2b171f38cd81b4b446d9f3803df70 to your computer and use it in GitHub Desktop.
Running Erlang, BEAM and Tyn kernel
# Quickstart: Running BEAM on Tyn
Step-by-step guide to get the Erlang BEAM VM running on the Tyn microkernel with KVM/QEMU.
## Prerequisites
- Rust nightly toolchain with `rust-src` component
- QEMU with KVM support (`qemu-system-x86_64`)
- `musl-tools` (`sudo apt install musl-tools`)
- `autoconf`, `m4`, `gcc`, `make`
- Your user in the `kvm` group (`sudo usermod -aG kvm $USER`, then re-login)
This example ran on a HP ProDesk G4 Mini PC running Debian Linux:
```bash
$ uname -a
Linux ozzy 7.0.4+deb14-amd64 #1 SMP PREEMPT_DYNAMIC Debian 7.0.4-1 (2026-05-07) x86_64 GNU/Linux
```
## Step 1: Clone OTP 27
```bash
cd /path/to/tyn-kernel
git clone --branch OTP-27.3.4.2 https://github.com/erlang/otp.git otp27
```
## Step 2: Build OTP 27 ERTS (static musl binary)
```bash
cd otp27
./configure --disable-jit --without-javac --without-odbc --without-wx \
--without-termcap --without-ssl --without-ssh --without-megaco \
--without-diameter --without-observer --without-debugger \
--without-et --without-reltool --without-common-test --without-eunit \
--without-edoc --without-eldap --without-ftp --without-tftp \
--without-snmp --without-docs --without-mnesia \
CC=musl-gcc CFLAGS="-O2 -static" LDFLAGS=-static
make -j$(nproc)
cd ..
```
This produces `otp27/bin/x86_64-pc-linux-musl/beam.smp` (~9 MB static ELF).
## Step 3: Package the VFS (cpio archive)
The kernel and stdlib directories use **unversioned** paths. Check the actual version strings:
```bash
grep vsn otp27/lib/kernel/ebin/kernel.app # e.g. {vsn, "10.2.7.2"}
grep vsn otp27/lib/stdlib/ebin/stdlib.app # e.g. {vsn, "6.2.2.2"}
```
Create the staging directory and cpio archive:
```bash
rm -rf staging
mkdir -p staging/otp/bin staging/otp/lib/kernel/ebin staging/otp/lib/stdlib/ebin
cp otp27/bin/start.boot staging/otp/bin/
cp otp27/lib/kernel/ebin/*.beam staging/otp/lib/kernel/ebin/
cp otp27/lib/stdlib/ebin/*.beam staging/otp/lib/stdlib/ebin/
# Fallback copies at root level (for code_server fallback loading)
cp otp27/lib/kernel/ebin/*.beam staging/
cp otp27/lib/stdlib/ebin/*.beam staging/
# Build cpio
cd staging
find . -type f | sed 's|^\./||' | cpio -o -H newc > ../src/otp-rootfs.cpio
cd ..
# Install the ERTS binary
cp otp27/bin/x86_64-pc-linux-musl/beam.smp src/beam.smp.elf
```
**Important:** Directory names must NOT have version suffixes.
BEAM looks for `/otp/lib/kernel/ebin/`, not `/otp/lib/kernel-10.2.7.2/ebin/`.
## Step 4: Build the kernel
```bash
cargo build --release --target x86_64-tyn.json \
-Zbuild-std=core,alloc,compiler_builtins \
-Zbuild-std-features=compiler-builtins-mem \
-Zjson-target-spec
```
## Step 5: Run on QEMU/KVM
```bash
qemu-system-x86_64 \
-kernel target/x86_64-tyn/release/tyn-kernel \
-m 2560M -machine q35 -cpu host -enable-kvm -smp 2 \
-nographic -no-reboot -serial mon:stdio \
-device virtio-net-pci,netdev=net0,disable-legacy=on,disable-modern=off \
-netdev user,id=net0,hostfwd=tcp::5555-:8080
```
The serial console appears in the terminal where you launched QEMU.
**Notes:**
- `-smp 2` is more reliable than `-smp 8` (avoids a known ERTS thread-progress race)
- If QEMU fails with "Permission denied" for KVM, ensure your user is in the `kvm` group
- To exit QEMU: press `Ctrl-A` then `X`
## Step 6: Test
Wait for `listening` on the serial console, then from another terminal:
```bash
echo "Hello from Tyn!" | nc localhost 5555
# → Hello from Tyn!
```
## Changing the BEAM startup expression
The `-eval` argument in `src/main.rs` controls what Erlang code runs after OTP boots.
Look for the `args` array around line 125. The current TCP echo server:
```rust
b"-eval\0", b"{ok, L} = gen_tcp:listen(8080, [...]), ...\0",
```
After editing, rebuild the kernel (step 4) and relaunch QEMU (step 5).
## Troubleshooting
| Symptom | Cause | Fix |
|---------|-------|-----|
| `Permission denied` for KVM | User not in `kvm` group | `sudo usermod -aG kvm $USER` + re-login |
| `.json target specs require -Zjson-target-spec` | Newer Rust nightly | Add `-Zjson-target-spec` to cargo build |
| `ENOENT /otp/lib/stdlib/ebin/...` then crash | Missing .beam files in cpio | Rebuild cpio with correct paths |
| Hangs at `[clock_gettime] called 1000 times` | ERTS thread-progress race | Kill QEMU, retry, or use `-smp 2` |
| `curl: Received HTTP/0.9 when not allowed` | Echo server returns raw bytes, not HTTP | Use `nc` or `curl --http0.9` |
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment