x86: bus_irq_enable()
[akaros.git] / kern / arch / x86 / trap.c
index 583ac16..3a0067c 100644 (file)
@@ -28,10 +28,14 @@ taskstate_t RO ts;
 gatedesc_t __attribute__((aligned (16))) idt[256] = { { 0 } };
 pseudodesc_t idt_pd;
 
-/* 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[NUM_INTERRUPT_HANDLERS];
+/* interrupt handler table, each element is a linked list of handlers for a
+ * given IRQ.  Modification requires holding the lock (TODO: RCU) */
+struct irq_handler *irq_handlers[NUM_IRQS];
+spinlock_t irq_handler_wlock = SPINLOCK_INITIALIZER_IRQSAVE;
+
+/* Which pci devices hang off of which irqs */
+/* TODO: make this an array of SLISTs (pain from ioapic.c, etc...) */
+struct pci_device *irq_pci_map[NUM_IRQS] = {0};
 
 const char *x86_trapname(int trapno)
 {
@@ -85,6 +89,8 @@ uintptr_t get_stack_top(void)
        struct per_cpu_info *pcpui = &per_cpu_info[core_id()];
        uintptr_t stacktop;
        /* so we can check this in interrupt handlers (before smp_boot()) */
+       /* TODO: These are dangerous - it assumes we're on a one-page stack.  If we
+        * change it to KSTKSIZE, then we assume stacks are KSTKSIZE-aligned */
        if (!pcpui->tss)
                return ROUNDUP(read_sp(), PGSIZE);
        stacktop = x86_get_stacktop_tss(pcpui->tss);
@@ -113,6 +119,7 @@ void idt_init(void)
        extern struct trapinfo trap_tbl_end[];
        int i, trap_tbl_size = trap_tbl_end - trap_tbl;
        extern void ISR_default(void);
+       extern void ISR_syscall(void);
 
        /* set all to default, to catch everything */
        for (i = 0; i < 256; i++)
@@ -124,7 +131,11 @@ void idt_init(void)
         * the idt[] */
        for (i = 0; i < trap_tbl_size - 1; i++)
                SETGATE(idt[trap_tbl[i].trapnumber], 0, GD_KT, trap_tbl[i].trapaddr, 0);
-
+       /* Sanity check */
+       assert((uintptr_t)ISR_syscall ==
+              ((uintptr_t)idt[T_SYSCALL].gd_off_63_32 << 32 |
+               (uintptr_t)idt[T_SYSCALL].gd_off_31_16 << 16 |
+               (uintptr_t)idt[T_SYSCALL].gd_off_15_0));
        /* turn on trap-based syscall handling and other user-accessible ints
         * DPL 3 means this can be triggered by the int instruction */
        idt[T_SYSCALL].gd_dpl = SINIT(3);
@@ -136,9 +147,7 @@ void idt_init(void)
        x86_sysenter_init((uintptr_t)bootstacktop);
 
 #ifdef CONFIG_KTHREAD_POISON
-       /* TODO: KTHR-STACK */
-       uintptr_t *poison = (uintptr_t*)ROUNDDOWN(bootstacktop - 1, PGSIZE);
-       *poison = 0xdeadbeef;
+       *kstack_bottom_addr((uintptr_t)bootstacktop) = 0xdeadbeef;
 #endif /* CONFIG_KTHREAD_POISON */
 
        /* Initialize the TSS field of the gdt.  The size of the TSS desc differs
@@ -156,20 +165,34 @@ void idt_init(void)
 
        asm volatile("lidt %0" : : "m"(idt_pd));
 
-       // This will go away when we start using the IOAPIC properly
        pic_remap();
+       pic_mask_all();
+
+#ifdef CONFIG_ENABLE_MPTABLES
+       int ncleft;
+       int mpsinit(int maxcores);
+
+       ncleft = mpsinit(MAX_NUM_CPUS);
+       /* NEVER printd here ... */
+       printk("mpacpi is %d\n", mpacpi(ncleft));
+
+       void ioapiconline(void);
+       void apiconline(void);
+       apiconline(); /* TODO: do this this for all cores*/
+       ioapiconline();
+#else
        // 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();
+       unmask_lapic_lvt(LAPIC_LVT_LINT0);
+#endif
+
        /* register the generic timer_interrupt() handler for the per-core timers */
-       register_interrupt_handler(interrupt_handlers, LAPIC_TIMER_DEFAULT_VECTOR,
-                                  timer_interrupt, NULL);
+       register_irq(IdtLAPIC_TIMER, timer_interrupt, NULL,
+                        MKBUS(BusLAPIC, 0, 0, 0));
        /* register the kernel message handler */
-       register_interrupt_handler(interrupt_handlers, I_KERNEL_MSG,
-                                  handle_kmsg_ipi, NULL);
+       register_irq(I_KERNEL_MSG, handle_kmsg_ipi, NULL,
+                        MKBUS(BusLAPIC, 0, 0, 0));
 }
 
 static void handle_fperr(struct hw_trapframe *hw_tf)
