text: #include "types.h"
  #include "param.h"
  #include "memlayout.h"
  #include "mmu.h"
  #include "arm.h"
  #include "spinlock.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 = 0, sp, ustack[MAXARG+1], stackbase;
      struct elfhdr elf;
      struct inode *ip;
      struct proghdr ph;
      pagetable_t pagetable = 0, oldpagetable;
      struct proc *p = myproc();
      uint64 sz1;
  
      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 memeory
      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((sz1 = uvmalloc(pagetable, sz, ph.vaddr + ph.memsz)) == 0)
              goto bad;
          sz = sz1;
          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;
  
      p = myproc();
      uint64 oldsz = p->sz;
  
      // Allocate two pages at the next page boundary.
      // Use the second as the user stack.
      sz = PGROUNDUP(sz);
      if((sz1 = uvmalloc(pagetable, sz, sz + 2 * PGSIZE)) == 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 &= ~7;
          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 &= ~7;
      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->trapframe->x0 = argc;
      p->trapframe->x1 = sp;
  
      // Save programe 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->sp = sp;
      p->trapframe->elr = elf.entry;
  
      uvmswitch(p);
      proc_freepagetable(oldpagetable, oldsz);
  
      return argc;
  
  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)P2V(pa), offset + i, n) != n)
              return -1;
      }
  
      return 0;
  }