Merge remote branch 'origin/sparc-dev'
authorBarret Rhoden <brho@cs.berkeley.edu>
Thu, 3 Dec 2009 03:14:54 +0000 (19:14 -0800)
committerBarret Rhoden <brho@cs.berkeley.edu>
Thu, 3 Dec 2009 03:14:54 +0000 (19:14 -0800)
Still a few outstanding issues, like fully merging roslib into parlib
and sorting brk() and mmap().

Conflicts:
GNUmakefile
kern/arch/i386/atomic.h
kern/arch/sparc/trap.c
kern/include/env.h
kern/include/page_alloc.h
kern/include/ros/procdata.h
kern/src/env.c
kern/src/init.c
kern/src/kfs.c
kern/src/manager.c
kern/src/page_alloc.c
kern/src/syscall.c
user/apps/roslib/mhello.c

29 files changed:
1  2 
GNUmakefile
kern/arch/i386/atomic.h
kern/arch/i386/pmap.c
kern/arch/i386/smp_boot.c
kern/arch/sparc/atomic.h
kern/arch/sparc/init.c
kern/arch/sparc/pmap.c
kern/arch/sparc/smp.c
kern/arch/sparc/trap.c
kern/include/env.h
kern/include/page_alloc.h
kern/include/process.h
kern/include/ros/procdata.h
kern/include/testing.h
kern/src/Makefrag
kern/src/env.c
kern/src/init.c
kern/src/kfs.c
kern/src/manager.c
kern/src/page_alloc.c
kern/src/pmap.c
kern/src/process.c
kern/src/slab.c
kern/src/syscall.c
kern/src/testing.c
user/apps/parlib/run_binary.c
user/apps/parlib/run_binary_colored.c
user/parlib/src/i386/entry.S
user/roslib/inc/lib.h

diff --combined GNUmakefile
@@@ -27,22 -27,22 +27,22 @@@ V = 
  # Cross-compiler ros toolchain
  #
  # This Makefile will automatically use the cross-compiler toolchain
- # installed as 'i386-ros-elf-*', if one exists.  If the host tools ('gcc',
+ # installed as 'i386-ros-*', if one exists.  If the host tools ('gcc',
  # 'objdump', and so forth) compile for a 32-bit x86 ELF target, that will
  # be detected as well.  If you have the right compiler toolchain installed
- # using a different name, set GCCPREFIX explicitly in conf/env.mk
+ # using a different name, set GCCPREFIX explicitly in your Makelocal file
  
  # try to infer the correct GCCPREFIX
  ifndef GCCPREFIX
