diff options
| author | 2026-05-29 21:59:53 -0400 | |
|---|---|---|
| committer | 2026-05-29 22:04:38 -0400 | |
| commit | fe456c2014c8d1d88b7fd5b24ebda7f7b4c53460 (patch) | |
| tree | a1ae3682aede1ea7550f4be76a79118bd6502902 /src | |
| download | quail-fe456c2014c8d1d88b7fd5b24ebda7f7b4c53460.tar.gz quail-fe456c2014c8d1d88b7fd5b24ebda7f7b4c53460.zip | |
basic physical memory page allocator
Signed-off-by: Matthew Wozniak <me@woz.blue>
Diffstat (limited to 'src')
| -rw-r--r-- | src/asm/boot.S | 42 | ||||
| -rw-r--r-- | src/asm/trap.S | 6 | ||||
| -rw-r--r-- | src/ld/virt.ld | 46 | ||||
| -rw-r--r-- | src/main.zig | 39 | ||||
| -rw-r--r-- | src/mem.zig | 41 | ||||
| -rw-r--r-- | src/uart.zig | 80 |
6 files changed, 254 insertions, 0 deletions
diff --git a/src/asm/boot.S b/src/asm/boot.S new file mode 100644 index 0000000..bd50b4c --- /dev/null +++ b/src/asm/boot.S @@ -0,0 +1,42 @@ +# bootloader for my kernel +.option norvc +.section .data +.section .text.init +.global _start +_start: + # any harts not bootstrapping need to wait for an IPI + csrr t0, mhartid + bnez t0, 3f + # satp should be zero, but make sure + csrw satp, zero +.option push +.option norelax + la gp, __global_pointer$ +.option pop + # clear bss + la a0, _bss_start + la a1, _bss_end + bgeu a0, a1, 2f +1: + sd zero, (a0) + addi a0, a0, 8 + bltu a0, a1, 1b +2: + # set up stack + la sp, _stack_end + # set kmain to the return address and then return + li t0, (0b11 << 11) | (1 << 7) | (1 << 3) + la t1, kmain + la t2, asm_trap_vector + li t3, 0 # (1 << 3) | (1 << 7) | (1 << 11) + csrw mstatus, t0 + csrw mepc, t1 + csrw mtvec, t2 + csrw mie, t3 + mret + + +# wait for interrupt +3: + wfi + j 3b diff --git a/src/asm/trap.S b/src/asm/trap.S new file mode 100644 index 0000000..672efcd --- /dev/null +++ b/src/asm/trap.S @@ -0,0 +1,6 @@ +.section .text +.global asm_trap_vector +asm_trap_vector: + # We get here when the CPU is interrupted + # for any reason. + mret diff --git a/src/ld/virt.ld b/src/ld/virt.ld new file mode 100644 index 0000000..33532c4 --- /dev/null +++ b/src/ld/virt.ld @@ -0,0 +1,46 @@ +OUTPUT_ARCH( "riscv" ) +ENTRY( _start ) + +MEMORY +{ + ram : org = 0x80000000, len = 128M +} + +SECTIONS +{ + .text : { + _text_start = .; + *(.text.init) + *(.text .text.*) + _text_end = .; + } >ram + + .rodata : { + _rodata_start = .; + *(.rodata .rodata.*) + _rodata_end = .; + } >ram + + .data : { + . = ALIGN(4096); + _data_start = .; + *(.sdata .sdata.*) + *(.data .data.*) + _data_end = .; + } >ram + + .bss : { + _bss_start = .; + *(.sbss .sbss.*) + *(.bss .bss.*) + _bss_end = .; + } >ram + + . = ALIGN(4096); + _stack_start = .; + _stack_end = . + 0x80000; + _heap_start = _stack_end; + _memory_end = ORIGIN(ram) + LENGTH(ram); + _heap_size = _memory_end - _heap_start; +} + diff --git a/src/main.zig b/src/main.zig new file mode 100644 index 0000000..7896a09 --- /dev/null +++ b/src/main.zig @@ -0,0 +1,39 @@ +const uart = @import("uart.zig"); +const mem = @import("mem.zig"); +const std = @import("std"); + +fn logFn( + comptime message_level: std.log.Level, + comptime scope: @EnumLiteral(), + comptime format: []const u8, + args: anytype, +) void { + return std.log.defaultLogFileTerminal( + message_level, + scope, + format, + args, + uart.terminal, + ) catch {}; +} + +pub const std_options: std.Options = .{ + .logFn = logFn, + .page_size_max = 4096, + .page_size_min = 4096, +}; + +pub const panic = std.debug.FullPanic(panicFn); + +fn panicFn(msg: []const u8, first_trace_addr: ?usize) noreturn { + std.log.err("kernel panic at {?}", .{first_trace_addr}); + std.log.err("{s}", .{msg}); + while (true) {} +} + +export fn kmain() callconv(.c) noreturn { + uart.init(0x1000_0000); + std.log.info("hello, world", .{}); + mem.init(); + while (true) {} +} diff --git a/src/mem.zig b/src/mem.zig new file mode 100644 index 0000000..b361b88 --- /dev/null +++ b/src/mem.zig @@ -0,0 +1,41 @@ +const std = @import("std"); + +const Page = extern struct { + next: *Page, +}; + +var head = @extern(*align(4096) Page, .{ .name = "_heap_start" }); + +pub fn init() void { + // how many pages do we have? + const page_count = @intFromPtr(@extern( + *const anyopaque, + .{ .name = "_heap_size" }, + )) / 4096; + + // go through every page and make it point it to the next one + const pages: []Page = @as([*]Page, @ptrCast(head))[0..page_count]; + for (0..page_count - 1) |i| { + pages[i].next = &pages[i + 1]; + } + + std.log.scoped(.mem).info( + "{} physical pages of 4096 B = {} B free", + .{ page_count, page_count * 4096 }, + ); +} + +// allocates 1 page by taking it off the front of the linked list +pub fn alloc() []align(4096) u8 { + const page = @as([*]align(4096) u8, @ptrCast(head))[0..4096]; + head = head.next; + return page; +} + +// free the page by putting it back on the front of the linked list +pub fn free(mem: []align(4096) u8) void { + std.debug.assert(mem.len == 4096); + const page: *Page = @ptrCast(mem.ptr); + page.next = head; + head = page; +} diff --git a/src/uart.zig b/src/uart.zig new file mode 100644 index 0000000..661084c --- /dev/null +++ b/src/uart.zig @@ -0,0 +1,80 @@ +const std = @import("std"); + +var uart_ptr: ?[*]u8 = null; + +pub fn init(base_addr: usize) void { + uart_ptr = @ptrFromInt(base_addr); + // Set the word length to 8 bits. Offset 3 is the LCR (line control + // register). + // + // The bottom 2 bits set the word length: + // 0 = 5 bits + // 1 = 6 bits + // 2 = 7 bits + // 3 = 8 bits + const lcr: u8 = 3 << 0; + uart_ptr.?[3] = lcr; + // Now enable the FIFO, which is the bottom bit of the FIFO control + // register (offset 2) + uart_ptr.?[2] = 1 << 0; + // Enable receiver buffer interrupts, which is the bottom bit of the IER + // (interrupt enable register) + uart_ptr.?[1] = 1 << 0; + // Per the datasheet, we are to set the clock divisor to: + // divisor = ceil(clock_hz/(baud_sps * 16)) + // divisor = ceil(22_729_000/(115200 * 16)) = 13 + const divisor: u16 = 13; + const divisor_least: u8 = divisor & 0xff; + const divisor_most: u8 = divisor >> 8; + // To write the divisor, we have to open the divisor latch on bit 7 of the + // line control register. + uart_ptr.?[3] = lcr | 1 << 7; + // Now we write the divisor + uart_ptr.?[0] = divisor_least; + uart_ptr.?[1] = divisor_most; + // Now we lock it again + uart_ptr.?[3] = lcr; +} + +pub fn get() ?u8 { + if (uart_ptr.?[5] & 1 != 0) { + return uart_ptr.?[0]; + } else { + return null; + } +} + +pub fn put(c: u8) void { + uart_ptr.?[0] = c; +} + +fn drain(_: *std.Io.Writer, data: []const []const u8, splat: usize) !usize { + if (uart_ptr == null) return std.Io.Writer.Error.WriteFailed; + var written: u32 = 0; + for (data, 0..) |item, i| { + if (i == data.len - 1) { + for (0..splat) |_| for (item) |c| { + put(c); + written += 1; + }; + } else { + for (item) |c| { + put(c); + written += 1; + } + } + } + return written; +} + +pub var writer: std.Io.Writer = .{ + .buffer = &.{}, + .vtable = &.{ + .drain = drain, + }, +}; + +pub var terminal: std.Io.Terminal = .{ + .writer = &writer, + .mode = .escape_codes, +}; |
