0413dd5fe36913e037a3f33401703ff1e92c02fa
[akaros.git] / kern / arch / i686 / 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.  (Must be built at run time because
28  * shifted function addresses can't be represented in relocation records.)
29  */
30 // Aligned on an 8 byte boundary (SDM V3A 5-13)
31 gatedesc_t __attribute__ ((aligned (8))) (RO idt)[256] = { { 0 } };
32 pseudodesc_t RO idt_pd = {
33         sizeof(idt) - 1, (uint32_t) idt
34 };
35
36 /* global handler table, used by core0 (for now).  allows the registration
37  * of functions to be called when servicing an interrupt.  other cores
38  * can set up their own later.
39  */
40 #ifdef __IVY__
41 #pragma cilnoremove("iht_lock")
42 #endif
43 spinlock_t iht_lock;
44 handler_t TP(TV(t)) LCKD(&iht_lock) (RO interrupt_handlers)[NUM_INTERRUPT_HANDLERS];
45
46 static const char *NTS trapname(int trapno)
47 {
48     // zra: excnames is SREADONLY because Ivy doesn't trust const
49         static const char *NT const (RO excnames)[] = {
50                 "Divide error",
51                 "Debug",
52                 "Non-Maskable Interrupt",
53                 "Breakpoint",
54                 "Overflow",
55                 "BOUND Range Exceeded",
56                 "Invalid Opcode",
57                 "Device Not Available",
58                 "Double Fault",
59                 "Coprocessor Segment Overrun",
60                 "Invalid TSS",
61                 "Segment Not Present",
62                 "Stack Fault",
63                 "General Protection",
64                 "Page Fault",
65                 "(unknown trap)",
66                 "x87 FPU Floating-Point Error",
67                 "Alignment Check",
68                 "Machine-Check",
69                 "SIMD Floating-Point Exception"
70         };
71
72         if (trapno < sizeof(excnames)/sizeof(excnames[0]))
73                 return excnames[trapno];
74         if (trapno == T_SYSCALL)
75                 return "System call";
76         return "(unknown trap)";
77 }
78
79 /* Set stacktop for the current core to be the stack the kernel will start on
80  * when trapping/interrupting from userspace.  Don't use this til after
81  * smp_percpu_init().  We can probably get the TSS by reading the task register
82  * and then the GDT.  Still, it's a pain. */
83 void set_stack_top(uintptr_t stacktop)
84 {
85         struct per_cpu_info *pcpu = &per_cpu_info[core_id()];
86         /* No need to reload the task register, this takes effect immediately */
87         pcpu->tss->ts_esp0 = stacktop;
88         /* Also need to make sure sysenters come in correctly */
89         write_msr(MSR_IA32_SYSENTER_ESP, stacktop);
90 }
91
92 /* Note the check implies we only are on a one page stack (or the first page) */
93 uintptr_t get_stack_top(void)
94 {
95         struct per_cpu_info *pcpui = &per_cpu_info[core_id()];
96         uintptr_t stacktop;
97         /* so we can check this in interrupt handlers (before smp_boot()) */
98         if (!pcpui->tss)
99                 return ROUNDUP(read_esp(), PGSIZE);
100         stacktop = pcpui->tss->ts_esp0;
101         if (stacktop != ROUNDUP(read_esp(), PGSIZE))
102                 panic("Bad stacktop: %08p esp one is %08p\n", stacktop,
103                       ROUNDUP(read_esp(), PGSIZE));
104         return stacktop;
105 }
106
107 /* Starts running the current TF, just using ret. */
108 void pop_kernel_tf(struct trapframe *tf)
109 {
110         asm volatile ("movl %1,%%esp;           " /* move to future stack */
111                       "pushl %2;                " /* push cs */
112                       "movl %0,%%esp;           " /* move to TF */
113                       "addl $0x20,%%esp;        " /* move to tf_gs slot */
114                       "movl %1,(%%esp);         " /* write future esp */
115                       "subl $0x20,%%esp;        " /* move back to tf start */
116                       "popal;                   " /* restore regs */
117                       "popl %%esp;              " /* set stack ptr */
118                       "subl $0x4,%%esp;         " /* jump down past CS */
119                       "ret                      " /* return to the EIP */
120                       :
121                       : "g"(tf), "r"(tf->tf_esp), "r"(tf->tf_eip) : "memory");
122         panic("ret failed");                            /* mostly to placate your mom */
123 }
124
125 /* Sends a non-maskable interrupt; the handler will print a trapframe. */
126 void send_nmi(uint32_t os_coreid)
127 {
128         /* NMI / IPI for x86 are limited to 8 bits */
129         uint8_t hw_core = (uint8_t)get_hw_coreid(os_coreid);
130         __send_nmi(hw_core);
131 }
132
133 void idt_init(void)
134 {
135         extern segdesc_t (RO gdt)[];
136
137         // This table is made in trapentry.S by each macro in that file.
138         // It is layed out such that the ith entry is the ith's traphandler's
139         // (uint32_t) trap addr, then (uint32_t) trap number
140         struct trapinfo { uint32_t trapaddr; uint32_t trapnumber; };
141         extern struct trapinfo (BND(__this,trap_tbl_end) RO trap_tbl)[];
142         extern struct trapinfo (SNT RO trap_tbl_end)[];
143         int i, trap_tbl_size = trap_tbl_end - trap_tbl;
144         extern void ISR_default(void);
145
146         // set all to default, to catch everything
147         for(i = 0; i < 256; i++)
148                 ROSETGATE(idt[i], 0, GD_KT, &ISR_default, 0);
149
150         // set all entries that have real trap handlers
151         // we need to stop short of the last one, since the last is the default
152         // handler with a fake interrupt number (500) that is out of bounds of
153         // the idt[]
154         // if we set these to trap gates, be sure to handle the IRQs separately
155         // and we might need to break our pretty tables
156         for(i = 0; i < trap_tbl_size - 1; i++)
157                 ROSETGATE(idt[trap_tbl[i].trapnumber], 0, GD_KT, trap_tbl[i].trapaddr, 0);
158
159         // turn on syscall handling and other user-accessible ints
160         // DPL 3 means this can be triggered by the int instruction
161         // STS_TG32 sets the IDT type to a Trap Gate (interrupts enabled)
162         idt[T_SYSCALL].gd_dpl = SINIT(3);
163         idt[T_SYSCALL].gd_type = SINIT(STS_TG32);
164         idt[T_BRKPT].gd_dpl = SINIT(3);
165
166         /* Setup a TSS so that we get the right stack when we trap to the kernel. */
167         ts.ts_esp0 = (uintptr_t)bootstacktop;
168         ts.ts_ss0 = SINIT(GD_KD);
169 #ifdef __CONFIG_KTHREAD_POISON__
170         /* TODO: KTHR-STACK */
171         uintptr_t *poison = (uintptr_t*)ROUNDDOWN(bootstacktop - 1, PGSIZE);
172         *poison = 0xdeadbeef;
173 #endif /* __CONFIG_KTHREAD_POISON__ */
174
175         // Initialize the TSS field of the gdt.
176         SEG16ROINIT(gdt[GD_TSS >> 3],STS_T32A, (uint32_t)(&ts),sizeof(taskstate_t),0);
177         //gdt[GD_TSS >> 3] = (segdesc_t)SEG16(STS_T32A, (uint32_t) (&ts),
178         //                                 sizeof(taskstate_t), 0);
179         gdt[GD_TSS >> 3].sd_s = SINIT(0);
180
181         // Load the TSS
182         ltr(GD_TSS);
183
184         // Load the IDT
185         asm volatile("lidt idt_pd");
186
187         // This will go away when we start using the IOAPIC properly
188         pic_remap();
189         // set LINT0 to receive ExtINTs (KVM's default).  At reset they are 0x1000.
190         write_mmreg32(LAPIC_LVT_LINT0, 0x700);
191         // mask it to shut it up for now
192         mask_lapic_lvt(LAPIC_LVT_LINT0);
193         // and turn it on
194         lapic_enable();
195         /* register the generic timer_interrupt() handler for the per-core timers */
196         register_interrupt_handler(interrupt_handlers, LAPIC_TIMER_DEFAULT_VECTOR,
197                                    timer_interrupt, NULL);
198 }
199
200 void
201 print_regs(push_regs_t *regs)
202 {
203         cprintf("  edi  0x%08x\n", regs->reg_edi);
204         cprintf("  esi  0x%08x\n", regs->reg_esi);
205         cprintf("  ebp  0x%08x\n", regs->reg_ebp);
206         cprintf("  oesp 0x%08x\n", regs->reg_oesp);
207         cprintf("  ebx  0x%08x\n", regs->reg_ebx);
208         cprintf("  edx  0x%08x\n", regs->reg_edx);
209         cprintf("  ecx  0x%08x\n", regs->reg_ecx);
210         cprintf("  eax  0x%08x\n", regs->reg_eax);
211 }
212
213 void
214 print_trapframe(trapframe_t *tf)
215 {
216         static spinlock_t ptf_lock;
217
218         spin_lock_irqsave(&ptf_lock);
219         printk("TRAP frame at %p on core %d\n", tf, core_id());
220         print_regs(&tf->tf_regs);
221         printk("  gs   0x----%04x\n", tf->tf_gs);
222         printk("  fs   0x----%04x\n", tf->tf_fs);
223         printk("  es   0x----%04x\n", tf->tf_es);
224         printk("  ds   0x----%04x\n", tf->tf_ds);
225         printk("  trap 0x%08x %s\n", tf->tf_trapno, trapname(tf->tf_trapno));
226         printk("  err  0x%08x\n", tf->tf_err);
227         printk("  eip  0x%08x\n", tf->tf_eip);
228         printk("  cs   0x----%04x\n", tf->tf_cs);
229         printk("  flag 0x%08x\n", tf->tf_eflags);
230         /* Prevents us from thinking these mean something for nested interrupts. */
231         if (tf->tf_cs != GD_KT) {
232                 printk("  esp  0x%08x\n", tf->tf_esp);
233                 printk("  ss   0x----%04x\n", tf->tf_ss);
234         }
235         spin_unlock_irqsave(&ptf_lock);
236 }
237
238 static void trap_dispatch(struct trapframe *tf)
239 {
240         // Handle processor exceptions.
241         switch(tf->tf_trapno) {
242                 case T_NMI:
243                         print_trapframe(tf);
244                         char *fn_name = get_fn_name(tf->tf_eip);
245                         printk("Core %d is at %08p (%s)\n", core_id(), tf->tf_eip, fn_name);
246                         kfree(fn_name);
247                         break;
248                 case T_BRKPT:
249                         monitor(tf);
250                         break;
251                 case T_PGFLT:
252                         page_fault_handler(tf);
253                         break;
254                 case T_SYSCALL:
255                         // check for userspace, for now
256                         assert(tf->tf_cs != GD_KT);
257                         /* Set up and run the async calls */
258                         prep_syscalls(current, (struct syscall*)tf->tf_regs.reg_eax,
259                                       tf->tf_regs.reg_edx);
260                         break;
261                 default:
262                         // Unexpected trap: The user process or the kernel has a bug.
263                         print_trapframe(tf);
264                         if (tf->tf_cs == GD_KT)
265                                 panic("Damn Damn!  Unhandled trap in the kernel!");
266                         else {
267                                 warn("Unexpected trap from userspace");
268                                 proc_incref(current, 1);
269                                 proc_destroy(current);
270                                 assert(0);
271                                 return;
272                         }
273         }
274         return;
275 }
276
277 void
278 env_push_ancillary_state(env_t* e)
279 {
280         // TODO: (HSS) handle silly state (don't really want this per-process)
281         // Here's where you'll save FP/MMX/XMM regs
282 }
283
284 void
285 env_pop_ancillary_state(env_t* e)
286 {
287         // Here's where you'll restore FP/MMX/XMM regs
288 }
289
290 /* Helper.  For now, this copies out the TF to pcpui, and sets the tf to use it.
291  * Eventually, we ought to do this in trapentry.S */
292 static void set_current_tf(struct per_cpu_info *pcpui, struct trapframe **tf)
293 {
294         pcpui->actual_tf = **tf;
295         pcpui->cur_tf = &pcpui->actual_tf;
296         *tf = &pcpui->actual_tf;
297 }
298
299 void trap(struct trapframe *tf)
300 {
301         struct per_cpu_info *pcpui = &per_cpu_info[core_id()];
302         /* Copy out the TF for now, set tf to point to it.  */
303         if (!in_kernel(tf))
304                 set_current_tf(pcpui, &tf);
305
306         printd("Incoming TRAP %d on core %d, TF at %p\n", tf->tf_trapno, core_id(),
307                tf);
308         if ((tf->tf_cs & ~3) != GD_UT && (tf->tf_cs & ~3) != GD_KT) {
309                 print_trapframe(tf);
310                 panic("Trapframe with invalid CS!");
311         }
312         trap_dispatch(tf);
313         /* Return to the current process, which should be runnable.  If we're the
314          * kernel, we should just return naturally.  Note that current and tf need
315          * to still be okay (might not be after blocking) */
316         if (in_kernel(tf))
317                 return; /* TODO: think about this, might want a helper instead. */
318         proc_restartcore();
319         assert(0);
320 }
321
322 void irq_handler(struct trapframe *tf)
323 {
324         struct per_cpu_info *pcpui = &per_cpu_info[core_id()];
325         /* Copy out the TF for now, set tf to point to it. */
326         if (!in_kernel(tf))
327                 set_current_tf(pcpui, &tf);
328
329         //if (core_id())
330                 printd("Incoming IRQ, ISR: %d on core %d\n", tf->tf_trapno, core_id());
331
332         extern handler_wrapper_t (RO handler_wrappers)[NUM_HANDLER_WRAPPERS];
333
334         // determine the interrupt handler table to use.  for now, pick the global
335         handler_t TP(TV(t)) LCKD(&iht_lock) * handler_tbl = interrupt_handlers;
336
337         if (handler_tbl[tf->tf_trapno].isr != 0)
338                 handler_tbl[tf->tf_trapno].isr(tf, handler_tbl[tf->tf_trapno].data);
339         // if we're a general purpose IPI function call, down the cpu_list
340         if ((I_SMP_CALL0 <= tf->tf_trapno) && (tf->tf_trapno <= I_SMP_CALL_LAST))
341                 down_checklist(handler_wrappers[tf->tf_trapno & 0x0f].cpu_list);
342
343         // Send EOI.  might want to do this in assembly, and possibly earlier
344         // This is set up to work with an old PIC for now
345         // Convention is that all IRQs between 32 and 47 are for the PIC.
346         // All others are LAPIC (timer, IPIs, perf, non-ExtINT LINTS, etc)
347         // For now, only 235-255 are available
348         assert(tf->tf_trapno >= 32); // slows us down, but we should never have this
349
350 #ifdef __CONFIG_ENABLE_MPTABLES__
351         /* TODO: this should be for any IOAPIC EOI, not just MPTABLES */
352         lapic_send_eoi();
353 #else
354         //Old PIC relatd code. Should be gone for good, but leaving it just incase.
355         if (tf->tf_trapno < 48)
356                 pic_send_eoi(tf->tf_trapno - PIC1_OFFSET);
357         else
358                 lapic_send_eoi();
359 #endif
360         /* Return to the current process, which should be runnable.  If we're the
361          * kernel, we should just return naturally.  Note that current and tf need
362          * to still be okay (might not be after blocking) */
363         if (in_kernel(tf))
364                 return; /* TODO: think about this, might want a helper instead. */
365         proc_restartcore();
366         assert(0);
367 }
368
369 void
370 register_interrupt_handler(handler_t TP(TV(t)) table[],
371                            uint8_t int_num, poly_isr_t handler, TV(t) data)
372 {
373         table[int_num].isr = handler;
374         table[int_num].data = data;
375 }
376
377 void page_fault_handler(struct trapframe *tf)
378 {
379         uint32_t fault_va = rcr2();
380         int prot = tf->tf_err & PF_ERROR_WRITE ? PROT_WRITE : PROT_READ;
381         int err;
382
383         /* TODO - handle kernel page faults */
384         if ((tf->tf_cs & 3) == 0) {
385                 print_trapframe(tf);
386                 panic("Page Fault in the Kernel at 0x%08x!", fault_va);
387         }
388         if ((err = handle_page_fault(current, fault_va, prot))) {
389                 /* Destroy the faulting process */
390                 printk("[%08x] user %s fault va %08x ip %08x on core %d with err %d\n",
391                        current->pid, prot & PROT_READ ? "READ" : "WRITE", fault_va,
392                        tf->tf_eip, core_id(), err);
393                 print_trapframe(tf);
394                 proc_incref(current, 1);
395                 proc_destroy(current);
396                 assert(0);
397         }
398 }
399
400 void sysenter_init(void)
401 {
402         write_msr(MSR_IA32_SYSENTER_CS, GD_KT);
403         write_msr(MSR_IA32_SYSENTER_ESP, ts.ts_esp0);
404         write_msr(MSR_IA32_SYSENTER_EIP, (uint32_t) &sysenter_handler);
405 }
406
407 /* This is called from sysenter's asm, with the tf on the kernel stack. */
408 void sysenter_callwrapper(struct trapframe *tf)
409 {
410         struct per_cpu_info *pcpui = &per_cpu_info[core_id()];
411         /* Copy out the TF for now, set tf to point to it. */
412         if (!in_kernel(tf))
413                 set_current_tf(pcpui, &tf);
414
415         if (in_kernel(tf))
416                 panic("sysenter from a kernel TF!!");
417         /* Set up and run the async calls */
418         prep_syscalls(current, (struct syscall*)tf->tf_regs.reg_eax,
419                       tf->tf_regs.reg_esi);
420         /* If you use pcpui again, reread it, since you might have migrated */
421         proc_restartcore();
422 }
423
424 struct kmem_cache *kernel_msg_cache;
425 void kernel_msg_init(void)
426 {
427         kernel_msg_cache = kmem_cache_create("kernel_msgs",
428                            sizeof(struct kernel_message), HW_CACHE_ALIGN, 0, 0, 0);
429 }
430
431 uint32_t send_kernel_message(uint32_t dst, amr_t pc, TV(a0t) arg0, TV(a1t) arg1,
432                              TV(a2t) arg2, int type)
433 {
434         kernel_message_t *k_msg;
435         assert(pc);
436         // note this will be freed on the destination core
437         k_msg = (kernel_message_t *CT(1))TC(kmem_cache_alloc(kernel_msg_cache, 0));
438         k_msg->srcid = core_id();
439         k_msg->dstid = dst;
440         k_msg->pc = pc;
441         k_msg->arg0 = arg0;
442         k_msg->arg1 = arg1;
443         k_msg->arg2 = arg2;
444         switch (type) {
445                 case KMSG_IMMEDIATE:
446                         spin_lock_irqsave(&per_cpu_info[dst].immed_amsg_lock);
447                         STAILQ_INSERT_TAIL(&per_cpu_info[dst].immed_amsgs, k_msg, link);
448                         spin_unlock_irqsave(&per_cpu_info[dst].immed_amsg_lock);
449                         break;
450                 case KMSG_ROUTINE:
451                         spin_lock_irqsave(&per_cpu_info[dst].routine_amsg_lock);
452                         STAILQ_INSERT_TAIL(&per_cpu_info[dst].routine_amsgs, k_msg, link);
453                         spin_unlock_irqsave(&per_cpu_info[dst].routine_amsg_lock);
454                         break;
455                 default:
456                         panic("Unknown type of kernel message!");
457         }
458         /* since we touched memory the other core will touch (the lock), we don't
459          * need an wmb_f() */
460         /* if we're sending a routine message locally, we don't want/need an IPI */
461         if ((dst != k_msg->srcid) || (type == KMSG_IMMEDIATE))
462                 send_ipi(get_hw_coreid(dst), I_KERNEL_MSG);
463         return 0;
464 }
465
466 /* Helper function.  Returns 0 if the list was empty. */
467 static kernel_message_t *get_next_amsg(struct kernel_msg_list *list_head,
468                                        spinlock_t *list_lock)
469 {
470         kernel_message_t *k_msg;
471         spin_lock_irqsave(list_lock);
472         k_msg = STAILQ_FIRST(list_head);
473         if (k_msg)
474                 STAILQ_REMOVE_HEAD(list_head, link);
475         spin_unlock_irqsave(list_lock);
476         return k_msg;
477 }
478
479 /* Kernel message handler.  Extensive documentation is in
480  * Documentation/kernel_messages.txt.
481  *
482  * In general: this processes immediate messages, then routine messages.
483  * Routine messages might not return (__startcore, etc), so we need to be
484  * careful about a few things.
485  *
486  * Note that all of this happens from interrupt context, and interrupts are
487  * currently disabled for this gate.  Interrupts need to be disabled so that the
488  * self-ipi doesn't preempt the execution of this kernel message. */
489 void __kernel_message(struct trapframe *tf)
490 {
491         per_cpu_info_t *myinfo = &per_cpu_info[core_id()];
492         kernel_message_t msg_cp, *k_msg;
493
494         /* Copy out the TF for now, set tf to point to it. */
495         if (!in_kernel(tf))
496                 set_current_tf(myinfo, &tf);
497
498         lapic_send_eoi();
499         while (1) { // will break out when there are no more messages
500                 /* Try to get an immediate message.  Exec and free it. */
501                 k_msg = get_next_amsg(&myinfo->immed_amsgs, &myinfo->immed_amsg_lock);
502                 if (k_msg) {
503                         assert(k_msg->pc);
504                         k_msg->pc(tf, k_msg->srcid, k_msg->arg0, k_msg->arg1, k_msg->arg2);
505                         kmem_cache_free(kernel_msg_cache, (void*)k_msg);
506                 } else { // no immediate, might be a routine
507                         if (in_kernel(tf))
508                                 return; // don't execute routine msgs if we were in the kernel
509                         k_msg = get_next_amsg(&myinfo->routine_amsgs,
510                                               &myinfo->routine_amsg_lock);
511                         if (!k_msg) // no routines either
512                                 return;
513                         /* copy in, and then free, in case we don't return */
514                         msg_cp = *k_msg;
515                         kmem_cache_free(kernel_msg_cache, (void*)k_msg);
516                         /* make sure an IPI is pending if we have more work */
517                         /* techincally, we don't need to lock when checking */
518                         if (!STAILQ_EMPTY(&myinfo->routine_amsgs) &&
519                                !ipi_is_pending(I_KERNEL_MSG))
520                                 send_self_ipi(I_KERNEL_MSG);
521                         /* Execute the kernel message */
522                         assert(msg_cp.pc);
523                         assert(msg_cp.dstid == core_id());
524                         /* TODO: when batching syscalls, this should be reread from cur_tf*/
525                         msg_cp.pc(tf, msg_cp.srcid, msg_cp.arg0, msg_cp.arg1, msg_cp.arg2);
526                 }
527         }
528         /* TODO: should this proc_restartcore, like the irq/trap paths?  Or at least
529          * take some things from __proc_startcore() (since we don't want to re-run
530          * kmsgs). */
531 }
532
533 /* Runs any outstanding routine kernel messages from within the kernel.  Will
534  * make sure immediates still run first (or when they arrive, if processing a
535  * bunch of these messages).  This will disable interrupts, and restore them to
536  * whatever state you left them. */
537 void process_routine_kmsg(struct trapframe *tf)
538 {
539         per_cpu_info_t *myinfo = &per_cpu_info[core_id()];
540         kernel_message_t msg_cp, *k_msg;
541         int8_t irq_state = 0;
542
543         disable_irqsave(&irq_state);
544         /* If we were told what our TF was, use that.  o/w, go with current_tf. */
545         tf = tf ? tf : current_tf;
546         while (1) {
547                 /* normally, we want ints disabled, so we don't have an empty self-ipi
548                  * for every routine message. (imagine a long list of routines).  But we
549                  * do want immediates to run ahead of routines.  This enabling should
550                  * work (might not in some shitty VMs).  Also note we can receive an
551                  * extra self-ipi for routine messages before we turn off irqs again.
552                  * Not a big deal, since we will process it right away. 
553                  * TODO: consider calling __kernel_message() here. */
554                 if (!STAILQ_EMPTY(&myinfo->immed_amsgs)) {
555                         enable_irq();
556                         cpu_relax();
557                         disable_irq();
558                 }
559                 k_msg = get_next_amsg(&myinfo->routine_amsgs,
560                                       &myinfo->routine_amsg_lock);
561                 if (!k_msg) {
562                         enable_irqsave(&irq_state);
563                         return;
564                 }
565                 /* copy in, and then free, in case we don't return */
566                 msg_cp = *k_msg;
567                 kmem_cache_free(kernel_msg_cache, (void*)k_msg);
568                 /* make sure an IPI is pending if we have more work */
569                 if (!STAILQ_EMPTY(&myinfo->routine_amsgs) &&
570                        !ipi_is_pending(I_KERNEL_MSG))
571                         send_self_ipi(I_KERNEL_MSG);
572                 /* Execute the kernel message */
573                 assert(msg_cp.pc);
574                 assert(msg_cp.dstid == core_id());
575                 msg_cp.pc(tf, msg_cp.srcid, msg_cp.arg0, msg_cp.arg1, msg_cp.arg2);
576         }
577 }