/* * Copyright 2010 Stefan Lankes, Chair for Operating Systems, * RWTH Aachen University * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * This file is part of MetalSVM. */ #include #include #include #include #include /* * These are own ISRs that point to our special IRQ handler * instead of the regular 'fault_handler' function */ extern void irq0(void); extern void irq1(void); extern void irq2(void); extern void irq3(void); extern void irq4(void); extern void irq5(void); extern void irq6(void); extern void irq7(void); extern void irq8(void); extern void irq9(void); extern void irq10(void); extern void irq11(void); extern void irq12(void); extern void irq13(void); extern void irq14(void); extern void irq15(void); /* * This array is actually an array of function pointers. We use * this to handle custom IRQ handlers for a given IRQ */ static void *irq_routines[16] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; /* This installs a custom IRQ handler for the given IRQ */ void irq_install_handler(unsigned int irq, irq_handler_t handler) { irq_routines[irq] = handler; } /* This clears the handler for a given IRQ */ void irq_uninstall_handler(unsigned int irq) { irq_routines[irq] = NULL; } /* * Normally, IRQs 0 to 7 are mapped to entries 8 to 15. This * is a problem in protected mode, because IDT entry 8 is a * Double Fault! Without remapping, every time IRQ0 fires, * you get a Double Fault Exception, which is NOT actually * what's happening. We send commands to the Programmable * Interrupt Controller (PICs - also called the 8259's) in * order to make IRQ0 to 15 be remapped to IDT entries 32 to * 47 */ static void irq_remap(void) { outportb(0x20, 0x11); outportb(0xA0, 0x11); outportb(0x21, 0x20); outportb(0xA1, 0x28); outportb(0x21, 0x04); outportb(0xA1, 0x02); outportb(0x21, 0x01); outportb(0xA1, 0x01); outportb(0x21, 0x0); outportb(0xA1, 0x0); } /* * We first remap the interrupt controllers, and then we install * the appropriate ISRs to the correct entries in the IDT. This * is just like installing the exception handlers */ static void irq_install(void) { irq_remap(); idt_set_gate(32, (unsigned)irq0, 0x08, 0x8E); idt_set_gate(33, (unsigned)irq1, 0x08, 0x8E); idt_set_gate(34, (unsigned)irq2, 0x08, 0x8E); idt_set_gate(35, (unsigned)irq3, 0x08, 0x8E); idt_set_gate(36, (unsigned)irq4, 0x08, 0x8E); idt_set_gate(37, (unsigned)irq5, 0x08, 0x8E); idt_set_gate(38, (unsigned)irq6, 0x08, 0x8E); idt_set_gate(39, (unsigned)irq7, 0x08, 0x8E); idt_set_gate(40, (unsigned)irq8, 0x08, 0x8E); idt_set_gate(41, (unsigned)irq9, 0x08, 0x8E); idt_set_gate(42, (unsigned)irq10, 0x08, 0x8E); idt_set_gate(43, (unsigned)irq11, 0x08, 0x8E); idt_set_gate(44, (unsigned)irq12, 0x08, 0x8E); idt_set_gate(45, (unsigned)irq13, 0x08, 0x8E); idt_set_gate(46, (unsigned)irq14, 0x08, 0x8E); idt_set_gate(47, (unsigned)irq15, 0x08, 0x8E); } void irq_init(void) { idt_install(); isrs_install(); irq_install(); } /* * Each of the IRQ ISRs point to this function, rather than * the 'fault_handler' in 'isrs.c'. The IRQ Controllers need * to be told when you are done servicing them, so you need * to send them an "End of Interrupt" command (0x20). There * are two 8259 chips: The first exists at 0x20, the second * exists at 0xA0. If the second controller (an IRQ from 8 to * 15) gets an interrupt, you need to acknowledge the * interrupt at BOTH controllers, otherwise, you only send * an EOI command to the first controller. If you don't send * an EOI, you won't raise any more IRQs */ void irq_handler(struct state *s) { /* This is a blank function pointer */ void (*handler) (struct state * s); /* * Find out if we have a custom handler to run for this * IRQ, and then finally, run it */ handler = irq_routines[s->int_no - 32]; if (handler) { handler(s); } /* * If the IDT entry that was invoked was greater than 40 * (meaning IRQ8 - 15), then we need to send an EOI to * the slave controller */ if (s->int_no >= 40) { outportb(0xA0, 0x20); } /* * In either case, we need to send an EOI to the master * interrupt controller too */ outportb(0x20, 0x20); }