PIC helper functions to read the ISR, IRR, and IMR
authorBarret Rhoden <brho@cs.berkeley.edu>
Wed, 4 Apr 2012 23:40:39 +0000 (16:40 -0700)
committerBarret Rhoden <brho@cs.berkeley.edu>
Wed, 4 Apr 2012 23:40:39 +0000 (16:40 -0700)
Remember, bit 2 (0x4) will be high on the ISR/IRR when any of the high 8
IRQs are high, due to the chaining of the PICs.  Likewise, the IRR will
show an IRQ when it is masked - the chip will simply never send it.

Also note that these only apply for interrupts coming in via the PIC,
and not the IOAPIC/LAPIC.  You'll need to check the LAPIC for its
ISR/IRR.

kern/arch/i686/apic.c
kern/arch/i686/apic.h

index 5c4aa2b..4a930b9 100644 (file)
 
 system_timing_t RO system_timing = {0, 0, 0xffff, 0};
 
-/*
- * Remaps the Programmable Interrupt Controller to use IRQs 32-47
+/* * 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.
- */
+ * Check osdev for a more thorough explanation/implementation.
+ * http://bochs.sourceforge.net/techspec/PORTS.LST  */
 void pic_remap() 
 {
-       // start initialization
+       /* start initialization (ICW1) */
        outb(PIC1_CMD, 0x11);
        outb(PIC2_CMD, 0x11);
-       // set new offsets
+       /* set new offsets (ICW2) */
        outb(PIC1_DATA, PIC1_OFFSET);
        outb(PIC2_DATA, PIC2_OFFSET);
-       // set up cascading
+       /* set up cascading (ICW3) */
        outb(PIC1_DATA, 0x04);
        outb(PIC2_DATA, 0x02);
-       // other stuff (put in 8086/88 mode, or whatever)
+       /* other stuff (put in 8086/88 mode, or whatever) (ICW4) */
        outb(PIC1_DATA, 0x01);
        outb(PIC2_DATA, 0x01);
-       // set masks, defaulting to all masked for now
+       /* Init done, further data R/W access the interrupt mask */
+       /* set masks, defaulting to all masked for now */
        outb(PIC1_DATA, 0xff);
        outb(PIC2_DATA, 0xff);
 }
@@ -63,6 +60,33 @@ void pic_unmask_irq(uint8_t irq)
                outb(PIC1_DATA, inb(PIC1_DATA) & ~(1 << irq));
 }
 
+/* Aka, the IMR.  Simply reading the data port are OCW1s. */
+uint16_t pic_get_mask(void)
+{
+       return (inb(PIC2_DATA) << 8) | inb(PIC1_DATA);
+}
+
+static uint16_t __pic_get_irq_reg(int ocw3)
+{
+       /* OCW3 to PIC CMD to get the register values.  PIC2 is chained, and
+        * represents IRQs 8-15.  PIC1 is IRQs 0-7, with 2 being the chain */
+       outb(PIC1_CMD, ocw3);
+       outb(PIC2_CMD, ocw3);
+       return (inb(PIC2_CMD) << 8) | inb(PIC1_CMD);
+}
+
+/* Returns the combined value of the cascaded PICs irq request register */
+uint16_t pic_get_irr(void)
+{
+       return __pic_get_irq_reg(PIC_READ_IRR);
+}
+
+/* Returns the combined value of the cascaded PICs irq service register */
+uint16_t pic_get_isr(void)
+{
+       return __pic_get_irq_reg(PIC_READ_ISR);
+}
+
 /*
  * Sets the LAPIC timer to go off after a certain number of ticks.  The primary
  * clock freq is actually the bus clock, which we figure out during timer_init
index b1d5bad..1d43985 100644 (file)
 #include <arch/x86.h>
 #include <arch/ioapic.h>
 
-// PIC
+/* PIC (8259A)
+ * When looking at the specs, A0 is our CMD line, and A1 is the DATA line.  This
+ * means that blindly writing to PIC1_DATA is an OCW1 (interrupt masks).  When
+ * writing to CMD (A0), the chip can determine betweeb OCW2 and OCW3 by the
+ * setting of a few specific bits (OCW2 has bit 3 unset, OCW3 has it set). */
 #define PIC1_CMD                                       0x20
 #define PIC1_DATA                                      0x21
 #define PIC2_CMD                                       0xA0
 // These are also hardcoded into the IRQ_HANDLERs of kern/trapentry.S
 #define PIC1_OFFSET                                    0x20
 #define PIC2_OFFSET                                    0x28
-#define PIC_EOI                                                0x20
+#define PIC_EOI                                                0x20    /* OCW2 EOI */
+/* These set the next CMD read to return specific values.  Note that the chip
+ * remembers what setting we had before (IRR or ISR), if you do other reads of
+ * CMD. (not tested, written in the spec sheet) */
+#define PIC_READ_IRR                           0x0a    /* OCW3 irq ready next CMD read */
+#define PIC_READ_ISR                           0x0b    /* OCW3 irq service next CMD read */
 
 // Local APIC
 /* PBASE is the physical address.  It is mapped in at the VADDR LAPIC_BASE */
@@ -96,6 +105,9 @@ extern system_timing_t system_timing;
 void pic_remap(void);
 void pic_mask_irq(uint8_t irq);
 void pic_unmask_irq(uint8_t irq);
+uint16_t pic_get_mask(void);
+uint16_t pic_get_irr(void);
+uint16_t pic_get_isr(void);
 void __lapic_set_timer(uint32_t ticks, uint8_t vec, bool periodic, uint8_t div);
 void lapic_set_timer(uint32_t usec, bool periodic);
 uint32_t lapic_get_default_id(void);
@@ -273,6 +285,8 @@ static inline void __send_nmi(uint8_t hw_coreid)
  * ExtInts.  To prevent abuse, we'll use it just for IPIs for now. */
 static inline bool ipi_is_pending(uint8_t vector)
 {
+       /* TODO: look at this, it's so fucked up.  also, there's a bit that needs to
+        * be set to allow us to read from the IRR. */
        return (LAPIC_ISR & vector) || (LAPIC_IRR & vector);
 }