From c59361f1430ec485596d1bf5d43339af0b5a2705 Mon Sep 17 00:00:00 2001
From: rtm <rtm>
Date: Thu, 27 Jul 2006 21:10:00 +0000
Subject: [PATCH] primitive exec

---
 Makefile  |   4 +-
 defs.h    |   1 +
 fs.c      |  23 +++++++++
 main.c    |   1 +
 mkfs.c    | 148 +++++++++++++++++++++++++++++++++++++++++-------------
 proc.c    |   3 ++
 syscall.c | 100 ++++++++++++++++++++++++++++++++++--
 syscall.h |   1 +
 userfs.c  |   1 +
 usys.S    |   1 +
 10 files changed, 242 insertions(+), 41 deletions(-)

diff --git a/Makefile b/Makefile
index d995429..d45bc6f 100644
--- a/Makefile
+++ b/Makefile
@@ -73,8 +73,8 @@ userfs : userfs.o $(ULIB)
 mkfs : mkfs.c fs.h
 	cc -o mkfs mkfs.c
 
-fs.img : mkfs
-	./mkfs fs.img
+fs.img : mkfs usertests
+	./mkfs fs.img usertests
 
 -include *.d
 
diff --git a/defs.h b/defs.h
index 35a7139..84616c9 100644
--- a/defs.h
+++ b/defs.h
@@ -102,3 +102,4 @@ void iunlock(struct inode *ip);
 void iincref(struct inode *ip);
 void iput(struct inode *ip);
 struct inode * namei(char *path);
+int readi(struct inode *ip, void *xdst, uint off, uint n);
diff --git a/fs.c b/fs.c
index 3eda6e9..3b06bc7 100644
--- a/fs.c
+++ b/fs.c
@@ -133,6 +133,29 @@ bmap(struct inode *ip, uint bn)
   return x;
 }
 
