kern

annotate src/intr.c @ 55:88a6c4e192f9

Fixed most important task switching bugs. Now it seems that I can switch in and out of user space reliably.
author John Tsiombikas <nuclear@member.fsf.org>
date Mon, 15 Aug 2011 04:03:39 +0300
parents fa65b4f45366
children
rev   line source
nuclear@11 1 #include <stdio.h>
nuclear@11 2 #include "intr.h"
nuclear@11 3 #include "desc.h"
nuclear@11 4 #include "segm.h"
nuclear@11 5 #include "asmops.h"
nuclear@11 6 #include "panic.h"
nuclear@55 7 #include "syscall.h"
nuclear@11 8
nuclear@11 9 /* IDT gate descriptor bits */
nuclear@11 10 #define GATE_TASK (5 << 8)
nuclear@11 11 #define GATE_INTR (6 << 8)
nuclear@11 12 #define GATE_TRAP (7 << 8)
nuclear@11 13 #define GATE_DEFAULT (1 << 11)
nuclear@11 14 #define GATE_PRESENT (1 << 15)
nuclear@11 15
nuclear@11 16 /* PIC command and data ports */
nuclear@11 17 #define PIC1_CMD 0x20
nuclear@11 18 #define PIC1_DATA 0x21
nuclear@11 19 #define PIC2_CMD 0xa0
nuclear@11 20 #define PIC2_DATA 0xa1
nuclear@11 21
nuclear@11 22 /* PIC initialization command word 1 bits */
nuclear@11 23 #define ICW1_ICW4_NEEDED (1 << 0)
nuclear@11 24 #define ICW1_SINGLE (1 << 1)
nuclear@11 25 #define ICW1_INTERVAL4 (1 << 2)
nuclear@11 26 #define ICW1_LEVEL (1 << 3)
nuclear@11 27 #define ICW1_INIT (1 << 4)
nuclear@11 28 /* PIC initialization command word 4 bits */
nuclear@11 29 #define ICW4_8086 (1 << 0)
nuclear@11 30 #define ICW4_AUTO_EOI (1 << 1)
nuclear@11 31 #define ICW4_BUF_SLAVE (1 << 3) /* 1000 */
nuclear@11 32 #define ICW4_BUF_MASTER (3 << 2) /* 1100 */
nuclear@11 33 #define ICW4_SPECIAL (1 << 4)
nuclear@11 34
nuclear@11 35 /* PIC operation command word 2 bits */
nuclear@11 36 #define OCW2_EOI (1 << 5)
nuclear@11 37
nuclear@11 38
nuclear@11 39 static void init_pic(int offset);
nuclear@11 40 static void gate_desc(desc_t *desc, uint16_t sel, uint32_t addr, int dpl, int type);
nuclear@11 41 static void set_intr_entry(int num, void (*handler)(void));
nuclear@11 42
nuclear@11 43 /* defined in intr-asm.S */
nuclear@11 44 void set_idt(uint32_t addr, uint16_t limit);
nuclear@11 45 void intr_entry_default(void);
nuclear@11 46
nuclear@11 47 /* the IDT (interrupt descriptor table) */
nuclear@11 48 static desc_t idt[256];
nuclear@11 49 /* table of handler functions for all interrupts */
nuclear@11 50 static intr_func_t intr_func[256];
nuclear@11 51
nuclear@52 52 static struct intr_frame *cur_intr_frame;
nuclear@55 53 static int eoi_pending;
nuclear@52 54
nuclear@11 55
nuclear@11 56 void init_intr(void)
nuclear@11 57 {
nuclear@11 58 int i;
nuclear@11 59
nuclear@11 60 set_idt((uint32_t)idt, sizeof idt - 1);
nuclear@11 61
nuclear@11 62 /* initialize all entry points and interrupt handlers */
nuclear@11 63 for(i=0; i<256; i++) {
nuclear@11 64 set_intr_entry(i, intr_entry_default);
nuclear@11 65 interrupt(i, 0);
nuclear@11 66 }
nuclear@11 67
nuclear@11 68 /* by including interrupts.h here (without ASM being defined)
nuclear@11 69 * the series of INTR_ENTRY_* macros will be expanded to a series
nuclear@11 70 * of function prototypes for all interrupt entry points and the
nuclear@11 71 * corresponding calls to set_intr_entry to set up the IDT slots
nuclear@11 72 */
nuclear@11 73 #include "interrupts.h"
nuclear@11 74
nuclear@11 75 /* initialize the programmable interrupt controller
nuclear@11 76 * setting up the maping of IRQs [0, 15] to interrupts [32, 47]
nuclear@11 77 */
nuclear@11 78 init_pic(IRQ_OFFSET);
nuclear@55 79 eoi_pending = 0;
nuclear@11 80 }
nuclear@11 81
nuclear@52 82 /* retrieve the current interrupt frame.
nuclear@52 83 * returns 0 when called during kernel init.
nuclear@52 84 */
nuclear@52 85 struct intr_frame *get_intr_frame(void)
nuclear@52 86 {
nuclear@52 87 return cur_intr_frame;
nuclear@52 88 }
nuclear@52 89
nuclear@11 90 /* set an interrupt handler function for a particular interrupt */
nuclear@11 91 void interrupt(int intr_num, intr_func_t func)
nuclear@11 92 {
nuclear@11 93 intr_func[intr_num] = func;
nuclear@11 94 }
nuclear@11 95
nuclear@11 96 /* this function is called from all interrupt entry points
nuclear@11 97 * it calls the appropriate interrupt handlers if available and handles
nuclear@11 98 * sending an end-of-interrupt command to the PICs when finished.
nuclear@11 99 */
nuclear@11 100 void dispatch_intr(struct intr_frame frm)
nuclear@11 101 {
nuclear@52 102 cur_intr_frame = &frm;
nuclear@52 103
nuclear@55 104 if(IS_IRQ(frm.inum)) {
nuclear@55 105 eoi_pending = frm.inum;
nuclear@55 106 }
nuclear@55 107
nuclear@11 108 if(intr_func[frm.inum]) {
nuclear@52 109 intr_func[frm.inum](frm.inum);
nuclear@11 110 } else {
nuclear@11 111 if(frm.inum < 32) {
nuclear@11 112 panic("unhandled exception %d, error code: %d\n", frm.inum, frm.err);
nuclear@11 113 }
nuclear@11 114 printf("unhandled interrupt %d\n", frm.inum);
nuclear@11 115 }
nuclear@11 116
nuclear@55 117 disable_intr();
nuclear@55 118 if(eoi_pending) {
nuclear@55 119 end_of_irq(INTR_TO_IRQ(eoi_pending));
nuclear@11 120 }
nuclear@11 121 }
nuclear@11 122
nuclear@11 123 static void init_pic(int offset)
nuclear@11 124 {
nuclear@11 125 /* send ICW1 saying we'll follow with ICW4 later on */
nuclear@11 126 outb(ICW1_INIT | ICW1_ICW4_NEEDED, PIC1_CMD);
nuclear@11 127 outb(ICW1_INIT | ICW1_ICW4_NEEDED, PIC2_CMD);
nuclear@11 128 /* send ICW2 with IRQ remapping */
nuclear@11 129 outb(offset, PIC1_DATA);
nuclear@11 130 outb(offset + 8, PIC2_DATA);
nuclear@11 131 /* send ICW3 to setup the master/slave relationship */
nuclear@11 132 /* ... set bit3 = 3rd interrupt input has a slave */
nuclear@11 133 outb(4, PIC1_DATA);
nuclear@11 134 /* ... set slave ID to 2 */
nuclear@11 135 outb(2, PIC2_DATA);
nuclear@11 136 /* send ICW4 to set 8086 mode (no calls generated) */
nuclear@11 137 outb(ICW4_8086, PIC1_DATA);
nuclear@11 138 outb(ICW4_8086, PIC2_DATA);
nuclear@11 139 /* done, just reset the data port to 0 */
nuclear@11 140 outb(0, PIC1_DATA);
nuclear@11 141 outb(0, PIC2_DATA);
nuclear@11 142 }
nuclear@11 143
nuclear@11 144 static void gate_desc(desc_t *desc, uint16_t sel, uint32_t addr, int dpl, int type)
nuclear@11 145 {
nuclear@11 146 /* first 16bit part is the low 16bits of the entry address */
nuclear@11 147 desc->d[0] = addr & 0xffff;
nuclear@11 148 /* second 16bit part is the segment selector for the entry code */
nuclear@11 149 desc->d[1] = sel;
nuclear@11 150 /* third 16bit part has the privilege level, type, and present bit */
nuclear@11 151 desc->d[2] = ((dpl & 3) << 13) | type | GATE_DEFAULT | GATE_PRESENT;
nuclear@11 152 /* last 16bit part is the high 16bits of the entry address */
nuclear@11 153 desc->d[3] = (addr & 0xffff0000) >> 16;
nuclear@11 154 }
nuclear@11 155
nuclear@29 156 #define IS_TRAP(n) ((n) >= 32 && !IS_IRQ(n))
nuclear@11 157 static void set_intr_entry(int num, void (*handler)(void))
nuclear@11 158 {
nuclear@11 159 int type = IS_TRAP(num) ? GATE_TRAP : GATE_INTR;
nuclear@55 160
nuclear@55 161 /* the syscall interrupt has to have a dpl of 3 otherwise calling it from
nuclear@55 162 * user space will raise a general protection exception. All the rest should
nuclear@55 163 * have a dpl of 0 to disallow user programs to execute critical interrupt
nuclear@55 164 * handlers and possibly crashing the system.
nuclear@55 165 */
nuclear@55 166 int dpl = (num == SYSCALL_INT) ? 3 : 0;
nuclear@55 167
nuclear@55 168 gate_desc(idt + num, selector(SEGM_KCODE, 0), (uint32_t)handler, dpl, type);
nuclear@11 169 }
nuclear@11 170
nuclear@51 171 void end_of_irq(int irq)
nuclear@11 172 {
nuclear@55 173 int intr_state = get_intr_state();
nuclear@55 174 disable_intr();
nuclear@55 175
nuclear@55 176 if(!eoi_pending) {
nuclear@55 177 return;
nuclear@55 178 }
nuclear@55 179 eoi_pending = 0;
nuclear@55 180
nuclear@11 181 if(irq > 7) {
nuclear@11 182 outb(OCW2_EOI, PIC2_CMD);
nuclear@11 183 }
nuclear@11 184 outb(OCW2_EOI, PIC1_CMD);
nuclear@55 185
nuclear@55 186 set_intr_state(intr_state);
nuclear@11 187 }