// virt-board SD fallback — 64 MiB memory-backed scratch. // // QEMU's `-M virt` does not ship an SDHCI peripheral on default // invocation (passing `-drive if=sd,...` errors with "machine type // does not support if=sd"), so the real BCM2711 EMMC2 driver from // `src/board/rpi4b/emmc2.zig` cannot run here. The FAT32 backend // talks to the SD card exclusively through the `block_dev.sd_dev` // vtable, so a memory-backed fake satisfies the abstraction and // lets [TEST] emmc2-block + [TEST] fs-roundtrip pass on virt // without dragging in a QEMU SDHCI-on-PCI model. // // Future work (real virt storage) can swap the body for a // `virtio-blk-device` driver if anyone actually needs persistent // virt storage. The on-disk abstraction (`block_dev.BlockDev`) is // stable enough that the swap is a one-file change. const block_dev = #import("block_dev") const SCRATCH_BLOCKS u32 = 128 * 1024 // 64 MiB / 512 B per block // `linksection(".sdscratch")`: keep this 64 MiB fake disk OUT of // `.bss`. With `scratch` alive in `.bss`, `bss_end` lands 64 MiB past // `bss_begin` and boot.S's `adr x1, bss_end` overflows the // R_AARCH64_ADR_PREL_LO21 ±1 MiB PC-relative range. Its own NOLOAD // section (placed outside bss_begin..bss_end in src/board/virt/ // linker.ld) keeps bss_end reachable. init() @memsets the buffer // itself, so it does not need boot.S's bss memzero — safe to exclude. var scratch [#as(usize, SCRATCH_BLOCKS) * 512]u8 linksection(".sdscratch") = undefined // Init signature mirrors the rpi4b counterpart's i32 return // so kernel.zig can branch uniformly on failure. The virt fake has // no failure paths — always returns 0 — but the caller still treats // negative-return as a soft failure (log + continue) so the contract // holds across boards. pub fn init() i32 { #memset(&scratch, 0) block_dev.sd_dev = .{ .read_fn = read_block, .write_fn = write_block } return 0 } pub fn read_block(lba u32, buf *mut [512]u8) callconv(.c) i32 { if lba >= SCRATCH_BLOCKS { return -1 } const off usize = #as(usize, lba) * 512 #memcpy(buf, scratch[off..][0..512]) return 0 } pub fn write_block(lba u32, buf *[512]u8) callconv(.c) i32 { if lba >= SCRATCH_BLOCKS { return -1 } const off usize = #as(usize, lba) * 512 #memcpy(scratch[off..][0..512], buf) return 0 }