
RISC-V-Emulator in Rust, der Linux mit JIT auf ARM64/x86_64 und Sv39 virtuellem Speicher bootet
Ein RISC-V-Emulator in Rust. Er bootet Linux.
Die Maschine implementiert RV64IMAFDC mit M/S/U-Privilegstufen und Sv39 virtuellem Speicher.
Sie führt einen JIT auf ARM64- und x86_64-Hosts aus und fällt bei Bedarf auf einen Interpreter zurück.
Die Peripherie umfasst UART 16550, CLINT, PLIC und SBI 1.0. Es gibt eine externe Abhängigkeit: zstd.
Es ist nicht der schnellste Emulator. Im Interpreter-Modus läuft er etwa dreimal langsamer als QEMU. Im JIT-Modus etwa 29-mal langsamer. Code-Block-Chaining ist noch nicht implementiert; das wird helfen. Wir sagen das klar, weil Sie wissen sollten, was Sie bekommen.
Was es gut kann: Es ist einfach zu bedienen, es stellt eine HTTP-API für die Maschinensteuerung bereit, es kann ein Bare-Metal-ELF ohne Kernel ausführen, und es kann den vollständigen Maschinenzustand speichern und wiederherstellen. Das sind nützliche Dinge für Systemarbeit und Tests.
cargo build --release
Die Binärdatei ist target/release/emuko. Erstellen Sie einen Alias.
alias emuko=target/release/emuko
emuko dow
Lädt einen Debian RISC-V Netboot-Kernel und initrd nach ~/.emuko/ herunter.
SHA256 wird überprüft. Es ist kein eigener Kernel erforderlich.
emuko start
Der Emulator läuft als Daemon. Sie sind mit seiner Konsole verbunden. Sie werden den Kernel booten sehen. Sie erhalten eine Shell.
| Taste | Effekt |
|---|---|
| Strg+] | Trennen (Daemon läuft weiter) |
| Strg+C | Unterbrechen im Gast |
| Strg+D | EOF im Gast |
emuko start # reattach console
emuko dump # print CPU state
emuko stop # pause execution
emuko con # continue
emuko step 1000 # single-step N instructions
emuko snap # take a snapshot
emuko kill # shut down
Der Daemon hört auf http://127.0.0.1:7788/v1/api/ und akzeptiert WebSocket-Konsolenverbindungen
unter ws://127.0.0.1:7788/v1/ws/uart. Beide sind aus Skripten nutzbar.
Manchmal möchte man kein Betriebssystem. Man möchte die Maschine, eine Binärdatei und nichts dazwischen.
emuko run ./my_program.elf
Der Daemon setzt die Maschine zurück, lädt die ELF-Segmente an ihre physischen Adressen und beginnt mit der Ausführung. Die Ausgabe erfolgt über die UART-Konsole. Der Daemon muss bereits laufen.
Um von Bare-Metal aus auf die Konsole zu schreiben: Speichern Sie ein Byte an 0x1000_0000.
Das examples/-Verzeichnis enthält Programme, die auf diese Weise laufen.
| Beispiel | Funktion |
|---|---|
bare_printf | Druckt eine Nachricht alle 10 Millionen Iterationen. Keine libc. Direkter UART-Schreibzugriff. |
Ein RISC-V-Cross-Compiler ist erforderlich, um sie zu erstellen.
Unter macOS: brew install riscv64-elf-gcc.
Unter Linux: apt install gcc-riscv64-linux-gnu.
emuko start
cd examples/bare_printf
make run
Der Maschinenzustand – Register, Speicher, alles – kann gespeichert und wiederhergestellt werden.
emuko snap # snapshot now
emuko snap 5000000 # auto-snapshot every 5M steps
emuko snap stop # stop auto-snapshots
emuko ls # list snapshots
emuko restore <snapshot> # restore
Die virtuelle Maschine verwendet ein festes MMIO-Layout, das mit der QEMU RISC-V virt-Plattform kompatibel ist.
Mit Standardeinstellungen (load_addr=0x80200000, 1 GiB RAM):
0x8000_0000 ── RAM base
(gap — usable by bare-metal programs)
0x8020_0000 ── Kernel image
│ .text / .rodata / .data / .bss
└─ kernel end
(free)
── initrd
── DTB
── EFI region (128 KiB, PE/UEFI kernels only)
0xBFFF_FFFF ── RAM end
DTB, initrd und EFI werden vom oberen Ende des RAMs abwärts platziert. Der Emulator prüft auf Überschneidungen mit dem Kernel beim Booten und stoppt mit einer klaren Fehlermeldung, wenn der RAM zu klein ist.
ELF-PT_LOAD-Segmente werden an ihren p_vaddr-Adressen platziert.
bare_printf linkt bei 0x8000_0000:
0x8000_0000 ── .text
── .rodata
── .data
── .bss (zeroed by start.S)
── stack (16 KiB above BSS)
Kein Kernel. Kein initrd. Kein DTB. Register starten bei Null, außer sp,
das der Emulator auf ram_base + 0x100000 setzt, bevor start.S übernimmt.
Optionen können über die Kommandozeile übergeben, als Umgebungsvariablen gesetzt oder in emuko.yml abgelegt werden.
Direkter Vergleich mit häufig verwendeten RISC-V-Emulatoren.
✅ = unterstützt und dokumentiert. — = nicht integriert.
Quellen: QEMU virt · Spike · Renode
Apache 2.0. Siehe LICENSE.
| Region | Basis | Größe | Anmerkungen |
|---|
| CLINT | 0x0200_0000 | 64 KiB | Timer und Software-IRQ |
| PLIC | 0x0C00_0000 | 4 MiB | 32 Interruptquellen |
| UART | 0x1000_0000 | 256 B | NS16550A, IRQ 10 |
| RAM | 0x8000_0000 | 1 GiB | Konfigurierbar |
| Option | Umgebungsvariable | Standard | Anmerkungen |
|---|
--ram-size | RAM_SIZE | 1 GB | RAM in Bytes |
--backend | EMUKO_BACKEND | adaptive | adaptive, arm64_jit, amd64_jit, arm64, x86_64 |
--bootargs | BOOTARGS | serielle Konsole | Kernel-Befehlszeile |
| Funktion | emuko | QEMU | Spike | Renode |
|---|
| JIT / dynamische Übersetzung | ✅ | ✅ | — | — |
| Snapshot speichern und wiederherstellen | ✅ | ✅ | — | ✅ |
| Periodischer automatischer Snapshot | ✅ | — | — | ✅ |
| HTTP-API für Maschinensteuerung | ✅ | — | — | — |
| WebSocket-UART-Konsole | ✅ | — | — | — |
| Skriptierbare UART-Host-Brücke | ✅ | — | — | ✅ |
| Bare-Metal-ELF-Upload (kein OS) | ✅ | — | — | — |
| Kernel-Download mit einem Befehl + SHA256 | ✅ | — | — | — |
| JIT-vs.-Interpreter-Differenzprüfung | ✅ | — | — | — |
| GDB-Debugging | — | ✅ | ✅ | ✅ |
| Großes Geräte-Ökosystem (PCIe, virtio) | — | ✅ | — | — |
| Mehrknoten-Simulation | — | — | — | ✅ |
| Breite ISA-Abdeckung (RVV, Crypto, etc.) | — | — | ✅ | — |