diff options
| author | hachem <im@hachem.wtf> | 2026-09-25 11:31:59 +0200 |
|---|---|---|
| committer | hachem <im@hachem.wtf> | 2026-09-25 11:31:59 +0200 |
| commit | ed285c9fc665e262ed21c7bf5734acb2b7a532db (patch) | |
| tree | e51434e8069bc7219221cc1136515828d0293aca | |
| parent | d18d1da05a4d065d54787434d6bc46f3354626a5 (diff) | |
feat: load + run native ELF binaries
| -rw-r--r-- | boot/boot.zig | 33 | ||||
| -rw-r--r-- | boot/elf.zig | 2 | ||||
| -rw-r--r-- | boot/serial.zig | 18 | ||||
| -rw-r--r-- | boot/user.zig | 39 | ||||
| -rw-r--r-- | bridge/bridge.zig | 143 | ||||
| -rw-r--r-- | build.zig | 27 | ||||
| -rw-r--r-- | kernel/console.rv | 64 | ||||
| -rw-r--r-- | kernel/font.rv | 102 | ||||
| -rw-r--r-- | kernel/input.rv | 47 | ||||
| -rw-r--r-- | kernel/main.rv | 236 | ||||
| -rw-r--r-- | kernel/proc.rv | 40 | ||||
| -rw-r--r-- | kernel/shell.rv | 55 | ||||
| -rw-r--r-- | kernel/vmm.rv | 24 | ||||
| -rw-r--r-- | user/link.ld | 8 | ||||
| -rw-r--r-- | user/program.c | 22 |
15 files changed, 596 insertions, 264 deletions
diff --git a/boot/boot.zig b/boot/boot.zig index 4e9d0b4..108afc8 100644 --- a/boot/boot.zig +++ b/boot/boot.zig @@ -8,6 +8,7 @@ const pmm = @import("pmm.zig"); const heap = @import("heap.zig"); const vmm = @import("vmm.zig"); const user = @import("user.zig"); +const elf = @import("elf.zig"); const keyboard = @import("keyboard.zig"); const bridge = @import("bridge"); @@ -48,6 +49,21 @@ export var memmap_request: limine.MemoryMapRequest linksection(".limine_requests // 4 MiB stack export var stack_size_request: limine.StackSizeRequest linksection(".limine_requests") = .{ .stack_size = 4 * 1024 * 1024 }; +// wrappers matching bridge.KernelOps (pmm/vmm return optionals; the revo-facing +// primitives use 0 as the failure sentinel). +fn kPhysToVirt(p: u64) u64 { + return pmm.physToVirt(p); +} +fn kAllocFrame() u64 { + return pmm.alloc() orelse 0; +} +fn kCreateAddrspace() u64 { + return vmm.createAddressSpace() orelse 0; +} +fn kSerialNext() i64 { + return if (serial.mirrorNext()) |c| @intCast(c) else -1; +} + // ENTRY(_start) export fn _start() callconv(.c) noreturn { serial.init(); @@ -121,16 +137,27 @@ export fn _start() callconv(.c) noreturn { serial.write("vmm test: switched back OK\r\n"); } - user.runTestProgram(); + const elf_pages = (elf.hello_elf.len + 4095) / 4096; + const elf_phys = pmm.allocContig(elf_pages) orelse @panic("no room for the embedded ELF"); + @memcpy(@as([*]u8, @ptrFromInt(pmm.physToVirt(elf_phys)))[0..elf.hello_elf.len], elf.hello_elf); + const kops: bridge.KernelOps = .{ + .phys_to_virt = &kPhysToVirt, + .alloc_frame = &kAllocFrame, + .create_addrspace = &kCreateAddrspace, + .run_process = &user.runProcess, + .serial_next = &kSerialNext, + .elf_phys = elf_phys, + .elf_size = elf.hello_elf.len, + }; - // hand the kernel heap + raw framebuffer to revo and bring up the VM + // hand the kernel heap + framebuffer + low-level ops to revo and bring up the VM const fb: ?bridge.Fb = if (framebuffer.get()) |s| .{ .ptr = s.address, .width = s.width, .height = s.height, .pitch = s.pitch, } else null; - bridge.boot(heap.allocator(), serial.write, fb); + bridge.boot(heap.allocator(), serial.write, fb, kops); // - sleep until an interrupt // - drain the keyboard diff --git a/boot/elf.zig b/boot/elf.zig new file mode 100644 index 0000000..a9199db --- /dev/null +++ b/boot/elf.zig @@ -0,0 +1,2 @@ +// load c program +pub const hello_elf = @embedFile("user_program"); diff --git a/boot/serial.zig b/boot/serial.zig index fab0e66..ca0bb08 100644 --- a/boot/serial.zig +++ b/boot/serial.zig @@ -38,11 +38,29 @@ pub fn init() void { outb(COM1 + 4, 0x0B); // RTS/DSR set } +// every byte we send to the port is also captured here so the revo +// console can render the same stream +var mirror: [1 << 15]u8 = undefined; // 32 KiB, power of two +var m_head: usize = 0; // next byte the console will render +var m_tail: usize = 0; // next slot putc will write + // COM1+5 (bit 5) = transmit holding register and // spin until it's clear to send fn putc(c: u8) void { while (inb(COM1 + 5) & 0x20 == 0) {} outb(COM1, c); + mirror[m_tail & (mirror.len - 1)] = c; + m_tail +%= 1; +} + +// next un-rendered byte, or null if caught up. if the console fell far enough +// behind to lap the ring, drop the oldest bytes. +pub fn mirrorNext() ?u8 { + if (m_head == m_tail) return null; + if (m_tail -% m_head > mirror.len) m_head = m_tail -% mirror.len; + const c = mirror[m_head & (mirror.len - 1)]; + m_head +%= 1; + return c; } // a raw serial console needs \r\n, but revo (and most sane code) emits bare \n. diff --git a/boot/user.zig b/boot/user.zig index 61345e5..4272269 100644 --- a/boot/user.zig +++ b/boot/user.zig @@ -1,7 +1,7 @@ // ring3 entry + the syscall boundary // drop user mode with iretq, and come back to the kernel // -// see: - https://wiki.osdev.org/System_Calls +// see: - https://wiki.osdev.org/System_Calls // - https://wiki.osdev.org/Getting_to_Ring_3 const serial = @import("serial.zig"); @@ -52,17 +52,7 @@ pub fn enterUser(entry: u64, ustack: u64) void { g_user_stack = ustack; // this is the truest most correct statement ever // get this man a true - asm volatile ("call enterUserAsm" ::: .{ - .memory = true, - .rax = true, - .rcx = true, - .rdx = true, - .rsi = true, - .rdi = true, - .r8 = true, - .r9 = true, - .r10 = true, - .r11 = true }); + asm volatile ("call enterUserAsm" ::: .{ .memory = true, .rax = true, .rcx = true, .rdx = true, .rsi = true, .rdi = true, .r8 = true, .r9 = true, .r10 = true, .r11 = true }); } export fn enterUserAsm() callconv(.naked) void { @@ -134,15 +124,28 @@ export fn syscallStub() callconv(.naked) void { ); } -// returns 0 to resume the program, 1 to return to the kernel (exit). +pub fn runProcess(pml4: u64, entry: u64, ustack: u64) void { + const prev = vmm.activePml4(); + vmm.loadPml4(pml4); + enterUser(entry, ustack); + vmm.loadPml4(prev); +} + export fn syscallHandler(frame: *SyscallFrame) callconv(.c) u64 { switch (frame.rax) { - 0 => { // exit - serial.write("[syscall] exit -> back to kernel\r\n"); - return 1; + 0 => { // exit(code): code in rdi + return 1; // tell the stub to unwind back into the kernel }, - 1 => { // "hello" -- a fixed message, no user pointer to validate yet - serial.write("[syscall] hello from ring 3!\r\n"); + 1 => { // write(ptr, len): rdi = user pointer, rsi = length + const len = frame.rsi; + // the process's address space is active during the syscall, so the + // kernel can read the user pointer directly. cap the length so a + // bogus arg can't run us off into unmapped memory. + if (len > 0 and len <= 4096) { + const bytes: [*]const u8 = @ptrFromInt(frame.rdi); + serial.write(bytes[0..len]); // mirrored -> the revo console renders it + } + frame.rax = len; // syscall return value return 0; }, else => { diff --git a/bridge/bridge.zig b/bridge/bridge.zig index 0c934eb..8b5a30f 100644 --- a/bridge/bridge.zig +++ b/bridge/bridge.zig @@ -267,8 +267,114 @@ fn hostFbScroll(args: []const revo.Data, _: *revo.VM) anyerror!HostResult { return HostResult.data(Data.new.nil()); } -// install the primitives as globals the revo program can call by name -// idk how else we should be doing this, lung said he'll do stdlib stuff +pub const KernelOps = struct { + phys_to_virt: *const fn (u64) u64, + alloc_frame: *const fn () u64, // 0 on failure + create_addrspace: *const fn () u64, // pml4 phys, 0 on failure (raw 64-bit entry copy incl. possible NX bit -> not f64-representable) + run_process: *const fn (u64, u64, u64) void, // pml4, entry, ustack + serial_next: *const fn () i64, // next mirrored serial byte, or -1 if empty + elf_phys: u64, + elf_size: u64, +}; +var g_kops: ?KernelOps = null; + +// mem_read(phys, size) -> value: read 1/2/4/8 bytes of physical memory +fn hostMemRead(args: []const revo.Data, _: *revo.VM) anyerror!HostResult { + const ops = g_kops orelse return HostResult.other("no kernel ops"); + const phys = f64ToInt(u64, args[0].asNum().?) orelse return HostResult.other("mem_read: bad addr"); + const size = f64ToInt(u64, args[1].asNum().?) orelse return HostResult.other("mem_read: bad size"); + const v = ops.phys_to_virt(phys); + const val: u64 = switch (size) { + 1 => @as(*const u8, @ptrFromInt(v)).*, + 2 => @as(*align(1) const u16, @ptrFromInt(v)).*, + 4 => @as(*align(1) const u32, @ptrFromInt(v)).*, + 8 => @as(*align(1) const u64, @ptrFromInt(v)).*, + else => return HostResult.other("mem_read: size must be 1/2/4/8"), + }; + return HostResult.data(Data.new.num(val)); +} + +// mem_copy(dst_phys, src_phys, len): raw copy between physical regions +fn hostMemCopy(args: []const revo.Data, _: *revo.VM) anyerror!HostResult { + const ops = g_kops orelse return HostResult.other("no kernel ops"); + const dst = ops.phys_to_virt(f64ToInt(u64, args[0].asNum().?) orelse return HostResult.other("mem_copy: bad dst")); + const src = ops.phys_to_virt(f64ToInt(u64, args[1].asNum().?) orelse return HostResult.other("mem_copy: bad src")); + const len = f64ToInt(usize, args[2].asNum().?) orelse return HostResult.other("mem_copy: bad len"); + @memcpy(@as([*]u8, @ptrFromInt(dst))[0..len], @as([*]const u8, @ptrFromInt(src))[0..len]); + return HostResult.data(Data.new.nil()); +} + +// mem_zero(phys, len) +fn hostMemZero(args: []const revo.Data, _: *revo.VM) anyerror!HostResult { + const ops = g_kops orelse return HostResult.other("no kernel ops"); + const dst = ops.phys_to_virt(f64ToInt(u64, args[0].asNum().?) orelse return HostResult.other("mem_zero: bad addr")); + const len = f64ToInt(usize, args[1].asNum().?) orelse return HostResult.other("mem_zero: bad len"); + @memset(@as([*]u8, @ptrFromInt(dst))[0..len], 0); + return HostResult.data(Data.new.nil()); +} + +// mem_write(phys, val, size): write 1/2/4/8 bytes of physical memory +fn hostMemWrite(args: []const revo.Data, _: *revo.VM) anyerror!HostResult { + const ops = g_kops orelse return HostResult.other("no kernel ops"); + const phys = f64ToInt(u64, args[0].asNum().?) orelse return HostResult.other("mem_write: bad addr"); + const val = f64ToInt(u64, args[1].asNum().?) orelse return HostResult.other("mem_write: bad val"); + const size = f64ToInt(u64, args[2].asNum().?) orelse return HostResult.other("mem_write: bad size"); + const v = ops.phys_to_virt(phys); + switch (size) { + 1 => @as(*u8, @ptrFromInt(v)).* = @truncate(val), + 2 => @as(*align(1) u16, @ptrFromInt(v)).* = @truncate(val), + 4 => @as(*align(1) u32, @ptrFromInt(v)).* = @truncate(val), + 8 => @as(*align(1) u64, @ptrFromInt(v)).* = val, + else => return HostResult.other("mem_write: size must be 1/2/4/8"), + } + return HostResult.data(Data.new.nil()); +} + +// invlpg(virt): flush one page from the TLB after remapping it +fn hostInvlpg(args: []const revo.Data, _: *revo.VM) anyerror!HostResult { + const virt = f64ToInt(u64, args[0].asNum().?) orelse return HostResult.other("invlpg: bad addr"); + asm volatile ("invlpg (%[v])" + : + : [v] "r" (virt), + : .{ .memory = true }); + return HostResult.data(Data.new.nil()); +} + +fn hostFrameAlloc(_: []const revo.Data, _: *revo.VM) anyerror!HostResult { + const ops = g_kops orelse return HostResult.other("no kernel ops"); + return HostResult.data(Data.new.num(ops.alloc_frame())); +} + +fn hostAsCreate(_: []const revo.Data, _: *revo.VM) anyerror!HostResult { + const ops = g_kops orelse return HostResult.other("no kernel ops"); + return HostResult.data(Data.new.num(ops.create_addrspace())); +} + +fn hostProcRun(args: []const revo.Data, _: *revo.VM) anyerror!HostResult { + const ops = g_kops orelse return HostResult.other("no kernel ops"); + const as = f64ToInt(u64, args[0].asNum().?) orelse return HostResult.other("proc_run: bad as"); + const entry = f64ToInt(u64, args[1].asNum().?) orelse return HostResult.other("proc_run: bad entry"); + const ustack = f64ToInt(u64, args[2].asNum().?) orelse return HostResult.other("proc_run: bad ustack"); + ops.run_process(as, entry, ustack); + return HostResult.data(Data.new.nil()); +} + +// serial_next() -> byte or -1 +fn hostSerialNext(_: []const revo.Data, _: *revo.VM) anyerror!HostResult { + const ops = g_kops orelse return HostResult.other("no kernel ops"); + return HostResult.data(Data.new.num(ops.serial_next())); +} + +fn hostElfPhys(_: []const revo.Data, _: *revo.VM) anyerror!HostResult { + const ops = g_kops orelse return HostResult.other("no kernel ops"); + return HostResult.data(Data.new.num(ops.elf_phys)); +} + +fn hostElfSize(_: []const revo.Data, _: *revo.VM) anyerror!HostResult { + const ops = g_kops orelse return HostResult.other("no kernel ops"); + return HostResult.data(Data.new.num(ops.elf_size)); +} + fn registerPrimitives(vm: *revo.VM) !void { const define = revo.std_lib.define; const T = revo.std_lib.TypeSpec; @@ -279,16 +385,35 @@ fn registerPrimitives(vm: *revo.VM) !void { try vm.registerGlobal("fb_height", try vm.installHost("fb_height", define(&[_]T{}, hostFbHeight))); try vm.registerGlobal("fill_rect", try vm.installHost("fill_rect", define(&[_]T{ .number, .number, .number, .number, .number }, hostFillRect))); try vm.registerGlobal("fb_scroll", try vm.installHost("fb_scroll", define(&[_]T{ .number, .number }, hostFbScroll))); + // low-level: memory + address spaces + process launch (revo's ELF loader) + try vm.registerGlobal("mem_read", try vm.installHost("mem_read", define(&[_]T{ .number, .number }, hostMemRead))); + try vm.registerGlobal("mem_copy", try vm.installHost("mem_copy", define(&[_]T{ .number, .number, .number }, hostMemCopy))); + try vm.registerGlobal("mem_zero", try vm.installHost("mem_zero", define(&[_]T{ .number, .number }, hostMemZero))); + try vm.registerGlobal("mem_write", try vm.installHost("mem_write", define(&[_]T{ .number, .number, .number }, hostMemWrite))); + try vm.registerGlobal("invlpg", try vm.installHost("invlpg", define(&[_]T{.number}, hostInvlpg))); + try vm.registerGlobal("frame_alloc", try vm.installHost("frame_alloc", define(&[_]T{}, hostFrameAlloc))); + try vm.registerGlobal("as_create", try vm.installHost("as_create", define(&[_]T{}, hostAsCreate))); + try vm.registerGlobal("proc_run", try vm.installHost("proc_run", define(&[_]T{ .number, .number, .number }, hostProcRun))); + try vm.registerGlobal("serial_next", try vm.installHost("serial_next", define(&[_]T{}, hostSerialNext))); + try vm.registerGlobal("elf_phys", try vm.installHost("elf_phys", define(&[_]T{}, hostElfPhys))); + try vm.registerGlobal("elf_size", try vm.installHost("elf_size", define(&[_]T{}, hostElfSize))); } // the VM outlives boot() now, so we juts make this hoe static var g_vm: ?*revo.VM = null; -const init_program = @embedFile("kernel_main"); +const init_program = @embedFile("k_font") ++ "\n" ++ + @embedFile("k_console") ++ "\n" ++ + @embedFile("k_vmm") ++ "\n" ++ + @embedFile("k_proc") ++ "\n" ++ + @embedFile("k_shell") ++ "\n" ++ + @embedFile("k_input") ++ "\n" ++ + @embedFile("k_main"); -pub fn boot(alloc: std.mem.Allocator, out: Sink, fb: ?Fb) void { +pub fn boot(alloc: std.mem.Allocator, out: Sink, fb: ?Fb, ops: ?KernelOps) void { sink = out; g_fb = fb; + g_kops = ops; // NOTE: we drive the VM directly (compile -> run) instead of the high-level // Runtime.eval wrapper, which has a latent type error in revo that only @@ -328,9 +453,9 @@ pub fn boot(alloc: std.mem.Allocator, out: Sink, fb: ?Fb) void { return; }, }; + // NOTE: on purpose we don't free artifact.instructions/spans cause on_key's // bytecode lives in there and we call it for the life of the kernel - vm.setProgramDebugInfo(artifact.spans, "", "kernel") catch { out("bridge: setProgramDebugInfo failed\r\n"); return; @@ -362,4 +487,12 @@ pub fn onKey(scancode: u8) void { _ = vm.callFunctionParts(cb, null, &[_]revo.Data{revo.Data.new.num(scancode)}, null) catch { sink("bridge: on_key threw\r\n"); }; + flushConsole(vm); +} + +// render any serial output produced while handling the key +fn flushConsole(vm: *revo.VM) void { + const f = vm.getGlobal("flush_console") orelse return; + if (f.asFunction() == null) return; + _ = vm.callFunctionParts(f, null, &.{}, null) catch {}; } @@ -63,12 +63,33 @@ pub fn build(b: *std.Build) void { .link_libc = false, }); bridge_mod.addImport("revo", revo_mod); - // the OS source lives in kernel/*.rv; @embedFile("kernel_main") in the bridge - // pulls it in at build time (an anonymous import lets it cross the module dir) - bridge_mod.addAnonymousImport("kernel_main", .{ .root_source_file = b.path("kernel/main.rv") }); + inline for (.{ + .{ "k_font", "kernel/font.rv" }, + .{ "k_console", "kernel/console.rv" }, + .{ "k_vmm", "kernel/vmm.rv" }, + .{ "k_proc", "kernel/proc.rv" }, + .{ "k_shell", "kernel/shell.rv" }, + .{ "k_input", "kernel/input.rv" }, + .{ "k_main", "kernel/main.rv" }, + }) |e| { + bridge_mod.addAnonymousImport(e[0], .{ .root_source_file = b.path(e[1]) }); + } kernel.root_module.addImport("bridge", bridge_mod); + const cc = b.addSystemCommand(&.{ + "zig", "cc", + "-target", "x86_64-freestanding", + "-ffreestanding", "-nostdlib", + "-static", "-no-pie", + "-fno-stack-protector", "-fno-sanitize=all", + "-Wl,-T,user/link.ld", "-Wl,--build-id=none", + "-o", + }); + const program_elf = cc.addOutputFileArg("user_program.elf"); + cc.addFileArg(b.path("user/program.c")); + kernel.root_module.addAnonymousImport("user_program", .{ .root_source_file = program_elf }); + b.installArtifact(kernel); } diff --git a/kernel/console.rv b/kernel/console.rv new file mode 100644 index 0000000..a448ef9 --- /dev/null +++ b/kernel/console.rv @@ -0,0 +1,64 @@ +const FW = 8 # glyph width in pixels +const FH = 16 # glyph height in pixels (8x16 VGA bitmap font) +const FG = 0xd0d0d0 +const CON_BG = 0x101018 + +global con = { col = 0, row = 0, scale = 1 } + +# cell size in pixels at the current zoom. named cell_* (not cw/ch) so they don't +# collide with draw_glyph's `ch` character parameter. +fn cell_w() FW * con.scale +fn cell_h() FH * con.scale +fn con_cols() fb_width() / cell_w() +fn con_rows() fb_height() / cell_h() + +fn draw_glyph(col, row, ch) do + const x0 = col * cell_w() + const y0 = row * cell_h() + fill_rect(x0, y0, cell_w(), cell_h(), CON_BG) # clear the cell + const code = ch:ascii() + if code < 32 return :nil + if code > 127 return :nil + const g = FONT[code - 32] + for fy in 0..FH do + const bits = g[fy] + for fx in 0..FW do + if (bits shr fx) band 1 == 1 fill_rect(x0 + fx * con.scale, y0 + fy * con.scale, con.scale, con.scale, FG) + end + end +end + +fn con_newline() do + con.col = 0 + con.row = con.row + 1 + if con.row >= con_rows() do + fb_scroll(cell_h(), CON_BG) + con.row = con_rows() - 1 + end +end + +# we dont do Shift/Cntrl +# thats a job for future me lmao +fn con_putc(ch) do + const code = ch:ascii() + if code == 10 do con_newline() return :nil end # \n + if code == 13 do con.col = 0 return :nil end # \r + if code == 8 do # backspace: move left + if con.col > 0 con.col = con.col - 1 + return :nil + end + if code < 32 return :nil + draw_glyph(con.col, con.row, ch) + con.col = con.col + 1 + if con.col >= con_cols() con_newline() +end + +fn puts(s) serial_puts(s) + +global flush_console = fn() do + let c = serial_next() + while c >= 0 do + con_putc(string.of_ascii(c)) + c = serial_next() + end +end diff --git a/kernel/font.rv b/kernel/font.rv new file mode 100644 index 0000000..7bd7c6a --- /dev/null +++ b/kernel/font.rv @@ -0,0 +1,102 @@ +# IBM VGA 8x16 font (from Linux lib/fonts/font_8x16.c) +# ASCII 0x20..0x7f, indexed by (code - 32). +# one byte per row, 16 rows. +# bit 0 (LSB) of a byte is the leftmost pixel (VGA data is MSB-first, reversed here). +global FONT = { + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, # space + { 0x00, 0x00, 0x18, 0x3C, 0x3C, 0x3C, 0x18, 0x18, 0x18, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00 }, # ! + { 0x00, 0x66, 0x66, 0x66, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, # " + { 0x00, 0x00, 0x00, 0x36, 0x36, 0x7F, 0x36, 0x36, 0x36, 0x7F, 0x36, 0x36, 0x00, 0x00, 0x00, 0x00 }, # # + { 0x18, 0x18, 0x3E, 0x63, 0x43, 0x03, 0x3E, 0x60, 0x60, 0x61, 0x63, 0x3E, 0x18, 0x18, 0x00, 0x00 }, # $ + { 0x00, 0x00, 0x00, 0x00, 0x43, 0x63, 0x30, 0x18, 0x0C, 0x06, 0x63, 0x61, 0x00, 0x00, 0x00, 0x00 }, # % + { 0x00, 0x00, 0x1C, 0x36, 0x36, 0x1C, 0x6E, 0x3B, 0x33, 0x33, 0x33, 0x6E, 0x00, 0x00, 0x00, 0x00 }, # & + { 0x00, 0x0C, 0x0C, 0x0C, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, # ' + { 0x00, 0x00, 0x30, 0x18, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x18, 0x30, 0x00, 0x00, 0x00, 0x00 }, # ( + { 0x00, 0x00, 0x0C, 0x18, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x18, 0x0C, 0x00, 0x00, 0x00, 0x00 }, # ) + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x3C, 0xFF, 0x3C, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, # * + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x7E, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, # + + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0x0C, 0x00, 0x00, 0x00 }, # , + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, # - + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00 }, # . + { 0x00, 0x00, 0x00, 0x00, 0x40, 0x60, 0x30, 0x18, 0x0C, 0x06, 0x03, 0x01, 0x00, 0x00, 0x00, 0x00 }, # / + { 0x00, 0x00, 0x1C, 0x36, 0x63, 0x63, 0x6B, 0x6B, 0x63, 0x63, 0x36, 0x1C, 0x00, 0x00, 0x00, 0x00 }, # 0 + { 0x00, 0x00, 0x18, 0x1C, 0x1E, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x7E, 0x00, 0x00, 0x00, 0x00 }, # 1 + { 0x00, 0x00, 0x3E, 0x63, 0x60, 0x30, 0x18, 0x0C, 0x06, 0x03, 0x63, 0x7F, 0x00, 0x00, 0x00, 0x00 }, # 2 + { 0x00, 0x00, 0x3E, 0x63, 0x60, 0x60, 0x3C, 0x60, 0x60, 0x60, 0x63, 0x3E, 0x00, 0x00, 0x00, 0x00 }, # 3 + { 0x00, 0x00, 0x30, 0x38, 0x3C, 0x36, 0x33, 0x7F, 0x30, 0x30, 0x30, 0x78, 0x00, 0x00, 0x00, 0x00 }, # 4 + { 0x00, 0x00, 0x7F, 0x03, 0x03, 0x03, 0x3F, 0x60, 0x60, 0x60, 0x63, 0x3E, 0x00, 0x00, 0x00, 0x00 }, # 5 + { 0x00, 0x00, 0x1C, 0x06, 0x03, 0x03, 0x3F, 0x63, 0x63, 0x63, 0x63, 0x3E, 0x00, 0x00, 0x00, 0x00 }, # 6 + { 0x00, 0x00, 0x7F, 0x63, 0x60, 0x60, 0x30, 0x18, 0x0C, 0x0C, 0x0C, 0x0C, 0x00, 0x00, 0x00, 0x00 }, # 7 + { 0x00, 0x00, 0x3E, 0x63, 0x63, 0x63, 0x3E, 0x63, 0x63, 0x63, 0x63, 0x3E, 0x00, 0x00, 0x00, 0x00 }, # 8 + { 0x00, 0x00, 0x3E, 0x63, 0x63, 0x63, 0x7E, 0x60, 0x60, 0x60, 0x30, 0x1E, 0x00, 0x00, 0x00, 0x00 }, # 9 + { 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00 }, # : + { 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x18, 0x18, 0x0C, 0x00, 0x00, 0x00, 0x00 }, # ; + { 0x00, 0x00, 0x00, 0x60, 0x30, 0x18, 0x0C, 0x06, 0x0C, 0x18, 0x30, 0x60, 0x00, 0x00, 0x00, 0x00 }, # < + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x7E, 0x00, 0x00, 0x7E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, # = + { 0x00, 0x00, 0x00, 0x06, 0x0C, 0x18, 0x30, 0x60, 0x30, 0x18, 0x0C, 0x06, 0x00, 0x00, 0x00, 0x00 }, # > + { 0x00, 0x00, 0x3E, 0x63, 0x63, 0x30, 0x18, 0x18, 0x18, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00 }, # ? + { 0x00, 0x00, 0x00, 0x3E, 0x63, 0x63, 0x7B, 0x7B, 0x7B, 0x3B, 0x03, 0x3E, 0x00, 0x00, 0x00, 0x00 }, # @ + { 0x00, 0x00, 0x08, 0x1C, 0x36, 0x63, 0x63, 0x7F, 0x63, 0x63, 0x63, 0x63, 0x00, 0x00, 0x00, 0x00 }, # A + { 0x00, 0x00, 0x3F, 0x66, 0x66, 0x66, 0x3E, 0x66, 0x66, 0x66, 0x66, 0x3F, 0x00, 0x00, 0x00, 0x00 }, # B + { 0x00, 0x00, 0x3C, 0x66, 0x43, 0x03, 0x03, 0x03, 0x03, 0x43, 0x66, 0x3C, 0x00, 0x00, 0x00, 0x00 }, # C + { 0x00, 0x00, 0x1F, 0x36, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x36, 0x1F, 0x00, 0x00, 0x00, 0x00 }, # D + { 0x00, 0x00, 0x7F, 0x66, 0x46, 0x16, 0x1E, 0x16, 0x06, 0x46, 0x66, 0x7F, 0x00, 0x00, 0x00, 0x00 }, # E + { 0x00, 0x00, 0x7F, 0x66, 0x46, 0x16, 0x1E, 0x16, 0x06, 0x06, 0x06, 0x0F, 0x00, 0x00, 0x00, 0x00 }, # F + { 0x00, 0x00, 0x3C, 0x66, 0x43, 0x03, 0x03, 0x7B, 0x63, 0x63, 0x66, 0x5C, 0x00, 0x00, 0x00, 0x00 }, # G + { 0x00, 0x00, 0x63, 0x63, 0x63, 0x63, 0x7F, 0x63, 0x63, 0x63, 0x63, 0x63, 0x00, 0x00, 0x00, 0x00 }, # H + { 0x00, 0x00, 0x3C, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3C, 0x00, 0x00, 0x00, 0x00 }, # I + { 0x00, 0x00, 0x78, 0x30, 0x30, 0x30, 0x30, 0x30, 0x33, 0x33, 0x33, 0x1E, 0x00, 0x00, 0x00, 0x00 }, # J + { 0x00, 0x00, 0x67, 0x66, 0x66, 0x36, 0x1E, 0x1E, 0x36, 0x66, 0x66, 0x67, 0x00, 0x00, 0x00, 0x00 }, # K + { 0x00, 0x00, 0x0F, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x46, 0x66, 0x7F, 0x00, 0x00, 0x00, 0x00 }, # L + { 0x00, 0x00, 0x63, 0x77, 0x7F, 0x7F, 0x6B, 0x63, 0x63, 0x63, 0x63, 0x63, 0x00, 0x00, 0x00, 0x00 }, # M + { 0x00, 0x00, 0x63, 0x67, 0x6F, 0x7F, 0x7B, 0x73, 0x63, 0x63, 0x63, 0x63, 0x00, 0x00, 0x00, 0x00 }, # N + { 0x00, 0x00, 0x3E, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x3E, 0x00, 0x00, 0x00, 0x00 }, # O + { 0x00, 0x00, 0x3F, 0x66, 0x66, 0x66, 0x3E, 0x06, 0x06, 0x06, 0x06, 0x0F, 0x00, 0x00, 0x00, 0x00 }, # P + { 0x00, 0x00, 0x3E, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x6B, 0x7B, 0x3E, 0x30, 0x70, 0x00, 0x00 }, # Q + { 0x00, 0x00, 0x3F, 0x66, 0x66, 0x66, 0x3E, 0x36, 0x66, 0x66, 0x66, 0x67, 0x00, 0x00, 0x00, 0x00 }, # R + { 0x00, 0x00, 0x3E, 0x63, 0x63, 0x06, 0x1C, 0x30, 0x60, 0x63, 0x63, 0x3E, 0x00, 0x00, 0x00, 0x00 }, # S + { 0x00, 0x00, 0x7E, 0x7E, 0x5A, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3C, 0x00, 0x00, 0x00, 0x00 }, # T + { 0x00, 0x00, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x3E, 0x00, 0x00, 0x00, 0x00 }, # U + { 0x00, 0x00, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x36, 0x1C, 0x08, 0x00, 0x00, 0x00, 0x00 }, # V + { 0x00, 0x00, 0x63, 0x63, 0x63, 0x63, 0x6B, 0x6B, 0x6B, 0x7F, 0x77, 0x36, 0x00, 0x00, 0x00, 0x00 }, # W + { 0x00, 0x00, 0x63, 0x63, 0x36, 0x3E, 0x1C, 0x1C, 0x3E, 0x36, 0x63, 0x63, 0x00, 0x00, 0x00, 0x00 }, # X + { 0x00, 0x00, 0x66, 0x66, 0x66, 0x66, 0x3C, 0x18, 0x18, 0x18, 0x18, 0x3C, 0x00, 0x00, 0x00, 0x00 }, # Y + { 0x00, 0x00, 0x7F, 0x63, 0x61, 0x30, 0x18, 0x0C, 0x06, 0x43, 0x63, 0x7F, 0x00, 0x00, 0x00, 0x00 }, # Z + { 0x00, 0x00, 0x3C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x3C, 0x00, 0x00, 0x00, 0x00 }, # [ + { 0x00, 0x00, 0x00, 0x01, 0x03, 0x07, 0x0E, 0x1C, 0x38, 0x70, 0x60, 0x40, 0x00, 0x00, 0x00, 0x00 }, # \ + { 0x00, 0x00, 0x3C, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x3C, 0x00, 0x00, 0x00, 0x00 }, # ] + { 0x08, 0x1C, 0x36, 0x63, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, # ^ + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00 }, # _ + { 0x00, 0x0C, 0x18, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, # ` + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x1E, 0x30, 0x3E, 0x33, 0x33, 0x33, 0x6E, 0x00, 0x00, 0x00, 0x00 }, # a + { 0x00, 0x00, 0x07, 0x06, 0x06, 0x1E, 0x36, 0x66, 0x66, 0x66, 0x66, 0x3E, 0x00, 0x00, 0x00, 0x00 }, # b + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x3E, 0x63, 0x03, 0x03, 0x03, 0x63, 0x3E, 0x00, 0x00, 0x00, 0x00 }, # c + { 0x00, 0x00, 0x38, 0x30, 0x30, 0x3C, 0x36, 0x33, 0x33, 0x33, 0x33, 0x6E, 0x00, 0x00, 0x00, 0x00 }, # d + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x3E, 0x63, 0x7F, 0x03, 0x03, 0x63, 0x3E, 0x00, 0x00, 0x00, 0x00 }, # e + { 0x00, 0x00, 0x38, 0x6C, 0x4C, 0x0C, 0x1E, 0x0C, 0x0C, 0x0C, 0x0C, 0x1E, 0x00, 0x00, 0x00, 0x00 }, # f + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x6E, 0x33, 0x33, 0x33, 0x33, 0x33, 0x3E, 0x30, 0x33, 0x1E, 0x00 }, # g + { 0x00, 0x00, 0x07, 0x06, 0x06, 0x36, 0x6E, 0x66, 0x66, 0x66, 0x66, 0x67, 0x00, 0x00, 0x00, 0x00 }, # h + { 0x00, 0x00, 0x18, 0x18, 0x00, 0x1C, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3C, 0x00, 0x00, 0x00, 0x00 }, # i + { 0x00, 0x00, 0x60, 0x60, 0x00, 0x70, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x66, 0x66, 0x3C, 0x00 }, # j + { 0x00, 0x00, 0x07, 0x06, 0x06, 0x66, 0x36, 0x1E, 0x1E, 0x36, 0x66, 0x67, 0x00, 0x00, 0x00, 0x00 }, # k + { 0x00, 0x00, 0x1C, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3C, 0x00, 0x00, 0x00, 0x00 }, # l + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x37, 0x7F, 0x6B, 0x6B, 0x6B, 0x6B, 0x63, 0x00, 0x00, 0x00, 0x00 }, # m + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x3B, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00 }, # n + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x3E, 0x63, 0x63, 0x63, 0x63, 0x63, 0x3E, 0x00, 0x00, 0x00, 0x00 }, # o + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x3B, 0x66, 0x66, 0x66, 0x66, 0x66, 0x3E, 0x06, 0x06, 0x0F, 0x00 }, # p + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x6E, 0x33, 0x33, 0x33, 0x33, 0x33, 0x3E, 0x30, 0x30, 0x78, 0x00 }, # q + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x3B, 0x6E, 0x66, 0x06, 0x06, 0x06, 0x0F, 0x00, 0x00, 0x00, 0x00 }, # r + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x3E, 0x63, 0x06, 0x1C, 0x30, 0x63, 0x3E, 0x00, 0x00, 0x00, 0x00 }, # s + { 0x00, 0x00, 0x08, 0x0C, 0x0C, 0x3F, 0x0C, 0x0C, 0x0C, 0x0C, 0x6C, 0x38, 0x00, 0x00, 0x00, 0x00 }, # t + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x6E, 0x00, 0x00, 0x00, 0x00 }, # u + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0x63, 0x63, 0x63, 0x63, 0x36, 0x1C, 0x00, 0x00, 0x00, 0x00 }, # v + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0x63, 0x6B, 0x6B, 0x6B, 0x7F, 0x36, 0x00, 0x00, 0x00, 0x00 }, # w + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0x36, 0x1C, 0x1C, 0x1C, 0x36, 0x63, 0x00, 0x00, 0x00, 0x00 }, # x + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x7E, 0x60, 0x30, 0x1F, 0x00 }, # y + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0x33, 0x18, 0x0C, 0x06, 0x63, 0x7F, 0x00, 0x00, 0x00, 0x00 }, # z + { 0x00, 0x00, 0x70, 0x18, 0x18, 0x18, 0x0E, 0x18, 0x18, 0x18, 0x18, 0x70, 0x00, 0x00, 0x00, 0x00 }, # { + { 0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00 }, # | + { 0x00, 0x00, 0x0E, 0x18, 0x18, 0x18, 0x70, 0x18, 0x18, 0x18, 0x18, 0x0E, 0x00, 0x00, 0x00, 0x00 }, # } + { 0x00, 0x6E, 0x3B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, # ~ + { 0x00, 0x00, 0x00, 0x00, 0x08, 0x1C, 0x36, 0x63, 0x63, 0x63, 0x7F, 0x00, 0x00, 0x00, 0x00, 0x00 }, # +} diff --git a/kernel/input.rv b/kernel/input.rv new file mode 100644 index 0000000..33cf65d --- /dev/null +++ b/kernel/input.rv @@ -0,0 +1,47 @@ +# keyboard scancode table +# ts is NOT a good way to write a driver LMAO +const KBD = " 1234567890-= qwertyuiop[] asdfghjkl;'` \\zxcvbnm,./ " +# same layout, shifted. index 42 (left shift) / 54 (right shift) stay blank. +const KBD_SHIFT = " !@#$%^&*()_+ QWERTYUIOP{} ASDFGHJKL:\"~ |ZXCVBNM<>? " + +# 8259 PIC +fn pic_remap() do + outb(0x20, 0x11) outb(0xa0, 0x11) + outb(0x21, 0x20) outb(0xa1, 0x28) + outb(0x21, 0x04) outb(0xa1, 0x02) + outb(0x21, 0x01) outb(0xa1, 0x01) + outb(0x21, 0xff) outb(0xa1, 0xff) +end +fn pic_unmask(line) outb(0x21, inb(0x21) band (255 bxor (1 shl line))) + +# she stroke my keys till i handle +# no fr this is just a key stroke handler +global sh = { line = "", shift = :false } +global on_key = fn(code) do + # shift: 0x2a/0x36 press, + # 0xaa/0xb6 release + # index a blank cell and type a stray space. + if code == 0x2a do sh.shift = :true return :nil end + if code == 0x36 do sh.shift = :true return :nil end + if code == 0xaa do sh.shift = :false return :nil end + if code == 0xb6 do sh.shift = :false return :nil end + if code >= 128 return :nil # other key releases + if code == 28 do # Enter + puts("\n") + run(sh.line) + sh.line = "" + return :nil + end + if code == 14 do # Backspace + if sh.line:len() > 0 do + sh.line = sh.line:sub(0, sh.line:len() - 1) + puts("\x08 \x08") + end + return :nil + end + if code < 2 return :nil + if code >= KBD:len() return :nil + const ch = if sh.shift KBD_SHIFT[code] else KBD[code] + sh.line = sh.line ~ ch + puts(ch) +end diff --git a/kernel/main.rv b/kernel/main.rv index 8fe3b81..d3f1b23 100644 --- a/kernel/main.rv +++ b/kernel/main.rv @@ -1,240 +1,6 @@ -# font8x8_basic (printable ASCII 0x20..0x7f), one row of 8 glyphs per line -# each glyph is 8 bytes; bit 0 (LSB) of a byte is the leftmost pixel -global FONT = { - { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, - { 0x18, 0x3C, 0x3C, 0x18, 0x18, 0x00, 0x18, 0x00 }, - { 0x36, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, - { 0x36, 0x36, 0x7F, 0x36, 0x7F, 0x36, 0x36, 0x00 }, - { 0x0C, 0x3E, 0x03, 0x1E, 0x30, 0x1F, 0x0C, 0x00 }, - { 0x00, 0x63, 0x33, 0x18, 0x0C, 0x66, 0x63, 0x00 }, - { 0x1C, 0x36, 0x1C, 0x6E, 0x3B, 0x33, 0x6E, 0x00 }, - { 0x06, 0x06, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00 }, - { 0x18, 0x0C, 0x06, 0x06, 0x06, 0x0C, 0x18, 0x00 }, - { 0x06, 0x0C, 0x18, 0x18, 0x18, 0x0C, 0x06, 0x00 }, - { 0x00, 0x66, 0x3C, 0xFF, 0x3C, 0x66, 0x00, 0x00 }, - { 0x00, 0x0C, 0x0C, 0x3F, 0x0C, 0x0C, 0x00, 0x00 }, - { 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x0C, 0x06 }, - { 0x00, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x00, 0x00 }, - { 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x0C, 0x00 }, - { 0x60, 0x30, 0x18, 0x0C, 0x06, 0x03, 0x01, 0x00 }, - { 0x3E, 0x63, 0x73, 0x7B, 0x6F, 0x67, 0x3E, 0x00 }, - { 0x0C, 0x0E, 0x0C, 0x0C, 0x0C, 0x0C, 0x3F, 0x00 }, - { 0x1E, 0x33, 0x30, 0x1C, 0x06, 0x33, 0x3F, 0x00 }, - { 0x1E, 0x33, 0x30, 0x1C, 0x30, 0x33, 0x1E, 0x00 }, - { 0x38, 0x3C, 0x36, 0x33, 0x7F, 0x30, 0x78, 0x00 }, - { 0x3F, 0x03, 0x1F, 0x30, 0x30, 0x33, 0x1E, 0x00 }, - { 0x1C, 0x06, 0x03, 0x1F, 0x33, 0x33, 0x1E, 0x00 }, - { 0x3F, 0x33, 0x30, 0x18, 0x0C, 0x0C, 0x0C, 0x00 }, - { 0x1E, 0x33, 0x33, 0x1E, 0x33, 0x33, 0x1E, 0x00 }, - { 0x1E, 0x33, 0x33, 0x3E, 0x30, 0x18, 0x0E, 0x00 }, - { 0x00, 0x0C, 0x0C, 0x00, 0x00, 0x0C, 0x0C, 0x00 }, - { 0x00, 0x0C, 0x0C, 0x00, 0x00, 0x0C, 0x0C, 0x06 }, - { 0x18, 0x0C, 0x06, 0x03, 0x06, 0x0C, 0x18, 0x00 }, - { 0x00, 0x00, 0x3F, 0x00, 0x00, 0x3F, 0x00, 0x00 }, - { 0x06, 0x0C, 0x18, 0x30, 0x18, 0x0C, 0x06, 0x00 }, - { 0x1E, 0x33, 0x30, 0x18, 0x0C, 0x00, 0x0C, 0x00 }, - { 0x3E, 0x63, 0x7B, 0x7B, 0x7B, 0x03, 0x1E, 0x00 }, - { 0x0C, 0x1E, 0x33, 0x33, 0x3F, 0x33, 0x33, 0x00 }, - { 0x3F, 0x66, 0x66, 0x3E, 0x66, 0x66, 0x3F, 0x00 }, - { 0x3C, 0x66, 0x03, 0x03, 0x03, 0x66, 0x3C, 0x00 }, - { 0x1F, 0x36, 0x66, 0x66, 0x66, 0x36, 0x1F, 0x00 }, - { 0x7F, 0x46, 0x16, 0x1E, 0x16, 0x46, 0x7F, 0x00 }, - { 0x7F, 0x46, 0x16, 0x1E, 0x16, 0x06, 0x0F, 0x00 }, - { 0x3C, 0x66, 0x03, 0x03, 0x73, 0x66, 0x7C, 0x00 }, - { 0x33, 0x33, 0x33, 0x3F, 0x33, 0x33, 0x33, 0x00 }, - { 0x1E, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x1E, 0x00 }, - { 0x78, 0x30, 0x30, 0x30, 0x33, 0x33, 0x1E, 0x00 }, - { 0x67, 0x66, 0x36, 0x1E, 0x36, 0x66, 0x67, 0x00 }, - { 0x0F, 0x06, 0x06, 0x06, 0x46, 0x66, 0x7F, 0x00 }, - { 0x63, 0x77, 0x7F, 0x7F, 0x6B, 0x63, 0x63, 0x00 }, - { 0x63, 0x67, 0x6F, 0x7B, 0x73, 0x63, 0x63, 0x00 }, - { 0x1C, 0x36, 0x63, 0x63, 0x63, 0x36, 0x1C, 0x00 }, - { 0x3F, 0x66, 0x66, 0x3E, 0x06, 0x06, 0x0F, 0x00 }, - { 0x1E, 0x33, 0x33, 0x33, 0x3B, 0x1E, 0x38, 0x00 }, - { 0x3F, 0x66, 0x66, 0x3E, 0x36, 0x66, 0x67, 0x00 }, - { 0x1E, 0x33, 0x07, 0x0E, 0x38, 0x33, 0x1E, 0x00 }, - { 0x3F, 0x2D, 0x0C, 0x0C, 0x0C, 0x0C, 0x1E, 0x00 }, - { 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x3F, 0x00 }, - { 0x33, 0x33, 0x33, 0x33, 0x33, 0x1E, 0x0C, 0x00 }, - { 0x63, 0x63, 0x63, 0x6B, 0x7F, 0x77, 0x63, 0x00 }, - { 0x63, 0x63, 0x36, 0x1C, 0x1C, 0x36, 0x63, 0x00 }, - { 0x33, 0x33, 0x33, 0x1E, 0x0C, 0x0C, 0x1E, 0x00 }, - { 0x7F, 0x63, 0x31, 0x18, 0x4C, 0x66, 0x7F, 0x00 }, - { 0x1E, 0x06, 0x06, 0x06, 0x06, 0x06, 0x1E, 0x00 }, - { 0x03, 0x06, 0x0C, 0x18, 0x30, 0x60, 0x40, 0x00 }, - { 0x1E, 0x18, 0x18, 0x18, 0x18, 0x18, 0x1E, 0x00 }, - { 0x08, 0x1C, 0x36, 0x63, 0x00, 0x00, 0x00, 0x00 }, - { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF }, - { 0x0C, 0x0C, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00 }, - { 0x00, 0x00, 0x1E, 0x30, 0x3E, 0x33, 0x6E, 0x00 }, - { 0x07, 0x06, 0x06, 0x3E, 0x66, 0x66, 0x3B, 0x00 }, - { 0x00, 0x00, 0x1E, 0x33, 0x03, 0x33, 0x1E, 0x00 }, - { 0x38, 0x30, 0x30, 0x3E, 0x33, 0x33, 0x6E, 0x00 }, - { 0x00, 0x00, 0x1E, 0x33, 0x3F, 0x03, 0x1E, 0x00 }, - { 0x1C, 0x36, 0x06, 0x0F, 0x06, 0x06, 0x0F, 0x00 }, - { 0x00, 0x00, 0x6E, 0x33, 0x33, 0x3E, 0x30, 0x1F }, - { 0x07, 0x06, 0x36, 0x6E, 0x66, 0x66, 0x67, 0x00 }, - { 0x0C, 0x00, 0x0E, 0x0C, 0x0C, 0x0C, 0x1E, 0x00 }, - { 0x30, 0x00, 0x30, 0x30, 0x30, 0x33, 0x33, 0x1E }, - { 0x07, 0x06, 0x66, 0x36, 0x1E, 0x36, 0x67, 0x00 }, - { 0x0E, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x1E, 0x00 }, - { 0x00, 0x00, 0x33, 0x7F, 0x7F, 0x6B, 0x63, 0x00 }, - { 0x00, 0x00, 0x1F, 0x33, 0x33, 0x33, 0x33, 0x00 }, - { 0x00, 0x00, 0x1E, 0x33, 0x33, 0x33, 0x1E, 0x00 }, - { 0x00, 0x00, 0x3B, 0x66, 0x66, 0x3E, 0x06, 0x0F }, - { 0x00, 0x00, 0x6E, 0x33, 0x33, 0x3E, 0x30, 0x78 }, - { 0x00, 0x00, 0x3B, 0x6E, 0x66, 0x06, 0x0F, 0x00 }, - { 0x00, 0x00, 0x3E, 0x03, 0x1E, 0x30, 0x1F, 0x00 }, - { 0x08, 0x0C, 0x3E, 0x0C, 0x0C, 0x2C, 0x18, 0x00 }, - { 0x00, 0x00, 0x33, 0x33, 0x33, 0x33, 0x6E, 0x00 }, - { 0x00, 0x00, 0x33, 0x33, 0x33, 0x1E, 0x0C, 0x00 }, - { 0x00, 0x00, 0x63, 0x6B, 0x7F, 0x7F, 0x36, 0x00 }, - { 0x00, 0x00, 0x63, 0x36, 0x1C, 0x36, 0x63, 0x00 }, - { 0x00, 0x00, 0x33, 0x33, 0x33, 0x3E, 0x30, 0x1F }, - { 0x00, 0x00, 0x3F, 0x19, 0x0C, 0x26, 0x3F, 0x00 }, - { 0x38, 0x0C, 0x0C, 0x07, 0x0C, 0x0C, 0x38, 0x00 }, - { 0x18, 0x18, 0x18, 0x00, 0x18, 0x18, 0x18, 0x00 }, - { 0x07, 0x0C, 0x0C, 0x38, 0x0C, 0x0C, 0x07, 0x00 }, - { 0x6E, 0x3B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, - { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, -} - -const SCALE = 2 -const CW = 8 * SCALE -const CH = 8 * SCALE -const FG = 0xd0d0d0 -const CON_BG = 0x101018 -global con = { col = 0, row = 0 } - -fn con_cols() fb_width() / CW -fn con_rows() fb_height() / CH - -fn draw_glyph(col, row, ch) do - const x0 = col * CW - const y0 = row * CH - fill_rect(x0, y0, CW, CH, CON_BG) # clear the cell - const code = ch:ascii() - if code < 32 return :nil - if code > 127 return :nil - const g = FONT[code - 32] - for fy in 0..8 do - const bits = g[fy] - for fx in 0..8 do - if (bits shr fx) band 1 == 1 fill_rect(x0 + fx * SCALE, y0 + fy * SCALE, SCALE, SCALE, FG) - end - end -end - -fn con_newline() do - con.col = 0 - con.row = con.row + 1 - if con.row >= con_rows() do - fb_scroll(CH, CON_BG) - con.row = con_rows() - 1 - end -end - -# we dont do Shift/Cntrl -# thats a job for future me lmao -fn con_putc(ch) do - const code = ch:ascii() - if code == 10 do con_newline() return :nil end # \n - if code == 13 do con.col = 0 return :nil end # \r - if code == 8 do # backspace: move left - if con.col > 0 con.col = con.col - 1 - return :nil - end - if code < 32 return :nil - draw_glyph(con.col, con.row, ch) - con.col = con.col + 1 - if con.col >= con_cols() con_newline() -end - -# render a string to the screen and mirror to serial -fn puts(s) do - serial_puts(s) - for i in 0..s:len() do con_putc(s[i]) end -end - -# keyboard scancode table -# ts is NOT a good way to write a driver LMAO -const KBD = " 1234567890-= qwertyuiop[] asdfghjkl;'` \\zxcvbnm,./ " - -# 8259 PIC -fn pic_remap() do - outb(0x20, 0x11) outb(0xa0, 0x11) - outb(0x21, 0x20) outb(0xa1, 0x28) - outb(0x21, 0x04) outb(0xa1, 0x02) - outb(0x21, 0x01) outb(0xa1, 0x01) - outb(0x21, 0xff) outb(0xa1, 0xff) -end -fn pic_unmask(line) outb(0x21, inb(0x21) band (255 bxor (1 shl line))) - -# this is just a simple prompt to test that shit is working -fn cmd_help() puts("commands: help, clear, bars, echo <text>, time\n") - -fn cmd_clear() do - fill_rect(0, 0, fb_width(), fb_height(), CON_BG) - con.col = 0 - con.row = 0 -end - -fn cmd_bars() do - const bars = { 0xff5555, 0xffaa00, 0xffff55, 0x55ff55, 0x55ffff, 0x5599ff, 0xaa55ff, 0xffffff } - const bw = fb_width() / 8 - for i in 0..8 do fill_rect(i * bw, 0, bw, 40, bars[i]) end - puts("painted 8 bars from revo\n") -end - -fn bcd(b) (b shr 4) * 10 + (b band 15) -fn rtc(reg) do - outb(0x70, reg) - inb(0x71) -end -fn cmd_time() puts("utc " ~ bcd(rtc(4)) ~ ":" ~ bcd(rtc(2)) ~ ":" ~ bcd(rtc(0)) ~ "\n") - -fn run(line) do - const words = line:split(" ") - const cmd = words[0] - if cmd == "help" do cmd_help() end - else if cmd == "clear" do cmd_clear() end - else if cmd == "bars" do cmd_bars() end - else if cmd == "time" do cmd_time() end - else if cmd == "echo" do - if line:len() > 5 puts(line:sub(5, line:len() - 5) ~ "\n") else puts("\n") - end - else if line:len() == 0 do :nil end - else do puts("unknown command: " ~ cmd ~ "\n") end - puts("> ") -end - -# she stroke my keys till i handle -# no fr this is just a key stroke handler -global sh = { line = "" } -global on_key = fn(code) do - if code >= 128 return :nil # key release - if code == 28 do # Enter - puts("\n") - run(sh.line) - sh.line = "" - return :nil - end - if code == 14 do # Backspace - if sh.line:len() > 0 do - sh.line = sh.line:sub(0, sh.line:len() - 1) - puts("\x08 \x08") - end - return :nil - end - if code < 2 return :nil - if code >= KBD:len() return :nil - const ch = KBD[code] - sh.line = sh.line ~ ch - puts(ch) -end - # kmain pic_remap() pic_unmask(1) cmd_clear() puts("revel shell, live in revo. type `help`\n> ") +flush_console() diff --git a/kernel/proc.rv b/kernel/proc.rv new file mode 100644 index 0000000..af6e8f7 --- /dev/null +++ b/kernel/proc.rv @@ -0,0 +1,40 @@ +# parse the embedded ELF (physical address from elf_phys()) and run it in ring 3 + +const PT_LOAD = 1 +const PAGE_FLAGS = 6 # WRITE | USER + +fn cmd_run() do + const base = elf_phys() + if mem_read(base, 4) != 0x464c457f do + puts("run: not an ELF\n") + return :nil + end + const entry = mem_read(base + 24, 8) + const phoff = mem_read(base + 32, 8) + const phnum = mem_read(base + 56, 2) + const phentsize = mem_read(base + 54, 2) + const as = as_create() + for i in 0..phnum do + const ph = base + phoff + i * phentsize + if mem_read(ph, 4) == PT_LOAD do + const offset = mem_read(ph + 8, 8) + const vaddr = mem_read(ph + 16, 8) + const filesz = mem_read(ph + 32, 8) + const memsz = mem_read(ph + 40, 8) + let off = 0 + while off < memsz do + const frame = frame_alloc() + mem_zero(frame, 4096) + if off < filesz do + const left = filesz - off + mem_copy(frame, base + offset + off, if left > 4096 4096 else left) + end + as_map(as, vaddr + off, frame, PAGE_FLAGS) + off = off + 4096 + end + end + end + const stack_top = 0x7fff0000 + as_map(as, stack_top - 4096, frame_alloc(), PAGE_FLAGS) + proc_run(as, entry, stack_top) +end diff --git a/kernel/shell.rv b/kernel/shell.rv new file mode 100644 index 0000000..562f3c4 --- /dev/null +++ b/kernel/shell.rv @@ -0,0 +1,55 @@ +# this is just a simple prompt to test that shit is working +fn cmd_help() puts("commands: help, clear, bars, echo <text>, time, run, zoom in|out\n") + +fn zoom_by(d) do + let s = con.scale + d + if s < 1 do s = 1 end + if s > 6 do s = 6 end + con.scale = s + cmd_clear() + puts("zoom " ~ s ~ "x\n") +end + +fn cmd_zoom(words) do + const dir = if words:len() > 1 words[1] else "" + if dir == "in" do zoom_by(1) end + else if dir == "out" do zoom_by(-1) end + else do puts("usage: zoom in | zoom out\n") end +end + +fn cmd_clear() do + fill_rect(0, 0, fb_width(), fb_height(), CON_BG) + con.col = 0 + con.row = 0 +end + +fn cmd_bars() do + const bars = { 0xff5555, 0xffaa00, 0xffff55, 0x55ff55, 0x55ffff, 0x5599ff, 0xaa55ff, 0xffffff } + const bw = fb_width() / 8 + for i in 0..8 do fill_rect(i * bw, 0, bw, 40, bars[i]) end + puts("painted 8 bars from revo\n") +end + +fn bcd(b) (b shr 4) * 10 + (b band 15) +fn rtc(reg) do + outb(0x70, reg) + inb(0x71) +end +fn cmd_time() puts("utc " ~ bcd(rtc(4)) ~ ":" ~ bcd(rtc(2)) ~ ":" ~ bcd(rtc(0)) ~ "\n") + +fn run(line) do + const words = line:split(" ") + const cmd = words[0] + if cmd == "help" do cmd_help() end + else if cmd == "clear" do cmd_clear() end + else if cmd == "bars" do cmd_bars() end + else if cmd == "time" do cmd_time() end + else if cmd == "run" do cmd_run() end + else if cmd == "zoom" do cmd_zoom(words) end + else if cmd == "echo" do + if line:len() > 5 puts(line:sub(5, line:len() - 5) ~ "\n") else puts("\n") + end + else if line:len() == 0 do :nil end + else do puts("unknown command: " ~ cmd ~ "\n") end + puts("> ") +end diff --git a/kernel/vmm.rv b/kernel/vmm.rv new file mode 100644 index 0000000..d6673b3 --- /dev/null +++ b/kernel/vmm.rv @@ -0,0 +1,24 @@ +# build the 4-level page tables for one user page +# everything here only writes small entries we construct, so it's f64-safe +const PTE_ADDR = 0x000FFFFFFFFFF000 + +fn pte_idx(virt, level) (virt shr (12 + 9 * level)) band 0x1ff + +# next-level table phys under entry `i` of table `tbl`, creating it if absent +fn next_table(tbl, i) do + const slot = tbl + i * 8 + const entry = mem_read(slot, 8) + if (entry band 1) != 0 return entry band PTE_ADDR + const frame = frame_alloc() + mem_zero(frame, 4096) + mem_write(slot, frame bor 7, 8) # present | write | user + frame +end + +fn as_map(pml4, virt, phys, flags) do + const pdpt = next_table(pml4, pte_idx(virt, 3)) + const pd = next_table(pdpt, pte_idx(virt, 2)) + const pt = next_table(pd, pte_idx(virt, 1)) + mem_write(pt + pte_idx(virt, 0) * 8, (phys band PTE_ADDR) bor flags bor 1, 8) + invlpg(virt) +end diff --git a/user/link.ld b/user/link.ld new file mode 100644 index 0000000..9dc8402 --- /dev/null +++ b/user/link.ld @@ -0,0 +1,8 @@ +ENTRY(_start) +SECTIONS { + . = 0x400000; + .text : ALIGN(0x1000) { *(.text*) } + .rodata : ALIGN(0x1000) { *(.rodata*) } + .data : ALIGN(0x1000) { *(.data*) } + .bss : ALIGN(0x1000) { *(.bss*) *(COMMON) } +} diff --git a/user/program.c b/user/program.c new file mode 100644 index 0000000..8e26128 --- /dev/null +++ b/user/program.c @@ -0,0 +1,22 @@ +static long sys_write(const char* buffer, unsigned long buffer_size) +{ + long ret; + __asm__ volatile("int $0x80" : "=a"(ret) : "a"(1L), "D"(buffer), "S"(buffer_size) : "memory"); + return ret; +} +static void sys_exit(long code) +{ + __asm__ volatile("int $0x80" : : "a"(0L), "D"(code) : "memory"); + for (;;); +} + +void _start(void) +{ + const char* message = "wassup shawty\n"; + unsigned long message_length = 0; + while (message[message_length]) + message_length++; + + sys_write(message, message_length); + sys_exit(0); +} |
