From d127146846f4d6561e15000d1469b715dafdb627 Mon Sep 17 00:00:00 2001 From: Kimplul Date: Sat, 2 Nov 2024 15:28:23 +0200 Subject: initial exec support --- src/elf.c | 115 +++++++++++++++++++++++++++++++++++++++++--------------- src/initrd.c | 3 ++ src/proc.c | 3 ++ src/regions.c | 10 +++++ src/uapi/proc.c | 35 ++++------------- src/vmem.c | 18 ++++----- 6 files changed, 117 insertions(+), 67 deletions(-) (limited to 'src') diff --git a/src/elf.c b/src/elf.c index 259c77e..2cdcb8b 100644 --- a/src/elf.c +++ b/src/elf.c @@ -33,6 +33,41 @@ static uint8_t __elf_to_uvflags(uint8_t elf_flags) return uvflags; } +static stat_t __elf_map_section(struct tcb *t, + vm_t va, size_t vaz, + vm_t vf, size_t vfz, + uint8_t flags) +{ + size_t region_size; + vm_t v = alloc_fixed_region(&t->uvmem.region, va, vaz, ®ion_size, flags); + if (!v) + return ERR_INVAL; + + assert(v == align_down(va, BASE_PAGE_SIZE)); + + for (size_t runner = 0; runner < vaz; runner += BASE_PAGE_SIZE) { + pm_t page = alloc_page(BASE_PAGE); + if (!page) + return ERR_OOMEM; + + /* always zero out pages */ + memset((void *)page, 0, BASE_PAGE_SIZE); + + /* sometimes fill page with actual data */ + if (runner < vfz) { + size_t z = MIN(BASE_PAGE_SIZE, vfz - runner); + memcpy((void *)page, (void *)(vf + runner), z); + } + + if (map_vpage(t->proc.vmem, page, va + runner, flags, BASE_PAGE)) { + free_page(BASE_PAGE, page); + return ERR_OOMEM; + } + } + + return OK; +} + /** * Map ELF executable. * @@ -43,58 +78,76 @@ static uint8_t __elf_to_uvflags(uint8_t elf_flags) * @param phnum Number of program header entries. * @param phsize Size of page header entry. */ -static void __map_exec(struct tcb *t, vm_t bin, uint8_t ei_c, vm_t phstart, - size_t phnum, size_t phsize) +static stat_t __map_exec(struct tcb *t, + vm_t bin, + uint8_t ei_c, + vm_t phstart, + size_t phnum, + size_t phsize) { assert(t && is_proc(t)); /* temporarily visit process virtual memory */ use_vmem(t->proc.vmem); + /* create empty vmem so we don't have to worry about possible overlaps */ + struct vmem *new_vmem = create_vmem(); + if (!new_vmem) { + use_vmem(t->rpc.vmem); + return ERR_OOMEM; + } + struct vmem *old_vmem = t->proc.vmem; + t->proc.vmem = new_vmem; + + /* create new uvmem for same reason */ + struct uvmem old_uvmem = t->uvmem; + t->uvmem = (struct uvmem){0}; + + if (init_uvmem(t)) { + destroy_vmem(new_vmem); + t->uvmem = old_uvmem; + t->proc.vmem = old_vmem; + use_vmem(t->rpc.vmem); + return ERR_OOMEM; + } + /** \todo take alignment into consideration? */ - /** \todo take overlapping memory regions into account, probably mostly - * by keeping track of previously allocated area and seeing if the - * segment fits into it */ - /** \todo check if p_memsz is larger than p_filesz, the segment should be - * filled with zeroes. */ - /** \todo in general, make this a lot more clean. */ /* useful bit of info: all segments are sorted in ascending order of p_vaddr */ vm_t runner = phstart; - vmflags_t default_flags = VM_V | VM_R | VM_W | VM_X | VM_U; for (size_t i = 0; i < phnum; ++i, runner += phsize) { if (program_header_prop(ei_c, runner, p_type) != PT_LOAD) continue; + /* where to map section in virtual memory */ vm_t va = program_header_prop(ei_c, runner, p_vaddr); size_t vsz = program_header_prop(ei_c, runner, p_memsz); - vm_t start = alloc_fixed_uvmem(t, va, vsz, default_flags); - if (!start) - return; /* out of memory or something */ - - info("mapped ELF section to %lx\n", (long)start); + /* where section is in binary */ + vm_t vf = bin + program_header_prop(ei_c, runner, p_offset); + vm_t vfz = program_header_prop(ei_c, runner, p_filesz); uint8_t elf_flags = program_header_prop(ei_c, runner, p_flags); uint8_t uvflags = __elf_to_uvflags(elf_flags); - map_region(t->proc.vmem, start, vsz, max_order(), - default_flags); - memset((void *)start, 0, vsz); + if (__elf_map_section(t, va, vsz, vf, vfz, uvflags)) { + destroy_uvmem(t); - vm_t vo = bin + program_header_prop(ei_c, runner, p_offset); - vm_t vfz = program_header_prop(ei_c, runner, p_filesz); - memcpy((void *)va, (void *)vo, vfz); - - /* skip while testing - * \todo: also fix, this modifies only the first region. Create new - * function? - * - pm_t paddr = 0; - stat_vpage(t->b_r, va, &paddr, 0, 0); - mod_vpage(t->b_r, va, paddr, uvflags); - */ + t->proc.vmem = old_vmem; + t->uvmem = old_uvmem; + use_vmem(t->rpc.vmem); + return ERR_OOMEM; + } } + /* destroy old uvmem (kind of annoying to have it so agressively tied to + * the tcb but I guess it's find for now) */ + struct uvmem new_uvmem = t->uvmem; + t->uvmem = old_uvmem; + destroy_uvmem(t); + + t->proc.vmem = new_vmem; + t->uvmem = new_uvmem; use_vmem(t->rpc.vmem); + return OK; } /** @@ -140,7 +193,9 @@ static vm_t __prepare_proc(struct tcb *t, uint8_t ei_c, vm_t elf, vm_t interp) vm_t entry = elf_header_prop(ei_c, elf, e_entry); if (e_type == ET_EXEC) { - __map_exec(t, elf, ei_c, phstart, phnum, phsize); + if (__map_exec(t, elf, ei_c, phstart, phnum, phsize)) + return 0; + return entry; } else { vm_t o = __map_dyn(t, elf, ei_c, phstart, phnum, phsize); diff --git a/src/initrd.c b/src/initrd.c index 489d6f5..e1a0c1e 100644 --- a/src/initrd.c +++ b/src/initrd.c @@ -96,6 +96,9 @@ static struct cpio_header *__find_file(const char *c, const char *fname, continue; char *name = (char *)(cp + 1); + if (strcmp(name, "TAILER!!!") == 0) + return NULL; + if (fname[0] != '/') name += namelen - (fname_len + 1); /* match ending */ diff --git a/src/proc.c b/src/proc.c index 920cab0..c1dfc64 100644 --- a/src/proc.c +++ b/src/proc.c @@ -67,11 +67,14 @@ stat_t init_proc(void *fdt, vm_t *proc_fdt, vm_t *proc_initrd) *proc_fdt = map_shared_fixed_uvmem(t, (pm_t)fdt, fdt_totalsize(fdt), VM_V | VM_R | VM_U); + assert(*proc_fdt); pm_t initrd = (pm_t)__va(get_initrdbase(fdt)); *proc_initrd = map_shared_fixed_uvmem(t, initrd, get_initrdsize(fdt), VM_V | VM_R | VM_U); + assert(proc_initrd); + info("mapped fdt at %lx\n", *proc_fdt); info("mapped initrd at %lx\n", *proc_initrd); return OK; diff --git a/src/regions.c b/src/regions.c index 2db1a91..2c11db0 100644 --- a/src/regions.c +++ b/src/regions.c @@ -241,6 +241,16 @@ struct mem_region *find_used_region(struct mem_region_root *r, vm_t start) return 0; } +struct mem_region *find_addr_region(struct mem_region_root *r, vm_t addr) +{ + size_t ref = __page(addr); + struct mem_region *m = find_closest_used_region(r, addr); + if (!m || (ref < m->start || ref > m->end) || !is_region_used(m)) + return NULL; + + return m; +} + /** * Create memory region. * diff --git a/src/uapi/proc.c b/src/uapi/proc.c index 3b65a04..8d90687 100644 --- a/src/uapi/proc.c +++ b/src/uapi/proc.c @@ -108,46 +108,27 @@ SYSCALL_DEFINE2(exec)(struct tcb *t, sys_arg_t bin, sys_arg_t interp) return_args1(t, ERR_PERM); /* exec is only allowed if we own all our own resources */ - if (t->refcount) + if (t->refcount != 1) return_args1(t, ERR_INVAL); /* mark binary to be kept */ - struct mem_region *b = find_used_region(&t->uvmem.region, bin); + struct mem_region *b = find_addr_region(&t->uvmem.region, bin); if (!b) return_args1(t, ERR_ADDR); - set_bit(b->flags, MR_KEEP); - - struct mem_region *i = 0; + struct mem_region *i = NULL; if (interp) { /* mark interpreter to be kept */ - i = find_used_region(&t->uvmem.region, interp); + i = find_addr_region(&t->uvmem.region, interp); if (!i) - return_args1(t, ERR_INVAL); + return_args1(t, ERR_ADDR); - set_bit(i->flags, MR_KEEP); } - /* free everything except regions to be kept */ - clear_uvmem(t); - - /* restore to normal */ - clear_bit(b->flags, MR_KEEP); - if (interp) - clear_bit(b->flags, MR_KEEP); - - /* should hopefully never actually fail, but if it does, we don't really - * have any choice but to kill the thread. */ - if (prepare_proc(t, bin, interp)) { - /* this kills the thread */ - orphanize(t); - unorphanize(t); - /* should never be reached as we control the thread so we should - * be able to directly jump to pid 1 */ - assert(false); - } + if (prepare_proc(t, bin, interp)) + return_args1(t, ERR_INVAL); - return_args4(t, 0, t->tid, SYS_USER_SPAWNED, t->pid); + set_ret4(t, 0, t->tid, SYS_USER_SPAWNED, t->pid); } /** diff --git a/src/vmem.c b/src/vmem.c index 5681a85..2752d23 100644 --- a/src/vmem.c +++ b/src/vmem.c @@ -166,8 +166,8 @@ static vm_t __clone_shared_region(struct tcb *d, struct tcb *s, size_t size = end - start; vm_t v = alloc_shared_region(&d->uvmem.region, size, &size, MR_NONBACKED | m->flags, s->rid); - if (ERR_CODE(v)) - return v; + if (!v) + return 0; stat_t res = clone_region(d->uvmem.vmem, s->uvmem.vmem, start, v, size, flags); @@ -329,10 +329,10 @@ vm_t map_shared_fixed_uvmem(struct tcb *t, pm_t start, size_t size, { assert(is_aligned(start, BASE_PAGE_SIZE)); - const vm_t v = alloc_shared_region(&t->uvmem.region, size, &size, flags, - 0); - if (ERR_CODE(v)) - return v; + const vm_t v = alloc_shared_region(&t->uvmem.region, + size, &size, flags, 0); + if (!v) + return 0; stat_t ret = OK; if ((ret = map_fixed_region(t->proc.vmem, v, start, size, flags))) { @@ -438,10 +438,8 @@ void handle_pagefault(vm_t addr) } struct tcb *p = get_cproc(t); - size_t ref = __page(addr); - - struct mem_region *m = find_closest_used_region(&p->uvmem.region, addr); - if (!m || (ref < m->start || ref > m->end) || !is_region_used(m)) { + struct mem_region *m = find_addr_region(&p->uvmem.region, addr); + if (!m) { error("cannot handle actual page fault just yet :(\n"); kernel_panic(NULL, NULL, 0); return; -- cgit v1.3