//! system/services/exfat — the exFAT filesystem service. Like fat.zig, this is //! only the format-specific half: it finds its block device, sets up the DMA //! bounce buffer, mounts the exFAT engine on it, and hands the mounted volume to //! the shared filesystem harness (library/kernel/file-system-harness), which owns //! everything else — vfs serving, the open-node table, mount registration, the //! exit sweep, durable-on-close. The engine (engine.zig) is the pure, //! host-testable format code; on-disk.zig its byte layout. //! //! This service is a near-clone of fat.zig: the second engine reuses the harness //! wholesale, which is the reuse the storage architecture promised //! (docs/file-system-development/storage-architecture.md). The block data path //! never crosses IPC: a DMA bounce buffer is handed to the block driver by //! physical address, and the engine copies sectors in and out. const std = @import("std"); const channel = @import("channel"); const volume_manager_protocol = @import("volume-manager-protocol"); const ipc = @import("ipc"); const process = @import("process"); const block = @import("block"); const memory = @import("memory"); const logging = @import("logging"); const time = @import("time"); const engine = @import("engine.zig"); const envelope = @import("envelope"); const harness = @import("file-system-harness"); /// The serving harness, specialized for the exFAT engine. One volume per process. const Harness = harness.Server(engine.FileSystem); // The engine's BlockDevice, backed by the `.block` driver plus a DMA bounce // buffer the driver reads/writes by physical address. const IpcBlock = struct { device: block.Device, bounce: memory.DmaRegion, // engine.max_transfer_sectors * 512 bytes fn readBlocks(context: *anyopaque, lba: u64, count: u32, buffer: []u8) bool { const self: *IpcBlock = @ptrCast(@alignCast(context)); if (count == 0 or count > engine.max_transfer_sectors) return false; const len = count * 512; if (!self.device.read(lba, count, self.bounce.physical)) return false; const source: [*]const u8 = @ptrFromInt(self.bounce.virtual); @memcpy(buffer[0..len], source[0..len]); return true; } fn writeBlocks(context: *anyopaque, lba: u64, count: u32, buffer: []const u8) bool { const self: *IpcBlock = @ptrCast(@alignCast(context)); if (count == 0 or count > engine.max_transfer_sectors) return false; const len = count * 512; const destination: [*]u8 = @ptrFromInt(self.bounce.virtual); @memcpy(destination[0..len], buffer[0..len]); if (!self.device.write(lba, count, self.bounce.physical)) return false; device_dirty = true; // a block reached the device; a close will flush it return true; } }; var ipc_block: IpcBlock = undefined; // Set whenever a block is written, cleared when the device cache is flushed on a // file close — so writes are committed to stable media before a power-off. var device_dirty: bool = false; var filesystem: engine.FileSystem = undefined; /// The volume this exFAT process serves, its id given as argv[1] by the volume /// manager that spawned it. The startup hello names it so the manager returns the /// right volume's channel. var my_volume_id: u64 = 0; /// The volume's own mount path, handed in as argv[2] by the volume manager: the /// volume's content id-path (e.g. /volumes/exfat-12345678). Defaults to /// /volumes/exfat only for a bare launch with no argument; the manager always /// passes it. The slice points into the entry block, valid for the process life. var volume_mount_prefix: []const u8 = "/volumes/exfat"; /// The mounts this volume installs: its own root, plus — only if it is the boot /// volume (it resolves /system/configuration) — the two FHS rewrites, so the /// logger's /system/logs stays decoupled from which volume backs it. Boot-volume /// detection is by content, so it works no matter which volume carries /system. /// bound: mounts one volume installs (its root + the two boot rewrites) /// decided-by: ours /// protects: the mount_specs array /// at-limit: truncate - unreachable today (fixed at 3); more configured mounts /// would need this raised, a deliberate change /// observed-by: a mount silently missing from the harness's mount log const maximum_mounts_per_volume = 4; var mount_specs: [maximum_mounts_per_volume]harness.MountSpec = undefined; /// Get this volume's block channel from the volume manager (establishment by /// lineage — `block` is not a registry name). The manager spawned this process, /// confined it to its partition, and answers the hello with the channel; the /// channel is range-confined to this process's badge. Null until the manager has /// the volume ready — this retries. fn acquireVolume() ?block.Device { var attempts: u32 = 0; const vm = while (attempts < 500) : (attempts += 1) { if (channel.openEndpoint("volume-manager")) |handle| break handle; time.sleepMillis(20); } else return null; attempts = 0; while (attempts < 500) : (attempts += 1) { var packet: [volume_manager_protocol.message_maximum]u8 = undefined; const framed = volume_manager_protocol.Protocol.encodeRequest(.hello, my_volume_id, .{}, &.{}, &packet) orelse return null; var reply: [volume_manager_protocol.message_maximum]u8 = undefined; const answered = ipc.callCap(vm, framed, &reply, null) catch return null; const status = envelope.statusOf(reply[0..answered.len]) orelse return null; if (status.status != 0) { if (answered.cap) |stray| _ = ipc.close(stray); _ = logging.write("/system/services/exfat: volume manager refused the hello\n"); return null; } if (answered.cap) |bus| return .{ .endpoint = bus }; // Acked with no channel: the volume is not ready yet — retry. time.sleepMillis(20); } return null; } /// Durable-on-close: commit the device write cache if any block reached it since /// the last flush. The harness calls this on every close; the dirty check keeps /// it cheap. fn flushIfDirty() void { if (device_dirty) { _ = ipc_block.device.flush(); device_dirty = false; } } /// exFAT bring-up: find the block device, set up DMA, mount the engine, and hand /// the volume to the harness — or null to retry on the harness's timer. fn exfatBringUp(endpoint: ipc.Handle) ?Harness.Volume { _ = endpoint; const device = acquireVolume() orelse return null; const geometry = device.geometry() orelse { _ = logging.write("/system/services/exfat: block geometry unavailable\n"); return null; }; // Shareable so the buffer's capability can be attached down the chain, making // its physical addresses reachable under an enforcing IOMMU. No-op otherwise. const bounce = memory.dmaAlloc(engine.max_transfer_sectors * 512, memory.dma_coherent | memory.dma_shareable) orelse return null; if (bounce.handle) |handle| { // Attach, detach, and attach again: the round trip exercises BOTH verbs of // the DMA-window lifecycle through the whole chain on every boot. if (!device.attach(handle)) { _ = logging.write("/system/services/exfat: could not attach the DMA bounce buffer\n"); return null; } if (!device.detach(handle)) { _ = logging.write("/system/services/exfat: could not detach the DMA bounce buffer\n"); return null; } if (!device.attach(handle)) { _ = logging.write("/system/services/exfat: could not re-attach the DMA bounce buffer\n"); return null; } _ = ipc.close(handle); // the binding holds its own reference now } ipc_block = .{ .device = device, .bounce = bounce }; const block_device = engine.BlockDevice{ .context = &ipc_block, .block_size = geometry.block_size, .block_count = geometry.block_count, .readBlocksFn = IpcBlock.readBlocks, .writeBlocksFn = IpcBlock.writeBlocks, }; filesystem = engine.FileSystem.mount(block_device) orelse { _ = logging.write("/system/services/exfat: not an exFAT filesystem\n"); return null; }; std.log.info("mounted exFAT ({d} clusters, {d} sectors/cluster, serial 0x{x})", .{ filesystem.geometry.cluster_count, filesystem.geometry.sectors_per_cluster, filesystem.geometry.volume_serial_number }); // The volume mounts at its id-path (argv[2]). The boot/system volume — the one // carrying the /system tree — additionally installs the two FHS rewrites, by // CONTENT: it resolves /system/configuration on its own media. A data volume // mounts only at its id-path and never shadows the running system. mount_specs[0] = .{ .prefix = volume_mount_prefix }; var mount_count: usize = 1; if (filesystem.resolve("/system/configuration") != null) { std.log.info("volume {d} carries the system tree; backing /system/configuration and /system/logs", .{my_volume_id}); mount_specs[1] = .{ .prefix = "/system/configuration", .rewrite = "/system/configuration" }; mount_specs[2] = .{ .prefix = "/system/logs", .rewrite = "/system/logs" }; mount_count = 3; } else { std.log.info("volume {d} is a data volume; mounted at {s}", .{ my_volume_id, volume_mount_prefix }); } return .{ .engine = &filesystem, .mounts = mount_specs[0..mount_count], .flush = flushIfDirty }; } pub fn main(init: process.Init) void { // The volume manager spawns this process with its volume id as argv[1] and the // volume's mount path (its id-path) as argv[2]. if (init.arguments.get(1)) |id| { my_volume_id = std.fmt.parseInt(u64, id, 10) catch 0; } if (init.arguments.get(2)) |prefix| { volume_mount_prefix = prefix; } _ = logging.write("/system/services/exfat: starting, waiting for a block device\n"); Harness.run(.{ .bringUp = exfatBringUp }); }