@@ -209,25 +232,88 @@ static void handle_fperr(struct hw_trapframe *hw_tf)
        proc_destroy(current);
 }
 
+void backtrace_kframe(struct hw_trapframe *hw_tf)
+{
+       struct per_cpu_info *pcpui = &per_cpu_info[core_id()];
+       pcpui->__lock_checking_enabled--;
+       printk("\nBacktrace of faulting kernel context on Core %d:\n", core_id());
+       backtrace_frame(x86_get_hwtf_pc(hw_tf), x86_get_hwtf_fp(hw_tf));
+       pcpui->__lock_checking_enabled++;
+}
+
+static bool __handle_page_fault(struct hw_trapframe *hw_tf, unsigned long *aux)
+{
+       uintptr_t fault_va = rcr2();
+       int prot = hw_tf->tf_err & PF_ERROR_WRITE ? PROT_WRITE : PROT_READ;
+       int err;
+
+       /* TODO - handle kernel page faults */
+       if ((hw_tf->tf_cs & 3) == 0) {
+               print_trapframe(hw_tf);
+               backtrace_kframe(hw_tf);
+               panic("Page Fault in the Kernel at %p!", fault_va);
+               /* if we want to do something like kill a process or other code, be
+                * aware we are in a sort of irq-like context, meaning the main kernel
+                * code we 'interrupted' could be holding locks - even irqsave locks. */
+       }
+       /* safe to reenable after rcr2 */
+       enable_irq();
+       if ((err = handle_page_fault(current, fault_va, prot))) {
+               if (err == -EAGAIN)
+                       hw_tf->tf_err |= PF_VMR_BACKED;
+               *aux = fault_va;
+               return FALSE;
+               /* useful debugging */
+               printk("[%08x] user %s fault va %p ip %p on core %d with err %d\n",
+                      current->pid, prot & PROT_READ ? "READ" : "WRITE", fault_va,
+                      hw_tf->tf_rip, core_id(), err);
+               print_trapframe(hw_tf);
+               /* Turn this on to help debug bad function pointers */
+#ifdef CONFIG_X86_64
+               printd("rsp %p\n\t 0(rsp): %p\n\t 8(rsp): %p\n\t 16(rsp): %p\n"
+                      "\t24(rsp): %p\n", hw_tf->tf_rsp,
+                      *(uintptr_t*)(hw_tf->tf_rsp +  0),
+                      *(uintptr_t*)(hw_tf->tf_rsp +  8),
+                      *(uintptr_t*)(hw_tf->tf_rsp + 16),
+                      *(uintptr_t*)(hw_tf->tf_rsp + 24));
+#else
+               printd("esp %p\n\t 0(esp): %p\n\t 4(esp): %p\n\t 8(esp): %p\n"
+                      "\t12(esp): %p\n", hw_tf->tf_esp,
+                      *(uintptr_t*)(hw_tf->tf_esp +  0),
+                      *(uintptr_t*)(hw_tf->tf_esp +  4),
+                      *(uintptr_t*)(hw_tf->tf_esp +  8),
+                      *(uintptr_t*)(hw_tf->tf_esp + 12));
+#endif
+       }
+       return TRUE;
+}
+
 /* Certain traps want IRQs enabled, such as the syscall.  Others can't handle
  * it, like the page fault handler.  Turn them on on a case-by-case basis. */
 static void trap_dispatch(struct hw_trapframe *hw_tf)
 {
        struct per_cpu_info *pcpui;
+       bool handled = TRUE;
+       unsigned long aux = 0;
        // Handle processor exceptions.
        switch(hw_tf->tf_trapno) {
                case T_NMI:
                        /* Temporarily disable deadlock detection when we print.  We could
                         * deadlock if we were printing when we NMIed. */
                        pcpui = &per_cpu_info[core_id()];
-                       pcpui->__lock_depth_disabled++;
-                       print_trapframe(hw_tf);
+                       pcpui->__lock_checking_enabled--;
+                       /* This is a bit hacky, but we don't have a decent API yet */
+                       extern bool mon_verbose_trace;
+                       if (mon_verbose_trace) {
+                               print_trapframe(hw_tf);
+                               backtrace_kframe(hw_tf);
+                       }
                        char *fn_name = get_fn_name(x86_get_ip_hw(hw_tf));
                        printk("Core %d is at %p (%s)\n", core_id(), x86_get_ip_hw(hw_tf),
                               fn_name);
                        kfree(fn_name);
                        print_kmsgs(core_id());
-                       pcpui->__lock_depth_disabled--;
+                       pcpui->__lock_checking_enabled++;
                        break;
                case T_BRKPT:
                        enable_irq();
@@ -235,9 +321,10 @@ static void trap_dispatch(struct hw_trapframe *hw_tf)
                        break;
                case T_ILLOP:
                {
+                       /* TODO: this can PF if there is a concurrent unmap/PM removal. */
                        uintptr_t ip = x86_get_ip_hw(hw_tf);
                        pcpui = &per_cpu_info[core_id()];
-                       pcpui->__lock_depth_disabled++;         /* for print debugging */
+                       pcpui->__lock_checking_enabled--;               /* for print debugging */
                        /* We will muck with the actual TF.  If we're dealing with
                         * userspace, we need to make sure we edit the actual TF that will
                         * get restarted (pcpui), and not the TF on the kstack (which aren't
@@ -253,16 +340,16 @@ static void trap_dispatch(struct hw_trapframe *hw_tf)
                            *(uint8_t*)(ip + 1) == 0x01, 
                            *(uint8_t*)(ip + 2) == 0xf9) {
                                x86_fake_rdtscp(hw_tf);
-                               pcpui->__lock_depth_disabled--; /* for print debugging */
+                               pcpui->__lock_checking_enabled++;       /* for print debugging */
                                return;
                        }
                        enable_irq();
                        monitor(hw_tf);
-                       pcpui->__lock_depth_disabled--;         /* for print debugging */
+                       pcpui->__lock_checking_enabled++;               /* for print debugging */
                        break;
                }
                case T_PGFLT:
-                       page_fault_handler(hw_tf);
+                       handled = __handle_page_fault(hw_tf, &aux);
                        break;
                case T_FPERR:
                        handle_fperr(hw_tf);
@@ -272,22 +359,21 @@ static void trap_dispatch(struct hw_trapframe *hw_tf)
                        // check for userspace, for now
                        assert(hw_tf->tf_cs != GD_KT);
                        /* Set up and run the async calls */
+                       /* TODO: this is using the wrong reg1 for traps for 32 bit */
                        prep_syscalls(current,
-                                     (struct syscall*)x86_get_sysenter_arg0(hw_tf),
-                                                 (unsigned int)x86_get_sysenter_arg1(hw_tf));
+                                     (struct syscall*)x86_get_systrap_arg0(hw_tf),
+                                                 (unsigned int)x86_get_systrap_arg1(hw_tf));
                        break;
                default:
