Files
pokemon_pinball_wiki/docker-qemu-emulation.md
2026-07-07 00:25:38 -05:00

7.9 KiB
Raw Blame History

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/game imports libEGL.so.1, libGLESv2.so.2, libdrm.so.2, and libgbm.so.1; see analysis/static-triage/game.objdump-p.txt.
  • /games/pokemon_pro/game strings include /sys/class/drm, /dev/dri/card1, /dev/dri/card0, drmModeSetCrtc fail, drmModePageFlip, gbm_bo_create, eglGetPlatformDisplayEXT, and egl resolution: %d x %d; see analysis/static-triage/game.strings.
  • /games/pokemon_pro/spike3/spike_menu/game has matching GBM/EGL/DRM strings at offsets including 0x382cd0 (/sys/class/drm), 0x383030 (/dev/dri/card1), 0x383058 (/dev/dri/card0), and 0x383730 (drmModeSetCrtc fail); see analysis/static-triage/spike_menu_game.strings.
  • /etc/init.d/S12lvds_panel launches /games/spike3/bin/boot_display early, and /etc/init.d/game_monitor later stops boot_display before 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-display
  • spike-menu
  • game
  • monitors
  • shell

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.sh to start boot_display, SPIKE menu, game, or monitors.
  • Injects an optional LD_PRELOAD hardware stub at /usr/local/spike-emu/lib/libspike3emu_stub.so when the Docker image has aarch64-linux-gnu-gcc.
  • Injects overlay Mesa DRI fallback drivers under /usr/local/spike-emu/mesa-dri when the Docker image has ARM64 virtio_gpu, kms_swrast, and swrast drivers.
  • Starts a guest control agent that logs virtio-serial dashboard events to /run/spike-emu/events.log and /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.sock when QEMU is running. Verified APIs are POST /api/event and POST /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/vmlinuz and /opt/spike3-kernel/initrd.img; the initramfs includes drm.ko, drm_kms_helper.ko, virtio_dma_buf.ko, and virtio-gpu.ko.
  • QEMU boots the generated emulation/work/rootfs.ext4 and reaches SPIKE3 emulator init complete; autostart=boot-display in emulation/work/logs/qemu-serial.log.
  • QEMU serial log confirms virtio-gpu-pci detected, Initialized virtio_gpu, and fb0: 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 loaded virtio_gpu/drm modules.
  • noVNC responds at http://localhost:6080/vnc.html; the dashboard responds at http://localhost:8090/api/status.
  • POST /api/switch/start returns sent_to_guest: true and appends a switch event to emulation/work/dashboard-events.jsonl.

Current blocker:

  • /games/spike3/bin/boot_display is reached, but it still logs create_window RETRY 1 / 27 through 27 / 27, shader compile/link failures, and cpuspi: 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 games SPIKE nodebus model.
  • The optional LD_PRELOAD stub 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-gpu is 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:

  1. Fix the remaining boot_display userspace rendering path so a nonblank noVNC frame is produced.
  2. Run spike-menu before the full game because it uses the same display stack with a smaller binary/image payload.
  3. Run game and capture the first deterministic hardware/display failure from serial logs and /connectivity/dump/log/spike-emu/.
  4. Map dashboard switch events into a first SPIKE nodebus emulator, reusing recovered node command evidence from main-game-behavior.md and topper-serial-key-emulator.md.
  5. Add targeted stubs only for confirmed failing APIs or device paths.