Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
198767bb30 |
@@ -1,173 +0,0 @@
|
||||
//! The danos build API (docs/build-packages-plan.md): the one shared recipe
|
||||
//! for building a user-space binary. A binary package's build.zig names its
|
||||
//! binary and EXACTLY the modules its source imports — the moral equivalent
|
||||
//! of a C file's include list — and `userBinary` resolves each name from the
|
||||
//! library domain package that exports it. Nothing is pre-wired: an @import
|
||||
//! the package did not declare is a compile error, and a domain none of the
|
||||
//! imports come from never appears in the package's manifest. The only
|
||||
//! implicit dependency is the kernel package, because the shared root shim
|
||||
//! (root.zig, user.ld) lives there and itself reaches start + logging.
|
||||
//!
|
||||
//! Consumers declare this package in their build.zig.zon (as "build-support")
|
||||
//! and @import its build.zig from their own build.zig; nothing is compiled
|
||||
//! from this package itself — it exports build-time functions only.
|
||||
|
||||
const std = @import("std");
|
||||
|
||||
pub fn build(b: *std.Build) void {
|
||||
_ = b; // nothing to build: this package exports build-time functions only
|
||||
}
|
||||
|
||||
/// The freestanding x86-64 target every danos binary (kernel and user) is
|
||||
/// built for. SSE2 is part of the x86_64 baseline and UEFI leaves it enabled
|
||||
/// at handoff, so we keep it: disabling it forces soft-float and makes the
|
||||
/// compiler unable to encode the vector ops that std's formatting/runtime
|
||||
/// still emit.
|
||||
pub fn freestandingTarget(b: *std.Build) std.Build.ResolvedTarget {
|
||||
return b.resolveTargetQuery(.{
|
||||
.cpu_arch = .x86_64,
|
||||
.os_tag = .freestanding,
|
||||
.abi = .none,
|
||||
});
|
||||
}
|
||||
|
||||
/// Which library domain package exports each importable module — the one
|
||||
/// name -> home table. When a domain grows a module, it gets a row here; a
|
||||
/// binary naming a module whose home is missing from its own build.zig.zon
|
||||
/// fails loudly at dependency resolution.
|
||||
const ModuleHome = struct { name: []const u8, home: []const u8 };
|
||||
const module_homes = [_]ModuleHome{
|
||||
// library/kernel — the userspace private-ABI library, split by concern.
|
||||
.{ .name = "abi", .home = "kernel" },
|
||||
.{ .name = "system-call", .home = "kernel" },
|
||||
.{ .name = "ipc", .home = "kernel" },
|
||||
.{ .name = "time", .home = "kernel" },
|
||||
.{ .name = "thread", .home = "kernel" },
|
||||
.{ .name = "logging", .home = "kernel" },
|
||||
.{ .name = "process", .home = "kernel" },
|
||||
.{ .name = "file-system", .home = "kernel" },
|
||||
.{ .name = "memory", .home = "kernel" },
|
||||
.{ .name = "service", .home = "kernel" },
|
||||
.{ .name = "start", .home = "kernel" },
|
||||
// library/device — driver-side libraries + the flat reference data.
|
||||
.{ .name = "mmio", .home = "device" },
|
||||
.{ .name = "acpi-ids", .home = "device" },
|
||||
.{ .name = "device-abi", .home = "device" },
|
||||
.{ .name = "aml", .home = "device" },
|
||||
.{ .name = "usb-abi", .home = "device" },
|
||||
.{ .name = "usb-ids", .home = "device" },
|
||||
.{ .name = "usb", .home = "device" },
|
||||
.{ .name = "driver", .home = "device" },
|
||||
.{ .name = "block", .home = "device" },
|
||||
.{ .name = "pci", .home = "device" },
|
||||
.{ .name = "pci-class", .home = "device" },
|
||||
.{ .name = "device-registry", .home = "device" },
|
||||
// library/client — userspace service clients.
|
||||
.{ .name = "display-client", .home = "client" },
|
||||
.{ .name = "input-client", .home = "client" },
|
||||
// library/protocol — the wire protocols.
|
||||
.{ .name = "vfs-protocol", .home = "protocol" },
|
||||
.{ .name = "input-protocol", .home = "protocol" },
|
||||
.{ .name = "block-protocol", .home = "protocol" },
|
||||
.{ .name = "usb-transfer-protocol", .home = "protocol" },
|
||||
.{ .name = "device-manager-protocol", .home = "protocol" },
|
||||
.{ .name = "display-protocol", .home = "protocol" },
|
||||
.{ .name = "scanout-protocol", .home = "protocol" },
|
||||
.{ .name = "power-protocol", .home = "protocol" },
|
||||
// library/csv — the /etc/*.csv helpers.
|
||||
.{ .name = "csv", .home = "csv" },
|
||||
// library/xkeyboard-config — keycode -> keysym/character tables.
|
||||
.{ .name = "xkeyboard-config", .home = "xkeyboard-config" },
|
||||
};
|
||||
|
||||
fn moduleHome(name: []const u8) ?[]const u8 {
|
||||
for (module_homes) |entry| {
|
||||
if (std.mem.eql(u8, entry.name, name)) return entry.home;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/// What `userBinary` needs to know about one user binary.
|
||||
pub const UserBinaryOptions = struct {
|
||||
name: []const u8,
|
||||
/// The program's own source file — it becomes the `program` module the
|
||||
/// root shim imports; a program only defines `pub fn main`.
|
||||
root_source_file: std.Build.LazyPath,
|
||||
/// Exactly the modules the program's source @imports (directly or through
|
||||
/// its same-directory files) — no more, no less. Order is free; sorted
|
||||
/// reads best. An undeclared @import fails the compile; a declared name no
|
||||
/// domain exports fails the build graph with a pointer to module_homes.
|
||||
imports: []const []const u8,
|
||||
/// 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.
|
||||
threaded: bool = false,
|
||||
};
|
||||
|
||||
/// Build one user-space binary the same way for every program (init, the
|
||||
/// services, the drivers): freestanding, ReleaseSmall, `.large` code model
|
||||
/// (the image base is above 4 GiB — smaller models emit 32-bit relocations
|
||||
/// that can't reach), linked with the shared user link script. Pinned to
|
||||
/// LLVM + LLD so the script's PHDRS (segment permissions) are authoritative —
|
||||
/// the kernel's W^X user-ELF loader requires exact perms.
|
||||
///
|
||||
/// The compilation root is not the program's own file but the shared shim
|
||||
/// (the kernel package's root.zig), which supplies the root declarations
|
||||
/// (`main` re-export, panic handler, `_start` pull) so a program only defines
|
||||
/// `pub fn main`. The program's file becomes the `program` module the shim
|
||||
/// imports; reach it through `programModule` to add per-binary non-library
|
||||
/// modules (compile-time options).
|
||||
pub fn userBinary(b: *std.Build, options: UserBinaryOptions) *std.Build.Step.Compile {
|
||||
const kernel = b.dependency("kernel", .{});
|
||||
var imports: std.ArrayListUnmanaged(std.Build.Module.Import) = .empty;
|
||||
for (options.imports) |name| {
|
||||
const home = moduleHome(name) orelse @panic(b.fmt(
|
||||
"no library domain exports a module named '{s}' — if a domain grew it, add its row to module_homes in build-support/build.zig",
|
||||
.{name},
|
||||
));
|
||||
const dependency = if (std.mem.eql(u8, home, "kernel")) kernel else b.dependency(home, .{});
|
||||
imports.append(b.allocator, .{ .name = name, .module = dependency.module(name) }) catch @panic("OOM");
|
||||
}
|
||||
// Settings (target, optimize, code model, ...) live on the root module
|
||||
// only; the program module inherits them.
|
||||
const program_module = b.createModule(.{
|
||||
.root_source_file = options.root_source_file,
|
||||
.imports = imports.items,
|
||||
});
|
||||
const exe = b.addExecutable(.{
|
||||
.name = options.name,
|
||||
.root_module = b.createModule(.{
|
||||
.root_source_file = kernel.path("root.zig"),
|
||||
.target = freestandingTarget(b),
|
||||
.optimize = .ReleaseSmall,
|
||||
.code_model = .large,
|
||||
.single_threaded = !options.threaded, // a threaded binary needs real atomics/TLS
|
||||
.sanitize_c = .off,
|
||||
.stack_check = false,
|
||||
.stack_protector = false,
|
||||
// The root shim itself imports only start (_start + panic) and
|
||||
// logging (std_options) — straight from the kernel package, so a
|
||||
// program's own import list stays exactly its own.
|
||||
.imports = &.{
|
||||
.{ .name = "start", .module = kernel.module("start") },
|
||||
.{ .name = "logging", .module = kernel.module("logging") },
|
||||
.{ .name = "program", .module = program_module },
|
||||
},
|
||||
}),
|
||||
});
|
||||
exe.setLinkerScript(kernel.path("user.ld"));
|
||||
exe.entry = .{ .symbol_name = "_start" };
|
||||
exe.image_base = 0x7000_0000_0000;
|
||||
exe.use_llvm = true;
|
||||
exe.use_lld = true;
|
||||
return exe;
|
||||
}
|
||||
|
||||
/// The `program` module of a binary built by `userBinary` — the module rooted
|
||||
/// at the program's own source file. Per-binary non-library modules (an
|
||||
/// addOptions build_options) 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.
|
||||
pub fn programModule(exe: *std.Build.Step.Compile) *std.Build.Module {
|
||||
return exe.root_module.import_table.get("program").?;
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
.{
|
||||
.name = .build_support,
|
||||
.version = "0.0.0",
|
||||
.fingerprint = 0xad91962994f4be41, // Changing this has security and trust implications.
|
||||
.minimum_zig_version = "0.16.0",
|
||||
.dependencies = .{},
|
||||
.paths = .{""},
|
||||
}
|
||||
+8
-43
@@ -32,48 +32,6 @@
|
||||
// Once all dependencies are fetched, `zig build` no longer requires
|
||||
// internet connectivity.
|
||||
.dependencies = .{
|
||||
// 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" },
|
||||
// Binary packages (phase 2), consumed as artifacts for the boot image.
|
||||
.@"pci-bus" = .{ .path = "system/drivers/pci-bus" },
|
||||
.init = .{ .path = "system/services/init" },
|
||||
.fat = .{ .path = "system/services/fat" },
|
||||
.display = .{ .path = "system/services/display" },
|
||||
.@"display-demo" = .{ .path = "system/services/display-demo" },
|
||||
.@"device-manager" = .{ .path = "system/services/device-manager" },
|
||||
.input = .{ .path = "system/services/input" },
|
||||
.logger = .{ .path = "system/services/logger" },
|
||||
// The discovery pair and the /test fixtures are lazy: only what a
|
||||
// given build actually ships gets its build file loaded and compiled
|
||||
// (-Ddiscovery picks one of the pair; -Dtest-case pulls the fixtures).
|
||||
.acpi = .{ .path = "system/services/acpi", .lazy = true },
|
||||
.fdt = .{ .path = "system/services/fdt", .lazy = true },
|
||||
.@"ps2-bus" = .{ .path = "system/drivers/ps2-bus" },
|
||||
.@"usb-xhci-bus" = .{ .path = "system/drivers/usb-xhci-bus" },
|
||||
.@"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", .lazy = true },
|
||||
.@"fat-test" = .{ .path = "test/system/services/fat-test", .lazy = true },
|
||||
.@"shared-memory-server" = .{ .path = "test/system/services/shared-memory-server", .lazy = true },
|
||||
.@"shared-memory-client" = .{ .path = "test/system/services/shared-memory-client", .lazy = true },
|
||||
.@"crash-test" = .{ .path = "test/system/services/crash-test", .lazy = true },
|
||||
.@"device-list" = .{ .path = "test/system/services/device-list", .lazy = true },
|
||||
.@"pci-cap-test" = .{ .path = "test/system/services/pci-cap-test", .lazy = true },
|
||||
.@"iommu-fault-test" = .{ .path = "test/system/services/iommu-fault-test", .lazy = true },
|
||||
.@"input-source" = .{ .path = "test/system/services/input-source", .lazy = true },
|
||||
.@"input-test" = .{ .path = "test/system/services/input-test", .lazy = true },
|
||||
.@"args-echo" = .{ .path = "test/system/services/args-echo", .lazy = true },
|
||||
.@"process-test" = .{ .path = "test/system/services/process-test", .lazy = true },
|
||||
.@"thread-test" = .{ .path = "test/system/services/thread-test", .lazy = true },
|
||||
// See `zig fetch --save <url>` for a command-line interface for adding dependencies.
|
||||
//.example = .{
|
||||
// // When updating this field to a new URL, be sure to delete the corresponding
|
||||
@@ -112,5 +70,12 @@
|
||||
// Paths are relative to the build root. Use the empty string (`""`) to refer to
|
||||
// the build root itself.
|
||||
// A directory listed here means that all files within, recursively, are included.
|
||||
.paths = .{""},
|
||||
.paths = .{
|
||||
"build.zig",
|
||||
"build.zig.zon",
|
||||
"src",
|
||||
// For example...
|
||||
//"LICENSE",
|
||||
//"README.md",
|
||||
},
|
||||
}
|
||||
|
||||
@@ -1,166 +0,0 @@
|
||||
//! Boot-image assembly (docs/build-packages-plan.md, phase 3): everything
|
||||
//! between "here are the built binaries" and "here is a bootable volume".
|
||||
//! The FHS-shaped zig-out install tree, the boot manifest, the boot capsule,
|
||||
//! the FAT32 USB image (+ its serial-enabled twin for the QEMU run steps),
|
||||
//! and the release ISO — with their check steps. The root build.zig decides
|
||||
//! WHAT ships (the bundled list); this file owns HOW it becomes an image.
|
||||
|
||||
const std = @import("std");
|
||||
|
||||
/// One user binary and its FHS home on the boot volume (and in zig-out).
|
||||
pub const BundledBinary = struct { path: []const u8, binary: std.Build.LazyPath };
|
||||
|
||||
pub const Options = struct {
|
||||
/// The installed/flashable kernel (serial follows the root -Dserial).
|
||||
kernel: *std.Build.Step.Compile,
|
||||
/// The serial-enabled kernel variant the `run-x86-64` image boots.
|
||||
kernel_serial: *std.Build.Step.Compile,
|
||||
/// The UEFI loader (BOOTX64).
|
||||
efi: *std.Build.Step.Compile,
|
||||
/// Every user binary and data file at its FHS path.
|
||||
bundled: []const BundledBinary,
|
||||
};
|
||||
|
||||
/// Wire up the install tree, both FAT32 boot images, the release ISO, and the
|
||||
/// check steps. Returns the serial-enabled FAT image for the QEMU run steps.
|
||||
pub fn addImageSteps(b: *std.Build, options: Options) std.Build.LazyPath {
|
||||
// Everything installs into a FHS-shaped zig-out: it IS the danos filesystem *and*
|
||||
// the boot volume. Each binary lands at its addressed, leaf-collapsed path — the
|
||||
// kernel at zig-out/system/kernel (from system/kernel/kernel.zig), init at
|
||||
// zig-out/system/services/init, and so on (see docs/README.md). The bootloader
|
||||
// then loads these FHS paths off the volume.
|
||||
const kernel_install = b.addInstallArtifact(options.kernel, .{ .dest_dir = .{ .override = .{ .custom = "system" } } });
|
||||
b.getInstallStep().dependOn(&kernel_install.step);
|
||||
|
||||
// UEFI firmware requires the removable-media loader at exactly \EFI\BOOT\BOOTX64.efi,
|
||||
// so that path is fixed by the firmware (it is /boot's EFI stub, conceptually).
|
||||
const efi_install = b.addInstallArtifact(options.efi, .{ .dest_dir = .{ .override = .{ .custom = "EFI/BOOT" } } });
|
||||
b.getInstallStep().dependOn(&efi_install.step);
|
||||
|
||||
// The boot manifest: the FHS path of every bundled binary, one per line. The
|
||||
// EFI loader reads THIS by name and opens each listed path by name — FAT
|
||||
// name lookup is case-insensitive and firmware-portable, unlike directory
|
||||
// ENUMERATION, whose returned names vary by firmware (bare 8.3 entries come
|
||||
// back uppercase on some FAT drivers). The tree walk remains only as the
|
||||
// loader's fallback for hand-assembled sticks without a manifest.
|
||||
var manifest_text: std.ArrayListUnmanaged(u8) = .empty;
|
||||
for (options.bundled) |item| {
|
||||
manifest_text.append(b.allocator, '/') catch @panic("OOM");
|
||||
manifest_text.appendSlice(b.allocator, item.path) catch @panic("OOM");
|
||||
manifest_text.append(b.allocator, '\n') catch @panic("OOM");
|
||||
}
|
||||
const manifest_files = b.addWriteFiles();
|
||||
const manifest_file = manifest_files.add("manifest", manifest_text.items);
|
||||
const manifest_install = b.addInstallFileWithDir(manifest_file, .prefix, "system/manifest");
|
||||
b.getInstallStep().dependOn(&manifest_install.step);
|
||||
|
||||
// The boot capsule: the same bundled list packed into ONE file (v2
|
||||
// initial_ramdisk format), because a single open + sequential read is the
|
||||
// only firmware file I/O shape that is fast everywhere — a per-file tree
|
||||
// walk measured MINUTES on real firmware. The loader tries this first,
|
||||
// then the manifest, then the walk; the running system cannot tell the
|
||||
// difference (it always receives the same in-RAM table). Derived from the
|
||||
// tree in the same build graph, so the two cannot drift.
|
||||
const mk_capsule = b.addSystemCommand(&.{"python3"});
|
||||
mk_capsule.addFileArg(b.path("tools/pack-system-image.py"));
|
||||
const capsule_img = mk_capsule.addOutputFileArg("system.img");
|
||||
for (options.bundled) |item| {
|
||||
mk_capsule.addArg(item.path);
|
||||
mk_capsule.addFileArg(item.binary);
|
||||
}
|
||||
const capsule_install = b.addInstallFile(capsule_img, "boot/system.img");
|
||||
b.getInstallStep().dependOn(&capsule_install.step);
|
||||
|
||||
// Install every bundled binary to its FHS home, so zig-out is a true image of
|
||||
// the filesystem — the same tree make-fat-image.py lays out on the boot volume.
|
||||
for (options.bundled) |item| {
|
||||
const install = b.addInstallFileWithDir(item.binary, .prefix, item.path);
|
||||
b.getInstallStep().dependOn(&install.step);
|
||||
}
|
||||
|
||||
// --- danos-usb.img: the bootable FAT32 USB image ---
|
||||
// Format a real FAT32 image (the in-repo Python builder, no external tools)
|
||||
// holding the EFI stub, the kernel, and the whole /system tree of user
|
||||
// binaries at their FHS paths. 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 fat_image = addBootImage(b, options.kernel.getEmittedBin(), options.efi.getEmittedBin(), manifest_file, capsule_img, options.bundled);
|
||||
const fat_image_install = b.addInstallFile(fat_image, "danos-usb.img");
|
||||
b.getInstallStep().dependOn(&fat_image_install.step);
|
||||
|
||||
// The image `run-x86-64` boots: identical to the flashable one but with the
|
||||
// serial log sink compiled in, so a developer always gets the machine-readable
|
||||
// log captured to serial0 — without baking serial into the image users flash.
|
||||
// Built lazily (only when `run-x86-64` is requested), and never installed.
|
||||
const fat_image_serial = addBootImage(b, options.kernel_serial.getEmittedBin(), options.efi.getEmittedBin(), manifest_file, capsule_img, options.bundled);
|
||||
|
||||
// `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);
|
||||
|
||||
// --- release-x86-64: danos-x86-64.iso, the flashable release image ---
|
||||
// Wrap the FAT32 boot volume in a hybrid ISO (the in-repo Python builder
|
||||
// again, no xorriso/isohybrid): an ISO9660 whose El Torito EFI boot entry
|
||||
// and MBR ESP partition entry both point at the embedded FAT image. One
|
||||
// file then boots every way release media is consumed — flashed raw to a
|
||||
// USB stick with Etcher or dd, or burned to optical media — while
|
||||
// danos-usb.img stays the raw superfloppy QEMU and the test harness boot.
|
||||
const mk_iso = b.addSystemCommand(&.{"python3"});
|
||||
mk_iso.addFileArg(b.path("tools/make-iso-image.py"));
|
||||
const iso_image = mk_iso.addOutputFileArg("danos-x86-64.iso");
|
||||
mk_iso.addFileArg(fat_image);
|
||||
const iso_install = b.addInstallFile(iso_image, "danos-x86-64.iso");
|
||||
const release_step = b.step("release-x86-64", "Build the flashable x86-64 release ISO (zig-out/danos-x86-64.iso; flash with Etcher or dd)");
|
||||
release_step.dependOn(&iso_install.step);
|
||||
|
||||
// `zig build check-iso-image` — the ISO builder's own --verify (mirroring
|
||||
// check-fat-image): the MBR partition, the El Torito catalog, and the
|
||||
// embedded FAT32 image must all agree.
|
||||
const check_iso = b.addSystemCommand(&.{"python3"});
|
||||
check_iso.addFileArg(b.path("tools/make-iso-image.py"));
|
||||
check_iso.addArg("--verify");
|
||||
check_iso.addFileArg(iso_image);
|
||||
const check_iso_step = b.step("check-iso-image", "Verify the release ISO is a valid hybrid (MBR ESP partition + El Torito EFI entry)");
|
||||
check_iso_step.dependOn(&check_iso.step);
|
||||
|
||||
return fat_image_serial;
|
||||
}
|
||||
|
||||
/// Assemble the bootable FAT32 image (the in-repo Python builder) holding the
|
||||
/// EFI stub, the kernel, and every user binary at its FHS path — the volume's
|
||||
/// /system tree IS the system image; the EFI loader walks it at boot and builds
|
||||
/// the in-RAM initial_ramdisk from it. Factored so the serial-enabled
|
||||
/// `run-x86-64` variant can bundle its own serial kernel while sharing the
|
||||
/// loader and user tree (the loader's boot breadcrumbs and init's heartbeat both
|
||||
/// follow the top-level -Dserial). Returns the image's LazyPath.
|
||||
fn addBootImage(
|
||||
b: *std.Build,
|
||||
kernel_bin: std.Build.LazyPath,
|
||||
efi_bin: std.Build.LazyPath,
|
||||
manifest: std.Build.LazyPath,
|
||||
capsule: std.Build.LazyPath,
|
||||
bundled: []const BundledBinary,
|
||||
) std.Build.LazyPath {
|
||||
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(efi_bin);
|
||||
mk_fat.addArg("system/kernel");
|
||||
mk_fat.addFileArg(kernel_bin);
|
||||
mk_fat.addArg("system/manifest");
|
||||
mk_fat.addFileArg(manifest);
|
||||
mk_fat.addArg("boot/system.img");
|
||||
mk_fat.addFileArg(capsule);
|
||||
for (bundled) |item| {
|
||||
mk_fat.addArg(item.path);
|
||||
mk_fat.addFileArg(item.binary);
|
||||
}
|
||||
return fat_image;
|
||||
}
|
||||
-176
@@ -1,176 +0,0 @@
|
||||
//! The QEMU run steps (docs/build-packages-plan.md, phase 3): `run-x86-64`
|
||||
//! boots the serial-enabled FAT image via UEFI/OVMF; `run-x86-64-gpu` adds a
|
||||
//! virtio-gpu adapter for the native-present display path. OVMF firmware is
|
||||
//! probed across distro/OS layouts (-Dovmf-code / -Dovmf-vars override).
|
||||
|
||||
const std = @import("std");
|
||||
|
||||
/// Wire up the `run-x86-64` and `run-x86-64-gpu` steps around the given
|
||||
/// serial-enabled boot image (the guest boots that self-contained image
|
||||
/// attached as USB storage, not the installed FHS zig-out).
|
||||
pub fn addRunSteps(b: *std.Build, fat_image_serial: std.Build.LazyPath) void {
|
||||
// Firmware lives in different places per OS/distro, so probe the known
|
||||
// layouts (Architecture, Debian/Ubuntu, Fedora, macOS Homebrew) and use the first
|
||||
// that exists. Override with -Dovmf-code / -Dovmf-vars if yours is elsewhere.
|
||||
const ovmf_code = b.option(
|
||||
[]const u8,
|
||||
"ovmf-code",
|
||||
"Path to the OVMF_CODE firmware image",
|
||||
) orelse firstExisting(b.graph.io, &.{
|
||||
"/usr/share/edk2/x64/OVMF_CODE.4m.fd", // Architecture
|
||||
"/usr/share/OVMF/OVMF_CODE_4M.fd", // Debian/Ubuntu
|
||||
"/usr/share/OVMF/OVMF_CODE.fd", // older Debian/Ubuntu
|
||||
"/usr/share/edk2-ovmf/x64/OVMF_CODE.fd", // Fedora
|
||||
"/opt/homebrew/share/qemu/edk2-x86_64-code.fd", // macOS Homebrew (Apple Silicon)
|
||||
"/usr/local/share/qemu/edk2-x86_64-code.fd", // macOS Homebrew (Intel)
|
||||
});
|
||||
const ovmf_vars = b.option(
|
||||
[]const u8,
|
||||
"ovmf-vars",
|
||||
"Path to the OVMF_VARS firmware image (a writable copy is made)",
|
||||
) orelse firstExisting(b.graph.io, &.{
|
||||
"/usr/share/edk2/x64/OVMF_VARS.4m.fd", // Architecture
|
||||
"/usr/share/OVMF/OVMF_VARS_4M.fd", // Debian/Ubuntu
|
||||
"/usr/share/OVMF/OVMF_VARS.fd", // older Debian/Ubuntu
|
||||
"/usr/share/edk2-ovmf/x64/OVMF_VARS.fd", // Fedora
|
||||
"/opt/homebrew/share/qemu/edk2-i386-vars.fd", // macOS Homebrew (Apple Silicon)
|
||||
"/usr/local/share/qemu/edk2-i386-vars.fd", // macOS Homebrew (Intel)
|
||||
});
|
||||
|
||||
// The firmware needs to write NVRAM, so give it a writable copy of the vars.
|
||||
const vars_copy = b.addSystemCommand(&.{ "cp", "-f", ovmf_vars });
|
||||
const vars_out = vars_copy.addOutputFileArg("OVMF_VARS.4m.fd");
|
||||
|
||||
// Capture the guest's serial0 (danos's machine-readable log) to the qemu-test
|
||||
// scratch area — a dev/host artifact, kept out of the FHS boot volume we mount.
|
||||
// (/var/log/system is reserved for the kernel's own logging system later.) One
|
||||
// timestamped file per run.
|
||||
const log_dir = b.fmt("{s}/qemu-test", .{b.install_path});
|
||||
const make_log_dir = b.addSystemCommand(&.{ "mkdir", "-p", log_dir });
|
||||
|
||||
// --- run-x86-64: boot the x86-64 kernel in QEMU via UEFI/OVMF ---
|
||||
const run_efi = b.addSystemCommand(&.{
|
||||
"qemu-system-x86_64",
|
||||
"-device",
|
||||
"qemu-xhci,id=xhci",
|
||||
"-device",
|
||||
"usb-mouse,bus=xhci.0",
|
||||
"-device",
|
||||
"usb-kbd,bus=xhci.0",
|
||||
"-machine",
|
||||
"q35",
|
||||
"-m",
|
||||
"128M",
|
||||
"-drive",
|
||||
b.fmt("if=pflash,format=raw,readonly=on,file={s}", .{ovmf_code}),
|
||||
});
|
||||
run_efi.addArg("-drive");
|
||||
run_efi.addPrefixedFileArg("if=pflash,format=raw,file=", vars_out);
|
||||
// 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.
|
||||
// The serial-enabled variant, so serial0 carries the log for this dev boot.
|
||||
run_efi.addArg("-drive");
|
||||
run_efi.addPrefixedFileArg("if=none,id=bootusb,format=raw,file=", fat_image_serial);
|
||||
run_efi.addArgs(&.{
|
||||
"-device",
|
||||
"usb-storage,bus=xhci.0,drive=bootusb,removable=on,bootindex=0",
|
||||
"-net",
|
||||
"none",
|
||||
// Emulated display advertising 1280x720 as its native (EDID preferred)
|
||||
// resolution, so the kernel's native-resolution switch has something to
|
||||
// find. `-vga none` avoids a second, default adapter.
|
||||
"-vga",
|
||||
"none",
|
||||
"-device",
|
||||
"VGA,edid=on,xres=1280,yres=720",
|
||||
});
|
||||
const serial_log = b.fmt("{s}/run-x86-64-serial0-{s}.log", .{ log_dir, timestamp(b) });
|
||||
run_efi.addArgs(&.{ "-serial", b.fmt("file:{s}", .{serial_log}) });
|
||||
// We boot the self-contained `fat_image_serial` (added as a file arg above, so
|
||||
// it's already a dependency) — not the installed FHS zig-out — so `run-x86-64`
|
||||
// builds only the serial kernel, never the flashable one. Just make the serial
|
||||
// scratch dir first.
|
||||
run_efi.step.dependOn(&make_log_dir.step);
|
||||
|
||||
const run_efi_step = b.step("run-x86-64", "Boot the x86-64 kernel in QEMU (UEFI/OVMF); serial0 is logged to zig-out/qemu-test/run-x86-64-serial0-<timestamp>.log");
|
||||
run_efi_step.dependOn(&run_efi.step);
|
||||
|
||||
// --- run-x86-64-gpu: the same boot plus a virtio-gpu adapter ---
|
||||
// The VGA device still supplies the boot (GOP) framebuffer the compositor starts
|
||||
// on; the virtio-gpu function is discovered by the device-manager stack, its
|
||||
// driver announces a shared scanout, and the compositor upgrades off the GOP
|
||||
// floor to fenced, tear-free native presents (docs/display-v2.md).
|
||||
// This is the interactive twin of the `display-native` test case, and 512M
|
||||
// matches it (the whole driver stack + the compositor's surfaces at once).
|
||||
// QEMU shows one head per adapter: pick the virtio-gpu head in the View menu
|
||||
// to watch the native output.
|
||||
const run_gpu = b.addSystemCommand(&.{
|
||||
"qemu-system-x86_64",
|
||||
"-device",
|
||||
"qemu-xhci,id=xhci",
|
||||
"-device",
|
||||
"usb-mouse,bus=xhci.0",
|
||||
"-device",
|
||||
"usb-kbd,bus=xhci.0",
|
||||
"-machine",
|
||||
"q35",
|
||||
"-m",
|
||||
"512M",
|
||||
"-drive",
|
||||
b.fmt("if=pflash,format=raw,readonly=on,file={s}", .{ovmf_code}),
|
||||
});
|
||||
run_gpu.addArg("-drive");
|
||||
run_gpu.addPrefixedFileArg("if=pflash,format=raw,file=", vars_out);
|
||||
run_gpu.addArg("-drive");
|
||||
run_gpu.addPrefixedFileArg("if=none,id=bootusb,format=raw,file=", fat_image_serial);
|
||||
run_gpu.addArgs(&.{
|
||||
"-device",
|
||||
"usb-storage,bus=xhci.0,drive=bootusb,removable=on,bootindex=0",
|
||||
"-net",
|
||||
"none",
|
||||
"-vga",
|
||||
"none",
|
||||
"-device",
|
||||
"VGA,edid=on,xres=1280,yres=720",
|
||||
"-device",
|
||||
"virtio-gpu-pci",
|
||||
});
|
||||
const gpu_serial_log = b.fmt("{s}/run-x86-64-gpu-serial0-{s}.log", .{ log_dir, timestamp(b) });
|
||||
run_gpu.addArgs(&.{ "-serial", b.fmt("file:{s}", .{gpu_serial_log}) });
|
||||
run_gpu.step.dependOn(&make_log_dir.step);
|
||||
|
||||
const run_gpu_step = b.step("run-x86-64-gpu", "Boot in QEMU with a virtio-gpu adapter: the compositor upgrades to fenced (tear-free) native presents; watch the virtio-gpu head in QEMU's View menu");
|
||||
run_gpu_step.dependOn(&run_gpu.step);
|
||||
}
|
||||
|
||||
/// Return the first path in `candidates` that exists on the build host, else the
|
||||
/// first candidate as a fallback so a missing-firmware error still names a
|
||||
/// concrete (and, by convention, the primary) path. Used to locate OVMF firmware
|
||||
/// across distro/OS layouts without configuration.
|
||||
fn firstExisting(io: std.Io, candidates: []const []const u8) []const u8 {
|
||||
for (candidates) |path| {
|
||||
std.Io.Dir.accessAbsolute(io, path, .{}) catch continue;
|
||||
return path;
|
||||
}
|
||||
return candidates[0];
|
||||
}
|
||||
|
||||
/// A UTC timestamp like "20260708-153045", for naming a per-run artifact so
|
||||
/// repeated runs don't clobber each other's logs. Resolved when `zig build`
|
||||
/// runs, which is moments before QEMU launches.
|
||||
fn timestamp(b: *std.Build) []const u8 {
|
||||
const ns = std.Io.Clock.now(.real, b.graph.io).nanoseconds;
|
||||
const secs: u64 = @intCast(@divFloor(ns, std.time.ns_per_s));
|
||||
const es = std.time.epoch.EpochSeconds{ .secs = secs };
|
||||
const yd = es.getEpochDay().calculateYearDay();
|
||||
const md = yd.calculateMonthDay();
|
||||
const ds = es.getDaySeconds();
|
||||
return b.fmt("{d:0>4}{d:0>2}{d:0>2}-{d:0>2}{d:0>2}{d:0>2}", .{
|
||||
yd.year,
|
||||
md.month.numeric(),
|
||||
@as(u32, md.day_index) + 1,
|
||||
ds.getHoursIntoDay(),
|
||||
ds.getMinutesIntoHour(),
|
||||
ds.getSecondsIntoMinute(),
|
||||
});
|
||||
}
|
||||
+4
-15
@@ -55,7 +55,9 @@ rather than restate it. Roughly in the order things happen at runtime:
|
||||
15. **[drivers.md](device-driver-development/drivers.md) — writing a driver.** The payoff: a driver is an
|
||||
ordinary ring-3 process that claims a device, maps its registers, and **sleeps
|
||||
until its hardware interrupts it**. The claim is the capability; `irq_ack` is the
|
||||
unmask.
|
||||
unmask. The condensed version: the
|
||||
[new-driver checklist](device-driver-development/new-driver-checklist.md) —
|
||||
the minimum steps from boot-log line to mapped registers.
|
||||
16. **[driver-model.md](device-driver-development/driver-model.md) — buses, classes and host controllers.** How
|
||||
real driver stacks factor into three shapes and how families share code. The
|
||||
three primitives it proposed are long since built (M13 capability passing,
|
||||
@@ -263,20 +265,9 @@ test/ → /test the test tree: the QEMU harness (qemu_test.py, h
|
||||
system/services/ beside the on-image test fixtures — vfs-test/ thread-test/
|
||||
crash-test/ … — whose repo path IS their boot-volume path
|
||||
(/test/system/services/<name>)
|
||||
build-support/ the danos build API (build-time only, nothing on the image):
|
||||
the shared user-binary recipe + default-import wiring every
|
||||
build file consumes (docs/build-packages-plan.md)
|
||||
build/ root-build helpers: image assembly (images.zig) + the QEMU
|
||||
run steps (qemu.zig)
|
||||
tools/ host-side build scripts
|
||||
```
|
||||
|
||||
**Builds are packages** (docs/build-packages-plan.md): each `library/` domain owns a
|
||||
`build.zig`/`build.zig.zon` exporting its modules (with a standalone `zig build test`),
|
||||
every binary directory is a ~15-line package build, and the root `build.zig`
|
||||
orchestrates — the kernel + loader, what ships, and the aggregate test step — with
|
||||
image assembly in `build/images.zig` and the QEMU run steps in `build/qemu.zig`.
|
||||
|
||||
**Wire protocols live in `library/protocol/`**, one module per directory
|
||||
(`library/protocol/vfs/vfs-protocol.zig` is the `vfs-protocol` module), imported by module
|
||||
name. A protocol is the seam between a low-level driver and the higher-level service it
|
||||
@@ -331,7 +322,5 @@ exception in [coding-standards.md](coding-standards.md) applies to that seam.
|
||||
| System services (init, the `fat` filesystem, the device-manager) | `system/services/` |
|
||||
| Device drivers, one sub-project each (`pci-bus`, `ps2-bus`, `usb-xhci-bus` bus drivers) | `system/drivers/` |
|
||||
| On-image test fixtures for the QEMU cases (`vfs-test`, `crash-test`, `thread-test`, …) → `/test/system/services` | `test/system/services/` |
|
||||
| Build orchestration (kernel + loader, what ships, the aggregate test step) | `build.zig` (root; the shared user-binary recipe is `build-support/`, and each `library/` domain + binary package carries its own `build.zig`) |
|
||||
| Image assembly + `release-x86-64` (the flashable ISO) | `build/images.zig` |
|
||||
| `run-x86-64` / `run-x86-64-gpu` (QEMU/OVMF) | `build/qemu.zig` |
|
||||
| Build + `run-x86-64` (QEMU/OVMF) + `release-x86-64` (the flashable ISO) | `build.zig` |
|
||||
| QEMU integration test harness | `test/qemu_test.py` |
|
||||
|
||||
@@ -1,177 +0,0 @@
|
||||
# Plan: packages — hierarchical builds for libraries and binaries
|
||||
|
||||
**Status: complete** (branch `claude/build-packages-plan-174144`). Phase 0
|
||||
(`build-support`), phase 1 (all six library domains), phase 2 (every binary —
|
||||
the pci-bus pilot first, then services, drivers, and test fixtures in waves;
|
||||
multi-binary directories like ps2-bus and usb-hid are one package exporting
|
||||
several artifacts, and the acpi/fdt discovery pair each export an artifact
|
||||
named "discovery" that the root's -Ddiscovery picks between), and phase 3 (the
|
||||
root split into `build/images.zig` + `build/qemu.zig`; the root `build.zig` is
|
||||
~460 lines of orchestration, down from ~1,250). Every phase landed green: unit
|
||||
tests, the QEMU suite at parity with main, boot-image file list unchanged.
|
||||
The `lazyDependency` payoff (What-this-buys #4) is in too: the /test fixtures
|
||||
and the unselected discovery package are lazy — a build loads and compiles
|
||||
only what it ships. And imports are exact: the pre-wired default set is gone;
|
||||
every binary names precisely the modules its source imports and carries only
|
||||
those domains in its manifest (rule 1 below).
|
||||
|
||||
## Why
|
||||
|
||||
`build.zig` was ~1,250 lines, growing by three hand-written stanzas per binary;
|
||||
at a driver per device family that does not scale. More fundamentally: in one
|
||||
monolithic build every binary compiles against library *source*, so a library
|
||||
interface break is silently absorbed by whoever edits everything in one commit —
|
||||
the interface never has to be honest. danos is about isolation; the build should
|
||||
mirror it.
|
||||
|
||||
A **package** here is a build-time unit only — a directory owning a `build.zig`
|
||||
(recipe: what it exports, how to test it) and a `build.zig.zon` (manifest: name
|
||||
+ dependencies). Binaries remain fully static freestanding ELFs; packages change
|
||||
who declares what, not what links to what. Source code is untouched: `@import`
|
||||
uses module names (`"pci"`, `"service"`) exactly as today — only build files
|
||||
know where anything lives.
|
||||
|
||||
## Target shape
|
||||
|
||||
```
|
||||
build-support/ package: the danos build API (userBinary(), defaultImports(), targets)
|
||||
library/kernel/ package "kernel": modules abi, ipc, service, memory, process, logging, time, ... (depends on protocol)
|
||||
library/device/ package "device": modules driver, pci, usb-abi, model, ... (depends on kernel, protocol, csv)
|
||||
library/protocol/ package "protocol": the wire protocols
|
||||
library/client/ package "client" (depends on kernel, protocol)
|
||||
library/csv/ package "csv"
|
||||
library/xkeyboard-config/ package "xkeyboard-config"
|
||||
system/services/<name>/ one package per binary: ~15-line build.zig + zon
|
||||
system/drivers/<name>/ one package per binary
|
||||
build.zig (root) orchestrator: dependency() per binary, image assembly, QEMU, test steps
|
||||
```
|
||||
|
||||
The three shared contracts: `boot-handoff` stays a root module (only the
|
||||
loader↔kernel pair speaks it); `abi` is exported by the kernel package from
|
||||
`../../system/abi.zig` (the source stays with the kernel; userspace's one view
|
||||
of it lives in the package, so every consumer names the same module instance);
|
||||
`device-abi` is exported by device. Reaching outside the package root means the
|
||||
kernel package is valid only as an in-repo path dependency — it could never be
|
||||
fetched by hash — which is fine: path dependencies are the only way any of
|
||||
these packages is consumed.
|
||||
|
||||
Rules:
|
||||
|
||||
- **Imports are exact and per binary.** A binary's build.zig names precisely
|
||||
the modules its source `@import`s — the moral equivalent of a C file's
|
||||
include list — and its zon names only the domains those modules come from
|
||||
(plus `build-support` and `kernel`, which is implicit in every binary: the
|
||||
root shim and user link script live there). Nothing is pre-wired: an
|
||||
undeclared `@import` is a compile error, and build-support's one
|
||||
module-to-domain table (`module_homes`) resolves each name. Availability
|
||||
never meant bloat — Zig only compiles what a program actually imports — but
|
||||
exactness makes the declared interface honest and machine-checked.
|
||||
- **Modules export source, not artifacts** — each consumer compiles libraries
|
||||
with its own flags, so per-binary optimization choices keep working; Zig's
|
||||
cache deduplicates.
|
||||
- **Zon paths are relative and that is accepted.** Binaries sit exactly three
|
||||
levels deep, so the `../../../` prefix is a constant idiom; a library-domain
|
||||
move is a rare, already-breaking event fixed by one sed across manifests, and
|
||||
a stale path fails loudly before anything compiles.
|
||||
- **Cross-cutting build changes live in `build-support` only** — that is the
|
||||
contract that keeps per-binary build files declarative.
|
||||
|
||||
## What this buys
|
||||
|
||||
1. Library interfaces become machine-checked: a consumer can only import what
|
||||
it declared — per binary, down to the single module — and each domain's zon
|
||||
declares what it needs (claim-before-touch, applied to source). A keyboard
|
||||
driver carries `xkeyboard-config` in its manifest; nothing else does.
|
||||
2. Each library domain gets a standalone `zig build test` — runtime-library
|
||||
stability testing in isolation.
|
||||
3. Adding a binary = adding a directory (source + two small files), not editing
|
||||
three places in a 1,250-line file.
|
||||
4. `lazyDependency` lets an image target build only what it ships: the /test
|
||||
fixtures resolve only under -Dtest-case, and only the -Ddiscovery-selected
|
||||
discovery package ever loads.
|
||||
|
||||
## Phases
|
||||
|
||||
Each phase ends green: `zig build test` passes (88/88 QEMU) and the boot
|
||||
image's file list is unchanged. Byte-identical binaries are expected but not
|
||||
required (module reorganization can perturb symbol order); file list is the
|
||||
hard gate.
|
||||
|
||||
**Phase 0 — `build-support`.** Extract `addUserBinary`/`addThreadedUserBinary`,
|
||||
the freestanding target setup, and the default-import wiring into the
|
||||
`build-support` package. Root build consumes it; nothing else moves. This is
|
||||
the cross-cutting-change home, so it lands first.
|
||||
|
||||
**Phase 1 — library domains become packages.** In dependency order: `protocol`
|
||||
and `csv` (the roots) → `kernel` (depends on protocol: file-system speaks
|
||||
vfs-protocol) → `device`, `client`; `xkeyboard-config` stands alone. Each gets
|
||||
build.zig + zon + a standalone test step (client's is empty until its modules
|
||||
grow host tests — kept for uniformity, since the root aggregate depends on
|
||||
every domain's test step). The root build swaps its `createModule` calls for
|
||||
`b.dependency("<domain>").module("<name>")`. **No binary moves in this phase**
|
||||
— the root build is the pilot consumer, which proves the packages without
|
||||
touching 30 binaries.
|
||||
|
||||
**Phase 2 — binaries become packages, in waves.** The template was shaken out
|
||||
by the pci-bus pilot (see Status). Wave A: services (done). Wave B: the
|
||||
remaining drivers (done). Wave C: test fixtures (done). Root build shrank to
|
||||
orchestration per wave. init's `-Dserial` heartbeat flag rides a dependency
|
||||
option; a directory with several binaries (ps2-bus, usb-hid) is one package
|
||||
exporting several artifacts.
|
||||
|
||||
**Phase 3 — root cleanup (done).** What remained of the root build split into
|
||||
`build/images.zig` (the FHS install tree, boot manifest + capsule, FAT32
|
||||
images, release ISO, check steps) and `build/qemu.zig` (the run steps + OVMF
|
||||
probing), imported by a short root `build.zig`.
|
||||
|
||||
**Afterwards** (outside this plan): the intel-uhd-graphics-750 driver is
|
||||
(re)created as a greenfield package. The new-driver checklist's build step
|
||||
(docs/device-driver-development/new-driver-checklist.md, step 2) is already
|
||||
rewritten against the package template.
|
||||
|
||||
## Execution notes (the finished shape)
|
||||
|
||||
- The shared recipe lives in `build-support/build.zig`: `userBinary` (what
|
||||
every binary package calls, resolving each named import through the
|
||||
`module_homes` table) and `programModule` (for per-binary addOptions
|
||||
modules). The `start` root shim and `user.ld` are named through the kernel
|
||||
package (Dependency.path).
|
||||
- Adding a binary = adding a directory with source + a ~15-line build.zig +
|
||||
zon (copy any existing binary package, e.g.
|
||||
`system/drivers/pci-bus/build.zig`) listing exactly the modules the source
|
||||
imports and the domains they come from, then one dependency + one bundled
|
||||
entry in the root build.zig and one zon line.
|
||||
- The boot-tree array in the root (search `"etc/init.csv"` or
|
||||
`.getEmittedBin()`) is the image file list — the authoritative comparison
|
||||
target for any future build change.
|
||||
- Package unit tests live in each package's own `test` step; the root
|
||||
aggregate depends on every test-bearing package's step, so `zig build test`
|
||||
at the root still runs everything.
|
||||
|
||||
Verification per phase:
|
||||
|
||||
- Unit tests: `zig build test`.
|
||||
- QEMU integration suite: `python3 test/qemu_test.py` (docs/testing.md; the
|
||||
full suite, all cases must pass).
|
||||
- Image file list: the boot-tree array is the source of truth — snapshot it
|
||||
(paths only) before phase 0 and diff after each phase; `zig build
|
||||
check-fat-image` must also stay green.
|
||||
|
||||
Context a fresh session should read first: this doc, docs/testing.md,
|
||||
docs/coding-standards.md (kebab-case names, no abbreviations), and the
|
||||
`userBinary`/`userBinaryFromImports` bodies in build-support/build.zig. Commit
|
||||
style: no Co-Authored-By trailers.
|
||||
|
||||
## Risks / notes
|
||||
|
||||
- Zig version churn: the package API (`b.dependency`, zon schema) has moved
|
||||
between releases; the work pins against the repo's current Zig and any
|
||||
upgrade lands separately, never mid-phase.
|
||||
- The QEMU size-check tests hardcode source paths (e.g. virtio-gpu protocol
|
||||
struct sizes) — they moved into their binaries' packages with their waves,
|
||||
discharging the carry-along obligation.
|
||||
- Doc updates ride each phase: docs/README.md (repo layout + source map),
|
||||
docs/device-driver-development/new-driver-checklist.md (step 2) and
|
||||
devices-csv.md ("Adding a driver"), and the docs that cite the build recipe
|
||||
(driver-model.md, threading.md, system-requirements.md) reference build
|
||||
shapes that keep changing.
|
||||
@@ -96,15 +96,7 @@ in different namespaces — against the right `bus` column.
|
||||
|
||||
## Adding a driver
|
||||
|
||||
(The step-by-step walkthrough with a worked example is
|
||||
[new-driver-checklist.md](new-driver-checklist.md).)
|
||||
|
||||
1. Create `system/drivers/<name>/` with the driver source plus a ~15-line
|
||||
package `build.zig` + `build.zig.zon` (copy an existing driver package,
|
||||
e.g. `system/drivers/pci-bus/`; per-driver extras go through
|
||||
`build_support.programModule`). Then bundle it at `/system/drivers/<name>`:
|
||||
one dependency + one bundled entry in the root `build.zig`, one line in the
|
||||
root `build.zig.zon`.
|
||||
1. Build the driver binary and bundle it at `/system/drivers/<name>` (build.zig).
|
||||
2. Add a row to `etc/devices.csv` naming the identity it binds and its full path.
|
||||
|
||||
No device-manager change is required — the registry is the seam.
|
||||
|
||||
@@ -20,10 +20,9 @@ Read [display.md](display.md) first for the *why*; this is the *what* and the *o
|
||||
|
||||
Follow [coding-standards.md](../coding-standards.md): spell out non-acronym abbreviations in
|
||||
full, kebab-case file names, no `Co-Authored-By` trailers on commits. New user binaries
|
||||
go through build-support's shared user-binary recipe and get packed into the
|
||||
initial-ramdisk; protocols are modules exported by the `library/protocol` package.
|
||||
(This section predates the build-packages split; see
|
||||
[build-packages-plan.md](../build-packages-plan.md) for the current build shape.)
|
||||
go through `addUserBinary` in [build.zig](../../build.zig) and get packed into the
|
||||
initial-ramdisk; protocols are `b.addModule("…-protocol", …)` and imported into the
|
||||
`runtime` module.
|
||||
|
||||
## How to verify along the way
|
||||
|
||||
|
||||
@@ -18,11 +18,9 @@ lands on its own and ends in a **verifiable gate** — shaped for a `/loop` run,
|
||||
|
||||
Follow [coding-standards.md](../coding-standards.md): spell out non-acronym abbreviations,
|
||||
kebab-case file names, no `Co-Authored-By` trailers. New user binaries go through
|
||||
build-support's shared user-binary recipe and get packed into the initial-ramdisk;
|
||||
protocols are modules exported by the `library/protocol` package; new syscalls extend
|
||||
[abi.zig](../../system/abi.zig) `SystemCall` + a `library/kernel` wrapper.
|
||||
(This section predates the build-packages split; see
|
||||
[build-packages-plan.md](../build-packages-plan.md) for the current build shape.)
|
||||
`addUserBinary` and get packed into the initial-ramdisk; protocols are
|
||||
`b.addModule("…-protocol", …)` imported into `runtime`; new syscalls extend
|
||||
[abi.zig](../../system/abi.zig) `SystemCall` + a `library/runtime` wrapper.
|
||||
|
||||
## How to verify along the way
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ which one you're holding decides what you can do.
|
||||
|
||||
- **The PCI class-0x03 device is the raw controller** — BARs, config space, registers,
|
||||
IO ports. It is what you actually *own* after boot. On QEMU's emulated adapter
|
||||
([`-device VGA,edid=on`](../../build/qemu.zig), the Bochs VBE/DISPI model) the `base` GOP handed
|
||||
([`-device VGA,edid=on`](../../build.zig), the Bochs VBE/DISPI model) the `base` GOP handed
|
||||
you *is* that device's linear-framebuffer BAR — the same physical memory, seen through
|
||||
a different door. On a real discrete GPU, GOP's `base` is an aperture inside the GPU's
|
||||
VRAM BAR. danos already decodes this device
|
||||
|
||||
@@ -138,17 +138,13 @@ a higher-level service (block ↔ filesystem, a scanout driver ↔ the composito
|
||||
private wire to its *hardware* — virtio-gpu's command set — is not that; it stays a
|
||||
driver-private file, like the virtio-pci transport beside it.
|
||||
|
||||
The build side of this has since landed: every binary owns a package whose
|
||||
~15-line `build.zig` names EXACTLY the modules its source imports — the moral
|
||||
equivalent of a C file's include list — and the shared recipe in
|
||||
[`build-support/build.zig`](../../build-support/build.zig) (`userBinary`)
|
||||
resolves each name from the library domain that exports it (kernel's concern
|
||||
modules, the device driver libraries, the service clients, the protocols). An
|
||||
undeclared `@import` is a compile error, and a domain none of the imports come
|
||||
from never appears in the binary's manifest — a keyboard driver declares
|
||||
`xkeyboard-config`; nothing else does (see
|
||||
[build-packages-plan.md](../build-packages-plan.md)). That's the *entire*
|
||||
mechanism — Zig modules already give you everything else.
|
||||
The build side of this has since landed: [`addUserBinary`](build.zig) injects the
|
||||
default modules — the library/kernel concern modules (`ipc`, `memory`, `process`, `time`,
|
||||
`logging`, `file-system`, `thread`, `service`), the device/service clients (`driver`,
|
||||
`block`, `display`, `input`), plus `mmio`, `xkeyboard-config`, `acpi-ids` — into every user
|
||||
binary, and per-binary extras — protocol modules, bus logic — are added with
|
||||
`programModule(exe).addImport(...)`. That's the *entire* mechanism — Zig modules
|
||||
already give you everything else.
|
||||
|
||||
The discipline that makes this work: **a class driver must not import a bus's *hardware*
|
||||
logic module.** `usb-hid` imports `usb` (the transfer client) and `input-protocol`, never
|
||||
|
||||
@@ -4,7 +4,9 @@ In a monolithic kernel a driver is a function call away from everything: it runs
|
||||
ring 0, dereferences any physical address, and its interrupt handler *is* the ISR. In
|
||||
danos a driver is **an ordinary ring-3 process**. It has its own address space, it
|
||||
can crash without taking the kernel with it, and — the point of this document — it
|
||||
can be restarted ([resilience](../os-development/resilience.md)).
|
||||
can be restarted ([resilience](../os-development/resilience.md)). This document is
|
||||
the reasoning; the condensed do-this-then-that version is the
|
||||
[new-driver checklist](new-driver-checklist.md).
|
||||
|
||||
That leaves three questions the kernel has to answer, because a process can't answer
|
||||
them for itself:
|
||||
|
||||
@@ -1,218 +0,0 @@
|
||||
# New driver: the minimum steps
|
||||
|
||||
The shortest path from "a device shows up in the boot log" to "my process is
|
||||
running with its registers mapped". This is the checklist; the reasoning behind
|
||||
every step lives in [Writing a driver](drivers.md), the matching rules in
|
||||
[devices.csv](devices-csv.md), and interrupts in
|
||||
[device interrupts](device-interrupts.md).
|
||||
|
||||
Worked example throughout: the Intel UHD 750 iGPU, which the boot log reports as
|
||||
|
||||
```
|
||||
pci-bus: 0:2.0 bus=pci base=03 class=00 prog_if=00 vendor=8086 device=4C8A ...
|
||||
```
|
||||
|
||||
## 1. Create the source file
|
||||
|
||||
`system/drivers/<name>/<name>.zig` — kebab-case, abbreviations spelled out
|
||||
([coding standards](../coding-standards.md)). The directory name, the binary
|
||||
name, and the `devices.csv` driver path must all agree; a mismatch fails
|
||||
silently (the device-manager logs the spawn failure, nothing else happens).
|
||||
|
||||
The complete minimal driver — claims its device, logs every resource, maps the
|
||||
register window, then sleeps in the harness loop:
|
||||
|
||||
```zig
|
||||
//! /system/drivers/intel-uhd-graphics-750 — spawned by the device manager with
|
||||
//! the device-tree id as argv[1]; claims that device and no other.
|
||||
|
||||
const std = @import("std");
|
||||
const device = @import("driver");
|
||||
const ipc = @import("ipc");
|
||||
const memory = @import("memory");
|
||||
const process = @import("process");
|
||||
const service = @import("service");
|
||||
|
||||
/// No protocol yet: the kernel's IPC ceiling (MESSAGE_MAXIMUM) sizes the buffers.
|
||||
const message_maximum = 256;
|
||||
|
||||
var controller_id: u64 = 0;
|
||||
var register_base: usize = 0;
|
||||
|
||||
fn initialise(endpoint: ipc.Handle) bool {
|
||||
_ = endpoint; // needed later, for irq binding and timers
|
||||
|
||||
if (!device.claim(controller_id)) {
|
||||
std.log.err("unable to claim device {d}", .{controller_id});
|
||||
return false;
|
||||
}
|
||||
|
||||
// Fetch our own descriptor back for the device's resources.
|
||||
const buffer = memory.allocator().alloc(device.DeviceDescriptor, 64) catch return false;
|
||||
defer memory.allocator().free(buffer);
|
||||
const total = device.enumerate(buffer);
|
||||
const descriptor = for (buffer[0..@min(total, buffer.len)]) |d| {
|
||||
if (d.id == controller_id) break d;
|
||||
} else {
|
||||
std.log.err("device {d} not in the device tree", .{controller_id});
|
||||
return false;
|
||||
};
|
||||
|
||||
// Log every resource BEFORE choosing one (see step 5).
|
||||
var register_index: u64 = 0;
|
||||
for (descriptor.resources[0..@intCast(descriptor.resource_count)], 0..) |resource, index| {
|
||||
std.log.info("resource {d}: kind={d} start=0x{x} len=0x{x}", .{
|
||||
index, resource.kind, resource.start, resource.len,
|
||||
});
|
||||
// The 16 MiB window is GTTMMADR, the register BAR (this device also has
|
||||
// a 256 MiB memory BAR, GMADR — "first memory resource" would be wrong).
|
||||
if (resource.kind == @intFromEnum(device.ResourceKind.memory) and
|
||||
resource.len == 16 * 1024 * 1024) register_index = index;
|
||||
}
|
||||
if (register_index == 0) {
|
||||
std.log.err("register BAR not found", .{});
|
||||
return false;
|
||||
}
|
||||
|
||||
register_base = device.mmioMap(controller_id, register_index) orelse {
|
||||
std.log.err("mmio_map failed", .{});
|
||||
return false;
|
||||
};
|
||||
std.log.info("registers mapped at 0x{x}", .{register_base});
|
||||
return true;
|
||||
}
|
||||
|
||||
fn onMessage(message: []const u8, reply: []u8, sender: u32, capability: ?ipc.Handle) usize {
|
||||
_ = message;
|
||||
_ = reply;
|
||||
_ = sender;
|
||||
_ = capability;
|
||||
return 0; // no protocol yet; the zero-length ping is answered by the harness
|
||||
}
|
||||
|
||||
pub fn main(init: process.Init) void {
|
||||
const argument = init.arguments.get(1) orelse {
|
||||
std.log.err("missing device id (argv[1])", .{});
|
||||
return;
|
||||
};
|
||||
controller_id = std.fmt.parseInt(u64, argument, 10) catch {
|
||||
std.log.err("malformed device id '{s}'", .{argument});
|
||||
return;
|
||||
};
|
||||
service.run(message_maximum, .{
|
||||
.init = initialise,
|
||||
.on_message = onMessage,
|
||||
// .on_notification only once an IRQ or timer is bound
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
`claim` is the capability gate: MMIO mapping, DMA grants, and IRQ binding all
|
||||
require it, and it pins the IOMMU domain to this process
|
||||
([drivers.md — claim before touch](drivers.md#the-capability-claim-before-touch)).
|
||||
|
||||
## 2. Create the build package and register it in the root build
|
||||
|
||||
The driver directory is its own build package
|
||||
([build-packages-plan.md](../build-packages-plan.md)): a ~15-line `build.zig`
|
||||
plus a `build.zig.zon` beside the source. Copy both from an existing driver —
|
||||
`system/drivers/pci-bus/` is the template — and adjust the name, root source
|
||||
file, and the import list. The list names EXACTLY the modules the driver's
|
||||
source `@import`s (the moral equivalent of its include list; an undeclared
|
||||
import is a compile error):
|
||||
|
||||
```zig
|
||||
pub fn build(b: *std.Build) void {
|
||||
const exe = build_support.userBinary(b, .{
|
||||
.name = "intel-uhd-graphics-750",
|
||||
.root_source_file = b.path("intel-uhd-graphics-750.zig"),
|
||||
.imports = &.{ "driver", "ipc", "memory", "process", "service" },
|
||||
});
|
||||
b.installArtifact(exe);
|
||||
}
|
||||
```
|
||||
|
||||
The zon declares `build-support`, `kernel` (implicit in every binary: the root
|
||||
shim lives there), and the homes of the listed imports — for the minimal
|
||||
driver above that is kernel alone plus `device` (for `driver`); add
|
||||
`protocol`, `client`, ... only when an import comes from them (again, copy
|
||||
pci-bus's zon and adjust). For the `.fingerprint` field, leave the copied
|
||||
value in place and `zig build` will reject it and suggest the fresh one to
|
||||
paste.
|
||||
|
||||
Then three one-liners in the root build register the package: the dependency
|
||||
and a row in the boot-tree array in `build.zig` (search for
|
||||
`virtio_gpu_package` to land in the right places),
|
||||
|
||||
```zig
|
||||
const intel_uhd_graphics_750_exe = b.dependency("intel-uhd-graphics-750", .{}).artifact("intel-uhd-graphics-750");
|
||||
```
|
||||
|
||||
```zig
|
||||
.{ .path = "system/drivers/intel-uhd-graphics-750", .binary = intel_uhd_graphics_750_exe.getEmittedBin() },
|
||||
```
|
||||
|
||||
and the path entry in the root `build.zig.zon`:
|
||||
|
||||
```zig
|
||||
.@"intel-uhd-graphics-750" = .{ .path = "system/drivers/intel-uhd-graphics-750" },
|
||||
```
|
||||
|
||||
Without the boot-tree row the binary never reaches the image and the
|
||||
device-manager has nothing to spawn. (The package also builds standalone:
|
||||
`cd system/drivers/intel-uhd-graphics-750 && zig build`.)
|
||||
|
||||
## 3. Add the match rule to `etc/devices.csv`
|
||||
|
||||
One row: bus, class triplet, vendor/device, driver path. **Copy the class
|
||||
triplet from the pci-bus boot log line, not from another row** — for the iGPU
|
||||
above the correct rule is
|
||||
|
||||
```
|
||||
pci, 03, 00, 00, 8086, 4C8A, *, *, /system/drivers/intel-uhd-graphics-750
|
||||
```
|
||||
|
||||
Field-by-field rules and the most-specific-wins policy: [devices.csv](devices-csv.md).
|
||||
The registry is authoritative: an unmatched device is logged unbound, never
|
||||
guessed — so a wrong nibble here means the driver simply never starts.
|
||||
|
||||
## 4. First contact: read, predict, verify
|
||||
|
||||
Before writing any register, read one whose value you can predict from state
|
||||
the firmware already programmed (for a display controller: the pipe source
|
||||
size of the live mode). Registers are volatile loads at `register_base +
|
||||
offset`, where `offset` is what the device's manual lists:
|
||||
|
||||
```zig
|
||||
fn read32(offset: usize) u32 {
|
||||
return @as(*volatile u32, @ptrFromInt(register_base + offset)).*;
|
||||
}
|
||||
```
|
||||
|
||||
A matching read proves the whole chain — CSV match, spawn, claim, BAR choice,
|
||||
mapping — with zero risk to the hardware.
|
||||
|
||||
## 5. Verify the plumbing
|
||||
|
||||
- `zig build test` still passes.
|
||||
- On the image: `/var/log/<boot-stamp>/system/services/device-manager.log`
|
||||
shows `spawned <name> for device <N>`, and
|
||||
`/var/log/<boot-stamp>/system/drivers/<name>.log` holds the resource list and
|
||||
your first read.
|
||||
- If the driver did not spawn, diagnose in this order: binary on the image
|
||||
(step 2) → CSV row matches the log line exactly (step 3) → path identical in
|
||||
both (step 1).
|
||||
|
||||
## Later, when the device needs them
|
||||
|
||||
- **Interrupts**: MSI/MSI-X via the `pci` module, delivered as notifications to
|
||||
`on_notification` — see [device interrupts](device-interrupts.md) and the
|
||||
xHCI driver's `setupMsi` (QEMU trap documented there: enable MSI-X before
|
||||
unmasking the device's own interrupt-enable bit).
|
||||
- **DMA**: grant-backed buffers, bounded by the IOMMU domain established at
|
||||
claim time ([driver model](driver-model.md)).
|
||||
- **Children**: a bus driver publishes what it finds via `device_register`
|
||||
([drivers.md — publishing children](drivers.md#publishing-children-device_register)).
|
||||
- **A protocol**: replace `message_maximum` with the protocol's own maximum and
|
||||
dispatch on the operation word in `onMessage` — every service under
|
||||
`system/services/` is an example.
|
||||
@@ -24,9 +24,8 @@ EFI/BOOT/BOOTX64.efi <- the "removable media" default for x86-64
|
||||
```
|
||||
|
||||
The boot volume is **FHS-shaped** (see the repository-layout note in
|
||||
[README.md](../README.md)): the root `build.zig` compiles `boot/efi.zig` (built
|
||||
for the `uefi` target) and `build/images.zig` places it at
|
||||
`EFI/BOOT/BOOTX64.efi` — the one path UEFI firmware fixes — and lays
|
||||
[README.md](../README.md)): `build.zig` installs `boot/efi.zig` (built for the `uefi`
|
||||
target) at `EFI/BOOT/BOOTX64.efi` — the one path UEFI firmware fixes — and lays
|
||||
the rest out by FHS path: the kernel at `system/kernel`, init at
|
||||
`system/services/init`, the pre-packed boot capsule at `boot/system.img`
|
||||
([system-image.md](system-image.md)).
|
||||
|
||||
@@ -22,12 +22,11 @@ lands on its own and ends in a **verifiable gate** — shaped for a `/loop` run,
|
||||
## Conventions
|
||||
|
||||
Follow [coding-standards.md](../coding-standards.md): spell out non-acronym abbreviations,
|
||||
kebab-case file names, no `Co-Authored-By` trailers. New user binaries are
|
||||
packages whose build.zig calls `build_support.userBinary` (with `.threaded =
|
||||
true` where a binary spawns threads) and get packed into the initial-ramdisk;
|
||||
new syscalls extend [abi.zig](../../system/abi.zig) `SystemCall` + a
|
||||
`library/kernel` wrapper; test services live beside the code they exercise and
|
||||
register a `ServiceId` if they must be looked up.
|
||||
kebab-case file names, no `Co-Authored-By` trailers. New user binaries go through
|
||||
`addUserBinary` (with the new `threaded` flag where a binary spawns threads) and get
|
||||
packed into the initial-ramdisk; new syscalls extend [abi.zig](../../system/abi.zig)
|
||||
`SystemCall` + a `library/runtime` wrapper; test services live beside the code they
|
||||
exercise and register a `ServiceId` if they must be looked up.
|
||||
|
||||
## How to verify along the way
|
||||
|
||||
|
||||
@@ -61,10 +61,9 @@ runtime — rebuilt in lockstep — knows the mapping.
|
||||
backend would either bake danos syscall numbers into std (breaking ABI privacy and
|
||||
renumbering) or fork std to route back through the runtime — a permanent rebase
|
||||
cost that buys nothing the native type doesn't.
|
||||
2. **Our user binaries are built `single_threaded = true`** (the shared recipe in
|
||||
[build-support/build.zig](../../build-support/build.zig)), which compiles threading
|
||||
out entirely and makes atomics and TLS single-threaded. Threads need this flipped
|
||||
per binary regardless.
|
||||
2. **Our user binaries are built `single_threaded = true`** ([build.zig](../../build.zig)
|
||||
`addUserBinary`), which compiles threading out entirely and makes atomics and TLS
|
||||
single-threaded. Threads need this flipped per binary regardless.
|
||||
|
||||
So we take the *shape* of `std.Thread`, not the *type*. The cost of replicating the
|
||||
surface (spawn/join/Mutex/Condition) is small; the cost of the std type is the ABI
|
||||
@@ -237,10 +236,9 @@ see the intro). Two scoped pieces, as built:
|
||||
|
||||
### Build: multi-threaded codegen, opt-in
|
||||
|
||||
A binary opts in with `.threaded = true` in its package's
|
||||
`build_support.userBinary` call — the shared recipe in build-support then builds it
|
||||
`single_threaded = false` — so atomics
|
||||
and (later) TLS are real. Threads and atomics are unsound in a `single_threaded` image,
|
||||
A binary opts in by being added with `addThreadedUserBinary` — as `addUserBinary`,
|
||||
but the shared implementation builds it `single_threaded = false` — so atomics and
|
||||
(later) TLS are real. Threads and atomics are unsound in a `single_threaded` image,
|
||||
so a binary must opt in **before** it may call `Thread.spawn`. Everyone else
|
||||
stays single-threaded and lean.
|
||||
|
||||
|
||||
@@ -95,9 +95,9 @@ hypervisor configured for UEFI firmware and an xHCI USB controller.
|
||||
|
||||
| Requirement | Detail | Source |
|
||||
|---|---|---|
|
||||
| **x86-64, 64-bit only** | Kernel and loader are built exclusively for `x86_64`; the loader rejects any non-x86-64 kernel ELF (`error.WrongArchitecture`). | `build-support/build.zig` (`freestandingTarget`), `boot/efi.zig:622` |
|
||||
| **x86-64, 64-bit only** | Kernel and loader are built exclusively for `x86_64`; the loader rejects any non-x86-64 kernel ELF (`error.WrongArchitecture`). | `build.zig:481`, `boot/efi.zig:622` |
|
||||
| **Long mode + PAE + NX** | AP trampoline sets `CR4.PAE`, `EFER.LME`, `EFER.NXE`; NX is used in kernel page-table entries. | `system/kernel/architecture/x86_64/trampoline.s:62` |
|
||||
| **SSE / SSE2** | Baseline: the compiler emits SSE for ordinary struct copies. Trampoline enables `CR4.OSFXSR` + `OSXMMEXCPT` and clears `CR0.EM`. | `build-support/build.zig` (`freestandingTarget`), `trampoline.s:62` |
|
||||
| **SSE / SSE2** | Baseline: the compiler emits SSE for ordinary struct copies. Trampoline enables `CR4.OSFXSR` + `OSXMMEXCPT` and clears `CR0.EM`. | `build.zig:477`, `trampoline.s:62` |
|
||||
| **`syscall` / `sysret`** | Primary user↔kernel entry path. `EFER.SCE` enabled; `STAR`/`LSTAR`/`SFMASK` programmed per core. (`int 0x80` exists as a parallel gate.) | `architecture/x86_64/per-cpu.zig:71`, `isr.s:196` |
|
||||
| **Local APIC (xAPIC)** | LAPIC accessed via MMIO at `0xFEE00000`. LAPIC ID read as a `u8` — classic xAPIC. **x2APIC is not supported** (no MSR path). | `apic.zig:67`, `apic.zig:646` |
|
||||
| **CPUID + RDTSC** | CPUID leaf `0x15` for TSC frequency; RDTSC is the monotonic clock. | `apic.zig:333`, `apic.zig:113` |
|
||||
@@ -108,7 +108,7 @@ hypervisor configured for UEFI firmware and an xHCI USB controller.
|
||||
- **UEFI only.** A custom UEFI application loader is installed to
|
||||
`\EFI\BOOT\BOOTX64.efi`. There is **no BIOS, multiboot, or limine** path. The
|
||||
loader tolerates UEFI Class-3 machines with no legacy PIC/PIT.
|
||||
(`build/images.zig` — the EFI/BOOT install — and `boot/efi.zig`)
|
||||
(`build.zig:246`, `boot/efi.zig`)
|
||||
- **ACPI is the hardware-discovery mechanism.** The RSDP is taken from the UEFI
|
||||
configuration table (ACPI 2.0 GUID preferred, 1.0 fallback). Without a valid
|
||||
RSDP there is **no device discovery** — no SMP, no IOAPIC routing, no PCI/USB.
|
||||
|
||||
+1
-3
@@ -12,9 +12,7 @@ There are two layers:
|
||||
`system/abi.zig`, `library/device/model/device-abi.zig`) now spans ~26 modules:
|
||||
protocol and on-wire definitions (VFS, USB, virtio-gpu), the FAT engine, the
|
||||
display compositor, PS/2 and HID decoding, the kernel log ring, and the
|
||||
runtime's `time`/`thread` — the list is distributed across the library-domain
|
||||
and binary packages' own `test` steps, which the root `zig build test`
|
||||
aggregates (docs/build-packages-plan.md).
|
||||
runtime's `time`/`thread` — the full list is the test step in `build.zig`.
|
||||
These compile for the host and run natively.
|
||||
- **QEMU integration tests** (`python3 test/qemu_test.py`) — boot the real kernel
|
||||
and check its behaviour. This is the interesting part.
|
||||
|
||||
@@ -0,0 +1,339 @@
|
||||
Vendor ID,Device ID,Graphics Family,GPU Name
|
||||
0x8086,0x0152,HD Graphics,Xeon E3-1200 v2/3rd Gen Core GT1
|
||||
0x8086,0x0155,HD Graphics,Xeon E3-1200 v2/3rd Gen Core
|
||||
0x8086,0x0156,HD Graphics,Ivy Bridge mobile GT1
|
||||
0x8086,0x0157,HD Graphics,Ivy Bridge mobile GT1
|
||||
0x8086,0x015a,HD Graphics,Xeon E3-1200 v2/Ivy Bridge
|
||||
0x8086,0x0162,HD Graphics,Ivy Bridge GT2 (HD Graphics 4000)
|
||||
0x8086,0x0166,HD Graphics,Ivy Bridge mobile GT2 (HD Graphics 4000)
|
||||
0x8086,0x016a,HD Graphics,Xeon E3-1200 v2/3rd Gen Core GT3
|
||||
0x8086,0x0402,HD Graphics,Xeon E3-1200 v3/4th Gen Core GT1
|
||||
0x8086,0x0406,HD Graphics,Haswell GT1
|
||||
0x8086,0x040a,HD Graphics,Xeon E3-1200 v3 GT1
|
||||
0x8086,0x040b,HD Graphics,Haswell GT1
|
||||
0x8086,0x040e,HD Graphics,Haswell GT1
|
||||
0x8086,0x0412,HD Graphics,Xeon E3-1200 v3/4th Gen Core GT2
|
||||
0x8086,0x0416,HD Graphics,4th Gen Core GT2
|
||||
0x8086,0x041a,HD Graphics,Xeon E3-1200 v3 GT2
|
||||
0x8086,0x041b,HD Graphics,Haswell GT2
|
||||
0x8086,0x041e,HD Graphics,4th Gen Core Family GT2
|
||||
0x8086,0x0422,HD Graphics,Haswell GT3
|
||||
0x8086,0x0426,HD Graphics,Haswell GT3
|
||||
0x8086,0x042a,HD Graphics,Haswell GT3
|
||||
0x8086,0x042b,HD Graphics,Haswell GT3
|
||||
0x8086,0x042e,HD Graphics,Haswell GT3
|
||||
0x8086,0x0a02,HD Graphics,Haswell-ULT GT1
|
||||
0x8086,0x0a06,HD Graphics,Haswell-ULT GT1
|
||||
0x8086,0x0a0a,HD Graphics,Haswell-ULT GT1
|
||||
0x8086,0x0a0b,HD Graphics,Haswell-ULT GT1
|
||||
0x8086,0x0a0e,HD Graphics,Haswell-ULT GT1
|
||||
0x8086,0x0a12,HD Graphics,Haswell-ULT GT2
|
||||
0x8086,0x0a16,HD Graphics,Haswell-ULT GT2
|
||||
0x8086,0x0a1a,HD Graphics,Haswell-ULT GT2
|
||||
0x8086,0x0a1b,HD Graphics,Haswell-ULT GT2
|
||||
0x8086,0x0a1e,HD Graphics,Haswell-ULT GT2
|
||||
0x8086,0x0a22,HD Graphics,Haswell-ULT GT2
|
||||
0x8086,0x0a26,HD Graphics,Haswell-ULT GT2
|
||||
0x8086,0x0a2a,HD Graphics,Haswell-ULT GT2
|
||||
0x8086,0x0a2b,HD Graphics,Haswell-ULT GT2
|
||||
0x8086,0x0a2e,HD Graphics,Haswell-ULT GT2
|
||||
0x8086,0x0d02,Iris Pro Graphics,Crystal Well GT1
|
||||
0x8086,0x0d06,Iris Pro Graphics,Crystal Well GT2
|
||||
0x8086,0x0d0a,Iris Pro Graphics,Crystal Well GT2
|
||||
0x8086,0x0d0b,Iris Pro Graphics,Crystal Well GT2
|
||||
0x8086,0x0d0e,Iris Pro Graphics,Crystal Well GT2
|
||||
0x8086,0x0d12,Iris Pro Graphics,Crystal Well GT3 (Iris Pro 5200)
|
||||
0x8086,0x0d16,Iris Pro Graphics,Crystal Well GT3
|
||||
0x8086,0x0d1a,Iris Pro Graphics,Crystal Well GT3
|
||||
0x8086,0x0d1b,Iris Pro Graphics,Crystal Well GT3
|
||||
0x8086,0x0d1e,Iris Pro Graphics,Crystal Well GT3
|
||||
0x8086,0x0d22,Iris Pro Graphics,Crystal Well (Iris Pro 5200)
|
||||
0x8086,0x0d26,Iris Pro Graphics,Crystal Well GT3
|
||||
0x8086,0x0d2a,Iris Pro Graphics,Crystal Well GT3
|
||||
0x8086,0x0d2b,Iris Pro Graphics,Crystal Well GT3
|
||||
0x8086,0x0d2e,Iris Pro Graphics,Crystal Well GT3
|
||||
0x8086,0x0d32,Iris Pro Graphics,Crystal Well GT3
|
||||
0x8086,0x0d36,Iris Pro Graphics,Crystal Well GT3
|
||||
0x8086,0x0d3a,Iris Pro Graphics,Crystal Well GT3
|
||||
0x8086,0x1602,HD Graphics,Broadwell-U GT1
|
||||
0x8086,0x1606,HD Graphics,Broadwell-U GT1
|
||||
0x8086,0x160a,HD Graphics,Broadwell-U GT1
|
||||
0x8086,0x160b,HD Graphics,Broadwell-U GT1
|
||||
0x8086,0x160d,HD Graphics,Broadwell-U GT1
|
||||
0x8086,0x160e,HD Graphics,Broadwell-U GT1
|
||||
0x8086,0x1612,HD Graphics,Broadwell-H GT2 (HD Graphics 5600)
|
||||
0x8086,0x1616,HD Graphics,Broadwell-U GT2 (HD Graphics 5500)
|
||||
0x8086,0x161a,HD Graphics,Broadwell-U GT2
|
||||
0x8086,0x161b,HD Graphics,Broadwell-U GT2
|
||||
0x8086,0x161d,HD Graphics,Broadwell-U GT2
|
||||
0x8086,0x161e,HD Graphics,Broadwell-Y GT2 (HD Graphics 5300)
|
||||
0x8086,0x1622,Iris Pro Graphics,Broadwell-DT/H GT3 (Iris Pro 6200)
|
||||
0x8086,0x1626,HD Graphics,Broadwell-U GT3 (HD Graphics 6000)
|
||||
0x8086,0x162a,Iris Pro Graphics,Broadwell-DT GT3 (Iris Pro P6300)
|
||||
0x8086,0x162b,Iris Graphics,Broadwell-U GT3 (Iris 6100)
|
||||
0x8086,0x162d,HD Graphics,Broadwell-U GT3
|
||||
0x8086,0x162e,HD Graphics,Broadwell-U GT3
|
||||
0x8086,0x1632,HD Graphics,Broadwell-U GT3
|
||||
0x8086,0x1636,HD Graphics,Broadwell-U GT3
|
||||
0x8086,0x163a,HD Graphics,Broadwell-U GT3
|
||||
0x8086,0x163b,HD Graphics,Broadwell-U GT3
|
||||
0x8086,0x163d,HD Graphics,Broadwell-U GT3
|
||||
0x8086,0x163e,HD Graphics,Broadwell-U GT3
|
||||
0x8086,0x1902,HD Graphics,Skylake-S GT1 (HD Graphics 510)
|
||||
0x8086,0x1906,HD Graphics,Skylake-U GT1 (HD Graphics 510)
|
||||
0x8086,0x190a,HD Graphics,Skylake-DT GT1
|
||||
0x8086,0x190b,HD Graphics,Skylake GT1 (HD Graphics 510)
|
||||
0x8086,0x190e,HD Graphics,Skylake GT1
|
||||
0x8086,0x1912,HD Graphics,Skylake-S GT2 (HD Graphics 530)
|
||||
0x8086,0x1913,HD Graphics,Skylake GT2
|
||||
0x8086,0x1915,HD Graphics,Skylake GT2
|
||||
0x8086,0x1916,HD Graphics,Skylake-U GT2 (HD Graphics 520)
|
||||
0x8086,0x1917,HD Graphics,Skylake GT2
|
||||
0x8086,0x191a,HD Graphics,Skylake GT2
|
||||
0x8086,0x191b,HD Graphics,Skylake-H GT2 (HD Graphics 530)
|
||||
0x8086,0x191d,HD Graphics,Skylake-DT/H GT2 (HD Graphics P530)
|
||||
0x8086,0x191e,HD Graphics,Skylake-Y GT2 (HD Graphics 515)
|
||||
0x8086,0x1921,HD Graphics,Skylake GT2 (HD Graphics 520)
|
||||
0x8086,0x1923,HD Graphics,Skylake GT2 (HD Graphics 535)
|
||||
0x8086,0x1926,Iris Graphics,Skylake-U GT3 (Iris Graphics 540)
|
||||
0x8086,0x1927,Iris Graphics,Skylake-U GT3 (Iris Graphics 550)
|
||||
0x8086,0x192a,Iris Graphics,Skylake GT3
|
||||
0x8086,0x192b,Iris Graphics,Skylake GT3 (Iris Graphics 555)
|
||||
0x8086,0x192d,Iris Graphics,Skylake-H GT3 (Iris Graphics P555)
|
||||
0x8086,0x1932,Iris Pro Graphics,Skylake GT4 (Iris Pro 580)
|
||||
0x8086,0x193a,Iris Pro Graphics,Skylake-H GT4 (Iris Pro P580)
|
||||
0x8086,0x193b,Iris Pro Graphics,Skylake-H GT4 (Iris Pro 580)
|
||||
0x8086,0x193d,Iris Pro Graphics,Skylake-H GT4 (Iris Pro P580)
|
||||
0x8086,0x1a84,UHD Graphics,Skylake-DT
|
||||
0x8086,0x1a85,UHD Graphics,Skylake-DT
|
||||
0x8086,0x5902,HD Graphics,Kaby Lake-S GT1 (HD Graphics 610)
|
||||
0x8086,0x5906,HD Graphics,Kaby Lake-U GT1 (HD Graphics 610)
|
||||
0x8086,0x5908,HD Graphics,Kaby Lake GT1
|
||||
0x8086,0x590a,HD Graphics,Kaby Lake GT1
|
||||
0x8086,0x590b,HD Graphics,Kaby Lake GT1 (HD Graphics 610)
|
||||
0x8086,0x590e,HD Graphics,Kaby Lake GT1
|
||||
0x8086,0x5912,HD Graphics,Kaby Lake-S GT2 (HD Graphics 630)
|
||||
0x8086,0x5913,HD Graphics,Kaby Lake GT2
|
||||
0x8086,0x5915,HD Graphics,Kaby Lake GT2
|
||||
0x8086,0x5916,HD Graphics,Kaby Lake-U GT2 (HD Graphics 620)
|
||||
0x8086,0x5917,UHD Graphics,Kaby Lake-R GT2 (UHD Graphics 620)
|
||||
0x8086,0x591a,HD Graphics,Kaby Lake GT2
|
||||
0x8086,0x591b,HD Graphics,Kaby Lake-H GT2 (HD Graphics 630)
|
||||
0x8086,0x591c,UHD Graphics,Kaby Lake GT2 (UHD Graphics 615)
|
||||
0x8086,0x591d,HD Graphics,Kaby Lake-DT GT2 (HD Graphics P630)
|
||||
0x8086,0x591e,HD Graphics,Kaby Lake-Y GT2 (HD Graphics 615)
|
||||
0x8086,0x5921,HD Graphics,Kaby Lake GT2 (HD Graphics 620)
|
||||
0x8086,0x5923,HD Graphics,Kaby Lake GT2 (HD Graphics 635)
|
||||
0x8086,0x5926,Iris Plus Graphics,Kaby Lake-U GT3 (Iris Plus 640)
|
||||
0x8086,0x5927,Iris Plus Graphics,Kaby Lake-U GT3 (Iris Plus 650)
|
||||
0x8086,0x592a,Iris Plus Graphics,Kaby Lake GT3
|
||||
0x8086,0x592b,Iris Plus Graphics,Kaby Lake GT3
|
||||
0x8086,0x5932,Iris Plus Graphics,Kaby Lake GT3
|
||||
0x8086,0x593a,Iris Plus Graphics,Kaby Lake GT3
|
||||
0x8086,0x593b,Iris Plus Graphics,Kaby Lake GT3
|
||||
0x8086,0x593d,Iris Plus Graphics,Kaby Lake GT3
|
||||
0x8086,0x5a40,Intel Graphics,Apollolake
|
||||
0x8086,0x5a41,Intel Graphics,Apollolake
|
||||
0x8086,0x5a42,Intel Graphics,Apollolake
|
||||
0x8086,0x5a44,Intel Graphics,Apollolake
|
||||
0x8086,0x5a49,Intel Graphics,Apollolake
|
||||
0x8086,0x5a4a,Intel Graphics,Apollolake
|
||||
0x8086,0x5a4c,Intel Graphics,Apollolake
|
||||
0x8086,0x5a50,Intel Graphics,Apollolake
|
||||
0x8086,0x5a51,Intel Graphics,Apollolake
|
||||
0x8086,0x5a52,Intel Graphics,Apollolake
|
||||
0x8086,0x5a54,Intel Graphics,Apollolake
|
||||
0x8086,0x5a59,Intel Graphics,Apollolake
|
||||
0x8086,0x5a5a,Intel Graphics,Apollolake
|
||||
0x8086,0x5a5c,Intel Graphics,Apollolake
|
||||
0x8086,0x5a71,Intel Graphics,Apollolake
|
||||
0x8086,0x5a79,Intel Graphics,Apollolake
|
||||
0x8086,0x5a84,HD Graphics,Apollo Lake GT1 (HD Graphics 505)
|
||||
0x8086,0x5a85,HD Graphics,Apollo Lake GT1 (HD Graphics 500)
|
||||
0x8086,0x3184,UHD Graphics,GeminiLake (UHD Graphics 605)
|
||||
0x8086,0x3185,UHD Graphics,GeminiLake (UHD Graphics 600)
|
||||
0x8086,0x3e90,UHD Graphics,Coffee Lake-S GT1 (UHD Graphics 610)
|
||||
0x8086,0x3e91,UHD Graphics,Coffee Lake-S GT2 (UHD Graphics 630)
|
||||
0x8086,0x3e92,UHD Graphics,Coffee Lake-S GT2 (UHD Graphics 630)
|
||||
0x8086,0x3e93,UHD Graphics,Coffee Lake-S GT1 (UHD Graphics 610)
|
||||
0x8086,0x3e94,UHD Graphics,Coffee Lake-S GT2 (UHD Graphics P630)
|
||||
0x8086,0x3e96,UHD Graphics,Coffee Lake-S GT2 (UHD Graphics P630)
|
||||
0x8086,0x3e98,UHD Graphics,Coffee Lake-S GT2 (UHD Graphics 630)
|
||||
0x8086,0x3e99,UHD Graphics,Coffee Lake GT2
|
||||
0x8086,0x3e9a,UHD Graphics,Coffee Lake-S GT2 (UHD Graphics P630)
|
||||
0x8086,0x3e9b,UHD Graphics,Coffee Lake-H GT2 (UHD Graphics 630)
|
||||
0x8086,0x3e9c,UHD Graphics,Coffee Lake-S GT1 (UHD Graphics 610)
|
||||
0x8086,0x3ea0,UHD Graphics,Whiskey Lake-U GT2 (UHD Graphics 620)
|
||||
0x8086,0x3ea1,UHD Graphics,Whiskey Lake-U GT1 (UHD Graphics 610)
|
||||
0x8086,0x3ea2,UHD Graphics,Whiskey Lake GT1
|
||||
0x8086,0x3ea3,UHD Graphics,Whiskey Lake GT1
|
||||
0x8086,0x3ea4,UHD Graphics,Whiskey Lake GT1
|
||||
0x8086,0x3ea5,Iris Plus Graphics,Coffee Lake-U GT3e (Iris Plus 655)
|
||||
0x8086,0x3ea6,Iris Plus Graphics,Coffee Lake-U GT3 (Iris Plus 645)
|
||||
0x8086,0x3ea7,Iris Plus Graphics,Whiskey Lake GT3
|
||||
0x8086,0x3ea8,Iris Plus Graphics,Coffee Lake-U GT3 (Iris Plus 655)
|
||||
0x8086,0x3ea9,UHD Graphics,Coffee Lake-U GT2 (UHD Graphics 620)
|
||||
0x8086,0x87c0,UHD Graphics,9th Gen Core (UHD Graphics 617)
|
||||
0x8086,0x87ca,UHD Graphics,9th Gen Core (UHD Graphics 617)
|
||||
0x8086,0x8a50,Iris Plus Graphics,Ice Lake Gen1
|
||||
0x8086,0x8a51,Iris Plus Graphics,Ice Lake GT2 (Iris Plus G7)
|
||||
0x8086,0x8a52,Iris Plus Graphics,Ice Lake GT2 (Iris Plus G7)
|
||||
0x8086,0x8a53,Iris Plus Graphics,Ice Lake GT2 (Iris Plus G7)
|
||||
0x8086,0x8a54,Iris Plus Graphics,Ice Lake GT2
|
||||
0x8086,0x8a56,Iris Plus Graphics,Ice Lake GT1 (Iris Plus G1)
|
||||
0x8086,0x8a57,Iris Plus Graphics,Ice Lake GT1
|
||||
0x8086,0x8a58,UHD Graphics,Ice Lake-Y GT1 (UHD Graphics G1)
|
||||
0x8086,0x8a59,UHD Graphics,Ice Lake GT1
|
||||
0x8086,0x8a5a,Iris Plus Graphics,Ice Lake GT4 (Iris Plus G4)
|
||||
0x8086,0x8a5b,Iris Plus Graphics,Ice Lake GT4
|
||||
0x8086,0x8a5c,Iris Plus Graphics,Ice Lake GT4 (Iris Plus G4)
|
||||
0x8086,0x8a5d,Iris Plus Graphics,Ice Lake GT4
|
||||
0x8086,0x8a70,Iris Plus Graphics,Ice Lake
|
||||
0x8086,0x8a71,Iris Plus Graphics,Ice Lake
|
||||
0x8086,0x9a40,Iris Xe Graphics,Tiger Lake-UP4 GT2
|
||||
0x8086,0x9a49,Iris Xe Graphics,Tiger Lake-LP GT2
|
||||
0x8086,0x9a59,Iris Xe Graphics,Tiger Lake GT2
|
||||
0x8086,0x9a60,UHD Graphics,Tiger Lake-H GT1
|
||||
0x8086,0x9a68,UHD Graphics,Tiger Lake-H GT1
|
||||
0x8086,0x9a70,UHD Graphics,Tiger Lake-H GT1
|
||||
0x8086,0x9a78,UHD Graphics,Tiger Lake-LP GT2 (UHD Graphics G4)
|
||||
0x8086,0x9ac0,Iris Xe Graphics,Tiger Lake
|
||||
0x8086,0x9ac9,Iris Xe Graphics,Tiger Lake
|
||||
0x8086,0x9ad9,Iris Xe Graphics,Tiger Lake
|
||||
0x8086,0x9af8,Iris Xe Graphics,Tiger Lake
|
||||
0x8086,0x9b21,UHD Graphics,Comet Lake-U GT2 (UHD Graphics 620)
|
||||
0x8086,0x9b41,UHD Graphics,Comet Lake-U GT2
|
||||
0x8086,0x9ba0,UHD Graphics,Comet Lake GT1
|
||||
0x8086,0x9ba2,UHD Graphics,Comet Lake GT1
|
||||
0x8086,0x9ba4,UHD Graphics,Comet Lake-H GT1 (UHD Graphics 610)
|
||||
0x8086,0x9ba5,UHD Graphics,Comet Lake GT1
|
||||
0x8086,0x9ba8,UHD Graphics,Comet Lake-S GT1 (UHD Graphics 610)
|
||||
0x8086,0x9baa,UHD Graphics,Comet Lake GT1
|
||||
0x8086,0x9bab,UHD Graphics,Comet Lake GT1
|
||||
0x8086,0x9bac,UHD Graphics,Comet Lake GT1
|
||||
0x8086,0x9bc0,UHD Graphics,Comet Lake GT2
|
||||
0x8086,0x9bc2,UHD Graphics,Comet Lake GT2
|
||||
0x8086,0x9bc4,UHD Graphics,Comet Lake-H GT2
|
||||
0x8086,0x9bc5,UHD Graphics,Comet Lake-S GT2 (UHD Graphics 630)
|
||||
0x8086,0x9bc6,UHD Graphics,Comet Lake-S GT2 (UHD Graphics P630)
|
||||
0x8086,0x9bc8,UHD Graphics,Comet Lake-S GT2 (UHD Graphics 630)
|
||||
0x8086,0x9bca,UHD Graphics,Comet Lake GT2
|
||||
0x8086,0x9bcb,UHD Graphics,Comet Lake GT2
|
||||
0x8086,0x9bcc,UHD Graphics,Comet Lake GT2
|
||||
0x8086,0x9be6,UHD Graphics,Comet Lake-S GT2 (UHD Graphics P630)
|
||||
0x8086,0x9bf6,UHD Graphics,Coffee Lake-S GT2 (UHD Graphics P630)
|
||||
0x8086,0x4555,UHD Graphics,Elkhart Lake GT2 (UHD Graphics Gen11 16EU)
|
||||
0x8086,0x4571,UHD Graphics,Elkhart Lake GT2 (UHD Graphics Gen11 32EU)
|
||||
0x8086,0x4500,Intel Graphics,Elkhart Lake GT1
|
||||
0x8086,0x4541,Intel Graphics,Elkhart Lake GT1
|
||||
0x8086,0x4551,Intel Graphics,Elkhart Lake GT1
|
||||
0x8086,0x4557,Intel Graphics,Elkhart Lake GT1
|
||||
0x8086,0x4570,Intel Graphics,Elkhart Lake GT1
|
||||
0x8086,0x4c80,Intel Graphics,Rocket Lake
|
||||
0x8086,0x4c8a,UHD Graphics,Rocket Lake-S GT1 (UHD Graphics 750)
|
||||
0x8086,0x4c8b,UHD Graphics,Rocket Lake-S GT1 (UHD Graphics 730)
|
||||
0x8086,0x4c8c,Intel Graphics,Rocket Lake GT1
|
||||
0x8086,0x4c90,UHD Graphics,Rocket Lake-S GT1 (UHD Graphics P750)
|
||||
0x8086,0x4c9a,UHD Graphics,Rocket Lake-S
|
||||
0x8086,0x4e51,Intel Graphics,Jasper Lake GT1
|
||||
0x8086,0x4e55,UHD Graphics,Jasper Lake
|
||||
0x8086,0x4e57,Intel Graphics,Jasper Lake GT1
|
||||
0x8086,0x4e61,UHD Graphics,Jasper Lake
|
||||
0x8086,0x4e71,UHD Graphics,Jasper Lake
|
||||
0x8086,0x4626,Intel Graphics,Alder Lake-P GT1
|
||||
0x8086,0x4628,UHD Graphics,Alder Lake-UP3 GT2
|
||||
0x8086,0x462a,Intel Graphics,Alder Lake-P GT1
|
||||
0x8086,0x4680,UHD Graphics,Alder Lake-S GT1 (UHD Graphics 770)
|
||||
0x8086,0x4681,Intel Graphics,Alder Lake-S GT1
|
||||
0x8086,0x4682,UHD Graphics,Alder Lake-S GT1 (UHD Graphics 730)
|
||||
0x8086,0x4683,Intel Graphics,Alder Lake-S GT1
|
||||
0x8086,0x4688,UHD Graphics,Alder Lake-HX GT1 (UHD Graphics 770)
|
||||
0x8086,0x4689,Intel Graphics,Alder Lake-HX GT1
|
||||
0x8086,0x468a,Intel Graphics,Alder Lake-S
|
||||
0x8086,0x468b,Intel Graphics,Alder Lake-S
|
||||
0x8086,0x4690,UHD Graphics,Alder Lake-S GT1 (UHD Graphics 770)
|
||||
0x8086,0x4691,Intel Graphics,Alder Lake-S GT1
|
||||
0x8086,0x4692,UHD Graphics,Alder Lake-S GT1 (UHD Graphics 730)
|
||||
0x8086,0x4693,UHD Graphics,Alder Lake-S GT1 (UHD Graphics 710)
|
||||
0x8086,0x46a0,Intel Graphics,Alder Lake-P GT2
|
||||
0x8086,0x46a1,UHD Graphics,Alder Lake-P GT1
|
||||
0x8086,0x46a2,Intel Graphics,Alder Lake-P GT1
|
||||
0x8086,0x46a3,UHD Graphics,Alder Lake-P GT1
|
||||
0x8086,0x46a6,Iris Xe Graphics,Alder Lake-P GT2
|
||||
0x8086,0x46a8,Iris Xe Graphics,Alder Lake-UP3 GT2
|
||||
0x8086,0x46aa,Iris Xe Graphics,Alder Lake-UP4 GT2
|
||||
0x8086,0x46b0,Iris Xe Graphics,Alder Lake-P
|
||||
0x8086,0x46b1,Iris Xe Graphics,Alder Lake-P
|
||||
0x8086,0x46b2,Intel Graphics,Alder Lake-P GT1
|
||||
0x8086,0x46b3,UHD Graphics,Alder Lake-UP3 GT1
|
||||
0x8086,0x46c0,Intel Graphics,Alder Lake-M GT1
|
||||
0x8086,0x46c1,Iris Xe Graphics,Alder Lake-M
|
||||
0x8086,0x46c2,Intel Graphics,Alder Lake-M GT1
|
||||
0x8086,0x46c3,UHD Graphics,Alder Lake-UP4 GT1
|
||||
0x8086,0x46d0,UHD Graphics,Alder Lake-N
|
||||
0x8086,0x46d1,UHD Graphics,Alder Lake-N
|
||||
0x8086,0x46d2,UHD Graphics,Alder Lake-N
|
||||
0x8086,0x46d3,Intel Graphics,Alder Lake-N
|
||||
0x8086,0x46d4,Intel Graphics,Alder Lake-N
|
||||
0x8086,0x7d40,Intel Graphics,Meteor Lake-M
|
||||
0x8086,0x7d41,Intel Graphics,Arrow Lake-U
|
||||
0x8086,0x7d45,Intel Graphics,Meteor Lake-P
|
||||
0x8086,0x7d51,Arc Pro Graphics,Arrow Lake-P (Arc Pro 130T/140T)
|
||||
0x8086,0x7d55,Intel Arc Graphics,Meteor Lake-P
|
||||
0x8086,0x7d60,Intel Graphics,Meteor Lake-M
|
||||
0x8086,0x7d67,Intel Graphics,Arrow Lake-S
|
||||
0x8086,0x7dd1,Intel Graphics,Arrow Lake-P
|
||||
0x8086,0x7dd5,Intel Graphics,Meteor Lake-P
|
||||
0x8086,0xa720,UHD Graphics,Raptor Lake-P
|
||||
0x8086,0xa721,UHD Graphics,Raptor Lake-P
|
||||
0x8086,0xa780,UHD Graphics,Raptor Lake-S GT1 (UHD Graphics 770)
|
||||
0x8086,0xa781,UHD Graphics,Raptor Lake-S
|
||||
0x8086,0xa782,UHD Graphics,Raptor Lake-S
|
||||
0x8086,0xa783,UHD Graphics,Raptor Lake-S
|
||||
0x8086,0xa788,UHD Graphics,Raptor Lake-S
|
||||
0x8086,0xa789,UHD Graphics,Raptor Lake-S
|
||||
0x8086,0xa78a,UHD Graphics,Raptor Lake-S
|
||||
0x8086,0xa78b,UHD Graphics,Raptor Lake-S
|
||||
0x8086,0xa7a0,Iris Xe Graphics,Raptor Lake-P
|
||||
0x8086,0xa7a1,Iris Xe Graphics,Raptor Lake-P
|
||||
0x8086,0xa7a8,UHD Graphics,Raptor Lake-P
|
||||
0x8086,0xa7a9,UHD Graphics,Raptor Lake-P
|
||||
0x8086,0xa7aa,Intel Graphics,Raptor Lake-P
|
||||
0x8086,0xa7ab,Intel Graphics,Raptor Lake-P
|
||||
0x8086,0xa7ac,Intel Graphics,Raptor Lake-U
|
||||
0x8086,0xa7ad,Intel Graphics,Raptor Lake-U
|
||||
0x8086,0xb640,Intel Graphics,Arrow Lake-H
|
||||
0x8086,0x4905,Iris Xe MAX Graphics,DG1
|
||||
0x8086,0x4906,Iris Xe Graphics,DG1
|
||||
0x8086,0x4907,Intel Graphics,DG1 Server
|
||||
0x8086,0x4908,Iris Xe Graphics,DG1
|
||||
0x8086,0x4909,Iris Xe MAX Graphics,DG1 (Iris Xe MAX 100)
|
||||
0x8086,0x5690,Arc Graphics,DG2 (Arc A770M)
|
||||
0x8086,0x5691,Arc Graphics,DG2 (Arc A730M)
|
||||
0x8086,0x5692,Arc Graphics,DG2 (Arc A550M)
|
||||
0x8086,0x5693,Arc Graphics,DG2 (Arc A370M)
|
||||
0x8086,0x5694,Arc Graphics,DG2 (Arc A350M)
|
||||
0x8086,0x5695,Iris Xe MAX Graphics,DG2 (Iris Xe MAX A200M)
|
||||
0x8086,0x5696,Arc Graphics,DG2 (Arc A570M)
|
||||
0x8086,0x5697,Arc Graphics,DG2 (Arc A530M)
|
||||
0x8086,0x5698,Arc Graphics,DG2 (Arc Xe Graphics)
|
||||
0x8086,0x56a0,Arc Graphics,DG2 (Arc A770)
|
||||
0x8086,0x56a1,Arc Graphics,DG2 (Arc A750)
|
||||
0x8086,0x56a2,Arc Graphics,DG2 (Arc A580)
|
||||
0x8086,0x56a3,Arc Graphics,DG2 (Arc Xe Graphics)
|
||||
0x8086,0x56a4,Arc Graphics,DG2 (Arc Xe Graphics)
|
||||
0x8086,0x56a5,Arc Graphics,DG2 (Arc A380)
|
||||
0x8086,0x56a6,Arc Graphics,DG2 (Arc A310)
|
||||
0x8086,0x56b0,Arc Pro Graphics,DG2 (Arc Pro A30M)
|
||||
0x8086,0x56b1,Arc Pro Graphics,DG2 (Arc Pro A40/A50)
|
||||
0x8086,0x56b2,Arc Pro Graphics,DG2 (Arc Pro A60M)
|
||||
0x8086,0x56b3,Arc Pro Graphics,DG2 (Arc Pro A60)
|
||||
0x8086,0x56ba,Arc Graphics,DG2 (Arc A380E)
|
||||
0x8086,0x56bb,Arc Graphics,DG2 (Arc A310E)
|
||||
0x8086,0x56bc,Arc Graphics,DG2 (Arc A370E)
|
||||
0x8086,0x56bd,Arc Graphics,DG2 (Arc A350E)
|
||||
0x8086,0x56be,Arc Graphics,DG2 (Arc A750E)
|
||||
0x8086,0x56bf,Arc Graphics,DG2 (Arc A580E)
|
||||
0x8086,0x56c0,Arc Graphics,DG2 (Data Center GPU Flex 170)
|
||||
0x8086,0x56c1,Arc Graphics,DG2 (Data Center GPU Flex 140)
|
||||
0x8086,0x56c2,Arc Graphics,DG2 (Data Center GPU Flex 170V)
|
||||
|
@@ -25,6 +25,7 @@
|
||||
#
|
||||
# bus base class prog_if vendor device subsystem hid driver
|
||||
pci, 0C, 03, 30, *, *, *, *, /system/drivers/usb-xhci-bus
|
||||
pci, 03, 00, 00, 8086, 4C8A, *, *, /system/drivers/intel-uhd-graphics-750
|
||||
pci, 03, 80, *, 1AF4, 1050, *, *, /system/drivers/virtio-gpu
|
||||
usb, 03, 01, 01, *, *, *, *, /system/drivers/usb-hid-keyboard
|
||||
usb, 03, 01, 02, *, *, *, *, /system/drivers/usb-hid-mouse
|
||||
|
||||
|
@@ -1,38 +0,0 @@
|
||||
//! The "client" library domain (library/client): userspace-service clients —
|
||||
//! they talk to services over IPC, not to the kernel. Client modules end in
|
||||
//! `-client` the way wire protocols end in `-protocol`, so a service, its
|
||||
//! protocol, and its client never share a name (`display` the service,
|
||||
//! `display-protocol` the wire contract, `display-client` a program's view).
|
||||
|
||||
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-client", .{
|
||||
.root_source_file = b.path("display/display-client.zig"),
|
||||
.imports = &.{
|
||||
.{ .name = "ipc", .module = ipc },
|
||||
.{ .name = "time", .module = time },
|
||||
.{ .name = "display-protocol", .module = protocol.module("display-protocol") },
|
||||
},
|
||||
});
|
||||
_ = b.addModule("input-client", .{
|
||||
.root_source_file = b.path("input/input-client.zig"),
|
||||
.imports = &.{
|
||||
.{ .name = "ipc", .module = ipc },
|
||||
.{ .name = "time", .module = time },
|
||||
.{ .name = "input-protocol", .module = protocol.module("input-protocol") },
|
||||
},
|
||||
});
|
||||
|
||||
// Standalone `zig build test`, kept for uniformity across the domains (the
|
||||
// root aggregate depends on every domain's test step). The clients have no
|
||||
// host-runnable unit tests yet — they are thin IPC conversation wrappers —
|
||||
// so the step is empty until one grows some.
|
||||
_ = b.step("test", "Run the client unit tests (none yet)");
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
.{
|
||||
.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 = .{""},
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
//! 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);
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
.{
|
||||
.name = .csv,
|
||||
.version = "0.0.0",
|
||||
.fingerprint = 0x8a4525791f4e5b6, // Changing this has security and trust implications.
|
||||
.minimum_zig_version = "0.16.0",
|
||||
.dependencies = .{},
|
||||
.paths = .{""},
|
||||
}
|
||||
@@ -1,137 +0,0 @@
|
||||
//! 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);
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
.{
|
||||
.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 = .{""},
|
||||
}
|
||||
@@ -1,104 +0,0 @@
|
||||
//! 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. Reaching
|
||||
//! outside the package root means this package is valid only as an in-repo
|
||||
//! path dependency (never fetchable by hash) — fine, since path dependencies
|
||||
//! are the only way danos packages are consumed.
|
||||
//!
|
||||
//! 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);
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
.{
|
||||
.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 = .{""},
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
//! 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);
|
||||
}
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
.{
|
||||
.name = .protocol,
|
||||
.version = "0.0.0",
|
||||
.fingerprint = 0xc8c0bc4c4d551283, // Changing this has security and trust implications.
|
||||
.minimum_zig_version = "0.16.0",
|
||||
.dependencies = .{},
|
||||
.paths = .{""},
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
//! 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);
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
.{
|
||||
.name = .xkeyboard_config,
|
||||
.version = "0.0.0",
|
||||
.fingerprint = 0xea5abe82f08b6eae, // Changing this has security and trust implications.
|
||||
.minimum_zig_version = "0.16.0",
|
||||
.dependencies = .{},
|
||||
.paths = .{""},
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
//! /system/drivers/intel-uhd-graphics-750 — spawned by the device manager with
|
||||
//! the device-tree id as argv[1]; claims that device and no other.
|
||||
|
||||
const std = @import("std");
|
||||
const device = @import("driver");
|
||||
const ipc = @import("ipc");
|
||||
const memory = @import("memory");
|
||||
const process = @import("process");
|
||||
const service = @import("service");
|
||||
|
||||
/// No protocol yet: the kernel's IPC ceiling (MESSAGE_MAXIMUM) sizes the buffers.
|
||||
const message_maximum = 256;
|
||||
|
||||
var controller_id: u64 = 0;
|
||||
var register_base: usize = 0;
|
||||
|
||||
fn initialise(endpoint: ipc.Handle) bool {
|
||||
_ = endpoint; // needed later, for irq binding and timers
|
||||
|
||||
if (!device.claim(controller_id)) {
|
||||
std.log.err("unable to claim device {d}", .{controller_id});
|
||||
return false;
|
||||
}
|
||||
|
||||
// Fetch our own descriptor back for the device's resources.
|
||||
const buffer = memory.allocator().alloc(device.DeviceDescriptor, 64) catch return false;
|
||||
defer memory.allocator().free(buffer);
|
||||
const total = device.enumerate(buffer);
|
||||
const descriptor = for (buffer[0..@min(total, buffer.len)]) |d| {
|
||||
if (d.id == controller_id) break d;
|
||||
} else {
|
||||
std.log.err("device {d} not in the device tree", .{controller_id});
|
||||
return false;
|
||||
};
|
||||
|
||||
// Log every resource BEFORE choosing one (see step 5).
|
||||
var register_index: u64 = 0;
|
||||
for (descriptor.resources[0..@intCast(descriptor.resource_count)], 0..) |resource, index| {
|
||||
std.log.info("resource {d}: kind={d} start=0x{x} len=0x{x}", .{
|
||||
index, resource.kind, resource.start, resource.len,
|
||||
});
|
||||
// The 16 MiB window is GTTMMADR, the register BAR (this device also has
|
||||
// a 256 MiB memory BAR, GMADR — "first memory resource" would be wrong).
|
||||
if (resource.kind == @intFromEnum(device.ResourceKind.memory) and
|
||||
resource.len == 16 * 1024 * 1024) register_index = index;
|
||||
}
|
||||
if (register_index == 0) {
|
||||
std.log.err("register BAR not found", .{});
|
||||
return false;
|
||||
}
|
||||
|
||||
register_base = device.mmioMap(controller_id, register_index) orelse {
|
||||
std.log.err("mmio_map failed", .{});
|
||||
return false;
|
||||
};
|
||||
std.log.info("registers mapped at 0x{x}", .{register_base});
|
||||
return true;
|
||||
}
|
||||
|
||||
fn onMessage(message: []const u8, reply: []u8, sender: u32, capability: ?ipc.Handle) usize {
|
||||
_ = message;
|
||||
_ = reply;
|
||||
_ = sender;
|
||||
_ = capability;
|
||||
return 0; // no protocol yet; the zero-length ping is answered by the harness
|
||||
}
|
||||
|
||||
pub fn main(init: process.Init) void {
|
||||
const argument = init.arguments.get(1) orelse {
|
||||
std.log.err("missing device id (argv[1])", .{});
|
||||
return;
|
||||
};
|
||||
controller_id = std.fmt.parseInt(u64, argument, 10) catch {
|
||||
std.log.err("malformed device id '{s}'", .{argument});
|
||||
return;
|
||||
};
|
||||
service.run(message_maximum, .{
|
||||
.init = initialise,
|
||||
.on_message = onMessage,
|
||||
// .on_notification only once an IRQ or timer is bound
|
||||
});
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
//! The pci-bus driver as a binary package (docs/build-packages-plan.md):
|
||||
//! this file names the binary and EXACTLY the modules its source imports —
|
||||
//! the shared recipe and the module-to-domain map live in build-support.
|
||||
|
||||
const std = @import("std");
|
||||
const build_support = @import("build-support");
|
||||
|
||||
pub fn build(b: *std.Build) void {
|
||||
const exe = build_support.userBinary(b, .{
|
||||
.name = "pci-bus",
|
||||
.root_source_file = b.path("pci-bus.zig"),
|
||||
.imports = &.{
|
||||
"device-manager-protocol", "driver", "ipc", "logging", "memory", "pci-class",
|
||||
"process", "service",
|
||||
},
|
||||
});
|
||||
b.installArtifact(exe);
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
.{
|
||||
.name = .pci_bus,
|
||||
.version = "0.0.0",
|
||||
.fingerprint = 0x283fca121f0bb145, // Changing this has security and trust implications.
|
||||
.minimum_zig_version = "0.16.0",
|
||||
.dependencies = .{
|
||||
// build-support supplies the shared recipe; kernel is implicit in
|
||||
// every binary (the root shim + link script live there). The rest
|
||||
// are exactly the homes of this binary's declared imports.
|
||||
.@"build-support" = .{ .path = "../../../build-support" },
|
||||
.kernel = .{ .path = "../../../library/kernel" },
|
||||
.device = .{ .path = "../../../library/device" },
|
||||
.protocol = .{ .path = "../../../library/protocol" },
|
||||
},
|
||||
.paths = .{""},
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
//! The ps2-bus driver as a binary package (docs/build-packages-plan.md):
|
||||
//! this file names the binary and EXACTLY the modules its source imports —
|
||||
//! the shared recipe and the module-to-domain map live in build-support.
|
||||
|
||||
const std = @import("std");
|
||||
const build_support = @import("build-support");
|
||||
|
||||
pub fn build(b: *std.Build) void {
|
||||
const ps2_bus_exe = build_support.userBinary(b, .{
|
||||
.name = "ps2-bus",
|
||||
.root_source_file = b.path("ps2-bus.zig"),
|
||||
.imports = &.{ "acpi-ids", "driver", "ipc", "logging", "memory", "process", "service", "time" },
|
||||
});
|
||||
b.installArtifact(ps2_bus_exe);
|
||||
|
||||
const ps2_keyboard_exe = build_support.userBinary(b, .{
|
||||
.name = "ps2-keyboard",
|
||||
.root_source_file = b.path("keyboard.zig"),
|
||||
.imports = &.{
|
||||
"acpi-ids", "driver", "input-client", "input-protocol", "ipc", "logging", "memory",
|
||||
"process", "time", "xkeyboard-config",
|
||||
},
|
||||
});
|
||||
b.installArtifact(ps2_keyboard_exe);
|
||||
|
||||
const ps2_mouse_exe = build_support.userBinary(b, .{
|
||||
.name = "ps2-mouse",
|
||||
.root_source_file = b.path("mouse.zig"),
|
||||
.imports = &.{
|
||||
"acpi-ids", "driver", "input-client", "input-protocol", "ipc", "logging", "memory",
|
||||
"process", "time",
|
||||
},
|
||||
});
|
||||
b.installArtifact(ps2_mouse_exe);
|
||||
|
||||
// Standalone `zig build test`; the root aggregate depends on this step.
|
||||
const test_step = b.step("test", "Run the ps2-bus unit tests");
|
||||
for ([_][]const u8{
|
||||
"scancode.zig", // set-2 decode + keyboard state machine
|
||||
"mouse-packet.zig", // 3-byte mouse packet assembly
|
||||
}) |test_root| {
|
||||
const unit_tests = b.addTest(.{
|
||||
.root_module = b.createModule(.{
|
||||
.root_source_file = b.path(test_root),
|
||||
.target = b.resolveTargetQuery(.{}),
|
||||
}),
|
||||
});
|
||||
test_step.dependOn(&b.addRunArtifact(unit_tests).step);
|
||||
}
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
.{
|
||||
.name = .ps2_bus,
|
||||
.version = "0.0.0",
|
||||
.fingerprint = 0x642a365353bf7de9, // Changing this has security and trust implications.
|
||||
.minimum_zig_version = "0.16.0",
|
||||
.dependencies = .{
|
||||
// build-support supplies the shared recipe; kernel is implicit in
|
||||
// every binary (the root shim + link script live there). The rest
|
||||
// are exactly the homes of this binary's declared imports.
|
||||
.@"build-support" = .{ .path = "../../../build-support" },
|
||||
.kernel = .{ .path = "../../../library/kernel" },
|
||||
.client = .{ .path = "../../../library/client" },
|
||||
.device = .{ .path = "../../../library/device" },
|
||||
.protocol = .{ .path = "../../../library/protocol" },
|
||||
.@"xkeyboard-config" = .{ .path = "../../../library/xkeyboard-config" },
|
||||
},
|
||||
.paths = .{""},
|
||||
}
|
||||
@@ -19,7 +19,7 @@ const device = @import("driver");
|
||||
const ipc = @import("ipc");
|
||||
const process = @import("process");
|
||||
const time = @import("time");
|
||||
const input = @import("input-client");
|
||||
const input = @import("input");
|
||||
const memory = @import("memory");
|
||||
const logging = @import("logging");
|
||||
const xkb = @import("xkeyboard-config");
|
||||
|
||||
@@ -19,7 +19,7 @@ const device = @import("driver");
|
||||
const ipc = @import("ipc");
|
||||
const process = @import("process");
|
||||
const time = @import("time");
|
||||
const input = @import("input-client");
|
||||
const input = @import("input");
|
||||
const memory = @import("memory");
|
||||
const logging = @import("logging");
|
||||
const ps2 = @import("ps2-library.zig");
|
||||
|
||||
@@ -1,42 +0,0 @@
|
||||
//! The usb-hid driver as a binary package (docs/build-packages-plan.md):
|
||||
//! this file names the binary and EXACTLY the modules its source imports —
|
||||
//! the shared recipe and the module-to-domain map live in build-support.
|
||||
|
||||
const std = @import("std");
|
||||
const build_support = @import("build-support");
|
||||
|
||||
pub fn build(b: *std.Build) void {
|
||||
const usb_hid_keyboard_exe = build_support.userBinary(b, .{
|
||||
.name = "usb-hid-keyboard",
|
||||
.root_source_file = b.path("keyboard.zig"),
|
||||
.imports = &.{
|
||||
"driver", "input-client", "input-protocol", "ipc", "logging", "process", "service",
|
||||
"usb", "usb-abi", "xkeyboard-config",
|
||||
},
|
||||
});
|
||||
b.installArtifact(usb_hid_keyboard_exe);
|
||||
|
||||
const usb_hid_mouse_exe = build_support.userBinary(b, .{
|
||||
.name = "usb-hid-mouse",
|
||||
.root_source_file = b.path("mouse.zig"),
|
||||
.imports = &.{
|
||||
"driver", "input-client", "input-protocol", "ipc", "logging", "process", "service",
|
||||
"usb", "usb-abi",
|
||||
},
|
||||
});
|
||||
b.installArtifact(usb_hid_mouse_exe);
|
||||
|
||||
// Standalone `zig build test`; the root aggregate depends on this step.
|
||||
const test_step = b.step("test", "Run the usb-hid unit tests");
|
||||
for ([_][]const u8{
|
||||
"hid-report.zig", // HID boot-report keyboard/mouse decode
|
||||
}) |test_root| {
|
||||
const unit_tests = b.addTest(.{
|
||||
.root_module = b.createModule(.{
|
||||
.root_source_file = b.path(test_root),
|
||||
.target = b.resolveTargetQuery(.{}),
|
||||
}),
|
||||
});
|
||||
test_step.dependOn(&b.addRunArtifact(unit_tests).step);
|
||||
}
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
.{
|
||||
.name = .usb_hid,
|
||||
.version = "0.0.0",
|
||||
.fingerprint = 0x66328b738fffff01, // Changing this has security and trust implications.
|
||||
.minimum_zig_version = "0.16.0",
|
||||
.dependencies = .{
|
||||
// build-support supplies the shared recipe; kernel is implicit in
|
||||
// every binary (the root shim + link script live there). The rest
|
||||
// are exactly the homes of this binary's declared imports.
|
||||
.@"build-support" = .{ .path = "../../../build-support" },
|
||||
.kernel = .{ .path = "../../../library/kernel" },
|
||||
.client = .{ .path = "../../../library/client" },
|
||||
.device = .{ .path = "../../../library/device" },
|
||||
.protocol = .{ .path = "../../../library/protocol" },
|
||||
.@"xkeyboard-config" = .{ .path = "../../../library/xkeyboard-config" },
|
||||
},
|
||||
.paths = .{""},
|
||||
}
|
||||
@@ -18,7 +18,7 @@ const std = @import("std");
|
||||
const ipc = @import("ipc");
|
||||
const process = @import("process");
|
||||
const service = @import("service");
|
||||
const input = @import("input-client");
|
||||
const input = @import("input");
|
||||
const device_manager = @import("driver");
|
||||
const logging = @import("logging");
|
||||
const usb = @import("usb");
|
||||
|
||||
@@ -14,7 +14,7 @@ const std = @import("std");
|
||||
const ipc = @import("ipc");
|
||||
const process = @import("process");
|
||||
const service = @import("service");
|
||||
const input = @import("input-client");
|
||||
const input = @import("input");
|
||||
const device_manager = @import("driver");
|
||||
const logging = @import("logging");
|
||||
const usb = @import("usb");
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
//! The usb-storage driver as a binary package (docs/build-packages-plan.md):
|
||||
//! this file names the binary and EXACTLY the modules its source imports —
|
||||
//! the shared recipe and the module-to-domain map live in build-support.
|
||||
|
||||
const std = @import("std");
|
||||
const build_support = @import("build-support");
|
||||
|
||||
pub fn build(b: *std.Build) void {
|
||||
const exe = build_support.userBinary(b, .{
|
||||
.name = "usb-storage",
|
||||
.root_source_file = b.path("usb-storage.zig"),
|
||||
.imports = &.{
|
||||
"block-protocol", "driver", "ipc", "logging", "memory", "process", "service",
|
||||
"time", "usb",
|
||||
},
|
||||
});
|
||||
b.installArtifact(exe);
|
||||
|
||||
// Standalone `zig build test`; the root aggregate depends on this step.
|
||||
const test_step = b.step("test", "Run the usb-storage unit tests");
|
||||
for ([_][]const u8{
|
||||
"bulk-only-transport.zig", // CBW/CSW wrapper sizes
|
||||
"scsi.zig", // SCSI CDB encodings (big-endian)
|
||||
}) |test_root| {
|
||||
const unit_tests = b.addTest(.{
|
||||
.root_module = b.createModule(.{
|
||||
.root_source_file = b.path(test_root),
|
||||
.target = b.resolveTargetQuery(.{}),
|
||||
}),
|
||||
});
|
||||
test_step.dependOn(&b.addRunArtifact(unit_tests).step);
|
||||
}
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
.{
|
||||
.name = .usb_storage,
|
||||
.version = "0.0.0",
|
||||
.fingerprint = 0xce09fdc4c50bb4fe, // Changing this has security and trust implications.
|
||||
.minimum_zig_version = "0.16.0",
|
||||
.dependencies = .{
|
||||
// build-support supplies the shared recipe; kernel is implicit in
|
||||
// every binary (the root shim + link script live there). The rest
|
||||
// are exactly the homes of this binary's declared imports.
|
||||
.@"build-support" = .{ .path = "../../../build-support" },
|
||||
.kernel = .{ .path = "../../../library/kernel" },
|
||||
.device = .{ .path = "../../../library/device" },
|
||||
.protocol = .{ .path = "../../../library/protocol" },
|
||||
},
|
||||
.paths = .{""},
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
//! The usb-xhci-bus driver as a binary package (docs/build-packages-plan.md):
|
||||
//! this file names the binary and EXACTLY the modules its source imports —
|
||||
//! the shared recipe and the module-to-domain map live in build-support.
|
||||
|
||||
const std = @import("std");
|
||||
const build_support = @import("build-support");
|
||||
|
||||
pub fn build(b: *std.Build) void {
|
||||
const exe = build_support.userBinary(b, .{
|
||||
.name = "usb-xhci-bus",
|
||||
.root_source_file = b.path("usb-xhci-bus.zig"),
|
||||
.imports = &.{
|
||||
"device-manager-protocol", "driver", "input-client", "ipc", "logging", "memory",
|
||||
"mmio", "pci", "process", "service", "time", "usb-abi", "usb-ids",
|
||||
"usb-transfer-protocol",
|
||||
},
|
||||
});
|
||||
b.installArtifact(exe);
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
.{
|
||||
.name = .usb_xhci_bus,
|
||||
.version = "0.0.0",
|
||||
.fingerprint = 0x46d21373f05f6b20, // Changing this has security and trust implications.
|
||||
.minimum_zig_version = "0.16.0",
|
||||
.dependencies = .{
|
||||
// build-support supplies the shared recipe; kernel is implicit in
|
||||
// every binary (the root shim + link script live there). The rest
|
||||
// are exactly the homes of this binary's declared imports.
|
||||
.@"build-support" = .{ .path = "../../../build-support" },
|
||||
.kernel = .{ .path = "../../../library/kernel" },
|
||||
.client = .{ .path = "../../../library/client" },
|
||||
.device = .{ .path = "../../../library/device" },
|
||||
.protocol = .{ .path = "../../../library/protocol" },
|
||||
},
|
||||
.paths = .{""},
|
||||
}
|
||||
@@ -19,7 +19,7 @@ const ipc = @import("ipc");
|
||||
const process = @import("process");
|
||||
const service = @import("service");
|
||||
const time = @import("time");
|
||||
const input = @import("input-client");
|
||||
const input = @import("input");
|
||||
const device_manager = @import("driver");
|
||||
const memory = @import("memory");
|
||||
const logging = @import("logging");
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
//! The virtio-gpu driver as a binary package (docs/build-packages-plan.md):
|
||||
//! this file names the binary and EXACTLY the modules its source imports —
|
||||
//! the shared recipe and the module-to-domain map live in build-support.
|
||||
|
||||
const std = @import("std");
|
||||
const build_support = @import("build-support");
|
||||
|
||||
pub fn build(b: *std.Build) void {
|
||||
const exe = build_support.userBinary(b, .{
|
||||
.name = "virtio-gpu",
|
||||
.root_source_file = b.path("virtio-gpu.zig"),
|
||||
.imports = &.{
|
||||
"display-protocol", "driver", "ipc", "logging", "memory", "mmio", "pci", "process",
|
||||
"scanout-protocol", "service", "time",
|
||||
},
|
||||
});
|
||||
b.installArtifact(exe);
|
||||
|
||||
// Standalone `zig build test`; the root aggregate depends on this step.
|
||||
const test_step = b.step("test", "Run the virtio-gpu unit tests");
|
||||
for ([_][]const u8{
|
||||
"virtio-gpu-protocol.zig", // virtio-gpu command struct sizes
|
||||
"virtio-pci.zig", // virtio 1.0 PCI transport struct sizes
|
||||
}) |test_root| {
|
||||
const unit_tests = b.addTest(.{
|
||||
.root_module = b.createModule(.{
|
||||
.root_source_file = b.path(test_root),
|
||||
.target = b.resolveTargetQuery(.{}),
|
||||
}),
|
||||
});
|
||||
test_step.dependOn(&b.addRunArtifact(unit_tests).step);
|
||||
}
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
.{
|
||||
.name = .virtio_gpu,
|
||||
.version = "0.0.0",
|
||||
.fingerprint = 0xfb704899c18b9a23, // Changing this has security and trust implications.
|
||||
.minimum_zig_version = "0.16.0",
|
||||
.dependencies = .{
|
||||
// build-support supplies the shared recipe; kernel is implicit in
|
||||
// every binary (the root shim + link script live there). The rest
|
||||
// are exactly the homes of this binary's declared imports.
|
||||
.@"build-support" = .{ .path = "../../../build-support" },
|
||||
.kernel = .{ .path = "../../../library/kernel" },
|
||||
.device = .{ .path = "../../../library/device" },
|
||||
.protocol = .{ .path = "../../../library/protocol" },
|
||||
},
|
||||
.paths = .{""},
|
||||
}
|
||||
+11
-6
@@ -106,6 +106,12 @@ pub const PlatformInformation = struct {
|
||||
/// INCLUDE_PCI_ALL (the catch-all unit; falls back to the first), or the AMD-Vi
|
||||
/// IOMMU's control-register base from the first IVHD.
|
||||
iommu_base: u64 = 0,
|
||||
/// The unit's Version register (offset 0x00) — its low byte is major.minor;
|
||||
/// reading it back nonzero confirms a real, mappable VT-d unit.
|
||||
iommu_version: u32 = 0,
|
||||
/// The unit's Capability register (offset 0x08): supported address widths, number
|
||||
/// of domains, etc. Consumed by the IOMMU core when it enables translation.
|
||||
iommu_capabilities: u64 = 0,
|
||||
/// Whether the selected unit carries INCLUDE_PCI_ALL. False means every unit is
|
||||
/// device-scoped (unusual) — the core still enables on the selected unit but
|
||||
/// devices outside its scope remain untranslated.
|
||||
@@ -496,7 +502,7 @@ fn handleTable(device_tree: *DeviceTree, hal: Hal, sdt_physical: u64) !void {
|
||||
} else if (std.mem.eql(u8, &sig, &SPCR)) {
|
||||
parseSpcr(header);
|
||||
} else if (std.mem.eql(u8, &sig, &DMAR)) {
|
||||
parseDmar(header);
|
||||
parseDmar(hal, header);
|
||||
} else if (std.mem.eql(u8, &sig, &IVRS)) {
|
||||
parseIvrs(header);
|
||||
} else if (std.mem.eql(u8, &sig, &SSDT)) {
|
||||
@@ -816,7 +822,7 @@ const scope_path_offset = 6;
|
||||
/// and records single-path endpoint RMRRs for the IOMMU core to pre-map before it
|
||||
/// enables translation. Multi-hop RMRR scopes are skipped loudly: better a named gap
|
||||
/// than a silent one.
|
||||
fn parseDmar(header: *const SystemDescriptorTableHeader) void {
|
||||
fn parseDmar(hal: Hal, header: *const SystemDescriptorTableHeader) void {
|
||||
const base: [*]align(1) const u8 = @ptrCast(header);
|
||||
const total: usize = header.length;
|
||||
|
||||
@@ -834,14 +840,13 @@ fn parseDmar(header: *const SystemDescriptorTableHeader) void {
|
||||
const replace = !platform_information.iommu_present or
|
||||
(include_all and !platform_information.iommu_include_all);
|
||||
if (replace) {
|
||||
// Table facts only: the unit's registers are the IOMMU
|
||||
// backend's business (it maps and validates them at
|
||||
// detect) — discovery records where they live, never
|
||||
// reads them.
|
||||
if (platform_information.iommu_present) platform_information.iommu_extra_units += 1;
|
||||
const regs = hal.mapMmio(register_base, abi.page_size, true);
|
||||
platform_information.iommu_present = true;
|
||||
platform_information.iommu_base = register_base;
|
||||
platform_information.iommu_include_all = include_all;
|
||||
platform_information.iommu_version = @as(*const volatile u32, @ptrFromInt(regs + 0x00)).*;
|
||||
platform_information.iommu_capabilities = @as(*const volatile u64, @ptrFromInt(regs + 0x08)).*;
|
||||
} else {
|
||||
platform_information.iommu_extra_units += 1;
|
||||
}
|
||||
|
||||
@@ -17,11 +17,6 @@ const io = @import("io.zig");
|
||||
const smp = @import("smp.zig");
|
||||
const pcpu = @import("per-cpu.zig");
|
||||
|
||||
/// The x86-64 IOMMU backends (Intel VT-d, AMD-Vi) behind their dispatch
|
||||
/// surface — the architecture-neutral IOMMU core (system/kernel/iommu.zig)
|
||||
/// reaches the hardware only through this.
|
||||
pub const iommu = @import("iommu.zig");
|
||||
|
||||
/// The saved register/trap frame passed to a fault handler.
|
||||
pub const CpuState = idt.CpuState;
|
||||
|
||||
|
||||
@@ -1,77 +0,0 @@
|
||||
//! x86-64 IOMMU backends, behind the architecture boundary: Intel VT-d
|
||||
//! (iommu-intel.zig) and AMD-Vi (iommu-amd.zig). The architecture-neutral
|
||||
//! core (system/kernel/iommu.zig) owns the domain table and the shared
|
||||
//! page-table walker; it hands this file the firmware discovery facts and an
|
||||
//! environment (frame allocation + the log sink, injected the same way
|
||||
//! enablePaging receives its frame hooks), and gets back a hardware vtable.
|
||||
//! A new architecture supplies its own unit (ARM: the SMMU) from its own
|
||||
//! directory with no core change.
|
||||
|
||||
const intel = @import("iommu-intel.zig");
|
||||
const amd = @import("iommu-amd.zig");
|
||||
|
||||
/// What the platform's firmware tables reported: where the unit's registers
|
||||
/// live, and which programming model its table implies (an IVRS table
|
||||
/// describes AMD-Vi; a DMAR table describes Intel VT-d).
|
||||
pub const Discovery = struct {
|
||||
register_base: u64,
|
||||
amd: bool,
|
||||
};
|
||||
|
||||
/// What the backends need from the generic kernel, injected at detect so this
|
||||
/// module never imports kernel internals: physical-frame allocation for the
|
||||
/// hardware structures, and the kernel log sink (fault reports, warnings, the
|
||||
/// enable banner).
|
||||
pub const Environment = struct {
|
||||
allocateFrame: *const fn () ?u64,
|
||||
allocateContiguous: *const fn (count: usize, max_physical: u64) ?u64,
|
||||
write: *const fn (bytes: []const u8) void,
|
||||
};
|
||||
|
||||
/// The bit encodings and hardware operations a backend supplies to the shared
|
||||
/// core. Entry helpers build the raw page-table entries for the backend's
|
||||
/// format; the core walks the tree with them. The hardware ops act on a whole
|
||||
/// domain (identified by its hardware domain id = core index + 1) or device
|
||||
/// (by requester id / bdf).
|
||||
pub const Backend = struct {
|
||||
/// Number of page-table levels (3 or 4) the backend selected from hardware caps.
|
||||
levels: u8,
|
||||
/// Largest leaf the walker may emit: 4 KiB always, 2 MiB when the backend allows.
|
||||
supports_huge_pages: bool,
|
||||
|
||||
/// Raw entry bits for a leaf mapping `physical` (with the given size), and for a
|
||||
/// non-leaf entry pointing at `table_physical` at `level` (level counts down to 1
|
||||
/// at the leaf's parent). `isPresent` tests a read-back entry.
|
||||
makeLeaf: *const fn (physical: u64, huge: bool) u64,
|
||||
makeTable: *const fn (table_physical: u64, level: u8) u64,
|
||||
isPresent: *const fn (entry: u64) bool,
|
||||
/// Flush a cache line holding IOMMU structures the hardware reads non-coherently
|
||||
/// (VT-d with ECAP.C==0). A no-op where the unit snoops caches.
|
||||
flushStructure: *const fn (address: usize) void,
|
||||
|
||||
/// Turn translation on (the core has already seeded any pre-claim domains)
|
||||
/// and write the unit's identity lines to the log — the core follows with
|
||||
/// the neutral posture lines.
|
||||
enable: *const fn () void,
|
||||
/// Point `bdf`'s translation structure at `domain` (hardware id) and invalidate the
|
||||
/// context/device caches so the change takes effect.
|
||||
attach: *const fn (bdf: u16, domain: u16, page_table_root: u64) void,
|
||||
/// Return `bdf`'s translation structure to not-present + invalidate — all its DMA
|
||||
/// faults afterward.
|
||||
detach: *const fn (bdf: u16) void,
|
||||
/// Invalidate cached translations for `domain` (after a map or unmap).
|
||||
invalidateDomain: *const fn (domain: u16) void,
|
||||
/// Pull pending faults out of the hardware, log them (rate-limited), return the
|
||||
/// count seen this call.
|
||||
faultDrain: *const fn () usize,
|
||||
};
|
||||
|
||||
/// The injected kernel services, stored for the backends at detect time.
|
||||
pub var environment: Environment = undefined;
|
||||
|
||||
/// Probe the discovered unit and return its vtable, or null when it is
|
||||
/// unusable (the core stays fail-open and says so).
|
||||
pub fn detect(discovery: Discovery, injected: Environment) ?Backend {
|
||||
environment = injected;
|
||||
return if (discovery.amd) amd.detect(discovery) else intel.detect(discovery);
|
||||
}
|
||||
@@ -1,6 +1,5 @@
|
||||
//! AMD-Vi (AMD I/O Virtualization) backend for the IOMMU core, behind the
|
||||
//! architecture boundary. The AMD analogue of iommu-intel.zig: it supplies
|
||||
//! the architecture-neutral core's `Backend` vtable (iommu.zig beside this file)
|
||||
//! system/kernel/iommu-amd.zig — AMD-Vi (AMD I/O Virtualization) backend for the IOMMU
|
||||
//! core. The AMD analogue of iommu-intel.zig: it supplies the core's `Backend` vtable
|
||||
//! with AMD-Vi's page-table entry bits and drives the device table, command buffer, and
|
||||
//! event log.
|
||||
//!
|
||||
@@ -15,7 +14,10 @@
|
||||
const std = @import("std");
|
||||
const abi = @import("abi");
|
||||
const boot_handoff = @import("boot-handoff");
|
||||
const paging = @import("paging.zig");
|
||||
const pmm = @import("pmm.zig");
|
||||
const platform = @import("platform");
|
||||
const architecture = @import("architecture");
|
||||
const log = @import("log.zig");
|
||||
const iommu = @import("iommu.zig");
|
||||
|
||||
const page_size = abi.page_size;
|
||||
@@ -86,10 +88,10 @@ fn ram(physical: u64) [*]volatile u64 {
|
||||
|
||||
/// Map the register window, allocate the device table / command buffer / event log.
|
||||
/// Returns the vtable, or null if the boot-time allocations fail.
|
||||
pub fn detect(discovery: iommu.Discovery) ?iommu.Backend {
|
||||
register_base = paging.mapMmio(discovery.register_base, 16 * 1024, true);
|
||||
pub fn detect(info: platform.PlatformInformation) ?iommu.Backend {
|
||||
register_base = architecture.mapMmio(info.iommu_base, 16 * 1024, true);
|
||||
|
||||
device_table = iommu.environment.allocateContiguous(device_table_pages, ~@as(u64, 0)) orelse return null;
|
||||
device_table = pmm.allocContiguous(device_table_pages, ~@as(u64, 0)) orelse return null;
|
||||
zero(device_table, device_table_pages); // all-zero DTE = V=0 = deny every device
|
||||
command_buffer = allocZeroedFrame() orelse return null;
|
||||
event_log = allocZeroedFrame() orelse return null;
|
||||
@@ -98,7 +100,6 @@ pub fn detect(discovery: iommu.Discovery) ?iommu.Backend {
|
||||
return iommu.Backend{
|
||||
.levels = levels,
|
||||
.supports_huge_pages = false, // 4 KiB leaves only (AMD superpage encoding deferred)
|
||||
.enable = enable,
|
||||
.makeLeaf = makeLeaf,
|
||||
.makeTable = makeTable,
|
||||
.isPresent = isPresent,
|
||||
@@ -113,7 +114,7 @@ pub fn detect(discovery: iommu.Discovery) ?iommu.Backend {
|
||||
/// Program the base registers and enable translation. The device table is already
|
||||
/// zeroed (every device denied) except any entries `attach` wrote for RMRR/claimed
|
||||
/// devices, so turning translation on blocks all other DMA and logs it.
|
||||
fn enable() void {
|
||||
pub fn enable() void {
|
||||
write64(reg_device_table_base, (device_table & address_mask) | (device_table_pages - 1));
|
||||
write64(reg_command_buffer_base, (command_buffer & address_mask) | (ring_length_code << 56));
|
||||
write64(reg_event_log_base, (event_log & address_mask) | (ring_length_code << 56));
|
||||
@@ -125,12 +126,6 @@ fn enable() void {
|
||||
// Buffers first, then the master enable.
|
||||
write64(reg_control, control_command_buffer_enable | control_event_log_enable);
|
||||
write64(reg_control, control_command_buffer_enable | control_event_log_enable | control_iommu_enable);
|
||||
|
||||
iommu.environment.write("/system/kernel: iommu online (AMD-Vi) — UNTESTED on real AMD hardware (QEMU-verified only)\n");
|
||||
var buffer: [48]u8 = undefined;
|
||||
if (std.fmt.bufPrint(&buffer, " levels : {d} (48-bit)\n", .{levels})) |line|
|
||||
iommu.environment.write(line)
|
||||
else |_| {}
|
||||
}
|
||||
|
||||
// --- Backend vtable ------------------------------------------------------------------
|
||||
@@ -200,14 +195,13 @@ fn faultDrain() usize {
|
||||
fn logFault(source: u16, address: u64) void {
|
||||
if (fault_log_budget == 0) return;
|
||||
fault_log_budget -= 1;
|
||||
var buffer: [128]u8 = undefined;
|
||||
if (std.fmt.bufPrint(&buffer, "DANOS-IOMMU-FAULT: bdf={x:0>2}:{x:0>2}.{d} addr=0x{x} reason=amd-io-page-fault\n", .{
|
||||
log.print("DANOS-IOMMU-FAULT: bdf={x:0>2}:{x:0>2}.{d} addr=0x{x} reason=amd-io-page-fault\n", .{
|
||||
source >> 8,
|
||||
(source >> 3) & 0x1F,
|
||||
source & 0x7,
|
||||
address,
|
||||
})) |line| iommu.environment.write(line) else |_| {}
|
||||
if (fault_log_budget == 0) iommu.environment.write("DANOS-IOMMU-FAULT: (further faults suppressed)\n");
|
||||
});
|
||||
if (fault_log_budget == 0) log.write("DANOS-IOMMU-FAULT: (further faults suppressed)\n");
|
||||
}
|
||||
|
||||
// --- command ring --------------------------------------------------------------------
|
||||
@@ -246,7 +240,7 @@ fn completeAndWait() void {
|
||||
if (spins > 100_000) {
|
||||
if (!completion_warned) {
|
||||
completion_warned = true;
|
||||
iommu.environment.write("/system/kernel: AMD-Vi COMPLETION_WAIT store not observed — proceeding (QEMU processes commands synchronously)\n");
|
||||
log.write("/system/kernel: AMD-Vi COMPLETION_WAIT store not observed — proceeding (QEMU processes commands synchronously)\n");
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -254,7 +248,7 @@ fn completeAndWait() void {
|
||||
}
|
||||
|
||||
fn allocZeroedFrame() ?u64 {
|
||||
const frame = iommu.environment.allocateFrame() orelse return null;
|
||||
const frame = pmm.alloc() orelse return null;
|
||||
zero(frame, 1);
|
||||
return frame;
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
//! Intel VT-d backend for the IOMMU core, behind the architecture boundary.
|
||||
//! system/kernel/iommu-intel.zig — Intel VT-d backend for the IOMMU core.
|
||||
//!
|
||||
//! Provides the architecture-neutral core (system/kernel/iommu.zig) with the VT-d
|
||||
//! hardware specifics behind the `Backend` vtable (iommu.zig beside this file): second-level page-table entry bits, the root/context table structure, the
|
||||
//! Provides the core (iommu.zig) with the VT-d hardware specifics behind its `Backend`
|
||||
//! vtable: second-level page-table entry bits, the root/context table structure, the
|
||||
//! translation-enable and invalidation register sequences, and the fault drain. The
|
||||
//! core owns the domain table and the page-table walk; this file owns the registers.
|
||||
//!
|
||||
@@ -15,7 +15,10 @@
|
||||
const std = @import("std");
|
||||
const abi = @import("abi");
|
||||
const boot_handoff = @import("boot-handoff");
|
||||
const paging = @import("paging.zig");
|
||||
const pmm = @import("pmm.zig");
|
||||
const platform = @import("platform");
|
||||
const architecture = @import("architecture");
|
||||
const log = @import("log.zig");
|
||||
const iommu = @import("iommu.zig");
|
||||
|
||||
const page_size = abi.page_size;
|
||||
@@ -63,7 +66,6 @@ const slpte_page_size: u64 = 1 << 7; // a 2 MiB leaf (== iommu.huge_leaf_bit)
|
||||
const address_mask: u64 = 0x000F_FFFF_FFFF_F000;
|
||||
|
||||
var register_base: usize = 0;
|
||||
var version: u32 = 0;
|
||||
var capabilities: u64 = 0;
|
||||
var extended_capabilities: u64 = 0;
|
||||
var coherent: bool = true; // ECAP.C — whether clflush is unnecessary
|
||||
@@ -95,15 +97,11 @@ fn tableAt(physical: u64) [*]volatile u64 {
|
||||
}
|
||||
|
||||
/// Map the register window, read caps, pick the address width. Returns the vtable, or
|
||||
/// null when the unit is not live or advertises no address width danos can drive.
|
||||
pub fn detect(discovery: iommu.Discovery) ?iommu.Backend {
|
||||
// Map 16 KiB: FRCD and IOTLB registers can sit past the first page (CAP.FRO /
|
||||
// ECAP.IRO are 16-byte-unit offsets).
|
||||
register_base = paging.mapMmio(discovery.register_base, 16 * 1024, true);
|
||||
// The Version register's low byte is major.minor; reading it back nonzero is
|
||||
// the live-mappable-unit sanity check (previously a kernel-test assertion).
|
||||
version = read32(0x00);
|
||||
if (version == 0) return null;
|
||||
/// null when the unit advertises no address width danos can drive.
|
||||
pub fn detect(info: platform.PlatformInformation) ?iommu.Backend {
|
||||
// Remap 16 KiB: FRCD and IOTLB registers can sit past the first page (CAP.FRO /
|
||||
// ECAP.IRO are 16-byte-unit offsets). Idempotent with the detection-time mapping.
|
||||
register_base = architecture.mapMmio(info.iommu_base, 16 * 1024, true);
|
||||
capabilities = read64(reg_cap);
|
||||
extended_capabilities = read64(reg_ecap);
|
||||
coherent = (extended_capabilities & ecap_coherent) != 0;
|
||||
@@ -124,7 +122,6 @@ pub fn detect(discovery: iommu.Discovery) ?iommu.Backend {
|
||||
return iommu.Backend{
|
||||
.levels = levels,
|
||||
.supports_huge_pages = true,
|
||||
.enable = enable,
|
||||
.makeLeaf = makeLeaf,
|
||||
.makeTable = makeTable,
|
||||
.isPresent = isPresent,
|
||||
@@ -140,7 +137,7 @@ pub fn detect(discovery: iommu.Discovery) ?iommu.Backend {
|
||||
/// and populated the RMRR domains (their context entries are live via `attach`), so at
|
||||
/// this instant every OTHER device's context entry is not-present and will fault — which
|
||||
/// for stale firmware bus-mastering is the desired evidence, not a bug.
|
||||
fn enable() void {
|
||||
pub fn enable() void {
|
||||
write64(reg_rtaddr, root_table); // legacy mode (bits 11:10 = 00)
|
||||
setGlobalCommand(gcmd_srtp);
|
||||
spinStatus(gsts_rtps);
|
||||
@@ -148,15 +145,6 @@ fn enable() void {
|
||||
setGlobalCommand(gcmd_te);
|
||||
spinStatus(gsts_tes);
|
||||
gcmd_shadow |= gcmd_te;
|
||||
|
||||
iommu.environment.write("/system/kernel: iommu online (Intel VT-d)\n");
|
||||
var buffer: [64]u8 = undefined;
|
||||
if (std.fmt.bufPrint(&buffer, " version : 0x{x}\n", .{version})) |line|
|
||||
iommu.environment.write(line)
|
||||
else |_| {}
|
||||
if (std.fmt.bufPrint(&buffer, " agaw : {d} levels\n", .{levels})) |line|
|
||||
iommu.environment.write(line)
|
||||
else |_| {}
|
||||
}
|
||||
|
||||
// --- Backend vtable ------------------------------------------------------------------
|
||||
@@ -253,17 +241,16 @@ fn faultDrain() usize {
|
||||
fn logFault(source: u16, address: u64, reason: u8, is_read: bool) void {
|
||||
if (fault_log_budget > 0) {
|
||||
fault_log_budget -= 1;
|
||||
var buffer: [128]u8 = undefined;
|
||||
if (std.fmt.bufPrint(&buffer, "DANOS-IOMMU-FAULT: bdf={x:0>2}:{x:0>2}.{d} addr=0x{x} reason=0x{x} write={d}\n", .{
|
||||
log.print("DANOS-IOMMU-FAULT: bdf={x:0>2}:{x:0>2}.{d} addr=0x{x} reason=0x{x} write={d}\n", .{
|
||||
source >> 8,
|
||||
(source >> 3) & 0x1F,
|
||||
source & 0x7,
|
||||
address,
|
||||
reason,
|
||||
@intFromBool(!is_read),
|
||||
})) |line| iommu.environment.write(line) else |_| {}
|
||||
});
|
||||
if (fault_log_budget == 0)
|
||||
iommu.environment.write("DANOS-IOMMU-FAULT: (further faults suppressed)\n");
|
||||
log.write("DANOS-IOMMU-FAULT: (further faults suppressed)\n");
|
||||
} else {
|
||||
faults_suppressed += 1;
|
||||
}
|
||||
@@ -282,7 +269,7 @@ fn spinStatus(bit: u32) void {
|
||||
while (read32(reg_gsts) & bit == 0) {
|
||||
spins += 1;
|
||||
if (spins > 10_000_000) {
|
||||
iommu.environment.write("/system/kernel: WARNING VT-d status bit never set — translation may be incomplete\n");
|
||||
log.write("/system/kernel: WARNING VT-d status bit never set — translation may be incomplete\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -319,7 +306,7 @@ fn iotlbOffset() usize {
|
||||
}
|
||||
|
||||
fn allocZeroed() ?u64 {
|
||||
const frame = iommu.environment.allocateFrame() orelse return null;
|
||||
const frame = pmm.alloc() orelse return null;
|
||||
const table = tableAt(frame);
|
||||
var i: usize = 0;
|
||||
while (i < 512) : (i += 1) table[i] = 0;
|
||||
+104
-53
@@ -1,6 +1,5 @@
|
||||
//! system/kernel/iommu.zig — architecture-neutral IOMMU core: per-device DMA
|
||||
//! translation domains over a backend the architecture module supplies
|
||||
//! (x86-64: Intel VT-d or AMD-Vi, behind architecture/x86_64/iommu.zig).
|
||||
//! system/kernel/iommu.zig — vendor-neutral IOMMU core: per-device DMA translation
|
||||
//! domains over an Intel VT-d or AMD-Vi backend.
|
||||
//!
|
||||
//! The problem this closes: without an IOMMU, a claimed bus-mastering device can DMA to
|
||||
//! ANY physical address, so a compromised or buggy driver reaches all of memory through
|
||||
@@ -14,12 +13,11 @@
|
||||
//! physical address they program into hardware; a domain simply makes that same
|
||||
//! address the ONLY thing the device can reach. No IOVA allocator, and every
|
||||
//! driver's register-programming code is untouched.
|
||||
//! - **Architecture-neutral**: this file owns the domain table and a shared
|
||||
//! 512-entry page-table walker; the architecture module's `Backend` vtable
|
||||
//! supplies the hardware specifics — the entry-bit encodings, the
|
||||
//! enable/invalidate register dances, and the fault drain — with the frame
|
||||
//! allocator and log sink injected the other way.
|
||||
//! - **Fail-open**: when no IOMMU is found, nothing activates and every entry point is a
|
||||
//! - **Vendor-neutral**: this file owns the domain table and a shared 512-entry
|
||||
//! page-table walker; a `Backend` vtable supplies the hardware specifics (VT-d in
|
||||
//! iommu-intel.zig, AMD-Vi in iommu-amd.zig) — the entry-bit encodings, the
|
||||
//! enable/invalidate register dances, and the fault drain.
|
||||
//! - **Fail-open**: when no IOMMU is found, `kind == .none` and every entry point is a
|
||||
//! success no-op, so callers in process.zig stay unconditional and behavior is
|
||||
//! byte-for-byte the pre-IOMMU kernel. The boot log states the posture.
|
||||
//!
|
||||
@@ -31,18 +29,54 @@ const abi = @import("abi");
|
||||
const boot_handoff = @import("boot-handoff");
|
||||
const pmm = @import("pmm.zig");
|
||||
const platform = @import("platform");
|
||||
const architecture = @import("architecture");
|
||||
const devices_broker = @import("devices-broker.zig");
|
||||
const log = @import("log.zig");
|
||||
const intel = @import("iommu-intel.zig");
|
||||
const amd = @import("iommu-amd.zig");
|
||||
|
||||
const page_size: u64 = abi.page_size;
|
||||
const page_mask: u64 = page_size - 1;
|
||||
const huge_page_size: u64 = 2 * 1024 * 1024;
|
||||
|
||||
pub const Kind = enum { none, intel_vtd, amd_vi };
|
||||
|
||||
/// One domain per claimed PCI function. 64 mirrors devices-broker's device cap.
|
||||
pub const maximum_domains = 64;
|
||||
pub const invalid_domain: u16 = 0xFFFF;
|
||||
|
||||
/// The bit encodings and hardware operations a backend supplies to the shared core.
|
||||
/// Entry helpers build the raw page-table entries for the backend's format; the core
|
||||
/// walks the tree with them. The hardware ops act on a whole domain (identified by its
|
||||
/// hardware domain id = core index + 1) or device (by requester id / bdf).
|
||||
pub const Backend = struct {
|
||||
/// Number of page-table levels (3 or 4) the backend selected from hardware caps.
|
||||
levels: u8,
|
||||
/// Largest leaf the walker may emit: 4 KiB always, 2 MiB when the backend allows.
|
||||
supports_huge_pages: bool,
|
||||
|
||||
/// Raw entry bits for a leaf mapping `physical` (with the given size), and for a
|
||||
/// non-leaf entry pointing at `table_physical` at `level` (level counts down to 1
|
||||
/// at the leaf's parent). `isPresent` tests a read-back entry.
|
||||
makeLeaf: *const fn (physical: u64, huge: bool) u64,
|
||||
makeTable: *const fn (table_physical: u64, level: u8) u64,
|
||||
isPresent: *const fn (entry: u64) bool,
|
||||
/// Flush a cache line holding IOMMU structures the hardware reads non-coherently
|
||||
/// (VT-d with ECAP.C==0). A no-op where the unit snoops caches.
|
||||
flushStructure: *const fn (address: usize) void,
|
||||
|
||||
/// Point `bdf`'s translation structure at `domain` (hardware id) and invalidate the
|
||||
/// context/device caches so the change takes effect.
|
||||
attach: *const fn (bdf: u16, domain: u16, page_table_root: u64) void,
|
||||
/// Return `bdf`'s translation structure to not-present + invalidate — all its DMA
|
||||
/// faults afterward.
|
||||
detach: *const fn (bdf: u16) void,
|
||||
/// Invalidate cached translations for `domain` (after a map or unmap).
|
||||
invalidateDomain: *const fn (domain: u16) void,
|
||||
/// Pull pending faults out of the hardware, log them (rate-limited), return the
|
||||
/// count seen this call.
|
||||
faultDrain: *const fn () usize,
|
||||
};
|
||||
|
||||
const Domain = struct {
|
||||
in_use: bool = false,
|
||||
owner: u32 = 0, // task that owns the attached device
|
||||
@@ -51,44 +85,53 @@ const Domain = struct {
|
||||
rmrr: bool = false, // a firmware reserved-region domain (persists across claims)
|
||||
};
|
||||
|
||||
var active: bool = false;
|
||||
var backend: architecture.iommu.Backend = undefined;
|
||||
var kind: Kind = .none;
|
||||
var backend: Backend = undefined;
|
||||
var domains: [maximum_domains]Domain = .{Domain{}} ** maximum_domains;
|
||||
|
||||
pub fn kindOf() Kind {
|
||||
return kind;
|
||||
}
|
||||
pub fn enabled() bool {
|
||||
return active;
|
||||
return kind != .none;
|
||||
}
|
||||
|
||||
/// Detect the IOMMU (the architecture module probes the discovered unit and
|
||||
/// returns its backend), pre-map firmware reserved regions, and enable
|
||||
/// translation. Fail-open (nothing activates) when no usable unit exists — the
|
||||
/// caller logs the posture. Must run after platform discovery and before any
|
||||
/// user process starts.
|
||||
/// Detect the IOMMU, pick a backend, pre-map firmware reserved regions, and enable
|
||||
/// translation. Fail-open (kind stays .none) when no unit exists — the caller logs the
|
||||
/// posture. Must run after platform discovery and before any user process starts.
|
||||
pub fn init() void {
|
||||
const info = platform.platformInformation();
|
||||
if (!info.iommu_present) return;
|
||||
// A present-but-unusable unit stays fail-open with a logged reason rather
|
||||
// than half-enabling. The backend receives the kernel services it needs
|
||||
// (frames, the log sink) here — it never imports kernel internals.
|
||||
backend = architecture.iommu.detect(.{
|
||||
.register_base = info.iommu_base,
|
||||
.amd = info.iommu_is_amd,
|
||||
}, .{
|
||||
.allocateFrame = pmm.alloc,
|
||||
.allocateContiguous = pmm.allocContiguous,
|
||||
.write = log.write,
|
||||
}) orelse {
|
||||
log.write("/system/kernel: WARNING IOMMU present but unusable — staying fail-open\n");
|
||||
if (!info.iommu_present) {
|
||||
kind = .none;
|
||||
return;
|
||||
};
|
||||
active = true;
|
||||
}
|
||||
// Pick the backend by vendor. A present-but-unusable unit stays fail-open with a
|
||||
// logged reason rather than half-enabling.
|
||||
if (info.iommu_is_amd) {
|
||||
if (amd.detect(info)) |be| {
|
||||
backend = be;
|
||||
kind = .amd_vi;
|
||||
} else {
|
||||
kind = .none;
|
||||
log.write("/system/kernel: WARNING AMD-Vi present but unusable — staying fail-open\n");
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
if (intel.detect(info)) |be| {
|
||||
backend = be;
|
||||
kind = .intel_vtd;
|
||||
} else {
|
||||
kind = .none;
|
||||
log.write("/system/kernel: WARNING IOMMU present but unusable — staying fail-open\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// The translation structures start empty: every device is denied until its driver
|
||||
// claims it (confineDevice gives it a private domain). PCI functions are enumerated
|
||||
// post-boot by the ring-3 pci-bus driver, so there is nothing to attach at init.
|
||||
// The backend writes its identity lines; the neutral posture lines follow.
|
||||
backend.enable();
|
||||
logPosture(info);
|
||||
if (kind == .amd_vi) amd.enable() else intel.enable();
|
||||
logEnabled(info);
|
||||
}
|
||||
|
||||
/// Per-claimed-device record: its private domain, so a driver's death tears down
|
||||
@@ -104,7 +147,7 @@ var confined: [maximum_domains]Confined = .{Confined{}} ** maximum_domains;
|
||||
/// the claim back (a claim that can't be confined must not stand). No-op success when no
|
||||
/// IOMMU exists (fail-open).
|
||||
pub fn confineDevice(device_id: u64, bdf: u16, owner: u32) bool {
|
||||
if (!active) return true;
|
||||
if (kind == .none) return true;
|
||||
if (device_id >= confined.len) return true; // unusual id; leave it to fail-open
|
||||
const domain = domainCreate(owner, bdf) orelse return false;
|
||||
|
||||
@@ -131,14 +174,14 @@ fn confinedOf(device_id: u64) ?*Confined {
|
||||
/// Map a DMA region into a specific claimed device's domain (the device owner binding a
|
||||
/// granted buffer). false if the device is not confined. No-op success without an IOMMU.
|
||||
pub fn mapForDevice(device_id: u64, physical: u64, len: u64) bool {
|
||||
if (!active) return true;
|
||||
if (kind == .none) return true;
|
||||
const c = confinedOf(device_id) orelse return false;
|
||||
return map(c.domain, physical, len);
|
||||
}
|
||||
|
||||
/// Unmap a DMA region from a specific claimed device's domain. No-op if not confined.
|
||||
pub fn unmapForDevice(device_id: u64, physical: u64, len: u64) void {
|
||||
if (!active) return;
|
||||
if (kind == .none) return;
|
||||
const c = confinedOf(device_id) orelse return;
|
||||
unmap(c.domain, physical, len);
|
||||
}
|
||||
@@ -146,7 +189,7 @@ pub fn unmapForDevice(device_id: u64, physical: u64, len: u64) void {
|
||||
/// Map a region into every claimed device owned by `owner` — the auto-bind of a task's
|
||||
/// own freshly-`dma_alloc`'d buffer into the devices it drives.
|
||||
pub fn mapRegionForOwner(owner: u32, physical: u64, len: u64) void {
|
||||
if (!active) return;
|
||||
if (kind == .none) return;
|
||||
for (&confined) |*c| {
|
||||
if (c.active and c.owner == owner) _ = map(c.domain, physical, len);
|
||||
}
|
||||
@@ -157,7 +200,7 @@ pub fn mapRegionForOwner(owner: u32, physical: u64, len: u64) void {
|
||||
/// use-after-free this prevents. Cross-device because a granted buffer may be bound in a
|
||||
/// domain other than its owner's.
|
||||
pub fn unmapRegionEverywhere(physical: u64, len: u64) void {
|
||||
if (!active) return;
|
||||
if (kind == .none) return;
|
||||
for (&confined) |*c| {
|
||||
if (c.active) unmap(c.domain, physical, len);
|
||||
}
|
||||
@@ -167,7 +210,7 @@ pub fn unmapRegionEverywhere(physical: u64, len: u64) void {
|
||||
/// device, free the tables) so their DMA is blocked again and a restarted driver
|
||||
/// re-claims cleanly. Runs BEFORE the broker claims and the DMA frames are released.
|
||||
pub fn releaseAllOwnedBy(owner: u32) void {
|
||||
if (!active) return;
|
||||
if (kind == .none) return;
|
||||
for (&confined) |*c| {
|
||||
if (c.active and c.owner == owner) {
|
||||
detachDevice(c.bdf);
|
||||
@@ -180,7 +223,7 @@ pub fn releaseAllOwnedBy(owner: u32) void {
|
||||
|
||||
/// Allocate an empty domain (an empty top-level table). null when the table is full.
|
||||
pub fn domainCreate(owner: u32, bdf: u16) ?u16 {
|
||||
if (!active) return 0; // fail-open: a dummy id the no-op ops ignore
|
||||
if (kind == .none) return 0; // fail-open: a dummy id the no-op ops ignore
|
||||
for (&domains, 0..) |*d, index| {
|
||||
if (d.in_use) continue;
|
||||
const root = allocTable() orelse return null;
|
||||
@@ -193,7 +236,7 @@ pub fn domainCreate(owner: u32, bdf: u16) ?u16 {
|
||||
/// Free a domain's page-table frames and its slot. Precondition: no device attached
|
||||
/// (detach first).
|
||||
pub fn domainDestroy(domain: u16) void {
|
||||
if (!active) return;
|
||||
if (kind == .none) return;
|
||||
const d = &domains[domain];
|
||||
if (!d.in_use) return;
|
||||
freeTables(d.page_table_root, backend.levels);
|
||||
@@ -202,7 +245,7 @@ pub fn domainDestroy(domain: u16) void {
|
||||
|
||||
/// Attach `bdf`'s device to `domain` and pre-load any RMRR range recorded for it.
|
||||
pub fn attachDevice(domain: u16, bdf: u16) void {
|
||||
if (!active) return;
|
||||
if (kind == .none) return;
|
||||
const d = &domains[domain];
|
||||
d.bdf = bdf;
|
||||
backend.attach(bdf, hardwareId(domain), d.page_table_root);
|
||||
@@ -210,7 +253,7 @@ pub fn attachDevice(domain: u16, bdf: u16) void {
|
||||
|
||||
/// Return `bdf`'s device to not-present + invalidate.
|
||||
pub fn detachDevice(bdf: u16) void {
|
||||
if (!active) return;
|
||||
if (kind == .none) return;
|
||||
backend.detach(bdf);
|
||||
}
|
||||
|
||||
@@ -218,7 +261,7 @@ pub fn detachDevice(bdf: u16) void {
|
||||
/// Unconditional domain-selective invalidation after every map — correct under VT-d
|
||||
/// caching-mode and free otherwise.
|
||||
pub fn map(domain: u16, physical: u64, len: u64) bool {
|
||||
if (!active) return true;
|
||||
if (kind == .none) return true;
|
||||
const d = &domains[domain];
|
||||
if (!d.in_use) return false;
|
||||
if (!mapRange(d.page_table_root, physical, len)) return false;
|
||||
@@ -230,7 +273,7 @@ pub fn map(domain: u16, physical: u64, len: u64) bool {
|
||||
/// invalidation before the caller returns the frames to pmm — a stale IOTLB entry
|
||||
/// pointing at a reallocated frame is the use-after-free this ordering prevents.
|
||||
pub fn unmap(domain: u16, physical: u64, len: u64) void {
|
||||
if (!active) return;
|
||||
if (kind == .none) return;
|
||||
const d = &domains[domain];
|
||||
if (!d.in_use) return;
|
||||
unmapRange(d.page_table_root, physical, len);
|
||||
@@ -240,14 +283,14 @@ pub fn unmap(domain: u16, physical: u64, len: u64) void {
|
||||
/// Poll the hardware for translation faults, log them, return the count. Called by the
|
||||
/// IOMMU test case and opportunistically after a device detaches.
|
||||
pub fn faultDrain() usize {
|
||||
if (!active) return 0;
|
||||
if (kind == .none) return 0;
|
||||
return backend.faultDrain();
|
||||
}
|
||||
|
||||
/// The physical address `virtual` maps to in `domain`, or null if unmapped — a test
|
||||
/// helper that walks the domain's page tables (identity mappings return `virtual`).
|
||||
pub fn translationOf(domain: u16, virtual: u64) ?u64 {
|
||||
if (!active) return virtual;
|
||||
if (kind == .none) return virtual;
|
||||
const d = &domains[domain];
|
||||
if (!d.in_use) return null;
|
||||
var table = d.page_table_root;
|
||||
@@ -382,16 +425,24 @@ fn isHugeLeaf(entry: u64) bool {
|
||||
/// The size-bit the backends set on a 2 MiB leaf (VT-d SL-PTE PS bit 7; AMD encodes a
|
||||
/// leaf as next-level 0, so the core marks huge leaves with this software bit — an
|
||||
/// ignored bit in both formats — to tell them apart from table pointers when freeing).
|
||||
const huge_leaf_bit: u64 = 1 << 7;
|
||||
pub const huge_leaf_bit: u64 = 1 << 7;
|
||||
|
||||
fn hardwareId(domain: u16) u16 {
|
||||
return domain + 1; // id 0 is reserved by both architectures
|
||||
}
|
||||
|
||||
fn logPosture(info: platform.PlatformInformation) void {
|
||||
fn logEnabled(info: platform.PlatformInformation) void {
|
||||
if (kind == .amd_vi) {
|
||||
log.write("/system/kernel: iommu online (AMD-Vi) — UNTESTED on real AMD hardware (QEMU-verified only)\n");
|
||||
log.print(" levels : {d} (48-bit)\n", .{backend.levels});
|
||||
return;
|
||||
}
|
||||
log.write("/system/kernel: iommu online (Intel VT-d)\n");
|
||||
log.print(" version : 0x{x}\n", .{info.iommu_version});
|
||||
log.print(" agaw : {d} levels\n", .{backend.levels});
|
||||
log.print(" rmrr : {d} region(s) premapped\n", .{info.rmrr_count});
|
||||
if (info.rmrr_skipped > 0)
|
||||
log.print(" rmrr : WARNING {d} scope(s) skipped — a device keeps an unmapped firmware buffer\n", .{info.rmrr_skipped});
|
||||
if (info.iommu_extra_units > 0)
|
||||
log.print(" units : WARNING {d} other unit(s) — their scoped devices are NOT translated\n", .{info.iommu_extra_units});
|
||||
log.print(" units : WARNING {d} other DRHD(s) — their scoped devices are NOT translated\n", .{info.iommu_extra_units});
|
||||
}
|
||||
|
||||
+12
-25
@@ -1253,10 +1253,11 @@ fn iommuTest() void {
|
||||
const pinfo = platform.platformInformation();
|
||||
check("IOMMU found in the firmware tables", pinfo.iommu_present);
|
||||
check("IOMMU unit has a register base", pinfo.iommu_base != 0);
|
||||
// The live-unit sanity (the version register reading back nonzero) now
|
||||
// gates detect itself: an unusable unit stays fail-open, so `enabled()`
|
||||
// below subsumes the old vendor-gated register check.
|
||||
log("DANOS-IOMMU: base=0x{x}\n", .{pinfo.iommu_base});
|
||||
// The VT-d version register is a live-unit sanity check; AMD-Vi (from IVRS) records
|
||||
// no version, so gate it on the vendor.
|
||||
if (!pinfo.iommu_is_amd)
|
||||
check("VT-d version register reads back nonzero (real, mappable unit)", pinfo.iommu_version != 0);
|
||||
log("DANOS-IOMMU: base=0x{x} version=0x{x} capabilities=0x{x}\n", .{ pinfo.iommu_base, pinfo.iommu_version, pinfo.iommu_capabilities });
|
||||
|
||||
// Translation was enabled at boot (kernel.zig: iommu.init before any driver claims
|
||||
// a device). The blanket domain keeps every device identity-mapped, so DMA still
|
||||
@@ -1969,20 +1970,15 @@ fn initTest(boot_information: *const BootInformation) void {
|
||||
};
|
||||
check("init loaded and spawned as a process", spawned);
|
||||
|
||||
// Wait (real time) until the LAST write is a heartbeat — proving init got
|
||||
// through its boot chatter (heap ok, the /etc/init.csv lookup) and settled
|
||||
// into its beat-and-sleep loop (~1 s between beats). Waiting on the text
|
||||
// rather than a raw write count: the boot chatter alone satisfies a count,
|
||||
// which is exactly the too-early check that used to fail here.
|
||||
// Wait (real time) for at least two heartbeats — proving it runs, writes,
|
||||
// and sleeps repeatedly (init sleeps ~1 s between beats).
|
||||
scheduler.setPriority(1);
|
||||
const prefix = "init: heartbeat";
|
||||
const deadline = architecture.millis() + 8000;
|
||||
var beat_ok = false;
|
||||
while (!beat_ok and architecture.millis() < deadline) {
|
||||
beat_ok = bufferHas(prefix);
|
||||
scheduler.yield();
|
||||
}
|
||||
while (process.write_count < 2 and architecture.millis() < deadline) scheduler.yield();
|
||||
scheduler.setPriority(4);
|
||||
|
||||
const prefix = "init: heartbeat";
|
||||
const beat_ok = bufferHas(prefix);
|
||||
check("init produced repeated heartbeats (>=2)", process.write_count >= 2);
|
||||
check("heartbeat text arrived intact", beat_ok);
|
||||
check("heartbeats came from user mode (CPL 3)", process.write_from_user);
|
||||
@@ -2796,24 +2792,15 @@ fn initialRamdiskTest(boot_information: *const BootInformation) void {
|
||||
|
||||
process.write_count = 0;
|
||||
process.write_from_user = false;
|
||||
var programs: u32 = 0;
|
||||
var spawned: u32 = 0;
|
||||
var i: u32 = 0;
|
||||
while (i < rd.count) : (i += 1) {
|
||||
const item = rd.entry(i) orelse continue;
|
||||
// The FHS boot tree ferries data files too (/etc/devices.csv,
|
||||
// /etc/init.csv — served read-only by the kernel VFS, never spawned);
|
||||
// only the /system and /test trees hold programs, so only those count
|
||||
// toward the spawn-everything sweep.
|
||||
const is_program = std.mem.startsWith(u8, item.name, "/system/") or
|
||||
std.mem.startsWith(u8, item.name, "/test/");
|
||||
if (!is_program) continue;
|
||||
programs += 1;
|
||||
if (process.spawnProcess(item.blob, 4, &.{item.name})) spawned += 1 else |err| {
|
||||
log("DANOS-INITRD-ERR: {s}: {s}\n", .{ item.name, @errorName(err) });
|
||||
}
|
||||
}
|
||||
check("every initial_ramdisk program spawned", programs >= 1 and spawned == programs);
|
||||
check("every initial_ramdisk binary spawned", spawned == rd.count);
|
||||
|
||||
// Wait for the spawned programs to run and make syscalls (they write + sleep).
|
||||
scheduler.setPriority(1);
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
//! The acpi service as a binary package (docs/build-packages-plan.md):
|
||||
//! this file names the binary and EXACTLY the modules its source imports —
|
||||
//! the shared recipe and the module-to-domain map live in build-support.
|
||||
//!
|
||||
//! The artifact is named "discovery": one swappable process per firmware
|
||||
//! fills the ramdisk's neutral `discovery` slot (docs/discovery.md); the
|
||||
//! root's -Ddiscovery picks this package or `fdt`.
|
||||
|
||||
const std = @import("std");
|
||||
const build_support = @import("build-support");
|
||||
|
||||
pub fn build(b: *std.Build) void {
|
||||
const exe = build_support.userBinary(b, .{
|
||||
.name = "discovery",
|
||||
.root_source_file = b.path("acpi.zig"),
|
||||
.imports = &.{
|
||||
"acpi-ids", "aml", "device-manager-protocol", "driver", "ipc", "logging", "memory",
|
||||
"power-protocol", "process", "service", "time",
|
||||
},
|
||||
});
|
||||
b.installArtifact(exe);
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
.{
|
||||
.name = .acpi,
|
||||
.version = "0.0.0",
|
||||
.fingerprint = 0xf31e9a0903256b64, // Changing this has security and trust implications.
|
||||
.minimum_zig_version = "0.16.0",
|
||||
.dependencies = .{
|
||||
// build-support supplies the shared recipe; kernel is implicit in
|
||||
// every binary (the root shim + link script live there). The rest
|
||||
// are exactly the homes of this binary's declared imports.
|
||||
.@"build-support" = .{ .path = "../../../build-support" },
|
||||
.kernel = .{ .path = "../../../library/kernel" },
|
||||
.device = .{ .path = "../../../library/device" },
|
||||
.protocol = .{ .path = "../../../library/protocol" },
|
||||
},
|
||||
.paths = .{""},
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
//! The device-manager service as a binary package (docs/build-packages-plan.md):
|
||||
//! this file names the binary and EXACTLY the modules its source imports —
|
||||
//! the shared recipe and the module-to-domain map live in build-support.
|
||||
|
||||
const std = @import("std");
|
||||
const build_support = @import("build-support");
|
||||
|
||||
pub fn build(b: *std.Build) void {
|
||||
const exe = build_support.userBinary(b, .{
|
||||
.name = "device-manager",
|
||||
.root_source_file = b.path("device-manager.zig"),
|
||||
.imports = &.{
|
||||
"device-manager-protocol", "device-registry", "driver", "file-system", "ipc",
|
||||
"logging", "memory", "process", "service", "time",
|
||||
},
|
||||
});
|
||||
b.installArtifact(exe);
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
.{
|
||||
.name = .device_manager,
|
||||
.version = "0.0.0",
|
||||
.fingerprint = 0x7092fc24905cd147, // Changing this has security and trust implications.
|
||||
.minimum_zig_version = "0.16.0",
|
||||
.dependencies = .{
|
||||
// build-support supplies the shared recipe; kernel is implicit in
|
||||
// every binary (the root shim + link script live there). The rest
|
||||
// are exactly the homes of this binary's declared imports.
|
||||
.@"build-support" = .{ .path = "../../../build-support" },
|
||||
.kernel = .{ .path = "../../../library/kernel" },
|
||||
.device = .{ .path = "../../../library/device" },
|
||||
.protocol = .{ .path = "../../../library/protocol" },
|
||||
},
|
||||
.paths = .{""},
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
//! The display-demo service as a binary package (docs/build-packages-plan.md):
|
||||
//! this file names the binary and EXACTLY the modules its source imports —
|
||||
//! the shared recipe and the module-to-domain map live in build-support.
|
||||
|
||||
const std = @import("std");
|
||||
const build_support = @import("build-support");
|
||||
|
||||
pub fn build(b: *std.Build) void {
|
||||
const exe = build_support.userBinary(b, .{
|
||||
.name = "display-demo",
|
||||
.root_source_file = b.path("display-demo.zig"),
|
||||
.imports = &.{ "display-client", "logging", "time" },
|
||||
});
|
||||
b.installArtifact(exe);
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
.{
|
||||
.name = .display_demo,
|
||||
.version = "0.0.0",
|
||||
.fingerprint = 0x5d2832e1e2880143, // Changing this has security and trust implications.
|
||||
.minimum_zig_version = "0.16.0",
|
||||
.dependencies = .{
|
||||
// build-support supplies the shared recipe; kernel is implicit in
|
||||
// every binary (the root shim + link script live there). The rest
|
||||
// are exactly the homes of this binary's declared imports.
|
||||
.@"build-support" = .{ .path = "../../../build-support" },
|
||||
.kernel = .{ .path = "../../../library/kernel" },
|
||||
.client = .{ .path = "../../../library/client" },
|
||||
},
|
||||
.paths = .{""},
|
||||
}
|
||||
@@ -11,7 +11,7 @@
|
||||
//! is deliberately independent of the mouse.
|
||||
|
||||
|
||||
const display = @import("display-client");
|
||||
const display = @import("display");
|
||||
const time = @import("time");
|
||||
const logging = @import("logging");
|
||||
pub fn main() void {
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
//! The display service as a binary package (docs/build-packages-plan.md):
|
||||
//! this file names the binary and EXACTLY the modules its source imports —
|
||||
//! the shared recipe and the module-to-domain map live in build-support.
|
||||
|
||||
const std = @import("std");
|
||||
const build_support = @import("build-support");
|
||||
|
||||
pub fn build(b: *std.Build) void {
|
||||
const exe = build_support.userBinary(b, .{
|
||||
.name = "display",
|
||||
.root_source_file = b.path("display.zig"),
|
||||
.imports = &.{
|
||||
"display-client", "display-protocol", "driver", "input-client", "ipc", "logging",
|
||||
"memory", "scanout-protocol", "service", "thread", "time",
|
||||
},
|
||||
.threaded = true, // real atomics/TLS (docs/threading.md)
|
||||
});
|
||||
b.installArtifact(exe);
|
||||
|
||||
// Standalone `zig build test`; the root aggregate depends on this step.
|
||||
const test_step = b.step("test", "Run the display unit tests");
|
||||
for ([_][]const u8{
|
||||
"compositor.zig", // Rect math + fill/composite/blit-tile
|
||||
}) |test_root| {
|
||||
const unit_tests = b.addTest(.{
|
||||
.root_module = b.createModule(.{
|
||||
.root_source_file = b.path(test_root),
|
||||
.target = b.resolveTargetQuery(.{}),
|
||||
}),
|
||||
});
|
||||
test_step.dependOn(&b.addRunArtifact(unit_tests).step);
|
||||
}
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
.{
|
||||
.name = .display,
|
||||
.version = "0.0.0",
|
||||
.fingerprint = 0xcd172a34b127a19, // Changing this has security and trust implications.
|
||||
.minimum_zig_version = "0.16.0",
|
||||
.dependencies = .{
|
||||
// build-support supplies the shared recipe; kernel is implicit in
|
||||
// every binary (the root shim + link script live there). The rest
|
||||
// are exactly the homes of this binary's declared imports.
|
||||
.@"build-support" = .{ .path = "../../../build-support" },
|
||||
.kernel = .{ .path = "../../../library/kernel" },
|
||||
.client = .{ .path = "../../../library/client" },
|
||||
.device = .{ .path = "../../../library/device" },
|
||||
.protocol = .{ .path = "../../../library/protocol" },
|
||||
},
|
||||
.paths = .{""},
|
||||
}
|
||||
@@ -17,11 +17,11 @@
|
||||
|
||||
const std = @import("std");
|
||||
const ipc = @import("ipc");
|
||||
const input = @import("input-client");
|
||||
const input = @import("input");
|
||||
const Thread = @import("thread").Thread;
|
||||
const service = @import("service");
|
||||
const time = @import("time");
|
||||
const display = @import("display-client");
|
||||
const display = @import("display");
|
||||
const memory = @import("memory");
|
||||
const logging = @import("logging");
|
||||
const compositor = @import("compositor.zig");
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
//! The fat service as a binary package (docs/build-packages-plan.md):
|
||||
//! this file names the binary and EXACTLY the modules its source imports —
|
||||
//! the shared recipe and the module-to-domain map live in build-support.
|
||||
|
||||
const std = @import("std");
|
||||
const build_support = @import("build-support");
|
||||
|
||||
pub fn build(b: *std.Build) void {
|
||||
const exe = build_support.userBinary(b, .{
|
||||
.name = "fat",
|
||||
.root_source_file = b.path("fat.zig"),
|
||||
.imports = &.{
|
||||
"block", "file-system", "ipc", "logging", "memory", "process", "service", "time",
|
||||
"vfs-protocol",
|
||||
},
|
||||
});
|
||||
b.installArtifact(exe);
|
||||
|
||||
// Standalone `zig build test`; the root aggregate depends on this step.
|
||||
const test_step = b.step("test", "Run the fat unit tests");
|
||||
for ([_][]const u8{
|
||||
"on-disk.zig", // FAT on-disk struct sizes + type detection
|
||||
"engine.zig", // FAT read/write over a RAM-backed image
|
||||
}) |test_root| {
|
||||
const unit_tests = b.addTest(.{
|
||||
.root_module = b.createModule(.{
|
||||
.root_source_file = b.path(test_root),
|
||||
.target = b.resolveTargetQuery(.{}),
|
||||
}),
|
||||
});
|
||||
test_step.dependOn(&b.addRunArtifact(unit_tests).step);
|
||||
}
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
.{
|
||||
.name = .fat,
|
||||
.version = "0.0.0",
|
||||
.fingerprint = 0x98958143ecfe1636, // Changing this has security and trust implications.
|
||||
.minimum_zig_version = "0.16.0",
|
||||
.dependencies = .{
|
||||
// build-support supplies the shared recipe; kernel is implicit in
|
||||
// every binary (the root shim + link script live there). The rest
|
||||
// are exactly the homes of this binary's declared imports.
|
||||
.@"build-support" = .{ .path = "../../../build-support" },
|
||||
.kernel = .{ .path = "../../../library/kernel" },
|
||||
.device = .{ .path = "../../../library/device" },
|
||||
.protocol = .{ .path = "../../../library/protocol" },
|
||||
},
|
||||
.paths = .{""},
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
//! The fdt service as a binary package (docs/build-packages-plan.md):
|
||||
//! this file names the binary and EXACTLY the modules its source imports —
|
||||
//! the shared recipe and the module-to-domain map live in build-support.
|
||||
//!
|
||||
//! The artifact is named "discovery" like acpi's: the Raspberry Pis hand over
|
||||
//! a flattened device tree, and the aarch64 target flips the root's
|
||||
//! -Ddiscovery default when it lands (docs/arm.md). A placeholder until the
|
||||
//! ARM bring-up.
|
||||
|
||||
const std = @import("std");
|
||||
const build_support = @import("build-support");
|
||||
|
||||
pub fn build(b: *std.Build) void {
|
||||
const exe = build_support.userBinary(b, .{
|
||||
.name = "discovery",
|
||||
.root_source_file = b.path("fdt.zig"),
|
||||
.imports = &.{ "process" },
|
||||
});
|
||||
b.installArtifact(exe);
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
.{
|
||||
.name = .fdt,
|
||||
.version = "0.0.0",
|
||||
.fingerprint = 0xe5e27506c7fc2eea, // Changing this has security and trust implications.
|
||||
.minimum_zig_version = "0.16.0",
|
||||
.dependencies = .{
|
||||
// build-support supplies the shared recipe; kernel is implicit in
|
||||
// every binary (the root shim + link script live there). The rest
|
||||
// are exactly the homes of this binary's declared imports.
|
||||
.@"build-support" = .{ .path = "../../../build-support" },
|
||||
.kernel = .{ .path = "../../../library/kernel" },
|
||||
},
|
||||
.paths = .{""},
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
//! init (PID 1) as a binary package (docs/build-packages-plan.md): this file
|
||||
//! names the binary and EXACTLY the modules its source imports — the shared
|
||||
//! recipe and the module-to-domain map live in build-support. The root build
|
||||
//! consumes the artifact for the boot image and forwards its -Dserial here.
|
||||
|
||||
const std = @import("std");
|
||||
const build_support = @import("build-support");
|
||||
|
||||
pub fn build(b: *std.Build) void {
|
||||
const exe = build_support.userBinary(b, .{
|
||||
.name = "init",
|
||||
.root_source_file = b.path("init.zig"),
|
||||
.imports = &.{
|
||||
"csv", "file-system", "ipc", "logging", "memory", "power-protocol",
|
||||
"process", "time",
|
||||
},
|
||||
});
|
||||
// init reads the same `serial` flag the kernel does: its liveness heartbeat
|
||||
// is a serial/test-build diagnostic (the QEMU harness's init tests assert
|
||||
// on it, and -Dserial images emit it), so a flashable image runs a purely
|
||||
// event-driven PID 1 that wakes only for real work. The root build forwards
|
||||
// its top-level -Dserial as this dependency option.
|
||||
const serial = b.option(bool, "serial", "Compile the serial liveness heartbeat in (forwarded from the root -Dserial)") orelse false;
|
||||
const init_options = b.addOptions();
|
||||
init_options.addOption(bool, "serial", serial);
|
||||
build_support.programModule(exe).addImport("build_options", init_options.createModule());
|
||||
b.installArtifact(exe);
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
.{
|
||||
.name = .init,
|
||||
.version = "0.0.0",
|
||||
.fingerprint = 0xc674e474eeeced43, // Changing this has security and trust implications.
|
||||
.minimum_zig_version = "0.16.0",
|
||||
.dependencies = .{
|
||||
// build-support supplies the shared recipe; kernel is implicit in
|
||||
// every binary (the root shim + link script live there). The rest
|
||||
// are exactly the homes of this binary's declared imports.
|
||||
.@"build-support" = .{ .path = "../../../build-support" },
|
||||
.kernel = .{ .path = "../../../library/kernel" },
|
||||
.csv = .{ .path = "../../../library/csv" },
|
||||
.protocol = .{ .path = "../../../library/protocol" },
|
||||
},
|
||||
.paths = .{""},
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
//! The input service as a binary package (docs/build-packages-plan.md):
|
||||
//! this file names the binary and EXACTLY the modules its source imports —
|
||||
//! the shared recipe and the module-to-domain map live in build-support.
|
||||
|
||||
const std = @import("std");
|
||||
const build_support = @import("build-support");
|
||||
|
||||
pub fn build(b: *std.Build) void {
|
||||
const exe = build_support.userBinary(b, .{
|
||||
.name = "input",
|
||||
.root_source_file = b.path("input.zig"),
|
||||
.imports = &.{ "input-client", "input-protocol", "ipc", "logging", "process", "service" },
|
||||
});
|
||||
b.installArtifact(exe);
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
.{
|
||||
.name = .input,
|
||||
.version = "0.0.0",
|
||||
.fingerprint = 0xd82832d7113ed94e, // Changing this has security and trust implications.
|
||||
.minimum_zig_version = "0.16.0",
|
||||
.dependencies = .{
|
||||
// build-support supplies the shared recipe; kernel is implicit in
|
||||
// every binary (the root shim + link script live there). The rest
|
||||
// are exactly the homes of this binary's declared imports.
|
||||
.@"build-support" = .{ .path = "../../../build-support" },
|
||||
.kernel = .{ .path = "../../../library/kernel" },
|
||||
.client = .{ .path = "../../../library/client" },
|
||||
.protocol = .{ .path = "../../../library/protocol" },
|
||||
},
|
||||
.paths = .{""},
|
||||
}
|
||||
@@ -23,7 +23,7 @@ const std = @import("std");
|
||||
const ipc = @import("ipc");
|
||||
const process = @import("process");
|
||||
const service = @import("service");
|
||||
const input = @import("input-client");
|
||||
const input = @import("input");
|
||||
const logging = @import("logging");
|
||||
const input_protocol = @import("input-protocol");
|
||||
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
//! The logger service as a binary package (docs/build-packages-plan.md):
|
||||
//! this file names the binary and EXACTLY the modules its source imports —
|
||||
//! the shared recipe and the module-to-domain map live in build-support.
|
||||
|
||||
const std = @import("std");
|
||||
const build_support = @import("build-support");
|
||||
|
||||
pub fn build(b: *std.Build) void {
|
||||
const exe = build_support.userBinary(b, .{
|
||||
.name = "logger",
|
||||
.root_source_file = b.path("logger.zig"),
|
||||
.imports = &.{ "file-system", "ipc", "logging", "service", "time" },
|
||||
});
|
||||
b.installArtifact(exe);
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
.{
|
||||
.name = .logger,
|
||||
.version = "0.0.0",
|
||||
.fingerprint = 0x987e13f37b0eaea2, // Changing this has security and trust implications.
|
||||
.minimum_zig_version = "0.16.0",
|
||||
.dependencies = .{
|
||||
// build-support supplies the shared recipe; kernel is implicit in
|
||||
// every binary (the root shim + link script live there). The rest
|
||||
// are exactly the homes of this binary's declared imports.
|
||||
.@"build-support" = .{ .path = "../../../build-support" },
|
||||
.kernel = .{ .path = "../../../library/kernel" },
|
||||
},
|
||||
.paths = .{""},
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
//! The args-echo test fixture as a binary package (docs/build-packages-plan.md):
|
||||
//! this file names the binary and EXACTLY the modules its source imports —
|
||||
//! the shared recipe and the module-to-domain map live in build-support.
|
||||
|
||||
const std = @import("std");
|
||||
const build_support = @import("build-support");
|
||||
|
||||
pub fn build(b: *std.Build) void {
|
||||
const exe = build_support.userBinary(b, .{
|
||||
.name = "args-echo",
|
||||
.root_source_file = b.path("args-echo.zig"),
|
||||
.imports = &.{ "logging", "process" },
|
||||
});
|
||||
b.installArtifact(exe);
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
.{
|
||||
.name = .args_echo,
|
||||
.version = "0.0.0",
|
||||
.fingerprint = 0xcc0ad94cf777eeb, // Changing this has security and trust implications.
|
||||
.minimum_zig_version = "0.16.0",
|
||||
.dependencies = .{
|
||||
// build-support supplies the shared recipe; kernel is implicit in
|
||||
// every binary (the root shim + link script live there). The rest
|
||||
// are exactly the homes of this binary's declared imports.
|
||||
.@"build-support" = .{ .path = "../../../../build-support" },
|
||||
.kernel = .{ .path = "../../../../library/kernel" },
|
||||
},
|
||||
.paths = .{""},
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
//! The crash-test test fixture as a binary package (docs/build-packages-plan.md):
|
||||
//! this file names the binary and EXACTLY the modules its source imports —
|
||||
//! the shared recipe and the module-to-domain map live in build-support.
|
||||
|
||||
const std = @import("std");
|
||||
const build_support = @import("build-support");
|
||||
|
||||
pub fn build(b: *std.Build) void {
|
||||
const exe = build_support.userBinary(b, .{
|
||||
.name = "crash-test",
|
||||
.root_source_file = b.path("crash-test.zig"),
|
||||
.imports = &.{ "device-manager-protocol", "driver", "ipc", "logging", "process", "time" },
|
||||
});
|
||||
b.installArtifact(exe);
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
.{
|
||||
.name = .crash_test,
|
||||
.version = "0.0.0",
|
||||
.fingerprint = 0x611122f815277984, // Changing this has security and trust implications.
|
||||
.minimum_zig_version = "0.16.0",
|
||||
.dependencies = .{
|
||||
// build-support supplies the shared recipe; kernel is implicit in
|
||||
// every binary (the root shim + link script live there). The rest
|
||||
// are exactly the homes of this binary's declared imports.
|
||||
.@"build-support" = .{ .path = "../../../../build-support" },
|
||||
.kernel = .{ .path = "../../../../library/kernel" },
|
||||
.device = .{ .path = "../../../../library/device" },
|
||||
.protocol = .{ .path = "../../../../library/protocol" },
|
||||
},
|
||||
.paths = .{""},
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
//! The device-list test fixture as a binary package (docs/build-packages-plan.md):
|
||||
//! this file names the binary and EXACTLY the modules its source imports —
|
||||
//! the shared recipe and the module-to-domain map live in build-support.
|
||||
|
||||
const std = @import("std");
|
||||
const build_support = @import("build-support");
|
||||
|
||||
pub fn build(b: *std.Build) void {
|
||||
const exe = build_support.userBinary(b, .{
|
||||
.name = "device-list",
|
||||
.root_source_file = b.path("device-list.zig"),
|
||||
.imports = &.{ "device-manager-protocol", "ipc", "logging", "time" },
|
||||
});
|
||||
b.installArtifact(exe);
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
.{
|
||||
.name = .device_list,
|
||||
.version = "0.0.0",
|
||||
.fingerprint = 0xcdf12d2276a06cb1, // Changing this has security and trust implications.
|
||||
.minimum_zig_version = "0.16.0",
|
||||
.dependencies = .{
|
||||
// build-support supplies the shared recipe; kernel is implicit in
|
||||
// every binary (the root shim + link script live there). The rest
|
||||
// are exactly the homes of this binary's declared imports.
|
||||
.@"build-support" = .{ .path = "../../../../build-support" },
|
||||
.kernel = .{ .path = "../../../../library/kernel" },
|
||||
.protocol = .{ .path = "../../../../library/protocol" },
|
||||
},
|
||||
.paths = .{""},
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
//! The fat-test test fixture as a binary package (docs/build-packages-plan.md):
|
||||
//! this file names the binary and EXACTLY the modules its source imports —
|
||||
//! the shared recipe and the module-to-domain map live in build-support.
|
||||
|
||||
const std = @import("std");
|
||||
const build_support = @import("build-support");
|
||||
|
||||
pub fn build(b: *std.Build) void {
|
||||
const exe = build_support.userBinary(b, .{
|
||||
.name = "fat-test",
|
||||
.root_source_file = b.path("fat-test.zig"),
|
||||
.imports = &.{ "file-system", "logging", "process", "time" },
|
||||
});
|
||||
b.installArtifact(exe);
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
.{
|
||||
.name = .fat_test,
|
||||
.version = "0.0.0",
|
||||
.fingerprint = 0x8dac87d9dc3aabca, // Changing this has security and trust implications.
|
||||
.minimum_zig_version = "0.16.0",
|
||||
.dependencies = .{
|
||||
// build-support supplies the shared recipe; kernel is implicit in
|
||||
// every binary (the root shim + link script live there). The rest
|
||||
// are exactly the homes of this binary's declared imports.
|
||||
.@"build-support" = .{ .path = "../../../../build-support" },
|
||||
.kernel = .{ .path = "../../../../library/kernel" },
|
||||
},
|
||||
.paths = .{""},
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
//! The input-source test fixture as a binary package (docs/build-packages-plan.md):
|
||||
//! this file names the binary and EXACTLY the modules its source imports —
|
||||
//! the shared recipe and the module-to-domain map live in build-support.
|
||||
|
||||
const std = @import("std");
|
||||
const build_support = @import("build-support");
|
||||
|
||||
pub fn build(b: *std.Build) void {
|
||||
const exe = build_support.userBinary(b, .{
|
||||
.name = "input-source",
|
||||
.root_source_file = b.path("input-source.zig"),
|
||||
.imports = &.{ "input-client", "logging", "process", "time" },
|
||||
});
|
||||
b.installArtifact(exe);
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
.{
|
||||
.name = .input_source,
|
||||
.version = "0.0.0",
|
||||
.fingerprint = 0x933ef62ed7d33db7, // Changing this has security and trust implications.
|
||||
.minimum_zig_version = "0.16.0",
|
||||
.dependencies = .{
|
||||
// build-support supplies the shared recipe; kernel is implicit in
|
||||
// every binary (the root shim + link script live there). The rest
|
||||
// are exactly the homes of this binary's declared imports.
|
||||
.@"build-support" = .{ .path = "../../../../build-support" },
|
||||
.kernel = .{ .path = "../../../../library/kernel" },
|
||||
.client = .{ .path = "../../../../library/client" },
|
||||
},
|
||||
.paths = .{""},
|
||||
}
|
||||
@@ -11,7 +11,7 @@
|
||||
//! decoded hardware is a follow-up (see docs/input.md).
|
||||
|
||||
const std = @import("std");
|
||||
const input = @import("input-client");
|
||||
const input = @import("input");
|
||||
const process = @import("process");
|
||||
const time = @import("time");
|
||||
const logging = @import("logging");
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
//! The input-test test fixture as a binary package (docs/build-packages-plan.md):
|
||||
//! this file names the binary and EXACTLY the modules its source imports —
|
||||
//! the shared recipe and the module-to-domain map live in build-support.
|
||||
|
||||
const std = @import("std");
|
||||
const build_support = @import("build-support");
|
||||
|
||||
pub fn build(b: *std.Build) void {
|
||||
const exe = build_support.userBinary(b, .{
|
||||
.name = "input-test",
|
||||
.root_source_file = b.path("input-test.zig"),
|
||||
.imports = &.{ "input-client", "logging" },
|
||||
});
|
||||
b.installArtifact(exe);
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
.{
|
||||
.name = .input_test,
|
||||
.version = "0.0.0",
|
||||
.fingerprint = 0x9b12c53bde4d070e, // Changing this has security and trust implications.
|
||||
.minimum_zig_version = "0.16.0",
|
||||
.dependencies = .{
|
||||
// build-support supplies the shared recipe; kernel is implicit in
|
||||
// every binary (the root shim + link script live there). The rest
|
||||
// are exactly the homes of this binary's declared imports.
|
||||
.@"build-support" = .{ .path = "../../../../build-support" },
|
||||
.kernel = .{ .path = "../../../../library/kernel" },
|
||||
.client = .{ .path = "../../../../library/client" },
|
||||
},
|
||||
.paths = .{""},
|
||||
}
|
||||
@@ -7,7 +7,7 @@
|
||||
//! device classes to one subscription — source -> service -> subscriber, per device.
|
||||
|
||||
const std = @import("std");
|
||||
const input = @import("input-client");
|
||||
const input = @import("input");
|
||||
const logging = @import("logging");
|
||||
|
||||
fn writeLine(comptime fmt: []const u8, arguments: anytype) void {
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
//! The iommu-fault-test test fixture as a binary package (docs/build-packages-plan.md):
|
||||
//! this file names the binary and EXACTLY the modules its source imports —
|
||||
//! the shared recipe and the module-to-domain map live in build-support.
|
||||
|
||||
const std = @import("std");
|
||||
const build_support = @import("build-support");
|
||||
|
||||
pub fn build(b: *std.Build) void {
|
||||
const exe = build_support.userBinary(b, .{
|
||||
.name = "iommu-fault-test",
|
||||
.root_source_file = b.path("iommu-fault-test.zig"),
|
||||
.imports = &.{ "driver", "logging", "mmio", "pci", "pci-class", "time" },
|
||||
});
|
||||
b.installArtifact(exe);
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
.{
|
||||
.name = .iommu_fault_test,
|
||||
.version = "0.0.0",
|
||||
.fingerprint = 0xd7ec3a4e1a61696c, // Changing this has security and trust implications.
|
||||
.minimum_zig_version = "0.16.0",
|
||||
.dependencies = .{
|
||||
// build-support supplies the shared recipe; kernel is implicit in
|
||||
// every binary (the root shim + link script live there). The rest
|
||||
// are exactly the homes of this binary's declared imports.
|
||||
.@"build-support" = .{ .path = "../../../../build-support" },
|
||||
.kernel = .{ .path = "../../../../library/kernel" },
|
||||
.device = .{ .path = "../../../../library/device" },
|
||||
},
|
||||
.paths = .{""},
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
//! The pci-cap-test test fixture as a binary package (docs/build-packages-plan.md):
|
||||
//! this file names the binary and EXACTLY the modules its source imports —
|
||||
//! the shared recipe and the module-to-domain map live in build-support.
|
||||
|
||||
const std = @import("std");
|
||||
const build_support = @import("build-support");
|
||||
|
||||
pub fn build(b: *std.Build) void {
|
||||
const exe = build_support.userBinary(b, .{
|
||||
.name = "pci-cap-test",
|
||||
.root_source_file = b.path("pci-cap-test.zig"),
|
||||
.imports = &.{ "driver", "ipc", "logging", "mmio", "pci", "pci-class", "time" },
|
||||
});
|
||||
b.installArtifact(exe);
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
.{
|
||||
.name = .pci_cap_test,
|
||||
.version = "0.0.0",
|
||||
.fingerprint = 0x2d4f027d811c3953, // Changing this has security and trust implications.
|
||||
.minimum_zig_version = "0.16.0",
|
||||
.dependencies = .{
|
||||
// build-support supplies the shared recipe; kernel is implicit in
|
||||
// every binary (the root shim + link script live there). The rest
|
||||
// are exactly the homes of this binary's declared imports.
|
||||
.@"build-support" = .{ .path = "../../../../build-support" },
|
||||
.kernel = .{ .path = "../../../../library/kernel" },
|
||||
.device = .{ .path = "../../../../library/device" },
|
||||
},
|
||||
.paths = .{""},
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user