From aeb5950e56637d3883a11e2991bdc942d7337f73 Mon Sep 17 00:00:00 2001 From: Mark Poliakov Date: Fri, 14 Mar 2025 22:16:36 +0200 Subject: [PATCH] Basic thread switching --- build.zig | 17 ++- etc/riscv64-unknown-none.ld | 5 + etc/riscv64.lldb | 11 +- src/arch/riscv64.zig | 50 ++++++++ src/arch/riscv64/boot.zig | 41 ++++++- src/arch/riscv64/entry.S | 101 +++++++++++++--- src/arena.zig | 30 +++++ src/debug.zig | 14 ++- src/kernel.zig | 28 ++++- src/mem.zig | 4 +- src/mem/phys.zig | 66 +++++++++++ src/thread.zig | 87 ++++++++++++++ src/util/dtb.zig | 228 +++++++++++++++++++++++++++++++----- src/util/list.zig | 0 src/util/value.zig | 9 -- src/util/vec.zig | 18 +++ 16 files changed, 633 insertions(+), 76 deletions(-) create mode 100644 src/arena.zig create mode 100644 src/mem/phys.zig create mode 100644 src/thread.zig create mode 100644 src/util/list.zig delete mode 100644 src/util/value.zig create mode 100644 src/util/vec.zig diff --git a/build.zig b/build.zig index 0d5dc97..9c3835c 100644 --- a/build.zig +++ b/build.zig @@ -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; } } diff --git a/etc/riscv64-unknown-none.ld b/etc/riscv64-unknown-none.ld index d2e55ca..b31e5a4 100644 --- a/etc/riscv64-unknown-none.ld +++ b/etc/riscv64-unknown-none.ld @@ -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 = .); } diff --git a/etc/riscv64.lldb b/etc/riscv64.lldb index a7aa4a3..d03ad6b 100644 --- a/etc/riscv64.lldb +++ b/etc/riscv64.lldb @@ -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 diff --git a/src/arch/riscv64.zig b/src/arch/riscv64.zig index 4ef7940..87c0080 100644 --- a/src/arch/riscv64.zig +++ b/src/arch/riscv64.zig @@ -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); diff --git a/src/arch/riscv64/boot.zig b/src/arch/riscv64/boot.zig index 4bcb28c..0c96909 100644 --- a/src/arch/riscv64/boot.zig +++ b/src/arch/riscv64/boot.zig @@ -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(); } diff --git a/src/arch/riscv64/entry.S b/src/arch/riscv64/entry.S index e2e817c..dea208c 100644 --- a/src/arch/riscv64/entry.S +++ b/src/arch/riscv64/entry.S @@ -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 diff --git a/src/arena.zig b/src/arena.zig new file mode 100644 index 0000000..cbd66d9 --- /dev/null +++ b/src/arena.zig @@ -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; + } +}; diff --git a/src/debug.zig b/src/debug.zig index 96b3a0a..3055021 100644 --- a/src/debug.zig +++ b/src/debug.zig @@ -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; diff --git a/src/kernel.zig b/src/kernel.zig index 900001b..4456806 100644 --- a/src/kernel.zig +++ b/src/kernel.zig @@ -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(); } diff --git a/src/mem.zig b/src/mem.zig index 9d5daef..b397cdd 100644 --- a/src/mem.zig +++ b/src/mem.zig @@ -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, diff --git a/src/mem/phys.zig b/src/mem/phys.zig new file mode 100644 index 0000000..1ee5b95 --- /dev/null +++ b/src/mem/phys.zig @@ -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; +} diff --git a/src/thread.zig b/src/thread.zig new file mode 100644 index 0000000..f542c78 --- /dev/null +++ b/src/thread.zig @@ -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); + } +} diff --git a/src/util/dtb.zig b/src/util/dtb.zig index d9e8bd8..c1470d5 100644 --- a/src/util/dtb.zig +++ b/src/util/dtb.zig @@ -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 }); + // } + // } + // } + // } } }; diff --git a/src/util/list.zig b/src/util/list.zig new file mode 100644 index 0000000..e69de29 diff --git a/src/util/value.zig b/src/util/value.zig deleted file mode 100644 index cf33e0e..0000000 --- a/src/util/value.zig +++ /dev/null @@ -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; - } -} diff --git a/src/util/vec.zig b/src/util/vec.zig new file mode 100644 index 0000000..c0a8f74 --- /dev/null +++ b/src/util/vec.zig @@ -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]; + } + }; +}