C2: migrate consumers off the runtime shim to direct concern-module imports
Every user binary and the two device-logic library modules (pci, usb) now
`@import` the concern modules directly instead of aliasing through `runtime`:
runtime.ipc/process/time/service/input/block/display -> @import("<module>")
runtime.device / runtime.device_manager -> @import("driver")
runtime.fs -> @import("file-system")
runtime.Thread -> @import("thread").Thread
runtime.system.{write,writeRecord,klog*} -> logging.*
runtime.system.{sleep,timerOnce,wallClock,clock} -> time.*
runtime.system.{spawn*,kill,exit,yield,processes,...}-> process.*
runtime.system.{mmap,munmap,PROT_*} -> memory.*
runtime.dma.* / runtime.shared_memory.* / runtime.allocator -> memory.*
Each consumer keeps its own alias name (e.g. `const device = @import("driver")`),
so call sites are unchanged and there are no collisions with local `driver`
variables. build.zig now injects the concern modules into every user binary via
`default_imports`; pci/usb module import lists were updated to match.
The `runtime` and `system` shims remain for one more step (root.zig still uses
runtime); they are deleted in C5. Nothing but root.zig imports `runtime` now.
Verified: zig build, zig build test, and 17 QEMU cases (smoke, device-manager,
logger, fat-mount, fat-mutations, usb-storage, usb-hid, display-native,
virtio-gpu, input, thread-spawn, thread-mutex, process-kill, shared-memory,
driver-restart, acpi-ps2, pci-scan).
This commit is contained in:
@@ -63,58 +63,46 @@ fn timestamp(b: *std.Build) []const u8 {
|
|||||||
fn addUserBinary(
|
fn addUserBinary(
|
||||||
b: *std.Build,
|
b: *std.Build,
|
||||||
target: std.Build.ResolvedTarget,
|
target: std.Build.ResolvedTarget,
|
||||||
|
default_imports: []const std.Build.Module.Import,
|
||||||
runtime_module: *std.Build.Module,
|
runtime_module: *std.Build.Module,
|
||||||
mmio_module: *std.Build.Module,
|
|
||||||
xkeyboard_config_module: *std.Build.Module,
|
|
||||||
acpi_ids_module: *std.Build.Module,
|
|
||||||
name: []const u8,
|
name: []const u8,
|
||||||
root: []const u8,
|
root: []const u8,
|
||||||
) *std.Build.Step.Compile {
|
) *std.Build.Step.Compile {
|
||||||
return addUserBinaryImpl(b, target, runtime_module, mmio_module, xkeyboard_config_module, acpi_ids_module, name, root, false);
|
return addUserBinaryImpl(b, target, default_imports, runtime_module, name, root, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// As `addUserBinary`, but built multi-threaded (`single_threaded = false`) so real
|
/// As `addUserBinary`, but built multi-threaded (`single_threaded = false`) so real
|
||||||
/// atomics/TLS work — required before a binary may call `runtime.Thread.spawn`
|
/// atomics/TLS work — required before a binary may call `Thread.spawn`
|
||||||
/// (docs/threading.md). Threads are a deliberate per-binary opt-in.
|
/// (docs/threading.md). Threads are a deliberate per-binary opt-in.
|
||||||
fn addThreadedUserBinary(
|
fn addThreadedUserBinary(
|
||||||
b: *std.Build,
|
b: *std.Build,
|
||||||
target: std.Build.ResolvedTarget,
|
target: std.Build.ResolvedTarget,
|
||||||
|
default_imports: []const std.Build.Module.Import,
|
||||||
runtime_module: *std.Build.Module,
|
runtime_module: *std.Build.Module,
|
||||||
mmio_module: *std.Build.Module,
|
|
||||||
xkeyboard_config_module: *std.Build.Module,
|
|
||||||
acpi_ids_module: *std.Build.Module,
|
|
||||||
name: []const u8,
|
name: []const u8,
|
||||||
root: []const u8,
|
root: []const u8,
|
||||||
) *std.Build.Step.Compile {
|
) *std.Build.Step.Compile {
|
||||||
return addUserBinaryImpl(b, target, runtime_module, mmio_module, xkeyboard_config_module, acpi_ids_module, name, root, true);
|
return addUserBinaryImpl(b, target, default_imports, runtime_module, name, root, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn addUserBinaryImpl(
|
fn addUserBinaryImpl(
|
||||||
b: *std.Build,
|
b: *std.Build,
|
||||||
target: std.Build.ResolvedTarget,
|
target: std.Build.ResolvedTarget,
|
||||||
|
default_imports: []const std.Build.Module.Import,
|
||||||
runtime_module: *std.Build.Module,
|
runtime_module: *std.Build.Module,
|
||||||
mmio_module: *std.Build.Module,
|
|
||||||
xkeyboard_config_module: *std.Build.Module,
|
|
||||||
acpi_ids_module: *std.Build.Module,
|
|
||||||
name: []const u8,
|
name: []const u8,
|
||||||
root: []const u8,
|
root: []const u8,
|
||||||
threaded: bool,
|
threaded: bool,
|
||||||
) *std.Build.Step.Compile {
|
) *std.Build.Step.Compile {
|
||||||
// Settings (target, optimize, code model, ...) live on the root module only;
|
// Every user binary gets the same default set of importable modules — the library/kernel
|
||||||
// the program and runtime modules leave theirs null and inherit them.
|
// concern modules (ipc, memory, process, time, logging, file-system, ...), the device/
|
||||||
|
// service clients (driver, block, display, input), mmio, acpi-ids, xkeyboard-config, and
|
||||||
|
// the compatibility `runtime` shim. Per-binary extras go through programModule(exe).addImport.
|
||||||
|
// Settings (target, optimize, code model, ...) live on the root module only; the program
|
||||||
|
// and runtime modules leave theirs null and inherit them.
|
||||||
const program_module = b.createModule(.{
|
const program_module = b.createModule(.{
|
||||||
.root_source_file = b.path(root),
|
.root_source_file = b.path(root),
|
||||||
.imports = &.{
|
.imports = default_imports,
|
||||||
.{ .name = "runtime", .module = runtime_module },
|
|
||||||
// Typed volatile MMIO + memory barriers, for drivers. See library/mmio/.
|
|
||||||
.{ .name = "mmio", .module = mmio_module },
|
|
||||||
// Keyboard layouts (keycode + modifiers -> keysym/character), available
|
|
||||||
// to any program that wants it. See library/xkeyboard-config/.
|
|
||||||
.{ .name = "xkeyboard-config", .module = xkeyboard_config_module },
|
|
||||||
// ACPI/PnP hardware-ID registry, so drivers name devices
|
|
||||||
// (HardwareId.ps2_keyboard) instead of magic "_HID" strings.
|
|
||||||
.{ .name = "acpi-ids", .module = acpi_ids_module },
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
const exe = b.addExecutable(.{
|
const exe = b.addExecutable(.{
|
||||||
.name = name,
|
.name = name,
|
||||||
@@ -554,12 +542,12 @@ pub fn build(b: *std.Build) void {
|
|||||||
|
|
||||||
// A device driver's view of its claimed PCI function: config-space header fields, BAR
|
// A device driver's view of its claimed PCI function: config-space header fields, BAR
|
||||||
// decode + map, and the capability walk (library/device/pci/pci.zig). The generic PCI
|
// decode + map, and the capability walk (library/device/pci/pci.zig). The generic PCI
|
||||||
// mechanics every leaf PCI driver used to re-derive inline. Imports runtime (device
|
// mechanics every leaf PCI driver used to re-derive inline. Imports the driver (device
|
||||||
// access) + mmio + the pci-class data module (config-space layout constants).
|
// access) client + mmio + the pci-class data module (config-space layout constants).
|
||||||
const pci_module = b.addModule("pci", .{
|
const pci_module = b.addModule("pci", .{
|
||||||
.root_source_file = b.path("library/device/pci/pci.zig"),
|
.root_source_file = b.path("library/device/pci/pci.zig"),
|
||||||
.imports = &.{
|
.imports = &.{
|
||||||
.{ .name = "runtime", .module = runtime_module },
|
.{ .name = "driver", .module = driver_module },
|
||||||
.{ .name = "mmio", .module = mmio_module },
|
.{ .name = "mmio", .module = mmio_module },
|
||||||
.{ .name = "pci-class", .module = pci_class_module },
|
.{ .name = "pci-class", .module = pci_class_module },
|
||||||
},
|
},
|
||||||
@@ -567,12 +555,13 @@ pub fn build(b: *std.Build) void {
|
|||||||
|
|
||||||
// The USB class-driver transfer client (library/device/usb/usb.zig): open a device on
|
// The USB class-driver transfer client (library/device/usb/usb.zig): open a device on
|
||||||
// the xHCI bus and drive it (control / interrupt / bulk). Bus-family logic a class
|
// the xHCI bus and drive it (control / interrupt / bulk). Bus-family logic a class
|
||||||
// driver imports directly — no longer funnelled through runtime. Re-exports usb-abi /
|
// driver imports directly — over ipc + time. Re-exports usb-abi / usb-ids as
|
||||||
// usb-ids as usb.abi / usb.ids for a single USB import.
|
// usb.abi / usb.ids for a single USB import.
|
||||||
const usb_module = b.addModule("usb", .{
|
const usb_module = b.addModule("usb", .{
|
||||||
.root_source_file = b.path("library/device/usb/usb.zig"),
|
.root_source_file = b.path("library/device/usb/usb.zig"),
|
||||||
.imports = &.{
|
.imports = &.{
|
||||||
.{ .name = "runtime", .module = runtime_module },
|
.{ .name = "ipc", .module = ipc_module },
|
||||||
|
.{ .name = "time", .module = time_module },
|
||||||
.{ .name = "usb-transfer-protocol", .module = usb_transfer_protocol_module },
|
.{ .name = "usb-transfer-protocol", .module = usb_transfer_protocol_module },
|
||||||
.{ .name = "usb-abi", .module = usb_abi_module },
|
.{ .name = "usb-abi", .module = usb_abi_module },
|
||||||
.{ .name = "usb-ids", .module = usb_ids_module },
|
.{ .name = "usb-ids", .module = usb_ids_module },
|
||||||
@@ -645,10 +634,35 @@ pub fn build(b: *std.Build) void {
|
|||||||
b.getInstallStep().dependOn(&kernel_install.step);
|
b.getInstallStep().dependOn(&kernel_install.step);
|
||||||
|
|
||||||
// --- init: the first user-space program (a system service) ---
|
// --- init: the first user-space program (a system service) ---
|
||||||
|
// The default module set every user binary can import directly: the library/kernel
|
||||||
|
// concern modules, the device/service clients, mmio, the keyboard layouts, the ACPI id
|
||||||
|
// registry, and the compatibility `runtime` shim (retired once every consumer has migrated
|
||||||
|
// off it in C2/C5). Per-binary extras are added with programModule(exe).addImport.
|
||||||
|
const default_imports = [_]std.Build.Module.Import{
|
||||||
|
.{ .name = "runtime", .module = runtime_module },
|
||||||
|
.{ .name = "mmio", .module = mmio_module },
|
||||||
|
.{ .name = "xkeyboard-config", .module = xkeyboard_config_module },
|
||||||
|
.{ .name = "acpi-ids", .module = acpi_ids_module },
|
||||||
|
.{ .name = "system-call", .module = system_call_module },
|
||||||
|
.{ .name = "ipc", .module = ipc_module },
|
||||||
|
.{ .name = "memory", .module = memory_module },
|
||||||
|
.{ .name = "process", .module = process_module },
|
||||||
|
.{ .name = "thread", .module = thread_module },
|
||||||
|
.{ .name = "time", .module = time_module },
|
||||||
|
.{ .name = "logging", .module = logging_module },
|
||||||
|
.{ .name = "file-system", .module = file_system_module },
|
||||||
|
.{ .name = "service", .module = service_module },
|
||||||
|
.{ .name = "start", .module = start_module },
|
||||||
|
.{ .name = "driver", .module = driver_module },
|
||||||
|
.{ .name = "block", .module = block_client_module },
|
||||||
|
.{ .name = "display", .module = display_client_module },
|
||||||
|
.{ .name = "input", .module = input_client_module },
|
||||||
|
};
|
||||||
|
|
||||||
// Built by the shared user-binary recipe (see addUserBinary): freestanding,
|
// Built by the shared user-binary recipe (see addUserBinary): freestanding,
|
||||||
// linked into the kernel's user region against the `runtime` runtime library, and
|
// linked into the kernel's user region against the library/kernel modules, and
|
||||||
// started in ring 3 by the kernel's user-ELF loader.
|
// started in ring 3 by the kernel's user-ELF loader.
|
||||||
const init_exe = addUserBinary(b, kernel_target, runtime_module, mmio_module, xkeyboard_config_module, acpi_ids_module, "init", "system/services/init/init.zig");
|
const init_exe = addUserBinary(b, kernel_target, &default_imports, runtime_module, "init", "system/services/init/init.zig");
|
||||||
programModule(init_exe).addImport("power-protocol", power_protocol_module);
|
programModule(init_exe).addImport("power-protocol", power_protocol_module);
|
||||||
// init reads the same `serial` flag the kernel does: its liveness heartbeat is a
|
// 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
|
// serial/test-build diagnostic (the QEMU harness's init tests assert on it, and
|
||||||
@@ -664,13 +678,13 @@ pub fn build(b: *std.Build) void {
|
|||||||
// Each is built by the same user-binary recipe and laid out at its FHS path on
|
// Each is built by the same user-binary recipe and laid out at its FHS path on
|
||||||
// the boot volume (see `bundled` below). The EFI loader walks the tree at boot
|
// the boot volume (see `bundled` below). The EFI loader walks the tree at boot
|
||||||
// and hands the kernel an in-RAM initial_ramdisk of it (system/initial-ramdisk.zig).
|
// and hands the kernel an in-RAM initial_ramdisk of it (system/initial-ramdisk.zig).
|
||||||
const vfstest_exe = addUserBinary(b, kernel_target, runtime_module, mmio_module, xkeyboard_config_module, acpi_ids_module, "vfs-test", "system/services/vfs-test/vfs-test.zig");
|
const vfstest_exe = addUserBinary(b, kernel_target, &default_imports, runtime_module, "vfs-test", "system/services/vfs-test/vfs-test.zig");
|
||||||
const ps2_bus_exe = addUserBinary(b, kernel_target, runtime_module, mmio_module, xkeyboard_config_module, acpi_ids_module, "ps2-bus", "system/drivers/ps2-bus/ps2-bus.zig");
|
const ps2_bus_exe = addUserBinary(b, kernel_target, &default_imports, runtime_module, "ps2-bus", "system/drivers/ps2-bus/ps2-bus.zig");
|
||||||
const ps2_keyboard_exe = addUserBinary(b, kernel_target, runtime_module, mmio_module, xkeyboard_config_module, acpi_ids_module, "ps2-keyboard", "system/drivers/ps2-bus/keyboard.zig");
|
const ps2_keyboard_exe = addUserBinary(b, kernel_target, &default_imports, runtime_module, "ps2-keyboard", "system/drivers/ps2-bus/keyboard.zig");
|
||||||
programModule(ps2_keyboard_exe).addImport("input-protocol", input_protocol_module);
|
programModule(ps2_keyboard_exe).addImport("input-protocol", input_protocol_module);
|
||||||
const ps2_mouse_exe = addUserBinary(b, kernel_target, runtime_module, mmio_module, xkeyboard_config_module, acpi_ids_module, "ps2-mouse", "system/drivers/ps2-bus/mouse.zig");
|
const ps2_mouse_exe = addUserBinary(b, kernel_target, &default_imports, runtime_module, "ps2-mouse", "system/drivers/ps2-bus/mouse.zig");
|
||||||
programModule(ps2_mouse_exe).addImport("input-protocol", input_protocol_module);
|
programModule(ps2_mouse_exe).addImport("input-protocol", input_protocol_module);
|
||||||
const usb_xhci_bus_exe = addUserBinary(b, kernel_target, runtime_module, mmio_module, xkeyboard_config_module, acpi_ids_module, "usb-xhci-bus", "system/drivers/usb-xhci-bus/usb-xhci-bus.zig");
|
const usb_xhci_bus_exe = addUserBinary(b, kernel_target, &default_imports, runtime_module, "usb-xhci-bus", "system/drivers/usb-xhci-bus/usb-xhci-bus.zig");
|
||||||
programModule(usb_xhci_bus_exe).addImport("device-manager-protocol", device_manager_protocol_module);
|
programModule(usb_xhci_bus_exe).addImport("device-manager-protocol", device_manager_protocol_module);
|
||||||
// The xHCI bus driver builds chapter-9 requests and decodes descriptors from
|
// The xHCI bus driver builds chapter-9 requests and decodes descriptors from
|
||||||
// usb-abi, and reports each interface's (class,subclass,protocol) identity via
|
// usb-abi, and reports each interface's (class,subclass,protocol) identity via
|
||||||
@@ -681,46 +695,46 @@ pub fn build(b: *std.Build) void {
|
|||||||
// The USB HID class drivers: keyboard and mouse. They own no hardware — each
|
// The USB HID class drivers: keyboard and mouse. They own no hardware — each
|
||||||
// opens its device through runtime.usb (the transfer protocol) and publishes to
|
// opens its device through runtime.usb (the transfer protocol) and publishes to
|
||||||
// the input service. They build chapter-9 class requests from usb-abi.
|
// the input service. They build chapter-9 class requests from usb-abi.
|
||||||
const usb_hid_keyboard_exe = addUserBinary(b, kernel_target, runtime_module, mmio_module, xkeyboard_config_module, acpi_ids_module, "usb-hid-keyboard", "system/drivers/usb-hid/keyboard.zig");
|
const usb_hid_keyboard_exe = addUserBinary(b, kernel_target, &default_imports, runtime_module, "usb-hid-keyboard", "system/drivers/usb-hid/keyboard.zig");
|
||||||
programModule(usb_hid_keyboard_exe).addImport("usb", usb_module);
|
programModule(usb_hid_keyboard_exe).addImport("usb", usb_module);
|
||||||
programModule(usb_hid_keyboard_exe).addImport("usb-abi", usb_abi_module);
|
programModule(usb_hid_keyboard_exe).addImport("usb-abi", usb_abi_module);
|
||||||
programModule(usb_hid_keyboard_exe).addImport("input-protocol", input_protocol_module);
|
programModule(usb_hid_keyboard_exe).addImport("input-protocol", input_protocol_module);
|
||||||
const usb_hid_mouse_exe = addUserBinary(b, kernel_target, runtime_module, mmio_module, xkeyboard_config_module, acpi_ids_module, "usb-hid-mouse", "system/drivers/usb-hid/mouse.zig");
|
const usb_hid_mouse_exe = addUserBinary(b, kernel_target, &default_imports, runtime_module, "usb-hid-mouse", "system/drivers/usb-hid/mouse.zig");
|
||||||
programModule(usb_hid_mouse_exe).addImport("usb", usb_module);
|
programModule(usb_hid_mouse_exe).addImport("usb", usb_module);
|
||||||
programModule(usb_hid_mouse_exe).addImport("usb-abi", usb_abi_module);
|
programModule(usb_hid_mouse_exe).addImport("usb-abi", usb_abi_module);
|
||||||
programModule(usb_hid_mouse_exe).addImport("input-protocol", input_protocol_module);
|
programModule(usb_hid_mouse_exe).addImport("input-protocol", input_protocol_module);
|
||||||
// The USB mass-storage class driver: opens its device via runtime.usb, drives it
|
// The USB mass-storage class driver: opens its device via runtime.usb, drives it
|
||||||
// with Bulk-Only Transport + SCSI, and serves the block protocol under `.block`.
|
// with Bulk-Only Transport + SCSI, and serves the block protocol under `.block`.
|
||||||
const usb_storage_exe = addUserBinary(b, kernel_target, runtime_module, mmio_module, xkeyboard_config_module, acpi_ids_module, "usb-storage", "system/drivers/usb-storage/usb-storage.zig");
|
const usb_storage_exe = addUserBinary(b, kernel_target, &default_imports, runtime_module, "usb-storage", "system/drivers/usb-storage/usb-storage.zig");
|
||||||
programModule(usb_storage_exe).addImport("usb", usb_module);
|
programModule(usb_storage_exe).addImport("usb", usb_module);
|
||||||
programModule(usb_storage_exe).addImport("block-protocol", block_protocol_module);
|
programModule(usb_storage_exe).addImport("block-protocol", block_protocol_module);
|
||||||
// The FAT filesystem server: mounts the block device and serves it into the VFS
|
// The FAT filesystem server: mounts the block device and serves it into the VFS
|
||||||
// at /mnt/usb. Its engine (engine.zig / on-disk.zig) is imported relatively.
|
// at /mnt/usb. Its engine (engine.zig / on-disk.zig) is imported relatively.
|
||||||
const fat_exe = addUserBinary(b, kernel_target, runtime_module, mmio_module, xkeyboard_config_module, acpi_ids_module, "fat", "system/services/fat/fat.zig");
|
const fat_exe = addUserBinary(b, kernel_target, &default_imports, runtime_module, "fat", "system/services/fat/fat.zig");
|
||||||
programModule(fat_exe).addImport("vfs-protocol", vfs_protocol_module);
|
programModule(fat_exe).addImport("vfs-protocol", vfs_protocol_module);
|
||||||
// Threaded: the display runs a mouse-listener thread alongside its compositor loop
|
// Threaded: the display runs a mouse-listener thread alongside its compositor loop
|
||||||
// (docs/threading.md, docs/display.md), so it opts into real atomics/TLS.
|
// (docs/threading.md, docs/display.md), so it opts into real atomics/TLS.
|
||||||
const display_exe = addThreadedUserBinary(b, kernel_target, runtime_module, mmio_module, xkeyboard_config_module, acpi_ids_module, "display", "system/services/display/display.zig");
|
const display_exe = addThreadedUserBinary(b, kernel_target, &default_imports, runtime_module, "display", "system/services/display/display.zig");
|
||||||
programModule(display_exe).addImport("display-protocol", display_protocol_module);
|
programModule(display_exe).addImport("display-protocol", display_protocol_module);
|
||||||
programModule(display_exe).addImport("scanout-protocol", scanout_protocol_module);
|
programModule(display_exe).addImport("scanout-protocol", scanout_protocol_module);
|
||||||
const display_demo_exe = addUserBinary(b, kernel_target, runtime_module, mmio_module, xkeyboard_config_module, acpi_ids_module, "display-demo", "system/services/display-demo/display-demo.zig");
|
const display_demo_exe = addUserBinary(b, kernel_target, &default_imports, runtime_module, "display-demo", "system/services/display-demo/display-demo.zig");
|
||||||
const virtio_gpu_exe = addUserBinary(b, kernel_target, runtime_module, mmio_module, xkeyboard_config_module, acpi_ids_module, "virtio-gpu", "system/drivers/virtio-gpu/virtio-gpu.zig");
|
const virtio_gpu_exe = addUserBinary(b, kernel_target, &default_imports, runtime_module, "virtio-gpu", "system/drivers/virtio-gpu/virtio-gpu.zig");
|
||||||
programModule(virtio_gpu_exe).addImport("pci", pci_module); // library/device/pci — the claimed-function view
|
programModule(virtio_gpu_exe).addImport("pci", pci_module); // library/device/pci — the claimed-function view
|
||||||
programModule(virtio_gpu_exe).addImport("display-protocol", display_protocol_module);
|
programModule(virtio_gpu_exe).addImport("display-protocol", display_protocol_module);
|
||||||
programModule(virtio_gpu_exe).addImport("scanout-protocol", scanout_protocol_module);
|
programModule(virtio_gpu_exe).addImport("scanout-protocol", scanout_protocol_module);
|
||||||
const shared_memory_server_exe = addUserBinary(b, kernel_target, runtime_module, mmio_module, xkeyboard_config_module, acpi_ids_module, "shared-memory-server", "system/services/shared-memory-server/shared-memory-server.zig");
|
const shared_memory_server_exe = addUserBinary(b, kernel_target, &default_imports, runtime_module, "shared-memory-server", "system/services/shared-memory-server/shared-memory-server.zig");
|
||||||
const shared_memory_client_exe = addUserBinary(b, kernel_target, runtime_module, mmio_module, xkeyboard_config_module, acpi_ids_module, "shared-memory-client", "system/services/shared-memory-client/shared-memory-client.zig");
|
const shared_memory_client_exe = addUserBinary(b, kernel_target, &default_imports, runtime_module, "shared-memory-client", "system/services/shared-memory-client/shared-memory-client.zig");
|
||||||
const fat_test_exe = addUserBinary(b, kernel_target, runtime_module, mmio_module, xkeyboard_config_module, acpi_ids_module, "fat-test", "system/services/fat/fat-test.zig");
|
const fat_test_exe = addUserBinary(b, kernel_target, &default_imports, runtime_module, "fat-test", "system/services/fat/fat-test.zig");
|
||||||
const pci_bus_exe = addUserBinary(b, kernel_target, runtime_module, mmio_module, xkeyboard_config_module, acpi_ids_module, "pci-bus", "system/drivers/pci-bus/pci-bus.zig");
|
const pci_bus_exe = addUserBinary(b, kernel_target, &default_imports, runtime_module, "pci-bus", "system/drivers/pci-bus/pci-bus.zig");
|
||||||
programModule(pci_bus_exe).addImport("device-manager-protocol", device_manager_protocol_module);
|
programModule(pci_bus_exe).addImport("device-manager-protocol", device_manager_protocol_module);
|
||||||
// The PCI bus driver decodes each function's class triple to human names in its
|
// The PCI bus driver decodes each function's class triple to human names in its
|
||||||
// boot log (class/subclass/prog-IF), so pull in the shared pci-class reference.
|
// boot log (class/subclass/prog-IF), so pull in the shared pci-class reference.
|
||||||
programModule(pci_bus_exe).addImport("pci-class", pci_class_module);
|
programModule(pci_bus_exe).addImport("pci-class", pci_class_module);
|
||||||
// A test fixture, not a real driver: hellos to the device manager, then faults —
|
// A test fixture, not a real driver: hellos to the device manager, then faults —
|
||||||
// what the driver-restart scenario drives the crash-loop cap with.
|
// what the driver-restart scenario drives the crash-loop cap with.
|
||||||
const crash_test_exe = addUserBinary(b, kernel_target, runtime_module, mmio_module, xkeyboard_config_module, acpi_ids_module, "crash-test", "system/services/crash-test/crash-test.zig");
|
const crash_test_exe = addUserBinary(b, kernel_target, &default_imports, runtime_module, "crash-test", "system/services/crash-test/crash-test.zig");
|
||||||
programModule(crash_test_exe).addImport("device-manager-protocol", device_manager_protocol_module);
|
programModule(crash_test_exe).addImport("device-manager-protocol", device_manager_protocol_module);
|
||||||
const device_list_exe = addUserBinary(b, kernel_target, runtime_module, mmio_module, xkeyboard_config_module, acpi_ids_module, "device-list", "system/services/device-list/device-list.zig");
|
const device_list_exe = addUserBinary(b, kernel_target, &default_imports, runtime_module, "device-list", "system/services/device-list/device-list.zig");
|
||||||
programModule(device_list_exe).addImport("device-manager-protocol", device_manager_protocol_module);
|
programModule(device_list_exe).addImport("device-manager-protocol", device_manager_protocol_module);
|
||||||
// The discovery service: one swappable process per firmware
|
// The discovery service: one swappable process per firmware
|
||||||
// (docs/discovery.md), bundled under the neutral ramdisk name
|
// (docs/discovery.md), bundled under the neutral ramdisk name
|
||||||
@@ -735,11 +749,11 @@ pub fn build(b: *std.Build) void {
|
|||||||
.acpi => "system/services/acpi/acpi.zig",
|
.acpi => "system/services/acpi/acpi.zig",
|
||||||
.fdt => "system/services/fdt/fdt.zig",
|
.fdt => "system/services/fdt/fdt.zig",
|
||||||
};
|
};
|
||||||
const discovery_exe = addUserBinary(b, kernel_target, runtime_module, mmio_module, xkeyboard_config_module, acpi_ids_module, "discovery", discovery_source);
|
const discovery_exe = addUserBinary(b, kernel_target, &default_imports, runtime_module, "discovery", discovery_source);
|
||||||
if (discovery == .acpi) programModule(discovery_exe).addImport("aml", aml_module);
|
if (discovery == .acpi) programModule(discovery_exe).addImport("aml", aml_module);
|
||||||
if (discovery == .acpi) programModule(discovery_exe).addImport("device-manager-protocol", device_manager_protocol_module);
|
if (discovery == .acpi) programModule(discovery_exe).addImport("device-manager-protocol", device_manager_protocol_module);
|
||||||
if (discovery == .acpi) programModule(discovery_exe).addImport("power-protocol", power_protocol_module);
|
if (discovery == .acpi) programModule(discovery_exe).addImport("power-protocol", power_protocol_module);
|
||||||
const device_manager_exe = addUserBinary(b, kernel_target, runtime_module, mmio_module, xkeyboard_config_module, acpi_ids_module, "device-manager", "system/services/device-manager/device-manager.zig");
|
const device_manager_exe = addUserBinary(b, kernel_target, &default_imports, runtime_module, "device-manager", "system/services/device-manager/device-manager.zig");
|
||||||
// Names the xHCI PCI class triple from the shared taxonomy instead of a bare 0x0C0330.
|
// Names the xHCI PCI class triple from the shared taxonomy instead of a bare 0x0C0330.
|
||||||
programModule(device_manager_exe).addImport("pci-class", pci_class_module);
|
programModule(device_manager_exe).addImport("pci-class", pci_class_module);
|
||||||
programModule(device_manager_exe).addImport("device-manager-protocol", device_manager_protocol_module);
|
programModule(device_manager_exe).addImport("device-manager-protocol", device_manager_protocol_module);
|
||||||
@@ -748,16 +762,16 @@ pub fn build(b: *std.Build) void {
|
|||||||
programModule(device_manager_exe).addImport("usb-ids", usb_ids_module);
|
programModule(device_manager_exe).addImport("usb-ids", usb_ids_module);
|
||||||
// The input service and its exercisers: the fan-out server, a hardware-free synthetic
|
// The input service and its exercisers: the fan-out server, a hardware-free synthetic
|
||||||
// source, and a subscriber that doubles as the `input` test's oracle. See docs/input.md.
|
// source, and a subscriber that doubles as the `input` test's oracle. See docs/input.md.
|
||||||
const input_exe = addUserBinary(b, kernel_target, runtime_module, mmio_module, xkeyboard_config_module, acpi_ids_module, "input", "system/services/input/input.zig");
|
const input_exe = addUserBinary(b, kernel_target, &default_imports, runtime_module, "input", "system/services/input/input.zig");
|
||||||
programModule(input_exe).addImport("input-protocol", input_protocol_module);
|
programModule(input_exe).addImport("input-protocol", input_protocol_module);
|
||||||
const input_source_exe = addUserBinary(b, kernel_target, runtime_module, mmio_module, xkeyboard_config_module, acpi_ids_module, "input-source", "system/services/input-source/input-source.zig");
|
const input_source_exe = addUserBinary(b, kernel_target, &default_imports, runtime_module, "input-source", "system/services/input-source/input-source.zig");
|
||||||
const input_test_exe = addUserBinary(b, kernel_target, runtime_module, mmio_module, xkeyboard_config_module, acpi_ids_module, "input-test", "system/services/input-test/input-test.zig");
|
const input_test_exe = addUserBinary(b, kernel_target, &default_imports, runtime_module, "input-test", "system/services/input-test/input-test.zig");
|
||||||
const args_echo_exe = addUserBinary(b, kernel_target, runtime_module, mmio_module, xkeyboard_config_module, acpi_ids_module, "args-echo", "system/services/args-echo/args-echo.zig");
|
const args_echo_exe = addUserBinary(b, kernel_target, &default_imports, runtime_module, "args-echo", "system/services/args-echo/args-echo.zig");
|
||||||
const process_test_exe = addUserBinary(b, kernel_target, runtime_module, mmio_module, xkeyboard_config_module, acpi_ids_module, "process-test", "system/services/process-test/process-test.zig");
|
const process_test_exe = addUserBinary(b, kernel_target, &default_imports, runtime_module, "process-test", "system/services/process-test/process-test.zig");
|
||||||
const logger_exe = addUserBinary(b, kernel_target, runtime_module, mmio_module, xkeyboard_config_module, acpi_ids_module, "logger", "system/services/logger/logger.zig");
|
const logger_exe = addUserBinary(b, kernel_target, &default_imports, runtime_module, "logger", "system/services/logger/logger.zig");
|
||||||
// The first multi-threaded binary: exercises runtime.Thread over the thread ABI
|
// The first multi-threaded binary: exercises runtime.Thread over the thread ABI
|
||||||
// (docs/threading.md). Built threaded so its shared-memory poll is real.
|
// (docs/threading.md). Built threaded so its shared-memory poll is real.
|
||||||
const thread_test_exe = addThreadedUserBinary(b, kernel_target, runtime_module, mmio_module, xkeyboard_config_module, acpi_ids_module, "thread-test", "system/services/thread-test/thread-test.zig");
|
const thread_test_exe = addThreadedUserBinary(b, kernel_target, &default_imports, runtime_module, "thread-test", "system/services/thread-test/thread-test.zig");
|
||||||
|
|
||||||
// Every user binary and its FHS home on the boot volume. There is no packed
|
// Every user binary and its FHS home on the boot volume. There is no packed
|
||||||
// ramdisk artifact any more: make-fat-image.py lays each binary out at this
|
// ramdisk artifact any more: make-fat-image.py lays each binary out at this
|
||||||
|
|||||||
@@ -7,13 +7,12 @@
|
|||||||
//! The bus enumerator's view — probing arbitrary, not-yet-claimed functions and sizing
|
//! The bus enumerator's view — probing arbitrary, not-yet-claimed functions and sizing
|
||||||
//! their BARs — is a different mechanism and lives in the pci-bus driver. The pure
|
//! their BARs — is a different mechanism and lives in the pci-bus driver. The pure
|
||||||
//! config-space layout both need (offsets, BAR bit fields) is named once in the `pci-class`
|
//! config-space layout both need (offsets, BAR bit fields) is named once in the `pci-class`
|
||||||
//! data module; this logic module adds the parts that need `mmio` + `runtime.device`.
|
//! data module; this logic module adds the parts that need `mmio` + the `driver` client.
|
||||||
|
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const runtime = @import("runtime");
|
|
||||||
const mmio = @import("mmio");
|
const mmio = @import("mmio");
|
||||||
const pci_class = @import("pci-class");
|
const pci_class = @import("pci-class");
|
||||||
const device = runtime.device;
|
const device = @import("driver");
|
||||||
|
|
||||||
/// A claimed PCI function whose configuration space is mapped (resource 0). `descriptor`
|
/// A claimed PCI function whose configuration space is mapped (resource 0). `descriptor`
|
||||||
/// must outlive the Function — the driver's `device.enumerate` buffer does, for the whole
|
/// must outlive the Function — the driver's `device.enumerate` buffer does, for the whole
|
||||||
|
|||||||
@@ -16,9 +16,8 @@
|
|||||||
//! the service harness drops buffered-message payloads — see service.zig).
|
//! the service harness drops buffered-message payloads — see service.zig).
|
||||||
|
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const runtime = @import("runtime");
|
const ipc = @import("ipc");
|
||||||
const ipc = runtime.ipc;
|
const time = @import("time");
|
||||||
const system = runtime.system;
|
|
||||||
const usb_transfer_protocol = @import("usb-transfer-protocol");
|
const usb_transfer_protocol = @import("usb-transfer-protocol");
|
||||||
|
|
||||||
/// The USB chapter-9 wire ABI and the class taxonomy, re-exported so a class driver reaches
|
/// The USB chapter-9 wire ABI and the class taxonomy, re-exported so a class driver reaches
|
||||||
@@ -125,7 +124,7 @@ pub fn open(device_id: u64) ?Device {
|
|||||||
var attempts: usize = 0;
|
var attempts: usize = 0;
|
||||||
const bus = while (attempts < 100) : (attempts += 1) {
|
const bus = while (attempts < 100) : (attempts += 1) {
|
||||||
if (ipc.lookup(.usb_bus)) |handle| break handle;
|
if (ipc.lookup(.usb_bus)) |handle| break handle;
|
||||||
system.sleep(20);
|
time.sleepMillis(20);
|
||||||
} else return null;
|
} else return null;
|
||||||
|
|
||||||
const endpoint = ipc.createIpcEndpoint() orelse return null;
|
const endpoint = ipc.createIpcEndpoint() orelse return null;
|
||||||
|
|||||||
@@ -12,13 +12,13 @@
|
|||||||
//!
|
//!
|
||||||
//! It takes over the framebuffer feature that was setup during system boot.
|
//! It takes over the framebuffer feature that was setup during system boot.
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const runtime = @import("runtime");
|
const device = @import("driver");
|
||||||
|
const ipc = @import("ipc");
|
||||||
|
const process = @import("process");
|
||||||
|
const service = @import("service");
|
||||||
|
const device_manager = @import("driver");
|
||||||
|
const logging = @import("logging");
|
||||||
const mmio = @import("mmio");
|
const mmio = @import("mmio");
|
||||||
const device = runtime.device;
|
|
||||||
const dma = runtime.dma;
|
|
||||||
const shared_memory = runtime.shared_memory;
|
|
||||||
const system = runtime.system;
|
|
||||||
const ipc = runtime.ipc;
|
|
||||||
const display_protocol = @import("display-protocol");
|
const display_protocol = @import("display-protocol");
|
||||||
const scanout_protocol = @import("scanout-protocol");
|
const scanout_protocol = @import("scanout-protocol");
|
||||||
var device_id: u64 = 0;
|
var device_id: u64 = 0;
|
||||||
@@ -29,7 +29,7 @@ fn initialise(endpoint: ipc.Handle) bool {
|
|||||||
// and serve its display engine; we report no children). Best-effort: without a
|
// and serve its display engine; we report no children). Best-effort: without a
|
||||||
// manager the driver still runs standalone; when present, the manager marks us
|
// manager the driver still runs standalone; when present, the manager marks us
|
||||||
// up before the hello deadline and restarts us if we die.
|
// up before the hello deadline and restarts us if we die.
|
||||||
_ = runtime.device_manager.hello(.device, device_id);
|
_ = device_manager.hello(.device, device_id);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -42,16 +42,16 @@ fn onMessage(message: []const u8, reply: []u8, sender: u32, capability: ?ipc.Han
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn main(init: runtime.process.Init) void {
|
pub fn main(init: process.Init) void {
|
||||||
const argument = init.arguments.get(1) orelse {
|
const argument = init.arguments.get(1) orelse {
|
||||||
_ = system.write("display: missing device id (argv[1])\n");
|
_ = logging.write("display: missing device id (argv[1])\n");
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
device_id = std.fmt.parseInt(u64, argument, 10) catch {
|
device_id = std.fmt.parseInt(u64, argument, 10) catch {
|
||||||
std.log.info("malformed device id '{s}'", .{argument});
|
std.log.info("malformed device id '{s}'", .{argument});
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
runtime.service.run(256, .{
|
service.run(256, .{
|
||||||
.service = .scanout,
|
.service = .scanout,
|
||||||
.init = initialise,
|
.init = initialise,
|
||||||
.on_message = onMessage,
|
.on_message = onMessage,
|
||||||
|
|||||||
@@ -1,12 +1,11 @@
|
|||||||
//! /system/drivers/display/intel-integrated - the intel 985 family display engine driver.
|
//! /system/drivers/display/intel-integrated - the intel 985 family display engine driver.
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const runtime = @import("runtime");
|
const device = @import("driver");
|
||||||
|
const ipc = @import("ipc");
|
||||||
|
const process = @import("process");
|
||||||
|
const service = @import("service");
|
||||||
|
const logging = @import("logging");
|
||||||
const mmio = @import("mmio");
|
const mmio = @import("mmio");
|
||||||
const device = runtime.device;
|
|
||||||
const dma = runtime.dma;
|
|
||||||
const shared_memory = runtime.shared_memory;
|
|
||||||
const system = runtime.system;
|
|
||||||
const ipc = runtime.ipc;
|
|
||||||
const display_protocol = @import("display-protocol");
|
const display_protocol = @import("display-protocol");
|
||||||
const scanout_protocol = @import("scanout-protocol");
|
const scanout_protocol = @import("scanout-protocol");
|
||||||
const device_manager_protocol = @import("device-manager-protocol");
|
const device_manager_protocol = @import("device-manager-protocol");
|
||||||
@@ -53,16 +52,16 @@ fn onMessage(message: []const u8, reply: []u8, sender: u32, capability: ?ipc.Han
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn main(init: runtime.process.Init) void {
|
pub fn main(init: process.Init) void {
|
||||||
const argument = init.arguments.get(1) orelse {
|
const argument = init.arguments.get(1) orelse {
|
||||||
_ = system.write("display/intel-985: missing device id (argv[1])\n");
|
_ = logging.write("display/intel-985: missing device id (argv[1])\n");
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
device_id = std.fmt.parseInt(u64, argument, 10) catch {
|
device_id = std.fmt.parseInt(u64, argument, 10) catch {
|
||||||
std.log.info("malformed device id '{s}'", .{argument});
|
std.log.info("malformed device id '{s}'", .{argument});
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
runtime.service.run(256, .{
|
service.run(256, .{
|
||||||
.service = .scanout,
|
.service = .scanout,
|
||||||
.init = initialise,
|
.init = initialise,
|
||||||
.on_message = onMessage,
|
.on_message = onMessage,
|
||||||
|
|||||||
@@ -11,9 +11,14 @@
|
|||||||
//! the kernel walk's retirement (M19.3), build on this proven-equivalent scan.
|
//! the kernel walk's retirement (M19.3), build on this proven-equivalent scan.
|
||||||
|
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const runtime = @import("runtime");
|
const device = @import("driver");
|
||||||
|
const ipc = @import("ipc");
|
||||||
|
const process = @import("process");
|
||||||
|
const service = @import("service");
|
||||||
|
const device_manager = @import("driver");
|
||||||
|
const memory = @import("memory");
|
||||||
|
const logging = @import("logging");
|
||||||
const device_manager_protocol = @import("device-manager-protocol");
|
const device_manager_protocol = @import("device-manager-protocol");
|
||||||
const device = runtime.device;
|
|
||||||
const pci_class = @import("pci-class");
|
const pci_class = @import("pci-class");
|
||||||
|
|
||||||
/// Log a discovered function with its (class / subclass / prog-IF) triple decoded
|
/// Log a discovered function with its (class / subclass / prog-IF) triple decoded
|
||||||
@@ -29,7 +34,7 @@ fn logFunction(bus: u64, dev: u64, function: u64, class_triple: u32) void {
|
|||||||
std.fmt.bufPrint(&line, "/system/drivers/pci-bus: {d}:{d}.{d} class 0x{x:0>2} ({s}) subclass 0x{x:0>2} ({s}) progif 0x{x:0>2} ({s})\n", .{ bus, dev, function, cc.base, pci_class.className(cc.base), cc.subclass, pci_class.subclassName(cc.base, cc.subclass), cc.prog_if, pif }) catch return
|
std.fmt.bufPrint(&line, "/system/drivers/pci-bus: {d}:{d}.{d} class 0x{x:0>2} ({s}) subclass 0x{x:0>2} ({s}) progif 0x{x:0>2} ({s})\n", .{ bus, dev, function, cc.base, pci_class.className(cc.base), cc.subclass, pci_class.subclassName(cc.base, cc.subclass), cc.prog_if, pif }) catch return
|
||||||
else
|
else
|
||||||
std.fmt.bufPrint(&line, "/system/drivers/pci-bus: {d}:{d}.{d} class 0x{x:0>2} ({s}) subclass 0x{x:0>2} ({s}) progif 0x{x:0>2}\n", .{ bus, dev, function, cc.base, pci_class.className(cc.base), cc.subclass, pci_class.subclassName(cc.base, cc.subclass), cc.prog_if }) catch return;
|
std.fmt.bufPrint(&line, "/system/drivers/pci-bus: {d}:{d}.{d} class 0x{x:0>2} ({s}) subclass 0x{x:0>2} ({s}) progif 0x{x:0>2}\n", .{ bus, dev, function, cc.base, pci_class.className(cc.base), cc.subclass, pci_class.subclassName(cc.base, cc.subclass), cc.prog_if }) catch return;
|
||||||
_ = runtime.system.write(text);
|
_ = logging.write(text);
|
||||||
}
|
}
|
||||||
|
|
||||||
var bridge_id: u64 = device_manager_protocol.no_device;
|
var bridge_id: u64 = device_manager_protocol.no_device;
|
||||||
@@ -37,7 +42,7 @@ var ecam_base: usize = 0;
|
|||||||
var ecam_physical: u64 = 0;
|
var ecam_physical: u64 = 0;
|
||||||
var start_bus: u64 = 0;
|
var start_bus: u64 = 0;
|
||||||
var bus_count: u64 = 0;
|
var bus_count: u64 = 0;
|
||||||
var manager_handle: runtime.ipc.Handle = 0;
|
var manager_handle: ipc.Handle = 0;
|
||||||
|
|
||||||
/// One aligned 32-bit read from a function's configuration space.
|
/// One aligned 32-bit read from a function's configuration space.
|
||||||
fn configRead(bus: u64, dev: u64, function: u64, offset: u64) u32 {
|
fn configRead(bus: u64, dev: u64, function: u64, offset: u64) u32 {
|
||||||
@@ -66,14 +71,14 @@ fn configWrite16(bus: u64, dev: u64, function: u64, offset: u64, value: u16) voi
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Claim the bridge, map the ECAM, hello the manager, then scan.
|
/// Claim the bridge, map the ECAM, hello the manager, then scan.
|
||||||
fn initialise(endpoint: runtime.ipc.Handle) bool {
|
fn initialise(endpoint: ipc.Handle) bool {
|
||||||
_ = endpoint;
|
_ = endpoint;
|
||||||
if (!device.claim(bridge_id)) {
|
if (!device.claim(bridge_id)) {
|
||||||
std.log.info("unable to claim bridge device {d}", .{bridge_id});
|
std.log.info("unable to claim bridge device {d}", .{bridge_id});
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
const buffer = runtime.allocator().alloc(device.DeviceDescriptor, 64) catch {
|
const buffer = memory.allocator().alloc(device.DeviceDescriptor, 64) catch {
|
||||||
_ = runtime.system.write("/system/drivers/pci-bus: out of memory\n");
|
_ = logging.write("/system/drivers/pci-bus: out of memory\n");
|
||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
const total = device.enumerate(buffer);
|
const total = device.enumerate(buffer);
|
||||||
@@ -86,27 +91,27 @@ fn initialise(endpoint: runtime.ipc.Handle) bool {
|
|||||||
// Resource 0 is the ECAM window (1 MiB of config space per bus); the bus
|
// Resource 0 is the ECAM window (1 MiB of config space per bus); the bus
|
||||||
// range rides beside it. The MMIO apertures (M19.0) come after both.
|
// range rides beside it. The MMIO apertures (M19.0) come after both.
|
||||||
if (descriptor.resource_count < 2 or descriptor.resources[0].kind != @intFromEnum(device.ResourceKind.memory)) {
|
if (descriptor.resource_count < 2 or descriptor.resources[0].kind != @intFromEnum(device.ResourceKind.memory)) {
|
||||||
_ = runtime.system.write("/system/drivers/pci-bus: bridge has no ECAM window\n");
|
_ = logging.write("/system/drivers/pci-bus: bridge has no ECAM window\n");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
const bus_range = for (descriptor.resources[0..@intCast(descriptor.resource_count)]) |resource| {
|
const bus_range = for (descriptor.resources[0..@intCast(descriptor.resource_count)]) |resource| {
|
||||||
if (resource.kind == @intFromEnum(device.ResourceKind.bus_range)) break resource;
|
if (resource.kind == @intFromEnum(device.ResourceKind.bus_range)) break resource;
|
||||||
} else {
|
} else {
|
||||||
_ = runtime.system.write("/system/drivers/pci-bus: bridge has no bus range\n");
|
_ = logging.write("/system/drivers/pci-bus: bridge has no bus range\n");
|
||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
start_bus = bus_range.start;
|
start_bus = bus_range.start;
|
||||||
bus_count = bus_range.len;
|
bus_count = bus_range.len;
|
||||||
ecam_physical = descriptor.resources[0].start;
|
ecam_physical = descriptor.resources[0].start;
|
||||||
ecam_base = device.mmioMap(bridge_id, 0) orelse {
|
ecam_base = device.mmioMap(bridge_id, 0) orelse {
|
||||||
_ = runtime.system.write("/system/drivers/pci-bus: ECAM mmio_map failed\n");
|
_ = logging.write("/system/drivers/pci-bus: ECAM mmio_map failed\n");
|
||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
|
|
||||||
// The handshake (role: bus — we enumerate PCI and report the functions we
|
// The handshake (role: bus — we enumerate PCI and report the functions we
|
||||||
// find), then the scan. Keep the manager handle to report children through;
|
// find), then the scan. Keep the manager handle to report children through;
|
||||||
// a supervised bus that cannot reach its manager has nothing to serve.
|
// a supervised bus that cannot reach its manager has nothing to serve.
|
||||||
manager_handle = runtime.device_manager.hello(.bus, bridge_id) orelse return false;
|
manager_handle = device_manager.hello(.bus, bridge_id) orelse return false;
|
||||||
|
|
||||||
scan();
|
scan();
|
||||||
return true;
|
return true;
|
||||||
@@ -216,12 +221,12 @@ fn registerAndReport(bus: u64, dev: u64, function: u64, class_triple: u32) void
|
|||||||
.device_id = registered,
|
.device_id = registered,
|
||||||
};
|
};
|
||||||
var reply: [device_manager_protocol.message_maximum]u8 = undefined;
|
var reply: [device_manager_protocol.message_maximum]u8 = undefined;
|
||||||
_ = runtime.ipc.call(manager_handle, std.mem.asBytes(&report), &reply) catch {
|
_ = ipc.call(manager_handle, std.mem.asBytes(&report), &reply) catch {
|
||||||
std.log.info("child report for {d}:{d}.{d} failed", .{ bus, dev, function });
|
std.log.info("child report for {d}:{d}.{d} failed", .{ bus, dev, function });
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
fn onMessage(message: []const u8, reply: []u8, sender: u32, capability: ?runtime.ipc.Handle) usize {
|
fn onMessage(message: []const u8, reply: []u8, sender: u32, capability: ?ipc.Handle) usize {
|
||||||
_ = message;
|
_ = message;
|
||||||
_ = reply;
|
_ = reply;
|
||||||
_ = sender;
|
_ = sender;
|
||||||
@@ -229,13 +234,13 @@ fn onMessage(message: []const u8, reply: []u8, sender: u32, capability: ?runtime
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn main(init: runtime.process.Init) void {
|
pub fn main(init: process.Init) void {
|
||||||
const argument = init.arguments.get(1) orelse return; // bare (ramdisk sweep): stay silent
|
const argument = init.arguments.get(1) orelse return; // bare (ramdisk sweep): stay silent
|
||||||
bridge_id = std.fmt.parseInt(u64, argument, 10) catch {
|
bridge_id = std.fmt.parseInt(u64, argument, 10) catch {
|
||||||
std.log.info("malformed bridge device id '{s}'", .{argument});
|
std.log.info("malformed bridge device id '{s}'", .{argument});
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
runtime.service.run(device_manager_protocol.message_maximum, .{
|
service.run(device_manager_protocol.message_maximum, .{
|
||||||
.init = initialise,
|
.init = initialise,
|
||||||
.on_message = onMessage,
|
.on_message = onMessage,
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -15,12 +15,16 @@
|
|||||||
//! -> xkeyboard-config -> character -> key_press
|
//! -> xkeyboard-config -> character -> key_press
|
||||||
|
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const runtime = @import("runtime");
|
const device = @import("driver");
|
||||||
|
const ipc = @import("ipc");
|
||||||
|
const process = @import("process");
|
||||||
|
const time = @import("time");
|
||||||
|
const input = @import("input");
|
||||||
|
const memory = @import("memory");
|
||||||
|
const logging = @import("logging");
|
||||||
const xkb = @import("xkeyboard-config");
|
const xkb = @import("xkeyboard-config");
|
||||||
const ps2 = @import("ps2-library.zig");
|
const ps2 = @import("ps2-library.zig");
|
||||||
const scancode = @import("scancode.zig");
|
const scancode = @import("scancode.zig");
|
||||||
const device = runtime.device;
|
|
||||||
const ipc = runtime.ipc;
|
|
||||||
const input_protocol = @import("input-protocol");
|
const input_protocol = @import("input-protocol");
|
||||||
|
|
||||||
/// Look up the ps2-bus service, retrying while the bus (which spawned us before
|
/// Look up the ps2-bus service, retrying while the bus (which spawned us before
|
||||||
@@ -29,7 +33,7 @@ fn lookupBus() ?ipc.Handle {
|
|||||||
var attempts: usize = 0;
|
var attempts: usize = 0;
|
||||||
while (attempts < 100) : (attempts += 1) {
|
while (attempts < 100) : (attempts += 1) {
|
||||||
if (ipc.lookup(.ps2_bus)) |handle| return handle;
|
if (ipc.lookup(.ps2_bus)) |handle| return handle;
|
||||||
runtime.system.sleep(50);
|
time.sleepMillis(50);
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -64,16 +68,16 @@ fn modifierWord(modifiers: scancode.ModifierSnapshot) u32 {
|
|||||||
return word;
|
return word;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn main(init: runtime.process.Init) void {
|
pub fn main(init: process.Init) void {
|
||||||
const hid = init.arguments.get(1).?;
|
const hid = init.arguments.get(1).?;
|
||||||
if (hid.len == 0) {
|
if (hid.len == 0) {
|
||||||
_ = runtime.system.write("/system/drivers/ps2-bus/keyboard: no HID argument\n");
|
_ = logging.write("/system/drivers/ps2-bus/keyboard: no HID argument\n");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
std.log.info("starting for hid {s}", .{hid});
|
std.log.info("starting for hid {s}", .{hid});
|
||||||
|
|
||||||
const buffer = runtime.allocator().alloc(device.DeviceDescriptor, 64) catch {
|
const buffer = memory.allocator().alloc(device.DeviceDescriptor, 64) catch {
|
||||||
_ = runtime.system.write("/system/drivers/ps2-bus/keyboard: out of memory\n");
|
_ = logging.write("/system/drivers/ps2-bus/keyboard: out of memory\n");
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
if (device.findDeviceDescriptorByHid(buffer, hid) == null) {
|
if (device.findDeviceDescriptorByHid(buffer, hid) == null) {
|
||||||
@@ -90,33 +94,33 @@ pub fn main(init: runtime.process.Init) void {
|
|||||||
// Attach to the bus: hand it our endpoint, and it forwards every byte the
|
// Attach to the bus: hand it our endpoint, and it forwards every byte the
|
||||||
// keyboard sends (it owns the controller; we own the decoding).
|
// keyboard sends (it owns the controller; we own the decoding).
|
||||||
const bus = lookupBus() orelse {
|
const bus = lookupBus() orelse {
|
||||||
_ = runtime.system.write("/system/drivers/ps2-bus/keyboard: ps2-bus service unavailable\n");
|
_ = logging.write("/system/drivers/ps2-bus/keyboard: ps2-bus service unavailable\n");
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
const endpoint = ipc.createIpcEndpoint() orelse {
|
const endpoint = ipc.createIpcEndpoint() orelse {
|
||||||
_ = runtime.system.write("/system/drivers/ps2-bus/keyboard: no endpoint\n");
|
_ = logging.write("/system/drivers/ps2-bus/keyboard: no endpoint\n");
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
var attach = ps2.AttachRequest{ .device_type = @intFromEnum(ps2.DeviceType.keyboard) };
|
var attach = ps2.AttachRequest{ .device_type = @intFromEnum(ps2.DeviceType.keyboard) };
|
||||||
var attach_reply: [@sizeOf(ps2.AttachReply)]u8 = undefined;
|
var attach_reply: [@sizeOf(ps2.AttachReply)]u8 = undefined;
|
||||||
const attached = ipc.callCap(bus, std.mem.asBytes(&attach), &attach_reply, endpoint) catch {
|
const attached = ipc.callCap(bus, std.mem.asBytes(&attach), &attach_reply, endpoint) catch {
|
||||||
_ = runtime.system.write("/system/drivers/ps2-bus/keyboard: attach call failed\n");
|
_ = logging.write("/system/drivers/ps2-bus/keyboard: attach call failed\n");
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
if (attached.len < @sizeOf(ps2.AttachReply) or
|
if (attached.len < @sizeOf(ps2.AttachReply) or
|
||||||
std.mem.bytesToValue(ps2.AttachReply, attach_reply[0..@sizeOf(ps2.AttachReply)]).status != @intFromEnum(ps2.AttachStatus.ok))
|
std.mem.bytesToValue(ps2.AttachReply, attach_reply[0..@sizeOf(ps2.AttachReply)]).status != @intFromEnum(ps2.AttachStatus.ok))
|
||||||
{
|
{
|
||||||
_ = runtime.system.write("/system/drivers/ps2-bus/keyboard: attach refused\n");
|
_ = logging.write("/system/drivers/ps2-bus/keyboard: attach refused\n");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Broadcast keyboard events through the input service so programs can listen
|
// Broadcast keyboard events through the input service so programs can listen
|
||||||
// for them (docs/input.md).
|
// for them (docs/input.md).
|
||||||
var source = runtime.input.connectSource() orelse {
|
var source = input.connectSource() orelse {
|
||||||
_ = runtime.system.write("/system/drivers/ps2-bus/keyboard: input service unavailable\n");
|
_ = logging.write("/system/drivers/ps2-bus/keyboard: input service unavailable\n");
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
_ = runtime.system.write("/system/drivers/ps2-bus/keyboard: ok\n");
|
_ = logging.write("/system/drivers/ps2-bus/keyboard: ok\n");
|
||||||
|
|
||||||
var decoder = scancode.Decoder{};
|
var decoder = scancode.Decoder{};
|
||||||
var state = scancode.KeyboardState{};
|
var state = scancode.KeyboardState{};
|
||||||
|
|||||||
@@ -15,11 +15,15 @@
|
|||||||
//! -> movement -> motion (dx/dy, screen convention)
|
//! -> movement -> motion (dx/dy, screen convention)
|
||||||
|
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const runtime = @import("runtime");
|
const device = @import("driver");
|
||||||
|
const ipc = @import("ipc");
|
||||||
|
const process = @import("process");
|
||||||
|
const time = @import("time");
|
||||||
|
const input = @import("input");
|
||||||
|
const memory = @import("memory");
|
||||||
|
const logging = @import("logging");
|
||||||
const ps2 = @import("ps2-library.zig");
|
const ps2 = @import("ps2-library.zig");
|
||||||
const mouse_packet = @import("mouse-packet.zig");
|
const mouse_packet = @import("mouse-packet.zig");
|
||||||
const device = runtime.device;
|
|
||||||
const ipc = runtime.ipc;
|
|
||||||
const input_protocol = @import("input-protocol");
|
const input_protocol = @import("input-protocol");
|
||||||
|
|
||||||
/// Look up the ps2-bus service, retrying while the bus (which spawned us before
|
/// Look up the ps2-bus service, retrying while the bus (which spawned us before
|
||||||
@@ -28,7 +32,7 @@ fn lookupBus() ?ipc.Handle {
|
|||||||
var attempts: usize = 0;
|
var attempts: usize = 0;
|
||||||
while (attempts < 100) : (attempts += 1) {
|
while (attempts < 100) : (attempts += 1) {
|
||||||
if (ipc.lookup(.ps2_bus)) |handle| return handle;
|
if (ipc.lookup(.ps2_bus)) |handle| return handle;
|
||||||
runtime.system.sleep(50);
|
time.sleepMillis(50);
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -42,17 +46,17 @@ fn buttonMask(packet: mouse_packet.Packet) u32 {
|
|||||||
return mask;
|
return mask;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn main(init: runtime.process.Init) void {
|
pub fn main(init: process.Init) void {
|
||||||
const hid = init.arguments.get(1).?;
|
const hid = init.arguments.get(1).?;
|
||||||
|
|
||||||
if (hid.len == 0) {
|
if (hid.len == 0) {
|
||||||
_ = runtime.system.write("/system/drivers/ps2-bus/mouse: no HID argument\n");
|
_ = logging.write("/system/drivers/ps2-bus/mouse: no HID argument\n");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
std.log.info("starting for hid {s}", .{hid});
|
std.log.info("starting for hid {s}", .{hid});
|
||||||
|
|
||||||
const buffer = runtime.allocator().alloc(device.DeviceDescriptor, 64) catch {
|
const buffer = memory.allocator().alloc(device.DeviceDescriptor, 64) catch {
|
||||||
_ = runtime.system.write("/system/drivers/ps2-bus/mouse: out of memory\n");
|
_ = logging.write("/system/drivers/ps2-bus/mouse: out of memory\n");
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
if (ps2.findMouseDescriptor(buffer) == null) {
|
if (ps2.findMouseDescriptor(buffer) == null) {
|
||||||
@@ -63,33 +67,33 @@ pub fn main(init: runtime.process.Init) void {
|
|||||||
// Attach to the bus: hand it our endpoint, and it forwards every byte the
|
// Attach to the bus: hand it our endpoint, and it forwards every byte the
|
||||||
// mouse sends (it owns the controller; we own the decoding).
|
// mouse sends (it owns the controller; we own the decoding).
|
||||||
const bus = lookupBus() orelse {
|
const bus = lookupBus() orelse {
|
||||||
_ = runtime.system.write("/system/drivers/ps2-bus/mouse: ps2-bus service unavailable\n");
|
_ = logging.write("/system/drivers/ps2-bus/mouse: ps2-bus service unavailable\n");
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
const endpoint = ipc.createIpcEndpoint() orelse {
|
const endpoint = ipc.createIpcEndpoint() orelse {
|
||||||
_ = runtime.system.write("/system/drivers/ps2-bus/mouse: no endpoint\n");
|
_ = logging.write("/system/drivers/ps2-bus/mouse: no endpoint\n");
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
var attach = ps2.AttachRequest{ .device_type = @intFromEnum(ps2.DeviceType.mouse) };
|
var attach = ps2.AttachRequest{ .device_type = @intFromEnum(ps2.DeviceType.mouse) };
|
||||||
var attach_reply: [@sizeOf(ps2.AttachReply)]u8 = undefined;
|
var attach_reply: [@sizeOf(ps2.AttachReply)]u8 = undefined;
|
||||||
const attached = ipc.callCap(bus, std.mem.asBytes(&attach), &attach_reply, endpoint) catch {
|
const attached = ipc.callCap(bus, std.mem.asBytes(&attach), &attach_reply, endpoint) catch {
|
||||||
_ = runtime.system.write("/system/drivers/ps2-bus/mouse: attach call failed\n");
|
_ = logging.write("/system/drivers/ps2-bus/mouse: attach call failed\n");
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
if (attached.len < @sizeOf(ps2.AttachReply) or
|
if (attached.len < @sizeOf(ps2.AttachReply) or
|
||||||
std.mem.bytesToValue(ps2.AttachReply, attach_reply[0..@sizeOf(ps2.AttachReply)]).status != @intFromEnum(ps2.AttachStatus.ok))
|
std.mem.bytesToValue(ps2.AttachReply, attach_reply[0..@sizeOf(ps2.AttachReply)]).status != @intFromEnum(ps2.AttachStatus.ok))
|
||||||
{
|
{
|
||||||
_ = runtime.system.write("/system/drivers/ps2-bus/mouse: attach refused\n");
|
_ = logging.write("/system/drivers/ps2-bus/mouse: attach refused\n");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Broadcast mouse events through the input service so programs can listen
|
// Broadcast mouse events through the input service so programs can listen
|
||||||
// for them (docs/input.md).
|
// for them (docs/input.md).
|
||||||
var source = runtime.input.connectSource() orelse {
|
var source = input.connectSource() orelse {
|
||||||
_ = runtime.system.write("/system/drivers/ps2-bus/mouse: input service unavailable\n");
|
_ = logging.write("/system/drivers/ps2-bus/mouse: input service unavailable\n");
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
_ = runtime.system.write("/system/drivers/ps2-bus/mouse: ok\n");
|
_ = logging.write("/system/drivers/ps2-bus/mouse: ok\n");
|
||||||
|
|
||||||
var assembler = mouse_packet.Assembler{};
|
var assembler = mouse_packet.Assembler{};
|
||||||
var buttons: u32 = 0;
|
var buttons: u32 = 0;
|
||||||
|
|||||||
@@ -10,11 +10,14 @@
|
|||||||
//! MOU_ [acpi_device] hid=PNP0F13 (PS/2 Mouse)
|
//! MOU_ [acpi_device] hid=PNP0F13 (PS/2 Mouse)
|
||||||
//! - irq 0xc len 0x1
|
//! - irq 0xc len 0x1
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const runtime = @import("runtime");
|
const device = @import("driver");
|
||||||
|
const ipc = @import("ipc");
|
||||||
|
const process = @import("process");
|
||||||
|
const service = @import("service");
|
||||||
|
const memory = @import("memory");
|
||||||
|
const logging = @import("logging");
|
||||||
const acpi_ids = @import("acpi-ids");
|
const acpi_ids = @import("acpi-ids");
|
||||||
const ps2 = @import("ps2-library.zig");
|
const ps2 = @import("ps2-library.zig");
|
||||||
const device = runtime.device;
|
|
||||||
const ipc = runtime.ipc;
|
|
||||||
|
|
||||||
/// Ask the device on `port` what it is, then spawn the matching driver from the
|
/// Ask the device on `port` what it is, then spawn the matching driver from the
|
||||||
/// initial-ramdisk, handing it the device's HID as argv[1]. The driver is chosen
|
/// initial-ramdisk, handing it the device's HID as argv[1]. The driver is chosen
|
||||||
@@ -31,7 +34,7 @@ fn spawnIdentifiedDriver(controller: ps2.Controller, port: ps2.Port) ?ps2.Device
|
|||||||
return null;
|
return null;
|
||||||
};
|
};
|
||||||
const hid = device_type.hid() orelse "";
|
const hid = device_type.hid() orelse "";
|
||||||
if (runtime.system.spawnWithArguments(driver_name, &.{hid}) != null) {
|
if (process.spawnWithArguments(driver_name, &.{hid}) != null) {
|
||||||
std.log.info("port {s} is a {s}, spawned {s}", .{ @tagName(port), hid, driver_name });
|
std.log.info("port {s} is a {s}, spawned {s}", .{ @tagName(port), hid, driver_name });
|
||||||
return device_type;
|
return device_type;
|
||||||
}
|
}
|
||||||
@@ -83,8 +86,8 @@ fn handleAttach(message: []const u8, got: ipc.Received, out: []u8) usize {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn main() void {
|
pub fn main() void {
|
||||||
const buffer = runtime.allocator().alloc(device.DeviceDescriptor, 64) catch {
|
const buffer = memory.allocator().alloc(device.DeviceDescriptor, 64) catch {
|
||||||
_ = runtime.system.write("/system/drivers/ps2-bus: out of memory\n");
|
_ = logging.write("/system/drivers/ps2-bus: out of memory\n");
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -96,16 +99,16 @@ pub fn main() void {
|
|||||||
// is on which port is decided later by identify, not by this HID.
|
// is on which port is decided later by identify, not by this HID.
|
||||||
const maybe_controller_device_descriptor = device.findDeviceDescriptorByHid(buffer, acpi_ids.HardwareId.ps2_keyboard.hid());
|
const maybe_controller_device_descriptor = device.findDeviceDescriptorByHid(buffer, acpi_ids.HardwareId.ps2_keyboard.hid());
|
||||||
if (maybe_controller_device_descriptor) |controller_device_descriptor| {
|
if (maybe_controller_device_descriptor) |controller_device_descriptor| {
|
||||||
_ = runtime.system.write("/system/drivers/ps2-bus: found PS/2 controller\n");
|
_ = logging.write("/system/drivers/ps2-bus: found PS/2 controller\n");
|
||||||
_ = runtime.system.write("/system/drivers/ps2-bus: initializing controller\n");
|
_ = logging.write("/system/drivers/ps2-bus: initializing controller\n");
|
||||||
|
|
||||||
if (!device.claim(controller_device_descriptor.id)) {
|
if (!device.claim(controller_device_descriptor.id)) {
|
||||||
_ = runtime.system.write("/system/drivers/ps2-bus: unable to claim controller \n");
|
_ = logging.write("/system/drivers/ps2-bus: unable to claim controller \n");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const controller = ps2.Controller.init(controller_device_descriptor) orelse {
|
const controller = ps2.Controller.init(controller_device_descriptor) orelse {
|
||||||
_ = runtime.system.write("/system/drivers/ps2-bus: controller is missing its IO ports\n");
|
_ = logging.write("/system/drivers/ps2-bus: controller is missing its IO ports\n");
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
maybe_controller = controller;
|
maybe_controller = controller;
|
||||||
@@ -116,7 +119,7 @@ pub fn main() void {
|
|||||||
controller.flushOutputBuffer();
|
controller.flushOutputBuffer();
|
||||||
|
|
||||||
const current = controller.readConfigurationByte() orelse {
|
const current = controller.readConfigurationByte() orelse {
|
||||||
_ = runtime.system.write("/system/drivers/ps2-bus: controller configuration timed out\n");
|
_ = logging.write("/system/drivers/ps2-bus: controller configuration timed out\n");
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -125,49 +128,49 @@ pub fn main() void {
|
|||||||
ps2.configuration_first_port_translation);
|
ps2.configuration_first_port_translation);
|
||||||
|
|
||||||
if (controller.writeConfigurationByte(update) == null) {
|
if (controller.writeConfigurationByte(update) == null) {
|
||||||
_ = runtime.system.write("/system/drivers/ps2-bus: controller configuration timed out\n");
|
_ = logging.write("/system/drivers/ps2-bus: controller configuration timed out\n");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (controller.performSelfTest()) |reply| {
|
if (controller.performSelfTest()) |reply| {
|
||||||
if (reply != ps2.response_controller_test_passed) {
|
if (reply != ps2.response_controller_test_passed) {
|
||||||
_ = runtime.system.write("/system/drivers/ps2-bus: perform controller self test failed\n");
|
_ = logging.write("/system/drivers/ps2-bus: perform controller self test failed\n");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
_ = runtime.system.write("/system/drivers/ps2-bus: controller self test timed out\n");
|
_ = logging.write("/system/drivers/ps2-bus: controller self test timed out\n");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
has_two_channels = controller.hasTwoChannels() orelse {
|
has_two_channels = controller.hasTwoChannels() orelse {
|
||||||
_ = runtime.system.write("/system/drivers/ps2-bus: controller channels timed out\n");
|
_ = logging.write("/system/drivers/ps2-bus: controller channels timed out\n");
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
if (has_two_channels) {
|
if (has_two_channels) {
|
||||||
_ = runtime.system.write("/system/drivers/ps2-bus: has two channels\n");
|
_ = logging.write("/system/drivers/ps2-bus: has two channels\n");
|
||||||
// keep the bus quiet until we have tested the ports and are ready to use them
|
// keep the bus quiet until we have tested the ports and are ready to use them
|
||||||
controller.disablePort(.two);
|
controller.disablePort(.two);
|
||||||
} else {
|
} else {
|
||||||
_ = runtime.system.write("/system/drivers/ps2-bus: has one channel\n");
|
_ = logging.write("/system/drivers/ps2-bus: has one channel\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
// interface tests: always test port 1, test port 2 only if it exists
|
// interface tests: always test port 1, test port 2 only if it exists
|
||||||
const port_one_works = (controller.testPort(.one) orelse {
|
const port_one_works = (controller.testPort(.one) orelse {
|
||||||
_ = runtime.system.write("/system/drivers/ps2-bus: port 1 test timed out\n");
|
_ = logging.write("/system/drivers/ps2-bus: port 1 test timed out\n");
|
||||||
return;
|
return;
|
||||||
}) == ps2.response_port_test_passed;
|
}) == ps2.response_port_test_passed;
|
||||||
|
|
||||||
var port_two_works = false;
|
var port_two_works = false;
|
||||||
if (has_two_channels) {
|
if (has_two_channels) {
|
||||||
port_two_works = (controller.testPort(.two) orelse {
|
port_two_works = (controller.testPort(.two) orelse {
|
||||||
_ = runtime.system.write("/system/drivers/ps2-bus: port 2 test timed out\n");
|
_ = logging.write("/system/drivers/ps2-bus: port 2 test timed out\n");
|
||||||
return;
|
return;
|
||||||
}) == ps2.response_port_test_passed;
|
}) == ps2.response_port_test_passed;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!port_one_works and !port_two_works) {
|
if (!port_one_works and !port_two_works) {
|
||||||
_ = runtime.system.write("/system/drivers/ps2-bus: no usable ports\n");
|
_ = logging.write("/system/drivers/ps2-bus: no usable ports\n");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -181,16 +184,16 @@ pub fn main() void {
|
|||||||
// abort bring-up of the other one
|
// abort bring-up of the other one
|
||||||
if (port_one_works) {
|
if (port_one_works) {
|
||||||
if (controller.resetDevice(.one)) |passed| {
|
if (controller.resetDevice(.one)) |passed| {
|
||||||
if (!passed) _ = runtime.system.write("/system/drivers/ps2-bus: port 1 device reset failed\n");
|
if (!passed) _ = logging.write("/system/drivers/ps2-bus: port 1 device reset failed\n");
|
||||||
} else {
|
} else {
|
||||||
_ = runtime.system.write("/system/drivers/ps2-bus: port 1 device reset timed out\n");
|
_ = logging.write("/system/drivers/ps2-bus: port 1 device reset timed out\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (port_two_works) {
|
if (port_two_works) {
|
||||||
if (controller.resetDevice(.two)) |passed| {
|
if (controller.resetDevice(.two)) |passed| {
|
||||||
if (!passed) _ = runtime.system.write("/system/drivers/ps2-bus: port 2 device reset failed\n");
|
if (!passed) _ = logging.write("/system/drivers/ps2-bus: port 2 device reset failed\n");
|
||||||
} else {
|
} else {
|
||||||
_ = runtime.system.write("/system/drivers/ps2-bus: port 2 device reset timed out\n");
|
_ = logging.write("/system/drivers/ps2-bus: port 2 device reset timed out\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -200,13 +203,13 @@ pub fn main() void {
|
|||||||
if (port_one_works) port_device_types[@intFromEnum(ps2.Port.one)] = spawnIdentifiedDriver(controller, .one);
|
if (port_one_works) port_device_types[@intFromEnum(ps2.Port.one)] = spawnIdentifiedDriver(controller, .one);
|
||||||
if (port_two_works) port_device_types[@intFromEnum(ps2.Port.two)] = spawnIdentifiedDriver(controller, .two);
|
if (port_two_works) port_device_types[@intFromEnum(ps2.Port.two)] = spawnIdentifiedDriver(controller, .two);
|
||||||
} else {
|
} else {
|
||||||
_ = runtime.system.write("/system/drivers/ps2-bus: no PS/2 controller found\n");
|
_ = logging.write("/system/drivers/ps2-bus: no PS/2 controller found\n");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const controller = maybe_controller.?;
|
const controller = maybe_controller.?;
|
||||||
const interrupt_index = maybe_interrupt_index orelse {
|
const interrupt_index = maybe_interrupt_index orelse {
|
||||||
_ = runtime.system.write("/system/drivers/ps2-bus: controller is missing its IRQ\n");
|
_ = logging.write("/system/drivers/ps2-bus: controller is missing its IRQ\n");
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -214,11 +217,11 @@ pub fn main() void {
|
|||||||
// well-known id so the children can find it, the way input subscribers find
|
// well-known id so the children can find it, the way input subscribers find
|
||||||
// the input service.
|
// the input service.
|
||||||
const endpoint = ipc.createIpcEndpoint() orelse {
|
const endpoint = ipc.createIpcEndpoint() orelse {
|
||||||
_ = runtime.system.write("/system/drivers/ps2-bus: no endpoint\n");
|
_ = logging.write("/system/drivers/ps2-bus: no endpoint\n");
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
if (!ipc.register(.ps2_bus, endpoint)) {
|
if (!ipc.register(.ps2_bus, endpoint)) {
|
||||||
_ = runtime.system.write("/system/drivers/ps2-bus: register failed\n");
|
_ = logging.write("/system/drivers/ps2-bus: register failed\n");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -227,7 +230,7 @@ pub fn main() void {
|
|||||||
// let the controller raise them — an interrupt with nobody bound is lost.
|
// let the controller raise them — an interrupt with nobody bound is lost.
|
||||||
controller.drainOutputBuffer();
|
controller.drainOutputBuffer();
|
||||||
if (!device.irqBind(controller.device_id, interrupt_index, endpoint)) {
|
if (!device.irqBind(controller.device_id, interrupt_index, endpoint)) {
|
||||||
_ = runtime.system.write("/system/drivers/ps2-bus: irq_bind failed\n");
|
_ = logging.write("/system/drivers/ps2-bus: irq_bind failed\n");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -246,21 +249,21 @@ pub fn main() void {
|
|||||||
.gsi = descriptor.resources[auxiliary_index].start,
|
.gsi = descriptor.resources[auxiliary_index].start,
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
_ = runtime.system.write("/system/drivers/ps2-bus: auxiliary irq_bind failed\n");
|
_ = logging.write("/system/drivers/ps2-bus: auxiliary irq_bind failed\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var configuration = controller.readConfigurationByte() orelse {
|
var configuration = controller.readConfigurationByte() orelse {
|
||||||
_ = runtime.system.write("/system/drivers/ps2-bus: controller configuration timed out\n");
|
_ = logging.write("/system/drivers/ps2-bus: controller configuration timed out\n");
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
if (port_device_types[@intFromEnum(ps2.Port.one)] != null) configuration |= ps2.Port.one.interruptBit();
|
if (port_device_types[@intFromEnum(ps2.Port.one)] != null) configuration |= ps2.Port.one.interruptBit();
|
||||||
if (maybe_auxiliary_interrupt != null) configuration |= ps2.Port.two.interruptBit();
|
if (maybe_auxiliary_interrupt != null) configuration |= ps2.Port.two.interruptBit();
|
||||||
_ = controller.writeConfigurationByte(configuration);
|
_ = controller.writeConfigurationByte(configuration);
|
||||||
|
|
||||||
_ = runtime.system.write("/system/drivers/ps2-bus: ok\n");
|
_ = logging.write("/system/drivers/ps2-bus: ok\n");
|
||||||
|
|
||||||
// The forwarding loop: an IRQ1 notification drains the output buffer, routing
|
// The forwarding loop: an IRQ1 notification drains the output buffer, routing
|
||||||
// each byte to the attached driver of the port it came from; a client message
|
// each byte to the attached driver of the port it came from; a client message
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
//! shared definitions between the different PS/2 drivers
|
//! shared definitions between the different PS/2 drivers
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const runtime = @import("runtime");
|
const device = @import("driver");
|
||||||
|
const ipc = @import("ipc");
|
||||||
|
const time = @import("time");
|
||||||
const acpi_ids = @import("acpi-ids");
|
const acpi_ids = @import("acpi-ids");
|
||||||
const device = runtime.device;
|
|
||||||
const system = runtime.system;
|
|
||||||
|
|
||||||
/// PS-2 io ports:
|
/// PS-2 io ports:
|
||||||
/// The PS/2 Controller itself uses 2 IO ports (usually, IO ports 0x60 and 0x64). Like many IO
|
/// The PS/2 Controller itself uses 2 IO ports (usually, IO ports 0x60 and 0x64). Like many IO
|
||||||
@@ -114,16 +114,16 @@ pub const identify_mouse_scroll: u8 = 0x03; // mouse with scroll wheel
|
|||||||
pub const identify_mouse_five_button: u8 = 0x04; // 5-button mouse
|
pub const identify_mouse_five_button: u8 = 0x04; // 5-button mouse
|
||||||
|
|
||||||
fn waitReadable(id: u64, cmd_index: u64, wait_timeout_nanoseconds: u64) bool {
|
fn waitReadable(id: u64, cmd_index: u64, wait_timeout_nanoseconds: u64) bool {
|
||||||
const deadline = system.clock() + wait_timeout_nanoseconds;
|
const deadline = time.clock() + wait_timeout_nanoseconds;
|
||||||
while (system.clock() < deadline) {
|
while (time.clock() < deadline) {
|
||||||
if (status(id, cmd_index) & status_output_buffer_full != 0) return true; // OBF set -> data ready
|
if (status(id, cmd_index) & status_output_buffer_full != 0) return true; // OBF set -> data ready
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn waitWritable(id: u64, cmd_index: u64, wait_timeout_nanoseconds: u64) bool {
|
fn waitWritable(id: u64, cmd_index: u64, wait_timeout_nanoseconds: u64) bool {
|
||||||
const deadline = system.clock() + wait_timeout_nanoseconds;
|
const deadline = time.clock() + wait_timeout_nanoseconds;
|
||||||
while (system.clock() < deadline) {
|
while (time.clock() < deadline) {
|
||||||
if (status(id, cmd_index) & status_input_buffer_full == 0) return true; // IBF clear -> ok to write
|
if (status(id, cmd_index) & status_input_buffer_full == 0) return true; // IBF clear -> ok to write
|
||||||
}
|
}
|
||||||
return false; // timed out
|
return false; // timed out
|
||||||
|
|||||||
@@ -15,13 +15,16 @@
|
|||||||
//! HID keyboard page 0x07), the decode is nearly 1:1 — no scancode translation.
|
//! HID keyboard page 0x07), the decode is nearly 1:1 — no scancode translation.
|
||||||
|
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const runtime = @import("runtime");
|
const ipc = @import("ipc");
|
||||||
|
const process = @import("process");
|
||||||
|
const service = @import("service");
|
||||||
|
const input = @import("input");
|
||||||
|
const device_manager = @import("driver");
|
||||||
|
const logging = @import("logging");
|
||||||
const usb = @import("usb");
|
const usb = @import("usb");
|
||||||
const usb_abi = @import("usb-abi");
|
const usb_abi = @import("usb-abi");
|
||||||
const xkb = @import("xkeyboard-config");
|
const xkb = @import("xkeyboard-config");
|
||||||
const hid = @import("hid-report.zig");
|
const hid = @import("hid-report.zig");
|
||||||
const ipc = runtime.ipc;
|
|
||||||
const process = runtime.process;
|
|
||||||
const input_protocol = @import("input-protocol");
|
const input_protocol = @import("input-protocol");
|
||||||
|
|
||||||
// The modifier state a character lookup needs — derived from the report's
|
// The modifier state a character lookup needs — derived from the report's
|
||||||
@@ -61,9 +64,9 @@ fn modifierWord(modifiers: u8) u32 {
|
|||||||
return word;
|
return word;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn main(init: runtime.process.Init) void {
|
pub fn main(init: process.Init) void {
|
||||||
const argument = init.arguments.get(1) orelse {
|
const argument = init.arguments.get(1) orelse {
|
||||||
_ = runtime.system.write("/system/drivers/usb-hid/keyboard: missing device id (argv[1])\n");
|
_ = logging.write("/system/drivers/usb-hid/keyboard: missing device id (argv[1])\n");
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
const device_id = std.fmt.parseInt(u64, argument, 10) catch {
|
const device_id = std.fmt.parseInt(u64, argument, 10) catch {
|
||||||
@@ -73,13 +76,13 @@ pub fn main(init: runtime.process.Init) void {
|
|||||||
const layout = xkb.byName(init.arguments.get(2) orelse "us") orelse xkb.us;
|
const layout = xkb.byName(init.arguments.get(2) orelse "us") orelse xkb.us;
|
||||||
|
|
||||||
// Hello the manager first (meet the spawn deadline), then open the device.
|
// Hello the manager first (meet the spawn deadline), then open the device.
|
||||||
if (runtime.device_manager.hello(.device, device_id) == null) return;
|
if (device_manager.hello(.device, device_id) == null) return;
|
||||||
var device = usb.open(device_id) orelse {
|
var device = usb.open(device_id) orelse {
|
||||||
std.log.info("could not open device {d}", .{device_id});
|
std.log.info("could not open device {d}", .{device_id});
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
const endpoint = device.findEndpoint(usb.transfer_type_interrupt, true) orelse {
|
const endpoint = device.findEndpoint(usb.transfer_type_interrupt, true) orelse {
|
||||||
_ = runtime.system.write("/system/drivers/usb-hid/keyboard: no interrupt-IN endpoint\n");
|
_ = logging.write("/system/drivers/usb-hid/keyboard: no interrupt-IN endpoint\n");
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -88,12 +91,12 @@ pub fn main(init: runtime.process.Init) void {
|
|||||||
_ = device.controlOut(@bitCast(usb_abi.setIdle(@enumFromInt(device.interface_number), 0, 0)));
|
_ = device.controlOut(@bitCast(usb_abi.setIdle(@enumFromInt(device.interface_number), 0, 0)));
|
||||||
|
|
||||||
if (!device.subscribeInterrupt(endpoint.address, endpoint.max_packet_size)) {
|
if (!device.subscribeInterrupt(endpoint.address, endpoint.max_packet_size)) {
|
||||||
_ = runtime.system.write("/system/drivers/usb-hid/keyboard: interrupt subscribe failed\n");
|
_ = logging.write("/system/drivers/usb-hid/keyboard: interrupt subscribe failed\n");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var source = runtime.input.connectSource() orelse {
|
var source = input.connectSource() orelse {
|
||||||
_ = runtime.system.write("/system/drivers/usb-hid/keyboard: input service unavailable\n");
|
_ = logging.write("/system/drivers/usb-hid/keyboard: input service unavailable\n");
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
_ = process.bindSignals(device.endpoint);
|
_ = process.bindSignals(device.endpoint);
|
||||||
@@ -153,7 +156,7 @@ pub fn main(init: runtime.process.Init) void {
|
|||||||
// the kernel console is a log sink). Printable ASCII and
|
// the kernel console is a log sink). Printable ASCII and
|
||||||
// newline only; other keys are left to the input service.
|
// newline only; other keys are left to the input service.
|
||||||
if (character == '\n' or (character >= 0x20 and character < 0x7F)) {
|
if (character == '\n' or (character >= 0x20 and character < 0x7F)) {
|
||||||
_ = runtime.system.write(&[1]u8{@intCast(character)});
|
_ = logging.write(&[1]u8{@intCast(character)});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -11,12 +11,15 @@
|
|||||||
//! is passed straight through (the decode in hid-report.zig does not negate it).
|
//! is passed straight through (the decode in hid-report.zig does not negate it).
|
||||||
|
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const runtime = @import("runtime");
|
const ipc = @import("ipc");
|
||||||
|
const process = @import("process");
|
||||||
|
const service = @import("service");
|
||||||
|
const input = @import("input");
|
||||||
|
const device_manager = @import("driver");
|
||||||
|
const logging = @import("logging");
|
||||||
const usb = @import("usb");
|
const usb = @import("usb");
|
||||||
const usb_abi = @import("usb-abi");
|
const usb_abi = @import("usb-abi");
|
||||||
const hid = @import("hid-report.zig");
|
const hid = @import("hid-report.zig");
|
||||||
const ipc = runtime.ipc;
|
|
||||||
const process = runtime.process;
|
|
||||||
const input_protocol = @import("input-protocol");
|
const input_protocol = @import("input-protocol");
|
||||||
|
|
||||||
// The current pressed-button bitmask in input-protocol terms.
|
// The current pressed-button bitmask in input-protocol terms.
|
||||||
@@ -28,9 +31,9 @@ fn buttonMask(buttons: u8) u32 {
|
|||||||
return mask;
|
return mask;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn main(init: runtime.process.Init) void {
|
pub fn main(init: process.Init) void {
|
||||||
const argument = init.arguments.get(1) orelse {
|
const argument = init.arguments.get(1) orelse {
|
||||||
_ = runtime.system.write("/system/drivers/usb-hid/mouse: missing device id (argv[1])\n");
|
_ = logging.write("/system/drivers/usb-hid/mouse: missing device id (argv[1])\n");
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
const device_id = std.fmt.parseInt(u64, argument, 10) catch {
|
const device_id = std.fmt.parseInt(u64, argument, 10) catch {
|
||||||
@@ -38,25 +41,25 @@ pub fn main(init: runtime.process.Init) void {
|
|||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
if (runtime.device_manager.hello(.device, device_id) == null) return;
|
if (device_manager.hello(.device, device_id) == null) return;
|
||||||
var device = usb.open(device_id) orelse {
|
var device = usb.open(device_id) orelse {
|
||||||
std.log.info("could not open device {d}", .{device_id});
|
std.log.info("could not open device {d}", .{device_id});
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
const endpoint = device.findEndpoint(usb.transfer_type_interrupt, true) orelse {
|
const endpoint = device.findEndpoint(usb.transfer_type_interrupt, true) orelse {
|
||||||
_ = runtime.system.write("/system/drivers/usb-hid/mouse: no interrupt-IN endpoint\n");
|
_ = logging.write("/system/drivers/usb-hid/mouse: no interrupt-IN endpoint\n");
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
_ = device.controlOut(@bitCast(usb_abi.setProtocol(@enumFromInt(device.interface_number), .boot)));
|
_ = device.controlOut(@bitCast(usb_abi.setProtocol(@enumFromInt(device.interface_number), .boot)));
|
||||||
|
|
||||||
if (!device.subscribeInterrupt(endpoint.address, endpoint.max_packet_size)) {
|
if (!device.subscribeInterrupt(endpoint.address, endpoint.max_packet_size)) {
|
||||||
_ = runtime.system.write("/system/drivers/usb-hid/mouse: interrupt subscribe failed\n");
|
_ = logging.write("/system/drivers/usb-hid/mouse: interrupt subscribe failed\n");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var source = runtime.input.connectSource() orelse {
|
var source = input.connectSource() orelse {
|
||||||
_ = runtime.system.write("/system/drivers/usb-hid/mouse: input service unavailable\n");
|
_ = logging.write("/system/drivers/usb-hid/mouse: input service unavailable\n");
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
_ = process.bindSignals(device.endpoint);
|
_ = process.bindSignals(device.endpoint);
|
||||||
|
|||||||
@@ -12,12 +12,17 @@
|
|||||||
//! physical address, which the data stage DMAs straight to/from.
|
//! physical address, which the data stage DMAs straight to/from.
|
||||||
|
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const runtime = @import("runtime");
|
const ipc = @import("ipc");
|
||||||
|
const process = @import("process");
|
||||||
|
const service = @import("service");
|
||||||
|
const time = @import("time");
|
||||||
|
const device_manager = @import("driver");
|
||||||
|
const memory = @import("memory");
|
||||||
|
const logging = @import("logging");
|
||||||
const usb = @import("usb");
|
const usb = @import("usb");
|
||||||
const scsi = @import("scsi.zig");
|
const scsi = @import("scsi.zig");
|
||||||
const bot = @import("bulk-only-transport.zig");
|
const bot = @import("bulk-only-transport.zig");
|
||||||
const block_protocol = @import("block-protocol");
|
const block_protocol = @import("block-protocol");
|
||||||
const dma = runtime.dma;
|
|
||||||
|
|
||||||
var device_id: u64 = 0;
|
var device_id: u64 = 0;
|
||||||
var device: usb.Device = undefined;
|
var device: usb.Device = undefined;
|
||||||
@@ -26,9 +31,9 @@ var bulk_out: usb.Endpoint = undefined;
|
|||||||
|
|
||||||
// DMA buffers for the transport: the 31-byte CBW, the 13-byte CSW, and a page
|
// DMA buffers for the transport: the 31-byte CBW, the 13-byte CSW, and a page
|
||||||
// for the small command data (INQUIRY / READ CAPACITY / the self-check sector).
|
// for the small command data (INQUIRY / READ CAPACITY / the self-check sector).
|
||||||
var command_wrapper: dma.Region = undefined;
|
var command_wrapper: memory.DmaRegion = undefined;
|
||||||
var status_wrapper: dma.Region = undefined;
|
var status_wrapper: memory.DmaRegion = undefined;
|
||||||
var command_data: dma.Region = undefined;
|
var command_data: memory.DmaRegion = undefined;
|
||||||
|
|
||||||
var next_tag: u32 = 1;
|
var next_tag: u32 = 1;
|
||||||
var block_size: u32 = 512;
|
var block_size: u32 = 512;
|
||||||
@@ -68,26 +73,26 @@ fn transact(cdb: []const u8, direction_in: bool, data_physical: u64, data_length
|
|||||||
/// Device-absent paths stay clean exits: nothing to serve, nothing to retry.
|
/// Device-absent paths stay clean exits: nothing to serve, nothing to retry.
|
||||||
var bring_up_failed = false;
|
var bring_up_failed = false;
|
||||||
|
|
||||||
fn initialise(endpoint: runtime.ipc.Handle) bool {
|
fn initialise(endpoint: ipc.Handle) bool {
|
||||||
_ = endpoint;
|
_ = endpoint;
|
||||||
if (runtime.device_manager.hello(.device, device_id) == null) return false;
|
if (device_manager.hello(.device, device_id) == null) return false;
|
||||||
device = usb.open(device_id) orelse {
|
device = usb.open(device_id) orelse {
|
||||||
std.log.info("could not open device {d}", .{device_id});
|
std.log.info("could not open device {d}", .{device_id});
|
||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
bulk_in = device.findEndpoint(usb.transfer_type_bulk, true) orelse {
|
bulk_in = device.findEndpoint(usb.transfer_type_bulk, true) orelse {
|
||||||
_ = runtime.system.write("/system/drivers/usb-storage: no bulk-IN endpoint\n");
|
_ = logging.write("/system/drivers/usb-storage: no bulk-IN endpoint\n");
|
||||||
bring_up_failed = true;
|
bring_up_failed = true;
|
||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
bulk_out = device.findEndpoint(usb.transfer_type_bulk, false) orelse {
|
bulk_out = device.findEndpoint(usb.transfer_type_bulk, false) orelse {
|
||||||
_ = runtime.system.write("/system/drivers/usb-storage: no bulk-OUT endpoint\n");
|
_ = logging.write("/system/drivers/usb-storage: no bulk-OUT endpoint\n");
|
||||||
bring_up_failed = true;
|
bring_up_failed = true;
|
||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
command_wrapper = dma.alloc(4096, dma.coherent) orelse return false;
|
command_wrapper = memory.dmaAlloc(4096, memory.dma_coherent) orelse return false;
|
||||||
status_wrapper = dma.alloc(4096, dma.coherent) orelse return false;
|
status_wrapper = memory.dmaAlloc(4096, memory.dma_coherent) orelse return false;
|
||||||
command_data = dma.alloc(4096, dma.coherent) orelse return false;
|
command_data = memory.dmaAlloc(4096, memory.dma_coherent) orelse return false;
|
||||||
|
|
||||||
// Bring the LUN up: wait for it to be ready (clearing the initial unit-attention
|
// Bring the LUN up: wait for it to be ready (clearing the initial unit-attention
|
||||||
// with REQUEST SENSE), identify it, and read its capacity.
|
// with REQUEST SENSE), identify it, and read its capacity.
|
||||||
@@ -97,14 +102,14 @@ fn initialise(endpoint: runtime.ipc.Handle) bool {
|
|||||||
if (transact(&ready, false, 0, 0)) break;
|
if (transact(&ready, false, 0, 0)) break;
|
||||||
const sense = scsi.requestSense(18);
|
const sense = scsi.requestSense(18);
|
||||||
_ = transact(&sense, true, command_data.physical, 18);
|
_ = transact(&sense, true, command_data.physical, 18);
|
||||||
runtime.system.sleep(50);
|
time.sleepMillis(50);
|
||||||
}
|
}
|
||||||
const inquiry = scsi.inquiry(36);
|
const inquiry = scsi.inquiry(36);
|
||||||
_ = transact(&inquiry, true, command_data.physical, 36);
|
_ = transact(&inquiry, true, command_data.physical, 36);
|
||||||
|
|
||||||
const capacity_command = scsi.readCapacity10();
|
const capacity_command = scsi.readCapacity10();
|
||||||
if (!transact(&capacity_command, true, command_data.physical, 8)) {
|
if (!transact(&capacity_command, true, command_data.physical, 8)) {
|
||||||
_ = runtime.system.write("/system/drivers/usb-storage: READ CAPACITY failed\n");
|
_ = logging.write("/system/drivers/usb-storage: READ CAPACITY failed\n");
|
||||||
bring_up_failed = true;
|
bring_up_failed = true;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -128,7 +133,7 @@ fn initialise(endpoint: runtime.ipc.Handle) bool {
|
|||||||
|
|
||||||
/// Serve the block protocol: geometry, and whole-block read/write to/from the
|
/// Serve the block protocol: geometry, and whole-block read/write to/from the
|
||||||
/// caller's DMA buffer (named by physical address).
|
/// caller's DMA buffer (named by physical address).
|
||||||
fn onMessage(message: []const u8, reply: []u8, sender: u32, capability: ?runtime.ipc.Handle) usize {
|
fn onMessage(message: []const u8, reply: []u8, sender: u32, capability: ?ipc.Handle) usize {
|
||||||
_ = sender;
|
_ = sender;
|
||||||
_ = capability;
|
_ = capability;
|
||||||
if (message.len < block_protocol.request_size) return 0;
|
if (message.len < block_protocol.request_size) return 0;
|
||||||
@@ -167,21 +172,21 @@ fn writeReply(reply: []u8, value: block_protocol.Reply) usize {
|
|||||||
return bytes.len;
|
return bytes.len;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn main(init: runtime.process.Init) void {
|
pub fn main(init: process.Init) void {
|
||||||
const argument = init.arguments.get(1) orelse {
|
const argument = init.arguments.get(1) orelse {
|
||||||
_ = runtime.system.write("/system/drivers/usb-storage: missing device id (argv[1])\n");
|
_ = logging.write("/system/drivers/usb-storage: missing device id (argv[1])\n");
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
device_id = std.fmt.parseInt(u64, argument, 10) catch {
|
device_id = std.fmt.parseInt(u64, argument, 10) catch {
|
||||||
std.log.info("malformed device id '{s}'", .{argument});
|
std.log.info("malformed device id '{s}'", .{argument});
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
runtime.service.run(block_protocol.message_maximum, .{
|
service.run(block_protocol.message_maximum, .{
|
||||||
.service = .block,
|
.service = .block,
|
||||||
.init = initialise,
|
.init = initialise,
|
||||||
.on_message = onMessage,
|
.on_message = onMessage,
|
||||||
});
|
});
|
||||||
// A failure exit (nonzero -> .aborted) tells the device manager to restart
|
// A failure exit (nonzero -> .aborted) tells the device manager to restart
|
||||||
// us with backoff; a clean return means there was nothing to serve.
|
// us with backoff; a clean return means there was nothing to serve.
|
||||||
if (bring_up_failed) runtime.system.exit(1);
|
if (bring_up_failed) process.exit(1);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,9 +14,16 @@
|
|||||||
//! not the controller is running.
|
//! not the controller is running.
|
||||||
|
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const runtime = @import("runtime");
|
const device = @import("driver");
|
||||||
|
const ipc = @import("ipc");
|
||||||
|
const process = @import("process");
|
||||||
|
const service = @import("service");
|
||||||
|
const time = @import("time");
|
||||||
|
const input = @import("input");
|
||||||
|
const device_manager = @import("driver");
|
||||||
|
const memory = @import("memory");
|
||||||
|
const logging = @import("logging");
|
||||||
const device_manager_protocol = @import("device-manager-protocol");
|
const device_manager_protocol = @import("device-manager-protocol");
|
||||||
const device = runtime.device;
|
|
||||||
const usb_ids = @import("usb-ids");
|
const usb_ids = @import("usb-ids");
|
||||||
const usb_abi = @import("usb-abi");
|
const usb_abi = @import("usb-abi");
|
||||||
const usb_transfer_protocol = @import("usb-transfer-protocol");
|
const usb_transfer_protocol = @import("usb-transfer-protocol");
|
||||||
@@ -27,7 +34,7 @@ var controller: ?library.Controller = null;
|
|||||||
|
|
||||||
/// This driver's service endpoint (registered as `.usb_bus`), where class-driver
|
/// This driver's service endpoint (registered as `.usb_bus`), where class-driver
|
||||||
/// requests, signals, and the interrupt-poll timer all arrive.
|
/// requests, signals, and the interrupt-poll timer all arrive.
|
||||||
var service_endpoint: runtime.ipc.Handle = 0;
|
var service_endpoint: ipc.Handle = 0;
|
||||||
|
|
||||||
/// How often the driver drains the event ring for interrupt reports (~125 Hz),
|
/// How often the driver drains the event ring for interrupt reports (~125 Hz),
|
||||||
/// re-armed each tick. Frequent enough for responsive input.
|
/// re-armed each tick. Frequent enough for responsive input.
|
||||||
@@ -69,7 +76,7 @@ var controller_id: u64 = device_manager_protocol.no_device;
|
|||||||
/// Claim the assigned controller, find its register window, and hello the
|
/// Claim the assigned controller, find its register window, and hello the
|
||||||
/// manager. Any failure returns false: the process exits cleanly, which the
|
/// manager. Any failure returns false: the process exits cleanly, which the
|
||||||
/// manager reads as "meant to stop" — a missing assignment is not a crash loop.
|
/// manager reads as "meant to stop" — a missing assignment is not a crash loop.
|
||||||
fn initialise(endpoint: runtime.ipc.Handle) bool {
|
fn initialise(endpoint: ipc.Handle) bool {
|
||||||
service_endpoint = endpoint;
|
service_endpoint = endpoint;
|
||||||
if (!device.claim(controller_id)) {
|
if (!device.claim(controller_id)) {
|
||||||
std.log.info("unable to claim controller device {d}", .{controller_id});
|
std.log.info("unable to claim controller device {d}", .{controller_id});
|
||||||
@@ -77,8 +84,8 @@ fn initialise(endpoint: runtime.ipc.Handle) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Fetch our own descriptor back for the controller's resources.
|
// Fetch our own descriptor back for the controller's resources.
|
||||||
const buffer = runtime.allocator().alloc(device.DeviceDescriptor, 64) catch {
|
const buffer = memory.allocator().alloc(device.DeviceDescriptor, 64) catch {
|
||||||
_ = runtime.system.write("/system/drivers/usb-xhci-bus: out of memory\n");
|
_ = logging.write("/system/drivers/usb-xhci-bus: out of memory\n");
|
||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
const total = device.enumerate(buffer);
|
const total = device.enumerate(buffer);
|
||||||
@@ -107,14 +114,14 @@ fn initialise(endpoint: runtime.ipc.Handle) bool {
|
|||||||
register_window.len,
|
register_window.len,
|
||||||
});
|
});
|
||||||
register_base = device.mmioMap(controller_id, register_index) orelse {
|
register_base = device.mmioMap(controller_id, register_index) orelse {
|
||||||
_ = runtime.system.write("/system/drivers/usb-xhci-bus: mmio_map failed\n");
|
_ = logging.write("/system/drivers/usb-xhci-bus: mmio_map failed\n");
|
||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Bring the controller up: reset it, stand up the command and event rings,
|
// Bring the controller up: reset it, stand up the command and event rings,
|
||||||
// and start it running (the hardware half lives in usb-xhci-library.zig).
|
// and start it running (the hardware half lives in usb-xhci-library.zig).
|
||||||
controller = library.Controller.init(register_base) orelse {
|
controller = library.Controller.init(register_base) orelse {
|
||||||
_ = runtime.system.write("/system/drivers/usb-xhci-bus: controller reset/bring-up failed\n");
|
_ = logging.write("/system/drivers/usb-xhci-bus: controller reset/bring-up failed\n");
|
||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
std.log.info("controller running ({d} slots, {d}-byte contexts)", .{
|
std.log.info("controller running ({d} slots, {d}-byte contexts)", .{
|
||||||
@@ -125,23 +132,23 @@ fn initialise(endpoint: runtime.ipc.Handle) bool {
|
|||||||
// ring, the doorbell, and the cycle-bit bookkeeping. If this completes, the
|
// ring, the doorbell, and the cycle-bit bookkeeping. If this completes, the
|
||||||
// engine is sound; transfers build on exactly this machinery.
|
// engine is sound; transfers build on exactly this machinery.
|
||||||
if (controller.?.noOpCommand()) {
|
if (controller.?.noOpCommand()) {
|
||||||
_ = runtime.system.write("/system/drivers/usb-xhci-bus: command ring running (no-op ok)\n");
|
_ = logging.write("/system/drivers/usb-xhci-bus: command ring running (no-op ok)\n");
|
||||||
} else {
|
} else {
|
||||||
_ = runtime.system.write("/system/drivers/usb-xhci-bus: no-op command did not complete\n");
|
_ = logging.write("/system/drivers/usb-xhci-bus: no-op command did not complete\n");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// The handshake (role: bus — we enumerate USB ports and report the devices
|
// The handshake (role: bus — we enumerate USB ports and report the devices
|
||||||
// behind them), inside the manager's hello deadline. Keep the handle: the
|
// behind them), inside the manager's hello deadline. Keep the handle: the
|
||||||
// tick's hot-plug dispatch reports through it.
|
// tick's hot-plug dispatch reports through it.
|
||||||
const handle = runtime.device_manager.hello(.bus, controller_id) orelse return false;
|
const handle = device_manager.hello(.bus, controller_id) orelse return false;
|
||||||
manager_handle = handle;
|
manager_handle = handle;
|
||||||
|
|
||||||
scanPorts(handle);
|
scanPorts(handle);
|
||||||
|
|
||||||
// Arm the poll timer that drains interrupt reports from the event ring. It is
|
// Arm the poll timer that drains interrupt reports from the event ring. It is
|
||||||
// re-armed on each tick in onNotification; class drivers subscribe later.
|
// re-armed on each tick in onNotification; class drivers subscribe later.
|
||||||
_ = runtime.system.timerOnce(service_endpoint, poll_interval_ms);
|
_ = time.timerOnce(service_endpoint, poll_interval_ms);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -168,15 +175,15 @@ fn speedName(speed: u32) []const u8 {
|
|||||||
/// report one child per interface — carrying the interface's (class, subclass,
|
/// report one child per interface — carrying the interface's (class, subclass,
|
||||||
/// protocol) triple as identity, which is what the device manager matches a
|
/// protocol) triple as identity, which is what the device manager matches a
|
||||||
/// class driver against.
|
/// class driver against.
|
||||||
var manager_handle: ?runtime.ipc.Handle = null;
|
var manager_handle: ?ipc.Handle = null;
|
||||||
|
|
||||||
// Per-root-port connected state from the previous tick, so the poll acts on
|
// Per-root-port connected state from the previous tick, so the poll acts on
|
||||||
// empty->connected transitions (edge), never re-attempting a level every tick.
|
// empty->connected transitions (edge), never re-attempting a level every tick.
|
||||||
var prev_connected: [64]bool = [_]bool{false} ** 64;
|
var prev_connected: [64]bool = [_]bool{false} ** 64;
|
||||||
|
|
||||||
fn scanPorts(manager: runtime.ipc.Handle) void {
|
fn scanPorts(manager: ipc.Handle) void {
|
||||||
const engine = if (controller) |*c| c else {
|
const engine = if (controller) |*c| c else {
|
||||||
_ = runtime.system.write("/system/drivers/usb-xhci-bus: controller not initialised\n");
|
_ = logging.write("/system/drivers/usb-xhci-bus: controller not initialised\n");
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
std.log.info("{d} root-hub ports", .{engine.max_ports});
|
std.log.info("{d} root-hub ports", .{engine.max_ports});
|
||||||
@@ -190,7 +197,7 @@ fn scanPorts(manager: runtime.ipc.Handle) void {
|
|||||||
bringUpPort(manager, engine, port);
|
bringUpPort(manager, engine, port);
|
||||||
}
|
}
|
||||||
if (connected == 0) {
|
if (connected == 0) {
|
||||||
_ = runtime.system.write("/system/drivers/usb-xhci-bus: no devices connected\n");
|
_ = logging.write("/system/drivers/usb-xhci-bus: no devices connected\n");
|
||||||
engine.dumpPortTopology(); // help diagnose an empty scan: the xECP map + raw PORTSC
|
engine.dumpPortTopology(); // help diagnose an empty scan: the xECP map + raw PORTSC
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -198,7 +205,7 @@ fn scanPorts(manager: runtime.ipc.Handle) void {
|
|||||||
/// Bring up whatever is on `port`: setup + enumerate + register/report one child
|
/// Bring up whatever is on `port`: setup + enumerate + register/report one child
|
||||||
/// per interface. Shared by the boot scan and hot-plug (a port-change event with
|
/// per interface. Shared by the boot scan and hot-plug (a port-change event with
|
||||||
/// the port now connected).
|
/// the port now connected).
|
||||||
fn bringUpPort(manager: runtime.ipc.Handle, engine: *library.Controller, port: u32) void {
|
fn bringUpPort(manager: ipc.Handle, engine: *library.Controller, port: u32) void {
|
||||||
const speed = (engine.portStatus(port) >> 10) & 0xF; // the PORTSC port-speed class
|
const speed = (engine.portStatus(port) >> 10) & 0xF; // the PORTSC port-speed class
|
||||||
std.log.info("port {d} connected — {s} (speed class {d})", .{ port, speedName(speed), speed });
|
std.log.info("port {d} connected — {s} (speed class {d})", .{ port, speedName(speed), speed });
|
||||||
|
|
||||||
@@ -249,7 +256,7 @@ fn hubPortKey(hub_slot: u8, port: u16) u32 {
|
|||||||
/// new device) or a disconnect (tear the old one down). Recurses for a hub
|
/// new device) or a disconnect (tear the old one down). Recurses for a hub
|
||||||
/// behind a hub — a nested hub is set up on connect and its downstream devices
|
/// behind a hub — a nested hub is set up on connect and its downstream devices
|
||||||
/// torn down first on disconnect.
|
/// torn down first on disconnect.
|
||||||
fn bringUpBehindHub(manager: runtime.ipc.Handle, engine: *library.Controller, hub: *library.Device, port: u16) void {
|
fn bringUpBehindHub(manager: ipc.Handle, engine: *library.Controller, hub: *library.Device, port: u16) void {
|
||||||
const status = engine.hubPortStatusAck(hub, port) orelse return;
|
const status = engine.hubPortStatusAck(hub, port) orelse return;
|
||||||
const connected = library.Controller.hubPortConnected(status);
|
const connected = library.Controller.hubPortConnected(status);
|
||||||
const existing = engine.deviceOnHubPort(hub, port);
|
const existing = engine.deviceOnHubPort(hub, port);
|
||||||
@@ -291,7 +298,7 @@ fn bringUpBehindHub(manager: runtime.ipc.Handle, engine: *library.Controller, hu
|
|||||||
/// Tear down a device that disconnected from a hub: recursively tear down its
|
/// Tear down a device that disconnected from a hub: recursively tear down its
|
||||||
/// own downstream devices first if it is a hub, report each interface removed,
|
/// own downstream devices first if it is a hub, report each interface removed,
|
||||||
/// then Disable Slot. Mirrors tearDownPort for a hub-attached device.
|
/// then Disable Slot. Mirrors tearDownPort for a hub-attached device.
|
||||||
fn tearDownHubDevice(manager: runtime.ipc.Handle, engine: *library.Controller, dev: *library.Device) void {
|
fn tearDownHubDevice(manager: ipc.Handle, engine: *library.Controller, dev: *library.Device) void {
|
||||||
// A hub that left takes its whole subtree with it — tear children down first.
|
// A hub that left takes its whole subtree with it — tear children down first.
|
||||||
if (dev.is_hub) {
|
if (dev.is_hub) {
|
||||||
while (engine.nextChildOf(dev.slot_id, 0)) |child| tearDownHubDevice(manager, engine, child);
|
while (engine.nextChildOf(dev.slot_id, 0)) |child| tearDownHubDevice(manager, engine, child);
|
||||||
@@ -305,7 +312,7 @@ fn tearDownHubDevice(manager: runtime.ipc.Handle, engine: *library.Controller, d
|
|||||||
.bus_address = (@as(u64, key) << 8) | interface.number,
|
.bus_address = (@as(u64, key) << 8) | interface.number,
|
||||||
};
|
};
|
||||||
var reply: [device_manager_protocol.message_maximum]u8 = undefined;
|
var reply: [device_manager_protocol.message_maximum]u8 = undefined;
|
||||||
_ = runtime.ipc.call(manager, std.mem.asBytes(&event), &reply) catch {};
|
_ = ipc.call(manager, std.mem.asBytes(&event), &reply) catch {};
|
||||||
interface.registered_device_id = 0;
|
interface.registered_device_id = 0;
|
||||||
}
|
}
|
||||||
engine.tearDownDevice(dev);
|
engine.tearDownDevice(dev);
|
||||||
@@ -325,7 +332,7 @@ fn deviceIsHub(usb_device: *const library.Device) bool {
|
|||||||
/// interface as removed (the manager prunes the node, notifies watchers, and
|
/// interface as removed (the manager prunes the node, notifies watchers, and
|
||||||
/// stops the class driver's world honestly), then release the controller-side
|
/// stops the class driver's world honestly), then release the controller-side
|
||||||
/// device state (Disable Slot).
|
/// device state (Disable Slot).
|
||||||
fn tearDownPort(manager: runtime.ipc.Handle, engine: *library.Controller, port: u32) void {
|
fn tearDownPort(manager: ipc.Handle, engine: *library.Controller, port: u32) void {
|
||||||
const usb_device = engine.deviceOnPort(port) orelse return;
|
const usb_device = engine.deviceOnPort(port) orelse return;
|
||||||
std.log.info("port {d} disconnected", .{port});
|
std.log.info("port {d} disconnected", .{port});
|
||||||
for (usb_device.interfaces[0..usb_device.interface_count]) |*interface| {
|
for (usb_device.interfaces[0..usb_device.interface_count]) |*interface| {
|
||||||
@@ -335,7 +342,7 @@ fn tearDownPort(manager: runtime.ipc.Handle, engine: *library.Controller, port:
|
|||||||
.bus_address = (@as(u64, port) << 8) | interface.number,
|
.bus_address = (@as(u64, port) << 8) | interface.number,
|
||||||
};
|
};
|
||||||
var reply: [device_manager_protocol.message_maximum]u8 = undefined;
|
var reply: [device_manager_protocol.message_maximum]u8 = undefined;
|
||||||
_ = runtime.ipc.call(manager, std.mem.asBytes(&event), &reply) catch {
|
_ = ipc.call(manager, std.mem.asBytes(&event), &reply) catch {
|
||||||
std.log.info("child-removed report for port {d} interface {d} failed", .{ port, interface.number });
|
std.log.info("child-removed report for port {d} interface {d} failed", .{ port, interface.number });
|
||||||
};
|
};
|
||||||
interface.registered_device_id = 0;
|
interface.registered_device_id = 0;
|
||||||
@@ -348,7 +355,7 @@ fn tearDownPort(manager: runtime.ipc.Handle, engine: *library.Controller, port:
|
|||||||
/// manager can match a class driver (HID keyboard, mouse, mass storage); the
|
/// manager can match a class driver (HID keyboard, mouse, mass storage); the
|
||||||
/// registered device id becomes that driver's argv[1] assignment. Returns the
|
/// registered device id becomes that driver's argv[1] assignment. Returns the
|
||||||
/// registered device id, or null if registration or the report failed.
|
/// registered device id, or null if registration or the report failed.
|
||||||
fn reportInterface(manager: runtime.ipc.Handle, port: u32, interface: library.InterfaceInfo) ?u64 {
|
fn reportInterface(manager: ipc.Handle, port: u32, interface: library.InterfaceInfo) ?u64 {
|
||||||
const identity = usb_ids.packTriple(interface.class, interface.subclass, interface.protocol);
|
const identity = usb_ids.packTriple(interface.class, interface.subclass, interface.protocol);
|
||||||
|
|
||||||
// A USB device is reached through its controller, not by MMIO, so the child
|
// A USB device is reached through its controller, not by MMIO, so the child
|
||||||
@@ -378,7 +385,7 @@ fn reportInterface(manager: runtime.ipc.Handle, port: u32, interface: library.In
|
|||||||
.device_id = registered,
|
.device_id = registered,
|
||||||
};
|
};
|
||||||
var reply: [device_manager_protocol.message_maximum]u8 = undefined;
|
var reply: [device_manager_protocol.message_maximum]u8 = undefined;
|
||||||
_ = runtime.ipc.call(manager, std.mem.asBytes(&report), &reply) catch {
|
_ = ipc.call(manager, std.mem.asBytes(&report), &reply) catch {
|
||||||
std.log.info("child report for port {d} interface {d} failed", .{ port, interface.number });
|
std.log.info("child report for port {d} interface {d} failed", .{ port, interface.number });
|
||||||
return null;
|
return null;
|
||||||
};
|
};
|
||||||
@@ -396,7 +403,7 @@ fn reportInterface(manager: runtime.ipc.Handle, port: u32, interface: library.In
|
|||||||
|
|
||||||
/// Serve the USB transfer protocol: a class driver opens its device, then issues
|
/// Serve the USB transfer protocol: a class driver opens its device, then issues
|
||||||
/// control / interrupt-subscribe / bulk requests against it.
|
/// control / interrupt-subscribe / bulk requests against it.
|
||||||
fn onMessage(message: []const u8, reply: []u8, sender: u32, capability: ?runtime.ipc.Handle) usize {
|
fn onMessage(message: []const u8, reply: []u8, sender: u32, capability: ?ipc.Handle) usize {
|
||||||
_ = sender;
|
_ = sender;
|
||||||
if (message.len < 4) return 0;
|
if (message.len < 4) return 0;
|
||||||
const operation = std.mem.readInt(u32, message[0..4], .little);
|
const operation = std.mem.readInt(u32, message[0..4], .little);
|
||||||
@@ -418,7 +425,7 @@ fn writeReply(reply: []u8, value: anytype) usize {
|
|||||||
/// open: resolve the assigned device id to an interface, remember the caller's
|
/// open: resolve the assigned device id to an interface, remember the caller's
|
||||||
/// endpoint (for interrupt reports), and answer with a device token + the
|
/// endpoint (for interrupt reports), and answer with a device token + the
|
||||||
/// interface's endpoints so the class driver need not re-read the config.
|
/// interface's endpoints so the class driver need not re-read the config.
|
||||||
fn handleOpen(message: []const u8, reply: []u8, capability: ?runtime.ipc.Handle) usize {
|
fn handleOpen(message: []const u8, reply: []u8, capability: ?ipc.Handle) usize {
|
||||||
if (message.len < @sizeOf(usb_transfer_protocol.OpenRequest)) return writeReply(reply, usb_transfer_protocol.OpenReply{ .status = -1, .endpoint_count = 0, .device_token = 0, .interface_class = 0, .interface_subclass = 0, .interface_protocol = 0, .interface_number = 0 });
|
if (message.len < @sizeOf(usb_transfer_protocol.OpenRequest)) return writeReply(reply, usb_transfer_protocol.OpenReply{ .status = -1, .endpoint_count = 0, .device_token = 0, .interface_class = 0, .interface_subclass = 0, .interface_protocol = 0, .interface_number = 0 });
|
||||||
const request = std.mem.bytesToValue(usb_transfer_protocol.OpenRequest, message[0..@sizeOf(usb_transfer_protocol.OpenRequest)]);
|
const request = std.mem.bytesToValue(usb_transfer_protocol.OpenRequest, message[0..@sizeOf(usb_transfer_protocol.OpenRequest)]);
|
||||||
const engine = if (controller) |*c| c else return writeReply(reply, usb_transfer_protocol.OpenReply{ .status = -1, .endpoint_count = 0, .device_token = 0, .interface_class = 0, .interface_subclass = 0, .interface_protocol = 0, .interface_number = 0 });
|
const engine = if (controller) |*c| c else return writeReply(reply, usb_transfer_protocol.OpenReply{ .status = -1, .endpoint_count = 0, .device_token = 0, .interface_class = 0, .interface_subclass = 0, .interface_protocol = 0, .interface_number = 0 });
|
||||||
@@ -493,7 +500,7 @@ fn handleBulk(message: []const u8, reply: []u8) usize {
|
|||||||
/// The poll timer landed: drain any interrupt reports off the event ring and push
|
/// The poll timer landed: drain any interrupt reports off the event ring and push
|
||||||
/// each to the class driver that subscribed, then re-arm the timer.
|
/// each to the class driver that subscribed, then re-arm the timer.
|
||||||
fn onNotification(badge: u64) void {
|
fn onNotification(badge: u64) void {
|
||||||
if (badge & runtime.ipc.notify_timer_bit == 0) return;
|
if (badge & ipc.notify_timer_bit == 0) return;
|
||||||
if (controller) |*engine| {
|
if (controller) |*engine| {
|
||||||
engine.pump();
|
engine.pump();
|
||||||
// Poll every root port and reconcile — a device present but not yet
|
// Poll every root port and reconcile — a device present but not yet
|
||||||
@@ -550,22 +557,22 @@ fn onNotification(badge: u64) void {
|
|||||||
};
|
};
|
||||||
const n = @min(report.length, usb_transfer_protocol.max_report_data);
|
const n = @min(report.length, usb_transfer_protocol.max_report_data);
|
||||||
@memcpy(message.data[0..n], report.data[0..n]);
|
@memcpy(message.data[0..n], report.data[0..n]);
|
||||||
_ = runtime.ipc.send(report.report_endpoint, std.mem.asBytes(&message));
|
_ = ipc.send(report.report_endpoint, std.mem.asBytes(&message));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ = runtime.system.timerOnce(service_endpoint, poll_interval_ms);
|
_ = time.timerOnce(service_endpoint, poll_interval_ms);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn main(init: runtime.process.Init) void {
|
pub fn main(init: process.Init) void {
|
||||||
const argument = init.arguments.get(1) orelse {
|
const argument = init.arguments.get(1) orelse {
|
||||||
_ = runtime.system.write("/system/drivers/usb-xhci-bus: missing controller device id (argv[1])\n");
|
_ = logging.write("/system/drivers/usb-xhci-bus: missing controller device id (argv[1])\n");
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
controller_id = std.fmt.parseInt(u64, argument, 10) catch {
|
controller_id = std.fmt.parseInt(u64, argument, 10) catch {
|
||||||
std.log.info("malformed controller device id '{s}'", .{argument});
|
std.log.info("malformed controller device id '{s}'", .{argument});
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
runtime.service.run(usb_transfer_protocol.message_maximum, .{
|
service.run(usb_transfer_protocol.message_maximum, .{
|
||||||
.service = .usb_bus,
|
.service = .usb_bus,
|
||||||
.init = initialise,
|
.init = initialise,
|
||||||
.on_message = onMessage,
|
.on_message = onMessage,
|
||||||
|
|||||||
@@ -19,12 +19,11 @@
|
|||||||
//! proven.
|
//! proven.
|
||||||
|
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const runtime = @import("runtime");
|
const time = @import("time");
|
||||||
|
const memory = @import("memory");
|
||||||
const mmio = @import("mmio");
|
const mmio = @import("mmio");
|
||||||
const usb_abi = @import("usb-abi");
|
const usb_abi = @import("usb-abi");
|
||||||
const usb_ids = @import("usb-ids");
|
const usb_ids = @import("usb-ids");
|
||||||
const dma = runtime.dma;
|
|
||||||
const system = runtime.system;
|
|
||||||
|
|
||||||
// --- register offsets -------------------------------------------------------
|
// --- register offsets -------------------------------------------------------
|
||||||
|
|
||||||
@@ -134,7 +133,7 @@ const trbs_per_ring = page_size / @sizeOf(Trb); // 256
|
|||||||
// A producer ring (command ring, or a transfer ring): a page of TRBs whose last
|
// A producer ring (command ring, or a transfer ring): a page of TRBs whose last
|
||||||
// entry is a Link TRB back to the start. `cycle` is the producer cycle state.
|
// entry is a Link TRB back to the start. `cycle` is the producer cycle state.
|
||||||
const ProducerRing = struct {
|
const ProducerRing = struct {
|
||||||
region: dma.Region,
|
region: memory.DmaRegion,
|
||||||
enqueue_index: usize = 0,
|
enqueue_index: usize = 0,
|
||||||
cycle: bool = true,
|
cycle: bool = true,
|
||||||
|
|
||||||
@@ -182,8 +181,8 @@ const ProducerRing = struct {
|
|||||||
// The event ring: a single segment the controller fills and the driver drains.
|
// The event ring: a single segment the controller fills and the driver drains.
|
||||||
// `cycle` is the consumer cycle state, flipped each time the dequeue wraps.
|
// `cycle` is the consumer cycle state, flipped each time the dequeue wraps.
|
||||||
const EventRing = struct {
|
const EventRing = struct {
|
||||||
segment: dma.Region,
|
segment: memory.DmaRegion,
|
||||||
table: dma.Region,
|
table: memory.DmaRegion,
|
||||||
dequeue_index: usize = 0,
|
dequeue_index: usize = 0,
|
||||||
cycle: bool = true,
|
cycle: bool = true,
|
||||||
|
|
||||||
@@ -269,12 +268,12 @@ pub const Device = struct {
|
|||||||
port: u32 = 0,
|
port: u32 = 0,
|
||||||
speed: u32 = 0,
|
speed: u32 = 0,
|
||||||
max_packet_size_0: u32 = 8,
|
max_packet_size_0: u32 = 8,
|
||||||
input_context: dma.Region = .{ .virtual = 0, .physical = 0 },
|
input_context: memory.DmaRegion = .{ .virtual = 0, .physical = 0 },
|
||||||
device_context: dma.Region = .{ .virtual = 0, .physical = 0 },
|
device_context: memory.DmaRegion = .{ .virtual = 0, .physical = 0 },
|
||||||
ep0_ring: ProducerRing = .{ .region = .{ .virtual = 0, .physical = 0 } },
|
ep0_ring: ProducerRing = .{ .region = .{ .virtual = 0, .physical = 0 } },
|
||||||
// A page-sized bounce buffer for control-transfer data (descriptors are read
|
// A page-sized bounce buffer for control-transfer data (descriptors are read
|
||||||
// here, then copied out to the caller).
|
// here, then copied out to the caller).
|
||||||
control_buffer: dma.Region = .{ .virtual = 0, .physical = 0 },
|
control_buffer: memory.DmaRegion = .{ .virtual = 0, .physical = 0 },
|
||||||
device_descriptor: usb_abi.DeviceDescriptor = std.mem.zeroes(usb_abi.DeviceDescriptor),
|
device_descriptor: usb_abi.DeviceDescriptor = std.mem.zeroes(usb_abi.DeviceDescriptor),
|
||||||
configuration_value: u8 = 0,
|
configuration_value: u8 = 0,
|
||||||
interface_count: u8 = 0,
|
interface_count: u8 = 0,
|
||||||
@@ -308,7 +307,7 @@ const Subscription = struct {
|
|||||||
dci: u32 = 0,
|
dci: u32 = 0,
|
||||||
endpoint_address: u8 = 0,
|
endpoint_address: u8 = 0,
|
||||||
ring: *ProducerRing = undefined,
|
ring: *ProducerRing = undefined,
|
||||||
buffer: dma.Region = .{ .virtual = 0, .physical = 0 },
|
buffer: memory.DmaRegion = .{ .virtual = 0, .physical = 0 },
|
||||||
max_length: u16 = 0,
|
max_length: u16 = 0,
|
||||||
armed_trb_physical: u64 = 0,
|
armed_trb_physical: u64 = 0,
|
||||||
// The bus layer's per-subscription IPC state (opaque here): the class driver's
|
// The bus layer's per-subscription IPC state (opaque here): the class driver's
|
||||||
@@ -447,7 +446,7 @@ pub const Controller = struct {
|
|||||||
max_ports: u32,
|
max_ports: u32,
|
||||||
context_size: usize, // 32 or 64 (CSZ)
|
context_size: usize, // 32 or 64 (CSZ)
|
||||||
|
|
||||||
device_context_array: dma.Region,
|
device_context_array: memory.DmaRegion,
|
||||||
command_ring: ProducerRing,
|
command_ring: ProducerRing,
|
||||||
event_ring: EventRing,
|
event_ring: EventRing,
|
||||||
devices: [max_devices]Device = [_]Device{.{}} ** max_devices,
|
devices: [max_devices]Device = [_]Device{.{}} ** max_devices,
|
||||||
@@ -601,19 +600,19 @@ pub const Controller = struct {
|
|||||||
write32(self.operational(op_config), self.max_slots);
|
write32(self.operational(op_config), self.max_slots);
|
||||||
|
|
||||||
// The Device Context Base Address Array (entry 0 = scratchpad array).
|
// The Device Context Base Address Array (entry 0 = scratchpad array).
|
||||||
self.device_context_array = dma.alloc(page_size, dma.coherent) orelse return null;
|
self.device_context_array = memory.dmaAlloc(page_size, memory.dma_coherent) orelse return null;
|
||||||
self.setupScratchpad(register_base);
|
self.setupScratchpad(register_base);
|
||||||
write64(self.operational(op_dcbaap), self.device_context_array.physical);
|
write64(self.operational(op_dcbaap), self.device_context_array.physical);
|
||||||
|
|
||||||
// The command ring: a page of TRBs, last entry a Link back to the start.
|
// The command ring: a page of TRBs, last entry a Link back to the start.
|
||||||
self.command_ring = .{ .region = dma.alloc(page_size, dma.coherent) orelse return null };
|
self.command_ring = .{ .region = memory.dmaAlloc(page_size, memory.dma_coherent) orelse return null };
|
||||||
self.command_ring.installLink();
|
self.command_ring.installLink();
|
||||||
write64(self.operational(op_crcr), self.command_ring.region.physical | cycle_bit);
|
write64(self.operational(op_crcr), self.command_ring.region.physical | cycle_bit);
|
||||||
|
|
||||||
// The event ring: one segment + a one-entry segment table.
|
// The event ring: one segment + a one-entry segment table.
|
||||||
self.event_ring = .{
|
self.event_ring = .{
|
||||||
.segment = dma.alloc(page_size, dma.coherent) orelse return null,
|
.segment = memory.dmaAlloc(page_size, memory.dma_coherent) orelse return null,
|
||||||
.table = dma.alloc(page_size, dma.coherent) orelse return null,
|
.table = memory.dmaAlloc(page_size, memory.dma_coherent) orelse return null,
|
||||||
};
|
};
|
||||||
const table: *volatile ErstEntry = @ptrFromInt(self.event_ring.table.virtual);
|
const table: *volatile ErstEntry = @ptrFromInt(self.event_ring.table.virtual);
|
||||||
table.ring_segment_base = self.event_ring.segment.physical;
|
table.ring_segment_base = self.event_ring.segment.physical;
|
||||||
@@ -663,11 +662,11 @@ pub const Controller = struct {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// One page per scratchpad buffer, plus a page holding their address array.
|
// One page per scratchpad buffer, plus a page holding their address array.
|
||||||
const pointers = dma.alloc(page_size, dma.coherent) orelse return;
|
const pointers = memory.dmaAlloc(page_size, memory.dma_coherent) orelse return;
|
||||||
const pointer_array: [*]volatile u64 = @ptrFromInt(pointers.virtual);
|
const pointer_array: [*]volatile u64 = @ptrFromInt(pointers.virtual);
|
||||||
var index: u32 = 0;
|
var index: u32 = 0;
|
||||||
while (index < count) : (index += 1) {
|
while (index < count) : (index += 1) {
|
||||||
const buffer = dma.alloc(page_size, dma.coherent) orelse return;
|
const buffer = memory.dmaAlloc(page_size, memory.dma_coherent) orelse return;
|
||||||
pointer_array[index] = buffer.physical;
|
pointer_array[index] = buffer.physical;
|
||||||
}
|
}
|
||||||
array[0] = pointers.physical;
|
array[0] = pointers.physical;
|
||||||
@@ -675,16 +674,16 @@ pub const Controller = struct {
|
|||||||
|
|
||||||
// Spin (with a deadline) until every bit in `mask` reads back as zero / one.
|
// Spin (with a deadline) until every bit in `mask` reads back as zero / one.
|
||||||
fn waitClear(address: usize, mask: u32) bool {
|
fn waitClear(address: usize, mask: u32) bool {
|
||||||
const deadline = system.clock() + 1_000_000_000; // 1 s
|
const deadline = time.clock() + 1_000_000_000; // 1 s
|
||||||
while (read32(address) & mask != 0) {
|
while (read32(address) & mask != 0) {
|
||||||
if (system.clock() >= deadline) return false;
|
if (time.clock() >= deadline) return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
fn waitSet(address: usize, mask: u32) bool {
|
fn waitSet(address: usize, mask: u32) bool {
|
||||||
const deadline = system.clock() + 1_000_000_000;
|
const deadline = time.clock() + 1_000_000_000;
|
||||||
while (read32(address) & mask == 0) {
|
while (read32(address) & mask == 0) {
|
||||||
if (system.clock() >= deadline) return false;
|
if (time.clock() >= deadline) return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -723,7 +722,7 @@ pub const Controller = struct {
|
|||||||
write64(self.interrupter(event_ring_dequeue_pointer), dequeue | (1 << 3));
|
write64(self.interrupter(event_ring_dequeue_pointer), dequeue | (1 << 3));
|
||||||
return event;
|
return event;
|
||||||
}
|
}
|
||||||
if (system.clock() >= deadline_ns) return null;
|
if (time.clock() >= deadline_ns) return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -732,7 +731,7 @@ pub const Controller = struct {
|
|||||||
/// serviced even during a command); other events are ignored. Returns the
|
/// serviced even during a command); other events are ignored. Returns the
|
||||||
/// completion code, or null on timeout.
|
/// completion code, or null on timeout.
|
||||||
fn awaitCommand(self: *Controller, command_physical: u64) ?u8 {
|
fn awaitCommand(self: *Controller, command_physical: u64) ?u8 {
|
||||||
const deadline = system.clock() + 1_000_000_000;
|
const deadline = time.clock() + 1_000_000_000;
|
||||||
while (true) {
|
while (true) {
|
||||||
const event = self.nextEvent(deadline) orelse return null;
|
const event = self.nextEvent(deadline) orelse return null;
|
||||||
const kind = trbType(event.control);
|
const kind = trbType(event.control);
|
||||||
@@ -764,9 +763,9 @@ pub const Controller = struct {
|
|||||||
// bits and PED are untouched) and preserving PP.
|
// bits and PED are untouched) and preserving PP.
|
||||||
const before = self.portStatus(port);
|
const before = self.portStatus(port);
|
||||||
self.writePortStatus(port, (before & ~portsc_write_1_to_clear) | portsc_reset);
|
self.writePortStatus(port, (before & ~portsc_write_1_to_clear) | portsc_reset);
|
||||||
const deadline = system.clock() + 500_000_000;
|
const deadline = time.clock() + 500_000_000;
|
||||||
while (self.portStatus(port) & portsc_reset_change == 0) {
|
while (self.portStatus(port) & portsc_reset_change == 0) {
|
||||||
if (system.clock() >= deadline) return false;
|
if (time.clock() >= deadline) return false;
|
||||||
}
|
}
|
||||||
// Clear the Port Reset Change bit (write 1 to PRC, 0 to the rest).
|
// Clear the Port Reset Change bit (write 1 to PRC, 0 to the rest).
|
||||||
const after = self.portStatus(port);
|
const after = self.portStatus(port);
|
||||||
@@ -778,7 +777,7 @@ pub const Controller = struct {
|
|||||||
/// assigned (carried in bits 31:24 of the completion event's control field).
|
/// assigned (carried in bits 31:24 of the completion event's control field).
|
||||||
fn enableSlot(self: *Controller) ?u8 {
|
fn enableSlot(self: *Controller) ?u8 {
|
||||||
const physical = self.submitCommand(.{ .control = trbControl(.enable_slot, 0) });
|
const physical = self.submitCommand(.{ .control = trbControl(.enable_slot, 0) });
|
||||||
const deadline = system.clock() + 1_000_000_000;
|
const deadline = time.clock() + 1_000_000_000;
|
||||||
while (true) {
|
while (true) {
|
||||||
const event = self.nextEvent(deadline) orelse return null;
|
const event = self.nextEvent(deadline) orelse return null;
|
||||||
if (trbType(event.control) == @intFromEnum(TrbType.command_completion_event) and
|
if (trbType(event.control) == @intFromEnum(TrbType.command_completion_event) and
|
||||||
@@ -858,7 +857,7 @@ pub const Controller = struct {
|
|||||||
// retried here; its port was reset in serviceHubPort.)
|
// retried here; its port was reset in serviceHubPort.)
|
||||||
if (device.parent_slot == 0) {
|
if (device.parent_slot == 0) {
|
||||||
if (!self.resetPort(device.port)) return false;
|
if (!self.resetPort(device.port)) return false;
|
||||||
system.sleep(10);
|
time.sleepMillis(10);
|
||||||
} else return false;
|
} else return false;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
@@ -891,7 +890,7 @@ pub const Controller = struct {
|
|||||||
// (TRSTRCY, 10 ms) after reset before it answers SET_ADDRESS.
|
// (TRSTRCY, 10 ms) after reset before it answers SET_ADDRESS.
|
||||||
// Addressing immediately gives a USB Transaction Error (code 4) on
|
// Addressing immediately gives a USB Transaction Error (code 4) on
|
||||||
// real full-speed devices; QEMU tolerates the omission.
|
// real full-speed devices; QEMU tolerates the omission.
|
||||||
system.sleep(10);
|
time.sleepMillis(10);
|
||||||
}
|
}
|
||||||
const slot_id = self.enableSlot() orelse {
|
const slot_id = self.enableSlot() orelse {
|
||||||
std.log.info("port {d} setup: Enable Slot failed", .{port});
|
std.log.info("port {d} setup: Enable Slot failed", .{port});
|
||||||
@@ -909,11 +908,11 @@ pub const Controller = struct {
|
|||||||
.max_packet_size_0 = defaultMaxPacketSize0(effective_speed),
|
.max_packet_size_0 = defaultMaxPacketSize0(effective_speed),
|
||||||
.root_port = port, // a root-port device: the chain root IS this port
|
.root_port = port, // a root-port device: the chain root IS this port
|
||||||
};
|
};
|
||||||
device.input_context = dma.alloc(page_size, dma.coherent) orelse return self.abandon(device);
|
device.input_context = memory.dmaAlloc(page_size, memory.dma_coherent) orelse return self.abandon(device);
|
||||||
device.device_context = dma.alloc(page_size, dma.coherent) orelse return self.abandon(device);
|
device.device_context = memory.dmaAlloc(page_size, memory.dma_coherent) orelse return self.abandon(device);
|
||||||
device.ep0_ring = .{ .region = dma.alloc(page_size, dma.coherent) orelse return self.abandon(device) };
|
device.ep0_ring = .{ .region = memory.dmaAlloc(page_size, memory.dma_coherent) orelse return self.abandon(device) };
|
||||||
device.ep0_ring.installLink();
|
device.ep0_ring.installLink();
|
||||||
device.control_buffer = dma.alloc(page_size, dma.coherent) orelse return self.abandon(device);
|
device.control_buffer = memory.dmaAlloc(page_size, memory.dma_coherent) orelse return self.abandon(device);
|
||||||
|
|
||||||
self.buildAddressInputContext(device);
|
self.buildAddressInputContext(device);
|
||||||
const array: [*]volatile u64 = @ptrFromInt(self.device_context_array.virtual);
|
const array: [*]volatile u64 = @ptrFromInt(self.device_context_array.virtual);
|
||||||
@@ -991,7 +990,7 @@ pub const Controller = struct {
|
|||||||
if (!is_interrupt or !is_in) continue;
|
if (!is_interrupt or !is_in) continue;
|
||||||
const ring = self.getOrConfigureEndpoint(device, endpoint) orelse return;
|
const ring = self.getOrConfigureEndpoint(device, endpoint) orelse return;
|
||||||
const subscription = self.allocateSubscription() orelse return;
|
const subscription = self.allocateSubscription() orelse return;
|
||||||
const buffer = dma.alloc(page_size, dma.coherent) orelse return;
|
const buffer = memory.dmaAlloc(page_size, memory.dma_coherent) orelse return;
|
||||||
const number: u8 = endpoint.address & 0x0F;
|
const number: u8 = endpoint.address & 0x0F;
|
||||||
subscription.* = .{
|
subscription.* = .{
|
||||||
.active = true,
|
.active = true,
|
||||||
@@ -1068,7 +1067,7 @@ pub const Controller = struct {
|
|||||||
_ = self.controlTransfer(hub, hubreq.setPortFeature(hubreq.feature_port_reset, port), &.{}, false);
|
_ = self.controlTransfer(hub, hubreq.setPortFeature(hubreq.feature_port_reset, port), &.{}, false);
|
||||||
var tries: u32 = 0;
|
var tries: u32 = 0;
|
||||||
while (tries < 200) : (tries += 1) {
|
while (tries < 200) : (tries += 1) {
|
||||||
system.sleep(5);
|
time.sleepMillis(5);
|
||||||
const s = self.readHubPortStatus(hub, port) orelse return null;
|
const s = self.readHubPortStatus(hub, port) orelse return null;
|
||||||
if (s & hubreq.status_enable != 0) break;
|
if (s & hubreq.status_enable != 0) break;
|
||||||
}
|
}
|
||||||
@@ -1113,11 +1112,11 @@ pub const Controller = struct {
|
|||||||
.parent_slot = hub.slot_id,
|
.parent_slot = hub.slot_id,
|
||||||
.parent_port = @intCast(port),
|
.parent_port = @intCast(port),
|
||||||
};
|
};
|
||||||
device.input_context = dma.alloc(page_size, dma.coherent) orelse return self.abandon(device);
|
device.input_context = memory.dmaAlloc(page_size, memory.dma_coherent) orelse return self.abandon(device);
|
||||||
device.device_context = dma.alloc(page_size, dma.coherent) orelse return self.abandon(device);
|
device.device_context = memory.dmaAlloc(page_size, memory.dma_coherent) orelse return self.abandon(device);
|
||||||
device.ep0_ring = .{ .region = dma.alloc(page_size, dma.coherent) orelse return self.abandon(device) };
|
device.ep0_ring = .{ .region = memory.dmaAlloc(page_size, memory.dma_coherent) orelse return self.abandon(device) };
|
||||||
device.ep0_ring.installLink();
|
device.ep0_ring.installLink();
|
||||||
device.control_buffer = dma.alloc(page_size, dma.coherent) orelse return self.abandon(device);
|
device.control_buffer = memory.dmaAlloc(page_size, memory.dma_coherent) orelse return self.abandon(device);
|
||||||
|
|
||||||
self.buildAddressInputContext(device);
|
self.buildAddressInputContext(device);
|
||||||
const array: [*]volatile u64 = @ptrFromInt(self.device_context_array.virtual);
|
const array: [*]volatile u64 = @ptrFromInt(self.device_context_array.virtual);
|
||||||
@@ -1164,7 +1163,7 @@ pub const Controller = struct {
|
|||||||
/// a way that survived every driver restart (the 1-in-3 READ CAPACITY
|
/// a way that survived every driver restart (the 1-in-3 READ CAPACITY
|
||||||
/// failure at boot, with a USB keyboard and mouse polling concurrently).
|
/// failure at boot, with a USB keyboard and mouse polling concurrently).
|
||||||
fn awaitTransfer(self: *Controller, slot_id: u8, dci: u32, requested_length: u32) ?u8 {
|
fn awaitTransfer(self: *Controller, slot_id: u8, dci: u32, requested_length: u32) ?u8 {
|
||||||
const deadline = system.clock() + 1_000_000_000;
|
const deadline = time.clock() + 1_000_000_000;
|
||||||
while (true) {
|
while (true) {
|
||||||
const event = self.nextEvent(deadline) orelse return null;
|
const event = self.nextEvent(deadline) orelse return null;
|
||||||
if (trbType(event.control) != @intFromEnum(TrbType.transfer_event)) continue;
|
if (trbType(event.control) != @intFromEnum(TrbType.transfer_event)) continue;
|
||||||
@@ -1401,7 +1400,7 @@ pub const Controller = struct {
|
|||||||
|
|
||||||
const configured = &device.endpoint_rings[device.endpoint_ring_count];
|
const configured = &device.endpoint_rings[device.endpoint_ring_count];
|
||||||
configured.dci = dci;
|
configured.dci = dci;
|
||||||
configured.ring = .{ .region = dma.alloc(page_size, dma.coherent) orelse return null };
|
configured.ring = .{ .region = memory.dmaAlloc(page_size, memory.dma_coherent) orelse return null };
|
||||||
configured.ring.installLink();
|
configured.ring.installLink();
|
||||||
self.buildConfigureEndpointInputContext(device, endpoint, dci, &configured.ring);
|
self.buildConfigureEndpointInputContext(device, endpoint, dci, &configured.ring);
|
||||||
if (!self.configureEndpointCommand(device)) return null;
|
if (!self.configureEndpointCommand(device)) return null;
|
||||||
@@ -1481,7 +1480,7 @@ pub const Controller = struct {
|
|||||||
pub fn subscribeInterrupt(self: *Controller, device: *Device, endpoint: EndpointInfo, device_token: u64, report_endpoint: usize) bool {
|
pub fn subscribeInterrupt(self: *Controller, device: *Device, endpoint: EndpointInfo, device_token: u64, report_endpoint: usize) bool {
|
||||||
const ring = self.getOrConfigureEndpoint(device, endpoint) orelse return false;
|
const ring = self.getOrConfigureEndpoint(device, endpoint) orelse return false;
|
||||||
const subscription = self.allocateSubscription() orelse return false;
|
const subscription = self.allocateSubscription() orelse return false;
|
||||||
const buffer = dma.alloc(page_size, dma.coherent) orelse return false;
|
const buffer = memory.dmaAlloc(page_size, memory.dma_coherent) orelse return false;
|
||||||
const number: u8 = endpoint.address & 0x0F;
|
const number: u8 = endpoint.address & 0x0F;
|
||||||
const direction_in = endpoint.address & 0x80 != 0;
|
const direction_in = endpoint.address & 0x80 != 0;
|
||||||
subscription.* = .{
|
subscription.* = .{
|
||||||
@@ -1570,7 +1569,7 @@ pub const Controller = struct {
|
|||||||
/// driver's timer tick.
|
/// driver's timer tick.
|
||||||
pub fn pump(self: *Controller) void {
|
pub fn pump(self: *Controller) void {
|
||||||
while (true) {
|
while (true) {
|
||||||
const event = self.nextEvent(system.clock()) orelse return; // deadline=now: null when empty
|
const event = self.nextEvent(time.clock()) orelse return; // deadline=now: null when empty
|
||||||
const kind = trbType(event.control);
|
const kind = trbType(event.control);
|
||||||
if (kind == @intFromEnum(TrbType.transfer_event)) {
|
if (kind == @intFromEnum(TrbType.transfer_event)) {
|
||||||
_ = self.serviceInterruptEvent(event);
|
_ = self.serviceInterruptEvent(event);
|
||||||
|
|||||||
@@ -14,14 +14,16 @@
|
|||||||
//! restart/re-attach are V4–V6. See docs/display-v2.md.
|
//! restart/re-attach are V4–V6. See docs/display-v2.md.
|
||||||
|
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const runtime = @import("runtime");
|
const device = @import("driver");
|
||||||
|
const ipc = @import("ipc");
|
||||||
|
const process = @import("process");
|
||||||
|
const service = @import("service");
|
||||||
|
const time = @import("time");
|
||||||
|
const device_manager = @import("driver");
|
||||||
|
const memory = @import("memory");
|
||||||
|
const logging = @import("logging");
|
||||||
const mmio = @import("mmio");
|
const mmio = @import("mmio");
|
||||||
const pci = @import("pci");
|
const pci = @import("pci");
|
||||||
const device = runtime.device;
|
|
||||||
const dma = runtime.dma;
|
|
||||||
const shared_memory = runtime.shared_memory;
|
|
||||||
const system = runtime.system;
|
|
||||||
const ipc = runtime.ipc;
|
|
||||||
const display_protocol = @import("display-protocol");
|
const display_protocol = @import("display-protocol");
|
||||||
const scanout_protocol = @import("scanout-protocol");
|
const scanout_protocol = @import("scanout-protocol");
|
||||||
const vp = @import("virtio-pci.zig");
|
const vp = @import("virtio-pci.zig");
|
||||||
@@ -86,14 +88,14 @@ var notify_multiplier: u32 = 0;
|
|||||||
var notify_addr: usize = 0;
|
var notify_addr: usize = 0;
|
||||||
|
|
||||||
// DMA memory: the virtqueue rings and the command scratch.
|
// DMA memory: the virtqueue rings and the command scratch.
|
||||||
var ring: dma.Region = undefined;
|
var ring: memory.DmaRegion = undefined;
|
||||||
var command: dma.Region = undefined;
|
var command: memory.DmaRegion = undefined;
|
||||||
|
|
||||||
// The scanout backing is a **shared** (shared-memory) region, not DMA: cacheable so the compositor
|
// The scanout backing is a **shared** (shared-memory) region, not DMA: cacheable so the compositor
|
||||||
// composites into it cheaply (x86 DMA is coherent, so the device still sees the writes), and
|
// composites into it cheaply (x86 DMA is coherent, so the device still sees the writes), and
|
||||||
// shareable so the same physical pages the device scans out of are the ones the compositor
|
// shareable so the same physical pages the device scans out of are the ones the compositor
|
||||||
// paints. The driver keeps the capability to hand to the compositor in the announce.
|
// paints. The driver keeps the capability to hand to the compositor in the announce.
|
||||||
var surface: shared_memory.Region = undefined;
|
var surface: memory.SharedRegion = undefined;
|
||||||
|
|
||||||
// Split-virtqueue producer/consumer shadows.
|
// Split-virtqueue producer/consumer shadows.
|
||||||
var avail_shadow: u16 = 0;
|
var avail_shadow: u16 = 0;
|
||||||
@@ -161,7 +163,7 @@ fn waitUsed() bool {
|
|||||||
used_shadow = idx;
|
used_shadow = idx;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (tries > 8) system.sleep(1);
|
if (tries > 8) time.sleepMillis(1);
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -282,11 +284,11 @@ fn initialise(endpoint: ipc.Handle) bool {
|
|||||||
std.log.info("control queue too small ({d})", .{device_qsize});
|
std.log.info("control queue too small ({d})", .{device_qsize});
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
ring = dma.alloc(4096, dma.coherent) orelse {
|
ring = memory.dmaAlloc(4096, memory.dma_coherent) orelse {
|
||||||
std.log.info("virtqueue allocation failed", .{});
|
std.log.info("virtqueue allocation failed", .{});
|
||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
command = dma.alloc(4096, dma.coherent) orelse {
|
command = memory.dmaAlloc(4096, memory.dma_coherent) orelse {
|
||||||
std.log.info("command-buffer allocation failed", .{});
|
std.log.info("command-buffer allocation failed", .{});
|
||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
@@ -322,11 +324,11 @@ fn initialise(endpoint: ipc.Handle) bool {
|
|||||||
|
|
||||||
// Back the resource with a shared (shared-memory) surface, so the compositor and the device work
|
// Back the resource with a shared (shared-memory) surface, so the compositor and the device work
|
||||||
// the same physical pages. The device needs the guest-physical base for attach_backing.
|
// the same physical pages. The device needs the guest-physical base for attach_backing.
|
||||||
surface = shared_memory.create(scanout_bytes) orelse {
|
surface = memory.sharedCreate(scanout_bytes) orelse {
|
||||||
std.log.info("scanout surface allocation failed", .{});
|
std.log.info("scanout surface allocation failed", .{});
|
||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
const surface_physical = shared_memory.physical(surface.handle) orelse {
|
const surface_physical = memory.sharedPhysical(surface.handle) orelse {
|
||||||
std.log.info("could not resolve the scanout surface physical address", .{});
|
std.log.info("could not resolve the scanout surface physical address", .{});
|
||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
@@ -354,7 +356,7 @@ fn initialise(endpoint: ipc.Handle) bool {
|
|||||||
// deadline). Role: device — we claim one PCI function and serve its scanout; we report
|
// deadline). Role: device — we claim one PCI function and serve its scanout; we report
|
||||||
// no children. A restarted instance re-hellos here and re-announces below — the compositor
|
// no children. A restarted instance re-hellos here and re-announces below — the compositor
|
||||||
// re-attaches to the fresh scanout (V6). Best-effort: standalone bring-up has no manager.
|
// re-attaches to the fresh scanout (V6). Best-effort: standalone bring-up has no manager.
|
||||||
_ = runtime.device_manager.hello(.device, device_id);
|
_ = device_manager.hello(.device, device_id);
|
||||||
|
|
||||||
// Read the monitor's EDID (best-effort, when the device offers it) — the mode list a real
|
// Read the monitor's EDID (best-effort, when the device offers it) — the mode list a real
|
||||||
// driver derives from it; we log the preferred mode and keep our fixed offered list.
|
// driver derives from it; we log the preferred mode and keep our fixed offered list.
|
||||||
@@ -474,7 +476,7 @@ fn announce() void {
|
|||||||
var tries: u32 = 0;
|
var tries: u32 = 0;
|
||||||
const display = while (tries < 50) : (tries += 1) {
|
const display = while (tries < 50) : (tries += 1) {
|
||||||
if (ipc.lookup(.display)) |h| break h;
|
if (ipc.lookup(.display)) |h| break h;
|
||||||
system.sleep(20);
|
time.sleepMillis(20);
|
||||||
} else {
|
} else {
|
||||||
std.log.info("no display service to announce to (scanout-only)", .{});
|
std.log.info("no display service to announce to (scanout-only)", .{});
|
||||||
return;
|
return;
|
||||||
@@ -535,16 +537,16 @@ fn onMessage(message: []const u8, reply: []u8, sender: u32, capability: ?ipc.Han
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn main(init: runtime.process.Init) void {
|
pub fn main(init: process.Init) void {
|
||||||
const argument = init.arguments.get(1) orelse {
|
const argument = init.arguments.get(1) orelse {
|
||||||
_ = system.write("virtio-gpu: missing device id (argv[1])\n");
|
_ = logging.write("virtio-gpu: missing device id (argv[1])\n");
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
device_id = std.fmt.parseInt(u64, argument, 10) catch {
|
device_id = std.fmt.parseInt(u64, argument, 10) catch {
|
||||||
std.log.info("malformed device id '{s}'", .{argument});
|
std.log.info("malformed device id '{s}'", .{argument});
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
runtime.service.run(256, .{
|
service.run(256, .{
|
||||||
.service = .scanout,
|
.service = .scanout,
|
||||||
.init = initialise,
|
.init = initialise,
|
||||||
.on_message = onMessage,
|
.on_message = onMessage,
|
||||||
|
|||||||
@@ -8,13 +8,18 @@
|
|||||||
//! service, binds the SCI (System Control Interrupt), and on a power-button
|
//! service, binds the SCI (System Control Interrupt), and on a power-button
|
||||||
//! fixed event publishes `power_button` to subscribers — and on init's request
|
//! fixed event publishes `power_button` to subscribers — and on init's request
|
||||||
//! writes S5 to power the machine off. The device discovery (M20) and the event
|
//! writes S5 to power the machine off. The device discovery (M20) and the event
|
||||||
//! handling both run in one `runtime.service.run` loop.
|
//! handling both run in one `service.run` loop.
|
||||||
|
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const runtime = @import("runtime");
|
const device = @import("driver");
|
||||||
|
const ipc = @import("ipc");
|
||||||
|
const process = @import("process");
|
||||||
|
const service = @import("service");
|
||||||
|
const time = @import("time");
|
||||||
|
const memory = @import("memory");
|
||||||
|
const logging = @import("logging");
|
||||||
const aml = @import("aml");
|
const aml = @import("aml");
|
||||||
const acpi_ids = @import("acpi-ids");
|
const acpi_ids = @import("acpi-ids");
|
||||||
const device = runtime.device;
|
|
||||||
const device_manager_protocol = @import("device-manager-protocol");
|
const device_manager_protocol = @import("device-manager-protocol");
|
||||||
const power_protocol = @import("power-protocol");
|
const power_protocol = @import("power-protocol");
|
||||||
/// AML opcode/prefix bytes by name (`zero_opcode`, `byte_prefix`, …) — so the `_HID`
|
/// AML opcode/prefix bytes by name (`zero_opcode`, `byte_prefix`, …) — so the `_HID`
|
||||||
@@ -60,7 +65,7 @@ const slp_en: u32 = 1 << 13;
|
|||||||
// stands in for "only the system supervisor may power off" without hardcoding
|
// stands in for "only the system supervisor may power off" without hardcoding
|
||||||
// a pid the kernel's idle tasks would have taken.
|
// a pid the kernel's idle tasks would have taken.
|
||||||
const maximum_subscribers = 8;
|
const maximum_subscribers = 8;
|
||||||
var subscribers: [maximum_subscribers]?runtime.ipc.Handle = .{null} ** maximum_subscribers;
|
var subscribers: [maximum_subscribers]?ipc.Handle = .{null} ** maximum_subscribers;
|
||||||
var subscriber_tasks: [maximum_subscribers]u32 = .{0} ** maximum_subscribers;
|
var subscriber_tasks: [maximum_subscribers]u32 = .{0} ** maximum_subscribers;
|
||||||
|
|
||||||
// Pass-1 registration record (see main): what pass 2 reports.
|
// Pass-1 registration record (see main): what pass 2 reports.
|
||||||
@@ -97,24 +102,24 @@ fn findTablesNode(buffer: []device.DeviceDescriptor) ?device.DeviceDescriptor {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn main(init: runtime.process.Init) void {
|
pub fn main(init: process.Init) void {
|
||||||
// When the acpi-parse scenario spawns this directly, argv[1] is a device-count
|
// When the acpi-parse scenario spawns this directly, argv[1] is a device-count
|
||||||
// *floor* to self-verify against. The kernel no longer parses AML, so there is
|
// *floor* to self-verify against. The kernel no longer parses AML, so there is
|
||||||
// no exact count to match — proving the ring-3 parse found at least a floor of
|
// no exact count to match — proving the ring-3 parse found at least a floor of
|
||||||
// devices is the check. Deterministic, no log-scraping.
|
// devices is the check. Deterministic, no log-scraping.
|
||||||
const floor: ?usize = if (init.arguments.get(1)) |a| (std.fmt.parseInt(usize, a, 10) catch null) else null;
|
const floor: ?usize = if (init.arguments.get(1)) |a| (std.fmt.parseInt(usize, a, 10) catch null) else null;
|
||||||
|
|
||||||
const buffer = runtime.allocator().alloc(device.DeviceDescriptor, 64) catch {
|
const buffer = memory.allocator().alloc(device.DeviceDescriptor, 64) catch {
|
||||||
_ = runtime.system.write("/system/services/acpi: out of memory\n");
|
_ = logging.write("/system/services/acpi: out of memory\n");
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
const node = findTablesNode(buffer) orelse {
|
const node = findTablesNode(buffer) orelse {
|
||||||
_ = runtime.system.write("/system/services/acpi: no acpi-tables node to claim\n");
|
_ = logging.write("/system/services/acpi: no acpi-tables node to claim\n");
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
node_id = node.id;
|
node_id = node.id;
|
||||||
if (!device.claim(node_id)) {
|
if (!device.claim(node_id)) {
|
||||||
_ = runtime.system.write("/system/services/acpi: unable to claim acpi-tables\n");
|
_ = logging.write("/system/services/acpi: unable to claim acpi-tables\n");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -148,12 +153,12 @@ pub fn main(init: runtime.process.Init) void {
|
|||||||
block_count += 1;
|
block_count += 1;
|
||||||
}
|
}
|
||||||
if (block_count == 0) {
|
if (block_count == 0) {
|
||||||
_ = runtime.system.write("/system/services/acpi: no AML blobs on the node\n");
|
_ = logging.write("/system/services/acpi: no AML blobs on the node\n");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const result = aml.parse(runtime.allocator(), blocks[0..block_count]) catch {
|
const result = aml.parse(memory.allocator(), blocks[0..block_count]) catch {
|
||||||
_ = runtime.system.write("/system/services/acpi: AML parse failed\n");
|
_ = logging.write("/system/services/acpi: AML parse failed\n");
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
var namespace = result.namespace;
|
var namespace = result.namespace;
|
||||||
@@ -161,19 +166,19 @@ pub fn main(init: runtime.process.Init) void {
|
|||||||
std.log.info("parsed {d} AML blob(s), {d} namespace devices", .{ block_count, devices });
|
std.log.info("parsed {d} AML blob(s), {d} namespace devices", .{ block_count, devices });
|
||||||
if (floor) |minimum| {
|
if (floor) |minimum| {
|
||||||
if (devices >= minimum) {
|
if (devices >= minimum) {
|
||||||
_ = runtime.system.write("acpi-parse: ok\n");
|
_ = logging.write("acpi-parse: ok\n");
|
||||||
} else {
|
} else {
|
||||||
std.log.info("acpi-parse: too few (ring-3 {d} < floor {d})", .{ devices, minimum });
|
std.log.info("acpi-parse: too few (ring-3 {d} < floor {d})", .{ devices, minimum });
|
||||||
}
|
}
|
||||||
// Self-verify mode is standalone (no manager); stop before reporting.
|
// Self-verify mode is standalone (no manager); stop before reporting.
|
||||||
while (true) runtime.system.sleep(1000);
|
while (true) time.sleepMillis(1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Register + report the present _HID devices (M20), then set up the power
|
// Register + report the present _HID devices (M20), then set up the power
|
||||||
// event side (M21), then serve — all in one harness loop. The interpreter
|
// event side (M21), then serve — all in one harness loop. The interpreter
|
||||||
// and namespace outlive this frame (static), so the harness callbacks can
|
// and namespace outlive this frame (static), so the harness callbacks can
|
||||||
// reach them.
|
// reach them.
|
||||||
interpreter_arena = std.heap.ArenaAllocator.init(runtime.allocator());
|
interpreter_arena = std.heap.ArenaAllocator.init(memory.allocator());
|
||||||
persistent_namespace = namespace;
|
persistent_namespace = namespace;
|
||||||
global_interpreter = aml.Interpreter.init(&persistent_namespace, .{
|
global_interpreter = aml.Interpreter.init(&persistent_namespace, .{
|
||||||
.mapMmio = halMapMmio,
|
.mapMmio = halMapMmio,
|
||||||
@@ -184,7 +189,7 @@ pub fn main(init: runtime.process.Init) void {
|
|||||||
readFadt(fadt);
|
readFadt(fadt);
|
||||||
s5_valid = readSleepS5(&persistent_namespace);
|
s5_valid = readSleepS5(&persistent_namespace);
|
||||||
|
|
||||||
runtime.service.run(power_protocol.message_maximum, .{
|
service.run(power_protocol.message_maximum, .{
|
||||||
.service = .power,
|
.service = .power,
|
||||||
.init = onInit,
|
.init = onInit,
|
||||||
.on_message = onMessage,
|
.on_message = onMessage,
|
||||||
@@ -200,11 +205,11 @@ var interpreter_arena: std.heap.ArenaAllocator = undefined;
|
|||||||
|
|
||||||
/// Startup under the harness: register + report the discovered devices to the
|
/// Startup under the harness: register + report the discovered devices to the
|
||||||
/// manager (M20), then enable ACPI mode and arm the power button (M21).
|
/// manager (M20), then enable ACPI mode and arm the power button (M21).
|
||||||
fn onInit(endpoint: runtime.ipc.Handle) bool {
|
fn onInit(endpoint: ipc.Handle) bool {
|
||||||
registered_count = 0;
|
registered_count = 0;
|
||||||
walkDevices(persistent_namespace.root, &global_interpreter);
|
walkDevices(persistent_namespace.root, &global_interpreter);
|
||||||
|
|
||||||
const manager = runtime.ipc.lookup(.device_manager);
|
const manager = ipc.lookup(.device_manager);
|
||||||
var i: usize = 0;
|
var i: usize = 0;
|
||||||
while (i < registered_count) : (i += 1) {
|
while (i < registered_count) : (i += 1) {
|
||||||
const entry = registered[i];
|
const entry = registered[i];
|
||||||
@@ -218,7 +223,7 @@ fn onInit(endpoint: runtime.ipc.Handle) bool {
|
|||||||
var report = device_manager_protocol.ChildAdded{ .parent = node_id, .bus_address = entry.device_id, .identity = 0, .device_id = entry.device_id };
|
var report = device_manager_protocol.ChildAdded{ .parent = node_id, .bus_address = entry.device_id, .identity = 0, .device_id = entry.device_id };
|
||||||
@memcpy(report.hid[0..entry.hid_len], entry.hid[0..entry.hid_len]);
|
@memcpy(report.hid[0..entry.hid_len], entry.hid[0..entry.hid_len]);
|
||||||
var reply: [device_manager_protocol.message_maximum]u8 = undefined;
|
var reply: [device_manager_protocol.message_maximum]u8 = undefined;
|
||||||
_ = runtime.ipc.call(h, std.mem.asBytes(&report), &reply) catch {};
|
_ = ipc.call(h, std.mem.asBytes(&report), &reply) catch {};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
std.log.info("reported {d} device(s) to the manager", .{registered_count});
|
std.log.info("reported {d} device(s) to the manager", .{registered_count});
|
||||||
@@ -235,7 +240,7 @@ fn onInit(endpoint: runtime.ipc.Handle) bool {
|
|||||||
/// FADT populates them.
|
/// FADT populates them.
|
||||||
fn readFadt(fadt: ?[]const u8) void {
|
fn readFadt(fadt: ?[]const u8) void {
|
||||||
const f = fadt orelse {
|
const f = fadt orelse {
|
||||||
_ = runtime.system.write("acpi: no FADT on the node — power events off\n");
|
_ = logging.write("acpi: no FADT on the node — power events off\n");
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
smi_cmd = @truncate(rd32(f, 48));
|
smi_cmd = @truncate(rd32(f, 48));
|
||||||
@@ -260,22 +265,22 @@ fn readSleepS5(ns: *aml.Namespace) bool {
|
|||||||
|
|
||||||
/// Enable ACPI mode if the firmware isn't already in it, then bind the SCI and
|
/// Enable ACPI mode if the firmware isn't already in it, then bind the SCI and
|
||||||
/// set PWRBTN_EN so the power button raises an interrupt we can see.
|
/// set PWRBTN_EN so the power button raises an interrupt we can see.
|
||||||
fn armPowerButton(endpoint: runtime.ipc.Handle) void {
|
fn armPowerButton(endpoint: ipc.Handle) void {
|
||||||
if (pm1a_cnt != 0 and (halPioRead(2, pm1a_cnt) & sci_en_bit) == 0 and smi_cmd != 0) {
|
if (pm1a_cnt != 0 and (halPioRead(2, pm1a_cnt) & sci_en_bit) == 0 and smi_cmd != 0) {
|
||||||
// Switch to ACPI mode: write ACPI_ENABLE to the SMI command port, then
|
// Switch to ACPI mode: write ACPI_ENABLE to the SMI command port, then
|
||||||
// spin (bounded) until SCI_EN latches.
|
// spin (bounded) until SCI_EN latches.
|
||||||
halPioWrite(1, smi_cmd, acpi_enable_value);
|
halPioWrite(1, smi_cmd, acpi_enable_value);
|
||||||
var tries: u32 = 0;
|
var tries: u32 = 0;
|
||||||
while (tries < 1000 and (halPioRead(2, pm1a_cnt) & sci_en_bit) == 0) : (tries += 1) {
|
while (tries < 1000 and (halPioRead(2, pm1a_cnt) & sci_en_bit) == 0) : (tries += 1) {
|
||||||
runtime.system.sleep(1);
|
time.sleepMillis(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!has_sci) {
|
if (!has_sci) {
|
||||||
_ = runtime.system.write("acpi: no SCI resource — power button unavailable\n");
|
_ = logging.write("acpi: no SCI resource — power button unavailable\n");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!device.irqBind(node_id, sci_resource_index, endpoint)) {
|
if (!device.irqBind(node_id, sci_resource_index, endpoint)) {
|
||||||
_ = runtime.system.write("acpi: SCI irq_bind failed\n");
|
_ = logging.write("acpi: SCI irq_bind failed\n");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// PWRBTN_EN lives in the PM1 enable register at evt_blk + evt_len/2.
|
// PWRBTN_EN lives in the PM1 enable register at evt_blk + evt_len/2.
|
||||||
@@ -287,7 +292,7 @@ fn armPowerButton(endpoint: runtime.ipc.Handle) void {
|
|||||||
const en_port = pm1b_evt + pm1_evt_len / 2;
|
const en_port = pm1b_evt + pm1_evt_len / 2;
|
||||||
halPioWrite(2, en_port, @as(u16, @truncate(halPioRead(2, en_port))) | pwrbtn_bit);
|
halPioWrite(2, en_port, @as(u16, @truncate(halPioRead(2, en_port))) | pwrbtn_bit);
|
||||||
}
|
}
|
||||||
_ = runtime.system.write("acpi: power button armed\n");
|
_ = logging.write("acpi: power button armed\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The SCI fired. Read PM1 status; a set PWRBTN_STS is the power button — clear
|
/// The SCI fired. Read PM1 status; a set PWRBTN_STS is the power button — clear
|
||||||
@@ -307,7 +312,7 @@ fn onSci() void {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (handled) {
|
if (handled) {
|
||||||
_ = runtime.system.write("power: button pressed\n");
|
_ = logging.write("power: button pressed\n");
|
||||||
publishButton();
|
publishButton();
|
||||||
}
|
}
|
||||||
handleGpe();
|
handleGpe();
|
||||||
@@ -387,7 +392,7 @@ fn publishButton() void {
|
|||||||
fn publishEvent(bytes: []const u8) void {
|
fn publishEvent(bytes: []const u8) void {
|
||||||
for (&subscribers) |*slot| {
|
for (&subscribers) |*slot| {
|
||||||
if (slot.*) |handle| {
|
if (slot.*) |handle| {
|
||||||
if (!runtime.ipc.send(handle, bytes)) slot.* = null;
|
if (!ipc.send(handle, bytes)) slot.* = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -404,15 +409,15 @@ fn isSubscriber(task: u32) bool {
|
|||||||
/// AML parse. Only reached from a PID-1 shutdown request (M21.3).
|
/// AML parse. Only reached from a PID-1 shutdown request (M21.3).
|
||||||
fn enterS5() void {
|
fn enterS5() void {
|
||||||
if (!s5_valid or pm1a_cnt == 0) {
|
if (!s5_valid or pm1a_cnt == 0) {
|
||||||
_ = runtime.system.write("power: S5 unavailable\n");
|
_ = logging.write("power: S5 unavailable\n");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
_ = runtime.system.write("power: entering S5\n");
|
_ = logging.write("power: entering S5\n");
|
||||||
halPioWrite(2, pm1a_cnt, (@as(u32, s5_slp_typ_a & 0x7) << 10) | slp_en);
|
halPioWrite(2, pm1a_cnt, (@as(u32, s5_slp_typ_a & 0x7) << 10) | slp_en);
|
||||||
if (pm1b_cnt != 0) halPioWrite(2, pm1b_cnt, (@as(u32, s5_slp_typ_b & 0x7) << 10) | slp_en);
|
if (pm1b_cnt != 0) halPioWrite(2, pm1b_cnt, (@as(u32, s5_slp_typ_b & 0x7) << 10) | slp_en);
|
||||||
// If control returns, the write did not take — say so instead of hanging.
|
// If control returns, the write did not take — say so instead of hanging.
|
||||||
runtime.system.sleep(500);
|
time.sleepMillis(500);
|
||||||
_ = runtime.system.write("power: S5 write did not take\n");
|
_ = logging.write("power: S5 write did not take\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- harness callbacks --------------------------------------------------------
|
// --- harness callbacks --------------------------------------------------------
|
||||||
@@ -426,7 +431,7 @@ fn onNotification(badge: u64) void {
|
|||||||
/// The `.power` protocol: subscribe (endpoint as the call's capability),
|
/// The `.power` protocol: subscribe (endpoint as the call's capability),
|
||||||
/// shutdown (PID 1 only). Device discovery uses a different endpoint (the
|
/// shutdown (PID 1 only). Device discovery uses a different endpoint (the
|
||||||
/// device manager's), so nothing here handles ChildAdded.
|
/// device manager's), so nothing here handles ChildAdded.
|
||||||
fn onMessage(message: []const u8, reply: []u8, sender: u32, capability: ?runtime.ipc.Handle) usize {
|
fn onMessage(message: []const u8, reply: []u8, sender: u32, capability: ?ipc.Handle) usize {
|
||||||
if (message.len < 1) return 0;
|
if (message.len < 1) return 0;
|
||||||
switch (message[0]) {
|
switch (message[0]) {
|
||||||
@intFromEnum(power_protocol.Operation.subscribe) => {
|
@intFromEnum(power_protocol.Operation.subscribe) => {
|
||||||
|
|||||||
@@ -8,8 +8,9 @@
|
|||||||
//! proving the kernel-built System V entry stack (argc, argv pointers,
|
//! proving the kernel-built System V entry stack (argc, argv pointers,
|
||||||
//! NUL-terminated strings) and the runtime's parsing of it, end to end.
|
//! NUL-terminated strings) and the runtime's parsing of it, end to end.
|
||||||
|
|
||||||
const runtime = @import("runtime");
|
|
||||||
|
|
||||||
|
const process = @import("process");
|
||||||
|
const logging = @import("logging");
|
||||||
/// Recurse with a real frame each level: `depth` levels of ~0.5 KiB, touched
|
/// Recurse with a real frame each level: `depth` levels of ~0.5 KiB, touched
|
||||||
/// through a volatile pointer so no optimiser can flatten the frames away.
|
/// through a volatile pointer so no optimiser can flatten the frames away.
|
||||||
fn burnStack(depth: usize) u8 {
|
fn burnStack(depth: usize) u8 {
|
||||||
@@ -21,10 +22,10 @@ fn burnStack(depth: usize) u8 {
|
|||||||
return touch[0] +% burnStack(depth - 1);
|
return touch[0] +% burnStack(depth - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn main(init: runtime.process.Init) void {
|
pub fn main(init: process.Init) void {
|
||||||
if (init.arguments.count <= 1) {
|
if (init.arguments.count <= 1) {
|
||||||
// First instance: spawn the second with real arguments, then exit.
|
// First instance: spawn the second with real arguments, then exit.
|
||||||
_ = runtime.system.spawnWithArguments("args-echo", &.{ "alpha", "beta-42" });
|
_ = process.spawnWithArguments("args-echo", &.{ "alpha", "beta-42" });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -46,5 +47,5 @@ pub fn main(init: runtime.process.Init) void {
|
|||||||
}
|
}
|
||||||
buffer[len] = '\n';
|
buffer[len] = '\n';
|
||||||
len += 1;
|
len += 1;
|
||||||
_ = runtime.system.write(buffer[0..len]);
|
_ = logging.write(buffer[0..len]);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,33 +7,37 @@
|
|||||||
//! binary), it exits silently so it cannot derange other tests.
|
//! binary), it exits silently so it cannot derange other tests.
|
||||||
|
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const runtime = @import("runtime");
|
const ipc = @import("ipc");
|
||||||
|
const process = @import("process");
|
||||||
|
const time = @import("time");
|
||||||
|
const device = @import("driver");
|
||||||
|
const logging = @import("logging");
|
||||||
const device_manager_protocol = @import("device-manager-protocol");
|
const device_manager_protocol = @import("device-manager-protocol");
|
||||||
|
|
||||||
pub fn main(init: runtime.process.Init) void {
|
pub fn main(init: process.Init) void {
|
||||||
const argument = init.arguments.get(1) orelse return; // bare: stay silent
|
const argument = init.arguments.get(1) orelse return; // bare: stay silent
|
||||||
const assigned = std.fmt.parseInt(u64, argument, 10) catch return;
|
const assigned = std.fmt.parseInt(u64, argument, 10) catch return;
|
||||||
|
|
||||||
// The respawn only reaches this line because the kernel released the
|
// The respawn only reaches this line because the kernel released the
|
||||||
// previous instance's claim at death. A failed claim exits cleanly — the
|
// previous instance's claim at death. A failed claim exits cleanly — the
|
||||||
// manager reads "meant to stop" and the scenario fails loudly by silence.
|
// manager reads "meant to stop" and the scenario fails loudly by silence.
|
||||||
if (!runtime.device.claim(assigned)) {
|
if (!device.claim(assigned)) {
|
||||||
_ = runtime.system.write("crash-test: claim failed\n");
|
_ = logging.write("crash-test: claim failed\n");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var manager: ?runtime.ipc.Handle = null;
|
var manager: ?ipc.Handle = null;
|
||||||
var tries: u32 = 0;
|
var tries: u32 = 0;
|
||||||
while (manager == null and tries < 100) : (tries += 1) {
|
while (manager == null and tries < 100) : (tries += 1) {
|
||||||
manager = runtime.ipc.lookup(.device_manager);
|
manager = ipc.lookup(.device_manager);
|
||||||
if (manager == null) runtime.system.sleep(20);
|
if (manager == null) time.sleepMillis(20);
|
||||||
}
|
}
|
||||||
const h = manager orelse return;
|
const h = manager orelse return;
|
||||||
const hello = device_manager_protocol.Hello{ .role = @intFromEnum(device_manager_protocol.Role.device), .device_id = assigned };
|
const hello = device_manager_protocol.Hello{ .role = @intFromEnum(device_manager_protocol.Role.device), .device_id = assigned };
|
||||||
var reply: [device_manager_protocol.message_maximum]u8 = undefined;
|
var reply: [device_manager_protocol.message_maximum]u8 = undefined;
|
||||||
_ = runtime.ipc.call(h, std.mem.asBytes(&hello), &reply) catch return;
|
_ = ipc.call(h, std.mem.asBytes(&hello), &reply) catch return;
|
||||||
|
|
||||||
_ = runtime.system.write("crash-test: faulting now\n");
|
_ = logging.write("crash-test: faulting now\n");
|
||||||
const poison: *volatile u32 = @ptrFromInt(0xdead0000);
|
const poison: *volatile u32 = @ptrFromInt(0xdead0000);
|
||||||
poison.* = 1; // the restart machinery's fuel: a real segmentation fault
|
poison.* = 1; // the restart machinery's fuel: a real segmentation fault
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,23 +5,25 @@
|
|||||||
//! device_* system call.
|
//! device_* system call.
|
||||||
|
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const runtime = @import("runtime");
|
const ipc = @import("ipc");
|
||||||
|
const time = @import("time");
|
||||||
|
const logging = @import("logging");
|
||||||
const device_manager_protocol = @import("device-manager-protocol");
|
const device_manager_protocol = @import("device-manager-protocol");
|
||||||
|
|
||||||
fn writeLine(comptime fmt: []const u8, arguments: anytype) void {
|
fn writeLine(comptime fmt: []const u8, arguments: anytype) void {
|
||||||
var line: [96]u8 = undefined;
|
var line: [96]u8 = undefined;
|
||||||
_ = runtime.system.write(std.fmt.bufPrint(&line, fmt, arguments) catch return);
|
_ = logging.write(std.fmt.bufPrint(&line, fmt, arguments) catch return);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn main() void {
|
pub fn main() void {
|
||||||
var manager: ?runtime.ipc.Handle = null;
|
var manager: ?ipc.Handle = null;
|
||||||
var tries: u32 = 0;
|
var tries: u32 = 0;
|
||||||
while (manager == null and tries < 200) : (tries += 1) {
|
while (manager == null and tries < 200) : (tries += 1) {
|
||||||
manager = runtime.ipc.lookup(.device_manager);
|
manager = ipc.lookup(.device_manager);
|
||||||
if (manager == null) runtime.system.sleep(20);
|
if (manager == null) time.sleepMillis(20);
|
||||||
}
|
}
|
||||||
const h = manager orelse {
|
const h = manager orelse {
|
||||||
_ = runtime.system.write("device-list: no device manager\n");
|
_ = logging.write("device-list: no device manager\n");
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -33,12 +35,12 @@ pub fn main() void {
|
|||||||
tries = 0;
|
tries = 0;
|
||||||
while (tries < 20) : (tries += 1) {
|
while (tries < 20) : (tries += 1) {
|
||||||
const request = device_manager_protocol.Enumerate{};
|
const request = device_manager_protocol.Enumerate{};
|
||||||
length = runtime.ipc.call(h, std.mem.asBytes(&request), &reply) catch 0;
|
length = ipc.call(h, std.mem.asBytes(&request), &reply) catch 0;
|
||||||
if (length >= @sizeOf(device_manager_protocol.EnumerateReply)) {
|
if (length >= @sizeOf(device_manager_protocol.EnumerateReply)) {
|
||||||
count = std.mem.bytesToValue(device_manager_protocol.EnumerateReply, reply[0..@sizeOf(device_manager_protocol.EnumerateReply)]).count;
|
count = std.mem.bytesToValue(device_manager_protocol.EnumerateReply, reply[0..@sizeOf(device_manager_protocol.EnumerateReply)]).count;
|
||||||
if (count != 0) break;
|
if (count != 0) break;
|
||||||
}
|
}
|
||||||
runtime.system.sleep(100);
|
time.sleepMillis(100);
|
||||||
}
|
}
|
||||||
writeLine("device-list: {d} devices\n", .{count});
|
writeLine("device-list: {d} devices\n", .{count});
|
||||||
var offset: usize = @sizeOf(device_manager_protocol.EnumerateReply);
|
var offset: usize = @sizeOf(device_manager_protocol.EnumerateReply);
|
||||||
@@ -51,20 +53,20 @@ pub fn main() void {
|
|||||||
|
|
||||||
// The subscription: our endpoint rides as the call's capability; events
|
// The subscription: our endpoint rides as the call's capability; events
|
||||||
// arrive as buffered messages carrying the same structs the bus sends.
|
// arrive as buffered messages carrying the same structs the bus sends.
|
||||||
const endpoint = runtime.ipc.createIpcEndpoint() orelse {
|
const endpoint = ipc.createIpcEndpoint() orelse {
|
||||||
_ = runtime.system.write("device-list: no endpoint\n");
|
_ = logging.write("device-list: no endpoint\n");
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
const subscribe = device_manager_protocol.Subscribe{};
|
const subscribe = device_manager_protocol.Subscribe{};
|
||||||
_ = runtime.ipc.callCap(h, std.mem.asBytes(&subscribe), &reply, endpoint) catch {
|
_ = ipc.callCap(h, std.mem.asBytes(&subscribe), &reply, endpoint) catch {
|
||||||
_ = runtime.system.write("device-list: subscribe failed\n");
|
_ = logging.write("device-list: subscribe failed\n");
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
_ = runtime.system.write("device-list: subscribed\n");
|
_ = logging.write("device-list: subscribed\n");
|
||||||
|
|
||||||
var receive: [device_manager_protocol.message_maximum]u8 = undefined;
|
var receive: [device_manager_protocol.message_maximum]u8 = undefined;
|
||||||
while (true) {
|
while (true) {
|
||||||
const got = runtime.ipc.replyWait(endpoint, &.{}, &receive, null);
|
const got = ipc.replyWait(endpoint, &.{}, &receive, null);
|
||||||
if (!got.isMessage() or got.len < 1) continue;
|
if (!got.isMessage() or got.len < 1) continue;
|
||||||
switch (receive[0]) {
|
switch (receive[0]) {
|
||||||
@intFromEnum(device_manager_protocol.Operation.child_added) => {
|
@intFromEnum(device_manager_protocol.Operation.child_added) => {
|
||||||
|
|||||||
@@ -16,13 +16,17 @@
|
|||||||
//! restart. Tree reports (`child_added`) land in M18.2.
|
//! restart. Tree reports (`child_added`) land in M18.2.
|
||||||
|
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const runtime = @import("runtime");
|
const device = @import("driver");
|
||||||
|
const ipc = @import("ipc");
|
||||||
|
const process = @import("process");
|
||||||
|
const service = @import("service");
|
||||||
|
const time = @import("time");
|
||||||
|
const memory = @import("memory");
|
||||||
|
const logging = @import("logging");
|
||||||
const acpi_ids = @import("acpi-ids");
|
const acpi_ids = @import("acpi-ids");
|
||||||
const pci_class = @import("pci-class");
|
const pci_class = @import("pci-class");
|
||||||
const usb_ids = @import("usb-ids");
|
const usb_ids = @import("usb-ids");
|
||||||
const device_manager_protocol = @import("device-manager-protocol");
|
const device_manager_protocol = @import("device-manager-protocol");
|
||||||
const device = runtime.device;
|
|
||||||
const system = runtime.system;
|
|
||||||
|
|
||||||
/// The PCI class/subclass/prog-IF triple of an xHCI (USB 3) host controller —
|
/// The PCI class/subclass/prog-IF triple of an xHCI (USB 3) host controller —
|
||||||
/// Serial Bus Controller / USB Controller / XHCI — named from pci-class.zig rather
|
/// Serial Bus Controller / USB Controller / XHCI — named from pci-class.zig rather
|
||||||
@@ -154,7 +158,7 @@ const Driver = struct {
|
|||||||
|
|
||||||
const maximum_drivers = 16;
|
const maximum_drivers = 16;
|
||||||
var drivers: [maximum_drivers]Driver = .{Driver{}} ** maximum_drivers;
|
var drivers: [maximum_drivers]Driver = .{Driver{}} ** maximum_drivers;
|
||||||
var manager_endpoint: runtime.ipc.Handle = 0;
|
var manager_endpoint: ipc.Handle = 0;
|
||||||
var test_restart_mode = false;
|
var test_restart_mode = false;
|
||||||
var test_usb_restart_mode = false;
|
var test_usb_restart_mode = false;
|
||||||
var test_usb_killed = false;
|
var test_usb_killed = false;
|
||||||
@@ -169,14 +173,14 @@ var test_kill_due_ns: u64 = 0;
|
|||||||
/// buffered message. A subscriber whose endpoint stops accepting (it died) is
|
/// buffered message. A subscriber whose endpoint stops accepting (it died) is
|
||||||
/// dropped on the failed send.
|
/// dropped on the failed send.
|
||||||
const maximum_subscribers = 8;
|
const maximum_subscribers = 8;
|
||||||
var subscribers: [maximum_subscribers]?runtime.ipc.Handle = .{null} ** maximum_subscribers;
|
var subscribers: [maximum_subscribers]?ipc.Handle = .{null} ** maximum_subscribers;
|
||||||
|
|
||||||
/// Publish one event (a ChildAdded or ChildRemoved struct, the same encoding
|
/// Publish one event (a ChildAdded or ChildRemoved struct, the same encoding
|
||||||
/// the bus drivers send) to every subscriber.
|
/// the bus drivers send) to every subscriber.
|
||||||
fn publishEvent(event: []const u8) void {
|
fn publishEvent(event: []const u8) void {
|
||||||
for (&subscribers) |*slot| {
|
for (&subscribers) |*slot| {
|
||||||
if (slot.*) |handle| {
|
if (slot.*) |handle| {
|
||||||
if (!runtime.ipc.send(handle, event)) slot.* = null; // dead subscriber
|
if (!ipc.send(handle, event)) slot.* = null; // dead subscriber
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -284,17 +288,17 @@ fn spawnDriver(driver: *Driver) void {
|
|||||||
arguments[0] = std.fmt.bufPrint(&id_text, "{d}", .{driver.device_id}) catch return;
|
arguments[0] = std.fmt.bufPrint(&id_text, "{d}", .{driver.device_id}) catch return;
|
||||||
argument_count = 1;
|
argument_count = 1;
|
||||||
}
|
}
|
||||||
const child = system.spawnSupervised(driver.name(), arguments[0..argument_count], manager_endpoint) orelse {
|
const child = process.spawnSupervised(driver.name(), arguments[0..argument_count], manager_endpoint) orelse {
|
||||||
std.log.info("failed to spawn {s}", .{driver.name()});
|
std.log.info("failed to spawn {s}", .{driver.name()});
|
||||||
driver.state = .failed;
|
driver.state = .failed;
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
driver.process_id = child;
|
driver.process_id = child;
|
||||||
driver.spawn_ns = system.clock();
|
driver.spawn_ns = time.clock();
|
||||||
if (driver.speaks_protocol) {
|
if (driver.speaks_protocol) {
|
||||||
driver.state = .awaiting_hello;
|
driver.state = .awaiting_hello;
|
||||||
driver.hello_deadline_ns = driver.spawn_ns + hello_deadline_ms * 1_000_000;
|
driver.hello_deadline_ns = driver.spawn_ns + hello_deadline_ms * 1_000_000;
|
||||||
_ = system.timerOnce(manager_endpoint, hello_deadline_ms + 100);
|
_ = time.timerOnce(manager_endpoint, hello_deadline_ms + 100);
|
||||||
} else {
|
} else {
|
||||||
driver.state = .running;
|
driver.state = .running;
|
||||||
}
|
}
|
||||||
@@ -310,13 +314,13 @@ fn spawnDriver(driver: *Driver) void {
|
|||||||
/// restarts with backoff until the crash-loop cap.
|
/// restarts with backoff until the crash-loop cap.
|
||||||
fn onDriverExit(driver: *Driver) void {
|
fn onDriverExit(driver: *Driver) void {
|
||||||
pruneChildrenOf(driver.process_id);
|
pruneChildrenOf(driver.process_id);
|
||||||
const reason = runtime.process.exitReason(driver.process_id) orelse .fault;
|
const reason = process.exitReason(driver.process_id) orelse .fault;
|
||||||
if (reason == .exited) {
|
if (reason == .exited) {
|
||||||
driver.state = .stopped;
|
driver.state = .stopped;
|
||||||
std.log.info("{s} exited cleanly; not restarting", .{driver.name()});
|
std.log.info("{s} exited cleanly; not restarting", .{driver.name()});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const now = system.clock();
|
const now = time.clock();
|
||||||
const alive_ns = now - driver.spawn_ns;
|
const alive_ns = now - driver.spawn_ns;
|
||||||
driver.restarts = if (alive_ns < fast_death_ns) driver.restarts + 1 else 1;
|
driver.restarts = if (alive_ns < fast_death_ns) driver.restarts + 1 else 1;
|
||||||
if (driver.restarts >= crash_loop_cap) {
|
if (driver.restarts >= crash_loop_cap) {
|
||||||
@@ -328,7 +332,7 @@ fn onDriverExit(driver: *Driver) void {
|
|||||||
driver.state = .restarting;
|
driver.state = .restarting;
|
||||||
driver.restart_due_ns = now + delay_ms * 1_000_000;
|
driver.restart_due_ns = now + delay_ms * 1_000_000;
|
||||||
std.log.info("restarting {s} in {d} ms (died: {s})", .{ driver.name(), delay_ms, @tagName(reason) });
|
std.log.info("restarting {s} in {d} ms (died: {s})", .{ driver.name(), delay_ms, @tagName(reason) });
|
||||||
_ = system.timerOnce(manager_endpoint, delay_ms + 50);
|
_ = time.timerOnce(manager_endpoint, delay_ms + 50);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A timer landed: sweep every deadline. Overdue hellos are killed (the exit
|
/// A timer landed: sweep every deadline. Overdue hellos are killed (the exit
|
||||||
@@ -336,10 +340,10 @@ fn onDriverExit(driver: *Driver) void {
|
|||||||
/// respawn. Timers carry no id on purpose — the table is the state, and one
|
/// respawn. Timers carry no id on purpose — the table is the state, and one
|
||||||
/// sweep serves every armed deadline.
|
/// sweep serves every armed deadline.
|
||||||
fn sweepDeadlines() void {
|
fn sweepDeadlines() void {
|
||||||
const now = system.clock();
|
const now = time.clock();
|
||||||
if (test_kill_pid != 0 and now >= test_kill_due_ns) {
|
if (test_kill_pid != 0 and now >= test_kill_due_ns) {
|
||||||
std.log.info("test mode: killing the reporter", .{});
|
std.log.info("test mode: killing the reporter", .{});
|
||||||
_ = system.kill(test_kill_pid);
|
_ = process.kill(test_kill_pid);
|
||||||
test_kill_pid = 0;
|
test_kill_pid = 0;
|
||||||
}
|
}
|
||||||
for (&drivers) |*driver| {
|
for (&drivers) |*driver| {
|
||||||
@@ -347,7 +351,7 @@ fn sweepDeadlines() void {
|
|||||||
switch (driver.state) {
|
switch (driver.state) {
|
||||||
.awaiting_hello => if (now >= driver.hello_deadline_ns) {
|
.awaiting_hello => if (now >= driver.hello_deadline_ns) {
|
||||||
std.log.info("{s} missed its hello deadline", .{driver.name()});
|
std.log.info("{s} missed its hello deadline", .{driver.name()});
|
||||||
_ = system.kill(driver.process_id);
|
_ = process.kill(driver.process_id);
|
||||||
// The exit notification finishes the job via onDriverExit.
|
// The exit notification finishes the job via onDriverExit.
|
||||||
},
|
},
|
||||||
.restarting => if (now >= driver.restart_due_ns) spawnDriver(driver),
|
.restarting => if (now >= driver.restart_due_ns) spawnDriver(driver),
|
||||||
@@ -358,12 +362,12 @@ fn sweepDeadlines() void {
|
|||||||
|
|
||||||
// --- the harness callbacks -----------------------------------------------------
|
// --- the harness callbacks -----------------------------------------------------
|
||||||
|
|
||||||
fn initialise(endpoint: runtime.ipc.Handle) bool {
|
fn initialise(endpoint: ipc.Handle) bool {
|
||||||
manager_endpoint = endpoint;
|
manager_endpoint = endpoint;
|
||||||
|
|
||||||
// Enumerate into a heap buffer (too big for the one-page user stack).
|
// Enumerate into a heap buffer (too big for the one-page user stack).
|
||||||
const buffer = runtime.allocator().alloc(device.DeviceDescriptor, 64) catch {
|
const buffer = memory.allocator().alloc(device.DeviceDescriptor, 64) catch {
|
||||||
_ = runtime.system.write("/system/services/device-manager: out of memory\n");
|
_ = logging.write("/system/services/device-manager: out of memory\n");
|
||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
const total = device.enumerate(buffer);
|
const total = device.enumerate(buffer);
|
||||||
@@ -400,14 +404,14 @@ fn initialise(endpoint: runtime.ipc.Handle) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (matched == 0) {
|
if (matched == 0) {
|
||||||
_ = runtime.system.write("/system/services/device-manager: no matchable devices\n");
|
_ = logging.write("/system/services/device-manager: no matchable devices\n");
|
||||||
} else {
|
} else {
|
||||||
_ = runtime.system.write("/system/services/device-manager: ok\n");
|
_ = logging.write("/system/services/device-manager: ok\n");
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn onMessage(message: []const u8, reply: []u8, sender: u32, capability: ?runtime.ipc.Handle) usize {
|
fn onMessage(message: []const u8, reply: []u8, sender: u32, capability: ?ipc.Handle) usize {
|
||||||
if (message.len < 1) return 0;
|
if (message.len < 1) return 0;
|
||||||
switch (message[0]) {
|
switch (message[0]) {
|
||||||
@intFromEnum(device_manager_protocol.Operation.child_added) => return onChildAdded(message, reply, sender),
|
@intFromEnum(device_manager_protocol.Operation.child_added) => return onChildAdded(message, reply, sender),
|
||||||
@@ -432,8 +436,8 @@ fn onMessage(message: []const u8, reply: []u8, sender: u32, capability: ?runtime
|
|||||||
if (test_scanout_restart_mode and !test_scanout_killed and std.mem.eql(u8, driver.name(), "/system/drivers/virtio-gpu")) {
|
if (test_scanout_restart_mode and !test_scanout_killed and std.mem.eql(u8, driver.name(), "/system/drivers/virtio-gpu")) {
|
||||||
test_scanout_killed = true;
|
test_scanout_killed = true;
|
||||||
test_kill_pid = sender;
|
test_kill_pid = sender;
|
||||||
test_kill_due_ns = system.clock() + 1_500_000_000;
|
test_kill_due_ns = time.clock() + 1_500_000_000;
|
||||||
_ = system.timerOnce(manager_endpoint, 1600);
|
_ = time.timerOnce(manager_endpoint, 1600);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
status = -1;
|
status = -1;
|
||||||
@@ -489,8 +493,8 @@ fn onChildAdded(message: []const u8, reply: []u8, sender: u32) usize {
|
|||||||
// (M19.0 idempotence, proven end to end by pci-scan).
|
// (M19.0 idempotence, proven end to end by pci-scan).
|
||||||
test_usb_killed = true;
|
test_usb_killed = true;
|
||||||
test_kill_pid = sender;
|
test_kill_pid = sender;
|
||||||
test_kill_due_ns = system.clock() + 1_000_000_000;
|
test_kill_due_ns = time.clock() + 1_000_000_000;
|
||||||
_ = system.timerOnce(manager_endpoint, 1100);
|
_ = time.timerOnce(manager_endpoint, 1100);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -503,8 +507,8 @@ fn onChildAdded(message: []const u8, reply: []u8, sender: u32) usize {
|
|||||||
// needs a window to enumerate and subscribe before the events.
|
// needs a window to enumerate and subscribe before the events.
|
||||||
test_usb_killed = true;
|
test_usb_killed = true;
|
||||||
test_kill_pid = sender;
|
test_kill_pid = sender;
|
||||||
test_kill_due_ns = system.clock() + 2_000_000_000;
|
test_kill_due_ns = time.clock() + 2_000_000_000;
|
||||||
_ = system.timerOnce(manager_endpoint, 2100);
|
_ = time.timerOnce(manager_endpoint, 2100);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -547,7 +551,7 @@ fn onEnumerate(reply: []u8) usize {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// An application subscribed: its endpoint arrived as the call's capability.
|
/// An application subscribed: its endpoint arrived as the call's capability.
|
||||||
fn onSubscribe(reply: []u8, capability: ?runtime.ipc.Handle) usize {
|
fn onSubscribe(reply: []u8, capability: ?ipc.Handle) usize {
|
||||||
var status: i32 = -1;
|
var status: i32 = -1;
|
||||||
if (capability) |handle| {
|
if (capability) |handle| {
|
||||||
for (&subscribers) |*slot| {
|
for (&subscribers) |*slot| {
|
||||||
@@ -564,22 +568,22 @@ fn onSubscribe(reply: []u8, capability: ?runtime.ipc.Handle) usize {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn onNotification(badge: u64) void {
|
fn onNotification(badge: u64) void {
|
||||||
if (badge & runtime.ipc.notify_exit_bit != 0) {
|
if (badge & ipc.notify_exit_bit != 0) {
|
||||||
const dead: u32 = @intCast(badge & ~(runtime.ipc.notify_badge_bit | runtime.ipc.notify_exit_bit));
|
const dead: u32 = @intCast(badge & ~(ipc.notify_badge_bit | ipc.notify_exit_bit));
|
||||||
if (driverByProcess(dead)) |driver| onDriverExit(driver);
|
if (driverByProcess(dead)) |driver| onDriverExit(driver);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (badge & runtime.ipc.notify_timer_bit != 0) sweepDeadlines();
|
if (badge & ipc.notify_timer_bit != 0) sweepDeadlines();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn main(init: runtime.process.Init) void {
|
pub fn main(init: process.Init) void {
|
||||||
if (init.arguments.get(1)) |mode| {
|
if (init.arguments.get(1)) |mode| {
|
||||||
test_restart_mode = std.mem.eql(u8, mode, "test-restart");
|
test_restart_mode = std.mem.eql(u8, mode, "test-restart");
|
||||||
test_usb_restart_mode = std.mem.eql(u8, mode, "test-usb-restart");
|
test_usb_restart_mode = std.mem.eql(u8, mode, "test-usb-restart");
|
||||||
test_pci_restart_mode = std.mem.eql(u8, mode, "test-pci-restart");
|
test_pci_restart_mode = std.mem.eql(u8, mode, "test-pci-restart");
|
||||||
test_scanout_restart_mode = std.mem.eql(u8, mode, "test-scanout-restart");
|
test_scanout_restart_mode = std.mem.eql(u8, mode, "test-scanout-restart");
|
||||||
}
|
}
|
||||||
runtime.service.run(device_manager_protocol.message_maximum, .{
|
service.run(device_manager_protocol.message_maximum, .{
|
||||||
.service = .device_manager,
|
.service = .device_manager,
|
||||||
.init = initialise,
|
.init = initialise,
|
||||||
.on_message = onMessage,
|
.on_message = onMessage,
|
||||||
|
|||||||
@@ -10,14 +10,13 @@
|
|||||||
//! is only to prove client-driven animation, so its loop runs on its own frame timer and
|
//! is only to prove client-driven animation, so its loop runs on its own frame timer and
|
||||||
//! is deliberately independent of the mouse.
|
//! is deliberately independent of the mouse.
|
||||||
|
|
||||||
const runtime = @import("runtime");
|
|
||||||
const display = runtime.display;
|
|
||||||
const system = runtime.system;
|
|
||||||
const time = runtime.time;
|
|
||||||
|
|
||||||
|
const display = @import("display");
|
||||||
|
const time = @import("time");
|
||||||
|
const logging = @import("logging");
|
||||||
pub fn main() void {
|
pub fn main() void {
|
||||||
const mode = display.info() orelse {
|
const mode = display.info() orelse {
|
||||||
_ = system.write("display-demo: no display service\n");
|
_ = logging.write("display-demo: no display service\n");
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -33,7 +32,7 @@ pub fn main() void {
|
|||||||
_ = box.fill(0, 0, box_w, box_h, display.color(0xE0, 0x60, 0x40));
|
_ = box.fill(0, 0, box_w, box_h, display.color(0xE0, 0x60, 0x40));
|
||||||
|
|
||||||
_ = display.present();
|
_ = display.present();
|
||||||
_ = system.write("display-demo: scene up; animating\n");
|
_ = logging.write("display-demo: scene up; animating\n");
|
||||||
|
|
||||||
const span: i32 = @as(i32, @intCast(mode.width)) - @as(i32, @intCast(box_w));
|
const span: i32 = @as(i32, @intCast(mode.width)) - @as(i32, @intCast(box_w));
|
||||||
var x: i32 = 0;
|
var x: i32 = 0;
|
||||||
@@ -53,11 +52,11 @@ pub fn main() void {
|
|||||||
_ = display.present();
|
_ = display.present();
|
||||||
// A run of frames drawn through the compositor is the automated proof (the visible
|
// A run of frames drawn through the compositor is the automated proof (the visible
|
||||||
// motion is a screenshot away via `zig build run-x86-64`).
|
// motion is a screenshot away via `zig build run-x86-64`).
|
||||||
if (frame == 20) _ = system.write("display-demo: ok\n");
|
if (frame == 20) _ = logging.write("display-demo: ok\n");
|
||||||
time.sleep(time.Duration.fromMillis(30));
|
time.sleep(time.Duration.fromMillis(30));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn createFailed() void {
|
fn createFailed() void {
|
||||||
_ = system.write("display-demo: create failed\n");
|
_ = logging.write("display-demo: create failed\n");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,12 +6,13 @@
|
|||||||
//! slots in beside it later (V4); the compositor never learns which is active.
|
//! slots in beside it later (V4); the compositor never learns which is active.
|
||||||
|
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const runtime = @import("runtime");
|
const device = @import("driver");
|
||||||
|
const ipc = @import("ipc");
|
||||||
|
const time = @import("time");
|
||||||
|
const memory = @import("memory");
|
||||||
|
const logging = @import("logging");
|
||||||
const compositor = @import("compositor.zig");
|
const compositor = @import("compositor.zig");
|
||||||
|
|
||||||
const system = runtime.system;
|
|
||||||
const device = runtime.device;
|
|
||||||
const ipc = runtime.ipc;
|
|
||||||
const scanout_protocol = @import("scanout-protocol");
|
const scanout_protocol = @import("scanout-protocol");
|
||||||
const Rect = compositor.Rect;
|
const Rect = compositor.Rect;
|
||||||
const Surface = compositor.Surface;
|
const Surface = compositor.Surface;
|
||||||
@@ -66,26 +67,26 @@ pub const Gop = struct {
|
|||||||
var tries: u32 = 0;
|
var tries: u32 = 0;
|
||||||
const found = while (tries < 100) : (tries += 1) {
|
const found = while (tries < 100) : (tries += 1) {
|
||||||
if (findDisplay()) |f| break f;
|
if (findDisplay()) |f| break f;
|
||||||
system.sleep(50);
|
time.sleepMillis(50);
|
||||||
} else {
|
} else {
|
||||||
_ = system.write("display: no framebuffer device (headless?)\n");
|
_ = logging.write("display: no framebuffer device (headless?)\n");
|
||||||
return null;
|
return null;
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!device.claim(found.id)) {
|
if (!device.claim(found.id)) {
|
||||||
_ = system.write("display: could not claim the framebuffer\n");
|
_ = logging.write("display: could not claim the framebuffer\n");
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
// Resource 0 is the framebuffer memory window; the kernel maps it write-combining
|
// Resource 0 is the framebuffer memory window; the kernel maps it write-combining
|
||||||
// because the resource carries that flag (docs/display-plan.md D1).
|
// because the resource carries that flag (docs/display-plan.md D1).
|
||||||
const front_base = device.mmioMap(found.id, 0) orelse {
|
const front_base = device.mmioMap(found.id, 0) orelse {
|
||||||
_ = system.write("display: could not map the framebuffer\n");
|
_ = logging.write("display: could not map the framebuffer\n");
|
||||||
return null;
|
return null;
|
||||||
};
|
};
|
||||||
const size = @as(usize, found.height) * found.pitch;
|
const size = @as(usize, found.height) * found.pitch;
|
||||||
const back_base = system.mmap(size, system.PROT_READ | system.PROT_WRITE);
|
const back_base = memory.mmap(size, memory.PROT_READ | memory.PROT_WRITE);
|
||||||
if (system.mmapFailed(back_base)) {
|
if (memory.mmapFailed(back_base)) {
|
||||||
_ = system.write("display: could not allocate the back buffer\n");
|
_ = logging.write("display: could not allocate the back buffer\n");
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return .{
|
return .{
|
||||||
|
|||||||
@@ -16,15 +16,18 @@
|
|||||||
//! (docs/display-v2.md).
|
//! (docs/display-v2.md).
|
||||||
|
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const runtime = @import("runtime");
|
const ipc = @import("ipc");
|
||||||
|
const input = @import("input");
|
||||||
|
const Thread = @import("thread").Thread;
|
||||||
|
const service = @import("service");
|
||||||
|
const time = @import("time");
|
||||||
|
const display = @import("display");
|
||||||
|
const memory = @import("memory");
|
||||||
|
const logging = @import("logging");
|
||||||
const compositor = @import("compositor.zig");
|
const compositor = @import("compositor.zig");
|
||||||
const backend_mod = @import("backend.zig");
|
const backend_mod = @import("backend.zig");
|
||||||
|
|
||||||
const display_protocol = @import("display-protocol");
|
const display_protocol = @import("display-protocol");
|
||||||
const ipc = runtime.ipc;
|
|
||||||
const system = runtime.system;
|
|
||||||
const input = runtime.input;
|
|
||||||
const Thread = runtime.Thread;
|
|
||||||
const Rect = compositor.Rect;
|
const Rect = compositor.Rect;
|
||||||
const Surface = compositor.Surface;
|
const Surface = compositor.Surface;
|
||||||
|
|
||||||
@@ -105,7 +108,7 @@ fn updateFrameClock() void {
|
|||||||
const rate: u64 = if (reported == 0) 60 else @min(@max(reported, 30), 120);
|
const rate: u64 = if (reported == 0) 60 else @min(@max(reported, 30), 120);
|
||||||
frame_interval_milliseconds = @max(1000 / rate, 1);
|
frame_interval_milliseconds = @max(1000 / rate, 1);
|
||||||
var line: [96]u8 = undefined;
|
var line: [96]u8 = undefined;
|
||||||
_ = system.write(std.fmt.bufPrint(&line, "display: frame clock {d} Hz ({s})\n", .{
|
_ = logging.write(std.fmt.bufPrint(&line, "display: frame clock {d} Hz ({s})\n", .{
|
||||||
1000 / frame_interval_milliseconds,
|
1000 / frame_interval_milliseconds,
|
||||||
if (reported == 0) "default" else "panel EDID",
|
if (reported == 0) "default" else "panel EDID",
|
||||||
}) catch return);
|
}) catch return);
|
||||||
@@ -116,7 +119,7 @@ fn updateFrameClock() void {
|
|||||||
fn schedulePresent() void {
|
fn schedulePresent() void {
|
||||||
if (frame_timer_armed) return;
|
if (frame_timer_armed) return;
|
||||||
frame_timer_armed = true;
|
frame_timer_armed = true;
|
||||||
_ = system.timerOnce(service_endpoint, frame_interval_milliseconds);
|
_ = time.timerOnce(service_endpoint, frame_interval_milliseconds);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A timer landing — the frame clock, or the deferred first native present armed by
|
/// A timer landing — the frame clock, or the deferred first native present armed by
|
||||||
@@ -177,8 +180,8 @@ fn createLayer(x: i32, y: i32, w: u32, h: u32, z: u32, visible: bool) ?u32 {
|
|||||||
if (w == 0 or h == 0) return null;
|
if (w == 0 or h == 0) return null;
|
||||||
const slot = freeLayer() orelse return null;
|
const slot = freeLayer() orelse return null;
|
||||||
const len = @as(usize, w) * h * 4;
|
const len = @as(usize, w) * h * 4;
|
||||||
const base = system.mmap(len, system.PROT_READ | system.PROT_WRITE);
|
const base = memory.mmap(len, memory.PROT_READ | memory.PROT_WRITE);
|
||||||
if (system.mmapFailed(base)) return null;
|
if (memory.mmapFailed(base)) return null;
|
||||||
layers[slot] = .{
|
layers[slot] = .{
|
||||||
.used = true,
|
.used = true,
|
||||||
.x = x,
|
.x = x,
|
||||||
@@ -222,7 +225,7 @@ fn configureLayer(id: u32, x: i32, y: i32, z: u32, visible: bool) bool {
|
|||||||
fn destroyLayer(id: u32) bool {
|
fn destroyLayer(id: u32) bool {
|
||||||
const l = layerAt(id) orelse return false;
|
const l = layerAt(id) orelse return false;
|
||||||
addDamage(layerScreenRect(l));
|
addDamage(layerScreenRect(l));
|
||||||
_ = system.munmap(@intFromPtr(l.surface.pixels), l.surface_len);
|
_ = memory.munmap(@intFromPtr(l.surface.pixels), l.surface_len);
|
||||||
l.* = .{};
|
l.* = .{};
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -294,9 +297,9 @@ fn verifyNativePresent() void {
|
|||||||
const s = backend.surface();
|
const s = backend.surface();
|
||||||
const sample = s.pixels[@as(usize, s.height / 2) * s.stride + s.width / 2];
|
const sample = s.pixels[@as(usize, s.height / 2) * s.stride + s.width / 2];
|
||||||
if (sample != 0) {
|
if (sample != 0) {
|
||||||
_ = system.write("display: native present verified\n");
|
_ = logging.write("display: native present verified\n");
|
||||||
} else {
|
} else {
|
||||||
_ = system.write("display: native present FAILED (blank surface)\n");
|
_ = logging.write("display: native present FAILED (blank surface)\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -307,7 +310,7 @@ fn verifyNativePresent() void {
|
|||||||
fn attachScanout(stride: u32, width: u32, height: u32, format: u32, refresh_hz: u32, capability: ?ipc.Handle, reply: []u8) usize {
|
fn attachScanout(stride: u32, width: u32, height: u32, format: u32, refresh_hz: u32, capability: ?ipc.Handle, reply: []u8) usize {
|
||||||
const cap = capability orelse return fail(reply);
|
const cap = capability orelse return fail(reply);
|
||||||
if (width == 0 or height == 0 or stride < width) return fail(reply);
|
if (width == 0 or height == 0 or stride < width) return fail(reply);
|
||||||
const mapped = runtime.shared_memory.map(cap) orelse return fail(reply);
|
const mapped = memory.sharedMap(cap) orelse return fail(reply);
|
||||||
const scanout = ipc.lookup(.scanout) orelse return fail(reply);
|
const scanout = ipc.lookup(.scanout) orelse return fail(reply);
|
||||||
// A second announce means the driver died and was restarted (V6): re-attach to its fresh
|
// A second announce means the driver died and was restarted (V6): re-attach to its fresh
|
||||||
// scanout. (The previous shared mapping leaks — there is no shared_memory_unmap syscall yet — but the
|
// scanout. (The previous shared mapping leaks — there is no shared_memory_unmap syscall yet — but the
|
||||||
@@ -331,8 +334,8 @@ fn attachScanout(stride: u32, width: u32, height: u32, format: u32, refresh_hz:
|
|||||||
addDamage(screenRect()); // the whole new surface must be painted
|
addDamage(screenRect()); // the whole new surface must be painted
|
||||||
pending_native_verify = true;
|
pending_native_verify = true;
|
||||||
if (!reattach) pending_modeset_check = true; // the mode-set self-check runs once, on first upgrade
|
if (!reattach) pending_modeset_check = true; // the mode-set self-check runs once, on first upgrade
|
||||||
_ = system.timerOnce(service_endpoint, 50); // present once the driver is serving .scanout
|
_ = time.timerOnce(service_endpoint, 50); // present once the driver is serving .scanout
|
||||||
_ = system.write(if (reattach)
|
_ = logging.write(if (reattach)
|
||||||
"display: scanout re-attached\n"
|
"display: scanout re-attached\n"
|
||||||
else
|
else
|
||||||
"display: scanout upgraded to virtio-gpu\n");
|
"display: scanout upgraded to virtio-gpu\n");
|
||||||
@@ -349,7 +352,7 @@ fn modesetSelfCheck() void {
|
|||||||
var mode_list: [4]backend_mod.Mode = undefined;
|
var mode_list: [4]backend_mod.Mode = undefined;
|
||||||
const count = backend.modes(&mode_list);
|
const count = backend.modes(&mode_list);
|
||||||
if (count == 0) {
|
if (count == 0) {
|
||||||
_ = system.write("display: mode-set self-check: no modes reported\n");
|
_ = logging.write("display: mode-set self-check: no modes reported\n");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const current = backend.info();
|
const current = backend.info();
|
||||||
@@ -361,11 +364,11 @@ fn modesetSelfCheck() void {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
const wanted = target orelse {
|
const wanted = target orelse {
|
||||||
_ = system.write("display: mode-set self-check: no alternate mode offered\n");
|
_ = logging.write("display: mode-set self-check: no alternate mode offered\n");
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
if (!backend.setMode(wanted.width, wanted.height)) {
|
if (!backend.setMode(wanted.width, wanted.height)) {
|
||||||
_ = system.write("display: mode set FAILED\n");
|
_ = logging.write("display: mode set FAILED\n");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
addDamage(screenRect()); // repaint the whole screen at the new resolution, then present it
|
addDamage(screenRect()); // repaint the whole screen at the new resolution, then present it
|
||||||
@@ -374,10 +377,10 @@ fn modesetSelfCheck() void {
|
|||||||
const now = backend.info();
|
const now = backend.info();
|
||||||
if (now.width == wanted.width and now.height == wanted.height) {
|
if (now.width == wanted.width and now.height == wanted.height) {
|
||||||
var line: [80]u8 = undefined;
|
var line: [80]u8 = undefined;
|
||||||
_ = system.write(std.fmt.bufPrint(&line, "display: mode set to {d}x{d}, verified\n", .{ now.width, now.height }) catch "display: mode set, verified\n");
|
_ = logging.write(std.fmt.bufPrint(&line, "display: mode set to {d}x{d}, verified\n", .{ now.width, now.height }) catch "display: mode set, verified\n");
|
||||||
if (backend.hasFencedPresent()) _ = system.write("display: fenced present ok\n");
|
if (backend.hasFencedPresent()) _ = logging.write("display: fenced present ok\n");
|
||||||
} else {
|
} else {
|
||||||
_ = system.write("display: mode set FAILED (geometry unchanged)\n");
|
_ = logging.write("display: mode set FAILED (geometry unchanged)\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -406,14 +409,14 @@ fn selfCheck() void {
|
|||||||
present(); // repaint the self-check region back to the background
|
present(); // repaint the self-check region back to the background
|
||||||
|
|
||||||
if (overlap == green and bottom_only == red) {
|
if (overlap == green and bottom_only == red) {
|
||||||
_ = system.write("display: compositor self-check ok\n");
|
_ = logging.write("display: compositor self-check ok\n");
|
||||||
} else {
|
} else {
|
||||||
_ = system.write("display: compositor self-check FAILED\n");
|
_ = logging.write("display: compositor self-check FAILED\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fail_check(_: []const u8) void {
|
fn fail_check(_: []const u8) void {
|
||||||
_ = system.write("display: compositor self-check FAILED (setup)\n");
|
_ = logging.write("display: compositor self-check FAILED (setup)\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- cursor + mouse-input thread --------------------------------------------
|
// --- cursor + mouse-input thread --------------------------------------------
|
||||||
@@ -499,7 +502,7 @@ fn clampAxis(value: i32, max: i32) i32 {
|
|||||||
/// compositor — so no lock guards the framebuffer.
|
/// compositor — so no lock guards the framebuffer.
|
||||||
fn mouseListener(width: u32, height: u32) void {
|
fn mouseListener(width: u32, height: u32) void {
|
||||||
var mouse = input.subscribeMouse() orelse {
|
var mouse = input.subscribeMouse() orelse {
|
||||||
_ = system.write("display: mouse subscribe failed\n");
|
_ = logging.write("display: mouse subscribe failed\n");
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
// Our own handle to the compositor's endpoint. IPC handles are per-thread, so we
|
// Our own handle to the compositor's endpoint. IPC handles are per-thread, so we
|
||||||
@@ -507,7 +510,7 @@ fn mouseListener(width: u32, height: u32) void {
|
|||||||
// handle in this thread's table. A poke posted here wakes the compositor loop parked
|
// handle in this thread's table. A poke posted here wakes the compositor loop parked
|
||||||
// in replyWait (docs/threading.md: handles do not cross threads).
|
// in replyWait (docs/threading.md: handles do not cross threads).
|
||||||
cursor_channel.poke_endpoint = ipc.lookup(.display) orelse {
|
cursor_channel.poke_endpoint = ipc.lookup(.display) orelse {
|
||||||
_ = system.write("display: mouse listener could not reach the compositor endpoint\n");
|
_ = logging.write("display: mouse listener could not reach the compositor endpoint\n");
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
const max_x: i32 = @as(i32, @intCast(width)) - 1;
|
const max_x: i32 = @as(i32, @intCast(width)) - 1;
|
||||||
@@ -543,7 +546,7 @@ fn renderCursor() void {
|
|||||||
@abs(snapshot.y - cursor_origin_y) >= cursor_report_threshold)
|
@abs(snapshot.y - cursor_origin_y) >= cursor_report_threshold)
|
||||||
{
|
{
|
||||||
cursor_tracking_reported = true;
|
cursor_tracking_reported = true;
|
||||||
_ = system.write("display: cursor tracking mouse ok\n");
|
_ = logging.write("display: cursor tracking mouse ok\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -555,7 +558,7 @@ fn startCursorTracking() void {
|
|||||||
cursor_origin_x = @divTrunc(@as(i32, @intCast(mode.width)), 2);
|
cursor_origin_x = @divTrunc(@as(i32, @intCast(mode.width)), 2);
|
||||||
cursor_origin_y = @divTrunc(@as(i32, @intCast(mode.height)), 2);
|
cursor_origin_y = @divTrunc(@as(i32, @intCast(mode.height)), 2);
|
||||||
const id = createLayer(cursor_origin_x, cursor_origin_y, cursor_size, cursor_size, cursor_z, true) orelse {
|
const id = createLayer(cursor_origin_x, cursor_origin_y, cursor_size, cursor_size, cursor_z, true) orelse {
|
||||||
_ = system.write("display: could not create cursor layer\n");
|
_ = logging.write("display: could not create cursor layer\n");
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
cursor_layer = id;
|
cursor_layer = id;
|
||||||
@@ -563,7 +566,7 @@ fn startCursorTracking() void {
|
|||||||
present(); // show the cursor at its start position
|
present(); // show the cursor at its start position
|
||||||
|
|
||||||
_ = Thread.spawn(.{}, mouseListener, .{ mode.width, mode.height }) catch {
|
_ = Thread.spawn(.{}, mouseListener, .{ mode.width, mode.height }) catch {
|
||||||
_ = system.write("display: could not spawn mouse listener\n");
|
_ = logging.write("display: could not spawn mouse listener\n");
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -583,11 +586,11 @@ fn initialise(endpoint: ipc.Handle) bool {
|
|||||||
present();
|
present();
|
||||||
|
|
||||||
var line: [96]u8 = undefined;
|
var line: [96]u8 = undefined;
|
||||||
_ = system.write(std.fmt.bufPrint(&line, "display: online {d}x{d} pitch {d} format {d}\n", .{
|
_ = logging.write(std.fmt.bufPrint(&line, "display: online {d}x{d} pitch {d} format {d}\n", .{
|
||||||
mode.width, mode.height, mode.pitch, mode.format,
|
mode.width, mode.height, mode.pitch, mode.format,
|
||||||
}) catch "display: online\n");
|
}) catch "display: online\n");
|
||||||
updateFrameClock();
|
updateFrameClock();
|
||||||
_ = system.write("display: presented frame 0\n");
|
_ = logging.write("display: presented frame 0\n");
|
||||||
|
|
||||||
selfCheck();
|
selfCheck();
|
||||||
|
|
||||||
@@ -692,7 +695,7 @@ fn onNotification(badge: u64) void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn main() void {
|
pub fn main() void {
|
||||||
runtime.service.run(display_protocol.message_maximum, .{
|
service.run(display_protocol.message_maximum, .{
|
||||||
.service = .display,
|
.service = .display,
|
||||||
.init = initialise,
|
.init = initialise,
|
||||||
.on_message = onMessage,
|
.on_message = onMessage,
|
||||||
|
|||||||
@@ -5,15 +5,17 @@
|
|||||||
//! kernel test spawns it alongside init.
|
//! kernel test spawns it alongside init.
|
||||||
|
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const runtime = @import("runtime");
|
const fs = @import("file-system");
|
||||||
const fs = runtime.fs;
|
const process = @import("process");
|
||||||
|
const time = @import("time");
|
||||||
|
const logging = @import("logging");
|
||||||
|
|
||||||
fn writeLine(comptime fmt: []const u8, arguments: anytype) void {
|
fn writeLine(comptime fmt: []const u8, arguments: anytype) void {
|
||||||
var line: [128]u8 = undefined;
|
var line: [128]u8 = undefined;
|
||||||
_ = runtime.system.write(std.fmt.bufPrint(&line, fmt, arguments) catch return);
|
_ = logging.write(std.fmt.bufPrint(&line, fmt, arguments) catch return);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn main(init: runtime.process.Init) void {
|
pub fn main(init: process.Init) void {
|
||||||
_ = init;
|
_ = init;
|
||||||
|
|
||||||
// Wait for /mnt/usb to be mounted — the fat server races us at boot (it must
|
// Wait for /mnt/usb to be mounted — the fat server races us at boot (it must
|
||||||
@@ -22,10 +24,10 @@ pub fn main(init: runtime.process.Init) void {
|
|||||||
var tries: u32 = 0;
|
var tries: u32 = 0;
|
||||||
while (opened == null and tries < 1400) : (tries += 1) {
|
while (opened == null and tries < 1400) : (tries += 1) {
|
||||||
opened = fs.openDirectory("/mnt/usb");
|
opened = fs.openDirectory("/mnt/usb");
|
||||||
if (opened == null) runtime.system.sleep(50);
|
if (opened == null) time.sleepMillis(50);
|
||||||
}
|
}
|
||||||
var dir = opened orelse {
|
var dir = opened orelse {
|
||||||
_ = runtime.system.write("fat-test: /mnt/usb never became available\n");
|
_ = logging.write("fat-test: /mnt/usb never became available\n");
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -47,7 +49,7 @@ pub fn main(init: runtime.process.Init) void {
|
|||||||
const n = file.read(&magic) orelse 0;
|
const n = file.read(&magic) orelse 0;
|
||||||
file.close();
|
file.close();
|
||||||
if (n == 4 and magic[0] == 0x7F and magic[1] == 'E' and magic[2] == 'L' and magic[3] == 'F') {
|
if (n == 4 and magic[0] == 0x7F and magic[1] == 'E' and magic[2] == 'L' and magic[3] == 'F') {
|
||||||
_ = runtime.system.write("fat-test: read /mnt/usb/system/kernel ELF magic ok\n");
|
_ = logging.write("fat-test: read /mnt/usb/system/kernel ELF magic ok\n");
|
||||||
} else {
|
} else {
|
||||||
writeLine("fat-test: /mnt/usb/system/kernel read {d} bytes (not ELF magic)\n", .{n});
|
writeLine("fat-test: /mnt/usb/system/kernel read {d} bytes (not ELF magic)\n", .{n});
|
||||||
}
|
}
|
||||||
@@ -68,12 +70,12 @@ pub fn main(init: runtime.process.Init) void {
|
|||||||
writeLine("fat-test: mtime {d}\n", .{attrs.mtime});
|
writeLine("fat-test: mtime {d}\n", .{attrs.mtime});
|
||||||
mtime_ok = attrs.mtime > 1_577_836_800; // after 2020-01-01
|
mtime_ok = attrs.mtime > 1_577_836_800; // after 2020-01-01
|
||||||
}
|
}
|
||||||
if (mtime_ok) _ = runtime.system.write("fat-test: mtime ok\n");
|
if (mtime_ok) _ = logging.write("fat-test: mtime ok\n");
|
||||||
|
|
||||||
// Rename it, then read from the new name and confirm the old name is gone.
|
// Rename it, then read from the new name and confirm the old name is gone.
|
||||||
const renamed = fs.rename("/mnt/usb/TESTDIR/HELLO.TXT", "/mnt/usb/TESTDIR/RENAMED.TXT");
|
const renamed = fs.rename("/mnt/usb/TESTDIR/HELLO.TXT", "/mnt/usb/TESTDIR/RENAMED.TXT");
|
||||||
const old_gone = !fs.exists("/mnt/usb/TESTDIR/HELLO.TXT");
|
const old_gone = !fs.exists("/mnt/usb/TESTDIR/HELLO.TXT");
|
||||||
if (renamed and old_gone) _ = runtime.system.write("fat-test: rename ok\n");
|
if (renamed and old_gone) _ = logging.write("fat-test: rename ok\n");
|
||||||
var readback = false;
|
var readback = false;
|
||||||
if (fs.open("/mnt/usb/TESTDIR/RENAMED.TXT", .{})) |reopened| {
|
if (fs.open("/mnt/usb/TESTDIR/RENAMED.TXT", .{})) |reopened| {
|
||||||
var f = reopened;
|
var f = reopened;
|
||||||
@@ -85,19 +87,19 @@ pub fn main(init: runtime.process.Init) void {
|
|||||||
const removed = fs.remove("/mnt/usb/TESTDIR/RENAMED.TXT");
|
const removed = fs.remove("/mnt/usb/TESTDIR/RENAMED.TXT");
|
||||||
const gone = !fs.exists("/mnt/usb/TESTDIR/RENAMED.TXT");
|
const gone = !fs.exists("/mnt/usb/TESTDIR/RENAMED.TXT");
|
||||||
if (wrote and mtime_ok and renamed and old_gone and readback and removed and gone) {
|
if (wrote and mtime_ok and renamed and old_gone and readback and removed and gone) {
|
||||||
_ = runtime.system.write("fat-test: mutations ok\n");
|
_ = logging.write("fat-test: mutations ok\n");
|
||||||
} else {
|
} else {
|
||||||
writeLine("fat-test: mutations FAILED (wrote={} mtime={} renamed={} oldgone={} read={} removed={} gone={})\n", .{ wrote, mtime_ok, renamed, old_gone, readback, removed, gone });
|
writeLine("fat-test: mutations FAILED (wrote={} mtime={} renamed={} oldgone={} read={} removed={} gone={})\n", .{ wrote, mtime_ok, renamed, old_gone, readback, removed, gone });
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
_ = runtime.system.write("fat-test: mkdir /mnt/usb/TESTDIR failed\n");
|
_ = logging.write("fat-test: mkdir /mnt/usb/TESTDIR failed\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (count > 0) {
|
if (count > 0) {
|
||||||
while (true) {
|
while (true) {
|
||||||
_ = runtime.system.write("fat-test: ok\n");
|
_ = logging.write("fat-test: ok\n");
|
||||||
runtime.system.sleep(1000);
|
time.sleepMillis(1000);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ = runtime.system.write("fat-test: root listing was empty\n");
|
_ = logging.write("fat-test: root listing was empty\n");
|
||||||
}
|
}
|
||||||
|
|||||||
+29
-23
@@ -10,19 +10,25 @@
|
|||||||
//! it.
|
//! it.
|
||||||
|
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const runtime = @import("runtime");
|
const ipc = @import("ipc");
|
||||||
|
const process = @import("process");
|
||||||
|
const service = @import("service");
|
||||||
|
const time = @import("time");
|
||||||
|
const block = @import("block");
|
||||||
|
const file_system = @import("file-system");
|
||||||
|
const memory = @import("memory");
|
||||||
|
const logging = @import("logging");
|
||||||
const engine = @import("engine.zig");
|
const engine = @import("engine.zig");
|
||||||
const on_disk = @import("on-disk.zig");
|
const on_disk = @import("on-disk.zig");
|
||||||
const vfs_protocol = @import("vfs-protocol");
|
const vfs_protocol = @import("vfs-protocol");
|
||||||
const dma = runtime.dma;
|
|
||||||
|
|
||||||
const mount_point = "/mnt/usb";
|
const mount_point = "/mnt/usb";
|
||||||
|
|
||||||
// The engine's BlockDevice, backed by the `.block` driver plus a DMA bounce
|
// The engine's BlockDevice, backed by the `.block` driver plus a DMA bounce
|
||||||
// buffer the driver reads/writes by physical address.
|
// buffer the driver reads/writes by physical address.
|
||||||
const IpcBlock = struct {
|
const IpcBlock = struct {
|
||||||
device: runtime.block.Device,
|
device: block.Device,
|
||||||
bounce: dma.Region, // engine.max_transfer_sectors * 512 bytes
|
bounce: memory.DmaRegion, // engine.max_transfer_sectors * 512 bytes
|
||||||
|
|
||||||
fn readBlocks(context: *anyopaque, lba: u64, count: u32, buffer: []u8) bool {
|
fn readBlocks(context: *anyopaque, lba: u64, count: u32, buffer: []u8) bool {
|
||||||
const self: *IpcBlock = @ptrCast(@alignCast(context));
|
const self: *IpcBlock = @ptrCast(@alignCast(context));
|
||||||
@@ -89,17 +95,17 @@ fn fail(out: []u8) usize {
|
|||||||
const mount_retry_ms = 500;
|
const mount_retry_ms = 500;
|
||||||
|
|
||||||
var mounted = false;
|
var mounted = false;
|
||||||
var service_endpoint: runtime.ipc.Handle = 0;
|
var service_endpoint: ipc.Handle = 0;
|
||||||
|
|
||||||
fn initialise(endpoint: runtime.ipc.Handle) bool {
|
fn initialise(endpoint: ipc.Handle) bool {
|
||||||
service_endpoint = endpoint;
|
service_endpoint = endpoint;
|
||||||
_ = runtime.system.write("/system/services/fat: starting, waiting for a block device\n");
|
_ = logging.write("/system/services/fat: starting, waiting for a block device\n");
|
||||||
// With the router in the kernel, clients hold OUR node ids directly; sweep
|
// With the router in the kernel, clients hold OUR node ids directly; sweep
|
||||||
// a dead client's open handles via the published exit events (the pattern
|
// a dead client's open handles via the published exit events (the pattern
|
||||||
// the old userspace router used for its own table).
|
// the old userspace router used for its own table).
|
||||||
_ = runtime.process.subscribeExits(endpoint);
|
_ = process.subscribeExits(endpoint);
|
||||||
tryBringUp();
|
tryBringUp();
|
||||||
if (!mounted) _ = runtime.system.timerOnce(endpoint, mount_retry_ms);
|
if (!mounted) _ = time.timerOnce(endpoint, mount_retry_ms);
|
||||||
return true; // serve regardless: requests fail politely until storage mounts
|
return true; // serve regardless: requests fail politely until storage mounts
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -107,12 +113,12 @@ fn initialise(endpoint: runtime.ipc.Handle) bool {
|
|||||||
/// `mounted` on success; a failure leaves everything untouched for the next tick.
|
/// `mounted` on success; a failure leaves everything untouched for the next tick.
|
||||||
fn tryBringUp() void {
|
fn tryBringUp() void {
|
||||||
if (mounted) return;
|
if (mounted) return;
|
||||||
const device = runtime.block.tryOpen() orelse return;
|
const device = block.tryOpen() orelse return;
|
||||||
const geometry = device.geometry() orelse {
|
const geometry = device.geometry() orelse {
|
||||||
_ = runtime.system.write("/system/services/fat: block geometry unavailable\n");
|
_ = logging.write("/system/services/fat: block geometry unavailable\n");
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
const bounce = dma.alloc(engine.max_transfer_sectors * 512, dma.coherent) orelse return;
|
const bounce = memory.dmaAlloc(engine.max_transfer_sectors * 512, memory.dma_coherent) orelse return;
|
||||||
ipc_block = .{ .device = device, .bounce = bounce };
|
ipc_block = .{ .device = device, .bounce = bounce };
|
||||||
|
|
||||||
const block_device = engine.BlockDevice{
|
const block_device = engine.BlockDevice{
|
||||||
@@ -123,7 +129,7 @@ fn tryBringUp() void {
|
|||||||
.writeBlocksFn = IpcBlock.writeBlocks,
|
.writeBlocksFn = IpcBlock.writeBlocks,
|
||||||
};
|
};
|
||||||
filesystem = engine.FileSystem.mount(block_device) orelse {
|
filesystem = engine.FileSystem.mount(block_device) orelse {
|
||||||
_ = runtime.system.write("/system/services/fat: not a FAT filesystem\n");
|
_ = logging.write("/system/services/fat: not a FAT filesystem\n");
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
std.log.info("mounted FAT ({s}, {d} clusters, partition lba {d})", .{ @tagName(filesystem.geometry.fat_type), filesystem.geometry.cluster_count, filesystem.base_lba });
|
std.log.info("mounted FAT ({s}, {d} clusters, partition lba {d})", .{ @tagName(filesystem.geometry.fat_type), filesystem.geometry.cluster_count, filesystem.base_lba });
|
||||||
@@ -131,30 +137,30 @@ fn tryBringUp() void {
|
|||||||
// Mount ourselves into the kernel VFS at /mnt/usb — and serve /var from the
|
// Mount ourselves into the kernel VFS at /mnt/usb — and serve /var from the
|
||||||
// volume's /var subtree, so FHS paths (the logger's /var/log) stay decoupled
|
// volume's /var subtree, so FHS paths (the logger's /var/log) stay decoupled
|
||||||
// from which volume carries them.
|
// from which volume carries them.
|
||||||
if (runtime.fs.mount(mount_point, endpointForMount())) {
|
if (file_system.mount(mount_point, endpointForMount())) {
|
||||||
std.log.info("mounted {s}", .{mount_point});
|
std.log.info("mounted {s}", .{mount_point});
|
||||||
} else {
|
} else {
|
||||||
_ = runtime.system.write("/system/services/fat: could not mount /mnt/usb\n");
|
_ = logging.write("/system/services/fat: could not mount /mnt/usb\n");
|
||||||
}
|
}
|
||||||
if (runtime.fs.mountRewritten("/var", endpointForMount(), "/var")) {
|
if (file_system.mountRewritten("/var", endpointForMount(), "/var")) {
|
||||||
std.log.info("mounted /var", .{});
|
std.log.info("mounted /var", .{});
|
||||||
} else {
|
} else {
|
||||||
_ = runtime.system.write("/system/services/fat: could not mount /var\n");
|
_ = logging.write("/system/services/fat: could not mount /var\n");
|
||||||
}
|
}
|
||||||
mounted = true;
|
mounted = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn endpointForMount() runtime.ipc.Handle {
|
fn endpointForMount() ipc.Handle {
|
||||||
return service_endpoint;
|
return service_endpoint;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A subscribed process-exit event: release every open handle the dead client
|
/// A subscribed process-exit event: release every open handle the dead client
|
||||||
/// held, so a crashed reader can't pin table slots (or, later, locks).
|
/// held, so a crashed reader can't pin table slots (or, later, locks).
|
||||||
fn onNotification(badge: u64) void {
|
fn onNotification(badge: u64) void {
|
||||||
const got = runtime.ipc.Received{ .len = 0, .badge = badge, .cap = null };
|
const got = ipc.Received{ .len = 0, .badge = badge, .cap = null };
|
||||||
if (got.isTimer()) {
|
if (got.isTimer()) {
|
||||||
tryBringUp();
|
tryBringUp();
|
||||||
if (!mounted) _ = runtime.system.timerOnce(service_endpoint, mount_retry_ms);
|
if (!mounted) _ = time.timerOnce(service_endpoint, mount_retry_ms);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!got.isChildExit()) return;
|
if (!got.isChildExit()) return;
|
||||||
@@ -199,7 +205,7 @@ fn handleOpen(out: []u8, path: []const u8, flags: u32, sender: u32) usize {
|
|||||||
return writeReply(out, .{ .status = 0, .node = index }, &.{});
|
return writeReply(out, .{ .status = 0, .node = index }, &.{});
|
||||||
}
|
}
|
||||||
|
|
||||||
fn onMessage(message: []const u8, out: []u8, sender: u32, capability: ?runtime.ipc.Handle) usize {
|
fn onMessage(message: []const u8, out: []u8, sender: u32, capability: ?ipc.Handle) usize {
|
||||||
_ = capability;
|
_ = capability;
|
||||||
if (!mounted) return fail(out); // storage not up (yet): fail politely, clients retry
|
if (!mounted) return fail(out); // storage not up (yet): fail politely, clients retry
|
||||||
if (message.len < vfs_protocol.request_size) return fail(out);
|
if (message.len < vfs_protocol.request_size) return fail(out);
|
||||||
@@ -208,7 +214,7 @@ fn onMessage(message: []const u8, out: []u8, sender: u32, capability: ?runtime.i
|
|||||||
|
|
||||||
// Stamp create/write with the current wall-clock time (mtime). Cheap, and it
|
// Stamp create/write with the current wall-clock time (mtime). Cheap, and it
|
||||||
// keeps the engine pure (it takes the time as data, not a syscall).
|
// keeps the engine pure (it takes the time as data, not a syscall).
|
||||||
filesystem.current_time_epoch = runtime.system.wallClock();
|
filesystem.current_time_epoch = time.wallClock();
|
||||||
|
|
||||||
switch (request.operation) {
|
switch (request.operation) {
|
||||||
.open => return handleOpen(out, payload[0..@min(payload.len, request.len)], request.flags, sender),
|
.open => return handleOpen(out, payload[0..@min(payload.len, request.len)], request.flags, sender),
|
||||||
@@ -287,7 +293,7 @@ fn onMessage(message: []const u8, out: []u8, sender: u32, capability: ?runtime.i
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn main() void {
|
pub fn main() void {
|
||||||
runtime.service.run(vfs_protocol.message_maximum, .{
|
service.run(vfs_protocol.message_maximum, .{
|
||||||
.service = .fat,
|
.service = .fat,
|
||||||
.init = initialise,
|
.init = initialise,
|
||||||
.on_message = onMessage,
|
.on_message = onMessage,
|
||||||
|
|||||||
@@ -19,9 +19,9 @@
|
|||||||
//! cannot hold an FDT `compatible` string ("brcm,bcm2835-aux-uart") — identity
|
//! cannot hold an FDT `compatible` string ("brcm,bcm2835-aux-uart") — identity
|
||||||
//! widens before this file grows a body.
|
//! widens before this file grows a body.
|
||||||
|
|
||||||
const runtime = @import("runtime");
|
|
||||||
|
|
||||||
pub fn main(init: runtime.process.Init) void {
|
const process = @import("process");
|
||||||
|
pub fn main(init: process.Init) void {
|
||||||
_ = init;
|
_ = init;
|
||||||
// Not implemented: exit cleanly and silently (a bare spawn by the
|
// Not implemented: exit cleanly and silently (a bare spawn by the
|
||||||
// initial-ramdisk sweep must not derange other tests' markers). The
|
// initial-ramdisk sweep must not derange other tests' markers). The
|
||||||
|
|||||||
@@ -18,7 +18,11 @@
|
|||||||
//! composing into a clean poweroff.
|
//! composing into a clean poweroff.
|
||||||
|
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const runtime = @import("runtime");
|
const ipc = @import("ipc");
|
||||||
|
const process = @import("process");
|
||||||
|
const time = @import("time");
|
||||||
|
const memory = @import("memory");
|
||||||
|
const logging = @import("logging");
|
||||||
const power_protocol = @import("power-protocol");
|
const power_protocol = @import("power-protocol");
|
||||||
const build_options = @import("build_options");
|
const build_options = @import("build_options");
|
||||||
|
|
||||||
@@ -55,7 +59,7 @@ const boot_services = if (build_options.diagnose) [_][]const u8{
|
|||||||
var child_ids: [boot_services.len]u32 = .{0} ** boot_services.len;
|
var child_ids: [boot_services.len]u32 = .{0} ** boot_services.len;
|
||||||
var restart_counts: [boot_services.len]u32 = .{0} ** boot_services.len;
|
var restart_counts: [boot_services.len]u32 = .{0} ** boot_services.len;
|
||||||
var shutting_down = false;
|
var shutting_down = false;
|
||||||
var supervision_endpoint: runtime.ipc.Handle = 0;
|
var supervision_endpoint: ipc.Handle = 0;
|
||||||
|
|
||||||
/// Give up restarting a service after this many crashes — a crash-loop cap, so a service
|
/// Give up restarting a service after this many crashes — a crash-loop cap, so a service
|
||||||
/// that faults immediately on every spawn doesn't respawn forever.
|
/// that faults immediately on every spawn doesn't respawn forever.
|
||||||
@@ -68,28 +72,28 @@ pub fn main() void {
|
|||||||
// free it. A fault here would kill init before it heartbeats — so the init
|
// free it. A fault here would kill init before it heartbeats — so the init
|
||||||
// test doubles as the heap regression test. (C code links the same heap via
|
// test doubles as the heap regression test. (C code links the same heap via
|
||||||
// the extern malloc/free symbols; Zig code uses this allocator.)
|
// the extern malloc/free symbols; Zig code uses this allocator.)
|
||||||
const gpa = runtime.allocator();
|
const gpa = memory.allocator();
|
||||||
if (gpa.alloc(u8, 64)) |buffer| {
|
if (gpa.alloc(u8, 64)) |buffer| {
|
||||||
const message = "/system/services/init: heap ok\n";
|
const message = "/system/services/init: heap ok\n";
|
||||||
@memcpy(buffer[0..message.len], message);
|
@memcpy(buffer[0..message.len], message);
|
||||||
_ = runtime.system.write(buffer[0..message.len]);
|
_ = logging.write(buffer[0..message.len]);
|
||||||
gpa.free(buffer);
|
gpa.free(buffer);
|
||||||
} else |_| {}
|
} else |_| {}
|
||||||
|
|
||||||
// One endpoint carries everything init waits on: children's exit
|
// One endpoint carries everything init waits on: children's exit
|
||||||
// notifications (they are spawned supervised against it), init's own
|
// notifications (they are spawned supervised against it), init's own
|
||||||
// signals, and power events it subscribes to. All arrive in the loop below.
|
// signals, and power events it subscribes to. All arrive in the loop below.
|
||||||
supervision_endpoint = runtime.ipc.createIpcEndpoint() orelse {
|
supervision_endpoint = ipc.createIpcEndpoint() orelse {
|
||||||
_ = runtime.system.write("/system/services/init: no endpoint\n");
|
_ = logging.write("/system/services/init: no endpoint\n");
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
_ = runtime.process.bindSignals(supervision_endpoint);
|
_ = process.bindSignals(supervision_endpoint);
|
||||||
|
|
||||||
// Bring up the boot services, supervised so init can stop them cleanly.
|
// Bring up the boot services, supervised so init can stop them cleanly.
|
||||||
// Best-effort and silent: each service announces its own readiness, and in
|
// Best-effort and silent: each service announces its own readiness, and in
|
||||||
// an isolation test with no initial-ramdisk the spawns simply no-op.
|
// an isolation test with no initial-ramdisk the spawns simply no-op.
|
||||||
for (boot_services, 0..) |service, i| {
|
for (boot_services, 0..) |service, i| {
|
||||||
if (runtime.system.spawnSupervised(service, &.{}, supervision_endpoint)) |id| child_ids[i] = id;
|
if (process.spawnSupervised(service, &.{}, supervision_endpoint)) |id| child_ids[i] = id;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Subscribe to power events (retry: the power service registers well after
|
// Subscribe to power events (retry: the power service registers well after
|
||||||
@@ -103,18 +107,18 @@ pub fn main() void {
|
|||||||
// wakes only for real work (signals, power events, children's exits), never for a
|
// wakes only for real work (signals, power events, children's exits), never for a
|
||||||
// periodic beat. `build_options.serial` is comptime, so the heartbeat — its timer
|
// periodic beat. `build_options.serial` is comptime, so the heartbeat — its timer
|
||||||
// and the handler below — folds away entirely when serial is off.
|
// and the handler below — folds away entirely when serial is off.
|
||||||
if (build_options.serial) _ = runtime.system.timerOnce(supervision_endpoint, 1000);
|
if (build_options.serial) _ = time.timerOnce(supervision_endpoint, 1000);
|
||||||
|
|
||||||
var receive: [power_protocol.message_maximum]u8 = undefined;
|
var receive: [power_protocol.message_maximum]u8 = undefined;
|
||||||
while (true) {
|
while (true) {
|
||||||
const got = runtime.ipc.replyWait(supervision_endpoint, &.{}, &receive, null);
|
const got = ipc.replyWait(supervision_endpoint, &.{}, &receive, null);
|
||||||
if (runtime.process.signalsFrom(got.badge)) |signals| {
|
if (process.signalsFrom(got.badge)) |signals| {
|
||||||
if (signals.has(.terminate)) shutDown();
|
if (signals.has(.terminate)) shutDown();
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (build_options.serial and got.isTimer()) {
|
if (build_options.serial and got.isTimer()) {
|
||||||
_ = runtime.system.write("/system/services/init: heartbeat\n");
|
_ = logging.write("/system/services/init: heartbeat\n");
|
||||||
_ = runtime.system.timerOnce(supervision_endpoint, 1000);
|
_ = time.timerOnce(supervision_endpoint, 1000);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (got.isMessage() and got.len >= 2 and receive[0] == @intFromEnum(power_protocol.Operation.event)) {
|
if (got.isMessage() and got.len >= 2 and receive[0] == @intFromEnum(power_protocol.Operation.event)) {
|
||||||
@@ -141,7 +145,7 @@ fn restartChild(id: u32) void {
|
|||||||
if (child_ids[i] != id) continue;
|
if (child_ids[i] != id) continue;
|
||||||
child_ids[i] = 0;
|
child_ids[i] = 0;
|
||||||
// An unknown reason (the record aged out) is treated as a crash worth restarting.
|
// An unknown reason (the record aged out) is treated as a crash worth restarting.
|
||||||
const reason = runtime.process.exitReason(id) orelse .fault;
|
const reason = process.exitReason(id) orelse .fault;
|
||||||
if (reason == .exited) {
|
if (reason == .exited) {
|
||||||
std.log.info("{s} exited cleanly; not restarting", .{service});
|
std.log.info("{s} exited cleanly; not restarting", .{service});
|
||||||
return;
|
return;
|
||||||
@@ -152,7 +156,7 @@ fn restartChild(id: u32) void {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
std.log.info("{s} died ({s}); restarting ({d}/{d})", .{ service, @tagName(reason), restart_counts[i], maximum_restarts });
|
std.log.info("{s} died ({s}); restarting ({d}/{d})", .{ service, @tagName(reason), restart_counts[i], maximum_restarts });
|
||||||
if (runtime.system.spawnSupervised(service, &.{}, supervision_endpoint)) |new_id| child_ids[i] = new_id;
|
if (process.spawnSupervised(service, &.{}, supervision_endpoint)) |new_id| child_ids[i] = new_id;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// An untracked child (e.g. the log-flush one-shot): nothing to restart.
|
// An untracked child (e.g. the log-flush one-shot): nothing to restart.
|
||||||
@@ -161,11 +165,11 @@ fn restartChild(id: u32) void {
|
|||||||
/// Look up the power service and subscribe our endpoint (handed over as the
|
/// Look up the power service and subscribe our endpoint (handed over as the
|
||||||
/// call's capability) so events arrive as buffered messages here.
|
/// call's capability) so events arrive as buffered messages here.
|
||||||
fn subscribePower() void {
|
fn subscribePower() void {
|
||||||
var handle: ?runtime.ipc.Handle = null;
|
var handle: ?ipc.Handle = null;
|
||||||
var tries: u32 = 0;
|
var tries: u32 = 0;
|
||||||
while (handle == null and tries < 200) : (tries += 1) {
|
while (handle == null and tries < 200) : (tries += 1) {
|
||||||
handle = runtime.ipc.lookup(.power);
|
handle = ipc.lookup(.power);
|
||||||
if (handle == null) runtime.system.sleep(20);
|
if (handle == null) time.sleepMillis(20);
|
||||||
}
|
}
|
||||||
// A missing power service is not fatal — init proceeds to its heartbeat and
|
// A missing power service is not fatal — init proceeds to its heartbeat and
|
||||||
// a `terminate` signal still drives shutdown. Silent so the no-ramdisk init
|
// a `terminate` signal still drives shutdown. Silent so the no-ramdisk init
|
||||||
@@ -173,7 +177,7 @@ fn subscribePower() void {
|
|||||||
const h = handle orelse return;
|
const h = handle orelse return;
|
||||||
const request = power_protocol.Subscribe{};
|
const request = power_protocol.Subscribe{};
|
||||||
var reply: [power_protocol.message_maximum]u8 = undefined;
|
var reply: [power_protocol.message_maximum]u8 = undefined;
|
||||||
_ = runtime.ipc.callCap(h, std.mem.asBytes(&request), &reply, supervision_endpoint) catch {};
|
_ = ipc.callCap(h, std.mem.asBytes(&request), &reply, supervision_endpoint) catch {};
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The stop sequence: persist the log while storage is still up, then terminate
|
/// The stop sequence: persist the log while storage is still up, then terminate
|
||||||
@@ -182,20 +186,20 @@ fn subscribePower() void {
|
|||||||
/// power service to enter S5.
|
/// power service to enter S5.
|
||||||
fn shutDown() void {
|
fn shutDown() void {
|
||||||
shutting_down = true; // the stop loop below kills children — those deaths aren't crashes
|
shutting_down = true; // the stop loop below kills children — those deaths aren't crashes
|
||||||
_ = runtime.system.write("/system/services/init: shutting down\n");
|
_ = logging.write("/system/services/init: shutting down\n");
|
||||||
// Log persistence is the logger service's job: it is the LAST boot service,
|
// Log persistence is the logger service's job: it is the LAST boot service,
|
||||||
// so the reverse-order stop below terminates it first and its final drain
|
// so the reverse-order stop below terminates it first and its final drain
|
||||||
// runs while the whole storage chain is still alive.
|
// runs while the whole storage chain is still alive.
|
||||||
var i = boot_services.len;
|
var i = boot_services.len;
|
||||||
while (i > 0) {
|
while (i > 0) {
|
||||||
i -= 1;
|
i -= 1;
|
||||||
if (child_ids[i] != 0) runtime.process.stop(child_ids[i], 2000, supervision_endpoint);
|
if (child_ids[i] != 0) process.stop(child_ids[i], 2000, supervision_endpoint);
|
||||||
}
|
}
|
||||||
if (runtime.ipc.lookup(.power)) |h| {
|
if (ipc.lookup(.power)) |h| {
|
||||||
const request = power_protocol.Shutdown{};
|
const request = power_protocol.Shutdown{};
|
||||||
var reply: [power_protocol.message_maximum]u8 = undefined;
|
var reply: [power_protocol.message_maximum]u8 = undefined;
|
||||||
_ = runtime.ipc.call(h, std.mem.asBytes(&request), &reply) catch {};
|
_ = ipc.call(h, std.mem.asBytes(&request), &reply) catch {};
|
||||||
}
|
}
|
||||||
// If S5 did not take, init has nothing left to do but idle.
|
// If S5 did not take, init has nothing left to do but idle.
|
||||||
while (true) runtime.system.sleep(1000);
|
while (true) time.sleepMillis(1000);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,13 +11,14 @@
|
|||||||
//! decoded hardware is a follow-up (see docs/input.md).
|
//! decoded hardware is a follow-up (see docs/input.md).
|
||||||
|
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const runtime = @import("runtime");
|
const input = @import("input");
|
||||||
const input = runtime.input;
|
const process = @import("process");
|
||||||
const system = runtime.system;
|
const time = @import("time");
|
||||||
|
const logging = @import("logging");
|
||||||
|
|
||||||
pub fn main(init: runtime.process.Init) void {
|
pub fn main(init: process.Init) void {
|
||||||
var source = input.connectSource() orelse {
|
var source = input.connectSource() orelse {
|
||||||
_ = system.write("input-source: input service unavailable\n");
|
_ = logging.write("input-source: input service unavailable\n");
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -26,7 +27,7 @@ pub fn main(init: runtime.process.Init) void {
|
|||||||
// classes to exercise the service's per-device routing (the `input` test).
|
// classes to exercise the service's per-device routing (the `input` test).
|
||||||
const mode = init.arguments.get(1) orelse "rotate";
|
const mode = init.arguments.get(1) orelse "rotate";
|
||||||
if (std.mem.eql(u8, mode, "mouse")) {
|
if (std.mem.eql(u8, mode, "mouse")) {
|
||||||
_ = system.write("input-source: publishing synthetic mouse motion\n");
|
_ = logging.write("input-source: publishing synthetic mouse motion\n");
|
||||||
while (true) {
|
while (true) {
|
||||||
_ = source.publishMouseEvent(.{
|
_ = source.publishMouseEvent(.{
|
||||||
.kind = @intFromEnum(input.MouseEventKind.motion),
|
.kind = @intFromEnum(input.MouseEventKind.motion),
|
||||||
@@ -37,11 +38,11 @@ pub fn main(init: runtime.process.Init) void {
|
|||||||
.scroll_y = 0,
|
.scroll_y = 0,
|
||||||
.buttons = 0,
|
.buttons = 0,
|
||||||
});
|
});
|
||||||
system.sleep(20); // ~50 events/sec: moves the cursor briskly
|
time.sleepMillis(20); // ~50 events/sec: moves the cursor briskly
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_ = system.write("input-source: publishing synthetic input events\n");
|
_ = logging.write("input-source: publishing synthetic input events\n");
|
||||||
var step: usize = 0;
|
var step: usize = 0;
|
||||||
while (true) : (step +%= 1) {
|
while (true) : (step +%= 1) {
|
||||||
// Rotate across the device classes so every publish path (and the service's
|
// Rotate across the device classes so every publish path (and the service's
|
||||||
@@ -51,6 +52,6 @@ pub fn main(init: runtime.process.Init) void {
|
|||||||
1 => _ = source.publishMouseEvent(input.syntheticMouseEvent(step)),
|
1 => _ = source.publishMouseEvent(input.syntheticMouseEvent(step)),
|
||||||
else => _ = source.publishJoystickEvent(input.syntheticJoystickEvent(step)),
|
else => _ = source.publishJoystickEvent(input.syntheticJoystickEvent(step)),
|
||||||
}
|
}
|
||||||
system.sleep(200);
|
time.sleepMillis(200);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,21 +7,20 @@
|
|||||||
//! device classes to one subscription — source -> service -> subscriber, per device.
|
//! device classes to one subscription — source -> service -> subscriber, per device.
|
||||||
|
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const runtime = @import("runtime");
|
const input = @import("input");
|
||||||
const input = runtime.input;
|
const logging = @import("logging");
|
||||||
const system = runtime.system;
|
|
||||||
|
|
||||||
fn writeLine(comptime fmt: []const u8, arguments: anytype) void {
|
fn writeLine(comptime fmt: []const u8, arguments: anytype) void {
|
||||||
var line: [128]u8 = undefined;
|
var line: [128]u8 = undefined;
|
||||||
_ = system.write(std.fmt.bufPrint(&line, fmt, arguments) catch return);
|
_ = logging.write(std.fmt.bufPrint(&line, fmt, arguments) catch return);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn main() void {
|
pub fn main() void {
|
||||||
var listener = input.subscribeAll() orelse {
|
var listener = input.subscribeAll() orelse {
|
||||||
_ = system.write("input-test: could not subscribe\n");
|
_ = logging.write("input-test: could not subscribe\n");
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
_ = system.write("input-test: subscribed\n");
|
_ = logging.write("input-test: subscribed\n");
|
||||||
|
|
||||||
var seen_keyboard = false;
|
var seen_keyboard = false;
|
||||||
var seen_mouse = false;
|
var seen_mouse = false;
|
||||||
@@ -45,7 +44,7 @@ pub fn main() void {
|
|||||||
// The success marker: only once every class has been routed here does this appear,
|
// The success marker: only once every class has been routed here does this appear,
|
||||||
// and then it heartbeats. Seeing "input-test: ok" proves per-device fan-out works.
|
// and then it heartbeats. Seeing "input-test: ok" proves per-device fan-out works.
|
||||||
if (seen_keyboard and seen_mouse and seen_joystick) {
|
if (seen_keyboard and seen_mouse and seen_joystick) {
|
||||||
_ = system.write("input-test: ok all classes received (keyboard, mouse, joystick)\n");
|
_ = logging.write("input-test: ok all classes received (keyboard, mouse, joystick)\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,10 +20,12 @@
|
|||||||
//! handle and `ipc.send`s each event to it.
|
//! handle and `ipc.send`s each event to it.
|
||||||
|
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const runtime = @import("runtime");
|
const ipc = @import("ipc");
|
||||||
|
const process = @import("process");
|
||||||
|
const service = @import("service");
|
||||||
|
const input = @import("input");
|
||||||
|
const logging = @import("logging");
|
||||||
const input_protocol = @import("input-protocol");
|
const input_protocol = @import("input-protocol");
|
||||||
const ipc = runtime.ipc;
|
|
||||||
const system = runtime.system;
|
|
||||||
|
|
||||||
/// One registered subscriber: the endpoint we push events to (a capability it handed us at
|
/// One registered subscriber: the endpoint we push events to (a capability it handed us at
|
||||||
/// subscribe time) and the task id that owns it (the subscribe call's badge), so a slot
|
/// subscribe time) and the task id that owns it (the subscribe call's badge), so a slot
|
||||||
@@ -44,8 +46,8 @@ var subscribers = [_]Subscriber{.{}} ** 8;
|
|||||||
/// `send` to a dead subscriber's orphaned endpoint is harmless (it just fills a queue no
|
/// `send` to a dead subscriber's orphaned endpoint is harmless (it just fills a queue no
|
||||||
/// one drains), so this is housekeeping, not correctness.
|
/// one drains), so this is housekeeping, not correctness.
|
||||||
fn pruneDeadSubscribers() void {
|
fn pruneDeadSubscribers() void {
|
||||||
var table: [32]system.ProcessDescriptor = undefined;
|
var table: [32]process.ProcessDescriptor = undefined;
|
||||||
const total = system.processes(&table);
|
const total = process.processes(&table);
|
||||||
const count = @min(total, table.len);
|
const count = @min(total, table.len);
|
||||||
for (&subscribers) |*sub| {
|
for (&subscribers) |*sub| {
|
||||||
if (!sub.used) continue;
|
if (!sub.used) continue;
|
||||||
@@ -115,14 +117,14 @@ fn handle(message: []const u8, got: ipc.Received, out: []u8) usize {
|
|||||||
|
|
||||||
pub fn main() void {
|
pub fn main() void {
|
||||||
const endpoint = ipc.createIpcEndpoint() orelse {
|
const endpoint = ipc.createIpcEndpoint() orelse {
|
||||||
_ = system.write("/system/services/input: no endpoint\n");
|
_ = logging.write("/system/services/input: no endpoint\n");
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
if (!ipc.register(.input, endpoint)) {
|
if (!ipc.register(.input, endpoint)) {
|
||||||
_ = system.write("/system/services/input: register failed\n");
|
_ = logging.write("/system/services/input: register failed\n");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
_ = system.write("/system/services/input: ready\n");
|
_ = logging.write("/system/services/input: ready\n");
|
||||||
|
|
||||||
var reply_buffer: [input_protocol.reply_size]u8 = undefined;
|
var reply_buffer: [input_protocol.reply_size]u8 = undefined;
|
||||||
var reply_len: usize = 0;
|
var reply_len: usize = 0;
|
||||||
|
|||||||
@@ -30,10 +30,12 @@
|
|||||||
//! stops the logger FIRST — reverse boot order — while fat is still up).
|
//! stops the logger FIRST — reverse boot order — while fat is still up).
|
||||||
|
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const runtime = @import("runtime");
|
const fs = @import("file-system");
|
||||||
|
const ipc = @import("ipc");
|
||||||
|
const service = @import("service");
|
||||||
|
const time = @import("time");
|
||||||
|
const logging = @import("logging");
|
||||||
|
|
||||||
const system = runtime.system;
|
|
||||||
const fs = runtime.fs;
|
|
||||||
|
|
||||||
/// Where log trees live: the FHS path. The kernel VFS routes /var to whatever
|
/// Where log trees live: the FHS path. The kernel VFS routes /var to whatever
|
||||||
/// volume the fat server mounted there (today: the /var subtree of the USB
|
/// volume the fat server mounted there (today: the /var subtree of the USB
|
||||||
@@ -52,13 +54,13 @@ const maximum_files = 24;
|
|||||||
|
|
||||||
const CachedFile = struct {
|
const CachedFile = struct {
|
||||||
used: bool = false,
|
used: bool = false,
|
||||||
name: [system.maximum_process_name]u8 = undefined,
|
name: [logging.maximum_process_name]u8 = undefined,
|
||||||
name_len: usize = 0,
|
name_len: usize = 0,
|
||||||
file: fs.File = undefined,
|
file: fs.File = undefined,
|
||||||
};
|
};
|
||||||
|
|
||||||
var files: [maximum_files]CachedFile = @splat(.{});
|
var files: [maximum_files]CachedFile = @splat(.{});
|
||||||
var endpoint: runtime.ipc.Handle = 0;
|
var endpoint: ipc.Handle = 0;
|
||||||
|
|
||||||
/// The drain cursor into the ring's byte stream, and loss accounting.
|
/// The drain cursor into the ring's byte stream, and loss accounting.
|
||||||
var cursor: u64 = 0;
|
var cursor: u64 = 0;
|
||||||
@@ -77,7 +79,7 @@ var announced = false;
|
|||||||
var ticks_since_record: u32 = 0;
|
var ticks_since_record: u32 = 0;
|
||||||
|
|
||||||
pub fn main() void {
|
pub fn main() void {
|
||||||
runtime.service.run(64, .{
|
service.run(64, .{
|
||||||
.init = initialise,
|
.init = initialise,
|
||||||
.on_message = onMessage,
|
.on_message = onMessage,
|
||||||
.on_notification = onNotification,
|
.on_notification = onNotification,
|
||||||
@@ -85,19 +87,19 @@ pub fn main() void {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
fn initialise(harness_endpoint: runtime.ipc.Handle) bool {
|
fn initialise(harness_endpoint: ipc.Handle) bool {
|
||||||
endpoint = harness_endpoint;
|
endpoint = harness_endpoint;
|
||||||
const status = system.klogStatus() orelse return false;
|
const status = logging.klogStatus() orelse return false;
|
||||||
cursor = status.tail;
|
cursor = status.tail;
|
||||||
// Sequence expectations start at the tail record's sequence — discovered on
|
// Sequence expectations start at the tail record's sequence — discovered on
|
||||||
// the first drain; 0 is right for a fresh boot either way.
|
// the first drain; 0 is right for a fresh boot either way.
|
||||||
formatBootDirectory(status.boot_unix_seconds);
|
formatBootDirectory(status.boot_unix_seconds);
|
||||||
_ = system.timerOnce(endpoint, tick_ms);
|
_ = time.timerOnce(endpoint, tick_ms);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The logger serves no protocol; the ping is answered by the harness.
|
/// The logger serves no protocol; the ping is answered by the harness.
|
||||||
fn onMessage(message: []const u8, reply: []u8, sender: u32, capability: ?runtime.ipc.Handle) usize {
|
fn onMessage(message: []const u8, reply: []u8, sender: u32, capability: ?ipc.Handle) usize {
|
||||||
_ = message;
|
_ = message;
|
||||||
_ = reply;
|
_ = reply;
|
||||||
_ = sender;
|
_ = sender;
|
||||||
@@ -106,9 +108,9 @@ fn onMessage(message: []const u8, reply: []u8, sender: u32, capability: ?runtime
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn onNotification(badge: u64) void {
|
fn onNotification(badge: u64) void {
|
||||||
if (badge & runtime.ipc.notify_timer_bit == 0) return;
|
if (badge & ipc.notify_timer_bit == 0) return;
|
||||||
tick();
|
tick();
|
||||||
_ = system.timerOnce(endpoint, tick_ms);
|
_ = time.timerOnce(endpoint, tick_ms);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn onTerminate() void {
|
fn onTerminate() void {
|
||||||
@@ -116,12 +118,12 @@ fn onTerminate() void {
|
|||||||
// final drain, so the drain carries it into logger.log — a directory whose
|
// final drain, so the drain carries it into logger.log — a directory whose
|
||||||
// logger.log ends with this marker is complete through shutdown; one that
|
// logger.log ends with this marker is complete through shutdown; one that
|
||||||
// doesn't was cut early and may be missing tails.
|
// doesn't was cut early and may be missing tails.
|
||||||
_ = system.write("logger: shutting down; final flush\n");
|
_ = logging.write("logger: shutting down; final flush\n");
|
||||||
drain();
|
drain();
|
||||||
closeAll();
|
closeAll();
|
||||||
// Serial-only epilogue (after the drain, so it reaches no file — by design).
|
// Serial-only epilogue (after the drain, so it reaches no file — by design).
|
||||||
var line: [96]u8 = undefined;
|
var line: [96]u8 = undefined;
|
||||||
_ = system.write(std.fmt.bufPrint(&line, "logger: flushed through sequence {d}\n", .{next_expected_sequence}) catch return);
|
_ = logging.write(std.fmt.bufPrint(&line, "logger: flushed through sequence {d}\n", .{next_expected_sequence}) catch return);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn tick() void {
|
fn tick() void {
|
||||||
@@ -134,7 +136,7 @@ fn tick() void {
|
|||||||
if (!announced) {
|
if (!announced) {
|
||||||
announced = true; // once — a periodic line would feed the stream we drain
|
announced = true; // once — a periodic line would feed the stream we drain
|
||||||
var line: [128]u8 = undefined;
|
var line: [128]u8 = undefined;
|
||||||
_ = system.write(std.fmt.bufPrint(&line, "logger: logging to {s}\n", .{boot_directory[0..boot_directory_len]}) catch "");
|
_ = logging.write(std.fmt.bufPrint(&line, "logger: logging to {s}\n", .{boot_directory[0..boot_directory_len]}) catch "");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
drain();
|
drain();
|
||||||
@@ -148,10 +150,10 @@ fn drain() void {
|
|||||||
var chunk: [4096]u8 = undefined;
|
var chunk: [4096]u8 = undefined;
|
||||||
while (true) {
|
while (true) {
|
||||||
@memcpy(chunk[0..carry_len], carry[0..carry_len]);
|
@memcpy(chunk[0..carry_len], carry[0..carry_len]);
|
||||||
const got = system.klogRead(cursor, chunk[carry_len..]) orelse {
|
const got = logging.klogRead(cursor, chunk[carry_len..]) orelse {
|
||||||
// Cursor overwritten: re-sync to the ring tail; the sequence gap is
|
// Cursor overwritten: re-sync to the ring tail; the sequence gap is
|
||||||
// reported by the next record's header.
|
// reported by the next record's header.
|
||||||
const status = system.klogStatus() orelse return;
|
const status = logging.klogStatus() orelse return;
|
||||||
cursor = status.tail;
|
cursor = status.tail;
|
||||||
carry_len = 0;
|
carry_len = 0;
|
||||||
continue;
|
continue;
|
||||||
@@ -164,14 +166,14 @@ fn drain() void {
|
|||||||
|
|
||||||
/// Parse whole records out of `bytes`; keep any trailing partial in `carry`.
|
/// Parse whole records out of `bytes`; keep any trailing partial in `carry`.
|
||||||
fn consume(bytes: []u8) void {
|
fn consume(bytes: []u8) void {
|
||||||
const header_size = system.klog_record_header_size;
|
const header_size = logging.klog_record_header_size;
|
||||||
var offset: usize = 0;
|
var offset: usize = 0;
|
||||||
while (bytes.len - offset >= header_size) {
|
while (bytes.len - offset >= header_size) {
|
||||||
const header = std.mem.bytesToValue(system.KlogRecordHeader, bytes[offset..][0..32]);
|
const header = std.mem.bytesToValue(logging.KlogRecordHeader, bytes[offset..][0..32]);
|
||||||
if (header.magic != system.klog_record_magic) {
|
if (header.magic != logging.klog_record_magic) {
|
||||||
// Corrupt frame — should not happen; drop the carry and re-sync.
|
// Corrupt frame — should not happen; drop the carry and re-sync.
|
||||||
carry_len = 0;
|
carry_len = 0;
|
||||||
const status = system.klogStatus() orelse return;
|
const status = logging.klogStatus() orelse return;
|
||||||
cursor = status.head;
|
cursor = status.head;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -191,7 +193,7 @@ fn consume(bytes: []u8) void {
|
|||||||
carry_len = rest;
|
carry_len = rest;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn deliver(header: system.KlogRecordHeader, name: []const u8, message: []const u8) void {
|
fn deliver(header: logging.KlogRecordHeader, name: []const u8, message: []const u8) void {
|
||||||
ticks_since_record = 0;
|
ticks_since_record = 0;
|
||||||
const file = fileFor(if (header.pid == 0 or name.len == 0) "kernel" else name) orelse return;
|
const file = fileFor(if (header.pid == 0 or name.len == 0) "kernel" else name) orelse return;
|
||||||
|
|
||||||
@@ -218,7 +220,7 @@ fn deliver(header: system.KlogRecordHeader, name: []const u8, message: []const u
|
|||||||
_ = file.writeAll(prefix);
|
_ = file.writeAll(prefix);
|
||||||
} else |_| {}
|
} else |_| {}
|
||||||
_ = file.writeAll(message);
|
_ = file.writeAll(message);
|
||||||
if (header.flags & system.klog_flag_truncated != 0) _ = file.writeAll("~");
|
if (header.flags & logging.klog_flag_truncated != 0) _ = file.writeAll("~");
|
||||||
_ = file.writeAll("\n");
|
_ = file.writeAll("\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -238,7 +240,7 @@ fn fileFor(name: []const u8) ?*fs.File {
|
|||||||
}
|
}
|
||||||
const cached = slot orelse evictOne() orelse return null;
|
const cached = slot orelse evictOne() orelse return null;
|
||||||
|
|
||||||
var path: [base.len + 1 + 19 + 1 + system.maximum_process_name + 4]u8 = undefined;
|
var path: [base.len + 1 + 19 + 1 + logging.maximum_process_name + 4]u8 = undefined;
|
||||||
const relative = if (name.len != 0 and name[0] == '/') name[1..] else name;
|
const relative = if (name.len != 0 and name[0] == '/') name[1..] else name;
|
||||||
const full = std.fmt.bufPrint(&path, "{s}/{s}.log", .{ boot_directory[0..boot_directory_len], relative }) catch return null;
|
const full = std.fmt.bufPrint(&path, "{s}/{s}.log", .{ boot_directory[0..boot_directory_len], relative }) catch return null;
|
||||||
|
|
||||||
@@ -278,8 +280,8 @@ fn closeAll() void {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn recordLength(header: system.KlogRecordHeader) usize {
|
fn recordLength(header: logging.KlogRecordHeader) usize {
|
||||||
return std.mem.alignForward(usize, system.klog_record_header_size + header.name_len + header.message_len, system.klog_record_alignment);
|
return std.mem.alignForward(usize, logging.klog_record_header_size + header.name_len + header.message_len, logging.klog_record_alignment);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Format the per-boot directory "<base>/YYYY-MM-DDTHHMMSSZ" from the boot
|
/// Format the per-boot directory "<base>/YYYY-MM-DDTHHMMSSZ" from the boot
|
||||||
|
|||||||
@@ -17,20 +17,24 @@
|
|||||||
//! binary bare), it exits silently so it cannot derange other tests' output.
|
//! binary bare), it exits silently so it cannot derange other tests' output.
|
||||||
|
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const runtime = @import("runtime");
|
const ipc = @import("ipc");
|
||||||
|
const process = @import("process");
|
||||||
|
const service = @import("service");
|
||||||
|
const time = @import("time");
|
||||||
|
const logging = @import("logging");
|
||||||
|
|
||||||
fn fail(step: []const u8) noreturn {
|
fn fail(step: []const u8) noreturn {
|
||||||
_ = runtime.system.write("process-test: FAIL ");
|
_ = logging.write("process-test: FAIL ");
|
||||||
_ = runtime.system.write(step);
|
_ = logging.write(step);
|
||||||
_ = runtime.system.write("\n");
|
_ = logging.write("\n");
|
||||||
runtime.system.exit(1);
|
process.exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Whether process `id` appears in a fresh `process_enumerate` snapshot, named
|
/// Whether process `id` appears in a fresh `process_enumerate` snapshot, named
|
||||||
/// `name` (an id present under the wrong name is a table mix-up, not a pass).
|
/// `name` (an id present under the wrong name is a table mix-up, not a pass).
|
||||||
fn listed(id: u32, name: []const u8) bool {
|
fn listed(id: u32, name: []const u8) bool {
|
||||||
var table: [32]runtime.system.ProcessDescriptor = undefined;
|
var table: [32]process.ProcessDescriptor = undefined;
|
||||||
const total = runtime.system.processes(&table);
|
const total = process.processes(&table);
|
||||||
for (table[0..@min(total, table.len)]) |descriptor| {
|
for (table[0..@min(total, table.len)]) |descriptor| {
|
||||||
if (descriptor.id != id) continue;
|
if (descriptor.id != id) continue;
|
||||||
return std.mem.eql(u8, descriptor.name[0..descriptor.name_length], name);
|
return std.mem.eql(u8, descriptor.name[0..descriptor.name_length], name);
|
||||||
@@ -41,9 +45,9 @@ fn listed(id: u32, name: []const u8) bool {
|
|||||||
/// Block on the exit endpoint until a child-exit notification arrives; returns
|
/// Block on the exit endpoint until a child-exit notification arrives; returns
|
||||||
/// the ended child's id. A wrong wake-up (there should be none — nothing else
|
/// the ended child's id. A wrong wake-up (there should be none — nothing else
|
||||||
/// knows this endpoint) fails the test rather than looping forever.
|
/// knows this endpoint) fails the test rather than looping forever.
|
||||||
fn awaitChildExit(endpoint: runtime.ipc.Handle) u32 {
|
fn awaitChildExit(endpoint: ipc.Handle) u32 {
|
||||||
var scratch: [8]u8 = undefined;
|
var scratch: [8]u8 = undefined;
|
||||||
const received = runtime.ipc.replyWait(endpoint, scratch[0..0], &scratch, null);
|
const received = ipc.replyWait(endpoint, scratch[0..0], &scratch, null);
|
||||||
if (!received.isChildExit()) fail("expected a child-exit notification");
|
if (!received.isChildExit()) fail("expected a child-exit notification");
|
||||||
return received.childProcessId();
|
return received.childProcessId();
|
||||||
}
|
}
|
||||||
@@ -51,7 +55,7 @@ fn awaitChildExit(endpoint: runtime.ipc.Handle) u32 {
|
|||||||
/// The harness-run child of the signals test: echoes requests, logs the two
|
/// The harness-run child of the signals test: echoes requests, logs the two
|
||||||
/// signals it handles. Terminate makes run() return, and returning from main is
|
/// signals it handles. Terminate makes run() return, and returning from main is
|
||||||
/// the clean exit the parent reads as ExitReason.exited.
|
/// the clean exit the parent reads as ExitReason.exited.
|
||||||
fn echo(message: []const u8, reply: []u8, sender: u32, capability: ?runtime.ipc.Handle) usize {
|
fn echo(message: []const u8, reply: []u8, sender: u32, capability: ?ipc.Handle) usize {
|
||||||
_ = sender;
|
_ = sender;
|
||||||
_ = capability;
|
_ = capability;
|
||||||
const n = @min(message.len, reply.len);
|
const n = @min(message.len, reply.len);
|
||||||
@@ -60,69 +64,69 @@ fn echo(message: []const u8, reply: []u8, sender: u32, capability: ?runtime.ipc.
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn onReload() void {
|
fn onReload() void {
|
||||||
_ = runtime.system.write("process-test: reloaded\n");
|
_ = logging.write("process-test: reloaded\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
fn onTerminate() void {
|
fn onTerminate() void {
|
||||||
_ = runtime.system.write("process-test: terminating\n");
|
_ = logging.write("process-test: terminating\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The parent of the signals test: drives ping, echo, reload, the one-shot
|
/// The parent of the signals test: drives ping, echo, reload, the one-shot
|
||||||
/// timer, and both endings of the stop sequence (polite -> exited; deaf ->
|
/// timer, and both endings of the stop sequence (polite -> exited; deaf ->
|
||||||
/// killed at the deadline). Prints "process-test: signals ok" as the marker.
|
/// killed at the deadline). Prints "process-test: signals ok" as the marker.
|
||||||
fn signalRun() void {
|
fn signalRun() void {
|
||||||
const endpoint = runtime.ipc.createIpcEndpoint() orelse fail("create exit endpoint");
|
const endpoint = ipc.createIpcEndpoint() orelse fail("create exit endpoint");
|
||||||
const child = runtime.system.spawnSupervised("process-test", &.{"service"}, endpoint) orelse fail("spawn service child");
|
const child = process.spawnSupervised("process-test", &.{"service"}, endpoint) orelse fail("spawn service child");
|
||||||
|
|
||||||
// Reach the child's endpoint through the registry (retry: it may not be up).
|
// Reach the child's endpoint through the registry (retry: it may not be up).
|
||||||
var service_handle: ?runtime.ipc.Handle = null;
|
var service_handle: ?ipc.Handle = null;
|
||||||
var tries: u32 = 0;
|
var tries: u32 = 0;
|
||||||
while (service_handle == null and tries < 200) : (tries += 1) {
|
while (service_handle == null and tries < 200) : (tries += 1) {
|
||||||
service_handle = runtime.ipc.lookup(.input);
|
service_handle = ipc.lookup(.input);
|
||||||
if (service_handle == null) runtime.system.sleep(20);
|
if (service_handle == null) time.sleepMillis(20);
|
||||||
}
|
}
|
||||||
const h = service_handle orelse fail("service child never registered");
|
const h = service_handle orelse fail("service child never registered");
|
||||||
|
|
||||||
// The universal ping: a zero-length call answered zero-length by the harness.
|
// The universal ping: a zero-length call answered zero-length by the harness.
|
||||||
var reply: [16]u8 = undefined;
|
var reply: [16]u8 = undefined;
|
||||||
const pong = runtime.ipc.call(h, &.{}, &reply) catch fail("ping call failed");
|
const pong = ipc.call(h, &.{}, &reply) catch fail("ping call failed");
|
||||||
if (pong != 0) fail("ping reply not empty");
|
if (pong != 0) fail("ping reply not empty");
|
||||||
|
|
||||||
// An ordinary request still reaches on_message.
|
// An ordinary request still reaches on_message.
|
||||||
const n = runtime.ipc.call(h, "echo!", &reply) catch fail("echo call failed");
|
const n = ipc.call(h, "echo!", &reply) catch fail("echo call failed");
|
||||||
if (n != 5 or !std.mem.eql(u8, reply[0..5], "echo!")) fail("echo mismatch");
|
if (n != 5 or !std.mem.eql(u8, reply[0..5], "echo!")) fail("echo mismatch");
|
||||||
|
|
||||||
// reload: a statement — the child logs it; the kernel test reads the serial.
|
// reload: a statement — the child logs it; the kernel test reads the serial.
|
||||||
if (!runtime.process.sendSignal(child, .reload)) fail("send reload");
|
if (!process.sendSignal(child, .reload)) fail("send reload");
|
||||||
runtime.system.sleep(200);
|
time.sleepMillis(200);
|
||||||
|
|
||||||
// The one-shot timer: armed on our endpoint, lands as isTimer.
|
// The one-shot timer: armed on our endpoint, lands as isTimer.
|
||||||
if (!runtime.system.timerOnce(endpoint, 100)) fail("arm timer");
|
if (!time.timerOnce(endpoint, 100)) fail("arm timer");
|
||||||
var scratch: [8]u8 = undefined;
|
var scratch: [8]u8 = undefined;
|
||||||
const landing = runtime.ipc.replyWait(endpoint, scratch[0..0], &scratch, null);
|
const landing = ipc.replyWait(endpoint, scratch[0..0], &scratch, null);
|
||||||
if (!landing.isTimer()) fail("expected the timer landing");
|
if (!landing.isTimer()) fail("expected the timer landing");
|
||||||
|
|
||||||
// The stop sequence, polite path: terminate, clean exit inside the deadline.
|
// The stop sequence, polite path: terminate, clean exit inside the deadline.
|
||||||
runtime.process.stop(child, 2000, endpoint);
|
process.stop(child, 2000, endpoint);
|
||||||
if ((runtime.process.exitReason(child) orelse .killed) != .exited) fail("service child reason not exited");
|
if ((process.exitReason(child) orelse .killed) != .exited) fail("service child reason not exited");
|
||||||
|
|
||||||
// The deaf child: binds nothing, hears nothing — the deadline kills it.
|
// The deaf child: binds nothing, hears nothing — the deadline kills it.
|
||||||
const deaf = runtime.system.spawnSupervised("process-test", &.{"sleeper"}, endpoint) orelse fail("spawn deaf child");
|
const deaf = process.spawnSupervised("process-test", &.{"sleeper"}, endpoint) orelse fail("spawn deaf child");
|
||||||
runtime.system.sleep(50); // let it reach its sleep
|
time.sleepMillis(50); // let it reach its sleep
|
||||||
runtime.process.stop(deaf, 300, endpoint);
|
process.stop(deaf, 300, endpoint);
|
||||||
if ((runtime.process.exitReason(deaf) orelse .exited) != .killed) fail("deaf child reason not killed");
|
if ((process.exitReason(deaf) orelse .exited) != .killed) fail("deaf child reason not killed");
|
||||||
|
|
||||||
_ = runtime.system.write("process-test: signals ok\n");
|
_ = logging.write("process-test: signals ok\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn main(init: runtime.process.Init) void {
|
pub fn main(init: process.Init) void {
|
||||||
const role = init.arguments.get(1) orelse return; // spawned bare (ramdisk sweep): stay silent
|
const role = init.arguments.get(1) orelse return; // spawned bare (ramdisk sweep): stay silent
|
||||||
if (std.mem.eql(u8, role, "sleeper")) {
|
if (std.mem.eql(u8, role, "sleeper")) {
|
||||||
while (true) runtime.system.sleep(500);
|
while (true) time.sleepMillis(500);
|
||||||
}
|
}
|
||||||
if (std.mem.eql(u8, role, "service")) {
|
if (std.mem.eql(u8, role, "service")) {
|
||||||
// Borrowed well-known id: the input service is not part of this scenario.
|
// Borrowed well-known id: the input service is not part of this scenario.
|
||||||
runtime.service.run(64, .{
|
service.run(64, .{
|
||||||
.service = .input,
|
.service = .input,
|
||||||
.on_message = echo,
|
.on_message = echo,
|
||||||
.on_reload = onReload,
|
.on_reload = onReload,
|
||||||
@@ -141,30 +145,30 @@ pub fn main(init: runtime.process.Init) void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// The supervisor ("run").
|
// The supervisor ("run").
|
||||||
const endpoint = runtime.ipc.createIpcEndpoint() orelse fail("create exit endpoint");
|
const endpoint = ipc.createIpcEndpoint() orelse fail("create exit endpoint");
|
||||||
|
|
||||||
const sleeper = runtime.system.spawnSupervised("process-test", &.{"sleeper"}, endpoint) orelse fail("spawn sleeper");
|
const sleeper = process.spawnSupervised("process-test", &.{"sleeper"}, endpoint) orelse fail("spawn sleeper");
|
||||||
const spinner = runtime.system.spawnSupervised("process-test", &.{"spinner"}, endpoint) orelse fail("spawn spinner");
|
const spinner = process.spawnSupervised("process-test", &.{"spinner"}, endpoint) orelse fail("spawn spinner");
|
||||||
|
|
||||||
runtime.system.sleep(100); // let the sleeper block and the spinner get a core
|
time.sleepMillis(100); // let the sleeper block and the spinner get a core
|
||||||
if (!listed(sleeper, "/system/tests/process-test")) fail("sleeper not in process_enumerate");
|
if (!listed(sleeper, "/system/tests/process-test")) fail("sleeper not in process_enumerate");
|
||||||
if (!listed(spinner, "/system/tests/process-test")) fail("spinner not in process_enumerate");
|
if (!listed(spinner, "/system/tests/process-test")) fail("spinner not in process_enumerate");
|
||||||
|
|
||||||
// Kills that must be refused: a kernel task (id 0), and an id that was never
|
// Kills that must be refused: a kernel task (id 0), and an id that was never
|
||||||
// issued — both -ESRCH. (-EPERM needs a second supervisor; the kernel-level
|
// issued — both -ESRCH. (-EPERM needs a second supervisor; the kernel-level
|
||||||
// `process-kill` test covers it.)
|
// `process-kill` test covers it.)
|
||||||
if (runtime.system.kill(0)) fail("killing a kernel task was allowed");
|
if (process.kill(0)) fail("killing a kernel task was allowed");
|
||||||
if (runtime.system.kill(0xFFFF_FFF0)) fail("killing an unknown id was allowed");
|
if (process.kill(0xFFFF_FFF0)) fail("killing an unknown id was allowed");
|
||||||
|
|
||||||
// The blocked child: usually reaped on the spot (it sits in `sleep`). The
|
// The blocked child: usually reaped on the spot (it sits in `sleep`). The
|
||||||
// notification is the fence — after it, the child is certainly gone, so the
|
// notification is the fence — after it, the child is certainly gone, so the
|
||||||
// second kill must miss (its id is never reused).
|
// second kill must miss (its id is never reused).
|
||||||
if (!runtime.system.kill(sleeper)) fail("kill sleeper");
|
if (!process.kill(sleeper)) fail("kill sleeper");
|
||||||
if (awaitChildExit(endpoint) != sleeper) fail("sleeper exit notification");
|
if (awaitChildExit(endpoint) != sleeper) fail("sleeper exit notification");
|
||||||
if (runtime.system.kill(sleeper)) fail("double kill was allowed");
|
if (process.kill(sleeper)) fail("double kill was allowed");
|
||||||
|
|
||||||
// The running child: the deferred path — condemned now, dead by the next tick.
|
// The running child: the deferred path — condemned now, dead by the next tick.
|
||||||
if (!runtime.system.kill(spinner)) fail("kill spinner");
|
if (!process.kill(spinner)) fail("kill spinner");
|
||||||
if (awaitChildExit(endpoint) != spinner) fail("spinner exit notification");
|
if (awaitChildExit(endpoint) != spinner) fail("spinner exit notification");
|
||||||
|
|
||||||
if (listed(sleeper, "/system/tests/process-test")) fail("sleeper still listed after kill");
|
if (listed(sleeper, "/system/tests/process-test")) fail("sleeper still listed after kill");
|
||||||
@@ -172,9 +176,9 @@ pub fn main(init: runtime.process.Init) void {
|
|||||||
|
|
||||||
// M17.2: both children were killed by us, and the reason says so — the whole
|
// M17.2: both children were killed by us, and the reason says so — the whole
|
||||||
// restart-policy input, read through the runtime like a real supervisor would.
|
// restart-policy input, read through the runtime like a real supervisor would.
|
||||||
if ((runtime.process.exitReason(sleeper) orelse .exited) != .killed) fail("sleeper reason not killed");
|
if ((process.exitReason(sleeper) orelse .exited) != .killed) fail("sleeper reason not killed");
|
||||||
if ((runtime.process.exitReason(spinner) orelse .exited) != .killed) fail("spinner reason not killed");
|
if ((process.exitReason(spinner) orelse .exited) != .killed) fail("spinner reason not killed");
|
||||||
if (runtime.process.exitReason(0xFFFF_FFF0) != null) fail("unknown id had a reason");
|
if (process.exitReason(0xFFFF_FFF0) != null) fail("unknown id had a reason");
|
||||||
|
|
||||||
_ = runtime.system.write("process-test: ok\n");
|
_ = logging.write("process-test: ok\n");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,11 +4,11 @@
|
|||||||
//! confirms the pattern is visible — proving cross-process shared memory over the extended
|
//! confirms the pattern is visible — proving cross-process shared memory over the extended
|
||||||
//! capability-passing path.
|
//! capability-passing path.
|
||||||
|
|
||||||
const runtime = @import("runtime");
|
|
||||||
const system = runtime.system;
|
|
||||||
const shared_memory = runtime.shared_memory;
|
|
||||||
const ipc = runtime.ipc;
|
|
||||||
|
|
||||||
|
const ipc = @import("ipc");
|
||||||
|
const time = @import("time");
|
||||||
|
const memory = @import("memory");
|
||||||
|
const logging = @import("logging");
|
||||||
const pattern_len = 4096;
|
const pattern_len = 4096;
|
||||||
|
|
||||||
/// The pattern the server checks — must match shared-memory-server.zig.
|
/// The pattern the server checks — must match shared-memory-server.zig.
|
||||||
@@ -20,27 +20,27 @@ fn lookupServer() ?ipc.Handle {
|
|||||||
var attempts: usize = 0;
|
var attempts: usize = 0;
|
||||||
while (attempts < 100) : (attempts += 1) {
|
while (attempts < 100) : (attempts += 1) {
|
||||||
if (ipc.lookup(.shared_memory_test)) |h| return h;
|
if (ipc.lookup(.shared_memory_test)) |h| return h;
|
||||||
system.sleep(50);
|
time.sleepMillis(50);
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn main() void {
|
pub fn main() void {
|
||||||
const region = shared_memory.create(pattern_len) orelse {
|
const region = memory.sharedCreate(pattern_len) orelse {
|
||||||
_ = system.write("shared-memory: create failed\n");
|
_ = logging.write("shared-memory: create failed\n");
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
var i: usize = 0;
|
var i: usize = 0;
|
||||||
while (i < pattern_len) : (i += 1) region.ptr[i] = expected(i);
|
while (i < pattern_len) : (i += 1) region.ptr[i] = expected(i);
|
||||||
|
|
||||||
const server = lookupServer() orelse {
|
const server = lookupServer() orelse {
|
||||||
_ = system.write("shared-memory: no server\n");
|
_ = logging.write("shared-memory: no server\n");
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
// A non-empty message (so it reaches on_message, not the ping path), carrying the shared-memory
|
// A non-empty message (so it reaches on_message, not the ping path), carrying the shared-memory
|
||||||
// region's capability. The reply is empty; we just need the round trip.
|
// region's capability. The reply is empty; we just need the round trip.
|
||||||
var reply: [64]u8 = undefined;
|
var reply: [64]u8 = undefined;
|
||||||
_ = ipc.callCap(server, "shared-memory", &reply, region.handle) catch {
|
_ = ipc.callCap(server, "shared-memory", &reply, region.handle) catch {
|
||||||
_ = system.write("shared-memory: call failed\n");
|
_ = logging.write("shared-memory: call failed\n");
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,11 +4,11 @@
|
|||||||
//! is visible through the mapping — proving the two processes share the same physical pages
|
//! is visible through the mapping — proving the two processes share the same physical pages
|
||||||
//! (not a copy). On success it prints `shared-memory: shared 4096 bytes ok`, the test's marker.
|
//! (not a copy). On success it prints `shared-memory: shared 4096 bytes ok`, the test's marker.
|
||||||
|
|
||||||
const runtime = @import("runtime");
|
|
||||||
const system = runtime.system;
|
|
||||||
const shared_memory = runtime.shared_memory;
|
|
||||||
const ipc = runtime.ipc;
|
|
||||||
|
|
||||||
|
const ipc = @import("ipc");
|
||||||
|
const service = @import("service");
|
||||||
|
const memory = @import("memory");
|
||||||
|
const logging = @import("logging");
|
||||||
const pattern_len = 4096;
|
const pattern_len = 4096;
|
||||||
|
|
||||||
/// The pattern the client writes — must match shared-memory-client.zig.
|
/// The pattern the client writes — must match shared-memory-client.zig.
|
||||||
@@ -21,24 +21,24 @@ fn onMessage(message: []const u8, reply: []u8, sender: u32, capability: ?ipc.Han
|
|||||||
_ = reply;
|
_ = reply;
|
||||||
_ = sender;
|
_ = sender;
|
||||||
const cap = capability orelse {
|
const cap = capability orelse {
|
||||||
_ = system.write("shared-memory: shared FAILED (no capability)\n");
|
_ = logging.write("shared-memory: shared FAILED (no capability)\n");
|
||||||
return 0;
|
return 0;
|
||||||
};
|
};
|
||||||
const ptr = shared_memory.map(cap) orelse {
|
const ptr = memory.sharedMap(cap) orelse {
|
||||||
_ = system.write("shared-memory: shared FAILED (map)\n");
|
_ = logging.write("shared-memory: shared FAILED (map)\n");
|
||||||
return 0;
|
return 0;
|
||||||
};
|
};
|
||||||
var i: usize = 0;
|
var i: usize = 0;
|
||||||
while (i < pattern_len) : (i += 1) {
|
while (i < pattern_len) : (i += 1) {
|
||||||
if (ptr[i] != expected(i)) {
|
if (ptr[i] != expected(i)) {
|
||||||
_ = system.write("shared-memory: shared FAILED (mismatch)\n");
|
_ = logging.write("shared-memory: shared FAILED (mismatch)\n");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ = system.write("shared-memory: shared 4096 bytes ok\n");
|
_ = logging.write("shared-memory: shared 4096 bytes ok\n");
|
||||||
return 0; // empty reply — the client only needs the round trip to unblock
|
return 0; // empty reply — the client only needs the round trip to unblock
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn main() void {
|
pub fn main() void {
|
||||||
runtime.service.run(64, .{ .service = .shared_memory_test, .on_message = onMessage });
|
service.run(64, .{ .service = .shared_memory_test, .on_message = onMessage });
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
//!
|
//!
|
||||||
//! Two modes, chosen by argv[1] (default "spawn"):
|
//! Two modes, chosen by argv[1] (default "spawn"):
|
||||||
//! spawn — M2: one worker writes a shared global; the main thread observes it, proving
|
//! spawn — M2: one worker writes a shared global; the main thread observes it, proving
|
||||||
//! `runtime.Thread.spawn` started a task in the **same** address space.
|
//! `Thread.spawn` started a task in the **same** address space.
|
||||||
//! join — M3: N workers each do K atomic increments on a shared counter and stamp the
|
//! join — M3: N workers each do K atomic increments on a shared counter and stamp the
|
||||||
//! core they ran on; the main thread `join`s all N and checks the total is
|
//! core they ran on; the main thread `join`s all N and checks the total is
|
||||||
//! exactly N*K (every worker ran, join waited) and that >1 core was used
|
//! exactly N*K (every worker ran, join waited) and that >1 core was used
|
||||||
@@ -12,10 +12,14 @@
|
|||||||
//! Built multi-threaded (`addThreadedUserBinary`) so atomics/shared reads are real.
|
//! Built multi-threaded (`addThreadedUserBinary`) so atomics/shared reads are real.
|
||||||
|
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const runtime = @import("runtime");
|
const process = @import("process");
|
||||||
|
const time = @import("time");
|
||||||
|
const memory = @import("memory");
|
||||||
|
const logging = @import("logging");
|
||||||
|
const Thread = @import("thread").Thread;
|
||||||
|
|
||||||
fn write(comptime s: []const u8) void {
|
fn write(comptime s: []const u8) void {
|
||||||
_ = runtime.system.write(s);
|
_ = logging.write(s);
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- M2: spawn mode ---------------------------------------------------------
|
// --- M2: spawn mode ---------------------------------------------------------
|
||||||
@@ -31,13 +35,13 @@ fn spawnWorker() void {
|
|||||||
|
|
||||||
fn runSpawnMode() void {
|
fn runSpawnMode() void {
|
||||||
write("thread-test: starting\n");
|
write("thread-test: starting\n");
|
||||||
_ = runtime.Thread.spawn(.{}, spawnWorker, .{}) catch {
|
_ = Thread.spawn(.{}, spawnWorker, .{}) catch {
|
||||||
write("thread-test: FAIL spawn refused\n");
|
write("thread-test: FAIL spawn refused\n");
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
var spins: usize = 0;
|
var spins: usize = 0;
|
||||||
while (spawn_done.load(.acquire) == 0 and spins < 50_000_000) : (spins += 1) {
|
while (spawn_done.load(.acquire) == 0 and spins < 50_000_000) : (spins += 1) {
|
||||||
runtime.system.yield();
|
process.yield();
|
||||||
}
|
}
|
||||||
if (spawn_done.load(.acquire) == 1 and shared_value == sentinel) {
|
if (spawn_done.load(.acquire) == 1 and shared_value == sentinel) {
|
||||||
write("thread-test: child ran in shared address space ok\n");
|
write("thread-test: child ran in shared address space ok\n");
|
||||||
@@ -64,7 +68,7 @@ fn joinWorker() void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn stampCore() void {
|
fn stampCore() void {
|
||||||
const core = runtime.Thread.currentCore();
|
const core = Thread.currentCore();
|
||||||
if (core < 32) _ = cores_seen.fetchOr(@as(u32, 1) << @intCast(core), .monotonic);
|
if (core < 32) _ = cores_seen.fetchOr(@as(u32, 1) << @intCast(core), .monotonic);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -79,10 +83,10 @@ fn noopWorker() void {}
|
|||||||
fn runJoinMode() void {
|
fn runJoinMode() void {
|
||||||
write("thread-test: join mode starting\n");
|
write("thread-test: join mode starting\n");
|
||||||
|
|
||||||
var threads: [worker_count]runtime.Thread = undefined;
|
var threads: [worker_count]Thread = undefined;
|
||||||
var spawned: u32 = 0;
|
var spawned: u32 = 0;
|
||||||
while (spawned < worker_count) : (spawned += 1) {
|
while (spawned < worker_count) : (spawned += 1) {
|
||||||
threads[spawned] = runtime.Thread.spawn(.{}, joinWorker, .{}) catch break;
|
threads[spawned] = Thread.spawn(.{}, joinWorker, .{}) catch break;
|
||||||
}
|
}
|
||||||
if (spawned != worker_count) {
|
if (spawned != worker_count) {
|
||||||
write("thread-test: FAIL could not spawn all workers\n");
|
write("thread-test: FAIL could not spawn all workers\n");
|
||||||
@@ -102,14 +106,14 @@ fn runJoinMode() void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// detach: the worker runs and we never join it.
|
// detach: the worker runs and we never join it.
|
||||||
const dt = runtime.Thread.spawn(.{}, detachWorker, .{}) catch {
|
const dt = Thread.spawn(.{}, detachWorker, .{}) catch {
|
||||||
write("thread-test: FAIL detach spawn refused\n");
|
write("thread-test: FAIL detach spawn refused\n");
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
dt.detach();
|
dt.detach();
|
||||||
var spins: usize = 0;
|
var spins: usize = 0;
|
||||||
while (detach_done.load(.acquire) == 0 and spins < 50_000_000) : (spins += 1) {
|
while (detach_done.load(.acquire) == 0 and spins < 50_000_000) : (spins += 1) {
|
||||||
runtime.system.yield();
|
process.yield();
|
||||||
}
|
}
|
||||||
if (detach_done.load(.acquire) != 1) {
|
if (detach_done.load(.acquire) != 1) {
|
||||||
write("thread-test: FAIL detached worker did not run\n");
|
write("thread-test: FAIL detached worker did not run\n");
|
||||||
@@ -121,7 +125,7 @@ fn runJoinMode() void {
|
|||||||
// 16-slot handle table well before 40; here they all succeed.
|
// 16-slot handle table well before 40; here they all succeed.
|
||||||
var cycle: u32 = 0;
|
var cycle: u32 = 0;
|
||||||
while (cycle < 40) : (cycle += 1) {
|
while (cycle < 40) : (cycle += 1) {
|
||||||
const th = runtime.Thread.spawn(.{}, noopWorker, .{}) catch {
|
const th = Thread.spawn(.{}, noopWorker, .{}) catch {
|
||||||
write("thread-test: FAIL spawn exhausted across join cycles (endpoint leak?)\n");
|
write("thread-test: FAIL spawn exhausted across join cycles (endpoint leak?)\n");
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
@@ -133,7 +137,7 @@ fn runJoinMode() void {
|
|||||||
|
|
||||||
// --- M4: futex mode ---------------------------------------------------------
|
// --- M4: futex mode ---------------------------------------------------------
|
||||||
|
|
||||||
const Futex = runtime.Thread.Futex;
|
const Futex = Thread.Futex;
|
||||||
|
|
||||||
var futex_word = std.atomic.Value(u32).init(0);
|
var futex_word = std.atomic.Value(u32).init(0);
|
||||||
var waiter_parked = std.atomic.Value(u32).init(0);
|
var waiter_parked = std.atomic.Value(u32).init(0);
|
||||||
@@ -151,16 +155,16 @@ fn futexWaiter() void {
|
|||||||
fn runFutexMode() void {
|
fn runFutexMode() void {
|
||||||
write("thread-futex: starting\n");
|
write("thread-futex: starting\n");
|
||||||
|
|
||||||
const waiter = runtime.Thread.spawn(.{}, futexWaiter, .{}) catch {
|
const waiter = Thread.spawn(.{}, futexWaiter, .{}) catch {
|
||||||
write("thread-futex: FAIL spawn refused\n");
|
write("thread-futex: FAIL spawn refused\n");
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
// Let the waiter reach its wait, then give it a beat to actually park in-kernel.
|
// Let the waiter reach its wait, then give it a beat to actually park in-kernel.
|
||||||
var spins: usize = 0;
|
var spins: usize = 0;
|
||||||
while (waiter_parked.load(.acquire) == 0 and spins < 50_000_000) : (spins += 1) {
|
while (waiter_parked.load(.acquire) == 0 and spins < 50_000_000) : (spins += 1) {
|
||||||
runtime.system.yield();
|
process.yield();
|
||||||
}
|
}
|
||||||
runtime.system.sleep(50);
|
time.sleepMillis(50);
|
||||||
|
|
||||||
// The handshake: publish the value, then wake the parked waiter.
|
// The handshake: publish the value, then wake the parked waiter.
|
||||||
futex_word.store(1, .release);
|
futex_word.store(1, .release);
|
||||||
@@ -182,8 +186,8 @@ fn runFutexMode() void {
|
|||||||
|
|
||||||
// --- M5: mutex mode (bounded producer/consumer over Mutex + Condition) ------
|
// --- M5: mutex mode (bounded producer/consumer over Mutex + Condition) ------
|
||||||
|
|
||||||
const Mutex = runtime.Thread.Mutex;
|
const Mutex = Thread.Mutex;
|
||||||
const Condition = runtime.Thread.Condition;
|
const Condition = Thread.Condition;
|
||||||
|
|
||||||
const producers: u32 = 2;
|
const producers: u32 = 2;
|
||||||
const consumers: u32 = 2;
|
const consumers: u32 = 2;
|
||||||
@@ -237,11 +241,11 @@ fn consumer() void {
|
|||||||
fn runMutexMode() void {
|
fn runMutexMode() void {
|
||||||
write("thread-mutex: starting\n");
|
write("thread-mutex: starting\n");
|
||||||
|
|
||||||
var threads: [producers + consumers]runtime.Thread = undefined;
|
var threads: [producers + consumers]Thread = undefined;
|
||||||
var n: usize = 0;
|
var n: usize = 0;
|
||||||
var p: u32 = 0;
|
var p: u32 = 0;
|
||||||
while (p < producers) : (p += 1) {
|
while (p < producers) : (p += 1) {
|
||||||
threads[n] = runtime.Thread.spawn(.{}, producer, .{p * per_producer}) catch {
|
threads[n] = Thread.spawn(.{}, producer, .{p * per_producer}) catch {
|
||||||
write("thread-mutex: FAIL producer spawn\n");
|
write("thread-mutex: FAIL producer spawn\n");
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
@@ -249,7 +253,7 @@ fn runMutexMode() void {
|
|||||||
}
|
}
|
||||||
var c: u32 = 0;
|
var c: u32 = 0;
|
||||||
while (c < consumers) : (c += 1) {
|
while (c < consumers) : (c += 1) {
|
||||||
threads[n] = runtime.Thread.spawn(.{}, consumer, .{}) catch {
|
threads[n] = Thread.spawn(.{}, consumer, .{}) catch {
|
||||||
write("thread-mutex: FAIL consumer spawn\n");
|
write("thread-mutex: FAIL consumer spawn\n");
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
@@ -276,18 +280,18 @@ fn runMutexMode() void {
|
|||||||
var worker_ids: [2]std.atomic.Value(u32) = .{ std.atomic.Value(u32).init(0), std.atomic.Value(u32).init(0) };
|
var worker_ids: [2]std.atomic.Value(u32) = .{ std.atomic.Value(u32).init(0), std.atomic.Value(u32).init(0) };
|
||||||
|
|
||||||
fn idWorker(slot: usize) void {
|
fn idWorker(slot: usize) void {
|
||||||
worker_ids[slot].store(runtime.Thread.getCurrentId(), .release);
|
worker_ids[slot].store(Thread.getCurrentId(), .release);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn runIdMode() void {
|
fn runIdMode() void {
|
||||||
write("thread-id: starting\n");
|
write("thread-id: starting\n");
|
||||||
const main_id = runtime.Thread.getCurrentId();
|
const main_id = Thread.getCurrentId();
|
||||||
|
|
||||||
const t0 = runtime.Thread.spawn(.{}, idWorker, .{@as(usize, 0)}) catch {
|
const t0 = Thread.spawn(.{}, idWorker, .{@as(usize, 0)}) catch {
|
||||||
write("thread-id: FAIL spawn\n");
|
write("thread-id: FAIL spawn\n");
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
const t1 = runtime.Thread.spawn(.{}, idWorker, .{@as(usize, 1)}) catch {
|
const t1 = Thread.spawn(.{}, idWorker, .{@as(usize, 1)}) catch {
|
||||||
write("thread-id: FAIL spawn\n");
|
write("thread-id: FAIL spawn\n");
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
@@ -315,7 +319,7 @@ const allocs_per_thread: u32 = 500;
|
|||||||
var allocs_clean = std.atomic.Value(u32).init(0);
|
var allocs_clean = std.atomic.Value(u32).init(0);
|
||||||
|
|
||||||
fn allocWorker(seed: u32) void {
|
fn allocWorker(seed: u32) void {
|
||||||
const gpa = runtime.allocator();
|
const gpa = memory.allocator();
|
||||||
var rng: u32 = seed | 1;
|
var rng: u32 = seed | 1;
|
||||||
var round: u32 = 0;
|
var round: u32 = 0;
|
||||||
while (round < allocs_per_thread) : (round += 1) {
|
while (round < allocs_per_thread) : (round += 1) {
|
||||||
@@ -338,10 +342,10 @@ fn allocWorker(seed: u32) void {
|
|||||||
|
|
||||||
fn runAllocMode() void {
|
fn runAllocMode() void {
|
||||||
write("thread-alloc: starting\n");
|
write("thread-alloc: starting\n");
|
||||||
var threads: [alloc_threads]runtime.Thread = undefined;
|
var threads: [alloc_threads]Thread = undefined;
|
||||||
var n: u32 = 0;
|
var n: u32 = 0;
|
||||||
while (n < alloc_threads) : (n += 1) {
|
while (n < alloc_threads) : (n += 1) {
|
||||||
threads[n] = runtime.Thread.spawn(.{}, allocWorker, .{n +% 1}) catch {
|
threads[n] = Thread.spawn(.{}, allocWorker, .{n +% 1}) catch {
|
||||||
write("thread-alloc: FAIL spawn\n");
|
write("thread-alloc: FAIL spawn\n");
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
@@ -384,20 +388,20 @@ fn tlsWorker(marker: u64) void {
|
|||||||
// marker — cross-talk. A per-thread FS base keeps each thread's slot private.
|
// marker — cross-talk. A per-thread FS base keeps each thread's slot private.
|
||||||
var spins: usize = 0;
|
var spins: usize = 0;
|
||||||
while (tls_written.load(.acquire) < 2 and spins < 50_000_000) : (spins += 1) {
|
while (tls_written.load(.acquire) < 2 and spins < 50_000_000) : (spins += 1) {
|
||||||
runtime.system.yield();
|
process.yield();
|
||||||
}
|
}
|
||||||
if (readTlsSlot() == marker and runtime.Thread.getCurrentId() != 0) {
|
if (readTlsSlot() == marker and Thread.getCurrentId() != 0) {
|
||||||
_ = tls_ok.fetchAdd(1, .monotonic);
|
_ = tls_ok.fetchAdd(1, .monotonic);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn runTlsMode() void {
|
fn runTlsMode() void {
|
||||||
write("thread-tls: starting\n");
|
write("thread-tls: starting\n");
|
||||||
const t0 = runtime.Thread.spawn(.{}, tlsWorker, .{@as(u64, 0xAAAA_0000)}) catch {
|
const t0 = Thread.spawn(.{}, tlsWorker, .{@as(u64, 0xAAAA_0000)}) catch {
|
||||||
write("thread-tls: FAIL spawn\n");
|
write("thread-tls: FAIL spawn\n");
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
const t1 = runtime.Thread.spawn(.{}, tlsWorker, .{@as(u64, 0xBBBB_0000)}) catch {
|
const t1 = Thread.spawn(.{}, tlsWorker, .{@as(u64, 0xBBBB_0000)}) catch {
|
||||||
write("thread-tls: FAIL spawn\n");
|
write("thread-tls: FAIL spawn\n");
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
@@ -412,7 +416,7 @@ fn runTlsMode() void {
|
|||||||
|
|
||||||
// --- M11: rwlock mode (readers/writers over an RwLock) ----------------------
|
// --- M11: rwlock mode (readers/writers over an RwLock) ----------------------
|
||||||
|
|
||||||
const RwLock = runtime.Thread.RwLock;
|
const RwLock = Thread.RwLock;
|
||||||
|
|
||||||
var rwlock = RwLock{};
|
var rwlock = RwLock{};
|
||||||
var rw_a: u64 = 0;
|
var rw_a: u64 = 0;
|
||||||
@@ -444,16 +448,16 @@ fn rwReader() void {
|
|||||||
|
|
||||||
fn runRwlockMode() void {
|
fn runRwlockMode() void {
|
||||||
write("thread-rwlock: starting\n");
|
write("thread-rwlock: starting\n");
|
||||||
var writers: [2]runtime.Thread = undefined;
|
var writers: [2]Thread = undefined;
|
||||||
var readers: [3]runtime.Thread = undefined;
|
var readers: [3]Thread = undefined;
|
||||||
for (&writers) |*w| {
|
for (&writers) |*w| {
|
||||||
w.* = runtime.Thread.spawn(.{}, rwWriter, .{}) catch {
|
w.* = Thread.spawn(.{}, rwWriter, .{}) catch {
|
||||||
write("thread-rwlock: FAIL spawn\n");
|
write("thread-rwlock: FAIL spawn\n");
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
for (&readers) |*r| {
|
for (&readers) |*r| {
|
||||||
r.* = runtime.Thread.spawn(.{}, rwReader, .{}) catch {
|
r.* = Thread.spawn(.{}, rwReader, .{}) catch {
|
||||||
write("thread-rwlock: FAIL spawn\n");
|
write("thread-rwlock: FAIL spawn\n");
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
@@ -484,11 +488,11 @@ fn faultingWorker() void {
|
|||||||
/// main thread parks forever and never prints anything more.
|
/// main thread parks forever and never prints anything more.
|
||||||
fn runFaultWorkerMode() void {
|
fn runFaultWorkerMode() void {
|
||||||
write("thread-test: spawning faulting worker\n");
|
write("thread-test: spawning faulting worker\n");
|
||||||
_ = runtime.Thread.spawn(.{}, faultingWorker, .{}) catch {
|
_ = Thread.spawn(.{}, faultingWorker, .{}) catch {
|
||||||
write("thread-test: FAIL spawn refused\n");
|
write("thread-test: FAIL spawn refused\n");
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
while (true) runtime.system.yield();
|
while (true) process.yield();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn spinningWorker() void {
|
fn spinningWorker() void {
|
||||||
@@ -498,27 +502,27 @@ fn spinningWorker() void {
|
|||||||
/// spin-forever: a kill target. The worker spins without syscalls (the condemned
|
/// spin-forever: a kill target. The worker spins without syscalls (the condemned
|
||||||
/// path); the main thread yields (the parked path).
|
/// path); the main thread yields (the parked path).
|
||||||
fn runSpinForeverMode() void {
|
fn runSpinForeverMode() void {
|
||||||
_ = runtime.Thread.spawn(.{}, spinningWorker, .{}) catch {
|
_ = Thread.spawn(.{}, spinningWorker, .{}) catch {
|
||||||
write("thread-test: FAIL spawn refused\n");
|
write("thread-test: FAIL spawn refused\n");
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
write("thread-test: spinning\n");
|
write("thread-test: spinning\n");
|
||||||
while (true) runtime.system.yield();
|
while (true) process.yield();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn exitingWorker() void {
|
fn exitingWorker() void {
|
||||||
write("thread-test: worker exiting the process\n");
|
write("thread-test: worker exiting the process\n");
|
||||||
runtime.system.exit(3); // exit from ANY thread is group death (.aborted)
|
process.exit(3); // exit from ANY thread is group death (.aborted)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// exit-worker: a WORKER calls exit(3); the group must die with the leader's
|
/// exit-worker: a WORKER calls exit(3); the group must die with the leader's
|
||||||
/// reason reading .aborted.
|
/// reason reading .aborted.
|
||||||
fn runExitWorkerMode() void {
|
fn runExitWorkerMode() void {
|
||||||
_ = runtime.Thread.spawn(.{}, exitingWorker, .{}) catch {
|
_ = Thread.spawn(.{}, exitingWorker, .{}) catch {
|
||||||
write("thread-test: FAIL spawn refused\n");
|
write("thread-test: FAIL spawn refused\n");
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
while (true) runtime.system.yield();
|
while (true) process.yield();
|
||||||
}
|
}
|
||||||
|
|
||||||
var race_go = std.atomic.Value(u32).init(0);
|
var race_go = std.atomic.Value(u32).init(0);
|
||||||
@@ -531,7 +535,7 @@ fn racingWorker() void {
|
|||||||
/// race: two members fault as near-simultaneously as user space can arrange —
|
/// race: two members fault as near-simultaneously as user space can arrange —
|
||||||
/// the group-dying latch must make the two triggers count as one death.
|
/// the group-dying latch must make the two triggers count as one death.
|
||||||
fn runRaceMode() void {
|
fn runRaceMode() void {
|
||||||
_ = runtime.Thread.spawn(.{}, racingWorker, .{}) catch {
|
_ = Thread.spawn(.{}, racingWorker, .{}) catch {
|
||||||
write("thread-test: FAIL spawn refused\n");
|
write("thread-test: FAIL spawn refused\n");
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
@@ -543,17 +547,17 @@ fn runRaceMode() void {
|
|||||||
var leader_exit_done = std.atomic.Value(u32).init(0);
|
var leader_exit_done = std.atomic.Value(u32).init(0);
|
||||||
|
|
||||||
fn patientWorker() void {
|
fn patientWorker() void {
|
||||||
while (leader_exit_done.load(.acquire) == 0) runtime.system.yield();
|
while (leader_exit_done.load(.acquire) == 0) process.yield();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// leader-exit: the MAIN thread asks for thread_exit; the kernel must refuse
|
/// leader-exit: the MAIN thread asks for thread_exit; the kernel must refuse
|
||||||
/// (-EPERM) and the worker must be entirely unaffected.
|
/// (-EPERM) and the worker must be entirely unaffected.
|
||||||
fn runLeaderExitMode() void {
|
fn runLeaderExitMode() void {
|
||||||
const worker = runtime.Thread.spawn(.{}, patientWorker, .{}) catch {
|
const worker = Thread.spawn(.{}, patientWorker, .{}) catch {
|
||||||
write("thread-test: FAIL spawn refused\n");
|
write("thread-test: FAIL spawn refused\n");
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
runtime.Thread.tryExitCurrent(); // refused: we are the leader
|
Thread.tryExitCurrent(); // refused: we are the leader
|
||||||
write("thread-test: leader thread_exit refused ok\n");
|
write("thread-test: leader thread_exit refused ok\n");
|
||||||
leader_exit_done.store(1, .release);
|
leader_exit_done.store(1, .release);
|
||||||
worker.join();
|
worker.join();
|
||||||
@@ -564,7 +568,7 @@ fn promptWorker() void {}
|
|||||||
/// solo: regression — a WORKER's thread_exit stays per-thread; the sibling
|
/// solo: regression — a WORKER's thread_exit stays per-thread; the sibling
|
||||||
/// (main) survives it.
|
/// (main) survives it.
|
||||||
fn runSoloMode() void {
|
fn runSoloMode() void {
|
||||||
const worker = runtime.Thread.spawn(.{}, promptWorker, .{}) catch {
|
const worker = Thread.spawn(.{}, promptWorker, .{}) catch {
|
||||||
write("thread-test: FAIL spawn refused\n");
|
write("thread-test: FAIL spawn refused\n");
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
@@ -580,7 +584,7 @@ fn patternByte(i: usize) u8 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn shmWorker() void {
|
fn shmWorker() void {
|
||||||
const region = runtime.shared_memory.create(shm_pattern_length) orelse {
|
const region = memory.sharedCreate(shm_pattern_length) orelse {
|
||||||
write("thread-shm: FAIL create refused\n");
|
write("thread-shm: FAIL create refused\n");
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
@@ -596,7 +600,7 @@ fn shmWorker() void {
|
|||||||
/// then checks the mapping is intact — freed frames would have been reused and
|
/// then checks the mapping is intact — freed frames would have been reused and
|
||||||
/// scribbled on.
|
/// scribbled on.
|
||||||
fn runShmWorkerMode() void {
|
fn runShmWorkerMode() void {
|
||||||
const worker = runtime.Thread.spawn(.{}, shmWorker, .{}) catch {
|
const worker = Thread.spawn(.{}, shmWorker, .{}) catch {
|
||||||
write("thread-test: FAIL spawn refused\n");
|
write("thread-test: FAIL spawn refused\n");
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
@@ -605,7 +609,7 @@ fn runShmWorkerMode() void {
|
|||||||
if (base == 0) return; // the worker already printed the failure
|
if (base == 0) return; // the worker already printed the failure
|
||||||
var churn: usize = 0;
|
var churn: usize = 0;
|
||||||
while (churn < 8) : (churn += 1) {
|
while (churn < 8) : (churn += 1) {
|
||||||
const noise = runtime.shared_memory.create(shm_pattern_length) orelse break;
|
const noise = memory.sharedCreate(shm_pattern_length) orelse break;
|
||||||
@memset(noise.ptr[0..shm_pattern_length], 0xFF);
|
@memset(noise.ptr[0..shm_pattern_length], 0xFF);
|
||||||
}
|
}
|
||||||
const view: [*]const u8 = @ptrFromInt(base);
|
const view: [*]const u8 = @ptrFromInt(base);
|
||||||
@@ -620,7 +624,7 @@ fn runShmWorkerMode() void {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn main(init: runtime.process.Init) void {
|
pub fn main(init: process.Init) void {
|
||||||
const mode = init.arguments.get(1) orelse "spawn";
|
const mode = init.arguments.get(1) orelse "spawn";
|
||||||
if (std.mem.eql(u8, mode, "join")) {
|
if (std.mem.eql(u8, mode, "join")) {
|
||||||
runJoinMode();
|
runJoinMode();
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
//! /system/tests/vfs-test — a ring-3 client that proves the kernel VFS end to
|
//! /system/tests/vfs-test — a ring-3 client that proves the kernel VFS end to
|
||||||
//! end through the plain `runtime.fs` API: resolve its OWN binary under the
|
//! end through the plain `file_system` API: resolve its OWN binary under the
|
||||||
//! kernel-served /system mount, check its metadata, read its ELF magic, and
|
//! kernel-served /system mount, check its metadata, read its ELF magic, and
|
||||||
//! list /system/services. On success it heartbeats "vfstest: ok" so the kernel
|
//! list /system/services. On success it heartbeats "vfstest: ok" so the kernel
|
||||||
//! test can observe it; on failure it reports what went wrong.
|
//! test can observe it; on failure it reports what went wrong.
|
||||||
@@ -9,10 +9,12 @@
|
|||||||
//! server's release-on-death sweep are the point.
|
//! server's release-on-death sweep are the point.
|
||||||
|
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const runtime = @import("runtime");
|
const fs = @import("file-system");
|
||||||
const fs = runtime.fs;
|
const process = @import("process");
|
||||||
|
const time = @import("time");
|
||||||
|
const logging = @import("logging");
|
||||||
|
|
||||||
pub fn main(init: runtime.process.Init) void {
|
pub fn main(init: process.Init) void {
|
||||||
if (init.arguments.count > 1) {
|
if (init.arguments.count > 1) {
|
||||||
park();
|
park();
|
||||||
return;
|
return;
|
||||||
@@ -21,30 +23,30 @@ pub fn main(init: runtime.process.Init) void {
|
|||||||
// Our own binary, resolved through the kernel mount table.
|
// Our own binary, resolved through the kernel mount table.
|
||||||
const self_path = "/system/tests/vfs-test";
|
const self_path = "/system/tests/vfs-test";
|
||||||
var file = fs.open(self_path, .{}) orelse {
|
var file = fs.open(self_path, .{}) orelse {
|
||||||
_ = runtime.system.write("vfstest: open of own binary failed\n");
|
_ = logging.write("vfstest: open of own binary failed\n");
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
defer file.close();
|
defer file.close();
|
||||||
|
|
||||||
const attributes = file.attributes() orelse {
|
const attributes = file.attributes() orelse {
|
||||||
_ = runtime.system.write("vfstest: attributes failed\n");
|
_ = logging.write("vfstest: attributes failed\n");
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
if (attributes.kind != .regular or attributes.size == 0) {
|
if (attributes.kind != .regular or attributes.size == 0) {
|
||||||
_ = runtime.system.write("vfstest: bad attributes\n");
|
_ = logging.write("vfstest: bad attributes\n");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var header: [4]u8 = undefined;
|
var header: [4]u8 = undefined;
|
||||||
const n = file.read(&header) orelse 0;
|
const n = file.read(&header) orelse 0;
|
||||||
if (n != 4 or header[0] != 0x7f or header[1] != 'E' or header[2] != 'L' or header[3] != 'F') {
|
if (n != 4 or header[0] != 0x7f or header[1] != 'E' or header[2] != 'L' or header[3] != 'F') {
|
||||||
_ = runtime.system.write("vfstest: ELF magic mismatch\n");
|
_ = logging.write("vfstest: ELF magic mismatch\n");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// The write refusal: /system is read-only by construction.
|
// The write refusal: /system is read-only by construction.
|
||||||
if (file.write("x") != null or fs.open("/system/tests/new-file", .{ .create = true }) != null) {
|
if (file.write("x") != null or fs.open("/system/tests/new-file", .{ .create = true }) != null) {
|
||||||
_ = runtime.system.write("vfstest: /system accepted a write\n");
|
_ = logging.write("vfstest: /system accepted a write\n");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -59,13 +61,13 @@ pub fn main(init: runtime.process.Init) void {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!saw_init) {
|
if (!saw_init) {
|
||||||
_ = runtime.system.write("vfstest: /system/services listing missed init\n");
|
_ = logging.write("vfstest: /system/services listing missed init\n");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
_ = runtime.system.write("vfstest: ok\n");
|
_ = logging.write("vfstest: ok\n");
|
||||||
runtime.system.sleep(1000);
|
time.sleepMillis(1000);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -76,14 +78,14 @@ fn park() void {
|
|||||||
var tries: u32 = 0;
|
var tries: u32 = 0;
|
||||||
while (parked == null and tries < 1000) : (tries += 1) {
|
while (parked == null and tries < 1000) : (tries += 1) {
|
||||||
parked = fs.open("/mnt/usb/parked", .{ .create = true });
|
parked = fs.open("/mnt/usb/parked", .{ .create = true });
|
||||||
if (parked == null) runtime.system.sleep(20);
|
if (parked == null) time.sleepMillis(20);
|
||||||
}
|
}
|
||||||
if (parked == null) {
|
if (parked == null) {
|
||||||
_ = runtime.system.write("vfstest: park open failed\n");
|
_ = logging.write("vfstest: park open failed\n");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
while (true) {
|
while (true) {
|
||||||
_ = runtime.system.write("vfstest: parked\n");
|
_ = logging.write("vfstest: parked\n");
|
||||||
runtime.system.sleep(500);
|
time.sleepMillis(500);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user