Basic thread switching
This commit is contained in:
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
@@ -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 });
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
}
|
||||
};
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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];
|
||||
}
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user