diff options
-rw-r--r-- | src/Console.zig | 46 | ||||
-rw-r--r-- | src/fdt.zig | 59 | ||||
-rw-r--r-- | src/instructions.zig | 4 | ||||
-rw-r--r-- | src/interrupts.zig | 5 | ||||
-rw-r--r-- | src/main.zig | 51 | ||||
-rw-r--r-- | src/paging.zig | 22 | ||||
-rw-r--r-- | src/uart.zig | 58 |
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) }; +} |