2 * Basic support for controlling the 8259 Programmable Interrupt Controllers.
4 * Initially written by Michael Brown (mcb30).
9 #include "old_realmode.h"
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;
15 /* The actual trivial IRQ handler
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.
22 * These must *not* be the first variables to appear in this file; the
23 * first variable to appear gets the ".data" directive.
25 RM_FRAGMENT(_trivial_irq_handler,
27 "call 1f\n1:\tpopw %bx\n\t" /* PIC access to variables */
28 "incw %cs:(_trivial_irq_trigger_count-1b)(%bx)\n\t"
32 ".globl _trivial_irq_trigger_count\n\t"
33 "_trivial_irq_trigger_count: .short 0\n\t"
35 ".globl _trivial_irq_chain_to\n\t"
36 "_trivial_irq_chain_to: .short 0,0\n\t"
38 ".globl _trivial_irq_chain\n\t"
39 "_trivial_irq_chain: .byte 0\n\t"
41 extern volatile uint16_t _trivial_irq_trigger_count;
42 extern segoff_t _trivial_irq_chain_to;
43 extern int8_t _trivial_irq_chain;
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.
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;
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.
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 );
68 if ( irq > IRQ_MAX ) {
69 DBG ( "Invalid IRQ number %d\n" );
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),"
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 );
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.
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 );
99 if ( irq > IRQ_MAX ) {
100 DBG ( "Invalid IRQ number %d\n" );
103 if ( ( irq_vector->segment != handler->segment ) ||
104 ( irq_vector->offset != handler->offset ) ) {
105 DBG ( "Cannot remove handler for IRQ %d\n" );
109 DBG ( "Removing handler for IRQ %d\n", irq );
111 irq_vector->segment = previous_handler->segment;
112 irq_vector->offset = previous_handler->offset;
113 if ( *previously_enabled ) enable_irq ( irq );
117 /* Install the trivial IRQ handler. This routine installs the
118 * handler, tests it and enables the IRQ.
121 int install_trivial_irq_handler ( irq_t irq ) {
122 segoff_t trivial_irq_handler_segoff = SEGOFF(trivial_irq_handler);
124 if ( trivial_irq_installed_on != IRQ_NONE ) {
125 DBG ( "Can install trivial IRQ handler only once\n" );
128 if ( SEGMENT(trivial_irq_handler) > 0xffff ) {
129 DBG ( "Trivial IRQ handler not in base memory\n" );
133 DBG ( "Installing trivial IRQ handler on IRQ %d\n", irq );
134 if ( ! install_irq_handler ( irq, &trivial_irq_handler_segoff,
136 trivial_irq_chain_to ) )
138 trivial_irq_installed_on = irq;
140 DBG ( "Testing trivial IRQ handler\n" );
142 *trivial_irq_trigger_count = 0;
143 trivial_irq_previous_trigger_count = 0;
145 if ( ! trivial_irq_triggered ( irq ) ) {
146 DBG ( "Installation of trivial IRQ handler failed\n" );
147 remove_trivial_irq_handler ( irq );
150 /* Send EOI just in case there was a leftover interrupt */
151 send_specific_eoi ( irq );
152 DBG ( "Trivial IRQ handler installed successfully\n" );
157 /* Remove the trivial IRQ handler.
160 int remove_trivial_irq_handler ( irq_t irq ) {
161 segoff_t trivial_irq_handler_segoff = SEGOFF(trivial_irq_handler);
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 );
171 if ( ! remove_irq_handler ( irq, &trivial_irq_handler_segoff,
173 trivial_irq_chain_to ) )
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 );
181 trivial_irq_installed_on = IRQ_NONE;
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.
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 );
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.
199 * Avoid compiler warning about unused variable.
201 if ( irq == IRQ_NONE ) {};
203 trivial_irq_previous_trigger_count = trivial_irq_this_trigger_count;
204 return triggered ? 1 : 0;
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.
211 * Call with target=NULL in order to restore the handler to its
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 );
220 if (( target != NULL ) && ( target_size < TRIVIAL_IRQ_HANDLER_SIZE )) {
221 DBG ( "Insufficient space to copy trivial IRQ handler\n" );
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 ) )
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 );
238 DBG ( "Restoring trivial IRQ handler to original location\n" );
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 );
250 if ( currently_installed_on != IRQ_NONE ) {
251 if ( ! install_trivial_irq_handler ( currently_installed_on ) )
257 /* Send non-specific EOI(s). This seems to be inherently unsafe.
260 void send_nonspecific_eoi ( irq_t irq ) {
261 DBG ( "Sending non-specific EOI for IRQ %d\n", irq );
262 if ( irq >= IRQ_PIC_CUTOFF ) {
263 outb ( ICR_EOI_NON_SPECIFIC, PIC2_ICR );
265 outb ( ICR_EOI_NON_SPECIFIC, PIC1_ICR );
268 /* Send specific EOI(s).
271 void send_specific_eoi ( irq_t irq ) {
272 DBG ( "Sending specific EOI for IRQ %d\n", irq );
273 outb ( ICR_EOI_SPECIFIC | ICR_VALUE(irq), ICR_REG(irq) );
274 if ( irq >= IRQ_PIC_CUTOFF ) {
275 outb ( ICR_EOI_SPECIFIC | ICR_VALUE(CHAINED_IRQ),
276 ICR_REG(CHAINED_IRQ) );
283 void fake_irq ( irq_t irq ) {
288 /* Convert IRQ to INT number:
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
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.
298 in_stack.int_number = ( ( irq - 8 ) ^ 0x70 ) & 0x7f;
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 */
307 real_call ( rm_fake_irq, &in_stack, NULL );
310 /* Dump current 8259 status: enabled IRQs and handler addresses.
314 void dump_irq_status ( void ) {
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 );