M7: in-repo FAT32 image builder + boot the whole system off a USB stick
The system now boots off a real FAT32 filesystem on a USB mass-storage device instead of QEMU's synthesized VVFAT drive. A new in-repo image builder formats that filesystem from the FHS boot tree, and both QEMU call sites (the run step and the test harness) attach it as a usb-storage device on the xHCI bus, so every boot exercises the full USB path OVMF -> BOOTX64.efi -> kernel. - tools/make-fat-image.py: a Python 3 stdlib-only FAT32 formatter (mirrors tools/make-initial-ramdisk.py — no external host dependencies). It lays down the boot sector + BPB/EBPB32, FSInfo, backup boot sector, two FATs, and the root/subdir/file cluster chains, emitting long-name entries where a name is not 8.3. Packs the four boot inputs (EFI/BOOT/BOOTX64.efi, system/kernel, system/services/init, boot/initial-ramdisk.img) into their boot paths. A --verify subcommand re-checks the 0xAA55 signature, recomputes the cluster count -> FAT32, and resolves EFI/BOOT/BOOTX64.efi, all with no dependencies. - build.zig: a mk_fat step builds zig-out/danos-usb.img from the four boot artifacts (so changing -Dtest-case rebuilds the image with that kernel), a check-fat-image step runs --verify, and run-x86-64 boots the image on a usb-storage device (if=none,id=bootusb + usb-storage,bus=xhci.0,bootindex=0), keeping usb-kbd/usb-mouse on the same controller. - test/qemu_test.py: the default boot config now boots off danos-usb.img on a usb-storage device (xHCI + usb-kbd + usb-mouse + the boot stick). The seven per-case qemu_extra blocks that added their own qemu-xhci/usb-kbd/usb-mouse (or a VVFAT stick) collided on id=xhci and are removed — the default provides the bus and the boot device. usb-storage and fat-mount now exercise the real FAT32 boot image (usb-storage reads its 0x55AA boot sector; fat mounts it at /mnt/usb). A build_case override lets a case reuse another's kernel, used by a new usb-boot case: an explicit, named boot-from-USB regression guard. Verified: zig build, zig build test, and zig build check-fat-image are green (FAT32, 128992 clusters, BOOTX64.efi present); a broad sequential QEMU sweep passes — smoke, init, vfs, input, device-manager, usb-report, usb-hid, usb-storage, fat-mount, device-list, driver-restart, acpi-report, iommu, orderly-shutdown, usb-boot, dma, msi, initial-ramdisk, args, process — proving the boot switch holds across kernel tests, the full init tree, the USB stack, the FAT mount, and orderly shutdown.
This commit is contained in:
@@ -531,6 +531,36 @@ pub fn build(b: *std.Build) void {
|
||||
const efi_install = b.addInstallArtifact(efiexe, .{ .dest_dir = .{ .override = .{ .custom = "EFI/BOOT" } } });
|
||||
b.getInstallStep().dependOn(&efi_install.step);
|
||||
|
||||
// --- danos-usb.img: the bootable FAT32 USB image ---
|
||||
// Format a real FAT32 image (the in-repo Python builder, no external tools)
|
||||
// holding exactly what the firmware and bootloader need off the ESP: the EFI
|
||||
// stub, the kernel, init, and the initial-ramdisk. QEMU presents this image as
|
||||
// a USB mass-storage device the guest boots from (see run-x86-64 and the test
|
||||
// harness), and the danos fat driver mounts the same image at /mnt/usb.
|
||||
const mk_fat = b.addSystemCommand(&.{"python3"});
|
||||
mk_fat.addFileArg(b.path("tools/make-fat-image.py"));
|
||||
const fat_image = mk_fat.addOutputFileArg("danos-usb.img");
|
||||
mk_fat.addArg("64"); // MiB
|
||||
mk_fat.addArg("EFI/BOOT/BOOTX64.efi");
|
||||
mk_fat.addFileArg(efiexe.getEmittedBin());
|
||||
mk_fat.addArg("system/kernel");
|
||||
mk_fat.addFileArg(exe.getEmittedBin());
|
||||
mk_fat.addArg("system/services/init");
|
||||
mk_fat.addFileArg(init_exe.getEmittedBin());
|
||||
mk_fat.addArg("boot/initial-ramdisk.img");
|
||||
mk_fat.addFileArg(initial_ramdisk_img);
|
||||
const fat_image_install = b.addInstallFile(fat_image, "danos-usb.img");
|
||||
b.getInstallStep().dependOn(&fat_image_install.step);
|
||||
|
||||
// `zig build check-fat-image` — validate the produced image is a real FAT32
|
||||
// with the EFI stub present (the builder's own --verify, no external tools).
|
||||
const check_fat = b.addSystemCommand(&.{"python3"});
|
||||
check_fat.addFileArg(b.path("tools/make-fat-image.py"));
|
||||
check_fat.addArg("--verify");
|
||||
check_fat.addFileArg(fat_image);
|
||||
const check_fat_step = b.step("check-fat-image", "Verify the FAT32 USB image is valid and bootable");
|
||||
check_fat_step.dependOn(&check_fat.step);
|
||||
|
||||
// --- run-x86-64: boot the x86-64 kernel in QEMU via UEFI/OVMF ---
|
||||
// Firmware lives in different places per OS/distro, so probe the known
|
||||
// layouts (Architecture, Debian/Ubuntu, Fedora, macOS Homebrew) and use the first
|
||||
@@ -591,10 +621,13 @@ pub fn build(b: *std.Build) void {
|
||||
});
|
||||
run_efi.addArg("-drive");
|
||||
run_efi.addPrefixedFileArg("if=pflash,format=raw,file=", vars_out);
|
||||
// Present the FHS zig-out to the guest as a FAT drive — it is the boot volume.
|
||||
// Boot off the FAT32 USB image: a mass-storage device on the same xHCI bus as
|
||||
// the keyboard and mouse. OVMF finds \EFI\BOOT\BOOTX64.efi on it and boots.
|
||||
run_efi.addArg("-drive");
|
||||
run_efi.addPrefixedFileArg("if=none,id=bootusb,format=raw,file=", fat_image);
|
||||
run_efi.addArgs(&.{
|
||||
"-drive",
|
||||
b.fmt("format=raw,file=fat:rw:{s}", .{b.install_path}),
|
||||
"-device",
|
||||
"usb-storage,bus=xhci.0,drive=bootusb,removable=on,bootindex=0",
|
||||
"-net",
|
||||
"none",
|
||||
// Emulated display advertising 1280x720 as its native (EDID preferred)
|
||||
|
||||
Reference in New Issue
Block a user