7.9 KiB
Docker/QEMU Emulation Scaffold
Scope
Workspace-local implementation:
emulation/
Purpose:
- Build a Docker-packaged QEMU environment for booting a generated copy of the extracted AArch64 rootfs.
- Expose the guest display through QEMU VNC and noVNC in a browser.
- Provide a browser dashboard for logs and early virtual switch events.
- Keep target rootfs evidence files unmodified.
Static-analysis boundary: this page documents the emulator scaffold and evidence-based design. Creating and booting emulation/work/rootfs.ext4 executes a generated guest copy, not the extracted evidence files directly.
Evidence Basis
The main game and SPIKE menu use a direct DRM/KMS display path rather than a desktop windowing path.
Confirmed string/import evidence:
/games/pokemon_pro/gameimportslibEGL.so.1,libGLESv2.so.2,libdrm.so.2, andlibgbm.so.1; seeanalysis/static-triage/game.objdump-p.txt./games/pokemon_pro/gamestrings include/sys/class/drm,/dev/dri/card1,/dev/dri/card0,drmModeSetCrtc fail,drmModePageFlip,gbm_bo_create,eglGetPlatformDisplayEXT, andegl resolution: %d x %d; seeanalysis/static-triage/game.strings./games/pokemon_pro/spike3/spike_menu/gamehas matching GBM/EGL/DRM strings at offsets including0x382cd0(/sys/class/drm),0x383030(/dev/dri/card1),0x383058(/dev/dri/card0), and0x383730(drmModeSetCrtc fail); seeanalysis/static-triage/spike_menu_game.strings./etc/init.d/S12lvds_panellaunches/games/spike3/bin/boot_displayearly, and/etc/init.d/game_monitorlater stopsboot_displaybefore launching/games/game.
Implementation consequence: the first emulator path uses QEMU virtio-gpu and browser-visible VNC/noVNC, not X11 forwarding.
Implemented Files
Primary files:
| Path | Purpose |
|---|---|
emulation/compose.yaml |
Docker Compose entry point and port mapping. |
emulation/docker/Dockerfile |
Debian-based image with QEMU, noVNC, ffmpeg, qemu-user-static, and AArch64 cross tools. |
emulation/docker/fetch-debian-arm64-kernel.sh |
Installs a Debian ARM64 kernel and initramfs into /opt/spike3-kernel/; the initramfs seeds DRM/virtio_gpu modules. |
emulation/scripts/prepare-rootfs-image.sh |
Copies target-root entries into emulation/work/rootfs-stage, injects helpers, and builds emulation/work/rootfs.ext4. |
emulation/scripts/run-qemu.sh |
Boots QEMU virt with virtio-blk, virtio-gpu, VNC, serial logging, user networking, and a virtio-serial control socket. |
emulation/guest/emu-init |
Guest init used by QEMU with init=/usr/local/spike-emu/bin/emu-init. |
emulation/dashboard/server.py |
Browser dashboard on port 8090 for links, logs, and virtual switch events. |
emulation/stubs/spike3emu_stub.c |
Optional AArch64 LD_PRELOAD stub for inert fake device opens/ioctls on GPIO, I2C, serial, input, and /dev/mem paths. |
Generated runtime files:
| Path | Purpose |
|---|---|
emulation/work/rootfs.ext4 |
Generated guest root disk; not checked in as evidence. |
emulation/work/logs/ |
Host/container logs for QEMU, dashboard, noVNC, and conagent backend. |
emulation/work/control.sock |
QEMU virtio-serial socket used by the dashboard event bridge. |
Usage
Start the default boot-display target:
docker compose -f emulation/compose.yaml up --build
Open:
- Dashboard:
http://localhost:8090/ - Display:
http://localhost:6080/vnc.html?host=localhost&port=6080&autoconnect=true - Conagent backend emulator:
http://localhost:8088/
Try later targets after the virtual display is visible:
SPIKE3_AUTOSTART=spike-menu docker compose -f emulation/compose.yaml up
SPIKE3_AUTOSTART=game docker compose -f emulation/compose.yaml up
SPIKE3_AUTOSTART=monitors docker compose -f emulation/compose.yaml up
The guest init accepts these autostart targets:
boot-displayspike-menugamemonitorsshell
Current Emulator Behavior
The generated guest image:
- Mounts
/proc,/sys,/dev,/dev/pts,/run, and/tmp. - Creates writable
/connectivity,/connectivity/dump,/data, and/tmp/cache/mesa. - Preserves
/games/game -> /games/pokemon_pro/game. - Injects
/usr/local/spike-emu/bin/launch-target.shto startboot_display, SPIKE menu, game, or monitors. - Injects an optional
LD_PRELOADhardware stub at/usr/local/spike-emu/lib/libspike3emu_stub.sowhen the Docker image hasaarch64-linux-gnu-gcc. - Injects overlay Mesa DRI fallback drivers under
/usr/local/spike-emu/mesa-driwhen the Docker image has ARM64virtio_gpu,kms_swrast, andswrastdrivers. - Starts a guest control agent that logs virtio-serial dashboard events to
/run/spike-emu/events.logand/connectivity/dump/log/spike-emu/control-agent.log.
The dashboard:
- Links to noVNC and the existing conagent backend emulator.
- Reads host logs from
emulation/work/logs/. - Sends virtual switch/button events as JSON to
emulation/work/control.sockwhen QEMU is running. Verified APIs arePOST /api/eventandPOST /api/switch/<name>.
Verification Status
Commands run from the workspace root:
docker compose -f emulation/compose.yaml build spike3-emu
docker compose -f emulation/compose.yaml run --rm --entrypoint /workspace/emulation/scripts/prepare-rootfs-image.sh spike3-emu
SPIKE3_SKIP_PREPARE=1 SPIKE3_AUTOSTART=boot-display docker compose -f emulation/compose.yaml up --force-recreate
Confirmed results:
- Docker image contains
/opt/spike3-kernel/vmlinuzand/opt/spike3-kernel/initrd.img; the initramfs includesdrm.ko,drm_kms_helper.ko,virtio_dma_buf.ko, andvirtio-gpu.ko. - QEMU boots the generated
emulation/work/rootfs.ext4and reachesSPIKE3 emulator init complete; autostart=boot-displayinemulation/work/logs/qemu-serial.log. - QEMU serial log confirms
virtio-gpu-pci detected,Initialized virtio_gpu, andfb0: virtio_gpudrmfb frame buffer device. - Guest hardware log confirms
/dev/dri/card0,/dev/dri/renderD128,/sys/class/drm/card0,/sys/class/drm/card0-Virtual-1, and loadedvirtio_gpu/drmmodules. - noVNC responds at
http://localhost:6080/vnc.html; the dashboard responds athttp://localhost:8090/api/status. POST /api/switch/startreturnssent_to_guest: trueand appends a switch event toemulation/work/dashboard-events.jsonl.
Current blocker:
/games/spike3/bin/boot_displayis reached, but it still logscreate_window RETRY 1 / 27through27 / 27, shader compile/link failures, andcpuspi: device open failed.- A nonblank SPIKE frame is not yet confirmed. The next display pass should focus on the userspace Mesa/GBM/EGL path or a targeted display interposer.
Limitations and Next Steps
Confirmed current limitations:
- The dashboard event path is only a guest-visible event log. It does not yet inject events into the game’s SPIKE nodebus model.
- The optional
LD_PRELOADstub is intentionally broad and inert. It can move early hardware probing forward, but any deterministic game failure still needs specific static/dynamic analysis before adding a targeted shim. - QEMU
virtio-gpuis not Raspberry Pi VC4/V3D. It is the right first display target because the binary asks for standard DRM/GBM/EGL surfaces, but shader/extension mismatches may still require a display interposer or patch. - Docker Desktop on macOS runs this under QEMU TCG without KVM acceleration, so startup may be slow.
Next implementation targets:
- Fix the remaining
boot_displayuserspace rendering path so a nonblank noVNC frame is produced. - Run
spike-menubefore the full game because it uses the same display stack with a smaller binary/image payload. - Run
gameand capture the first deterministic hardware/display failure from serial logs and/connectivity/dump/log/spike-emu/. - Map dashboard switch events into a first SPIKE nodebus emulator, reusing recovered node command evidence from
main-game-behavior.mdandtopper-serial-key-emulator.md. - Add targeted stubs only for confirmed failing APIs or device paths.