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