
Émulateur RISC-V en Rust qui démarre Linux avec JIT sur ARM64/x86_64 et mémoire virtuelle Sv39
Un émulateur RISC-V en Rust. Il démarre Linux.
La machine implémente RV64IMAFDC avec les niveaux de privilège M/S/U et la mémoire virtuelle Sv39.
Elle exécute un JIT sur les hôtes ARM64 et x86_64, avec un interpréteur en secours si nécessaire.
Les périphériques sont UART 16550, CLINT, PLIC et SBI 1.0. Une seule dépendance externe : zstd.
Ce n'est pas l'émulateur le plus rapide. En mode interpréteur, il est environ trois fois plus lent que QEMU. En mode JIT, environ vingt-neuf fois plus lent. Le chaînage de blocs de code n'est pas encore implémenté ; cela améliora les performances. Nous disons cela clairement pour que vous sachiez ce que vous obtenez.
Ce qu'il fait bien : il est simple à utiliser, expose une API HTTP pour le contrôle de la machine, peut exécuter un ELF bare-metal sans noyau, et peut sauvegarder et restaurer l'état complet de la machine. Ce sont des fonctionnalités utiles pour le travail système et les tests.
cargo build --release
Le binaire est target/release/emuko. Créez un alias.
alias emuko=target/release/emuko
emuko dow
Télécharge un noyau et un initrd Debian RISC-V netboot dans ~/.emuko/.
Le SHA256 est vérifié. Aucun noyau propre n'est nécessaire.
emuko start
L'émulateur s'exécute en tant que démon. Vous êtes attaché à sa console. Vous verrez le noyau démarrer. Vous obtiendrez un shell.
| Touche | Effet |
|---|---|
| Ctrl+] | Détachement (le démon continue) |
| Ctrl+C | Interruption dans l'invité |
| Ctrl+D | EOF dans l'invité |
emuko start # rattacher la console
emuko dump # afficher l'état du CPU
emuko stop # suspendre l'exécution
emuko con # continuer
emuko step 1000 # exécuter pas à pas N instructions
emuko snap # prendre un instantané
emuko kill # arrêter
Le démon écoute sur http://127.0.0.1:7788/v1/api/ et accepte les connexions console WebSocket
sur ws://127.0.0.1:7788/v1/ws/uart. Les deux sont utilisables depuis des scripts.
Parfois, vous ne voulez pas de système d'exploitation. Vous voulez la machine, un binaire, et rien d'autre entre eux.
emuko run ./my_program.elf
Le démon réinitialise la machine, charge les segments ELF à leurs adresses physiques, et commence l'exécution. La sortie arrive sur la console UART. Le démon doit déjà être en cours d'exécution.
Pour écrire sur la console depuis le bare-metal : stockez un octet à 0x1000_0000.
Le répertoire examples/ contient des programmes qui s'exécutent de cette façon.
| Exemple | Ce qu'il fait |
|---|---|
bare_printf | Affiche un message toutes les 10 millions d'itérations. Pas de libc. Écriture UART directe. |
Un compilateur croisé RISC-V est nécessaire pour les compiler.
Sur macOS : brew install riscv64-elf-gcc.
Sur Linux : apt install gcc-riscv64-linux-gnu.
emuko start
cd examples/bare_printf
make run
L'état de la machine — registres, mémoire, tout — peut être sauvegardé et restauré.
emuko snap # instantané maintenant
emuko snap 5000000 # instantané automatique toutes les 5M étapes
emuko snap stop # arrêter les instantanés automatiques
emuko ls # lister les instantanés
emuko restore <snapshot> # restaurer
La machine virtuelle utilise une disposition MMIO fixe compatible avec la plateforme virt QEMU RISC-V.
Avec les paramètres par défaut (load_addr=0x80200000, 1 Gio de RAM) :
0x8000_0000 ── Base RAM
(espace — utilisable par les programmes bare-metal)
0x8020_0000 ── Image du noyau
│ .text / .rodata / .data / .bss
└─ fin du noyau
(libre)
── initrd
── DTB
── Region EFI (128 Kio, noyaux PE/UEFI uniquement)
0xBFFF_FFFF ── Fin RAM
Le DTB, l'initrd et l'EFI sont placés vers le bas à partir du sommet de la RAM. L'émulateur vérifie les chevauchements avec le noyau au démarrage et s'arrête avec une erreur claire si la RAM est trop petite.
Les segments PT_LOAD de l'ELF sont placés à leurs adresses p_vaddr.
bare_printf est lié à 0x8000_0000 :
0x8000_0000 ── .text
── .rodata
── .data
── .bss (mis à zéro par start.S)
── pile (16 Kio au-dessus de BSS)
Pas de noyau. Pas d'initrd. Pas de DTB. Les registres démarrent à zéro sauf sp,
que l'émulateur initialise à ram_base + 0x100000 avant que start.S ne prenne le relais.
Les options peuvent être passées en ligne de commande, définies comme variables d'environnement ou placées dans emuko.yml.
Comparaison directe avec les émulateurs RISC-V couramment utilisés.
✅ = supporté et documenté. — = non intégré.
Sources : QEMU virt · Spike · Renode
Apache 2.0. Voir LICENSE.
| Région | Base | Taille | Remarques |
|---|
| CLINT | 0x0200_0000 | 64 Kio | Temporisateur et IRQ logicielle |
| PLIC | 0x0C00_0000 | 4 Mio | 32 sources d'interruption |
| UART | 0x1000_0000 | 256 o | NS16550A, IRQ 10 |
| RAM | 0x8000_0000 | 1 Gio | Configurable |
| Option | Variable d'env | Défaut | Remarques |
|---|
--ram-size | RAM_SIZE | 1 Go | RAM en octets |
--backend | EMUKO_BACKEND | adaptive | adaptive, arm64_jit, amd64_jit, arm64, x86_64 |
--bootargs | BOOTARGS | console série | Ligne de commande du noyau |
| Fonctionnalité | emuko | QEMU | Spike | Renode |
|---|
| Traduction JIT/dynamique | ✅ | ✅ | — | — |
| Sauvegarde et restauration d'instantané | ✅ | ✅ | — | ✅ |
| Instantané automatique périodique | ✅ | — | — | ✅ |
| API HTTP pour le contrôle machine | ✅ | — | — | — |
| Console UART WebSocket | ✅ | — | — | — |
| Pont UART hôte scriptable | ✅ | — | — | ✅ |
| Téléchargement d'ELF bare-metal (sans OS) | ✅ | — | — | — |
| Téléchargement du noyau en une commande + SHA256 | ✅ | — | — | — |
| Vérificateur différentiel JIT vs interpréteur | ✅ | — | — | — |
| Débogage GDB | — | ✅ | ✅ | ✅ |
| Grand écosystème de périphériques (PCIe, virtio) | — | ✅ | — | — |
| Simulation multi-nœuds | — | — | — | ✅ |
| Large couverture ISA (RVV, crypto, etc.) | — | — | ✅ | — |