aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/elf.c115
-rw-r--r--src/initrd.c3
-rw-r--r--src/proc.c3
-rw-r--r--src/regions.c10
-rw-r--r--src/uapi/proc.c35
-rw-r--r--src/vmem.c18
6 files changed, 117 insertions, 67 deletions
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, &region_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);
-
- 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);
+ if (__elf_map_section(t, va, vsz, vf, vfz, uvflags)) {
+ destroy_uvmem(t);
- /* 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;