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
install_irq_handler(irq_t irq,segoff_t * handler,uint8_t * previously_enabled,segoff_t * previous_handler)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
remove_irq_handler(irq_t irq,segoff_t * handler,uint8_t * previously_enabled,segoff_t * previous_handler)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
send_specific_eoi(irq_t irq)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
dump_irq_status(void)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
install_undi_irq_handler(irq_t irq,segoff_t entrypointsp)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
remove_undi_irq_handler(irq_t irq)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
undi_irq_triggered(irq_t irq)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