From 48421fc0efc89eada468b15e792384000a737071 Mon Sep 17 00:00:00 2001 From: Lorenzo Torres Date: Sat, 31 Jan 2026 15:57:54 +0100 Subject: [PATCH] add UART console --- src/Fdt.zig | 38 +------------------------------- src/drivers/Console.zig | 48 +++++++++++++++++++++++++++++++++++++++++ src/main.zig | 17 ++++++--------- 3 files changed, 55 insertions(+), 48 deletions(-) create mode 100644 src/drivers/Console.zig diff --git a/src/Fdt.zig b/src/Fdt.zig index 1522122..89250f9 100644 --- a/src/Fdt.zig +++ b/src/Fdt.zig @@ -11,7 +11,6 @@ pub const Token = enum(u32) { end = 0x09, }; -/// Raw FDT header as it appears in memory (big-endian) pub const RawHeader = extern struct { magic: u32, totalsize: u32, @@ -25,7 +24,6 @@ pub const RawHeader = extern struct { size_dt_struct: u32, }; -/// Memory reservation entry pub const ReserveEntry = extern struct { address: u64, size: u64, @@ -38,7 +36,6 @@ pub const ReserveEntry = extern struct { } }; -/// Parsed property data pub const Property = struct { name: []const u8, data: []const u8, @@ -53,7 +50,6 @@ pub const Property = struct { return std.mem.bigToNative(u64, @as(*const u64, @alignCast(@ptrCast(self.data.ptr))).*); } - /// Read property as a null-terminated string pub fn asString(self: Property) ?[]const u8 { if (self.data.len == 0) return null; for (self.data, 0..) |c, i| { @@ -91,7 +87,7 @@ pub const StringListIterator = struct { self.pos += 1; } const end = self.pos; - self.pos += 1; // skip null terminator + self.pos += 1; if (start == end) return null; return self.data[start..end]; @@ -130,15 +126,11 @@ pub const U64ArrayIterator = struct { } }; -/// FDT node with its properties and children pub const Node = struct { - /// Full path to this node (e.g., "/soc/uart@10000000") name: []const u8, fdt: *const Fdt, - /// Offset in structure block where this node's content starts (after name) struct_offset: usize, - /// Get the unit address part of the node name (after '@') pub fn unitAddress(self: Node) ?[]const u8 { for (self.name, 0..) |c, i| { if (c == '@') return self.name[i + 1 ..]; @@ -146,7 +138,6 @@ pub const Node = struct { return null; } - /// Get the base name (before '@') pub fn baseName(self: Node) []const u8 { for (self.name, 0..) |c, i| { if (c == '@') return self.name[0..i]; @@ -154,7 +145,6 @@ pub const Node = struct { return self.name; } - /// Iterate over all properties of this node pub fn properties(self: Node) PropertyIterator { return .{ .fdt = self.fdt, @@ -162,7 +152,6 @@ pub const Node = struct { }; } - /// Get a specific property by name pub fn getProperty(self: Node, name: []const u8) ?Property { var iter = self.properties(); while (iter.next()) |prop| { @@ -173,7 +162,6 @@ pub const Node = struct { return null; } - /// Iterate over direct children of this node pub fn children(self: Node) ChildIterator { // Skip to the end of properties to find children var pos = self.struct_offset; @@ -202,7 +190,6 @@ pub const Node = struct { }; } - /// Find a child node by name pub fn getChild(self: Node, name: []const u8) ?Node { var iter = self.children(); while (iter.next()) |child| { @@ -213,13 +200,11 @@ pub const Node = struct { return null; } - /// Get the 'compatible' property as a string list pub fn compatible(self: Node) ?StringListIterator { const prop = self.getProperty("compatible") orelse return null; return prop.asStringList(); } - /// Check if node is compatible with given string pub fn isCompatible(self: Node, compat: []const u8) bool { var iter = self.compatible() orelse return false; while (iter.next()) |c| { @@ -228,38 +213,31 @@ pub const Node = struct { return false; } - /// Get the 'reg' property - returns iterator over address/size pairs - /// Note: caller must know address-cells and size-cells from parent pub fn reg(self: Node) ?[]const u8 { const prop = self.getProperty("reg") orelse return null; return prop.data; } - /// Get the 'status' property pub fn status(self: Node) ?[]const u8 { const prop = self.getProperty("status") orelse return null; return prop.asString(); } - /// Check if node is enabled (status = "okay" or "ok" or no status property) pub fn isEnabled(self: Node) bool { const s = self.status() orelse return true; return std.mem.eql(u8, s, "okay") or std.mem.eql(u8, s, "ok"); } - /// Get #address-cells (defaults to 2 if not present) pub fn addressCells(self: Node) u32 { const prop = self.getProperty("#address-cells") orelse return 2; return prop.asU32() orelse 2; } - /// Get #size-cells (defaults to 1 if not present) pub fn sizeCells(self: Node) u32 { const prop = self.getProperty("#size-cells") orelse return 1; return prop.asU32() orelse 1; } - /// Get phandle of this node pub fn phandle(self: Node) ?u32 { // Try both 'phandle' and legacy 'linux,phandle' if (self.getProperty("phandle")) |prop| { @@ -360,7 +338,6 @@ pub const ChildIterator = struct { } }; -/// Iterator for all nodes in the tree (depth-first) pub const NodeIterator = struct { fdt: *const Fdt, pos: usize, @@ -465,7 +442,6 @@ pub const Error = error{ InvalidStructure, }; -/// Parse an FDT from a raw pointer (as provided by OpenSBI) pub fn parse(ptr: *const anyopaque) Error!Fdt { const base: [*]const u8 = @ptrCast(ptr); const header: *const RawHeader = @alignCast(@ptrCast(ptr)); @@ -503,7 +479,6 @@ pub fn parse(ptr: *const anyopaque) Error!Fdt { }; } -/// Get the root node pub fn root(self: *const Fdt) ?Node { if (self.struct_block.len < 4) return null; @@ -525,7 +500,6 @@ pub fn root(self: *const Fdt) ?Node { }; } -/// Iterate over all nodes in the tree pub fn nodes(self: *const Fdt) NodeIterator { return .{ .fdt = self, @@ -534,7 +508,6 @@ pub fn nodes(self: *const Fdt) NodeIterator { }; } -/// Iterate over all nodes except the root pub fn nodesWithoutRoot(self: *const Fdt) NodeIterator { return .{ .fdt = self, @@ -543,7 +516,6 @@ pub fn nodesWithoutRoot(self: *const Fdt) NodeIterator { }; } -/// Find a node by path (e.g., "/soc/uart@10000000") pub fn findNode(self: *const Fdt, path: []const u8) ?Node { if (path.len == 0 or path[0] != '/') return null; @@ -574,7 +546,6 @@ pub fn findNode(self: *const Fdt, path: []const u8) ?Node { return current; } -/// Find a node by its phandle pub fn findByPhandle(self: *const Fdt, handle: u32) ?Node { var iter = self.nodes(); while (iter.next()) |node| { @@ -585,7 +556,6 @@ pub fn findByPhandle(self: *const Fdt, handle: u32) ?Node { return null; } -/// Find all nodes compatible with a given string pub fn findCompatible(self: *const Fdt, compat: []const u8) CompatibleIterator { return .{ .inner = self.nodesWithoutRoot(), @@ -593,13 +563,11 @@ pub fn findCompatible(self: *const Fdt, compat: []const u8) CompatibleIterator { }; } -/// Find the first node compatible with a given string pub fn findFirstCompatible(self: *const Fdt, compat: []const u8) ?Node { var iter = self.findCompatible(compat); return iter.next(); } -/// Iterate over memory reservation entries pub fn memoryReservations(self: *const Fdt) MemoryReservationIterator { return .{ .data = self.mem_rsv_block, @@ -607,17 +575,14 @@ pub fn memoryReservations(self: *const Fdt) MemoryReservationIterator { }; } -/// Get the chosen node (boot parameters) pub fn chosen(self: *const Fdt) ?Node { return self.findNode("/chosen"); } -/// Get memory node(s) pub fn memory(self: *const Fdt) ?Node { return self.findNode("/memory") orelse self.findFirstCompatible("memory"); } -/// Get the CPUs node pub fn cpus(self: *const Fdt) ?Node { return self.findNode("/cpus"); } @@ -679,7 +644,6 @@ fn alignUp(value: anytype, alignment: @TypeOf(value)) @TypeOf(value) { return (value + alignment - 1) & ~(alignment - 1); } -/// Helper to parse reg property with known cell sizes pub fn parseReg(data: []const u8, address_cells: u32, size_cells: u32) RegIterator { return .{ .data = data, diff --git a/src/drivers/Console.zig b/src/drivers/Console.zig new file mode 100644 index 0000000..8b478b4 --- /dev/null +++ b/src/drivers/Console.zig @@ -0,0 +1,48 @@ +const std = @import("std"); +const Fdt = @import("../Fdt.zig"); +const Console = @This(); + +pub const Writer = struct { + base: *volatile u8, + interface: std.Io.Writer, + + pub fn drain(io_w: *std.Io.Writer, data: []const []const u8, splat: usize) !usize { + _ = splat; + const self: *Writer = @fieldParentPtr("interface", io_w); + self.base.* = data[0][0]; + + return data.len; + } +}; + +mmio: *volatile u8, + +pub fn init(fdt: Fdt) ?Console { + if (fdt.findFirstCompatible("ns16550a")) |console| { + var iter = console.getProperty("reg").?.asU32Array(); + _ = iter.next(); + const start = iter.next().?; + return .{ + .mmio = @ptrFromInt(@as(usize, @intCast(start))), + }; + } + + return null; +} + +pub fn writer(self: *const Console) Writer { + return .{ + .base = self.mmio, + .interface = std.Io.Writer { + .buffer = &[_]u8{}, + .vtable = &.{.drain = Writer.drain}, + }, + }; +} + +pub fn print(self: *const Console, comptime s: []const u8, args: anytype) void { + var w = self.writer(); + w.interface.print(s, args) catch { + @panic("Failed to print debug message.\n"); + }; +} diff --git a/src/main.zig b/src/main.zig index c3f18c3..f99108d 100644 --- a/src/main.zig +++ b/src/main.zig @@ -1,9 +1,12 @@ const std = @import("std"); const isa = @import("riscv/isa.zig"); const Fdt = @import("Fdt.zig"); +const Console = @import("drivers/Console.zig"); const UART_BASE: usize = 0x10000000; +var console: Console = undefined; + fn uart_put(c: u8) void { const uart: *volatile u8 = @ptrFromInt(UART_BASE); uart.* = c; @@ -24,22 +27,14 @@ export fn _start() linksection(".text.init") callconv(.naked) noreturn { export fn kmain(hartid: u64, fdt_ptr: *const anyopaque) callconv(.c) noreturn { _ = hartid; - print("Booting hydra...\n"); const fdt = Fdt.parse(fdt_ptr) catch { - print("Failed to parse FDT\n"); while (true) asm volatile ("wfi"); }; - if (fdt.findFirstCompatible("ns16550a")) |console| { - var iter = console.getProperty("reg").?.asU32Array(); - const start = iter.next(); - const end = iter.next(); - _ = end; - if (start == 0x100) { - print("Found console\n"); - } - } + console = Console.init(fdt).?; + + console.print("booting hydra...\n", .{}); while (true) { asm volatile ("wfi");