Proc data structure management, env gutting
[akaros.git] / kern / arch / i386 / trap.c
index 0efd4ed..b865fe7 100644 (file)
@@ -1,5 +1,6 @@
-#ifdef __DEPUTY__
-#pragma noasync
+#ifdef __SHARC__
+#pragma nosharc
+#define SINIT(x) x
 #endif
 
 #include <arch/mmu.h>
 #endif
 
 #include <arch/mmu.h>
@@ -7,23 +8,25 @@
 #include <arch/arch.h>
 #include <arch/console.h>
 #include <arch/apic.h>
 #include <arch/arch.h>
 #include <arch/console.h>
 #include <arch/apic.h>
+#include <ros/common.h>
 #include <smp.h>
 #include <assert.h>
 #include <pmap.h>
 #include <trap.h>
 #include <monitor.h>
 #include <smp.h>
 #include <assert.h>
 #include <pmap.h>
 #include <trap.h>
 #include <monitor.h>
-#include <env.h>
-
+#include <process.h>
+#include <stdio.h>
+#include <slab.h>
 #include <syscall.h>
 
 #include <syscall.h>
 
-taskstate_t ts;
+taskstate_t RO 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)
 
 /* 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 = {
+gatedesc_t __attribute__ ((aligned (8))) (RO idt)[256] = { { 0 } };
+pseudodesc_t RO idt_pd = {
        sizeof(idt) - 1, (uint32_t) idt
 };
 
        sizeof(idt) - 1, (uint32_t) idt
 };
 
@@ -31,12 +34,16 @@ pseudodesc_t idt_pd = {
  * of functions to be called when servicing an interrupt.  other cores
  * can set up their own later.
  */
  * of functions to be called when servicing an interrupt.  other cores
  * can set up their own later.
  */
