// SPDX-FileCopyrightText: 2024 Himbeer // // SPDX-License-Identifier: AGPL-3.0-or-later const config = @import("config"); const std = @import("std"); const hw_info = @embedFile("cfg/platform/" ++ config.platform ++ ".hwi"); const devices = std.io.FixedBufferStream([]const u8){ .buffer = hw_info, .pos = 0 }; pub const ParseError = error{ MissingKind, MissingRegAddr, MissingRegLen, UnknownDevKind, }; pub const DevKind = enum(u32) { cpus, plic, pcie, pci, _, pub fn parse(buf: []const u8) !DevKind { if (std.mem.eql(u8, buf, "cpus")) { return .cpus; } else if (std.mem.eql(u8, buf, "plic")) { return .plic; } else if (std.mem.eql(u8, buf, "pcie")) { return .pcie; } else if (std.mem.eql(u8, buf, "pci")) { return .pci; } return ParseError.UnknownDevKind; } pub fn isUserManageable(self: DevKind) bool { return switch (self) { .pcie, .pci => true, else => false, }; } pub fn isMemoryMapped(self: DevKind) bool { return switch (self) { .cpus => false, .plic => true, .pcie => true, .pci => true, _ => false, }; } }; pub const Dev = extern struct { kind: DevKind, reg: Reg, value: u64, pub fn parse(buf: []const u8) !Dev { var columns = std.mem.tokenizeScalar(u8, buf, ' '); const kind_buf = columns.next() orelse return ParseError.MissingKind; const reg_addr_buf = columns.next() orelse return ParseError.MissingRegAddr; const reg_len_buf = columns.next() orelse return ParseError.MissingRegLen; const value_buf = columns.next() orelse "0"; return .{ .kind = try DevKind.parse(kind_buf), .reg = .{ .addr = try std.fmt.parseUnsigned(u64, reg_addr_buf, 0), .len = try std.fmt.parseUnsigned(u64, reg_len_buf, 0), }, .value = try std.fmt.parseUnsigned(u64, value_buf, 0), }; } }; pub const Reg = extern struct { addr: u64, len: u64, pub fn slice(self: Reg, comptime T: type) []volatile T { const ptr: [*]volatile T = @ptrFromInt(self.addr); return ptr[0 .. self.len / @sizeOf(T)]; } }; pub const Iterator = struct { stream: std.io.FixedBufferStream([]const u8), endian: std.builtin.Endian, pub fn init(stream: std.io.FixedBufferStream([]const u8)) !Iterator { var fbs = stream; const big_endian = try fbs.reader().readByte(); return .{ .stream = fbs, .endian = if (big_endian != 0) .big else .little, }; } pub fn next(it: *Iterator) ?Dev { const reader = it.stream.reader(); return reader.readStructEndian(Dev, it.endian) catch null; } pub fn reset(it: *Iterator) void { try it.stream.seekTo(1); } pub fn byKind(it: Iterator, kind: DevKind) ByKind { return .{ .iterator = it, .kind = kind }; } }; pub fn iterator() !Iterator { return Iterator.init(devices); } pub const ByKind = struct { iterator: Iterator, kind: DevKind, pub fn next(it: *ByKind) ?Dev { while (it.iterator.next()) |device| { if (device.kind == it.kind) return device; } else return null; } pub fn reset(it: *ByKind) void { it.iterator.reset(); } }; pub inline fn byKind(kind: DevKind) !ByKind { const it = try iterator(); return it.byKind(kind); } pub fn byAddress(reg_addr: usize, only_user_manageable: bool) !?Dev { var it = try iterator(); while (it.next()) |device| { const authorized = !only_user_manageable or device.kind.isUserManageable(); const eligible = device.kind.isMemoryMapped() and authorized; if (device.reg.addr == reg_addr and eligible) return device; } else return null; }