Basic thread switching

This commit is contained in:
2025-03-14 22:16:36 +02:00
parent bebdb21c4e
commit aeb5950e56
16 changed files with 633 additions and 76 deletions
+14 -3
View File
@@ -9,9 +9,21 @@ fn insertFakeLinuxImageHeader(step: *std.Build.Step, opts: std.Build.Step.MakeOp
var ehdr: elf.Ehdr = undefined;
var file = try std.fs.cwd().openFile("zig-out/bin/kernel", .{ .mode = .read_write });
const stat = try file.stat();
_ = try file.readAll(std.mem.asBytes(&ehdr));
// Figure out total image size
var imageAddrMax: u64 = 0;
for (0..ehdr.e_phnum) |i| {
var phdr: elf.Phdr = undefined;
_ = try file.preadAll(std.mem.asBytes(&phdr), ehdr.e_phoff + i * ehdr.e_phentsize);
const end = (phdr.p_vaddr + phdr.p_memsz + 0xFFF) & ~@as(u64, 0xFFF);
if (phdr.p_type == elf.PT_LOAD and end > imageAddrMax) {
imageAddrMax = end;
}
}
for (0..ehdr.e_phnum) |i| {
var phdr: elf.Phdr = undefined;
var data: [64]u8 = undefined;
@@ -25,8 +37,7 @@ fn insertFakeLinuxImageHeader(step: *std.Build.Step, opts: std.Build.Step.MakeOp
_ = try file.preadAll(&data, phdr.p_offset);
if (std.mem.eql(u8, RISCV_MAGIC1, data[48..56]) and std.mem.eql(u8, RISCV_MAGIC2, data[56..60])) {
const size: u64 = stat.size;
try file.pwriteAll(std.mem.asBytes(&size), phdr.p_offset + 16);
try file.pwriteAll(std.mem.asBytes(&imageAddrMax), phdr.p_offset + 16);
break;
}
}
+5
View File
@@ -3,6 +3,8 @@ ENTRY(__rv64_entry);
SECTIONS {
. = 0x0;
PROVIDE(__kernel_start = .);
.text : ALIGN(4K) {
*(.text.entry*)
*(.text*)
@@ -36,4 +38,7 @@ SECTIONS {
. = ALIGN(4K);
PROVIDE(__bss_end = .);
}
. = ALIGN(4K);
PROVIDE(__kernel_end = .);
}
+7 -4
View File
@@ -1,7 +1,10 @@
gdb-remote localhost:1234
target modules add -s kernel zig-out/bin/kernel
# target modules load -f zig-out/bin/kernel -s 0x80200000
target modules load -f zig-out/bin/kernel -s 0x200200000
target modules add zig-out/bin/kernel
target modules load -f zig-out/bin/kernel -s 0xFFFFFFF000200000
breakpoint set -n arch.riscv64.boot.rv64BspEntryLower
breakpoint set -H -a 0x80200000
process continue
breakpoint set -H -n arch.riscv64.boot.rv64BspEntryLower
# process continue
+50
View File
@@ -1,11 +1,61 @@
const boot = @import("riscv64/boot.zig");
const regs = @import("riscv64/regs.zig");
const thread = @import("../thread.zig");
const std = @import("std");
const builtin = @import("builtin");
const Arena = @import("../arena.zig").Arena;
export const _ = boot.rv64BspLowerEntry;
extern fn __rv64_enter_task(cx: *arch().Context) callconv(.C) noreturn;
extern fn __rv64_switch_task(dcx: *arch().Context, scx: *arch().Context) callconv(.C) void;
extern fn __rv64_task_enter_kernel() callconv(.C) noreturn;
pub fn arch() type {
return struct {
pub const Context = extern struct {
const STACK_SIZE: usize = 8192;
// Has to be exactly at offset 0x00, used in assembly
kstack: thread.KStack(STACK_SIZE),
pub fn kernel(a: *Arena, pc: usize, arg: usize) @This() {
var ks = thread.KStack(STACK_SIZE).create(a);
const entry = @intFromPtr(&__rv64_task_enter_kernel);
ks.push(pc);
ks.push(arg);
ks.push(0); // x8/s0/fp
ks.push(0); // x9/s1
ks.push(0); // x18/s2
ks.push(0); // x19/s3
ks.push(0); // x20/s4
ks.push(0); // x21/s5
ks.push(0); // x22/s6
ks.push(0); // x23/s7
ks.push(0); // x24/s8
ks.push(0); // x25/s9
ks.push(0); // x26/s10
ks.push(0); // x27/s11
ks.push(0); // x4/gp
ks.push(entry); // x1/ra return address
return .{
.kstack = ks
};
}
pub fn enter(self: *@This()) noreturn {
__rv64_enter_task(self);
}
pub fn switchFrom(self: *@This(), from: *@This()) void {
__rv64_switch_task(self, from);
}
};
pub inline fn halt() noreturn {
while (true) {
_ = setInterruptMask(true);
+37 -4
View File
@@ -4,6 +4,8 @@ const kernel = @import("../../kernel.zig");
const vmm = @import("vmm.zig");
const regs = @import("regs.zig");
const dtb = @import("../../util/dtb.zig");
const physMemory = @import("../../mem/phys.zig");
const arena = @import("../../arena.zig");
const log = debug.log;
const arch = kernel.arch;
@@ -11,6 +13,8 @@ const arch = kernel.arch;
extern const __rela_start: u8;
extern const __rela_end: u8;
extern const __rv64_bsp_stack_top: u8;
extern const __kernel_start: u8;
extern const __kernel_end: u8;
var gDtbAddress: usize = 0;
var gBspHartId: usize = 0;
@@ -31,6 +35,38 @@ pub export fn rv64RelocateKernel(imageBase: usize, relaStart: usize, relaEnd: us
}
}
fn setupMemoryFromFdt(realAddress: usize) void {
const kernelStart = @intFromPtr(&__kernel_start);
const kernelEnd = @intFromPtr(&__kernel_end);
var cells: [2]u64 = undefined;
const fdt = dtb.Fdt.fromPhysicalAddress(.{ .raw = gDtbAddress }) catch |err| {
log.panic("Cannot initialize raw DTB: {}", .{err});
};
var memoryRegions = fdt.memoryRegionIterator();
while (memoryRegions.next()) |region| {
physMemory.addMemoryRegion(region.name, region.base, region.size);
}
const reservedRegions = fdt.rootNode().child("reserved-memory");
if (reservedRegions) |resv| {
var children = resv.children();
while (children.next()) |region| {
if (region.property("reg")) |reg| {
// TODO #address-cells, #size-cells
if (reg.readCells(0, &cells, &.{ 2, 2 })) {
physMemory.addReservedRegion(region.name, cells[0], cells[1]);
}
}
}
}
physMemory.addReservedRegion("kernel", kernelStart - (vmm.KERNEL_VIRTUAL_BASE + vmm.L1.offset(realAddress)) + realAddress, kernelEnd - kernelStart);
physMemory.addReservedRegion("fdt", gDtbAddress, vmm.L3.align_up(fdt.bytes.len));
}
fn bspUpperEntry(realAddress: usize, unused: usize) callconv(.C) noreturn {
_ = unused;
@@ -50,10 +86,7 @@ fn bspUpperEntry(realAddress: usize, unused: usize) callconv(.C) noreturn {
kernel.mem.PhysicalAddress.gVirtualizeSize = vmm.virtualizeRange();
// Setup physical memory management
const fdt = dtb.Fdt.fromPhysicalAddress(.{ .raw = gDtbAddress }) catch |err| {
log.panic("Cannot initialize raw DTB: {}", .{ err });
};
fdt.dump();
setupMemoryFromFdt(realAddress);
kernel.kernel_main();
}
+84 -17
View File
@@ -10,31 +10,30 @@
.pushsection .text.entry
.type __rv64_entry, @function
// .option push
// .option rvc
.option push
.option rvc
__rv64_entry:
// .ascii "MZ" // Magic 0
// j __rv64_real_entry // Jump to real entry (if entered by non-Linux bootloader)
// .long 0
// .quad 0x200000 // Offset from RAM start
// #.quad 0 // Image size (filled by build.zig)
// .quad 0x1122334455667788 // Image size (filled by build.zig)
// .quad 0 // Kernel flags
// .long 0x2 // Header version
// .long 0
// .quad 0
// .ascii "RISCV\x00\x00\x00" // Magic 1
// .ascii "RSC\x05" // Magic 2
// .long 0
// .option pop
.ascii "MZ" // Magic 0
j __rv64_real_entry // Jump to real entry (if entered by non-Linux bootloader)
.long 0
.quad 0x200000 // Offset from RAM start
.quad 0 // Image size (filled by build.zig)
.quad 0 // Kernel flags
.long 0x2 // Header version
.long 0
.quad 0
.ascii "RISCV\x00\x00\x00" // Magic 1
.ascii "RSC\x05" // Magic 2
.long 0
.option pop
.option push
.option norvc
__rv64_real_entry:
// a0 - bootstrap HART ID
// a1 - device tree blob physical address
auipc s0, 0 // s0 = real PC (also a real load address/offset)
addi s0, s0, -0x40 // Subtract header size
mv s1, a0
mv s2, a1
@@ -90,3 +89,71 @@ __rv64_bsp_stack_bottom:
.skip 65536
__rv64_bsp_stack_top:
.popsection
.pushsection .text
.option push
.option norvc
.global __rv64_enter_task
.global __rv64_switch_task
.global __rv64_task_enter_kernel
.macro LOAD_TASK_STATE
ld ra, 0 * 8(sp)
ld gp, 1 * 8(sp)
ld s11, 2 * 8(sp)
ld s10, 3 * 8(sp)
ld s9, 4 * 8(sp)
ld s8, 5 * 8(sp)
ld s7, 6 * 8(sp)
ld s6, 7 * 8(sp)
ld s5, 8 * 8(sp)
ld s4, 9 * 8(sp)
ld s3, 10 * 8(sp)
ld s2, 11 * 8(sp)
ld s1, 12 * 8(sp)
ld s0, 13 * 8(sp)
addi sp, sp, 14 * 8
.endm
.macro SAVE_TASK_STATE
addi sp, sp, -(14 * 8)
sd ra, 0 * 8(sp)
sd gp, 1 * 8(sp)
sd s11, 2 * 8(sp)
sd s10, 3 * 8(sp)
sd s9, 4 * 8(sp)
sd s8, 5 * 8(sp)
sd s7, 6 * 8(sp)
sd s6, 7 * 8(sp)
sd s5, 8 * 8(sp)
sd s4, 9 * 8(sp)
sd s3, 10 * 8(sp)
sd s2, 11 * 8(sp)
sd s1, 12 * 8(sp)
sd s0, 13 * 8(sp)
.endm
__rv64_task_enter_kernel:
ld a0, (sp) // argument
ld ra, 8(sp) // entry
addi sp, sp, 16
// TODO S-mode -> S-mode return via sret
ret
__rv64_switch_task:
// a0 - new context
// a1 - old context
SAVE_TASK_STATE
sd sp, (a1)
__rv64_enter_task:
// a0 -- new context
ld sp, (a0)
LOAD_TASK_STATE
ret
.option pop
.popsection
+30
View File
@@ -0,0 +1,30 @@
const physMemory = @import("mem/phys.zig");
const log = @import("debug.zig").log;
const mem = @import("mem.zig");
pub const Arena = struct {
physBase: mem.PhysicalAddress,
capacity: usize,
len: usize,
pub fn setup(cap: usize) ?Arena {
const base = physMemory.allocateChunk(cap / 0x1000) orelse return null;
return .{
.physBase = .{ .raw = base },
.capacity = cap,
.len = 0,
};
}
pub fn create(self: *@This(), comptime T: type) *T {
if (self.len + @sizeOf(T) > self.capacity) {
log.panic("Out of memory. Cannot allocate {} bytes", .{ @sizeOf(T) });
}
const v = self.physBase.add(self.len).virtualize();
const ptr = @as(*T, @ptrFromInt(v));
self.len += @sizeOf(T);
return ptr;
}
};
+9 -5
View File
@@ -28,26 +28,30 @@ pub const log = struct {
}
pub inline fn info(comptime format: []const u8, args: anytype) void {
write(.info, format, args);
writeln(.info, format, args);
}
pub inline fn debug(comptime format: []const u8, args: anytype) void {
write(.debug, format, args);
writeln(.debug, format, args);
}
pub inline fn warn(comptime format: []const u8, args: anytype) void {
write(.warn, format, args);
writeln(.warn, format, args);
}
pub inline fn err(comptime format: []const u8, args: anytype) void {
write(.err, format, args);
writeln(.err, format, args);
}
pub fn writeRaw(data: []const u8) void {
_ = writeWrapperFn(0, data) catch return;
}
pub fn write(comptime level: Level, comptime format: []const u8, args: anytype) void {
pub fn write(comptime format: []const u8, args: anytype) void {
writer.print(format, args) catch return;
}
pub fn writeln(comptime level: Level, comptime format: []const u8, args: anytype) void {
const prefix = comptime logPrefix(level);
const suffix = comptime logSuffix(level);
writer.print(prefix ++ format ++ suffix ++ "\r\n", args) catch return;
+27 -1
View File
@@ -2,13 +2,39 @@
pub const arch = @import("arch.zig").arch();
pub const mem = @import("mem.zig");
pub const debug = @import("debug.zig");
pub const arena = @import("arena.zig");
pub const thread = @import("thread.zig");
pub const log = debug.log;
pub const vmm = mem.vmm;
const std = @import("std");
fn f0(arg: usize) callconv(.C) noreturn {
log.write("\x1B[2J", .{});
var c: usize = 0;
while (true) {
f1(arg, c);
c += 1;
thread.yield();
}
}
noinline fn f1(arg: usize, c: usize) void {
log.write("\x1B[1;{}H{}", .{ arg + 1, (c + arg) % 10 });
}
pub export fn kernel_main() callconv(.C) noreturn {
log.info("Hello", .{});
var a = arena.Arena.setup(256 * 0x1000) orelse @panic("Could not setup kernel arena");
const pc = @intFromPtr(&f0);
for (0..8) |i| {
const t = thread.Thread.create(&a, pc, i);
thread.addThread(t);
}
thread.enter();
arch.halt();
}
+3 -1
View File
@@ -1,4 +1,6 @@
pub const translationLevel = @import("mem/vmm.zig").translationLevel;
pub const vmm = @import("mem/vmm.zig");
pub const translationLevel = vmm.translationLevel;
pub const PhysicalAddress = packed struct(u64) {
raw: u64,
+66
View File
@@ -0,0 +1,66 @@
const vec = @import("../util/vec.zig");
const log = @import("../debug.zig").log;
pub const MemoryRegion = struct {
name: []const u8,
base: u64,
size: u64,
pub fn contains(self: *const @This(), page: u64) bool {
return (page >= self.base) and (page - self.base < self.size);
}
};
var gMemoryRegions: vec.FixedVec(MemoryRegion, 16) = .{};
var gReservedRegions: vec.FixedVec(MemoryRegion, 16) = .{};
fn isReserved(page: u64) bool {
for (gReservedRegions.asConstSlice()) |region| {
if (region.contains(page)) {
return true;
}
}
return false;
}
pub fn addMemoryRegion(name: []const u8, base: u64, size: u64) void {
log.info("Memory: '{s}', base 0x{x}, size 0x{x}", .{ name, base, size });
gMemoryRegions.push(.{
.name = name,
.base = base,
.size = size
});
}
pub fn addReservedRegion(name: []const u8, base: u64, size: u64) void {
log.info("Reserved: '{s}', base 0x{x}, size 0x{x}", .{ name, base, size });
gReservedRegions.push(.{
.name = name,
.base = base,
.size = size
});
}
pub fn allocateChunk(pageCount: usize) ?u64 {
for (gMemoryRegions.asConstSlice()) |region| {
var i: usize = 0;
while (i < region.size) {
var res = false;
for (0..pageCount) |j| {
if (isReserved(region.base + i + j * 0x1000)) {
res = true;
i += (j + 1) * 0x1000;
break;
}
}
if (res) {
continue;
}
const addr = region.base + i;
addReservedRegion("alloc", addr, pageCount * 0x1000);
return addr;
}
}
return null;
}
+87
View File
@@ -0,0 +1,87 @@
const arena = @import("arena.zig");
const arch = @import("kernel.zig").arch;
const log = @import("debug.zig").log;
pub const Thread = struct {
allocator: *arena.Arena,
archContext: arch.Context,
next: ?*Thread = null,
prev: ?*Thread = null,
pub fn create(a: *arena.Arena, pc: usize, sp: usize) *Thread {
const thread = a.create(Thread);
thread.* = .{
.allocator = a,
.archContext = arch.Context.kernel(a, pc, sp),
};
return thread;
}
pub fn enter(self: *@This()) noreturn {
self.archContext.enter();
}
pub fn switchFrom(self: *@This(), from: *@This()) void {
self.archContext.switchFrom(&from.archContext);
}
};
pub fn KStack(comptime SIZE: usize) type {
return extern struct {
// Has to be at exactly offset 0x00, used in assembly
sp: *usize,
data: *[SIZE]usize,
pub fn create(a: *arena.Arena) @This() {
const ptr = a.create([SIZE]usize);
return .{
.data = ptr,
.sp = @ptrFromInt(@intFromPtr(&ptr[0]) + SIZE * @sizeOf(usize))
};
}
pub fn push(self: *@This(), value: usize) void {
if (self.sp == &self.data[0]) {
@panic("KStack overflow");
}
self.sp = @ptrFromInt(@intFromPtr(self.sp) - @sizeOf(usize));
self.sp.* = value;
}
};
}
var gThreadHead: ?*Thread = null;
var gCurrent: ?*Thread = null;
pub fn addThread(t: *Thread) void {
if (gThreadHead) |gt| {
t.next = gt;
t.prev = gt.prev;
gt.prev.?.next = t;
gt.prev = t;
} else {
gThreadHead = t;
t.next = t;
t.prev = t;
}
}
pub fn enter() noreturn {
if (gThreadHead) |gt| {
gCurrent = gt;
gt.enter();
}
@panic("Unreachable");
}
pub fn yield() void {
const curr = gCurrent orelse @panic("No current thread");
const next = curr.next orelse @panic("No next thread");
if (curr != next) {
gCurrent = next;
next.switchFrom(curr);
}
}
+196 -32
View File
@@ -1,6 +1,5 @@
const mem = @import("../mem.zig");
const log = @import("../debug.zig").log;
const value = @import("value.zig");
const std = @import("std");
const fdt_header = extern struct {
@@ -37,6 +36,12 @@ const FdtTag = union(enum) {
end,
};
pub const FdtMemoryRegion = struct {
name: []const u8,
base: u64,
size: u64,
};
pub const FdtNode = struct {
fdt: *const Fdt,
off: usize,
@@ -46,12 +51,124 @@ pub const FdtNode = struct {
pub fn propIterator(self: *const @This()) FdtNodePropIterator {
return .{ .node = self, .tagIter = self.fdt.tagIteratorAt(self.off) };
}
pub fn children(self: *const @This()) FdtNodeIterator {
return .{ .tagIter = self.fdt.tagIteratorAt(self.off), .depth = self.depth + 1, .depthLower = self.depth };
}
pub fn child(self: *const @This(), name: []const u8) ?FdtNode {
var iter = self.children();
while (iter.next()) |c| {
if (std.mem.eql(u8, c.name, name)) {
return c;
}
}
return null;
}
pub fn property(self: *const @This(), name: []const u8) ?FdtNodeProp {
var propIter = self.propIterator();
while (propIter.next()) |prop| {
if (std.mem.eql(u8, name, prop.name)) {
return prop;
}
}
return null;
}
};
pub const FdtNodeProp = struct {
node: *const FdtNode,
name: []const u8,
value: []const u8,
pub inline fn getStringArray(self: *const @This()) FdtStringArrayIterator {
return .{ .prop = self };
}
pub inline fn lenU32(self: *const @This()) usize {
return self.value.len / @sizeOf(u32);
}
pub fn getU32(self: *const @This(), index: usize) ?u32 {
if (index >= self.lenU32()) {
return null;
}
return self.getU32Unchecked(index);
}
fn getU32Unchecked(self: *const @This(), index: usize) u32 {
return std.mem.bigToNative(u32, @as(*const u32, @ptrCast(@alignCast(&self.value[index * 4]))).*);
}
pub fn readCells(self: *const @This(), index: usize, output: []u64, sizes: []const usize) bool {
const count = @min(output.len, sizes.len);
const len = self.lenU32();
var total: usize = 0;
for (sizes[0..count]) |s| {
total += s;
}
var offset = index;
if (offset + total <= len) {
for (0..count) |i| {
switch (sizes[i]) {
1 => {
output[i] = self.getU32Unchecked(offset);
},
2 => {
output[i] = self.getU32Unchecked(offset + 1);
output[i] |= @as(u64, self.getU32Unchecked(offset)) << 32;
},
else => @panic("Invalid cell size"),
}
offset += sizes[i];
}
return true;
} else {
return false;
}
}
};
pub const FdtStringArrayIterator = struct {
prop: *const FdtNodeProp,
off: usize = 0,
pub inline fn next(self: *@This()) ?[]const u8 {
if (self.off >= self.prop.value.len) {
return null;
}
const ptr = @as([*c]const u8, @ptrCast(self.prop.value[self.off..]));
const len = std.mem.len(ptr);
const str = self.prop.value[self.off .. self.off + len];
self.off += len + 1;
return str;
}
};
pub const FdtMemoryRegionIterator = struct {
nodeIter: FdtNodeIterator,
cellSizes: [2]usize,
pub fn next(self: *FdtMemoryRegionIterator) ?FdtMemoryRegion {
while (self.nodeIter.next()) |node| {
if (std.mem.startsWith(u8, node.name, "memory@")) {
const reg = node.property("reg") orelse continue;
var cells: [2]u64 = undefined;
if (reg.readCells(0, &cells, &self.cellSizes)) {
return .{
.name = node.name,
.base = cells[0],
.size = cells[1],
};
}
}
}
return null;
}
};
pub const FdtNodePropIterator = struct {
@@ -69,11 +186,7 @@ pub const FdtNodePropIterator = struct {
.prop => |prop| {
if (self.depth == 0) {
const name = self.node.fdt.stringAt(prop.nameoff);
return .{
.node = self.node,
.value = prop.data,
.name = name
};
return .{ .node = self.node, .value = prop.data, .name = name };
}
},
.end_node => {
@@ -85,7 +198,7 @@ pub const FdtNodePropIterator = struct {
},
.end => {
return null;
}
},
}
}
@@ -96,8 +209,9 @@ pub const FdtNodePropIterator = struct {
pub const FdtNodeIterator = struct {
tagIter: FdtTagIterator,
depth: usize = 0,
depthLower: ?usize = null,
fn next(self: *FdtNodeIterator) ?FdtNode {
pub fn next(self: *FdtNodeIterator) ?FdtNode {
while (self.tagIter.next()) |tag| {
switch (tag) {
.begin_node => |name| {
@@ -111,6 +225,12 @@ pub const FdtNodeIterator = struct {
},
.end_node => {
self.depth -= 1;
if (self.depthLower) |lower| {
if (self.depth == lower) {
return null;
}
}
},
else => {},
}
@@ -129,7 +249,7 @@ pub const FdtTagIterator = struct {
return null;
}
const tag: fdt_op = @enumFromInt(value.u32FromBigEndian(@as(*const u32, @ptrCast(@alignCast(&self.raw[self.off]))).*));
const tag: fdt_op = @enumFromInt(std.mem.bigToNative(u32, @as(*const u32, @ptrCast(@alignCast(&self.raw[self.off]))).*));
self.off += @sizeOf(u32);
@@ -143,8 +263,8 @@ pub const FdtTagIterator = struct {
},
.FDT_PROP => {
const info: *const fdt_prop = @ptrCast(@alignCast(&self.raw[self.off]));
const nameoff = value.u32FromBigEndian(info.nameoff);
const len = value.u32FromBigEndian(info.len);
const nameoff = std.mem.bigToNative(u32, info.nameoff);
const len = std.mem.bigToNative(u32, info.len);
self.off += @sizeOf(fdt_prop);
const data = self.raw[self.off .. self.off + len];
self.off += (len + 3) & ~@as(usize, 3);
@@ -178,10 +298,10 @@ pub const Fdt = struct {
pub fn fromPhysicalAddress(phys: mem.PhysicalAddress) FdtError!@This() {
const virt = phys.virtualize();
const hdr = @as(*const fdt_header, @ptrFromInt(virt));
if (value.u32FromBigEndian(hdr.magic) != FDT_MAGIC) {
if (std.mem.bigToNative(u32, hdr.magic) != FDT_MAGIC) {
return error.invalid_magic;
}
const totalsize = value.u32FromBigEndian(hdr.totalsize);
const totalsize = std.mem.bigToNative(u32, hdr.totalsize);
const x = @as([*]const u8, @ptrFromInt(virt));
return .{ .bytes = x[0..totalsize] };
}
@@ -191,7 +311,7 @@ pub const Fdt = struct {
}
fn data(self: *const @This()) []const u8 {
const off = value.u32FromBigEndian(self.header().off_dt_struct);
const off = std.mem.bigToNative(u32, self.header().off_dt_struct);
return self.bytes[off..];
}
@@ -207,12 +327,30 @@ pub const Fdt = struct {
return .{ .tagIter = self.tagIterator() };
}
pub fn rootNode(self: *const @This()) FdtNode {
var nodeIter = self.nodeIterator();
while (nodeIter.next()) |node| {
if (node.depth == 0 and node.name.len == 0) {
return node;
}
}
@panic("Unreachable code");
}
pub fn memoryRegionIterator(self: *const @This()) FdtMemoryRegionIterator {
const r = self.rootNode();
const addressCells = if (r.property("#address-cells")) |o| (if (o.getU32(0)) |p| p else 1) else 1;
const sizeCells = if (r.property("#size-cells")) |o| (if (o.getU32(0)) |p| p else 1) else 1;
return .{ .nodeIter = self.nodeIterator(), .cellSizes = .{ addressCells, sizeCells } };
}
fn stringData(self: *const @This()) [*c]const u8 {
const offStrings = value.u32FromBigEndian(self.header().off_dt_strings);
const sizeStrings = value.u32FromBigEndian(self.header().off_dt_strings);
const offStrings = std.mem.bigToNative(u32, self.header().off_dt_strings);
const sizeStrings = std.mem.bigToNative(u32, self.header().off_dt_strings);
const off = @min(offStrings, self.bytes.len);
const len = @min(sizeStrings, self.bytes.len - off);
return @ptrCast(self.bytes[off..off + len]);
return @ptrCast(self.bytes[off .. off + len]);
}
pub fn stringAt(self: *const @This(), off: usize) []const u8 {
@@ -222,23 +360,49 @@ pub const Fdt = struct {
}
pub fn dump(self: *const @This()) void {
var nodeIter = self.nodeIterator();
while (nodeIter.next()) |node| {
for (0..node.depth) |_| {
log.writeRaw(" ");
}
if (node.name.len == 0) {
log.info("Root node", .{});
} else {
log.info("Node {s}", .{ node.name });
}
var nodePropIter = node.propIterator();
while (nodePropIter.next()) |prop| {
for (0..node.depth + 1) |_| {
log.writeRaw(" ");
const root = self.rootNode();
var cells: [2]u64 = undefined;
if (root.child("reserved-memory")) |resv| {
var regions = resv.children();
while (regions.next()) |region| {
if (region.property("reg")) |reg| {
if (reg.readCells(0, &cells, &.{ 2, 2 })) {
log.info("Reserved memory region {s}: base=0x{x}, size=0x{x}", .{ region.name, cells[0], cells[1] });
}
}
log.info("Prop {s}", .{ prop.name });
}
}
// var memIter = self.memoryRegionIterator();
// while (memIter.next()) |region| {
// log.info("base=0x{x}, size=0x{x}", .{ region.base, region.size });
// }
// var nodeIter = self.nodeIterator();
// while (nodeIter.next()) |node| {
// for (0..node.depth) |_| {
// log.writeRaw(" ");
// }
// if (node.name.len == 0) {
// log.info("Root node, depth = {}", .{ node.depth });
// } else {
// log.info("Node {s}, depth {}", .{ node.name, node.depth });
// }
// var nodePropIter = node.propIterator();
// while (nodePropIter.next()) |prop| {
// for (0..node.depth + 1) |_| {
// log.writeRaw(" ");
// }
// log.info("Prop {s}", .{ prop.name });
// if (std.mem.eql(u8, prop.name, "compatible")) {
// var strings = prop.getStringArray();
// while (strings.next()) |s| {
// for (0..node.depth + 2) |_| {
// log.writeRaw(" ");
// }
// log.info("= {s}", .{ s });
// }
// }
// }
// }
}
};
View File
-9
View File
@@ -1,9 +0,0 @@
const builtin = @import("builtin");
pub fn u32FromBigEndian(input: u32) u32 {
if (comptime builtin.cpu.arch.endian() == .little) {
return @byteSwap(input);
} else {
return input;
}
}
+18
View File
@@ -0,0 +1,18 @@
pub fn FixedVec(comptime T: type, comptime N: usize) type {
return struct {
data: [N]T = undefined,
len: usize = 0,
pub fn push(self: *@This(), value: T) void {
if (self.len + 1 >= self.data.len) {
@panic("Fixed vector overflowed");
}
self.data[self.len] = value;
self.len += 1;
}
pub fn asConstSlice(self: *const @This()) []const T {
return self.data[0..self.len];
}
};
}