#include "types.h" #include "param.h" #include "memlayout.h" #include "riscv.h" #include "spinlock.h" #include "proc.h" #include "defs.h" #include "elf.h" static int loadseg(pde_t *, u64, struct inode *, u32, u32); int flags2perm(int flags) { int perm = 0; if(flags & 0x1) perm = PTE_X; if(flags & 0x2) perm |= PTE_W; return perm; } int exec(char *path, char **argv) { char *s, *last; int i, off; u64 argc, sz = 0, sp, ustack[MAXARG], stackbase; struct elfhdr elf; struct inode *ip; struct proghdr ph; pagetable_t pagetable = 0, oldpagetable; struct proc *p = myproc(); begin_op(); if((ip = namei(path)) == 0) { end_op(); return -1; } ilock(ip); // Check ELF header if(readi(ip, 0, (u64)&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. for(i = 0, off = elf.phoff; i < elf.phnum; i++, off += sizeof(ph)) { if(readi(ip, 0, (u64)&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(ph.vaddr % PGSIZE != 0) goto bad; u64 sz1; if((sz1 = uvmalloc(pagetable, sz, ph.vaddr + ph.memsz, flags2perm(ph.flags))) == 0) goto bad; sz = sz1; if(loadseg(pagetable, ph.vaddr, ip, ph.off, ph.filesz) < 0) goto bad; } iunlockput(ip); end_op(); ip = 0; p = myproc(); u64 oldsz = p->sz; // Allocate two pages at the next page boundary. // Make the first inaccessible as a stack guard. // Use the second as the user stack. sz = PGROUNDUP(sz); u64 sz1; if((sz1 = uvmalloc(pagetable, sz, sz + 2 * PGSIZE, PTE_W)) == 0) goto bad; sz = sz1; uvmclear(pagetable, sz - 2 * PGSIZE); 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(u64); sp -= sp % 16; if(sp < stackbase) goto bad; if(copyout(pagetable, sp, (char *)ustack, (argc + 1) * sizeof(u64)) < 0) goto bad; // arguments to user main(argc, argv) // argc is returned via the system call return // value, which goes in a0. p->trapframe->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->trapframe->epc = elf.entry; // initial program counter = main p->trapframe->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, u64 va, struct inode *ip, u32 offset, u32 sz) { u32 i, n; u64 pa; 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, (u64)pa, offset + i, n) != n) return -1; } return 0; }