
Emulador de RISC-V en Rust que arranca Linux con JIT en ARM64/x86_64 y memoria virtual Sv39
Un emulador RISC-V en Rust. Arranca Linux.
La máquina implementa RV64IMAFDC con niveles de privilegio M/S/U y memoria virtual Sv39.
Ejecuta un JIT en hosts ARM64 y x86_64, cayendo en un intérprete cuando es necesario.
Los periféricos son UART 16550, CLINT, PLIC y SBI 1.0. Hay una dependencia externa: zstd.
No es el emulador más rápido. En modo intérprete funciona aproximadamente tres veces más lento que QEMU. En modo JIT, aproximadamente veintinueve veces más lento. El encadenamiento de bloques de código aún no está implementado; eso ayudará. Lo decimos claramente para que sepas lo que estás obteniendo.
Lo que hace bien: es simple de operar, expone una API HTTP para control de la máquina, puede ejecutar un ELF bare-metal sin un kernel, y puede guardar y restaurar el estado completo de la máquina. Estas son cosas útiles para trabajo con sistemas y para pruebas.
cargo build --release
El binario es target/release/emuko. Crea un alias.
alias emuko=target/release/emuko
emuko dow
Descarga un kernel y initrd de Debian RISC-V netboot a ~/.emuko/.
Se verifica SHA256. No se requiere un kernel propio.
emuko start
El emulador se ejecuta como un daemon. Estás conectado a su consola. Verás arrancar el kernel. Obtendrás un shell.
| Tecla | Efecto |
|---|---|
| Ctrl+] | Desconectar (el daemon continúa) |
| Ctrl+C | Interrupción en el invitado |
| Ctrl+D | EOF en el invitado |
emuko start # reconectar consola
emuko dump # imprimir estado de la CPU
emuko stop # pausar ejecución
emuko con # continuar
emuko step 1000 # ejecutar paso a paso N instrucciones
emuko snap # tomar una instantánea
emuko kill # apagar
El daemon escucha en http://127.0.0.1:7788/v1/api/ y acepta conexiones de consola WebSocket
en ws://127.0.0.1:7788/v1/ws/uart. Ambos son utilizables desde scripts.
A veces no quieres un sistema operativo. Quieres la máquina, un binario, y nada más entre ellos.
emuko run ./my_program.elf
El daemon reinicia la máquina, carga los segmentos ELF en sus direcciones físicas, y comienza la ejecución. La salida llega a través de la consola UART. El daemon ya debe estar en ejecución.
Para escribir en la consola desde bare-metal: almacena un byte en 0x1000_0000.
El directorio examples/ contiene programas que se ejecutan de esta manera.
| Ejemplo | Qué hace |
|---|---|
bare_printf | Imprime un mensaje cada 10 millones de iteraciones. Sin libc. Escritura directa UART. |
Se requiere un cross-compilador RISC-V para construirlos.
En macOS: brew install riscv64-elf-gcc.
En Linux: apt install gcc-riscv64-linux-gnu.
emuko start
cd examples/bare_printf
make run
El estado de la máquina — registros, memoria, todo — puede guardarse y restaurarse.
emuko snap # instantánea ahora
emuko snap 5000000 # instantánea automática cada 5M pasos
emuko snap stop # detener instantáneas automáticas
emuko ls # listar instantáneas
emuko restore <snapshot> # restaurar
La máquina virtual utiliza un diseño MMIO fijo compatible con la plataforma RISC-V virt de QEMU.
Con configuración predeterminada (load_addr=0x80200000, 1 GiB de RAM):
0x8000_0000 ── Base RAM
(hueco — utilizable por programas bare-metal)
0x8020_0000 ── Imagen del kernel
│ .text / .rodata / .data / .bss
└─ fin del kernel
(libre)
── initrd
── DTB
── Región EFI (128 KiB, solo kernels PE/UEFI)
0xBFFF_FFFF ── Fin RAM
DTB, initrd y EFI se colocan hacia abajo desde la parte superior de la RAM. El emulador verifica si hay superposición con el kernel al arrancar y se detiene con un error claro si la RAM es demasiado pequeña.
Los segmentos ELF PT_LOAD se colocan en sus direcciones p_vaddr.
bare_printf enlaza en 0x8000_0000:
0x8000_0000 ── .text
── .rodata
── .data
── .bss (puesto a cero por start.S)
── stack (16 KiB por encima de BSS)
Sin kernel. Sin initrd. Sin DTB. Los registros comienzan en cero excepto sp,
que el emulador establece a ram_base + 0x100000 antes de que start.S tome el control.
Las opciones pueden pasarse en la línea de comandos, establecerse como variables de entorno,
o colocarse en emuko.yml.
Comparación directa con emuladores RISC-V comúnmente utilizados.
✅ = soportado y documentado. — = no integrado.
Fuentes: QEMU virt · Spike · Renode
Apache 2.0. Ver LICENSE.
| Región | Base | Tamaño | Notas |
|---|
| CLINT | 0x0200_0000 | 64 KiB | IRQ de temporizador y software |
| PLIC | 0x0C00_0000 | 4 MiB | 32 fuentes de interrupción |
| UART | 0x1000_0000 | 256 B | NS16550A, IRQ 10 |
| RAM | 0x8000_0000 | 1 GiB | Configurable |
| Opción | Var de Entorno | Por Defecto | Notas |
|---|
--ram-size | RAM_SIZE | 1 GB | RAM en bytes |
--backend | EMUKO_BACKEND | adaptive | adaptive, arm64_jit, amd64_jit, arm64, x86_64 |
--bootargs | BOOTARGS | consola serie | Línea de comandos del kernel |
| Característica | emuko | QEMU | Spike | Renode |
|---|
| JIT / traducción dinámica | ✅ | ✅ | — | — |
| Guardar y restaurar instantánea | ✅ | ✅ | — | ✅ |
| Instantánea automática periódica | ✅ | — | — | ✅ |
| API HTTP para control de máquina | ✅ | — | — | — |
| Consola UART WebSocket | ✅ | — | — | — |
| Puente host UART programable | ✅ | — | — | ✅ |
| Carga de ELF bare-metal (sin SO) | ✅ | — | — | — |
| Descarga de kernel con un comando + SHA256 | ✅ | — | — | — |
| Comprobador diferencial JIT vs intérprete | ✅ | — | — | — |
| Depuración GDB | — | ✅ | ✅ | ✅ |
| Amplio ecosistema de dispositivos (PCIe, virtio) | — | ✅ | — | — |
| Simulación multi-nodo | — | — | — | ✅ |
| Amplia cobertura ISA (RVV, cripto, etc.) | — | — | ✅ | — |