Merge branch 'master' into proc-work
authorBarret Rhoden <brho@cs.berkeley.edu>
Tue, 4 Aug 2009 00:08:31 +0000 (17:08 -0700)
committerBarret Rhoden <brho@cs.berkeley.edu>
Tue, 4 Aug 2009 00:08:31 +0000 (17:08 -0700)
Conflicts:
kern/arch/i386/trap.c
kern/arch/i386/x86.h
kern/boot
kern/include/env.h
kern/include/monitor.h
kern/include/ros/env.h
kern/include/ros/syscall.h
kern/include/smp.h
kern/src/Makefrag
kern/src/atomic.c
kern/src/env.c
kern/src/init.c
kern/src/manager.c
kern/src/monitor.c
kern/src/pmap.c
kern/src/smp.c
kern/src/syscall.c
kern/src/testing.c
kern/src/workqueue.c
user/parlib/inc/parlib.h
user/parlib/src/syscall.c

44 files changed:
1  2 
kern/arch/i386/apic.c
kern/arch/i386/apic.h
kern/arch/i386/cpuinfo.c
kern/arch/i386/env.c
kern/arch/i386/kdebug.c
kern/arch/i386/pmap.c
kern/arch/i386/smp.c
kern/arch/i386/smp.h
kern/arch/i386/smp_boot.c
kern/arch/i386/trap.c
kern/include/env.h
kern/include/kfs.h
kern/include/monitor.h
kern/include/pmap.h
kern/include/process.h
kern/include/ros/error.h
kern/include/ros/syscall.h
kern/include/smp.h
kern/include/syscall.h
kern/include/workqueue.h
kern/src/Makefrag
kern/src/env.c
kern/src/init.c
kern/src/kfs.c
kern/src/manager.c
kern/src/monitor.c
kern/src/pmap.c
kern/src/process.c
kern/src/smp.c
kern/src/syscall.c
kern/src/testing.c
kern/src/workqueue.c
user/apps/roslib/measurements.c
user/apps/roslib/proctests.c
user/parlib/inc/channel.h
user/parlib/inc/parlib.h
user/parlib/src/channel.c
user/parlib/src/newlib_backend.c
user/parlib/src/syscall.c
user/roslib/inc/lib.h
user/roslib/inc/measure.h
user/roslib/src/entry_i386.S
user/roslib/src/entry_sparc.S
user/roslib/src/syscall.c

index 0000000,ee4817e..8edc67e
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,220 +1,221 @@@
+ /*
+  * Copyright (c) 2009 The Regents of the University of California
++ * Barret Rhoden <brho@cs.berkeley.edu>
+  * See LICENSE for details.
+  */
+ #include <arch/mmu.h>
+ #include <arch/x86.h>
+ #include <arch/arch.h>
+ #include <arch/apic.h>
+ #include <ros/timer.h>
+ #include <assert.h>
+ system_timing_t system_timing = {0, 0, 0xffff, 0};
+ /*
+  * Remaps the Programmable Interrupt Controller to use IRQs 32-47
+  * http://wiki.osdev.org/PIC
+  * Not 100% on this stuff, after looking over 
+  * http://bochs.sourceforge.net/techspec/PORTS.LST  The cascading and other 
+  * stuff might need to be in one command, and after that all we are doing
+  * is toggling masks.
+  */
+ void pic_remap() 
+ {
+       // start initialization
+       outb(PIC1_CMD, 0x11);
+       outb(PIC2_CMD, 0x11);
+       // set new offsets
+       outb(PIC1_DATA, PIC1_OFFSET);
+       outb(PIC2_DATA, PIC2_OFFSET);
+       // set up cascading
+       outb(PIC1_DATA, 0x04);
+       outb(PIC2_DATA, 0x02);
+       // other stuff (put in 8086/88 mode, or whatever)
+       outb(PIC1_DATA, 0x01);
+       outb(PIC2_DATA, 0x01);
+       // set masks, defaulting to all masked for now
+       outb(PIC1_DATA, 0xff);
+       outb(PIC2_DATA, 0xff);
+ }
+ void pic_mask_irq(uint8_t irq)
+ {
+       if (irq > 7)
+               outb(PIC2_DATA, inb(PIC2_DATA) | (1 << (irq - 8)));
+       else
+               outb(PIC1_DATA, inb(PIC1_DATA) | (1 << irq));
+ }
+ void pic_unmask_irq(uint8_t irq)
+ {
+       if (irq > 7) {
+               outb(PIC2_DATA, inb(PIC2_DATA) & ~(1 << (irq - 8)));
+               outb(PIC1_DATA, inb(PIC1_DATA) & 0xfb); // make sure irq2 is unmasked
+       } else
+               outb(PIC1_DATA, inb(PIC1_DATA) & ~(1 << irq));
+ }
+ /*
+  * Sets the LAPIC timer to go off after a certain number of ticks.  The primary
+  * clock freq is actually the bus clock, which we figure out during timer_init
+  * Unmasking is implied.  Ref SDM, 3A, 9.6.4
+  */
+ void __lapic_set_timer(uint32_t ticks, uint8_t vec, bool periodic, uint8_t div)
+ {
+       // clears bottom bit and then set divider
+       write_mmreg32(LAPIC_TIMER_DIVIDE, (read_mmreg32(LAPIC_TIMER_DIVIDE) &~0xf) |
+                     (div & 0xf));
+       // set LVT with interrupt handling information
+       write_mmreg32(LAPIC_LVT_TIMER, vec | (periodic << 17));
+       write_mmreg32(LAPIC_TIMER_INIT, ticks);
+       // For debugging when we expand this
+       //cprintf("LAPIC LVT Timer: 0x%08x\n", read_mmreg32(LAPIC_LVT_TIMER));
+       //cprintf("LAPIC Init Count: 0x%08x\n", read_mmreg32(LAPIC_TIMER_INIT));
+       //cprintf("LAPIC Current Count: 0x%08x\n", read_mmreg32(LAPIC_TIMER_CURRENT));
+ }
+ void lapic_set_timer(uint32_t usec, bool periodic)
+ {
+       // divide the bus clock by 128, which is the max.
+       uint32_t ticks = (usec * system_timing.bus_freq / 128) / 1000000;
+       __lapic_set_timer(ticks, LAPIC_TIMER_DEFAULT_VECTOR, periodic,
+                         LAPIC_TIMER_DEFAULT_DIVISOR);
+ }
+ uint32_t lapic_get_default_id(void)
+ {
+       uint32_t ebx;
+       cpuid(1, 0, &ebx, 0, 0);
+       // p6 family only uses 4 bits here, and 0xf is reserved for the IOAPIC
+       return (ebx & 0xFF000000) >> 24;
+ }
+ // timer init calibrates both tsc timer and lapic timer using PIT
+ void timer_init(void){
+       uint64_t tscval[2];
+       long timercount[2];
+       pit_set_timer(0xffff, TIMER_RATEGEN);
+       // assume tsc exist
+       tscval[0] = read_tsc();
+       udelay_pit(1000000);
+       tscval[1] = read_tsc();
+       system_timing.tsc_freq = tscval[1] - tscval[0];
+       
+       cprintf("TSC Frequency: %llu\n", system_timing.tsc_freq);
+       __lapic_set_timer(0xffffffff, LAPIC_TIMER_DEFAULT_VECTOR, FALSE,
+                         LAPIC_TIMER_DEFAULT_DIVISOR);
+       // Mask the LAPIC Timer, so we never receive this interrupt (minor race)
+       mask_lapic_lvt(LAPIC_LVT_TIMER);
+       timercount[0] = read_mmreg32(LAPIC_TIMER_CURRENT);
+       udelay_pit(1000000);
+       timercount[1] = read_mmreg32(LAPIC_TIMER_CURRENT);
+       system_timing.bus_freq = (timercount[0] - timercount[1])*128;
+               
+       cprintf("Bus Frequency: %llu\n", system_timing.bus_freq);
+ }
+ void pit_set_timer(uint32_t divisor, uint32_t mode)
+ {
+       if (divisor & 0xffff0000)
+               warn("Divisor too large!");
+       mode = TIMER_SEL0|TIMER_16BIT|mode;
+       outb(TIMER_MODE, mode); 
+       outb(TIMER_CNTR0, divisor & 0xff);
+       outb(TIMER_CNTR0, (divisor >> 8) );
+       system_timing.pit_mode = mode;
+       system_timing.pit_divisor = divisor;
+       // cprintf("timer mode set to %d, divisor %d\n",mode, divisor);
+ }
+ static int getpit()
+ {
+     int high, low;
+       // TODO: need a lock to protect access to PIT
+     /* Select timer0 and latch counter value. */
+     outb(TIMER_MODE, TIMER_SEL0 | TIMER_LATCH);
+     
+     low = inb(TIMER_CNTR0);
+     high = inb(TIMER_CNTR0);
+     return ((high << 8) | low);
+ }
+ // forces cpu to relax for usec miliseconds
+ void udelay(uint64_t usec)
+ {
+       #if !defined(__BOCHS__)
+       if (system_timing.tsc_freq != 0)
+       {
+               uint64_t start, end, now;
+               start = read_tsc();
+         end = start + (system_timing.tsc_freq * usec) / 1000000;
+         //cprintf("start %llu, end %llu\n", start, end);
+               if (end == 0) cprintf("This is terribly wrong \n");
+               do {
+             cpu_relax();
+             now = read_tsc();
+                       //cprintf("now %llu\n", now);
+               } while (now < end || (now > start && end < start));
+         return;
+       } else
+       #endif
+       {
+               udelay_pit(usec);
+       }
+ }
+ void udelay_pit(uint64_t usec)
+ {
+       
+       int64_t delta, prev_tick, tick, ticks_left;
+       prev_tick = getpit();
+       /*
+        * Calculate (n * (i8254_freq / 1e6)) without using floating point
+        * and without any avoidable overflows.
+        */
+       if (usec <= 0)
+               ticks_left = 0;
+       // some optimization from bsd code
+       else if (usec < 256)
+               /*
+                * Use fixed point to avoid a slow division by 1000000.
+                * 39099 = 1193182 * 2^15 / 10^6 rounded to nearest.
+                * 2^15 is the first power of 2 that gives exact results
+                * for n between 0 and 256.
+                */
+               ticks_left = ((uint64_t)usec * 39099 + (1 << 15) - 1) >> 15;
+       else
+               // round up the ticks left
+               ticks_left = ((uint64_t)usec * (long long)PIT_FREQ+ 999999)
+                            / 1000000; 
+       while (ticks_left > 0) {
+               tick = getpit();
+               delta = prev_tick - tick;
+               prev_tick = tick;
+               if (delta < 0) {
+                       // counter looped around during the delta time period
+                       delta += system_timing.pit_divisor; // maximum count 
+                       if (delta < 0)
+                               delta = 0;
+               }
+               ticks_left -= delta;
+       }
+ }
+ uint64_t gettimer(void)
+ {
+       return read_tsc();      
+ }
+ uint64_t getfreq(void)
+ {
+       return system_timing.tsc_freq;
+ }
index 0000000,1df0369..2a5863f
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,229 +1,236 @@@
+ /*
+  * Copyright (c) 2009 The Regents of the University of California
++ * Barret Rhoden <brho@cs.berkeley.edu>
+  * See LICENSE for details.
+  */
+ #ifndef ROS_KERN_APIC_H
+ #define ROS_KERN_APIC_H
+ /* 
+  * Functions and definitions for dealing with the APIC and PIC, specific to
+  * Intel.  Does not handle an x2APIC.
+  */
+ #include <arch/mmu.h>
+ #include <arch/x86.h>
+ // PIC
+ #define PIC1_CMD                                      0x20
+ #define PIC1_DATA                                     0x21
+ #define PIC2_CMD                                      0xA0
+ #define PIC2_DATA                                     0xA1
+ // These are also hardcoded into the IRQ_HANDLERs of kern/trapentry.S
+ #define PIC1_OFFSET                                   0x20
+ #define PIC2_OFFSET                                   0x28
+ #define PIC_EOI                                               0x20
+ // Local APIC
+ #define LAPIC_BASE                                    0xfee00000 // this is the default, can be changed
+ #define LAPIC_EOI                                     (LAPIC_BASE + 0x0b0)
+ #define LAPIC_SPURIOUS                                (LAPIC_BASE + 0x0f0)
+ #define LAPIC_VERSION                         (LAPIC_BASE + 0x030)
+ #define LAPIC_ERROR                                   (LAPIC_BASE + 0x280)
+ #define LAPIC_ID                                      (LAPIC_BASE + 0x020)
+ #define LAPIC_LOGICAL_ID                      (LAPIC_BASE + 0x0d0)
+ // LAPIC Local Vector Table
+ #define LAPIC_LVT_TIMER                               (LAPIC_BASE + 0x320)
+ #define LAPIC_LVT_LINT0                               (LAPIC_BASE + 0x350)
+ #define LAPIC_LVT_LINT1                               (LAPIC_BASE + 0x360)
+ #define LAPIC_LVT_ERROR                               (LAPIC_BASE + 0x370)
+ #define LAPIC_LVT_PERFMON                     (LAPIC_BASE + 0x340)
+ #define LAPIC_LVT_THERMAL                     (LAPIC_BASE + 0x330)
+ #define LAPIC_LVT_MASK                                0x00010000
+ // LAPIC Timer
+ #define LAPIC_TIMER_INIT                      (LAPIC_BASE + 0x380)
+ #define LAPIC_TIMER_CURRENT                   (LAPIC_BASE + 0x390)
+ #define LAPIC_TIMER_DIVIDE                    (LAPIC_BASE + 0x3e0)
+ #define LAPIC_TIMER_DEFAULT_VECTOR    0xeb
+ #define LAPIC_TIMER_DEFAULT_DIVISOR   0xa // This is 128.  Ref SDM 3.a 9.6.4
+ // IPI Interrupt Command Register
+ #define LAPIC_IPI_ICR_LOWER                   (LAPIC_BASE + 0x300)
+ #define LAPIC_IPI_ICR_UPPER                   (LAPIC_BASE + 0x310)
+ // IOAPIC
+ #define IOAPIC_BASE                                   0xfec00000 // this is the default, can be changed
+ // PIT (Programmable Interval Timer)
+ #define       TIMER_REG_CNTR0 0       /* timer 0 counter port */
+ #define       TIMER_REG_CNTR1 1       /* timer 1 counter port */
+ #define       TIMER_REG_CNTR2 2       /* timer 2 counter port */
+ #define       TIMER_REG_MODE  3       /* timer mode port */
+ #define       TIMER_SEL0      0x00    /* select counter 0 */
+ #define       TIMER_SEL1      0x40    /* select counter 1 */
+ #define       TIMER_SEL2      0x80    /* select counter 2 */
+ #define       TIMER_INTTC     0x00    /* mode 0, intr on terminal cnt */
+ #define       TIMER_ONESHOT   0x02    /* mode 1, one shot */
+ #define       TIMER_RATEGEN   0x04    /* mode 2, rate generator */
+ #define       TIMER_SQWAVE    0x06    /* mode 3, square wave */
+ #define       TIMER_SWSTROBE  0x08    /* mode 4, s/w triggered strobe */
+ #define       TIMER_HWSTROBE  0x0a    /* mode 5, h/w triggered strobe */
+ #define       TIMER_LATCH     0x00    /* latch counter for reading */
+ #define       TIMER_LSB       0x10    /* r/w counter LSB */
+ #define       TIMER_MSB       0x20    /* r/w counter MSB */
+ #define       TIMER_16BIT     0x30    /* r/w counter 16 bits, LSB first */
+ #define       TIMER_BCD       0x01    /* count in BCD */
+ #define PIT_FREQ                                      1193182
+ #define IO_TIMER1   0x40        /* 8253 Timer #1 */
+ #define TIMER_CNTR0 (IO_TIMER1 + TIMER_REG_CNTR0)
+ #define TIMER_CNTR1 (IO_TIMER1 + TIMER_REG_CNTR1)
+ #define TIMER_CNTR2 (IO_TIMER1 + TIMER_REG_CNTR2)
+ #define TIMER_MODE  (IO_TIMER1 + TIMER_REG_MODE)
+ typedef struct system_timing {
+       uint64_t tsc_freq;
+       uint64_t bus_freq;
+       uint16_t pit_divisor;
+       uint8_t pit_mode;
+ } system_timing_t;
+ extern system_timing_t system_timing;
+ void pic_remap(void);
+ void pic_mask_irq(uint8_t irq);
+ void pic_unmask_irq(uint8_t irq);
+ void __lapic_set_timer(uint32_t ticks, uint8_t vec, bool periodic, uint8_t div);
+ void lapic_set_timer(uint32_t usec, bool periodic);
+ uint32_t lapic_get_default_id(void);
+ // PIT related
+ void pit_set_timer(uint32_t freq, uint32_t mode);
+ void timer_init(void);
+ void udelay(uint64_t usec);
+ void udelay_pit(uint64_t usec);
+ // TODO: right now timer defaults to TSC
+ uint64_t gettimer(void);
+ uint64_t inline getfreq(void);
+ static inline void pic_send_eoi(uint32_t irq);
+ static inline void lapic_send_eoi(void);
+ static inline uint32_t lapic_get_version(void);
+ static inline uint32_t lapic_get_error(void);
+ static inline uint32_t lapic_get_id(void);
++static inline void lapic_set_id(uint8_t id); // Careful, may not actually work
+ static inline uint8_t lapic_get_logid(void);
+ static inline void lapic_set_logid(uint8_t id);
+ static inline void lapic_disable(void);
+ static inline void lapic_enable(void);
+ static inline void lapic_wait_to_send(void);
+ static inline void send_init_ipi(void);
+ static inline void send_startup_ipi(uint8_t vector);
+ static inline void send_self_ipi(uint8_t vector);
+ static inline void send_broadcast_ipi(uint8_t vector);
+ static inline void send_all_others_ipi(uint8_t vector);
+ static inline void send_ipi(uint8_t dest, bool logical_mode, uint8_t vector);
+ #define mask_lapic_lvt(entry) \
+       write_mmreg32(entry, read_mmreg32(entry) | LAPIC_LVT_MASK)
+ #define unmask_lapic_lvt(entry) \
+       write_mmreg32(entry, read_mmreg32(entry) & ~LAPIC_LVT_MASK)
+ static inline void pic_send_eoi(uint32_t irq)
+ {
+       // all irqs beyond the first seven need to be chained to the slave
+       if (irq > 7)
+               outb(PIC2_CMD, PIC_EOI);
+       outb(PIC1_CMD, PIC_EOI);
+ }
+ static inline void lapic_send_eoi(void)
+ {
+       write_mmreg32(LAPIC_EOI, 0);
+ }
+ static inline uint32_t lapic_get_version(void)
+ {
+       return read_mmreg32(LAPIC_VERSION);     
+ }
+ static inline uint32_t lapic_get_error(void)
+ {
+       write_mmreg32(LAPIC_ERROR, 0xdeadbeef);
+       return read_mmreg32(LAPIC_ERROR);
+ }
+ static inline uint32_t lapic_get_id(void)
+ {
+       return read_mmreg32(LAPIC_ID) >> 24;
+ }
++static inline void lapic_set_id(uint8_t id)
++{
++      write_mmreg32(LAPIC_ID, id << 24);
++}
++
+ static inline uint8_t lapic_get_logid(void)
+ {
+       return read_mmreg32(LAPIC_LOGICAL_ID) >> 24;
+ }
+ static inline void lapic_set_logid(uint8_t id)
+ {
+       write_mmreg32(LAPIC_LOGICAL_ID, id << 24);
+ }
+ /* There are a couple ways to do it.  The MSR route doesn't seem to work
+  * in KVM.  It's also a somewhat permanent thing
+  */
+ static inline void lapic_disable(void)
+ {
+       write_mmreg32(LAPIC_SPURIOUS, read_mmreg32(LAPIC_SPURIOUS) & 0xffffefff);
+       //write_msr(IA32_APIC_BASE, read_msr(IA32_APIC_BASE) & ~MSR_APIC_ENABLE);
+ }
+ /* Spins until previous IPIs are delivered.  Not sure if we want it inlined
+  * Also not sure when we really need to do this. 
+  */
+ static inline void lapic_wait_to_send(void)
+ {
+       static inline void cpu_relax(void);
+       while(read_mmreg32(LAPIC_IPI_ICR_LOWER) & 0x1000)
+               cpu_relax();
+ }
+ static inline void lapic_enable(void)
+ {
+       write_mmreg32(LAPIC_SPURIOUS, read_mmreg32(LAPIC_SPURIOUS) | 0x00000100);
+ }
+ static inline void send_init_ipi(void)
+ {
+       write_mmreg32(LAPIC_IPI_ICR_LOWER, 0x000c4500);
+ }
+ static inline void send_startup_ipi(uint8_t vector)
+ {
+       write_mmreg32(LAPIC_IPI_ICR_LOWER, 0x000c4600 | vector);
+ }
+ static inline void send_self_ipi(uint8_t vector)
+ {
+       write_mmreg32(LAPIC_IPI_ICR_LOWER, 0x00044000 | vector);
+ }
+ static inline void send_broadcast_ipi(uint8_t vector)
+ {
+       write_mmreg32(LAPIC_IPI_ICR_LOWER, 0x00084000 | vector);
+ }
+ static inline void send_all_others_ipi(uint8_t vector)
+ {
+       write_mmreg32(LAPIC_IPI_ICR_LOWER, 0x000c4000 | vector);
+ }
+ static inline void send_ipi(uint8_t dest, bool logical_mode, uint8_t vector)
+ {
+       write_mmreg32(LAPIC_IPI_ICR_UPPER, dest << 24);
+       write_mmreg32(LAPIC_IPI_ICR_LOWER, 0x00004000 | (logical_mode << 11) | vector);
+ }
+ /* To change the LAPIC Base (not recommended):
+       msr_val = read_msr(IA32_APIC_BASE);
+       msr_val = msr_val & ~MSR_APIC_BASE_ADDRESS | 0xfaa00000;
+       write_msr(IA32_APIC_BASE, msr_val);
+ */
+ #endif /* ROS_KERN_APIC_H */
index 0000000,077f0fb..61f1f82
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,143 +1,146 @@@
 -/* See COPYRIGHT for copyright information. */
