Uses kref in the FS, fixes up refcount bugs
[akaros.git] / kern / src / elf.c
index 69e8b50..31d65ee 100644 (file)
@@ -1,28 +1,47 @@
 #include <mm.h>
+#include <frontend.h>
+#include <string.h>
 #include <ros/mman.h>
 #include <kmalloc.h>
 #include <syscall.h>
 #include <elf.h>
 #include <pmap.h>
 
-struct elf_info
+typedef struct
 {
        long entry;
+       long highest_addr;
        long phdr;
        int phnum;
        int dynamic;
        char interp[256];
-};
+} elf_info_t;
 
-int load_one_elf(struct proc* p, int fd, int pgoffset, struct elf_info* ei)
+static int
+load_one_elf(struct proc* p, struct file* f, int pgoffset, elf_info_t* ei)
 {
        int ret = -1;
        ei->phdr = -1;
        ei->dynamic = 0;
+       ei->highest_addr = 0;
+       off_t f_off = 0;
+       physaddr_t old_cr3 = rcr3();
 
+       /* Load the proc's address space, in case we need to directly write to its
+        * pages (like when we zero some of the BSS) */
+       lcr3(p->env_cr3);
+
+       // assume program headers fit in a page.
+       // if this isn't true, change the code below that maps in program headers
        char* elf = (char*)kmalloc(PGSIZE,0);
-       if(!elf || read_page(p,fd,PADDR(elf),0) == -1)
+       
+       /* 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 *cur_proc = current;
+       current = 0;
+       if(!elf || f->f_op->read(f, elf, PGSIZE, &f_off) == -1)
                goto fail;
+       current = cur_proc;
 
        elf_t* elfhdr = (elf_t*)elf;
        proghdr_t* proghdrs = (proghdr_t*)(elf+elfhdr->e_phoff);
@@ -63,103 +82,105 @@ int load_one_elf(struct proc* p, int fd, int pgoffset, struct elf_info* ei)
                        uintptr_t memstart = ROUNDDOWN(ph->p_va,PGSIZE);
                        uintptr_t memend = ROUNDUP(ph->p_va + ph->p_memsz,PGSIZE);
                        uintptr_t memsz = memend-memstart;
-
-                       // mmap will zero the rest of the page if filesz % PGSIZE != 0
-                       if(filesz)
-                               // TODO: waterman, figure out proper permissions
-                               if(mmap(p, memstart+pgoffset*PGSIZE, filesz,
-                                       PROT_READ|PROT_WRITE|PROT_EXEC, MAP_FIXED,
-                                       fd, filestart/PGSIZE) == MAP_FAILED)
+                       if(memend > ei->highest_addr)
+                               ei->highest_addr = memend;
+                       /* This needs to be a PRIVATE mapping, and the stuff after the file
+                        * needs to be zeroed. */
+                       if (filesz) {
+                               /* TODO: figure out proper permissions from the elf */
+                               if (do_mmap(p, memstart + pgoffset * PGSIZE, filesz,
+                                          PROT_READ|PROT_WRITE|PROT_EXEC, MAP_FIXED|MAP_PRIVATE,
+                                          f, filestart) == MAP_FAILED)
                                        goto fail;
-
-                       filesz = ROUNDUP(filesz,PGSIZE);
-                       if(filesz < memsz)
-                               if(mmap(p, memstart+filesz+pgoffset*PGSIZE, memsz-filesz,
-                                       PROT_READ|PROT_WRITE|PROT_EXEC, MAP_FIXED|MAP_ANON,
-                                       -1, 0) == MAP_FAILED)
+                               /* Due to elf-ghetto-ness, we need to zero the first part of the
+                                * BSS from the last page of the data segment */
+                               uintptr_t z_s = memstart + pgoffset * PGSIZE + filesz;
+                               uintptr_t z_e = ROUNDUP(z_s, PGSIZE);
+                               memset((void*)z_s, 0, z_e - z_s);
+                               filesz = ROUNDUP(filesz, PGSIZE);
+                       }
+                       /* Any extra pages are mapped anonymously... (a bit weird) */
+                       if (filesz < memsz)
+                               if (do_mmap(p, memstart + filesz + pgoffset*PGSIZE, memsz-filesz,
+                                          PROT_READ|PROT_WRITE|PROT_EXEC, MAP_FIXED|MAP_ANON,
+                                          NULL, 0) == MAP_FAILED)
                                        goto fail;
                }
        }
 
