81 lines
3.6 KiB
Markdown
81 lines
3.6 KiB
Markdown
|
|
# Bad Apple for UEFI (Rust)
|
|||
|
|
|
|||
|
|
Firmware payload that plays the Bad Apple animation directly from a UEFI image. The project uses the [`uefi`](https://github.com/rust-osdev/uefi-rs) crate, renders video frames through GOP, and synthesises audio via the legacy PC speaker using a 1-bit sigma-delta stream. Everything fits inside a `no_std` Rust binary and can be driven under QEMU.
|
|||
|
|
|
|||
|
|
## Repository layout
|
|||
|
|
|
|||
|
|
- `src/` – modular firmware code (`app` contains assets, graphics, audio, timing, and player logic).
|
|||
|
|
- `assets/` – tiny demo clip/audio used as an embedded fallback.
|
|||
|
|
- `tools/` – helper utilities, notably `process_badapple.py` for converting the real video/audio into firmware-friendly blobs.
|
|||
|
|
- `ai/state.json` – persistent JSON with personas/tasks as requested.
|
|||
|
|
- `flake.nix` – reproducible Nix environment with Rust, ffmpeg, and QEMU.
|
|||
|
|
|
|||
|
|
## Building the EFI binary
|
|||
|
|
|
|||
|
|
1. Ensure you have a recent stable Rust toolchain with the UEFI target installed:
|
|||
|
|
```bash
|
|||
|
|
rustup target add aarch64-unknown-uefi
|
|||
|
|
```
|
|||
|
|
2. Build the binary:
|
|||
|
|
```bash
|
|||
|
|
cargo build --release --target aarch64-unknown-uefi
|
|||
|
|
```
|
|||
|
|
The resulting image will be at `target/aarch64-unknown-uefi/release/badapple-uefi.efi`.
|
|||
|
|
|
|||
|
|
Using Nix instead? Enter the dev shell or boot straight from the flake:
|
|||
|
|
```bash
|
|||
|
|
nix develop
|
|||
|
|
# (rustup target add aarch64-unknown-uefi is invoked automatically in the shell)
|
|||
|
|
|
|||
|
|
# Build & boot in one go under QEMU (cross-emulates aarch64 firmware):
|
|||
|
|
nix run .#qemu
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## Preparing real assets
|
|||
|
|
|
|||
|
|
The repository embeds a tiny placeholder clip so builds work immediately. To convert the actual Bad Apple video/audio:
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
python3 tools/process_badapple.py \
|
|||
|
|
--input /path/to/badapple.mp4 \
|
|||
|
|
--output-dir build/assets \
|
|||
|
|
--width 320 --height 240 --fps 30 \
|
|||
|
|
--sample-rate 44100
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Copy the generated `video.baa` and `audio.pdm` onto the EFI system partition alongside the application under `\EFI\BADAPPLE\`. The firmware attempts to load external assets first, then falls back to the embedded demo when they are missing.
|
|||
|
|
|
|||
|
|
### Asset format summary
|
|||
|
|
|
|||
|
|
- `video.baa`: magic `"BAA\0"`, header with width/height/fps/frame count, followed by 1-bit packed frames (LSB-first).
|
|||
|
|
- `audio.pdm`: magic `"BAP\0"`, header with sample rate and sample count, followed by 1-bit sigma-delta stream (LSB-first).
|
|||
|
|
|
|||
|
|
## Running under QEMU
|
|||
|
|
|
|||
|
|
A minimal invocation after copying the EFI binary and assets to a FAT image might look like:
|
|||
|
|
```bash
|
|||
|
|
qemu-system-aarch64 \
|
|||
|
|
-machine virt \
|
|||
|
|
-cpu cortex-a72 \
|
|||
|
|
-m 1024 \
|
|||
|
|
-drive if=pflash,format=raw,readonly=on,file=/path/to/AAVMF_CODE.fd \
|
|||
|
|
-drive if=pflash,format=raw,file=/path/to/AAVMF_VARS.fd \
|
|||
|
|
-drive file=fat:rw:esp,format=raw \
|
|||
|
|
-device virtio-gpu-pci \
|
|||
|
|
-device ramfb \
|
|||
|
|
-serial mon:stdio
|
|||
|
|
```
|
|||
|
|
Make sure the `esp` directory contains `EFI/BOOT/BOOTAA64.EFI` (your compiled binary) plus the `EFI/BADAPPLE` assets. Audio output is currently disabled on non-x86 platforms because the legacy PC speaker is unavailable; video playback continues in sync.
|
|||
|
|
|
|||
|
|
## Notes & limitations
|
|||
|
|
|
|||
|
|
- Audio playback uses the legacy PC speaker path on x86 machines; on aarch64 builds the backend is currently silent because that device is absent.
|
|||
|
|
- Timing relies on TSC calibration during boot services. Firmware with non-constant TSCs may require additional guarding.
|
|||
|
|
- Video rendering assumes the selected GOP mode exposes a writable framebuffer (non-BLT-only). Modes with vendor-specific bitmasks are supported via mask calculation.
|
|||
|
|
|
|||
|
|
## Next steps
|
|||
|
|
|
|||
|
|
- Replace the placeholder clip with the full Bad Apple dataset via the tooling above.
|
|||
|
|
- Consider double buffering and smarter frame pacing to reduce tearing.
|
|||
|
|
- Experiment with richer audio backends (e.g. direct HDA/AC97 programming) if available on your platform.
|