diff --git a/sys/include/thread.h b/sys/include/thread.h index 7212625..bff2efc 100644 --- a/sys/include/thread.h +++ b/sys/include/thread.h @@ -56,6 +56,7 @@ typedef struct Process { uint64_t pid; AS *space; Spinlock lock; + uintptr_t entrypoint; uint64_t nextThreadID; uintptr_t ustackNext; // Next user stack TAILQ_ENTRY(Process) processList; diff --git a/sys/kern/loader.c b/sys/kern/loader.c index 5023435..11e8919 100644 --- a/sys/kern/loader.c +++ b/sys/kern/loader.c @@ -19,6 +19,13 @@ #include +extern Handle *Console_OpenHandle(); + +/* + * Loader_CheckHeader -- + * + * Check that the program has a valid ELF header. + */ bool Loader_CheckHeader(const Elf64_Ehdr *ehdr) { @@ -39,125 +46,15 @@ Loader_CheckHeader(const Elf64_Ehdr *ehdr) return true; } -bool -Loader_LoadFirst(Thread *thr, VNode *vn, void *buf, uint64_t len) -{ - int i; - const Elf64_Ehdr *ehdr; - const Elf64_Phdr *phdr; - AS *as = thr->space; - - ehdr = (const Elf64_Ehdr *)(buf); - phdr = (const Elf64_Phdr *)(buf + ehdr->e_phoff); - - if (!Loader_CheckHeader(ehdr)) { - Log(loader, "Not a valid executable!\n"); - return false; - } - - Log(loader, "%8s %16s %8s %8s\n", "Offset", "VAddr", "FileSize", "MemSize"); - for (i = 0; i < ehdr->e_phnum; i++) - { - ASSERT(phdr[i].p_type != PT_DYNAMIC); - if (phdr[i].p_type == PT_LOAD) { - uint64_t va = phdr[i].p_vaddr; - uint64_t memsz = phdr[i].p_memsz; - Log(loader, "%08llx %016llx %08llx %08llx\n", phdr[i].p_offset, - phdr[i].p_vaddr, phdr[i].p_filesz, phdr[i].p_memsz); - - // Make sure it is page aligned - va = va & ~(uint64_t)PGMASK; - memsz += phdr[i].p_vaddr - va; - - Log(loader, "AllocMap %016llx %08llx\n", va, memsz); - if (!PMap_AllocMap(as, va, memsz, PTE_W)) { - // XXX: Cleanup! - ASSERT(false); - return false; - } - } - } - - PMap_AllocMap(as, MEM_USERSPACE_STKBASE, MEM_USERSPACE_STKLEN, PTE_W); - PMap_LoadAS(as); // Reload CR3 - - for (i = 0; i < ehdr->e_phnum; i++) - { - ASSERT(phdr[i].p_type != PT_DYNAMIC); - if (phdr[i].p_type == PT_LOAD) { - if (phdr[i].p_filesz != 0) { - Log(loader, "Read %lx %lx %lx\n", phdr[i].p_vaddr, phdr[i].p_offset, phdr[i].p_filesz); - VFS_Read(vn, (void *)phdr[i].p_vaddr, - phdr[i].p_offset, phdr[i].p_filesz); - } - memset((void *)(phdr[i].p_vaddr + phdr[i].p_filesz), - 0, - (uintptr_t)(phdr[i].p_memsz - phdr[i].p_filesz)); - } - } - - Log(loader, "Jumping to userspace\n"); - - uintptr_t ap[3]; - ap[0] = 0; - ap[1] = 0; - ap[2] = 0xDEADBEEF; - uintptr_t rsp = MEM_USERSPACE_STKTOP - PGSIZE; - - Copy_Out(&ap[0], rsp, sizeof(uintptr_t)*3); - - TrapFrame tf; - memset(&tf, 0, sizeof(tf)); - tf.ds = SEL_UDS | 3; - tf.rip = ehdr->e_entry; - tf.cs = SEL_UCS | 3; - tf.rsp = rsp; - tf.ss = SEL_UDS | 3; - tf.rflags = RFLAGS_IF; - tf.rdi = rsp; - Trap_Pop(&tf); - - return true; -} - -Handle *Console_OpenHandle(); - -void -Loader_LoadInit() -{ - int status; - void *pg; - VNode *init; - - pg = PAlloc_AllocPage(); - if (!pg) - Panic("Not enough memory!"); - - init = VFS_Lookup("/sbin/init"); - status = VFS_Open(init); - if (status < 0) - Panic("Loading init process failed!"); - status = VFS_Read(init, pg, 0, 1024); - if (status < 0) - Panic("Reading init process failed!"); - - Thread *thr = Sched_Current(); - - // Open stdin/out/err - Handle *handle = Console_OpenHandle(); - Handle_Add(thr->proc, handle); - handle = Console_OpenHandle(); - Handle_Add(thr->proc, handle); - handle = Console_OpenHandle(); - Handle_Add(thr->proc, handle); - - Loader_LoadFirst(thr, init, pg, 1024); - - VFS_Close(init); -} - -void -LoaderFileHelper(AS *as, VNode *vn, uintptr_t vaddr, +/* + * LoaderLoadSegment -- + * + * Loads a single segment into the target address space. This function + * loads a single page at a time because it has to lookup the address + * mappings through the page tables. + */ +static void +LoaderLoadSegment(AS *as, VNode *vn, uintptr_t vaddr, uintptr_t offset, uintptr_t len) { void *raddr; @@ -187,8 +84,14 @@ LoaderFileHelper(AS *as, VNode *vn, uintptr_t vaddr, } } -void -LoaderZeroHelper(AS *as, uintptr_t vaddr, uintptr_t len) +/* + * LoaderZeroSegment -- + * + * Zeroes a segment of memory in the target address space. This is done + * one page a time while translating the virtual address to physical. + */ +static void +LoaderZeroSegment(AS *as, uintptr_t vaddr, uintptr_t len) { void *raddr; @@ -215,6 +118,11 @@ LoaderZeroHelper(AS *as, uintptr_t vaddr, uintptr_t len) } } +/* + * Loader_Load -- + * + * Load the ELF binary into the process belonging to the thread. + */ bool Loader_Load(Thread *thr, VNode *vn, void *buf, uint64_t len) { @@ -261,17 +169,101 @@ Loader_Load(Thread *thr, VNode *vn, void *buf, uint64_t len) ASSERT(phdr[i].p_type != PT_DYNAMIC); if (phdr[i].p_type == PT_LOAD) { if (phdr[i].p_filesz != 0) { - LoaderFileHelper(as, vn, phdr[i].p_vaddr, - phdr[i].p_offset, phdr[i].p_filesz); + LoaderLoadSegment(as, vn, phdr[i].p_vaddr, + phdr[i].p_offset, phdr[i].p_filesz); } - LoaderZeroHelper(as, - phdr[i].p_vaddr + phdr[i].p_filesz, - phdr[i].p_memsz - phdr[i].p_filesz); + LoaderZeroSegment(as, + phdr[i].p_vaddr + phdr[i].p_filesz, + phdr[i].p_memsz - phdr[i].p_filesz); } } - Thread_SetupUThread(thr, ehdr->e_entry, MEM_USERSPACE_STKTOP - PGSIZE); + /* Save the process entry point (i.e., _start) */ + thr->proc->entrypoint = ehdr->e_entry; return true; } +/* + * Loader_LoadInit -- + * + * The init process is created from the execution kernel thread that + * initializes the system. This function initializes the thread and + * process state then loads the init binary. + */ +void +Loader_LoadInit() +{ + int status; + void *pg; + VNode *initvn; + + pg = PAlloc_AllocPage(); + if (!pg) + Panic("Not enough memory!"); + + initvn = VFS_Lookup("/sbin/init"); + status = VFS_Open(initvn); + if (status < 0) + Panic("Loading init process failed!"); + status = VFS_Read(initvn, pg, 0, 1024); + if (status < 0) + Panic("Reading init process failed!"); + + Thread *thr = Sched_Current(); + + // Open stdin/out/err + Handle *handle = Console_OpenHandle(); + Handle_Add(thr->proc, handle); + handle = Console_OpenHandle(); + Handle_Add(thr->proc, handle); + handle = Console_OpenHandle(); + Handle_Add(thr->proc, handle); + + /* + * Load init binary + */ + Loader_Load(thr, initvn, pg, 1024); + + VFS_Close(initvn); + + Log(loader, "Jumping to userspace\n"); + + /* + * Reload the page tables for the current process + */ + PMap_LoadAS(thr->space); // Reload CR3 + + /* + * Pass in zero arguments with null pointers to init + */ + uintptr_t ap[3]; + ap[0] = 0; + ap[1] = 0; + ap[2] = 0xDEADBEEF; + uintptr_t rsp = MEM_USERSPACE_STKTOP - PGSIZE; + + Copy_Out(&ap[0], rsp, sizeof(uintptr_t)*3); + + /* + * The last step is to return into userspace handing control to init. We + * create a valid trap frame and return into userspace using Trap_Pop(). + */ + TrapFrame tf; + memset(&tf, 0, sizeof(tf)); + tf.ds = SEL_UDS | 3; + tf.rip = thr->proc->entrypoint; + tf.cs = SEL_UCS | 3; + tf.rsp = rsp; + tf.ss = SEL_UDS | 3; + tf.rflags = RFLAGS_IF; + tf.rdi = rsp; + Trap_Pop(&tf); + + /* + * We should never reach this point! + */ + Panic("Unreachable: Trap_Pop() returned!\n"); +} + + diff --git a/sys/kern/syscall.c b/sys/kern/syscall.c index de815cd..e77c021 100644 --- a/sys/kern/syscall.c +++ b/sys/kern/syscall.c @@ -168,6 +168,9 @@ Syscall_Spawn(uint64_t user_path, uint64_t user_argv) Loader_Load(thr, file, pg, 1024); + /* Initialize the trap frame for entering into the process. */ + Thread_SetupUThread(thr, proc->entrypoint, MEM_USERSPACE_STKTOP - PGSIZE); + /* Translate mapping for stack page */ argstart = (char *)DMPA2VA(PMap_Translate(thr->space, MEM_USERSPACE_STKTOP - PGSIZE)); argstart += sizeof(uintptr_t)*8;