Chameleon

Chameleon Svn Source Tree

Root/trunk/i386/libsa/interrupts.c

1#ifdef __i386__
2
3#include "libsa.h"
4#include "saio_internal.h"
5
6#ifdef DEBUG_INTERRUPTS
7#define DBG(x...) printf(x)
8#else
9#define DBG(x...)
10#endif
11
12#define CODE_SEGMENT_SELECTOR 0x28U
13#define CODED_EXCEPTION_MASK 0x27D00U
14#define IA32_APIC_BASE 27U
15#define IA32_APIC_BASE_BSP 0x100U
16#define IA32_APIC_BASE_EN 0x800U
17#define LAPIC_WANTED_FLAGS (IA32_APIC_BASE_EN | IA32_APIC_BASE_BSP)
18#define LAPIC_EOI_OFFSET 0xB0U
19#define LAPIC_ISR_OFFSET 0x100U
20#define PIC_READ_ISR_COMMAND 11U
21#define PIC_EOI_COMMAND 0x20U
22#define PIC_PORT0 0
23#define PIC_PORT1 1
24
25#define BDA_TICK_COUNT 0x46CU// DWORD
26#define BDA_MIDNIGHT_FLAG 0x470U// BYTE
27#define BDA_24HR_TURNOVER 0x1800B0U
28
29enum InterruptSources
30{
31IS_Unknown = 0,
32IS_APIC = 1,// Covers both LAPIC and IOAPIC
33IS_PIC0 = 2,
34IS_PIC1 = 3,
35IS_Software = 4
36};
37
38struct InterruptFrame
39{
40uint32_t edx;
41uint32_t ecx;
42uint32_t eax;
43uint32_t index;
44uint32_t eip;
45uint32_t cs;
46uint32_t eflags;
47};
48
49struct ExceptionFrame
50{
51uint32_t edi;
52uint32_t esi;
53uint32_t ebp;
54uint32_t esp;
55uint32_t ebx;
56uint32_t edx;
57uint32_t ecx;
58uint32_t eax;
59uint32_t index;
60uint32_t exception_code;
61uint32_t eip;
62uint32_t cs;
63uint32_t eflags;
64};
65
66struct InterruptGate
67{
68uint16_t offset_0_15;
69uint16_t selector;
70uint16_t flags;
71uint16_t offset_16_31;
72};
73
74#pragma mark -
75#pragma mark Global Data
76#pragma mark -
77
78extern
79uint16_t Idtr_prot[];
80
81static
82uint32_t* counters = NULL;
83
84static
85uint32_t lapic_base = 0U;
86
87static
88uint8_t const PicPorts[2][2] = { { 0x20U, 0x21U }, { 0xA0U, 0xA1U } };
89
90#pragma mark -
91#pragma mark Assembly Stubs
92#pragma mark -
93
94static
95__attribute__((naked, noreturn))
96void InterruptStub(void)
97{
98__asm__ volatile ("pushl %%eax\n\t"
99 "pushl %%ecx\n\t"
100 "pushl %%edx\n\t"
101 "pushl %%esp\n\t"
102 "calll _InterruptHandler\n\t"
103 "addl $4, %%esp\n\t"
104 "popl %%edx\n\t"
105 "popl %%ecx\n\t"
106 "popl %%eax\n\t"
107 "addl $4, %%esp\n\t"
108 "iretl"
109 :);
110__builtin_unreachable();
111}
112
113static
114__attribute__((naked, noreturn))
115void ExceptionWithCodeStub(void)
116{
117__asm__ volatile("testl $-57, 8(%%esp)\n\t"
118 "je 0f\n\t"
119 "pushal\n\t"
120 "jmp 1f\n"
121 "_ExceptionNoCodeStub:\n"
122 "0:\tsub $4, %%esp\n\t"
123 "pushal\n\t"
124 "xorl %%eax, %%eax\n\t"
125 "xchgl %%eax, 36(%%esp)\n\t"
126 "movl %%eax, 32(%%esp)\n"
127 "1:\taddl $20, 12(%%esp)\n\t"
128 "pushl %%esp\n\t"
129 "calll _ExceptionHandler\n\t"
130 "addl $4, %%esp\n\t"
131 "popal\n\t"
132 "addl $8, %%esp\n\t"
133 "iretl"
134 :);
135__builtin_unreachable();
136}
137
138/*
139 * Make _ExceptionNoCodeStub accessible to C
140 */
141static
142__attribute__((naked, noreturn, weakref("ExceptionNoCodeStub")))
143void ExceptionNoCodeStubAlias(void);
144
145static
146__attribute__((/* naked, */noinline, regparm(1), section("__INIT,__text")))
147void DispatchBiosVector(uint8_t vector)
148{
149__asm__ volatile ("movb %0, 0f + 1\n\t"
150 "calll __prot_to_real\n\t"
151 ".code16\n"
152 "0:\tint $0\n\t"
153 "calll __real_to_prot\n\t"
154 ".code32"
155 : : "r"(vector));
156}
157
158static
159__attribute__((noreturn, noinline, section("__INIT,__text")))
160void DisplayErrorAndStop(void)
161{
162__asm__ volatile ("calll __prot_to_real\n\t"
163 ".code16\n\t"
164 "movw $2, %%ax\n\t"
165 "int $0x10\n\t"
166 "xorw %%ax, %%ax\n\t"
167 "movw %%ax, %%ds\n\t"
168 "movw $0x6000, %%si\n\t"
169 "cld\n"
170 "0:\tlodsb\n\t"
171 "testb %%al, %%al\n\t"
172 "je 1f\n\t"
173 "movb $0xE, %%ah\n\t"
174 "movw $0xFF, %%bx\n\t"
175 "int $0x10\n\t"
176 "jmp 0b\n"
177 "1:\thlt\n\t"
178 "jmp 1b\n\t"
179 ".code32"
180 :);
181__builtin_unreachable();
182}
183
184#pragma mark -
185#pragma mark Other Inline Assembly
186#pragma mark -
187
188static inline
189uint32_t ReadLapic(uint32_t offset)
190{
191return *(uint32_t const volatile*) (lapic_base + offset);
192}
193
194static inline
195void WriteLapic(uint32_t offset, uint32_t value)
196{
197*(uint32_t volatile*) (lapic_base + offset) = value;
198}
199
200static inline
201uint8_t ReadPic(int pic, int index)
202{
203uint8_t value;
204__asm__ volatile ("inb %1, %0" : "=a"(value) : "N"(PicPorts[pic][index]));
205return value;
206}
207
208static inline
209void WritePic(int pic, int index, uint8_t value)
210{
211__asm__ volatile ("outb %0, %1" : : "a"(value), "N"(PicPorts[pic][index]));
212}
213
214#pragma mark -
215#pragma mark Main Code
216#pragma mark -
217
218static
219int IdentifyInterruptSource(uint8_t vector, uint32_t eip)
220{
221if (lapic_base)
222{
223uint32_t value = ReadLapic(LAPIC_ISR_OFFSET + ((vector & 0xE0U) >> 1));
224if (value & (1U << (vector & 31U)))
225return IS_APIC;
226}
227if (vector >= 8U && vector < 16U)
228{
229uint8_t value;
230WritePic(0, PIC_PORT0, PIC_READ_ISR_COMMAND);
231value = ReadPic(0, PIC_PORT0);
232if (value & (1U << (vector & 7U)))
233return IS_PIC0;
234}
235if (vector >= 0x70U && vector < 0x78U)
236{
237uint8_t value;
238WritePic(1, PIC_PORT0, PIC_READ_ISR_COMMAND);
239value = ReadPic(1, PIC_PORT0);
240if (value & (1U << (vector & 7U)))
241return IS_PIC1;
242}
243if (eip)
244{
245uint8_t const volatile* pInstruction = (uint8_t const volatile*) (eip - 2U);
246if ((*pInstruction) == 0xCDU && pInstruction[1] == vector)
247return IS_Software;
248/*
249 * There are other software interrupt opcodes
250 * debug breakpoint 0xCC
251 * interrupt on overflow 0xCE
252 * bound instruction 0x62
253 * but those all trigger specific vectors, so are handled as exceptions.
254 */
255}
256return IS_Unknown;
257}
258
259static
260void SignalEOI(int source)
261{
262switch (source)
263{
264case IS_APIC:
265if (lapic_base)
266WriteLapic(LAPIC_EOI_OFFSET, 0U);
267break;
268case IS_PIC1:
269WritePic(1, PIC_PORT0, PIC_EOI_COMMAND);
270case IS_PIC0:
271WritePic(0, PIC_PORT0, PIC_EOI_COMMAND);
272default:
273break;
274}
275}
276
277static
278void HandleIRQ(int source, uint8_t vector)
279{
280if (source == IS_PIC0 && vector == 8U)
281{
282uint32_t* pTickCount = (uint32_t*) BDA_TICK_COUNT;
283if (++(*pTickCount) == BDA_24HR_TURNOVER)
284{
285*pTickCount = 0U;
286++(*(uint8_t*)BDA_MIDNIGHT_FLAG);
287}
288SignalEOI(source);
289return;
290}
291/*
292 * Default Approach: send to bios
293 */
294DispatchBiosVector(vector);
295}
296
297static
298__attribute__((used))
299void ExceptionHandler(struct ExceptionFrame* pFrame)
300{
301uint8_t vector;
302int interruptSource;
303char* errorString;
304
305/*
306 * FIXME: Should check if 0x10000U <= ESP <= 0x1FFFF0 here and switch stacks if not.
307 */
308if (!pFrame)
309{
310return;
311}
312vector = (uint8_t) pFrame->index;
313if (counters)
314++counters[vector];
315interruptSource = IdentifyInterruptSource(vector, pFrame->eip);
316switch (interruptSource)
317{
318case IS_APIC:
319case IS_PIC0:
320case IS_PIC1:
321HandleIRQ(interruptSource, vector);
322case IS_Software:
323return;
324default:
325break;
326}
327errorString = (char*) 0x6000U;
328switch (vector)
329{
330case 0U:
331strcpy(errorString, "Division By Zero Exception");
332break;
333case 1U:
334strcpy(errorString, "Debug Exception");
335break;
336case 2U:
337strcpy(errorString, "NMI Interrupt");
338break;
339case 3U:
340strcpy(errorString, "Debug Breakpoint");
341break;
342case 4U:
343strcpy(errorString, "Overflow Exception");
344break;
345case 5U:
346strcpy(errorString, "BOUND Range Exception");
347break;
348case 6U:
349strcpy(errorString, "Invalid Opcode Exception");
350break;
351case 7U:
352strcpy(errorString, "Math Coprocessor Unavailable Exception");
353break;
354case 8U:
355strcpy(errorString, "Double Fault");
356break;
357case 9U:
358strcpy(errorString, "Coprocessor Segment Overrun Exception");
359break;
360case 10U:
361strcpy(errorString, "Invalid TSS Exception");
362break;
363case 11U:
364strcpy(errorString, "Segment Not Present Exception");
365break;
366case 12U:
367strcpy(errorString, "Stack-Segment Fault");
368break;
369case 13U:
370strcpy(errorString, "General Protection Fault");
371break;
372case 14U:
373strcpy(errorString, "Page Fault");
374break;
375case 16U:
376strcpy(errorString, "x87 FPU Floating-Point Error");
377break;
378case 17U:
379strcpy(errorString, "Alignment Check Exception");
380break;
381case 18U:
382strcpy(errorString, "Machine Check Exception");
383break;
384case 19U:
385strcpy(errorString, "SIMD Floating-Point Exception");
386break;
387case 20U:
388strcpy(errorString, "Virtualization Exception");
389break;
390default:
391sprintf(errorString, "Unknown Exception Vector %d", (int) vector);
392break;
393}
394errorString += strlen(errorString);
395
396errorString += sprintf(errorString, "\r\nEDI 0x%x, ESI 0x%x, EBP 0x%x, ESP 0x%x",
397 pFrame->edi, pFrame->esi, pFrame->ebp, pFrame->esp);
398errorString += sprintf(errorString, "\r\nEBX 0x%x, EDX 0x%x, ECX 0x%x, EAX 0x%x",
399 pFrame->ebx, pFrame->edx, pFrame->ecx, pFrame->eax);
400errorString += sprintf(errorString, "\r\nException Code 0x%x, EIP 0x%x, CS 0x%x, EFLAGS 0x%x\r\nSystem Halted\r\n",
401 pFrame->exception_code, pFrame->eip, pFrame->cs, pFrame->eflags);
402DisplayErrorAndStop();
403}
404
405static
406__attribute__((used))
407void InterruptHandler(struct InterruptFrame* pFrame)
408{
409uint8_t vector;
410int interruptSource;
411
412if (!pFrame)
413{
414return;
415}
416vector = (uint8_t) pFrame->index;
417if (counters)
418++counters[vector];
419interruptSource = IdentifyInterruptSource(vector, pFrame->eip);
420switch (interruptSource)
421{
422case IS_APIC:
423case IS_PIC0:
424case IS_PIC1:
425HandleIRQ(interruptSource, vector);
426default:
427break;
428}
429}
430
431#if UNUSED
432void dumpMasks(void)
433{
434int idx;
435uint8_t port_val;
436uint8_t volatile* apic_index;
437uint32_t const volatile* apic_data;
438
439port_val = ReadPic(0, 1);
440DBG("pic0 Masks 0x%x\n", port_val);
441port_val = ReadPic(1, 1);
442DBG("pic1 Masks 0x%x\n", port_val);
443getchar();
444DBG("IOAPIC vectors\n");
445apic_index = (uint8_t volatile*) 0xFEC00000U;
446apic_data = (uint32_t const volatile*) 0xFEC00010U;
447for (idx = 0; idx != 24; ++idx)
448{
449uint32_t v1, v2;
450*apic_index = (uint8_t) (16U + 2U * (unsigned) idx);
451v1 = *apic_data;
452if (v1 & 0x10000U)
453continue;
454*apic_index = (uint8_t) (16U + 2U * (unsigned) idx + 1U);
455v2 = *apic_data;
456DBG("index %d vector 0x%x%08x\n", idx, v2, v1);
457}
458getchar();
459if (!lapic_base)
460return;
461DBG("LAPIC vectors\n");
462for (idx = 0; idx != 7; ++idx)
463{
464uint32_t offs, v;
465if (!idx)
466offs = 0x2F0U;
467else
468offs = 0x320U + 16U * (unsigned) (idx - 1);
469v = ReadLapic(offs);
470if (v & 0x10000U)
471continue;
472DBG("index %d vector 0x%x\n", idx, v);
473}
474}
475#endif
476
477void ShowInterruptCounters(void)
478{
479int j;
480
481if (!counters)
482return;
483msglog("Interrupt Counters\n");
484for (j = 0; j != 256; ++j)
485if (counters[j])
486msglog("counters[%d] == %d\n", j, counters[j]);
487}
488
489int SetupInterrupts(void)
490{
491int idx;
492uint32_t stub_address;
493uint64_t ia32_apic_base;
494size_t const total = 2048U + 2048U + 1024U;
495uint8_t* workArea = (uint8_t*) malloc(total);
496if (!workArea)
497{
498msglog("%s: Memory Allocation Failed\n", __FUNCTION__);
499return 0;
500}
501counters = (uint32_t*) (workArea + 4096);
502bzero(counters, 1024U);
503for (idx = 0; idx != 256; ++idx)
504{
505struct InterruptGate* gate = (struct InterruptGate*) (workArea + idx * sizeof(struct InterruptGate));
506uint8_t* thunk = workArea + 2048 + idx * 8;
507gate->offset_0_15 = ((uint32_t) thunk) & 0xFFFFU;
508gate->selector = CODE_SEGMENT_SELECTOR;
509gate->flags = 0x8E00U; // Interrupt Gate, Present, DPL 0, 32-bit
510gate->offset_16_31 = (((uint32_t) thunk) >> 16) & 0xFFFFU;
511thunk[0] = 0x6AU;// push byte
512thunk[1] = (uint8_t) idx;
513thunk[2] = 0xE9U;// jmp rel32
514if (idx >= 32)
515stub_address = (uint32_t) &InterruptStub;
516else if ((1U << idx) & CODED_EXCEPTION_MASK)
517stub_address = (uint32_t) &ExceptionWithCodeStub;
518else
519stub_address = (uint32_t) &ExceptionNoCodeStubAlias;
520*(uint32_t*) (&thunk[3]) = stub_address - (uint32_t) &thunk[7];
521thunk[7] = 0x90U;// nop
522}
523Idtr_prot[0] = 0x7FFU;
524Idtr_prot[1] = ((uint32_t) workArea) & 0xFFFFU;
525Idtr_prot[2] = (((uint32_t) workArea) >> 16) & 0xFFFFU;
526__asm__ volatile ("lidt %0" : : "m"(Idtr_prot[0]));
527__asm__ volatile ("rdmsr" : "=A"(ia32_apic_base) : "c"((uint32_t) IA32_APIC_BASE));
528if ((ia32_apic_base & LAPIC_WANTED_FLAGS) == LAPIC_WANTED_FLAGS &&
529!((ia32_apic_base >> 32) & 255U))
530lapic_base = ((uint32_t) ia32_apic_base) & ~0xFFFU;
531DBG("%s: Work Area 0x%x, lapic_base 0x%x\n", __FUNCTION__, (uint32_t) workArea, lapic_base);
532return 1;
533}
534#endif
535

Archive Download this file

Revision: 2603