#include <pmap.h>
#include <smp.h>
#include <arch/arch.h>
+#include <umem.h>
#ifdef CONFIG_64BIT
# define elf_field(obj, field) (elf64 ? (obj##64)->field : (obj##32)->field)
{
elf64_t h;
off64_t o = 0;
- struct proc *c = switch_to(0);
+ uintptr_t c = switch_to_ktask();
if (f->f_op->read(f, (char*)&h, sizeof(elf64_t), &o) != sizeof(elf64_t)) {
goto fail;
goto fail;
}
success:
- switch_back(0, c);
+ switch_back_from_ktask(c);
return TRUE;
fail:
- switch_back(0, c);
+ switch_back_from_ktask(c);
return FALSE;
}
+static uintptr_t populate_stack(struct proc *p, int argc, char *argv[],
+ int envc, char *envp[],
+ int auxc, elf_aux_t auxv[])
+{
+ /* Map in pages for p's stack. */
+ int flags = MAP_FIXED | MAP_ANONYMOUS | MAP_PRIVATE;
+ uintptr_t stacksz = USTACK_NUM_PAGES*PGSIZE;
+ if (do_mmap(p, USTACKTOP-stacksz, stacksz, PROT_READ | PROT_WRITE,
+ flags, NULL, 0) == MAP_FAILED)
+ return 0;
+
+ /* Function to get the lengths of the argument and environment strings. */
+ int get_lens(int argc, char *argv[], int arg_lens[])
+ {
+ int total = 0;
+ for (int i = 0; i < argc; i++) {
+ arg_lens[i] = strlen(argv[i]) + 1;
+ total += arg_lens[i];
+ }
+ return total;
+ }
+
+ /* Function to help map the argument and environment strings, to their
+ * final location. */
+ int remap(int argc, char *argv[], char *new_argv[],
+ char new_argbuf[], int arg_lens[])
+ {
+ int offset = 0;
+ char *temp_argv[argc + 1];
+ for(int i = 0; i < argc; i++) {
+ if (memcpy_to_user(p, new_argbuf + offset, argv[i], arg_lens[i]))
+ return -1;
+ temp_argv[i] = new_argbuf + offset;
+ offset += arg_lens[i];
+ }
+ temp_argv[argc] = NULL;
+ if (memcpy_to_user(p, new_argv, temp_argv, sizeof(temp_argv)))
+ return -1;
+ return offset;
+ }
+
+ /* Start tracking the size of the buffer necessary to hold all of our data
+ * on the stack. Preallocate space for argc, argv, envp, and auxv in this
+ * buffer. */
+ int bufsize = 0;
+ bufsize += 1 * sizeof(size_t);
+ bufsize += (auxc + 1) * sizeof(elf_aux_t);
+ bufsize += (envc + 1) * sizeof(char**);
+ bufsize += (argc + 1) * sizeof(char**);
+
+ /* Add in the size of the env and arg strings. */
+ int arg_lens[argc];
+ int env_lens[envc];
+ bufsize += get_lens(argc, argv, arg_lens);
+ bufsize += get_lens(envc, envp, env_lens);
+
+ /* Adjust bufsize so that our buffer will ultimately be 16 byte aligned. */
+ bufsize = ROUNDUP(bufsize, 16);
+
+ /* Set up pointers to all of the appropriate data regions we map to. */
+ size_t *new_argc = (size_t*)(USTACKTOP - bufsize);
+ char **new_argv = (char**)(new_argc + 1);
+ char **new_envp = new_argv + argc + 1;
+ elf_aux_t *new_auxv = (elf_aux_t*)(new_envp + envc + 1);
+ char *new_argbuf = (char*)(new_auxv + auxc + 1);
+
+ /* Verify that all data associated with our argv, envp, and auxv arrays
+ * (and any corresponding strings they point to) will fit in the space
+ * alloted. */
+ if (bufsize > ARG_MAX)
+ return 0;
+
+ /* Map argc into its final location. */
+ if (memcpy_to_user(p, new_argc, &argc, sizeof(size_t)))
+ return 0;
+
+ /* Map all data for argv and envp into its final location. */
+ int offset = 0;
+ offset = remap(argc, argv, new_argv, new_argbuf, arg_lens);
+ if (offset == -1)
+ return 0;
+ offset = remap(envc, envp, new_envp, new_argbuf + offset, env_lens);
+ if (offset == -1)
+ return 0;
+
+ /* Map auxv into its final location. */
+ elf_aux_t null_aux = {0, 0};
+ if (memcpy_to_user(p, new_auxv, auxv, auxc * sizeof(elf_aux_t)))
+ return 0;
+ if (memcpy_to_user(p, new_auxv + auxc, &null_aux, sizeof(elf_aux_t)))
+ return 0;
+
+ return USTACKTOP - bufsize;
+}
+
/* We need the writable flag for ld. Even though the elf header says it wants
* RX (and not W) for its main program header, it will page fault (eip 56f0,
* 46f0 after being relocated to 0x1000, va 0x20f4). */
-static int load_one_elf(struct proc *p, struct file *f, uintptr_t pgoffset,
+static int load_one_elf(struct proc *p, struct file *f, uintptr_t pg_num,
elf_info_t *ei, bool writable)
{
int ret = -1;
ei->highest_addr = 0;
off64_t f_off = 0;
void* phdrs = 0;
- int mm_perms, mm_flags = MAP_FIXED;
-
- /* When reading on behalf of the kernel, we need to make sure no proc is
- * "current". This is a bit ghetto (TODO: KFOP) */
- struct proc *old_proc = switch_to(0);
+ int mm_perms, mm_flags;
+
+ /* When reading on behalf of the kernel, we need to switch to a ktask so
+ * the VFS (and maybe other places) know. (TODO: KFOP) */
+ uintptr_t old_ret = switch_to_ktask();
/* Read in ELF header. */
elf64_t elfhdr_storage;
p_flags |= (writable ? ELF_PROT_WRITE : 0);
/* All mmaps need to be fixed to their VAs. If the program wants it to
* be a writable region, we also need the region to be private. */
- mm_flags = MAP_FIXED | (p_flags & ELF_PROT_WRITE ? MAP_PRIVATE : 0);
+ mm_flags = MAP_FIXED |
+ (p_flags & ELF_PROT_WRITE ? MAP_PRIVATE : MAP_SHARED);
if (p_type == ELF_PROG_PHDR)
ei->phdr = p_va;
uintptr_t memstart = ROUNDDOWN(p_va, PGSIZE);
uintptr_t memsz = ROUNDUP(p_va + p_memsz, PGSIZE) - memstart;
- memstart += pgoffset * PGSIZE;
+ memstart += pg_num * PGSIZE;
if (memstart + memsz > ei->highest_addr)
ei->highest_addr = memstart + memsz;
/* Need our own populated, private copy of the page so that
* we can zero the remainder - and not zero chunks of the
* real file in the page cache. */
+ mm_flags &= ~MAP_SHARED;
mm_flags |= MAP_PRIVATE | MAP_POPULATE;
/* Map the final partial page. */
}
ei->phdr = (long)phdr_addr + e_phoff;
}
- ei->entry = elf_field(elfhdr, e_entry) + pgoffset*PGSIZE;
+ ei->entry = elf_field(elfhdr, e_entry) + pg_num * PGSIZE;
ei->phnum = e_phnum;
ei->elf64 = elf64;
ret = 0;
fail:
if (phdrs)
kfree(phdrs);
- switch_back(0, old_proc);
+ switch_back_from_ktask(old_ret);
return ret;
}
-int load_elf(struct proc* p, struct file* f)
+int load_elf(struct proc* p, struct file* f,
+ int argc, char *argv[], int envc, char *envp[])
{
elf_info_t ei, interp_ei;
if (load_one_elf(p, f, 0, &ei, FALSE))
return -1;
if (ei.dynamic) {
- struct file *interp = do_file_open(ei.interp, 0, 0);
+ struct file *interp = do_file_open(ei.interp, O_READ, 0);
if (!interp)
return -1;
/* Load dynamic linker at 1M. Obvious MIB joke avoided.
* helped us waste a full day debugging a bug in the Go runtime. True!
* Note that MMAP_LOWEST_VA also has this value but we want to make this
* explicit. */
- int error = load_one_elf(p, interp, MiB>>12, &interp_ei, TRUE);
+ int error = load_one_elf(p, interp, MMAP_LD_FIXED_VA >> PGSHIFT,
+ &interp_ei, TRUE);
kref_put(&interp->f_kref);
if (error)
return -1;
}
- // fill in auxiliary info for dynamic linker/runtime
- elf_aux_t auxp[] = {{ELF_AUX_PHDR, ei.phdr},
+ /* Set up the auxiliary info for dynamic linker/runtime */
+ elf_aux_t auxv[] = {{ELF_AUX_PHDR, ei.phdr},
{ELF_AUX_PHENT, sizeof(proghdr32_t)},
{ELF_AUX_PHNUM, ei.phnum},
- {ELF_AUX_ENTRY, ei.entry},
- {0, 0}};
-
- // put auxp after argv, envp in procinfo
- int auxp_pos = -1;
- for (int i = 0, zeros = 0; i < PROCINFO_MAX_ARGP; i++)
- if (p->procinfo->argp[i] == NULL)
- if (++zeros == 2)
- auxp_pos = i + 1;
- if (auxp_pos == -1 ||
- auxp_pos + sizeof(auxp) / sizeof(char*) >= PROCINFO_MAX_ARGP)
+ {ELF_AUX_ENTRY, ei.entry}};
+ int auxc = sizeof(auxv)/sizeof(auxv[0]);
+
+ /* Populate the stack with the required info. */
+ uintptr_t stack_top = populate_stack(p, argc, argv, envc, envp, auxc, auxv);
+ if (!stack_top)
return -1;
- memcpy(p->procinfo->argp+auxp_pos,auxp,sizeof(auxp));
+ /* Initialize the process as an SCP. */
uintptr_t core0_entry = ei.dynamic ? interp_ei.entry : ei.entry;
- proc_init_ctx(&p->scp_ctx, 0, core0_entry, USTACKTOP, 0);
- p->env_entry = ei.entry;
-
- int flags = MAP_FIXED | MAP_ANONYMOUS;
- uintptr_t stacksz = USTACK_NUM_PAGES*PGSIZE;
- if (do_mmap(p, USTACKTOP-stacksz, stacksz, PROT_READ | PROT_WRITE,
- flags, NULL, 0) == MAP_FAILED)
- return -1;
+ proc_init_ctx(&p->scp_ctx, 0, core0_entry, stack_top, 0);
- // Set the heap bottom and top to just past where the text
- // region has been loaded
- p->heap_top = (void*)ei.highest_addr;
- p->procinfo->heap_bottom = p->heap_top;
+ p->procinfo->program_end = ei.highest_addr;
+ p->args_base = (void *) stack_top;
return 0;
}
+ssize_t get_startup_argc(struct proc *p)
+{
+ const char *sptr = (const char *) p->args_base;
+ ssize_t argc = 0;
+
+ /* TODO,DL: Use copy_from_user() when available.
+ */
+ if (memcpy_from_user(p, &argc, sptr, sizeof(size_t)))
+ return -1;
+
+ return argc;
+}
+
+char *get_startup_argv(struct proc *p, size_t idx, char *argp,
+ size_t max_size)
+{
+ size_t stack_space = (const char *) USTACKTOP - (const char *) p->args_base;
+ const char *sptr = (const char *) p->args_base + sizeof(size_t) +
+ idx * sizeof(char *);
+ const char *argv = NULL;
+
+ /* TODO,DL: Use copy_from_user() when available.
+ */
+ if (memcpy_from_user(p, &argv, sptr, sizeof(char *)))
+ return NULL;
+
+ /* TODO,DL: Use strncpy_from_user() when available.
+ */
+ max_size = MIN(max_size, stack_space);
+ if (memcpy_from_user(p, argp, argv, max_size))
+ return NULL;
+ argp[max_size - 1] = 0;
+
+ return argp;
+}