metalsvm/arch/x86/kernel/irq.c

300 lines
9.5 KiB
C
Raw Permalink Normal View History

/*
* 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.
*/
/**
* @author Stefan Lankes
* @file arch/x86/kernel/irq.c
* @brief Function definitions for irq.h and a standard IRQ-handler
*
*
*/
#include <metalsvm/stdio.h>
#include <metalsvm/string.h>
#include <metalsvm/tasks.h>
#include <metalsvm/errno.h>
#include <metalsvm/page.h>
#include <asm/irq.h>
#include <asm/idt.h>
#include <asm/isrs.h>
#include <asm/apic.h>
/*
* 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 irq16(void);
extern void irq17(void);
extern void irq18(void);
extern void irq19(void);
extern void irq20(void);
extern void irq21(void);
extern void irq22(void);
extern void irq23(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 256
/** @brief IRQ handle pointers
*
* 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;
}
/** @brief Remapping IRQs with a couple of IO output operations
*
* 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)
{
#ifndef CONFIG_ROCKCREEK
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);
#endif
return 0;
}
int disable_timer_irq(void)
{
if (BUILTIN_EXPECT(apic_is_enabled(), 1))
return apic_disable_timer();
return -EINVAL;
}
int enable_timer_irq(void)
{
if (BUILTIN_EXPECT(apic_is_enabled(), 1))
return apic_enable_timer();
return -EINVAL;
}
/** @brief Remap IRQs and install ISRs in IDT
*
* We first remap the interrupt controllers, and then we install
* the appropriate ISRs to the correct entries in the IDT.\n
* This is just like installing the exception handlers
*/
static int irq_install(void)
{
irq_remap();
idt_set_gate(32, (size_t)irq0, KERNEL_CODE_SELECTOR,
IDT_FLAG_PRESENT|IDT_FLAG_RING0|IDT_FLAG_32BIT|IDT_FLAG_INTTRAP);
idt_set_gate(33, (size_t)irq1, KERNEL_CODE_SELECTOR,
IDT_FLAG_PRESENT|IDT_FLAG_RING0|IDT_FLAG_32BIT|IDT_FLAG_INTTRAP);
idt_set_gate(34, (size_t)irq2, KERNEL_CODE_SELECTOR,
IDT_FLAG_PRESENT|IDT_FLAG_RING0|IDT_FLAG_32BIT|IDT_FLAG_INTTRAP);
idt_set_gate(35, (size_t)irq3, KERNEL_CODE_SELECTOR,
IDT_FLAG_PRESENT|IDT_FLAG_RING0|IDT_FLAG_32BIT|IDT_FLAG_INTTRAP);
idt_set_gate(36, (size_t)irq4, KERNEL_CODE_SELECTOR,
IDT_FLAG_PRESENT|IDT_FLAG_RING0|IDT_FLAG_32BIT|IDT_FLAG_INTTRAP);
idt_set_gate(37, (size_t)irq5, KERNEL_CODE_SELECTOR,
IDT_FLAG_PRESENT|IDT_FLAG_RING0|IDT_FLAG_32BIT|IDT_FLAG_INTTRAP);
idt_set_gate(38, (size_t)irq6, KERNEL_CODE_SELECTOR,
IDT_FLAG_PRESENT|IDT_FLAG_RING0|IDT_FLAG_32BIT|IDT_FLAG_INTTRAP);
idt_set_gate(39, (size_t)irq7, KERNEL_CODE_SELECTOR,
IDT_FLAG_PRESENT|IDT_FLAG_RING0|IDT_FLAG_32BIT|IDT_FLAG_INTTRAP);
idt_set_gate(40, (size_t)irq8, KERNEL_CODE_SELECTOR,
IDT_FLAG_PRESENT|IDT_FLAG_RING0|IDT_FLAG_32BIT|IDT_FLAG_INTTRAP);
idt_set_gate(41, (size_t)irq9, KERNEL_CODE_SELECTOR,
IDT_FLAG_PRESENT|IDT_FLAG_RING0|IDT_FLAG_32BIT|IDT_FLAG_INTTRAP);
idt_set_gate(42, (size_t)irq10, KERNEL_CODE_SELECTOR,
IDT_FLAG_PRESENT|IDT_FLAG_RING0|IDT_FLAG_32BIT|IDT_FLAG_INTTRAP);
idt_set_gate(43, (size_t)irq11, KERNEL_CODE_SELECTOR,
IDT_FLAG_PRESENT|IDT_FLAG_RING0|IDT_FLAG_32BIT|IDT_FLAG_INTTRAP);
idt_set_gate(44, (size_t)irq12, KERNEL_CODE_SELECTOR,
IDT_FLAG_PRESENT|IDT_FLAG_RING0|IDT_FLAG_32BIT|IDT_FLAG_INTTRAP);
idt_set_gate(45, (size_t)irq13, KERNEL_CODE_SELECTOR,
IDT_FLAG_PRESENT|IDT_FLAG_RING0|IDT_FLAG_32BIT|IDT_FLAG_INTTRAP);
idt_set_gate(46, (size_t)irq14, KERNEL_CODE_SELECTOR,
IDT_FLAG_PRESENT|IDT_FLAG_RING0|IDT_FLAG_32BIT|IDT_FLAG_INTTRAP);
idt_set_gate(47, (size_t)irq15, KERNEL_CODE_SELECTOR,
IDT_FLAG_PRESENT|IDT_FLAG_RING0|IDT_FLAG_32BIT|IDT_FLAG_INTTRAP);
2012-07-22 13:13:46 +02:00
idt_set_gate(48, (size_t)irq16, KERNEL_CODE_SELECTOR,
IDT_FLAG_PRESENT|IDT_FLAG_RING0|IDT_FLAG_32BIT|IDT_FLAG_INTTRAP);
idt_set_gate(49, (size_t)irq17, KERNEL_CODE_SELECTOR,
IDT_FLAG_PRESENT|IDT_FLAG_RING0|IDT_FLAG_32BIT|IDT_FLAG_INTTRAP);
idt_set_gate(50, (size_t)irq18, KERNEL_CODE_SELECTOR,
IDT_FLAG_PRESENT|IDT_FLAG_RING0|IDT_FLAG_32BIT|IDT_FLAG_INTTRAP);
idt_set_gate(51, (size_t)irq19, KERNEL_CODE_SELECTOR,
IDT_FLAG_PRESENT|IDT_FLAG_RING0|IDT_FLAG_32BIT|IDT_FLAG_INTTRAP);
idt_set_gate(52, (size_t)irq20, KERNEL_CODE_SELECTOR,
IDT_FLAG_PRESENT|IDT_FLAG_RING0|IDT_FLAG_32BIT|IDT_FLAG_INTTRAP);
idt_set_gate(53, (size_t)irq21, KERNEL_CODE_SELECTOR,
IDT_FLAG_PRESENT|IDT_FLAG_RING0|IDT_FLAG_32BIT|IDT_FLAG_INTTRAP);
idt_set_gate(54, (size_t)irq22, KERNEL_CODE_SELECTOR,
IDT_FLAG_PRESENT|IDT_FLAG_RING0|IDT_FLAG_32BIT|IDT_FLAG_INTTRAP);
idt_set_gate(55, (size_t)irq23, KERNEL_CODE_SELECTOR,
IDT_FLAG_PRESENT|IDT_FLAG_RING0|IDT_FLAG_32BIT|IDT_FLAG_INTTRAP);
2012-07-22 13:13:46 +02:00
// add APIC interrupt handler
idt_set_gate(123, (size_t)apic_timer, KERNEL_CODE_SELECTOR,
IDT_FLAG_PRESENT|IDT_FLAG_RING0|IDT_FLAG_32BIT|IDT_FLAG_INTTRAP);
idt_set_gate(124, (size_t)apic_lint0, KERNEL_CODE_SELECTOR,
IDT_FLAG_PRESENT|IDT_FLAG_RING0|IDT_FLAG_32BIT|IDT_FLAG_INTTRAP);
idt_set_gate(125, (size_t)apic_lint1, KERNEL_CODE_SELECTOR,
IDT_FLAG_PRESENT|IDT_FLAG_RING0|IDT_FLAG_32BIT|IDT_FLAG_INTTRAP);
idt_set_gate(126, (size_t)apic_error, KERNEL_CODE_SELECTOR,
IDT_FLAG_PRESENT|IDT_FLAG_RING0|IDT_FLAG_32BIT|IDT_FLAG_INTTRAP);
idt_set_gate(127, (size_t)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;
}
/** @brief Default IRQ handler
*
* 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, it won't raise any more IRQs.
*
* Note: If we enabled the APIC, we also disabled the PIC. Afterwards,
* we get no interrupts between 0 and 15.
*/
size_t** irq_handler(struct state *s)
{
/* This is a blank function pointer */
void (*handler) (struct state * s);
2011-10-10 06:23:00 +02:00
check_workqueues_in_irqhandler(s->int_no);
/*
* Find out if we have a custom handler to run for this
* IRQ and then finally, run it
*/
if (BUILTIN_EXPECT(s->int_no < MAX_HANDLERS, 1)) {
handler = irq_routines[s->int_no];
if (handler)
handler(s);
} else kprintf("Invalid interrupt number %d\n", s->int_no);
/*
* If the IDT entry that was invoked was greater-than-or-equal to 48,
* then we use the APIC
*/
if (apic_is_enabled() || s->int_no >= 123) {
apic_eoi();
goto leave_handler;
}
2011-07-12 10:26:50 -07:00
#ifndef CONFIG_ROCKCREEK
/*
* 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);
2011-07-12 10:26:50 -07:00
#else
kprintf("Upps, RockCreek uses IRQs below 123!\n");
#endif
leave_handler:
// timer interrupt?
if ((s->int_no == 32) || (s->int_no == 123))
return scheduler(); // switch to a new task
else if ((s->int_no >= 32) && (get_highest_priority() > per_core(current_task)->prio))
return scheduler();
return NULL;
}