+       // map in program headers anyway if not present in binary.
+       // useful for TLS in static programs.
+       if(ei->phdr == -1)
+       {
+               void *phdr_addr = do_mmap(p, MMAP_LOWEST_VA, PGSIZE, PROT_READ, 0, f,
+                                         0);
+               if(phdr_addr == MAP_FAILED)
+                       goto fail;
+               ei->phdr = (long)phdr_addr + elfhdr->e_phoff;
+       }
+
        ei->entry = elfhdr->e_entry + pgoffset*PGSIZE;
        ei->phnum = elfhdr->e_phnum;
 
        ret = 0;
 fail:
        kfree(elf);
+       lcr3(old_cr3);
        return ret;
 }
 
-int load_elf(struct proc* p, const char* fn)
+int load_elf(struct proc* p, struct file* f)
 {
-       struct elf_info ei,interp_ei;
-       int fd = open_file(p,fn,0,0);
-       if(fd == -1 || load_one_elf(p,fd,0,&ei))
+       elf_info_t ei,interp_ei;
+       if(load_one_elf(p,f,0,&ei))
                return -1;
-       close_file(p,fd);
 
-       if(ei.dynamic)
-       {
-               int fd2 = open_file(p,ei.interp,0,0);
-               if(fd2 == -1 || load_one_elf(p,fd2,1,&interp_ei))
+       if (ei.dynamic) {
+               struct file *interp = path_to_file(ei.interp);
+               if (!interp)
                        return -1;
-               close_file(p,fd2);
-
-               // fill in info for dynamic linker
-               elf_aux_t auxp[] = {{ELF_AUX_PHDR,ei.phdr},
-                                   {ELF_AUX_PHENT,sizeof(proghdr_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->env_procinfo->argp[i] == NULL)
-                               if(++zeros == 2)
-                                       auxp_pos = i+1;
-               if(auxp_pos == -1 ||
-                  auxp_pos+sizeof(auxp)/sizeof(char*) >= PROCINFO_MAX_ARGP)
+               /* careful, this could conflict with the mmap from the TLS up above */
+               int error = load_one_elf(p, interp, 2, &interp_ei);
+               kref_put(&interp->f_kref);
+               if (error)
                        return -1;
-               memcpy(p->env_procinfo->argp+auxp_pos,auxp,sizeof(auxp));
        }
 
-       intptr_t core0_entry = ei.dynamic ? interp_ei.entry : ei.entry;
-       proc_set_program_counter(&p->env_tf,core0_entry);
-       p->env_entry = ei.entry;
-
-       uintptr_t stacksz = USTACK_NUM_PAGES*PGSIZE;
-       if(mmap(p,USTACKTOP-stacksz,stacksz,PROT_READ|PROT_WRITE,
-               MAP_FIXED|MAP_ANON,-1,0) == MAP_FAILED)
-               return -1;
-
-       return 0;
-}
-
-intreg_t sys_exec(struct proc* p, const char fn[MAX_PATH_LEN], procinfo_t* pi)
-{
-       if(p->state != PROC_RUNNING_S)
+       // fill in auxiliary info for dynamic linker/runtime
+       elf_aux_t auxp[] = {{ELF_AUX_PHDR,ei.phdr},
+                           {ELF_AUX_PHENT,sizeof(proghdr_t)},
+                           {ELF_AUX_PHNUM,ei.phnum},
+                           {ELF_AUX_ENTRY,ei.entry},
+                           #ifdef __sparc_v8__
+                           {ELF_AUX_HWCAP,ELF_HWCAP_SPARC_FLUSH},
+                           #endif
+                           {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)
                return -1;
+       memcpy(p->procinfo->argp+auxp_pos,auxp,sizeof(auxp));
 
-       char kfn[MAX_PATH_LEN];
-       if(memcpy_from_user(p,kfn,fn,MAX_PATH_LEN))
-               return -1;
+       uintptr_t core0_entry = ei.dynamic ? interp_ei.entry : ei.entry;
+       proc_init_trapframe(&p->env_tf,0,core0_entry,USTACKTOP);
+       p->env_entry = ei.entry;
 
-       if(memcpy_from_user(p,p->env_procinfo,pi,sizeof(procinfo_t)))
-       {
-               proc_destroy(p);
+       // map in stack using POPULATE (because SPARC requires it)
+       uintptr_t stacksz = USTACK_NUM_PAGES*PGSIZE;
+       if(do_mmap(p, USTACKTOP-stacksz, stacksz, PROT_READ | PROT_WRITE,
+                  MAP_FIXED | MAP_ANONYMOUS | MAP_POPULATE, NULL, 0) == MAP_FAILED)
                return -1;
-       }
-       proc_init_procinfo(p);
-
-       env_segment_free(p,0,USTACKTOP);
 
-       if(load_elf(p,kfn))
-       {
-               proc_destroy(p);
-               return -1;
-       }
-       *current_tf = p->env_tf;
+       // 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;
 
        return 0;
 }