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:
@@ -19,12 +19,11 @@
|
||||
//! proven.
|
||||
|
||||
const std = @import("std");
|
||||
const runtime = @import("runtime");
|
||||
const time = @import("time");
|
||||
const memory = @import("memory");
|
||||
const mmio = @import("mmio");
|
||||
const usb_abi = @import("usb-abi");
|
||||
const usb_ids = @import("usb-ids");
|
||||
const dma = runtime.dma;
|
||||
const system = runtime.system;
|
||||
|
||||
// --- 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
|
||||
// entry is a Link TRB back to the start. `cycle` is the producer cycle state.
|
||||
const ProducerRing = struct {
|
||||
region: dma.Region,
|
||||
region: memory.DmaRegion,
|
||||
enqueue_index: usize = 0,
|
||||
cycle: bool = true,
|
||||
|
||||
@@ -182,8 +181,8 @@ const ProducerRing = struct {
|
||||
// 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.
|
||||
const EventRing = struct {
|
||||
segment: dma.Region,
|
||||
table: dma.Region,
|
||||
segment: memory.DmaRegion,
|
||||
table: memory.DmaRegion,
|
||||
dequeue_index: usize = 0,
|
||||
cycle: bool = true,
|
||||
|
||||
@@ -269,12 +268,12 @@ pub const Device = struct {
|
||||
port: u32 = 0,
|
||||
speed: u32 = 0,
|
||||
max_packet_size_0: u32 = 8,
|
||||
input_context: dma.Region = .{ .virtual = 0, .physical = 0 },
|
||||
device_context: dma.Region = .{ .virtual = 0, .physical = 0 },
|
||||
input_context: memory.DmaRegion = .{ .virtual = 0, .physical = 0 },
|
||||
device_context: memory.DmaRegion = .{ .virtual = 0, .physical = 0 },
|
||||
ep0_ring: ProducerRing = .{ .region = .{ .virtual = 0, .physical = 0 } },
|
||||
// A page-sized bounce buffer for control-transfer data (descriptors are read
|
||||
// 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),
|
||||
configuration_value: u8 = 0,
|
||||
interface_count: u8 = 0,
|
||||
@@ -308,7 +307,7 @@ const Subscription = struct {
|
||||
dci: u32 = 0,
|
||||
endpoint_address: u8 = 0,
|
||||
ring: *ProducerRing = undefined,
|
||||
buffer: dma.Region = .{ .virtual = 0, .physical = 0 },
|
||||
buffer: memory.DmaRegion = .{ .virtual = 0, .physical = 0 },
|
||||
max_length: u16 = 0,
|
||||
armed_trb_physical: u64 = 0,
|
||||
// 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,
|
||||
context_size: usize, // 32 or 64 (CSZ)
|
||||
|
||||
device_context_array: dma.Region,
|
||||
device_context_array: memory.DmaRegion,
|
||||
command_ring: ProducerRing,
|
||||
event_ring: EventRing,
|
||||
devices: [max_devices]Device = [_]Device{.{}} ** max_devices,
|
||||
@@ -601,19 +600,19 @@ pub const Controller = struct {
|
||||
write32(self.operational(op_config), self.max_slots);
|
||||
|
||||
// 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);
|
||||
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.
|
||||
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();
|
||||
write64(self.operational(op_crcr), self.command_ring.region.physical | cycle_bit);
|
||||
|
||||
// The event ring: one segment + a one-entry segment table.
|
||||
self.event_ring = .{
|
||||
.segment = dma.alloc(page_size, dma.coherent) orelse return null,
|
||||
.table = dma.alloc(page_size, dma.coherent) orelse return null,
|
||||
.segment = memory.dmaAlloc(page_size, memory.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);
|
||||
table.ring_segment_base = self.event_ring.segment.physical;
|
||||
@@ -663,11 +662,11 @@ pub const Controller = struct {
|
||||
return;
|
||||
}
|
||||
// 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);
|
||||
var index: u32 = 0;
|
||||
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;
|
||||
}
|
||||
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.
|
||||
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) {
|
||||
if (system.clock() >= deadline) return false;
|
||||
if (time.clock() >= deadline) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
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) {
|
||||
if (system.clock() >= deadline) return false;
|
||||
if (time.clock() >= deadline) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -723,7 +722,7 @@ pub const Controller = struct {
|
||||
write64(self.interrupter(event_ring_dequeue_pointer), dequeue | (1 << 3));
|
||||
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
|
||||
/// completion code, or null on timeout.
|
||||
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) {
|
||||
const event = self.nextEvent(deadline) orelse return null;
|
||||
const kind = trbType(event.control);
|
||||
@@ -764,9 +763,9 @@ pub const Controller = struct {
|
||||
// bits and PED are untouched) and preserving PP.
|
||||
const before = self.portStatus(port);
|
||||
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) {
|
||||
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).
|
||||
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).
|
||||
fn enableSlot(self: *Controller) ?u8 {
|
||||
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) {
|
||||
const event = self.nextEvent(deadline) orelse return null;
|
||||
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.)
|
||||
if (device.parent_slot == 0) {
|
||||
if (!self.resetPort(device.port)) return false;
|
||||
system.sleep(10);
|
||||
time.sleepMillis(10);
|
||||
} else return false;
|
||||
}
|
||||
return false;
|
||||
@@ -891,7 +890,7 @@ pub const Controller = struct {
|
||||
// (TRSTRCY, 10 ms) after reset before it answers SET_ADDRESS.
|
||||
// Addressing immediately gives a USB Transaction Error (code 4) on
|
||||
// real full-speed devices; QEMU tolerates the omission.
|
||||
system.sleep(10);
|
||||
time.sleepMillis(10);
|
||||
}
|
||||
const slot_id = self.enableSlot() orelse {
|
||||
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),
|
||||
.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.device_context = dma.alloc(page_size, dma.coherent) orelse return self.abandon(device);
|
||||
device.ep0_ring = .{ .region = 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 = memory.dmaAlloc(page_size, memory.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.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);
|
||||
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;
|
||||
const ring = self.getOrConfigureEndpoint(device, endpoint) 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;
|
||||
subscription.* = .{
|
||||
.active = true,
|
||||
@@ -1068,7 +1067,7 @@ pub const Controller = struct {
|
||||
_ = self.controlTransfer(hub, hubreq.setPortFeature(hubreq.feature_port_reset, port), &.{}, false);
|
||||
var tries: u32 = 0;
|
||||
while (tries < 200) : (tries += 1) {
|
||||
system.sleep(5);
|
||||
time.sleepMillis(5);
|
||||
const s = self.readHubPortStatus(hub, port) orelse return null;
|
||||
if (s & hubreq.status_enable != 0) break;
|
||||
}
|
||||
@@ -1113,11 +1112,11 @@ pub const Controller = struct {
|
||||
.parent_slot = hub.slot_id,
|
||||
.parent_port = @intCast(port),
|
||||
};
|
||||
device.input_context = dma.alloc(page_size, dma.coherent) orelse return self.abandon(device);
|
||||
device.device_context = dma.alloc(page_size, dma.coherent) orelse return self.abandon(device);
|
||||
device.ep0_ring = .{ .region = 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 = memory.dmaAlloc(page_size, memory.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.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);
|
||||
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
|
||||
/// failure at boot, with a USB keyboard and mouse polling concurrently).
|
||||
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) {
|
||||
const event = self.nextEvent(deadline) orelse return null;
|
||||
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];
|
||||
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();
|
||||
self.buildConfigureEndpointInputContext(device, endpoint, dci, &configured.ring);
|
||||
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 {
|
||||
const ring = self.getOrConfigureEndpoint(device, endpoint) 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 direction_in = endpoint.address & 0x80 != 0;
|
||||
subscription.* = .{
|
||||
@@ -1570,7 +1569,7 @@ pub const Controller = struct {
|
||||
/// driver's timer tick.
|
||||
pub fn pump(self: *Controller) void {
|
||||
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);
|
||||
if (kind == @intFromEnum(TrbType.transfer_event)) {
|
||||
_ = self.serviceInterruptEvent(event);
|
||||
|
||||
Reference in New Issue
Block a user