+#define min(a, b) ((a) < (b) ? (a) : (b))
+
+int
+readi(struct inode *ip, void *xdst, uint off, uint n)
+{
+  char *dst = (char *) xdst;
+  uint target = n, n1;
+  struct buf *bp;
+
+  while(n > 0 && off < ip->size){
+    bp = bread(ip->dev, bmap(ip, off / 512));
+    n1 = min(n, ip->size - off);
+    n1 = min(n1, 512 - (off % 512));
+    memmove(dst, bp->data + (off % 512), n1);
+    n -= n1;
+    off += n1;
+    dst += n1;
+    brelse(bp);
+  }
+
+  return target - n;
+}
+
 struct inode *
 namei(char *path)
 {
diff --git a/main.c b/main.c
index 8a86908..1b8bdbe 100644
--- a/main.c
+++ b/main.c
@@ -100,6 +100,7 @@ mpmain(void)
   lapic_enableintr();
 
   // Enable interrupts on this processor.
+  cprintf("cpu %d initial nlock %d\n", cpu(), cpus[cpu()].nlock);
   cpus[cpu()].nlock--;
   sti();
 
diff --git a/mkfs.c b/mkfs.c
index 9dcb29c..ae66dfd 100644
--- a/mkfs.c
+++ b/mkfs.c
@@ -3,6 +3,7 @@
 #include <stdlib.h>
 #include <string.h>
 #include <fcntl.h>
+#include <assert.h>
 #include "types.h"
 #include "param.h"
 #include "fs.h"
@@ -10,14 +11,17 @@
 int nblocks = 1009;
 int ninodes = 100;
 
-int fd;
+int fsfd;
 struct superblock sb;
 char zeroes[512];
 uint freeblock;
+uint freeinode = 1;
 
 void wsect(uint, void *);
 void winode(uint, struct dinode *);
 void rsect(uint sec, void *buf);
+uint ialloc(ushort type);
+void iappend(uint inum, void *p, int n);
 
 // convert to intel byte order
 ushort
@@ -44,27 +48,21 @@ xint(uint x)
 
 main(int argc, char *argv[])
 {
-  int i;
-  struct dinode din;
-  char dbuf[512];
-  uint bn;
+  int i, cc, fd;
+  uint bn, rootino, inum;
+  struct dirent de;
+  char buf[512];
 
-  if(argc != 2){
-    fprintf(stderr, "Usage: mkfs fs.img\n");
+  if(argc < 2){
+    fprintf(stderr, "Usage: mkfs fs.img files...\n");
     exit(1);
   }
 
-  if((512 % sizeof(struct dinode)) != 0){
-    fprintf(stderr, "sizeof(dinode) must divide 512\n");
-    exit(1);
-  }
-  if((512 % sizeof(struct dirent)) != 0){
-    fprintf(stderr, "sizeof(dirent) must divide 512\n");
-    exit(1);
-  }
+  assert((512 % sizeof(struct dinode)) == 0);
+  assert((512 % sizeof(struct dirent)) == 0);
 
-  fd = open(argv[1], O_RDWR|O_CREAT|O_TRUNC, 0666);
-  if(fd < 0){
+  fsfd = open(argv[1], O_RDWR|O_CREAT|O_TRUNC, 0666);
+  if(fsfd < 0){
     perror(argv[1]);
     exit(1);
   }
@@ -79,20 +77,38 @@ main(int argc, char *argv[])
 
   wsect(1, &sb);
 
-  bzero(&din, sizeof(din));
-  din.type = xshort(T_DIR);
-  din.nlink = xshort(2);
-  din.size = xint(512);
-  bn = freeblock++;
-  din.addrs[0] = xint(bn);
-  winode(1, &din);
+  rootino = ialloc(T_DIR);
+  assert(rootino == 1);
 
-  bzero(dbuf, sizeof(dbuf));
-  ((struct dirent *) dbuf)[0].inum = xshort(1);
-  strcpy(((struct dirent *) dbuf)[0].name, ".");
-  ((struct dirent *) dbuf)[1].inum = xshort(1);
-  strcpy(((struct dirent *) dbuf)[1].name, "..");
-  wsect(bn, dbuf);
+  bzero(&de, sizeof(de));
+  de.inum = xshort(rootino);
+  strcpy(de.name, ".");
+  iappend(rootino, &de, sizeof(de));
+
+  bzero(&de, sizeof(de));
+  de.inum = xshort(rootino);
+  strcpy(de.name, "..");
+  iappend(rootino, &de, sizeof(de));
+
+  for(i = 2; i < argc; i++){
+    assert(index(argv[i], '/') == 0);
+    if((fd = open(argv[i], 0)) < 0){
+      perror(argv[i]);
+      exit(1);
+    }
+
+    inum = ialloc(T_FILE);
+
+    bzero(&de, sizeof(de));
+    de.inum = xshort(inum);
+    strncpy(de.name, argv[i], DIRSIZ);
+    iappend(rootino, &de, sizeof(de));
+    
+    while((cc = read(fd, buf, sizeof(buf))) > 0)
+      iappend(inum, buf, cc);
+
+    close(fd);
+  }
 
   exit(0);
 }
@@ -100,11 +116,11 @@ main(int argc, char *argv[])
 void
 wsect(uint sec, void *buf)
 {
-  if(lseek(fd, sec * 512L, 0) != sec * 512L){
+  if(lseek(fsfd, sec * 512L, 0) != sec * 512L){
     perror("lseek");
     exit(1);
   }
-  if(write(fd, buf, 512) != 512){
+  if(write(fsfd, buf, 512) != 512){
     perror("write");
     exit(1);
   }
@@ -127,20 +143,80 @@ winode(uint inum, struct dinode *ip)
   rsect(bn, buf);
   dip = ((struct dinode *) buf) + (inum % IPB);
   *dip = *ip;
-  printf("bn %d off %d\n",
-         bn, (unsigned)dip - (unsigned) buf);
   wsect(bn, buf);
+  printf("wi %d size %d addrs %d %d...\n",
+         inum,
+         xint(dip->size),
+         xint(dip->addrs[0]),
+         xint(dip->addrs[1]));
+}
+
+void
+rinode(uint inum, struct dinode *ip)
+{
+  char buf[512];
+  uint bn;
+  struct dinode *dip;
+
+  bn = i2b(inum);
+  rsect(bn, buf);
+  dip = ((struct dinode *) buf) + (inum % IPB);
+  *ip = *dip;
 }
 
 void
 rsect(uint sec, void *buf)
 {
-  if(lseek(fd, sec * 512L, 0) != sec * 512L){
+  if(lseek(fsfd, sec * 512L, 0) != sec * 512L){
     perror("lseek");
     exit(1);
   }
-  if(read(fd, buf, 512) != 512){
+  if(read(fsfd, buf, 512) != 512){
     perror("read");
     exit(1);
   }
 }
+
+uint
+ialloc(ushort type)
+{
+  uint inum = freeinode++;
+  struct dinode din;
+
+  bzero(&din, sizeof(din));
+  din.type = xshort(type);
+  din.nlink = xshort(1);
+  din.size = xint(0);
+  winode(inum, &din);
+  return inum;
+}
+
+#define min(a, b) ((a) < (b) ? (a) : (b))
+
+void
+iappend(uint inum, void *xp, int n)
+{
+  char *p = (char *) xp;
+  uint fbn, off, n1;
+  struct dinode din;
+  char buf[512];
+
+  rinode(inum, &din);
+
+  off = xint(din.size);
+  while(n > 0){
+    fbn = off / 512;
+    assert(fbn < NDIRECT);
+    if(din.addrs[fbn] == 0)
+      din.addrs[fbn] = xint(freeblock++);
+    n1 = min(n, (fbn + 1) * 512 - off);
+    rsect(xint(din.addrs[fbn]), buf);
+    bcopy(p, buf + off - (fbn * 512), n1);
+    wsect(xint(din.addrs[fbn]), buf);
+    n -= n1;
+    off += n1;
+    p += n1;
+  }
+  din.size = xint(off);
+  winode(inum, &din);
+}
diff --git a/proc.c b/proc.c
index 573da18..4e44a8e 100644
--- a/proc.c
+++ b/proc.c
@@ -137,6 +137,9 @@ scheduler(void)
   cprintf("start scheduler on cpu %d jmpbuf %p\n", cpu(), &cpus[cpu()].jmpbuf);
   cpus[cpu()].lastproc = &proc[0];
 
+  if(cpus[cpu()].nlock != 0)
+    panic("holding locks at first entry to scheduler");
+
   for(;;){
     // Loop over process table looking for process to run.
     acquire(&proc_table_lock);
diff --git a/syscall.c b/syscall.c
index c6ae0db..a855774 100644
--- a/syscall.c
+++ b/syscall.c
@@ -10,6 +10,7 @@
 #include "buf.h"
 #include "fs.h"
 #include "fsvar.h"
+#include "elf.h"
 
 /*
  * User code makes a system call with INT T_SYSCALL.
@@ -57,6 +58,21 @@ fetcharg(int argno, void *ip)
   return fetchint(curproc[cpu()], esp + 4 + 4*argno, ip);
 }
 
+// check that an entire string is valid in user space
+int
+checkstring(uint s)
+{
+  char c;
+
+  while(1){
+    if(fetchbyte(curproc[cpu()], s, &c) < 0)
+      return -1;
+    if(c == '\0')
+      return 0;
+    s++;
+  }
+}
+
 int
 putint(struct proc *p, uint addr, int x)
 {
@@ -224,6 +240,81 @@ sys_cons_puts(void)
   return 0;
 }
 
+int
+sys_exec(void)
+{
+  struct proc *cp = curproc[cpu()];
+  uint arg0, sz;
+  int i;
+  struct inode *ip;
+  struct elfhdr elf;
+  struct proghdr ph;
+
+  if(fetcharg(0, &arg0) < 0)
+    return -1;
+  if(checkstring(arg0) < 0)
+    return -1;
+  ip = namei(cp->mem + arg0);
+  if(ip == 0)
+    return -1;
+
+  if(readi(ip, &elf, 0, sizeof(elf)) < sizeof(elf))
+    goto bad;
+
+  if(elf.magic != ELF_MAGIC)
+    goto bad;
+
+  sz = 0;
+  for(i = 0; i < elf.phnum; i++){
+    if(readi(ip, &ph, elf.phoff + i * sizeof(ph), sizeof(ph)) != sizeof(ph))
+      goto bad;
+    if(ph.type != ELF_PROG_LOAD)
+      continue;
+    if(ph.memsz < ph.filesz)
+      goto bad;
+    sz += ph.memsz;
+  }
+  
+  sz += 4096 - (sz % 4096);
+  sz += 4096;
+
+  // commit to the new image.
+  kfree(cp->mem, cp->sz);
+  cp->sz = sz;
+  cp->mem = kalloc(cp->sz);
+
+  for(i = 0; i < elf.phnum; i++){
+    if(readi(ip, &ph, elf.phoff + i * sizeof(ph), sizeof(ph)) != sizeof(ph))
+      goto bad;
+    if(ph.type != ELF_PROG_LOAD)
+      continue;
+    if(ph.va + ph.memsz > sz)
+      goto bad2;
+    if(readi(ip, cp->mem + ph.va, ph.offset, ph.filesz) != ph.filesz)
+      goto bad2;
+    memset(cp->mem + ph.va + ph.filesz, 0, ph.memsz - ph.filesz);
+  }
+
+  iput(ip);
+
+  cp->tf->eip = elf.entry;
+  cp->tf->esp = cp->sz;
+  setupsegs(cp);
+
+  return 0;
+
+ bad:
+  cprintf("exec failed early\n");
+  iput(ip);
+  return -1;
+
+ bad2:
+  cprintf("exec failed late\n");
+  iput(ip);
+  proc_exit();
+  return 0;
+}
+
 int
 sys_block(void)
 {
@@ -248,14 +339,14 @@ sys_block(void)
           ip->type, ip->nlink, ip->size, ip->addrs[0]);
   iput(ip);
 
-  ip = namei(".././//./../");
+  ip = namei(".././//./../usertests");
   if(ip){
-    cprintf("namei: %d %d %d %d %d %d %d %d\n",
+    cprintf("namei(usertests): %d %d %d %d %d %d %d %d\n",
             ip->dev, ip->inum, ip->count, ip->busy,
             ip->type, ip->nlink, ip->size, ip->addrs[0]);
     iput(ip);
   } else {
-    cprintf("namei failed\n");
+    cprintf("namei(usertests) failed\n");
   }
 
   return 0;
@@ -317,6 +408,9 @@ syscall(void)
   case SYS_cons_puts:
     ret = sys_cons_puts();
     break;
+  case SYS_exec:
+    ret = sys_exec();
+    break;
   default:
     cprintf("unknown sys call %d\n", num);
     // XXX fault
diff --git a/syscall.h b/syscall.h
index 9ca3bd4..799d8ed 100644
--- a/syscall.h
+++ b/syscall.h
@@ -10,3 +10,4 @@
 #define SYS_kill 10
 #define SYS_panic 11
 #define SYS_cons_puts 12
+#define SYS_exec 13
diff --git a/userfs.c b/userfs.c
index 5772e1d..aad9326 100644
--- a/userfs.c
+++ b/userfs.c
@@ -9,5 +9,6 @@ main(void)
 {
   puts("userfs running\n");
   block();
+  exec("usertests");
   return 0;
 }
diff --git a/usys.S b/usys.S
index 45d6ca8..09c753e 100644
--- a/usys.S
+++ b/usys.S
@@ -20,3 +20,4 @@ STUB(block)
 STUB(kill)
 STUB(panic)
 STUB(cons_puts)
+STUB(exec)