summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/asm/boot.S42
-rw-r--r--src/asm/trap.S6
-rw-r--r--src/ld/virt.ld46
-rw-r--r--src/main.zig39
-rw-r--r--src/mem.zig41
-rw-r--r--src/uart.zig80
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,
+};