++/*
++ * Copyright (c) 2009 The Regents of the University of California
++ * Barret Rhoden <brho@cs.berkeley.edu>
++ * See LICENSE for details.
++ */
+ #ifdef __DEPUTY__
+ #pragma nodeputy
+ #endif
+ #include <arch/arch.h>
+ #include <arch/x86.h>
+ #include <arch/mmu.h>
+ #include <stdio.h>
+ #include <assert.h>
+ #include <ros/memlayout.h>
+ #include <pmap.h>
+ #include <kdebug.h>
+ #include <string.h>
 -void
 -print_cpuinfo(void) {
++void print_cpuinfo(void)
++{
+       uint32_t eax, ebx, ecx, edx;
+       uint32_t model, family;
+       uint64_t msr_val;
+       char vendor_id[13];
+       extern char (SNT _start)[];
+       asm volatile ("cpuid;"
+                 "movl    %%ebx, (%2);"
+                 "movl    %%edx, 4(%2);"
+                 "movl    %%ecx, 8(%2);"
+                : "=a"(eax)
+                : "a"(0), "D"(vendor_id)
+                : "%ebx", "%ecx", "%edx");
+       vendor_id[12] = '\0';
+       cprintf("Vendor ID: %s\n", vendor_id);
+       cprintf("Largest Standard Function Number Supported: %d\n", eax);
+       cpuid(0x80000000, &eax, 0, 0, 0);
+       cprintf("Largest Extended Function Number Supported: 0x%08x\n", eax);
+       cpuid(1, &eax, &ebx, &ecx, &edx);
+       family = ((eax & 0x0FF00000) >> 20) + ((eax & 0x00000F00) >> 8);
+       model = ((eax & 0x000F0000) >> 12) + ((eax & 0x000000F0) >> 4);
+       cprintf("Family: %d\n", family);
+       cprintf("Model: %d\n", model);
+       cprintf("Stepping: %d\n", eax & 0x0000000F);
+       // eventually can fill this out with SDM Vol3B App B info, or
+       // better yet with stepping info.  or cpuid 8000_000{2,3,4}
+       switch ( family << 8 | model ) {
+               case(0x061a):
+                       cprintf("Processor: Core i7\n");
+                       break;
+               case(0x060f):
+                       cprintf("Processor: Core 2 Duo or Similar\n");
+                       break;
+               default:
+                       cprintf("Unknown or non-Intel CPU\n");
+       }
+       if (!(edx & 0x00000020))
+               panic("MSRs not supported!");
+       if (!(edx & 0x00001000))
+               panic("MTRRs not supported!");
+       if (!(edx & 0x00002000))
+               panic("Global Pages not supported!");
+       if (!(edx & 0x00000200))
+               panic("Local APIC Not Detected!");
+       if (ecx & 0x00200000)
+               cprintf("x2APIC Detected\n");
+       else
+               cprintf("x2APIC Not Detected\n");
+       cpuid(0x80000008, &eax, &ebx, &ecx, &edx);
+       cprintf("Physical Address Bits: %d\n", eax & 0x000000FF);
+       cprintf("Cores per Die: %d\n", (ecx & 0x000000FF) + 1);
+     cprintf("This core's Default APIC ID: 0x%08x\n", lapic_get_default_id());
+       msr_val = read_msr(IA32_APIC_BASE);
+       if (msr_val & MSR_APIC_ENABLE)
+               cprintf("Local APIC Enabled\n");
+       else
+               cprintf("Local APIC Disabled\n");
+       if (msr_val & 0x00000100)
+               cprintf("I am the Boot Strap Processor\n");
+       else
+               cprintf("I am an Application Processor\n");
+       cpuid(0x80000007, &eax, &ebx, &ecx, &edx);
+       if (edx & 0x00000100)
+               printk("Invariant TSC present\n");
+       else
+               printk("Invariant TSC not present\n");
+ }
+ void show_mapping(uintptr_t start, size_t size)
+ {
+       pde_t* pgdir = (pde_t*)vpd;
+       pte_t *pte, *pde;
+       page_t* page;
+       uintptr_t i;
+       cprintf("   Virtual    Physical  Ps Dr Ac CD WT U W\n");
+       cprintf("------------------------------------------\n");
+       for(i = 0; i < size; i += PGSIZE, start += PGSIZE) {
+               page = page_lookup(pgdir, (void*)start, &pte);
+               cprintf("%08p  ", start);
+               if (page) {
+                       pde = &pgdir[PDX(start)];
+                       // for a jumbo, pde = pte and PTE_PS (better be) = 1
+                       cprintf("%08p  %1d  %1d  %1d  %1d  %1d  %1d %1d\n", page2pa(page),
+                              (*pte & PTE_PS) >> 7, (*pte & PTE_D) >> 6, (*pte & PTE_A) >> 5,
+                              (*pte & PTE_PCD) >> 4, (*pte & PTE_PWT) >> 3,
+                              (*pte & *pde & PTE_U) >> 2, (*pte & *pde & PTE_W) >> 1);
+               } else
+                       cprintf("%08p\n", 0);
+       }
+ }
 -void
 -backtrace(void)
++void backtrace(void)
+ {
+       uint32_t* ebp, eip;
+       eipdebuginfo_t debuginfo;
+       char buf[256];
+       int j, i = 1;
+       ebp = (uint32_t*)read_ebp();    
+       // this is part of the way back into the call() instruction's bytes
+       // eagle-eyed readers should be able to explain why this is good enough,
+       // and retaddr (just *(ebp + 1) is not)
+       eip = *(ebp + 1) - 1;
+       // jump back a frame (out of backtrace)
+       ebp = (uint32_t*)(*ebp);
+       cprintf("Stack Backtrace:\n");
+       // on each iteration, ebp holds the stack frame and eip an addr in that func
+       while (ebp != 0) {
+               debuginfo_eip(eip, &debuginfo);
+               memset(buf, 0, 256);
+               strncpy(buf, debuginfo.eip_fn_name, MIN(debuginfo.eip_fn_namelen, 256));
+               buf[MIN(debuginfo.eip_fn_namelen, 255)] = 0;
+               cprintf("#%02d [<%x>] in %s+%x(%p) from %s:%d\n", i++,  eip, buf, 
+                       debuginfo.eip_fn_addr - (uint32_t)_start, debuginfo.eip_fn_addr, 
+                       debuginfo.eip_file, debuginfo.eip_line);
+               cprintf("    ebp: %x   Args:", ebp);
+               for (j = 0; j < MIN(debuginfo.eip_fn_narg, 5); j++)
+                       cprintf(" %08x", *(ebp + 2 + j));
+               cprintf("\n");
+               eip = *(ebp + 1) - 1;
+               ebp = (uint32_t*)(*ebp);
+       }
+ }
index 0000000,4f4f57f..42df5a8
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,98 +1,106 @@@
 -      if(tf->tf_cs)
 -      {
+ /* See COPYRIGHT for copyright information. */
+ #ifdef __DEPUTY__
+ #pragma noasync
+ #endif
+ #include <arch/trap.h>
+ #include <env.h>
+ #include <assert.h>
+ #include <pmap.h>
+ //
+ // This exits the kernel and starts executing some environment's code.
+ // This function does not return.
+ // Uses 'iret' or 'sysexit' depending on CS.
+ //
+ void env_pop_tf(trapframe_t *tf)
+ {
 -      }
 -      else
 -      {
++      /*
++       * If the process entered the kernel via sysenter, we need to leave via
++       * sysexit.  sysenter trapframes have 0 for a CS, which is pushed in
++       * sysenter_handler.
++       */
++      if(tf->tf_cs) {
++              /*
++               * Restores the register values in the Trapframe with the 'iret'
++               * instruction.  This exits the kernel and starts executing some
++               * environment's code.  This function does not return.
++               */
+               asm volatile ("movl %0,%%esp;           "
+                             "popal;                   "
+                             "popl %%es;               "
+                             "popl %%ds;               "
+                             "addl $0x8,%%esp;         "
+                             "iret                     "
+                             : : "g" (tf) : "memory");
+               panic("iret failed");  /* mostly to placate the compiler */
++      } else {
++              /* Return path of sysexit.  See sysenter_handler's asm for details. */
+               asm volatile ("movl %0,%%esp;           "
+                             "popal;                   "
+                             "popl %%es;               "
+                             "popl %%ds;               "
+                             "addl $0x10, %%esp;       "
+                             "popfl;                   "
+                             "movl %%ebp, %%ecx;       "
+                             "movl %%esi, %%edx;       "
+                             "sysexit                  "
+                             : : "g" (tf) : "memory");
+               panic("sysexit failed");  /* mostly to placate the compiler */
+       }
+ }
+ void
+ env_set_program_counter(env_t* e, uintptr_t pc)
+ {
+       e->env_tf.tf_eip = pc;
+ }
+ void
+ env_init_trapframe(env_t* e)
+ {
+       // Set up appropriate initial values for the segment registers.
+       // GD_UD is the user data segment selector in the GDT, and
+       // GD_UT is the user text segment selector (see inc/memlayout.h).
+       // The low 2 bits of each segment register contains the
+       // Requestor Privilege Level (RPL); 3 means user mode.
+       e->env_tf.tf_ds = GD_UD | 3;
+       e->env_tf.tf_es = GD_UD | 3;
+       e->env_tf.tf_ss = GD_UD | 3;
+       e->env_tf.tf_esp = USTACKTOP;
+       e->env_tf.tf_cs = GD_UT | 3;
+       // You will set e->env_tf.tf_eip later.
+       // set the env's EFLAGSs to have interrupts enabled
+       e->env_tf.tf_eflags |= 0x00000200; // bit 9 is the interrupts-enabled
+ }
+ // Flush all mapped pages in the user portion of the address space
+ void
+ env_user_mem_free(env_t* e)
+ {
+       pte_t *pt;
+       uint32_t pdeno, pteno;
+       physaddr_t pa;
+       static_assert(UTOP % PTSIZE == 0);
+       for (pdeno = 0; pdeno < PDX(UTOP); pdeno++) {
+               // only look at mapped page tables
+               if (!(e->env_pgdir[pdeno] & PTE_P))
+                       continue;
+               // find the pa and va of the page table
+               pa = PTE_ADDR(e->env_pgdir[pdeno]);
+               pt = (pte_t*COUNT(NPTENTRIES)) KADDR(pa);
+               // unmap all PTEs in this page table 
+               for (pteno = 0; pteno <= PTX(~0); pteno++) {
+                       if (pt[pteno] & PTE_P)
+                               page_remove(e->env_pgdir, PGADDR(pdeno, pteno, 0));
+               }
+               // free the page table itself
+               e->env_pgdir[pdeno] = 0;
+               page_decref(pa2page(pa));
+       }
+ }
index 0000000,43371f9..77ad476
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,241 +1,241 @@@
 -#include <env.h>
+ #ifdef __DEPUTY__
+ #pragma nodeputy
+ #endif
+ #include <stab.h>
+ #include <string.h>
+ #include <assert.h>
+ #include <kdebug.h>
+ #include <pmap.h>
++#include <process.h>
+ #include <ros/memlayout.h>
+ extern const stab_t __STAB_BEGIN__[]; // Beginning of stabs table
+ extern const stab_t __STAB_END__[];   // End of stabs table
+ extern const char __STABSTR_BEGIN__[];                // Beginning of string table
+ extern const char __STABSTR_END__[];          // End of string table
+ typedef struct UserStabData {
+       const stab_t *stabs;
+       const stab_t *stab_end;
+       const char *stabstr;
+       const char *stabstr_end;
+ } user_stab_data_t;
+ // stab_binsearch(stabs, region_left, region_right, type, addr)
+ //
+ //    Some stab types are arranged in increasing order by instruction
+ //    address.  For example, N_FUN stabs (stab entries with n_type ==
+ //    N_FUN), which mark functions, and N_SO stabs, which mark source files.
+ //
+ //    Given an instruction address, this function finds the single stab
+ //    entry of type 'type' that contains that address.
+ //
+ //    The search takes place within the range [*region_left, *region_right].
+ //    Thus, to search an entire set of N stabs, you might do:
+ //
+ //            left = 0;
+ //            right = N - 1;     /* rightmost stab */
+ //            stab_binsearch(stabs, &left, &right, type, addr);
+ //
+ //    The search modifies *region_left and *region_right to bracket the
+ //    'addr'.  *region_left points to the matching stab that contains
+ //    'addr', and *region_right points just before the next stab.  If
+ //    *region_left > *region_right, then 'addr' is not contained in any
+ //    matching stab.
+ //
+ //    For example, given these N_SO stabs:
+ //            Index  Type   Address
+ //            0      SO     f0100000
+ //            13     SO     f0100040
+ //            117    SO     f0100176
+ //            118    SO     f0100178
+ //            555    SO     f0100652
+ //            556    SO     f0100654
+ //            657    SO     f0100849
+ //    this code:
+ //            left = 0, right = 657;
+ //            stab_binsearch(stabs, &left, &right, N_SO, 0xf0100184);
+ //    will exit setting left = 118, right = 554.
+ //
+ static void
+ stab_binsearch(const stab_t *stabs, int *region_left, int *region_right,
+              int type, uintptr_t addr)
+ {
+       int l = *region_left, r = *region_right, any_matches = 0;
+       
+       while (l <= r) {
+               int true_m = (l + r) / 2, m = true_m;
+               
+               // search for earliest stab with right type
+               while (m >= l && stabs[m].n_type != type)
+                       m--;
+               if (m < l) {    // no match in [l, m]
+                       l = true_m + 1;
+                       continue;
+               }
+               // actual binary search
+               any_matches = 1;
+               if (stabs[m].n_value < addr) {
+                       *region_left = m;
+                       l = true_m + 1;
+               } else if (stabs[m].n_value > addr) {
+                       *region_right = m - 1;
+                       r = m - 1;
+               } else {
+                       // exact match for 'addr', but continue loop to find
+                       // *region_right
+                       *region_left = m;
+                       l = m;
+                       addr++;
+               }
+       }
+       if (!any_matches)
+               *region_right = *region_left - 1;
+       else {
+               // find rightmost region containing 'addr'
+               for (l = *region_right;
+                    l > *region_left && stabs[l].n_type != type;
+                    l--)
+                       /* do nothing */;
+               *region_left = l;
+       }
+ }
+ // debuginfo_eip(addr, info)
+ //
+ //    Fill in the 'info' structure with information about the specified
+ //    instruction address, 'addr'.  Returns 0 if information was found, and
+ //    negative if not.  But even if it returns negative it has stored some
+ //    information into '*info'.
+ //
+ int
+ debuginfo_eip(uintptr_t addr, eipdebuginfo_t *info)
+ {
+       const stab_t *stabs, *stab_end;
+       const char *stabstr, *stabstr_end;
+       int lfile, rfile, lfun, rfun, lline, rline;
+       // Initialize *info
+       info->eip_file = "<unknown>";
+       info->eip_line = 0;
+       info->eip_fn_name = "<unknown>";
+       info->eip_fn_namelen = 9;
+       info->eip_fn_addr = addr;
+       info->eip_fn_narg = 0;
+       // Find the relevant set of stabs
+       if (addr >= ULIM) {
+               stabs = __STAB_BEGIN__;
+               stab_end = __STAB_END__;
+               stabstr = __STABSTR_BEGIN__;
+               stabstr_end = __STABSTR_END__;
+       } else {
+               // The user-application linker script, user/user.ld,
+               // puts information about the application's stabs (equivalent
+               // to __STAB_BEGIN__, __STAB_END__, __STABSTR_BEGIN__, and
+               // __STABSTR_END__) in a structure located at virtual address
+               // USTABDATA.
+               const user_stab_data_t *usd = (const user_stab_data_t *) USTABDATA;
+               // Make sure this memory is valid.
+               // Return -1 if it is not.  Hint: Call user_mem_check.
+               // LAB 3: Your code here.
+               
+               stabs = usd->stabs;
+               stab_end = usd->stab_end;
+               stabstr = usd->stabstr;
+               stabstr_end = usd->stabstr_end;
+               // Make sure the STABS and string table memory is valid.
+               // LAB 3: Your code here.
+       }
+       // String table validity checks
+       if (stabstr_end <= stabstr || stabstr_end[-1] != 0)
+               return -1;
+       // Now we find the right stabs that define the function containing
+       // 'eip'.  First, we find the basic source file containing 'eip'.
+       // Then, we look in that source file for the function.  Then we look
+       // for the line number.
+       
+       // Search the entire set of stabs for the source file (type N_SO).
+       lfile = 0;
+       rfile = (stab_end - stabs) - 1;
+       stab_binsearch(stabs, &lfile, &rfile, N_SO, addr);
+       if (lfile == 0)
+               return -1;
+       // Search within that file's stabs for the function definition
+       // (N_FUN).
+       lfun = lfile;
+       rfun = rfile;
+       stab_binsearch(stabs, &lfun, &rfun, N_FUN, addr);
+       if (lfun <= rfun) {
+               // stabs[lfun] points to the function name
+               // in the string table, but check bounds just in case.
+               if (stabs[lfun].n_strx < stabstr_end - stabstr)
+                       info->eip_fn_name = stabstr + stabs[lfun].n_strx;
+               info->eip_fn_addr = stabs[lfun].n_value;
+               addr -= info->eip_fn_addr;
+               // Search within the function definition for the line number.
+               lline = lfun;
+               rline = rfun;
+       } else {
+               // Couldn't find function stab!  Maybe we're in an assembly
+               // file.  Search the whole file for the line number.
+               info->eip_fn_addr = addr;
+               lline = lfile;
+               rline = rfile;
+       }
+       // Ignore stuff after the colon.
+       info->eip_fn_namelen = strfind(info->eip_fn_name, ':') - info->eip_fn_name;
+       
+       // Search within [lline, rline] for the line number stab.
+       // If found, set info->eip_line to the right line number.
+       // If not found, return -1.
+       //
+       // Hint:
+       //      There's a particular stabs type used for line numbers.
+       //      Look at the STABS documentation and <inc/stab.h> to find
+       //      which one.
+       // Your code here.
+       stab_binsearch(stabs, &lline, &rline, N_SLINE, addr);
+       if (lline <= rline) 
+               // stabs[lline] points to the line number
+               info->eip_line = stabs[lline].n_value;
+       else
+               return -1;
+       
+       // Search backwards from the line number for the relevant filename
+       // stab.
+       // We can't just use the "lfile" stab because inlined functions
+       // can interpolate code from a different file!
+       // Such included source files use the N_SOL stab type.
+       while (lline >= lfile
+              && stabs[lline].n_type != N_SOL
+              && (stabs[lline].n_type != N_SO || !stabs[lline].n_value))
+               lline--;
+       if (lline >= lfile && stabs[lline].n_strx < stabstr_end - stabstr)
+               info->eip_file = stabstr + stabs[lline].n_strx;
+       // Set eip_fn_narg to the number of arguments taken by the function,
+       // or 0 if there was no containing function.
+       // Your code here.
+       info->eip_fn_narg = 0;
+       if (lfun <= rfun) {
+               lfun++;
+               while (stabs[lfun++].n_type == N_PSYM)
+                       info->eip_fn_narg++;
+       }
+       
+       return 0;
+ }
index 0000000,fe1f651..df67a96
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,942 +1,942 @@@
 -      lcr3(boot_cr3);
+ /* See COPYRIGHT for copyright information. */
+ #ifdef __DEPUTY__
+ #pragma nodeputy
+ #endif
+ #include <arch/x86.h>
+ #include <arch/arch.h>
+ #include <arch/mmu.h>
+ #include <arch/apic.h>
+ #include <ros/error.h>
+ #include <atomic.h>
+ #include <string.h>
+ #include <assert.h>
+ #include <pmap.h>
+ #include <kclock.h>
+ #include <env.h>
+ // These variables are set in i386_vm_init()
+ pde_t* boot_pgdir;            // Virtual address of boot time page directory
+ physaddr_t boot_cr3;          // Physical address of boot time page directory
+ char* boot_freemem;           // Pointer to next byte of free mem
+ page_t *pages;                // Virtual address of physical page array
+ page_list_t page_free_list;   // Free list of physical pages
+ extern env_t *envs;
+ // Global descriptor table.
+ //
+ // The kernel and user segments are identical (except for the DPL).
+ // To load the SS register, the CPL must equal the DPL.  Thus,
+ // we must duplicate the segments for the user and the kernel.
+ //
+ segdesc_t gdt[] =
+ {
+       // 0x0 - unused (always faults -- for trapping NULL far pointers)
+       SEG_NULL,
+       // 0x8 - kernel code segment
+       [GD_KT >> 3] = SEG(STA_X | STA_R, 0x0, 0xffffffff, 0),
+       // 0x10 - kernel data segment
+       [GD_KD >> 3] = SEG(STA_W, 0x0, 0xffffffff, 0),
+       // 0x18 - user code segment
+       [GD_UT >> 3] = SEG(STA_X | STA_R, 0x0, 0xffffffff, 3),
+       // 0x20 - user data segment
+       [GD_UD >> 3] = SEG(STA_W, 0x0, 0xffffffff, 3),
+       // 0x28 - tss, initialized in idt_init()
+       [GD_TSS >> 3] = SEG_NULL
+ };
+ pseudodesc_t gdt_pd = {
+       sizeof(gdt) - 1, (unsigned long) gdt
+ };
+ static int
+ nvram_read(int r)
+ {
+       return mc146818_read(r) | (mc146818_read(r + 1) << 8);
+ }
+ bool enable_pse(void)
+ {
+       uint32_t edx, cr4;
+       cpuid(1, 0, 0, 0, &edx);
+       if (edx & CPUID_PSE_SUPPORT) {
+               cr4 = rcr4();
+               cr4 |= CR4_PSE;
+               lcr4(cr4);
+               return 1;
+       } else
+               return 0;
+ }
+ // --------------------------------------------------------------
+ // Set up initial memory mappings and turn on MMU.
+ // --------------------------------------------------------------
+ static void check_boot_pgdir(bool pse);
+ //
+ // Given pgdir, a pointer to a page directory,
+ // walk the 2-level page table structure to find
+ // the page table entry (PTE) for linear address la.
+ // Return a pointer to this PTE.
+ //
+ // If the relevant page table doesn't exist in the page directory:
+ //    - If create == 0, return 0.
+ //    - Otherwise allocate a new page table, install it into pgdir,
+ //      and return a pointer into it.
+ //        (Questions: What data should the new page table contain?
+ //      And what permissions should the new pgdir entry have?
+ //      Note that we use the 486-only "WP" feature of %cr0, which
+ //      affects the way supervisor-mode writes are checked.)
+ //
+ // This function abstracts away the 2-level nature of
+ // the page directory by allocating new page tables
+ // as needed.
+ // 
+ // boot_pgdir_walk may ONLY be used during initialization,
+ // before the page_free_list has been set up.
+ // It should panic on failure.  (Note that boot_alloc already panics
+ // on failure.)
+ //
+ // Supports returning jumbo (4MB PSE) PTEs.  To create with a jumbo, pass in 2.
+ // 
+ // Maps non-PSE PDEs as U/W.  W so the kernel can, U so the user can read via
+ // UVPT.  UVPT security comes from the UVPT mapping (U/R).  All other kernel pages
+ // protected at the second layer
+ static pte_t*
+ boot_pgdir_walk(pde_t *pgdir, uintptr_t la, int create)
+ {
+       pde_t* the_pde = &pgdir[PDX(la)];
+       void* new_table;
+       if (*the_pde & PTE_P) {
+               if (*the_pde & PTE_PS)
+                       return (pte_t*)the_pde;
+               return &((pde_t*)KADDR(PTE_ADDR(*the_pde)))[PTX(la)];
+       }
+       if (!create)
+               return NULL;
+       if (create == 2) {
+               if (JPGOFF(la))
+                       panic("Attempting to find a Jumbo PTE at an unaligned VA!");
+               *the_pde = PTE_PS | PTE_P;
+               return (pte_t*)the_pde;
+       }
+       new_table = boot_alloc(PGSIZE, PGSIZE);
+       memset(new_table, 0, PGSIZE);
+       *the_pde = (pde_t)PADDR(new_table) | PTE_P | PTE_W | PTE_U | PTE_G;
+       return &((pde_t*)KADDR(PTE_ADDR(*the_pde)))[PTX(la)];
+ }
+ //
+ // Map [la, la+size) of linear address space to physical [pa, pa+size)
+ // in the page table rooted at pgdir.  Size is a multiple of PGSIZE.
+ // Use permission bits perm|PTE_P for the entries.
+ //
+ // This function may ONLY be used during initialization,
+ // before the page_free_list has been set up.
+ //
+ // To map with Jumbos, set PTE_PS in perm
+ static void
+ boot_map_segment(pde_t *pgdir, uintptr_t la, size_t size, physaddr_t pa, int perm)
+ {
+       uintptr_t i;
+       pte_t *pte;
+       // la can be page unaligned, but weird things will happen
+       // unless pa has the same offset.  pa always truncates any
+       // possible offset.  will warn.  size can be weird too. 
+       if (PGOFF(la)) {
+               warn("la not page aligned in boot_map_segment!");
+               size += PGOFF(la);
+       }
+       if (perm & PTE_PS) {
+               if (JPGOFF(la) || JPGOFF(pa))
+                       panic("Tried to map a Jumbo page at an unaligned address!");
+               // need to index with i instead of la + size, in case of wrap-around
+               for (i = 0; i < size; i += JPGSIZE, la += JPGSIZE, pa += JPGSIZE) {
+                       pte = boot_pgdir_walk(pgdir, la, 2);
+                       *pte = PTE_ADDR(pa) | PTE_P | perm;
+               }
+       } else {
+               for (i = 0; i < size; i += PGSIZE, la += PGSIZE, pa += PGSIZE) {
+                       pte = boot_pgdir_walk(pgdir, la, 1);
+                       if (*pte & PTE_PS)
+                               // if we start using the extra flag for PAT, which we aren't,
+                               // this will warn, since PTE_PS and PTE_PAT are the same....
+                               warn("Possibly attempting to map a regular page into a Jumbo PDE");
+                       *pte = PTE_ADDR(pa) | PTE_P | perm;
+               }
+       }
+ }
+ // could consider having an API to allow these to dynamically change
+ // MTRRs are for physical, static ranges.  PAT are linear, more granular, and 
+ // more dynamic
+ void setup_default_mtrrs(barrier_t* smp_barrier)
+ {
+       // disable interrupts
+       int8_t state = 0;
+       disable_irqsave(&state);
+       // barrier - if we're meant to do this for all cores, we'll be 
+       // passed a pointer to an initialized barrier
+       if (smp_barrier)
+               waiton_barrier(smp_barrier);
+       
+       // disable caching      cr0: set CD and clear NW
+       lcr0((rcr0() | CR0_CD) & ~CR0_NW);
+       // flush caches
+       cache_flush();
+       // flush tlb
+       tlb_flush_global();
+       // disable MTRRs, and sets default type to WB (06)
+       write_msr(IA32_MTRR_DEF_TYPE, 0x00000006);
+       // Now we can actually safely adjust the MTRRs
+       // MTRR for IO Holes (note these are 64 bit values we are writing)
+       // 0x000a0000 - 0x000c0000 : VGA - WC 0x01
+       write_msr(IA32_MTRR_PHYSBASE0, PTE_ADDR(VGAPHYSMEM) | 0x01);
+       // if we need to have a full 64bit val, use the UINT64 macro
+       write_msr(IA32_MTRR_PHYSMASK0, 0x0000000ffffe0800);
+       // 0x000c0000 - 0x00100000 : IO devices (and ROM BIOS) - UC 0x00
+       write_msr(IA32_MTRR_PHYSBASE1, PTE_ADDR(DEVPHYSMEM) | 0x00);
+       write_msr(IA32_MTRR_PHYSMASK1, 0x0000000ffffc0800);
+       // APIC/IOAPIC holes
+       /* Going to skip them, since we set their mode using PAT when we 
+        * map them in 
+        */
+       // make sure all other MTRR ranges are disabled (should be unnecessary)
+       write_msr(IA32_MTRR_PHYSMASK2, 0);
+       write_msr(IA32_MTRR_PHYSMASK3, 0);
+       write_msr(IA32_MTRR_PHYSMASK4, 0);
+       write_msr(IA32_MTRR_PHYSMASK5, 0);
+       write_msr(IA32_MTRR_PHYSMASK6, 0);
+       write_msr(IA32_MTRR_PHYSMASK7, 0);
+       // keeps default type to WB (06), turns MTRRs on, and turns off fixed ranges
+       write_msr(IA32_MTRR_DEF_TYPE, 0x00000806);
+       // reflush caches and TLB
+       cache_flush();
+       tlb_flush_global();
+       // turn on caching
+       lcr0(rcr0() & ~(CR0_CD | CR0_NW));
+       // barrier
+       if (smp_barrier)
+               waiton_barrier(smp_barrier);
+       // enable interrupts
+       enable_irqsave(&state);
+ }
+ // Set up a two-level page table:
+ //    boot_pgdir is its linear (virtual) address of the root
+ //    boot_cr3 is the physical adresss of the root
+ // Then turn on paging.  Then effectively turn off segmentation.
+ // (i.e., the segment base addrs are set to zero).
+ // 
+ // This function only sets up the kernel part of the address space
+ // (ie. addresses >= UTOP).  The user part of the address space
+ // will be setup later.
+ //
+ // From UTOP to ULIM, the user is allowed to read but not write.
+ // Above ULIM the user cannot read (or write). 
+ void
+ vm_init(void)
+ {
+       pde_t* pgdir;
+       uint32_t cr0, edx;
+       size_t n;
+       bool pse;
+       pse = enable_pse();
+       if (pse)
+               cprintf("PSE capability detected.\n");
+       // we paniced earlier if we don't support PGE.  turn it on now.
+       // it's used in boot_map_segment, which covers all of the mappings that are
+       // the same for all address spaces.  and also for the VPT mapping below.
+       lcr4(rcr4() | CR4_PGE);
+       // set up mtrr's for core0.  other cores will do the same later
+       setup_default_mtrrs(0);
+       /*
+        * PSE status: 
+        * - can walk and set up boot_map_segments with jumbos but can't
+        *   insert yet.  need to look at the page_dir and friends.
+        * - anything related to a single struct Page still can't handle 
+        *   jumbos.  will need to think about and adjust Page functions
+        * - do we want to store info like this in the struct Page?  or just check
+        *   by walking the PTE
+        * - when we alloc a page, and we want it to be 4MB, we'll need
+        *   to have contiguous memory, etc
+        * - there's a difference between having 4MB page table entries
+        *   and having 4MB Page tracking structs.  changing the latter will
+        *   break a lot of things
+        * - showmapping and friends work on a 4KB granularity, but map to the
+        *   correct entries
+        * - need to not insert / boot_map a single page into an area that is 
+        *   already holding a jumbo page.  will need to break the jumbo up so that
+        *   we can then insert the lone page.  currently warns.
+        * - some inherent issues with the pgdir_walks returning a PTE, and we
+        *   don't know whether it is a jumbo (PDE) or a regular PTE.
+        */
+       //////////////////////////////////////////////////////////////////////
+       // create initial page directory.
+       pgdir = boot_alloc(PGSIZE, PGSIZE);
+       memset(pgdir, 0, PGSIZE);
+       boot_pgdir = pgdir;
+       boot_cr3 = PADDR(pgdir);
+       // helpful if you want to manually walk with kvm / bochs
+       //printk("pgdir va = %08p, pgdir pa = %08p\n\n", pgdir, PADDR(pgdir));
+       //////////////////////////////////////////////////////////////////////
+       // Recursively insert PD in itself as a page table, to form
+       // a virtual page table at virtual address VPT.
+       // (For now, you don't have understand the greater purpose of the
+       // following two lines.  Unless you are eagle-eyed, in which case you
+       // should already know.)
+       // Permissions: kernel RW, user NONE, Global Page
+       pgdir[PDX(VPT)] = PADDR(pgdir) | PTE_W | PTE_P | PTE_G;
+       // same for UVPT
+       // Permissions: kernel R, user R, Global Page
+       pgdir[PDX(UVPT)] = PADDR(pgdir) | PTE_U | PTE_P | PTE_G;
+       //////////////////////////////////////////////////////////////////////
+       // Map the kernel stack (symbol name "bootstack").  The complete VA
+       // range of the stack, [KSTACKTOP-PTSIZE, KSTACKTOP), breaks into two
+       // pieces:
+       //     * [KSTACKTOP-KSTKSIZE, KSTACKTOP) -- backed by physical memory
+       //     * [KSTACKTOP-PTSIZE, KSTACKTOP-KSTKSIZE) -- not backed => faults
+       //     Permissions: kernel RW, user NONE
+       // Your code goes here:
+       // remember that the space for the kernel stack is allocated in the binary.
+       // bootstack and bootstacktop point to symbols in the data section, which 
+       // at this point are like 0xc010b000.  KSTACKTOP is the desired loc in VM
+       boot_map_segment(pgdir, (uintptr_t)KSTACKTOP - KSTKSIZE, 
+                        KSTKSIZE, PADDR(bootstack), PTE_W | PTE_G);
+       //////////////////////////////////////////////////////////////////////
+       // Map all of physical memory at KERNBASE. 
+       // Ie.  the VA range [KERNBASE, 2^32) should map to
+       //      the PA range [0, 2^32 - KERNBASE)
+       // We might not have 2^32 - KERNBASE bytes of physical memory, but
+       // we just set up the mapping anyway.
+       // Permissions: kernel RW, user NONE
+       // Your code goes here: 
+       
+       // this maps all of the possible phys memory
+       // note the use of unsigned underflow to get size = 0x40000000
+       //boot_map_segment(pgdir, KERNBASE, -KERNBASE, 0, PTE_W);
+       // but this only maps what is available, and saves memory.  every 4MB of
+       // mapped memory requires a 2nd level page: 2^10 entries, each covering 2^12
+       // need to modify tests below to account for this
+       if (pse) {
+               // map the first 4MB as regular entries, to support different MTRRs
+               boot_map_segment(pgdir, KERNBASE, JPGSIZE, 0, PTE_W | PTE_G);
+               boot_map_segment(pgdir, KERNBASE + JPGSIZE, maxaddrpa - JPGSIZE, JPGSIZE,
+                                PTE_W | PTE_G | PTE_PS);
+       } else
+               boot_map_segment(pgdir, KERNBASE, maxaddrpa, 0, PTE_W | PTE_G);
+       // APIC mapping: using PAT (but not *the* PAT flag) to make these type UC
+       // IOAPIC
+       boot_map_segment(pgdir, (uintptr_t)IOAPIC_BASE, PGSIZE, IOAPIC_BASE, 
+                        PTE_PCD | PTE_PWT | PTE_W | PTE_G);
+       // Local APIC
+       boot_map_segment(pgdir, (uintptr_t)LAPIC_BASE, PGSIZE, LAPIC_BASE,
+                        PTE_PCD | PTE_PWT | PTE_W | PTE_G);
+       //////////////////////////////////////////////////////////////////////
+       // Make 'pages' point to an array of size 'npage' of 'struct Page'.
+       // The kernel uses this structure to keep track of physical pages;
+       // 'npage' equals the number of physical pages in memory.  User-level
+       // programs get read-only access to the array as well.
+       // You must allocate the array yourself.
+       // Map this array read-only by the user at linear address UPAGES
+       // (ie. perm = PTE_U | PTE_P)
+       // Permissions:
+       //    - pages -- kernel RW, user NONE
+       //    - the read-only version mapped at UPAGES -- kernel R, user R
+       // Your code goes here: 
+       
+       // round up to the nearest page
+       size_t page_array_size = ROUNDUP(npage*sizeof(page_t), PGSIZE);
+       pages = (page_t *)boot_alloc(page_array_size, PGSIZE);
+       memset(pages, 0, page_array_size);
+       if (page_array_size > PTSIZE) {
+               warn("page_array_size bigger than PTSIZE, userland will not see all pages");
+               page_array_size = PTSIZE;
+       }
+       boot_map_segment(pgdir, UPAGES, page_array_size, PADDR(pages), PTE_U | 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);
+       envs = (env_t *)boot_alloc(env_array_size, PGSIZE);
+       memset(envs, 0, env_array_size);
+       // Check that the initial page directory has been set up correctly.
+       check_boot_pgdir(pse);
+       //////////////////////////////////////////////////////////////////////
+       // On x86, segmentation maps a VA to a LA (linear addr) and
+       // paging maps the LA to a PA.  I.e. VA => LA => PA.  If paging is
+       // turned off the LA is used as the PA.  Note: there is no way to
+       // turn off segmentation.  The closest thing is to set the base
+       // address to 0, so the VA => LA mapping is the identity.
+       // Current mapping: VA KERNBASE+x => PA x.
+       //     (segmentation base=-KERNBASE and paging is off)
+       // From here on down we must maintain this VA KERNBASE + x => PA x
+       // mapping, even though we are turning on paging and reconfiguring
+       // segmentation.
+       // Map VA 0:4MB same as VA KERNBASE, i.e. to PA 0:4MB.
+       // (Limits our kernel to <4MB)
+       /* They mean linear address 0:4MB, and the kernel < 4MB is only until 
+        * segmentation is turned off.
+        * once we turn on paging, segmentation is still on, so references to
+        * KERNBASE+x will get mapped to linear address x, which we need to make 
+        * sure can map to phys addr x, until we can turn off segmentation and
+        * KERNBASE+x maps to LA KERNBASE+x, which maps to PA x, via paging
+        */
+       pgdir[0] = pgdir[PDX(KERNBASE)];
+       // Install page table.
+       lcr3(boot_cr3);
+       // Turn on paging.
+       cr0 = rcr0();
+       // CD and NW should already be on, but just in case these turn on caching
+       cr0 |= CR0_PE|CR0_PG|CR0_AM|CR0_WP|CR0_NE|CR0_MP;
+       cr0 &= ~(CR0_TS|CR0_EM|CR0_CD|CR0_NW);
+       lcr0(cr0);
+       // Current mapping: KERNBASE+x => x => x.
+       // (x < 4MB so uses paging pgdir[0])
+       // Reload all segment registers.
+       asm volatile("lgdt gdt_pd");
+       asm volatile("movw %%ax,%%gs" :: "a" (GD_UD|3));
+       asm volatile("movw %%ax,%%fs" :: "a" (GD_UD|3));
+       asm volatile("movw %%ax,%%es" :: "a" (GD_KD));
+       asm volatile("movw %%ax,%%ds" :: "a" (GD_KD));
+       asm volatile("movw %%ax,%%ss" :: "a" (GD_KD));
+       asm volatile("ljmp %0,$1f\n 1:\n" :: "i" (GD_KT));  // reload cs
+       asm volatile("lldt %%ax" :: "a" (0));
+       // Final mapping: KERNBASE+x => KERNBASE+x => x.
+       // This mapping was only used after paging was turned on but
+       // before the segment registers were reloaded.
+       pgdir[0] = 0;
+       // Flush the TLB for good measure, to kill the pgdir[0] mapping.
++      tlb_flush_global();
+ }
+ //
+ // Checks that the kernel part of virtual address space
+ // has been setup roughly correctly(by i386_vm_init()).
+ //
+ // This function doesn't test every corner case,
+ // in fact it doesn't test the permission bits at all,
+ // but it is a pretty good sanity check. 
+ //
+ static physaddr_t check_va2pa(pde_t *pgdir, uintptr_t va);
+ static pte_t get_vaperms(pde_t *pgdir, uintptr_t va);
+ static void
+ check_boot_pgdir(bool pse)
+ {
+       uint32_t i, n;
+       pde_t *pgdir, pte;
+       pgdir = boot_pgdir;
+       // check pages array
+       n = ROUNDUP(naddrpage*sizeof(page_t), PGSIZE);
+       for (i = 0; i < n; i += PGSIZE)
+               assert(check_va2pa(pgdir, UPAGES + i) == PADDR(pages) + i);
+       // check phys mem
+       //for (i = 0; KERNBASE + i != 0; i += PGSIZE)
+       // adjusted check to account for only mapping avail mem
+       if (pse)
+               for (i = 0; i < maxaddrpa; i += JPGSIZE)
+                       assert(check_va2pa(pgdir, KERNBASE + i) == i);
+       else
+               for (i = 0; i < maxaddrpa; i += PGSIZE)
+                       assert(check_va2pa(pgdir, KERNBASE + i) == i);
+       // check kernel stack
+       for (i = 0; i < KSTKSIZE; i += PGSIZE)
+               assert(check_va2pa(pgdir, KSTACKTOP - KSTKSIZE + i) == PADDR(bootstack) + i);
+       // check for zero/non-zero in PDEs
+       for (i = 0; i < NPDENTRIES; i++) {
+               switch (i) {
+               case PDX(VPT):
+               case PDX(UVPT):
+               case PDX(KSTACKTOP-1):
+               case PDX(UPAGES):
+               case PDX(LAPIC_BASE): // LAPIC mapping.  TODO: remove when MTRRs are up
+                       assert(pgdir[i]);
+                       break;
+               default:
+                       //if (i >= PDX(KERNBASE))
+                       // adjusted check to account for only mapping avail mem
+                       // and you can't KADDR maxpa (just above legal range)
+                       // maxaddrpa can be up to maxpa, so assume the worst
+                       if (i >= PDX(KERNBASE) && i <= PDX(KADDR(maxaddrpa-1)))
+                               assert(pgdir[i]);
+                       else
+                               assert(pgdir[i] == 0);
+                       break;
+               }
+       }
+       // check permissions
+       // user read-only.  check for user and write, should be only user
+       // eagle-eyed viewers should be able to explain the extra cases
+       for (i = UTOP; i < ULIM; i+=PGSIZE) {
+               pte = get_vaperms(pgdir, i);
+               if ((pte & PTE_P) && (i != UVPT+(VPT>>10))) {
+                       if (pte & PTE_PS) {
+                               assert((pte & PTE_U) != PTE_U);
+                               assert((pte & PTE_W) != PTE_W);
+                       } else {
+                               assert((pte & PTE_U) == PTE_U);
+                               assert((pte & PTE_W) != PTE_W);
+                       }
+               }
+       }
+       // kernel read-write.
+       for (i = ULIM; i <= KERNBASE + maxaddrpa - PGSIZE; i+=PGSIZE) {
+               pte = get_vaperms(pgdir, i);
+               if ((pte & PTE_P) && (i != VPT+(UVPT>>10))) {
+                       assert((pte & PTE_U) != PTE_U);
+                       assert((pte & PTE_W) == PTE_W);
+               }
+       }
+       // special mappings
+       pte = get_vaperms(pgdir, UVPT+(VPT>>10));
+       assert((pte & PTE_U) != PTE_U);
+       assert((pte & PTE_W) != PTE_W);
+       // note this means the kernel cannot directly manipulate this virtual address
+       // convince yourself this isn't a big deal, eagle-eyes!
+       pte = get_vaperms(pgdir, VPT+(UVPT>>10));
+       assert((pte & PTE_U) != PTE_U);
+       assert((pte & PTE_W) != PTE_W);
+       cprintf("check_boot_pgdir() succeeded!\n");
+ }
+ // This function returns the physical address of the page containing 'va',
+ // defined by the page directory 'pgdir'.  The hardware normally performs
+ // this functionality for us!  We define our own version to help check
+ // the check_boot_pgdir() function; it shouldn't be used elsewhere.
+ static physaddr_t
+ check_va2pa(pde_t *pgdir, uintptr_t va)
+ {
+       pte_t *p;
+       pgdir = &pgdir[PDX(va)];
+       if (!(*pgdir & PTE_P))
+               return ~0;
+       if (*pgdir & PTE_PS)
+               return PTE_ADDR(*pgdir);
+       p = (pte_t*) KADDR(PTE_ADDR(*pgdir));
+       if (!(p[PTX(va)] & PTE_P))
+               return ~0;
+       return PTE_ADDR(p[PTX(va)]);
+ }
+ /* 
+  * This function returns a PTE with the aggregate permissions equivalent
+  * to walking the two levels of paging.  PPN = 0.  Somewhat fragile, in that
+  * it returns PTE_PS if either entry has PTE_PS (which should only happen
+  * for some of the recusive walks)
+  */
+ static pte_t
+ get_vaperms(pde_t *pgdir, uintptr_t va)
+ {
+       pde_t* pde = &pgdir[PDX(va)];
+       pte_t* pte = pgdir_walk(pgdir, (void*)va, 0);
+       if (!pte || !(*pte & PTE_P))
+               return 0;
+       return PGOFF(*pde & *pte) + PTE_PS & (*pde | *pte);
+ }
+               
+ // --------------------------------------------------------------
+ // Tracking of physical pages.
+ // The 'pages' array has one 'page_t' entry per physical page.
+ // Pages are reference counted, and free pages are kept on a linked list.
+ // --------------------------------------------------------------
+ //  
+ // Initialize page structure and memory free list.
+ // After this point, ONLY use the functions below
+ // to allocate and deallocate physical memory via the page_free_list,
+ // and NEVER use boot_alloc() or the related boot-time functions above.
+ //
+ void
+ page_init(void)
+ {
+       // The example code here marks all pages as free.
+       // However this is not truly the case.  What memory is free?
+       //  1) Mark page 0 as in use.
+       //     This way we preserve the real-mode IDT and BIOS structures
+       //     in case we ever need them.  (Currently we don't, but...)
+       //  2) Mark the rest of base memory as free.
+       //  3) Then comes the IO hole [IOPHYSMEM, EXTPHYSMEM).
+       //     Mark it as in use so that it can never be allocated.      
+       //  4) Then extended memory [EXTPHYSMEM, ...).
+       //     Some of it is in use, some is free. Where is the kernel?
+       //     Which pages are used for page tables and other data structures?
+       //
+       // Change the code to reflect this.
+       int i;
+       physaddr_t physaddr_after_kernel = PADDR(ROUNDUP(boot_freemem, PGSIZE));
+       LIST_INIT(&page_free_list);
+       pages[0].pp_ref = 1;
+       // alloc the second page, since we will need it later to init the other cores
+       // probably need to be smarter about what page we use (make this dynamic) TODO
+       pages[1].pp_ref = 1;
+       for (i = 2; i < PPN(IOPHYSMEM); i++) {
+               pages[i].pp_ref = 0;
+               LIST_INSERT_HEAD(&page_free_list, &pages[i], pp_link);
+       }
+       for (i = PPN(IOPHYSMEM); i < PPN(EXTPHYSMEM); i++) {
+               pages[i].pp_ref = 1;
+       }
+       for (i = PPN(EXTPHYSMEM); i < PPN(physaddr_after_kernel); i++) {
+               pages[i].pp_ref = 1;
+       }
+       for (i = PPN(physaddr_after_kernel); i < PPN(maxaddrpa); i++) {
+               pages[i].pp_ref = 0;
+               LIST_INSERT_HEAD(&page_free_list, &pages[i], pp_link);
+       }
+       // this block out all memory above maxaddrpa.  will need another mechanism
+       // to allocate and map these into the kernel address space
+       for (i = PPN(maxaddrpa); i < npage; i++) {
+               pages[i].pp_ref = 1;
+       }
+ }
+ /* 
+  * Remove the second level page table associated with virtual address va.
+  * Will 0 out the PDE for that page table.
+  * Panics if the page table has any present entries.
+  * This should be called rarely and with good cause.
+  * Currently errors if the PDE is jumbo or not present.
+  */
+ error_t       pagetable_remove(pde_t *pgdir, void *va)
+ {
+       pde_t* the_pde = &pgdir[PDX(va)];
+       if (!(*the_pde & PTE_P) || (*the_pde & PTE_PS))
+               return -EFAULT;
+       pte_t* page_table = (pde_t*)KADDR(PTE_ADDR(*the_pde));
+       for (int i = 0; i < NPTENTRIES; i++) 
+               if (page_table[i] & PTE_P)
+                       panic("Page table not empty during attempted removal!");
+       *the_pde = 0;
+       page_decref(pa2page(PADDR(page_table)));
+       return 0;
+ }
+ // Given 'pgdir', a pointer to a page directory, pgdir_walk returns
+ // a pointer to the page table entry (PTE) for linear address 'va'.
+ // This requires walking the two-level page table structure.
+ //
+ // If the relevant page table doesn't exist in the page directory, then:
+ //    - If create == 0, pgdir_walk returns NULL.
+ //    - Otherwise, pgdir_walk tries to allocate a new page table
+ //    with page_alloc.  If this fails, pgdir_walk returns NULL.
+ //    - Otherwise, pgdir_walk returns a pointer into the new page table.
+ //
+ // This is boot_pgdir_walk, but using page_alloc() instead of boot_alloc().
+ // Unlike boot_pgdir_walk, pgdir_walk can fail.
+ //
+ // Hint: you can turn a Page * into the physical address of the
+ // page it refers to with page2pa() from kern/pmap.h.
+ //
+ // Supports returning jumbo (4MB PSE) PTEs.  To create with a jumbo, pass in 2.
+ pte_t*
+ pgdir_walk(pde_t *pgdir, const void *SNT va, int create)
+ {
+       pde_t* the_pde = &pgdir[PDX(va)];
+       page_t *new_table;
+       if (*the_pde & PTE_P) {
+               if (*the_pde & PTE_PS)
+                       return (pte_t*)the_pde;
+               return &((pde_t*)KADDR(PTE_ADDR(*the_pde)))[PTX(va)];
+       }
+       if (!create)
+               return NULL;
+       if (create == 2) {
+               if (JPGOFF(va))
+                       panic("Attempting to find a Jumbo PTE at an unaligned VA!");
+               *the_pde = PTE_PS | PTE_P;
+               return (pte_t*)the_pde;
+       }
+       if (page_alloc(&new_table))
+               return NULL;
+       new_table->pp_ref = 1;
+       memset(page2kva(new_table), 0, PGSIZE);
+       *the_pde = (pde_t)page2pa(new_table) | PTE_P | PTE_W | PTE_U;
+       return &((pde_t*)KADDR(PTE_ADDR(*the_pde)))[PTX(va)];
+ }
+ /* Flushes a TLB, including global pages.  We should always have the CR4_PGE
+  * flag set, but just in case, we'll check.  Toggling this bit flushes the TLB.
+  */
+ void tlb_flush_global(void)
+ {
+       uint32_t cr4 = rcr4();
+       if (cr4 & CR4_PGE) {
+               lcr4(cr4 & ~CR4_PGE);
+               lcr4(cr4);
+       } else 
+               lcr3(rcr3());
+ }
+ void
+ page_check(void)
+ {
+       page_t *pp, *pp0, *pp1, *pp2;
+       page_list_t fl;
+       pte_t *ptep;
+       // 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(pp0);
+       assert(pp1 && pp1 != pp0);
+       assert(pp2 && pp2 != pp1 && pp2 != pp0);
+       // temporarily steal the rest of the free pages
+       fl = page_free_list;
+       LIST_INIT(&page_free_list);
+       // should be no free memory
+       assert(page_alloc(&pp) == -ENOMEM);
+       // Fill pp1 with bogus data and check for invalid tlb entries
+       memset(page2kva(pp1), 0xFFFFFFFF, PGSIZE);
+       // there is no page allocated at address 0
+       assert(page_lookup(boot_pgdir, (void *) 0x0, &ptep) == NULL);
+       // there is no free memory, so we can't allocate a page table 
+       assert(page_insert(boot_pgdir, pp1, 0x0, 0) < 0);
+       // free pp0 and try again: pp0 should be used for page table
+       page_free(pp0);
+       assert(page_insert(boot_pgdir, pp1, 0x0, 0) == 0);
+       tlb_invalidate(boot_pgdir, 0x0);
+       // DEP Should have shot down invalid TLB entry - let's check
+       {
+         int *x = 0x0;
+         assert(*x == 0xFFFFFFFF);
+       }
+       assert(PTE_ADDR(boot_pgdir[0]) == page2pa(pp0));
+       assert(check_va2pa(boot_pgdir, 0x0) == page2pa(pp1));
+       assert(pp1->pp_ref == 1);
+       assert(pp0->pp_ref == 1);
+       // should be able to map pp2 at PGSIZE because pp0 is already allocated for page table
+       assert(page_insert(boot_pgdir, pp2, (void*) PGSIZE, 0) == 0);
+       assert(check_va2pa(boot_pgdir, PGSIZE) == page2pa(pp2));
+       assert(pp2->pp_ref == 1);
+       // Make sure that pgdir_walk returns a pointer to the pte and
+       // not the table or some other garbage
+       {
+         pte_t *p = KADDR(PTE_ADDR(boot_pgdir[PDX(PGSIZE)]));
+         assert(pgdir_walk(boot_pgdir, (void *)PGSIZE, 0) == &p[PTX(PGSIZE)]);
+       }
+       // should be no free memory
+       assert(page_alloc(&pp) == -ENOMEM);
+       // should be able to map pp2 at PGSIZE because it's already there
+       assert(page_insert(boot_pgdir, pp2, (void*) PGSIZE, PTE_U) == 0);
+       assert(check_va2pa(boot_pgdir, PGSIZE) == page2pa(pp2));
+       assert(pp2->pp_ref == 1);
+       // Make sure that we actually changed the permission on pp2 when we re-mapped it
+       {
+         pte_t *p = pgdir_walk(boot_pgdir, (void*)PGSIZE, 0);
+         assert(((*p) & PTE_U) == PTE_U);
+       }
+       // pp2 should NOT be on the free list
+       // could happen in ref counts are handled sloppily in page_insert
+       assert(page_alloc(&pp) == -ENOMEM);
+       // should not be able to map at PTSIZE because need free page for page table
+       assert(page_insert(boot_pgdir, pp0, (void*) PTSIZE, 0) < 0);
+       // insert pp1 at PGSIZE (replacing pp2)
+       assert(page_insert(boot_pgdir, pp1, (void*) PGSIZE, 0) == 0);
+       // should have pp1 at both 0 and PGSIZE, pp2 nowhere, ...
+       assert(check_va2pa(boot_pgdir, 0) == page2pa(pp1));
+       assert(check_va2pa(boot_pgdir, PGSIZE) == page2pa(pp1));
+       // ... and ref counts should reflect this
+       assert(pp1->pp_ref == 2);
+       assert(pp2->pp_ref == 0);
+       // pp2 should be returned by page_alloc
+       assert(page_alloc(&pp) == 0 && pp == pp2);
+       // unmapping pp1 at 0 should keep pp1 at PGSIZE
+       page_remove(boot_pgdir, 0x0);
+       assert(check_va2pa(boot_pgdir, 0x0) == ~0);
+       assert(check_va2pa(boot_pgdir, PGSIZE) == page2pa(pp1));
+       assert(pp1->pp_ref == 1);
+       assert(pp2->pp_ref == 0);
+       // unmapping pp1 at PGSIZE should free it
+       page_remove(boot_pgdir, (void*) PGSIZE);
+       assert(check_va2pa(boot_pgdir, 0x0) == ~0);
+       assert(check_va2pa(boot_pgdir, PGSIZE) == ~0);
+       assert(pp1->pp_ref == 0);
+       assert(pp2->pp_ref == 0);
+       // so it should be returned by page_alloc
+       assert(page_alloc(&pp) == 0 && pp == pp1);
+       // should be no free memory
+       assert(page_alloc(&pp) == -ENOMEM);
+       // forcibly take pp0 back
+       assert(PTE_ADDR(boot_pgdir[0]) == page2pa(pp0));
+       boot_pgdir[0] = 0;
+       assert(pp0->pp_ref == 1);
+       pp0->pp_ref = 0;
+       // Catch invalid pointer addition in pgdir_walk - i.e. pgdir + PDX(va)
+       {
+         // Give back pp0 for a bit
+         page_free(pp0);
+         void * va = (void *)((PGSIZE * NPDENTRIES) + PGSIZE);
+         pte_t *p2 = pgdir_walk(boot_pgdir, va, 1);
+         pte_t *p = KADDR(PTE_ADDR(boot_pgdir[PDX(va)]));
+         assert(p2 == &p[PTX(va)]);
+         // Clean up again
+         boot_pgdir[PDX(va)] = 0;
+         pp0->pp_ref = 0;
+       }
+       // give free list back
+       page_free_list = fl;
+       // free the pages we took
+       page_free(pp0);
+       page_free(pp1);
+       page_free(pp2);
+       cprintf("page_check() succeeded!\n");
+ }
+ /* 
+     // testing code for boot_pgdir_walk 
+       pte_t* temp;
+       temp = boot_pgdir_walk(pgdir, VPT + (VPT >> 10), 1);
+       cprintf("pgdir = %p\n", pgdir);
+       cprintf("test recursive walking pte_t* = %p\n", temp);
+       cprintf("test recursive walking entry = %p\n", PTE_ADDR(temp));
+       temp = boot_pgdir_walk(pgdir, 0xc0400000, 1);
+       cprintf("LA = 0xc0400000 = %p\n", temp);
+       temp = boot_pgdir_walk(pgdir, 0xc0400070, 1);
+       cprintf("LA = 0xc0400070 = %p\n", temp);
+       temp = boot_pgdir_walk(pgdir, 0xc0800000, 0);
+       cprintf("LA = 0xc0800000, no create = %p\n", temp);
+       temp = boot_pgdir_walk(pgdir, 0xc0600070, 1);
+       cprintf("LA = 0xc0600070 = %p\n", temp);
+       temp = boot_pgdir_walk(pgdir, 0xc0600090, 0);
+       cprintf("LA = 0xc0600090, nc = %p\n", temp);
+       temp = boot_pgdir_walk(pgdir, 0xc0608070, 0);
+       cprintf("LA = 0xc0608070, nc = %p\n", temp);
+       temp = boot_pgdir_walk(pgdir, 0xc0800070, 1);
+       cprintf("LA = 0xc0800070 = %p\n", temp);
+       temp = boot_pgdir_walk(pgdir, 0xc0b00070, 0);
+       cprintf("LA = 0xc0b00070, nc = %p\n", temp);
+       temp = boot_pgdir_walk(pgdir, 0xc0c00000, 0);
+       cprintf("LA = 0xc0c00000, nc = %p\n", temp);
+       // testing for boot_map_seg
+       cprintf("\n");
+       cprintf("before mapping 1 page to 0x00350000\n");
+       cprintf("0xc4000000's &pte: %08x\n",boot_pgdir_walk(pgdir, 0xc4000000, 1));
+       cprintf("0xc4000000's pte: %08x\n",*(boot_pgdir_walk(pgdir, 0xc4000000, 1)));
+       boot_map_segment(pgdir, 0xc4000000, 4096, 0x00350000, PTE_W);
+       cprintf("after mapping\n");
+       cprintf("0xc4000000's &pte: %08x\n",boot_pgdir_walk(pgdir, 0xc4000000, 1));
+       cprintf("0xc4000000's pte: %08x\n",*(boot_pgdir_walk(pgdir, 0xc4000000, 1)));
+       cprintf("\n");
+       cprintf("before mapping 3 pages to 0x00700000\n");
+       cprintf("0xd0000000's &pte: %08x\n",boot_pgdir_walk(pgdir, 0xd0000000, 1));
+       cprintf("0xd0000000's pte: %08x\n",*(boot_pgdir_walk(pgdir, 0xd0000000, 1)));
+       cprintf("0xd0001000's &pte: %08x\n",boot_pgdir_walk(pgdir, 0xd0001000, 1));
+       cprintf("0xd0001000's pte: %08x\n",*(boot_pgdir_walk(pgdir, 0xd0001000, 1)));
+       cprintf("0xd0002000's &pte: %08x\n",boot_pgdir_walk(pgdir, 0xd0002000, 1));
+       cprintf("0xd0002000's pte: %08x\n",*(boot_pgdir_walk(pgdir, 0xd0002000, 1)));
+       boot_map_segment(pgdir, 0xd0000000, 4096*3, 0x00700000, 0);
+       cprintf("after mapping\n");
+       cprintf("0xd0000000's &pte: %08x\n",boot_pgdir_walk(pgdir, 0xd0000000, 1));
+       cprintf("0xd0000000's pte: %08x\n",*(boot_pgdir_walk(pgdir, 0xd0000000, 1)));
+       cprintf("0xd0001000's &pte: %08x\n",boot_pgdir_walk(pgdir, 0xd0001000, 1));
+       cprintf("0xd0001000's pte: %08x\n",*(boot_pgdir_walk(pgdir, 0xd0001000, 1)));
+       cprintf("0xd0002000's &pte: %08x\n",boot_pgdir_walk(pgdir, 0xd0002000, 1));
+       cprintf("0xd0002000's pte: %08x\n",*(boot_pgdir_walk(pgdir, 0xd0002000, 1)));
+       cprintf("\n");
+       cprintf("before mapping 1 unaligned to 0x00500010\n");
+       cprintf("0xc8000010's &pte: %08x\n",boot_pgdir_walk(pgdir, 0xc8000010, 1));
+       cprintf("0xc8000010's pte: %08x\n",*(boot_pgdir_walk(pgdir, 0xc8000010, 1)));
+       cprintf("0xc8001010's &pte: %08x\n",boot_pgdir_walk(pgdir, 0xc8001010, 1));
+       cprintf("0xc8001010's pte: %08x\n",*(boot_pgdir_walk(pgdir, 0xc8001010, 1)));
+       boot_map_segment(pgdir, 0xc8000010, 4096, 0x00500010, PTE_W);
+       cprintf("after mapping\n");
+       cprintf("0xc8000010's &pte: %08x\n",boot_pgdir_walk(pgdir, 0xc8000010, 1));
+       cprintf("0xc8000010's pte: %08x\n",*(boot_pgdir_walk(pgdir, 0xc8000010, 1)));
+       cprintf("0xc8001010's &pte: %08x\n",boot_pgdir_walk(pgdir, 0xc8001010, 1));
+       cprintf("0xc8001010's pte: %08x\n",*(boot_pgdir_walk(pgdir, 0xc8001010, 1)));
+       cprintf("\n");
+       boot_map_segment(pgdir, 0xe0000000, 4096, 0x10000000, PTE_W);
+ */
index 0000000,dd3cf0d..c585d02
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,170 +1,176 @@@
++/*
++ * Copyright (c) 2009 The Regents of the University of California
++ * Barret Rhoden <brho@cs.berkeley.edu>
++ * See LICENSE for details.
++ */
++
+ #ifdef __DEPUTY__
+ #pragma nodeputy
+ #endif
+ #include <arch/arch.h>
+ #include <smp.h>
+ #include <atomic.h>
+ #include <ros/error.h>
+ #include <stdio.h>
+ #include <string.h>
+ #include <assert.h>
+ #include <pmap.h>
+ #include <env.h>
+ #include <trap.h>
+ /*************************** IPI Wrapper Stuff ********************************/
+ // checklists to protect the global interrupt_handlers for 0xf0, f1, f2, f3, f4
+ // need to be global, since there is no function that will always exist for them
+ handler_wrapper_t             handler_wrappers[NUM_HANDLER_WRAPPERS];
+ static int smp_call_function(uint8_t type, uint8_t dest, isr_t handler, void* data,
+                               handler_wrapper_t** wait_wrapper)
+ {
+       extern handler_t interrupt_handlers[];
+       int8_t state = 0;
+       uint32_t wrapper_num;
+       handler_wrapper_t* wrapper;
+       extern atomic_t outstanding_calls;
+       // prevents us from ever having more than NUM_HANDLER_WRAPPERS callers in
+       // the process of competing for vectors.  not decremented until both after
+       // the while(1) loop and after it's been waited on.
+       atomic_inc(&outstanding_calls);
+       if (atomic_read(&outstanding_calls) > NUM_HANDLER_WRAPPERS) {
+               atomic_dec(&outstanding_calls);
+               return -EBUSY;
+       }
+       
+       // assumes our cores are numbered in order
+       if ((type == 4) && (dest >= num_cpus))
+               panic("Destination CPU does not exist!");
+       // build the mask based on the type and destination
+       INIT_CHECKLIST_MASK(cpu_mask, MAX_NUM_CPUS);
+       // set checklist mask's size dynamically to the num cpus actually present
+       cpu_mask.size = num_cpus;
+       switch (type) {
+               case 1: // self
+                       SET_BITMASK_BIT(cpu_mask.bits, core_id());
+                       break;
+               case 2: // all
+                       FILL_BITMASK(cpu_mask.bits, num_cpus);
+                       break;
+               case 3: // all but self
+                       FILL_BITMASK(cpu_mask.bits, num_cpus);
+                       CLR_BITMASK_BIT(cpu_mask.bits, core_id());
+                       break;
+               case 4: // physical mode
+                       // note this only supports sending to one specific physical id
+                       // (only sets one bit, so if multiple cores have the same phys id
+                       // the first one through will set this).
+                       SET_BITMASK_BIT(cpu_mask.bits, dest);
+                       break;
+               case 5: // logical mode
+                       // TODO
+                       warn("Logical mode bitmask handler protection not implemented!");
+                       break;
+               default:
+                       panic("Invalid type for cross-core function call!");
+       }
+       // Find an available vector/wrapper.  Starts with this core's id (mod the
+       // number of wrappers).  Walk through on conflict.
+       // Commit returns an error if it wanted to give up for some reason,
+       // like taking too long to acquire the lock or clear the mask, at which
+       // point, we try the next one.
+       // When we are done, wrapper points to the one we finally got.
+       // this wrapper_num trick doesn't work as well if you send a bunch in a row
+       // and wait, since you always check your main one (which is currently busy).
+       wrapper_num = core_id() % NUM_HANDLER_WRAPPERS;
+       while(1) {
+               wrapper = &handler_wrappers[wrapper_num];
+               if (!commit_checklist_wait(wrapper->cpu_list, &cpu_mask))
+                       break;
+               wrapper_num = (wrapper_num + 1) % NUM_HANDLER_WRAPPERS;
+               /*
+               uint32_t count = 0;
+               // instead of deadlock, smp_call can fail with this.  makes it harder
+               // to use (have to check your return value).  consider putting a delay
+               // here too (like if wrapper_num == initial_wrapper_num)
+               if (count++ > NUM_HANDLER_WRAPPERS * 1000) // note 1000 isn't enough...
+                       return -EBUSY;
+               */
+       }
+       // Wanting to wait is expressed by having a non-NULL handler_wrapper_t**
+       // passed in.  Pass out our reference to wrapper, to wait later.
+       // If we don't want to wait, release the checklist (though it is still not
+       // clear, so it can't be used til everyone checks in).
+       if (wait_wrapper)
+               *wait_wrapper = wrapper;
+       else {
+               release_checklist(wrapper->cpu_list);
+               atomic_dec(&outstanding_calls);
+       }
+       // now register our handler to run
+       register_interrupt_handler(interrupt_handlers, wrapper->vector, handler, data);
+       // WRITE MEMORY BARRIER HERE
+       enable_irqsave(&state);
+       // Send the proper type of IPI.  I made up these numbers.
+       switch (type) {
+               case 1:
+                       send_self_ipi(wrapper->vector);
+                       break;
+               case 2:
+                       send_broadcast_ipi(wrapper->vector);
+                       break;
+               case 3:
+                       send_all_others_ipi(wrapper->vector);
+                       break;
+               case 4: // physical mode
+                       send_ipi(dest, 0, wrapper->vector);
+                       break;
+               case 5: // logical mode
+                       send_ipi(dest, 1, wrapper->vector);
+                       break;
+               default:
+                       panic("Invalid type for cross-core function call!");
+       }
+       // wait long enough to receive our own broadcast (PROBABLY WORKS) TODO
+       lapic_wait_to_send();
+       disable_irqsave(&state);
+       return 0;
+ }
+ // Wrapper functions.  Add more as they are needed.
+ int smp_call_function_self(isr_t handler, void* data,
+                            handler_wrapper_t** wait_wrapper)
+ {
+       return smp_call_function(1, 0, handler, data, wait_wrapper);
+ }
+ int smp_call_function_all(isr_t handler, void* data,
+                           handler_wrapper_t** wait_wrapper)
+ {
+       return smp_call_function(2, 0, handler, data, wait_wrapper);
+ }
+ int smp_call_function_single(uint8_t dest, isr_t handler, void* data,
+                              handler_wrapper_t** wait_wrapper)
+ {
+       return smp_call_function(4, dest, handler, data, wait_wrapper);
+ }
+ // If you want to wait, pass the address of a pointer up above, then call
+ // this to do the actual waiting.  Be somewhat careful about uninitialized 
+ // or old wrapper pointers.
+ int smp_call_wait(handler_wrapper_t* wrapper)
+ {
+       if (wrapper) {
+               waiton_checklist(wrapper->cpu_list);
+               return 0;
+       } else {
+               warn("Attempting to wait on null wrapper!  Check your return values!");
+               return -EFAIL;
+       }
+ }
index 0000000,8eedef4..4f7c092
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,14 +1,20 @@@
++/*
++ * Copyright (c) 2009 The Regents of the University of California
++ * Barret Rhoden <brho@cs.berkeley.edu>
++ * See LICENSE for details.
++ */
++
+ #ifndef ROS_ARCH_SMP_H
+ #define ROS_ARCH_SMP_H
+ #include <atomic.h>
+ // be careful changing this, esp if you go over 16
+ #define NUM_HANDLER_WRAPPERS          5
+ typedef struct HandlerWrapper {
+       checklist_t* cpu_list;
+       uint8_t vector;
+ } handler_wrapper_t;
+ #endif
index 0000000,1c27a44..20c479e
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,210 +1,216 @@@
++/*
++ * Copyright (c) 2009 The Regents of the University of California
++ * Barret Rhoden <brho@cs.berkeley.edu>
++ * See LICENSE for details.
++ */
++
+ #ifdef __DEPUTY__
+ #pragma nodeputy
+ #endif
+ #include <arch/x86.h>
+ #include <arch/arch.h>
+ #include <smp.h>
+ #include <arch/console.h>
+ #include <arch/apic.h>
+ #include <atomic.h>
+ #include <ros/error.h>
+ #include <stdio.h>
+ #include <string.h>
+ #include <assert.h>
+ #include <pmap.h>
+ #include <env.h>
+ #include <trap.h>
+ extern handler_wrapper_t handler_wrappers[NUM_HANDLER_WRAPPERS];
+ volatile uint8_t num_cpus = 0xee;
+ uintptr_t smp_stack_top;
+ #define DECLARE_HANDLER_CHECKLISTS(vector)                          \
+       INIT_CHECKLIST(f##vector##_cpu_list, MAX_NUM_CPUS);
+ #define INIT_HANDLER_WRAPPER(v)                                     \
+ {                                                                   \
+       handler_wrappers[(v)].vector = 0xf##v;                          \
+       handler_wrappers[(v)].cpu_list = &f##v##_cpu_list;              \
+       handler_wrappers[(v)].cpu_list->mask.size = num_cpus;           \
+ }
+ DECLARE_HANDLER_CHECKLISTS(0);
+ DECLARE_HANDLER_CHECKLISTS(1);
+ DECLARE_HANDLER_CHECKLISTS(2);
+ DECLARE_HANDLER_CHECKLISTS(3);
+ DECLARE_HANDLER_CHECKLISTS(4);
+ static void init_smp_call_function(void)
+ {
+       INIT_HANDLER_WRAPPER(0);
+       INIT_HANDLER_WRAPPER(1);
+       INIT_HANDLER_WRAPPER(2);
+       INIT_HANDLER_WRAPPER(3);
+       INIT_HANDLER_WRAPPER(4);
+ }
+ /******************************************************************************/
+ static void smp_mtrr_handler(trapframe_t *tf, void* data)
+ {
+       setup_default_mtrrs((barrier_t*)data);
+ }
+ void smp_boot(void)
+ {
+       // this needs to be set in smp_entry too...
+       #define trampoline_pg 0x00001000
+       page_t *smp_stack;
+       // NEED TO GRAB A LOWMEM FREE PAGE FOR AP BOOTUP CODE
+       // page1 (2nd page) is reserved, hardcoded in pmap.c
+       extern smp_entry(), smp_entry_end(), smp_boot_lock(), smp_semaphore();
+       memset(KADDR(trampoline_pg), 0, PGSIZE);
+       memcpy(KADDR(trampoline_pg), &smp_entry, &smp_entry_end - &smp_entry);
+       // This mapping allows access to the trampoline with paging on and off
+       // via trampoline_pg
+       page_insert(boot_pgdir, pa2page(trampoline_pg), (void*)trampoline_pg, PTE_W);
+       // Allocate a stack for the cores starting up.  One for all, must share
+       if (page_alloc(&smp_stack))
+               panic("No memory for SMP boot stack!");
+       smp_stack->pp_ref++;
+       smp_stack_top = (uintptr_t)(page2kva(smp_stack) + PGSIZE);
+       // Start the IPI process (INIT, wait, SIPI, wait, SIPI, wait)
+       send_init_ipi();
+       // SDM 3A is a little wonky wrt the proper delays.  These are my best guess.
+       udelay(10000);
+       // first SIPI
+       send_startup_ipi(0x01);
+       /* BOCHS does not like this second SIPI.
+       // second SIPI
+       udelay(200);
+       send_startup_ipi(0x01);
+       */
+       udelay(100000);
+       // Each core will also increment smp_semaphore, and decrement when it is done,
+       // all in smp_entry.  It's purpose is to keep Core0 from competing for the
+       // smp_boot_lock.  So long as one AP increments the sem before the final
+       // LAPIC timer goes off, all available cores will be initialized.
+       while(*(volatile uint32_t*)(&smp_semaphore - &smp_entry + trampoline_pg));
+       // From here on, no other cores are coming up.  Grab the lock to ensure it.
+       // Another core could be in it's prelock phase and be trying to grab the lock
+       // forever....
+       // The lock exists on the trampoline, so it can be grabbed right away in
+       // real mode.  If core0 wins the race and blocks other CPUs from coming up
+       // it can crash the machine if the other cores are allowed to proceed with
+       // 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((uint32_t*)(&smp_boot_lock - &smp_entry + trampoline_pg));
+       cprintf("Num_Cpus Detected: %d\n", num_cpus);
+       // Remove the mapping of the page used by the trampoline
+       page_remove(boot_pgdir, (void*)trampoline_pg);
+       // It had a refcount of 2 earlier, so we need to dec once more to free it
+       // but only if all cores are in (or we reset / reinit those that failed)
+       // TODO after we parse ACPI tables
+       if (num_cpus == 8) // TODO - ghetto coded for our 8 way SMPs
+               page_decref(pa2page(trampoline_pg));
+       // Remove the page table used for that mapping
+       pagetable_remove(boot_pgdir, (void*)trampoline_pg);
+       // Dealloc the temp shared stack
+       page_decref(smp_stack);
+       // Set up the generic remote function call facility
+       init_smp_call_function();
+       // Set up all cores to use the proper MTRRs
+       barrier_t generic_barrier;
+       init_barrier(&generic_barrier, num_cpus); // barrier used by smp_mtrr_handler
+       smp_call_function_all(smp_mtrr_handler, &generic_barrier, 0);
+       // Should probably flush everyone's TLB at this point, to get rid of
+       // temp mappings that were removed.  TODO
+ }
+ /*
+  * This is called from smp_entry by each core to finish the core bootstrapping.
+  * There is a spinlock around this entire function in smp_entry, for a few reasons,
+  * the most important being that all cores use the same stack when entering here.
+  */
+ uint32_t smp_main(void)
+ {
+       /*
+       // Print some diagnostics.  Uncomment if there're issues.
+       cprintf("Good morning Vietnam!\n");
+       cprintf("This core's Default APIC ID: 0x%08x\n", lapic_get_default_id());
+       cprintf("This core's Current APIC ID: 0x%08x\n", lapic_get_id());
+       if (read_msr(IA32_APIC_BASE) & 0x00000100)
+               cprintf("I am the Boot Strap Processor\n");
+       else
+               cprintf("I am an Application Processor\n");
+       cprintf("Num_Cpus: %d\n\n", num_cpus);
+       */
+       // Get a per-core kernel stack
+       page_t *my_stack;
+       if (page_alloc(&my_stack))
+               panic("Unable to alloc a per-core stack!");
+       my_stack->pp_ref++;
+       memset(page2kva(my_stack), 0, PGSIZE);
+       // Set up a gdt / gdt_pd for this core, stored at the top of the stack
+       // This is necessary, eagle-eyed readers know why
+       // GDT should be 4-byte aligned.  TS isn't aligned.  Not sure if it matters.
+       pseudodesc_t *my_gdt_pd = page2kva(my_stack) + PGSIZE -
+               sizeof(pseudodesc_t) - sizeof(segdesc_t)*SEG_COUNT;
+       segdesc_t *my_gdt = page2kva(my_stack) + PGSIZE -
+               sizeof(segdesc_t)*SEG_COUNT;
+       // TS also needs to be permanent
+       taskstate_t *my_ts = page2kva(my_stack) + PGSIZE -
+               sizeof(pseudodesc_t) - sizeof(segdesc_t)*SEG_COUNT -
+               sizeof(taskstate_t);
+       // Usable portion of the KSTACK grows down from here
+       // Won't actually start using this stack til our first interrupt
+       // (issues with changing the stack pointer and then trying to "return")
+       uintptr_t my_stack_top = (uintptr_t)my_ts;
+       
+       // Set up MSR for SYSENTER 
+       write_msr(MSR_IA32_SYSENTER_CS, GD_KT);
+       write_msr(MSR_IA32_SYSENTER_ESP, my_stack_top);
+       write_msr(MSR_IA32_SYSENTER_EIP, (uint32_t) &sysenter_handler);
+       // Build and load the gdt / gdt_pd
+       memcpy(my_gdt, gdt, sizeof(segdesc_t)*SEG_COUNT);
+       *my_gdt_pd = (pseudodesc_t) {
+               sizeof(segdesc_t)*SEG_COUNT - 1, (uintptr_t) my_gdt };
+       asm volatile("lgdt %0" : : "m"(*my_gdt_pd));
+       // Need to set the TSS so we know where to trap on this core
+       my_ts->ts_esp0 = my_stack_top;
+       my_ts->ts_ss0 = GD_KD;
+       // Initialize the TSS field of my_gdt.
+       my_gdt[GD_TSS >> 3] = SEG16(STS_T32A, (uint32_t) (my_ts), sizeof(taskstate_t), 0);
+       my_gdt[GD_TSS >> 3].sd_s = 0;
+       // Load the TSS
+       ltr(GD_TSS);
+       // Loads the same IDT used by the other cores
+       asm volatile("lidt idt_pd");
+       // APIC setup
+       // set LINT0 to receive ExtINTs (KVM's default).  At reset they are 0x1000.
+       write_mmreg32(LAPIC_LVT_LINT0, 0x700);
+       // mask it to shut it up for now.  Doesn't seem to matter yet, since both
+       // KVM and Bochs seem to only route the PIC to core0.
+       mask_lapic_lvt(LAPIC_LVT_LINT0);
+       // and then turn it on
+       lapic_enable();
+       // set a default logical id for now
+       lapic_set_logid(lapic_get_id());
+       return my_stack_top; // will be loaded in smp_entry.S
+ }
index 0000000,498022f..c06c489
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,361 +1,360 @@@
 -#include <env.h>
+ #ifdef __DEPUTY__
+ #pragma noasync
+ #endif
+ #include <arch/mmu.h>
+ #include <arch/x86.h>
+ #include <arch/arch.h>
+ #include <arch/console.h>
+ #include <arch/apic.h>
+ #include <smp.h>
+ #include <assert.h>
+ #include <pmap.h>
+ #include <trap.h>
+ #include <monitor.h>
 -      cprintf("TRAP frame at %p on core %d\n", tf, lapic_get_id());
++#include <process.h>
+ #include <syscall.h>
+ taskstate_t ts;
+ /* Interrupt descriptor table.  (Must be built at run time because
+  * shifted function addresses can't be represented in relocation records.)
+  */
+ // Aligned on an 8 byte boundary (SDM V3A 5-13)
+ gatedesc_t __attribute__ ((aligned (8))) idt[256] = { { 0 } };
+ pseudodesc_t idt_pd = {
+       sizeof(idt) - 1, (uint32_t) idt
+ };
+ /* global handler table, used by core0 (for now).  allows the registration
+  * of functions to be called when servicing an interrupt.  other cores
+  * can set up their own later.
+  */
+ handler_t interrupt_handlers[256];
+ static const char *NTS (IN_HANDLER trapname)(int trapno)
+ {
+     // zra: excnames is NORACE because Ivy doesn't trust const
+       static const char *NT const (NORACE excnames)[] = {
+               "Divide error",
+               "Debug",
+               "Non-Maskable Interrupt",
+               "Breakpoint",
+               "Overflow",
+               "BOUND Range Exceeded",
+               "Invalid Opcode",
+               "Device Not Available",
+               "Double Fault",
+               "Coprocessor Segment Overrun",
+               "Invalid TSS",
+               "Segment Not Present",
+               "Stack Fault",
+               "General Protection",
+               "Page Fault",
+               "(unknown trap)",
+               "x87 FPU Floating-Point Error",
+               "Alignment Check",
+               "Machine-Check",
+               "SIMD Floating-Point Exception"
+       };
+       if (trapno < sizeof(excnames)/sizeof(excnames[0]))
+               return excnames[trapno];
+       if (trapno == T_SYSCALL)
+               return "System call";
+       return "(unknown trap)";
+ }
+ void
+ idt_init(void)
+ {
+       extern segdesc_t gdt[];
+       // This table is made in trapentry.S by each macro in that file.
+       // It is layed out such that the ith entry is the ith's traphandler's
+       // (uint32_t) trap addr, then (uint32_t) trap number
+       struct trapinfo { uint32_t trapaddr; uint32_t trapnumber; };
+       extern struct trapinfo (BND(__this,trap_tbl_end) trap_tbl)[];
+       extern struct trapinfo (SNT trap_tbl_end)[];
+       int i, trap_tbl_size = trap_tbl_end - trap_tbl;
+       extern void ISR_default(void);
+       // set all to default, to catch everything
+       for(i = 0; i < 256; i++)
+               SETGATE(idt[i], 0, GD_KT, &ISR_default, 0);
+       // set all entries that have real trap handlers
+       // we need to stop short of the last one, since the last is the default
+       // handler with a fake interrupt number (500) that is out of bounds of
+       // the idt[]
+       // if we set these to trap gates, be sure to handle the IRQs separately
+       // and we might need to break our pretty tables
+       for(i = 0; i < trap_tbl_size - 1; i++)
+               SETGATE(idt[trap_tbl[i].trapnumber], 0, GD_KT, trap_tbl[i].trapaddr, 0);
+       // turn on syscall handling and other user-accessible ints
+       // DPL 3 means this can be triggered by the int instruction
+       // STS_TG32 sets the IDT type to a Trap Gate (interrupts enabled)
+       idt[T_SYSCALL].gd_dpl = 3;
+       idt[T_SYSCALL].gd_type = STS_TG32;
+       idt[T_BRKPT].gd_dpl = 3;
+       // Setup a TSS so that we get the right stack
+       // when we trap to the kernel.
+       ts.ts_esp0 = KSTACKTOP;
+       ts.ts_ss0 = GD_KD;
+       // Initialize the TSS field of the gdt.
+       gdt[GD_TSS >> 3] = SEG16(STS_T32A, (uint32_t) (&ts),
+                                       sizeof(taskstate_t), 0);
+       gdt[GD_TSS >> 3].sd_s = 0;
+       // Load the TSS
+       ltr(GD_TSS);
+       // Load the IDT
+       asm volatile("lidt idt_pd");
+       // This will go away when we start using the IOAPIC properly
+       pic_remap();
+       // set LINT0 to receive ExtINTs (KVM's default).  At reset they are 0x1000.
+       write_mmreg32(LAPIC_LVT_LINT0, 0x700);
+       // mask it to shut it up for now
+       mask_lapic_lvt(LAPIC_LVT_LINT0);
+       // and turn it on
+       lapic_enable();
+ }
+ void
+ (IN_HANDLER print_regs)(push_regs_t *regs)
+ {
+       cprintf("  edi  0x%08x\n", regs->reg_edi);
+       cprintf("  esi  0x%08x\n", regs->reg_esi);
+       cprintf("  ebp  0x%08x\n", regs->reg_ebp);
+       cprintf("  oesp 0x%08x\n", regs->reg_oesp);
+       cprintf("  ebx  0x%08x\n", regs->reg_ebx);
+       cprintf("  edx  0x%08x\n", regs->reg_edx);
+       cprintf("  ecx  0x%08x\n", regs->reg_ecx);
+       cprintf("  eax  0x%08x\n", regs->reg_eax);
+ }
+ void
+ (IN_HANDLER print_trapframe)(trapframe_t *tf)
+ {
 -      env_t* curenv = curenvs[core_id()];
 -
++      cprintf("TRAP frame at %p on core %d\n", tf, core_id());
+       print_regs(&tf->tf_regs);
+       cprintf("  es   0x----%04x\n", tf->tf_es);
+       cprintf("  ds   0x----%04x\n", tf->tf_ds);
+       cprintf("  trap 0x%08x %s\n", tf->tf_trapno, trapname(tf->tf_trapno));
+       cprintf("  err  0x%08x\n", tf->tf_err);
+       cprintf("  eip  0x%08x\n", tf->tf_eip);
+       cprintf("  cs   0x----%04x\n", tf->tf_cs);
+       cprintf("  flag 0x%08x\n", tf->tf_eflags);
+       cprintf("  esp  0x%08x\n", tf->tf_esp);
+       cprintf("  ss   0x----%04x\n", tf->tf_ss);
+ }
+ static void
+ (IN_HANDLER trap_dispatch)(trapframe_t *tf)
+ {
 -                              syscall(curenv, tf->tf_regs.reg_eax, tf->tf_regs.reg_edx,
+       // Handle processor exceptions.
+       switch(tf->tf_trapno) {
+               case T_BRKPT:
+                       while (1)
+                               monitor(tf);
+                       // never get to this
+                       assert(0);
+               case T_PGFLT:
+                       page_fault_handler(tf);
+                       break;
+               case T_SYSCALL:
+                       // check for userspace, for now
+                       assert(tf->tf_cs != GD_KT);
+                       tf->tf_regs.reg_eax =
 -                      env_run(curenv);
++                              syscall(current, tf->tf_regs.reg_eax, tf->tf_regs.reg_edx,
+                                       tf->tf_regs.reg_ecx, tf->tf_regs.reg_ebx,
+                                       tf->tf_regs.reg_edi, tf->tf_regs.reg_esi);
 -                              env_destroy(curenv);
++                      proc_startcore(current, tf); // Note the comment in syscall.c
+                       break;
+               default:
+                       // Unexpected trap: The user process or the kernel has a bug.
+                       print_trapframe(tf);
+                       if (tf->tf_cs == GD_KT)
+                               panic("Damn Damn!  Unhandled trap in the kernel!");
+                       else {
+                               warn("Unexpected trap from userspace");
 -      env_t* curenv = curenvs[lapic_get_id()];
 -
 -      env_push_ancillary_state(curenv);
++                              env_destroy(current);
+                               return;
+                       }
+       }
+       return;
+ }
+ void
+ (IN_HANDLER env_push_ancillary_state)(env_t* e)
+ {
+       // Here's where you'll save FP/MMX/XMM regs
+ }
+ void
+ (IN_HANDLER env_pop_ancillary_state)(env_t* e)
+ {
+       // Here's where you'll restore FP/MMX/XMM regs
+ }
+ void
+ (IN_HANDLER trap)(trapframe_t *tf)
+ {
+       //cprintf("Incoming TRAP frame at %p\n", tf);
 -              // into 'curenv->env_tf', so that running the environment
++      env_push_ancillary_state(current);
+       if ((tf->tf_cs & ~3) != GD_UT && (tf->tf_cs & ~3) != GD_KT) {
+               print_trapframe(tf);
+               panic("Trapframe with invalid CS!");
+       }
+       if ((tf->tf_cs & 3) == 3) {
+               // Trapped from user mode.
+               // TODO: this will change when an env has more than one context
+               // Copy trap frame (which is currently on the stack)
 -              assert(curenv);
 -              curenv->env_tf = *tf;
++              // into 'current->env_tf', so that running the environment
+               // will restart at the trap point.
 -              tf = &curenv->env_tf;
++              assert(current);
++              current->env_tf = *tf;
+               // The trapframe on the stack should be ignored from here on.
 -        // Return to the current environment, which should be runnable.
 -        assert(curenv && curenv->env_status == ENV_RUNNABLE);
 -        env_run(curenv);
++              tf = &current->env_tf;
+       }
+       // Dispatch based on what type of trap occurred
+       trap_dispatch(tf);
+       // should this be if == 3?  Sort out later when we handle traps.
+       // so far we never get here
+       assert(0);
 -      //if (lapic_get_id())
 -      //      cprintf("Incoming IRQ, ISR: %d on core %d\n", tf->tf_trapno, lapic_get_id());
++      // Return to the current environment, which should be runnable.
++      proc_startcore(current, tf); // Note the comment in syscall.c
+ }
+ void
+ (IN_HANDLER irq_handler)(trapframe_t *tf)
+ {
 -      // UXSTACKTOP), then branch to curenv->env_pgfault_upcall.
++      //if (core_id())
++      //      cprintf("Incoming IRQ, ISR: %d on core %d\n", tf->tf_trapno, core_id());
+       // merge this with alltraps?  other than the EOI... or do the same in all traps
+       extern handler_wrapper_t handler_wrappers[NUM_HANDLER_WRAPPERS];
+       // determine the interrupt handler table to use.  for now, pick the global
+       handler_t* handler_tbl = interrupt_handlers;
+       if (handler_tbl[tf->tf_trapno].isr != 0)
+               handler_tbl[tf->tf_trapno].isr(tf, handler_tbl[tf->tf_trapno].data);
+       // if we're a general purpose IPI function call, down the cpu_list
+       if ((0xf0 <= tf->tf_trapno) && (tf->tf_trapno < 0xf0 +NUM_HANDLER_WRAPPERS))
+               down_checklist(handler_wrappers[tf->tf_trapno & 0x0f].cpu_list);
+       // Send EOI.  might want to do this in assembly, and possibly earlier
+       // This is set up to work with an old PIC for now
+       // Convention is that all IRQs between 32 and 47 are for the PIC.
+       // All others are LAPIC (timer, IPIs, perf, non-ExtINT LINTS, etc)
+       // For now, only 235-255 are available
+       assert(tf->tf_trapno >= 32); // slows us down, but we should never have this
+       if (tf->tf_trapno < 48)
+               pic_send_eoi(tf->tf_trapno - PIC1_OFFSET);
+       else
+               lapic_send_eoi();
+ }
+ void
+ register_interrupt_handler(handler_t table[], uint8_t int_num, isr_t handler,
+                            void* data)
+ {
+       table[int_num].isr = handler;
+       table[int_num].data = data;
+ }
+ void
+ page_fault_handler(trapframe_t *tf)
+ {
+       uint32_t fault_va;
+       // Read processor's CR2 register to find the faulting address
+       fault_va = rcr2();
+       // Handle kernel-mode page faults.
+       // TODO - one day, we'll want to handle this.
+       if ((tf->tf_cs & 3) == 0) {
+               print_trapframe(tf);
+               panic("Page Fault in the Kernel at 0x%08x!", fault_va);
+       }
+       // We've already handled kernel-mode exceptions, so if we get here,
+       // the page fault happened in user mode.
+       // Call the environment's page fault upcall, if one exists.  Set up a
+       // page fault stack frame on the user exception stack (below
 -      //   To change what the user environment runs, modify 'curenv->env_tf'
 -      //   (the 'tf' variable points at 'curenv->env_tf').
++      // UXSTACKTOP), then branch to current->env_pgfault_upcall.
+       //
+       // The page fault upcall might cause another page fault, in which case
+       // we branch to the page fault upcall recursively, pushing another
+       // page fault stack frame on top of the user exception stack.
+       //
+       // The trap handler needs one word of scratch space at the top of the
+       // trap-time stack in order to return.  In the non-recursive case, we
+       // don't have to worry about this because the top of the regular user
+       // stack is free.  In the recursive case, this means we have to leave
+       // an extra word between the current top of the exception stack and
+       // the new stack frame because the exception stack _is_ the trap-time
+       // stack.
+       //
+       // If there's no page fault upcall, the environment didn't allocate a
+       // page for its exception stack, or the exception stack overflows,
+       // then destroy the environment that caused the fault.
+       //
+       // Hints:
+       //   user_mem_assert() and env_run() are useful here.
 -      env_t* curenv = curenvs[lapic_get_id()];
++      //   To change what the user environment runs, modify 'current->env_tf'
++      //   (the 'tf' variable points at 'current->env_tf').
+       // LAB 4: Your code here.
+       // Destroy the environment that caused the fault.
 -              curenv->env_id, fault_va, tf->tf_eip, lapic_get_id());
+       cprintf("[%08x] user fault va %08x ip %08x from core %d\n",
 -      env_destroy(curenv);
++              current->env_id, fault_va, tf->tf_eip, core_id());
+       print_trapframe(tf);
 -      env_t* curenv = curenvs[lapic_get_id()];
 -      curenv->env_tf = *tf;
++      env_destroy(current);
+ }
+ void sysenter_init(void)
+ {
+       write_msr(MSR_IA32_SYSENTER_CS, GD_KT);
+       write_msr(MSR_IA32_SYSENTER_ESP, ts.ts_esp0);
+       write_msr(MSR_IA32_SYSENTER_EIP, (uint32_t) &sysenter_handler);
+ }
+ /* This is called from sysenter's asm, with the tf on the kernel stack. */
+ void sysenter_callwrapper(struct Trapframe *tf)
+ {
 -      tf = &curenv->env_tf;
 -      tf->tf_regs.reg_eax = (intreg_t) syscall(curenv,
++      current->env_tf = *tf;
+       
+       // The trapframe on the stack should be ignored from here on.
 -      env_run(curenv);
++      tf = &current->env_tf;
++      tf->tf_regs.reg_eax = (intreg_t) syscall(current,
+                                                tf->tf_regs.reg_eax,
+                                                tf->tf_regs.reg_edx,
+                                                tf->tf_regs.reg_ecx,
+                                                tf->tf_regs.reg_ebx,
+                                                tf->tf_regs.reg_edi,
+                                                0);
++      /*
++       * careful here - we need to make sure that this current is the right
++       * process, which could be weird if the syscall blocked.  it would need to
++       * restore the proper value in current before returning to here.
++       * likewise, tf could be pointing to random gibberish.
++       */
++      proc_startcore(current, tf);
+ }
index 0000000,da6a102..54ebf75
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,48 +1,128 @@@
 -#include <ros/env.h>
+ /* See COPYRIGHT for copyright information. */
+ #ifndef ROS_KERN_ENV_H
+ #define ROS_KERN_ENV_H
++/* Note that the old include/ros/env.h is merged into this file */
 -int   env_alloc(env_t **e, envid_t parent_id);
++#include <ros/memlayout.h>
++#include <ros/syscall.h>
++#include <ros/sysevent.h>
+ #include <ros/error.h>
++#include <arch/trap.h>
++#include <arch/types.h>
+ #include <arch/arch.h>
++#include <sys/queue.h>
++
++struct Env;
++typedef struct Env env_t;
++
++typedef int32_t envid_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.
++
++#define LOG2NENV              10
++#define NENV                  (1 << LOG2NENV)
++#define ENVX(envid)           ((envid) & (NENV - 1))
++
++// TODO: clean this up.
++struct Env {
++      LIST_ENTRY(Env) env_link NOINIT;        // Free list link pointers
++      uint32_t 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
++      envid_t env_id;                         // Unique environment identifier
++      envid_t env_parent_id;          // env_id of this env's parent
++      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;
++      // The backring for processing asynchronous system calls from the user
++      // Note this is the actual backring, not a pointer to it somewhere else
++      syscall_back_ring_t env_syscallbackring;
++      // The front ring for pushing asynchronous system events out to the user
++      // Note this is the actual frontring, not a pointer to it somewhere else
++      sysevent_front_ring_t env_syseventfrontring;
++
++      // Address space
++      pde_t *COUNT(NPDENTRIES) env_pgdir;                     // Kernel virtual address of page dir
++      physaddr_t env_cr3;                     // Physical address of page dir
++
++      // TODO - give this a proper type (pointers to a struct)
++      // - not always going to be PGSIZE either!
++      void*COUNT(PGSIZE) env_procinfo;                // KVA of per-process shared info table (RO)
++      // TODO - do we really want to get rid of procdata?
++      //void*COUNT(PGSIZE) env_procdata;              // KVA of per-process shared data table (RW)
++      
++      // Do we need these and the above back/front rings?
++      // Ring buffers for communicating with user space
++      syscall_sring_t*SAFE env_syscallring;   // Per-process ring buffer for async syscalls
++      sysevent_sring_t*SAFE env_syseventring; // Per-process ring buffer for async sysevents
++};
++
++/* Process Flags */
++// None yet
+ extern env_t *COUNT(NENV) envs;               // All environments
+ extern atomic_t num_envs;             // Number of envs
+ extern env_t* NORACE curenvs[MAX_NUM_CPUS];
+ LIST_HEAD(env_list, Env);             // Declares 'struct env_list'
+ typedef struct env_list env_list_t;
+ void  env_init(void);
++int           env_alloc(env_t *SAFE*SAFE e, envid_t parent_id);
+ void  env_init_trapframe(env_t* e);
+ void  env_set_program_counter(env_t* e, uintptr_t pc);
+ void  env_push_ancillary_state(env_t* e);
+ void  env_pop_ancillary_state(env_t* e);
 -      env_create(_binary_obj_user_apps_##x##_start,                              \
+ void  env_free(env_t *SAFE e);
+ void  env_user_mem_free(env_t* e);
+ error_t       env_incref(env_t* e);
+ void  env_decref(env_t *SAFE e);
+ env_t*        env_create(uint8_t *COUNT(size) binary, size_t size);
+ void  (IN_HANDLER env_destroy)(env_t *SAFE e);        // Does not return if e == curenv
+ // Temporary scheduler function
+ void  schedule(void);
++/*
++ * 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 (curenvs[core_id()])
++
+ int   envid2env(envid_t envid, env_t **env_store, bool checkperm);
+ // The following three functions do not return
+ void  (IN_HANDLER env_run)(env_t *e) __attribute__((noreturn));
+ void  env_pop_tf(trapframe_t *tf) __attribute__((noreturn));
+ /* Helper handler for smp_call to dispatch jobs to other cores */
+ void run_env_handler(trapframe_t *tf, void* data);
++// TODO remove this legacy crap
+ #define ENV_CREATE(x)                 ({                                             \
+       extern uint8_t _binary_obj_user_apps_##x##_start[],                        \
+               _binary_obj_user_apps_##x##_size[];                                    \
++      env_t *e = env_create(_binary_obj_user_apps_##x##_start,                   \
+               (int)_binary_obj_user_apps_##x##_size);                                \
++      proc_set_state(e, PROC_RUNNABLE_S);                                        \
++      e;                                                                         \
+ })
+ #endif // !ROS_KERN_ENV_H
index 0000000,0000000..60fadc1
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,35 @@@
++/*
++ * Copyright (c) 2009 The Regents of the University of California
++ * Barret Rhoden <brho@cs.berkeley.edu>
++ * See LICENSE for details.
++ *
++ * KFS (Kernel File System)
++ *
++ * This gives runtime access to the binary blobs (usually userspace programs)
++ * linked at the end of the kernel.  Extremely rudimentary.
++ * Also allows for process creation from file (can consider moving this).
++ *
++ * Add the files you want in KFS in kfs.c.
++ */
++
++#ifndef ROS_KERN_KFS_H
++#define ROS_KERN_KFS_H
++
++#include <arch/types.h>
++#include <process.h>
++
++#pragma nodeputy
++
++struct kfs_entry {
++      char name[256];
++      uint8_t *start;
++      size_t size;
++};
++
++#define MAX_KFS_FILES 10
++extern struct kfs_entry kfs[MAX_KFS_FILES];
++
++ssize_t kfs_lookup_path(char* path);
++struct proc *kfs_proc_create(int kfs_inode);
++
++#endif // !ROS_KERN_KFS_H
index 0000000,7fb653c..114e56f
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,23 +1,25 @@@
+ #ifndef ROS_KERN_MONITOR_H
+ #define ROS_KERN_MONITOR_H
+ #ifndef ROS_KERNEL
+ # error "This is a ROS kernel header; user programs should not #include it"
+ #endif
+ // Activate the kernel monitor,
+ // optionally providing a trap frame indicating the current state
+ // (NULL if none).
+ void (IN_HANDLER monitor)(trapframe_t *tf);
+ // Functions implementing monitor commands.
+ int mon_help(int argc, char *NTS *NT COUNT(argc) argv, trapframe_t *tf);
+ int mon_kerninfo(int argc, char *NTS *NT COUNT(argc) argv, trapframe_t *tf);
+ int mon_backtrace(int argc, char *NTS *NT COUNT(argc) argv, trapframe_t *tf);
+ int mon_reboot(int argc, char *NTS *NT COUNT(argc) argv, trapframe_t *tf);
+ int mon_showmapping(int argc, char *NTS *NT COUNT(argc) argv, trapframe_t *tf);
+ int mon_setmapperm(int argc, char *NTS *NT COUNT(argc) argv, trapframe_t *tf);
+ int mon_cpuinfo(int argc, char *NTS *NT COUNT(argc) argv, trapframe_t *tf);
+ int mon_nanwan(int argc, char **argv, trapframe_t *tf);
++int mon_kfs_ls(int argc, char *NTS *NT COUNT(argc) argv, trapframe_t *tf);
++int mon_kfs_run(int argc, char *NTS *NT COUNT(argc) argv, trapframe_t *tf);
+ int mon_manager(int argc, char **argv, trapframe_t *tf);
+ #endif        // !ROS_KERN_MONITOR_H
index 0000000,e3b56a5..2248621
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,156 +1,156 @@@
 -#include <env.h>
+ /* See COPYRIGHT for copyright information. */
+ #ifndef ROS_KERN_PMAP_H
+ #define ROS_KERN_PMAP_H
+ #ifndef ROS_KERNEL
+ # error "This is a ROS kernel header; user programs should not #include it"
+ #endif
+ #include <ros/memlayout.h>
+ #include <multiboot.h>
+ #include <atomic.h>
++#include <process.h>
+ #include <assert.h>
+ #include <sys/queue.h>
+ /* This macro takes a kernel virtual address -- an address that points above
+  * KERNBASE, where the machine's maximum 256MB of physical memory is mapped --
+  * and returns the corresponding physical address.  It panics if you pass it a
+  * non-kernel virtual address.
+  */
+ #define PADDR(kva)                                            \
+ ({                                                            \
+       physaddr_t __m_kva = (physaddr_t) (kva);                \
+       if (__m_kva < KERNBASE)                                 \
+               panic("PADDR called with invalid kva %08lx", __m_kva);\
+       __m_kva - KERNBASE;                                     \
+ })
+ /* This macro takes a physical address and returns the corresponding kernel
+  * virtual address.  It warns if you pass an invalid physical address. */
+ #define KADDR(pa)                                             \
+ ({                                                            \
+       physaddr_t __m_pa = (pa);                               \
+       size_t __m_ppn = PPN(__m_pa);                           \
+       if (__m_ppn >= npage)                                   \
+               warn("KADDR called with invalid pa %08lx", __m_pa);\
+       (void*TRUSTED) (__m_pa + KERNBASE);                             \
+ })
+ /*
+  * Page descriptor structures, mapped at UPAGES.
+  * Read/write to the kernel, read-only to user programs.
+  *
+  * Each Page describes one physical page.
+  * You can map a Page * to the corresponding physical address
+  * with page2pa() in kern/pmap.h.
+  */
+ struct Page;
+ typedef struct Page page_t;
+ LIST_HEAD(page_list, Page);
+ typedef struct page_list page_list_t;
+ typedef LIST_ENTRY(Page) page_list_entry_t;
+ struct Page {
+       page_list_entry_t pp_link;      /* free list link */
+       size_t num_cons_links;
+       // pp_ref is the count of pointers (usually in page table entries)
+       // to this page, for pages allocated using page_alloc.
+       // Pages allocated at boot time using pmap.c's
+       // boot_alloc do not have valid reference count fields.
+       uint16_t pp_ref;
+ };
+ extern char bootstacktop[], bootstack[];
+ extern page_t *COUNT(npage) pages;
+ extern size_t npage;
+ extern physaddr_t boot_cr3;
+ extern pde_t *COUNT(NPDENTRIES) boot_pgdir;
+ extern char* boot_freemem;
+ extern page_list_t page_free_list;
+ void* boot_alloc(uint32_t n, uint32_t align);
+ void  multiboot_detect_memory(multiboot_info_t *mbi);
+ void  multiboot_print_memory_map(multiboot_info_t *mbi);
+ bool  enable_pse(void);
+ void  vm_init(void);
+ void  page_init(void);
+ void  page_check(void);
+ int       page_alloc(page_t **pp_store);
+ int     page_alloc_specific(page_t **pp_store, size_t ppn);
+ void  page_free(page_t *pp);
+ int           page_is_free(size_t ppn);
+ int       page_insert(pde_t *COUNT(NPDENTRIES) pgdir, page_t *pp, void *SNT va, int perm);
+ void*   page_insert_in_range(pde_t *COUNT(NPDENTRIES) pgdir, page_t *pp, 
+                              void *SNT vab, void *SNT vae, int perm);
+ void  page_remove(pde_t *COUNT(NPDENTRIES) pgdir, void *SNT va);
+ page_t* page_lookup(pde_t *COUNT(NPDENTRIES) pgdir, void *va, pte_t **pte_store);
+ error_t       pagetable_remove(pde_t *COUNT(NPDENTRIES) pgdir, void *va);
+ void  page_decref(page_t *pp);
+ void setup_default_mtrrs(barrier_t* smp_barrier);
+ void  tlb_invalidate(pde_t *COUNT(NPDENTRIES) pgdir, void *va);
+ void tlb_flush_global(void);
+ void *COUNT(len)
+ user_mem_check(env_t *env, const void *DANGEROUS va, size_t len, int perm);
+ void *COUNT(len)
+ user_mem_assert(env_t *env, const void *DANGEROUS va, size_t len, int perm);
+ error_t
+ memcpy_from_user(env_t* env, void* COUNT(len) dest,
+                  const void *DANGEROUS va, size_t len);
+ static inline page_t* ppn2page(size_t ppn)
+ {
+       if( ppn >= npage )
+               warn("ppn2page called with ppn (%08u) larger than npage", ppn);
+       return &(pages[ppn]);
+ }
+ static inline ppn_t page2ppn(page_t *pp)
+ {
+       return pp - pages;
+ }
+ static inline physaddr_t page2pa(page_t *pp)
+ {
+       return page2ppn(pp) << PGSHIFT;
+ }
+ static inline page_t* pa2page(physaddr_t pa)
+ {
+       if (PPN(pa) >= npage)
+               warn("pa2page called with pa (0x%08x) larger than npage", pa);
+       return &pages[PPN(pa)];
+ }
+ static inline void*COUNT(PGSIZE) page2kva(page_t *pp)
+ {
+       return KADDR(page2pa(pp));
+ }
+ static inline void*COUNT(PGSIZE) ppn2kva(size_t pp)
+ {
+       return page2kva(ppn2page(pp));
+ }
+ static inline page_t* kva2page(void* addr) 
+ {
+       return pa2page(PADDR(addr));
+ }
+ pte_t *pgdir_walk(pde_t *COUNT(NPDENTRIES) pgdir, const void *SNT va, int create);
+ #endif /* !ROS_KERN_PMAP_H */
index 0000000,0000000..4975a15
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,36 @@@
++/*
++ * Copyright (c) 2009 The Regents of the University of California
++ * Barret Rhoden <brho@cs.berkeley.edu>
++ * See LICENSE for details.
++ *
++ * All things processes!  As we move away from the old envs to processes,
++ * we'll move things into here that are designed for multicore processes.
++ */
++
++#ifndef ROS_KERN_PROCESS_H
++#define ROS_KERN_PROCESS_H
++
++#include <arch/types.h>
++
++/* Process States.  Not 100% on the names yet. */
++#define PROC_CREATED                  0x01
++#define PROC_RUNNABLE_S                       0x02
++#define PROC_RUNNING_S                        0x04
++#define PROC_WAITING                  0x08  // can split out to INT and UINT
++#define PROC_DYING                            0x10
++#define PROC_RUNNABLE_M                       0x20 // ready, needs all of its resources (cores)
++#define PROC_RUNNING_M                        0x40 // running, manycore style
++// TODO don't use this shit for process allocation flagging
++#define ENV_FREE                              0x80
++
++#include <env.h>
++
++// Till we remove the old struct Env
++#define proc Env
++
++int proc_set_state(struct proc *p, uint32_t state) WRITES(p->state);
++struct proc *get_proc(unsigned pid);
++bool proc_controls(struct proc *actor, struct proc *target);
++void proc_startcore(struct proc *p, trapframe_t *tf) __attribute__((noreturn));
++
++#endif // !ROS_KERN_PROCESS_H
index 0000000,ed0ec3a..6c34999
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,45 +1,47 @@@
 -      "Invalid arguments"
 -      "Segmentation fault"
 -      "Bad environment"
 -      "No free environment"
 -      "Unspecified"
+ /* See COPYRIGHT for copyright information. */
+ #ifndef ROS_INC_ERROR_H
+ #define ROS_INC_ERROR_H
+ #define DECLARE_ERROR_CODE(e, s)
+ typedef enum {
+       ESUCCESS = 0,            // Success
+       EFAIL,                   // Generic Failure
+       EPERM,                   // Wrong permissions
+       EDEADLOCK,               // Would cause deadlock
+       EBUSY,                   // Currently busy, try again later
+       ENOMEM,                  // No memory available
+       EINVAL,                  // Invalid arguments
+       EFAULT,                  // Segmentation fault
+       EBADENV,                 // Bad environment 
+       ENOFREEENV,              // No free environment
+       EUNSPECIFIED,            // Unspecified
++      EMORON,                  // Moron
+       NUMERRORS,               // Total number of error codes
+ } error_t;
+ /* 
+  * The special format for printk %e takes an integer 
+  * error code and prints a string describing the error.
+  * The integer may be positive or negative,
+  * so that -ENOMEM and ENOMEM are equivalent.
+  */
+ static const char * const error_string[NUMERRORS] =
+ {
+       "Success",
+       "Generic Failure",
+       "Wrong permissions",
+       "Would cause deadlock",
+       "Currently busy, try again later",
+       "No memory available",
++      "Invalid arguments",
++      "Segmentation fault",
++      "Bad environment",
++      "No free environment",
++      "You are a moron",
++      "Unspecified",
+ };
+ #endif        // !ROS_INC_ERROR_H */
index 0000000,128ff43..b0696cd
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,55 +1,55 @@@
 -      SYS_getenvid,
 -      SYS_env_destroy,
+ #ifndef ROS_INCLUDE_SYSCALL_H
+ #define ROS_INCLUDE_SYSCALL_H
+ #include <arch/types.h>
+ #include <ros/ring_buffer.h>
+ /* system call numbers */
+ enum
+ {
+       SYS_begofcalls, //Should always be first
+       SYS_null,
+       SYS_cache_buster,
+       SYS_cache_invalidate,
+       SYS_cputs,
+       SYS_cgetc,
+       SYS_getcpuid,
+       SYS_serial_write,
+       SYS_serial_read,
++      SYS_getpid,
++      SYS_proc_destroy,
+       SYS_shared_page_alloc,
+       SYS_shared_page_free,
+       SYS_yield,
+       SYS_proc_create,
+       SYS_proc_run,
+       SYS_endofcalls //Should always be last
+ };
+ #define NSYSCALLS (SYS_endofcalls -1)
+ // syscall number starts at 1 and goes up to NSYSCALLS, without holes.
+ #define INVALID_SYSCALL(syscallno) ((syscallno) > NSYSCALLS)
+ /* For Buster Measurement Flags */
+ #define BUSTER_SHARED                 0x0001
+ #define BUSTER_STRIDED                        0x0002
+ #define BUSTER_LOCKED                 0x0004
+ #define BUSTER_PRINT_TICKS            0x0008
+ #define BUSTER_JUST_LOCKS             0x0010 // unimplemented
+ #define NUM_SYSCALL_ARGS 6
+ typedef struct syscall_req {
+       uint32_t num;
+       uint32_t flags;
+       uint32_t args[NUM_SYSCALL_ARGS];
+ } syscall_req_t;
+ typedef struct syscall_rsp {
+       int32_t retval;
+ } syscall_rsp_t;
+ // Generic Syscall Ring Buffer
+ DEFINE_RING_TYPES(syscall, syscall_req_t, syscall_rsp_t);
+ #endif /* !ROS_INCLUDE_SYSCALL_H */
index 0000000,975693b..77909b4
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,43 +1,47 @@@
 -#ifdef __BOCHS__
 -#define SMP_CALL_FUNCTION_TIMEOUT    0x00ffffff
 -#define SMP_BOOT_TIMEOUT             0x0000ffff
 -#else
 -#define SMP_CALL_FUNCTION_TIMEOUT    0x7ffffff0
 -#define SMP_BOOT_TIMEOUT             0x002fffff
 -#endif
++/*
++ * Copyright (c) 2009 The Regents of the University of California
++ * Barret Rhoden <brho@cs.berkeley.edu>
++ * See LICENSE for details.
++ */
++
+ #ifndef ROS_INC_SMP_H
+ #define ROS_INC_SMP_H
+ /* SMP related functions */
+ #include <arch/smp.h>
+ #include <arch/types.h>
+ #include <trap.h>
+ #include <atomic.h>
+ #include <workqueue.h>
 -typedef struct per_cpu_info {
 -      uint32_t lock;
 -      // Once we have a real kmalloc, we can make this dynamic.  Want a queue.
 -      work_t delayed_work;
 -      // will want it padded out to an even cacheline
 -} per_cpu_info_t;
++// be careful changing this, esp if you go over 16
++#define NUM_HANDLER_WRAPPERS          5
 -extern per_cpu_info_t per_cpu_info[MAX_NUM_CPUS];
++typedef struct HandlerWrapper {
++      checklist_t* cpu_list;
++      uint8_t vector;
++} handler_wrapper_t;
++// will want this padded out to cacheline alignment
++struct per_cpu_info {
++      uint32_t lock;
++      struct workqueue workqueue;
++};
++extern struct per_cpu_info  per_cpu_info[MAX_NUM_CPUS];
+ extern volatile uint8_t num_cpus;
+ /* SMP bootup functions */
+ void smp_boot(void);
+ void smp_idle(void);
+ /* SMP utility functions */
+ int smp_call_function_self(isr_t handler, void* data,
+                            handler_wrapper_t** wait_wrapper);
+ int smp_call_function_all(isr_t handler, void* data,
+                           handler_wrapper_t** wait_wrapper);
+ int smp_call_function_single(uint8_t dest, isr_t handler, void* data,
+                              handler_wrapper_t** wait_wrapper);
+ int smp_call_wait(handler_wrapper_t*SAFE wrapper);
+ #endif /* !ROS_INC_SMP_H */
index 0000000,3b8240b..5677a99
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,14 +1,14 @@@
 -#include <env.h>
+ #ifndef ROS_KERN_SYSCALL_H
+ #define ROS_KERN_SYSCALL_H
+ #ifndef ROS_KERNEL
+ # error "This is ROS kernel header; user programs should not #include it"
+ #endif
+ #include <ros/syscall.h>
++#include <process.h>
+ int32_t (SYNCHRONOUS syscall)(env_t* e, uint32_t num, uint32_t a1, uint32_t a2,
+                               uint32_t a3, uint32_t a4, uint32_t a5);
+ int32_t syscall_async(env_t* e, syscall_req_t *syscall);
+ int32_t process_generic_syscalls(env_t* e, uint32_t max);
+ #endif /* !ROS_KERN_SYSCALL_H */
index 0000000,c06d39c..ba7dc15
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,21 +1,34 @@@
 -#ifndef ROS_KERNEL
 -# error "This is an ROS kernel header; user programs should not #include it"
 -#endif
+ /*
+  * Copyright (c) 2009 The Regents of the University of California
++ * Barret Rhoden <brho@cs.berkeley.edu>
+  * See LICENSE for details.
++ *
++ * Workqueue: This is a todo list of func, void* that get executed whenever
++ * process_workqueue is called.  Typically, this is called from smp_idle().
++ * Note that every core will run this, so be careful with dynamic memory mgmt.
+  */
+ #ifndef ROS_KERN_WORKQUEUE_H
+ #define ROS_KERN_WORKQUEUE_H
 -// Once we have a real kmalloc, we can make this dynamic.  Want a list.
 -typedef void (*func_t)(void* data);
 -typedef struct work {
 -      void* data;
 -} work_t;
++#include <sys/queue.h>
++#include <arch/types.h>
++
++typedef void (*func_t)(void *data);
++struct work {
++      LIST_ENTRY(work) work_link;
+       func_t func;
++      void *data;
++};
++
++// TODO make these dynamic and hold more than 1.  might want better list macros.
++#define WORKQUEUE_ELEMENTS 1
++struct workqueue {
++      struct work statics[WORKQUEUE_ELEMENTS];
++};
+ void process_workqueue(void);
++// For now, the caller should free their struct work after this call
++int enqueue_work(struct workqueue *queue, struct work *job);
+ #endif /* ROS_KERN_WORKQUEUE_H */
@@@ -11,42 -11,35 +11,38 @@@ OBJDIRS += $(KERN_SRC_DIR
  #
  # We also snatch the use of a couple handy source files
  # from the lib directory, to avoid gratuitous code duplication.
- KERN_SRCFILES := $(KERN_SRC_DIR)/entry.S \
-                  $(KERN_SRC_DIR)/smp_entry.S \
+ KERN_SRCFILES := $(KERN_ARCH_SRCFILES) \
                   $(KERN_SRC_DIR)/init.c \
-                  $(KERN_SRC_DIR)/console.c \
                   $(KERN_SRC_DIR)/monitor.c \
-                  $(KERN_SRC_DIR)/pmap.c \
-                  $(KERN_SRC_DIR)/env.c \
-                  $(KERN_SRC_DIR)/kclock.c \
-                  $(KERN_SRC_DIR)/picirq.c \
                   $(KERN_SRC_DIR)/printf.c \
-                  $(KERN_SRC_DIR)/trap.c \
-                  $(KERN_SRC_DIR)/trapentry.S \
-                  $(KERN_SRC_DIR)/sched.c \
-                  $(KERN_SRC_DIR)/syscall.c \
-                  $(KERN_SRC_DIR)/kdebug.c \
-                  $(KERN_SRC_DIR)/apic.c \
-                  $(KERN_SRC_DIR)/testing.c \
-                  $(KERN_SRC_DIR)/workqueue.c \
-                  $(KERN_SRC_DIR)/manager.c \
-                  $(KERN_SRC_DIR)/atomic.c \
-                  $(KERN_SRC_DIR)/smp.c \
                   $(KERN_SRC_DIR)/printfmt.c \
+                  $(KERN_SRC_DIR)/smp.c \
+                  $(KERN_SRC_DIR)/multiboot.c \
                   $(KERN_SRC_DIR)/readline.c \
                   $(KERN_SRC_DIR)/string.c \
+                  $(KERN_SRC_DIR)/atomic.c \
+                  $(KERN_SRC_DIR)/workqueue.c \
+                  $(KERN_SRC_DIR)/pmap.c \
+                  $(KERN_SRC_DIR)/env.c \
+                  $(KERN_SRC_DIR)/manager.c \
+                  $(KERN_SRC_DIR)/syscall.c \
                   $(KERN_SRC_DIR)/timer.c \
-                  $(KERN_SRC_DIR)/kmalloc.c
 +                 $(KERN_SRC_DIR)/kfs.c \
 +                 $(KERN_SRC_DIR)/process.c \
+                  $(KERN_SRC_DIR)/kmalloc.c \
+                  $(KERN_SRC_DIR)/testing.c
  # Only build files if they exist.
  KERN_SRCFILES := $(wildcard $(KERN_SRCFILES))
  
- KERN_APPFILES :=    $(USER_APPS_PARLIB_DIR)/matrix \
+ KERN_APPFILES := \
+                     $(USER_APPS_ROSLIB_DIR)/proctests \
+                     $(USER_APPS_ROSLIB_DIR)/fptest \
                      $(USER_APPS_ROSLIB_DIR)/null \
                      $(USER_APPS_ROSLIB_DIR)/hello \
-                     $(USER_APPS_ROSLIB_DIR)/proctests \
 +                    $(USER_APPS_ROSLIB_DIR)/spawn \
+                     $(USER_APPS_PARLIB_DIR)/channel_test_client \
+                     $(USER_APPS_PARLIB_DIR)/channel_test_server \
                      $(USER_APPS_ROSLIB_DIR)/measurements
  #                    $(USER_APPS_PARLIB_DIR)/draw_nanwan
  #                    $(USER_APPS_PARLIB_DIR)/open_read \
diff --cc kern/src/env.c
@@@ -90,9 -88,10 +89,10 @@@ env_init(void
  {
        int i;
        LIST_INIT(&env_free_list);
-       for (i = NENV-1; i >= 0; i--) {
+       assert(envs != NULL);
+       for (i = NENV-1; i >= 0; i--) { TRUSTEDBLOCK // asw ivy workaround
                // these should already be set from when i memset'd the array to 0
 -              envs[i].env_status = ENV_FREE;
 +              envs[i].state = ENV_FREE;
                envs[i].env_id = 0;
                LIST_INSERT_HEAD(&env_free_list, &envs[i], env_link);
        }
@@@ -161,8 -184,7 +185,10 @@@ WRITES(e->env_pgdir, e->env_cr3, e->env
  
        // should be able to do this so long as boot_pgdir never has
        // anything put below UTOP
-       memcpy(e->env_pgdir, boot_pgdir, PGSIZE);
 +      // TODO check on this!  had a nasty bug because of it
++      // this is a bit wonky, since if it's not PGSIZE, lots of other things are
++      // screwed up...
+       memcpy(e->env_pgdir, boot_pgdir, NPDENTRIES*sizeof(pde_t));
  
        // something like this.  TODO, if you want
        //memcpy(&e->env_pgdir[PDX(UTOP)], &boot_pgdir[PDX(UTOP)], PGSIZE - PDX(UTOP));
@@@ -247,39 -279,25 +281,16 @@@ env_alloc(env_t **newenv_store, envid_
        e->env_refcnt = 1;
        e->env_flags = 0;
  
-       // Clear out all the saved register state,
-       // to prevent the register values
-       // of a prior environment inhabiting this Env structure
-       // from "leaking" into our new environment.
 -      memset(&e->env_ancillary_state,0,sizeof(e->env_ancillary_state));
 -      memset(&e->env_tf,0,sizeof(e->env_tf));
++      memset(&e->env_ancillary_state, 0, sizeof(e->env_ancillary_state));
 +      memset(&e->env_tf, 0, sizeof(e->env_tf));
-       // Set up appropriate initial values for the segment registers.
-       // GD_UD is the user data segment selector in the GDT, and
-       // GD_UT is the user text segment selector (see inc/memlayout.h).
-       // The low 2 bits of each segment register contains the
-       // Requestor Privilege Level (RPL); 3 means user mode.
-       e->env_tf.tf_ds = GD_UD | 3;
-       e->env_tf.tf_es = GD_UD | 3;
-       e->env_tf.tf_ss = GD_UD | 3;
-       e->env_tf.tf_esp = USTACKTOP;
-       e->env_tf.tf_cs = GD_UT | 3;
-       // You will set e->env_tf.tf_eip later.
-       // set the env's EFLAGSs to have interrupts enabled
-       e->env_tf.tf_eflags |= 0x00000200; // bit 9 is the interrupts-enabled
+       env_init_trapframe(e);
  
        // commit the allocation
        LIST_REMOVE(e, env_link);
        *newenv_store = e;
        atomic_inc(&num_envs);
  
--      e->env_tscfreq = system_timing.tsc_freq;
--      // TODO: for now, the only info at procinfo is this env's struct
--      // note that we need to copy this over every time we make a change to env
--      // that we want userspace to see.  also note that we don't even want to
--      // show them all of env, only specific things like PID, PPID, etc
--      memcpy(e->env_procinfo, e, sizeof(env_t));
 -
 -      env_t* curenv = curenvs[core_id()];
--
 -      printk("[%08x] new env %08x\n", curenv ? curenv->env_id : 0, e->env_id);
 +      printk("[%08x] new env %08x\n", current ? current->env_id : 0, e->env_id);
        } // INIT_STRUCT
        return 0;
  }
@@@ -335,11 -356,52 +347,15 @@@ segment_alloc(env_t *e, void *SNT va, s
  // that are marked in the program header as being mapped
  // but not actually present in the ELF file - i.e., the program's bss section.
  //
 -// All this is very similar to what our boot loader does, except the boot
 -// loader also needs to read the code from disk.  Take a look at
 -// boot/main.c to get ideas.
 -//
  // Finally, this function maps one page for the program's initial stack.
 -//
 -// load_icode panics if it encounters problems.
 -//  - How might load_icode fail?  What might be wrong with the given input?
 -//
  static void
 -load_icode(env_t *e, uint8_t *COUNT(size) binary, size_t size)
 +load_icode(env_t *SAFE e, uint8_t *COUNT(size) binary, size_t size)
  {
-       elf_t *elfhdr = (elf_t *)binary;
 -      // Hints:
 -      //  Load each program segment into virtual memory
 -      //  at the address specified in the ELF section header.
 -      //  You should only load segments with ph->p_type == ELF_PROG_LOAD.
 -      //  Each segment's virtual address can be found in ph->p_va
 -      //  and its size in memory can be found in ph->p_memsz.
 -      //  The ph->p_filesz bytes from the ELF binary, starting at
 -      //  'binary + ph->p_offset', should be copied to virtual address
 -      //  ph->p_va.  Any remaining memory bytes should be cleared to zero.
 -      //  (The ELF header should have ph->p_filesz <= ph->p_memsz.)
 -      //  Use functions from the previous lab to allocate and map pages.
 -      //
 -      //  All page protection bits should be user read/write for now.
 -      //  ELF segments are not necessarily page-aligned, but you can
 -      //  assume for this function that no two segments will touch
 -      //  the same virtual page.
 -      //
 -      //  You may find a function like segment_alloc useful.
 -      //
 -      //  Loading the segments is much simpler if you can move data
 -      //  directly into the virtual addresses stored in the ELF binary.
 -      //  So which page directory should be in force during
 -      //  this function?
 -      //
 -      // Hint:
 -      //  You must also do something with the program's entry point,
 -      //  to make sure that the environment starts executing there.
 -      //  What?  (See env_run() and env_pop_tf() below.)
 -
+       // asw: copy the headers because they might not be aligned.
+       elf_t elfhdr;
+       proghdr_t phdr;
 -      memcpy(&elfhdr,binary,sizeof(elfhdr));
++      memcpy(&elfhdr, binary, sizeof(elfhdr));
        int i, r;
  
        // is this an elf?
        lcr3(e->env_cr3);
  
        // TODO: how do we do a runtime COUNT?
--      {TRUSTEDBLOCK
-       proghdr_t* phdr = (proghdr_t*)(binary + elfhdr->e_phoff);
-       for (i = 0; i < elfhdr->e_phnum; i++, phdr++) {
-         // zra: TRUSTEDBLOCK until validation is done.
-               if (phdr->p_type != ELF_PROG_LOAD)
++      {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));
 -        // zra: TRUSTEDBLOCK until validation is done.
++              memcpy(&phdr, binary + elfhdr.e_phoff + i*sizeof(phdr), sizeof(phdr));
+               if (phdr.p_type != ELF_PROG_LOAD)
                        continue;
          // 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);
+               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);
        }}
  
-       e->env_tf.tf_eip = elfhdr->e_entry;
 -      env_set_program_counter(e,elfhdr.e_entry);
++      env_set_program_counter(e, elfhdr.e_entry);
  
        // Now map one page for the program's initial stack
        // at virtual address USTACKTOP - PGSIZE.
@@@ -416,35 -461,16 +431,13 @@@ env_free(env_t *e
        physaddr_t pa;
  
        // Note the environment's demise.
 -      env_t* curenv = curenvs[core_id()];
 -      cprintf("[%08x] free env %08x\n", curenv ? curenv->env_id : 0, e->env_id);
 +      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);
  
        // Flush all mapped pages in the user portion of the address space
-       static_assert(UTOP % PTSIZE == 0);
-       for (pdeno = 0; pdeno < PDX(UTOP); pdeno++) {
-               // only look at mapped page tables
-               if (!(e->env_pgdir[pdeno] & PTE_P))
-                       continue;
-               // find the pa and va of the page table
-               pa = PTE_ADDR(e->env_pgdir[pdeno]);
-               pt = (pte_t*COUNT(NPTENTRIES)) KADDR(pa);
-               // unmap all PTEs in this page table
-               for (pteno = 0; pteno <= PTX(~0); pteno++) {
-                       if (pt[pteno] & PTE_P)
-                               page_remove(e->env_pgdir, PGADDR(pdeno, pteno, 0));
-               }
-               // free the page table itself
-               e->env_pgdir[pdeno] = 0;
-               page_decref(pa2page(pa));
-       }
+       env_user_mem_free(e);
  
 -      // Moved to page_decref
 -      // need a known good pgdir before releasing the old one
 -      //lcr3(PADDR(boot_pgdir));
 -
        // free the page directory
        pa = e->env_cr3;
        e->env_pgdir = 0;
@@@ -544,9 -539,9 +537,9 @@@ env_destroy(env_t *e
        // for old envs that die on user cores.  since env run never returns, cores
        // never get back to their old hlt/relaxed/spin state, so we need to force
        // them back to an idle function.
-       uint32_t id = coreid();
+       uint32_t id = core_id();
 -      // There is no longer a curenv for this core. (TODO: Think about this.)
 -      curenvs[id] = NULL;
 +      // There is no longer a current process for this core. (TODO: Think about this.)
 +      current = NULL;
        if (id) {
                smp_idle();
                panic("should never see me");
@@@ -629,27 -591,45 +589,26 @@@ void schedule(void
  void
  env_run(env_t *e)
  {
 -      // Step 1: If this is a context switch (a new environment is running),
 -      //         then set 'curenv' to the new environment,
 -      //         update its 'env_runs' counter, and
 -      //         and use lcr3() to switch to its address space.
 -      // Step 2: Use env_pop_tf() to restore the environment's
 -      //         registers and drop into user mode in the
 -      //         environment.
 -
 -      // Hint: This function loads the new environment's state from
 -      //      e->env_tf.  Go back through the code you wrote above
 -      //      and make sure you have set the relevant parts of
 -      //      e->env_tf to sensible values.
 -
 -      // TODO: race here with env destroy on the status and refcnt
 +      // TODO: XME race here with env destroy on the status and refcnt
        // Could up the refcnt and down it when a process is not running
 -      e->env_status = ENV_RUNNING;
 -      if (e != curenvs[core_id()]) {
 -              curenvs[core_id()] = e;
 -              e->env_runs++;
 -              lcr3(e->env_cr3);
 -      }
 -
 -      env_pop_ancillary_state(e);
 -
 -      env_pop_tf(&e->env_tf);
 +      
 +      proc_set_state(e, PROC_RUNNING_S);
 +      proc_startcore(e, &e->env_tf);
  }
  
  /* This is the top-half of an interrupt handler, where the bottom half is
   * env_run (which never returns).  Just add it to the delayed work queue,
 - * which isn't really a queue yet.
 + * which (incidentally) can only hold one item at this point.
   */
 -void run_env_handler(trapframe_t *tf, voiddata)
 +void run_env_handler(trapframe_t *tf, void *data)
  {
        assert(data);
 -      per_cpu_info_t *cpuinfo = &per_cpu_info[core_id()];
 -      spin_lock_irqsave(&cpuinfo->lock);
 +      struct work job;
-       struct workqueue *workqueue = &per_cpu_info[coreid()].workqueue;
++      struct workqueue *workqueue = &per_cpu_info[core_id()].workqueue;
        { TRUSTEDBLOCK // TODO: how do we make this func_t cast work?
 -      cpuinfo->delayed_work.func = (func_t)env_run;
 -      cpuinfo->delayed_work.data = data;
 +      job.func = (func_t)env_run;
 +      job.data = data;
        }
 -      spin_unlock_irqsave(&cpuinfo->lock);
 +      if (enqueue_work(workqueue, &job))
 +              panic("Failed to enqueue work!");
  }
diff --cc kern/src/init.c
Simple merge
diff --cc kern/src/kfs.c
index f5091ed,0000000..819c8c4
mode 100644,000000..100644
--- /dev/null
@@@ -1,56 -1,0 +1,60 @@@
- DECL_PROG(parlib_matrix);
 +/*
 + * Copyright (c) 2009 The Regents of the University of California
 + * Barret Rhoden <brho@cs.berkeley.edu>
 + * See LICENSE for details.
 + */
 +
 +#include <kfs.h>
 +#include <string.h>
 +#include <assert.h>
 +#include <ros/error.h>
 +
 +#define DECL_PROG(x) extern uint8_t _binary_obj_user_apps_##x##_start[], _binary_obj_user_apps_##x##_size[];      
 +
 +#define KFS_ENTRY(x) {#x, _binary_obj_user_apps_##x##_start, (size_t) _binary_obj_user_apps_##x##_size},
 +
 +/*
 + * Hardcode the files included in the KFS.  This needs to be in sync with the
 + * userapps in kern/src/Makefrag.
 + * Make sure to declare it, and add an entry.  Keep MAX_KFS_FILES big enough too
 + */
- DECL_PROG(roslib_proctests);
++DECL_PROG(roslib_proctests);
++DECL_PROG(roslib_fptest);
 +DECL_PROG(roslib_null);
 +DECL_PROG(roslib_hello);
-       KFS_ENTRY(parlib_matrix)
 +DECL_PROG(roslib_spawn);
++DECL_PROG(parlib_channel_test_client);
++DECL_PROG(parlib_channel_test_server);
 +DECL_PROG(roslib_measurements);
 +
 +struct kfs_entry kfs[MAX_KFS_FILES] = {
-       KFS_ENTRY(roslib_proctests)
++      KFS_ENTRY(roslib_proctests)
++      KFS_ENTRY(roslib_fptest)
 +      KFS_ENTRY(roslib_null)
 +      KFS_ENTRY(roslib_hello)
 +      KFS_ENTRY(roslib_spawn)
++      KFS_ENTRY(parlib_channel_test_client)
++      KFS_ENTRY(parlib_channel_test_server)
 +      KFS_ENTRY(roslib_measurements)
 +};
 +
 +ssize_t kfs_lookup_path(char* path)
 +{
 +      for (int i = 0; i < MAX_KFS_FILES; i++)
 +              // need to think about how to copy-in something of unknown length
 +              if (!strncmp(kfs[i].name, path, strlen(path)))
 +                      return i;
 +      return -EINVAL;
 +}
 +
 +/*
 + * 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
 + */
 +struct proc *kfs_proc_create(size_t kfs_inode)
 +{
 +      if (kfs_inode < 0 || kfs_inode >= MAX_KFS_FILES)
 +              panic("Invalid kfs_inode.  Check you error codes!");
 +      return env_create(kfs[kfs_inode].start, kfs[kfs_inode].size);
 +}
@@@ -41,28 -38,24 +40,37 @@@ void manager(void
                        envs[0] = ENV_CREATE(roslib_proctests);
                        envs[1] = ENV_CREATE(roslib_proctests);
                        envs[2] = ENV_CREATE(roslib_proctests);
-                       envs[3] = ENV_CREATE(roslib_proctests);
+                       envs[3] = ENV_CREATE(roslib_fptest);
+                       envs[4] = ENV_CREATE(roslib_fptest);
+                       envs[4] = ENV_CREATE(roslib_fptest);
+                       envs[5] = ENV_CREATE(roslib_hello);
+                       envs[6] = ENV_CREATE(roslib_null);
+                       //envs[6] = ENV_CREATE(roslib_measurements);
                        env_run(envs[0]);
                        break;
 -              case 1:
 +                      #if 0
 +                      #endif
                case 2:
 +                      #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);
 +                              smp_call_function_single(i, run_env_handler, envs[i], 0);
 +                      }
 +                      process_workqueue();
 +                      #endif
                case 3:
 -              #endif
                #if 0
                case 0:
                        printk("Beginning Tests\n");
                        test_run_measurements(progress-1);  // should never return
                        break;
                case 1:
++                      envs[0] = ENV_CREATE(parlib_channel_test_client);
++                      envs[1] = ENV_CREATE(parlib_channel_test_server);
++                      smp_call_function_single(1, run_env_handler, envs[0], 0);
++                      smp_call_function_single(2, run_env_handler, envs[1], 0);
                case 2:
                case 3:
                case 4:
@@@ -19,7 -18,7 +18,8 @@@
  #include <pmap.h>
  #include <kdebug.h>
  #include <testing.h>
 +#include <kfs.h>
+ #include <manager.h>
  
  #include <ros/memlayout.h>
  
@@@ -41,8 -40,7 +41,9 @@@ static command_t commands[] = 
        { "setmapperm", "Sets permissions on a VA->PA mapping", mon_setmapperm},
        { "cpuinfo", "Prints CPU diagnostics", mon_cpuinfo},
        { "nanwan", "Meet Nanwan!!", mon_nanwan},
 +      { "kfs_ls", "List files in KFS", mon_kfs_ls},
 +      { "kfs_run", "Create and run a program from KFS", mon_kfs_run},
+       { "manager", "Run the manager", mon_manager},
  };
  #define NCOMMANDS (sizeof(commands)/sizeof(commands[0]))
  
@@@ -337,8 -275,8 +308,8 @@@ static int runcmd(char *COUNT(CMDBUF_SI
  void monitor(trapframe_t *tf) {
        char *buf;
  
-       printk("Welcome to the ROS kernel monitor on core %d!\n", coreid());
 -      cprintf("Welcome to the ROS kernel monitor!\n");
 -      cprintf("Type 'help' for a list of commands.\n");
++      printk("Welcome to the ROS kernel monitor on core %d!\n", core_id());
 +      printk("Type 'help' for a list of commands.\n");
  
        if (tf != NULL)
                print_trapframe(tf);
diff --cc kern/src/pmap.c
  #include <assert.h>
  #include <pmap.h>
  #include <kclock.h>
 -#include <env.h>
 +#include <process.h>
  
- // These variables are set by i386_detect_memory()
- static physaddr_t maxpa;      // Maximum physical address
- static physaddr_t maxaddrpa;  // Maximum directly addressable physical address
- size_t npage;                 // Amount of physical memory (in pages)
- size_t naddrpage;                     // Amount of addressable physical memory (in pages)
- static size_t basemem;                // Amount of base memory (in bytes)
- static size_t extmem;         // Amount of extended memory (in bytes)
- // These variables are set in i386_vm_init()
- pde_t* boot_pgdir;            // Virtual address of boot time page directory
- physaddr_t boot_cr3;          // Physical address of boot time page directory
- static char* boot_freemem;    // Pointer to next byte of free mem
- page_t *pages;                // Virtual address of physical page array
- static page_list_t page_free_list;    // Free list of physical pages
- extern env_t *envs;
- // Global descriptor table.
- //
- // The kernel and user segments are identical (except for the DPL).
- // To load the SS register, the CPL must equal the DPL.  Thus,
- // we must duplicate the segments for the user and the kernel.
- //
- segdesc_t gdt[] =
- {
-       // 0x0 - unused (always faults -- for trapping NULL far pointers)
-       SEG_NULL,
-       // 0x8 - kernel code segment
-       [GD_KT >> 3] = SEG(STA_X | STA_R, 0x0, 0xffffffff, 0),
-       // 0x10 - kernel data segment
-       [GD_KD >> 3] = SEG(STA_W, 0x0, 0xffffffff, 0),
-       // 0x18 - user code segment
-       [GD_UT >> 3] = SEG(STA_X | STA_R, 0x0, 0xffffffff, 3),
-       // 0x20 - user data segment
-       [GD_UD >> 3] = SEG(STA_W, 0x0, 0xffffffff, 3),
-       // 0x28 - tss, initialized in idt_init()
-       [GD_TSS >> 3] = SEG_NULL
- };
- pseudodesc_t gdt_pd = {
-       sizeof(gdt) - 1, (unsigned long) gdt
- };
- static int
- nvram_read(int r)
- {
-       return mc146818_read(r) | (mc146818_read(r + 1) << 8);
- }
- void i386_print_memory_map(multiboot_info_t *mbi) {
-       const char* memory_type[] = {"", "FREE", "RESERVED", "UNDEFINED", "UNDEFINED4"};
-       if(CHECK_FLAG(mbi->flags, 6)) {
-               cprintf ("mmap_addr = 0x%x, mmap_length = 0x%x\n", (unsigned long)mbi->mmap_addr,
-                                                                  (unsigned long)mbi->mmap_length);
-               
-               memory_map_t* mmap = (memory_map_t*) ((uint32_t)mbi->mmap_addr + KERNBASE);
-               while((uint32_t)mmap < ((uint32_t)mbi->mmap_addr + KERNBASE) + mbi->mmap_length) {                      
-                       cprintf ("base = 0x%08x%08x, length = 0x%08x%08x, type = %s\n",
-                               (unsigned) mmap->base_addr_high,
-                               (unsigned) mmap->base_addr_low,
-                               (unsigned) mmap->length_high,
-                               (unsigned) mmap->length_low,
-                               (unsigned) memory_type[mmap->type]);
-                       mmap = (memory_map_t*) ((uint32_t) mmap + mmap->size + sizeof (mmap->size));
-               }
-       }
- }
- void i386_detect_memory(multiboot_info_t *mbi)
- {
-       // Tells us how many kilobytes there are
-       basemem = ROUNDDOWN(mbi->mem_lower*1024, PGSIZE);
-       extmem = ROUNDDOWN(mbi->mem_upper*1024, PGSIZE);
-       // Calculate the maximum physical address based on whether
-       // or not there is any extended memory.  See comment in <inc/memlayout.h>
-       if (extmem)
-               maxpa = EXTPHYSMEM + extmem;
-       else
-               maxpa = basemem;
-       npage = maxpa / PGSIZE;
-       // IOAPIC - KERNBASE is the max amount of virtual addresses we can use
-       // for the physical memory mapping (aka - the KERNBASE mapping)
-       maxaddrpa = MIN(maxpa, IOAPIC_BASE - KERNBASE);
-       naddrpage = maxaddrpa / PGSIZE;
-       cprintf("Physical memory: %dK available, ", (int)(maxpa/1024));
-       cprintf("base = %dK, extended = %dK\n", (int)(basemem/1024), (int)(extmem/1024));
-       printk("Maximum directly addressable physical memory: %dK\n", (int)(maxaddrpa/1024));
- }
- bool enable_pse(void)
- {
-       uint32_t edx, cr4;
-       cpuid(1, 0, 0, 0, &edx);
-       if (edx & CPUID_PSE_SUPPORT) {
-               cr4 = rcr4();
-               cr4 |= CR4_PSE;
-               lcr4(cr4);
-               return 1;
-       } else
-               return 0;
- }
- // --------------------------------------------------------------
- // Set up initial memory mappings and turn on MMU.
- // --------------------------------------------------------------
- static void check_boot_pgdir(bool pse);
  //
  // Allocate n bytes of physical memory aligned on an 
  // align-byte boundary.  Align must be a power of two.
index af90949,0000000..5f9686a
mode 100644,000000..100644
--- /dev/null
@@@ -1,148 -1,0 +1,143 @@@
-       /* If the process entered the kernel via sysenter, we need to leave via
-        * sysexit.  sysenter trapframes have 0 for a CS, which is pushed in
-        * sysenter_handler.
-        */
-       if (tf->tf_cs)
-               env_pop_tf(tf);
-       else
-               env_pop_tf_sysexit(tf);
 +/*
 + * Copyright (c) 2009 The Regents of the University of California
 + * Barret Rhoden <brho@cs.berkeley.edu>
 + * See LICENSE for details.
 + */
 +
 +#include <process.h>
 +#include <atomic.h>
 +#include <assert.h>
 +
 +/*
 + * 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)
 +{
 +      uint32_t curstate = p->state;
 +      /* Valid transitions:
 +       * C   -> RBS
 +       * RBS -> RGS
 +       * RGS -> RBS
 +       * RGS -> W
 +       * W   -> RBS
 +       * RGS -> RBM
 +       * RBM -> RGM
 +       * RGM -> RBM
 +       * RGM -> RBS
 +       * RGS -> D
 +       * RGM -> D
 +       * 
 +       * These ought to be implemented later (allowed, not thought through yet).
 +       * RBS -> D
 +       * RBM -> D
 +       *
 +       * This isn't allowed yet, may be later.
 +       * C   -> D
 +       */
 +      #if 1 // some sort of correctness flag
 +      switch (curstate) {
 +              case PROC_CREATED:
 +                      if (state != PROC_RUNNABLE_S)
 +                              panic("Invalid State Transition! PROC_CREATED to %d", state);
 +                      break;
 +              case PROC_RUNNABLE_S:
 +                      if (!(state & (PROC_RUNNING_S | PROC_DYING)))
 +                              panic("Invalid State Transition! PROC_RUNNABLE_S to %d", state);
 +                      break;
 +              case PROC_RUNNING_S:
 +                      if (!(state & (PROC_RUNNABLE_S | PROC_RUNNABLE_M | PROC_WAITING |
 +                                     PROC_DYING)))
 +                              panic("Invalid State Transition! PROC_RUNNING_S to %d", state);
 +                      break;
 +              case PROC_WAITING:
 +                      if (state != PROC_RUNNABLE_S)
 +                              panic("Invalid State Transition! PROC_WAITING to %d", state);
 +                      break;
 +              case PROC_DYING:
 +                      if (state != PROC_CREATED) // when it is reused (TODO)
 +                              panic("Invalid State Transition! PROC_DYING to %d", state);
 +                      break;
 +              case PROC_RUNNABLE_M:
 +                      if (!(state & (PROC_RUNNING_M | PROC_DYING)))
 +                              panic("Invalid State Transition! PROC_RUNNABLE_M to %d", state);
 +                      break;
 +              case PROC_RUNNING_M:
 +                      if (!(state & (PROC_RUNNABLE_S | PROC_RUNNABLE_M | PROC_DYING)))
 +                              panic("Invalid State Transition! PROC_RUNNING_M to %d", state);
 +                      break;
 +      }
 +      #endif
 +      p->state = state;
 +      return 0;
 +}
 +
 +/* Change this when we aren't using an array */
 +struct proc *get_proc(unsigned pid)
 +{
 +      // should have some error checking when we do this for real
 +      return &envs[ENVX(pid)];
 +}
 +
 +/* Whether or not actor can control target */
 +bool proc_controls(struct proc *actor, struct proc *target)
 +{
 +      return target->env_parent_id == actor->env_id;
 +}
 +
 +/*
 + * 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
 + *
 + * TODO: think about how an interrupt could abort this, esp when we want to
 + * destroy it.  need a way to not lose the kernel stack.  For example, we could
 + * receive an IPI that tells us to preempt this process (or even kill it) and
 + * run something different.
 + * TODO: in general, think about when we no longer need the stack, in case we
 + * are preempted and expected to run again from somewhere else.  we can't
 + * expect to have the kernel stack around anymore.
 + *
 + * I think we need to make it such that the kernel in "process context"
 + * never gets removed from the core (displaced from its stack)
 + * would like to leave interrupts on too, so long as we come back.
 + * Consider a moveable flag or something.
 + *
 + * Perhaps we could have a workqueue with the todo item put there by the
 + * interrupt handler when it realizes we were in the kernel in the first place.
 + * disable ints before checking the queue and deciding to pop out or whatever to
 + * ensure atomicity.
 + */
 +void proc_startcore(struct proc *p, trapframe_t *tf) {
 +      /*
 +       * TODO: okay, we have this.  now handle scenarios based on these
 +       * assumptions (transitions from these states) like:
 +       *              death attempt
 +       *              preempt attempt
 +       */
 +      assert(p->state & (PROC_RUNNING_S | PROC_RUNNING_M));
 +      /* If the process wasn't here, then we need to load its address space. */
 +      if (p != current) {
 +              if (env_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)
 +                      panic("Proc is dying, handle me!"); 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".
 +              if (current)
 +                      env_decref(current);
 +              current = p;
 +              /* also need to load our silly state, though this implies it's the same
 +               * context, and not just the same process
 +               * TODO: this is probably a lie, think about page faults
++               * for now, we load this silly state down below
 +               */
 +              // load_our_silly_state();
 +      }
++      env_pop_ancillary_state(p);
++      env_pop_tf(&p->env_tf);
 +}
diff --cc kern/src/smp.c
  #pragma nodeputy
  #endif
  
- #include <arch/x86.h>
- #include <arch/smp.h>
- #include <arch/console.h>
- #include <arch/apic.h>
+ #include <arch/arch.h>
  #include <atomic.h>
- volatile uint8_t num_cpus = 0xee;
- uintptr_t smp_stack_top;
+ #include <smp.h>
 +#include <ros/error.h>
 +#include <stdio.h>
 +#include <string.h>
 +#include <assert.h>
 +#include <pmap.h>
 +#include <process.h>
 +#include <trap.h>
 +
 +struct per_cpu_info per_cpu_info[MAX_NUM_CPUS];
  
- /*************************** IPI Wrapper Stuff ********************************/
- // checklists to protect the global interrupt_handlers for 0xf0, f1, f2, f3, f4
- // need to be global, since there is no function that will always exist for them
- handler_wrapper_t             handler_wrappers[NUM_HANDLER_WRAPPERS];
  // tracks number of global waits on smp_calls, must be <= NUM_HANDLER_WRAPPERS
- uint32_t outstanding_calls = 0; 
- #define DECLARE_HANDLER_CHECKLISTS(vector)                          \
-       INIT_CHECKLIST(f##vector##_cpu_list, MAX_NUM_CPUS);
- #define INIT_HANDLER_WRAPPER(v)                                     \
- {                                                                   \
-       handler_wrappers[(v)].vector = 0xf##v;                          \
-       handler_wrappers[(v)].cpu_list = &f##v##_cpu_list;              \
-       handler_wrappers[(v)].cpu_list->mask.size = num_cpus;           \
- }
- DECLARE_HANDLER_CHECKLISTS(0);
- DECLARE_HANDLER_CHECKLISTS(1);
- DECLARE_HANDLER_CHECKLISTS(2);
- DECLARE_HANDLER_CHECKLISTS(3);
- DECLARE_HANDLER_CHECKLISTS(4);
- static void init_smp_call_function(void)
- {
-       INIT_HANDLER_WRAPPER(0);
-       INIT_HANDLER_WRAPPER(1);
-       INIT_HANDLER_WRAPPER(2);
-       INIT_HANDLER_WRAPPER(3);
-       INIT_HANDLER_WRAPPER(4);
- }
- /******************************************************************************/
- static void smp_mtrr_handler(trapframe_t *tf, void* data)
- {
-       setup_default_mtrrs((barrier_t*)data);
- }
- void smp_boot(void)
- {
-       // this needs to be set in smp_entry too...
-       #define trampoline_pg 0x00001000
-       page_t *smp_stack;
-       // NEED TO GRAB A LOWMEM FREE PAGE FOR AP BOOTUP CODE
-       // page1 (2nd page) is reserved, hardcoded in pmap.c
-       extern smp_entry(), smp_entry_end(), smp_boot_lock(), smp_semaphore();
-       memset(KADDR(trampoline_pg), 0, PGSIZE);
-       memcpy(KADDR(trampoline_pg), &smp_entry, &smp_entry_end - &smp_entry);
-       // This mapping allows access to the trampoline with paging on and off
-       // via trampoline_pg
-       page_insert(boot_pgdir, pa2page(trampoline_pg), (void*)trampoline_pg, PTE_W);
-       // Allocate a stack for the cores starting up.  One for all, must share
-       if (page_alloc(&smp_stack))
-               panic("No memory for SMP boot stack!");
-       smp_stack->pp_ref++;
-       smp_stack_top = (uintptr_t)(page2kva(smp_stack) + PGSIZE);
-       // Start the IPI process (INIT, wait, SIPI, wait, SIPI, wait)
-       send_init_ipi();
-       // SDM 3A is a little wonky wrt the proper delays.  These are my best guess.
-       udelay(10000);
-       // first SIPI
-       send_startup_ipi(0x01);
-       /* BOCHS does not like this second SIPI.
-       // second SIPI
-       udelay(200);
-       send_startup_ipi(0x01);
-       */
-       udelay(100000);
-       // Each core will also increment smp_semaphore, and decrement when it is done,
-       // all in smp_entry.  It's purpose is to keep Core0 from competing for the
-       // smp_boot_lock.  So long as one AP increments the sem before the final
-       // LAPIC timer goes off, all available cores will be initialized.
-       while(*(volatile uint32_t*)(&smp_semaphore - &smp_entry + trampoline_pg));
-       // From here on, no other cores are coming up.  Grab the lock to ensure it.
-       // Another core could be in it's prelock phase and be trying to grab the lock
-       // forever....
-       // The lock exists on the trampoline, so it can be grabbed right away in
-       // real mode.  If core0 wins the race and blocks other CPUs from coming up
-       // it can crash the machine if the other cores are allowed to proceed with
-       // 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((uint32_t*)(&smp_boot_lock - &smp_entry + trampoline_pg));
-       cprintf("Num_Cpus Detected: %d\n", num_cpus);
-       // Remove the mapping of the page used by the trampoline
-       page_remove(boot_pgdir, (void*)trampoline_pg);
-       // It had a refcount of 2 earlier, so we need to dec once more to free it
-       // but only if all cores are in (or we reset / reinit those that failed)
-       // TODO after we parse ACPI tables
-       if (num_cpus == 8) // TODO - ghetto coded for our 8 way SMPs
-               page_decref(pa2page(trampoline_pg));
-       // Remove the page table used for that mapping
-       pagetable_remove(boot_pgdir, (void*)trampoline_pg);
-       // Dealloc the temp shared stack
-       page_decref(smp_stack);
-       // Set up the generic remote function call facility
-       init_smp_call_function();
-       // Set up all cores to use the proper MTRRs
-       barrier_t generic_barrier;
-       init_barrier(&generic_barrier, num_cpus); // barrier used by smp_mtrr_handler
-       smp_call_function_all(smp_mtrr_handler, &generic_barrier, 0);
-       // Should probably flush everyone's TLB at this point, to get rid of
-       // temp mappings that were removed.  TODO
- }
- /*
-  * This is called from smp_entry by each core to finish the core bootstrapping.
-  * There is a spinlock around this entire function in smp_entry, for a few reasons,
-  * the most important being that all cores use the same stack when entering here.
-  */
- uint32_t smp_main(void)
- {
-       /*
-       // Print some diagnostics.  Uncomment if there're issues.
-       cprintf("Good morning Vietnam!\n");
-       cprintf("This core's Default APIC ID: 0x%08x\n", lapic_get_default_id());
-       cprintf("This core's Current APIC ID: 0x%08x\n", lapic_get_id());
-       if (read_msr(IA32_APIC_BASE) & 0x00000100)
-               cprintf("I am the Boot Strap Processor\n");
-       else
-               cprintf("I am an Application Processor\n");
-       cprintf("Num_Cpus: %d\n\n", num_cpus);
-       */
-       // Get a per-core kernel stack
-       page_t *my_stack;
-       if (page_alloc(&my_stack))
-               panic("Unable to alloc a per-core stack!");
-       my_stack->pp_ref++;
-       memset(page2kva(my_stack), 0, PGSIZE);
-       // Set up a gdt / gdt_pd for this core, stored at the top of the stack
-       // This is necessary, eagle-eyed readers know why
-       // GDT should be 4-byte aligned.  TS isn't aligned.  Not sure if it matters.
-       pseudodesc_t *my_gdt_pd = page2kva(my_stack) + PGSIZE -
-               sizeof(pseudodesc_t) - sizeof(segdesc_t)*SEG_COUNT;
-       segdesc_t *my_gdt = page2kva(my_stack) + PGSIZE -
-               sizeof(segdesc_t)*SEG_COUNT;
-       // TS also needs to be permanent
-       taskstate_t *my_ts = page2kva(my_stack) + PGSIZE -
-               sizeof(pseudodesc_t) - sizeof(segdesc_t)*SEG_COUNT -
-               sizeof(taskstate_t);
-       // Usable portion of the KSTACK grows down from here
-       // Won't actually start using this stack til our first interrupt
-       // (issues with changing the stack pointer and then trying to "return")
-       uintptr_t my_stack_top = (uintptr_t)my_ts;
-       
-       // Set up MSR for SYSENTER 
-       write_msr(MSR_IA32_SYSENTER_CS, GD_KT);
-       write_msr(MSR_IA32_SYSENTER_ESP, my_stack_top);
-       write_msr(MSR_IA32_SYSENTER_EIP, (uint32_t) &sysenter_handler);
-       // Build and load the gdt / gdt_pd
-       memcpy(my_gdt, gdt, sizeof(segdesc_t)*SEG_COUNT);
-       *my_gdt_pd = (pseudodesc_t) {
-               sizeof(segdesc_t)*SEG_COUNT - 1, (uintptr_t) my_gdt };
-       asm volatile("lgdt %0" : : "m"(*my_gdt_pd));
-       // Need to set the TSS so we know where to trap on this core
-       my_ts->ts_esp0 = my_stack_top;
-       my_ts->ts_ss0 = GD_KD;
-       // Initialize the TSS field of my_gdt.
-       my_gdt[GD_TSS >> 3] = SEG16(STS_T32A, (uint32_t) (my_ts), sizeof(taskstate_t), 0);
-       my_gdt[GD_TSS >> 3].sd_s = 0;
-       // Load the TSS
-       ltr(GD_TSS);
-       // Loads the same IDT used by the other cores
-       asm volatile("lidt idt_pd");
-       // APIC setup
-       // set LINT0 to receive ExtINTs (KVM's default).  At reset they are 0x1000.
-       write_mmreg32(LAPIC_LVT_LINT0, 0x700);
-       // mask it to shut it up for now.  Doesn't seem to matter yet, since both
-       // KVM and Bochs seem to only route the PIC to core0.
-       mask_lapic_lvt(LAPIC_LVT_LINT0);
-       // and then turn it on
-       lapic_enable();
-       // set a default logical id for now
-       lapic_set_logid(coreid());
-       return my_stack_top; // will be loaded in smp_entry.S
- }
+ atomic_t outstanding_calls = 0;
  
 -per_cpu_info_t per_cpu_info[MAX_NUM_CPUS];
 -
  /* All non-zero cores call this at the end of their boot process.  They halt,
   * and wake up when interrupted, do any work on their work queue, then halt
   * when there is nothing to do.  
@@@ -234,152 -24,4 +37,3 @@@ void smp_idle(void
                cpu_halt();
        }
  }
- static int smp_call_function(uint8_t type, uint8_t dest, isr_t handler, void* data,
-                               handler_wrapper_t** wait_wrapper)
- {
-       extern handler_t interrupt_handlers[];
-       int8_t state = 0;
-       uint32_t wrapper_num;
-       handler_wrapper_t* wrapper;
-       // prevents us from ever having more than NUM_HANDLER_WRAPPERS callers in
-       // the process of competing for vectors.  not decremented until both after
-       // the while(1) loop and after it's been waited on.
-       atomic_inc(&outstanding_calls);
-       if (outstanding_calls > NUM_HANDLER_WRAPPERS) {
-               atomic_dec(&outstanding_calls);
-               return -EBUSY;
-       }
-       
-       // assumes our cores are numbered in order
-       if ((type == 4) && (dest >= num_cpus))
-               panic("Destination CPU does not exist!");
-       // build the mask based on the type and destination
-       INIT_CHECKLIST_MASK(cpu_mask, MAX_NUM_CPUS);
-       // set checklist mask's size dynamically to the num cpus actually present
-       cpu_mask.size = num_cpus;
-       switch (type) {
-               case 1: // self
-                       SET_BITMASK_BIT(cpu_mask.bits, coreid());
-                       break;
-               case 2: // all
-                       FILL_BITMASK(cpu_mask.bits, num_cpus);
-                       break;
-               case 3: // all but self
-                       FILL_BITMASK(cpu_mask.bits, num_cpus);
-                       CLR_BITMASK_BIT(cpu_mask.bits, coreid());
-                       break;
-               case 4: // physical mode
-                       // note this only supports sending to one specific physical id
-                       // (only sets one bit, so if multiple cores have the same phys id
-                       // the first one through will set this).
-                       SET_BITMASK_BIT(cpu_mask.bits, dest);
-                       break;
-               case 5: // logical mode
-                       // TODO
-                       warn("Logical mode bitmask handler protection not implemented!");
-                       break;
-               default:
-                       panic("Invalid type for cross-core function call!");
-       }
-       // Find an available vector/wrapper.  Starts with this core's id (mod the
-       // number of wrappers).  Walk through on conflict.
-       // Commit returns an error if it wanted to give up for some reason,
-       // like taking too long to acquire the lock or clear the mask, at which
-       // point, we try the next one.
-       // When we are done, wrapper points to the one we finally got.
-       // this wrapper_num trick doesn't work as well if you send a bunch in a row
-       // and wait, since you always check your main one (which is currently busy).
-       wrapper_num = coreid() % NUM_HANDLER_WRAPPERS;
-       while(1) {
-               wrapper = &handler_wrappers[wrapper_num];
-               if (!commit_checklist_wait(wrapper->cpu_list, &cpu_mask))
-                       break;
-               wrapper_num = (wrapper_num + 1) % NUM_HANDLER_WRAPPERS;
-               /*
-               uint32_t count = 0;
-               // instead of deadlock, smp_call can fail with this.  makes it harder
-               // to use (have to check your return value).  consider putting a delay
-               // here too (like if wrapper_num == initial_wrapper_num)
-               if (count++ > NUM_HANDLER_WRAPPERS * 1000) // note 1000 isn't enough...
-                       return -EBUSY;
-               */
-       }
-       // Wanting to wait is expressed by having a non-NULL handler_wrapper_t**
-       // passed in.  Pass out our reference to wrapper, to wait later.
-       // If we don't want to wait, release the checklist (though it is still not
-       // clear, so it can't be used til everyone checks in).
-       if (wait_wrapper)
-               *wait_wrapper = wrapper;
-       else {
-               release_checklist(wrapper->cpu_list);
-               atomic_dec(&outstanding_calls);
-       }
-       // now register our handler to run
-       register_interrupt_handler(interrupt_handlers, wrapper->vector, handler, data);
-       // WRITE MEMORY BARRIER HERE
-       enable_irqsave(&state);
-       // Send the proper type of IPI.  I made up these numbers.
-       switch (type) {
-               case 1:
-                       send_self_ipi(wrapper->vector);
-                       break;
-               case 2:
-                       send_broadcast_ipi(wrapper->vector);
-                       break;
-               case 3:
-                       send_all_others_ipi(wrapper->vector);
-                       break;
-               case 4: // physical mode
-                       send_ipi(dest, 0, wrapper->vector);
-                       break;
-               case 5: // logical mode
-                       send_ipi(dest, 1, wrapper->vector);
-                       break;
-               default:
-                       panic("Invalid type for cross-core function call!");
-       }
-       // wait long enough to receive our own broadcast (PROBABLY WORKS) TODO
-       lapic_wait_to_send();
-       disable_irqsave(&state);
-       return 0;
- }
- // Wrapper functions.  Add more as they are needed.
- int smp_call_function_self(isr_t handler, void* data,
-                            handler_wrapper_t** wait_wrapper)
- {
-       return smp_call_function(1, 0, handler, data, wait_wrapper);
- }
- int smp_call_function_all(isr_t handler, void* data,
-                           handler_wrapper_t** wait_wrapper)
- {
-       return smp_call_function(2, 0, handler, data, wait_wrapper);
- }
- int smp_call_function_single(uint8_t dest, isr_t handler, void* data,
-                              handler_wrapper_t** wait_wrapper)
- {
-       return smp_call_function(4, dest, handler, data, wait_wrapper);
- }
- // If you want to wait, pass the address of a pointer up above, then call
- // this to do the actual waiting.  Be somewhat careful about uninitialized 
- // or old wrapper pointers.
- int smp_call_wait(handler_wrapper_t* wrapper)
- {
-       if (wrapper) {
-               waiton_checklist(wrapper->cpu_list);
-               return 0;
-       } else {
-               warn("Attempting to wait on null wrapper!  Check your return values!");
-               return -EFAIL;
-       }
- }
--
  #include <pmap.h>
  #include <trap.h>
  #include <syscall.h>
 +#include <kfs.h> // eventually replace this with vfs.h
  
- /* This is called from sysenter's asm, with the tf on the kernel stack. */
- void sysenter_callwrapper(struct Trapframe *tf)
- {
-       current->env_tf = *tf;
-       
-       // The trapframe on the stack should be ignored from here on.
-       tf = &current->env_tf;
-       tf->tf_regs.reg_eax = (intreg_t) syscall(current,
-                                                tf->tf_regs.reg_eax,
-                                                tf->tf_regs.reg_edx,
-                                                tf->tf_regs.reg_ecx,
-                                                tf->tf_regs.reg_ebx,
-                                                tf->tf_regs.reg_edi,
-                                                0);
-       /*
-        * careful here - we need to make sure that this current is the right
-        * process, which could be weird if the syscall blocked.  it would need to
-        * restore the proper value in current before returning to here.
-        * likewise, tf could be pointing to random gibberish.
-        */
-       proc_startcore(current, tf);
- }
  //Do absolutely nothing.  Used for profiling.
  static void sys_null(void)
  {
@@@ -78,10 -53,47 +54,45 @@@ static ssize_t sys_serial_read(env_t* e
        #endif
  }
  
- // Invalidate the cache of this core
+ static ssize_t sys_shared_page_alloc(env_t* p1, 
+                                      void** addr, envid_t p2_id, 
+                                      int p1_flags, int p2_flags
+                                     ) 
+ {
+       if (!VALID_USER_PERMS(p1_flags)) return -EPERM;
+       if (!VALID_USER_PERMS(p2_flags)) return -EPERM;
+       page_t* page;
+       env_t* p2 = &(envs[ENVX(p2_id)]);
+       error_t e = page_alloc(&page);
+       if(e < 0) return e;
+       
+       void* p2_addr = page_insert_in_range(p2->env_pgdir, page, 
+                                            (void*)UTEXT, (void*)UTOP, p2_flags);
+       if(p2_addr == NULL) 
+               return -EFAIL;
+               
+       void* p1_addr = page_insert_in_range(p1->env_pgdir, page, 
+                                           (void*)UTEXT, (void*)UTOP, p1_flags);
+       if(p1_addr == NULL) {
+               page_remove(p2->env_pgdir, p2_addr);
+               return -EFAIL;
+       }
+       *addr = p1_addr;
+       return ESUCCESS;
+ }
+ static void sys_shared_page_free(env_t* p1, void* addr, envid_t p2)
+ {
+ }
 -// Invalidate the cache of this core
++// Invalidate the cache of this core.  Only useful if you want a cold cache for
++// performance testing reasons.
  static void sys_cache_invalidate(void)
  {
-       wbinvd();
 -      // why is this necessary with cache coherence?
 -      // is it for coherence with respect to i/o?  --asw
 -
+       #ifdef __i386__
+               wbinvd();
+       #endif
        return;
  }
  
@@@ -195,10 -207,9 +206,10 @@@ static envid_t sys_getenvid(env_t* e
  // Returns the id of the cpu this syscall is executed on.
  static envid_t sys_getcpuid(void)
  {
-       return coreid();
+       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:
@@@ -331,11 -292,10 +348,11 @@@ intreg_t syscall(env_t* e, uint32_t sys
                        return sys_env_destroy(e, (envid_t)a1);
                case SYS_yield:
                        sys_yield(e);
-                       return 0;
+                       return ESUCCESS;
                case SYS_proc_create:
 +                      return sys_proc_create(e, (char *DANGEROUS)a1);
                case SYS_proc_run:
 -                      panic("Not implemented");
 +                      return sys_proc_run(e, (size_t)a1);
                default:
                        // or just return -EINVAL
                        panic("Invalid syscall number %d for env %x!", syscallno, *e);
@@@ -20,6 -19,8 +19,8 @@@
  
  #define test_vector 0xeb
  
 -#if 0
++#ifdef __i386__
  void test_ipi_sending(void)
  {
        extern handler_t interrupt_handlers[];
@@@ -225,7 -228,9 +228,8 @@@ checklist_t* the_global_list
  
  void test_checklist_handler(trapframe_t *tf, void* data)
  {
 -      for (int i = 0; i < SMP_BOOT_TIMEOUT; i++);
 -      for (int i = 0; i < SMP_BOOT_TIMEOUT; i++);
 -      cprintf("down_checklist(%x,%d)\n",the_global_list,core_id());
 +      udelay(1000000);
++      cprintf("down_checklist(%x,%d)\n", the_global_list, core_id());
        down_checklist(the_global_list);
  }
  
@@@ -388,6 -385,7 +384,7 @@@ void test_smp_call_functions(void
        printk("Done\n");
  }
  
 -#if 0
++#ifdef __i386__
  void test_lapic_status_bit(void)
  {
        register_interrupt_handler(interrupt_handlers, test_vector,
@@@ -540,8 -539,17 +538,17 @@@ void test_run_measurements(uint32_t job
  
  void test_hello_world_handler(trapframe_t *tf, void* data)
  {
+       int trapno;
+       #if defined(__i386__)
+       trapno = tf->tf_trapno;
+       #elif defined(__sparc_v8__)
+       trapno = (tf->tbr >> 4) & 0xFF;
+       #else
+       trapno = 0;
+       #endif
        cprintf("Incoming IRQ, ISR: %d on core %d with tf at 0x%08x\n",
-               tf->tf_trapno, coreid(), tf);
 -              trapno, core_id(), tf);
++              trapno, core_id(), tf);
  }
  
  uint32_t print_info_lock = 0;
@@@ -550,7 -558,8 +557,8 @@@ void test_print_info_handler(trapframe_
  {
        spin_lock_irqsave(&print_info_lock);
        cprintf("----------------------------\n");
-       cprintf("This is Core %d\n", coreid());
+       cprintf("This is Core %d\n", core_id());
 -#if 0
++#ifdef __i386__
        cprintf("MTRR_DEF_TYPE = 0x%08x\n", read_msr(IA32_MTRR_DEF_TYPE));
        cprintf("MTRR Phys0 Base = 0x%016llx, Mask = 0x%016llx\n",
                read_msr(0x200), read_msr(0x201));
@@@ -590,9 -600,10 +599,10 @@@ void test_barrier_handler(trapframe_t *
  
  static void test_waiting_handler(trapframe_t *tf, void* data)
  {
-       {HANDLER_ATOMIC atomic_dec((uint32_t*)data);}
+       {HANDLER_ATOMIC atomic_dec((atomic_t*)data);}
  }
  
 -#if 0
++#ifdef __i386__
  void test_pit(void)
  {
        cprintf("Starting test for PIT now (10s)\n");
@@@ -4,14 -3,11 +4,13 @@@
   * See LICENSE for details.
   */
  
- #include <arch/x86.h>
- #include <arch/apic.h>
- #include <arch/smp.h>
+ #include <atomic.h>
+ #include <smp.h>
  
 -#include <atomic.h>
 +#include <ros/error.h>
  
 +#include <atomic.h>
 +#include <stdio.h>
  #include <workqueue.h>
  
  /*
   */
  void process_workqueue()
  {
 -      work_t work;
 -      per_cpu_info_t *cpuinfo = &per_cpu_info[core_id()];
 +      struct work work;
-       struct per_cpu_info *cpuinfo = &per_cpu_info[coreid()];
++      struct per_cpu_info *cpuinfo = &per_cpu_info[core_id()];
++
        // copy the work in, since we may never return to this stack frame
        spin_lock_irqsave(&cpuinfo->lock);
 -      work = cpuinfo->delayed_work;
 +      work = cpuinfo->workqueue.statics[0];
        spin_unlock_irqsave(&cpuinfo->lock);
        if (work.func) {
                // TODO: possible race with this.  sort it out when we have a queue.
                work.func(work.data);
        }
  }
-       struct per_cpu_info *cpuinfo = &per_cpu_info[coreid()];
 +
 +int enqueue_work(struct workqueue *queue, struct work *job)
 +{
 +      error_t retval = 0;
-              job->func, job->data, coreid());
++      struct per_cpu_info *cpuinfo = &per_cpu_info[core_id()];
 +
 +      spin_lock_irqsave(&cpuinfo->lock);
 +      printd("Enqueuing func 0x%08x and data 0x%08x on core %d.\n",
++             job->func, job->data, core_id());
 +      if (queue->statics[0].func)
 +              retval = -ENOMEM;
 +      else
 +              queue->statics[0] = *job;
 +      spin_unlock_irqsave(&cpuinfo->lock);
 +      return retval;
 +}
Simple merge
Simple merge
index 0000000,e2231bb..e26d2a1
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,82 +1,81 @@@
 -#include <ros/env.h>
+ /* Copyright (c) 2009 The Regents of the University  of California. 
+  * See the COPYRIGHT files at the top of this source tree for full 
+  * license information.
+  * 
+  * Kevin Klues <klueska@cs.berkeley.edu>    
+  */
+  
+ #ifndef ROS_CHANNEL_H
+ #define ROS_CHANNEL_H
+  
+ #include <stdint.h>
+ #include <arch/types.h>
+ #include <ros/error.h>
 -      envid_t endpoint;
+ #include <ros/ring_buffer.h>
+ /***************** Channel related constants *****************/
+ enum {
+       CHANNEL_POLL   = 0,
+ //    CHANNEL_NOTIFY = 1,
+ };
+ /*************************************************************/
+ /***************** Channel related structures *****************/
+ typedef struct channel_msg {
+       size_t len;
+       void* buf;
+ } channel_msg_t;
+ typedef struct channel_ack {
+       int32_t ack;
+ } channel_ack_t;
+ // Generic Syscall Ring Buffer
+ DEFINE_RING_TYPES(channel, channel_msg_t, channel_ack_t);
+ typedef enum {
+       CHANNEL_CLIENT = 1,
+       CHANNEL_SERVER = 2,
+ } channel_type_t;
+ typedef struct channel {
+       size_t id;
+       channel_type_t type;
 -error_t channel_create(envid_t server, channel_t* ch, channel_attr_t* attr);
++      pid_t endpoint;
+       void *COUNT(PGSIZE) data_addr;
+       channel_sring_t *COUNT(1) ring_addr;
+       union {
+               channel_front_ring_t front WHEN(type == CHANNEL_CLIENT);
+               channel_back_ring_t back WHEN(type == CHANNEL_SERVER);
+       } ring_side;
+ } channel_t;
+ typedef struct channel_attr {
+ } channel_attr_t;
+ /*************************************************************/
+ /***************** Channel related functions *****************/
++error_t channel_create(pid_t server, channel_t* ch, channel_attr_t* attr);
+ error_t channel_create_wait(channel_t* ch, channel_attr_t* attr);
+ error_t channel_destroy(channel_t* ch);
+ error_t channel_sendmsg(channel_t* ch, channel_msg_t* msg);
+ error_t channel_recvmsg(channel_t* ch, channel_msg_t* msg);
+ /*************************************************************/
+ #endif //ROS_CHANNEL_H
  #include <ros/error.h>
  #include <newlib_backend.h>
  
 -extern volatile env_t *env;
+ enum {
+       PG_RDONLY = 4,
+       PG_RDWR   = 6,
+ };
  // will need to change these types when we have real structs
  // seems like they need to be either arrays [] or functions () for it to work
  extern volatile uint8_t (COUNT(PGSIZE * UINFO_PAGES) procinfo)[];
@@@ -22,8 -33,11 +31,11 @@@ ssize_t     sys_cputs(const uint8_t *s
  uint16_t    sys_cgetc(void);
  ssize_t     sys_serial_write(void* buf, size_t len); 
  ssize_t     sys_serial_read(void* buf, size_t len);
 -ssize_t     sys_shared_page_alloc(void *COUNT(PGSIZE) *addr, envid_t p2, 
 +int         sys_getpid(void);
 +size_t      sys_getcpuid(void);
 +error_t     sys_proc_destroy(int pid);
++ssize_t     sys_shared_page_alloc(void *COUNT(PGSIZE) *addr, pid_t p2, 
+                                   int p1_flags, int p2_flags);
 -ssize_t     sys_shared_page_free(void *COUNT(PGSIZE) addr, envid_t p2);
 -envid_t     sys_getenvid(void);
 -envid_t     sys_getcpuid(void);
 -void        sys_env_destroy(envid_t);
++ssize_t     sys_shared_page_free(void *COUNT(PGSIZE) addr, pid_t p2);
  
  #endif        // !ROS_INC_LIB_H
index 0000000,d242ea1..9417a91
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,190 +1,189 @@@
 -#include <ros/env.h>
+ /* Copyright (c) 2009 The Regents of the University  of California. 
+  * See the COPYRIGHT files at the top of this source tree for full 
+  * license information.
+  * 
+  * Kevin Klues <klueska@cs.berkeley.edu>    
+  */
+ #include <parlib.h>
+ #include <stdio.h>
+ #include <string.h>
+ #include <channel.h>
 -error_t channel_create(envid_t server, channel_t* ch, channel_attr_t* ch_attr) {
+ #include <ros/syscall.h>
+ #include <arch/arch.h>
+ void simulate_rsp(channel_t* ch) {
+       channel_t ch_server;
+       channel_msg_t msg;
+       ch_server.endpoint = ch->endpoint;
+       ch_server.type = CHANNEL_SERVER;
+       ch_server.ring_addr = ch->ring_addr;
+       ch_server.data_addr = ch->data_addr;    
+       BACK_RING_INIT(&(ch_server.ring_side.back), (ch_server.ring_addr), PGSIZE);
+       
+       channel_recvmsg(&ch_server, &msg);
+ }
 -      envid_t* client;
++error_t channel_create(pid_t server, channel_t* ch, channel_attr_t* ch_attr) {
+       error_t e;
+       void *COUNT(PGSIZE) ring_addr = NULL;
+       void *COUNT(PGSIZE) data_addr = NULL;
+       
+       /*
+        * Attempt to create two shared pages with the 'server' partition.
+        * One for holding our ring buffer, and one for holding our data.
+        * If there is an error at any point during this process, we need
+        * to cleanup and abort, returning the proper error code.  First we
+        * do the page for the ring buffer, deferring the data page till later.
+        */
+       e = sys_shared_page_alloc(&ring_addr, server, PG_RDWR, PG_RDWR);
+       if(e < 0) return e;
+       
+       /* 
+        * If we've made it to here, then we know the shared memory page
+        * for our ring buffer has been mapped successfully into both our own 
+        * address space as well as the server's.  We also know that the 
+        * server has been notified of its creation.  We still haven't mapped in 
+        * our data page, but we will do that after this page has been initialized
+        * properly.  We do this because we need to somehow synchronize the 
+        * the initilization of the ring buffer on this page with 
+        * the server being able to access it.  We do this by forcing the 
+        * initialization now on this end, and only accessing it on the server side
+        * after our data page has been created.
+        */
+       memset((void*SAFE) TC(ch), 0, sizeof(channel_t));
+       ch->endpoint = server;
+       ch->ring_addr = (channel_sring_t *COUNT(1)) TC(ring_addr);
+       ch->type = CHANNEL_CLIENT;
+               
+       /*
+        * As the creator of this channel, we take on the responsibility of 
+        * setting up the ring buffer itself, as well as setting up our own front 
+        * ring so we can push requests (a.ka. messages) out for the server to 
+        * process.  It is the job of the server to setup its back ring so it can
+        * push responses (a.k.a acks) back for us to know the message has been 
+        * processed.
+        */
+       SHARED_RING_INIT(ch->ring_addr);
+       FRONT_RING_INIT(&(ch->ring_side.front), (ch->ring_addr), PGSIZE);
+       
+       /* 
+        * Now we map in the data page on both ends and add a pointer to it in 
+        * our channel struct.
+        */
+       e = sys_shared_page_alloc(&data_addr, server, PG_RDWR, PG_RDONLY);
+       if(e < 0) {
+               sys_shared_page_free(ring_addr, server);
+               return e;
+       }
+       ch->data_addr = data_addr;
+       
+       /* 
+        * Once both pages have been mapped, our data structures have been set up, 
+        * and everything is ready to go, we push an empty message out into the 
+        * ring indicating we are ready on our end.  This implicitly waits for the 
+        * server  to push a response indicating it has finished setting up the other 
+        * side of the channel.
+        */
+       channel_msg_t msg;
+       channel_sendmsg(ch, &msg);
+     
+     /*
+      * If everything has gone according to plan, both ends have set up 
+      * their respective ends of the channel and we can return successfully
+      */
+       return ESUCCESS;
+ }
+ error_t channel_destroy(channel_t* ch) {
+       sys_shared_page_free(ch->data_addr, ch->endpoint);
+       sys_shared_page_free((void *COUNT(PGSIZE)) TC(ch->ring_addr), ch->endpoint);
+       return ESUCCESS;
+ }
+ error_t channel_sendmsg(channel_t* ch, channel_msg_t* msg) {
+       /*
+        * As a first cut implementation, just copy the message handed to 
+        * us into our ring buffer and copy the buffer pointed to in 
+        * the message at the first address in our shared data page.
+        */
+       channel_msg_t* msg_copy;
+       msg_copy = RING_GET_REQUEST(&(ch->ring_side.front), 
+                                   ch->ring_side.front.req_prod_pvt++);
+       msg_copy->len = msg->len;
+       msg_copy->buf = 0;
+       memcpy(ch->data_addr + (size_t)(msg_copy->buf), msg->buf, msg->len);
+       
+       /*
+        * Now that we have copied the message properly, we push the request out
+        * and wait for a response from the server.
+        */
+       RING_PUSH_REQUESTS(&(ch->ring_side.front));
+       
+       while (!(RING_HAS_UNCONSUMED_RESPONSES(&(ch->ring_side.front))))
+               cpu_relax();
+       RING_GET_RESPONSE(&(ch->ring_side.front), ch->ring_side.front.rsp_cons++);
+       
+       return ESUCCESS;
+ }
+ error_t channel_create_wait(channel_t* ch, channel_attr_t* ch_attr) {
+ #if 0
+       error_t e;
++      pid_t* client;
+       void *COUNT(PGSIZE) ring_addr = NULL;
+       void *COUNT(PGSIZE) data_addr = NULL;
+       /* 
+        * Set the type of the channel to the server
+        */
+       ch->type = CHANNEL_SERVER;
+       
+       /*
+        * Wait for the shared ring page to be established and set up
+        * the channel struct with its properties
+        */
+       sysevent_shared_page_alloc_wait(client, ring_addr);
+       ch->endpoint = *client;
+       ch->ring_addr = ring_addr;
+       
+       /*
+        * Now wait for the shared data page to be established and then set up
+        * the backring of the shared ring buffer.
+        */
+       sysevent_shared_page_alloc_wait(client, data_addr);
+       ch->data_addr = data_addr;      
+       BACK_RING_INIT(&(ch->ring_side.back), (ch->ring_addr), PGSIZE);
+       
+       /*
+        * If we've reached this point, then the creating side should already
+        * have a message sitting in the ring buffer waiting for use to 
+        * process.  Now we pull that message off and acknowledge it.
+        */
+       channel_msg_t msg;
+       channel_recvmsg(&ch, &msg);
+ #endif        
+       return ESUCCESS;
+ }
+ error_t channel_recvmsg(channel_t* ch, channel_msg_t* msg) {
+       /*
+        * First copy the data contained in the message from shared page pointed 
+        * to by the entry in the ring buffer to the msg struct passedinto this 
+        * function
+        */
+       channel_msg_t* msg_copy = RING_GET_REQUEST(&(ch->ring_side.back), 
+                                                 ch->ring_side.back.req_cons++);
+       msg->len = msg_copy->len; 
+       memcpy(msg->buf, ch->data_addr + (size_t)(msg_copy->buf), msg->len);
+       
+       /*
+        * Then acknowledge that its been serviced / in the process of being 
+        * serviced.
+        */     
+       channel_ack_t* ack = RING_GET_RESPONSE(&(ch->ring_side.back), 
+                                              ch->ring_side.back.rsp_prod_pvt++);      
+       RING_PUSH_RESPONSES(&(ch->ring_side.back));     
+       return ESUCCESS;
+ }
Simple merge
@@@ -3,89 -3,20 +3,19 @@@
  #pragma nodeputy
  #endif
  
- #include <arch/x86.h>
  #include <parlib.h>
- #include <ros/trap.h>
- // TODO: modify to take only four parameters
- static intreg_t syscall_sysenter(uint16_t num, intreg_t a1,
-                                  intreg_t a2, intreg_t a3,
-                                  intreg_t a4, intreg_t a5)
- {
-       intreg_t ret;
-       asm volatile(
-           "pushl %%ebp\n\t"
-           "pushl %%esi\n\t"
-           "movl %%esp, %%ebp\n\t"
-           "leal after_sysenter, %%esi\n\t"
-           "sysenter\n\t"
-           "after_sysenter:\n\t"
-           "\tpopl %%esi\n"
-           "\tpopl %%ebp\n"
-           :"=a" (ret)
-           : "a" (num),
-             "d" (a1),
-             "c" (a2),
-             "b" (a3),
-             "D" (a4)
-           : "cc", "memory", "%esp");
-       return ret;
- }
- static intreg_t syscall_trap(uint16_t num, intreg_t a1,
-                              intreg_t a2, intreg_t a3,
-                              intreg_t a4, intreg_t a5)
- {
-       intreg_t ret;
-       // Generic system call: pass system call number in AX,
-       // up to five parameters in DX, CX, BX, DI, SI.
-       // Interrupt kernel with T_SYSCALL.
-       //
-       // The "volatile" tells the assembler not to optimize
-       // this instruction away just because we don't use the
-       // return value.
-       //
-       // The last clause tells the assembler that this can
-       // potentially change the condition codes and arbitrary
-       // memory locations.
-       asm volatile("int %1\n"
-               : "=a" (ret)
-               : "i" (T_SYSCALL),
-                 "a" (num),
-                 "d" (a1),
-                 "c" (a2),
-                 "b" (a3),
-                 "D" (a4),
-                 "S" (a5)
-               : "cc", "memory");
-       return ret;
- }
- static intreg_t syscall(uint16_t num, intreg_t a1,
-                         intreg_t a2, intreg_t a3,
-                         intreg_t a4, intreg_t a5)
- {
-       #ifndef SYSCALL_TRAP
-               return syscall_sysenter(num, a1, a2, a3, a4, a5);
-       #else
-               return syscall_trap(num, a1, a2, a3, a4, a5);
-       #endif
- }
  
 -void sys_env_destroy(envid_t envid)
 +error_t sys_proc_destroy(int pid)
  {
 -      syscall(SYS_env_destroy, envid, 0, 0, 0, 0);
 -      while(1); //Should never get here...
 +      return syscall(SYS_proc_destroy, pid, 0, 0, 0, 0);
  }
  
 -envid_t sys_getenvid(void)
 +int sys_getpid(void)
  {
 -       return syscall(SYS_getenvid, 0, 0, 0, 0, 0);
 +       return syscall(SYS_getpid, 0, 0, 0, 0, 0);
  }
  
 -envid_t sys_getcpuid(void)
 +size_t sys_getcpuid(void)
  {
         return syscall(SYS_getcpuid, 0, 0, 0, 0, 0);
  }
@@@ -100,6 -31,19 +30,19 @@@ uint16_t sys_cgetc(void
      return syscall(SYS_cgetc, 0, 0, 0, 0, 0);
  }
  
 -ssize_t sys_shared_page_alloc(void** addr, envid_t p2, 
++ssize_t sys_shared_page_alloc(void** addr, pid_t p2, 
+                               int p1_flags, int p2_flags
+                              ) 
+ {
+       return syscall(SYS_shared_page_alloc, (intreg_t)addr, 
+                      p2, p1_flags, p2_flags, 0);
+ }
 -ssize_t sys_shared_page_free(void* addr, envid_t p2) 
++ssize_t sys_shared_page_free(void* addr, pid_t p2) 
+ {
+       return syscall(SYS_shared_page_free, (intreg_t)addr, p2, 0,0,0);
+ }
  //Write a buffer over the serial port
  ssize_t sys_serial_write(void* buf, size_t len) 
  {
Simple merge
Simple merge
index 0000000,7b3a7e8..02cb4f3
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,39 +1,39 @@@
 -// Define the global symbols 'envs', 'pages', 'vpt', and 'vpd'
+ #include <arch/mmu.h>
+ #include <ros/memlayout.h>
+ .data
 -// Entrypoint - this is where the kernel (or our parent environment)
 -// starts us running when we are initially loaded into a new environment.
++// Define the global symbols 'procinfo', 'procdata', 'pages', 'vpt', and 'vpd'
+ // so that they can be used in C as if they were ordinary global arrays.
+       .globl procinfo
+       .set procinfo, UINFO
+       .globl procdata
+       .set procdata, USYSCALL
+       .globl pages
+       .set pages, UPAGES
+       .globl vpt
+       .set vpt, UVPT
+       .globl vpd
+       .set vpd, (UVPT+(UVPT>>12)*4)
++// Entrypoint - this is where the kernel (or our parent process)
++// starts us running when we are initially created and run
+ .text
+ .globl _start
+ _start:
+       // See if we were started with arguments on the stack
+       cmpl $USTACKTOP, %esp
+       jne args_exist
+       // If not, push dummy argc/argv arguments.
+       // This happens when we are loaded by the kernel,
+       // because the kernel does not know about passing arguments.
+       pushl $0
+       pushl $0
+ args_exist:
+       call libmain
+ 1:      jmp 1b
index 0000000,cd64677..ae19a6c
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,43 +1,43 @@@
 -// Define the global symbols 'envs', 'pages', 'vpt', and 'vpd'
+ #include <arch/mmu.h>
+ #include <ros/memlayout.h>
+ .data
 -// Entrypoint - this is where the kernel (or our parent environment)
 -// starts us running when we are initially loaded into a new environment.
++// Define the global symbols 'procinfo', 'procdata', 'pages', 'vpt', and 'vpd'
+ // so that they can be used in C as if they were ordinary global arrays.
+       .globl procinfo
+       .set procinfo, UINFO
+       .globl procdata
+       .set procdata, USYSCALL
+       .globl pages
+       .set pages, UPAGES
+       .globl vpt
+       .set vpt, UVPT
+       .globl vpd
+       .set vpd, (UVPT+(UVPT>>12)*4)
++// Entrypoint - this is where the kernel (or our parent process)
++// starts us running when we are initially created and run
+ .text
+ .globl _start
+ _start:
+       // See if we were started with arguments on the stack
+       tst     %o0
+       bne     args_exist
+        nop
+       // 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.
+       mov     0,%o0
+       mov     0,%o1
+ args_exist:
+       call    libmain
+        nop
+ 1:    ba      1b
+        nop
Simple merge