-handler_t interrupt_handlers[256];
+#ifdef __IVY__
+#pragma cilnoremove("iht_lock")
+#endif
+spinlock_t iht_lock;
+handler_t TP(TV(t)) LCKD(&iht_lock) (RO interrupt_handlers)[NUM_INTERRUPT_HANDLERS];
 
 
-static const char *NTS (IN_HANDLER trapname)(int trapno)
+static const char *NTS trapname(int trapno)
 {
 {
-    // zra: excnames is NORACE because Ivy doesn't trust const
-       static const char *NT const (NORACE excnames)[] = {
+    // zra: excnames is SREADONLY because Ivy doesn't trust const
+       static const char *NT const (RO excnames)[] = {
                "Divide error",
                "Debug",
                "Non-Maskable Interrupt",
                "Divide error",
                "Debug",
                "Non-Maskable Interrupt",
@@ -70,20 +77,20 @@ static const char *NTS (IN_HANDLER trapname)(int trapno)
 void
 idt_init(void)
 {
 void
 idt_init(void)
 {
-       extern segdesc_t gdt[];
+       extern segdesc_t (RO 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; };
 
        // 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)[];
+       extern struct trapinfo (BND(__this,trap_tbl_end) RO trap_tbl)[];
+       extern struct trapinfo (SNT RO 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++)
        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);
+               ROSETGATE(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
 
        // set all entries that have real trap handlers
        // we need to stop short of the last one, since the last is the default
@@ -92,24 +99,25 @@ idt_init(void)
        // 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++)
        // 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);
+               ROSETGATE(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)
 
        // 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;
+       idt[T_SYSCALL].gd_dpl = SINIT(3);
+       idt[T_SYSCALL].gd_type = SINIT(STS_TG32);
+       idt[T_BRKPT].gd_dpl = SINIT(3);
 
        // Setup a TSS so that we get the right stack
        // when we trap to the kernel.
 
        // 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;
+       ts.ts_esp0 = SINIT(KSTACKTOP);
+       ts.ts_ss0 = SINIT(GD_KD);
 
        // Initialize the TSS field of the gdt.
 
        // 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;
+       SEG16ROINIT(gdt[GD_TSS >> 3],STS_T32A, (uint32_t)(&ts),sizeof(taskstate_t),0);
+       //gdt[GD_TSS >> 3] = (segdesc_t)SEG16(STS_T32A, (uint32_t) (&ts),
+       //                                 sizeof(taskstate_t), 0);
+       gdt[GD_TSS >> 3].sd_s = SINIT(0);
 
        // Load the TSS
        ltr(GD_TSS);
 
        // Load the TSS
        ltr(GD_TSS);
@@ -128,7 +136,7 @@ idt_init(void)
 }
 
 void
 }
 
 void
-(IN_HANDLER print_regs)(push_regs_t *regs)
+print_regs(push_regs_t *regs)
 {
        cprintf("  edi  0x%08x\n", regs->reg_edi);
        cprintf("  esi  0x%08x\n", regs->reg_esi);
 {
        cprintf("  edi  0x%08x\n", regs->reg_edi);
        cprintf("  esi  0x%08x\n", regs->reg_esi);
@@ -141,9 +149,12 @@ void
 }
 
 void
 }
 
 void
-(IN_HANDLER print_trapframe)(trapframe_t *tf)
+print_trapframe(trapframe_t *tf)
 {
 {
-       cprintf("TRAP frame at %p on core %d\n", tf, lapic_get_id());
+       static spinlock_t ptf_lock;
+
+       spin_lock_irqsave(&ptf_lock);
+       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);
        print_regs(&tf->tf_regs);
        cprintf("  es   0x----%04x\n", tf->tf_es);
        cprintf("  ds   0x----%04x\n", tf->tf_ds);
@@ -154,13 +165,12 @@ void
        cprintf("  flag 0x%08x\n", tf->tf_eflags);
        cprintf("  esp  0x%08x\n", tf->tf_esp);
        cprintf("  ss   0x----%04x\n", tf->tf_ss);
        cprintf("  flag 0x%08x\n", tf->tf_eflags);
        cprintf("  esp  0x%08x\n", tf->tf_esp);
        cprintf("  ss   0x----%04x\n", tf->tf_ss);
+       spin_unlock_irqsave(&ptf_lock);
 }
 
 static void
 }
 
 static void
-(IN_HANDLER trap_dispatch)(trapframe_t *tf)
+trap_dispatch(trapframe_t *tf)
 {
 {
-       env_t* curenv = curenvs[core_id()];
-
        // Handle processor exceptions.
        switch(tf->tf_trapno) {
                case T_BRKPT:
        // Handle processor exceptions.
        switch(tf->tf_trapno) {
                case T_BRKPT:
@@ -175,10 +185,10 @@ static void
                        // check for userspace, for now
                        assert(tf->tf_cs != GD_KT);
                        tf->tf_regs.reg_eax =
                        // check for userspace, for now
                        assert(tf->tf_cs != GD_KT);
                        tf->tf_regs.reg_eax =
-                               syscall(curenv, tf->tf_regs.reg_eax, tf->tf_regs.reg_edx,
+                               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);
                                        tf->tf_regs.reg_ecx, tf->tf_regs.reg_ebx,
                                        tf->tf_regs.reg_edi, tf->tf_regs.reg_esi);
-                       env_run(curenv);
+                       proc_startcore(current, tf); // Note the comment in syscall.c
                        break;
                default:
                        // Unexpected trap: The user process or the kernel has a bug.
                        break;
                default:
                        // Unexpected trap: The user process or the kernel has a bug.
@@ -187,7 +197,7 @@ static void
                                panic("Damn Damn!  Unhandled trap in the kernel!");
                        else {
                                warn("Unexpected trap from userspace");
                                panic("Damn Damn!  Unhandled trap in the kernel!");
                        else {
                                warn("Unexpected trap from userspace");
-                               env_destroy(curenv);
+                               proc_destroy(current);
                                return;
                        }
        }
                                return;
                        }
        }
@@ -195,70 +205,65 @@ static void
 }
 
 void
 }
 
 void
-(IN_HANDLER env_push_ancillary_state)(env_t* e)
+env_push_ancillary_state(env_t* e)
 {
 {
+       // TODO: (HSS) handle silly state (don't really want this per-process)
        // Here's where you'll save FP/MMX/XMM regs
 }
 
 void
        // Here's where you'll save FP/MMX/XMM regs
 }
 
 void
-(IN_HANDLER env_pop_ancillary_state)(env_t* e)
+env_pop_ancillary_state(env_t* e)
 {
        // Here's where you'll restore FP/MMX/XMM regs
 }
 
 void
 {
        // Here's where you'll restore FP/MMX/XMM regs
 }
 
 void
-(IN_HANDLER trap)(trapframe_t *tf)
+trap(trapframe_t *tf)
 {
 {
-       //cprintf("Incoming TRAP frame at %p\n", tf);
-
-       env_t* curenv = curenvs[lapic_get_id()];
+       //printk("Incoming TRAP frame on core %d at %p\n", core_id(), tf);
 
 
-       env_push_ancillary_state(curenv);
+       /* Note we are not preemptively saving the TF in the env_tf.  We do maintain
+        * a reference to it in current_tf (a per-cpu pointer).
+        * In general, only save the tf and any silly state once you know it
+        * is necessary (blocking).  And only save it in env_tf when you know you
+        * are single core (PROC_RUNNING_S) */
+       set_current_tf(tf);
 
        if ((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) != 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)
-               // into 'curenv->env_tf', so that running the environment
-               // will restart at the trap point.
-               assert(curenv);
-               curenv->env_tf = *tf;
-               // The trapframe on the stack should be ignored from here on.
-               tf = &curenv->env_tf;
-       }
-
        // Dispatch based on what type of trap occurred
        trap_dispatch(tf);
 
        // should this be if == 3?  Sort out later when we handle traps.
        // 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
+       // so far we never get here.  managed to get here once when a MCP took two
+       // page faults and both called proc_destroy().  one of which returned since
+       // it was already DYING (though it should have waited for it's death IPI).
        assert(0);
        assert(0);
-        // Return to the current environment, which should be runnable.
-        assert(curenv && curenv->env_status == ENV_RUNNABLE);
-        env_run(curenv);
+       // Return to the current environment, which should be runnable.
+       proc_startcore(current, tf); // Note the comment in syscall.c
 }
 
 void
 }
 
 void
-(IN_HANDLER irq_handler)(trapframe_t *tf)
+irq_handler(trapframe_t *tf)
 {
 {
-       //if (lapic_get_id())
-       //      cprintf("Incoming IRQ, ISR: %d on core %d\n", tf->tf_trapno, lapic_get_id());           
+       // save a per-core reference to the tf
+       set_current_tf(tf);
+       //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
 
        // merge this with alltraps?  other than the EOI... or do the same in all traps
 
-       extern handler_wrapper_t handler_wrappers[NUM_HANDLER_WRAPPERS];
+       extern handler_wrapper_t (RO handler_wrappers)[NUM_HANDLER_WRAPPERS];
 
        // determine the interrupt handler table to use.  for now, pick the global
 
        // determine the interrupt handler table to use.  for now, pick the global
-       handler_t* handler_tbl = interrupt_handlers;
+       handler_t TP(TV(t)) LCKD(&iht_lock) * 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 (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))
+       if ((I_SMP_CALL0 <= tf->tf_trapno) && (tf->tf_trapno <= I_SMP_CALL_LAST))
                down_checklist(handler_wrappers[tf->tf_trapno & 0x0f].cpu_list);
 
        // Send EOI.  might want to do this in assembly, and possibly earlier
                down_checklist(handler_wrappers[tf->tf_trapno & 0x0f].cpu_list);
 
        // Send EOI.  might want to do this in assembly, and possibly earlier
@@ -270,18 +275,19 @@ void
        
        lapic_send_eoi();
        
        
        lapic_send_eoi();
        
-/*     Old PIC relatd code. Should be gone for good, but leaving it just incase.
+       /*
+       //Old PIC relatd code. Should be gone for good, but leaving it just incase.
        if (tf->tf_trapno < 48)
        if (tf->tf_trapno < 48)
-               //pic_send_eoi(tf->tf_trapno - PIC1_OFFSET);
-               ioapic_send_eoi(tf->tf_trapno); // Quick hack. Fix.
+               pic_send_eoi(tf->tf_trapno - PIC1_OFFSET);
        else
                lapic_send_eoi();
        else
                lapic_send_eoi();
-*/
+       */
+
 }
 
 void
 }
 
 void
