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