aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/Console.zig46
-rw-r--r--src/fdt.zig59
-rw-r--r--src/instructions.zig4
-rw-r--r--src/interrupts.zig5
-rw-r--r--src/main.zig51
-rw-r--r--src/paging.zig22
-rw-r--r--src/uart.zig58
7 files changed, 199 insertions, 46 deletions
diff --git a/src/Console.zig b/src/Console.zig
new file mode 100644
index 0000000..a54bb41
--- /dev/null
+++ b/src/Console.zig
@@ -0,0 +1,46 @@
+// SPDX-FileCopyrightText: 2024 Himbeer <himbeer@disroot.org>
+//
+// SPDX-License-Identifier: AGPL-3.0-or-later
+
+const std = @import("std");
+
+const debug_console = @import("sbi/debug_console.zig");
+const fdt = @import("fdt.zig");
+const paging = @import("paging.zig");
+const uart = @import("uart.zig");
+
+provider: Provider,
+
+const Self = @This();
+
+pub const Provider = union(enum) {
+ uart_port: uart.Port.Writer,
+ sbi_debug: debug_console.Writer,
+};
+
+pub fn init(kmem: *paging.Table, dt: *const fdt.Tree, allocator: std.mem.Allocator) !void {
+ if (uart.default == null) {
+ try uart.init(kmem, dt, allocator);
+ }
+}
+
+pub fn autoChoose() ?Self {
+ if (uart.default) |uart_con| {
+ return .{
+ .provider = .{ .uart_port = uart_con.writer() },
+ };
+ } else if (debug_console.writer()) |sbi_con| {
+ return .{
+ .provider = .{ .sbi_debug = sbi_con },
+ };
+ } else |_| {}
+
+ return null;
+}
+
+pub fn writer(console: *const Self) std.io.AnyWriter {
+ switch (console.provider) {
+ .uart_port => return console.provider.uart_port.any(),
+ .sbi_debug => return console.provider.sbi_debug.any(),
+ }
+}
diff --git a/src/fdt.zig b/src/fdt.zig
index 5d2f333..9ea85d0 100644
--- a/src/fdt.zig
+++ b/src/fdt.zig
@@ -35,6 +35,11 @@ pub const MemReservation = struct {
pub const Reg = struct {
start: usize,
len: usize,
+
+ pub fn slice(self: Reg, comptime T: type) []volatile T {
+ const start_ptr: [*]volatile T = @ptrFromInt(self.start);
+ return start_ptr[0 .. self.len / @sizeOf(T)];
+ }
};
pub const Tree = struct {
@@ -73,7 +78,7 @@ pub const Node = struct {
return self.preferredDriver(&[_][]const u8{with}) != null;
}
- pub fn reg(self: Node, allocator: std.mem.Allocator) !std.ArrayList(Reg) {
+ pub fn reg(self: *const Node, allocator: std.mem.Allocator) !std.ArrayList(Reg) {
if (self.parent == null) return NodeError.NoParent;
const address_cells_bytes = self.parent.?.props.get("#address-cells");
@@ -114,7 +119,7 @@ pub const PropertyDesc = struct {
len: u32,
name_offset: u32,
- pub fn fromPtr(ptr: *PropertyDesc) PropertyDesc {
+ pub fn fromPtr(ptr: *align(1) PropertyDesc) PropertyDesc {
return .{
.len = std.mem.bigToNative(u32, ptr.len),
.name_offset = std.mem.bigToNative(u32, ptr.name_offset),
@@ -157,7 +162,7 @@ pub const Header = struct {
const token_nop = 0x00000004;
const token_end = 0x00000009;
- raw_hdr: *RawHeader,
+ raw_hdr: *align(1) RawHeader,
magic: u32,
total_size: u32,
@@ -176,7 +181,7 @@ pub const Header = struct {
// v17
size_dt_struct: u32,
- pub fn parse(raw_hdr: *RawHeader) !Header {
+ pub fn parse(raw_hdr: *align(1) RawHeader) !Header {
const hdr = .{
.raw_hdr = raw_hdr,
@@ -225,10 +230,10 @@ pub const Header = struct {
const dt_strings_addr = @intFromPtr(self.raw_hdr) + @as(usize, self.offset_dt_strings);
const dt_strings: [*:0]const u8 = @ptrFromInt(dt_strings_addr);
- while (std.mem.bigToNative(u32, @as(*u32, @ptrFromInt(dt_struct_addr)).*) != token_end) {
+ while (std.mem.bigToNative(u32, @as(*align(1) u32, @ptrFromInt(dt_struct_addr)).*) != token_end) {
const parsed = try parseNode(allocator, dt_struct_addr, dt_strings);
dt_struct_addr = parsed.addr;
- try nodes.insert(0, parsed.value);
+ try nodes.append(parsed.value);
}
var tree = Tree{ .header = self, .nodes = nodes };
@@ -286,6 +291,34 @@ pub fn findPathExact(dt: *const Tree, path: []const u8) ?Node {
return node;
}
+pub fn findCompatible(node: *Node, compatible: []const []const u8, max: comptime_int) []Node {
+ var results: [max]Node = undefined;
+ var n: usize = 0;
+
+ for (node.subnodes.items) |subnode| {
+ var subnode_mut = subnode;
+
+ const subresults = findCompatible(&subnode_mut, compatible, max);
+ for (subresults) |subresult| {
+ if (n >= max) break;
+
+ results[n] = subresult;
+ n += 1;
+ }
+
+ for (compatible) |driver| {
+ if (n >= max) break;
+
+ if (subnode.isCompatible(driver)) {
+ results[n] = subnode;
+ n += 1;
+ }
+ }
+ }
+
+ return results[0..n];
+}
+
fn nodeNameFilter(node: Node, name: []const u8) bool {
var it = std.mem.splitScalar(u8, node.name, '@');
const trueName = it.first();
@@ -299,11 +332,11 @@ fn parseNode(allocator: std.mem.Allocator, dt_struct_addr: usize, dt_strings: [*
var addr = dt_struct_addr;
// Skip Nop tokens
- while (std.mem.bigToNative(u32, @as(*u32, @ptrFromInt(addr)).*) == Header.token_nop) {
+ while (std.mem.bigToNative(u32, @as(*align(1) u32, @ptrFromInt(addr)).*) == Header.token_nop) {
addr += @sizeOf(u32);
}
- if (std.mem.bigToNative(u32, @as(*u32, @ptrFromInt(addr)).*) != Header.token_begin_node) {
+ if (std.mem.bigToNative(u32, @as(*align(1) u32, @ptrFromInt(addr)).*) != Header.token_begin_node) {
return ParseError.ExpectedBeginNodeToken;
}
@@ -316,8 +349,8 @@ fn parseNode(allocator: std.mem.Allocator, dt_struct_addr: usize, dt_strings: [*
// Skip zeroed alignment padding
addr += (4 - ((std.mem.len(name) + 1) % 4)) % 4;
- while (std.mem.bigToNative(u32, @as(*u32, @ptrFromInt(addr)).*) != Header.token_end_node) {
- switch (std.mem.bigToNative(u32, @as(*u32, @ptrFromInt(addr)).*)) {
+ while (std.mem.bigToNative(u32, @as(*align(1) u32, @ptrFromInt(addr)).*) != Header.token_end_node) {
+ switch (std.mem.bigToNative(u32, @as(*align(1) u32, @ptrFromInt(addr)).*)) {
Header.token_prop => {
const parsed = try parseProperty(addr, dt_strings);
addr = parsed.addr;
@@ -334,7 +367,7 @@ fn parseNode(allocator: std.mem.Allocator, dt_struct_addr: usize, dt_strings: [*
Header.token_begin_node => {
const parsed = try parseNode(allocator, addr, dt_strings);
addr = parsed.addr;
- try subnodes.insert(0, parsed.value);
+ try subnodes.append(parsed.value);
},
else => return ParseError.BadToken,
}
@@ -357,11 +390,11 @@ fn parseProperty(dt_struct_addr: usize, dt_strings: [*:0]const u8) !ParseResult(
var addr = dt_struct_addr;
// Skip Nop tokens
- while (std.mem.bigToNative(u32, @as(*u32, @ptrFromInt(addr)).*) == Header.token_nop) {
+ while (std.mem.bigToNative(u32, @as(*align(1) u32, @ptrFromInt(addr)).*) == Header.token_nop) {
addr += @sizeOf(u32);
}
- if (std.mem.bigToNative(u32, @as(*u32, @ptrFromInt(addr)).*) != Header.token_prop) {
+ if (std.mem.bigToNative(u32, @as(*align(1) u32, @ptrFromInt(addr)).*) != Header.token_prop) {
return ParseError.ExpectedPropToken;
}
diff --git a/src/instructions.zig b/src/instructions.zig
index 818a35f..2ae53ec 100644
--- a/src/instructions.zig
+++ b/src/instructions.zig
@@ -48,11 +48,11 @@ pub const setSatp = setCsrFn(paging.Satp, "satp").?;
pub const setSscratch = setCsrFn(usize, "sscratch").?;
pub const setSepc = setCsrFn(usize, "sepc").?;
-pub fn setCsrFn(comptime T: type, csr: []const u8) ?fn (T) void {
+pub fn setCsrFn(comptime T: type, csr: []const u8) ?fn (T) callconv(.Inline) void {
if (csr.len > 8) return null;
return struct {
- fn setCsr(value: T) void {
+ inline fn setCsr(value: T) void {
const bits: usize = @bitCast(value);
comptime var buf = [_]u8{0} ** 23;
diff --git a/src/interrupts.zig b/src/interrupts.zig
index eb42ff1..de762d3 100644
--- a/src/interrupts.zig
+++ b/src/interrupts.zig
@@ -4,7 +4,7 @@
const std = @import("std");
-const debug_console = @import("sbi/debug_console.zig");
+const Console = @import("Console.zig");
const instructions = @import("instructions.zig");
const paging = @import("paging.zig");
const plic = @import("plic.zig");
@@ -114,7 +114,8 @@ export fn handleTrap(epc: usize, tval: usize, cause_bits: usize, hart_id: usize,
_ = &status;
_ = &frame;
- const w = debug_console.writer() catch while (true) {};
+ const console = Console.autoChoose() orelse while (true) asm volatile ("wfi");
+ const w = console.writer();
const cause: Cause = @bitCast(cause_bits);
diff --git a/src/main.zig b/src/main.zig
index d727d6f..540e8f8 100644
--- a/src/main.zig
+++ b/src/main.zig
@@ -4,7 +4,7 @@
const std = @import("std");
-const debug_console = @import("sbi/debug_console.zig");
+const Console = @import("Console.zig");
const fdt = @import("fdt.zig");
const instructions = @import("instructions.zig");
const interrupts = @import("interrupts.zig");
@@ -25,7 +25,8 @@ pub fn panic(msg: []const u8, error_return_trace: ?*std.builtin.StackTrace, ret_
@setCold(true);
- const w = debug_console.writer() catch while (true) asm volatile ("wfi");
+ const console = Console.autoChoose() orelse while (true) asm volatile ("wfi");
+ const w = console.writer();
w.print("\r\n", .{}) catch while (true) asm volatile ("wfi");
w.print("= !! ========== !! =\r\n", .{}) catch while (true) asm volatile ("wfi");
@@ -49,20 +50,11 @@ export fn _start() callconv(.Naked) noreturn {
}
fn kmain(hart_id: usize, fdt_blob: *fdt.RawHeader) noreturn {
- const w = debug_console.writer() catch while (true) {};
- w.print("Kernel init\r\n", .{}) catch while (true) {};
-
- run(hart_id, fdt_blob, w) catch |err| std.debug.panic("Hart {d}: {any}", .{ hart_id, err });
+ run(hart_id, fdt_blob) catch |err| std.debug.panic("Hart {d}: {any}", .{ hart_id, err });
}
-fn run(hart_id: usize, fdt_blob: *fdt.RawHeader, w: debug_console.Writer) !noreturn {
- try w.print("\r\n", .{});
-
- try w.print("Hart : {d}\r\n", .{hart_id});
- try w.print("FDT address : 0x{x:0>8}\r\n", .{@intFromPtr(fdt_blob)});
-
+fn run(hart_id: usize, fdt_blob: *fdt.RawHeader) !noreturn {
if (hart_id > ~@as(u16, 0)) return Error.HartIdOutOfRange;
- if (@intFromPtr(fdt_blob) < 0xf000000) return Error.SuspiciousFdtAddr;
const dt_header = try fdt.Header.parse(fdt_blob);
@@ -70,6 +62,26 @@ fn run(hart_id: usize, fdt_blob: *fdt.RawHeader, w: debug_console.Writer) !noret
const kmem: *paging.Table = @alignCast(@ptrCast(try paging.zeroedAlloc(1)));
+ try kmem.mapKernel();
+ try kmem.mapFdt(&dt_header);
+
+ var chunk_allocator = try mem.ChunkAllocator(.{ .auto_merge_free = true }).init(128);
+ const allocator = chunk_allocator.allocator();
+
+ fdt.default = try dt_header.parseTree(allocator);
+
+ try Console.init(kmem, &fdt.default, allocator);
+
+ const console = Console.autoChoose() orelse while (true) asm volatile ("wfi");
+ const w = console.writer();
+
+ try w.print("\r\n", .{});
+ try w.print("Console init\r\n", .{});
+ try w.print("\r\n", .{});
+
+ try w.print("Hart : {d}\r\n", .{hart_id});
+ try w.print("FDT address : 0x{x:0>8}\r\n", .{@intFromPtr(fdt_blob)});
+
try w.print("\r\n", .{});
try w.print("===================== Kernel Page Table =====================\r\n", .{});
try w.print("MMIO: 0x{x:0>8} - 0x{x:0>8} -> identity mapped (rw-)\r\n", .{ paging.mmio_start, paging.mmio_end });
@@ -87,10 +99,8 @@ fn run(hart_id: usize, fdt_blob: *fdt.RawHeader, w: debug_console.Writer) !noret
try w.print("=============================================================\r\n", .{});
try w.print("\r\n", .{});
- try kmem.mapKernel();
- try kmem.mapFdt(&dt_header);
-
instructions.setSatp(kmem.satp(0));
+
try w.print("Paging : Sv39\r\n", .{});
interrupts.init();
@@ -99,15 +109,6 @@ fn run(hart_id: usize, fdt_blob: *fdt.RawHeader, w: debug_console.Writer) !noret
try w.print("\r\n", .{});
- var chunk_allocator = try mem.ChunkAllocator(.{ .auto_merge_free = true }).init(64);
- const allocator = chunk_allocator.allocator();
-
- try w.print("Parse FDT\r\n", .{});
-
- fdt.default = try dt_header.parseTree(allocator);
-
- try w.print("\r\n", .{});
-
try w.print("Timer : {d} Hz\r\n", .{1 / (@as(f64, process.schedule_interval_millis) / 1000)});
try plic.init(&fdt.default, allocator);
diff --git a/src/paging.zig b/src/paging.zig
index 1c7b49f..e066451 100644
--- a/src/paging.zig
+++ b/src/paging.zig
@@ -7,9 +7,6 @@
const fdt = @import("fdt.zig");
-pub const mmio_start: usize = 0x00000000;
-pub const mmio_end: usize = 0x80000000;
-
// Defined by linker script.
pub const text_start = @extern(*anyopaque, .{ .name = "_text_start" });
pub const text_end = @extern(*anyopaque, .{ .name = "_text_end" });
@@ -32,6 +29,8 @@ inline fn heapSize() usize {
pub const page_size: usize = 0x1000; // 4096 bytes
+pub var next_mmio_vaddr: usize = 0xff000000;
+
// Aligns an address with an offset to the next page.
// Doesn't change addresses that are already aligned.
fn pageAlign(addr: usize) usize {
@@ -426,7 +425,6 @@ pub const Table = struct {
}
pub fn mapKernel(root: *Table) !void {
- try root.identityMapRange(mmio_start, mmio_end, EntryFlags.readWrite);
try root.identityMapRange(@intFromPtr(text_start), @intFromPtr(text_end), EntryFlags.readExec);
try root.identityMapRange(@intFromPtr(rodata_start), @intFromPtr(rodata_end), EntryFlags.readOnly);
try root.identityMapRange(@intFromPtr(data_start), @intFromPtr(data_end), EntryFlags.readWrite);
@@ -440,6 +438,22 @@ pub const Table = struct {
const fdt_blob = @intFromPtr(dt_header.raw_hdr);
try root.identityMapRange(fdt_blob, fdt_blob + dt_header.total_size, EntryFlags.readOnly);
}
+
+ pub fn mapDevice(root: *Table, reg: fdt.Reg) !fdt.Reg {
+ const physical_start = reg.start & ~(page_size - 1);
+ const physical_end = (reg.start + reg.len - 1) & ~(page_size - 1);
+
+ const offset = reg.start & (page_size - 1);
+ const vaddr = next_mmio_vaddr | offset;
+
+ var paddr = physical_start;
+ while (paddr <= physical_end) : (paddr += page_size) {
+ try root.map(next_mmio_vaddr, paddr, EntryFlags.readWrite, 0);
+ next_mmio_vaddr += page_size;
+ }
+
+ return .{ .start = vaddr, .len = reg.len };
+ }
};
pub fn init() void {
diff --git a/src/uart.zig b/src/uart.zig
new file mode 100644
index 0000000..02032da
--- /dev/null
+++ b/src/uart.zig
@@ -0,0 +1,58 @@
+// SPDX-FileCopyrightText: 2024 Himbeer <himbeer@disroot.org>
+//
+// SPDX-License-Identifier: AGPL-3.0-or-later
+
+const std = @import("std");
+
+const fdt = @import("fdt.zig");
+const paging = @import("paging.zig");
+
+pub var default: ?Port = null;
+
+pub const Error = error{
+ NoSocNode,
+};
+
+pub const Port = struct {
+ reg: []volatile u8,
+ reg_shift: u6,
+
+ const lsr_thre = 1 << 5;
+
+ pub const Writer = std.io.Writer(Port, error{}, write);
+
+ fn isWriteReady(self: Port) bool {
+ return self.reg[@as(usize, 5) << self.reg_shift] & lsr_thre != 0;
+ }
+
+ pub fn write(self: Port, bytes: []const u8) !usize {
+ for (bytes) |byte| {
+ while (!self.isWriteReady()) {}
+ self.reg[0] = byte;
+ }
+
+ return bytes.len;
+ }
+
+ pub fn writer(port: Port) Writer {
+ return .{ .context = port };
+ }
+};
+
+pub fn init(kmem: *paging.Table, dt: *const fdt.Tree, allocator: std.mem.Allocator) !void {
+ var soc = fdt.findPathExact(dt, "/soc") orelse return Error.NoSocNode;
+
+ const compatible = fdt.findCompatible(&soc, &[_][]const u8{ "ns16550", "snps,dw-apb-uart" }, 1);
+
+ const node = if (compatible.len > 0) compatible[0] else return;
+
+ const regs = try node.reg(allocator);
+ defer regs.deinit();
+
+ const reg = try kmem.mapDevice(regs.items[0]);
+
+ const reg_shift_bytes = node.props.get("reg-shift");
+ const reg_shift = if (reg_shift_bytes) |bytes| std.mem.readInt(u32, bytes[0..4], .big) else 0;
+
+ default = .{ .reg = reg.slice(u8), .reg_shift = @intCast(reg_shift) };
+}