WIP: WIP, WIP

This commit is contained in:
2025-03-14 13:00:54 +02:00
parent 467e4a944a
commit bebdb21c4e
13 changed files with 639 additions and 147 deletions
+57 -43
View File
@@ -1,72 +1,86 @@
const std = @import("std");
pub fn build(b: *std.Build) void {
const optimize = .Debug;
const target = b.standardTargetOptions(.{
.default_target = .{
.cpu_arch = .riscv64,
.os_tag = .freestanding,
.abi = .none,
fn insertFakeLinuxImageHeader(step: *std.Build.Step, opts: std.Build.Step.MakeOptions) anyerror!void {
const RISCV_MAGIC1 = "RISCV\x00\x00\x00";
const RISCV_MAGIC2 = "RSC\x05";
const elf = std.elf;
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));
for (0..ehdr.e_phnum) |i| {
var phdr: elf.Phdr = undefined;
var data: [64]u8 = undefined;
_ = try file.preadAll(std.mem.asBytes(&phdr), ehdr.e_phoff + i * ehdr.e_phentsize);
if (phdr.p_type != elf.PT_LOAD) {
continue;
}
});
const kernel_module = b.addModule("kernel", .{
.optimize = optimize,
.target = target,
.pic = true,
.red_zone = false,
.code_model = .medium,
.root_source_file = b.path("src/kernel.zig")
});
_ = try file.preadAll(&data, phdr.p_offset);
const kernel = b.addExecutable(.{
.name = "kernel",
.root_module = kernel_module,
.pic = true
});
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);
break;
}
}
_ = step;
_ = opts;
}
pub fn build(b: *std.Build) anyerror!void {
const optimize = b.standardOptimizeOption(.{ .preferred_optimize_mode = .ReleaseFast });
const target = b.standardTargetOptions(.{ .default_target = .{
.cpu_arch = .riscv64,
.os_tag = .freestanding,
.abi = .none,
} });
const kernel_module = b.addModule("kernel", .{ .optimize = optimize, .target = target, .pic = true, .red_zone = false, .code_model = .medium, .root_source_file = b.path("src/kernel.zig") });
const kernel = b.addExecutable(.{ .name = "kernel", .root_module = kernel_module, .pic = true });
kernel.pie = true;
kernel.entry = .{.symbol_name = "__rv64_entry"};
kernel.entry = .{ .symbol_name = "__rv64_entry" };
kernel.setLinkerScript(b.path("etc/riscv64-unknown-none.ld"));
kernel.addCSourceFiles(.{
.files = &.{
"src/arch/riscv64/entry.S"
},
.files = &.{"src/arch/riscv64/entry.S"},
.flags = &.{},
});
b.installArtifact(kernel);
const elf2bin = b.addSystemCommand(&.{
"llvm-objcopy",
"-O", "binary",
"zig-out/bin/kernel",
"zig-out/bin/kernel.bin"
const fakeLinuxHeader: *std.Build.Step = try b.allocator.create(std.Build.Step);
fakeLinuxHeader.* = std.Build.Step.init(.{
.id = std.Build.Step.Id.custom,
.name = "insert fake linux header",
.owner = kernel.step.owner,
.makeFn = insertFakeLinuxImageHeader,
});
const elf2bin = b.addSystemCommand(&.{ "llvm-objcopy", "-O", "binary", "zig-out/bin/kernel", "zig-out/bin/kernel.bin" });
// TODO QEMU binary override
const qemu_info = switch (target.result.cpu.arch) {
.riscv64 => .{ "qemu-system-riscv64", "rv64" },
else => unreachable,
};
const qemu_cmd = b.addSystemCommand(&.{
qemu_info[0],
"-M", "virt",
"-kernel", "zig-out/bin/kernel.bin",
"-m", "256M",
"-cpu", qemu_info[1],
"-serial", "mon:stdio",
"-display", "none"
});
const qemu_cmd = b.addSystemCommand(&.{ qemu_info[0], "-M", "virt", "-kernel", "zig-out/bin/kernel.bin", "-m", "256M", "-cpu", qemu_info[1], "-serial", "mon:stdio", "-display", "none" });
if (target.result.cpu.arch == .riscv64) {
qemu_cmd.addArgs(&.{
"-bios", "etc/boot/rv64_fw_jump.bin"
});
qemu_cmd.addArgs(&.{ "-bios", "etc/boot/rv64_fw_jump.bin" });
}
elf2bin.step.dependOn(b.getInstallStep());
fakeLinuxHeader.dependOn(b.getInstallStep());
elf2bin.step.dependOn(fakeLinuxHeader);
qemu_cmd.step.dependOn(&elf2bin.step);
if (b.args) |args| qemu_cmd.addArgs(args);
const run_step = b.step("run", "Start the OS in qemu");
+37 -5
View File
@@ -1,4 +1,7 @@
const boot = @import("riscv64/boot.zig");
const regs = @import("riscv64/regs.zig");
const std = @import("std");
const builtin = @import("builtin");
export const _ = boot.rv64BspLowerEntry;
pub fn arch() type {
@@ -6,18 +9,47 @@ pub fn arch() type {
pub inline fn halt() noreturn {
while (true) {
_ = setInterruptMask(true);
pause();
waitForInterrupt();
}
}
pub inline fn setInterruptMask(mask: bool) bool {
// TODO
_ = mask;
return true;
const old = interruptMask();
if (mask) {
regs.SSTATUS.modify(.{}, .{ .SIE = true });
} else {
regs.SSTATUS.modify(.{ .SIE = true }, .{});
}
return old;
}
pub inline fn pause() void {
pub fn interruptMask() bool {
return regs.SSTATUS.read().SIE;
}
pub inline fn waitForInterrupt() void {
asm volatile ("wfi");
}
pub inline fn spinHint() void {
// Don't want to explicitly enable Zihintpause ext, so just paste this as raw opcode
asm volatile (".word 0x0100000f");
}
pub inline fn barrier(comptime ordering: std.builtin.AtomicOrder) void {
switch (ordering) {
.acquire => {
asm volatile ("fence rw, w");
},
.release => {
asm volatile ("fence w, rw");
},
.acq_rel, .seq_cst => {
asm volatile ("fence rw, rw");
},
.unordered, .monotonic => {},
}
asm volatile ("":::"memory");
}
};
}
+32 -51
View File
@@ -1,15 +1,20 @@
const sbi = @import("sbi.zig");
const debug = @import("../../debug.zig");
const arch = @import("../../kernel.zig").arch;
const kernel = @import("../../kernel.zig");
const vmm = @import("vmm.zig");
const regs = @import("regs.zig");
const dtb = @import("../../util/dtb.zig");
const log = debug.log;
const arch = kernel.arch;
extern const __rela_start: u8;
extern const __rela_end: u8;
extern const __rv64_bsp_stack_top: u8;
var gDtbAddress: usize = 0;
var gBspHartId: usize = 0;
pub export fn rv64RelocateKernel(imageBase: usize, relaStart: usize, relaEnd: usize) void {
const elf = @import("std").elf;
@@ -26,78 +31,54 @@ pub export fn rv64RelocateKernel(imageBase: usize, relaStart: usize, relaEnd: us
}
}
fn bspUpperEntry(a0: usize, a1: usize) callconv(.C) noreturn {
asm volatile ("":::"memory");
fn bspUpperEntry(realAddress: usize, unused: usize) callconv(.C) noreturn {
_ = unused;
arch.barrier(.acq_rel);
_ = a1;
// Relocate the kernel yet again, this time to another base
const relaStart = @intFromPtr(&__rela_start);
const relaEnd = @intFromPtr(&__rela_end);
const relOffset = vmm.KERNEL_VIRTUAL_BASE + vmm.L1.offset(a0);
asm volatile ("":::"memory");
const relOffset = vmm.KERNEL_VIRTUAL_BASE + vmm.L1.offset(realAddress);
arch.barrier(.acq_rel);
rv64RelocateKernel(relOffset, relaStart, relaEnd);
asm volatile ("":::"memory");
// Can unmap lower half now
for (0..4) |i| {
vmm.fixed.entry(i).* = .INVALID;
}
asm volatile ("":::"memory");
vmm.unmapEarly();
debug.log.setWriteFn(&sbi.debugPrintByte);
kernel.mem.PhysicalAddress.gVirtualizeBase = 0;
kernel.mem.PhysicalAddress.gVirtualizeSize = vmm.virtualizeRange();
log.info("Still alive", .{});
// Setup physical memory management
const fdt = dtb.Fdt.fromPhysicalAddress(.{ .raw = gDtbAddress }) catch |err| {
log.panic("Cannot initialize raw DTB: {}", .{ err });
};
fdt.dump();
arch.halt();
kernel.kernel_main();
}
fn longJump(pc: usize, sp: usize, a0: usize, a1: usize) noreturn {
inline fn longJump(pc: usize, sp: usize, a0: usize, a1: usize) noreturn {
asm volatile (
\\ mv sp, %[sp]
\\ jr %[pc]
:
: [a0]"{a0}"(a0),
[a1]"{a1}"(a1),
[pc]"r"(pc),
[sp]"r"(sp)
:"memory"
: [a0] "{a0}" (a0),
[a1] "{a1}" (a1),
[pc] "r" (pc),
[sp] "r" (sp),
: "memory"
);
unreachable;
}
fn setupMmu(realAddress: usize) void {
var table = &vmm.fixed;
const realL1 = vmm.L1.index(realAddress);
// Lower half
for (0..4) |i| {
table.entry(i).* = vmm.TableEntry(vmm.L1).page(
.{ .raw = i * vmm.L1.SIZE },
.{ .r = true, .w = true, .x = true }
);
}
// Map 1GiB at KERNEL_VIRTUAL_BASE -> physical 1GiB where the kernel is loaded
table.entry(vmm.KERNEL_VIRTUAL_L1I).* = vmm.TableEntry(vmm.L1).page(
.{ .raw = vmm.L1.address(realL1) },
.{ .r = true, .w = true, .x = true }
);
const address = @as(usize, @intFromPtr(table));
regs.SATP.write(.{
.PPN = @intCast(address >> 12),
.MODE = .sv39
});
}
pub export fn rv64BspLowerEntry(realAddress: usize) callconv(.C) noreturn {
pub export fn rv64BspLowerEntry(realAddress: usize, bspHartId: usize, dtbAddress: usize) callconv(.C) noreturn {
debug.log.setWriteFn(&sbi.debugPrintByte);
setupMmu(realAddress);
gDtbAddress = dtbAddress;
gBspHartId = bspHartId;
vmm.mapEarly(realAddress);
// &bspUpperEntry will yield a pointer like: X + P, where
// * X is symbol's raw address,
+28 -3
View File
@@ -8,12 +8,35 @@
.extern RELOC_SYMBOL
.pushsection .text.entry
.option push
.option norvc
.type __rv64_entry, @function
// .option push
// .option rvc
__rv64_entry:
auipc s0, 0 // a0 = real PC (also a real load address/offset)
// .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
.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)
mv s1, a0
mv s2, a1
csrw sie, zero
csrw sip, zero
@@ -53,6 +76,8 @@ __rv64_entry:
addi t0, t0, %pcrel_lo(.P06)
mv a0, s0
mv a1, s1
mv a2, s2
jr t0
.size __rv64_entry, . - __rv64_entry
+22
View File
@@ -13,6 +13,11 @@ fn makeRegister(comptime name: []const u8, comptime bits: type) type {
set(@bitCast(value));
}
pub fn modify(s: bits, c: bits) void {
const v = get();
set((v & ~@as(repr, @bitCast(c))) | @as(repr, @bitCast(s)));
}
pub fn read() bits {
return @bitCast(get());
}
@@ -33,3 +38,20 @@ pub const SATP = makeRegister("satp", packed struct(u64) {
_,
} = .bare
});
pub const SSTATUS = makeRegister("sstatus", packed struct(u64) {
// 0
_0: u1 = 0,
// 1
SIE: bool = false,
// 2..8
_1: u6 = 0,
// 8
SPP: bool = false,
// 9..18
_2: u9 = 0,
// 18
SUM: bool = false,
// 19..64
_3: u45 = 0,
});
+80 -33
View File
@@ -1,30 +1,18 @@
const PhysicalAddress = @import("../../mem.zig").PhysicalAddress;
const sync = @import("../../sync.zig");
const regs = @import("regs.zig");
const mem = @import("../../mem.zig");
const arch = @import("../../kernel.zig").arch;
const PhysicalAddress = mem.PhysicalAddress;
pub const KERNEL_VIRTUAL_BASE: usize = 0xFFFFFFF000000000;
pub const KERNEL_VIRTUAL_L1I: usize = (KERNEL_VIRTUAL_BASE >> L1.SHIFT) & 511;
// 16 GiB
const EARLY_MAPPING_SIZE: usize = 16;
fn translationLevel(comptime shift: usize) type {
return struct {
pub const SHIFT: usize = shift;
pub const SIZE: usize = 1 << shift;
pub inline fn index(addr: usize) usize {
return (addr >> shift) & 511;
}
pub inline fn offset(addr: usize) usize {
return addr & ((1 << shift) - 1);
}
pub inline fn address(idx: usize) usize {
return idx << shift;
}
};
}
pub const L1 = translationLevel(30);
pub const L2 = translationLevel(21);
pub const L3 = translationLevel(12);
pub const L1 = mem.translationLevel(30);
pub const L2 = mem.translationLevel(21);
pub const L3 = mem.translationLevel(12);
pub const RawEntry = packed struct(u64) {
// 0: Valid
@@ -96,12 +84,10 @@ pub fn TableEntry(comptime Level: type) type {
pub fn table(addr: PhysicalAddress, flags: RawEntry) @This() {
flags.clear(.{ .r = true, .w = true, .x = true });
return .{
.raw = flags.makeUnion(.{
.address = @as(u39, @intCast(addr.raw >> 12)),
.v = true,
})
};
return .{ .raw = flags.makeUnion(.{
.address = @as(u39, @intCast(addr.raw >> 12)),
.v = true,
}) };
}
};
}
@@ -113,9 +99,7 @@ pub fn Table(comptime Level: type) type {
entries: [512]Entry align(4096),
pub fn empty() @This() {
return .{
.entries = [_]Entry{.INVALID} ** 512
};
return .{ .entries = [_]Entry{.INVALID} ** 512 };
}
pub inline fn entry(self: *@This(), index: usize) *Entry {
@@ -124,4 +108,67 @@ pub fn Table(comptime Level: type) type {
};
}
pub var fixed = Table(L1).empty();
var gFixed = Table(L1).empty();
var gFixedLock: sync.IrqSafeSpinlock = .{};
pub fn virtualizeRange() usize {
return EARLY_MAPPING_SIZE * L1.SIZE;
}
pub fn unmapEarly() void {
// Make lower half mappings non-executable
gFixedLock.lock();
defer gFixedLock.release();
for (0..EARLY_MAPPING_SIZE) |i| {
gFixed.entry(i).* = .page(
.{ .raw = L1.address(i) },
.{ .r = true, .w = true },
);
}
}
pub fn mapEarly(realAddress: usize) void {
const realL1 = L1.index(realAddress);
// Identity map first 16GiB of memory
for (0..EARLY_MAPPING_SIZE) |i| {
gFixed.entry(i).* = .page(.{ .raw = L1.address(i) }, .{ .r = true, .w = true, .x = true });
}
// Map 1GiB at KERNEL_VIRTUAL_BASE -> physical 1GiB where the kernel is loaded
gFixed.entry(KERNEL_VIRTUAL_L1I).* = .page(.{ .raw = L1.address(realL1) }, .{ .r = true, .w = true, .x = true });
const address = @as(usize, @intFromPtr(&gFixed));
regs.SATP.write(.{ .PPN = @intCast(address >> 12), .MODE = .sv39 });
}
pub inline fn flush_vma(page: usize) void {
asm volatile ("sfence.vma %[page], zero"
:
: [page] "r" (page),
: "memory"
);
}
pub inline fn flush_vma_asid(page: usize, asid: usize) void {
asm volatile ("sfence.vma %[page], %[asid]"
:
: [page] "r" (page),
[asid] "r" (asid),
);
}
pub fn flush_vma_range(start: usize, end: usize, asid: ?usize) void {
var i = L3.align_down(start);
const rend = L3.align_up(end);
if (asid) |a| {
while (i < rend) : (i += L3.SIZE) {
flush_vma_asid(i, a);
}
} else {
while (i < rend) : (i += L3.SIZE) {
flush_vma(i);
}
}
}
+45 -6
View File
@@ -27,16 +27,55 @@ pub const log = struct {
writeFn = f;
}
pub fn info(comptime format: []const u8, args: anytype) void {
write(.info, format ++ "\r\n", args);
pub inline fn info(comptime format: []const u8, args: anytype) void {
write(.info, format, args);
}
pub inline fn debug(comptime format: []const u8, args: anytype) void {
write(.debug, format, args);
}
pub inline fn warn(comptime format: []const u8, args: anytype) void {
write(.warn, format, args);
}
pub inline fn err(comptime format: []const u8, args: anytype) void {
write(.err, format, args);
}
pub fn writeRaw(data: []const u8) void {
writeWrapperFn(void, data);
_ = writeWrapperFn(0, data) catch return;
}
pub fn write(level: Level, comptime format: []const u8, args: anytype) void {
_ = level;
writer.print(format, args) catch return;
pub fn write(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;
}
pub fn todo(comptime msg: []const u8, args: anytype) noreturn {
err("Not yet implemented: " ++ msg, args);
@panic("Not yet implemented");
}
pub fn panic(comptime msg: []const u8, args: anytype) noreturn {
err("PANIC: " ++ msg, args);
@panic("Explicit kernel panic");
}
fn logPrefix(comptime level: Level) []const u8 {
switch (level) {
.debug => return "",
.info => return "\x1B[1;36m",
.warn => return "\x1B[1;33m",
.err => return "\x1B[1;31m",
}
}
fn logSuffix(comptime level: Level) []const u8 {
if (level == .debug) {
return "";
} else {
return "\x1B[0m";
}
}
};
+19 -1
View File
@@ -1,7 +1,25 @@
// export const _ = @import("arch/riscv64/boot.zig").rv64BspLowerEntry;
pub const arch = @import("arch.zig").arch();
pub const mem = @import("mem.zig");
pub const debug = @import("debug.zig");
pub const log = debug.log;
const std = @import("std");
pub export fn kernel_main() callconv(.C) noreturn {
log.info("Hello", .{});
arch.halt();
}
pub fn panic(msg: []const u8, error_return_trace: ?*std.builtin.StackTrace, return_address: ?usize) noreturn {
_ = error_return_trace;
const ra = return_address orelse @returnAddress();
log.err("!!! Kernel panic !!!", .{});
log.err(" Reason: {s}", .{ msg });
log.err(" At: 0x{x}", .{ ra });
export fn kernel_main() callconv(.C) void {
arch.halt();
}
+21
View File
@@ -1,9 +1,30 @@
pub const translationLevel = @import("mem/vmm.zig").translationLevel;
pub const PhysicalAddress = packed struct(u64) {
raw: u64,
pub const NULL: @This() = .{ .raw = 0 };
pub var gVirtualizeBase: usize = 0;
pub var gVirtualizeSize: usize = 0;
pub fn add(self: @This(), offset: usize) @This() {
return .{ .raw = self.raw + @as(u64, @intCast(offset)) };
}
pub fn virtualize(self: @This()) usize {
if (self.raw > gVirtualizeSize) {
@panic("Physical address out of virtualize bounds");
}
return self.raw + gVirtualizeBase;
}
pub fn from_virtualized(virt: usize) @This() {
if ((virt < gVirtualizeBase) || (virt - gVirtualizeBase > gVirtualizeSize)) {
@panic("Invalid virtualized physical address");
}
return .{ .raw = virt - gVirtualizeBase };
}
};
+26
View File
@@ -0,0 +1,26 @@
pub fn translationLevel(comptime shift: usize) type {
return struct {
pub const SHIFT: usize = shift;
pub const SIZE: usize = 1 << shift;
pub inline fn index(addr: usize) usize {
return (addr >> shift) & 511;
}
pub inline fn offset(addr: usize) usize {
return addr & (SIZE - 1);
}
pub inline fn address(idx: usize) usize {
return idx << shift;
}
pub inline fn align_down(addr: usize) usize {
return addr & ~(SIZE - 1);
}
pub inline fn align_up(addr: usize) usize {
return (addr + SIZE - 1) & ~(SIZE - 1);
}
};
}
+19 -5
View File
@@ -1,6 +1,20 @@
pub fn IrqSafeSpinlock(comptime T: type) type {
return struct {
inner: T,
const std = @import("std");
const arch = @import("kernel.zig").arch;
};
}
pub const IrqSafeSpinlock = struct {
state: std.atomic.Value(bool) = .{ .raw = false },
pub fn tryLock(self: *@This()) bool {
return self.state.cmpxchgStrong(false, true, .acquire, .monotonic) orelse false;
}
pub fn lock(self: *@This()) void {
while (!self.tryLock()) {
arch.spinHint();
}
}
pub fn release(self: *@This()) void {
self.state.store(false, .release);
}
};
+244
View File
@@ -0,0 +1,244 @@
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 {
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,
};
const fdt_op = enum(u32) { FDT_BEGIN_NODE = 1, FDT_END_NODE = 2, FDT_PROP = 3, FDT_NOP = 4, FDT_END = 9, _ };
const fdt_prop = extern struct {
len: u32,
nameoff: u32,
};
const FdtPropTag = struct {
len: u32,
nameoff: u32,
data: []const u8,
};
const FdtTag = union(enum) {
begin_node: []const u8,
end_node,
prop: FdtPropTag,
nop,
end,
};
pub const FdtNode = struct {
fdt: *const Fdt,
off: usize,
name: []const u8,
depth: usize,
pub fn propIterator(self: *const @This()) FdtNodePropIterator {
return .{ .node = self, .tagIter = self.fdt.tagIteratorAt(self.off) };
}
};
pub const FdtNodeProp = struct {
node: *const FdtNode,
name: []const u8,
value: []const u8,
};
pub const FdtNodePropIterator = struct {
node: *const FdtNode,
tagIter: FdtTagIterator,
depth: usize = 0,
fn next(self: *FdtNodePropIterator) ?FdtNodeProp {
while (self.tagIter.next()) |tag| {
switch (tag) {
.begin_node => |_| {
self.depth += 1;
},
.nop => {},
.prop => |prop| {
if (self.depth == 0) {
const name = self.node.fdt.stringAt(prop.nameoff);
return .{
.node = self.node,
.value = prop.data,
.name = name
};
}
},
.end_node => {
if (self.depth == 0) {
return null;
}
self.depth -= 1;
},
.end => {
return null;
}
}
}
return null;
}
};
pub const FdtNodeIterator = struct {
tagIter: FdtTagIterator,
depth: usize = 0,
fn next(self: *FdtNodeIterator) ?FdtNode {
while (self.tagIter.next()) |tag| {
switch (tag) {
.begin_node => |name| {
self.depth += 1;
return .{
.fdt = self.tagIter.fdt,
.off = self.tagIter.off,
.name = name,
.depth = self.depth - 1,
};
},
.end_node => {
self.depth -= 1;
},
else => {},
}
}
return null;
}
};
pub const FdtTagIterator = struct {
fdt: *const Fdt,
raw: []const u8,
off: usize,
fn next(self: *FdtTagIterator) ?FdtTag {
if (self.off >= self.raw.len) {
return null;
}
const tag: fdt_op = @enumFromInt(value.u32FromBigEndian(@as(*const u32, @ptrCast(@alignCast(&self.raw[self.off]))).*));
self.off += @sizeOf(u32);
switch (tag) {
.FDT_BEGIN_NODE => {
const nameCStr: [*c]const u8 = @ptrCast(self.raw[self.off..]);
const nameLength = std.mem.len(nameCStr);
const name = self.raw[self.off .. self.off + nameLength];
self.off += (nameLength + 4) & ~@as(usize, 3);
return .{ .begin_node = name };
},
.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);
self.off += @sizeOf(fdt_prop);
const data = self.raw[self.off .. self.off + len];
self.off += (len + 3) & ~@as(usize, 3);
return .{ .prop = .{ .nameoff = nameoff, .len = len, .data = data } };
},
.FDT_NOP => {
return .nop;
},
.FDT_END => {
self.off = self.raw.len;
return .end;
},
.FDT_END_NODE => {
return .end_node;
},
else => {
return null;
},
}
}
};
pub const FdtError = error{invalid_magic};
pub const FDT_MAGIC: u32 = 0xD00DFEED;
pub const Fdt = struct {
bytes: []const u8,
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) {
return error.invalid_magic;
}
const totalsize = value.u32FromBigEndian(hdr.totalsize);
const x = @as([*]const u8, @ptrFromInt(virt));
return .{ .bytes = x[0..totalsize] };
}
pub fn header(self: *const @This()) *const fdt_header {
return @ptrCast(@alignCast(&self.bytes[0]));
}
fn data(self: *const @This()) []const u8 {
const off = value.u32FromBigEndian(self.header().off_dt_struct);
return self.bytes[off..];
}
pub fn tagIterator(self: *const @This()) FdtTagIterator {
return self.tagIteratorAt(0);
}
pub fn tagIteratorAt(self: *const @This(), off: usize) FdtTagIterator {
return .{ .raw = self.data(), .fdt = self, .off = off };
}
pub fn nodeIterator(self: *const @This()) FdtNodeIterator {
return .{ .tagIter = self.tagIterator() };
}
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 off = @min(offStrings, self.bytes.len);
const len = @min(sizeStrings, self.bytes.len - off);
return @ptrCast(self.bytes[off..off + len]);
}
pub fn stringAt(self: *const @This(), off: usize) []const u8 {
const raw = self.stringData()[off..];
const len = std.mem.len(raw);
return @ptrCast(raw[0..len]);
}
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(" ");
}
log.info("Prop {s}", .{ prop.name });
}
}
}
};
+9
View File
@@ -0,0 +1,9 @@
const builtin = @import("builtin");
pub fn u32FromBigEndian(input: u32) u32 {
if (comptime builtin.cpu.arch.endian() == .little) {
return @byteSwap(input);
} else {
return input;
}
}