|
@@ -1,263 +1,37 @@
|
1
|
1
|
/*
|
2
|
|
- * Basic support for controlling the 8259 Programmable Interrupt Controllers.
|
|
2
|
+ * Copyright (C) 2007 Michael Brown <mbrown@fensystems.co.uk>.
|
3
|
3
|
*
|
4
|
|
- * Initially written by Michael Brown (mcb30).
|
5
|
|
- */
|
6
|
|
-
|
7
|
|
-#include <etherboot.h>
|
8
|
|
-#include "pic8259.h"
|
9
|
|
-#include "old_realmode.h"
|
10
|
|
-
|
11
|
|
-/* State of trivial IRQ handler */
|
12
|
|
-irq_t trivial_irq_installed_on = IRQ_NONE;
|
13
|
|
-static uint16_t trivial_irq_previous_trigger_count = 0;
|
14
|
|
-
|
15
|
|
-/* The actual trivial IRQ handler
|
|
4
|
+ * This program is free software; you can redistribute it and/or
|
|
5
|
+ * modify it under the terms of the GNU General Public License as
|
|
6
|
+ * published by the Free Software Foundation; either version 2 of the
|
|
7
|
+ * License, or any later version.
|
16
|
8
|
*
|
17
|
|
- * Note: we depend on the C compiler not realising that we're putting
|
18
|
|
- * variables in the ".text16" section and therefore not forcing them
|
19
|
|
- * back to the ".data" section. I don't see any reason to expect this
|
20
|
|
- * behaviour to change.
|
|
9
|
+ * This program is distributed in the hope that it will be useful, but
|
|
10
|
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
11
|
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
12
|
+ * General Public License for more details.
|
21
|
13
|
*
|
22
|
|
- * These must *not* be the first variables to appear in this file; the
|
23
|
|
- * first variable to appear gets the ".data" directive.
|
24
|
|
- */
|
25
|
|
-RM_FRAGMENT(_trivial_irq_handler,
|
26
|
|
- "pushw %bx\n\t"
|
27
|
|
- "call 1f\n1:\tpopw %bx\n\t" /* PIC access to variables */
|
28
|
|
- "incw %cs:(_trivial_irq_trigger_count-1b)(%bx)\n\t"
|
29
|
|
- "popw %bx\n\t"
|
30
|
|
- "iret\n\t"
|
31
|
|
- "\n\t"
|
32
|
|
- ".globl _trivial_irq_trigger_count\n\t"
|
33
|
|
- "_trivial_irq_trigger_count: .short 0\n\t"
|
34
|
|
- "\n\t"
|
35
|
|
- ".globl _trivial_irq_chain_to\n\t"
|
36
|
|
- "_trivial_irq_chain_to: .short 0,0\n\t"
|
37
|
|
- "\n\t"
|
38
|
|
- ".globl _trivial_irq_chain\n\t"
|
39
|
|
- "_trivial_irq_chain: .byte 0\n\t"
|
40
|
|
- );
|
41
|
|
-extern volatile uint16_t _trivial_irq_trigger_count;
|
42
|
|
-extern segoff_t _trivial_irq_chain_to;
|
43
|
|
-extern int8_t _trivial_irq_chain;
|
44
|
|
-
|
45
|
|
-/* Current locations of trivial IRQ handler. These will change at
|
46
|
|
- * runtime when relocation is used; the handler needs to be copied to
|
47
|
|
- * base memory before being installed.
|
48
|
|
- */
|
49
|
|
-void (*trivial_irq_handler)P((void)) = _trivial_irq_handler;
|
50
|
|
-uint16_t volatile *trivial_irq_trigger_count = &_trivial_irq_trigger_count;
|
51
|
|
-segoff_t *trivial_irq_chain_to = &_trivial_irq_chain_to;
|
52
|
|
-uint8_t *trivial_irq_chain = &_trivial_irq_chain;
|
53
|
|
-
|
54
|
|
-/* Install a handler for the specified IRQ. Address of previous
|
55
|
|
- * handler will be stored in previous_handler. Enabled/disabled state
|
56
|
|
- * of IRQ will be preserved across call, therefore if the handler does
|
57
|
|
- * chaining, ensure that either (a) IRQ is disabled before call, or
|
58
|
|
- * (b) previous_handler points directly to the place that the handler
|
59
|
|
- * picks up its chain-to address.
|
60
|
|
- */
|
61
|
|
-
|
62
|
|
-int install_irq_handler ( irq_t irq, segoff_t *handler,
|
63
|
|
- uint8_t *previously_enabled,
|
64
|
|
- segoff_t *previous_handler ) {
|
65
|
|
- segoff_t *irq_vector = IRQ_VECTOR ( irq );
|
66
|
|
- *previously_enabled = irq_enabled ( irq );
|
67
|
|
-
|
68
|
|
- if ( irq > IRQ_MAX ) {
|
69
|
|
- DBG ( "Invalid IRQ number %d\n" );
|
70
|
|
- return 0;
|
71
|
|
- }
|
72
|
|
-
|
73
|
|
- previous_handler->segment = irq_vector->segment;
|
74
|
|
- previous_handler->offset = irq_vector->offset;
|
75
|
|
- if ( *previously_enabled ) disable_irq ( irq );
|
76
|
|
- DBG ( "Installing handler at %hx:%hx for IRQ %d (vector 0000:%hx),"
|
77
|
|
- " leaving %s\n",
|
78
|
|
- handler->segment, handler->offset, irq, virt_to_phys(irq_vector),
|
79
|
|
- ( *previously_enabled ? "enabled" : "disabled" ) );
|
80
|
|
- DBG ( "...(previous handler at %hx:%hx)\n",
|
81
|
|
- previous_handler->segment, previous_handler->offset );
|
82
|
|
- irq_vector->segment = handler->segment;
|
83
|
|
- irq_vector->offset = handler->offset;
|
84
|
|
- if ( *previously_enabled ) enable_irq ( irq );
|
85
|
|
- return 1;
|
86
|
|
-}
|
87
|
|
-
|
88
|
|
-/* Remove handler for the specified IRQ. Routine checks that another
|
89
|
|
- * handler has not been installed that chains to handler before
|
90
|
|
- * uninstalling handler. Enabled/disabled state of the IRQ will be
|
91
|
|
- * restored to that specified by previously_enabled.
|
92
|
|
- */
|
93
|
|
-
|
94
|
|
-int remove_irq_handler ( irq_t irq, segoff_t *handler,
|
95
|
|
- uint8_t *previously_enabled,
|
96
|
|
- segoff_t *previous_handler ) {
|
97
|
|
- segoff_t *irq_vector = IRQ_VECTOR ( irq );
|
98
|
|
-
|
99
|
|
- if ( irq > IRQ_MAX ) {
|
100
|
|
- DBG ( "Invalid IRQ number %d\n" );
|
101
|
|
- return 0;
|
102
|
|
- }
|
103
|
|
- if ( ( irq_vector->segment != handler->segment ) ||
|
104
|
|
- ( irq_vector->offset != handler->offset ) ) {
|
105
|
|
- DBG ( "Cannot remove handler for IRQ %d\n" );
|
106
|
|
- return 0;
|
107
|
|
- }
|
108
|
|
-
|
109
|
|
- DBG ( "Removing handler for IRQ %d\n", irq );
|
110
|
|
- disable_irq ( irq );
|
111
|
|
- irq_vector->segment = previous_handler->segment;
|
112
|
|
- irq_vector->offset = previous_handler->offset;
|
113
|
|
- if ( *previously_enabled ) enable_irq ( irq );
|
114
|
|
- return 1;
|
115
|
|
-}
|
116
|
|
-
|
117
|
|
-/* Install the trivial IRQ handler. This routine installs the
|
118
|
|
- * handler, tests it and enables the IRQ.
|
119
|
|
- */
|
120
|
|
-
|
121
|
|
-int install_trivial_irq_handler ( irq_t irq ) {
|
122
|
|
- segoff_t trivial_irq_handler_segoff = SEGOFF(trivial_irq_handler);
|
123
|
|
-
|
124
|
|
- if ( trivial_irq_installed_on != IRQ_NONE ) {
|
125
|
|
- DBG ( "Can install trivial IRQ handler only once\n" );
|
126
|
|
- return 0;
|
127
|
|
- }
|
128
|
|
- if ( SEGMENT(trivial_irq_handler) > 0xffff ) {
|
129
|
|
- DBG ( "Trivial IRQ handler not in base memory\n" );
|
130
|
|
- return 0;
|
131
|
|
- }
|
132
|
|
-
|
133
|
|
- DBG ( "Installing trivial IRQ handler on IRQ %d\n", irq );
|
134
|
|
- if ( ! install_irq_handler ( irq, &trivial_irq_handler_segoff,
|
135
|
|
- trivial_irq_chain,
|
136
|
|
- trivial_irq_chain_to ) )
|
137
|
|
- return 0;
|
138
|
|
- trivial_irq_installed_on = irq;
|
139
|
|
-
|
140
|
|
- DBG ( "Testing trivial IRQ handler\n" );
|
141
|
|
- disable_irq ( irq );
|
142
|
|
- *trivial_irq_trigger_count = 0;
|
143
|
|
- trivial_irq_previous_trigger_count = 0;
|
144
|
|
- fake_irq ( irq );
|
145
|
|
- if ( ! trivial_irq_triggered ( irq ) ) {
|
146
|
|
- DBG ( "Installation of trivial IRQ handler failed\n" );
|
147
|
|
- remove_trivial_irq_handler ( irq );
|
148
|
|
- return 0;
|
149
|
|
- }
|
150
|
|
- /* Send EOI just in case there was a leftover interrupt */
|
151
|
|
- send_specific_eoi ( irq );
|
152
|
|
- DBG ( "Trivial IRQ handler installed successfully\n" );
|
153
|
|
- enable_irq ( irq );
|
154
|
|
- return 1;
|
155
|
|
-}
|
156
|
|
-
|
157
|
|
-/* Remove the trivial IRQ handler.
|
|
14
|
+ * You should have received a copy of the GNU General Public License
|
|
15
|
+ * along with this program; if not, write to the Free Software
|
|
16
|
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
158
|
17
|
*/
|
159
|
18
|
|
160
|
|
-int remove_trivial_irq_handler ( irq_t irq ) {
|
161
|
|
- segoff_t trivial_irq_handler_segoff = SEGOFF(trivial_irq_handler);
|
162
|
|
-
|
163
|
|
- if ( trivial_irq_installed_on == IRQ_NONE ) return 1;
|
164
|
|
- if ( irq != trivial_irq_installed_on ) {
|
165
|
|
- DBG ( "Cannot uninstall trivial IRQ handler from IRQ %d; "
|
166
|
|
- "is installed on IRQ %d\n", irq,
|
167
|
|
- trivial_irq_installed_on );
|
168
|
|
- return 0;
|
169
|
|
- }
|
170
|
|
-
|
171
|
|
- if ( ! remove_irq_handler ( irq, &trivial_irq_handler_segoff,
|
172
|
|
- trivial_irq_chain,
|
173
|
|
- trivial_irq_chain_to ) )
|
174
|
|
- return 0;
|
|
19
|
+#include <pic8259.h>
|
175
|
20
|
|
176
|
|
- if ( trivial_irq_triggered ( trivial_irq_installed_on ) ) {
|
177
|
|
- DBG ( "Sending EOI for unwanted trivial IRQ\n" );
|
178
|
|
- send_specific_eoi ( trivial_irq_installed_on );
|
179
|
|
- }
|
180
|
|
-
|
181
|
|
- trivial_irq_installed_on = IRQ_NONE;
|
182
|
|
- return 1;
|
183
|
|
-}
|
184
|
|
-
|
185
|
|
-/* Safe method to detect whether or not trivial IRQ has been
|
186
|
|
- * triggered. Using this call avoids potential race conditions. This
|
187
|
|
- * call will return success only once per trigger.
|
188
|
|
- */
|
189
|
|
-
|
190
|
|
-int trivial_irq_triggered ( irq_t irq ) {
|
191
|
|
- uint16_t trivial_irq_this_trigger_count = *trivial_irq_trigger_count;
|
192
|
|
- int triggered = ( trivial_irq_this_trigger_count -
|
193
|
|
- trivial_irq_previous_trigger_count );
|
194
|
|
-
|
195
|
|
- /* irq is not used at present, but we have it in the API for
|
196
|
|
- * future-proofing; in case we want the facility to have
|
197
|
|
- * multiple trivial IRQ handlers installed simultaneously.
|
198
|
|
- *
|
199
|
|
- * Avoid compiler warning about unused variable.
|
200
|
|
- */
|
201
|
|
- if ( irq == IRQ_NONE ) {};
|
202
|
|
-
|
203
|
|
- trivial_irq_previous_trigger_count = trivial_irq_this_trigger_count;
|
204
|
|
- return triggered ? 1 : 0;
|
205
|
|
-}
|
206
|
|
-
|
207
|
|
-/* Copy trivial IRQ handler to a new location. Typically used to copy
|
208
|
|
- * the handler into base memory; when relocation is being used we need
|
209
|
|
- * to do this before installing the handler.
|
|
21
|
+/** @file
|
|
22
|
+ *
|
|
23
|
+ * Minimal support for the 8259 Programmable Interrupt Controller
|
210
|
24
|
*
|
211
|
|
- * Call with target=NULL in order to restore the handler to its
|
212
|
|
- * original location.
|
213
|
25
|
*/
|
214
|
26
|
|
215
|
|
-int copy_trivial_irq_handler ( void *target, size_t target_size ) {
|
216
|
|
- irq_t currently_installed_on = trivial_irq_installed_on;
|
217
|
|
- uint32_t offset = ( target == NULL ? 0 :
|
218
|
|
- target - (void*)_trivial_irq_handler );
|
219
|
|
-
|
220
|
|
- if (( target != NULL ) && ( target_size < TRIVIAL_IRQ_HANDLER_SIZE )) {
|
221
|
|
- DBG ( "Insufficient space to copy trivial IRQ handler\n" );
|
222
|
|
- return 0;
|
223
|
|
- }
|
224
|
|
-
|
225
|
|
- if ( currently_installed_on != IRQ_NONE ) {
|
226
|
|
- DBG ("WARNING: relocating trivial IRQ handler while in use\n");
|
227
|
|
- if ( ! remove_trivial_irq_handler ( currently_installed_on ) )
|
228
|
|
- return 0;
|
229
|
|
- }
|
230
|
|
-
|
231
|
|
- /* Do the actual copy */
|
232
|
|
- if ( target != NULL ) {
|
233
|
|
- DBG ( "Copying trivial IRQ handler to %hx:%hx\n",
|
234
|
|
- SEGMENT(target), OFFSET(target) );
|
235
|
|
- memcpy ( target, _trivial_irq_handler,
|
236
|
|
- TRIVIAL_IRQ_HANDLER_SIZE );
|
237
|
|
- } else {
|
238
|
|
- DBG ( "Restoring trivial IRQ handler to original location\n" );
|
239
|
|
- }
|
240
|
|
- /* Update all the pointers to structures within the handler */
|
241
|
|
- trivial_irq_handler = ( void (*)P((void)) )
|
242
|
|
- ( (void*)_trivial_irq_handler + offset );
|
243
|
|
- trivial_irq_trigger_count = (uint16_t*)
|
244
|
|
- ( (void*)&_trivial_irq_trigger_count + offset );
|
245
|
|
- trivial_irq_chain_to = (segoff_t*)
|
246
|
|
- ( (void*)&_trivial_irq_chain_to + offset );
|
247
|
|
- trivial_irq_chain = (uint8_t*)
|
248
|
|
- ( (void*)&_trivial_irq_chain + offset );
|
249
|
|
-
|
250
|
|
- if ( currently_installed_on != IRQ_NONE ) {
|
251
|
|
- if ( ! install_trivial_irq_handler ( currently_installed_on ) )
|
252
|
|
- return 0;
|
253
|
|
- }
|
254
|
|
- return 1;
|
255
|
|
-}
|
256
|
|
-
|
257
|
|
-/* Send non-specific EOI(s). This seems to be inherently unsafe.
|
|
27
|
+/**
|
|
28
|
+ * Send non-specific EOI(s)
|
|
29
|
+ *
|
|
30
|
+ * @v irq IRQ number
|
|
31
|
+ *
|
|
32
|
+ * This seems to be inherently unsafe.
|
258
|
33
|
*/
|
259
|
|
-
|
260
|
|
-void send_nonspecific_eoi ( irq_t irq ) {
|
|
34
|
+static inline void send_nonspecific_eoi ( unsigned int irq ) {
|
261
|
35
|
DBG ( "Sending non-specific EOI for IRQ %d\n", irq );
|
262
|
36
|
if ( irq >= IRQ_PIC_CUTOFF ) {
|
263
|
37
|
outb ( ICR_EOI_NON_SPECIFIC, PIC2_ICR );
|
|
@@ -265,61 +39,25 @@ void send_nonspecific_eoi ( irq_t irq ) {
|
265
|
39
|
outb ( ICR_EOI_NON_SPECIFIC, PIC1_ICR );
|
266
|
40
|
}
|
267
|
41
|
|
268
|
|
-/* Send specific EOI(s).
|
|
42
|
+/**
|
|
43
|
+ * Send specific EOI(s)
|
|
44
|
+ *
|
|
45
|
+ * @v irq IRQ number
|
269
|
46
|
*/
|
270
|
|
-
|
271
|
|
-void send_specific_eoi ( irq_t irq ) {
|
|
47
|
+static inline void send_specific_eoi ( unsigned int irq ) {
|
272
|
48
|
DBG ( "Sending specific EOI for IRQ %d\n", irq );
|
273
|
|
- outb ( ICR_EOI_SPECIFIC | ICR_VALUE(irq), ICR_REG(irq) );
|
|
49
|
+ outb ( ( ICR_EOI_SPECIFIC | ICR_VALUE ( irq ) ), ICR_REG ( irq ) );
|
274
|
50
|
if ( irq >= IRQ_PIC_CUTOFF ) {
|
275
|
|
- outb ( ICR_EOI_SPECIFIC | ICR_VALUE(CHAINED_IRQ),
|
276
|
|
- ICR_REG(CHAINED_IRQ) );
|
|
51
|
+ outb ( ( ICR_EOI_SPECIFIC | ICR_VALUE ( CHAINED_IRQ ) ),
|
|
52
|
+ ICR_REG ( CHAINED_IRQ ) );
|
277
|
53
|
}
|
278
|
54
|
}
|
279
|
55
|
|
280
|
|
-/* Fake an IRQ
|
281
|
|
- */
|
282
|
|
-
|
283
|
|
-void fake_irq ( irq_t irq ) {
|
284
|
|
- struct {
|
285
|
|
- uint16_t int_number;
|
286
|
|
- } PACKED in_stack;
|
287
|
|
-
|
288
|
|
- /* Convert IRQ to INT number:
|
289
|
|
- *
|
290
|
|
- * subb $0x08,%cl Invert bit 3, set bits 4-7 iff irq < 8
|
291
|
|
- * xorb $0x70,%cl Invert bits 4-6
|
292
|
|
- * andb $0x7f,%cl Clear bit 7
|
293
|
|
- *
|
294
|
|
- * No, it's not the most intuitive method, but I was proud to
|
295
|
|
- * get it down to three lines of assembler when this routine
|
296
|
|
- * was originally implemented in pcbios.S.
|
297
|
|
- */
|
298
|
|
- in_stack.int_number = ( ( irq - 8 ) ^ 0x70 ) & 0x7f;
|
299
|
|
-
|
300
|
|
- RM_FRAGMENT(rm_fake_irq,
|
301
|
|
- "popw %ax\n\t" /* %ax = INT number */
|
302
|
|
- "call 1f\n1:\tpop %bx\n\t"
|
303
|
|
- "movb %al, %cs:(2f-1b+1)(%bx)\n\t" /* Overwrite INT number..*/
|
304
|
|
- "\n2:\tint $0x00\n\t" /* ..in this instruction */
|
305
|
|
- );
|
306
|
|
-
|
307
|
|
- real_call ( rm_fake_irq, &in_stack, NULL );
|
308
|
|
-}
|
309
|
|
-
|
310
|
|
-/* Dump current 8259 status: enabled IRQs and handler addresses.
|
|
56
|
+/**
|
|
57
|
+ * Send End-Of-Interrupt to the PIC
|
|
58
|
+ *
|
|
59
|
+ * @v irq IRQ number
|
311
|
60
|
*/
|
312
|
|
-
|
313
|
|
-#ifdef DEBUG_IRQ
|
314
|
|
-void dump_irq_status ( void ) {
|
315
|
|
- int irq = 0;
|
316
|
|
-
|
317
|
|
- for ( irq = 0; irq < 16; irq++ ) {
|
318
|
|
- if ( irq_enabled ( irq ) ) {
|
319
|
|
- printf ( "IRQ%d enabled, ISR at %hx:%hx\n", irq,
|
320
|
|
- IRQ_VECTOR(irq)->segment,
|
321
|
|
- IRQ_VECTOR(irq)->offset );
|
322
|
|
- }
|
323
|
|
- }
|
|
61
|
+void send_eoi ( unsigned int irq ) {
|
|
62
|
+ send_specific_eoi ( irq );
|
324
|
63
|
}
|
325
|
|
-#endif
|