diff --git a/build.zig b/build.zig index b5f1528..9882273 100644 --- a/build.zig +++ b/build.zig @@ -51,63 +51,6 @@ fn timestamp(b: *std.Build) []const u8 { }); } -/// One user-space binary via the shared build-support recipe (freestanding, -/// ReleaseSmall, `.large` code model, root shim + user link script — see -/// build-support/build.zig for the full story). The program's file becomes the -/// `program` module; reach it through `programModule` to add per-binary imports. -fn addUserBinary( - b: *std.Build, - target: std.Build.ResolvedTarget, - default_imports: []const std.Build.Module.Import, - name: []const u8, - root: []const u8, -) *std.Build.Step.Compile { - return addUserBinaryImpl(b, target, default_imports, name, root, false); -} - -/// As `addUserBinary`, but built multi-threaded (`single_threaded = false`) so real -/// atomics/TLS work — required before a binary may call `Thread.spawn` -/// (docs/threading.md). Threads are a deliberate per-binary opt-in. -fn addThreadedUserBinary( - b: *std.Build, - target: std.Build.ResolvedTarget, - default_imports: []const std.Build.Module.Import, - name: []const u8, - root: []const u8, -) *std.Build.Step.Compile { - return addUserBinaryImpl(b, target, default_imports, name, root, true); -} - -fn addUserBinaryImpl( - b: *std.Build, - target: std.Build.ResolvedTarget, - default_imports: []const std.Build.Module.Import, - name: []const u8, - root: []const u8, - threaded: bool, -) *std.Build.Step.Compile { - // The shim and link script live in the kernel domain package, so name them - // through it — the same spelling build-support's userBinary uses. - const kernel_library = b.dependency("kernel", .{}); - return build_support.userBinaryFromImports(b, .{ - .name = name, - .root_source_file = b.path(root), - .shim_source_file = kernel_library.path("root.zig"), - .linker_script = kernel_library.path("user.ld"), - .target = target, - .default_imports = default_imports, - .threaded = threaded, - }); -} - -/// The `program` module of a binary built by `addUserBinary` — the module rooted -/// at the program's own source file. Per-binary imports (protocol modules, bus -/// ABIs) go here, not on the root shim: module imports are not transitive, so an -/// import added to the root would be invisible to the program's code. -fn programModule(exe: *std.Build.Step.Compile) *std.Build.Module { - return build_support.programModule(exe); -} - /// The modules the kernel imports, gathered once so both kernel variants (the /// installed one and the serial-enabled one `run-x86-64` boots) are built from /// the same set. `build_options` is *not* here — it carries `serial`/`test_case`, @@ -250,7 +193,6 @@ pub fn build(b: *std.Build) void { }); const abi_module = kernel_library.module("abi"); const device_abi_module = device_library.module("device-abi"); - const pci_class_module = device_library.module("pci-class"); // Kernel tunables (maximum_cpus, stack sizes, tick rate). A dependency-free module of // compile-time constants, imported wherever a knob is read; keeps the trade-offs @@ -292,14 +234,7 @@ pub fn build(b: *std.Build) void { }, }); - // The wire protocols some test fixtures name as per-binary extras, - // exported by the protocol package. - const device_manager_protocol_module = protocol_library.module("device-manager-protocol"); - // The driver-side PCI library some test fixtures name as a per-binary - // extra. (The rest of the domain modules reach the root only through - // build-support's defaultImports below.) - const pci_module = device_library.module("pci"); // The initial_ramdisk container format, shared by the kernel (unpacks it) and // the EFI loader (packs it in RAM from the boot volume's /system tree). No @@ -346,18 +281,7 @@ pub fn build(b: *std.Build) void { const kernel_install = b.addInstallArtifact(exe, .{ .dest_dir = .{ .override = .{ .custom = "system" } } }); b.getInstallStep().dependOn(&kernel_install.step); - // --- init: the first user-space program (a system service) --- - // The default module set every user binary can import directly, drawn from - // build-support's single authoritative list so the root stanzas and the - // binary packages can never drift apart. Per-binary extras are added with - // programModule(exe).addImport. - const default_imports = build_support.defaultImports(.{ - .kernel = kernel_library, - .device = device_library, - .client = client_library, - .xkeyboard_config = xkeyboard_config_library, - }); - + // --- the user-space binaries, every one of them a package --- // Binary packages (docs/build-packages-plan.md, phase 2): each binary // builds itself against the domain packages via build-support's shared // recipe, started in ring 3 by the kernel's user-ELF loader like always; @@ -371,7 +295,7 @@ pub fn build(b: *std.Build) void { // Each is built by the same user-binary recipe and laid out at its FHS path on // the boot volume (see `bundled` below). The EFI loader walks the tree at boot // and hands the kernel an in-RAM initial_ramdisk of it (system/initial-ramdisk.zig). - const vfstest_exe = addUserBinary(b, kernel_target, &default_imports, "vfs-test", "test/system/services/vfs-test/vfs-test.zig"); + const vfstest_exe = b.dependency("vfs-test", .{}).artifact("vfs-test"); // The drivers, each directory its own package: the PS/2 bus family (bus + // keyboard + mouse from one package), the xHCI bus driver, the USB HID // class drivers, and USB mass storage. Their unit tests ride along. @@ -394,29 +318,21 @@ pub fn build(b: *std.Build) void { const display_demo_exe = b.dependency("display-demo", .{}).artifact("display-demo"); const virtio_gpu_package = b.dependency("virtio-gpu", .{}); const virtio_gpu_exe = virtio_gpu_package.artifact("virtio-gpu"); - const shared_memory_server_exe = addUserBinary(b, kernel_target, &default_imports, "shared-memory-server", "test/system/services/shared-memory-server/shared-memory-server.zig"); - const shared_memory_client_exe = addUserBinary(b, kernel_target, &default_imports, "shared-memory-client", "test/system/services/shared-memory-client/shared-memory-client.zig"); - const fat_test_exe = addUserBinary(b, kernel_target, &default_imports, "fat-test", "test/system/services/fat-test/fat-test.zig"); + const shared_memory_server_exe = b.dependency("shared-memory-server", .{}).artifact("shared-memory-server"); + const shared_memory_client_exe = b.dependency("shared-memory-client", .{}).artifact("shared-memory-client"); + const fat_test_exe = b.dependency("fat-test", .{}).artifact("fat-test"); // The first binary package (docs/build-packages-plan.md, phase 2): pci-bus // builds itself against the domain packages; the root build just takes the // artifact for the boot image. const pci_bus_exe = b.dependency("pci-bus", .{}).artifact("pci-bus"); - // A test fixture, not a real driver: hellos to the device manager, then faults — - // what the driver-restart scenario drives the crash-loop cap with. - const crash_test_exe = addUserBinary(b, kernel_target, &default_imports, "crash-test", "test/system/services/crash-test/crash-test.zig"); - programModule(crash_test_exe).addImport("device-manager-protocol", device_manager_protocol_module); - const device_list_exe = addUserBinary(b, kernel_target, &default_imports, "device-list", "test/system/services/device-list/device-list.zig"); - programModule(device_list_exe).addImport("device-manager-protocol", device_manager_protocol_module); - // A test fixture: claims the pci-caps case's extra unclaimed NIC and exercises the - // driver-side PCI library surface (capabilities, MSI, MSI-X, power, FLR) against it. - const pci_cap_test_exe = addUserBinary(b, kernel_target, &default_imports, "pci-cap-test", "test/system/services/pci-cap-test/pci-cap-test.zig"); - programModule(pci_cap_test_exe).addImport("pci", pci_module); - programModule(pci_cap_test_exe).addImport("pci-class", pci_class_module); - // The IOMMU-enforcement negative test: claims an unclaimed e1000e and fires a rogue - // DMA that VT-d must fault. Same PCI building blocks as pci-cap-test. - const iommu_fault_test_exe = addUserBinary(b, kernel_target, &default_imports, "iommu-fault-test", "test/system/services/iommu-fault-test/iommu-fault-test.zig"); - programModule(iommu_fault_test_exe).addImport("pci", pci_module); - programModule(iommu_fault_test_exe).addImport("pci-class", pci_class_module); + // crash-test is a fixture, not a real driver: it hellos to the device + // manager, then faults — what the driver-restart scenario drives the + // crash-loop cap with. pci-cap-test and iommu-fault-test exercise the + // driver-side PCI library surface and the VT-d rogue-DMA negative proof. + const crash_test_exe = b.dependency("crash-test", .{}).artifact("crash-test"); + const device_list_exe = b.dependency("device-list", .{}).artifact("device-list"); + const pci_cap_test_exe = b.dependency("pci-cap-test", .{}).artifact("pci-cap-test"); + const iommu_fault_test_exe = b.dependency("iommu-fault-test", .{}).artifact("iommu-fault-test"); // The discovery service: one swappable process per firmware // (docs/discovery.md), bundled under the neutral ramdisk name // "discovery" so the device manager never learns which firmware it is on. @@ -435,14 +351,14 @@ pub fn build(b: *std.Build) void { // The input service and its exercisers: the fan-out server, a hardware-free synthetic // source, and a subscriber that doubles as the `input` test's oracle. See docs/input.md. const input_exe = b.dependency("input", .{}).artifact("input"); - const input_source_exe = addUserBinary(b, kernel_target, &default_imports, "input-source", "test/system/services/input-source/input-source.zig"); - const input_test_exe = addUserBinary(b, kernel_target, &default_imports, "input-test", "test/system/services/input-test/input-test.zig"); - const args_echo_exe = addUserBinary(b, kernel_target, &default_imports, "args-echo", "test/system/services/args-echo/args-echo.zig"); - const process_test_exe = addUserBinary(b, kernel_target, &default_imports, "process-test", "test/system/services/process-test/process-test.zig"); + const input_source_exe = b.dependency("input-source", .{}).artifact("input-source"); + const input_test_exe = b.dependency("input-test", .{}).artifact("input-test"); + const args_echo_exe = b.dependency("args-echo", .{}).artifact("args-echo"); + const process_test_exe = b.dependency("process-test", .{}).artifact("process-test"); const logger_exe = b.dependency("logger", .{}).artifact("logger"); // The first multi-threaded binary: exercises runtime.Thread over the thread ABI - // (docs/threading.md). Built threaded so its shared-memory poll is real. - const thread_test_exe = addThreadedUserBinary(b, kernel_target, &default_imports, "thread-test", "test/system/services/thread-test/thread-test.zig"); + // (docs/threading.md). Its package opts into threading (real atomics/TLS). + const thread_test_exe = b.dependency("thread-test", .{}).artifact("thread-test"); // Every user binary and its FHS home on the boot volume. There is no packed // ramdisk artifact any more: make-fat-image.py lays each binary out at this diff --git a/build.zig.zon b/build.zig.zon index e75ac78..3f708cd 100644 --- a/build.zig.zon +++ b/build.zig.zon @@ -58,6 +58,19 @@ .@"usb-hid" = .{ .path = "system/drivers/usb-hid" }, .@"usb-storage" = .{ .path = "system/drivers/usb-storage" }, .@"virtio-gpu" = .{ .path = "system/drivers/virtio-gpu" }, + .@"vfs-test" = .{ .path = "test/system/services/vfs-test" }, + .@"fat-test" = .{ .path = "test/system/services/fat-test" }, + .@"shared-memory-server" = .{ .path = "test/system/services/shared-memory-server" }, + .@"shared-memory-client" = .{ .path = "test/system/services/shared-memory-client" }, + .@"crash-test" = .{ .path = "test/system/services/crash-test" }, + .@"device-list" = .{ .path = "test/system/services/device-list" }, + .@"pci-cap-test" = .{ .path = "test/system/services/pci-cap-test" }, + .@"iommu-fault-test" = .{ .path = "test/system/services/iommu-fault-test" }, + .@"input-source" = .{ .path = "test/system/services/input-source" }, + .@"input-test" = .{ .path = "test/system/services/input-test" }, + .@"args-echo" = .{ .path = "test/system/services/args-echo" }, + .@"process-test" = .{ .path = "test/system/services/process-test" }, + .@"thread-test" = .{ .path = "test/system/services/thread-test" }, // See `zig fetch --save ` for a command-line interface for adding dependencies. //.example = .{ // // When updating this field to a new URL, be sure to delete the corresponding