
Эмулятор RISC-V на Rust, загружающий Linux с JIT на ARM64/x86_64 и виртуальной памятью Sv39
Эмулятор RISC-V на Rust. Загружает Linux.
Машина реализует RV64IMAFDC с привилегированными уровнями M/S/U и виртуальной памятью Sv39.
Исполняет JIT на хостах ARM64 и x86_64, при необходимости переходя на интерпретатор.
Периферия: UART 16550, CLINT, PLIC и SBI 1.0. Есть одна внешняя зависимость: zstd.
Это не самый быстрый эмулятор. В режиме интерпретатора он работает примерно в три раза медленнее QEMU. В режиме JIT — примерно в двадцать девять раз медленнее. Связывание блоков кода пока не реализовано; это поможет. Мы говорим это честно, чтобы вы знали, что получаете.
Что он делает хорошо: он прост в управлении, предоставляет HTTP API для управления машиной, может выполнять голый ELF без ядра и умеет сохранять и восстанавливать полное состояние машины. Это полезные вещи для системной работы и тестирования.
cargo build --release
Бинарный файл: target/release/emuko. Создайте псевдоним.
alias emuko=target/release/emuko
emuko dow
Загружает ядро Debian RISC-V netboot и initrd в ~/.emuko/.
Проверяется SHA256. Собственное ядро не требуется.
emuko start
Эмулятор работает как демон. Вы подключены к его консоли. Вы увидите загрузку ядра. Вы получите оболочку.
| Клавиша | Действие |
|---|---|
| Ctrl+] | Отключиться (демон продолжает работу) |
| Ctrl+C | Прерывание в гостевой системе |
| Ctrl+D | EOF в гостевой системе |
emuko start # повторное подключение к консоли
emuko dump # вывод состояния CPU
emuko stop # приостановка выполнения
emuko con # продолжить
emuko step 1000 # пошаговое выполнение N инструкций
emuko snap # сделать снимок
emuko kill # завершение работы
Демон слушает http://127.0.0.1:7788/v1/api/ и принимает WebSocket-подключения к консоли
на ws://127.0.0.1:7788/v1/ws/uart. Оба доступны из скриптов.
Иногда вам не нужна операционная система. Вам нужна машина, бинарный файл и ничего между ними.
emuko run ./my_program.elf
Демон сбрасывает машину, загружает сегменты ELF по их физическим адресам и начинает выполнение. Вывод поступает через консоль UART. Демон уже должен быть запущен.
Для вывода на консоль из голого металла: сохраните байт по адресу 0x1000_0000.
Каталог examples/ содержит программы, работающие таким образом.
| Пример | Что делает |
|---|---|
bare_printf | Выводит сообщение каждые 10 миллионов итераций. Без libc. Прямая запись в UART. |
Для сборки требуется RISC-V кросс-компилятор.
На macOS: brew install riscv64-elf-gcc.
На Linux: apt install gcc-riscv64-linux-gnu.
emuko start
cd examples/bare_printf
make run
Состояние машины — регистры, память, всё — можно сохранять и восстанавливать.
emuko snap # снимок сейчас
emuko snap 5000000 # автоматический снимок каждые 5M шагов
emuko snap stop # остановить авто-снимки
emuko ls # список снимков
emuko restore <snapshot> # восстановить
Виртуальная машина использует фиксированную раскладку MMIO, совместимую с платформой QEMU RISC-V virt.
С настройками по умолчанию (load_addr=0x80200000, 1 ГБ RAM):
0x8000_0000 ── начало RAM
(промежуток — доступен для программ голого металла)
0x8020_0000 ── образ ядра
│ .text / .rodata / .data / .bss
└─ конец ядра
(свободно)
── initrd
── DTB
── область EFI (128 КБ, только для PE/UEFI ядер)
0xBFFF_FFFF ── конец RAM
DTB, initrd и EFI размещаются вниз от вершины RAM. Эмулятор проверяет пересечение с ядром при загрузке и останавливается с понятной ошибкой, если RAM слишком мала.
Сегменты ELF PT_LOAD размещаются по адресам p_vaddr.
bare_printf линкуется по адресу 0x8000_0000:
0x8000_0000 ── .text
── .rodata
── .data
── .bss (обнуляется start.S)
── стек (16 КБ выше BSS)
Нет ядра. Нет initrd. Нет DTB. Регистры начинаются с нуля, кроме sp,
который эмулятор устанавливает в ram_base + 0x100000 до того, как start.S вступает в работу.
Параметры можно передавать в командной строке, задавать через переменные окружения или помещать в emuko.yml.
Прямое сравнение с часто используемыми эмуляторами RISC-V.
✅ = поддерживается и задокументировано. — = не встроено.
Источники: QEMU virt · Spike · Renode
Apache 2.0. Смотрите LICENSE.
| Регион | Базовый адрес | Размер | Примечания |
|---|
| CLINT | 0x0200_0000 | 64 КБ | Таймер и программное IRQ |
| PLIC | 0x0C00_0000 | 4 МБ | 32 источника прерываний |
| UART | 0x1000_0000 | 256 Б | NS16550A, IRQ 10 |
| RAM | 0x8000_0000 | 1 ГБ | Настраивается |
| Параметр | Переменная окружения | По умолчанию | Примечания |
|---|
--ram-size | RAM_SIZE | 1 ГБ | RAM в байтах |
--backend | EMUKO_BACKEND | adaptive | adaptive, arm64_jit, amd64_jit, arm64, x86_64 |
--bootargs | BOOTARGS | последовательная консоль | Командная строка ядра |
| Функция | emuko | QEMU | Spike | Renode |
|---|
| JIT / динамическая трансляция | ✅ | ✅ | — | — |
| Сохранение и восстановление снимков | ✅ | ✅ | — | ✅ |
| Периодический авто-снимок | ✅ | — | — | ✅ |
| HTTP API для управления машиной | ✅ | — | — | — |
| WebSocket-консоль UART | ✅ | — | — | — |
| Программируемый мост UART-хост | ✅ | — | — | ✅ |
| Загрузка голого ELF (без ОС) | ✅ | — | — | — |
| Загрузка ядра одной командой + SHA256 | ✅ | — | — | — |
| Дифференциальная проверка JIT vs интерпретатор | ✅ | — | — | — |
| Отладка через GDB | — | ✅ | ✅ | ✅ |
| Большая экосистема устройств (PCIe, virtio) | — | ✅ | — | — |
| Многоузловое симулирование | — | — | — | ✅ |
| Широкий охват ISA (RVV, крипто и т.д.) | — | — | ✅ | — |