1 /* 2 * Basic support for controlling the 8259 Programmable Interrupt Controllers. 3 * 4 * Initially written by Michael Brown (mcb30). 5 */ 6 7 #include <etherboot.h> 8 #include <pic8259.h> 9 10 #ifdef DEBUG_IRQ 11 #define DBG(...) printf ( __VA_ARGS__ ) 12 #else 13 #define DBG(...) 14 #endif 15 16 /* Install a handler for the specified IRQ. Address of previous 17 * handler will be stored in previous_handler. Enabled/disabled state 18 * of IRQ will be preserved across call, therefore if the handler does 19 * chaining, ensure that either (a) IRQ is disabled before call, or 20 * (b) previous_handler points directly to the place that the handler 21 * picks up its chain-to address. 22 */ 23 24 int install_irq_handler ( irq_t irq, segoff_t *handler, 25 uint8_t *previously_enabled, 26 segoff_t *previous_handler ) { 27 segoff_t *irq_vector = IRQ_VECTOR ( irq ); 28 *previously_enabled = irq_enabled ( irq ); 29 30 if ( irq > IRQ_MAX ) { 31 DBG ( "Invalid IRQ number %d\n" ); 32 return 0; 33 } 34 35 previous_handler->segment = irq_vector->segment; 36 previous_handler->offset = irq_vector->offset; 37 if ( *previously_enabled ) disable_irq ( irq ); 38 DBG ( "Installing handler at %hx:%hx for IRQ %d, leaving %s\n", 39 handler->segment, handler->offset, irq, 40 ( *previously_enabled ? "enabled" : "disabled" ) ); 41 DBG ( "...(previous handler at %hx:%hx)\n", 42 previous_handler->segment, previous_handler->offset ); 43 irq_vector->segment = handler->segment; 44 irq_vector->offset = handler->offset; 45 if ( *previously_enabled ) enable_irq ( irq ); 46 return 1; 47 } 48 49 /* Remove handler for the specified IRQ. Routine checks that another 50 * handler has not been installed that chains to handler before 51 * uninstalling handler. Enabled/disabled state of the IRQ will be 52 * restored to that specified by previously_enabled. 53 */ 54 55 int remove_irq_handler ( irq_t irq, segoff_t *handler, 56 uint8_t *previously_enabled, 57 segoff_t *previous_handler ) { 58 segoff_t *irq_vector = IRQ_VECTOR ( irq ); 59 60 if ( irq > IRQ_MAX ) { 61 DBG ( "Invalid IRQ number %d\n" ); 62 return 0; 63 } 64 if ( ( irq_vector->segment != handler->segment ) || 65 ( irq_vector->offset != handler->offset ) ) { 66 DBG ( "Cannot remove handler for IRQ %d\n" ); 67 return 0; 68 } 69 70 DBG ( "Removing handler for IRQ %d\n", irq ); 71 disable_irq ( irq ); 72 irq_vector->segment = previous_handler->segment; 73 irq_vector->offset = previous_handler->offset; 74 if ( *previously_enabled ) enable_irq ( irq ); 75 return 1; 76 } 77 78 /* Send specific EOI(s). 79 */ 80 81 void send_specific_eoi ( irq_t irq ) { 82 DBG ( "Sending specific EOI for IRQ %d\n", irq ); 83 outb ( ICR_EOI_SPECIFIC | ICR_VALUE(irq), ICR_REG(irq) ); 84 if ( irq >= IRQ_PIC_CUTOFF ) { 85 outb ( ICR_EOI_SPECIFIC | ICR_VALUE(CHAINED_IRQ), 86 ICR_REG(CHAINED_IRQ) ); 87 } 88 } 89 90 /* Dump current 8259 status: enabled IRQs and handler addresses. 91 */ 92 93 #ifdef DEBUG_IRQ 94 void dump_irq_status (void) { 95 int irq = 0; 96 97 for ( irq = 0; irq < 16; irq++ ) { 98 if ( irq_enabled ( irq ) ) { 99 printf ( "IRQ%d enabled, ISR at %hx:%hx\n", irq, 100 IRQ_VECTOR(irq)->segment, 101 IRQ_VECTOR(irq)->offset ); 102 } 103 } 104 } 105 #endif 106 107 /******************************************************************** 108 * UNDI interrupt handling 109 * This essentially follows the defintion of the trivial interrupt 110 * handler routines. The text is assumed to locate in base memory. 111 */ 112 void (*undi_irq_handler)P((void)) = _undi_irq_handler; 113 uint16_t volatile *undi_irq_trigger_count = &_undi_irq_trigger_count; 114 segoff_t *undi_irq_chain_to = &_undi_irq_chain_to; 115 uint8_t *undi_irq_chain = &_undi_irq_chain; 116 irq_t undi_irq_installed_on = IRQ_NONE; 117 118 /* UNDI entry point and irq, used by interrupt handler 119 */ 120 segoff_t *pxenv_undi_entrypointsp = &_pxenv_undi_entrypointsp; 121 uint8_t *pxenv_undi_irq = &_pxenv_undi_irq; 122 123 /* Previous trigger count for undi IRQ handler */ 124 static uint16_t undi_irq_previous_trigger_count = 0; 125 126 /* Install the undi IRQ handler. Don't test as UNDI has not be opened. 127 */ 128 129 int install_undi_irq_handler ( irq_t irq, segoff_t entrypointsp ) { 130 segoff_t undi_irq_handler_segoff = SEGOFF(undi_irq_handler); 131 132 if ( undi_irq_installed_on != IRQ_NONE ) { 133 DBG ( "Can install undi IRQ handler only once\n" ); 134 return 0; 135 } 136 if ( SEGMENT(undi_irq_handler) > 0xffff ) { 137 DBG ( "Trivial IRQ handler not in base memory\n" ); 138 return 0; 139 } 140 141 DBG ( "Installing undi IRQ handler on IRQ %d\n", irq ); 142 *pxenv_undi_entrypointsp = entrypointsp; 143 *pxenv_undi_irq = irq; 144 if ( ! install_irq_handler ( irq, &undi_irq_handler_segoff, 145 undi_irq_chain, 146 undi_irq_chain_to ) ) 147 return 0; 148 undi_irq_installed_on = irq; 149 150 DBG ( "Disabling undi IRQ %d\n", irq ); 151 disable_irq ( irq ); 152 *undi_irq_trigger_count = 0; 153 undi_irq_previous_trigger_count = 0; 154 DBG ( "UNDI IRQ handler installed successfully\n" ); 155 return 1; 156 } 157 158 /* Remove the undi IRQ handler. 159 */ 160 161 int remove_undi_irq_handler ( irq_t irq ) { 162 segoff_t undi_irq_handler_segoff = SEGOFF(undi_irq_handler); 163 164 if ( undi_irq_installed_on == IRQ_NONE ) return 1; 165 if ( irq != undi_irq_installed_on ) { 166 DBG ( "Cannot uninstall undi IRQ handler from IRQ %d; " 167 "is installed on IRQ %d\n", irq, 168 undi_irq_installed_on ); 169 return 0; 170 } 171 172 if ( ! remove_irq_handler ( irq, &undi_irq_handler_segoff, 173 undi_irq_chain, 174 undi_irq_chain_to ) ) 175 return 0; 176 177 if ( undi_irq_triggered ( undi_irq_installed_on ) ) { 178 DBG ( "Sending EOI for unwanted undi IRQ\n" ); 179 send_specific_eoi ( undi_irq_installed_on ); 180 } 181 182 undi_irq_installed_on = IRQ_NONE; 183 return 1; 184 } 185 186 /* Safe method to detect whether or not undi IRQ has been 187 * triggered. Using this call avoids potential race conditions. This 188 * call will return success only once per trigger. 189 */ 190 191 int undi_irq_triggered ( irq_t irq ) { 192 uint16_t undi_irq_this_trigger_count = *undi_irq_trigger_count; 193 int triggered = ( undi_irq_this_trigger_count - 194 undi_irq_previous_trigger_count ); 195 196 /* irq is not used at present, but we have it in the API for 197 * future-proofing; in case we want the facility to have 198 * multiple undi IRQ handlers installed simultaneously. 199 * 200 * Avoid compiler warning about unused variable. 201 */ 202 if ( irq == IRQ_NONE ) {}; 203 undi_irq_previous_trigger_count = undi_irq_this_trigger_count; 204 return triggered ? 1 : 0; 205 } 206