add UART console
This commit is contained in:
parent
25ec14c4a1
commit
48421fc0ef
3 changed files with 55 additions and 48 deletions
38
src/Fdt.zig
38
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,
|
||||
|
|
|
|||
48
src/drivers/Console.zig
Normal file
48
src/drivers/Console.zig
Normal file
|
|
@ -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");
|
||||
};
|
||||
}
|
||||
17
src/main.zig
17
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");
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue