Microkernel thing OS experiment (Zig ⚡)

load root task elf

pci.express c9b9b1c6 4c810fd8

verified
Changed files
+83 -40
components
root_server
ukernel
arch
amd64
common
+4 -3
components/root_server/src/main.zig
···
export fn _start() callconv(.c) noreturn {
_ = os.syscall1(SYS_poke, 0xB16B00B5BADBABE);
-
_ = os.syscall1(SYS_exit, 0);
+
_ = os.syscall1(SYS_exit, 0x69696969);
+
unreachable;
}
-
pub const SYS_exit = 69;
-
pub const SYS_poke = 420;
+
pub const SYS_exit = 0x420;
+
pub const SYS_poke = 0x69;
+2 -2
components/root_server/src/os.zig
···
pub fn syscall1(number: usize, arg1: usize) usize {
return asm volatile ("syscall"
: [ret] "={rax}" (-> usize),
-
: [number] "{rax}" (number),
-
[arg1] "{rdi}" (arg1),
+
: [number] "{rdi}" (number),
+
[arg1] "{rsi}" (arg1),
: .{ .rcx = true, .r11 = true });
}
};
+11 -35
components/ukernel/arch/amd64/boot.zig
···
log.info("Allocating code for userspace...", .{});
-
const user_code = common.init_data.bootmem.allocPhys(0x1000) catch @panic("alloc bruh");
-
const user_stack = common.init_data.bootmem.allocPhys(0x1000) catch @panic("alloc bruh2");
-
-
// TODO: load the actual root task ELF for fucks sake
-
// instead of the current glorified shellcode
-
-
// Map our executable page to 0x1000
-
common.mm.paging.mapPhys(.{
-
.vaddr = 0x8000,
-
.paddr = user_code,
-
.size = 0x1000,
-
.memory_type = .MemoryWriteBack,
-
.perms = .{
-
.executable = true,
-
.writable = false,
-
.userspace_accessible = true,
-
},
-
}) catch {
-
@panic("Mapping failed!!!!!");
-
};
-
-
// Map our stack page to 0x3000 (starts at 0x4000)
-
common.mm.paging.mapPhys(.{
+
// Allocate a stack (0x3000 - 0x4000)
+
common.mm.paging.map(.{
.vaddr = 0x3000,
-
.paddr = user_stack,
.size = 0x1000,
.memory_type = .MemoryWriteBack,
.perms = .{
.executable = false,
-
.writable = true,
.userspace_accessible = true,
+
.writable = true,
},
-
}) catch {
-
@panic("Mapping failed!!!!!");
-
};
+
}) catch @panic("couldn't map user stack");
-
// Place shellcode there (does a couple syscalls then jmp $ infinite loop)
-
const memory: [*]u8 = common.mm.physToHHDM([*]u8, user_code);
-
const shellcode = [_]u8{ 0x48, 0xBF, 0xE1, 0xAB, 0xDB, 0xBA, 0xB5, 0x00, 0x6B, 0xB1, 0x48, 0xBE, 0x06, 0x42, 0x69, 0x20, 0x94, 0x06, 0x42, 0x69, 0x0F, 0x05, 0xBF, 0x11, 0xCA, 0x00, 0x00, 0xBE, 0x69, 0x69, 0x69, 0x69, 0x0F, 0x05, 0xEB, 0xFE };
-
@memcpy(memory[0..@sizeOf(@TypeOf(shellcode))], shellcode[0..]);
+
const entry = common.loadRootTask() catch |err| {
+
log.err("Couldn't load the root task! {}", .{err});
+
@panic("ggz");
+
};
+
log.info("Dropping to userspace entry 0x{x:0>16}", .{entry});
-
// Set up MSRs to enable syscalls
init_syscalls();
-
// Finally, iretq ourselves into this bitch
-
enter_userspace(0x8000, 0x69, 0x3800);
+
enter_userspace(entry, 0x69, 0x4000);
}
// Get ready for system calls (set MSRs)
···
});
export fn syscall_handler(rdi: usize, rsi: usize) callconv(.c) void {
std.log.info("Got a syscall! rdi=0x{x}, rsi=0x{x}", .{ rdi, rsi });
+
if (rdi == 0x420) arch.instructions.die();
}
fn enter_userspace(entry: u64, arg: u64, stack: u64) noreturn {
+52
components/ukernel/common/loader.zig
···
+
const common = @import("root.zig");
+
const paging = common.mm.paging;
+
const std = @import("std");
+
const elf = std.elf;
+
const log = std.log.scoped(.elf_loader);
+
+
// Load root task, return the entry point
+
pub fn loadRootTask() !usize {
+
const root_task = common.init_data.root_task;
+
const hdr = blk: {
+
const hdr: *elf.Elf64_Ehdr = @ptrCast(root_task);
+
break :blk elf.Header.init(hdr.*, .little);
+
};
+
var iter = hdr.iterateProgramHeadersBuffer(root_task);
+
while (try iter.next()) |entry| {
+
if ((entry.p_type != elf.PT_LOAD) or (entry.p_memsz == 0)) continue;
+
+
// 1. Allocate pages to back this allocation
+
const real_vaddr = std.mem.alignBackward(usize, entry.p_vaddr, std.heap.pageSize());
+
const vaddr_shift = entry.p_vaddr - real_vaddr;
+
const memsz_pages = std.mem.alignForward(usize, vaddr_shift + entry.p_memsz, std.heap.pageSize());
+
+
log.debug("Allocating 0x{x} bytes (0x{x} real) to 0x{x:0>16} from offset 0x{x}", .{ entry.p_filesz, memsz_pages, entry.p_vaddr, entry.p_offset });
+
+
const page_backing = try common.init_data.bootmem.allocPhys(memsz_pages);
+
try paging.mapPhys(.{
+
.vaddr = real_vaddr,
+
.paddr = page_backing,
+
.size = memsz_pages,
+
.memory_type = .MemoryWriteBack,
+
.perms = .{
+
.executable = entry.p_flags & elf.PF_X > 0,
+
.writable = entry.p_flags & elf.PF_W > 0,
+
.userspace_accessible = true,
+
},
+
});
+
+
// 2. Copy filesz bytes from offset to this new page
+
const dst = common.mm.physToHHDM([*]u8, page_backing + vaddr_shift);
+
const dst_slice = dst[0..entry.p_filesz];
+
+
const src_slice = root_task[entry.p_offset..][0..entry.p_filesz];
+
@memcpy(dst_slice, src_slice);
+
+
// 3. Add memsz - filesz zeroes
+
const zero_slice = dst[entry.p_filesz..entry.p_memsz];
+
@memset(zero_slice, 0);
+
}
+
+
// Return the entry point
+
return hdr.entry;
+
}
+12
components/ukernel/common/mm/paging.zig
···
try mapPageImpl(&vaddr, &paddr, &size, root, args.perms, args.memory_type);
}
+
pub fn map(args: struct {
+
vaddr: usize,
+
size: usize,
+
perms: Perms,
+
memory_type: MemoryType,
+
}) !void {
+
const root = arch.mm.paging.root_table(args.vaddr);
+
var vaddr = args.vaddr;
+
var size = args.size;
+
try mapPageImpl(&vaddr, null, &size, root, args.perms, args.memory_type);
+
}
+
fn mapPageImpl(
vaddr: *usize,
paddr: ?*usize,
+2
components/ukernel/common/root.zig
···
pub const aux = @import("aux.zig");
pub const mm = @import("mm/root.zig");
+
pub const loadRootTask = loader.loadRootTask;
+
const loader = @import("loader.zig");
// Arch init must set up appropriate fields!
pub var init_data: aux.InitState = .{};