diff options
-rw-r--r-- | src/kernel.zig | 6 | ||||
-rw-r--r-- | src/lib/process.zig | 157 |
2 files changed, 118 insertions, 45 deletions
diff --git a/src/kernel.zig b/src/kernel.zig index f63fb5e..9f1ee45 100644 --- a/src/kernel.zig +++ b/src/kernel.zig @@ -13,6 +13,7 @@ const paging = @import("lib/paging.zig"); const pci = @import("lib/pci.zig"); const plic = @import("lib/plic.zig"); const process = @import("lib/process.zig"); +const userinit = @import("lib/userinit.zig"); const Error = error{ HartIdOutOfRange, @@ -195,6 +196,7 @@ fn pagedRun() !noreturn { } } - try w.print("Enter process demo\r\n", .{}); - try process.demo(allocator); + try w.print("Start init process\r\n", .{}); + var userinit_stream = std.io.fixedBufferStream(userinit.tarball); + try process.runInit(allocator, userinit_stream.reader()); } diff --git a/src/lib/process.zig b/src/lib/process.zig index 241c2f5..17b90df 100644 --- a/src/lib/process.zig +++ b/src/lib/process.zig @@ -2,6 +2,7 @@ // // SPDX-License-Identifier: AGPL-3.0-or-later +const builtin = @import("builtin"); const std = @import("std"); const instructions = @import("instructions.zig"); @@ -9,6 +10,8 @@ const paging = @import("paging.zig"); const time = @import("sbi/time.zig"); const trap = @import("trap.zig"); +const elf = std.elf; + pub const schedule_interval_millis = 1; pub var list = std.mem.zeroInit(std.DoublyLinkedList(Info), .{}); @@ -19,6 +22,15 @@ var next_pid: u16 = 1; pub const Error = error{ EmptySchedule, + NoInit, +}; + +pub const ExeError = error{ + BadEndian, + BadArch, + BadBitLen, + NotStaticExe, + BranchPerms, }; pub const State = enum(u8) { @@ -47,40 +59,6 @@ pub const Info = extern struct { } }; -fn new(entry: usize) !Info { - const stack = try paging.alloc(num_stack_pages); - errdefer paging.free(stack); - - const procmem: *paging.Table = @alignCast(@ptrCast(try paging.zeroedAlloc(1))); - errdefer paging.free(procmem); - - var proc = Info{ - .id = next_pid, - .trap_frame = std.mem.zeroInit(trap.Frame, .{}), - .stack = @ptrCast(stack), - .pc = entry, - .page_table = procmem, - .state = .waiting, - }; - - const stack_top = @intFromPtr(proc.stack) + num_stack_pages * paging.page_size; - proc.trap_frame.general_purpose_registers[2] = stack_top; - - try procmem.mapKernel(); - - try procmem.map(entry, entry, paging.EntryFlags.userReadExec, 0); - // Not using identityMapRange because this is going to be expanded for non-relocatable binaries. - for (0..num_stack_pages) |page| { - const vaddr = @intFromPtr(proc.stack) + page * paging.page_size; - const paddr = @intFromPtr(proc.stack) + page * paging.page_size; - - try procmem.map(vaddr, paddr, paging.EntryFlags.userReadWrite, 0); - } - - next_pid += 1; - return proc; -} - pub fn next() ?*Info { if (list.popFirst()) |info| { list.append(info); @@ -151,20 +129,113 @@ pub fn switchTo(proc: *Info) noreturn { unreachable; } -pub fn demo(allocator: std.mem.Allocator) !noreturn { - const entry: []align(4) volatile u8 = try paging.zeroedAlloc(1); - defer paging.free(entry); +pub fn create(allocator: std.mem.Allocator, elf_buf: []align(@alignOf(elf.Elf64_Ehdr)) const u8) !*Info { + const hdr_buf: *align(@alignOf(elf.Elf64_Ehdr)) const [@sizeOf(elf.Elf64_Ehdr)]u8 = elf_buf[0..@sizeOf(elf.Elf64_Ehdr)]; + const hdr = try elf.Header.parse(@ptrCast(hdr_buf)); + + try validateElfHeader(hdr, hdr_buf); - entry[0] = 0x73; - entry[1] = 0x00; - entry[2] = 0x00; - entry[3] = 0x00; + const len_aligned = std.mem.alignForwardLog2(elf_buf.len, paging.log2_page_size); + const num_pages = len_aligned / paging.page_size + 1; + const pages = try paging.zeroedAlloc(num_pages); + errdefer paging.free(pages); + + const procmem: *paging.Table = @ptrCast(try paging.zeroedAlloc(1)); + errdefer paging.free(procmem); + + try procmem.mapKernel(); + + const parse_source = std.io.fixedBufferStream(elf_buf[@sizeOf(elf.Elf64_Ehdr)..]); + + var it = hdr.program_header_iterator(parse_source); + while (try it.next()) |phdr| { + if (phdr.p_type != elf.PT_LOAD) continue; + if (phdr.p_memsz == 0) continue; + + // fixme: Could crash (out-of-bounds read). + @memcpy(pages[phdr.p_offset..], elf_buf[phdr.p_offset .. phdr.p_offset + phdr.p_memsz]); + + const memsz_aligned = std.mem.alignForwardLog2(phdr.p_memsz, paging.log2_page_size); + const num_mappings = @divExact(memsz_aligned, paging.page_size); + for (0..num_mappings) |page| { + const vaddr = phdr.p_vaddr + page * paging.page_size; + const paddr = @intFromPtr(pages.ptr) + phdr.p_offset + page * paging.page_size; + const flags = paging.EntryFlags{ + .valid = 1, + .read = @bitCast(phdr.p_flags & elf.PF_R != 0), + .write = @bitCast(phdr.p_flags & elf.PF_W != 0), + .exec = @bitCast(phdr.p_flags & elf.PF_X != 0), + .user = 1, + .global = 0, + .accessed = 1, + .dirty = @bitCast(phdr.p_flags & elf.PF_W != 0), + }; + + if (!@bitCast(flags.read) and !@bitCast(flags.write) and !@bitCast(flags.exec)) { + return ExeError.BranchPerms; + } + + try procmem.map(vaddr, paddr, flags, 0); + } + } + + const stack = try paging.zeroedAlloc(num_stack_pages); + errdefer paging.free(stack); + + const stack_top = @intFromPtr(stack.ptr) + num_stack_pages * paging.page_size; + try procmem.identityMapRange(@intFromPtr(stack.ptr), stack_top, paging.EntryFlags.userReadWrite); + + var proc = Info{ + .id = next_pid, + .trap_frame = std.mem.zeroInit(trap.Frame, .{}), + .stack = @ptrCast(stack), + .pc = hdr.entry, + .page_table = procmem, + .state = .waiting, + }; + proc.trap_frame.general_purpose_registers[2] = stack_top; - const proc = try new(@intFromPtr(entry.ptr)); const proc_node = try allocator.create(std.DoublyLinkedList(Info).Node); proc_node.data = proc; list.prepend(proc_node); + return &proc_node.data; +} + +pub fn runInit(allocator: std.mem.Allocator, reader: anytype) !noreturn { + var file_name_buffer: [4096]u8 = undefined; + var link_name_buffer: [4096]u8 = undefined; + + var it = std.tar.iterator(reader, .{ + .file_name_buffer = file_name_buffer[0..], + .link_name_buffer = link_name_buffer[0..], + }); + const exe = while (try it.next()) |file| { + if (std.mem.eql(u8, file.name, "./init")) { + break file; + } + } else return Error.NoInit; + + const alignment = @alignOf(elf.Elf64_Ehdr); + + var exe_list = std.ArrayListAligned(u8, alignment).init(allocator); + defer exe_list.deinit(); + try exe.reader().readAllArrayListAligned(alignment, &exe_list, exe.size); + + const proc = try create(allocator, exe_list.items); + try time.interruptInMillis(schedule_interval_millis); - switchTo(&proc_node.data); + switchTo(proc); +} + +fn validateElfHeader(hdr: elf.Header, hdr_buf: *align(@alignOf(elf.Elf64_Ehdr)) const [@sizeOf(elf.Elf64_Ehdr)]u8) !void { + const arch = builtin.cpu.arch; + + if (hdr.endian != arch.endian()) return ExeError.BadEndian; + if (hdr.machine != arch.toElfMachine()) return ExeError.BadArch; + if (!hdr.is_64) return ExeError.BadBitLen; + + const hdr64 = @as(*const elf.Elf64_Ehdr, @ptrCast(hdr_buf)); + + if (hdr64.e_type != .EXEC) return ExeError.NotStaticExe; } |