summaryrefslogtreecommitdiff
path: root/kernel/support.c
blob: 89834ee43847d5f5737ffc3c485b7723db64ff59 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
/*
** SCCS ID:	@(#)support.c	2.6	1/22/25
**
** @file	support.c
**
** @author  4003-506 class of 20003
** @authors K. Reek, Warren R. Carithers
**
** Miscellaneous system initialization functions, interrupt
** support routines, and data structures.
*/

#include <common.h>

#include <support.h>
#include <cio.h>
#include <x86/arch.h>
#include <x86/pic.h>
#include <x86/ops.h>
#include <bootstrap.h>
#include <syscalls.h>

/*
** Global variables and local data types.
*/

/*
** This is the table that contains pointers to the C-language ISR for
** each interrupt.  These functions are called from the isr stub based
** on the interrupt number.
*/
void (*isr_table[256])(int vector, int code);

/*
** Format of an IDT entry.
*/
typedef struct {
	short offset_15_0;
	short segment_selector;
	short flags;
	short offset_31_16;
} IDT_Gate;

/*
** LOCAL ROUTINES - not intended to be used outside this module.
*/

/**
** unexpected_handler
**
** This routine catches interrupts that we do not expect to ever occur.
** It handles them by (optionally) reporting them and then calling panic().
**
** @param vector   vector number for the interrupt that occurred
** @param code     error code, or a dummy value
**
** Does not return.
*/
#ifdef RPT_INT_UNEXP
/* add any header includes you need here */
#endif
static void unexpected_handler(int vector, int code)
{
#ifdef RPT_INT_UNEXP
	cio_printf("\n** UNEXPECTED vector %d code %d\n", vector, code);
#endif
	panic("Unexpected interrupt");
}

/**
** default_handler
**
** Default handler for interrupts we expect may occur but are not
** handling (yet).  We just reset the PIC and return.
**
** @param vector   vector number for the interrupt that occurred
** @param code     error code, or a dummy value
*/
static void default_handler(int vector, int code)
{
#ifdef RPT_INT_UNEXP
	cio_printf("\n** vector %d code %d\n", vector, code);
#endif
	if (vector >= 0x20 && vector < 0x30) {
		if (vector > 0x27) {
			// must also ACK the secondary PIC
			outb(PIC2_CMD, PIC_EOI);
		}
		outb(PIC1_CMD, PIC_EOI);
	} else {
		/*
		** All the "expected" interrupts will be handled by the
		** code above.  If we get down here, the isr table may
		** have been corrupted.  Print a message and don't return.
		*/
		panic("Unexpected \"expected\" interrupt!");
	}
}

/**
** mystery_handler
**
** Default handler for the "mystery" interrupt that comes through vector
** 0x27.  This is a non-repeatable interrupt whose source has not been
** identified, but it appears to be the famous "spurious level 7 interrupt"
** source.
**
** @param vector   vector number for the interrupt that occurred
** @param code     error code, or a dummy value
*/
static void mystery_handler(int vector, int code)
{
#if defined(RPT_INT_MYSTERY) || defined(RPT_INT_UNEXP)
	cio_printf("\nMystery interrupt!\nVector=0x%02x, code=%d\n", vector, code);
#endif
	outb(PIC1_CMD, PIC_EOI);
}

/**
** init_pic
**
** Initialize the 8259 Programmable Interrupt Controller.
*/
static void init_pic(void)
{
	/*
	** ICW1: start the init sequence, update ICW4
	*/
	outb(PIC1_CMD, PIC_CW1_INIT | PIC_CW1_NEED4);
	outb(PIC2_CMD, PIC_CW1_INIT | PIC_CW1_NEED4);

	/*
	** ICW2: primary offset of 0x20 in the IDT, secondary offset of 0x28
	*/
	outb(PIC1_DATA, PIC1_CW2_VECBASE);
	outb(PIC2_DATA, PIC2_CW2_VECBASE);

	/*
	** ICW3: secondary attached to line 2 of primary, bit mask is 00000100
	**   secondary id is 2
	*/
	outb(PIC1_DATA, PIC1_CW3_SEC_IRQ2);
	outb(PIC2_DATA, PIC2_CW3_SEC_ID);

	/*
	** ICW4: want 8086 mode, not 8080/8085 mode
	*/
	outb(PIC1_DATA, PIC_CW4_PM86);
	outb(PIC2_DATA, PIC_CW4_PM86);

	/*
	** OCW1: allow interrupts on all lines
	*/
	outb(PIC1_DATA, PIC_MASK_NONE);
	outb(PIC2_DATA, PIC_MASK_NONE);
}

