More PIC, PIT, and registration of ISRs
authorBarret Rhoden <brho@cs.berkeley.edu>
Tue, 24 Mar 2009 02:31:42 +0000 (19:31 -0700)
committerBarret Rhoden <brho@cs.berkeley.edu>
Tue, 24 Mar 2009 02:31:42 +0000 (19:31 -0700)
Additional PIC functionality (masking), ability to program the PIT, and
a mechanism to register an interrupt handler.  Changed the smp_boot to
use the new registration system.

inc/trap.h
kern/apic.c
kern/apic.h
kern/init.c
kern/trap.c
kern/trap.h
kern/trapentry.S

index 2ff289b..22a28c9 100644 (file)
@@ -27,7 +27,7 @@
 // These are arbitrarily chosen, but with care not to overlap
 // processor defined exceptions or interrupt vectors.
 #define T_SYSCALL   48         // system call
-#define T_DEFAULT   500                // catchall
+#define T_DEFAULT   0xdeadbeef         // catchall
 
 #ifndef __ASSEMBLER__
 
index 54f7ab3..c291b7a 100644 (file)
 /*
  * Remaps the Programmable Interrupt Controller to use IRQs 32-47
  * http://wiki.osdev.org/PIC
+ * Not 100% on this stuff, after looking over 
+ * http://bochs.sourceforge.net/techspec/PORTS.LST  The cascading and other 
+ * stuff might need to be in one command, and after that all we are doing
+ * is toggling masks.
  */
-void remap_pic() 
+void pic_remap() 
 {
        // start initialization
        outb(PIC1_CMD, 0x11);
@@ -32,6 +36,23 @@ void remap_pic()
        outb(PIC2_DATA, 0xff);
 }
 