-                       // Unexpected trap: The user process or the kernel has a bug.
-                       print_trapframe(hw_tf);
-                       if (hw_tf->tf_cs == GD_KT)
+                       if (hw_tf->tf_cs == GD_KT) {
+                               print_trapframe(hw_tf);
                                panic("Damn Damn!  Unhandled trap in the kernel!");
-                       else {
-                               warn("Unexpected trap from userspace");
-                               enable_irq();
-                               proc_destroy(current);
+                       } else {
+                               handled = FALSE;
                        }
        }
-       return;
+       if (!handled)
+               reflect_unhandled_trap(hw_tf->tf_trapno, hw_tf->tf_err, aux);
 }
 
 /* Helper.  For now, this copies out the TF to pcpui.  Eventually, we should
@@ -367,74 +453,6 @@ void trap(struct hw_trapframe *hw_tf)
        assert(0);
 }
 
-/* Tells us if an interrupt (trap_nr) came from the PIC or not */
-static bool irq_from_pic(uint32_t trap_nr)
-{
-       /* The 16 IRQs within the range [PIC1_OFFSET, PIC1_OFFSET + 15] came from
-        * the PIC.  [32-47] */
-       if (trap_nr < PIC1_OFFSET)
-               return FALSE;
-       if (trap_nr > PIC1_OFFSET + 15)
-               return FALSE;
-       return TRUE;
-}
-
-/* Helper: returns TRUE if the irq is spurious.  Pass in the trap_nr, not the
- * IRQ number (trap_nr = PIC_OFFSET + irq) */
-static bool check_spurious_irq(uint32_t trap_nr)
-{
-#ifndef CONFIG_ENABLE_MPTABLES         /* TODO: our proxy for using the PIC */
-       /* the PIC may send spurious irqs via one of the chips irq 7.  if the isr
-        * doesn't show that irq, then it was spurious, and we don't send an eoi.
-        * Check out http://wiki.osdev.org/8259_PIC#Spurious_IRQs */
-       if ((trap_nr == PIC1_SPURIOUS) && !(pic_get_isr() & PIC1_SPURIOUS)) {
-               printk("Spurious PIC1 irq!\n"); /* want to know if this happens */
-               return TRUE;
-       }
-       if ((trap_nr == PIC2_SPURIOUS) && !(pic_get_isr() & PIC2_SPURIOUS)) {
-               printk("Spurious PIC2 irq!\n"); /* want to know if this happens */
-               /* for the cascaded PIC, we *do* need to send an EOI to the master's
-                * cascade irq (2). */
-               pic_send_eoi(2);
-               return TRUE;
-       }
-       /* At this point, we know the PIC didn't send a spurious IRQ */
-       if (irq_from_pic(trap_nr))
-               return FALSE;
-#endif
-       /* Either way (with or without a PIC), we need to check the LAPIC.
-        * FYI: lapic_spurious is 255 on qemu and 15 on the nehalem..  We actually
-        * can set bits 4-7, and P6s have 0-3 hardwired to 0.  YMMV.
-        *
-        * The SDM recommends not using the spurious vector for any other IRQs (LVT
-        * or IOAPIC RTE), since the handlers don't send an EOI.  However, our check
-        * here allows us to use the vector since we can tell the diff btw a
-        * spurious and a real IRQ. */
-       uint8_t lapic_spurious = read_mmreg32(LAPIC_SPURIOUS) & 0xff;
-       /* Note the lapic's vectors are not shifted by an offset. */
-       if ((trap_nr == lapic_spurious) && !lapic_get_isr_bit(lapic_spurious)) {
-               printk("Spurious LAPIC irq %d, core %d!\n", lapic_spurious, core_id());
-               lapic_print_isr();
-               return TRUE;
-       }
-       return FALSE;
-}
-
-/* Helper, sends an end-of-interrupt for the trap_nr (not HW IRQ number). */
-static void send_eoi(uint32_t trap_nr)
-{
-#ifndef CONFIG_ENABLE_MPTABLES         /* TODO: our proxy for using the PIC */
-       /* WARNING: this will break if the LAPIC requests vectors that overlap with
-        * the PIC's range. */
-       if (irq_from_pic(trap_nr))
-               pic_send_eoi(trap_nr - PIC1_OFFSET);
-       else
-               lapic_send_eoi();
-#else
-       lapic_send_eoi();
-#endif
-}
-
 /* Note IRQs are disabled unless explicitly turned on.
  *
  * In general, we should only get trapno's >= PIC1_OFFSET (32).  Anything else
@@ -444,9 +462,10 @@ static void send_eoi(uint32_t trap_nr)
  *
  * Note that from hardware's perspective (PIC, etc), IRQs start from 0, but they
  * are all mapped up at PIC1_OFFSET for the cpu / irq_handler. */
