Lab3 initial merge
authorBarret Rhoden <brho@cs.berkeley.edu>
Fri, 6 Feb 2009 01:10:30 +0000 (17:10 -0800)
committerBarret Rhoden <brho@cs.berkeley.edu>
Fri, 6 Feb 2009 01:10:30 +0000 (17:10 -0800)
38 files changed:
GNUmakefile
conf/lab.mk
inc/lib.h [new file with mode: 0644]
inc/syscall.h [new file with mode: 0644]
kern/Makefrag
kern/entry.S
kern/init.c
kern/kdebug.c
kern/monitor.c
kern/monitor.h
kern/pmap.c
kern/pmap.h
kern/sched.h [new file with mode: 0644]
kern/syscall.c [new file with mode: 0644]
kern/syscall.h [new file with mode: 0644]
kern/trap.c [new file with mode: 0644]
kern/trapentry.S [new file with mode: 0644]
lib/Makefrag [new file with mode: 0644]
lib/console.c [new file with mode: 0644]
lib/entry.S [new file with mode: 0644]
lib/exit.c [new file with mode: 0644]
lib/libmain.c [new file with mode: 0644]
lib/panic.c [new file with mode: 0644]
lib/printf.c [new file with mode: 0644]
lib/syscall.c [new file with mode: 0644]
user/Makefrag [new file with mode: 0644]
user/badsegment.c [new file with mode: 0644]
user/breakpoint.c [new file with mode: 0644]
user/buggyhello.c [new file with mode: 0644]
user/divzero.c [new file with mode: 0644]
user/evilhello.c [new file with mode: 0644]
user/faultread.c [new file with mode: 0644]
user/faultreadkernel.c [new file with mode: 0644]
user/faultwrite.c [new file with mode: 0644]
user/faultwritekernel.c [new file with mode: 0644]
user/hello.c [new file with mode: 0644]
user/softint.c [new file with mode: 0644]
user/testbss.c [new file with mode: 0644]

index 2227725..39a2780 100644 (file)
@@ -100,6 +100,8 @@ USER_CFLAGS := $(CFLAGS) -DJOS_USER -gstabs
 # Include Makefrags for subdirectories
 include boot/Makefrag
 include kern/Makefrag
+include lib/Makefrag
+include user/Makefrag
 
 
 IMAGES = $(OBJDIR)/kern/bochs.img
index ab474ab..ce926b6 100644 (file)
@@ -1,2 +1,2 @@
-LAB=1
-PACKAGEDATE=Sat Jan 13 22:14:53 CST 2007
+LAB=3
+PACKAGEDATE=Sun Jan 14 15:36:47 CST 2007
diff --git a/inc/lib.h b/inc/lib.h
new file mode 100644 (file)
index 0000000..6f21a01
--- /dev/null
+++ b/inc/lib.h
@@ -0,0 +1,51 @@
+// Main public header file for our user-land support library,
+// whose code lives in the lib directory.
+// This library is roughly our OS's version of a standard C library,
+// and is intended to be linked into all user-mode applications
+// (NOT the kernel or boot loader).
+
+#ifndef JOS_INC_LIB_H
+#define JOS_INC_LIB_H 1
+
+#include <inc/types.h>
+#include <inc/stdio.h>
+#include <inc/stdarg.h>
+#include <inc/string.h>
+#include <inc/error.h>
+#include <inc/assert.h>
+#include <inc/env.h>
+#include <inc/memlayout.h>
+#include <inc/syscall.h>
+
+#define USED(x)                (void)(x)
+
+// libos.c or entry.S
+extern char *binaryname;
+extern volatile struct Env *env;
+extern volatile struct Env envs[NENV];
+extern volatile struct Page pages[];
+void   exit(void);
+
+// readline.c
+char*  readline(const char *buf);
+
+// syscall.c
+void   sys_cputs(const char *string, size_t len);
+int    sys_cgetc(void);
+envid_t        sys_getenvid(void);
+int    sys_env_destroy(envid_t);
+
+
+
+/* File open modes */
+#define        O_RDONLY        0x0000          /* open for reading only */
+#define        O_WRONLY        0x0001          /* open for writing only */
+#define        O_RDWR          0x0002          /* open for reading and writing */
+#define        O_ACCMODE       0x0003          /* mask for above modes */
+
+#define        O_CREAT         0x0100          /* create if nonexistent */
+#define        O_TRUNC         0x0200          /* truncate to zero length */
+#define        O_EXCL          0x0400          /* error if already exists */
+#define O_MKDIR                0x0800          /* create directory, not regular file */
+
+#endif // !JOS_INC_LIB_H
diff --git a/inc/syscall.h b/inc/syscall.h
new file mode 100644 (file)
index 0000000..b5f92e5
--- /dev/null
@@ -0,0 +1,14 @@
+#ifndef JOS_INC_SYSCALL_H
+#define JOS_INC_SYSCALL_H
+
+/* system call numbers */
+enum
+{
+       SYS_cputs = 0,
+       SYS_cgetc,
+       SYS_getenvid,
+       SYS_env_destroy,
+       NSYSCALLS
+};
+
+#endif /* !JOS_INC_SYSCALL_H */
index b3287ef..1db0863 100644 (file)
@@ -36,7 +36,18 @@ KERN_SRCFILES :=     kern/entry.S \
 # Only build files if they exist.
 KERN_SRCFILES := $(wildcard $(KERN_SRCFILES))
 
-KERN_BINFILES := 
+KERN_BINFILES :=       user/hello \
+                       user/buggyhello \
+                       user/evilhello \
+                       user/testbss \
+                       user/divzero \
+                       user/breakpoint \
+                       user/softint \
+                       user/badsegment \
+                       user/faultread \
+                       user/faultreadkernel \
+                       user/faultwrite \
+                       user/faultwritekernel
 
 KERN_OBJFILES := $(patsubst %.c, $(OBJDIR)/%.o, $(KERN_SRCFILES))
 KERN_OBJFILES := $(patsubst %.S, $(OBJDIR)/%.o, $(KERN_OBJFILES))
index 54da2f6..ef4063a 100644 (file)
@@ -2,6 +2,7 @@
 
 #include <inc/mmu.h>
 #include <inc/memlayout.h>
+#include <inc/trap.h>
 
 # Shift Right Logical 
 #define SRL(val, shamt)                (((val) >> (shamt)) & ~(-1 << (32 - (shamt))))
