toaruos/kernel/cpu/irq.c

179 lines
4.2 KiB
C
Raw Normal View History

2014-06-08 10:51:01 +04:00
/* vim: tabstop=4 shiftwidth=4 noexpandtab
* This file is part of ToaruOS and is released under the terms
* of the NCSA / University of Illinois License - see LICENSE.md
* Copyright (C) 2011-2014 Kevin Lange
2015-05-21 02:52:19 +03:00
* Copyright (C) 2015 Dale Weiler
*
* Interrupt Requests
*
*/
2011-01-16 07:17:42 +03:00
#include <system.h>
2011-12-27 05:23:58 +04:00
#include <logging.h>
2011-01-16 07:17:42 +03:00
2015-05-21 06:08:58 +03:00
/* Programmable interrupt controller */
#define PIC1 0x20
#define PIC1_COMMAND PIC1
#define PIC1_OFFSET 0x20
#define PIC1_DATA (PIC1+1)
#define PIC2 0xA0
#define PIC2_COMMAND PIC2
#define PIC2_OFFSET 0x28
#define PIC2_DATA (PIC2+1)
#define PIC_EOI 0x20
#define ICW1_ICW4 0x01
#define ICW1_INIT 0x10
#define PIC_WAIT() \
do { \
/* Port 0x80 is used for 'checkpoints' during POST */ \
asm volatile("outb %%al, $0x80" : : "a"(0)); \
} while (0)
/* Interrupts */
static volatile int sync_depth = 0;
#define SYNC_CLI() asm volatile("cli")
#define SYNC_STI() asm volatile("sti")
void int_disable(void) {
/* Check if interrupts are enabled */
uint32_t flags;
asm volatile("pushf\n\t"
"pop %%eax\n\t"
"movl %%eax, %0\n\t"
: "=r"(flags)
:
: "%eax");
/* Disable interrupts */
SYNC_CLI();
/* If interrupts were enabled, then this is the first call depth */
if (flags & (1 << 9)) {
sync_depth = 1;
} else {
/* Otherwise there is now an additional call depth */
sync_depth++;
}
}
void int_resume(void) {
/* If there is one or no call depths, reenable interrupts */
if (sync_depth == 0 || sync_depth == 1) {
SYNC_STI();
} else {
sync_depth--;
}
}
void int_enable(void) {
sync_depth = 0;
SYNC_STI();
}
/* Interrupt Requests */
2013-06-06 10:10:36 +04:00
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);
2011-01-16 07:17:42 +03:00
2015-05-21 02:52:19 +03:00
static void (*irqs[])(void) = {
_irq0, _irq1, _irq2, _irq3, _irq4, _irq5, _irq6, _irq7,
_irq8, _irq9, _irq10, _irq11, _irq12, _irq13, _irq14, _irq15
};
#define IRQ_CHAIN_SIZE (sizeof(irqs)/sizeof(*irqs))
#define IRQ_CHAIN_DEPTH 4
static irq_handler_chain_t irq_routines[IRQ_CHAIN_SIZE * IRQ_CHAIN_DEPTH] = { NULL };
2011-01-16 07:17:42 +03:00
void irq_install_handler(size_t irq, irq_handler_chain_t handler) {
2015-05-21 02:52:19 +03:00
/* Disable interrupts when changing handlers */
SYNC_CLI();
for (size_t i = 0; i < IRQ_CHAIN_DEPTH; i++) {
if (irq_routines[i * IRQ_CHAIN_SIZE + irq])
continue;
irq_routines[i * IRQ_CHAIN_SIZE + irq] = handler;
break;
}
2015-05-21 02:52:19 +03:00
SYNC_STI();
2011-01-16 07:17:42 +03:00
}
2013-06-06 10:10:36 +04:00
void irq_uninstall_handler(size_t irq) {
2015-05-21 02:52:19 +03:00
/* Disable interrupts when changing handlers */
SYNC_CLI();
for (size_t i = 0; i < IRQ_CHAIN_DEPTH; i++)
irq_routines[i * IRQ_CHAIN_SIZE + irq] = NULL;
SYNC_STI();
}
2015-05-21 06:08:58 +03:00
static void irq_remap(void) {
/* Send EOI on each PIC just in case the boot loader messed up */
outportb(PIC1_COMMAND, PIC_EOI); PIC_WAIT();
outportb(PIC2_COMMAND, PIC_EOI); PIC_WAIT();
/* Cascade initialization */
outportb(PIC1_COMMAND, ICW1_INIT|ICW1_ICW4); PIC_WAIT();
outportb(PIC2_COMMAND, ICW1_INIT|ICW1_ICW4); PIC_WAIT();
/* Remap */
outportb(PIC1_DATA, PIC1_OFFSET); PIC_WAIT();
outportb(PIC2_DATA, PIC2_OFFSET); PIC_WAIT();
2015-05-21 02:52:19 +03:00
2015-05-21 06:08:58 +03:00
/* Cascade identity with slave PIC at IRQ2 */
outportb(PIC1_DATA, 0x04); PIC_WAIT();
outportb(PIC2_DATA, 0x02); PIC_WAIT();
/* Request 8086 mode on each PIC */
outportb(PIC1_DATA, 0x01); PIC_WAIT();
outportb(PIC2_DATA, 0x01); PIC_WAIT();
2011-01-16 07:17:42 +03:00
}
2015-05-21 02:52:19 +03:00
static void irq_setup_gates(void) {
for (size_t i = 0; i < IRQ_CHAIN_SIZE; i++)
idt_set_gate(32 + i, irqs[i], 0x08, 0x8E);
2011-05-07 10:55:49 +04:00
}
2013-06-06 10:10:36 +04:00
void irq_install(void) {
2011-05-07 10:55:49 +04:00
irq_remap();
2015-05-21 02:52:19 +03:00
irq_setup_gates();
2011-01-16 07:17:42 +03:00
}
2013-06-06 10:10:36 +04:00
void irq_ack(size_t irq_no) {
2014-05-07 10:27:58 +04:00
if (irq_no >= 8) {
2015-05-21 06:08:58 +03:00
outportb(PIC2_COMMAND, PIC_EOI);
}
2015-05-21 06:08:58 +03:00
outportb(PIC1_COMMAND, PIC_EOI);
}
2013-06-06 10:10:36 +04:00
void irq_handler(struct regs *r) {
2015-05-21 06:08:58 +03:00
/* Disable interrupts when handling */
SYNC_CLI();
if (r->int_no <= 47 && r->int_no >= 32) {
for (size_t i = 0; i < IRQ_CHAIN_DEPTH; i++) {
irq_handler_chain_t handler = irq_routines[i * IRQ_CHAIN_SIZE + (r->int_no - 32)];
if (handler && handler(r)) {
goto done;
}
2015-05-21 02:52:19 +03:00
}
2015-05-21 06:08:58 +03:00
irq_ack(r->int_no - 32);
2015-05-21 02:52:19 +03:00
}
2015-05-21 06:08:58 +03:00
done:
SYNC_STI();
}