-void irq_handler(struct hw_trapframe *hw_tf)
+void handle_irq(struct hw_trapframe *hw_tf)
 {
        struct per_cpu_info *pcpui = &per_cpu_info[core_id()];
+       struct irq_handler *irq_h;
        /* Copy out the TF for now */
        if (!in_kernel(hw_tf))
                set_current_ctx_hw(pcpui, hw_tf);
@@ -454,27 +473,26 @@ void irq_handler(struct hw_trapframe *hw_tf)
        /* Coupled with cpu_halt() and smp_idle() */
        abort_halt(hw_tf);
        //if (core_id())
+       if (hw_tf->tf_trapno != IdtLAPIC_TIMER) /* timer irq */
+       if (hw_tf->tf_trapno != 255) /* kmsg */
+       if (hw_tf->tf_trapno != 36)     /* serial */
                printd("Incoming IRQ, ISR: %d on core %d\n", hw_tf->tf_trapno,
                       core_id());
-       if (check_spurious_irq(hw_tf->tf_trapno))
+       /* TODO: RCU read lock */
+       irq_h = irq_handlers[hw_tf->tf_trapno];
+       if (!irq_h || irq_h->check_spurious(hw_tf->tf_trapno))
                goto out_no_eoi;
-       /* Send the EOI.  This means the PIC/LAPIC can send us the same IRQ vector,
-        * and we'll handle it as soon as we reenable IRQs.  This does *not* mean
-        * the hardware device that triggered the IRQ had its IRQ reset.  This does
-        * mean we shouldn't enable irqs in a handler that isn't reentrant. */
-       assert(hw_tf->tf_trapno >= 32);
-       send_eoi(hw_tf->tf_trapno);
-
-       extern handler_wrapper_t (RO 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[hw_tf->tf_trapno].isr != 0)
-               handler_tbl[hw_tf->tf_trapno].isr(hw_tf,
-                                                 handler_tbl[hw_tf->tf_trapno].data);
+       while (irq_h) {
+               irq_h->isr(hw_tf, irq_h->data);
+               irq_h = irq_h->next;
+       }
        // if we're a general purpose IPI function call, down the cpu_list
+       extern handler_wrapper_t handler_wrappers[NUM_HANDLER_WRAPPERS];
        if ((I_SMP_CALL0 <= hw_tf->tf_trapno) &&
            (hw_tf->tf_trapno <= I_SMP_CALL_LAST))
                down_checklist(handler_wrappers[hw_tf->tf_trapno & 0x0f].cpu_list);
+       /* Keep in sync with ipi_is_pending */
+       irq_handlers[hw_tf->tf_trapno]->eoi(hw_tf->tf_trapno);
        /* Fall-through */
 out_no_eoi:
        dec_irq_depth(pcpui);
@@ -487,14 +505,56 @@ out_no_eoi:
        assert(0);
 }
 
