diff --git a/src/libraries/elf/include/elf/headers.h b/src/libraries/elf/include/elf/headers.h index a1c9c47..f6c8e82 100644 --- a/src/libraries/elf/include/elf/headers.h +++ b/src/libraries/elf/include/elf/headers.h @@ -54,11 +54,18 @@ struct file_header enum class segment_type : uint32_t { null, load, dynamic, interpreter, note }; +enum class segment_flags : uint32_t +{ + none = 0x00, + exec = 0x01, + write = 0x02, + read = 0x04, +}; struct program_header { segment_type type; - uint32_t flags; + segment_flags flags; uint64_t offset; uint64_t vaddr; diff --git a/src/user/srv.init/init.module b/src/user/srv.init/init.module index 307c77d..d03214c 100644 --- a/src/user/srv.init/init.module +++ b/src/user/srv.init/init.module @@ -1,10 +1,13 @@ # vim: ft=python -module("srv.init", +init = module("srv.init", targets = [ "user" ], - deps = [ "libc" ], + deps = [ "libc", "elf" ], sources = [ + "loader.cpp", "main.cpp", "modules.cpp", "start.s", ]) + +init.variables['ldflags'] = ["${ldflags}", "-section-start=.rodata=0x800000"] diff --git a/src/user/srv.init/loader.cpp b/src/user/srv.init/loader.cpp new file mode 100644 index 0000000..34193fd --- /dev/null +++ b/src/user/srv.init/loader.cpp @@ -0,0 +1,132 @@ +#include +#include +#include "enum_bitfields.h" +#include "elf/file.h" +#include "elf/headers.h" +#include "j6/errors.h" +#include "j6/flags.h" +#include "j6/syscalls.h" +#include "init_args.h" + +using kernel::init::module_flags; +using kernel::init::module_program; + +extern j6_handle_t handle_self; +extern j6_handle_t handle_system; + +constexpr uintptr_t load_addr_base = 0xf8000000; +constexpr size_t stack_size = 0x10000; +constexpr uintptr_t stack_top = 0x80000000000; + +bool +load_program(const module_program &prog, char *err_msg) +{ + if (bitfields::has(prog.mod_flags, module_flags::no_load)) { + sprintf(err_msg, " skipping pre-loaded program module '%s' at %lx", prog.filename, prog.base_address); + return true; + } + + j6_handle_t elf_vma = j6_handle_invalid; + j6_status_t res = j6_system_map_phys(handle_system, &elf_vma, prog.base_address, prog.size, 0); + if (res != j6_status_ok) { + sprintf(err_msg, " ** error loading program '%s': creating physical vma: %lx", prog.filename, res); + return false; + } + + res = j6_vma_map(elf_vma, handle_self, prog.base_address); + if (res != j6_status_ok) { + sprintf(err_msg, " ** error loading program '%s': mapping vma: %lx", prog.filename, res); + return false; + } + + const void *addr = reinterpret_cast(prog.base_address); + elf::file progelf {addr, prog.size}; + + if (!progelf.valid()) { + sprintf(err_msg, " ** error loading program '%s': ELF is invalid", prog.filename); + return false; + } + + j6_handle_t proc = j6_handle_invalid; + res = j6_process_create(&proc); + if (res != j6_status_ok) { + sprintf(err_msg, " ** error loading program '%s': creating process: %lx", prog.filename, res); + return false; + } + + uintptr_t load_addr = load_addr_base; + + for (auto &seg : progelf.programs()) { + if (seg.type != elf::segment_type::load) + continue; + + // TODO: way to remap VMA as read-only if there's no write flag on + // the segment + unsigned long flags = j6_vm_flag_write; + if (bitfields::has(seg.flags, elf::segment_flags::exec)) + flags |= j6_vm_flag_exec; + + j6_handle_t sub_vma = j6_handle_invalid; + res = j6_vma_create_map(&sub_vma, seg.mem_size, load_addr, flags); + if (res != j6_status_ok) { + sprintf(err_msg, " ** error loading program '%s': creating sub vma: %lx", prog.filename, res); + return false; + } + + void *src = reinterpret_cast(prog.base_address + seg.offset); + void *dest = reinterpret_cast(load_addr); + memcpy(dest, src, seg.file_size); + + res = j6_vma_map(sub_vma, proc, seg.vaddr); + if (res != j6_status_ok) { + sprintf(err_msg, " ** error loading program '%s': mapping sub vma to child: %lx", prog.filename, res); + return false; + } + + res = j6_vma_unmap(sub_vma, handle_self); + if (res != j6_status_ok) { + sprintf(err_msg, " ** error loading program '%s': unmapping sub vma: %lx", prog.filename, res); + return false; + } + + load_addr += 0x1000 * ((seg.mem_size + 0xfff) >> 12); + } + + j6_handle_t stack_vma = j6_handle_invalid; + res = j6_vma_create_map(&stack_vma, stack_size, load_addr, j6_vm_flag_write); + if (res != j6_status_ok) { + sprintf(err_msg, " ** error loading program '%s': creating stack vma: %lx", prog.filename, res); + return false; + } + + uint64_t *stack = reinterpret_cast(load_addr + stack_size); + memset(stack - 512, 0, 512 * sizeof(uint64_t)); // Zero top page + + res = j6_vma_map(stack_vma, proc, stack_top-stack_size); + if (res != j6_status_ok) { + sprintf(err_msg, " ** error loading program '%s': mapping stack vma: %lx", prog.filename, res); + return false; + } + + res = j6_vma_unmap(stack_vma, handle_self); + if (res != j6_status_ok) { + sprintf(err_msg, " ** error loading program '%s': unmapping stack vma: %lx", prog.filename, res); + return false; + } + + j6_handle_t thread = j6_handle_invalid; + res = j6_thread_create(&thread, proc, stack_top - 6*sizeof(uint64_t), progelf.entrypoint()); + if (res != j6_status_ok) { + sprintf(err_msg, " ** error loading program '%s': creating thread: %lx", prog.filename, res); + return false; + } + + res = j6_vma_unmap(elf_vma, handle_self); + if (res != j6_status_ok) { + sprintf(err_msg, " ** error loading program '%s': unmapping elf vma: %lx", prog.filename, res); + return false; + } + + return true; +} + diff --git a/src/user/srv.init/loader.h b/src/user/srv.init/loader.h new file mode 100644 index 0000000..89dc35c --- /dev/null +++ b/src/user/srv.init/loader.h @@ -0,0 +1,10 @@ +#pragma once +/// \file loader.h +/// Routines for loading and starting other programs + +namespace kernel { +namespace init { + struct module_program; +}} + +bool load_program(const kernel::init::module_program &prog, char *err_msg); diff --git a/src/user/srv.init/main.cpp b/src/user/srv.init/main.cpp index 3deed43..adf74d7 100644 --- a/src/user/srv.init/main.cpp +++ b/src/user/srv.init/main.cpp @@ -1,7 +1,8 @@ #include -#include "j6/syscalls.h" #include "init_args.h" +#include "j6/syscalls.h" +#include "loader.h" #include "modules.h" using kernel::init::module; @@ -24,11 +25,17 @@ main(int argc, const char **argv) modules mods = modules::load_modules(_arg_modules_phys, handle_system, handle_self); - char message[100]; for (auto &mod : mods.of_type(module_type::program)) { auto &prog = static_cast(mod); - sprintf(message, " program module '%s' at %lx", prog.filename, prog.base_address); + + char message[100]; + sprintf(message, " loading program module '%s' at %lx", prog.filename, prog.base_address); j6_log(message); + + if (!load_program(prog, message)) { + j6_log(message); + return 1; + } } return 0; diff --git a/src/user/testapp/main.cpp b/src/user/testapp/main.cpp index 3c9c19e..4aa523f 100644 --- a/src/user/testapp/main.cpp +++ b/src/user/testapp/main.cpp @@ -3,6 +3,7 @@ #include "j6/types.h" #include "j6/errors.h" +#include "j6/flags.h" #include "j6/signals.h" #include "j6/syscalls.h" @@ -17,6 +18,11 @@ extern "C" { int main(int, const char **); } +constexpr j6_handle_t handle_self = 1; // Self program handle is always 1 + +constexpr size_t stack_size = 0x10000; +constexpr uintptr_t stack_top = 0xf80000000; + void thread_proc() { @@ -52,7 +58,6 @@ thread_proc() int main(int argc, const char **argv) { - j6_handle_t children[2] = {j6_handle_invalid, j6_handle_invalid}; j6_signal_t out = 0; j6_log("main thread starting"); @@ -76,7 +81,13 @@ main(int argc, const char **argv) j6_log("main thread created endpoint"); - result = j6_thread_create(&children[1], reinterpret_cast(&thread_proc)); + j6_handle_t child_stack_vma = j6_handle_invalid; + result = j6_vma_create_map(&child_stack_vma, stack_size, stack_top-stack_size, j6_vm_flag_write); + if (result != j6_status_ok) + return result; + + j6_handle_t child = j6_handle_invalid; + result = j6_thread_create(&child, handle_self, stack_top, reinterpret_cast(&thread_proc)); if (result != j6_status_ok) return result; @@ -98,29 +109,20 @@ main(int argc, const char **argv) j6_log(message); - j6_log("main thread creating a new process"); - result = j6_process_create(&children[0]); + j6_log("main thread waiting on sub thread"); + + result = j6_kobject_wait(child, -1ull, &out); if (result != j6_status_ok) return result; - j6_log("main thread waiting on children"); + j6_log("main setting IOPL"); - j6_handle_t outhandle; - result = j6_kobject_wait_many(children, 2, -1ull, &outhandle, &out); + result = j6_system_request_iopl(__handle_sys, 3); if (result != j6_status_ok) return result; - if (outhandle == children[1]) { - j6_log(" ok from child thread"); - } else if (outhandle == children[0]) { - j6_log(" ok from child process"); - } else { - j6_log(" ... got unknown handle"); - } - j6_log("main testing irqs"); - serial_port com2(COM2); const char *fgseq = "\x1b[2J";