x86: bus_irq_enable()
[akaros.git] / kern / arch / x86 / trap.c
index 0b89652..3a0067c 100644 (file)
@@ -119,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++)
@@ -130,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);
@@ -160,18 +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_raw_irq(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_raw_irq(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)
@@ -220,11 +241,60 @@ void backtrace_kframe(struct hw_trapframe *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:
@@ -251,6 +321,7 @@ 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_checking_enabled--;               /* for print debugging */
@@ -278,7 +349,7 @@ static void trap_dispatch(struct hw_trapframe *hw_tf)
                        break;
                }
                case T_PGFLT:
-                       page_fault_handler(hw_tf);
+                       handled = __handle_page_fault(hw_tf, &aux);
                        break;
                case T_FPERR:
                        handle_fperr(hw_tf);
@@ -294,17 +365,15 @@ static void trap_dispatch(struct hw_trapframe *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
@@ -384,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() & (1 << 7))) {
-               printk("Spurious PIC1 irq!\n"); /* want to know if this happens */
-               return TRUE;
-       }
-       if ((trap_nr == PIC2_SPURIOUS) && !(pic_get_isr() & (1 << 15))) {
-               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
@@ -472,15 +473,15 @@ void handle_irq(struct hw_trapframe *hw_tf)
        /* Coupled with cpu_halt() and smp_idle() */
        abort_halt(hw_tf);
        //if (core_id())
-       if (hw_tf->tf_trapno != LAPIC_TIMER_DEFAULT_VECTOR)     /* timer irq */
+       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))
-               goto out_no_eoi;
        /* 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;
        while (irq_h) {
                irq_h->isr(hw_tf, irq_h->data);
                irq_h = irq_h->next;
@@ -491,7 +492,7 @@ void handle_irq(struct hw_trapframe *hw_tf)
            (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 */
-       send_eoi(hw_tf->tf_trapno);
+       irq_handlers[hw_tf->tf_trapno]->eoi(hw_tf->tf_trapno);
        /* Fall-through */
 out_no_eoi:
        dec_irq_depth(pcpui);
@@ -504,41 +505,33 @@ out_no_eoi:
        assert(0);
 }
 
-void register_raw_irq(unsigned int vector, isr_t handler, void *data)
+int register_irq(int irq, isr_t handler, void *irq_arg, uint32_t tbdf)
 {
        struct irq_handler *irq_h;
+       int vector;
        irq_h = kmalloc(sizeof(struct irq_handler), 0);
        assert(irq_h);
-       spin_lock_irqsave(&irq_handler_wlock);
+       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 = data;
+       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);
-}
-
-void unregister_raw_irq(unsigned int vector, isr_t handler, void *data)
-{
-       /* TODO: RCU */
-       printk("Unregistering not supported\n");
-}
-
-int register_dev_irq(int irq, isr_t handler, void *irq_arg)
-{
-       register_raw_irq(KERNEL_IRQ_OFFSET + irq, handler, irq_arg);
-
-       /* TODO: whenever we sort out the ACPI/IOAPIC business, we'll probably want
-        * a helper to reroute an irq? */
-#ifdef CONFIG_ENABLE_MPTABLES
-       /* TODO: this should be for any IOAPIC EOI, not just MPTABLES */
-       /* Just sending to core 0 for now */
-       ioapic_route_irq(irq, 0);
-#else
-       pic_unmask_irq(irq);
-       unmask_lapic_lvt(LAPIC_LVT_LINT0);
-       enable_irq();
-#endif
+       /* might need to pass the irq_h, in case unmask needs more info */
+       if (irq_h->unmask)
+               irq_h->unmask(vector);
        return 0;
 }
 
@@ -588,7 +581,7 @@ 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);