first commit
This commit is contained in:
commit
6f76f9dd9d
9 changed files with 1071 additions and 0 deletions
692
src/Fdt.zig
Normal file
692
src/Fdt.zig
Normal file
|
|
@ -0,0 +1,692 @@
|
|||
const std = @import("std");
|
||||
const Fdt = @This();
|
||||
|
||||
pub const MAGIC = 0xd00dfeed;
|
||||
|
||||
pub const Token = enum(u32) {
|
||||
begin_node = 0x01,
|
||||
end_node = 0x02,
|
||||
prop = 0x03,
|
||||
nop = 0x04,
|
||||
end = 0x09,
|
||||
};
|
||||
|
||||
/// Raw FDT header as it appears in memory (big-endian)
|
||||
pub const RawHeader = extern struct {
|
||||
magic: u32,
|
||||
totalsize: u32,
|
||||
off_dt_struct: u32,
|
||||
off_dt_strings: u32,
|
||||
off_mem_rsvmap: u32,
|
||||
version: u32,
|
||||
last_comp_version: u32,
|
||||
boot_cpuid_phys: u32,
|
||||
size_dt_strings: u32,
|
||||
size_dt_struct: u32,
|
||||
};
|
||||
|
||||
/// Memory reservation entry
|
||||
pub const ReserveEntry = extern struct {
|
||||
address: u64,
|
||||
size: u64,
|
||||
|
||||
pub fn toNative(self: *const ReserveEntry) struct { address: u64, size: u64 } {
|
||||
return .{
|
||||
.address = std.mem.bigToNative(u64, self.address),
|
||||
.size = std.mem.bigToNative(u64, self.size),
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
/// Parsed property data
|
||||
pub const Property = struct {
|
||||
name: []const u8,
|
||||
data: []const u8,
|
||||
|
||||
pub fn asU32(self: Property) ?u32 {
|
||||
if (self.data.len != 4) return null;
|
||||
return std.mem.bigToNative(u32, @as(*const u32, @alignCast(@ptrCast(self.data.ptr))).*);
|
||||
}
|
||||
|
||||
pub fn asU64(self: Property) ?u64 {
|
||||
if (self.data.len != 8) return null;
|
||||
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| {
|
||||
if (c == 0) return self.data[0..i];
|
||||
}
|
||||
return self.data;
|
||||
}
|
||||
|
||||
pub fn asStringList(self: Property) StringListIterator {
|
||||
return .{ .data = self.data };
|
||||
}
|
||||
|
||||
pub fn asU32Array(self: Property) U32ArrayIterator {
|
||||
return .{ .data = self.data };
|
||||
}
|
||||
|
||||
pub fn asU64Array(self: Property) U64ArrayIterator {
|
||||
return .{ .data = self.data };
|
||||
}
|
||||
|
||||
pub fn isEmpty(self: Property) bool {
|
||||
return self.data.len == 0;
|
||||
}
|
||||
};
|
||||
|
||||
pub const StringListIterator = struct {
|
||||
data: []const u8,
|
||||
pos: usize = 0,
|
||||
|
||||
pub fn next(self: *StringListIterator) ?[]const u8 {
|
||||
if (self.pos >= self.data.len) return null;
|
||||
|
||||
const start = self.pos;
|
||||
while (self.pos < self.data.len and self.data[self.pos] != 0) {
|
||||
self.pos += 1;
|
||||
}
|
||||
const end = self.pos;
|
||||
self.pos += 1; // skip null terminator
|
||||
|
||||
if (start == end) return null;
|
||||
return self.data[start..end];
|
||||
}
|
||||
};
|
||||
|
||||
pub const U32ArrayIterator = struct {
|
||||
data: []const u8,
|
||||
pos: usize = 0,
|
||||
|
||||
pub fn next(self: *U32ArrayIterator) ?u32 {
|
||||
if (self.pos + 4 > self.data.len) return null;
|
||||
const value = std.mem.bigToNative(u32, @as(*const u32, @alignCast(@ptrCast(self.data.ptr + self.pos))).*);
|
||||
self.pos += 4;
|
||||
return value;
|
||||
}
|
||||
|
||||
pub fn count(self: U32ArrayIterator) usize {
|
||||
return self.data.len / 4;
|
||||
}
|
||||
};
|
||||
|
||||
pub const U64ArrayIterator = struct {
|
||||
data: []const u8,
|
||||
pos: usize = 0,
|
||||
|
||||
pub fn next(self: *U64ArrayIterator) ?u64 {
|
||||
if (self.pos + 8 > self.data.len) return null;
|
||||
const value = std.mem.bigToNative(u64, @as(*const u64, @alignCast(@ptrCast(self.data.ptr + self.pos))).*);
|
||||
self.pos += 8;
|
||||
return value;
|
||||
}
|
||||
|
||||
pub fn count(self: U64ArrayIterator) usize {
|
||||
return self.data.len / 8;
|
||||
}
|
||||
};
|
||||
|
||||
/// 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 ..];
|
||||
}
|
||||
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];
|
||||
}
|
||||
return self.name;
|
||||
}
|
||||
|
||||
/// Iterate over all properties of this node
|
||||
pub fn properties(self: Node) PropertyIterator {
|
||||
return .{
|
||||
.fdt = self.fdt,
|
||||
.pos = self.struct_offset,
|
||||
};
|
||||
}
|
||||
|
||||
/// Get a specific property by name
|
||||
pub fn getProperty(self: Node, name: []const u8) ?Property {
|
||||
var iter = self.properties();
|
||||
while (iter.next()) |prop| {
|
||||
if (std.mem.eql(u8, prop.name, name)) {
|
||||
return prop;
|
||||
}
|
||||
}
|
||||
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;
|
||||
while (pos < self.fdt.struct_block.len) {
|
||||
const token = self.fdt.readToken(pos) orelse break;
|
||||
pos += 4;
|
||||
|
||||
switch (token) {
|
||||
.prop => {
|
||||
const len = self.fdt.readU32(pos) orelse break;
|
||||
pos += 8; // skip len and nameoff
|
||||
pos += alignUp(len, 4);
|
||||
},
|
||||
.nop => {},
|
||||
.begin_node, .end_node, .end => {
|
||||
pos -= 4; // back up to re-read this token
|
||||
break;
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
return .{
|
||||
.fdt = self.fdt,
|
||||
.pos = pos,
|
||||
.depth = 0,
|
||||
};
|
||||
}
|
||||
|
||||
/// Find a child node by name
|
||||
pub fn getChild(self: Node, name: []const u8) ?Node {
|
||||
var iter = self.children();
|
||||
while (iter.next()) |child| {
|
||||
if (std.mem.eql(u8, child.name, name)) {
|
||||
return child;
|
||||
}
|
||||
}
|
||||
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| {
|
||||
if (std.mem.eql(u8, c, compat)) return true;
|
||||
}
|
||||
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| {
|
||||
return prop.asU32();
|
||||
}
|
||||
if (self.getProperty("linux,phandle")) |prop| {
|
||||
return prop.asU32();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
pub const PropertyIterator = struct {
|
||||
fdt: *const Fdt,
|
||||
pos: usize,
|
||||
|
||||
pub fn next(self: *PropertyIterator) ?Property {
|
||||
while (self.pos < self.fdt.struct_block.len) {
|
||||
const token = self.fdt.readToken(self.pos) orelse return null;
|
||||
self.pos += 4;
|
||||
|
||||
switch (token) {
|
||||
.prop => {
|
||||
const len = self.fdt.readU32(self.pos) orelse return null;
|
||||
const nameoff = self.fdt.readU32(self.pos + 4) orelse return null;
|
||||
self.pos += 8;
|
||||
|
||||
const name = self.fdt.getString(nameoff) orelse return null;
|
||||
const data = if (len > 0 and self.pos + len <= self.fdt.struct_block.len)
|
||||
self.fdt.struct_block[self.pos..][0..len]
|
||||
else
|
||||
&[_]u8{};
|
||||
|
||||
self.pos += alignUp(len, 4);
|
||||
|
||||
return Property{
|
||||
.name = name,
|
||||
.data = data,
|
||||
};
|
||||
},
|
||||
.nop => continue,
|
||||
.begin_node, .end_node, .end => {
|
||||
self.pos -= 4;
|
||||
return null;
|
||||
},
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
pub const ChildIterator = struct {
|
||||
fdt: *const Fdt,
|
||||
pos: usize,
|
||||
depth: i32,
|
||||
|
||||
pub fn next(self: *ChildIterator) ?Node {
|
||||
while (self.pos < self.fdt.struct_block.len) {
|
||||
const token = self.fdt.readToken(self.pos) orelse return null;
|
||||
self.pos += 4;
|
||||
|
||||
switch (token) {
|
||||
.begin_node => {
|
||||
const name_start = self.pos;
|
||||
// Find end of name
|
||||
while (self.pos < self.fdt.struct_block.len and self.fdt.struct_block[self.pos] != 0) {
|
||||
self.pos += 1;
|
||||
}
|
||||
const name = self.fdt.struct_block[name_start..self.pos];
|
||||
self.pos += 1; // skip null
|
||||
self.pos = alignUp(self.pos, 4);
|
||||
|
||||
if (self.depth == 0) {
|
||||
self.depth = 1;
|
||||
return Node{
|
||||
.name = name,
|
||||
.fdt = self.fdt,
|
||||
.struct_offset = self.pos,
|
||||
};
|
||||
} else {
|
||||
self.depth += 1;
|
||||
}
|
||||
},
|
||||
.end_node => {
|
||||
self.depth -= 1;
|
||||
if (self.depth < 0) return null;
|
||||
},
|
||||
.prop => {
|
||||
const len = self.fdt.readU32(self.pos) orelse return null;
|
||||
self.pos += 8;
|
||||
self.pos += alignUp(len, 4);
|
||||
},
|
||||
.nop => {},
|
||||
.end => return null,
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
/// Iterator for all nodes in the tree (depth-first)
|
||||
pub const NodeIterator = struct {
|
||||
fdt: *const Fdt,
|
||||
pos: usize,
|
||||
depth: usize = 0,
|
||||
include_root: bool = true,
|
||||
root_seen: bool = false,
|
||||
|
||||
pub fn next(self: *NodeIterator) ?Node {
|
||||
while (self.pos < self.fdt.struct_block.len) {
|
||||
const token = self.fdt.readToken(self.pos) orelse return null;
|
||||
self.pos += 4;
|
||||
|
||||
switch (token) {
|
||||
.begin_node => {
|
||||
const name_start = self.pos;
|
||||
while (self.pos < self.fdt.struct_block.len and self.fdt.struct_block[self.pos] != 0) {
|
||||
self.pos += 1;
|
||||
}
|
||||
const name = self.fdt.struct_block[name_start..self.pos];
|
||||
self.pos += 1;
|
||||
self.pos = alignUp(self.pos, 4);
|
||||
self.depth += 1;
|
||||
|
||||
// Skip root node if requested
|
||||
if (!self.root_seen) {
|
||||
self.root_seen = true;
|
||||
if (!self.include_root) continue;
|
||||
}
|
||||
|
||||
return Node{
|
||||
.name = name,
|
||||
.fdt = self.fdt,
|
||||
.struct_offset = self.pos,
|
||||
};
|
||||
},
|
||||
.end_node => {
|
||||
if (self.depth > 0) self.depth -= 1;
|
||||
},
|
||||
.prop => {
|
||||
const len = self.fdt.readU32(self.pos) orelse return null;
|
||||
self.pos += 8;
|
||||
self.pos += alignUp(len, 4);
|
||||
},
|
||||
.nop => {},
|
||||
.end => return null,
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
pub const RegIterator = struct {
|
||||
data: []const u8,
|
||||
address_cells: u32,
|
||||
size_cells: u32,
|
||||
pos: usize,
|
||||
|
||||
pub const Entry = struct {
|
||||
address: u64,
|
||||
size: u64,
|
||||
};
|
||||
|
||||
pub fn next(self: *RegIterator) ?Entry {
|
||||
const entry_size = (self.address_cells + self.size_cells) * 4;
|
||||
if (self.pos + entry_size > self.data.len) return null;
|
||||
|
||||
var address: u64 = 0;
|
||||
var i: usize = 0;
|
||||
while (i < self.address_cells) : (i += 1) {
|
||||
const val = std.mem.bigToNative(u32, @as(*const u32, @alignCast(@ptrCast(self.data.ptr + self.pos + i * 4))).*);
|
||||
address = (address << 32) | val;
|
||||
}
|
||||
|
||||
var size: u64 = 0;
|
||||
i = 0;
|
||||
while (i < self.size_cells) : (i += 1) {
|
||||
const val = std.mem.bigToNative(u32, @as(*const u32, @alignCast(@ptrCast(self.data.ptr + self.pos + (self.address_cells + i) * 4))).*);
|
||||
size = (size << 32) | val;
|
||||
}
|
||||
|
||||
self.pos += entry_size;
|
||||
|
||||
return Entry{
|
||||
.address = address,
|
||||
.size = size,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
base: [*]const u8,
|
||||
totalsize: u32,
|
||||
version: u32,
|
||||
last_comp_version: u32,
|
||||
boot_cpuid_phys: u32,
|
||||
struct_block: []const u8,
|
||||
strings_block: []const u8,
|
||||
mem_rsv_block: []const u8,
|
||||
|
||||
pub const Error = error{
|
||||
InvalidMagic,
|
||||
UnsupportedVersion,
|
||||
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));
|
||||
|
||||
// Validate magic
|
||||
const magic = std.mem.bigToNative(u32, header.magic);
|
||||
if (magic != MAGIC) {
|
||||
return Error.InvalidMagic;
|
||||
}
|
||||
|
||||
const version = std.mem.bigToNative(u32, header.version);
|
||||
const last_comp_version = std.mem.bigToNative(u32, header.last_comp_version);
|
||||
|
||||
// We support FDT version 17 (current standard)
|
||||
if (last_comp_version > 17) {
|
||||
return Error.UnsupportedVersion;
|
||||
}
|
||||
|
||||
const totalsize = std.mem.bigToNative(u32, header.totalsize);
|
||||
const off_dt_struct = std.mem.bigToNative(u32, header.off_dt_struct);
|
||||
const size_dt_struct = std.mem.bigToNative(u32, header.size_dt_struct);
|
||||
const off_dt_strings = std.mem.bigToNative(u32, header.off_dt_strings);
|
||||
const size_dt_strings = std.mem.bigToNative(u32, header.size_dt_strings);
|
||||
const off_mem_rsvmap = std.mem.bigToNative(u32, header.off_mem_rsvmap);
|
||||
|
||||
return Fdt{
|
||||
.base = base,
|
||||
.totalsize = totalsize,
|
||||
.version = version,
|
||||
.last_comp_version = last_comp_version,
|
||||
.boot_cpuid_phys = std.mem.bigToNative(u32, header.boot_cpuid_phys),
|
||||
.struct_block = base[off_dt_struct..][0..size_dt_struct],
|
||||
.strings_block = base[off_dt_strings..][0..size_dt_strings],
|
||||
.mem_rsv_block = base[off_mem_rsvmap..totalsize],
|
||||
};
|
||||
}
|
||||
|
||||
/// Get the root node
|
||||
pub fn root(self: *const Fdt) ?Node {
|
||||
if (self.struct_block.len < 4) return null;
|
||||
|
||||
const token = self.readToken(0) orelse return null;
|
||||
if (token != .begin_node) return null;
|
||||
|
||||
var pos: usize = 4;
|
||||
// Skip root node name (usually empty)
|
||||
while (pos < self.struct_block.len and self.struct_block[pos] != 0) {
|
||||
pos += 1;
|
||||
}
|
||||
pos += 1;
|
||||
pos = alignUp(pos, 4);
|
||||
|
||||
return Node{
|
||||
.name = "",
|
||||
.fdt = self,
|
||||
.struct_offset = pos,
|
||||
};
|
||||
}
|
||||
|
||||
/// Iterate over all nodes in the tree
|
||||
pub fn nodes(self: *const Fdt) NodeIterator {
|
||||
return .{
|
||||
.fdt = self,
|
||||
.pos = 0,
|
||||
.include_root = true,
|
||||
};
|
||||
}
|
||||
|
||||
/// Iterate over all nodes except the root
|
||||
pub fn nodesWithoutRoot(self: *const Fdt) NodeIterator {
|
||||
return .{
|
||||
.fdt = self,
|
||||
.pos = 0,
|
||||
.include_root = false,
|
||||
};
|
||||
}
|
||||
|
||||
/// 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;
|
||||
|
||||
var current = self.root() orelse return null;
|
||||
|
||||
// Handle root path
|
||||
if (path.len == 1) return current;
|
||||
|
||||
// Parse path components
|
||||
var remaining = path[1..]; // skip leading '/'
|
||||
while (remaining.len > 0) {
|
||||
// Find next component
|
||||
var end: usize = 0;
|
||||
while (end < remaining.len and remaining[end] != '/') {
|
||||
end += 1;
|
||||
}
|
||||
|
||||
const component = remaining[0..end];
|
||||
if (component.len == 0) break;
|
||||
|
||||
// Find child with this name
|
||||
current = current.getChild(component) orelse return null;
|
||||
|
||||
// Move past this component and any '/'
|
||||
remaining = if (end < remaining.len) remaining[end + 1 ..] else &[_]u8{};
|
||||
}
|
||||
|
||||
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| {
|
||||
if (node.phandle()) |ph| {
|
||||
if (ph == handle) return 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(),
|
||||
.compat = compat,
|
||||
};
|
||||
}
|
||||
|
||||
/// 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,
|
||||
.pos = 0,
|
||||
};
|
||||
}
|
||||
|
||||
/// 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");
|
||||
}
|
||||
|
||||
// Internal helpers
|
||||
fn readToken(self: *const Fdt, offset: usize) ?Token {
|
||||
if (offset + 4 > self.struct_block.len) return null;
|
||||
const val = std.mem.bigToNative(u32, @as(*const u32, @alignCast(@ptrCast(self.struct_block.ptr + offset))).*);
|
||||
return @as(Token, @enumFromInt(val));
|
||||
}
|
||||
|
||||
fn readU32(self: *const Fdt, offset: usize) ?u32 {
|
||||
if (offset + 4 > self.struct_block.len) return null;
|
||||
return std.mem.bigToNative(u32, @as(*const u32, @alignCast(@ptrCast(self.struct_block.ptr + offset))).*);
|
||||
}
|
||||
|
||||
fn getString(self: *const Fdt, offset: u32) ?[]const u8 {
|
||||
if (offset >= self.strings_block.len) return null;
|
||||
const start = self.strings_block[offset..];
|
||||
for (start, 0..) |c, i| {
|
||||
if (c == 0) return start[0..i];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
pub const CompatibleIterator = struct {
|
||||
inner: NodeIterator,
|
||||
compat: []const u8,
|
||||
|
||||
pub fn next(self: *CompatibleIterator) ?Node {
|
||||
while (self.inner.next()) |node| {
|
||||
if (node.isCompatible(self.compat)) {
|
||||
return node;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
pub const MemoryReservationIterator = struct {
|
||||
data: []const u8,
|
||||
pos: usize,
|
||||
|
||||
pub fn next(self: *MemoryReservationIterator) ?struct { address: u64, size: u64 } {
|
||||
if (self.pos + 16 > self.data.len) return null;
|
||||
|
||||
const entry: *const ReserveEntry = @alignCast(@ptrCast(self.data.ptr + self.pos));
|
||||
const result = entry.toNative();
|
||||
self.pos += 16;
|
||||
|
||||
// End marker is all zeros
|
||||
if (result.address == 0 and result.size == 0) return null;
|
||||
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
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,
|
||||
.address_cells = address_cells,
|
||||
.size_cells = size_cells,
|
||||
.pos = 0,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
47
src/main.zig
Normal file
47
src/main.zig
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
const std = @import("std");
|
||||
const isa = @import("riscv/isa.zig");
|
||||
const Fdt = @import("Fdt.zig");
|
||||
|
||||
const UART_BASE: usize = 0x10000000;
|
||||
|
||||
fn uart_put(c: u8) void {
|
||||
const uart: *volatile u8 = @ptrFromInt(UART_BASE);
|
||||
uart.* = c;
|
||||
}
|
||||
|
||||
fn print(s: []const u8) void {
|
||||
for (s) |c| {
|
||||
uart_put(c);
|
||||
}
|
||||
}
|
||||
|
||||
export fn _start() linksection(".text.init") callconv(.naked) noreturn {
|
||||
asm volatile (
|
||||
\\li sp, 0x88000000
|
||||
\\tail kmain
|
||||
);
|
||||
}
|
||||
|
||||
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");
|
||||
}
|
||||
}
|
||||
|
||||
while (true) {
|
||||
asm volatile ("wfi");
|
||||
}
|
||||
}
|
||||
26
src/riscv/isa.zig
Normal file
26
src/riscv/isa.zig
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
pub const Satp = packed struct {
|
||||
pub const Mode = enum(u4) {
|
||||
bare = 0,
|
||||
sv38 = 8,
|
||||
sv48 = 9,
|
||||
sv57 = 10,
|
||||
sv64 = 11,
|
||||
};
|
||||
|
||||
ppn: u44 = 0,
|
||||
asid: u16 = 0,
|
||||
mode: Mode = .bare,
|
||||
};
|
||||
|
||||
pub inline fn write_satp(satp: Satp) void {
|
||||
asm volatile ("csrw satp, %[val]"
|
||||
:
|
||||
: [val] "r" (satp),
|
||||
);
|
||||
}
|
||||
|
||||
pub inline fn read_satp() Satp {
|
||||
return asm ("csrr %[ret], satp"
|
||||
: [ret] "=r" (-> Satp),
|
||||
);
|
||||
}
|
||||
0
src/root.zig
Normal file
0
src/root.zig
Normal file
Loading…
Add table
Add a link
Reference in a new issue