-register_interrupt_handler(handler_t table[], uint8_t int_num, isr_t handler,
-                           void* data)
+register_interrupt_handler(handler_t TP(TV(t)) table[],
+                           uint8_t int_num, poly_isr_t handler, TV(t) data)
 {
        table[int_num].isr = handler;
        table[int_num].data = data;
 {
        table[int_num].isr = handler;
        table[int_num].data = data;
@@ -308,7 +314,7 @@ page_fault_handler(trapframe_t *tf)
 
        // Call the environment's page fault upcall, if one exists.  Set up a
        // page fault stack frame on the user exception stack (below
 
        // Call the environment's page fault upcall, if one exists.  Set up a
        // page fault stack frame on the user exception stack (below
-       // UXSTACKTOP), then branch to curenv->env_pgfault_upcall.
+       // 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
        //
        // The page fault upcall might cause another page fault, in which case
        // we branch to the page fault upcall recursively, pushing another
@@ -328,17 +334,16 @@ page_fault_handler(trapframe_t *tf)
        //
        // Hints:
        //   user_mem_assert() and env_run() are useful here.
        //
        // Hints:
        //   user_mem_assert() and env_run() are useful here.
-       //   To change what the user environment runs, modify 'curenv->env_tf'
-       //   (the 'tf' variable points at 'curenv->env_tf').
+       //   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.
 
        // LAB 4: Your code here.
 
        // Destroy the environment that caused the fault.
-       env_t* curenv = curenvs[lapic_get_id()];
        cprintf("[%08x] user fault va %08x ip %08x from core %d\n",
        cprintf("[%08x] user fault va %08x ip %08x from core %d\n",
-               curenv->env_id, fault_va, tf->tf_eip, lapic_get_id());
+               current->pid, fault_va, tf->tf_eip, core_id());
        print_trapframe(tf);
        print_trapframe(tf);
-       env_destroy(curenv);
+       proc_destroy(current);
 }
 
 void sysenter_init(void)
 }
 
 void sysenter_init(void)
