2 pcboot - bootable PC demo/game kernel
3 Copyright (C) 2018 John Tsiombikas <nuclear@member.fsf.org>
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY, without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program. If not, see <https://www.gnu.org/licenses/>.
26 #define SYSCALL_INT 0x80
28 /* IDT gate descriptor bits */
29 #define GATE_TASK (5 << 8)
30 #define GATE_INTR (6 << 8)
31 #define GATE_TRAP (7 << 8)
32 #define GATE_DEFAULT (1 << 11)
33 #define GATE_PRESENT (1 << 15)
35 /* PIC command and data ports */
37 #define PIC1_DATA 0x21
39 #define PIC2_DATA 0xa1
41 /* PIC initialization command word 1 bits */
42 #define ICW1_ICW4_NEEDED (1 << 0)
43 #define ICW1_SINGLE (1 << 1)
44 #define ICW1_INTERVAL4 (1 << 2)
45 #define ICW1_LEVEL (1 << 3)
46 #define ICW1_INIT (1 << 4)
47 /* PIC initialization command word 4 bits */
48 #define ICW4_8086 (1 << 0)
49 #define ICW4_AUTO_EOI (1 << 1)
50 #define ICW4_BUF_SLAVE (1 << 3) /* 1000 */
51 #define ICW4_BUF_MASTER (3 << 2) /* 1100 */
52 #define ICW4_SPECIAL (1 << 4)
54 /* PIC operation command word 2 bits */
55 #define OCW2_EOI (1 << 5)
59 static void gate_desc(desc_t *desc, uint16_t sel, uint32_t addr, int dpl, int type);
61 /* defined in intr_asm.S */
62 void set_idt(uint32_t addr, uint16_t limit);
63 void intr_entry_default(void);
64 void irq7_entry_check_spurious(void);
65 void irq15_entry_check_spurious(void);
67 /* the IDT (interrupt descriptor table) */
68 static desc_t idt[256] __attribute__((aligned(8)));
70 /* table of handler functions for all interrupts */
71 static intr_func_t intr_func[256];
73 static struct intr_frame *cur_intr_frame;
74 static int eoi_pending;
76 #define INTR_ENTRY_EC(n, name) \
77 void intr_entry_##name(void); \
78 set_intr_entry(n, intr_entry_##name);
79 #define INTR_ENTRY_NOEC(n, name) INTR_ENTRY_EC(n, name)
85 set_idt((uint32_t)idt, sizeof idt - 1);
87 /* initialize all entry points and interrupt handlers */
88 for(i=0; i<256; i++) {
89 set_intr_entry(i, intr_entry_default);
93 /* by including intrtab.h here the series of INTR_ENTRY_* macros will be
94 * expanded to a series of function prototypes for all interrupt entry
95 * points and the corresponding calls to set_intr_entry to set up the IDT
100 /* change irq7 and irq15 to special entry points which first
101 * make sure we didn't get a spurious interrupt before proceeding
103 set_intr_entry(IRQ_TO_INTR(7), irq7_entry_check_spurious);
104 set_intr_entry(IRQ_TO_INTR(15), irq15_entry_check_spurious);
106 /* initialize the programmable interrupt controller
107 * setting up the maping of IRQs [0, 15] to interrupts [32, 47]
109 prog_pic(IRQ_OFFSET);
113 void cleanup_intr(void)
116 /* reprogram the PIC to the default BIOS offset (8) */
120 /* retrieve the current interrupt frame.
121 * returns 0 when called during init.
123 struct intr_frame *get_intr_frame(void)
125 return cur_intr_frame;
128 /* set an interrupt handler function for a particular interrupt */
129 void interrupt(int intr_num, intr_func_t func)
131 int iflag = get_intr_flag();
133 intr_func[intr_num] = func;
134 set_intr_flag(iflag);
137 /* this function is called from all interrupt entry points
138 * it calls the appropriate interrupt handlers if available and handles
139 * sending an end-of-interrupt command to the PICs when finished.
141 void dispatch_intr(struct intr_frame frm)
143 cur_intr_frame = &frm;
145 if(IS_IRQ(frm.inum)) {
146 eoi_pending = frm.inum;
149 if(intr_func[frm.inum]) {
150 intr_func[frm.inum](frm.inum);
153 panic("unhandled exception %u, error code: %u, at cs:eip=%x:%x\n",
154 frm.inum, frm.err, frm.cs, frm.eip);
156 /*printf("unhandled interrupt %d\n", frm.inum);*/
161 end_of_irq(INTR_TO_IRQ(eoi_pending));
165 void prog_pic(int offs)
167 /* send ICW1 saying we'll follow with ICW4 later on */
168 outp(PIC1_CMD, ICW1_INIT | ICW1_ICW4_NEEDED);
169 outp(PIC2_CMD, ICW1_INIT | ICW1_ICW4_NEEDED);
170 /* send ICW2 with IRQ remapping */
171 outp(PIC1_DATA, offs);
172 outp(PIC2_DATA, offs + 8);
173 /* send ICW3 to setup the master/slave relationship */
174 /* ... set bit3 = 3rd interrupt input has a slave */
176 /* ... set slave ID to 2 */
178 /* send ICW4 to set 8086 mode (no calls generated) */
179 outp(PIC1_DATA, ICW4_8086);
180 outp(PIC2_DATA, ICW4_8086);
181 /* done, just reset the data port to 0 */
186 static void gate_desc(desc_t *desc, uint16_t sel, uint32_t addr, int dpl, int type)
188 /* first 16bit part is the low 16bits of the entry address */
189 desc->d[0] = addr & 0xffff;
190 /* second 16bit part is the segment selector for the entry code */
192 /* third 16bit part has the privilege level, type, and present bit */
193 desc->d[2] = ((dpl & 3) << 13) | type | GATE_DEFAULT | GATE_PRESENT;
194 /* last 16bit part is the high 16bits of the entry address */
195 desc->d[3] = (addr & 0xffff0000) >> 16;
198 #define IS_TRAP(n) ((n) >= 32 && !IS_IRQ(n))
199 void set_intr_entry(int num, void (*handler)(void))
201 int type = IS_TRAP(num) ? GATE_TRAP : GATE_INTR;
203 /* the syscall interrupt has to have a dpl of 3 otherwise calling it from
204 * user space will raise a general protection exception. All the rest should
205 * have a dpl of 0 to disallow user programs to execute critical interrupt
206 * handlers and possibly crashing the system.
208 int dpl = (num == SYSCALL_INT) ? 3 : 0;
210 gate_desc(idt + num, selector(SEGM_CODE, 0), (uint32_t)handler, dpl, type);
213 void set_pic_mask(int pic, unsigned char mask)
215 outp(pic > 0 ? PIC2_DATA : PIC1_DATA, mask);
218 unsigned char get_pic_mask(int pic)
220 return inp(pic > 0 ? PIC2_DATA : PIC1_DATA);
223 void mask_irq(int irq)
235 mask = inp(port) | (1 << irq);
239 void unmask_irq(int irq)
251 mask = inp(port) & ~(1 << irq);
256 void end_of_irq(int irq)
258 int intr_state = get_intr_flag();
262 set_intr_flag(intr_state);
268 outp(PIC2_CMD, OCW2_EOI);
270 outp(PIC1_CMD, OCW2_EOI);
272 set_intr_flag(intr_state);
275 #ifdef ENABLE_GDB_STUB
276 void exceptionHandler(int id, void (*func)())
278 set_intr_entry(id, func);