/**
** set_idt_entry
**
** Construct an entry in the IDT
**
** @param entry    the vector number of the interrupt
** @param handler  ISR address to be put into the IDT entry
**
** Note: generally, the handler invoked from the IDT will be a "stub"
** that calls the second-level C handler via the isr_table array.
*/
static void set_idt_entry(int entry, void (*handler)(void))
{
	IDT_Gate *g = (IDT_Gate *)IDT_ADDR + entry;

	g->offset_15_0 = (int)handler & 0xffff;
	g->segment_selector = 0x0010;
	g->flags = IDT_PRESENT | IDT_DPL_0 | IDT_INT32_GATE;
	g->offset_31_16 = (int)handler >> 16 & 0xffff;
}

/**
** Name:    init_idt
**
** Initialize the Interrupt Descriptor Table (IDT).  This makes each of
** the entries in the IDT point to the isr stub for that entry, and
** installs a default handler in the handler table.  Temporary handlers
** are then installed for those interrupts we may get before a real
** handler is set up.
*/
static void init_idt(void)
{
	int i;
	extern void (*isr_stub_table[256])(void);

	/*
	** Make each IDT entry point to the stub for that vector.  Also
	** make each entry in the ISR table point to the default handler.
	*/
	for (i = 0; i < 256; i++) {
		set_idt_entry(i, isr_stub_table[i]);
		install_isr(i, unexpected_handler);
	}

	/*
	** Install the handlers for interrupts that have (or will have) a
	** specific handler. Comments indicate which module init function
	** will eventually install the "real" handler.
	*/

	install_isr(VEC_KBD, default_handler); // cio_init()
	install_isr(VEC_COM1, default_handler); // sio_init()
	install_isr(VEC_TIMER, default_handler); // clk_init()
	install_isr(VEC_SYSCALL, default_handler); // sys_init()
	install_isr(VEC_PAGE_FAULT, default_handler); // vm_init()

	install_isr(VEC_MYSTERY, mystery_handler);
}

/*
** END OF LOCAL ROUTINES.
**
** Full documentation for globally-visible routines is in the corresponding
** header file.
*/

/*
** panic
**
** Called when we find an unrecoverable error.
*/
void panic(char *reason)
{
	__asm__("cli");
	cio_printf("\nPANIC: %s\nHalting...", reason);
	for (;;) {
		;
	}
}

/*
** init_interrupts
**
** (Re)initilizes the interrupt system.
*/
void init_interrupts(void)
{
	init_idt();
	init_pic();
}

/*
** install_isr
**
** Installs a second-level handler for a specific interrupt.
*/
void (*install_isr(int vector, void (*handler)(int, int)))(int, int)
{
	void (*old_handler)(int vector, int code);

	old_handler = isr_table[vector];
	isr_table[vector] = handler;
	return old_handler;
}

/*
** Name:	delay
**
** Notes:  The parameter to the delay() function is ambiguous; it
** purports to indicate a delay length, but that isn't really tied
** to any real-world time measurement.
**
** On the original systems we used (dual 500MHz Intel P3 CPUs), each
** "unit" was approximately one tenth of a second, so delay(10) would
** delay for about one second.
**
** On the current machines (Intel Core i5-7500), delay(100) is about
** 2.5 seconds, so each "unit" is roughly 0.025 seconds.
**
** Ultimately, just remember that DELAY VALUES ARE APPROXIMATE AT BEST.
*/
void delay(int length)
{
	while (--length >= 0) {
		for (int i = 0; i < 10000000; ++i)
			;
	}
}