@@ -351,17 +356,93 @@ void sysenter_init(void)
 /* This is called from sysenter's asm, with the tf on the kernel stack. */
 void sysenter_callwrapper(struct Trapframe *tf)
 {
 /* This is called from sysenter's asm, with the tf on the kernel stack. */
 void sysenter_callwrapper(struct Trapframe *tf)
 {
-       env_t* curenv = curenvs[lapic_get_id()];
-       curenv->env_tf = *tf;
-       
-       // The trapframe on the stack should be ignored from here on.
-       tf = &curenv->env_tf;
-       tf->tf_regs.reg_eax = (intreg_t) syscall(curenv,
+       // save a per-core reference to the tf
+       set_current_tf(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);
                                                 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);
-       env_run(curenv);
+       /*
+        * 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);
+}
+
+struct kmem_cache *active_msg_cache;
+void active_msg_init(void)
+{
+       active_msg_cache = kmem_cache_create("active_msgs",
+                          sizeof(struct active_message), HW_CACHE_ALIGN, 0, 0, 0);
+}
+
+uint32_t send_active_message(uint32_t dst, amr_t pc,
+                             TV(a0t) arg0, TV(a1t) arg1, TV(a2t) arg2)
+{
+       active_message_t *a_msg;
+       assert(pc);
+       // note this will be freed on the destination core
+       a_msg = (active_message_t *CT(1))TC(kmem_cache_alloc(active_msg_cache, 0));
+       a_msg->srcid = core_id();
+       a_msg->pc = pc;
+       a_msg->arg0 = arg0;
+       a_msg->arg1 = arg1;
+       a_msg->arg2 = arg2;
+       spin_lock_irqsave(&per_cpu_info[dst].amsg_lock);
+       STAILQ_INSERT_TAIL(&per_cpu_info[dst].active_msgs, a_msg, link);
+       spin_unlock_irqsave(&per_cpu_info[dst].amsg_lock);
+       // since we touched memory the other core will touch (the lock), we don't
+       // need an wmb_f()
+       send_ipi(dst, 0, I_ACTIVE_MSG);
+       return 0;
+}
+
+/* Active message handler.  We don't want to block other AMs from coming in, so
+ * we'll copy out the message and let go of the lock.  This won't return until
+ * all pending AMs are executed.  If the PC is 0, then this was an extra IPI and
+ * we already handled the message (or someone is sending IPIs without loading
+ * the active message...)
+ * Note that all of this happens from interrupt context, and interrupts are
+ * currently disabled for this gate.  Interrupts need to be disabled so that the
+ * self-ipi doesn't preempt the execution of this active message. */
+void __active_message(trapframe_t *tf)
+{
+       per_cpu_info_t RO*myinfo = &per_cpu_info[core_id()];
+       active_message_t my_msg, *a_msg;
+
+       lapic_send_eoi();
+       while (1) { // will break out when there are no more messages
+               /* Get the message */
+               spin_lock_irqsave(&myinfo->amsg_lock);
+               a_msg = STAILQ_FIRST(&myinfo->active_msgs);
+               /* No messages to execute, so break out, etc. */
+               if (!a_msg) {
+                       spin_unlock_irqsave(&myinfo->amsg_lock);
+                       return;
+               }
+               STAILQ_REMOVE_HEAD(&myinfo->active_msgs, link);
+               spin_unlock_irqsave(&myinfo->amsg_lock);
+               // copy in, and then free, in case we don't return
+               my_msg = *a_msg;
+               kmem_cache_free(active_msg_cache, (void *CT(1))TC(a_msg));
+               assert(my_msg.pc);
+               /* In case the function doesn't return (which is common: __startcore,
+                * __death, etc), there is a chance we could lose an amsg.  We can only
+                * have up to two interrupts outstanding, and if we never return, we
+                * never deal with any other amsgs.  This extra IPI hurts performance
+                * but is only necessary if there is another outstanding message in the
+                * buffer, but makes sure we never miss out on an amsg. */
+               spin_lock_irqsave(&myinfo->amsg_lock);
+               if (!STAILQ_EMPTY(&myinfo->active_msgs))
+                       send_self_ipi(I_ACTIVE_MSG);
+               spin_unlock_irqsave(&myinfo->amsg_lock);
+               /* Execute the active message */
+               my_msg.pc(tf, my_msg.srcid, my_msg.arg0, my_msg.arg1, my_msg.arg2);
+       }
 }
 }