x86: IDT vector realignment (XCC)
[akaros.git] / kern / arch / x86 / trap.c
1 #ifdef __SHARC__
2 #pragma nosharc
3 #define SINIT(x) x
4 #endif
5
6 #include <arch/mmu.h>
7 #include <arch/x86.h>
8 #include <arch/arch.h>
9 #include <arch/console.h>
10 #include <arch/apic.h>
11 #include <ros/common.h>
12 #include <smp.h>
13 #include <assert.h>
14 #include <pmap.h>
15 #include <trap.h>
16 #include <monitor.h>
17 #include <process.h>
18 #include <mm.h>
19 #include <stdio.h>
20 #include <slab.h>
21 #include <syscall.h>
22 #include <kdebug.h>
23 #include <kmalloc.h>
24
25 taskstate_t RO ts;
26
27 /* Interrupt descriptor table.  64 bit needs 16 byte alignment (i think). */
28 gatedesc_t __attribute__((aligned (16))) idt[256] = { { 0 } };
29 pseudodesc_t idt_pd;
30
31 /* interrupt handler table, each element is a linked list of handlers for a
32  * given IRQ.  Modification requires holding the lock (TODO: RCU) */
33 struct irq_handler *irq_handlers[NUM_IRQS];
34 spinlock_t irq_handler_wlock = SPINLOCK_INITIALIZER_IRQSAVE;
35
36 /* Which pci devices hang off of which irqs */
37 /* TODO: make this an array of SLISTs (pain from ioapic.c, etc...) */
38 struct pci_device *irq_pci_map[NUM_IRQS] = {0};
39
40 const char *x86_trapname(int trapno)
41 {
42     // zra: excnames is SREADONLY because Ivy doesn't trust const
43         static const char *NT const (RO excnames)[] = {
44                 "Divide error",
45                 "Debug",
46                 "Non-Maskable Interrupt",
47                 "Breakpoint",
48                 "Overflow",
49                 "BOUND Range Exceeded",
50                 "Invalid Opcode",
51                 "Device Not Available",
52                 "Double Fault",
53                 "Coprocessor Segment Overrun",
54                 "Invalid TSS",
55                 "Segment Not Present",
56                 "Stack Fault",
57                 "General Protection",
58                 "Page Fault",
59                 "(unknown trap)",
60                 "x87 FPU Floating-Point Error",
61                 "Alignment Check",
62                 "Machine-Check",
63                 "SIMD Floating-Point Exception"
64         };
65
66         if (trapno < sizeof(excnames)/sizeof(excnames[0]))
67                 return excnames[trapno];
68         if (trapno == T_SYSCALL)
69                 return "System call";
70         return "(unknown trap)";
71 }
72
73 /* Set stacktop for the current core to be the stack the kernel will start on
74  * when trapping/interrupting from userspace.  Don't use this til after
75  * smp_percpu_init().  We can probably get the TSS by reading the task register
76  * and then the GDT.  Still, it's a pain. */
77 void set_stack_top(uintptr_t stacktop)
78 {
79         struct per_cpu_info *pcpui = &per_cpu_info[core_id()];
80         /* No need to reload the task register, this takes effect immediately */
81         x86_set_stacktop_tss(pcpui->tss, stacktop);
82         /* Also need to make sure sysenters come in correctly */
83         x86_set_sysenter_stacktop(stacktop);
84 }
85
86 /* Note the check implies we only are on a one page stack (or the first page) */
87 uintptr_t get_stack_top(void)
88 {
89         struct per_cpu_info *pcpui = &per_cpu_info[core_id()];
90         uintptr_t stacktop;
91         /* so we can check this in interrupt handlers (before smp_boot()) */
92         /* TODO: These are dangerous - it assumes we're on a one-page stack.  If we
93          * change it to KSTKSIZE, then we assume stacks are KSTKSIZE-aligned */
94         if (!pcpui->tss)
95                 return ROUNDUP(read_sp(), PGSIZE);
96         stacktop = x86_get_stacktop_tss(pcpui->tss);
97         if (stacktop != ROUNDUP(read_sp(), PGSIZE))
98                 panic("Bad stacktop: %p esp one is %p\n", stacktop,
99                       ROUNDUP(read_sp(), PGSIZE));
100         return stacktop;
101 }
102
103 /* Sends a non-maskable interrupt; the handler will print a trapframe. */
104 void send_nmi(uint32_t os_coreid)
105 {
106         /* NMI / IPI for x86 are limited to 8 bits */
107         uint8_t hw_core = (uint8_t)get_hw_coreid(os_coreid);
108         __send_nmi(hw_core);
109 }
110
111 void idt_init(void)
112 {
113         /* This table is made in trapentry$BITS.S by each macro in that file.
114          * It is layed out such that the ith entry is the ith's traphandler's
115          * (uintptr_t) trap addr, then (uint32_t) trap number. */
116         struct trapinfo { uintptr_t trapaddr; uint32_t trapnumber; }
117                __attribute__((packed));
118         extern struct trapinfo trap_tbl[];
119         extern struct trapinfo trap_tbl_end[];
120         int i, trap_tbl_size = trap_tbl_end - trap_tbl;
121         extern void ISR_default(void);
122         extern void ISR_syscall(void);
123
124         /* set all to default, to catch everything */
125         for (i = 0; i < 256; i++)
126                 SETGATE(idt[i], 0, GD_KT, &ISR_default, 0);
127
128         /* set all entries that have real trap handlers
129          * we need to stop short of the last one, since the last is the default
130          * handler with a fake interrupt number (500) that is out of bounds of
131          * the idt[] */
132         for (i = 0; i < trap_tbl_size - 1; i++)
133                 SETGATE(idt[trap_tbl[i].trapnumber], 0, GD_KT, trap_tbl[i].trapaddr, 0);
134         /* Sanity check */
135         assert((uintptr_t)ISR_syscall ==
136                ((uintptr_t)idt[T_SYSCALL].gd_off_63_32 << 32 |
137                 (uintptr_t)idt[T_SYSCALL].gd_off_31_16 << 16 |
138                 (uintptr_t)idt[T_SYSCALL].gd_off_15_0));
139         /* turn on trap-based syscall handling and other user-accessible ints
140          * DPL 3 means this can be triggered by the int instruction */
141         idt[T_SYSCALL].gd_dpl = SINIT(3);
142         idt[T_BRKPT].gd_dpl = SINIT(3);
143
144         /* Set up our kernel stack when changing rings */
145         /* Note: we want 16 byte aligned kernel stack frames (AMD 2:8.9.3) */
146         x86_set_stacktop_tss(&ts, (uintptr_t)bootstacktop);
147         x86_sysenter_init((uintptr_t)bootstacktop);
148
149 #ifdef CONFIG_KTHREAD_POISON
150         *kstack_bottom_addr((uintptr_t)bootstacktop) = 0xdeadbeef;
151 #endif /* CONFIG_KTHREAD_POISON */
152
153         /* Initialize the TSS field of the gdt.  The size of the TSS desc differs
154          * between 64 and 32 bit, hence the pointer acrobatics */
155         syssegdesc_t *ts_slot = (syssegdesc_t*)&gdt[GD_TSS >> 3];
156         *ts_slot = (syssegdesc_t)SEG_SYS_SMALL(STS_T32A, (uintptr_t)&ts,
157                                                sizeof(taskstate_t), 0);
158
159         /* Init the IDT PD.  Need to do this before ltr for some reason.  (Doing
160          * this between ltr and lidt causes the machine to reboot... */
161         idt_pd.pd_lim = sizeof(idt) - 1;
162         idt_pd.pd_base = (uintptr_t)idt;
163
164         ltr(GD_TSS);
165
166         asm volatile("lidt %0" : : "m"(idt_pd));
167
168         pic_remap();
169         pic_mask_all();
170
171 #ifdef CONFIG_ENABLE_MPTABLES
172         int ncleft;
173         int mpsinit(int maxcores);
174
175         ncleft = mpsinit(MAX_NUM_CPUS);
176         /* NEVER printd here ... */
177         printk("mpacpi is %d\n", mpacpi(ncleft));
178
179         void ioapiconline(void);
180         void apiconline(void);
181         apiconline(); /* TODO: do this this for all cores*/
182         ioapiconline();
183 #else
184         // set LINT0 to receive ExtINTs (KVM's default).  At reset they are 0x1000.
185         write_mmreg32(LAPIC_LVT_LINT0, 0x700);
186         lapic_enable();
187         unmask_lapic_lvt(LAPIC_LVT_LINT0);
188 #endif
189
190         /* register the generic timer_interrupt() handler for the per-core timers */
191         register_raw_irq(IdtLAPIC_TIMER, timer_interrupt, NULL);
192         /* register the kernel message handler */
193         register_raw_irq(I_KERNEL_MSG, handle_kmsg_ipi, NULL);
194 }
195
196 static void handle_fperr(struct hw_trapframe *hw_tf)
197 {
198         uint16_t fpcw, fpsw;
199         uint32_t mxcsr;
200         asm volatile ("fnstcw %0" : "=m"(fpcw));
201         asm volatile ("fnstsw %0" : "=m"(fpsw));
202         asm volatile ("stmxcsr %0" : "=m"(mxcsr));
203         print_trapframe(hw_tf);
204         printk("Core %d: FP ERR, CW: 0x%04x, SW: 0x%04x, MXCSR 0x%08x\n", core_id(),
205                fpcw, fpsw, mxcsr);
206         printk("Core %d: The following faults are unmasked:\n", core_id());
207         if (fpsw & ~fpcw & FP_EXCP_IE) {
208                 printk("\tInvalid Operation: ");
209                 if (fpsw & FP_SW_SF) {
210                         if (fpsw & FP_SW_C1)
211                                 printk("Stack overflow\n");
212                         else
213                                 printk("Stack underflow\n");
214                 } else {
215                         printk("invalid arithmetic operand\n");
216                 }
217         }
218         if (fpsw & ~fpcw & FP_EXCP_DE)
219                 printk("\tDenormalized operand\n");
220         if (fpsw & ~fpcw & FP_EXCP_ZE)
221                 printk("\tDivide by zero\n");
222         if (fpsw & ~fpcw & FP_EXCP_OE)
223                 printk("\tNumeric Overflow\n");
224         if (fpsw & ~fpcw & FP_EXCP_UE)
225                 printk("\tNumeric Underflow\n");
226         if (fpsw & ~fpcw & FP_EXCP_PE)
227                 printk("\tInexact result (precision)\n");
228         printk("Killing the process.\n");
229         enable_irq();
230         proc_destroy(current);
231 }
232
233 void backtrace_kframe(struct hw_trapframe *hw_tf)
234 {
235         struct per_cpu_info *pcpui = &per_cpu_info[core_id()];
236         pcpui->__lock_checking_enabled--;
237         printk("\nBacktrace of faulting kernel context on Core %d:\n", core_id());
238         backtrace_frame(x86_get_hwtf_pc(hw_tf), x86_get_hwtf_fp(hw_tf));
239         pcpui->__lock_checking_enabled++;
240 }
241
242 static bool __handle_page_fault(struct hw_trapframe *hw_tf, unsigned long *aux)
243 {
244         uintptr_t fault_va = rcr2();
245         int prot = hw_tf->tf_err & PF_ERROR_WRITE ? PROT_WRITE : PROT_READ;
246         int err;
247
248         /* TODO - handle kernel page faults */
249         if ((hw_tf->tf_cs & 3) == 0) {
250                 print_trapframe(hw_tf);
251                 backtrace_kframe(hw_tf);
252                 panic("Page Fault in the Kernel at %p!", fault_va);
253                 /* if we want to do something like kill a process or other code, be
254                  * aware we are in a sort of irq-like context, meaning the main kernel
255                  * code we 'interrupted' could be holding locks - even irqsave locks. */
256         }
257         /* safe to reenable after rcr2 */
258         enable_irq();
259         if ((err = handle_page_fault(current, fault_va, prot))) {
260                 if (err == -EAGAIN)
261                         hw_tf->tf_err |= PF_VMR_BACKED;
262                 *aux = fault_va;
263                 return FALSE;
264                 /* useful debugging */
265                 printk("[%08x] user %s fault va %p ip %p on core %d with err %d\n",
266                        current->pid, prot & PROT_READ ? "READ" : "WRITE", fault_va,
267                        hw_tf->tf_rip, core_id(), err);
268                 print_trapframe(hw_tf);
269                 /* Turn this on to help debug bad function pointers */
270 #ifdef CONFIG_X86_64
271                 printd("rsp %p\n\t 0(rsp): %p\n\t 8(rsp): %p\n\t 16(rsp): %p\n"
272                        "\t24(rsp): %p\n", hw_tf->tf_rsp,
273                        *(uintptr_t*)(hw_tf->tf_rsp +  0),
274                        *(uintptr_t*)(hw_tf->tf_rsp +  8),
275                        *(uintptr_t*)(hw_tf->tf_rsp + 16),
276                        *(uintptr_t*)(hw_tf->tf_rsp + 24));
277 #else
278                 printd("esp %p\n\t 0(esp): %p\n\t 4(esp): %p\n\t 8(esp): %p\n"
279                        "\t12(esp): %p\n", hw_tf->tf_esp,
280                        *(uintptr_t*)(hw_tf->tf_esp +  0),
281                        *(uintptr_t*)(hw_tf->tf_esp +  4),
282                        *(uintptr_t*)(hw_tf->tf_esp +  8),
283                        *(uintptr_t*)(hw_tf->tf_esp + 12));
284 #endif
285         }
286         return TRUE;
287 }
288
289 /* Certain traps want IRQs enabled, such as the syscall.  Others can't handle
290  * it, like the page fault handler.  Turn them on on a case-by-case basis. */
291 static void trap_dispatch(struct hw_trapframe *hw_tf)
292 {
293         struct per_cpu_info *pcpui;
294         bool handled = TRUE;
295         unsigned long aux = 0;
296         // Handle processor exceptions.
297         switch(hw_tf->tf_trapno) {
298                 case T_NMI:
299                         /* Temporarily disable deadlock detection when we print.  We could
300                          * deadlock if we were printing when we NMIed. */
301                         pcpui = &per_cpu_info[core_id()];
302                         pcpui->__lock_checking_enabled--;
303                         /* This is a bit hacky, but we don't have a decent API yet */
304                         extern bool mon_verbose_trace;
305                         if (mon_verbose_trace) {
306                                 print_trapframe(hw_tf);
307                                 backtrace_kframe(hw_tf);
308                         }
309                         char *fn_name = get_fn_name(x86_get_ip_hw(hw_tf));
310                         printk("Core %d is at %p (%s)\n", core_id(), x86_get_ip_hw(hw_tf),
311                                fn_name);
312                         kfree(fn_name);
313                         print_kmsgs(core_id());
314                         pcpui->__lock_checking_enabled++;
315                         break;
316                 case T_BRKPT:
317                         enable_irq();
318                         monitor(hw_tf);
319                         break;
320                 case T_ILLOP:
321                 {
322                         /* TODO: this can PF if there is a concurrent unmap/PM removal. */
323                         uintptr_t ip = x86_get_ip_hw(hw_tf);
324                         pcpui = &per_cpu_info[core_id()];
325                         pcpui->__lock_checking_enabled--;               /* for print debugging */
326                         /* We will muck with the actual TF.  If we're dealing with
327                          * userspace, we need to make sure we edit the actual TF that will
328                          * get restarted (pcpui), and not the TF on the kstack (which aren't
329                          * the same).  See set_current_ctx() for more info. */
330                         if (!in_kernel(hw_tf))
331                                 hw_tf = &pcpui->cur_ctx->tf.hw_tf;
332                         printd("bad opcode, eip: %p, next 3 bytes: %x %x %x\n", ip, 
333                                *(uint8_t*)(ip + 0), 
334                                *(uint8_t*)(ip + 1), 
335                                *(uint8_t*)(ip + 2)); 
336                         /* rdtscp: 0f 01 f9 */
337                         if (*(uint8_t*)(ip + 0) == 0x0f, 
338                             *(uint8_t*)(ip + 1) == 0x01, 
339                             *(uint8_t*)(ip + 2) == 0xf9) {
340                                 x86_fake_rdtscp(hw_tf);
341                                 pcpui->__lock_checking_enabled++;       /* for print debugging */
342                                 return;
343                         }
344                         enable_irq();
345                         monitor(hw_tf);
346                         pcpui->__lock_checking_enabled++;               /* for print debugging */
347                         break;
348                 }
349                 case T_PGFLT:
350                         handled = __handle_page_fault(hw_tf, &aux);
351                         break;
352                 case T_FPERR:
353                         handle_fperr(hw_tf);
354                         break;
355                 case T_SYSCALL:
356                         enable_irq();
357                         // check for userspace, for now
358                         assert(hw_tf->tf_cs != GD_KT);
359                         /* Set up and run the async calls */
360                         /* TODO: this is using the wrong reg1 for traps for 32 bit */
361                         prep_syscalls(current,
362                                       (struct syscall*)x86_get_systrap_arg0(hw_tf),
363                                                   (unsigned int)x86_get_systrap_arg1(hw_tf));
364                         break;
365                 default:
366                         if (hw_tf->tf_cs == GD_KT) {
367                                 print_trapframe(hw_tf);
368                                 panic("Damn Damn!  Unhandled trap in the kernel!");
369                         } else {
370                                 handled = FALSE;
371                         }
372         }
373         if (!handled)
374                 reflect_unhandled_trap(hw_tf->tf_trapno, hw_tf->tf_err, aux);
375 }
376
377 /* Helper.  For now, this copies out the TF to pcpui.  Eventually, we should
378  * consider doing this in trapentry.S
379  *
380  * TODO: consider having this return the tf used, so we can set tf in trap and
381  * irq handlers to edit the TF that will get restarted.  Right now, the kernel
382  * uses and restarts tf, but userspace restarts the old pcpui tf.  It is
383  * tempting to do this, but note that tf stays on the stack of the kthread,
384  * while pcpui->cur_ctx is for the core we trapped in on.  Meaning if we ever
385  * block, suddenly cur_ctx is pointing to some old clobbered state that was
386  * already returned to and can't be trusted.  Meanwhile tf can always be trusted
387  * (like with an in_kernel() check).  The only types of traps from the user that
388  * can be expected to have editable trapframes are ones that don't block. */
389 static void set_current_ctx_hw(struct per_cpu_info *pcpui,
390                                struct hw_trapframe *hw_tf)
391 {
392         assert(!irq_is_enabled());
393         assert(!pcpui->cur_ctx);
394         pcpui->actual_ctx.type = ROS_HW_CTX;
395         pcpui->actual_ctx.tf.hw_tf = *hw_tf;
396         pcpui->cur_ctx = &pcpui->actual_ctx;
397 }
398
399 static void set_current_ctx_sw(struct per_cpu_info *pcpui,
400                                struct sw_trapframe *sw_tf)
401 {
402         assert(!irq_is_enabled());
403         assert(!pcpui->cur_ctx);
404         pcpui->actual_ctx.type = ROS_SW_CTX;
405         pcpui->actual_ctx.tf.sw_tf = *sw_tf;
406         pcpui->cur_ctx = &pcpui->actual_ctx;
407 }
408
409 /* If the interrupt interrupted a halt, we advance past it.  Made to work with
410  * x86's custom cpu_halt() in arch/arch.h.  Note this nearly never gets called.
411  * I needed to insert exactly one 'nop' in cpu_halt() (that isn't there now) to
412  * get the interrupt to trip on the hlt, o/w the hlt will execute before the
413  * interrupt arrives (even with a pending interrupt that should hit right after
414  * an interrupt_enable (sti)).  This was on the i7. */
415 static void abort_halt(struct hw_trapframe *hw_tf)
416 {
417         /* Don't care about user TFs.  Incidentally, dereferencing user EIPs is
418          * reading userspace memory, which can be dangerous.  It can page fault,
419          * like immediately after a fork (which doesn't populate the pages). */
420         if (!in_kernel(hw_tf))
421                 return;
422         /* the halt instruction in is 0xf4, and it's size is 1 byte */
423         if (*(uint8_t*)x86_get_ip_hw(hw_tf) == 0xf4)
424                 x86_advance_ip(hw_tf, 1);
425 }
426
427 void trap(struct hw_trapframe *hw_tf)
428 {
429         struct per_cpu_info *pcpui = &per_cpu_info[core_id()];
430         /* Copy out the TF for now */
431         if (!in_kernel(hw_tf))
432                 set_current_ctx_hw(pcpui, hw_tf);
433         else
434                 inc_ktrap_depth(pcpui);
435
436         printd("Incoming TRAP %d on core %d, TF at %p\n", hw_tf->tf_trapno,
437                core_id(), hw_tf);
438         if ((hw_tf->tf_cs & ~3) != GD_UT && (hw_tf->tf_cs & ~3) != GD_KT) {
439                 print_trapframe(hw_tf);
440                 panic("Trapframe with invalid CS!");
441         }
442         trap_dispatch(hw_tf);
443         /* Return to the current process, which should be runnable.  If we're the
444          * kernel, we should just return naturally.  Note that current and tf need
445          * to still be okay (might not be after blocking) */
446         if (in_kernel(hw_tf)) {
447                 dec_ktrap_depth(pcpui);
448                 return;
449         }
450         proc_restartcore();
451         assert(0);
452 }
453
454 /* Note IRQs are disabled unless explicitly turned on.
455  *
456  * In general, we should only get trapno's >= PIC1_OFFSET (32).  Anything else
457  * should be a trap.  Even if we don't use the PIC, that should be the standard.
458  * It is possible to get a spurious LAPIC IRQ with vector 15 (or similar), but
459  * the spurious check should catch that.
460  *
461  * Note that from hardware's perspective (PIC, etc), IRQs start from 0, but they
462  * are all mapped up at PIC1_OFFSET for the cpu / irq_handler. */
463 void handle_irq(struct hw_trapframe *hw_tf)
464 {
465         struct per_cpu_info *pcpui = &per_cpu_info[core_id()];
466         struct irq_handler *irq_h;
467         /* Copy out the TF for now */
468         if (!in_kernel(hw_tf))
469                 set_current_ctx_hw(pcpui, hw_tf);
470         inc_irq_depth(pcpui);
471         /* Coupled with cpu_halt() and smp_idle() */
472         abort_halt(hw_tf);
473         //if (core_id())
474         if (hw_tf->tf_trapno != IdtLAPIC_TIMER) /* timer irq */
475         if (hw_tf->tf_trapno != 255) /* kmsg */
476         if (hw_tf->tf_trapno != 36)     /* serial */
477                 printd("Incoming IRQ, ISR: %d on core %d\n", hw_tf->tf_trapno,
478                        core_id());
479         /* TODO: RCU read lock */
480         irq_h = irq_handlers[hw_tf->tf_trapno];
481         if (!irq_h || irq_h->check_spurious(hw_tf->tf_trapno))
482                 goto out_no_eoi;
483         while (irq_h) {
484                 irq_h->isr(hw_tf, irq_h->data);
485                 irq_h = irq_h->next;
486         }
487         // if we're a general purpose IPI function call, down the cpu_list
488         extern handler_wrapper_t handler_wrappers[NUM_HANDLER_WRAPPERS];
489         if ((I_SMP_CALL0 <= hw_tf->tf_trapno) &&
490             (hw_tf->tf_trapno <= I_SMP_CALL_LAST))
491                 down_checklist(handler_wrappers[hw_tf->tf_trapno & 0x0f].cpu_list);
492         /* Keep in sync with ipi_is_pending */
493         irq_handlers[hw_tf->tf_trapno]->eoi(hw_tf->tf_trapno);
494         /* Fall-through */
495 out_no_eoi:
496         dec_irq_depth(pcpui);
497         /* Return to the current process, which should be runnable.  If we're the
498          * kernel, we should just return naturally.  Note that current and tf need
499          * to still be okay (might not be after blocking) */
500         if (in_kernel(hw_tf))
501                 return;
502         proc_restartcore();
503         assert(0);
504 }
505
506 /* Tells us if an interrupt (trap_nr) came from the PIC or not */
507 static bool irq_from_pic(uint32_t trap_nr)
508 {
509         /* The 16 IRQs within the range [PIC1_OFFSET, PIC1_OFFSET + 15] came from
510          * the PIC.  [32-47] */
511         if (trap_nr < PIC1_OFFSET)
512                 return FALSE;
513         if (trap_nr > PIC1_OFFSET + 15)
514                 return FALSE;
515         return TRUE;
516 }
517
518 /* TODO: remove the distinction btw raw and device IRQs */
519 void register_raw_irq(unsigned int vector, isr_t handler, void *data)
520 {
521         struct irq_handler *irq_h;
522         irq_h = kmalloc(sizeof(struct irq_handler), 0);
523         assert(irq_h);
524         spin_lock_irqsave(&irq_handler_wlock);
525         irq_h->isr = handler;
526         irq_h->data = data;
527         /* TODO: better way to sort out LAPIC vs PIC */
528         if (irq_from_pic(vector)) {
529                 irq_h->check_spurious = pic_check_spurious;
530                 irq_h->eoi = pic_send_eoi;
531                 irq_h->mask = pic_mask_irq;
532                 irq_h->unmask = pic_unmask_irq;
533                 irq_h->type = "pic";
534         } else {
535                 irq_h->check_spurious = lapic_check_spurious;
536                 irq_h->eoi = lapic_send_eoi;
537                 /* TODO: which mask we pick also depends on source: IOAPIC, LINT, etc */
538                 irq_h->mask = 0;
539                 irq_h->unmask = 0;
540                 irq_h->type = "lapic";
541         }
542         irq_h->apic_vector = vector;
543         irq_h->next = irq_handlers[vector];
544         wmb();  /* make sure irq_h is done before publishing to readers */
545         irq_handlers[vector] = irq_h;
546         spin_unlock_irqsave(&irq_handler_wlock);
547 }
548
549 void unregister_raw_irq(unsigned int vector, isr_t handler, void *data)
550 {
551         /* TODO: RCU */
552         printk("Unregistering not supported\n");
553 }
554
555 /* The devno is arbitrary data. Normally, however, it will be a
556  * PCI type-bus-dev.func. It is required for ioapics.
557  */
558 int register_dev_irq(int irq, isr_t handler, void *irq_arg, uint32_t tbdf)
559 {
560         /* TODO: whenever we sort out the ACPI/IOAPIC business, we'll probably want
561          * a helper to reroute an irq? */
562 #ifdef CONFIG_ENABLE_MPTABLES
563         /* TODO: dirty hack to get the IOAPIC vector */
564 extern int intrenable(int irq, void (*f) (void *, void *), void *a, int tbdf);
565 int x = intrenable(irq, handler, irq_arg, tbdf);
566         if (x > 0)
567                 register_raw_irq(x, handler, irq_arg);
568 #else
569         register_raw_irq(irq + IdtPIC, handler, irq_arg);
570         pic_unmask_irq(irq + IdtPIC);
571 #endif
572         return 0;
573 }
574
575 /* It's a moderate pain in the ass to put these in bit-specific files (header
576  * hell with the set_current_ helpers) */
577 #ifdef CONFIG_X86_64
578 void sysenter_callwrapper(struct syscall *sysc, unsigned long count,
579                           struct sw_trapframe *sw_tf)
580 {
581         struct per_cpu_info *pcpui = &per_cpu_info[core_id()];
582         set_current_ctx_sw(pcpui, sw_tf);
583         /* Once we've set_current_ctx, we can enable interrupts.  This used to be
584          * mandatory (we had immediate KMSGs that would muck with cur_ctx).  Now it
585          * should only help for sanity/debugging. */
586         enable_irq();
587         /* Set up and run the async calls */
588         prep_syscalls(current, sysc, count);
589         /* If you use pcpui again, reread it, since you might have migrated */
590         proc_restartcore();
591 }
592
593 #else
594
595 /* This is called from sysenter's asm, with the tf on the kernel stack. */
596 /* TODO: use a sw_tf for sysenter */
597 void sysenter_callwrapper(struct hw_trapframe *hw_tf)
598 {
599         struct per_cpu_info *pcpui = &per_cpu_info[core_id()];
600         assert(!in_kernel(hw_tf));
601         set_current_ctx_hw(pcpui, hw_tf);
602         /* Once we've set_current_ctx, we can enable interrupts.  This used to be
603          * mandatory (we had immediate KMSGs that would muck with cur_ctx).  Now it
604          * should only help for sanity/debugging. */
605         enable_irq();
606
607         /* Set up and run the async calls */
608         prep_syscalls(current,
609                                   (struct syscall*)x86_get_sysenter_arg0(hw_tf),
610                                   (unsigned int)x86_get_sysenter_arg1(hw_tf));
611         /* If you use pcpui again, reread it, since you might have migrated */
612         proc_restartcore();
613 }
614 #endif
615
616 /* Declared in x86/arch.h */
617 void send_ipi(uint32_t os_coreid, uint8_t vector)
618 {
619         int hw_coreid = get_hw_coreid(os_coreid);
620         if (hw_coreid == -1) {
621                 panic("Unmapped OS coreid (OS %d)!\n", os_coreid);
622                 return;
623         }
624         __send_ipi(hw_coreid, vector);
625 }