+void pic_mask_irq(uint8_t irq)
+{
+       if (irq > 7)
+               outb(PIC2_DATA, inb(PIC2_DATA) | (1 << (irq - 8)));
+       else
+               outb(PIC1_DATA, inb(PIC1_DATA) | (1 << irq));
+}
+
+void pic_unmask_irq(uint8_t irq)
+{
+       if (irq > 7) {
+               outb(PIC2_DATA, inb(PIC2_DATA) & ~(1 << (irq - 8)));
+               outb(PIC1_DATA, inb(PIC1_DATA) & 0xfd); // make sure irq2 is unmasked
+       } else
+               outb(PIC1_DATA, inb(PIC1_DATA) & ~(1 << irq));
+}
+
 /*
  * Sets the LAPIC timer to go off after a certain number of ticks.  The primary
  * clock freq is actually the bus clock, so we really will need to figure out
@@ -59,3 +80,13 @@ uint32_t lapic_get_default_id(void)
        // p6 family only uses 4 bits here, and 0xf is reserved for the IOAPIC
        return (ebx & 0xFF000000) >> 24;
 }
+
+void pit_set_timer(uint32_t freq, bool periodic)
+{
+       uint32_t divisor = PIT_FREQ / freq;
+       if (divisor & 0xffff0000)
+               warn("Divisor too large!");
+       outb(0x43, 0x32 | (periodic << 2));
+       outb(0x40, divisor & 0xff);
+       outb(0x40, (divisor >> 8) & 0xff);
+}
index 8555abd..3a68b06 100644 (file)
 // IOAPIC
 #define IOAPIC_BASE                                    0xfec00000 // this is the default, can be changed
 
-void remap_pic(void);
+// PIT (Programmable Interrupt Timer)
+#define PIT_FREQ                                       1193180
+
+void pic_remap(void);
+void pic_mask_irq(uint8_t irq);
+void pic_unmask_irq(uint8_t irq);
 void lapic_set_timer(uint32_t ticks, uint8_t vector, bool periodic);
 uint32_t lapic_get_default_id(void);
+void pit_set_timer(uint32_t freq, bool periodic); // consider adding callback func
 
 static inline void pic_send_eoi(uint32_t irq);
 static inline void lapic_send_eoi(void);
index 25354d0..23aded6 100644 (file)
@@ -26,6 +26,7 @@ volatile bool smp_boot_lock = 0;
 
 static void print_cpuinfo(void);
 void smp_boot(void);
+void smp_boot_handler(struct Trapframe *tf);
 
 void kernel_init(multiboot_info_t *mboot_info)
 {
@@ -73,6 +74,7 @@ void kernel_init(multiboot_info_t *mboot_info)
 void smp_boot(void)
 {
        struct Page* smp_stack;
+       extern isr_t interrupt_handlers[];
        // NEED TO GRAB A LOWMEM FREE PAGE FOR AP BOOTUP CODE
        // page1 (2nd page) is reserved, hardcoded in pmap.c
        extern smp_entry(), smp_entry_end();
@@ -87,14 +89,20 @@ void smp_boot(void)
                panic("No memory for SMP boot stack!");
        smp_stack_top = (uintptr_t)(page2kva(smp_stack) + PGSIZE - SIZEOF_STRUCT_TRAPFRAME);
 
-       // set up the local APIC timer to fire 0x21 once.  hardcoded to break
+       // set up the local APIC timer to fire 0xf0 once.  hardcoded to break
        // out of the spinloop on waiting.  really just want to wait a little
-       lapic_set_timer(0x0000ffff, 0x21, 0);
+       lapic_set_timer(0x0000ffff, 0xf0, 0);
+       // set the function handler to respond to this
+       register_interrupt_handler(interrupt_handlers, 0xf0, smp_boot_handler);
        cprintf("Num_Cpus: %d\n", num_cpus);
+       // Start the IPI process (INIT, wait, SIPI)
        send_init_ipi();
        asm volatile("sti"); // LAPIC timer will fire, extINTs are blocked at LINT0 now
-       while (waiting); // gets set in the lapic timer
+       while (waiting); // gets released in smp_boot_handler
        send_startup_ipi(0x01);
+       // Deregister smp_boot_handler
+       register_interrupt_handler(interrupt_handlers, 0xf0, 0);
+
        // replace this with something that checks to see if smp_entrys are done
        while(1); // want other cores to do stuff for now
        
@@ -106,6 +114,14 @@ void smp_boot(void)
        // Dealloc the temp shared stack
        page_decref(smp_stack);
 }
+
+/* Breaks us out of the waiting loop in smp_boot */
+void smp_boot_handler(struct Trapframe *tf)
+{
+       extern volatile bool waiting;
+       {HANDLER_ATOMIC waiting = 0; }
+}
+
 /* 
  * This is called from smp_entry by each core to finish the core bootstrapping.
  * There is a spinlock around this entire function in smp_entry, for a few reasons,
index f586d76..3e48cef 100644 (file)
@@ -1,3 +1,7 @@
+#ifdef __DEPUTY__
+#pragma noasync
+#endif
+
 #include <inc/mmu.h>
 #include <inc/x86.h>
 #include <inc/assert.h>
@@ -21,6 +25,11 @@ struct Pseudodesc idt_pd = {
        sizeof(idt) - 1, (uint32_t) idt
 };
 
+/* 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.
+ */
+isr_t interrupt_handlers[256];
 
 static const char *NTS (IN_HANDLER trapname)(int trapno)
 {
@@ -105,7 +114,7 @@ idt_init(void)
        asm volatile("lidt idt_pd");
 
        // This will go away when we start using the IOAPIC properly
-       remap_pic();
+       pic_remap();
        lapic_enable();
        // set LINT0 to receive ExtINTs (KVM's default).  At reset they are 0x1000.
        write_mmreg32(LAPIC_LVT_LINT0, 0x700); 
@@ -213,28 +222,29 @@ void
 void
 (IN_HANDLER irq_handler)(struct Trapframe *tf)
 {
-       // send EOI.  might want to do this later, and not earlier
-       // and probably in assembly.  just need to determine what's LAPIC or not
-       // this is set up to work with an old PIC for now
-       // and this is hardcoded to have only 32 be the PIC (LINT0 ExtINT)
-       if (tf->tf_trapno == 32) {
+       // determine the interrupt handler table to use.  for now, pick the global
+       isr_t* handler_table = interrupt_handlers;
+
+       // then call the appropriate handler
+       if (handler_table[tf->tf_trapno] != 0)
+               handler_table[tf->tf_trapno](tf);
+
+       // Send EOI.  might want to do this in assembly, and possibly earlier
+       // This is set up to work with an old PIC for now
+       // Convention is that all IRQs between 32 and 47 are for the PIC.
+       // All others are LAPIC (timer, IPIs, perf, non-ExtINT LINTS, etc)
+       // For now, only 235-255 are available
+       assert(tf->tf_trapno >= 32); // slows us down, but we should never have this
+       if (tf->tf_trapno < 48)
                pic_send_eoi(tf->tf_trapno - PIC1_OFFSET);
-       } else // LAPIC style
+       else
                lapic_send_eoi();
+}
 
-       // consider doing James-style register_interrupt_handler instead
-       //trap(tf);
-       // this is just for handling the one time use of this in smp_boot
-       cprintf("Incoming IRQ, ISR = %d\n", tf->tf_trapno);
-       if (tf->tf_trapno == 33) {
-               cprintf("LAPIC TIMER!!!\n");
-               extern volatile bool waiting;
-               extern volatile uint8_t num_cpus;
-               {HANDLER_ATOMIC 
-                       waiting = 0;
-                       cprintf("Num_Cpus: %d\n", num_cpus);
-               }
-               }
+void
+register_interrupt_handler(isr_t table[], uint8_t isr, isr_t handler)
+{
+       table[isr] = handler;
 }
 
 void
index 79e3739..1669300 100644 (file)
@@ -9,10 +9,14 @@
 #include <inc/trap.h>
 #include <inc/mmu.h>
 
+// func ptr for interrupt service routines
+typedef void (*isr_t)(struct Trapframe* tf);
+
 /* The kernel's interrupt descriptor table */
 extern struct Gatedesc idt[];
 
 void idt_init(void);
+void register_interrupt_handler(isr_t (COUNT(256)table)[], uint8_t isr, isr_t handler);
 void (IN_HANDLER print_regs)(struct PushRegs *regs);
 void (IN_HANDLER print_trapframe)(struct Trapframe *tf);
 void (IN_HANDLER page_fault_handler)(struct Trapframe *);
index a4cc312..a4cf9c9 100644 (file)
@@ -106,6 +106,28 @@ IRQ_HANDLER(IRQ12, 44)
 IRQ_HANDLER(IRQ13, 45)
 IRQ_HANDLER(IRQ14, 46)
 IRQ_HANDLER(IRQ15, 47)
+/* 20 general purpose vectors, for use by the LAPIC.  Can expand later. */
+IRQ_HANDLER(IRQ203, 235)
+IRQ_HANDLER(IRQ204, 236)
+IRQ_HANDLER(IRQ205, 237)
+IRQ_HANDLER(IRQ206, 238)
+IRQ_HANDLER(IRQ207, 239)
+IRQ_HANDLER(IRQ208, 240)
+IRQ_HANDLER(IRQ209, 241)
+IRQ_HANDLER(IRQ210, 242)
+IRQ_HANDLER(IRQ211, 243)
+IRQ_HANDLER(IRQ212, 244)
+IRQ_HANDLER(IRQ213, 245)
+IRQ_HANDLER(IRQ214, 246)
+IRQ_HANDLER(IRQ215, 247)
+IRQ_HANDLER(IRQ216, 248)
+IRQ_HANDLER(IRQ217, 249)
+IRQ_HANDLER(IRQ218, 250)
+IRQ_HANDLER(IRQ219, 251)
+IRQ_HANDLER(IRQ220, 252)
+IRQ_HANDLER(IRQ221, 253)
+IRQ_HANDLER(IRQ222, 254)
+IRQ_HANDLER(IRQ223, 255)
 
 TRAPHANDLER_NOEC(ISR_syscall, T_SYSCALL)
 /* Make sure default is last!! */