diff --git a/build.zig b/build.zig index c844d3e..298d429 100644 --- a/build.zig +++ b/build.zig @@ -221,76 +221,41 @@ pub fn build(b: *std.Build) void { const target = b.standardTargetOptions(.{}); const optimize = b.standardOptimizeOption(.{}); + // The library domain packages (docs/build-packages-plan.md, phase 1): each + // domain owns a build.zig/zon that wires and exports its modules, and this + // root build is a consumer — a library interface change now happens in the + // domain's own build file, not here. The per-module commentary lives with + // each domain's build.zig. + const kernel_library = b.dependency("kernel", .{}); + const device_library = b.dependency("device", .{}); + const client_library = b.dependency("client", .{}); + const protocol_library = b.dependency("protocol", .{}); + const csv_library = b.dependency("csv", .{}); + const xkeyboard_config_library = b.dependency("xkeyboard-config", .{}); + // The three shared contracts, each with its own audience so every import // declares which one it speaks (no target is set, so each inherits the target of // whichever binary imports it). See docs/coding-standards.md. // boot-handoff : loader <-> kernel (BootInformation, framebuffer, VM layout) // abi : kernel <-> runtime, core (SystemCall, mmap prot flags, page_size) // device-abi : kernel <-> user, devices (DeviceDescriptor, DeviceClass, ...) + // boot-handoff stays a root module (a system/ source the loader <-> kernel + // pair speaks); abi is exported by the kernel library package (its source + // also lives in system/), device-abi by the device package. const boot_handoff_module = b.addModule("boot-handoff", .{ .root_source_file = b.path("system/boot-handoff.zig"), }); - const abi_module = b.addModule("abi", .{ - .root_source_file = b.path("system/abi.zig"), - }); - // The devices sub-project's public interface (the flat wire types), exposed as - // its own module like vfs-protocol — importable by user space, unlike the - // kernel-internal device model it also feeds (system/kernel/device-model.zig). - const device_abi_module = b.addModule("device-abi", .{ - .root_source_file = b.path("library/device/model/device-abi.zig"), - }); - // PCI class-code decoding (class/subclass/prog-IF -> names). Pure reference data, - // shared by kernel discovery (the device-tree dump) and any user-space PCI tool. - const pci_class_module = b.addModule("pci-class", .{ - .root_source_file = b.path("library/device/pci/pci-class.zig"), - }); - // Shared CSV helpers (comment stripping, field iteration) for the /etc/*.csv - // config files — the device registry and the init service list both parse them. - const csv_module = b.addModule("csv", .{ - .root_source_file = b.path("library/csv/csv.zig"), - }); - // The device registry: parse /etc/devices.csv into match rules and bind a - // reported device to a driver — the data-driven, authoritative replacement for - // the manager's hand-written switch tables. Pure logic (no hardware, no - // syscalls), so it unit-tests on the host; the manager imports it. - const device_registry_module = b.addModule("device-registry", .{ - .root_source_file = b.path("library/device/registry/device-registry.zig"), - .imports = &.{.{ .name = "csv", .module = csv_module }}, - }); - // ACPI/PnP hardware-ID (_HID) names — the flat analog of pci-class for acpi_device - // nodes. Also shared reference data. - // The AML interpreter, a build module so the ring-3 acpi service can run the - // same parser the kernel does (docs/discovery.md — the shared AML module). - // Pure Zig, no kernel imports — one source, two builds. - const aml_module = b.addModule("aml", .{ - .root_source_file = b.path("library/device/acpi/aml/aml.zig"), - }); - - const acpi_ids_module = b.addModule("acpi-ids", .{ - .root_source_file = b.path("library/device/acpi/acpi-ids.zig"), - }); - - // The USB device-framework wire ABI (chapter-9 set-up packets, standard + - // class requests, descriptors) and the USB class-code taxonomy — the flat - // reference the xHCI bus driver, the USB class drivers, and the device - // manager's identity matcher all share. Pure data, like pci-class/acpi-ids. - const usb_abi_module = b.addModule("usb-abi", .{ - .root_source_file = b.path("library/device/usb/usb-abi.zig"), - }); - const usb_ids_module = b.addModule("usb-ids", .{ - .root_source_file = b.path("library/device/usb/usb-ids.zig"), - }); - // The USB transfer protocol: what a USB class driver says to the xHCI bus - // driver to drive its device (open / control / interrupt / bulk). A protocol - // module like vfs-protocol, shared by the bus driver and every class driver. - const usb_transfer_protocol_module = b.addModule("usb-transfer-protocol", .{ - .root_source_file = b.path("library/protocol/usb-transfer/usb-transfer-protocol.zig"), - }); - // The block-device protocol: read/write of fixed-size blocks, spoken between a - // filesystem and a block driver (usb-storage). A protocol module like the rest. - const block_protocol_module = b.addModule("block-protocol", .{ - .root_source_file = b.path("library/protocol/block/block-protocol.zig"), - }); + 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"); + const csv_module = csv_library.module("csv"); + const device_registry_module = device_library.module("device-registry"); + const aml_module = device_library.module("aml"); + const acpi_ids_module = device_library.module("acpi-ids"); + const usb_abi_module = device_library.module("usb-abi"); + const usb_ids_module = device_library.module("usb-ids"); + const usb_transfer_protocol_module = protocol_library.module("usb-transfer-protocol"); + const block_protocol_module = protocol_library.module("block-protocol"); // 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 @@ -332,208 +297,35 @@ pub fn build(b: *std.Build) void { }, }); - // The VFS wire protocol: the vfs sub-project's public interface, exposed as its - // own module. Both the vfs server and the runtime's file layer (unistd/stdio) - // depend on this contract by name — neither reaches into the other's files. This - // is the first "protocol module" (see docs/driver-model.md); usb/block will - // expose theirs the same way. - const vfs_protocol_module = b.addModule("vfs-protocol", .{ - .root_source_file = b.path("library/protocol/vfs/vfs-protocol.zig"), - }); + // The wire protocols, all exported by the protocol package. + const vfs_protocol_module = protocol_library.module("vfs-protocol"); + const input_protocol_module = protocol_library.module("input-protocol"); + const device_manager_protocol_module = protocol_library.module("device-manager-protocol"); + const display_protocol_module = protocol_library.module("display-protocol"); + const scanout_protocol_module = protocol_library.module("scanout-protocol"); + const power_protocol_module = protocol_library.module("power-protocol"); - // The input wire protocol: the input service's public interface, exposed as its own - // module the same way vfs-protocol is. Shared by the input service, the runtime's - // `input` helper (subscribe/publish), and every source and subscriber. - const input_protocol_module = b.addModule("input-protocol", .{ - .root_source_file = b.path("library/protocol/input/input-protocol.zig"), - }); - - // The danos-native user-space runtime: system_call wrappers, the C-convention - // heap, IPC helpers, the process start shim, device access. This is the stable - // application ABI; POSIX compatibility is a separate library on top (see below). - // Compiled into every user binary (see addUserBinary), so it inherits each exe's - // `.large` code model — do NOT set a target/code_model here. It imports `abi` - // for the shared SystemCall numbers / mmap flags, `device-abi` for the device - // types its `device` helper wraps, and re-exports `vfs-protocol` for the VFS - // server. It never touches `boot-handoff` — user space has no business with the - // loader↔kernel handoff. - // Wire-protocol modules the kernel-library client wrappers (device-manager/block/display) - // and the services speak. The `runtime` module itself is defined below, after the - // library/kernel concern modules it shims over. - const device_manager_protocol_module = b.addModule("device-manager-protocol", .{ - .root_source_file = b.path("library/protocol/device-manager/device-manager-protocol.zig"), - }); - const display_protocol_module = b.addModule("display-protocol", .{ - .root_source_file = b.path("library/protocol/display/display-protocol.zig"), - }); - - // The scanout protocol: the compositor's outbound present channel to a native scanout - // driver (virtio-gpu), separate from the client-facing display protocol (docs/display-v2.md). - // No runtime client speaks it — imported directly by the compositor and the scanout driver. - const scanout_protocol_module = b.addModule("scanout-protocol", .{ - .root_source_file = b.path("library/protocol/scanout/scanout-protocol.zig"), - }); - - // The power protocol: system power's domain-named surface (docs/power.md). No runtime - // client speaks it — imported directly by init and the acpi discovery service. - const power_protocol_module = b.addModule("power-protocol", .{ - .root_source_file = b.path("library/protocol/power/power-protocol.zig"), - }); - - // Typed volatile MMIO register access + memory-ordering barriers, for drivers on - // top of an mmio_map grant. Depends only on `builtin` (arch-conditional barriers); - // no target set, so it inherits each driver's. See library/device/mmio/mmio.zig. - const mmio_module = b.addModule("mmio", .{ - .root_source_file = b.path("library/device/mmio/mmio.zig"), - }); - - // --- library/kernel: the userspace private-ABI library (kernel32-style), split by - // concern into directly-importable modules. `system.zig` (the old dumping ground) and - // `runtime.zig` (the old aggregator) are compatibility shims re-exporting these until the - // consumers migrate to direct imports (reorg C1–C5). The graph is a DAG: memory depends on - // thread (heap needs Thread.Mutex), and thread does its own raw mmap so there is no cycle. - const system_call_module = b.addModule("system-call", .{ - .root_source_file = b.path("library/kernel/system-call.zig"), - .imports = &.{.{ .name = "abi", .module = abi_module }}, - }); - const ipc_module = b.addModule("ipc", .{ - .root_source_file = b.path("library/kernel/ipc.zig"), - .imports = &.{ .{ .name = "abi", .module = abi_module }, .{ .name = "system-call", .module = system_call_module } }, - }); - const time_module = b.addModule("time", .{ - .root_source_file = b.path("library/kernel/time.zig"), - .imports = &.{.{ .name = "system-call", .module = system_call_module }}, - }); - const thread_module = b.addModule("thread", .{ - .root_source_file = b.path("library/kernel/thread.zig"), - .imports = &.{ .{ .name = "abi", .module = abi_module }, .{ .name = "system-call", .module = system_call_module } }, - }); - const logging_module = b.addModule("logging", .{ - .root_source_file = b.path("library/kernel/logging.zig"), - .imports = &.{ .{ .name = "abi", .module = abi_module }, .{ .name = "system-call", .module = system_call_module } }, - }); - const process_module = b.addModule("process", .{ - .root_source_file = b.path("library/kernel/process.zig"), - .imports = &.{ - .{ .name = "abi", .module = abi_module }, - .{ .name = "system-call", .module = system_call_module }, - .{ .name = "ipc", .module = ipc_module }, - .{ .name = "time", .module = time_module }, - }, - }); - const file_system_module = b.addModule("file-system", .{ - .root_source_file = b.path("library/kernel/file-system.zig"), - .imports = &.{ - .{ .name = "abi", .module = abi_module }, - .{ .name = "system-call", .module = system_call_module }, - .{ .name = "ipc", .module = ipc_module }, - .{ .name = "vfs-protocol", .module = vfs_protocol_module }, - }, - }); - const memory_module = b.addModule("memory", .{ - .root_source_file = b.path("library/kernel/memory/memory.zig"), - .imports = &.{ - .{ .name = "abi", .module = abi_module }, - .{ .name = "system-call", .module = system_call_module }, - .{ .name = "ipc", .module = ipc_module }, - .{ .name = "thread", .module = thread_module }, - }, - }); - const service_module = b.addModule("service", .{ - .root_source_file = b.path("library/kernel/service.zig"), - .imports = &.{ - .{ .name = "abi", .module = abi_module }, - .{ .name = "ipc", .module = ipc_module }, - .{ .name = "process", .module = process_module }, - }, - }); - const start_module = b.addModule("start", .{ - .root_source_file = b.path("library/kernel/start.zig"), - .imports = &.{ .{ .name = "process", .module = process_module }, .{ .name = "logging", .module = logging_module } }, - }); - // The driver author's interface (library/device/driver): device access + the - // device-manager hello handshake, folded together. - const driver_module = b.addModule("driver", .{ - .root_source_file = b.path("library/device/driver/driver.zig"), - .imports = &.{ - .{ .name = "abi", .module = abi_module }, - .{ .name = "device-abi", .module = device_abi_module }, - .{ .name = "system-call", .module = system_call_module }, - .{ .name = "ipc", .module = ipc_module }, - .{ .name = "time", .module = time_module }, - .{ .name = "device-manager-protocol", .module = device_manager_protocol_module }, - }, - }); - // The block-device client — a device type, so library/device/block. - const block_client_module = b.addModule("block", .{ - .root_source_file = b.path("library/device/block/block.zig"), - .imports = &.{ - .{ .name = "ipc", .module = ipc_module }, - .{ .name = "time", .module = time_module }, - .{ .name = "block-protocol", .module = block_protocol_module }, - }, - }); - // Userspace-service clients live in library/client (they talk to services, not the kernel). - const display_client_module = b.addModule("display", .{ - .root_source_file = b.path("library/client/display/display.zig"), - .imports = &.{ - .{ .name = "ipc", .module = ipc_module }, - .{ .name = "time", .module = time_module }, - .{ .name = "display-protocol", .module = display_protocol_module }, - }, - }); - const input_client_module = b.addModule("input", .{ - .root_source_file = b.path("library/client/input/input.zig"), - .imports = &.{ - .{ .name = "ipc", .module = ipc_module }, - .{ .name = "time", .module = time_module }, - .{ .name = "input-protocol", .module = input_protocol_module }, - }, - }); - // A device driver's view of its claimed PCI function: config-space header fields, BAR - // decode + map, capability walks (legacy + extended), MSI/MSI-X programming, power - // state, and function-level reset (library/device/pci/pci.zig). The generic PCI - // mechanics every leaf PCI driver used to re-derive inline. Imports the driver (device - // access) client + mmio + the pci-class data module (config-space layout constants) + - // time (the D0 and FLR settle delays). - const pci_module = b.addModule("pci", .{ - .root_source_file = b.path("library/device/pci/pci.zig"), - .imports = &.{ - .{ .name = "driver", .module = driver_module }, - .{ .name = "mmio", .module = mmio_module }, - .{ .name = "pci-class", .module = pci_class_module }, - .{ .name = "time", .module = time_module }, - }, - }); - - // The USB class-driver transfer client (library/device/usb/usb.zig): open a device on - // the xHCI bus and drive it (control / interrupt / bulk). Bus-family logic a class - // driver imports directly — over ipc + time. Re-exports usb-abi / usb-ids as - // usb.abi / usb.ids for a single USB import. - const usb_module = b.addModule("usb", .{ - .root_source_file = b.path("library/device/usb/usb.zig"), - .imports = &.{ - .{ .name = "ipc", .module = ipc_module }, - .{ .name = "time", .module = time_module }, - .{ .name = "usb-transfer-protocol", .module = usb_transfer_protocol_module }, - .{ .name = "usb-abi", .module = usb_abi_module }, - .{ .name = "usb-ids", .module = usb_ids_module }, - }, - }); - - // Keyboard layouts compiled from the X11 xkeyboard-config database into native Zig - // (keycode + modifiers -> keysym/character). The `layouts` tables are generated by - // tools/make-xkeyboard-config.py; `xkeyboard-config` is the hand-written API over them. - // No target set, so each inherits its importer's. See library/xkeyboard-config/. - const xkb_layouts_module = b.addModule("layouts", .{ - .root_source_file = b.path("library/xkeyboard-config/generated/layouts.zig"), - }); - const xkeyboard_config_module = b.addModule("xkeyboard-config", .{ - .root_source_file = b.path("library/xkeyboard-config/xkeyboard-config.zig"), - .imports = &.{ - .{ .name = "layouts", .module = xkb_layouts_module }, - }, - }); + // The library/kernel concern modules (the userspace private-ABI library), + // the device-domain driver libraries, and the service clients. + const mmio_module = device_library.module("mmio"); + const system_call_module = kernel_library.module("system-call"); + const ipc_module = kernel_library.module("ipc"); + const time_module = kernel_library.module("time"); + const thread_module = kernel_library.module("thread"); + const logging_module = kernel_library.module("logging"); + const process_module = kernel_library.module("process"); + const file_system_module = kernel_library.module("file-system"); + const memory_module = kernel_library.module("memory"); + const service_module = kernel_library.module("service"); + const start_module = kernel_library.module("start"); + const driver_module = device_library.module("driver"); + const block_client_module = device_library.module("block"); + const display_client_module = client_library.module("display"); + const input_client_module = client_library.module("input"); + const pci_module = device_library.module("pci"); + const usb_module = device_library.module("usb"); + const xkb_layouts_module = xkeyboard_config_library.module("layouts"); + const xkeyboard_config_module = xkeyboard_config_library.module("xkeyboard-config"); // 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 diff --git a/build.zig.zon b/build.zig.zon index c397039..03e00a6 100644 --- a/build.zig.zon +++ b/build.zig.zon @@ -35,6 +35,13 @@ // The danos build API — the shared user-binary recipe every build file // (root and per-binary packages) consumes (docs/build-packages-plan.md). .@"build-support" = .{ .path = "build-support" }, + // The library domains, each a package exporting its modules. + .kernel = .{ .path = "library/kernel" }, + .device = .{ .path = "library/device" }, + .client = .{ .path = "library/client" }, + .protocol = .{ .path = "library/protocol" }, + .csv = .{ .path = "library/csv" }, + .@"xkeyboard-config" = .{ .path = "library/xkeyboard-config" }, // 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 diff --git a/library/client/build.zig b/library/client/build.zig new file mode 100644 index 0000000..ca20b20 --- /dev/null +++ b/library/client/build.zig @@ -0,0 +1,29 @@ +//! The "client" library domain (library/client): userspace-service clients — +//! they talk to services over IPC, not to the kernel. + +const std = @import("std"); + +pub fn build(b: *std.Build) void { + const kernel = b.dependency("kernel", .{}); + const protocol = b.dependency("protocol", .{}); + + const ipc = kernel.module("ipc"); + const time = kernel.module("time"); + + _ = b.addModule("display", .{ + .root_source_file = b.path("display/display.zig"), + .imports = &.{ + .{ .name = "ipc", .module = ipc }, + .{ .name = "time", .module = time }, + .{ .name = "display-protocol", .module = protocol.module("display-protocol") }, + }, + }); + _ = b.addModule("input", .{ + .root_source_file = b.path("input/input.zig"), + .imports = &.{ + .{ .name = "ipc", .module = ipc }, + .{ .name = "time", .module = time }, + .{ .name = "input-protocol", .module = protocol.module("input-protocol") }, + }, + }); +} diff --git a/library/client/build.zig.zon b/library/client/build.zig.zon new file mode 100644 index 0000000..af9ea9c --- /dev/null +++ b/library/client/build.zig.zon @@ -0,0 +1,13 @@ +.{ + .name = .client, + .version = "0.0.0", + .fingerprint = 0xc74404553e73d4ff, // Changing this has security and trust implications. + .minimum_zig_version = "0.16.0", + .dependencies = .{ + // The clients converse over ipc with time-bounded waits. + .kernel = .{ .path = "../kernel" }, + // Each client speaks its service's wire protocol. + .protocol = .{ .path = "../protocol" }, + }, + .paths = .{""}, +} diff --git a/library/csv/build.zig b/library/csv/build.zig new file mode 100644 index 0000000..f6ae051 --- /dev/null +++ b/library/csv/build.zig @@ -0,0 +1,20 @@ +//! The "csv" library domain: shared CSV helpers (comment stripping, field +//! iteration) for the /etc/*.csv config files — the device registry and the +//! init service list both parse them. + +const std = @import("std"); + +pub fn build(b: *std.Build) void { + _ = b.addModule("csv", .{ .root_source_file = b.path("csv.zig") }); + + // Standalone `zig build test` for this domain alone; the root build keeps + // its aggregate test step. + const test_step = b.step("test", "Run the csv unit tests"); + const csv_tests = b.addTest(.{ + .root_module = b.createModule(.{ + .root_source_file = b.path("csv.zig"), + .target = b.resolveTargetQuery(.{}), + }), + }); + test_step.dependOn(&b.addRunArtifact(csv_tests).step); +} diff --git a/library/csv/build.zig.zon b/library/csv/build.zig.zon new file mode 100644 index 0000000..148ca78 --- /dev/null +++ b/library/csv/build.zig.zon @@ -0,0 +1,8 @@ +.{ + .name = .csv, + .version = "0.0.0", + .fingerprint = 0x8a4525791f4e5b6, // Changing this has security and trust implications. + .minimum_zig_version = "0.16.0", + .dependencies = .{}, + .paths = .{""}, +} diff --git a/library/device/build.zig b/library/device/build.zig new file mode 100644 index 0000000..12580f6 --- /dev/null +++ b/library/device/build.zig @@ -0,0 +1,137 @@ +//! The "device" library domain (library/device): what a driver author imports. +//! The flat reference data (device-abi, pci-class, acpi-ids, usb-abi, usb-ids), +//! typed MMIO access, the driver-side client libraries (driver, pci, usb, +//! block), the AML interpreter, and the data-driven device registry. + +const std = @import("std"); + +pub fn build(b: *std.Build) void { + const kernel = b.dependency("kernel", .{}); + const protocol = b.dependency("protocol", .{}); + const csv = b.dependency("csv", .{}); + + const abi = kernel.module("abi"); + const system_call = kernel.module("system-call"); + const ipc = kernel.module("ipc"); + const time = kernel.module("time"); + + // The devices sub-project's public interface (the flat wire types), + // importable by user space, unlike the kernel-internal device model it + // also feeds (system/kernel/device-model.zig). + const device_abi = b.addModule("device-abi", .{ + .root_source_file = b.path("model/device-abi.zig"), + }); + // PCI class-code decoding (class/subclass/prog-IF -> names). Pure reference + // data, shared by kernel discovery and any user-space PCI tool. + const pci_class = b.addModule("pci-class", .{ + .root_source_file = b.path("pci/pci-class.zig"), + }); + // ACPI/PnP hardware-ID (_HID) names — the flat analog of pci-class. + _ = b.addModule("acpi-ids", .{ + .root_source_file = b.path("acpi/acpi-ids.zig"), + }); + // The AML interpreter, a build module so the ring-3 acpi service can run + // the same parser the kernel does (docs/discovery.md). Pure Zig, no kernel + // imports — one source, two builds. + _ = b.addModule("aml", .{ + .root_source_file = b.path("acpi/aml/aml.zig"), + }); + // The USB device-framework wire ABI (chapter-9 set-up packets, standard + + // class requests, descriptors) and the USB class-code taxonomy. + const usb_abi = b.addModule("usb-abi", .{ + .root_source_file = b.path("usb/usb-abi.zig"), + }); + const usb_ids = b.addModule("usb-ids", .{ + .root_source_file = b.path("usb/usb-ids.zig"), + }); + // Typed volatile MMIO register access + memory-ordering barriers, for + // drivers on top of an mmio_map grant. Depends only on `builtin`. + const mmio = b.addModule("mmio", .{ + .root_source_file = b.path("mmio/mmio.zig"), + }); + // The driver author's interface: device access + the device-manager hello + // handshake, folded together. + const driver = b.addModule("driver", .{ + .root_source_file = b.path("driver/driver.zig"), + .imports = &.{ + .{ .name = "abi", .module = abi }, + .{ .name = "device-abi", .module = device_abi }, + .{ .name = "system-call", .module = system_call }, + .{ .name = "ipc", .module = ipc }, + .{ .name = "time", .module = time }, + .{ .name = "device-manager-protocol", .module = protocol.module("device-manager-protocol") }, + }, + }); + // A device driver's view of its claimed PCI function: config-space header + // fields, BAR decode + map, capability walks (legacy + extended), MSI/MSI-X + // programming, power state, and function-level reset — the generic PCI + // mechanics every leaf PCI driver used to re-derive inline. + _ = b.addModule("pci", .{ + .root_source_file = b.path("pci/pci.zig"), + .imports = &.{ + .{ .name = "driver", .module = driver }, + .{ .name = "mmio", .module = mmio }, + .{ .name = "pci-class", .module = pci_class }, + .{ .name = "time", .module = time }, + }, + }); + // The USB class-driver transfer client: open a device on the xHCI bus and + // drive it (control / interrupt / bulk). Re-exports usb-abi / usb-ids as + // usb.abi / usb.ids for a single USB import. + _ = b.addModule("usb", .{ + .root_source_file = b.path("usb/usb.zig"), + .imports = &.{ + .{ .name = "ipc", .module = ipc }, + .{ .name = "time", .module = time }, + .{ .name = "usb-transfer-protocol", .module = protocol.module("usb-transfer-protocol") }, + .{ .name = "usb-abi", .module = usb_abi }, + .{ .name = "usb-ids", .module = usb_ids }, + }, + }); + // The block-device client — a device type, so it lives here. + _ = b.addModule("block", .{ + .root_source_file = b.path("block/block.zig"), + .imports = &.{ + .{ .name = "ipc", .module = ipc }, + .{ .name = "time", .module = time }, + .{ .name = "block-protocol", .module = protocol.module("block-protocol") }, + }, + }); + // The device registry: parse /etc/devices.csv into match rules and bind a + // reported device to a driver. Pure logic (no hardware, no syscalls), so it + // unit-tests on the host; the device manager imports it. + _ = b.addModule("device-registry", .{ + .root_source_file = b.path("registry/device-registry.zig"), + .imports = &.{.{ .name = "csv", .module = csv.module("csv") }}, + }); + + // Standalone `zig build test` for this domain alone; the root build keeps + // its aggregate test step. + const test_step = b.step("test", "Run the device library unit tests"); + for ([_][]const u8{ + "model/device-abi.zig", // wire-type sizes + "pci/pci-class.zig", // class/subclass/prog-IF name decoding + "acpi/acpi-ids.zig", // _HID name decoding + "acpi/aml/aml.zig", // AML parse + interpret, incl. Notify dispatch + "usb/usb-abi.zig", // wire sizes + bit packings + set-up packet encodings + "usb/usb-ids.zig", // class/subclass/protocol code assignments + "mmio/mmio.zig", // barriers assemble + registers round-trip + }) |root| { + const device_tests = b.addTest(.{ + .root_module = b.createModule(.{ + .root_source_file = b.path(root), + .target = b.resolveTargetQuery(.{}), + }), + }); + test_step.dependOn(&b.addRunArtifact(device_tests).step); + } + // The registry needs its csv import wired, so it doesn't fit the loop. + const registry_tests = b.addTest(.{ + .root_module = b.createModule(.{ + .root_source_file = b.path("registry/device-registry.zig"), + .target = b.resolveTargetQuery(.{}), + .imports = &.{.{ .name = "csv", .module = csv.module("csv") }}, + }), + }); + test_step.dependOn(&b.addRunArtifact(registry_tests).step); +} diff --git a/library/device/build.zig.zon b/library/device/build.zig.zon new file mode 100644 index 0000000..05b0797 --- /dev/null +++ b/library/device/build.zig.zon @@ -0,0 +1,15 @@ +.{ + .name = .device, + .version = "0.0.0", + .fingerprint = 0x92fb68eace23a4f, // Changing this has security and trust implications. + .minimum_zig_version = "0.16.0", + .dependencies = .{ + // driver/block/usb/pci build on the kernel library's concern modules. + .kernel = .{ .path = "../kernel" }, + // driver speaks device-manager-protocol; block/usb their transfer protocols. + .protocol = .{ .path = "../protocol" }, + // device-registry parses /etc/devices.csv with the shared csv helpers. + .csv = .{ .path = "../csv" }, + }, + .paths = .{""}, +} diff --git a/library/kernel/build.zig b/library/kernel/build.zig new file mode 100644 index 0000000..03595b1 --- /dev/null +++ b/library/kernel/build.zig @@ -0,0 +1,101 @@ +//! The "kernel" library domain (library/kernel): the userspace private-ABI +//! library (kernel32-style), split by concern into directly-importable +//! modules. The graph is a DAG: memory depends on thread (heap needs +//! Thread.Mutex), and thread does its own raw mmap so there is no cycle. +//! +//! This package also exports `abi` — the kernel <-> user contract (SystemCall +//! numbers, mmap prot flags, page_size). Its source lives with the kernel in +//! system/abi.zig, outside this directory, but userspace's one view of it is +//! exported here so every consumer names the same module instance. +//! +//! The root shim (root.zig) and the user link script (user.ld) are plain +//! files, not modules; build-support reaches them through this package's +//! directory (Dependency.path). + +const std = @import("std"); + +pub fn build(b: *std.Build) void { + const protocol = b.dependency("protocol", .{}); + + const abi = b.addModule("abi", .{ + .root_source_file = b.path("../../system/abi.zig"), + }); + const system_call = b.addModule("system-call", .{ + .root_source_file = b.path("system-call.zig"), + .imports = &.{.{ .name = "abi", .module = abi }}, + }); + const ipc = b.addModule("ipc", .{ + .root_source_file = b.path("ipc.zig"), + .imports = &.{ .{ .name = "abi", .module = abi }, .{ .name = "system-call", .module = system_call } }, + }); + const time = b.addModule("time", .{ + .root_source_file = b.path("time.zig"), + .imports = &.{.{ .name = "system-call", .module = system_call }}, + }); + const thread = b.addModule("thread", .{ + .root_source_file = b.path("thread.zig"), + .imports = &.{ .{ .name = "abi", .module = abi }, .{ .name = "system-call", .module = system_call } }, + }); + const logging = b.addModule("logging", .{ + .root_source_file = b.path("logging.zig"), + .imports = &.{ .{ .name = "abi", .module = abi }, .{ .name = "system-call", .module = system_call } }, + }); + const process = b.addModule("process", .{ + .root_source_file = b.path("process.zig"), + .imports = &.{ + .{ .name = "abi", .module = abi }, + .{ .name = "system-call", .module = system_call }, + .{ .name = "ipc", .module = ipc }, + .{ .name = "time", .module = time }, + }, + }); + _ = b.addModule("file-system", .{ + .root_source_file = b.path("file-system.zig"), + .imports = &.{ + .{ .name = "abi", .module = abi }, + .{ .name = "system-call", .module = system_call }, + .{ .name = "ipc", .module = ipc }, + .{ .name = "vfs-protocol", .module = protocol.module("vfs-protocol") }, + }, + }); + _ = b.addModule("memory", .{ + .root_source_file = b.path("memory/memory.zig"), + .imports = &.{ + .{ .name = "abi", .module = abi }, + .{ .name = "system-call", .module = system_call }, + .{ .name = "ipc", .module = ipc }, + .{ .name = "thread", .module = thread }, + }, + }); + _ = b.addModule("service", .{ + .root_source_file = b.path("service.zig"), + .imports = &.{ + .{ .name = "abi", .module = abi }, + .{ .name = "ipc", .module = ipc }, + .{ .name = "process", .module = process }, + }, + }); + _ = b.addModule("start", .{ + .root_source_file = b.path("start.zig"), + .imports = &.{ .{ .name = "process", .module = process }, .{ .name = "logging", .module = logging } }, + }); + + // Standalone `zig build test` for this domain alone; the root build keeps + // its aggregate test step. time and thread pull in the syscall wrappers, + // which need the `abi` module; their danos seams fall back to host + // primitives off the danos target, so they run with real host threads. + const test_step = b.step("test", "Run the kernel library unit tests"); + for ([_][]const u8{ + "time.zig", // Instant/Duration arithmetic + "thread.zig", // Mutex/Condition/RwLock/WaitGroup state machines + }) |root| { + const kernel_tests = b.addTest(.{ + .root_module = b.createModule(.{ + .root_source_file = b.path(root), + .target = b.resolveTargetQuery(.{}), + .imports = &.{.{ .name = "abi", .module = abi }}, + }), + }); + test_step.dependOn(&b.addRunArtifact(kernel_tests).step); + } +} diff --git a/library/kernel/build.zig.zon b/library/kernel/build.zig.zon new file mode 100644 index 0000000..91e8a1e --- /dev/null +++ b/library/kernel/build.zig.zon @@ -0,0 +1,11 @@ +.{ + .name = .kernel, + .version = "0.0.0", + .fingerprint = 0x5dd29aab36503453, // Changing this has security and trust implications. + .minimum_zig_version = "0.16.0", + .dependencies = .{ + // file-system speaks the VFS wire protocol. + .protocol = .{ .path = "../protocol" }, + }, + .paths = .{""}, +} diff --git a/library/protocol/build.zig b/library/protocol/build.zig new file mode 100644 index 0000000..27a88e6 --- /dev/null +++ b/library/protocol/build.zig @@ -0,0 +1,46 @@ +//! The "protocol" library domain: the wire protocols — each service's public +//! interface, exposed as its own module (docs/driver-model.md). Both sides of +//! every conversation depend on the contract by name; neither reaches into the +//! other's files. Pure flat wire types: no protocol module imports anything. +//! +//! vfs-protocol : the VFS server <-> the file layer (unistd/stdio) +//! input-protocol : the input fan-out service <-> sources + subscribers +//! block-protocol : a filesystem <-> a block driver (usb-storage) +//! usb-transfer-protocol : a USB class driver <-> the xHCI bus driver +//! device-manager-protocol : the device manager <-> drivers + discovery +//! display-protocol : the compositor's client-facing surface +//! scanout-protocol : the compositor -> a native scanout driver (docs/display-v2.md) +//! power-protocol : system power's domain-named surface (docs/power.md) + +const std = @import("std"); + +pub fn build(b: *std.Build) void { + for ([_]struct { name: []const u8, root: []const u8 }{ + .{ .name = "vfs-protocol", .root = "vfs/vfs-protocol.zig" }, + .{ .name = "input-protocol", .root = "input/input-protocol.zig" }, + .{ .name = "block-protocol", .root = "block/block-protocol.zig" }, + .{ .name = "usb-transfer-protocol", .root = "usb-transfer/usb-transfer-protocol.zig" }, + .{ .name = "device-manager-protocol", .root = "device-manager/device-manager-protocol.zig" }, + .{ .name = "display-protocol", .root = "display/display-protocol.zig" }, + .{ .name = "scanout-protocol", .root = "scanout/scanout-protocol.zig" }, + .{ .name = "power-protocol", .root = "power/power-protocol.zig" }, + }) |protocol| { + _ = b.addModule(protocol.name, .{ .root_source_file = b.path(protocol.root) }); + } + + // Standalone `zig build test` for this domain alone; the root build keeps + // its aggregate test step. + const test_step = b.step("test", "Run the protocol unit tests"); + for ([_][]const u8{ + "vfs/vfs-protocol.zig", // NodeKind / DirectoryEntry sizes + op values + "display/display-protocol.zig", // pack(): native pixel encoding per format + }) |root| { + const protocol_tests = b.addTest(.{ + .root_module = b.createModule(.{ + .root_source_file = b.path(root), + .target = b.resolveTargetQuery(.{}), + }), + }); + test_step.dependOn(&b.addRunArtifact(protocol_tests).step); + } +} diff --git a/library/protocol/build.zig.zon b/library/protocol/build.zig.zon new file mode 100644 index 0000000..7eb7e6f --- /dev/null +++ b/library/protocol/build.zig.zon @@ -0,0 +1,8 @@ +.{ + .name = .protocol, + .version = "0.0.0", + .fingerprint = 0xc8c0bc4c4d551283, // Changing this has security and trust implications. + .minimum_zig_version = "0.16.0", + .dependencies = .{}, + .paths = .{""}, +} diff --git a/library/xkeyboard-config/build.zig b/library/xkeyboard-config/build.zig new file mode 100644 index 0000000..11e53e4 --- /dev/null +++ b/library/xkeyboard-config/build.zig @@ -0,0 +1,31 @@ +//! The "xkeyboard-config" library domain: keyboard layouts compiled from the +//! X11 xkeyboard-config database into native Zig (keycode + modifiers -> +//! keysym/character). The `layouts` tables are generated by +//! tools/make-xkeyboard-config.py; `xkeyboard-config` is the hand-written API +//! over them. + +const std = @import("std"); + +pub fn build(b: *std.Build) void { + const layouts = b.addModule("layouts", .{ + .root_source_file = b.path("generated/layouts.zig"), + }); + _ = b.addModule("xkeyboard-config", .{ + .root_source_file = b.path("xkeyboard-config.zig"), + .imports = &.{.{ .name = "layouts", .module = layouts }}, + }); + + // Standalone `zig build test` for this domain alone; the root build keeps + // its aggregate test step. The keycode->character assertions are the + // end-to-end proof that the xkb-data -> generator -> Zig-lookup pipeline + // is correct. + const test_step = b.step("test", "Run the xkeyboard-config unit tests"); + const xkb_tests = b.addTest(.{ + .root_module = b.createModule(.{ + .root_source_file = b.path("xkeyboard-config.zig"), + .target = b.resolveTargetQuery(.{}), + .imports = &.{.{ .name = "layouts", .module = layouts }}, + }), + }); + test_step.dependOn(&b.addRunArtifact(xkb_tests).step); +} diff --git a/library/xkeyboard-config/build.zig.zon b/library/xkeyboard-config/build.zig.zon new file mode 100644 index 0000000..e295395 --- /dev/null +++ b/library/xkeyboard-config/build.zig.zon @@ -0,0 +1,8 @@ +.{ + .name = .xkeyboard_config, + .version = "0.0.0", + .fingerprint = 0xea5abe82f08b6eae, // Changing this has security and trust implications. + .minimum_zig_version = "0.16.0", + .dependencies = .{}, + .paths = .{""}, +}