- GCCPREFIX := $(shell if i386-ros-elf-objdump -i 2>&1 | grep '^elf32-i386$$' >/dev/null 2>&1; \
-       then echo 'i386-ros-elf-'; \
+ GCCPREFIX := $(shell if i386-ros-objdump -i 2>&1 | grep '^elf32-i386$$' >/dev/null 2>&1; \
+       then echo 'i386-ros-'; \
        elif objdump -i 2>&1 | grep 'elf32-i386' >/dev/null 2>&1; \
        then echo ''; \
        else echo "***" 1>&2; \
        echo "*** Error: Couldn't find an i386-*-elf version of GCC/binutils." 1>&2; \
-       echo "*** Is the directory with i386-ros-elf-gcc in your PATH?" 1>&2; \
+       echo "*** Is the directory with i386-ros-gcc in your PATH?" 1>&2; \
        echo "*** If your i386-*-elf toolchain is installed with a command" 1>&2; \
-       echo "*** prefix other than 'i386-ros-elf-', set your GCCPREFIX" 1>&2; \
+       echo "*** prefix other than 'i386-ros-', set your GCCPREFIX" 1>&2; \
        echo "*** environment variable to that prefix and run 'make' again." 1>&2; \
        echo "*** To turn off this error, run 'gmake GCCPREFIX= ...'." 1>&2; \
        echo "***" 1>&2; exit 1; fi)
@@@ -51,17 -51,16 +51,17 @@@ endi
  # Default programs for compilation
  ifeq ($(COMPILER),IVY)
  KERN_CFLAGS := --deputy\
 -               --enable-error-db\
 -               --no-rc-sharc\
 -               --sc-dynamic-is-error\
 -               --sc-ops=$(INCLUDE_DIR)/ivy/sharc.h\
 -               --sc-all-in-thread\
 -               --enable-precompile
 +                  --no-rc-sharc\
 +                  --sc-dynamic-is-error\
 +                  --sc-ops=$(INCLUDE_DIR)/ivy/sharc.h\
 +                  --sc-all-in-thread\
 +                  --enable-precompile\
 +#                  --enable-error-db\
 +
  USER_CFLAGS := --deputy --enable-error-db
  CC        := ivycc --gcc=$(GCCPREFIX)gcc
  else
- CC        := $(GCCPREFIX)gcc -std=gnu99 -fgnu89-inline
+ CC        := $(GCCPREFIX)gcc 
  endif
  
  AS        := $(GCCPREFIX)as
@@@ -72,13 -71,15 +72,15 @@@ OBJDUMP    := $(GCCPREFIX)objdum
  NM        := $(GCCPREFIX)nm
  PERL    := perl
  
+ EXTRAARGS ?= -std=gnu99 -Wno-attributes -fno-stack-protector -fgnu89-inline
  # Universal compiler flags
  # -fno-builtin is required to avoid refs to undefined functions in the kernel.
  # Only optimize to -O1 to discourage inlining, which complicates backtraces.
  CFLAGS := $(CFLAGS) -D$(TARGET_ARCH) $(EXTRAARGS)
- CFLAGS += -O2 -pipe -MD -fno-builtin -fno-stack-protector -gstabs
- CFLAGS += -Wall -Wno-format -Wno-unused -Wno-attributes
- CFLAGS += -nostdinc -Igccinclude/$(TARGET_ARCH)
+ CFLAGS += -O2 -pipe -MD -fno-builtin -gstabs
+ CFLAGS += -Wall -Wno-format -Wno-unused -fno-strict-aliasing
#CFLAGS += -nostdinc -Igccinclude/$(TARGET_ARCH)
  
  # Universal loader flags
  LDFLAGS := -nostdlib
  # GCC Library path 
  GCC_LIB := $(shell $(CC) -print-libgcc-file-name)
  
- # 64 Bit specific flags / definitions
- ifeq ($(TARGET_ARCH),i386)
-       ifeq ($(UNAME),x86_64)
-               CFLAGS += -m32
-               LDFLAGS += -melf_i386
-               GCC_LIB = $(shell $(CC) -print-libgcc-file-name | sed 's/libgcc.a/32\/libgcc.a/')
-       endif
- endif
  # List of directories that the */Makefrag makefile fragments will add to
  OBJDIRS :=
  
@@@ -137,6 -129,9 +130,9 @@@ docs
  doxyclean:
        rm -rf doc/rosdoc
  
+ augment-gcc: symlinks
+       scripts/augment-gcc $(dir $(shell which $(CC))).. $(TARGET_ARCH)
  # For deleting the build
  clean:
        @rm -rf $(OBJDIR)
diff --combined kern/arch/i386/atomic.h
@@@ -1,12 -1,7 +1,12 @@@
  #ifndef ROS_INCLUDE_ATOMIC_H
  #define ROS_INCLUDE_ATOMIC_H
  
 +/* This enables tracking who last locked a spinlock. */
 +#define SPINLOCK_DEBUG
 +
  #include <ros/common.h>
 +#include <arch/x86.h>
 +#include <arch/arch.h>
  
  #define mb() {rmb(); wmb();}
  #define rmb() ({ asm volatile("lfence"); })
  #define wmb_f() ({ asm volatile("sfence"); })
  
  typedef void * RACY atomic_t;
 -typedef volatile uint32_t RACY spinlock_t;
 +struct spinlock {
 +      volatile uint32_t RACY rlock;
 +#ifdef SPINLOCK_DEBUG
 +      void *call_site;        
 +      uint32_t calling_core;
 +#endif
 +};
 +typedef struct spinlock RACY spinlock_t;
 +#define SPINLOCK_INITIALIZER {0}
  
  static inline void atomic_init(atomic_t *number, int32_t val);
  static inline int32_t atomic_read(atomic_t *number);
@@@ -32,12 -19,9 +32,13 @@@ static inline void atomic_set(atomic_t 
  static inline void atomic_inc(atomic_t *number);
  static inline void atomic_dec(atomic_t *number);
  static inline void atomic_andb(volatile uint8_t RACY* number, uint8_t mask);
 -static inline void spin_lock(volatile uint32_t SRACY*COUNT(1) lock);
 -static inline void spin_unlock(volatile uint32_t SRACY* lock);
+ static inline void atomic_orb(volatile uint8_t RACY* number, uint8_t mask);
 +static inline uint32_t spin_locked(spinlock_t *SAFE lock);
 +static inline void __spin_lock(volatile uint32_t SRACY*CT(1) rlock);
 +static inline void spin_lock(spinlock_t *lock);
 +static inline void spin_unlock(spinlock_t *lock);
 +static inline void spinlock_init(spinlock_t *lock);
 +void spinlock_debug(spinlock_t *lock);
  
  /* Inlined functions declared above */
  static inline void atomic_init(atomic_t *number, int32_t val)
@@@ -73,13 -57,12 +74,18 @@@ static inline void atomic_andb(volatil
        asm volatile("lock andb %1,%0" : "=m"(*number) : "r"(mask) : "cc");
  }
  
 -static inline void spin_lock(volatile uint32_t* lock)
+ static inline void atomic_orb(volatile uint8_t RACY*number, uint8_t mask)
+ {
+       asm volatile("lock orb %1,%0" : "=m"(*number) : "r"(mask) : "cc");
+ }
 +static inline uint32_t spin_locked(spinlock_t *SAFE lock)
 +{
 +      // the lock status is the lowest byte of the lock
 +      return lock->rlock & 0xff;
 +}
 +
 +static inline void __spin_lock(volatile uint32_t *rlock)
  {
        asm volatile(
                        "1:                       "
                        "       xchgb %%al, %0;       "
                        "       cmpb $0, %%al;        "
                        "       jne 1b;               "
 -              : : "m"(*lock) : "eax", "cc");
 +              : : "m"(*rlock) : "eax", "cc");
 +}
 +
 +static inline void spin_lock(spinlock_t *lock)
 +{
 +      __spin_lock(&lock->rlock);
 +#ifdef SPINLOCK_DEBUG
 +      lock->call_site = (void RACY*CT(1))TC(read_eip());
 +      lock->calling_core = core_id();
 +#endif
 +}
 +
 +static inline void spin_unlock(spinlock_t *lock)
 +{
 +      lock->rlock = 0;
  }
  
 -static inline void spin_unlock(volatile uint32_t* lock)
 +static inline void spinlock_init(spinlock_t *lock)
 +#ifdef SPINLOCK_DEBUG
 +WRITES(lock->rlock,lock->call_site,lock->calling_core)
 +#else
 +WRITES(lock->rlock)
 +#endif
  {
 -      *lock = 0;
 +      lock->rlock = 0;
 +#ifdef SPINLOCK_DEBUG
 +      lock->call_site = 0;
 +      lock->calling_core = 0;
 +#endif
  }
  
  #endif /* !ROS_INCLUDE_ATOMIC_H */
diff --combined kern/arch/i386/pmap.c
@@@ -361,6 -361,16 +361,6 @@@ vm_init(void
        boot_map_segment(pgdir, (uintptr_t)LAPIC_BASE, PGSIZE, LAPIC_BASE,
                         PTE_PCD | PTE_PWT | PTE_W | PTE_G);
  
 -      //////////////////////////////////////////////////////////////////////
 -      // Make 'envs' point to an array of size 'NENV' of 'env_t'.
 -      // No longer mapping ENVS into the address space
 -      
 -      // round up to the nearest page
 -      size_t env_array_size = ROUNDUP(NENV*sizeof(env_t), PGSIZE);
 -      env_t * tmpenv = (env_t *)boot_calloc(env_array_size, 1, PGSIZE);
 -      envs = SINIT(tmpenv);
 -      //memset(envs, 0, env_array_size);
 -
        // Check that the initial page directory has been set up correctly.
        check_boot_pgdir(pse);
  
@@@ -601,7 -611,7 +601,7 @@@ pgdir_walk(pde_t *pgdir, const void *SN
                *the_pde = PTE_PS | PTE_P;
                return (pte_t*)the_pde;
        }
-       if (page_alloc(&new_table))
+       if (kpage_alloc(&new_table))
                return NULL;
        page_setref(new_table,1);
        memset(page2kva(new_table), 0, PGSIZE);
@@@ -640,7 -650,7 +640,7 @@@ void *get_free_va_range(pde_t *pgdir, u
        addr &= ~0xfff;
        if (!addr)
                // some sensible default.  can cache the previous value somewhere
-               addr = USTACKTOP - PGSIZE; // TODO: not looking down
+               addr = USTACKBOT; // TODO: not looking down
        startaddr = addr;       
        pte_t *pte = pgdir_walk(pgdir, (void*)addr, 0);
        // what about jumbo pages?
@@@ -679,22 -689,25 +679,25 @@@ page_check(void
  
        // should be able to allocate three pages
        pp0 = pp1 = pp2 = 0;
-       assert(page_alloc(&pp0) == 0);
-       assert(page_alloc(&pp1) == 0);
-       assert(page_alloc(&pp2) == 0);
+       assert(kpage_alloc(&pp0) == 0);
+       assert(kpage_alloc(&pp1) == 0);
+       assert(kpage_alloc(&pp2) == 0);
+       page_setref(pp0, 0);
+       page_setref(pp1, 0);
+       page_setref(pp2, 0);
  
        assert(pp0);
        assert(pp1 && pp1 != pp0);
        assert(pp2 && pp2 != pp1 && pp2 != pp0);
  
        // temporarily steal the rest of the free pages
-       for(int i=0; i<llc_num_colors; i++) {
+       for(int i=0; i<llc_cache->num_colors; i++) {
                fl[i] = colored_page_free_list[i];
                LIST_INIT(&colored_page_free_list[i]);
        }
  
        // should be no free memory
-       assert(page_alloc(&pp) == -ENOMEM);
+       assert(kpage_alloc(&pp) == -ENOMEM);
  
        // Fill pp1 with bogus data and check for invalid tlb entries
        memset(page2kva(pp1), 0xFFFFFFFF, PGSIZE);
        }
  
        // should be no free memory
-       assert(page_alloc(&pp) == -ENOMEM);
+       assert(kpage_alloc(&pp) == -ENOMEM);
  
        // should be able to map pp2 at PGSIZE because it's already there
        assert(page_insert(boot_pgdir, pp2, (void*SNT) PGSIZE, PTE_U) == 0);
  
        // pp2 should NOT be on the free list
        // could happen in ref counts are handled sloppily in page_insert
-       assert(page_alloc(&pp) == -ENOMEM);
+       assert(kpage_alloc(&pp) == -ENOMEM);
  
        // should not be able to map at PTSIZE because need free page for page table
        assert(page_insert(boot_pgdir, pp0, (void*SNT) PTSIZE, 0) < 0);
        assert(pp2->page_ref == 0);
  
        // pp2 should be returned by page_alloc
-       assert(page_alloc(&pp) == 0 && pp == pp2);
+       assert(kpage_alloc(&pp) == 0 && pp == pp2);
+       page_setref(pp, 0);
  
        // unmapping pp1 at 0 should keep pp1 at PGSIZE
        page_remove(boot_pgdir, 0x0);
        assert(pp2->page_ref == 0);
  
        // so it should be returned by page_alloc
-       assert(page_alloc(&pp) == 0 && pp == pp1);
+       assert(kpage_alloc(&pp) == 0 && pp == pp1);
+       page_setref(pp, 0);
  
        // should be no free memory
-       assert(page_alloc(&pp) == -ENOMEM);
+       assert(kpage_alloc(&pp) == -ENOMEM);
  
        // forcibly take pp0 back
        assert(PTE_ADDR(boot_pgdir[0]) == page2pa(pp0));
        }
  
        // give free list back
-       for(int i=0; i<llc_num_colors; i++)
+       for(int i=0; i<llc_cache->num_colors; i++)
                colored_page_free_list[i] = fl[i];
  
        // free the pages we took
@@@ -28,7 -28,7 +28,7 @@@
  #include <timing.h>
  
  extern handler_wrapper_t (RO handler_wrappers)[NUM_HANDLER_WRAPPERS];
 -volatile uint8_t num_cpus = 0xee;
 +volatile uint32_t num_cpus = 0xee;
  uintptr_t RO smp_stack_top;
  
  #define DECLARE_HANDLER_CHECKLISTS(vector)                          \
@@@ -100,9 -100,8 +100,8 @@@ void smp_boot(void
        page_insert(boot_pgdir, pa2page(trampoline_pg), (void*SNT)trampoline_pg, PTE_W);
  
        // Allocate a stack for the cores starting up.  One for all, must share
-       if (page_alloc(&smp_stack))
+       if (kpage_alloc(&smp_stack))
                panic("No memory for SMP boot stack!");
-       page_incref(smp_stack);
        smp_stack_top = SINIT((uintptr_t)(page2kva(smp_stack) + PGSIZE));
  
        // Start the IPI process (INIT, wait, SIPI, wait, SIPI, wait)
        // booting.  Specifically, it's when they turn on paging and have that temp
        // mapping pulled out from under them.  Now, if a core loses, it will spin
        // on the trampoline (which we must be careful to not deallocate)
 -      spin_lock(get_smp_bootlock());
 +      __spin_lock(get_smp_bootlock());
        cprintf("Num_Cpus Detected: %d\n", num_cpus);
  
        // Remove the mapping of the page used by the trampoline
        init_barrier(&generic_barrier, num_cpus); // barrier used by smp_mtrr_handler
        smp_call_function_all(smp_mtrr_handler, &generic_barrier, 0);
  
 +      // initialize my per-cpu info
 +      smp_percpu_init();
 +
        // Should probably flush everyone's TLB at this point, to get rid of
        // temp mappings that were removed.  TODO
  }
@@@ -207,9 -203,8 +206,8 @@@ uint32_t smp_main(void
  
        // Get a per-core kernel stack
        page_t *my_stack;
-       if (page_alloc(&my_stack))
+       if (kpage_alloc(&my_stack))
                panic("Unable to alloc a per-core stack!");
-       page_incref(my_stack);
        memset(page2kva(my_stack), 0, PGSIZE);
  
        // Set up a gdt / gdt_pd for this core, stored at the top of the stack
        // set a default logical id for now
        lapic_set_logid(lapic_get_id());
  
 +      // initialize my per-cpu info
 +      smp_percpu_init();
 +
        return my_stack_top; // will be loaded in smp_entry.S
  }
  
 +/* Perform any initialization needed by per_cpu_info.  Right now, this just
 + * inits the amsg list (which sparc will probably also want).  Make sure every
 + * core calls this at some point in the smp_boot process. */
 +void smp_percpu_init(void)
 +{
 +      uint32_t coreid = core_id();
 +      STAILQ_INIT(&per_cpu_info[coreid].active_msgs);
 +}
diff --combined kern/arch/sparc/atomic.h
@@@ -21,8 -21,8 +21,9 @@@ static inline void atomic_set(atomic_t
  static inline void atomic_add(atomic_t* number, int32_t inc);
  static inline void atomic_inc(atomic_t* number);
  static inline void atomic_dec(atomic_t* number);
+ static inline uint32_t atomic_swap(uint32_t* addr, uint32_t val);
  static inline uint32_t spin_trylock(spinlock_t*SAFE lock);
 +static inline uint32_t spin_locked(spinlock_t*SAFE lock);
  static inline void spin_lock(spinlock_t*SAFE lock);
  static inline void spin_unlock(spinlock_t*SAFE lock);
  
@@@ -71,6 -71,12 +72,12 @@@ static inline void atomic_dec(atomic_t
        atomic_add(number,-1);
  }
  
+ static inline uint32_t atomic_swap(uint32_t* addr, uint32_t val)
+ {
+       asm volatile ("swap [%2],%0" : "=r"(val) : "0"(val),"r"(addr) : "memory");
+       return val;
+ }
  static inline uint32_t spin_trylock(spinlock_t*SAFE lock)
  {
        uint32_t reg;
diff --combined kern/arch/sparc/init.c
@@@ -5,10 -5,34 +5,34 @@@
  #endif
  
  #include <smp.h>
+ #include <arch/init.h>
+ size_t argc;
+ char** argv;
+ // dummy values to avoid putting this in the BSS section
+ void* __args[(4096+8)/sizeof(void*)] __attribute__((aligned(8))) = {(void*)1};
+ static void
+ init_argc_argv()
+ {
+         argc = (size_t)__args[2];
+         argv = (char**)&__args[3];
+         //argv[0] should be "none" for ROS.  we'll ignore it.
+         if(argc > 0)
+         {
+                 argc--;
+                 argv++;
+         }
+         for(size_t i = 0; i < argc; i++)
+                 argv[i] += (char*)(&__args[2])-(char*)NULL;
+ }
  
  void arch_init()
  {             
        // this returns when all other cores are done and ready to receive IPIs
+       init_argc_argv();
        smp_boot();
 -      env_init();
 +      proc_init();
  }
diff --combined kern/arch/sparc/pmap.c
@@@ -26,6 -26,10 +26,6 @@@ vm_init(void
        extern pde_t l1_page_table[NL1ENTRIES];
        boot_pgdir = l1_page_table;
        boot_cr3 = PADDR(boot_pgdir);
 -
 -      size_t env_array_size = ROUNDUP(NENV*sizeof(env_t), PGSIZE);
 -      envs = /*(env_t *)*/boot_calloc(env_array_size, 1, PGSIZE);
 -      //memset(envs, 0, env_array_size);
  }
  
  error_t
@@@ -56,9 -60,8 +56,8 @@@ pgdir_walk(pde_t* l1pt, const void*SNT 
                // space than needed, so also use it for the adjacent
                // l2_tables_per_page-1 pages (if they're unmapped)
  
-               if(page_alloc(&new_table))
+               if(kpage_alloc(&new_table))
                        return NULL;
-               page_setref(new_table,1);
                memset(page2kva(new_table),0,PGSIZE);
  
                l2_tables_per_page = PGSIZE/(sizeof(pte_t)*NL2ENTRIES);
@@@ -89,9 -92,8 +88,8 @@@
                if(!create)
                        return NULL;
  
-               if(page_alloc(&new_table))
+               if(kpage_alloc(&new_table))
                        return NULL;
-               page_setref(new_table,1);
                memset(page2kva(new_table),0,PGSIZE);
  
                l3_tables_per_page = PGSIZE/(sizeof(pte_t)*NL3ENTRIES);
diff --combined kern/arch/sparc/smp.c
@@@ -94,13 -94,13 +94,13 @@@ int smp_call_function_all(isr_t handler
                if(i == core_id())
                        continue;
  
-               while(send_active_message(i,(amr_t)smp_call_wrapper,
-                                         handler, wrapper, data) != 0);
+               send_active_msg_sync(i,(amr_t)smp_call_wrapper,
+                                         handler, wrapper, data);
        }
  
        // send to me
-       while(send_active_message(core_id(),(amr_t)smp_call_wrapper,
-                                 handler,wrapper,data) != 0);
+       send_active_msg_sync(core_id(),(amr_t)smp_call_wrapper,
+                                 handler,wrapper,data);
  
        cpu_relax(); // wait to get the interrupt
  
@@@ -124,8 -124,8 +124,8 @@@ int smp_call_function_single(uint32_t d
  
        enable_irqsave(&state);
  
-       while(send_active_message(dest,(amr_t)smp_call_wrapper,
-                                 handler,wrapper,data) != 0);
+       send_active_msg_sync(dest,(amr_t)smp_call_wrapper,
+                                 handler,wrapper,data);
  
        cpu_relax(); // wait to get the interrupt, if it's to this core
  
@@@ -143,13 -143,3 +143,13 @@@ int smp_call_wait(handler_wrapper_t* wr
        spin_unlock(&wrapper->lock);
        return 0;
  }
 +
 +/* Perform any initialization needed by per_cpu_info.  Right now, this just
 + * inits the amsg list (which sparc will probably also want).  Make sure every
 + * core calls this at some point in the smp_boot process. */
 +void smp_percpu_init(void)
 +{
 +      static_assert(0);
 +      uint32_t coreid = core_id();
 +      STAILQ_INIT(&per_cpu_info[coreid].active_msgs);
 +}
diff --combined kern/arch/sparc/trap.c
  #pragma nodeputy
  #endif
  
- void
- idt_init(void)
+ spinlock_t active_message_buf_busy[MAX_NUM_CPUS] = {0};
+ active_message_t active_message_buf[MAX_NUM_CPUS];
+ uint32_t send_active_message(uint32_t dst, amr_t pc,
+                              TV(a0t) arg0, TV(a1t) arg1, TV(a2t) arg2)
  {
+       if(dst >= num_cpus || spin_trylock(&active_message_buf_busy[dst]))
+               return -1;
+       active_message_buf[dst].srcid = core_id();
+       active_message_buf[dst].pc = pc;
+       active_message_buf[dst].arg0 = arg0;
+       active_message_buf[dst].arg1 = arg1;
+       active_message_buf[dst].arg2 = arg2;
+       if(send_ipi(dst))
+       {
+               spin_unlock(&active_message_buf_busy[dst]);
+               return -1;
+       }
+       return 0;
  }
  
  void
sysenter_init(void)
idt_init(void)
  {
  }
  
  void
trap_handled(void)
sysenter_init(void)
  {
-       if(current)
-               proc_startcore(current,&current->env_tf);
-       else if(core_id() == 0)
-               manager();
-       else
-               smp_idle();
  }
  
  void
  
  #define TRAPNAME_MAX  32
  
- char*
static char*
  get_trapname(uint8_t tt, char buf[TRAPNAME_MAX])
  {
-       const char* trapnames[] = {
+       static const char* trapnames[] = {
                [0x00] "reset",
                [0x01] "instruction access exception",
                [0x02] "illegal instruction",
  }
  
  void
- trap(trapframe_t* state, active_message_t* msg,
-      void (*handler)(trapframe_t*,active_message_t*))
+ trap(trapframe_t* state, void (*handler)(trapframe_t*))
  {
-       // TODO: this will change with multicore processes
-       if(current)
+       // TODO: must save other cores' trap frames
+       // if we want them to migrate, block, etc.
+       if(current && current->vcoremap[0] == core_id())
        {
                current->env_tf = *state;
-               handler(&current->env_tf,msg);
+               handler(&current->env_tf);
        }
        else
-               handler(state,msg);
+               handler(state);
  }
  
  void
- handle_active_message(trapframe_t* state, active_message_t* message)
+ handle_ipi(trapframe_t* state)
  {
-       uint32_t src = message->srcid;
-       TV(a0t) a0 = message->arg0;
-       TV(a1t) a1 = message->arg1;
-       TV(a2t) a2 = message->arg2;
-       (message->pc)(state,src,a0,a1,a2);
+       active_message_t m;
+       m = active_message_buf[core_id()];
+       spin_unlock(&active_message_buf_busy[core_id()]);
+       uint32_t src = m.srcid;
+       TV(a0t) a0 = m.arg0;
+       TV(a1t) a1 = m.arg1;
+       TV(a2t) a2 = m.arg2;
+       (m.pc)(state,src,a0,a1,a2);
        env_pop_tf(state);
  }
  
@@@ -140,21 -157,22 +157,23 @@@ unhandled_trap(trapframe_t* state
        uint32_t trap_type = (state->tbr >> 4) & 0xFF;
        get_trapname(trap_type,buf);
  
-       print_trapframe(state);
        if(state->psr & PSR_PS)
+       {
+               print_trapframe(state);
                panic("Unhandled trap in kernel!\nTrap type: %s",buf);
+       }
        else
        {
+               print_trapframe(state);
                warn("Unhandled trap in user!\nTrap type: %s",buf);
                assert(current);
 +              proc_incref(current, 1);
                proc_destroy(current);
                panic("I shouldn't have gotten here!");
        }
  }
  
- void
static void
  stack_fucked(trapframe_t* state)
  {
        // see if the problem arose when flushing out
        extern uint32_t tflush;
        if(state->pc == (uint32_t)&tflush)
        {
-               // if so, copy original trap state, except for trap type
-               uint32_t tbr = state->tbr;
-               *state = *(trapframe_t*)(state->gpr[14]+64);
-               state->tbr = tbr;
+               // the trap happened while flushing out windows.
+               // hope this happened in the user, or else we're hosed...
+               extern char bootstacktop;
+               state = (trapframe_t*)(&bootstacktop-SIZEOF_TRAPFRAME_T-core_id()*KSTKSIZE);
        }
+       warn("You just got stack fucked!");
        unhandled_trap(state);
  }
  
@@@ -197,12 -217,34 +218,34 @@@ access_exception(trapframe_t* state
  }
  
  void
- fp_exception(trapframe_t* state)
+ illegal_instruction(trapframe_t* state)
+ {
+       unhandled_trap(state);
+ }
+ void
+ real_fp_exception(trapframe_t* state, ancillary_state_t* sillystate)
  {
        unhandled_trap(state);
  }
  
  void
+ fp_exception(trapframe_t* state)
+ {
+       ancillary_state_t sillystate;
+       save_fp_state(&sillystate);     
+       // since our FP HW exception behavior is sketchy, reexecute
+       // any faulting FP instruction in SW, which may call
+       // real_fp_exception above
+       emulate_fpu(state,&sillystate);
+       restore_fp_state(&sillystate);
+       env_pop_tf(state);
+ }
+ void
  fp_disabled(trapframe_t* state)
  {
        if(state->psr & PSR_PS)
@@@ -226,24 -268,14 +269,25 @@@ handle_syscall(trapframe_t* state
        state->pc = state->npc;
        state->npc += 4;
  
-       //env_push_ancillary_state(current); // remove this if you don't need it
 +      // this comment is from i386.  we don't save silly state early either
 +      // hopefully you don't need to save it now.  let me know if otherwise
 +      static_assert(0);
 +      /* Note we are not preemptively saving the TF in the env_tf.  We do maintain
 +       * a reference to it in current_tf (a per-cpu pointer).
 +       * In general, only save the tf and any silly state once you know it
 +       * is necessary (blocking).  And only save it in env_tf when you know you
 +       * are single core (PROC_RUNNING_S) */
 +      set_current_tf(tf);
 -      // if we want them to migrate, block, etc.
 -      if(current->vcoremap[0] == core_id())
 -              env_push_ancillary_state(current);
+       // TODO: must save other cores' ancillary state
++      //if(current->vcoremap[0] == core_id())
++      //      env_push_ancillary_state(current); // remove this if you don't need it
  
 -      state->gpr[8] = syscall(current,state,num,a1,a2,a3,a4,a5);
 +      // syscall code wants an edible reference for current
 +      proc_incref(current, 1);
 +      state->gpr[8] = syscall(current,num,a1,a2,a3,a4,a5);
 +      proc_decref(current, 1);
  
-       trap_handled();
+       proc_startcore(current,state);
  }
  
  void
@@@ -280,9 -312,10 +324,12 @@@ handle_breakpoint(trapframe_t* state
        state->pc = state->npc;
        state->npc += 4;
  
-       //env_push_ancillary_state(current);
 +      // see comment above about tf's
 +      static_assert(0);
 -      if(current->vcoremap[0] == core_id())
 -              env_push_ancillary_state(current);
+       // TODO: must save other cores' ancillary state
+       // if we want them to migrate, block, etc.
++      //if(current->vcoremap[0] == core_id())
++      //      env_push_ancillary_state(current);
  
        // run the monitor
        monitor(state);
diff --combined kern/include/env.h
  struct Env;
  typedef struct Env env_t;
  
 -// An environment ID 'envid_t' has three parts:
 -//
 -// +1+---------------21-----------------+--------10--------+
 -// |0|          Uniqueifier             |   Environment    |
 -// | |                                  |      Index       |
 -// +------------------------------------+------------------+
 -//                                       \--- ENVX(eid) --/
 -//
 -// The environment index ENVX(eid) equals the environment's offset in the
 -// 'envs[]' array.  The uniqueifier distinguishes environments that were
 -// created at different times, but share the same environment index.
 -//
 -// All real environments are greater than 0 (so the sign bit is zero).
 -// envid_ts less than 0 signify errors.  The envid_t == 0 is special, and
 -// stands for the current environment.
 -
 -typedef int32_t envid_t;
 -
 -#define LOG2NENV              10
 -#define NENV                  (1 << LOG2NENV)
 -#define ENVX(envid)           ((envid) & (NENV - 1))
 -
  // TODO: clean this up.
  struct Env {
        TAILQ_ENTRY(Env) proc_link NOINIT;      // Free list link pointers
        spinlock_t proc_lock;
-       trapframe_t env_tf                                              // Saved registers
-         __attribute__((aligned (8)));                 // for sparc --asw
-       ancillary_state_t env_ancillary_state   // State saved when descheduled
-         __attribute__((aligned (8)));                 // for sparc --asw
+       trapframe_t env_tf;                                             // Saved registers
+       ancillary_state_t env_ancillary_state;  // State saved when descheduled
 -      envid_t env_id;                         // Unique environment identifier
 -      envid_t env_parent_id;          // env_id of this env's parent
 +      pid_t pid;
 +      pid_t ppid;                 // Parent's PID
        uint32_t state;                         // Status of the process
 -      uint32_t env_runs;                      // Number of times environment has run
        uint32_t env_refcnt;            // Reference count of kernel contexts using this
        uint32_t env_flags;
        uint32_t env_entry;
        int32_t vcoremap[MAX_NUM_CPUS];
        uint32_t num_vcores;
  
+       /* Cache color map: bitmap of the cache colors currently allocated to this
+        * process */
+       uint8_t* cache_colors_map;
+       size_t next_cache_color;
        /* Info about this process's resources (granted, desired) for each type. */
        struct resource resources[MAX_NUM_RESOURCES];
  
 -      void* end_text_segment;
 -      void* end_data_segment;
+       /* Keeps track of this process's current memory allocation 
+      * (i.e. its heap pointer) */
++      void* heap_bottom;
++      void* heap_top;
        // Address space
        pde_t *COUNT(NPDENTRIES) env_pgdir;                     // Kernel virtual address of page dir
        physaddr_t env_cr3;                     // Physical address of page dir
  };
  
  /* Process Flags */
 -// None yet
 +#define PROC_TRANSITION_TO_M                  0x0001
  
 -extern env_t *CT(NENV) RO envs;               // All environments
  extern atomic_t num_envs;             // Number of envs
 -// TODO: consider moving this to struct per_cpu_info
 -extern env_t * (RO curenvs)[MAX_NUM_CPUS];
 -
 -static inline env_t *
 -get_cpu_curenv() TRUSTED
 -{
 -      return curenvs[core_id()];
 -}
 -
 -static inline void
 -set_cpu_curenv(env_t *p) TRUSTED
 -{
 -      curenvs[core_id()] = p;
 -}
 -
 -void  env_init(void);
 -int           env_alloc(env_t *SAFE*SAFE e, envid_t parent_id);
 +
 +int           env_setup_vm(env_t *e);
- void  load_icode(env_t *SAFE e, uint8_t *COUNT(size) binary, size_t size);
  void  env_push_ancillary_state(env_t* e);
  void  env_pop_ancillary_state(env_t* e);
 -void  env_free(env_t *SAFE e);
  void  env_user_mem_free(env_t* e);
 -env_t*        env_create();
+ void  env_segment_alloc(env_t *e, void *SNT va, size_t len);
+ void  env_segment_free(env_t *e, void *SNT va, size_t len);
+ void  env_load_icode(env_t* e, env_t* binary_env, uint8_t *COUNT(size) binary, size_t size);
  
 -/*
 - * Allows the kernel to figure out what process is running on its core.
 - * Can be used just like a pointer to a struct process.
 - */
 -#define current (get_cpu_curenv())
 -//#define current (curenvs[core_id()])
 -
 -int   envid2env(envid_t envid, env_t **env_store, bool checkperm);
  // The following three functions do not return
  void  env_pop_tf(trapframe_t *tf) __attribute__((noreturn));
  
@@@ -13,6 -13,7 +13,7 @@@
  #include <ros/error.h>
  #include <arch/mmu.h>
  #include <colored_page_alloc.h>
+ #include <process.h>
  
  /****************** Page Structures *********************/
  struct Page;
@@@ -31,25 -32,30 +32,30 @@@ struct Page 
  
  
  /******** Externally visible global variables ************/
- extern uint16_t RO llc_num_colors;
+ extern uint8_t* global_cache_colors_map;
  extern spinlock_t colored_page_free_list_lock;
  extern page_list_t LCKD(&colored_page_free_list_lock) * RO CT(llc_num_colors)
      colored_page_free_list;
  
  /*************** Functional Interface *******************/
  void page_alloc_init(void);
- error_t page_alloc(page_t *SAFE *page);
+ void colored_page_alloc_init(void);
+ error_t upage_alloc(struct proc* p, page_t *SAFE *page);
+ error_t kpage_alloc(page_t *SAFE *page);
+ error_t upage_alloc_specific(struct proc* p, page_t *SAFE *page, size_t ppn);
+ error_t kpage_alloc_specific(page_t *SAFE *page, size_t ppn);
+ error_t colored_upage_alloc(uint8_t* map, page_t *SAFE *page, size_t color);
+ error_t page_free(page_t *SAFE page);
 -void *get_cont_pages(size_t order, int flags);
 +void *CT(1 << order) get_cont_pages(size_t order, int flags);
  void free_cont_pages(void *buf, size_t order);
- error_t page_alloc_specific(page_t *SAFE *page, size_t ppn);
- error_t l1_page_alloc(page_t *SAFE *page, size_t color);
- error_t l2_page_alloc(page_t *SAFE *page, size_t color);
- error_t l3_page_alloc(page_t *SAFE *page, size_t color);
- error_t page_free(page_t *SAFE page);
  void page_incref(page_t *SAFE page);
  void page_decref(page_t *SAFE page);
  size_t page_getref(page_t *SAFE page);
  void page_setref(page_t *SAFE page, size_t val);
  int page_is_free(size_t ppn);
  
  #endif //PAGE_ALLOC_H
diff --combined kern/include/process.h
@@@ -39,6 -39,8 +39,6 @@@
  #define PROC_DYING                            0x10
  #define PROC_RUNNABLE_M                       0x20
  #define PROC_RUNNING_M                        0x40
 -// TODO don't use this shit for process allocation flagging
 -#define ENV_FREE                              0x80
  
  #include <env.h>
  
  
  TAILQ_HEAD(proc_list, proc);          // Declares 'struct proc_list'
  
 -extern spinlock_t freelist_lock;
 -extern struct proc_list LCKD(&freelist_lock)proc_freelist;
 -
  extern spinlock_t runnablelist_lock;
  extern struct proc_list LCKD(&runnablelist_lock) proc_runnablelist;
  
 +/* Can use a htable iterator to iterate through all active procs */
 +extern struct hashtable *pid_hash;
 +extern spinlock_t pid_hash_lock;
 +
  /* Idle cores: ones able to be exclusively given to a process (worker cores). */
  extern spinlock_t idle_lock;  // never grab this before a proc_lock
  extern uint32_t LCKD(&idle_lock) (RO idlecoremap)[MAX_NUM_CPUS];
  extern uint32_t LCKD(&idle_lock) num_idlecores;
  
 +/* Initialization */
 +void proc_init(void);
 +
  /* Process management: */
 -int proc_set_state(struct proc *p, uint32_t state) WRITES(p->state);
 -struct proc *get_proc(unsigned pid);
 +struct proc *proc_create(uint8_t *COUNT(size) binary, size_t size);
 +int __proc_set_state(struct proc *p, uint32_t state) WRITES(p->state);
 +struct proc *pid2proc(pid_t pid);
  bool proc_controls(struct proc *SAFE actor, struct proc *SAFE target);
 -/* Transition from RUNNABLE_* to RUNNING_*. */
  void proc_run(struct proc *SAFE p);
  void proc_startcore(struct proc *SAFE p, trapframe_t *SAFE tf)
       __attribute__((noreturn));
  void proc_destroy(struct proc *SAFE p);
  void proc_yield(struct proc *SAFE p);
  
+ /* argc/argv.  TODO: figure out how to move this out of the kernel. */
+ size_t proc_init_argc_argv(struct proc* p, size_t nargs, const char** args);
  /* Process core management.  Only call these if you are RUNNING_M or RUNNABLE_M.
   * These all adjust the vcoremap and take appropriate actions (like __startcore
   * if you were already RUNNING_M.  You could be RUNNABLE_M with no vcores when
   * these are done (basically preempted, and waiting to get run again).
 - * All of these could modify corelist and *num to communicate info back out,
 - * which would be the list of cores that are known to be free.
 + *
 + * These are internal functions.  Error checking is to catch bugs, and you
 + * shouldn't call these functions with parameters you are not sure about (like
 + * an invalid corelist).  
 + *
 + * They also may cause an IPI to be sent to core it is called on.  If so, the
 + * return value will be true.  Once you unlock (and enable interrupts) you will
 + * be preempted, and usually lose your stack.  There is a helper to unlock and
 + * handle the refcnt.
   *
   * WARNING: YOU MUST HOLD THE PROC_LOCK BEFORE CALLING THESE! */
  /* Gives process p the additional num cores listed in corelist */
 -error_t proc_give_cores(struct proc *SAFE p, uint32_t corelist[], size_t *num);
 -/* Makes process p's coremap look like corelist (add, remove, etc) */
 -error_t proc_set_allcores(struct proc *SAFE p, uint32_t corelist[], size_t *num,
 -                          amr_t message);
 +bool __proc_give_cores(struct proc *SAFE p, int32_t *corelist, size_t num);
 +/* Makes process p's coremap look like corelist (add, remove, etc). Not used */
 +bool __proc_set_allcores(struct proc *SAFE p, int32_t *corelist,
 +                         size_t *num, amr_t message, TV(a0t) arg0,
 +                         TV(a1t) arg1, TV(a2t) arg2);
  /* Takes from process p the num cores listed in corelist */
 -error_t proc_take_cores(struct proc *SAFE p, uint32_t corelist[], size_t *num,
 -                        amr_t message);
 -error_t proc_take_allcores(struct proc *SAFE p, amr_t message);
 -
 -/* Arch Specific */
 -void proc_init_trapframe(trapframe_t *SAFE tf);
 -void proc_set_program_counter(trapframe_t *SAFE tf, uintptr_t pc);
 -void proc_set_tfcoreid(trapframe_t *SAFE tf, uint32_t id);
 -void proc_set_syscall_retval(trapframe_t *SAFE tf, intreg_t value);
 -
 -/* The reference counts are mostly to track how many cores loaded the cr3 */
 -error_t proc_incref(struct proc *SAFE p);
 -void proc_decref(struct proc *SAFE p);
 +bool __proc_take_cores(struct proc *SAFE p, int32_t *corelist,
 +                       size_t num, amr_t message, TV(a0t) arg0,
 +                       TV(a1t) arg1, TV(a2t) arg2);
 +bool __proc_take_allcores(struct proc *SAFE p, amr_t message, TV(a0t) arg0,
 +                          TV(a1t) arg1, TV(a2t) arg2);
 +void __proc_unlock_ipi_pending(struct proc *p, bool ipi_pending);
 +
 +/* Will probably have generic versions of these later. */
 +void proc_incref(struct proc *SAFE p, size_t count);
 +void proc_decref(struct proc *SAFE p, size_t count);
 +
 +/* Allows the kernel to figure out what process is running on this core.  Can be
 + * used just like a pointer to a struct proc.  Need these to be macros due to
 + * some circular dependencies with smp.h. */
 +#include <smp.h>
 +#define current per_cpu_info[core_id()].cur_proc
 +#define set_current_proc(p) per_cpu_info[core_id()].cur_proc = (p)
 +
 +/* Allows the kernel to figure out what tf is on this core's stack.  Can be used
 + * just like a pointer to a struct Trapframe.  Need these to be macros due to
 + * some circular dependencies with smp.h.  This is done here instead of
 + * elsewhere (like trap.h) for other elliptical reasons. */
 +#define current_tf per_cpu_info[core_id()].cur_tf
 +#define set_current_tf(tf) per_cpu_info[core_id()].cur_tf = (tf)
  
  void abandon_core(void);
  
@@@ -135,15 -117,8 +138,15 @@@ void __death(trapframe_t *tf, uint32_t 
               void * a2);
  #endif
  
 +/* Arch Specific */
 +void proc_set_program_counter(trapframe_t *SAFE tf, uintptr_t pc);
 +void proc_init_trapframe(trapframe_t *SAFE tf);
 +void proc_set_tfcoreid(trapframe_t *SAFE tf, uint32_t id);
 +void proc_set_syscall_retval(trapframe_t *SAFE tf, intreg_t value);
 +
  /* Degubbing */
  void print_idlecoremap(void);
 +void print_allpids(void);
  void print_proc_info(pid_t pid);
  
  #endif // !ROS_KERN_PROCESS_H
@@@ -9,8 -9,16 +9,17 @@@
  #include <ros/error.h>
  #include <ros/common.h>
  
+ #define PROCINFO_MAX_ARGC 32
+ #define PROCINFO_MAX_ARGV_SIZE 1024
  typedef struct procinfo {
 -      pid_t id;
 +      pid_t pid;
+       size_t max_harts;
++      // Temp way to pass arguments to a new process
+       size_t argc;
+       char* argv[PROCINFO_MAX_ARGC];
+       char argv_buf[PROCINFO_MAX_ARGV_SIZE];
  } procinfo_t;
  #define PROCINFO_NUM_PAGES  ((sizeof(procinfo_t)-1)/PGSIZE + 1)       
  
diff --combined kern/include/testing.h
@@@ -13,6 -13,7 +13,7 @@@ void test_pic_reception(void)
  void test_ioapic_pit_reroute(void);
  void test_print_info(void);
  void test_page_coloring(void);
+ void test_color_alloc(void);
  void test_barrier(void);
  void test_interrupts_irqsave(void);
  void test_bitmasks(void);
@@@ -25,7 -26,6 +26,7 @@@ void test_circ_buffer(void)
  void test_active_messages(void);
  void test_slab(void);
  void test_kmalloc(void);
 +void test_hashtable(void);
  
  struct trapframe_t;
  
diff --combined kern/src/Makefrag
@@@ -15,7 -15,11 +15,7 @@@ KERN_SRCFILES := $(KERN_ARCH_SRCFILES) 
                   $(KERN_SRC_DIR)/init.c \
                   $(KERN_SRC_DIR)/monitor.c \
                   $(KERN_SRC_DIR)/printf.c \
 -                 $(KERN_SRC_DIR)/trap.c \
 -                 $(KERN_SRC_DIR)/trapentry.S \
                   $(KERN_SRC_DIR)/sched.c \
 -                 $(KERN_SRC_DIR)/kdebug.c \
 -                 $(KERN_SRC_DIR)/apic.c \
                   $(KERN_SRC_DIR)/printfmt.c \
                   $(KERN_SRC_DIR)/smp.c \
                   $(KERN_SRC_DIR)/multiboot.c \
@@@ -34,6 -38,7 +34,6 @@@
                   $(KERN_SRC_DIR)/process.c \
                   $(KERN_SRC_DIR)/kmalloc.c \
                   $(KERN_SRC_DIR)/hashtable.c \
 -                 $(KERN_SRC_DIR)/hashtable_itr.c \
                   $(KERN_SRC_DIR)/schedule.c \
                   $(KERN_SRC_DIR)/mm.c \
                   $(KERN_SRC_DIR)/resource.c \
@@@ -50,15 -55,31 +50,32 @@@ KERN_APPFILES := 
                   $(USER_APPS_ROSLIB_DIR)/spawn \
                   $(USER_APPS_ROSLIB_DIR)/hello \
                   $(USER_APPS_ROSLIB_DIR)/mhello \
 +                 $(USER_APPS_ROSLIB_DIR)/mproctests \
                   $(USER_APPS_ROSLIB_DIR)/measurements \
+                  $(USER_APPS_PARLIB_PTHREAD_DIR)/pthread_test \
+                  $(USER_APPS_PARLIB_PTHREAD_DIR)/blackscholes \
                   $(USER_APPS_PARLIB_DIR)/draw_nanwan_standalone \
                   $(USER_APPS_PARLIB_DIR)/channel_test_client \
                   $(USER_APPS_PARLIB_DIR)/channel_test_server \
                   $(USER_APPS_PARLIB_DIR)/hello \
+                  $(USER_APPS_PARLIB_DIR)/manycore_test \
                   $(USER_APPS_PARLIB_DIR)/matrix
  #                 $(USER_APPS_PARLIB_DIR)/open_read
  
+ ifeq ($(TARGET_ARCH),sparc)
+ KERN_APPFILES += \
+                  $(USER_APPS_USR_DIR)/blackscholes \
+                  $(USER_APPS_USR_DIR)/streamcluster \
+                  $(USER_APPS_USR_DIR)/swaptions \
+                  $(USER_APPS_USR_DIR)/bodytrack \
+                  $(USER_APPS_USR_DIR)/fluidanimate \
+                  $(USER_APPS_USR_DIR)/x264 \
+                  $(USER_APPS_USR_DIR)/cpptest \
+                  $(USER_APPS_USR_DIR)/condtest \
+                  $(USER_APPS_USR_DIR)/synthetic_procobv_cacheobv_offbwobv \
+                  $(USER_APPS_USR_DIR)/synthetic_procplus_cacheplus_offbwplus 
+ endif
  KERN_LDFLAGS   := $(KERN_LDFLAGS) -L$(OBJDIR)/$(KERN_DIR) \
                    -T $(KERN_ARCH_SRC_DIR)/kernel.ld
  
@@@ -76,7 -97,7 +93,7 @@@ KERN_LDDEPENDS := $(KERN_OBJFILES) $(KE
  
  KERN_LDLIBS    := -livykern
  
- KERN_GCC_LIB   := $(GCC_LIB)
+ KERN_GCC_LIB   ?= $(GCC_LIB)
  
  $(OBJDIR)/$(KERN_DIR)/%.o: $(KERN_DIR)/%.c
        @echo + cc [KERN] $<
  $(OBJDIR)/$(KERN_DIR)/%.o: $(KERN_DIR)/%.S
        @echo + as [KERN] $<
        @mkdir -p $(@D)
-       $(V)$(CC) $(KERN_CFLAGS) -c -o $@ $<
+       $(V)$(CC) $(KERN_CFLAGS) -D__ASSEMBLER__ -c -o $@ $<
  
  $(OBJDIR)/$(KERN_DIR)/kernel: $(KERN_LDDEPENDS)
        @echo + ld [KERN] $@
        $(V)$(OBJDUMP) -S $@ > $@.asm
        $(V)$(NM) -n $@ > $@.sym
  
+ all: $(OBJDIR)/$(KERN_DIR)/kernel
  #$(OBJDIR)/$(KERN_DIR)/bochs.img: $(OBJDIR)/$(KERN_DIR)/kernel $(OBJDIR)/$(KERN_DIR)/boot
  #     @echo + mk [KERN] $@
  #     $(V)dd if=/dev/zero of=$(OBJDIR)/$(KERN_DIR)/bochs.img~ count=10080 2>/dev/null
  #     $(V)dd if=$(OBJDIR)/$(KERN_DIR)/kernel of=$(OBJDIR)/$(KERN_DIR)/bochs.img~ seek=1 conv=notrunc 2>/dev/null
  #     $(V)mv $(OBJDIR)/kern/bochs.img~ $(OBJDIR)/kern/bochs.img
  
- all: $(OBJDIR)/$(KERN_DIR)/kernel
+ ifeq ($(AUGMENT_GCC),TRUE)
+ all: augment-gcc
+ endif
diff --combined kern/src/env.c
@@@ -6,6 -6,7 +6,7 @@@
  
  #include <arch/arch.h>
  #include <arch/mmu.h>
+ #include <arch/bitmask.h>
  #include <elf.h>
  #include <smp.h>
  
  #include <manager.h>
  #include <stdio.h>
  #include <schedule.h>
+ #include <kmalloc.h>
  
  #include <ros/syscall.h>
  #include <ros/error.h>
  
 -env_t *envs = NULL;           // All environments
  atomic_t num_envs;
 -// TODO: make this a struct of info including the pointer and cacheline-align it
 -// This lets the kernel know what process is running on the core it traps into.
 -// A lot of the Env business, including this and its usage, will change when we
 -// redesign the env as a multi-process.
 -env_t* (RO curenvs)[MAX_NUM_CPUS] = {[0 ... (MAX_NUM_CPUS-1)] NULL};
  
--#define ENVGENSHIFT   12              // >= LOGNENV
 -
 -//
 -// Converts an envid to an env pointer.
 -//
 -// RETURNS
 -//   0 on success, -EBADENV on error.
 -//   On success, sets *env_store to the environment.
 -//   On error, sets *env_store to NULL.
 -//
 -int
 -envid2env(envid_t envid, env_t **env_store, bool checkperm)
 -{
 -      env_t *e;
 -
 -      // If envid is zero, return the current environment.
 -      if (envid == 0) {
 -              *env_store = current;
 -              return 0;
 -      }
 -
 -      // Look up the Env structure via the index part of the envid,
 -      // then check the env_id field in that env_t
 -      // to ensure that the envid is not stale
 -      // (i.e., does not refer to a _previous_ environment
 -      // that used the same slot in the envs[] array).
 -      e = &envs[ENVX(envid)];
 -      if (e->state == ENV_FREE || e->env_id != envid) {
 -              *env_store = 0;
 -              return -EBADENV;
 -      }
 -
 -      // Check that the calling environment has legitimate permission
 -      // to manipulate the specified environment.
 -      // If checkperm is set, the specified environment
 -      // must be either the current environment
 -      // or an immediate child of the current environment.
 -      // TODO: should check for current being null
 -      if (checkperm && e != current && e->env_parent_id != current->env_id) {
 -              *env_store = 0;
 -              return -EBADENV;
 -      }
 -
 -      *env_store = e;
 -      return 0;
 -}
 -
 -//
 -// Mark all environments in 'envs' as free, set their env_ids to 0,
 -// and insert them into the proc_freelist.
 -// Insert in reverse order, so that the first call to env_alloc()
 -// returns envs[0].
 -// TODO: get rid of this whole array bullshit
 -//
 -void
 -env_init(void)
 -{
 -      int i;
 -
 -      schedule_init();
 -      // core 0 is not idle, all others are (for now)
 -      spin_lock(&idle_lock);
 -      num_idlecores = num_cpus; // hack to use all cores
 -      for (i = 0; i < num_idlecores; i++)
 -              idlecoremap[i] = i; // hack to use all cores
 -      spin_unlock(&idle_lock);
 -      atomic_init(&num_envs, 0);
 -      TAILQ_INIT(&proc_freelist);
 -      assert(envs != NULL);
 -      for (i = NENV-1; i >= 0; i--) {
 -              // these should already be set from when i memset'd the array to 0
 -              envs[i].state = ENV_FREE;
 -              envs[i].end_text_segment = (void*)UTEXT;
 -              envs[i].end_data_segment = (void*)UTEXT;
 -              envs[i].env_id = 0;
 -              TAILQ_INSERT_HEAD(&proc_freelist, &envs[i], proc_link);
 -      }
 -}
--
--//
  // Initialize the kernel virtual memory layout for environment e.
  // Allocate a page directory, set e->env_pgdir and e->env_cr3 accordingly,
  // and initialize the kernel portion of the new environment's address space.
  // Returns 0 on success, < 0 on error.  Errors include:
  //    -ENOMEM if page directory or table could not be allocated.
  //
 -static int
 -env_setup_vm(env_t *e)
 +int env_setup_vm(env_t *e)
  WRITES(e->env_pgdir, e->env_cr3, e->env_procinfo, e->env_procdata)
  {
        int i, r;
         * First, allocate a page for the pgdir of this process and up
         * its reference count since this will never be done elsewhere
         */
-       r = page_alloc(&pgdir);
+       r = kpage_alloc(&pgdir);
        if(r < 0) return r;
-       page_incref(pgdir);
  
        /*
         * Next, set up the e->env_pgdir and e->env_cr3 pointers to point
  
        // VPT and UVPT map the env's own page table, with
        // different permissions.
-       e->env_pgdir[PDX(VPT)]  = PTE(PPN(e->env_cr3), PTE_P | PTE_KERN_RW);
-       e->env_pgdir[PDX(UVPT)] = PTE(PPN(e->env_cr3), PTE_P | PTE_USER_RO);
+       e->env_pgdir[PDX(VPT)]  = PTE(LA2PPN(e->env_cr3), PTE_P | PTE_KERN_RW);
+       e->env_pgdir[PDX(UVPT)] = PTE(LA2PPN(e->env_cr3), PTE_P | PTE_USER_RO);
  
        /*
         * Now allocate and insert all pages required for the shared
         * procinfo structure into the page table
         */
        for(int i=0; i<PROCINFO_NUM_PAGES; i++) {
-               if(page_alloc(&pginfo[i]) < 0)
+               if(upage_alloc(e, &pginfo[i]) < 0)
                        goto env_setup_vm_error;
                if(page_insert(e->env_pgdir, pginfo[i], (void*SNT)(UINFO + i*PGSIZE),
                               PTE_USER_RO) < 0)
         * procdata structure into the page table
         */
        for(int i=0; i<PROCDATA_NUM_PAGES; i++) {
-               if(page_alloc(&pgdata[i]) < 0)
+               if(upage_alloc(e, &pgdata[i]) < 0)
                        goto env_setup_vm_error;
                if(page_insert(e->env_pgdir, pgdata[i], (void*SNT)(UDATA + i*PGSIZE),
                               PTE_USER_RW) < 0)
         * (TODO).  Note the page is alloced only the first time through
         */
        if (!shared_page) {
-               if(page_alloc(&shared_page) < 0)
+               if(upage_alloc(e, &shared_page) < 0)
                        goto env_setup_vm_error;
                // Up it, so it never goes away.  One per user, plus one from page_alloc
                // This is necessary, since it's in the per-process range of memory that
@@@ -147,15 -232,147 +145,57 @@@ env_setup_vm_error
        return -ENOMEM;
  }
  
- //
+ static void
+ proc_init_procinfo(struct proc* p)
+ {
 -      p->env_procinfo->id = (p->env_id & 0x3FF);
++      p->env_procinfo->pid = p->pid;
+       // TODO: maybe do something smarter here
+       p->env_procinfo->max_harts = MAX(1,num_cpus); // hack to use all cores
+ }
+ // Sets up argc/argv in procinfo.  Returns number of
+ // args successfully imported (because of size restrictions).
+ // The procinfo pages must have been mapped into the user's
+ // address space before this function can be called.
+ size_t
+ proc_init_argc_argv(struct proc* p, size_t nargs, const char** args)
+ {
+       // TODO: right now we assume procinfo can be directly addressed
+       // by the kernel (i.e. it's continguous.
+       static_assert(sizeof(struct procinfo) <= PGSIZE);
+       if(nargs > PROCINFO_MAX_ARGC)
+               nargs = PROCINFO_MAX_ARGC;
+       char* argv[PROCINFO_MAX_ARGC] = {0};
+       static_assert(sizeof(argv) == sizeof(p->env_procinfo->argv));
+       size_t size = 0, argc;
+       for(argc = 0; argc < nargs; argc++)
+       {
+               size_t len = strnlen(args[argc],PROCINFO_MAX_ARGV_SIZE);
+               if(size+len+1 > PROCINFO_MAX_ARGV_SIZE)
+                       break;
+               memcpy(&p->env_procinfo->argv_buf[size],args[argc],len+1);
+               argv[argc] = (char*)(UINFO+offsetof(struct procinfo,argv_buf)+size);
+               size += len+1;
+       }
+       p->env_procinfo->argc = argc;
+       memcpy(p->env_procinfo->argv,argv,sizeof(argv));
+       return argc;
+ }
 -//
 -// Allocates and initializes a new environment.
 -// On success, the new environment is stored in *newenv_store.
 -//
 -// Returns 0 on success, < 0 on failure.  Errors include:
 -//    -ENOFREEENV if all NENVS environments are allocated
 -//    -ENOMEM on memory exhaustion
 -//
 -int
 -env_alloc(env_t **newenv_store, envid_t parent_id)
 -{
 -      int32_t generation;
 -      int r;
 -      env_t *e;
 -
 -      spin_lock(&freelist_lock);
 -      e = TAILQ_FIRST(&proc_freelist);
 -      if (e) {
 -              TAILQ_REMOVE(&proc_freelist, e, proc_link);
 -              spin_unlock(&freelist_lock);
 -      } else {
 -              spin_unlock(&freelist_lock);
 -              return -ENOFREEENV;
 -      }
 -
 -    { INITSTRUCT(*e)
 -
 -      // Setup the default map of where to get cache colors from
 -      e->cache_colors_map = global_cache_colors_map;
 -      e->next_cache_color = 0;
 -
 -      // Allocate and set up the page directory for this environment.
 -      if ((r = env_setup_vm(e)) < 0) {
 -              spin_lock(&freelist_lock);
 -              TAILQ_INSERT_HEAD(&proc_freelist, e, proc_link);
 -              spin_unlock(&freelist_lock);
 -              return r;
 -      }
 -
 -      // Generate an env_id for this environment.
 -      generation = (e->env_id + (1 << ENVGENSHIFT)) & ~(NENV - 1);
 -      if (generation <= 0)    // Don't create a negative env_id.
 -              generation = 1 << ENVGENSHIFT;
 -      e->env_id = generation | (e - envs);
 -
 -      // Set the basic status variables.
 -      e->proc_lock = 0;
 -      e->env_parent_id = parent_id;
 -      proc_set_state(e, PROC_CREATED);
 -      e->env_runs = 0;
 -      e->env_refcnt = 1;
 -      e->env_flags = 0;
 -      e->env_entry = 0; // cheating.  this really gets set in load_icode
 -      e->num_vcores = 0;
 -      for (int i = 0; i < MAX_NUM_CPUS; i++)
 -              e->vcoremap[i] = -1;
 -      memset(&e->resources, 0, sizeof(e->resources));
 -
 -      memset(&e->env_ancillary_state, 0, sizeof(e->env_ancillary_state));
 -      memset(&e->env_tf, 0, sizeof(e->env_tf));
 -      proc_init_trapframe(&e->env_tf);
 -
 -      proc_init_procinfo(e);
 -
 -      /*
 -       * Initialize the contents of the e->env_procdata structure
 -       */
 -      // Initialize the generic syscall ring buffer
 -      SHARED_RING_INIT(&e->env_procdata->syscallring);
 -      // Initialize the backend of the syscall ring buffer
 -      BACK_RING_INIT(&e->syscallbackring,
 -                     &e->env_procdata->syscallring,
 -                     SYSCALLRINGSIZE);
 -
 -      // Initialize the generic sysevent ring buffer
 -      SHARED_RING_INIT(&e->env_procdata->syseventring);
 -      // Initialize the frontend of the sysevent ring buffer
 -      FRONT_RING_INIT(&e->syseventfrontring,
 -                      &e->env_procdata->syseventring,
 -                      SYSEVENTRINGSIZE);
 -
 -      *newenv_store = e;
 -      atomic_inc(&num_envs);
 -
 -      printk("[%08x] new env %08x\n", current ? current->env_id : 0, e->env_id);
 -      } // INIT_STRUCT
 -      return 0;
 -}
 -
 -//
  // Allocate len bytes of physical memory for environment env,
  // and map it at virtual address va in the environment's address space.
- // Does not zero or otherwise initialize the mapped pages in any way.
+ // Pages are zeroed by upage_alloc.
  // Pages should be writable by user and kernel.
  // Panic if any allocation attempt fails.
  //
static void
- segment_alloc(env_t *e, void *SNT va, size_t len)
+ void
env_segment_alloc(env_t *e, void *SNT va, size_t len)
  {
        void *SNT start, *SNT end;
        size_t num_pages;
                panic("Wrap-around in memory allocation addresses!");
        if ((uintptr_t)end > UTOP)
                panic("Attempting to map above UTOP!");
-       // page_insert/pgdir_walk alloc a page and read/write to it via its address
-       // starting from pgdir (e's), so we need to be using e's pgdir
-       assert(e->env_cr3 == rcr3());
-       num_pages = PPN(end - start);
+       num_pages = LA2PPN(end - start);
  
        for (i = 0; i < num_pages; i++, start += PGSIZE) {
                // skip if a page is already mapped.  yes, page_insert will page_remove
                pte = pgdir_walk(e->env_pgdir, start, 0);
                if (pte && *pte & PTE_P)
                        continue;
-               if ((r = page_alloc(&page)) < 0)
-                       panic("segment_alloc: %e", r);
+               if ((r = upage_alloc(e, &page)) < 0)
+                       panic("env_segment_alloc: %e", r);
                page_insert(e->env_pgdir, page, start, PTE_USER_RW);
        }
  }
  
 -static error_t
 -load_icode_memcpy(env_t* e, env_t* binary_env, void* dest, const void* src, size_t len)
+ void
+ env_segment_free(env_t *e, void *SNT va, size_t len)
+ {
+       void *SNT start, *SNT end;
+       size_t num_pages;
+       page_t *page;
+       pte_t *pte;
+       // Round this up this time so we don't free the page that va is actually on
+       start = ROUNDUP(va, PGSIZE);
+       end = ROUNDUP(va + len, PGSIZE);
+       if (start >= end)
+               panic("Wrap-around in memory free addresses!");
+       if ((uintptr_t)end > UTOP)
+               panic("Attempting to unmap above UTOP!");
+       // page_insert/pgdir_walk alloc a page and read/write to it via its address
+       // starting from pgdir (e's), so we need to be using e's pgdir
+       assert(e->env_cr3 == rcr3());
+       num_pages = LA2PPN(end - start);
+       for (int i = 0; i < num_pages; i++, start += PGSIZE) {
+               // skip if a page is already unmapped. 
+               pte = pgdir_walk(e->env_pgdir, start, 0);
+               if (pte && *pte & PTE_P)
+                       page_remove(e->env_pgdir,start);
+       }
+ }
+ // this helper function handles all cases of copying to/from user/kernel
+ // or between two users.
 -              if(binary_env == NULL)
++static error_t load_icode_memcpy(struct proc *dest_p, struct proc *src_p,
++                                 void* dest, const void* src, size_t len)
+ {
+       if(src < (void*)UTOP)
+       {
 -              if(e == NULL)
 -                      return memcpy_from_user(binary_env,dest,src,len);
++              if(src_p == NULL)
+                       return -EFAULT;
 -                              if(memcpy_from_user(binary_env,kbuf,src,thislen))
++              if(dest_p == NULL)
++                      return memcpy_from_user(src_p, dest, src, len);
+               else
+               {
+                       // TODO: do something more elegant & faster here.
+                       // e.g. a memcpy_from_user_to_user
+                       uint8_t kbuf[1024];
+                       while(len > 0)
+                       {
+                               size_t thislen = MIN(len,sizeof(kbuf));
 -                              if(memcpy_to_user(e,dest,kbuf,thislen))
++                              if (memcpy_from_user(src_p, kbuf, src, thislen))
+                                       return -EFAULT;
 -              if(binary_env != NULL)
++                              if (memcpy_to_user(dest_p, dest, kbuf, thislen))
+                                       panic("destination env isn't mapped!");
+                               len -= thislen;
+                               src += thislen;
+                               dest += thislen;
+                       }
+                       return ESUCCESS;
+               }
+       }
+       else
+       {
 -              if(e == NULL)
 -                      memcpy(dest,src,len);
 -              else if(memcpy_to_user(e,dest,src,len))
++              if(src_p != NULL)
+                       return -EFAULT;
++              if(dest_p == NULL)
++                      memcpy(dest, src, len);
++              else if(memcpy_to_user(dest_p, dest, src, len))
+                       panic("destination env isn't mapped!");
+               return ESUCCESS;
+       }
+ }
  //
  // Set up the initial program binary, stack, and processor flags
  // for a user process.
  // but not actually present in the ELF file - i.e., the program's bss section.
  //
  // Finally, this function maps one page for the program's initial stack.
- void load_icode(env_t *SAFE e, uint8_t *COUNT(size) binary, size_t size)
 -static void*
 -load_icode(env_t *SAFE e, env_t* binary_env, uint8_t *COUNT(size) binary, size_t size)
++static void* load_icode(env_t *SAFE e, env_t* binary_env,
++                        uint8_t *COUNT(size) binary, size_t size)
  {
        // asw: copy the headers because they might not be aligned.
        elf_t elfhdr;
        proghdr_t phdr;
-       memcpy(&elfhdr, binary, sizeof(elfhdr));
+       void* _end = 0;
 -      assert(load_icode_memcpy(NULL,binary_env,&elfhdr, binary, sizeof(elfhdr)) == ESUCCESS);
++      assert(load_icode_memcpy(NULL,binary_env,&elfhdr, binary, sizeof(elfhdr))
++             == ESUCCESS);
  
        int i, r;
  
        // make sure we have proghdrs to load
        assert(elfhdr.e_phnum);
  
-       // to actually access any pages alloc'd for this environment, we
-       // need to have the hardware use this environment's page tables.
-       uintreg_t old_cr3 = rcr3();
-       /*
-        * Even though we'll decref later and no one should be killing us at this
-        * stage, we're still going to wrap the lcr3s with incref/decref.
-        *
-        * Note we never decref on the old_cr3, since we aren't willing to let it
-        * die.  It's also not clear who the previous process is - sometimes it
-        * isn't even a process (when the kernel loads on its own, and not in
-        * response to a syscall).  Probably need to think more about this (TODO)
-        *
-        * This can get a bit tricky if this code blocks (will need to think about a
-        * decref then), if we try to change states, etc.
-        */
-       proc_incref(e, 1);
-       lcr3(e->env_cr3);
        // TODO: how do we do a runtime COUNT?
        {TRUSTEDBLOCK // zra: TRUSTEDBLOCK until validation is done.
        for (i = 0; i < elfhdr.e_phnum; i++) {
-               memcpy(&phdr, binary + elfhdr.e_phoff + i*sizeof(phdr), sizeof(phdr));
+               // copy phdr to kernel mem
+               assert(load_icode_memcpy(NULL,binary_env,&phdr, binary + elfhdr.e_phoff + i*sizeof(phdr), sizeof(phdr)) == ESUCCESS);
                if (phdr.p_type != ELF_PROG_LOAD)
                        continue;
-         // TODO: validate elf header fields!
+               // TODO: validate elf header fields!
                // seg alloc creates PTE_U|PTE_W pages.  if you ever want to change
                // this, there will be issues with overlapping sections
-               segment_alloc(e, (void*SNT)phdr.p_va, phdr.p_memsz);
-               memcpy((void*)phdr.p_va, binary + phdr.p_offset, phdr.p_filesz);
-               memset((void*)phdr.p_va + phdr.p_filesz, 0, phdr.p_memsz - phdr.p_filesz);
+               _end = MAX(_end, (void*)(phdr.p_va + phdr.p_memsz));
+               env_segment_alloc(e, (void*SNT)phdr.p_va, phdr.p_memsz);
+               // copy section to user mem
+               assert(load_icode_memcpy(e,binary_env,(void*)phdr.p_va, binary + phdr.p_offset, phdr.p_filesz) == ESUCCESS);
+               //no need to memclr the remaining p_memsz-p_filesz bytes
+               //because upage_alloc'd pages are zeroed
        }}
  
        proc_set_program_counter(&e->env_tf, elfhdr.e_entry);
        e->env_entry = elfhdr.e_entry;
  
-       // Now map one page for the program's initial stack
-       // at virtual address USTACKTOP - PGSIZE.
-       segment_alloc(e, (void*SNT)(USTACKTOP - PGSIZE), PGSIZE);
+       // Now map USTACK_NUM_PAGES pages for the program's initial stack
+       // starting at virtual address USTACKTOP - USTACK_NUM_PAGES*PGSIZE.
+       env_segment_alloc(e, (void*SNT)(USTACKTOP - USTACK_NUM_PAGES*PGSIZE), 
+                         USTACK_NUM_PAGES*PGSIZE);
+       
+       return _end;
+ }
  
-       // reload the original address space
-       lcr3(old_cr3);
-       proc_decref(e, 1);
 -//
 -// Allocates a new env and loads the named elf binary into it.
 -//
 -env_t* env_create()
 -{
 -      env_t *e;
 -      int r;
 -      envid_t curid;
 -
 -      curid = (current ? current->env_id : 0);
 -      if ((r = env_alloc(&e, curid)) < 0)
 -              panic("env_create: %e", r);
 -
 -      // default PC: will cause page fault if not otherwise set.
 -      proc_set_program_counter(&e->env_tf, 0);
 -      e->end_text_segment = 0;
 -      e->end_data_segment = 0;
 -
 -      return e;
 -}
 -
+ void env_load_icode(env_t* e, env_t* binary_env, uint8_t* binary, size_t size)
+ {
+       /* Load the binary and set the current locations of the elf segments.
+        * All end-of-segment pointers are page aligned (invariant) */
 -      e->end_text_segment = load_icode(e, binary_env, binary, size);
 -      e->end_data_segment = e->end_text_segment;
++      e->heap_bottom = load_icode(e, binary_env, binary, size);
++      e->heap_top = e->heap_bottom;
  }
  
 -//
 -// Frees env e and all memory it uses.
 -//
 -void
 -env_free(env_t *e)
 -{
 -      physaddr_t pa;
 -
 -      // Note the environment's demise.
 -      printk("[%08x] free env %08x\n", current ? current->env_id : 0, e->env_id);
 -      // All parts of the kernel should have decref'd before env_free was called.
 -      assert(e->env_refcnt == 0);
 -
 -      // Free any colors allocated to this process
 -      if(e->cache_colors_map != global_cache_colors_map) {
 -              for(int i=0; i<llc_cache->num_colors; i++)
 -                      cache_color_free(llc_cache, e->cache_colors_map);
 -              cache_colors_map_free(e->cache_colors_map);
 -      }
 -
 -      // Flush all mapped pages in the user portion of the address space
 -      env_user_mem_free(e);
 -
 -      // free the page directory
 -      pa = e->env_cr3;
 -      e->env_pgdir = 0;
 -      e->env_cr3 = 0;
 -      page_decref(pa2page(pa));
 -
 -      // return the environment to the free list
 -      e->state = ENV_FREE;
 -      spin_lock(&freelist_lock);
 -      TAILQ_INSERT_HEAD(&proc_freelist, e, proc_link);
 -      spin_unlock(&freelist_lock);
 -}
 -
 -
  #define PER_CPU_THING(type,name)\
  type SLOCKED(name##_lock) * RWPROTECT name;\
  type SLOCKED(name##_lock) *\
        }\
  }
  
 -
  /* This is the top-half of an interrupt handler, where the bottom half is
   * proc_run (which never returns).  Just add it to the delayed work queue,
   * which (incidentally) can only hold one item at this point.
diff --combined kern/src/init.c
  #include <manager.h>
  #include <testing.h>
  #include <kmalloc.h>
 +#include <hashtable.h>
  
  #include <arch/init.h>
+ #include <arch/bitmask.h>
  #include <slab.h>
+ #include <kfs.h>
  
  // zra: flag for Ivy
  int booting = 1;
@@@ -61,22 -62,26 +63,27 @@@ void kernel_init(multiboot_info_t *mboo
  
        multiboot_print_memory_map((multiboot_info_t*CT(1))KADDR((physaddr_t)mboot_info));
  
--      vm_init();
--
--      cache_init();
--      page_init();
-       page_check();
--      kmem_cache_init();
++      vm_init();                      // Sets up pages tables, turns on paging
++      cache_init();                                   // Determine systems's cache properties
++      page_init();                                    // Initializes free page list, etc
++      kmem_cache_init();              // Sets up slab allocator
        kmalloc_init();
 -      cache_color_alloc_init();
 -      colored_page_alloc_init();
 +      hashtable_init();
++      cache_color_alloc_init();       // Inits data structs
++      colored_page_alloc_init();      // Allocates colors for agnostic processes
+       page_check();
  
        idt_init();
 +      active_msg_init();
        sysenter_init();
        timer_init();
        
        // At this point our boot paths diverge based on arch. 
        arch_init();
+               
+ //    printk("Starting tests....\n");
+ //    test_color_alloc();
+ //    printk("Testing complete....\n");
  
        // zra: let's Ivy know we're done booting
        booting = 0;
@@@ -98,6 -103,9 +105,9 @@@ void _panic(const char *file, int line
        cprintf("\n");
        va_end(ap);
  
+       #ifndef __i386__
+               reboot();
+       #endif
  dead:
        /* break into the kernel monitor, if we're core 0 */
        if (core_id()) {
diff --combined kern/src/kfs.c
@@@ -34,13 -34,28 +34,29 @@@ DECL_PROG(roslib_null)
  DECL_PROG(roslib_spawn);
  DECL_PROG(roslib_hello);
  DECL_PROG(roslib_mhello);
 +DECL_PROG(roslib_mproctests);
  DECL_PROG(roslib_measurements);
  DECL_PROG(parlib_draw_nanwan_standalone);
  DECL_PROG(parlib_channel_test_client);
  DECL_PROG(parlib_channel_test_server);
  DECL_PROG(parlib_hello);
  DECL_PROG(parlib_matrix);
+ DECL_PROG(parlib_manycore_test);
+ DECL_PROG(parlib_pthread_pthread_test);
+ DECL_PROG(parlib_pthread_blackscholes);
+ #ifndef __i386__
+ DECL_PROG(usr_blackscholes);
+ DECL_PROG(usr_streamcluster);
+ DECL_PROG(usr_swaptions);
+ DECL_PROG(usr_bodytrack);
+ DECL_PROG(usr_fluidanimate);
+ DECL_PROG(usr_x264);
+ DECL_PROG(usr_synthetic_procobv_cacheobv_offbwobv);
+ DECL_PROG(usr_synthetic_procplus_cacheplus_offbwplus);
+ DECL_PROG(usr_cpptest);
+ DECL_PROG(usr_condtest);
+ #endif
  
  struct kfs_entry kfs[MAX_KFS_FILES] = {
        KFS_ENTRY(roslib_proctests)
        KFS_ENTRY(roslib_spawn)
        KFS_ENTRY(roslib_hello)
        KFS_ENTRY(roslib_mhello)
 +      KFS_ENTRY(roslib_mproctests)
        KFS_ENTRY(roslib_measurements)
        KFS_ENTRY(parlib_draw_nanwan_standalone)
        KFS_ENTRY(parlib_channel_test_client)
        KFS_ENTRY(parlib_channel_test_server)
        KFS_ENTRY(parlib_hello)
        KFS_ENTRY(parlib_matrix)
+       KFS_ENTRY(parlib_manycore_test)
+       KFS_ENTRY(parlib_pthread_pthread_test)
+       KFS_ENTRY(parlib_pthread_blackscholes)
+ #ifndef __i386__
+       KFS_ENTRY(usr_blackscholes)
+       KFS_ENTRY(usr_streamcluster)
+       KFS_ENTRY(usr_swaptions)
+       KFS_ENTRY(usr_bodytrack)
+       KFS_ENTRY(usr_fluidanimate)
+       KFS_ENTRY(usr_x264)
+       KFS_ENTRY(usr_synthetic_procobv_cacheobv_offbwobv)
+       KFS_ENTRY(usr_synthetic_procplus_cacheplus_offbwplus)
+       KFS_ENTRY(usr_cpptest)
+       KFS_ENTRY(usr_condtest)
+ #endif
  };
  
  ssize_t kfs_lookup_path(char* path)
  /*
   * Creates a process from the file pointed to by the KFS inode (index)
   * This should take a real inode or something to point to the real location,
 - * and env_create shouldn't assume everything is contiguous
 + * and proc_create shouldn't assume everything is contiguous
   */
  struct proc *kfs_proc_create(int kfs_inode)
  {
        if (kfs_inode < 0 || kfs_inode >= MAX_KFS_FILES)
                panic("Invalid kfs_inode.  Check you error codes!");
 -      struct proc* p = env_create();
 -      if(p != NULL)
 -              env_load_icode(p,NULL,kfs[kfs_inode].start, kfs[kfs_inode].size);
 -      return p;
 +      return proc_create(kfs[kfs_inode].start, kfs[kfs_inode].size);
  }
diff --combined kern/src/manager.c
@@@ -10,6 -10,7 +10,7 @@@
  
  #include <ros/common.h>
  #include <smp.h>
+ #include <arch/init.h>
  
  #include <assert.h>
  #include <manager.h>
  #include <kfs.h>
  #include <stdio.h>
  #include <timing.h>
 +#include <resource.h>
 +#include <monitor.h>
+ #include <colored_caches.h>
+ #include <string.h>
  
  /*
   * Currently, if you leave this function by way of proc_run (process_workqueue
   */
  void manager(void)
  {
+       #ifndef DEVELOPER_NAME
+               #define DEVELOPER_NAME brho
+       #endif
+       // LoL
+       #define PASTE(s1,s2) s1 ## s2
+       #define MANAGER_FUNC(dev) PASTE(manager_,dev)
+       void MANAGER_FUNC(DEVELOPER_NAME)(void);
+       MANAGER_FUNC(DEVELOPER_NAME)();
+ }
+ void manager_brho(void)
+ {
        static uint8_t RACY progress = 0;
  
-       struct proc *envs[256];
-       static struct proc *p;
+       static struct proc *envs[256];
+       static struct proc *p ;
  
 +      // for testing taking cores, check in case 1 for usage
        uint32_t corelist[MAX_NUM_CPUS];
        uint32_t num = 3;
  
-       /*
-       // This is a bypass of the standard manager structure, for network use
-       // If enabled, this spawns parlib_matrix, and allows the execution
-       // of a remote binary to function correctly (schedule() call below)
-       if (progress++ == 0) {
-               envs[0] = kfs_proc_create(kfs_lookup_path("parlib_matrix"));
-               __proc_set_state(envs[0], PROC_RUNNABLE_S);
-               proc_run(envs[0]);
-       }
-       schedule();
-       */
        switch (progress++) {
                case 0:
 -                      //p = kfs_proc_create(kfs_lookup_path("roslib_proctests"));
 -                      p = kfs_proc_create(kfs_lookup_path("roslib_mhello"));
 +                      // TODO: need to store the pid for future manager runs, not the *p
 +                      //p = kfs_proc_create(kfs_lookup_path("roslib_mhello"));
 +                      p = kfs_proc_create(kfs_lookup_path("roslib_mproctests"));
 +                      //p = kfs_proc_create(kfs_lookup_path("roslib_spawn"));
                        // being proper and all:
                        spin_lock_irqsave(&p->proc_lock);
 -                      proc_set_state(p, PROC_RUNNABLE_S);
 +                      __proc_set_state(p, PROC_RUNNABLE_S);
                        // normal single-cored way
                        spin_unlock_irqsave(&p->proc_lock);
                        proc_run(p);
 +                      proc_decref(p, 1);
                        #if 0
                        // this is how you can transition to a parallel process manually
                        // make sure you don't proc run first
 -                      proc_set_state(p, PROC_RUNNING_S);
 -                      proc_set_state(p, PROC_RUNNABLE_M);
 +                      __proc_set_state(p, PROC_RUNNING_S);
 +                      __proc_set_state(p, PROC_RUNNABLE_M);
                        p->resources[RES_CORES].amt_wanted = 5;
                        spin_unlock_irqsave(&p->proc_lock);
                        core_request(p);
                        #endif
                        break;
                case 1:
 +                      monitor(0);
                        #if 0
 -                      panic("This is okay");
                        udelay(10000000);
 -                      printk("taking 3 cores from p\n");
 -                      for (int i = 0; i < num; i++)
 -                              corelist[i] = 7-i; // 7, 6, and 5
 -                      spin_lock_irqsave(&p->proc_lock);
 -                      proc_take_cores(p, corelist, &num, __death);
 -                      spin_unlock_irqsave(&p->proc_lock);
 -                      udelay(5000000);
 -                      printk("Killing p\n");
 -                      proc_destroy(p);
 -                      printk("Killed p\n");
 -                      udelay(1000000);
 +                      // this is a ghetto way to test restarting an _M
 +                              printk("\nattempting to ghetto preempt...\n");
 +                              spin_lock_irqsave(&p->proc_lock);
 +                              proc_take_allcores(p, __death);
 +                              __proc_set_state(p, PROC_RUNNABLE_M);
 +                              spin_unlock_irqsave(&p->proc_lock);
 +                              udelay(5000000);
 +                              printk("\nattempting to restart...\n");
 +                              core_request(p); // proc still wants the cores
 +                      panic("This is okay");
 +                      // this tests taking some cores, and later killing an _M
 +                              printk("taking 3 cores from p\n");
 +                              for (int i = 0; i < num; i++)
 +                                      corelist[i] = 7-i; // 7, 6, and 5
 +                              spin_lock_irqsave(&p->proc_lock);
 +                              proc_take_cores(p, corelist, &num, __death);
 +                              spin_unlock_irqsave(&p->proc_lock);
 +                              udelay(5000000);
 +                              printk("Killing p\n");
 +                              proc_destroy(p);
 +                              printk("Killed p\n");
                        panic("This is okay");
  
                        envs[0] = kfs_proc_create(kfs_lookup_path("roslib_hello"));
 -                      proc_set_state(envs[0], PROC_RUNNABLE_S);
 +                      __proc_set_state(envs[0], PROC_RUNNABLE_S);
                        proc_run(envs[0]);
                        break;
                        #endif
--      #ifdef __i386__
                case 2:
                        #if 0
                        panic("Do not panic");
                        break;
                        #endif
                case 3:
--      #else // sparc
--              case 2:
--                      panic("Do not panic");
--                      envs[0] = kfs_proc_create(kfs_lookup_path("roslib_proctests"));
--                      envs[1] = kfs_proc_create(kfs_lookup_path("roslib_proctests"));
--                      envs[2] = kfs_proc_create(kfs_lookup_path("roslib_proctests"));
--                      envs[3] = kfs_proc_create(kfs_lookup_path("roslib_fptest"));
--                      envs[4] = kfs_proc_create(kfs_lookup_path("roslib_fptest"));
--                      envs[4] = kfs_proc_create(kfs_lookup_path("roslib_fptest"));
--                      envs[5] = kfs_proc_create(kfs_lookup_path("roslib_hello"));
--                      envs[6] = kfs_proc_create(kfs_lookup_path("roslib_null"));
--                      proc_run(envs[0]);
--                      break;
--              case 3:
--                      #if 0
--                      // reminder of how to spawn remotely
--                      for (int i = 0; i < 8; i++) {
--                              envs[i] = kfs_proc_create(kfs_lookup_path("roslib_hello"));
-                               __proc_set_state(envs[i], PROC_RUNNABLE_S);
 -                              proc_set_state(envs[i], PROC_RUNNABLE_S);
--                              smp_call_function_single(i, run_env_handler, envs[i], 0);
--                      }
--                      process_workqueue();
--                      #endif
--      #endif
--
                #if 0
                case 4:
                        printk("Beginning Tests\n");
        */
        return;
  }
 -              proc_set_state(envs[0], PROC_RUNNABLE_S);
+ void manager_klueska()
+ {
+       static struct proc *envs[256];
+       static uint8_t progress = 0;
+       if (progress++ == 0) {
+               envs[0] = kfs_proc_create(kfs_lookup_path("parlib_matrix"));
++              __proc_set_state(envs[0], PROC_RUNNABLE_S);
+               proc_run(envs[0]);
+       }
+       schedule();
+       panic("DON'T PANIC");
+ }
+ #ifdef __sparc_v8__
+ static char*
+ itoa(int num, char* buf0, size_t base)
+ {
+       if(base > 16)
+               return NULL;
+       char* buf = buf0;
+       int len = 0, i;
+       if(num < 0)
+       {
+               *buf++ = '-';
+               num = -num;
+       }
+       do {
+               buf[len++] = "0123456789abcdef"[num%base];
+               num /= base;
+       } while(num);
+       for(i = 0; i < len/2; i++)
+       {
+               char temp = buf[i];
+               buf[i] = buf[len-i-1];
+               buf[len-i-1] = temp;
+       }
+       buf[len] = 0;
+       return buf0;
+ }
+ void gsf_set_frame_cycles(int cycles)
+ {
+       store_alternate(26*4,2,cycles);
+ }
+ void gsf_set_partition_credits(int partition, int credits)
+ {
+       store_alternate((32+partition)*4,2,credits);
+ }
+ void gsf_set_core_partition(int core, int partition)
+ {
+       store_alternate((64+core)*4,2,partition);
+ }
+ #endif
+ void manager_waterman()
+ {
+ #ifdef __sparc_v8__
+         static uint8_t progress = 0;
+       if(progress > 0)
+               goto run_some_apps;     
+       #define MAX_APPS 2
+       struct app
+       {
+               int threads;
+               int colors;
+               int credits;
+               int argc;
+               char** argv;
+       };
+       static struct app apps[MAX_APPS];
+       static int napps = 0;
+       // arg format:
+       // #apps [#threads #colors #credits name args] - [#threads ...] - ...
+       assert(argc > 0);
+       napps = atoi(argv[0]);
+       assert(napps <= MAX_APPS);
+       argc--; argv++;
+       for(int a = 0; a < napps; a++)
+       {
+               assert(argc >= 4);
+               apps[a].threads = atoi(argv[0]);
+               apps[a].colors = atoi(argv[1]);
+               apps[a].credits = atoi(argv[2]);
+               argc -= 3; argv += 3;
+               apps[a].argc = 0;
+               apps[a].argv = argv;
+               while(argc)
+               {
+                       argc--;
+                       if(strcmp(*argv++,"-") != 0)
+                               apps[a].argc++;
+                       else
+                               break;
+               }
+               printk("app %d: %d threads, %d colors, %d credits\ncommand line: ",a,apps[a].threads,apps[a].colors,apps[a].credits);
+               for(int i = 0; i < apps[a].argc; i++)
+                       printk("%s ",apps[a].argv[i]);
+               printk("\n");
+       }
+       // DRAM can process requests every 40 cycles.
+       // In a 480-cycle window, this gives us 12 total credits.
+       gsf_set_frame_cycles(482);
+       for(int a = 0, cores_used = 0; a < napps; a++)
+       {
+               gsf_set_partition_credits(a,apps[a].credits);
+               for(int i = 0; i < apps[a].threads; i++, cores_used++)
+                       gsf_set_core_partition(num_cpus-cores_used-1,a);
+       }
+ run_some_apps:
+       ;
+       static struct proc *envs[MAX_APPS];
+       int apps_running = napps;
+       int envs_free[MAX_APPS] = {0};
+       if(progress == napps)
+       {
+               while(apps_running)
+               {
+                       for(int i = 0; i < napps; i++)
+                       {
+                               if(*(volatile uint32_t*)&envs[i]->state == ENV_FREE && !envs_free[i])
+                               {
+                                       envs_free[i] = 1;
+                                       apps_running--;
+                                       printk("Finished application %d at cycle %lld\n", i, read_tsc()); 
+                               }
+                       }
+               }
+               reboot();
+       }
+       else
+       {
+               envs[progress] = kfs_proc_create(kfs_lookup_path(apps[progress].argv[0]));
+               envs[progress]->cache_colors_map = cache_colors_map_alloc();
+               for(int i = 0; i < apps[progress].colors; i++)
+                       assert(cache_color_alloc(llc_cache, envs[progress]->cache_colors_map) == ESUCCESS);
+               proc_set_state(envs[progress], PROC_RUNNABLE_S);
+               if(apps[progress].argc)
+                       proc_init_argc_argv(envs[progress],apps[progress].argc,(const char**)apps[progress].argv);
+               proc_run(envs[progress++]);
+               schedule();
+       }
+ #endif
+       panic("professional bomb technician at work.  if you see me running, try to keep up!");
+ }
diff --combined kern/src/page_alloc.c
  #endif
  
  #include <sys/queue.h>
+ #include <arch/bitmask.h>
  #include <page_alloc.h>
  #include <pmap.h>
  #include <string.h>
+ #include <kmalloc.h>
+ #define l1 (available_caches.l1)
+ #define l2 (available_caches.l2)
+ #define l3 (available_caches.l3)
  
 -static void __page_decref(page_t *page);
 -static void __page_incref(page_t *page);
 +static void __page_decref(page_t *CT(1) page);
++static void __page_incref(page_t *CT(1) page);
  static error_t __page_alloc_specific(page_t** page, size_t ppn);
 -static error_t __page_free(page_t* page);
 +static error_t __page_free(page_t *CT(1) page);
  
+ // Global list of colors allocated to the general purpose memory allocator
+ uint8_t* global_cache_colors_map;
+ void colored_page_alloc_init()
+ {
+       global_cache_colors_map = 
+              kmalloc(BYTES_FOR_BITMASK(llc_cache->num_colors), 0);
+       CLR_BITMASK(global_cache_colors_map, llc_cache->num_colors);
+       cache_color_alloc(llc_cache, global_cache_colors_map);
+       cache_color_alloc(llc_cache, global_cache_colors_map);
+       cache_color_alloc(llc_cache, global_cache_colors_map);
+       cache_color_alloc(llc_cache, global_cache_colors_map);
+       cache_color_alloc(llc_cache, global_cache_colors_map);
+ }
  /**
   * @brief Clear a Page structure.
   *
   * The result has null links and 0 refcount.
   * Note that the corresponding physical page is NOT initialized!
   */
- static void page_clear(page_t *SAFE page)
+ static void __page_clear(page_t *SAFE page)
  {
        memset(page, 0, sizeof(page_t));
  }
  
- error_t page_alloc_from_color_range(page_t** page,  
-                                     uint16_t base_color,
-                                     uint16_t range) {
-       // Find first available color with pages available
-     //  in the proper range
-       int i = base_color;
-       spin_lock_irqsave(&colored_page_free_list_lock);
-       for(i; i<(base_color+range); i++) {
-               if(!LIST_EMPTY(&colored_page_free_list[i]))
-                       break;
-       }
-       // Alocate a page from that color
-       if(i < (base_color+range)) {
-               *page = LIST_FIRST(&colored_page_free_list[i]);
-               LIST_REMOVE(*page, page_link);
-               page_clear(*page);
-               spin_unlock_irqsave(&colored_page_free_list_lock);
-               return ESUCCESS;
-       }
-       spin_unlock_irqsave(&colored_page_free_list_lock);
+ #define __PAGE_ALLOC_FROM_RANGE_GENERIC(page, base_color, range, predicate) \
+       /* Find first available color with pages available */                   \
+     /* in the given range */                                                \
+       int i = base_color;                                                     \
+       for (i; i < (base_color+range); i++) {                                  \
+               if((predicate))                                                     \
+                       break;                                                          \
+       }                                                                       \
+       /* Allocate a page from that color */                                   \
+       if(i < (base_color+range)) {                                            \
+               *page = LIST_FIRST(&colored_page_free_list[i]);                     \
+               LIST_REMOVE(*page, page_link);                                      \
+               __page_clear(*page);                                                \
+               return i;                                                           \
+       }                                                                       \
        return -ENOMEM;
+ static ssize_t __page_alloc_from_color_range(page_t** page,  
+                                            uint16_t base_color,
+                                            uint16_t range) 
+ {
+       __PAGE_ALLOC_FROM_RANGE_GENERIC(page, base_color, range, 
+                        !LIST_EMPTY(&colored_page_free_list[i]));
+ }
+ static ssize_t __page_alloc_from_color_map_range(page_t** page, uint8_t* map, 
+                                               size_t base_color, size_t range)
+ {  
+       __PAGE_ALLOC_FROM_RANGE_GENERIC(page, base_color, range, 
+                   GET_BITMASK_BIT(map, i) && !LIST_EMPTY(&colored_page_free_list[i]))
+ }
+ static ssize_t __colored_page_alloc(uint8_t* map, page_t** page, 
+                                                size_t next_color)
+ {
+       ssize_t ret;
+       if((ret = __page_alloc_from_color_map_range(page, map, 
+                                  next_color, llc_cache->num_colors - next_color)) < 0)
+               ret = __page_alloc_from_color_map_range(page, map, 0, next_color);
+       return ret;
+ }
+ /* Internal version of page_alloc_specific.  Grab the lock first. */
+ static error_t __page_alloc_specific(page_t** page, size_t ppn)
+ {
+       page_t* sp_page = ppn2page(ppn);
+       if( sp_page->page_ref != 0 )
+               return -ENOMEM;
+       *page = sp_page;
+       LIST_REMOVE(*page, page_link);
+       __page_clear(*page);
+       return 0;
  }
  
  /**
-  * @brief Allocates a physical page from a pool of unused physical memory
+  * @brief Allocates a physical page from a pool of unused physical memory.
   *
-  * Does NOT set the contents of the physical page to zero -
-  * the caller must do that if necessary.
+  * Zeroes the page.
   *
   * @param[out] page  set to point to the Page struct
   *                   of the newly allocated page
   * @return ESUCCESS on success
   * @return -ENOMEM  otherwise
   */
- error_t page_alloc(page_t** page) 
+ error_t upage_alloc(struct proc* p, page_t** page)
  {
-       return page_alloc_from_color_range(page, 0, llc_num_colors);
+       spin_lock_irqsave(&colored_page_free_list_lock);
+       ssize_t ret = __colored_page_alloc(p->cache_colors_map, 
+                                            page, p->next_cache_color);
+       spin_unlock_irqsave(&colored_page_free_list_lock);
+       if(ret >= 0)
+       {
+               memset(page2kva(*page),0,PGSIZE);
+               p->next_cache_color = (ret + 1) % llc_cache->num_colors;
+       }
+       return ret;
+ }
+ error_t kpage_alloc(page_t** page) 
+ {
+       static size_t next_color = 0;
+       ssize_t ret;
+       spin_lock_irqsave(&colored_page_free_list_lock);
+       if((ret = __page_alloc_from_color_range(page, next_color, 
+                                   llc_cache->num_colors)) < 0)
+               ret = __page_alloc_from_color_range(page, 0, next_color);
+       if(ret >= 0) {
+               next_color = ret;        
+               page_incref(*page);
+               ret = ESUCCESS;
+       }
+       spin_unlock_irqsave(&colored_page_free_list_lock);
+       
+       return ret;
  }
  
  /**
 - * @brief Allocated 2^order contiguous physical pages.  Will incrememnt the
 + * @brief Allocated 2^order contiguous physical pages.  Will increment the
   * reference count for the pages.
   *
   * @param[in] order order of the allocation
@@@ -127,100 -208,31 +208,31 @@@ void free_cont_pages(void *buf, size_t 
  }
  
  /*
-  * This macro defines multiple functions of the form:
-  * error_t _cache##_page_alloc(page_t** page, size_t color)
-  *
-  * Each of these functions operates on a different level of 
-  * of the cache heirarchy, and allocates a physical page
-  * from the list of pages corresponding to the supplied 
-  * color for the given cache.  
-  * 
+  * Allocates a specific physical page.
   * Does NOT set the contents of the physical page to zero -
   * the caller must do that if necessary.
   *
-  * color       -- the color from which to allocate a page
+  * ppn         -- the page number to allocate
   * *page       -- is set to point to the Page struct 
   *                of the newly allocated page
   *
   * RETURNS 
   *   ESUCCESS  -- on success
   *   -ENOMEM   -- otherwise 
-  *
-  * error_t _cache##_page_alloc(page_t** page, size_t color)
-  * {
-  *     if(!LIST_EMPTY(&(_cache##_cache_colored_page_list)[(color)])) {
-  *      *(page) = LIST_FIRST(&(_cache##_cache_colored_page_list)[(color)]);
-  *             LIST_REMOVE(*page, global_link);
-  *             REMOVE_CACHE_COLORING_PAGE_FROM_FREE_LISTS(page);
-  *             page_clear(*page);
-  *             return ESUCCESS;
-  *     }
-  *     return -ENOMEM;
-  * }
   */
- error_t l1_page_alloc(page_t** page, size_t color)
- {
-       if(available_caches.l1)
-       {
-               uint16_t range = llc_num_colors / get_cache_num_page_colors(&l1);
-               uint16_t base_color = color*range;
-               return page_alloc_from_color_range(page, base_color, range);
-       }
-       return -ENOCACHE;
- }
- error_t l2_page_alloc(page_t** page, size_t color)
+ error_t upage_alloc_specific(struct proc* p, page_t** page, size_t ppn)
  {
-       if(available_caches.l2)
-       {
-               uint16_t range = llc_num_colors / get_cache_num_page_colors(&l2);
-               uint16_t base_color = color*range;
-               return page_alloc_from_color_range(page, base_color, range);
-       }
-       return -ENOCACHE;
- }
- error_t l3_page_alloc(page_t** page, size_t color)
- {
-       if(available_caches.l3)
-       {
-               uint16_t range = llc_num_colors / get_cache_num_page_colors(&l3);
-               uint16_t base_color = color*range;
-               return page_alloc_from_color_range(page, base_color, range);
-       }
-       return -ENOCACHE;
- }
- /* Internal version of page_alloc_specific.  Grab the lock first. */
- static error_t __page_alloc_specific(page_t** page, size_t ppn)
- {
-       page_t* sp_page = ppn2page(ppn);
-       if( sp_page->page_ref != 0 )
-               return -ENOMEM;
-       *page = sp_page;
-       LIST_REMOVE(*page, page_link);
-       page_clear(*page);
+       spin_lock_irqsave(&colored_page_free_list_lock);
+       __page_alloc_specific(page, ppn);
+       spin_unlock_irqsave(&colored_page_free_list_lock);
        return 0;
  }
  
- /*
-  * Allocates a specific physical page.
-  * Does NOT set the contents of the physical page to zero -
-  * the caller must do that if necessary.
-  *
-  * ppn         -- the page number to allocate
-  * *page       -- is set to point to the Page struct 
-  *                of the newly allocated page
-  *
-  * RETURNS 
-  *   ESUCCESS  -- on success
-  *   -ENOMEM   -- otherwise 
-  */
- error_t page_alloc_specific(page_t** page, size_t ppn)
+ error_t kpage_alloc_specific(page_t** page, size_t ppn)
  {
        spin_lock_irqsave(&colored_page_free_list_lock);
        __page_alloc_specific(page, ppn);
+       page_incref(*page);
        spin_unlock_irqsave(&colored_page_free_list_lock);
        return 0;
  }
   */
  static error_t __page_free(page_t* page) 
  {
-       page_clear(page);
-       cache_t* llc = available_caches.llc;
+       __page_clear(page);
  
        LIST_INSERT_HEAD(
-          &(colored_page_free_list[get_page_color(page2ppn(page), llc)]),
+          &(colored_page_free_list[get_page_color(page2ppn(page), llc_cache)]),
           page,
           page_link
        );
@@@ -268,6 -279,11 +279,11 @@@ int page_is_free(size_t ppn) 
   */
  void page_incref(page_t *page)
  {
+       __page_incref(page);
+ }
+ void __page_incref(page_t *page)
+ {
        page->page_ref++;
  }
  
@@@ -288,6 -304,10 +304,10 @@@ void page_decref(page_t *page
   */
  static void __page_decref(page_t *page)
  {
+       if (page->page_ref == 0) {
+               panic("Trying to Free already freed page: %d...\n", page2ppn(page));
+               return;
+       }
        if (--page->page_ref == 0)
                __page_free(page);
  }
diff --combined kern/src/pmap.c
@@@ -289,7 -289,7 +289,7 @@@ void* user_mem_check(env_t *env, const 
                warn("Blimey!  Wrap around in VM range calculation!");  
                return NULL;
        }
-       num_pages = PPN(end - start);
+       num_pages = LA2PPN(end - start);
        for (i = 0; i < num_pages; i++, start += PGSIZE) {
                page_perms = get_va_perms(env->env_pgdir, start);
                // ensures the bits we want on are turned on.  if not, error out
@@@ -379,10 -379,10 +379,10 @@@ user_mem_assert(env_t *env, const void 
                return NULL;
        }
        
-     void *COUNT(len) res = user_mem_check(env,va,len,perm | PTE_USER_RO);
+       void *COUNT(len) res = user_mem_check(env,va,len,perm | PTE_USER_RO);
        if (!res) {
                cprintf("[%08x] user_mem_check assertion failure for "
 -                      "va %08x\n", env->env_id, user_mem_check_addr);
 +                      "va %08x\n", env->pid, user_mem_check_addr);
                proc_destroy(env);      // may not return
          return NULL;
        }
@@@ -419,7 -419,7 +419,7 @@@ error_t memcpy_from_user(env_t* env, vo
        if(start >= (void*SNT)ULIM || end >= (void*SNT)ULIM)
                return -EFAULT;
  
-       num_pages = PPN(end - start);
+       num_pages = LA2PPN(end - start);
        for(i = 0; i < num_pages; i++)
        {
                pte = pgdir_walk(env->env_pgdir, start+i*PGSIZE, 0);
@@@ -474,7 -474,7 +474,7 @@@ error_t memcpy_to_user(env_t* env, void
        if(start >= (void*SNT)ULIM || end >= (void*SNT)ULIM)
                return -EFAULT;
  
-       num_pages = PPN(end - start);
+       num_pages = LA2PPN(end - start);
        for(i = 0; i < num_pages; i++)
        {
                pte = pgdir_walk(env->env_pgdir, start+i*PGSIZE, 0);
diff --combined kern/src/process.c
@@@ -9,7 -9,6 +9,7 @@@
  #endif
  
  #include <arch/arch.h>
 +#include <arch/bitmask.h>
  #include <process.h>
  #include <atomic.h>
  #include <smp.h>
  #include <stdio.h>
  #include <assert.h>
  #include <timing.h>
 +#include <hashtable.h>
 +#include <slab.h>
  #include <sys/queue.h>
  
  /* Process Lists */
 -struct proc_list proc_freelist = TAILQ_HEAD_INITIALIZER(proc_freelist);
 -spinlock_t freelist_lock = 0;
  struct proc_list proc_runnablelist = TAILQ_HEAD_INITIALIZER(proc_runnablelist);
 -spinlock_t runnablelist_lock = 0;
 +spinlock_t runnablelist_lock = SPINLOCK_INITIALIZER;
 +struct kmem_cache *proc_cache;
  
  /* Tracks which cores are idle, similar to the vcoremap.  Each value is the
   * physical coreid of an unallocated core. */
 -spinlock_t idle_lock = 0;
 +spinlock_t idle_lock = SPINLOCK_INITIALIZER;
  uint32_t LCKD(&idle_lock) (RO idlecoremap)[MAX_NUM_CPUS];
  uint32_t LCKD(&idle_lock) num_idlecores = 0;
  
 -/*
 - * While this could be done with just an assignment, this gives us the
 +/* Helper function to return a core to the idlemap.  It causes some more lock
 + * acquisitions (like in a for loop), but it's a little easier.  Plus, one day
 + * we might be able to do this without locks (for the putting). */
 +static void put_idle_core(uint32_t coreid)
 +{
 +      spin_lock(&idle_lock);
 +      idlecoremap[num_idlecores++] = coreid;
 +      spin_unlock(&idle_lock);
 +}
 +
 +/* Other helpers, implemented later. */
 +static uint32_t get_free_vcoreid(struct proc *SAFE p, uint32_t prev);
 +static uint32_t get_busy_vcoreid(struct proc *SAFE p, uint32_t prev);
 +static int32_t __get_vcoreid(int32_t *corelist, size_t num, int32_t pcoreid);
 +static int32_t get_vcoreid(struct proc *SAFE p, int32_t pcoreid);
 +static inline void __wait_for_ipi(const char *fnname);
 +
 +/* PID management. */
 +#define PID_MAX 32767 // goes from 0 to 32767, with 0 reserved
 +static DECL_BITMASK(pid_bmask, PID_MAX + 1);
 +spinlock_t pid_bmask_lock = SPINLOCK_INITIALIZER;
 +struct hashtable *pid_hash;
 +spinlock_t pid_hash_lock; // initialized in proc_init
 +
 +/* Finds the next free entry (zero) entry in the pid_bitmask.  Set means busy.
 + * PID 0 is reserved (in proc_init).  A return value of 0 is a failure (and
 + * you'll also see a warning, for now).  Consider doing this with atomics. */
 +static pid_t get_free_pid(void)
 +{
 +      static pid_t next_free_pid = 1;
 +      pid_t my_pid = 0;
 +
 +      spin_lock(&pid_bmask_lock);
 +      // atomically (can lock for now, then change to atomic_and_return
 +      FOR_CIRC_BUFFER(next_free_pid, PID_MAX + 1, i) {
 +              // always points to the next to test
 +              next_free_pid = (next_free_pid + 1) % (PID_MAX + 1);
 +              if (!GET_BITMASK_BIT(pid_bmask, i)) {
 +                      SET_BITMASK_BIT(pid_bmask, i);
 +                      my_pid = i;
 +                      break;
 +              }
 +      }
 +      spin_unlock(&pid_bmask_lock);
 +      if (!my_pid)
 +              warn("Shazbot!  Unable to find a PID!  You need to deal with this!\n");
 +      return my_pid;
 +}
 +
 +/* Return a pid to the pid bitmask */
 +static void put_free_pid(pid_t pid)
 +{
 +      spin_lock(&pid_bmask_lock);
 +      CLR_BITMASK_BIT(pid_bmask, pid);
 +      spin_unlock(&pid_bmask_lock);
 +}
 +
 +/* While this could be done with just an assignment, this gives us the
   * opportunity to check for bad transitions.  Might compile these out later, so
 - * we shouldn't rely on them for sanity checking from userspace.
 - */
 -int proc_set_state(struct proc *p, uint32_t state)
 + * we shouldn't rely on them for sanity checking from userspace.  */
 +int __proc_set_state(struct proc *p, uint32_t state)
  {
        uint32_t curstate = p->state;
        /* Valid transitions:
         * RBS -> D
         * RBM -> D
         *
 -       * This isn't allowed yet, may be later.
 +       * This isn't allowed yet, should be later.  Is definitely causable.
         * C   -> D
         */
        #if 1 // some sort of correctness flag
        return 0;
  }
  
 -/* Change this when we aren't using an array */
 -struct proc *get_proc(unsigned pid)
 +/* Returns a pointer to the proc with the given pid, or 0 if there is none */
 +struct proc *pid2proc(pid_t pid)
 +{
 +      spin_lock(&pid_hash_lock);
 +      struct proc *p = hashtable_search(pid_hash, (void*)pid);
 +      spin_unlock(&pid_hash_lock);
 +      /* if the refcnt was 0, decref and return 0 (we failed). (TODO) */
 +      if (p)
 +              proc_incref(p, 1); // TODO:(REF) to do this all atomically and not panic
 +      return p;
 +}
 +
 +/* Performs any intialization related to processes, such as create the proc
 + * cache, prep the scheduler, etc.  When this returns, we should be ready to use
 + * any process related function. */
 +void proc_init(void)
 +{
 +      proc_cache = kmem_cache_create("proc", sizeof(struct proc),
 +                   MAX(HW_CACHE_ALIGN, __alignof__(struct proc)), 0, 0, 0);
 +      /* Init PID mask and hash.  pid 0 is reserved. */
 +      SET_BITMASK_BIT(pid_bmask, 0);
 +      spinlock_init(&pid_hash_lock);
 +      spin_lock(&pid_hash_lock);
 +      pid_hash = create_hashtable(100, __generic_hash, __generic_eq);
 +      spin_unlock(&pid_hash_lock);
 +      schedule_init();
 +      /* Init idle cores.  core 0 is not idle, all others are (for now) */
 +      spin_lock(&idle_lock);
 +      num_idlecores = num_cpus - 1;
 +      for (int i = 0; i < num_idlecores; i++)
 +              idlecoremap[i] = i + 1;
 +      spin_unlock(&idle_lock);
 +      atomic_init(&num_envs, 0);
 +}
 +
 +/* Allocates and initializes a process, with the given parent.  Currently
 + * writes the *p into **pp, and returns 0 on success, < 0 for an error.
 + * Errors include:
 + *  - ENOFREEPID if it can't get a PID
 + *  - ENOMEM on memory exhaustion */
 +static error_t proc_alloc(struct proc *SAFE*SAFE pp, pid_t parent_id)
 +{
 +      error_t r;
 +      struct proc *p;
 +
 +      if (!(p = kmem_cache_alloc(proc_cache, 0)))
 +              return -ENOMEM;
 +
 +    { INITSTRUCT(*p)
 +
++      // Setup the default map of where to get cache colors from
++      p->cache_colors_map = global_cache_colors_map;
++      p->next_cache_color = 0;
++
 +      /* Initialize the address space */
 +      if ((r = env_setup_vm(p)) < 0) {
 +              kmem_cache_free(proc_cache, p);
 +              return r;
 +      }
 +
 +      /* Get a pid, then store a reference in the pid_hash */
 +      if (!(p->pid = get_free_pid())) {
 +              kmem_cache_free(proc_cache, p);
 +              return -ENOFREEPID;
 +      }
 +      spin_lock(&pid_hash_lock);
 +      hashtable_insert(pid_hash, (void*)p->pid, p);
 +      spin_unlock(&pid_hash_lock);
 +
 +      /* Set the basic status variables. */
 +    spinlock_init(&p->proc_lock);
 +      p->ppid = parent_id;
 +      __proc_set_state(p, PROC_CREATED);
 +      p->env_refcnt = 2; // one for the object, one for the ref we pass back
 +      p->env_flags = 0;
 +      p->env_entry = 0; // cheating.  this really gets set in load_icode
 +      p->num_vcores = 0;
++      p->heap_bottom = (void*)UTEXT;
++      p->heap_top = (void*)UTEXT;
 +      memset(&p->vcoremap, -1, sizeof(p->vcoremap));
 +      memset(&p->resources, 0, sizeof(p->resources));
 +      memset(&p->env_ancillary_state, 0, sizeof(p->env_ancillary_state));
 +      memset(&p->env_tf, 0, sizeof(p->env_tf));
 +      proc_init_trapframe(&p->env_tf);
 +
 +      /* Initialize the contents of the e->env_procinfo structure */
 +      p->env_procinfo->pid = p->pid;
 +      /* Initialize the contents of the e->env_procdata structure */
 +
 +      /* Initialize the generic syscall ring buffer */
 +      SHARED_RING_INIT(&p->env_procdata->syscallring);
 +      /* Initialize the backend of the syscall ring buffer */
 +      BACK_RING_INIT(&p->syscallbackring,
 +                     &p->env_procdata->syscallring,
 +                     SYSCALLRINGSIZE);
 +
 +      /* Initialize the generic sysevent ring buffer */
 +      SHARED_RING_INIT(&p->env_procdata->syseventring);
 +      /* Initialize the frontend of the sysevent ring buffer */
 +      FRONT_RING_INIT(&p->syseventfrontring,
 +                      &p->env_procdata->syseventring,
 +                      SYSEVENTRINGSIZE);
 +      *pp = p;
 +      atomic_inc(&num_envs);
 +
 +      printk("[%08x] new process %08x\n", current ? current->pid : 0, p->pid);
 +      } // INIT_STRUCT
 +      return 0;
 +}
 +
 +/* Creates a process from the specified binary, which is of size size.
 + * Currently, the binary must be a contiguous block of memory, which needs to
 + * change.  On any failure, it just panics, which ought to be sorted. */
 +struct proc *proc_create(uint8_t *binary, size_t size)
 +{
 +      struct proc *p;
 +      error_t r;
 +      pid_t curid;
 +
 +      curid = (current ? current->pid : 0);
 +      if ((r = proc_alloc(&p, curid)) < 0)
 +              panic("proc_create: %e", r); // one of 3 quaint usages of %e.
-       load_icode(p, binary, size);
++      env_load_icode(p, NULL, binary, size);
 +      return p;
 +}
 +
 +/* This is called by proc_decref, once the last reference to the process is
 + * gone.  Don't call this otherwise (it will panic).  It will clean up the
 + * address space and deallocate any other used memory. */
 +static void __proc_free(struct proc *p)
  {
 -      // should have some error checking when we do this for real
 -      return &envs[ENVX(pid)];
 +      physaddr_t pa;
 +
 +      printk("[PID %d] freeing proc: %d\n", current ? current->pid : 0, p->pid);
 +      // All parts of the kernel should have decref'd before __proc_free is called
 +      assert(p->env_refcnt == 0);
 +
++      // Free any colors allocated to this process
++      if(p->cache_colors_map != global_cache_colors_map) {
++              for(int i=0; i<llc_cache->num_colors; i++)
++                      cache_color_free(llc_cache, p->cache_colors_map);
++              cache_colors_map_free(p->cache_colors_map);
++      }
++
 +      // Flush all mapped pages in the user portion of the address space
 +      env_user_mem_free(p);
 +
 +      // free the page directory
 +      pa = p->env_cr3;
 +      p->env_pgdir = 0;
 +      p->env_cr3 = 0;
 +      page_decref(pa2page(pa));
 +
 +      /* Remove self from the pid hash, return PID.  Note the reversed order. */
 +      spin_lock(&pid_hash_lock);
 +      if (!hashtable_remove(pid_hash, (void*)p->pid))
 +              panic("Proc not in the pid table in %s", __FUNCTION__);
 +      spin_unlock(&pid_hash_lock);
 +      put_free_pid(p->pid);
 +      atomic_dec(&num_envs);
 +
 +      /* Dealloc the struct proc */
 +      kmem_cache_free(proc_cache, p);
  }
  
 -/* Whether or not actor can control target */
 +/* Whether or not actor can control target.  Note we currently don't need
 + * locking for this. TODO: think about that, esp wrt proc's dying. */
  bool proc_controls(struct proc *actor, struct proc *target)
  {
 -      return target->env_parent_id == actor->env_id;
 +      return ((actor == target) || (target->ppid == actor->pid));
  }
  
  /* Dispatches a process to run, either on the current core in the case of a
   * When a process goes from RUNNABLE_M to RUNNING_M, its vcoremap will be
   * "packed" (no holes in the vcore->pcore mapping), vcore0 will continue to run
   * it's old core0 context, and the other cores will come in at the entry point.
 - * Including in the case of preemption.  */
 + * Including in the case of preemption.
 + *
 + * This won't return if the current core is going to be one of the processes
 + * cores (either for _S mode or for _M if it's in the vcoremap).  proc_run will
 + * eat your reference if it does not return. */
  void proc_run(struct proc *p)
  {
 +      bool self_ipi_pending = FALSE;
        spin_lock_irqsave(&p->proc_lock);
        switch (p->state) {
                case (PROC_DYING):
                        spin_unlock_irqsave(&p->proc_lock);
 -                      printk("Process %d not starting due to async death\n", p->env_id);
 -                      // There should be no core cleanup to do (like decref).
 -                      assert(current != p);
 +                      printk("Process %d not starting due to async death\n", p->pid);
                        // if we're a worker core, smp_idle, o/w return
                        if (!management_core())
                                smp_idle(); // this never returns
                        return;
                case (PROC_RUNNABLE_S):
 -                      proc_set_state(p, PROC_RUNNING_S);
 +                      __proc_set_state(p, PROC_RUNNING_S);
                        /* We will want to know where this process is running, even if it is
                         * only in RUNNING_S.  can use the vcoremap, which makes death easy.
                         * Also, this is the signal used in trap.c to know to save the tf in
                        p->num_vcores = 0;
                        p->vcoremap[0] = core_id();
                        spin_unlock_irqsave(&p->proc_lock);
 -                      // This normally doesn't return, but might error out in the future.
 +                      /* Transferring our reference to startcore, where p will become
 +                       * current.  If it already is, decref in advance.  This is similar
 +                       * to __startcore(), in that it sorts out the refcnt accounting.  */
 +                      if (current == p)
 +                              proc_decref(p, 1);
                        proc_startcore(p, &p->env_tf);
                        break;
                case (PROC_RUNNABLE_M):
 -                      proc_set_state(p, PROC_RUNNING_M);
                        /* vcoremap[i] holds the coreid of the physical core allocated to
                         * this process.  It is set outside proc_run.  For the active
                         * message, a0 = struct proc*, a1 = struct trapframe*.   */
                        if (p->num_vcores) {
 +                              __proc_set_state(p, PROC_RUNNING_M);
 +                              int i = 0;
 +                              /* Up the refcnt, since num_vcores are going to start using this
 +                               * process and have it loaded in their 'current'. */
 +                              p->env_refcnt += p->num_vcores; // TODO: (REF) use incref
 +                              /* If the core we are running on is in the vcoremap, we will get
 +                               * an IPI (once we reenable interrupts) and never return. */
 +                              if (__get_vcoreid(p->vcoremap, p->num_vcores, core_id()) != -1)
 +                                      self_ipi_pending = TRUE;
                                // TODO: handle silly state (HSS)
 -                              // set virtual core 0 to run the main context
 +                              // set virtual core 0 to run the main context on transition
 +                              if (p->env_flags & PROC_TRANSITION_TO_M) {
 +                                      p->env_flags &= !PROC_TRANSITION_TO_M;
  #ifdef __IVY__
 -                              send_active_msg_sync(p->vcoremap[0], __startcore, p,
 -                                                   &p->env_tf, (void *SNT)0);
 +                                      send_active_message(p->vcoremap[0], __startcore, p,
 +                                                          &p->env_tf, (void *SNT)0);
  #else
 -                              send_active_msg_sync(p->vcoremap[0], (void *)__startcore,
 -                                                   (void *)p, (void *)&p->env_tf, 0);
 +                                      send_active_message(p->vcoremap[0], (void *)__startcore,
 +                                                          (void *)p, (void *)&p->env_tf, 0);
  #endif
 -                              /* handle the others.  note the sync message will spin until
 -                               * there is a free active message slot, which could lock up the
 -                               * system.  think about this. (TODO)(AMDL) */
 -                              for (int i = 1; i < p->num_vcores; i++)
 +                                      i = 1; // start at vcore1 in the loop below
 +                              }
 +                              /* handle the others. */
 +                              for (/* i set above */; i < p->num_vcores; i++)
  #ifdef __IVY__
 -                                      send_active_msg_sync(p->vcoremap[i], __startcore,
 -                                                           p, (trapframe_t *CT(1))NULL, (void *SNT)i);
 +                                      send_active_message(p->vcoremap[i], __startcore,
 +                                                          p, (trapframe_t *CT(1))NULL, (void *SNT)i);
  #else
 -                                      send_active_msg_sync(p->vcoremap[i], (void *)__startcore,
 -                                                           (void *)p, (void *)0, (void *)i);
 +                                      send_active_message(p->vcoremap[i], (void *)__startcore,
 +                                                          (void *)p, (void *)0, (void *)i);
  #endif
 +                      } else {
 +                              warn("Tried to proc_run() an _M with no vcores!");
                        }
 -                      /* There a subtle (attempted) race avoidance here.  proc_startcore
 -                       * can handle a death message, but we can't have the startcore come
 -                       * after the death message.  Otherwise, it would look like a new
 -                       * process.  So we hold the lock to make sure our message went out
 -                       * before a possible death message.
 +                      /* Unlock and decref/wait for the IPI if one is pending.  This will
 +                       * eat the reference if we aren't returning. 
 +                       *
 +                       * There a subtle race avoidance here.  proc_startcore can handle a
 +                       * death message, but we can't have the startcore come after the
 +                       * death message.  Otherwise, it would look like a new process.  So
 +                       * we hold the lock til after we send our message, which prevents a
 +                       * possible death message.
                         * - Likewise, we need interrupts to be disabled, in case one of the
                         *   messages was for us, and reenable them after letting go of the
                         *   lock.  This is done by spin_lock_irqsave, so be careful if you
                         *   change this.
 -                       * - This can also be done far more intelligently / efficiently,
 -                       *   like skipping in case it's busy and coming back later.
                         * - Note there is no guarantee this core's interrupts were on, so
                         *   it may not get the message for a while... */
 -                      spin_unlock_irqsave(&p->proc_lock);
 +                      __proc_unlock_ipi_pending(p, self_ipi_pending);
                        break;
                default:
                        spin_unlock_irqsave(&p->proc_lock);
        }
  }
  
 -/*
 - * Runs the given context (trapframe) of process p on the core this code
 - * executes on.  The refcnt tracks how many cores have "an interest" in this
 - * process, which so far just means it uses the process's page table.  See the
 - * massive comments around the incref function
 +/* Runs the given context (trapframe) of process p on the core this code
 + * executes on.
   *
   * Given we are RUNNING_*, an IPI for death or preemption could come in:
   * 1. death attempt (IPI to kill whatever is on your core):
   * I think we need to make it such that the kernel in "process context" never
   * gets removed from the core (displaced from its stack) without going through
   * some "bundling" code.
 - */
 + *
 + * A note on refcnting: this function will not return, and your proc reference
 + * will end up stored in current.  This will make no changes to p's refcnt, so
 + * do your accounting such that there is only the +1 for current.  This means if
 + * it is already in current (like in the trap return path), don't up it.  If
 + * it's already in current and you have another reference (like pid2proc or from
 + * an IPI), then down it (which is what happens in __startcore()).  If it's not
 + * in current and you have one reference, like proc_run(non_current_p), then
 + * also do nothing.  The refcnt for your *p will count for the reference stored
 + * in current. */
  void proc_startcore(struct proc *p, trapframe_t *tf) {
        // it's possible to be DYING, but it's a rare race.
        //if (p->state & (PROC_RUNNING_S | PROC_RUNNING_M))
        //      printk("dying before (re)startcore on core %d\n", core_id());
 -
        // sucks to have ints disabled when doing env_decref and possibly freeing
        disable_irq();
        if (per_cpu_info[core_id()].preempt_pending) {
        }
        /* If the process wasn't here, then we need to load its address space. */
        if (p != current) {
 -              if (proc_incref(p)) {
 -                      // getting here would mean someone tried killing this while we tried
 -                      // to start one of it's contexts (from scratch, o/w we had it's CR3
 -                      // loaded already)
 -                      // if this happens, a no-op death-IPI ought to be on its way...  we can
 -                      // just smp_idle()
 -                      smp_idle();
 -              }
 +              /* Do not incref here.  We were given the reference to current,
 +               * pre-upped. */
                lcr3(p->env_cr3);
 -              // we unloaded the old cr3, so decref it (if it exists)
 -              // TODO: Consider moving this to wherever we really "mean to leave the
 -              // process's context".  abandon_core() does this.
 +              /* This is "leaving the process context" of the previous proc.  The
 +               * previous lcr3 unloaded the previous proc's context.  This should
 +               * rarely happen, since we usually proactively leave process context,
 +               * but is the fallback. */
                if (current)
 -                      proc_decref(current);
 -              set_cpu_curenv(p);
 +                      proc_decref(current, 1);
 +              set_current_proc(p);
        }
        /* need to load our silly state, preferably somewhere other than here so we
         * can avoid the case where the context was just running here.  it's not
        env_pop_tf(tf);
  }
  
 -/* Helper function, helps with receiving local death IPIs, for the cases when
 - * this core is running the process.  We should received an IPI shortly.  If
 - * not, odds are interrupts are disabled, which shouldn't happen while servicing
 - * syscalls. */
 -static void check_for_local_death(struct proc *p)
 -{
 -      if (current == p) {
 -              /* a death IPI should be on its way, either from the RUNNING_S one, or
 -               * from proc_take_cores with a __death.  in general, interrupts should
 -               * be on when you call proc_destroy locally, but currently aren't for
 -               * all things (like traphandlers).  since we're dying anyway, it seems
 -               * reasonable to turn on interrupts.  note this means all non-proc
 -               * management interrupt handlers must return (which they need to do
 -               * anyway), so that we get back to this point.  Eventually, we can
 -               * remove the enable_irq.  think about this (TODO) */
 -              enable_irq();
 -              udelay(1000000);
 -              panic("Waiting too long on core %d for an IPI in proc_destroy()!",
 -                    core_id());
 -      }
 -}
 -
  /*
   * Destroys the given process.  This may be called from another process, a light
   * kernel thread (no real process context), asynchronously/cross-core, or from
   * (Last core/kernel thread to decref cleans up and deallocates resources.)
   *
   * Note that some cores can be processing async calls, but will eventually
 - * decref.  Should think about this more.
 - */
 + * decref.  Should think about this more, like some sort of callback/revocation.
 + *
 + * This will eat your reference if it won't return.  Note that this function
 + * needs to change anyways when we make __death more like __preempt.  (TODO) */
  void proc_destroy(struct proc *p)
  {
 -      // Note this code relies on this lock disabling interrupts, similar to
 -      // proc_run.
 +      /* TODO: this corelist is taking up a lot of space on the stack */
        uint32_t corelist[MAX_NUM_CPUS];
        size_t num_cores_freed;
 +      bool self_ipi_pending = FALSE;
        spin_lock_irqsave(&p->proc_lock);
 +
 +      /* TODO: (DEATH) look at this again when we sort the __death IPI */
 +      if (current == p)
 +              self_ipi_pending = TRUE;
 +
        switch (p->state) {
                case PROC_DYING: // someone else killed this already.
 -                      spin_unlock_irqsave(&p->proc_lock);
 -                      check_for_local_death(p); // IPI may be on it's way.
 +                      __proc_unlock_ipi_pending(p, self_ipi_pending);
                        return;
                case PROC_RUNNABLE_M:
                        /* Need to reclaim any cores this proc might have, even though it's
                         * not running yet. */
 -                      proc_take_allcores(p, 0);
 +                      __proc_take_allcores(p, NULL, NULL, NULL, NULL);
                        // fallthrough
                case PROC_RUNNABLE_S:
                        // Think about other lists, like WAITING, or better ways to do this
                        // here's how to do it manually
                        if (current == p) {
                                lcr3(boot_cr3);
 -                              proc_decref(p); // this decref is for the cr3
 +                              proc_decref(p, 1); // this decref is for the cr3
                                current = NULL;
                        }
                        #endif
 -                      send_active_msg_sync(p->vcoremap[0], __death, (void *SNT)0,
 -                                           (void *SNT)0, (void *SNT)0);
 +                      send_active_message(p->vcoremap[0], __death, (void *SNT)0,
 +                                          (void *SNT)0, (void *SNT)0);
                        #if 0
                        /* right now, RUNNING_S only runs on a mgmt core (0), not cores
                         * managed by the idlecoremap.  so don't do this yet. */
 -                      spin_lock(&idle_lock);
 -                      idlecoremap[num_idlecores++] = p->vcoremap[0];
 -                      spin_unlock(&idle_lock);
 +                      put_idle_core(p->vcoremap[0]);
                        #endif
                        break;
                case PROC_RUNNING_M:
                         * deallocate the cores.
                         * The rule is that the vcoremap is set before proc_run, and reset
                         * within proc_destroy */
 -                      proc_take_allcores(p, __death);
 +                      __proc_take_allcores(p, __death, (void *SNT)0, (void *SNT)0,
 +                                           (void *SNT)0);
                        break;
                default:
 -                      // TODO: getting here if it's already dead and free (ENV_FREE).
 -                      // Need to sort reusing process structures and having pointers to
 -                      // them floating around the system.
                        panic("Weird state(0x%08x) in proc_destroy", p->state);
        }
 -      proc_set_state(p, PROC_DYING);
 -
 -      atomic_dec(&num_envs);
 -      /* TODO: (REF) dirty hack.  decref currently uses a lock, but needs to use
 -       * CAS instead (another lock would be slightly less ghetto).  but we need to
 -       * decref before releasing the lock, since that could enable interrupts,
 -       * which would have us receive the DEATH IPI if this was called locally by
 -       * the target process. */
 -      //proc_decref(p); // this decref is for the process in general
 -      p->env_refcnt--;
 -      size_t refcnt = p->env_refcnt; // need to copy this in so it's not reloaded
 -
 -      /* After unlocking, we can receive a DEATH IPI and clean up */
 -      spin_unlock_irqsave(&p->proc_lock);
 -
 -      // coupled with the refcnt-- above, from decref.  if this happened,
 -      // proc_destroy was called "remotely", and with no one else refcnting
 -      if (!refcnt)
 -              env_free(p);
 +      __proc_set_state(p, PROC_DYING);
 +      /* this decref is for the process in general */
 +      p->env_refcnt--; // TODO (REF)
 +      //proc_decref(p, 1);
  
 -      /* if our core is part of process p, then check/wait for the death IPI. */
 -      check_for_local_death(p);
 +      /* Unlock and possible decref and wait.  A death IPI should be on its way,
 +       * either from the RUNNING_S one, or from proc_take_cores with a __death.
 +       * in general, interrupts should be on when you call proc_destroy locally,
 +       * but currently aren't for all things (like traphandlers). */
 +      __proc_unlock_ipi_pending(p, self_ipi_pending);
        return;
  }
  
@@@ -607,66 -416,37 +620,66 @@@ static uint32_t get_busy_vcoreid(struc
        return i;
  }
  
 -/* Helper function.  Find the vcoreid for a given physical core id.
 - * You better hold the lock before calling this.  If we use some sort of
 - * pcoremap, we can avoid this linear search. */
 -static uint32_t get_vcoreid(struct proc *SAFE p, int32_t pcoreid)
 +/* Helper function.  Find the vcoreid for a given physical core id.  If we use
 + * some sort of pcoremap, we can avoid this linear search.  You better hold the
 + * lock before calling this.  Returns -1 on failure. */
 +static int32_t __get_vcoreid(int32_t *corelist, size_t num, int32_t pcoreid)
  {
 -      uint32_t i;
 -      for (i = 0; i < MAX_NUM_CPUS; i++)
 -              if (p->vcoremap[i] == pcoreid)
 +      int32_t i;
 +      bool found = FALSE;
 +      for (i = 0; i < num; i++)
 +              if (corelist[i] == pcoreid) {
 +                      found = TRUE;
                        break;
 -      if (i + 1 >= MAX_NUM_CPUS)
 -              warn("At the end of the vcorelist.  Might want to check that out.");
 -      return i;
 +              }
 +      if (found)
 +              return i;
 +      else
 +              return -1;
 +}
 +
 +/* Helper function.  Just like the one above, but this one panics on failure.
 + * You better hold the lock before calling this.  */
 +static int32_t get_vcoreid(struct proc *SAFE p, int32_t pcoreid)
 +{
 +      int32_t vcoreid = __get_vcoreid(p->vcoremap, p->num_vcores, pcoreid);
 +      assert(vcoreid != -1);
 +      return vcoreid;
 +}
 +
 +/* Use this when you are waiting for an IPI that you sent yourself.  In most
 + * cases, interrupts should already be on (like after a spin_unlock_irqsave from
 + * process context), but aren't always, like in proc_destroy().  We might be
 + * able to remove the enable_irq in the future.  Think about this (TODO).
 + *
 + * Note this means all non-proc management interrupt handlers must return (which
 + * they need to do anyway), so that we get back to this point.  */
 +static inline void __wait_for_ipi(const char *fnname)
 +{
 +      enable_irq();
 +      udelay(1000000);
 +      panic("Waiting too long on core %d for an IPI in %s()!", core_id(), fnname);
  }
  
  /* Yields the calling core.  Must be called locally (not async) for now.
   * - If RUNNING_S, you just give up your time slice and will eventually return.
   * - If RUNNING_M, you give up the current vcore (which never returns), and
   *   adjust the amount of cores wanted/granted.
 - * - If you have only one vcore, you switch to RUNNABLE_S.
 - * - If you yield from vcore0 but are still RUNNING_M, your context will be
 - *   saved, but may not be restarted, depending on how you get that core back.
 - *   (currently)  see proc_give_cores for details.
 + * - If you have only one vcore, you switch to RUNNABLE_M.  When you run again,
 + *   you'll have one guaranteed core, starting from the entry point.
 + *
   * - RES_CORES amt_wanted will be the amount running after taking away the
 - *   yielder.
 - */
 + *   yielder, unless there are none left, in which case it will be 1.
 + *
 + * This does not return (abandon_core()), so it will eat your reference.  */
  void proc_yield(struct proc *SAFE p)
  {
        spin_lock_irqsave(&p->proc_lock);
        switch (p->state) {
                case (PROC_RUNNING_S):
 -                      proc_set_state(p, PROC_RUNNABLE_S);
 +                      p->env_tf= *current_tf;
 +                      env_push_ancillary_state(p);
 +                      __proc_set_state(p, PROC_RUNNABLE_S);
                        schedule_proc(p);
                        break;
                case (PROC_RUNNING_M):
                        // give up core
                        p->vcoremap[get_vcoreid(p, core_id())] = -1;
                        // add to idle list
 -                      spin_lock(&idle_lock);
 -                      idlecoremap[num_idlecores++] = core_id();
 -                      spin_unlock(&idle_lock);
 -                      // out of vcores?  if so, we're now a regular process
 +                      put_idle_core(core_id());
 +                      // last vcore?  then we really want 1, and to yield the gang
                        if (p->num_vcores == 0) {
 -                              // switch to runnable_s
 -                              proc_set_state(p, PROC_RUNNABLE_S);
 +                              // might replace this with m_yield, if we have it directly
 +                              p->resources[RES_CORES].amt_wanted = 1;
 +                              __proc_set_state(p, PROC_RUNNABLE_M);
                                schedule_proc(p);
                        }
                        break;
                default:
                        // there are races that can lead to this (async death, preempt, etc)
 -                      panic("Weird state(0x%08x) in sys_yield", p->state);
 +                      panic("Weird state(0x%08x) in proc_yield", p->state);
        }
        spin_unlock_irqsave(&p->proc_lock);
 -      // clean up the core and idle.  for mgmt cores, they will ultimately call
 -      // manager, which will call schedule(), which will repick the yielding proc.
 +      proc_decref(p, 1);
 +      /* Clean up the core and idle.  For mgmt cores, they will ultimately call
 +       * manager, which will call schedule() and will repick the yielding proc. */
        abandon_core();
  }
  
   * The other way would be to have this function have the side effect of changing
   * state, and finding another way to do the need_to_idle.
   *
 - * In the event of an error, corelist will include all the cores that were *NOT*
 - * given to the process (cores that are still free).  Practically, this will be
 - * all of them, since it seems like an all or nothing deal right now.
 + * The returned bool signals whether or not a stack-crushing IPI will come in
 + * once you unlock after this function.
   *
 - * WARNING: You must hold the proc_lock before calling this!*/
 -error_t proc_give_cores(struct proc *SAFE p, uint32_t corelist[], size_t *num)
 -{
 + * WARNING: You must hold the proc_lock before calling this! */
 +bool __proc_give_cores(struct proc *SAFE p, int32_t *corelist, size_t num)
 +{ TRUSTEDBLOCK
 +      bool self_ipi_pending = FALSE;
        uint32_t free_vcoreid = 0;
        switch (p->state) {
                case (PROC_RUNNABLE_S):
                case (PROC_RUNNING_S):
                        panic("Don't give cores to a process in a *_S state!\n");
 -                      return -EINVAL;
                        break;
                case (PROC_DYING):
 -                      // just FYI, for debugging
 -                      printk("[kernel] attempted to give cores to a DYING process.\n");
 -                      return -EFAIL;
 +                      panic("Attempted to give cores to a DYING process.\n");
                        break;
                case (PROC_RUNNABLE_M):
                        // set up vcoremap.  list should be empty, but could be called
                                        assert(p->vcoremap[i]);
                        }
                        // add new items to the vcoremap
 -                      for (int i = 0; i < *num; i++) {
 +                      for (int i = 0; i < num; i++) {
                                // find the next free slot, which should be the next one
                                free_vcoreid = get_free_vcoreid(p, free_vcoreid);
                                printd("setting vcore %d to pcore %d\n", free_vcoreid, corelist[i]);
                        }
                        break;
                case (PROC_RUNNING_M):
 -                      for (int i = 0; i < *num; i++) {
 +                      /* Up the refcnt, since num cores are going to start using this
 +                       * process and have it loaded in their 'current'. */
 +                      // TODO: (REF) use proc_incref once we have atomics
 +                      p->env_refcnt += num;
 +                      if (__get_vcoreid(corelist, num, core_id()) != -1)
 +                              self_ipi_pending = TRUE;
 +                      for (int i = 0; i < num; i++) {
                                free_vcoreid = get_free_vcoreid(p, free_vcoreid);
                                printd("setting vcore %d to pcore %d\n", free_vcoreid, corelist[i]);
                                p->vcoremap[free_vcoreid] = corelist[i];
                                p->num_vcores++;
 -                              // TODO: careful of active message deadlock (AMDL)
 -                              assert(corelist[i] != core_id()); // sanity
 -                              /* if we want to allow yielding of vcore0 and restarting it at
 -                               * its yield point *while still RUNNING_M*, uncomment this */
 -                              /*
 -                              if (i == 0)
 -                                      send_active_msg_sync(p->vcoremap[0], __startcore,
 -                                                           (uint32_t)p, (uint32_t)&p->env_tf, 0);
 -                              else */
 -                              send_active_msg_sync(corelist[i], __startcore, p,
 -                                                   (void*)0, (void*)free_vcoreid);
 +                              send_active_message(corelist[i], __startcore, p,
 +                                                  (struct Trapframe *)0,
 +                                                  (void*SNT)free_vcoreid);
                        }
                        break;
                default:
                        panic("Weird proc state %d in proc_give_cores()!\n", p->state);
        }
 -      return ESUCCESS;
 +      return self_ipi_pending;
  }
  
  /* Makes process p's coremap look like corelist (add, remove, etc).  Caller
   * any cores that are getting removed.
   *
   * Before implementing this, we should probably think about when this will be
 - * used.  Implies preempting for the message.
 + * used.  Implies preempting for the message.  The more that I think about this,
 + * the less I like it.  For now, don't use this, and think hard before
 + * implementing it.
   *
 - * WARNING: You must hold the proc_lock before calling this!*/
 -error_t proc_set_allcores(struct proc *SAFE p, uint32_t corelist[], size_t *num,
 -                          amr_t message)
 + * WARNING: You must hold the proc_lock before calling this! */
 +bool __proc_set_allcores(struct proc *SAFE p, int32_t *corelist,
 +                         size_t *num, amr_t message,TV(a0t) arg0,
 +                         TV(a1t) arg1, TV(a2t) arg2)
  {
        panic("Set all cores not implemented.\n");
  }
  
 -/* Takes from process p the num cores listed in corelist.  In the event of an
 - * error, corelist will contain the list of cores that are free, and num will
 - * contain how many items are in corelist.  This isn't implemented yet, but
 - * might be necessary later.  Or not, and we'll never do it.
 - *
 - * TODO: think about taking vcore0.  probably are issues...
 +/* Takes from process p the num cores listed in corelist, using the given
 + * message for the active message (__death, __preempt, etc).  Like the others
 + * in this function group, bool signals whether or not an IPI is pending.
   *
 - * WARNING: You must hold the proc_lock before calling this!*/
 -error_t proc_take_cores(struct proc *SAFE p, uint32_t corelist[], size_t *num,
 -                        amr_t message)
 -{
 + * WARNING: You must hold the proc_lock before calling this! */
 +bool __proc_take_cores(struct proc *SAFE p, int32_t *corelist,
 +                       size_t num, amr_t message, TV(a0t) arg0,
 +                       TV(a1t) arg1, TV(a2t) arg2)
 +{ TRUSTEDBLOCK
        uint32_t vcoreid;
 +      bool self_ipi_pending = FALSE;
        switch (p->state) {
                case (PROC_RUNNABLE_M):
                        assert(!message);
                        panic("Weird state %d in proc_take_cores()!\n", p->state);
        }
        spin_lock(&idle_lock);
 -      assert((*num <= p->num_vcores) && (num_idlecores + *num <= num_cpus));
 -      for (int i = 0; i < *num; i++) {
 +      assert((num <= p->num_vcores) && (num_idlecores + num <= num_cpus));
 +      spin_unlock(&idle_lock);
 +      for (int i = 0; i < num; i++) {
                vcoreid = get_vcoreid(p, corelist[i]);
                assert(p->vcoremap[vcoreid] == corelist[i]);
 -              if (message)
 -                      // TODO: careful of active message deadlock (AMDL)
 -                      send_active_msg_sync(corelist[i], message, 0, 0, 0);
 +              if (message) {
 +                      if (p->vcoremap[vcoreid] == core_id())
 +                              self_ipi_pending = TRUE;
 +                      send_active_message(corelist[i], message, arg0, arg1, arg2);
 +              }
                // give the pcore back to the idlecoremap
 -              idlecoremap[num_idlecores++] = corelist[i];
 +              put_idle_core(corelist[i]);
                p->vcoremap[vcoreid] = -1;
        }
 -      spin_unlock(&idle_lock);
 -      p->num_vcores -= *num;
 -      return 0;
 +      p->num_vcores -= num;
 +      p->resources[RES_CORES].amt_granted -= num;
 +      return self_ipi_pending;
  }
  
  /* Takes all cores from a process, which must be in an _M state.  Cores are
   * placed back in the idlecoremap.  If there's a message, such as __death or
 - * __preempt, it will be sent to the cores.
 + * __preempt, it will be sent to the cores.  The bool signals whether or not an
 + * IPI is coming in once you unlock.
   *
   * WARNING: You must hold the proc_lock before calling this! */
 -error_t proc_take_allcores(struct proc *SAFE p, amr_t message)
 +bool __proc_take_allcores(struct proc *SAFE p, amr_t message,
 +                          TV(a0t) arg0, TV(a1t) arg1, TV(a2t) arg2)
  {
        uint32_t active_vcoreid = 0;
 +      bool self_ipi_pending = FALSE;
        switch (p->state) {
                case (PROC_RUNNABLE_M):
                        assert(!message);
        }
        spin_lock(&idle_lock);
        assert(num_idlecores + p->num_vcores <= num_cpus); // sanity
 +      spin_unlock(&idle_lock);
        for (int i = 0; i < p->num_vcores; i++) {
                // find next active vcore
                active_vcoreid = get_busy_vcoreid(p, active_vcoreid);
 -              if (message)
 -                      // TODO: careful of active message deadlock (AMDL)
 -                      send_active_msg_sync(p->vcoremap[active_vcoreid], message,
 -                                           (void *SNT)0, (void *SNT)0, (void *SNT)0);
 +              if (message) {
 +                      if (p->vcoremap[active_vcoreid] == core_id())
 +                              self_ipi_pending = TRUE;
 +                      send_active_message(p->vcoremap[active_vcoreid], message,
 +                                           arg0, arg1, arg2);
 +              }
                // give the pcore back to the idlecoremap
 -              idlecoremap[num_idlecores++] = p->vcoremap[active_vcoreid];
 +              put_idle_core(p->vcoremap[active_vcoreid]);
                p->vcoremap[active_vcoreid] = -1;
        }
 -      spin_unlock(&idle_lock);
        p->num_vcores = 0;
 -      return 0;
 +      p->resources[RES_CORES].amt_granted = 0;
 +      return self_ipi_pending;
  }
  
 -/*
 - * The process refcnt is the number of places the process 'exists' in the
 - * system.  Creation counts as 1.  Having your page tables loaded somewhere
 - * (lcr3) counts as another 1.  A non-RUNNING_* process should have refcnt at
 - * least 1.  If the kernel is on another core and in a processes address space
 - * (like processing its backring), that counts as another 1.
 - *
 - * Note that the actual loading and unloading of cr3 is up to the caller, since
 - * that's not the only use for this (and decoupling is more flexible).
 - *
 - * The refcnt should always be greater than 0 for processes that aren't dying.
 - * When refcnt is 0, the process is dying and should not allow any more increfs.
 - * A process can be dying with a refcnt greater than 0, since it could be
 - * waiting for other cores to "get the message" to die, or a kernel core can be
 - * finishing work in the processes's address space.
 +/* Helper, to be used when unlocking after calling the above functions that
 + * might cause an IPI to be sent.  TODO inline this, so the __FUNCTION__ works.
 + * Will require an overhaul of core_request (break it up, etc) */
 +void __proc_unlock_ipi_pending(struct proc *p, bool ipi_pending)
 +{
 +      if (ipi_pending) {
 +              p->env_refcnt--; // TODO: (REF) (atomics)
 +              spin_unlock_irqsave(&p->proc_lock);
 +              __wait_for_ipi(__FUNCTION__);
 +      } else {
 +              spin_unlock_irqsave(&p->proc_lock);
 +      }
 +}
 +
 +
 +/* This takes a referenced process and ups the refcnt by count.  If the refcnt
 + * was already 0, then someone has a bug, so panic.  Check out the Documentation
 + * for brutal details about refcnting.
   *
   * Implementation aside, the important thing is that we atomically increment
 - * only if it wasn't already 0.  If it was 0, then we shouldn't be attaching to
 - * the process, so we return an error, which should be handled however is
 - * appropriate.  We currently use spinlocks, but some sort of clever atomics
 - * would work too.
 + * only if it wasn't already 0.  If it was 0, panic.
   *
 - * Also, no one should ever update the refcnt outside of these functions.
 - * Eventually, we'll have Ivy support for this. (TODO)
 - *
 - * TODO: (REF) change to use CAS.
 - */
 -error_t proc_incref(struct proc *p)
 + * TODO: (REF) change to use CAS / atomics. */
 +void proc_incref(struct proc *p, size_t count)
  {
 -      error_t retval = 0;
        spin_lock_irqsave(&p->proc_lock);
        if (p->env_refcnt)
 -              p->env_refcnt++;
 +              p->env_refcnt += count;
        else
 -              retval = -EBADENV;
 +              panic("Tried to incref a proc with no existing refernces!");
        spin_unlock_irqsave(&p->proc_lock);
 -      return retval;
  }
  
 -/*
 - * When the kernel is done with a process, it decrements its reference count.
 - * When the count hits 0, no one is using it and it should be freed.
 - * "Last one out" actually finalizes the death of the process.  This is tightly
 - * coupled with the previous function (incref)
 - * Be sure to load a different cr3 before calling this!
 +/* When the kernel is done with a process, it decrements its reference count.
 + * When the count hits 0, no one is using it and it should be freed.  "Last one
 + * out" actually finalizes the death of the process.  This is tightly coupled
 + * with the previous function (incref)
   *
   * TODO: (REF) change to use CAS.  Note that when we do so, we may be holding
 - * the process lock when calling env_free().
 - */
 -void proc_decref(struct proc *p)
 + * the process lock when calling __proc_free(). */
 +void proc_decref(struct proc *p, size_t count)
  {
        spin_lock_irqsave(&p->proc_lock);
 -      p->env_refcnt--;
 +      p->env_refcnt -= count;
        size_t refcnt = p->env_refcnt; // need to copy this in so it's not reloaded
        spin_unlock_irqsave(&p->proc_lock);
        // if we hit 0, no one else will increment and we can check outside the lock
        if (!refcnt)
 -              env_free(p);
 +              __proc_free(p);
 +      if (refcnt < 0)
 +              panic("Too many decrefs!");
  }
  
  /* Active message handler to start a process's context on this core.  Tightly
@@@ -939,7 -720,7 +952,8 @@@ void __startcore(trapframe_t *tf, uint3
        trapframe_t local_tf;
        trapframe_t *tf_to_pop = (trapframe_t *CT(1))a1;
  
-       printk("[kernel] Startcore on physical core %d\n", coreid);
 -      printk("[kernel] Startcore on physical core %d for Process %d\n", coreid, p_to_run->env_id);
++      printk("[kernel] Startcore on physical core %d for Process %d\n",
++             coreid, p_to_run->pid);
        assert(p_to_run);
        // TODO: handle silly state (HSS)
        if (!tf_to_pop) {
                proc_set_tfcoreid(tf_to_pop, (uint32_t)a2);
                proc_set_program_counter(tf_to_pop, p_to_run->env_entry);
        }
 +      /* the sender of the amsg increfed, thinking we weren't running current. */
 +      if (p_to_run == current)
 +              proc_decref(p_to_run, 1);
        proc_startcore(p_to_run, tf_to_pop);
  }
  
 -/* Stop running whatever context is on this core and to 'idle'.  Note this
 - * leaves no trace of what was running. This "leaves the process's context. */
 +/* Stop running whatever context is on this core, load a known-good cr3, and
 + * 'idle'.  Note this leaves no trace of what was running. This "leaves the
 + * process's context. */
  void abandon_core(void)
  {
        /* If we are currently running an address space on our core, we need a known
 -       * good pgdir before releasing the old one.  This is currently the major
 -       * practical implication of the kernel caring about a processes existence
 -       * (the inc and decref).  This decref corresponds to the incref in
 -       * proc_startcore (though it's not the only one). */
 +       * good pgdir before releasing the old one.  We decref, since current no
 +       * longer tracks the proc (and current no longer protects the cr3). */
        if (current) {
                lcr3(boot_cr3);
 -              proc_decref(current);
 -              set_cpu_curenv(NULL);
 +              proc_decref(current, 1);
 +              set_current_proc(NULL);
        }
        smp_idle();
  }
 +
  /* Active message handler to clean up the core when a process is dying.
   * Note this leaves no trace of what was running.
   * It's okay if death comes to a core that's already idling and has no current.
@@@ -991,34 -769,22 +1005,34 @@@ void print_idlecoremap(void
        spin_unlock(&idle_lock);
  }
  
 +void print_allpids(void)
 +{
 +      spin_lock(&pid_hash_lock);
 +      if (hashtable_count(pid_hash)) {
 +              hashtable_itr_t *phtable_i = hashtable_iterator(pid_hash);
 +              do {
 +                      printk("PID: %d\n", hashtable_iterator_key(phtable_i));
 +              } while (hashtable_iterator_advance(phtable_i));
 +      }
 +      spin_unlock(&pid_hash_lock);
 +}
 +
  void print_proc_info(pid_t pid)
  {
        int j = 0;
 -      struct proc *p = 0;
 -      envid2env(pid, &p, 0);
 +      struct proc *p = pid2proc(pid);
        // not concerned with a race on the state...
 -      if ((!p) || (p->state == ENV_FREE)) {
 +      if (!p) {
                printk("Bad PID.\n");
                return;
        }
 +      spinlock_debug(&p->proc_lock);
        spin_lock_irqsave(&p->proc_lock);
 -      printk("PID: %d\n", p->env_id);
 -      printk("PPID: %d\n", p->env_parent_id);
 +      printk("struct proc: %p\n", p);
 +      printk("PID: %d\n", p->pid);
 +      printk("PPID: %d\n", p->ppid);
        printk("State: 0x%08x\n", p->state);
 -      printk("Runs: %d\n", p->env_runs);
 -      printk("Refcnt: %d\n", p->env_refcnt);
 +      printk("Refcnt: %d\n", p->env_refcnt - 1); // don't report our ref
        printk("Flags: 0x%08x\n", p->env_flags);
        printk("CR3(phys): 0x%08x\n", p->env_cr3);
        printk("Num Vcores: %d\n", p->num_vcores);
        printk("Vcore 0's Last Trapframe:\n");
        print_trapframe(&p->env_tf);
        spin_unlock_irqsave(&p->proc_lock);
 +      proc_decref(p, 1); /* decref for the pid2proc reference */
  }
 -
diff --combined kern/src/slab.c
   * controlling bufctl at the top of the slab object.  Fix this with TODO (BUF).
   */
  
 +#ifdef __IVY__
 +#pragma nodeputy
 +#pragma nosharc
 +#endif
 +
  #include <slab.h>
  #include <stdio.h>
  #include <assert.h>
@@@ -35,7 -30,7 +35,7 @@@ static void __kmem_cache_create(struct 
  {
        assert(kc);
        assert(align);
 -      kc->cache_lock = 0;
 +      spinlock_init(&kc->cache_lock);
        kc->name = name;
        kc->obj_size = obj_size;
        kc->align = align;
@@@ -65,7 -60,7 +65,7 @@@
  
  void kmem_cache_init(void)
  {
 -      kmem_caches_lock = 0;
 +      spinlock_init(&kmem_caches_lock);
        SLIST_INIT(&kmem_caches);
        /* We need to call the __ version directly to bootstrap the global
         * kmem_cache_cache. */
@@@ -245,9 -240,8 +245,8 @@@ void kmem_cache_grow(struct kmem_cache 
        if (cp->obj_size <= SLAB_LARGE_CUTOFF) {
                // Just get a single page for small slabs
                page_t *a_page;
-               if (page_alloc(&a_page))
+               if (kpage_alloc(&a_page))
                        panic("[German Accent]: OOM!!!");
-               page_incref(a_page);
                // the slab struct is stored at the end of the page
                a_slab = (struct kmem_slab*)(page2kva(a_page) + PGSIZE -
                                             sizeof(struct kmem_slab));
diff --combined kern/src/syscall.c
@@@ -23,6 -23,8 +23,8 @@@
  #include <kmalloc.h>
  #include <stdio.h>
  #include <resource.h>
+ #include <colored_caches.h>
+ #include <arch/bitmask.h>
  #include <kfs.h> // eventually replace this with vfs.h
  
  #ifdef __sparc_v8__
@@@ -81,20 -83,19 +83,19 @@@ static ssize_t sys_serial_read(env_t* e
  //
  
  static ssize_t sys_run_binary(env_t* e, void *DANGEROUS binary_buf,
-                               void*DANGEROUS arg, size_t len) {
-       uint8_t *CT(len) checked_binary_buf;
-       checked_binary_buf = user_mem_assert(e, binary_buf, len, PTE_USER_RO);
-       uint8_t* new_binary = kmalloc(len, 0);
-       if(new_binary == NULL)
-               return -ENOMEM;
-       memcpy(new_binary, checked_binary_buf, len);
-       env_t* env = proc_create(new_binary, len);
-       kfree(new_binary);
+                   void*DANGEROUS arg, size_t len, size_t num_colors)
+ {
 -      env_t* env = env_create();
 -      env_load_icode(env,e,binary_buf,len);
 -
++      env_t* env = proc_create(0, 0);
++      env_load_icode(env, e, binary_buf, len);
 +      __proc_set_state(env, PROC_RUNNABLE_S);
 +      schedule_proc(env);
+       if(num_colors > 0) {
+               env->cache_colors_map = cache_colors_map_alloc();
+               for(int i=0; i<num_colors; i++)
+                       cache_color_alloc(llc_cache, env->cache_colors_map);
+       }
 -      proc_set_state(env, PROC_RUNNABLE_S);
 -      schedule_proc(env);
 +      proc_decref(env, 1);
+       proc_yield(e);
        return 0;
  }
  
@@@ -177,7 -178,7 +178,7 @@@ static ssize_t sys_eth_read(env_t* e, c
  //
  
  static ssize_t sys_shared_page_alloc(env_t* p1,
 -                                     void**DANGEROUS _addr, envid_t p2_id,
 +                                     void**DANGEROUS _addr, pid_t p2_id,
                                       int p1_flags, int p2_flags
                                      )
  {
  
        void * COUNT(1) * COUNT(1) addr = user_mem_assert(p1, _addr, sizeof(void *),
                                                        PTE_USER_RW);
-       env_t* p2 = pid2proc(p2_id);
++      struct proc *p2 = pid2proc(p2_id);
 +      if (!p2)
 +              return -EBADPROC;
 +
        page_t* page;
-       error_t e = page_alloc(&page);
 -      env_t* p2 = &(envs[ENVX(p2_id)]);
+       error_t e = upage_alloc(p1, &page);
 -
 -      if(e < 0) return e;
 +      if (e < 0) {
 +              proc_decref(p2, 1);
 +              return e;
 +      }
  
        void* p2_addr = page_insert_in_range(p2->env_pgdir, page,
-                                            (void*SNT)UTEXT, (void*SNT)UTOP, p2_flags);
+                       (void*SNT)UTEXT, (void*SNT)UTOP, p2_flags);
 -      if(p2_addr == NULL)
 +      if (p2_addr == NULL) {
 +              page_free(page);
 +              proc_decref(p2, 1);
                return -EFAIL;
 +      }
  
        void* p1_addr = page_insert_in_range(p1->env_pgdir, page,
-                                           (void*SNT)UTEXT, (void*SNT)UTOP, p1_flags);
+                       (void*SNT)UTEXT, (void*SNT)UTOP, p1_flags);
        if(p1_addr == NULL) {
                page_remove(p2->env_pgdir, p2_addr);
 +              page_free(page);
 +              proc_decref(p2, 1);
                return -EFAIL;
        }
        *addr = p1_addr;
 +      proc_decref(p2, 1);
        return ESUCCESS;
  }
  
 -static void sys_shared_page_free(env_t* p1, void*DANGEROUS addr, envid_t p2)
 +static void sys_shared_page_free(env_t* p1, void*DANGEROUS addr, pid_t p2)
  {
  }
  
@@@ -236,15 -226,15 +237,15 @@@ static void sys_cache_invalidate(void
  // address space.  It's just #defined to be some random 4MB chunk (which ought
  // to be boot_alloced or something).  Meant to grab exclusive access to cache
  // lines, to simulate doing something useful.
 -static void sys_cache_buster(env_t* e, uint32_t num_writes, uint32_t num_pages,
 -                             uint32_t flags)
 +static void sys_cache_buster(struct proc *p, uint32_t num_writes,
 +                             uint32_t num_pages, uint32_t flags)
  { TRUSTEDBLOCK /* zra: this is not really part of the kernel */
        #define BUSTER_ADDR             0xd0000000  // around 512 MB deep
        #define MAX_WRITES              1048576*8
        #define MAX_PAGES               32
        #define INSERT_ADDR     (UINFO + 2*PGSIZE) // should be free for these tests
        uint32_t* buster = (uint32_t*)BUSTER_ADDR;
 -      static uint32_t buster_lock = 0;
 +      static spinlock_t buster_lock = SPINLOCK_INITIALIZER;
        uint64_t ticks = -1;
        page_t* a_page[MAX_PAGES];
  
        if (num_pages) {
                spin_lock(&buster_lock);
                for (int i = 0; i < MIN(num_pages, MAX_PAGES); i++) {
-                       page_alloc(&a_page[i]);
 -                      upage_alloc(e, &a_page[i]);
 -                      page_insert(e->env_pgdir, a_page[i], (void*)INSERT_ADDR + PGSIZE*i,
++                      upage_alloc(p, &a_page[i]);
 +                      page_insert(p->env_pgdir, a_page[i], (void*)INSERT_ADDR + PGSIZE*i,
                                    PTE_USER_RW);
                }
                spin_unlock(&buster_lock);
        if (num_pages) {
                spin_lock(&buster_lock);
                for (int i = 0; i < MIN(num_pages, MAX_PAGES); i++) {
 -                      page_remove(e->env_pgdir, (void*)(INSERT_ADDR + PGSIZE * i));
 +                      page_remove(p->env_pgdir, (void*)(INSERT_ADDR + PGSIZE * i));
                        page_decref(a_page[i]);
                }
                spin_unlock(&buster_lock);
@@@ -312,6 -302,7 +313,6 @@@ static ssize_t sys_cputs(env_t* e, cons
  {
        // Check that the user has permission to read memory [s, s+len).
        // Destroy the environment if not.
 -      pte_t* p = pgdir_walk(e->env_pgdir,s,0);
        char *COUNT(len) _s = user_mem_assert(e, s, len, PTE_USER_RO);
  
        // Print the string supplied by the user.
@@@ -333,42 -324,51 +334,56 @@@ static uint16_t sys_cgetc(env_t* e
        return c;
  }
  
 -// Returns the current environment's envid.
 -static envid_t sys_getenvid(env_t* e)
 +/* Returns the calling process's pid */
 +static pid_t sys_getpid(struct proc *p)
  {
 -      return e->env_id;
 +      return p->pid;
  }
  
 -// Returns the id of the cpu this syscall is executed on.
 -static envid_t sys_getcpuid(void)
 +/* Returns the id of the cpu this syscall is executed on. */
 +static uint32_t sys_getcpuid(void)
  {
        return core_id();
  }
  
 -// TODO FIX Me!!!! for processes
 -// Destroy a given environment (possibly the currently running environment).
 -//
 -// Returns 0 on success, < 0 on error.  Errors are:
 -//    -EBADENV if environment envid doesn't currently exist,
 -//            or the caller doesn't have permission to change envid.
 -static error_t sys_env_destroy(env_t* e, envid_t envid)
+ // TODO: Temporary hack until thread-local storage is implemented on i386
+ static size_t sys_getvcoreid(env_t* e)
+ {
+       if(e->state == PROC_RUNNING_S)
+               return 0;
+       size_t i;
+       for(i = 0; i < e->num_vcores; i++)
+               if(core_id() == e->vcoremap[i])
+                       return i;
+       panic("virtual core id not found in sys_getvcoreid()!");
+ }
 +/* Destroy proc pid.  If this is called by the dying process, it will never
 + * return.  o/w it will return 0 on success, or an error.  Errors include:
 + * - EBADPROC: if there is no such process with pid
 + * - EPERM: if caller does not control pid */
 +static error_t sys_proc_destroy(struct proc *p, pid_t pid)
  {
 -      int r;
 -      env_t *env_to_die;
 -
 -      if ((r = envid2env(envid, &env_to_die, 1)) < 0)
 -              return r;
 -      if (env_to_die == e)
 -              printk("[%08x] exiting gracefully\n", e->env_id);
 -      else
 +      error_t r;
 +      struct proc *p_to_die = pid2proc(pid);
 +
 +      if (!p_to_die)
 +              return -EBADPROC;
 +      if (!proc_controls(p, p_to_die)) {
 +              proc_decref(p_to_die, 1);
 +              return -EPERM;
 +      }
 +      if (p_to_die == p) {
 +              // syscall code and pid2proc both have edible references, only need 1.
 +              proc_decref(p, 1);
 +              printk("[PID %d] proc exiting gracefully\n", p->pid);
 +      } else {
                panic("Destroying other processes is not supported yet.");
 -              //printk("[%08x] destroying %08x\n", e->env_id, env_to_die->env_id);
 -      proc_destroy(env_to_die);
 +              //printk("[%d] destroying proc %d\n", p->pid, p_to_die->pid);
 +      }
 +      proc_destroy(p_to_die);
        return ESUCCESS;
  }
  
@@@ -402,37 -402,48 +417,57 @@@ static int sys_proc_create(struct proc 
        if (kfs_inode < 0)
                return -EINVAL;
        struct proc *new_p = kfs_proc_create(kfs_inode);
 -      return new_p->env_id; // TODO replace this with a real proc_id
 +      pid = new_p->pid;
 +      proc_decref(new_p, 1); // let go of the reference created in proc_create()
 +      return pid;
  }
  
 -/* Makes process PID runnable.  Consider moving the functionality to env.c */
 +/* Makes process PID runnable.  Consider moving the functionality to process.c */
  static error_t sys_proc_run(struct proc *p, unsigned pid)
  {
 -      struct proc *target = get_proc(pid);
 +      struct proc *target = pid2proc(pid);
        error_t retval = 0;
 -      spin_lock_irqsave(&p->proc_lock); // note we can get interrupted here. it's not bad.
 +
 +      if (!target)
 +              return -EBADPROC;
 +      // note we can get interrupted here. it's not bad.
 +      spin_lock_irqsave(&p->proc_lock);
        // make sure we have access and it's in the right state to be activated
        if (!proc_controls(p, target)) {
 +              proc_decref(target, 1);
                retval = -EPERM;
        } else if (target->state != PROC_CREATED) {
 +              proc_decref(target, 1);
                retval = -EINVAL;
        } else {
 -              proc_set_state(target, PROC_RUNNABLE_S);
 +              __proc_set_state(target, PROC_RUNNABLE_S);
                schedule_proc(target);
        }
        spin_unlock_irqsave(&p->proc_lock);
 +      proc_decref(target, 1);
        return retval;
  }
  
 -      if((addr < p->end_text_segment) || (addr >= (void*)USTACKBOT))
+ static error_t sys_brk(struct proc *p, void* addr) {
+       size_t range;
 -      if(addr == p->end_data_segment)
++      if((addr < p->heap_bottom) || (addr >= (void*)USTACKBOT))
+               return -EINVAL;
 -      if (addr > p->end_data_segment) {
 -              range = addr - p->end_data_segment;
 -              env_segment_alloc(p, p->end_data_segment, range);
++      if(addr == p->heap_top)
+               return ESUCCESS;
 -      else if (addr < p->end_data_segment) {
 -              range = p->end_data_segment - addr;
++      if (addr > p->heap_top) {
++              range = addr - p->heap_top;
++              env_segment_alloc(p, p->heap_top, range);
+       }
 -      p->end_data_segment = addr;
++      else if (addr < p->heap_top) {
++              range = p->heap_top - addr;
+               env_segment_free(p, addr, range);
+       }
++      p->heap_top = addr;
+       return ESUCCESS;
+ }
  /* Executes the given syscall.
   *
   * Note tf is passed in, which points to the tf of the context on the kernel
   * TODO: Build a dispatch table instead of switching on the syscallno
   * Dispatches to the correct kernel function, passing the arguments.
   */
 -intreg_t syscall(struct proc *p, trapframe_t *tf, uintreg_t syscallno,
 -                 uintreg_t a1, uintreg_t a2, uintreg_t a3, uintreg_t a4,
 -                               uintreg_t a5)
 +intreg_t syscall(struct proc *p, uintreg_t syscallno, uintreg_t a1,
 +                 uintreg_t a2, uintreg_t a3, uintreg_t a4, uintreg_t a5)
  {
        // Call the function corresponding to the 'syscallno' parameter.
        // Return any appropriate return value.
        // used if we need more args, like in mmap
        int32_t _a4, _a5, _a6, *COUNT(3) args;
  
 -      assert(p); // should always have an env for every syscall
 +      assert(p); // should always have a process for every syscall
        //printk("Running syscall: %d\n", syscallno);
        if (INVALID_SYSCALL(syscallno))
                return -EINVAL;
                        return sys_cgetc(p); // this will need to block
                case SYS_getcpuid:
                        return sys_getcpuid();
+               case SYS_getvcoreid:
+                       return sys_getvcoreid(p);
                case SYS_getpid:
 -                      return sys_getenvid(p);
 +                      return sys_getpid(p);
                case SYS_proc_destroy:
 -                      return sys_env_destroy(p, (envid_t)a1);
 +                      return sys_proc_destroy(p, (pid_t)a1);
                case SYS_yield:
                        proc_yield(p);
                        return ESUCCESS;
                        _a6 = args[2];
                        return (intreg_t) mmap(p, a1, a2, a3, _a4, _a5, _a6);
                case SYS_brk:
-                       printk("brk not implemented yet\n");
-                       return -EINVAL;
+                       return sys_brk(p, (void*)a1);
                case SYS_resource_req:
 -                      /* preemptively set the return code to 0.  if it's not, it will get
 -                       * overwriten on a proper return path.  if it ends up being a core
 -                       * request from a RUNNING_S, it will never return out this way
 -                       */
 -                      proc_set_syscall_retval(tf, ESUCCESS);
 -                      return resource_req(p, a1, a2, a3);
 +                      return resource_req(p, a1, a2, a3, a4);
  
        #ifdef __i386__
                case SYS_serial_write:
                        return sys_serial_read(p, (char *DANGEROUS)a1, (size_t)a2);
        #endif
                case SYS_run_binary:
-                       return sys_run_binary(p, (char *DANGEROUS)a1,
-                                             (char* DANGEROUS)a2, (size_t)a3);
+                       return sys_run_binary(p, (char *DANGEROUS)a1, (char* DANGEROUS)a2, 
+                                                                  (size_t)a3, (size_t)a4);
        #ifdef __NETWORK__
                case SYS_eth_write:
                        return sys_eth_write(p, (char *DANGEROUS)a1, (size_t)a2);
                        return frontend_syscall_from_user(p,a1,a2,a3,a4);
        #endif
  
+               case SYS_reboot:
+                       reboot();
+                       return 0;
                default:
                        // or just return -EINVAL
 -                      panic("Invalid syscall number %d for env %x!", syscallno, *p);
 +                      panic("Invalid syscall number %d for proc %x!", syscallno, *p);
        }
        return 0xdeadbeef;
  }
  
 -intreg_t syscall_async(env_t* e, syscall_req_t *call)
 +intreg_t syscall_async(struct proc *p, syscall_req_t *call)
  {
 -      return syscall(e, NULL, call->num, call->args[0], call->args[1],
 +      return syscall(p, call->num, call->args[0], call->args[1],
                       call->args[2], call->args[3], call->args[4]);
  }
  
 -intreg_t process_generic_syscalls(env_t* e, size_t max)
 +/* You should already have a refcnt'd ref to p before calling this */
 +intreg_t process_generic_syscalls(struct proc *p, size_t max)
  {
        size_t count = 0;
 -      syscall_back_ring_t* sysbr = &e->syscallbackring;
 +      syscall_back_ring_t* sysbr = &p->syscallbackring;
  
 -      // make sure the env is still alive.
 -      // incref will return ESUCCESS on success.
 -      if (proc_incref(e))
 -              return -EFAIL;
 +      /* make sure the proc is still alive, and keep it from dying from under us
 +       * incref will return ESUCCESS on success.  This might need some thought
 +       * regarding when the incref should have happened (like by whoever passed us
 +       * the *p). */
 +      // TODO: ought to be unnecessary, if you called this right, kept here for
 +      // now in case anyone actually uses the ARSCs.
 +      proc_incref(p, 1);
  
        // max is the most we'll process.  max = 0 means do as many as possible
        while (RING_HAS_UNCONSUMED_REQUESTS(sysbr) && ((!max)||(count < max)) ) {
                        // only switch cr3 for the very first request for this queue
                        // need to switch to the right context, so we can handle the user pointer
                        // that points to a data payload of the syscall
 -                      lcr3(e->env_cr3);
 +                      lcr3(p->env_cr3);
                }
                count++;
                //printk("DEBUG PRE: sring->req_prod: %d, sring->rsp_prod: %d\n",
                syscall_rsp_t rsp;
                // this assumes we get our answer immediately for the syscall.
                syscall_req_t* req = RING_GET_REQUEST(sysbr, ++(sysbr->req_cons));
 -              rsp.retval = syscall_async(e, req);
 +              rsp.retval = syscall_async(p, req);
                // write response into the slot it came from
                memcpy(req, &rsp, sizeof(syscall_rsp_t));
                // update our counter for what we've produced (assumes we went in order!)
        }
        // load sane page tables (and don't rely on decref to do it for you).
        lcr3(boot_cr3);
 -      proc_decref(e);
 +      proc_decref(p, 1);
        return (intreg_t)count;
  }
diff --combined kern/src/testing.c
  #include <pmap.h>
  #include <slab.h>
  #include <kmalloc.h>
 +#include <hashtable.h>
  
+ #define l1 (available_caches.l1)
+ #define l2 (available_caches.l2)
+ #define l3 (available_caches.l3)
  #ifdef __i386__
  
  void test_ipi_sending(void)
@@@ -113,12 -116,13 +117,13 @@@ void test_print_info(void
  
  void test_page_coloring(void) 
  {
+ /*
        //Print the different cache properties of our machine
-       print_cache_properties("L1", &l1);
+       print_cache_properties("L1", l1);
        cprintf("\n");
-       print_cache_properties("L2", &l2);
+       print_cache_properties("L2", l2);
        cprintf("\n");
-       print_cache_properties("L3", &l3);
+       print_cache_properties("L3", l3);
        cprintf("\n");
  
        //Print some stats about our memory
        page_t* page;
  
        cprintf("Contents of the page free list:\n");
-       for(int i=0; i<llc_num_colors; i++) {
+       for(int i=0; i<llc_cache->num_colors; i++) {
                cprintf("  COLOR %d:\n", i);
                LIST_FOREACH(page, &colored_page_free_list[i], page_link) {
                        cprintf("    Page: %d\n", page2ppn(page));
  
        //Run through and allocate all pages through l1_page_alloc
        cprintf("Allocating from L1 page colors:\n");
-       for(int i=0; i<get_cache_num_page_colors(&l1); i++) {
+       for(int i=0; i<get_cache_num_page_colors(l1); i++) {
                cprintf("  COLOR %d:\n", i);
-               while(l1_page_alloc(&page, i) != -ENOMEM)
+               while(colored_page_alloc(l1, &page, i) != -ENOMEM)
                        cprintf("    Page: %d\n", page2ppn(page));
        }
  
        
        //Run through and allocate all pages through l2_page_alloc
        cprintf("Allocating from L2 page colors:\n");
-       for(int i=0; i<get_cache_num_page_colors(&l2); i++) {
+       for(int i=0; i<get_cache_num_page_colors(l2); i++) {
                cprintf("  COLOR %d:\n", i);
-               while(l2_page_alloc(&page, i) != -ENOMEM)
+               while(colored_page_alloc(l2, &page, i) != -ENOMEM)
                        cprintf("    Page: %d\n", page2ppn(page));
        }
  
        
        //Run through and allocate all pages through l3_page_alloc
        cprintf("Allocating from L3 page colors:\n");
-       for(int i=0; i<get_cache_num_page_colors(&l3); i++) {
+       for(int i=0; i<get_cache_num_page_colors(l3); i++) {
                cprintf("  COLOR %d:\n", i);
-               while(l3_page_alloc(&page, i) != -ENOMEM)
+               while(colored_page_alloc(l3, &page, i) != -ENOMEM)
                        cprintf("    Page: %d\n", page2ppn(page));
        }
        
        
        //Run through and allocate all pages through page_alloc
        cprintf("Allocating from global allocator:\n");
-       while(page_alloc(&page) != -ENOMEM)
+       while(upage_alloc(&page) != -ENOMEM)
                cprintf("    Page: %d\n", page2ppn(page));
        
-       if(l2_page_alloc(&page, 0) != -ENOMEM)
+       if(colored_page_alloc(l2, &page, 0) != -ENOMEM)
                cprintf("Should not get here, all pages should already be gone!\n");
        cprintf("All pages gone for sure...\n");
        
        page_free(&pages[6]);
        page_free(&pages[4]);
  
-       while(page_alloc(&page) != -ENOMEM)
+       while(upage_alloc(&page) != -ENOMEM)
                cprintf("Page: %d\n", page2ppn(page));  
+       
+       page_init();
+ */
+ }
+ void test_color_alloc() {
+       size_t checkpoint = 0;
+       uint8_t* colors_map = kmalloc(BYTES_FOR_BITMASK(llc_cache->num_colors), 0);
+       cache_color_alloc(l2, colors_map);
+       cache_color_alloc(l3, colors_map);
+       cache_color_alloc(l3, colors_map);
+       cache_color_alloc(l2, colors_map);
+       cache_color_free(llc_cache, colors_map);
+       cache_color_free(llc_cache, colors_map);
+       cache_color_free(llc_cache, colors_map);
+       cache_color_free(llc_cache, colors_map);
+       cache_color_free(llc_cache, colors_map);
+       cache_color_free(llc_cache, colors_map);
+       cache_color_free(llc_cache, colors_map);
+       cache_color_free(llc_cache, colors_map);
+       cache_color_free(llc_cache, colors_map);
+       cache_color_free(llc_cache, colors_map);
+       cache_color_free(llc_cache, colors_map);
+       cache_color_free(llc_cache, colors_map);
+       cache_color_free(llc_cache, colors_map);
+       cache_color_free(llc_cache, colors_map);
+       cache_color_free(llc_cache, colors_map);
+       cache_color_free(llc_cache, colors_map);
+       cache_color_free(l2, colors_map);
+       cache_color_free(llc_cache, colors_map);
+       cache_color_free(llc_cache, colors_map);
+ print_cache_colors:
+       printk("L1 free colors, tot colors: %d\n", l1->num_colors);
+       PRINT_BITMASK(l1->free_colors_map, l1->num_colors);
+       printk("L2 free colors, tot colors: %d\n", l2->num_colors);
+       PRINT_BITMASK(l2->free_colors_map, l2->num_colors);
+       printk("L3 free colors, tot colors: %d\n", l3->num_colors);
+       PRINT_BITMASK(l3->free_colors_map, l3->num_colors);
+       printk("Process allocated colors\n");
+       PRINT_BITMASK(colors_map, llc_cache->num_colors);
+       printk("test_color_alloc() complete!\n");
  }
  
  barrier_t test_cpu_array;
@@@ -543,7 -589,6 +590,7 @@@ static void sync_tests(int start_core, 
        // we want to fake a run, to reenter manager for the next case
        env_t *env = kfs_proc_create(kfs_lookup_path("roslib_null"));
        smp_call_function_single(0, run_env_handler, env, 0);
 +      // Note we are still holding references to all the processes
        process_workqueue();
        panic("whoops!\n");
  }
@@@ -574,7 -619,6 +621,7 @@@ static void async_tests(int start_core
        // we want to fake a run, to reenter manager for the next case
        env_t *env = kfs_proc_create(kfs_lookup_path("roslib_null"));
        smp_call_function_single(0, run_env_handler, env, 0);
 +      // Note we are still holding references to all the processes
        process_workqueue();
        // this all never returns
        panic("whoops!\n");
@@@ -667,7 -711,7 +714,7 @@@ void test_hello_world_handler(trapframe
                trapno, core_id(), tf);
  }
  
 -uint32_t print_info_lock = 0;
 +spinlock_t print_info_lock = SPINLOCK_INITIALIZER;
  
  void test_print_info_handler(trapframe_t *tf, void* data)
  {
@@@ -819,17 -863,16 +866,17 @@@ static void test_single_cache(int iters
        printk("\n\n\n\n");
  }
  
 +void a_ctor(void *buf, size_t size)
 +{
 +      printk("constructin tests\n");
 +}
 +void a_dtor(void *buf, size_t size)
 +{
 +      printk("destructin tests\n");
 +}
 +
  void test_slab(void)
  {
 -      void a_ctor(void *buf, size_t size)
 -      {
 -              printk("constructin tests\n");
 -      }
 -      void a_dtor(void *buf, size_t size)
 -      {
 -              printk("destructin tests\n");
 -      }
        test_single_cache(10, 128, 512, 0, 0, 0);
        test_single_cache(10, 128, 4, 0, a_ctor, a_dtor);
        test_single_cache(10, 1024, 16, 0, 0, 0);
@@@ -856,103 -899,3 +903,103 @@@ void test_kmalloc(void
        kfree(bufs[0]);
  }
  
 +static size_t test_hash_fn_col(void *k)
 +{
 +      return (size_t)k % 2; // collisions in slots 0 and 1
 +}
 +
 +void test_hashtable(void)
 +{
 +      struct test {int x; int y;};
 +      struct test tstruct[10];
 +
 +      struct hashtable *h;
 +      int k = 5;
 +      struct test *v = &tstruct[0];
 +
 +      h = create_hashtable(32, __generic_hash, __generic_eq);
 +      
 +      // test inserting one item, then finding it again
 +      printk("Tesing one item, insert, search, and removal\n");
 +      if(!hashtable_insert(h, (void*)k, v))
 +              printk("Failed to insert to hashtable!\n");
 +      v = NULL;
 +      if (!(v = hashtable_search(h, (void*)k)))
 +              printk("Failed to find in hashtable!\n");
 +      if (v != &tstruct[0])
 +              printk("Got the wrong item! (got %p, wanted %p)\n", v, &tstruct[0]);
 +      v = NULL;
 +      if (!(v = hashtable_remove(h, (void*)k)))
 +              printk("Failed to remove from hashtable!\n");
 +      // shouldn't be able to find it again
 +      if ((v = hashtable_search(h, (void*)k)))
 +              printk("Should not have been able to find in hashtable!\n");
 +      
 +      printk("Tesing a bunch of items, insert, search, and removal\n");
 +      for (int i = 0; i < 10; i++) {
 +              k = i; // vary the key, we don't do KEY collisions
 +              if(!hashtable_insert(h, (void*)k, &tstruct[i]))
 +                      printk("Failed to insert iter %d to hashtable!\n", i);
 +      }
 +      // read out the 10 items
 +      for (int i = 0; i < 10; i++) {
 +              k = i;
 +              if (!(v = hashtable_search(h, (void*)k)))
 +                      printk("Failed to find in hashtable!\n");
 +              if (v != &tstruct[i])
 +                      printk("Got the wrong item! (got %p, wanted %p)\n", v, &tstruct[i]);
 +      }
 +      if (hashtable_count(h) != 10)
 +              printk("Wrong accounting of number of elements!\n");
 +      // remove the 10 items
 +      for (int i = 0; i < 10; i++) {
 +              k = i;
 +              if (!(v = hashtable_remove(h, (void*)k)))
 +                      printk("Failed to remove from hashtable!\n");
 +      }
 +      // make sure they are all gone
 +      for (int i = 0; i < 10; i++) {
 +              k = i;
 +              if ((v = hashtable_search(h, (void*)k)))
 +                      printk("Should not have been able to find in hashtable!\n");
 +      }
 +      if (hashtable_count(h))
 +              printk("Wrong accounting of number of elements!\n");
 +      hashtable_destroy(h);
 +
 +      // same test of a bunch of items, but with collisions.
 +      printk("Tesing a bunch of items with collisions, etc.\n");
 +      h = create_hashtable(32, test_hash_fn_col, __generic_eq);
 +      // insert 10 items
 +      for (int i = 0; i < 10; i++) {
 +              k = i; // vary the key, we don't do KEY collisions
 +              if(!hashtable_insert(h, (void*)k, &tstruct[i]))
 +                      printk("Failed to insert iter %d to hashtable!\n", i);
 +      }
 +      // read out the 10 items
 +      for (int i = 0; i < 10; i++) {
 +              k = i;
 +              if (!(v = hashtable_search(h, (void*)k)))
 +                      printk("Failed to find in hashtable!\n");
 +              if (v != &tstruct[i])
 +                      printk("Got the wrong item! (got %p, wanted %p)\n", v, &tstruct[i]);
 +      }
 +      if (hashtable_count(h) != 10)
 +              printk("Wrong accounting of number of elements!\n");
 +      // remove the 10 items
 +      for (int i = 0; i < 10; i++) {
 +              k = i;
 +              if (!(v = hashtable_remove(h, (void*)k)))
 +                      printk("Failed to remove from hashtable!\n");
 +      }
 +      // make sure they are all gone
 +      for (int i = 0; i < 10; i++) {
 +              k = i;
 +              if ((v = hashtable_search(h, (void*)k)))
 +                      printk("Should not have been able to find in hashtable!\n");
 +      }
 +      if (hashtable_count(h))
 +              printk("Wrong accounting of number of elements!\n");
 +      hashtable_destroy(h);
 +}
 +
@@@ -10,8 -10,8 +10,8 @@@
  
  extern char * readline(const char *prompt);
  
+ #define MALLOC_SIZE     1048576
  #define READ_SIZE       1024
- uint8_t* binary_buf;
  
  static void fd_error() {
        fprintf(stderr, "Error: Unable to run remote binary (fd error): %s\n", strerror(errno));
@@@ -22,13 -22,13 +22,13 @@@ static void malloc_error() 
  }
  
  static void read_error(void* buf, int fd) {
-       free(binary_buf);
+       free(buf);
        close(fd);
        fprintf(stderr, "Error: Unable to run remote binary (read error): %s\n", strerror(errno));
  }
  
  static void realloc_error(void* buf, int fd) {
-       free(binary_buf);
+       free(buf);
        close(fd);
        fprintf(stderr, "Error: Unable to run remote binary: No more memory available!\n");
  }
@@@ -40,34 -40,38 +40,39 @@@ void run_binary(
                printf("Error reading from console.\n");
                return;
        }
-       char * file_name = malloc(strlen(readline_result) + 8);
-       sprintf(file_name, "./apps/%s", readline_result);
+       char* file_name = readline_result;
+       //char * file_name = malloc(strlen(readline_result) + 8);
+       //sprintf(file_name, "./apps/%s", readline_result);
        int fd = open(file_name, O_RDONLY, 0);
-       free(file_name);
+       //free(file_name);
        if(fd < 0) { fd_error(); return; };
        
-       int iters = 1;
-       binary_buf = malloc(READ_SIZE);
-       if(binary_buf == NULL) { malloc_error(); return; }
-       
        int total_bytes_read = 0;
-       int bytes_read = read(fd, binary_buf, READ_SIZE);
-       if(bytes_read < 0) { read_error(binary_buf, fd); return; }
+       int bytes_read = 0;
+       int bufsz = 0;
+       void* binary_buf = NULL;
        
-       while(bytes_read > 0) {
-               total_bytes_read += bytes_read; 
-               void* temp_buf = realloc(binary_buf, READ_SIZE*(++iters));
-               if(temp_buf == NULL) { realloc_error(binary_buf, fd); return; } 
-               binary_buf = temp_buf;
+       while(1) {
+               if(total_bytes_read+READ_SIZE > bufsz)
+               {
+                       void* temp_buf = realloc(binary_buf,bufsz+MALLOC_SIZE);
+                       if(temp_buf == NULL) { realloc_error(binary_buf, fd); return; }
+                       binary_buf = temp_buf;
+                       bufsz += MALLOC_SIZE;
+               }
                bytes_read = read(fd, binary_buf+total_bytes_read, READ_SIZE);
+               total_bytes_read += bytes_read;
                if(bytes_read < 0) { read_error(binary_buf, fd); return; }
+               if(bytes_read == 0) break;
        }
        printf("Loading Binary: %s, ROMSIZE: %d\n", readline_result, total_bytes_read);
-       ssize_t error = sys_run_binary(binary_buf, NULL, total_bytes_read);
+       ssize_t error = sys_run_binary(binary_buf, NULL, total_bytes_read, 0);
        if(error < 0) {
                fprintf(stderr, "Error: Unable to run remote binary\n");
        }
        free(binary_buf);
        close(fd);
++      syscall(SYS_yield,0,0,0,0,0);
  }
  
index 0000000,b0d25c6..9e60ba0
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,86 +1,87 @@@
+ #include <string.h>
+ #include <stdlib.h>
+ #include <sys/types.h>
+ #include <sys/stat.h>
+ #include <fcntl.h>
+ #include <stdint.h>
+ #include <stdio.h>
+ #include <unistd.h>
+ #include <parlib.h>
+ extern char * readline(const char *prompt);
+ #define READ_SIZE       1024
+ uint8_t* binary_buf;
+ static void fd_error() {
+       fprintf(stderr, "Error: Unable to run remote binary (fd error): %s\n", 
+                                                             strerror(errno));
+ }
+ static void malloc_error() {
+       fprintf(stderr, 
+               "Error: Unable to run remote binary: No more memory avaialable!\n");
+ }
+ static void read_error(void* buf, int fd) {
+       free(binary_buf);
+       close(fd);
+       fprintf(stderr, "Error: Unable to run remote binary (read error): %s\n", 
+                                                               strerror(errno));
+ }
+ static void realloc_error(void* buf, int fd) {
+       free(binary_buf);
+       close(fd);
+       fprintf(stderr, 
+               "Error: Unable to run remote binary: No more memory available!\n");
+ }
+ void run_binary_colored()
+ {     
+       char* name = readline("\nEnter name of binary to execute: ");
+       if (name == NULL) {
+               printf("Error reading from console.\n");
+               return;
+       }
+       char* file_name = name;
+       //char * file_name = malloc(strlen(name) + 8);
+       //sprintf(file_name, "./apps/%s", name);
+       int fd = open(file_name, O_RDONLY, 0);
+       //free(file_name);
+       if(fd < 0) { fd_error(); return; };
+       char* colors = readline("\nEnter number of colors: ");
+       if (colors == NULL) {
+               printf("Error reading from console.\n");
+               return;
+       }
+       size_t num_colors = atoi(colors);
+       
+       int iters = 1;
+       binary_buf = malloc(READ_SIZE);
+       if(binary_buf == NULL) { malloc_error(); return; }
+       
+       int total_bytes_read = 0;
+       int bytes_read = read(fd, binary_buf, READ_SIZE);
+       if(bytes_read < 0) { read_error(binary_buf, fd); return; }
+       
+       while(bytes_read > 0) {
+               total_bytes_read += bytes_read; 
+               void* temp_buf = realloc(binary_buf, READ_SIZE*(++iters));
+               if(temp_buf == NULL) { realloc_error(binary_buf, fd); return; } 
+               binary_buf = temp_buf;
+               bytes_read = read(fd, binary_buf+total_bytes_read, READ_SIZE);
+               if(bytes_read < 0) { read_error(binary_buf, fd); return; }
+       }
+       printf("Loading Binary: %s, ROMSIZE: %d\n", name, total_bytes_read);
+       ssize_t error = sys_run_binary(binary_buf, NULL, 
+                           total_bytes_read, num_colors);
+       if(error < 0) {
+               fprintf(stderr, "Error: Unable to run remote binary\n");
+       }
+       free(binary_buf);
+       close(fd);
++      syscall(SYS_yield,0,0,0,0,0);
+ }
@@@ -1,4 -1,3 +1,3 @@@
- #include <arch/mmu.h>
  #include <ros/memlayout.h>
  
  .data
        .set procinfo, UINFO
        .globl procdata
        .set procdata, UDATA
-       .globl pages
-       .set pages, UPAGES
-       .globl vpt
-       .set vpt, UVPT
-       .globl vpd
-       .set vpd, (UVPT+(UVPT>>12)*4)
+ // Stack pointers, to be allocated by the hart lib
+       .globl stack_ptr_array
+       stack_ptr_array:
+               .word 0
++      .globl tls_array
++      tls_array:
++              .word 0
+ // TODO: We're not exposing these yet.  Think about how to do so judiciously.
+ //    .globl vpt
+ //    .set vpt, UVPT
+ //    .globl vpd
+ //    .set vpd, (UVPT+(UVPT>>12)*4)
  
  
  // Entrypoint - this is where the kernel (or our parent environment)
  .text
  .globl _start
  _start:
-       // See if we were started with arguments on the stack
-       cmpl $USTACKTOP, %esp
-       jne args_exist
+       // Are we core 0?
+       test    %eax,%eax
+       jne     notcore0
  
-       // 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:
+       // argc/argv are provided by procinfo and are accessed in parlibmain
        call parlibmain
- 1:      jmp 1b
  
+       // illegal instruction in case parlibmain accidentally returns
+       mov     (0),%eax
+ notcore0:
+       mov     stack_ptr_array,%edx
+       mov     (%edx,%eax,4),%esp
+       call    hart_entry
+       call    hart_yield
+       // illegal instruction in case hart_yield accidentally returns
+       mov     (0),%eax
diff --combined user/roslib/inc/lib.h
@@@ -24,6 -24,7 +24,7 @@@
  // libos.c or entry.S
  extern char *NTS binaryname;
  extern procinfo_t* procinfo;
+ extern void** stack_ptr_array;
  extern procdata_t* procdata;
  extern syscall_front_ring_t syscallfrontring;
  extern sysevent_back_ring_t syseventbackring;
@@@ -69,8 -70,7 +70,8 @@@ error_t     sys_proc_run(int pid)
  void *COUNT(length) sys_mmap(void *SNT addr, size_t length, int prot, int flags,
                               int fd, size_t offset);
  /* Resource Requests */
 -ssize_t sys_resource_req(int type, size_t amount, uint32_t flags);
 +ssize_t sys_resource_req(int type, size_t amt_wanted, size_t amt_wanted_min,
 +                         uint32_t flags);
  
  /* Generic Async Call */
  error_t     waiton_syscall(syscall_desc_t* desc, syscall_rsp_t*COUNT(1) rsp);
@@@ -118,7 -118,6 +119,7 @@@ error_t get_all_desc(async_desc_t** a_d
  // kernel's arch folder
  uint32_t newcore(void);
  void setvcore0(void);
 +void prepare_for_multi_mode(void);
  
  /* File open modes */
  #define       O_RDONLY        0x0000          /* open for reading only */