//! /system/drivers/pci-bus — the PCI bus driver: enumeration moved out of ring 0 //! (docs/m19-m20-plan.md, M19). The device manager matches the `pci_host_bridge` //! node and spawns one instance per bridge, the bridge's device id as argv[1] — //! the same per-device contract as usb-xhci-bus. //! //! M19.1 (this increment): claim the bridge, map its ECAM window (resource 0; //! the bus range and the MMIO apertures follow it), walk every //! bus/device/function config header, and log what the walk finds — ending //! with "pci-bus: N functions found", which the `pci-scan` scenario compares //! against the kernel's own enumeration. Registration and reports (M19.2), and //! the kernel walk's retirement (M19.3), build on this proven-equivalent scan. const std = @import("std"); const runtime = @import("runtime"); const protocol = runtime.device_manager_protocol; const device = runtime.device; fn writeLine(comptime fmt: []const u8, arguments: anytype) void { var line: [128]u8 = undefined; _ = runtime.system.write(std.fmt.bufPrint(&line, fmt, arguments) catch return); } var bridge_id: u64 = protocol.no_device; var ecam_base: usize = 0; var start_bus: u64 = 0; var bus_count: u64 = 0; /// One aligned 32-bit read from a function's configuration space. fn configRead(bus: u64, dev: u64, function: u64, offset: u64) u32 { const address = ecam_base + (((bus - start_bus) << 20) | (dev << 15) | (function << 12) | offset); const register: *volatile u32 = @ptrFromInt(address); return register.*; } /// Claim the bridge, map the ECAM, hello the manager, then scan. fn initialise(endpoint: runtime.ipc.Handle) bool { _ = endpoint; if (!device.claim(bridge_id)) { writeLine("pci-bus: unable to claim bridge device {d}\n", .{bridge_id}); return false; } const buffer = runtime.allocator().alloc(device.DeviceDescriptor, 64) catch { _ = runtime.system.write("pci-bus: out of memory\n"); return false; }; const total = device.enumerate(buffer); const descriptor = for (buffer[0..@min(total, buffer.len)]) |d| { if (d.id == bridge_id) break d; } else { writeLine("pci-bus: device {d} not in the device tree\n", .{bridge_id}); return false; }; // 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. if (descriptor.resource_count < 2 or descriptor.resources[0].kind != @intFromEnum(device.ResourceKind.memory)) { _ = runtime.system.write("pci-bus: bridge has no ECAM window\n"); return false; } const bus_range = for (descriptor.resources[0..@intCast(descriptor.resource_count)]) |resource| { if (resource.kind == @intFromEnum(device.ResourceKind.bus_range)) break resource; } else { _ = runtime.system.write("pci-bus: bridge has no bus range\n"); return false; }; start_bus = bus_range.start; bus_count = bus_range.len; ecam_base = device.mmioMap(bridge_id, 0) orelse { _ = runtime.system.write("pci-bus: ECAM mmio_map failed\n"); return false; }; // The handshake, then the scan (reports join in M19.2). var manager: ?runtime.ipc.Handle = null; var tries: u32 = 0; while (manager == null and tries < 100) : (tries += 1) { manager = runtime.ipc.lookup(.device_manager); if (manager == null) runtime.system.sleep(20); } const h = manager orelse { _ = runtime.system.write("pci-bus: no device manager to hello\n"); return false; }; const hello = protocol.Hello{ .role = @intFromEnum(protocol.Role.bus), .device_id = bridge_id }; var reply: [protocol.message_maximum]u8 = undefined; const n = runtime.ipc.call(h, std.mem.asBytes(&hello), &reply) catch { _ = runtime.system.write("pci-bus: hello call failed\n"); return false; }; if (n < protocol.reply_size or std.mem.bytesToValue(protocol.HelloReply, reply[0..protocol.reply_size]).status != 0) { _ = runtime.system.write("pci-bus: hello refused\n"); return false; } scan(); return true; } /// The brute-force walk the kernel does today, from ring 3: every bus in the /// range, 32 devices, 8 functions; vendor id FFFFh means nothing decodes there, /// and only multifunction devices get their functions 1..7 probed. fn scan() void { var found: u32 = 0; var bus: u64 = start_bus; while (bus < start_bus + bus_count) : (bus += 1) { var dev: u64 = 0; while (dev < 32) : (dev += 1) { const first = configRead(bus, dev, 0, 0); if (first & 0xFFFF == 0xFFFF) continue; const multifunction = (configRead(bus, dev, 0, 0x0C) >> 16) & 0x80 != 0; var function: u64 = 0; while (function < 8) : (function += 1) { if (function != 0 and !multifunction) break; const vendor_device = configRead(bus, dev, function, 0); if (vendor_device & 0xFFFF == 0xFFFF) continue; const class_revision = configRead(bus, dev, function, 0x08); found += 1; writeLine("pci-bus: {d}:{d}.{d} class 0x{x:0>6}\n", .{ bus, dev, function, class_revision >> 8 }); } } } writeLine("pci-bus: {d} functions found\n", .{found}); } fn onMessage(message: []const u8, reply: []u8, sender: u32, capability: ?runtime.ipc.Handle) usize { _ = message; _ = reply; _ = sender; _ = capability; return 0; } pub fn main(init: runtime.process.Init) void { const argument = init.arguments.get(1) orelse return; // bare (ramdisk sweep): stay silent bridge_id = std.fmt.parseInt(u64, argument, 10) catch { writeLine("pci-bus: malformed bridge device id '{s}'\n", .{argument}); return; }; runtime.service.run(protocol.message_maximum, .{ .init = initialise, .on_message = onMessage, }); } pub const panic = runtime.panic; comptime { _ = &runtime.start._start; // pull the runtime entry shim into the image }