diff --git a/src/PIC.cc b/src/PIC.cc index 76d84ea..474fb59 100644 --- a/src/PIC.cc +++ b/src/PIC.cc @@ -59,91 +59,29 @@ PIC::systemPIC() * Public */ +void +PIC::initialize(uint8_t pic1Offset, + uint8_t pic2Offset, + uint8_t pic2IRQ) +{ + initializePICs(pic1Offset, pic2Offset, pic2IRQ); + + // Turn off interrupts. + kernel::io::outb(PIC1.data, 0xFF); + kernel::io::outb(PIC2.data, 0xFF); +} + + void PIC::remap(uint8_t pic1Offset, uint8_t pic2Offset, uint8_t pic2IRQ) { - /* - * Initialization of the PIC chips requires programming both chips together. - * Each requires a series of four bytes: the first (ICW1) on their command - * ports, and the subsequent three (ICW2 through ICW4) on their data ports. - * - * ICW1 contains the following flags: - * Bit 0: 1 = ICW4 required, 0 = ICW4 not required - * Bit 1: 1 = single 8259, 0 = cascading 8259s - * Bit 2: 1 = 4 byte interrupt vectors, 0 = 8 byte interrupt vectors - * Bit 3: 1 = level triggered mode, 0 = edge triggered mode - * Bit 4: 1 = initialization (this is required to be 1 for ICW1) - * Bits 5, 6, 7 must all be 0 - * - * ICW2 is the interrupt vector offset that this PIC's interrupt should be - * mapped to. It must be divisible by 0x8. - * - * ICW3 is different for the PIC1 and PIC2. For the PIC1, ICW3 should have - * one bit set indicating on which IRQ PIC2 (the cascaded PIC) will be. For - * PIC2, the first three bits of ICW3 indicate on which IRQ it will reside - * on the PIC1. - * - * ICW4 contains the following flags: - * Bit 0: 1 = 80x86 mode, 0 = MCS 80/85 mode - * Bit 1: 1 = auto EOI mode, 0 = normal EOI mode - * Bits 2, 3: PIC2/PIC1 buffering mode - * Bit 4: 1 = special fully nested mode (SFNM), 0 = sequential mode - * Bits 5, 6, 7 must all be 0 - * - * The buffering modes mentioned above are as follows: - * 00: not buffered - * 01: not buffered - * 10: buffered mode PIC2 (PC mode) - * 11: buffered mode PIC1 (PC mode) - * - * See also: - * http://stanislavs.org/helppc/8259.html - * http://wiki.osdev.org/PIC - */ - - // Ensure divisiblity by 8. - pic1Offset &= ~uint8_t(0x7); - pic2Offset &= ~uint8_t(0x7); - - // TODO: Implement clamping and error handling. - uint8_t pic2IRQMask = 0; - if (pic2IRQ > 0) { - pic2IRQMask = 1; - for (int i = 1; i < pic2IRQ; i++) { - pic2IRQMask <<= 1; - } - } else { - // TODO: We have a problem. - } - // Save the current IRQ masks for each PIC uint8_t pic1Mask = kernel::io::inb(PIC1.data); uint8_t pic2Mask = kernel::io::inb(PIC2.data); -#ifdef CONFIG_PIC_SHOULD_WAIT -# define waitIfRequired() io::wait() -#else -# define waitIfRequired() -#endif - -#define sendToPIC(port, byte) \ - kernel::io::outb((port), (byte)); \ - waitIfRequired() - - sendToPIC(PIC1.command, ICW1::Initialize + ICW1::ICW4Required); - sendToPIC(PIC1.data, pic1Offset); - sendToPIC(PIC1.data, pic2IRQMask); - sendToPIC(PIC1.data, ICW4::X86Mode + ICW4::AutoEOI); - - sendToPIC(PIC2.command, ICW1::Initialize + ICW1::ICW4Required); - sendToPIC(PIC2.data, pic2Offset); - sendToPIC(PIC2.data, pic2IRQ); - sendToPIC(PIC2.data, ICW4::X86Mode + ICW4::AutoEOI); - -#undef sendToPIC -#undef waitIfRequired + initializePICs(pic1Offset, pic2Offset, pic2IRQ); // Restore the saved masks kernel::io::outb(PIC2.data, pic1Mask); @@ -210,4 +148,88 @@ PIC::disableIRQ(uint8_t irq) kernel::io::outb(port, value); } + +void +PIC::initializePICs(uint8_t pic1Offset, + uint8_t pic2Offset, + uint8_t pic2IRQ) +{ + /* + * Initialization of the PIC chips requires programming both chips together. + * Each requires a series of four bytes: the first (ICW1) on their command + * ports, and the subsequent three (ICW2 through ICW4) on their data ports. + * + * ICW1 contains the following flags: + * Bit 0: 1 = ICW4 required, 0 = ICW4 not required + * Bit 1: 1 = single 8259, 0 = cascading 8259s + * Bit 2: 1 = 4 byte interrupt vectors, 0 = 8 byte interrupt vectors + * Bit 3: 1 = level triggered mode, 0 = edge triggered mode + * Bit 4: 1 = initialization (this is required to be 1 for ICW1) + * Bits 5, 6, 7 must all be 0 + * + * ICW2 is the interrupt vector offset that this PIC's interrupt should be + * mapped to. It must be divisible by 0x8. + * + * ICW3 is different for the PIC1 and PIC2. For the PIC1, ICW3 should have + * one bit set indicating on which IRQ PIC2 (the cascaded PIC) will be. For + * PIC2, the first three bits of ICW3 indicate on which IRQ it will reside + * on the PIC1. + * + * ICW4 contains the following flags: + * Bit 0: 1 = 80x86 mode, 0 = MCS 80/85 mode + * Bit 1: 1 = auto EOI mode, 0 = normal EOI mode + * Bits 2, 3: PIC2/PIC1 buffering mode + * Bit 4: 1 = special fully nested mode (SFNM), 0 = sequential mode + * Bits 5, 6, 7 must all be 0 + * + * The buffering modes mentioned above are as follows: + * 00: not buffered + * 01: not buffered + * 10: buffered mode PIC2 (PC mode) + * 11: buffered mode PIC1 (PC mode) + * + * See also: + * http://stanislavs.org/helppc/8259.html + * http://wiki.osdev.org/PIC + */ + + // Ensure divisiblity by 8. + pic1Offset &= ~uint8_t(0x7); + pic2Offset &= ~uint8_t(0x7); + + // TODO: Implement clamping and error handling. + uint8_t pic2IRQMask = 0; + if (pic2IRQ < 16) { + pic2IRQMask = 1; + for (int i = 1; i < pic2IRQ; i++) { + pic2IRQMask <<= 1; + } + } else { + // TODO: We have a problem. + } + +#ifdef CONFIG_PIC_SHOULD_WAIT +# define waitIfRequired() io::wait() +#else +# define waitIfRequired() +#endif + +#define sendToPIC(port, byte) \ + kernel::io::outb((port), (byte)); \ + waitIfRequired() + + sendToPIC(PIC1.command, ICW1::Initialize + ICW1::ICW4Required); + sendToPIC(PIC1.data, pic1Offset); + sendToPIC(PIC1.data, pic2IRQMask); + sendToPIC(PIC1.data, ICW4::X86Mode); + + sendToPIC(PIC2.command, ICW1::Initialize + ICW1::ICW4Required); + sendToPIC(PIC2.data, pic2Offset); + sendToPIC(PIC2.data, pic2IRQ); + sendToPIC(PIC2.data, ICW4::X86Mode); + +#undef sendToPIC +#undef waitIfRequired +} + } /* namespace x86 */ diff --git a/src/PIC.hh b/src/PIC.hh index 26cd581..44fc845 100644 --- a/src/PIC.hh +++ b/src/PIC.hh @@ -30,11 +30,26 @@ struct PIC static PIC& systemPIC(); /** - * Initialize and map the PIC chips into the interrupt vector space. The - * offsets must be divisible by 8. The `pic2IRQ` argument specifies which - * IRQ on first PIC the second PIC will be mapped to. + * Initialize the PIC chips by mapping their ranges out of the default + * (which overlap with the system exceptions). The offsets must be divisable + * by 8. Once the chips are initialized disable all interrupts. + * + * @param [in] pic1Offset Offset of PIC1. + * @param [in] pic2Offset Offset of PIC2. + * @param [in] pic2IRQ IRQ on PIC1 where PIC2 will be mapped to. */ - void remap(uint8_t pic1Offset, uint8_t pic2ffset, uint8_t pic2IRQ); + void initialize(uint8_t pic1Offset, uint8_t pic2Offset, uint8_t pic2IRQ = 2); + + /** + * Remap the PIC chips into the interrupt vector space. The offsets must be + * divisible by 8. The interrupt masks are saved before remapping and + * restored afterwards. + * + * @param [in] pic1Offset Offset of PIC1. + * @param [in] pic2Offset Offset of PIC2. + * @param [in] pic2IRQ IRQ on PIC1 where PIC2 will be mapped to. + */ + void remap(uint8_t pic1Offset, uint8_t pic2Offset, uint8_t pic2IRQ = 2); /** * Notifies the PICs with an End Of Interrupt (EOI) command that the @@ -53,6 +68,8 @@ private: uint16_t portForIRQ(uint8_t irq); void enableIRQ(uint8_t irq); void disableIRQ(uint8_t irq); + + void initializePICs(uint8_t pic1Offset, uint8_t pic2Offset, uint8_t pic2IRQ); }; } /* namespace x86 */