@@ -65,7 +66,8 @@ relocated:
        movl    $0x0,%ebp                       # nuke frame pointer
 
     # Set the stack pointer
-       movl    $(bootstacktop),%esp
+    # Leave a few words on the stack for the user trap frame
+       movl    $(bootstacktop-SIZEOF_STRUCT_TRAPFRAME),%esp
 
        # Save multiboot info
        push    %ebx
index 86c25bd..2b27507 100644 (file)
@@ -10,6 +10,8 @@
 #include <kern/console.h>
 #include <kern/pmap.h>
 #include <kern/kclock.h>
+#include <kern/env.h>
+#include <kern/trap.h>
 
 void kernel_init(multiboot_info_t *mboot_info)
 {
@@ -30,9 +32,22 @@ void kernel_init(multiboot_info_t *mboot_info)
        page_init();
        page_check();
 
-       // Drop into the kernel monitor.
-       while (1)
-               monitor(NULL);
+       // Lab 3 user environment initialization functions
+       env_init();
+       idt_init();
+
+       // Temporary test code specific to LAB 3
+#if defined(TEST)
+       // Don't touch -- used by grading script!
+       ENV_CREATE2(TEST, TESTSIZE);
+#else
+       // Touch all you want.
+       ENV_CREATE(user_hello);
+#endif // TEST*
+
+
+       // We only have one user environment for now, so just run it.
+       env_run(&envs[0]);
 }
 
 
index 36d62d2..cf08d89 100644 (file)
@@ -12,6 +12,13 @@ extern const struct Stab __STAB_END__[];     // End of stabs table
 extern const char __STABSTR_BEGIN__[];         // Beginning of string table
 extern const char __STABSTR_END__[];           // End of string table
 
+struct UserStabData {
+       const struct Stab *stabs;
+       const struct Stab *stab_end;
+       const char *stabstr;
+       const char *stabstr_end;
+};
+
 
 // stab_binsearch(stabs, region_left, region_right, type, addr)
 //
@@ -125,8 +132,24 @@ debuginfo_eip(uintptr_t addr, struct Eipdebuginfo *info)
                stabstr = __STABSTR_BEGIN__;
                stabstr_end = __STABSTR_END__;
        } else {
-               // Can't search for user-level addresses yet!
-               panic("User address");
+               // The user-application linker script, user/user.ld,
+               // puts information about the application's stabs (equivalent
+               // to __STAB_BEGIN__, __STAB_END__, __STABSTR_BEGIN__, and
+               // __STABSTR_END__) in a structure located at virtual address
+               // USTABDATA.
+               const struct UserStabData *usd = (const struct UserStabData *) USTABDATA;
+
+               // Make sure this memory is valid.
+               // Return -1 if it is not.  Hint: Call user_mem_check.
+               // LAB 3: Your code here.
+               
+               stabs = usd->stabs;
+               stab_end = usd->stab_end;
+               stabstr = usd->stabstr;
+               stabstr_end = usd->stabstr_end;
+
+               // Make sure the STABS and string table memory is valid.
+               // LAB 3: Your code here.
        }
 
        // String table validity checks
index fae3991..1e29623 100644 (file)
@@ -10,6 +10,7 @@
 
 #include <kern/console.h>
 #include <kern/monitor.h>
+#include <kern/trap.h>
 #include <kern/kdebug.h>
 #include <kern/pmap.h>
 
