/* * 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 #include #include /* * These are our 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); extern void apic_timer(void); extern void apic_lint0(void); extern void apic_lint1(void); extern void apic_error(void); extern void apic_svr(void); #define MAX_HANDLERS 32 /* * 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[MAX_HANDLERS] = {[0 ... MAX_HANDLERS-1] = NULL }; /* This installs a custom IRQ handler for the given IRQ */ int irq_install_handler(unsigned int irq, irq_handler_t handler) { if (irq >= MAX_HANDLERS) return -EINVAL; irq_routines[irq] = handler; return 0; } /* This clears the handler for a given IRQ */ int irq_uninstall_handler(unsigned int irq) { if (irq >= MAX_HANDLERS) return -EINVAL; irq_routines[irq] = NULL; return 0; } /* * 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 what's * actually 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 int 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); return 0; } /* * 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 int irq_install(void) { irq_remap(); idt_set_gate(32, (unsigned)irq0, KERNEL_CODE_SELECTOR, IDT_FLAG_PRESENT|IDT_FLAG_RING0|IDT_FLAG_32BIT|IDT_FLAG_INTTRAP); idt_set_gate(33, (unsigned)irq1, KERNEL_CODE_SELECTOR, IDT_FLAG_PRESENT|IDT_FLAG_RING0|IDT_FLAG_32BIT|IDT_FLAG_INTTRAP); idt_set_gate(34, (unsigned)irq2, KERNEL_CODE_SELECTOR, IDT_FLAG_PRESENT|IDT_FLAG_RING0|IDT_FLAG_32BIT|IDT_FLAG_INTTRAP); idt_set_gate(35, (unsigned)irq3, KERNEL_CODE_SELECTOR, IDT_FLAG_PRESENT|IDT_FLAG_RING0|IDT_FLAG_32BIT|IDT_FLAG_INTTRAP); idt_set_gate(36, (unsigned)irq4, KERNEL_CODE_SELECTOR, IDT_FLAG_PRESENT|IDT_FLAG_RING0|IDT_FLAG_32BIT|IDT_FLAG_INTTRAP); idt_set_gate(37, (unsigned)irq5, KERNEL_CODE_SELECTOR, IDT_FLAG_PRESENT|IDT_FLAG_RING0|IDT_FLAG_32BIT|IDT_FLAG_INTTRAP); idt_set_gate(38, (unsigned)irq6, KERNEL_CODE_SELECTOR, IDT_FLAG_PRESENT|IDT_FLAG_RING0|IDT_FLAG_32BIT|IDT_FLAG_INTTRAP); idt_set_gate(39, (unsigned)irq7, KERNEL_CODE_SELECTOR, IDT_FLAG_PRESENT|IDT_FLAG_RING0|IDT_FLAG_32BIT|IDT_FLAG_INTTRAP); idt_set_gate(40, (unsigned)irq8, KERNEL_CODE_SELECTOR, IDT_FLAG_PRESENT|IDT_FLAG_RING0|IDT_FLAG_32BIT|IDT_FLAG_INTTRAP); idt_set_gate(41, (unsigned)irq9, KERNEL_CODE_SELECTOR, IDT_FLAG_PRESENT|IDT_FLAG_RING0|IDT_FLAG_32BIT|IDT_FLAG_INTTRAP); idt_set_gate(42, (unsigned)irq10, KERNEL_CODE_SELECTOR, IDT_FLAG_PRESENT|IDT_FLAG_RING0|IDT_FLAG_32BIT|IDT_FLAG_INTTRAP); idt_set_gate(43, (unsigned)irq11, KERNEL_CODE_SELECTOR, IDT_FLAG_PRESENT|IDT_FLAG_RING0|IDT_FLAG_32BIT|IDT_FLAG_INTTRAP); idt_set_gate(44, (unsigned)irq12, KERNEL_CODE_SELECTOR, IDT_FLAG_PRESENT|IDT_FLAG_RING0|IDT_FLAG_32BIT|IDT_FLAG_INTTRAP); idt_set_gate(45, (unsigned)irq13, KERNEL_CODE_SELECTOR, IDT_FLAG_PRESENT|IDT_FLAG_RING0|IDT_FLAG_32BIT|IDT_FLAG_INTTRAP); idt_set_gate(46, (unsigned)irq14, KERNEL_CODE_SELECTOR, IDT_FLAG_PRESENT|IDT_FLAG_RING0|IDT_FLAG_32BIT|IDT_FLAG_INTTRAP); idt_set_gate(47, (unsigned)irq15, KERNEL_CODE_SELECTOR, IDT_FLAG_PRESENT|IDT_FLAG_RING0|IDT_FLAG_32BIT|IDT_FLAG_INTTRAP); if (has_apic()) { idt_set_gate(48, (unsigned)apic_timer, KERNEL_CODE_SELECTOR, IDT_FLAG_PRESENT|IDT_FLAG_RING0|IDT_FLAG_32BIT|IDT_FLAG_INTTRAP); idt_set_gate(49, (unsigned)apic_lint0, KERNEL_CODE_SELECTOR, IDT_FLAG_PRESENT|IDT_FLAG_RING0|IDT_FLAG_32BIT|IDT_FLAG_INTTRAP); idt_set_gate(50, (unsigned)apic_lint1, KERNEL_CODE_SELECTOR, IDT_FLAG_PRESENT|IDT_FLAG_RING0|IDT_FLAG_32BIT|IDT_FLAG_INTTRAP); idt_set_gate(51, (unsigned)apic_error, KERNEL_CODE_SELECTOR, IDT_FLAG_PRESENT|IDT_FLAG_RING0|IDT_FLAG_32BIT|IDT_FLAG_INTTRAP); idt_set_gate(52, (unsigned)apic_svr, KERNEL_CODE_SELECTOR, IDT_FLAG_PRESENT|IDT_FLAG_RING0|IDT_FLAG_32BIT|IDT_FLAG_INTTRAP); } return 0; } int irq_init(void) { idt_install(); isrs_install(); irq_install(); return 0; } /* * 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. If we use the PIC * instead of the APIC, we have two 8259 chips: The first one * exists at 0x20, the second one 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 * * Note: If we enabled the APIC, we also disabled the PIC. Afterwards, * we got no interrupts between 0 and 15. */ 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-or-equal to 48, * then we use the APIC */ if (s->int_no >= 48) { apic_eoi(); return; } /* * If the IDT entry that was invoked was greater-than-or-equal to 40 * and lower than 48 (meaning IRQ8 - 15), then we need to * send an EOI to the slave controller of the PIC */ if (s->int_no >= 40) outportb(0xA0, 0x20); /* * In either case, we need to send an EOI to the master * interrupt controller of the PIC, too */ outportb(0x20, 0x20); }