-void
-register_interrupt_handler(handler_t TP(TV(t)) table[],
-                           uint8_t int_num, poly_isr_t handler, TV(t) data)
+int register_irq(int irq, isr_t handler, void *irq_arg, uint32_t tbdf)
 {
-       table[int_num].isr = handler;
-       table[int_num].data = data;
+       struct irq_handler *irq_h;
+       int vector;
+       irq_h = kmalloc(sizeof(struct irq_handler), 0);
+       assert(irq_h);
+       irq_h->dev_irq = irq;
+       irq_h->tbdf = tbdf;
+       vector = bus_irq_enable(irq_h);
+       if (vector == -1) {
+               kfree(irq_h);
+               return -1;
+       }
+       printd("IRQ %d, vector %d, type %s\n", irq, vector, irq_h->type);
+       assert(irq_h->check_spurious && irq_h->eoi);
+       irq_h->isr = handler;
+       irq_h->data = irq_arg;
+       irq_h->apic_vector = vector;
+       /* RCU write lock */
+       spin_lock_irqsave(&irq_handler_wlock);
+       irq_h->next = irq_handlers[vector];
+       wmb();  /* make sure irq_h is done before publishing to readers */
+       irq_handlers[vector] = irq_h;
+       spin_unlock_irqsave(&irq_handler_wlock);
+       /* might need to pass the irq_h, in case unmask needs more info */
+       if (irq_h->unmask)
+               irq_h->unmask(vector);
+       return 0;
 }
 
+/* It's a moderate pain in the ass to put these in bit-specific files (header
+ * hell with the set_current_ helpers) */
+#ifdef CONFIG_X86_64
+void sysenter_callwrapper(struct syscall *sysc, unsigned long count,
+                          struct sw_trapframe *sw_tf)
+{
+       struct per_cpu_info *pcpui = &per_cpu_info[core_id()];
+       set_current_ctx_sw(pcpui, sw_tf);
+       /* Once we've set_current_ctx, we can enable interrupts.  This used to be
+        * mandatory (we had immediate KMSGs that would muck with cur_ctx).  Now it
+        * should only help for sanity/debugging. */
+       enable_irq();
+       /* Set up and run the async calls */
+       prep_syscalls(current, sysc, count);
+       /* If you use pcpui again, reread it, since you might have migrated */
+       proc_restartcore();
+}
+
+#else
+
 /* This is called from sysenter's asm, with the tf on the kernel stack. */
 /* TODO: use a sw_tf for sysenter */
 void sysenter_callwrapper(struct hw_trapframe *hw_tf)
@@ -514,13 +574,14 @@ void sysenter_callwrapper(struct hw_trapframe *hw_tf)
        /* If you use pcpui again, reread it, since you might have migrated */
        proc_restartcore();
 }
+#endif
 
 /* Declared in x86/arch.h */
 void send_ipi(uint32_t os_coreid, uint8_t vector)
 {
        int hw_coreid = get_hw_coreid(os_coreid);
        if (hw_coreid == -1) {
-               warn("Unmapped OS coreid!\n");
+               panic("Unmapped OS coreid (OS %d)!\n", os_coreid);
                return;
        }
        __send_ipi(hw_coreid, vector);