diff options
Diffstat (limited to 'kernel/exec.c')
-rw-r--r-- | kernel/exec.c | 149 |
1 files changed, 149 insertions, 0 deletions
diff --git a/kernel/exec.c b/kernel/exec.c new file mode 100644 index 0000000..c9af395 --- /dev/null +++ b/kernel/exec.c @@ -0,0 +1,149 @@ +#include "types.h" +#include "param.h" +#include "memlayout.h" +#include "riscv.h" +#include "proc.h" +#include "defs.h" +#include "elf.h" + +static int loadseg(pde_t *pgdir, uint64 addr, struct inode *ip, uint offset, uint sz); + +int +exec(char *path, char **argv) +{ + char *s, *last; + int i, off; + uint64 argc, sz, sp, ustack[MAXARG+1], stackbase; + struct elfhdr elf; + struct inode *ip; + struct proghdr ph; + pagetable_t pagetable = 0, oldpagetable; + struct proc *p = myproc(); + uint64 oldsz = p->sz; + + begin_op(); + + if((ip = namei(path)) == 0){ + end_op(); + return -1; + } + ilock(ip); + + // Check ELF header + if(readi(ip, 0, (uint64)&elf, 0, sizeof(elf)) != sizeof(elf)) + goto bad; + if(elf.magic != ELF_MAGIC) + goto bad; + + if((pagetable = proc_pagetable(p)) == 0) + goto bad; + + // Load program into memory. + sz = 0; + for(i=0, off=elf.phoff; i<elf.phnum; i++, off+=sizeof(ph)){ + if(readi(ip, 0, (uint64)&ph, off, sizeof(ph)) != sizeof(ph)) + goto bad; + if(ph.type != ELF_PROG_LOAD) + continue; + if(ph.memsz < ph.filesz) + goto bad; + if(ph.vaddr + ph.memsz < ph.vaddr) + goto bad; + if((sz = uvmalloc(pagetable, sz, ph.vaddr + ph.memsz)) == 0) + goto bad; + if(ph.vaddr % PGSIZE != 0) + goto bad; + if(loadseg(pagetable, ph.vaddr, ip, ph.off, ph.filesz) < 0) + goto bad; + } + iunlockput(ip); + end_op(); + ip = 0; + + // Allocate two pages at the next page boundary. + // Use the second as the user stack. + sz = PGROUNDUP(sz); + if((sz = uvmalloc(pagetable, sz, sz + 2*PGSIZE)) == 0) + goto bad; + sp = sz; + stackbase = sp - PGSIZE; + + // Push argument strings, prepare rest of stack in ustack. + for(argc = 0; argv[argc]; argc++) { + if(argc >= MAXARG) + goto bad; + sp -= strlen(argv[argc]) + 1; + sp -= sp % 16; // riscv sp must be 16-byte aligned + if(sp < stackbase) + goto bad; + if(copyout(pagetable, sp, argv[argc], strlen(argv[argc]) + 1) < 0) + goto bad; + ustack[argc] = sp; + } + ustack[argc] = 0; + + // push the array of argv[] pointers. + sp -= (argc+1) * sizeof(uint64); + sp -= sp % 16; + if(sp < stackbase) + goto bad; + if(copyout(pagetable, sp, (char *)ustack, (argc+1)*sizeof(uint64)) < 0) + goto bad; + + // arguments to user main(argc, argv) + // argc is returned via the system call return + // value, which goes in a0. + p->tf->a1 = sp; + + // Save program name for debugging. + for(last=s=path; *s; s++) + if(*s == '/') + last = s+1; + safestrcpy(p->name, last, sizeof(p->name)); + + // Commit to the user image. + oldpagetable = p->pagetable; + p->pagetable = pagetable; + p->sz = sz; + p->tf->epc = elf.entry; // initial program counter = main + p->tf->sp = sp; // initial stack pointer + proc_freepagetable(oldpagetable, oldsz); + return argc; // this ends up in a0, the first argument to main(argc, argv) + + bad: + if(pagetable) + proc_freepagetable(pagetable, sz); + if(ip){ + iunlockput(ip); + end_op(); + } + return -1; +} + +// Load a program segment into pagetable at virtual address va. +// va must be page-aligned +// and the pages from va to va+sz must already be mapped. +// Returns 0 on success, -1 on failure. +static int +loadseg(pagetable_t pagetable, uint64 va, struct inode *ip, uint offset, uint sz) +{ + uint i, n; + uint64 pa; + + if((va % PGSIZE) != 0) + panic("loadseg: va must be page aligned"); + + for(i = 0; i < sz; i += PGSIZE){ + pa = walkaddr(pagetable, va + i); + if(pa == 0) + panic("loadseg: address should exist"); + if(sz - i < PGSIZE) + n = sz - i; + else + n = PGSIZE; + if(readi(ip, 0, (uint64)pa, offset+i, n) != n) + return -1; + } + + return 0; +} |