//! system/services/volume-manager — the storage layer's policy home //! (docs/file-system-development/storage-architecture.md). It sits beside the //! device manager: the device manager owns the DEVICE tree; this owns the VOLUME //! layer. It hears about storage providers, probes their partition tables and //! content identity, and — in later increments — confines each filesystem to its //! partition and spawns one per volume, answering that filesystem's startup //! hello with the (range-confined) block channel. //! //! This increment (V3a) is discovery and probe only: find the mass-storage //! provider, read block 0, parse the first volume out of it, and log what it //! found — additive, with the FAT service still acquiring its own volume. The //! delegation (confine + spawn + hand over the channel) and the mount map land //! next, keeping the FAT service working throughout. const std = @import("std"); const channel = @import("channel"); const device_manager_protocol = @import("device-manager-protocol"); const driver = @import("driver"); const ipc = @import("ipc"); const block = @import("block"); const memory = @import("memory"); const logging = @import("logging"); const process = @import("process"); const service = @import("service"); const time = @import("time"); const envelope = @import("envelope"); const partition = @import("partition.zig"); var service_endpoint: ipc.Handle = 0; var manager_handle: ?ipc.Handle = null; var bounce: memory.DmaRegion = undefined; var bounce_ready = false; var probed = false; const probe_retry_ms = 500; /// The first mass-storage provider's block channel, via the device manager's /// tree — the same lineage acquisition a filesystem makes (block is not a /// registry name). One enumerate sweep; null until the chain is up. fn acquireStorage() ?block.Device { const manager = manager_handle orelse opened: { const handle = channel.openEndpoint("device-manager") orelse return null; manager_handle = handle; break :opened handle; }; const Entry = device_manager_protocol.ChildEntry; var start: u64 = 0; while (true) { const enumerate = envelope.Header{ .operation = envelope.operation_enumerate, .target = start }; var reply: [device_manager_protocol.message_maximum]u8 = undefined; const length = ipc.call(manager, std.mem.asBytes(&enumerate), &reply) catch return null; const status = envelope.statusOf(reply[0..length]) orelse return null; if (status.status != 0) return null; const carried = @min(@as(usize, status.len), length -| envelope.prefix_size); const tail = reply[envelope.prefix_size..][0..carried]; const count = tail.len / @sizeOf(Entry); if (count == 0) return null; var index: usize = 0; while (index < count) : (index += 1) { const entry = std.mem.bytesToValue(Entry, tail[index * @sizeOf(Entry) ..][0..@sizeOf(Entry)]); if (entry.device_id == device_manager_protocol.no_device) continue; if ((entry.identity >> 16) & 0xff != 0x08 or (entry.identity >> 8) & 0xff != 0x06) continue; const exchanged = driver.helloOn(manager, .consumer, entry.device_id, null, true) orelse return null; const provider = exchanged.channel orelse continue; return .{ .endpoint = provider }; } start += count; } } /// One probe attempt: acquire the storage channel, read block 0, and parse the /// first volume. Sets `probed` and logs on success; a failure leaves everything /// for the next tick. fn tryProbe() void { if (probed) return; if (!bounce_ready) { bounce = memory.dmaAlloc(512, memory.dma_coherent | memory.dma_shareable) orelse return; bounce_ready = true; } const device = acquireStorage() orelse return; // Attach the read buffer to the controller (a no-op success without an // enforcing IOMMU). The volume manager is unconfined — it reads the whole // device to probe — so no range is defined here. if (bounce.handle) |handle| { if (!device.attach(handle)) return; _ = ipc.close(handle); bounce.handle = null; // attached once; do not re-forward on a retry } const geometry = device.geometry() orelse return; if (!device.read(0, 1, bounce.physical)) return; const sector: [*]const u8 = @ptrFromInt(bounce.virtual); const volume = partition.firstVolume(sector[0..512], geometry.block_count) orelse { _ = logging.write("volume-manager: no volume found on the storage device\n"); probed = true; // a device with no recognizable volume is not retried return; }; std.log.info("volume 0x{x} at lba {d}, {d} blocks", .{ volume.identity, volume.base_lba, volume.block_count }); probed = true; } fn initialise(endpoint: ipc.Handle) bool { service_endpoint = endpoint; _ = logging.write("volume-manager: starting, waiting for a storage device\n"); tryProbe(); if (!probed) _ = time.timerOnce(endpoint, probe_retry_ms); return true; } fn onNotification(badge: u64) void { const got = ipc.Received{ .len = 0, .badge = badge, .cap = null }; if (got.isTimer()) { tryProbe(); if (!probed) _ = time.timerOnce(service_endpoint, probe_retry_ms); } } /// No clients yet: a filesystem hello lands here in the next increment. Until /// then, refuse politely. fn onMessage(message: []const u8, out: []u8, sender: u32, arrived: *ipc.Arrival) usize { _ = message; _ = sender; _ = arrived; const status = envelope.Status{ .status = -envelope.ENOSYS, .len = 0 }; @memcpy(out[0..envelope.prefix_size], std.mem.asBytes(&status)); return envelope.prefix_size; } pub fn main(init: process.Init) void { _ = init; service.run(device_manager_protocol.message_maximum, .{ .service = "volume-manager", .init = initialise, .on_message = onMessage, .on_notification = onNotification, }); }