@@ -19,7 +20,7 @@ typedef struct command {
        const char *NTS name;
        const char *NTS desc;
        // return -1 to force monitor to exit
-       int (*func)(int argc, char *NTS *NT COUNT(argc) argv, trapframe_t* tf);
+       int (*func)(int argc, char *NTS *NT COUNT(argc) argv, struct Trapframe* tf);
 } command_t;
 
 static command_t commands[] = {
@@ -34,7 +35,7 @@ static command_t commands[] = {
 
 /***** Implementations of basic kernel monitor commands *****/
 
-int mon_help(int argc, char **argv, trapframe_t *tf)
+int mon_help(int argc, char **argv, struct Trapframe *tf)
 {
        int i;
 
@@ -43,7 +44,7 @@ int mon_help(int argc, char **argv, trapframe_t *tf)
        return 0;
 }
 
-int mon_kerninfo(int argc, char **argv, trapframe_t *tf)
+int mon_kerninfo(int argc, char **argv, struct Trapframe *tf)
 {
        extern char (SNT _start)[], (SNT etext)[], (SNT edata)[], (SNT end)[];
 
@@ -81,7 +82,7 @@ static char* function_of(uint32_t address)
        return stabstr + best_symtab->n_strx;
 }
 
-int mon_backtrace(int argc, char **argv, trapframe_t *tf)
+int mon_backtrace(int argc, char **argv, struct Trapframe *tf)
 {
        uint32_t* ebp, eip;
        struct Eipdebuginfo debuginfo;
@@ -112,7 +113,7 @@ int mon_backtrace(int argc, char **argv, trapframe_t *tf)
        return 0;
 }
 
-int mon_reboot(int argc, char **argv, trapframe_t *tf)
+int mon_reboot(int argc, char **argv, struct Trapframe *tf)
 {
        cprintf("[Irish Accent]: She's goin' down, Cap'n!\n");
        outb(0x92, 0x3);
@@ -120,7 +121,7 @@ int mon_reboot(int argc, char **argv, trapframe_t *tf)
        return 0;
 }
 
-int mon_showmapping(int argc, char **argv, trapframe_t *tf)
+int mon_showmapping(int argc, char **argv, struct Trapframe *tf)
 {
        if (argc < 2) {
                cprintf("Shows virtual -> physical mappings for a virtual address range.\n");
@@ -154,7 +155,7 @@ int mon_showmapping(int argc, char **argv, trapframe_t *tf)
        return 0;
 }
 
-int mon_setmapperm(int argc, char **argv, trapframe_t *tf)
+int mon_setmapperm(int argc, char **argv, struct Trapframe *tf)
 {
        if (argc < 2) {
                cprintf("Sets VIRT_ADDR's mapping's permissions to PERMS (in hex)\n");
@@ -192,7 +193,7 @@ int mon_setmapperm(int argc, char **argv, trapframe_t *tf)
 #define WHITESPACE "\t\r\n "
 #define MAXARGS 16
 
-static int runcmd(char *COUNT(CMDBUF_SIZE) real_buf, trapframe_t* tf) {
+static int runcmd(char *COUNT(CMDBUF_SIZE) real_buf, struct Trapframe* tf) {
        char *BND(real_buf, real_buf+CMDBUF_SIZE) buf = real_buf;
        int argc;
        char *NTS argv[MAXARGS];
@@ -231,12 +232,15 @@ static int runcmd(char *COUNT(CMDBUF_SIZE) real_buf, trapframe_t* tf) {
        return 0;
 }
 
-void monitor(trapframe_t* tf) {
+void monitor(struct Trapframe* tf) {
        char *buf;
 
        cprintf("Welcome to the ROS kernel monitor!\n");
        cprintf("Type 'help' for a list of commands.\n");
 
+       if (tf != NULL)
+               print_trapframe(tf);
+
        while (1) {
                buf = readline("K> ");
                if (buf != NULL)
index bfb3a47..8231b4c 100644 (file)
@@ -4,20 +4,17 @@
 # error "This is a JOS kernel header; user programs should not #include it"
 #endif
 
-typedef struct trapframe {
-} trapframe_t;
-
 // Activate the kernel monitor,
 // optionally providing a trap frame indicating the current state
 // (NULL if none).
-void monitor(trapframe_t *tf);
+void monitor(struct Trapframe *tf);
 
 // Functions implementing monitor commands.
-int mon_help(int argc, char *NTS *NT COUNT(argc) argv, trapframe_t *tf);
-int mon_kerninfo(int argc, char *NTS *NT COUNT(argc) argv, trapframe_t *tf);
-int mon_backtrace(int argc, char *NTS *NT COUNT(argc) argv, trapframe_t *tf);
-int mon_reboot(int argc, char *NTS *NT COUNT(argc) argv, trapframe_t *tf);
-int mon_showmapping(int argc, char *NTS *NT COUNT(argc) argv, trapframe_t *tf);
-int mon_setmapperm(int argc, char *NTS *NT COUNT(argc) argv, trapframe_t *tf);
+int mon_help(int argc, char *NTS *NT COUNT(argc) argv, struct Trapframe *tf);
+int mon_kerninfo(int argc, char *NTS *NT COUNT(argc) argv, struct Trapframe *tf);
+int mon_backtrace(int argc, char *NTS *NT COUNT(argc) argv, struct Trapframe *tf);
+int mon_reboot(int argc, char *NTS *NT COUNT(argc) argv, struct Trapframe *tf);
+int mon_showmapping(int argc, char *NTS *NT COUNT(argc) argv, struct Trapframe *tf);
+int mon_setmapperm(int argc, char *NTS *NT COUNT(argc) argv, struct Trapframe *tf);
 
 #endif // !JOS_KERN_MONITOR_H
index 579bf0a..3855853 100644 (file)
@@ -8,6 +8,7 @@
 
 #include <kern/pmap.h>
 #include <kern/kclock.h>
+#include <kern/env.h>
 
 // These variables are set by i386_detect_memory()
 static physaddr_t maxpa;       // Maximum physical address
@@ -345,6 +346,16 @@ i386_vm_init(void)
        }
        boot_map_segment(pgdir, UPAGES, page_array_size, PADDR(pages), PTE_U);
 
+       //////////////////////////////////////////////////////////////////////
+       // Make 'envs' point to an array of size 'NENV' of 'struct Env'.
+       // Map this array read-only by the user at linear address UENVS
+       // (ie. perm = PTE_U | PTE_P).
+       // Permissions:
+       //    - envs itself -- kernel RW, user NONE
+       //    - the image of envs mapped at UENVS  -- kernel R, user R
+       
+       // LAB 3: Your code here.
+
        // Check that the initial page directory has been set up correctly.
        check_boot_pgdir(pse);
 
@@ -430,6 +441,13 @@ check_boot_pgdir(bool pse)
        for (i = 0; i < n; i += PGSIZE)
                assert(check_va2pa(pgdir, UPAGES + i) == PADDR(pages) + i);
 
+       /* // TODO - turn this on
+       // check envs array (new test for lab 3)
+       n = ROUNDUP(NENV*sizeof(struct Env), PGSIZE);
+       for (i = 0; i < n; i += PGSIZE)
+               assert(check_va2pa(pgdir, UENVS + i) == PADDR(envs) + i);
+       */
+
        // check phys mem
        //for (i = 0; KERNBASE + i != 0; i += PGSIZE)
        // adjusted check to account for only mapping avail mem
@@ -452,6 +470,7 @@ check_boot_pgdir(bool pse)
                case PDX(UVPT):
                case PDX(KSTACKTOP-1):
                case PDX(UPAGES):
+               //case PDX(UENVS): // TODO - turn this on
                        assert(pgdir[i]);
                        break;
                default:
@@ -740,6 +759,52 @@ tlb_invalidate(pde_t *pgdir, void *va)
        invlpg(va);
 }
 
+static uintptr_t user_mem_check_addr;
+
+//
+// Check that an environment is allowed to access the range of memory
+// [va, va+len) with permissions 'perm | PTE_P'.
+// Normally 'perm' will contain PTE_U at least, but this is not required.
+// 'va' and 'len' need not be page-aligned; you must test every page that
+// contains any of that range.  You will test either 'len/PGSIZE',
+// 'len/PGSIZE + 1', or 'len/PGSIZE + 2' pages.
+//
+// A user program can access a virtual address if (1) the address is below
+// ULIM, and (2) the page table gives it permission.  These are exactly
+// the tests you should implement here.
+//
+// If there is an error, set the 'user_mem_check_addr' variable to the first
+// erroneous virtual address.
+//
+// Returns 0 if the user program can access this range of addresses,
+// and -E_FAULT otherwise.
+//
+// Hint: The TA solution uses pgdir_walk.
+//
+int
+user_mem_check(struct Env *env, const void *va, size_t len, int perm)
+{
+       // LAB 3: Your code here. 
+
+       return 0;
+}
+
+//
+// Checks that environment 'env' is allowed to access the range
+// of memory [va, va+len) with permissions 'perm | PTE_U'.
+// If it can, then the function simply returns.
+// If it cannot, 'env' is destroyed.
+//
+void
+user_mem_assert(struct Env *env, const void *va, size_t len, int perm)
+{
+       if (user_mem_check(env, va, len, perm | PTE_U) < 0) {
+               cprintf("[%08x] user_mem_check assertion failure for "
+                       "va %08x\n", curenv->env_id, user_mem_check_addr);
+               env_destroy(env);       // may not return
+       }
+}
+
 void
 page_check(void)
 {
@@ -764,6 +829,9 @@ page_check(void)
        // should be no free memory
        assert(page_alloc(&pp) == -E_NO_MEM);
 
+       // Fill pp1 with bogus data and check for invalid tlb entries
+       memset(page2kva(pp1), 0xFFFFFFFF, PGSIZE);
+
        // there is no page allocated at address 0
        assert(page_lookup(boot_pgdir, (void *) 0x0, &ptep) == NULL);
 
@@ -773,6 +841,12 @@ page_check(void)
        // free pp0 and try again: pp0 should be used for page table
        page_free(pp0);
        assert(page_insert(boot_pgdir, pp1, 0x0, 0) == 0);
+       tlb_invalidate(boot_pgdir, 0x0);
+       // DEP Should have shot down invalid TLB entry - let's check
+       {
+         int *x = 0x0;
+         assert(*x == 0xFFFFFFFF);
+       }
        assert(PTE_ADDR(boot_pgdir[0]) == page2pa(pp0));
        assert(check_va2pa(boot_pgdir, 0x0) == page2pa(pp1));
        assert(pp1->pp_ref == 1);
@@ -783,14 +857,27 @@ page_check(void)
        assert(check_va2pa(boot_pgdir, PGSIZE) == page2pa(pp2));
        assert(pp2->pp_ref == 1);
 
+       // Make sure that pgdir_walk returns a pointer to the pte and
+       // not the table or some other garbage
+       {
+         pte_t *p = KADDR(PTE_ADDR(boot_pgdir[PDX(PGSIZE)]));
+         assert(pgdir_walk(boot_pgdir, (void *)PGSIZE, 0) == &p[PTX(PGSIZE)]);
+       }
+
        // should be no free memory
        assert(page_alloc(&pp) == -E_NO_MEM);
 
        // should be able to map pp2 at PGSIZE because it's already there
-       assert(page_insert(boot_pgdir, pp2, (void*) PGSIZE, 0) == 0);
+       assert(page_insert(boot_pgdir, pp2, (void*) PGSIZE, PTE_U) == 0);
        assert(check_va2pa(boot_pgdir, PGSIZE) == page2pa(pp2));
        assert(pp2->pp_ref == 1);
 
+       // Make sure that we actually changed the permission on pp2 when we re-mapped it
+       {
+         pte_t *p = pgdir_walk(boot_pgdir, (void*)PGSIZE, 0);
+         assert(((*p) & PTE_U) == PTE_U);
+       }
+
        // pp2 should NOT be on the free list
        // could happen in ref counts are handled sloppily in page_insert
        assert(page_alloc(&pp) == -E_NO_MEM);
@@ -837,6 +924,21 @@ page_check(void)
        assert(pp0->pp_ref == 1);
        pp0->pp_ref = 0;
 
+       // Catch invalid pointer addition in pgdir_walk - i.e. pgdir + PDX(va)
+       {
+         // Give back pp0 for a bit
+         page_free(pp0);
+
+         void * va = (void *)((PGSIZE * NPDENTRIES) + PGSIZE);
+         pte_t *p2 = pgdir_walk(boot_pgdir, va, 1);
+         pte_t *p = KADDR(PTE_ADDR(boot_pgdir[PDX(va)]));
+         assert(p2 == &p[PTX(va)]);
+
+         // Clean up again
+         boot_pgdir[PDX(va)] = 0;
+         pp0->pp_ref = 0;
+       }
+
        // give free list back
        page_free_list = fl;
 
index 734dd97..0b22383 100644 (file)
@@ -8,6 +8,7 @@
 
 #include <inc/memlayout.h>
 #include <inc/assert.h>
+struct Env;
 
 
 /* This macro takes a kernel virtual address -- an address that points above
@@ -61,6 +62,9 @@ void  page_decref(struct Page *pp);
 
 void   tlb_invalidate(pde_t *pgdir, void *va);
 
+int    user_mem_check(struct Env *env, const void *va, size_t len, int perm);
+void   user_mem_assert(struct Env *env, const void *va, size_t len, int perm);
+
 static inline ppn_t
 page2ppn(struct Page *pp)
 {
diff --git a/kern/sched.h b/kern/sched.h
new file mode 100644 (file)
index 0000000..754f6a0
--- /dev/null
@@ -0,0 +1,12 @@
+/* See COPYRIGHT for copyright information. */
+
+#ifndef JOS_KERN_SCHED_H
+#define JOS_KERN_SCHED_H
+#ifndef JOS_KERNEL
+# error "This is a JOS kernel header; user programs should not #include it"
+#endif
+
+// This function does not return.
+void sched_yield(void) __attribute__((noreturn));
+
+#endif // !JOS_KERN_SCHED_H
diff --git a/kern/syscall.c b/kern/syscall.c
new file mode 100644 (file)
index 0000000..63a47ce
--- /dev/null
@@ -0,0 +1,84 @@
+/* See COPYRIGHT for copyright information. */
+
+#include <inc/x86.h>
+#include <inc/error.h>
+#include <inc/string.h>
+#include <inc/assert.h>
+
+#include <kern/env.h>
+#include <kern/pmap.h>
+#include <kern/trap.h>
+#include <kern/syscall.h>
+#include <kern/console.h>
+
+// Print a string to the system console.
+// The string is exactly 'len' characters long.
+// Destroys the environment on memory errors.
+static void
+sys_cputs(const char *s, size_t len)
+{
+       // Check that the user has permission to read memory [s, s+len).
+       // Destroy the environment if not.
+       
+       // LAB 3: Your code here.
+
+       // Print the string supplied by the user.
+       cprintf("%.*s", len, s);
+}
+
+// Read a character from the system console.
+// Returns the character.
+static int
+sys_cgetc(void)
+{
+       int c;
+
+       // The cons_getc() primitive doesn't wait for a character,
+       // but the sys_cgetc() system call does.
+       while ((c = cons_getc()) == 0)
+               /* do nothing */;
+
+       return c;
+}
+
+// Returns the current environment's envid.
+static envid_t
+sys_getenvid(void)
+{
+       return curenv->env_id;
+}
+
+// Destroy a given environment (possibly the currently running environment).
+//
+// Returns 0 on success, < 0 on error.  Errors are:
+//     -E_BAD_ENV if environment envid doesn't currently exist,
+//             or the caller doesn't have permission to change envid.
+static int
+sys_env_destroy(envid_t envid)
+{
+       int r;
+       struct Env *e;
+
+       if ((r = envid2env(envid, &e, 1)) < 0)
+               return r;
+       if (e == curenv)
+               cprintf("[%08x] exiting gracefully\n", curenv->env_id);
+       else
+               cprintf("[%08x] destroying %08x\n", curenv->env_id, e->env_id);
+       env_destroy(e);
+       return 0;
+}
+
+
+
+// Dispatches to the correct kernel function, passing the arguments.
+uint32_t
+syscall(uint32_t syscallno, uint32_t a1, uint32_t a2, uint32_t a3, uint32_t a4, uint32_t a5)
+{
+       // Call the function corresponding to the 'syscallno' parameter.
+       // Return any appropriate return value.
+       // LAB 3: Your code here.
+
+       panic("syscall not implemented");
+}
+
diff --git a/kern/syscall.h b/kern/syscall.h
new file mode 100644 (file)
index 0000000..cef046c
--- /dev/null
@@ -0,0 +1,11 @@
+#ifndef JOS_KERN_SYSCALL_H
+#define JOS_KERN_SYSCALL_H
+#ifndef JOS_KERNEL
+# error "This is a JOS kernel header; user programs should not #include it"
+#endif
+
+#include <inc/syscall.h>
+
+uint32_t syscall(uint32_t num, uint32_t a1, uint32_t a2, uint32_t a3, uint32_t a4, uint32_t a5);
+
+#endif /* !JOS_KERN_SYSCALL_H */
diff --git a/kern/trap.c b/kern/trap.c
new file mode 100644 (file)
index 0000000..efe3a5b
--- /dev/null
@@ -0,0 +1,199 @@
+#include <inc/mmu.h>
+#include <inc/x86.h>
+#include <inc/assert.h>
+
+#include <kern/pmap.h>
+#include <kern/trap.h>
+#include <kern/console.h>
+#include <kern/monitor.h>
+#include <kern/env.h>
+#include <kern/syscall.h>
+
+static struct Taskstate ts;
+
+/* Interrupt descriptor table.  (Must be built at run time because
+ * shifted function addresses can't be represented in relocation records.)
+ */
+struct Gatedesc idt[256] = { { 0 } };
+struct Pseudodesc idt_pd = {
+       sizeof(idt) - 1, (uint32_t) idt
+};
+
+
+static const char *trapname(int trapno)
+{
+       static const char * const excnames[] = {
+               "Divide error",
+               "Debug",
+               "Non-Maskable Interrupt",
+               "Breakpoint",
+               "Overflow",
+               "BOUND Range Exceeded",
+               "Invalid Opcode",
+               "Device Not Available",
+               "Double Falt",
+               "Coprocessor Segment Overrun",
+               "Invalid TSS",
+               "Segment Not Present",
+               "Stack Fault",
+               "General Protection",
+               "Page Fault",
+               "(unknown trap)",
+               "x87 FPU Floating-Point Error",
+               "Alignment Check",
+               "Machine-Check",
+               "SIMD Floating-Point Exception"
+       };
+
+       if (trapno < sizeof(excnames)/sizeof(excnames[0]))
+               return excnames[trapno];
+       if (trapno == T_SYSCALL)
+               return "System call";
+       return "(unknown trap)";
+}
+
+
+void
+idt_init(void)
+{
+       extern struct Segdesc gdt[];
+       
+       // LAB 3: Your code here.
+
+       // Setup a TSS so that we get the right stack
+       // when we trap to the kernel.
+       ts.ts_esp0 = KSTACKTOP;
+       ts.ts_ss0 = GD_KD;
+
+       // Initialize the TSS field of the gdt.
+       gdt[GD_TSS >> 3] = SEG16(STS_T32A, (uint32_t) (&ts),
+                                       sizeof(struct Taskstate), 0);
+       gdt[GD_TSS >> 3].sd_s = 0;
+
+       // Load the TSS
+       ltr(GD_TSS);
+
+       // Load the IDT
+       asm volatile("lidt idt_pd");
+}
+
+void
+print_trapframe(struct Trapframe *tf)
+{
+       cprintf("TRAP frame at %p\n", tf);
+       print_regs(&tf->tf_regs);
+       cprintf("  es   0x----%04x\n", tf->tf_es);
+       cprintf("  ds   0x----%04x\n", tf->tf_ds);
+       cprintf("  trap 0x%08x %s\n", tf->tf_trapno, trapname(tf->tf_trapno));
+       cprintf("  err  0x%08x\n", tf->tf_err);
+       cprintf("  eip  0x%08x\n", tf->tf_eip);
+       cprintf("  cs   0x----%04x\n", tf->tf_cs);
+       cprintf("  flag 0x%08x\n", tf->tf_eflags);
+       cprintf("  esp  0x%08x\n", tf->tf_esp);
+       cprintf("  ss   0x----%04x\n", tf->tf_ss);
+}
+
+void
+print_regs(struct PushRegs *regs)
+{
+       cprintf("  edi  0x%08x\n", regs->reg_edi);
+       cprintf("  esi  0x%08x\n", regs->reg_esi);
+       cprintf("  ebp  0x%08x\n", regs->reg_ebp);
+       cprintf("  oesp 0x%08x\n", regs->reg_oesp);
+       cprintf("  ebx  0x%08x\n", regs->reg_ebx);
+       cprintf("  edx  0x%08x\n", regs->reg_edx);
+       cprintf("  ecx  0x%08x\n", regs->reg_ecx);
+       cprintf("  eax  0x%08x\n", regs->reg_eax);
+}
+
+static void
+trap_dispatch(struct Trapframe *tf)
+{
+       // Handle processor exceptions.
+       // LAB 3: Your code here.
+       
+
+       // Unexpected trap: The user process or the kernel has a bug.
+       print_trapframe(tf);
+       if (tf->tf_cs == GD_KT)
+               panic("unhandled trap in kernel");
+       else {
+               env_destroy(curenv);
+               return;
+       }
+}
+
+void
+trap(struct Trapframe *tf)
+{
+       cprintf("Incoming TRAP frame at %p\n", tf);
+
+       if ((tf->tf_cs & 3) == 3) {
+               // Trapped from user mode.
+               // Copy trap frame (which is currently on the stack)
+               // into 'curenv->env_tf', so that running the environment
+               // will restart at the trap point.
+               assert(curenv);
+               curenv->env_tf = *tf;
+               // The trapframe on the stack should be ignored from here on.
+               tf = &curenv->env_tf;
+       }
+       
+       // Dispatch based on what type of trap occurred
+       trap_dispatch(tf);
+
+        // Return to the current environment, which should be runnable.
+        assert(curenv && curenv->env_status == ENV_RUNNABLE);
+        env_run(curenv);
+}
+
+
+void
+page_fault_handler(struct Trapframe *tf)
+{
+       uint32_t fault_va;
+
+       // Read processor's CR2 register to find the faulting address
+       fault_va = rcr2();
+
+       // Handle kernel-mode page faults.
+       
+       // LAB 3: Your code here.
+
+       // We've already handled kernel-mode exceptions, so if we get here,
+       // the page fault happened in user mode.
+
+       // Call the environment's page fault upcall, if one exists.  Set up a
+       // page fault stack frame on the user exception stack (below
+       // UXSTACKTOP), then branch to curenv->env_pgfault_upcall.
+       //
+       // The page fault upcall might cause another page fault, in which case
+       // we branch to the page fault upcall recursively, pushing another
+       // page fault stack frame on top of the user exception stack.
+       //
+       // The trap handler needs one word of scratch space at the top of the
+       // trap-time stack in order to return.  In the non-recursive case, we
+       // don't have to worry about this because the top of the regular user
+       // stack is free.  In the recursive case, this means we have to leave
+       // an extra word between the current top of the exception stack and
+       // the new stack frame because the exception stack _is_ the trap-time
+       // stack.
+       //
+       // If there's no page fault upcall, the environment didn't allocate a
+       // page for its exception stack, or the exception stack overflows,
+       // then destroy the environment that caused the fault.
+       //
+       // Hints:
+       //   user_mem_assert() and env_run() are useful here.
+       //   To change what the user environment runs, modify 'curenv->env_tf'
+       //   (the 'tf' variable points at 'curenv->env_tf').
+       
+       // LAB 4: Your code here.
+
+       // Destroy the environment that caused the fault.
+       cprintf("[%08x] user fault va %08x ip %08x\n",
+               curenv->env_id, fault_va, tf->tf_eip);
+       print_trapframe(tf);
+       env_destroy(curenv);
+}
+
diff --git a/kern/trapentry.S b/kern/trapentry.S
new file mode 100644 (file)
index 0000000..bab74b7
--- /dev/null
@@ -0,0 +1,49 @@
+/* See COPYRIGHT for copyright information. */
+
+#include <inc/mmu.h>
+#include <inc/memlayout.h>
+#include <inc/trap.h>
+
+
+
+###################################################################
+# exceptions/interrupts
+###################################################################
+
+/* The TRAPHANDLER macro defines a globally-visible function for handling
+ * a trap.  It pushes a trap number onto the stack, then jumps to _alltraps.
+ * Use TRAPHANDLER for traps where the CPU automatically pushes an error code.
+ */ 
+#define TRAPHANDLER(name, num)                                         \
+       .globl name;            /* define global symbol for 'name' */   \
+       .type name, @function;  /* symbol type is function */           \
+       .align 2;               /* align function definition */         \
+       name:                   /* function starts here */              \
+       pushl $(num);                                                   \
+       jmp _alltraps
+
+/* Use TRAPHANDLER_NOEC for traps where the CPU doesn't push an error code.
+ * It pushes a 0 in place of the error code, so the trap frame has the same
+ * format in either case.
+ */
+#define TRAPHANDLER_NOEC(name, num)                                    \
+       .globl name;                                                    \
+       .type name, @function;                                          \
+       .align 2;                                                       \
+       name:                                                           \
+       pushl $0;                                                       \
+       pushl $(num);                                                   \
+       jmp _alltraps
+
+.text
+
+/*
+ * Lab 3: Your code here for generating entry points for the different traps.
+ */
+
+       
+
+/*
+ * Lab 3: Your code here for _alltraps
+ */
+       
diff --git a/lib/Makefrag b/lib/Makefrag
new file mode 100644 (file)
index 0000000..60bf6f6
--- /dev/null
@@ -0,0 +1,31 @@
+OBJDIRS += lib
+
+LIB_SRCFILES :=                lib/console.c \
+                       lib/libmain.c \
+                       lib/exit.c \
+                       lib/panic.c \
+                       lib/printf.c \
+                       lib/printfmt.c \
+                       lib/readline.c \
+                       lib/string.c \
+                       lib/syscall.c
+
+
+
+
+LIB_OBJFILES := $(patsubst lib/%.c, $(OBJDIR)/lib/%.o, $(LIB_SRCFILES))
+LIB_OBJFILES := $(patsubst lib/%.S, $(OBJDIR)/lib/%.o, $(LIB_OBJFILES))
+
+$(OBJDIR)/lib/%.o: lib/%.c
+       @echo + cc[USER] $<
+       @mkdir -p $(@D)
+       $(V)$(CC) -nostdinc $(USER_CFLAGS) -c -o $@ $<
+
+$(OBJDIR)/lib/%.o: lib/%.S
+       @echo + as[USER] $<
+       @mkdir -p $(@D)
+       $(V)$(CC) -nostdinc $(USER_CFLAGS) -c -o $@ $<
+
+$(OBJDIR)/lib/libjos.a: $(LIB_OBJFILES)
+       @echo + ar $@
+       $(V)$(AR) r $@ $(LIB_OBJFILES)
diff --git a/lib/console.c b/lib/console.c
new file mode 100644 (file)
index 0000000..ac7d09c
--- /dev/null
@@ -0,0 +1,21 @@
+
+#include <inc/string.h>
+#include <inc/lib.h>
+
+void
+cputchar(int ch)
+{
+       char c = ch;
+
+       // Unlike standard Unix's putchar,
+       // the cputchar function _always_ outputs to the system console.
+       sys_cputs(&c, 1);
+}
+
+int
+getchar(void)
+{
+       return sys_cgetc();
+}
+
+
diff --git a/lib/entry.S b/lib/entry.S
new file mode 100644 (file)
index 0000000..9fd1fc9
--- /dev/null
@@ -0,0 +1,37 @@
+#include <inc/mmu.h>
+#include <inc/memlayout.h>
+
+.data
+
+
+// Define the global symbols 'envs', 'pages', 'vpt', and 'vpd'
+// so that they can be used in C as if they were ordinary global arrays.
+       .globl envs
+       .set envs, UENVS
+       .globl pages
+       .set pages, UPAGES
+       .globl vpt
+       .set vpt, UVPT
+       .globl vpd
+       .set vpd, (UVPT+(UVPT>>12)*4)
+
+
+// Entrypoint - this is where the kernel (or our parent environment)
+// starts us running when we are initially loaded into a new environment.
+.text
+.globl _start
+_start:
+       // See if we were started with arguments on the stack
+       cmpl $USTACKTOP, %esp
+       jne args_exist
+
+       // If not, push dummy argc/argv arguments.
+       // This happens when we are loaded by the kernel,
+       // because the kernel does not know about passing arguments.
+       pushl $0
+       pushl $0
+
+args_exist:
+       call libmain
+1:      jmp 1b
+
diff --git a/lib/exit.c b/lib/exit.c
new file mode 100644 (file)
index 0000000..85c64d6
--- /dev/null
@@ -0,0 +1,9 @@
+
+#include <inc/lib.h>
+
+void
+exit(void)
+{
+       sys_env_destroy(0);
+}
+
diff --git a/lib/libmain.c b/lib/libmain.c
new file mode 100644 (file)
index 0000000..e5c3bf4
--- /dev/null
@@ -0,0 +1,28 @@
+// Called from entry.S to get us going.
+// entry.S already took care of defining envs, pages, vpd, and vpt.
+
+#include <inc/lib.h>
+
+extern void umain(int argc, char **argv);
+
+volatile struct Env *env;
+char *binaryname = "(PROGRAM NAME UNKNOWN)";
+
+void
+libmain(int argc, char **argv)
+{
+       // set env to point at our env structure in envs[].
+       // LAB 3: Your code here.
+       env = 0;
+
+       // save the name of the program so that panic() can use it
+       if (argc > 0)
+               binaryname = argv[0];
+
+       // call user main routine
+       umain(argc, argv);
+
+       // exit gracefully
+       exit();
+}
+
diff --git a/lib/panic.c b/lib/panic.c
new file mode 100644 (file)
index 0000000..58f9afd
--- /dev/null
@@ -0,0 +1,29 @@
+
+#include <inc/lib.h>
+
+char *argv0;
+
+/*
+ * Panic is called on unresolvable fatal errors.
+ * It prints "panic: <message>", then causes a breakpoint exception,
+ * which causes JOS to enter the JOS kernel monitor.
+ */
+void
+_panic(const char *file, int line, const char *fmt,...)
+{
+       va_list ap;
+
+       va_start(ap, fmt);
+
+       // Print the panic message
+       if (argv0)
+               cprintf("%s: ", argv0);
+       cprintf("user panic in %s at %s:%d: ", binaryname, file, line);
+       vcprintf(fmt, ap);
+       cprintf("\n");
+
+       // Cause a breakpoint exception
+       while (1)
+               asm volatile("int3");
+}
+
diff --git a/lib/printf.c b/lib/printf.c
new file mode 100644 (file)
index 0000000..59d90c6
--- /dev/null
@@ -0,0 +1,62 @@
+// Implementation of cprintf console output for user environments,
+// based on printfmt() and the sys_cputs() system call.
+//
+// cprintf is a debugging statement, not a generic output statement.
+// It is very important that it always go to the console, especially when 
+// debugging file descriptor code!
+
+#include <inc/types.h>
+#include <inc/stdio.h>
+#include <inc/stdarg.h>
+#include <inc/lib.h>
+
+
+// Collect up to 256 characters into a buffer
+// and perform ONE system call to print all of them,
+// in order to make the lines output to the console atomic
+// and prevent interrupts from causing context switches
+// in the middle of a console output line and such.
+struct printbuf {
+       int idx;        // current buffer index
+       int cnt;        // total bytes printed so far
+       char buf[256];
+};
+
+
+static void
+putch(int ch, struct printbuf *b)
+{
+       b->buf[b->idx++] = ch;
+       if (b->idx == 256-1) {
+               sys_cputs(b->buf, b->idx);
+               b->idx = 0;
+       }
+       b->cnt++;
+}
+
+int
+vcprintf(const char *fmt, va_list ap)
+{
+       struct printbuf b;
+
+       b.idx = 0;
+       b.cnt = 0;
+       vprintfmt((void*)putch, &b, fmt, ap);
+       sys_cputs(b.buf, b.idx);
+
+       return b.cnt;
+}
+
+int
+cprintf(const char *fmt, ...)
+{
+       va_list ap;
+       int cnt;
+
+       va_start(ap, fmt);
+       cnt = vcprintf(fmt, ap);
+       va_end(ap);
+
+       return cnt;
+}
+
diff --git a/lib/syscall.c b/lib/syscall.c
new file mode 100644 (file)
index 0000000..061ac21
--- /dev/null
@@ -0,0 +1,61 @@
+// System call stubs.
+
+#include <inc/syscall.h>
+#include <inc/lib.h>
+
+static inline uint32_t
+syscall(int num, uint32_t a1, uint32_t a2, uint32_t a3, uint32_t a4, uint32_t a5)
+{
+       uint32_t ret;
+
+       // Generic system call: pass system call number in AX,
+       // up to five parameters in DX, CX, BX, DI, SI.
+       // Interrupt kernel with T_SYSCALL.
+       //
+       // The "volatile" tells the assembler not to optimize
+       // this instruction away just because we don't use the
+       // return value.
+       // 
+       // The last clause tells the assembler that this can
+       // potentially change the condition codes and arbitrary
+       // memory locations.
+
+       asm volatile("int %1\n"
+               : "=a" (ret)
+               : "i" (T_SYSCALL),
+                 "a" (num),
+                 "d" (a1),
+                 "c" (a2),
+                 "b" (a3),
+                 "D" (a4),
+                 "S" (a5)
+               : "cc", "memory");
+       
+       return ret;
+}
+
+void
+sys_cputs(const char *s, size_t len)
+{
+       syscall(SYS_cputs, (uint32_t) s, len, 0, 0, 0);
+}
+
+int
+sys_cgetc(void)
+{
+       return syscall(SYS_cgetc, 0, 0, 0, 0, 0);
+}
+
+int
+sys_env_destroy(envid_t envid)
+{
+       return syscall(SYS_env_destroy, envid, 0, 0, 0, 0);
+}
+
+envid_t
+sys_getenvid(void)
+{
+        return syscall(SYS_getenvid, 0, 0, 0, 0, 0);
+}
+
+
diff --git a/user/Makefrag b/user/Makefrag
new file mode 100644 (file)
index 0000000..dc8f6a5
--- /dev/null
@@ -0,0 +1,12 @@
+OBJDIRS += user
+
+$(OBJDIR)/user/%.o: user/%.c
+       @echo + cc[USER] $<
+       @mkdir -p $(@D)
+       $(V)$(CC) -nostdinc $(USER_CFLAGS) -c -o $@ $<
+
+$(OBJDIR)/user/%: $(OBJDIR)/user/%.o $(OBJDIR)/lib/entry.o $(OBJDIR)/lib/libjos.a user/user.ld
+       @echo + ld $@
+       $(V)$(LD) -o $@ $(ULDFLAGS) $(LDFLAGS) -nostdlib $(OBJDIR)/lib/entry.o $@.o -L$(OBJDIR)/lib -ljos $(GCC_LIB)
+       $(V)$(OBJDUMP) -S $@ > $@.asm
+       $(V)$(NM) -n $@ > $@.sym
diff --git a/user/badsegment.c b/user/badsegment.c
new file mode 100644 (file)
index 0000000..21e8ff7
--- /dev/null
@@ -0,0 +1,14 @@
+// program to cause a general protection exception
+
+#include <inc/lib.h>
+
+void
+umain(void)
+{
+       // Try to load the kernel's TSS selector into the DS register.
+        //asm volatile("movw $28,%ax; movw %ax,%ds");
+  
+        // DP: 0x28 == 40
+        asm volatile("movw $40,%ax; movw %ax,%ds");
+}
+
diff --git a/user/breakpoint.c b/user/breakpoint.c
new file mode 100644 (file)
index 0000000..85f6ebd
--- /dev/null
@@ -0,0 +1,10 @@
+// program to cause a breakpoint trap
+
+#include <inc/lib.h>
+
+void
+umain(void)
+{
+       asm volatile("int $3");
+}
+
diff --git a/user/buggyhello.c b/user/buggyhello.c
new file mode 100644 (file)
index 0000000..bc9c6b9
--- /dev/null
@@ -0,0 +1,11 @@
+// buggy hello world -- unmapped pointer passed to kernel
+// kernel should destroy user environment in response
+
+#include <inc/lib.h>
+
+void
+umain(void)
+{
+       sys_cputs((char*)1, 1);
+}
+
diff --git a/user/divzero.c b/user/divzero.c
new file mode 100644 (file)
index 0000000..c9d265f
--- /dev/null
@@ -0,0 +1,12 @@
+// buggy program - causes a divide by zero exception
+
+#include <inc/lib.h>
+
+int zero;
+
+void
+umain(void)
+{
+       cprintf("1/0 is %08x!\n", 1/zero);
+}
+
diff --git a/user/evilhello.c b/user/evilhello.c
new file mode 100644 (file)
index 0000000..4663e10
--- /dev/null
@@ -0,0 +1,12 @@
+// evil hello world -- kernel pointer passed to kernel
+// kernel should destroy user environment in response
+
+#include <inc/lib.h>
+
+void
+umain(void)
+{
+       // try to print the kernel entry point as a string!  mua ha ha!
+       sys_cputs((char*)0xf0100020, 100);
+}
+
diff --git a/user/faultread.c b/user/faultread.c
new file mode 100644 (file)
index 0000000..d637e6c
--- /dev/null
@@ -0,0 +1,10 @@
+// buggy program - faults with a read from location zero
+
+#include <inc/lib.h>
+
+void
+umain(void)
+{
+       cprintf("I read %08x from location 0!\n", *(unsigned*)0);
+}
+
diff --git a/user/faultreadkernel.c b/user/faultreadkernel.c
new file mode 100644 (file)
index 0000000..457235f
--- /dev/null
@@ -0,0 +1,10 @@
+// buggy program - faults with a read from kernel space
+
+#include <inc/lib.h>
+
+void
+umain(void)
+{
+       cprintf("I read %08x from location 0xf0100000!\n", *(unsigned*)0xf0100000);
+}
+
diff --git a/user/faultwrite.c b/user/faultwrite.c
new file mode 100644 (file)
index 0000000..3a628d5
--- /dev/null
@@ -0,0 +1,10 @@
+// buggy program - faults with a write to location zero
+
+#include <inc/lib.h>
+
+void
+umain(void)
+{
+       *(unsigned*)0 = 0;
+}
+
diff --git a/user/faultwritekernel.c b/user/faultwritekernel.c
new file mode 100644 (file)
index 0000000..7e94206
--- /dev/null
@@ -0,0 +1,10 @@
+// buggy program - faults with a write to a kernel location
+
+#include <inc/lib.h>
+
+void
+umain(void)
+{
+       *(unsigned*)0xf0100000 = 0;
+}
+
diff --git a/user/hello.c b/user/hello.c
new file mode 100644 (file)
index 0000000..e2fdd68
--- /dev/null
@@ -0,0 +1,9 @@
+// hello, world
+#include <inc/lib.h>
+
+void
+umain(void)
+{
+       cprintf("hello, world\n");
+       cprintf("i am environment %08x\n", env->env_id);
+}
diff --git a/user/softint.c b/user/softint.c
new file mode 100644 (file)
index 0000000..ec2c8d8
--- /dev/null
@@ -0,0 +1,10 @@
+// buggy program - causes an illegal software interrupt
+
+#include <inc/lib.h>
+
+void
+umain(void)
+{
+       asm volatile("int $14");        // page fault
+}
+
diff --git a/user/testbss.c b/user/testbss.c
new file mode 100644 (file)
index 0000000..ac83e18
--- /dev/null
@@ -0,0 +1,27 @@
+// test reads and writes to a large bss
+
+#include <inc/lib.h>
+
+#define ARRAYSIZE (1024*1024)
+
+uint32_t bigarray[ARRAYSIZE];
+
+void
+umain(void)
+{
+       int i;
+
+       cprintf("Making sure bss works right...\n");
+       for (i = 0; i < ARRAYSIZE; i++)
+               if (bigarray[i] != 0)
+                       panic("bigarray[%d] isn't cleared!\n", i);
+       for (i = 0; i < ARRAYSIZE; i++)
+               bigarray[i] = i;
+       for (i = 0; i < ARRAYSIZE; i++)
+               if (bigarray[i] != i)
+                       panic("bigarray[%d] didn't hold its value!\n", i);
+
+       cprintf("Yes, good.  Now doing a wild write off the end...\n");
+       bigarray[ARRAYSIZE+1024] = 0;
+       panic("SHOULD HAVE TRAPPED!!!");
+}