1 /*
2 * core/fs/pxe/isr.c
3 *
4 * Stub invoked on return from real mode including from an interrupt.
5 * Interrupts are locked out on entry.
6 */
7
8 #include "core.h"
9 #include "thread.h"
10 #include "pxe.h"
11 #include <string.h>
12 #include <sys/cpu.h>
13 #include <sys/io.h>
14
15 extern uint8_t pxe_irq_pending;
16 extern volatile uint8_t pxe_need_poll;
17 static DECLARE_INIT_SEMAPHORE(pxe_receive_thread_sem, 0);
18 static DECLARE_INIT_SEMAPHORE(pxe_poll_thread_sem, 0);
19 static struct thread *pxe_thread, *poll_thread;
20
21 #ifndef PXE_POLL_FORCE
22 # define PXE_POLL_FORCE 0
23 #endif
24
25 #ifndef PXE_POLL_BY_MODEL
26 # define PXE_POLL_BY_MODEL 1
27 #endif
28
29 /*
30 * Note: this *must* be called with interrupts enabled.
31 */
install_irq_vector(uint8_t irq,void (* isr)(void),far_ptr_t * old)32 static bool install_irq_vector(uint8_t irq, void (*isr)(void), far_ptr_t *old)
33 {
34 far_ptr_t *entry;
35 unsigned int vec;
36 uint8_t mask, mymask;
37 uint32_t now;
38 bool ok;
39
40 if (irq < 8)
41 vec = irq + 0x08;
42 else if (irq < 16)
43 vec = (irq - 8) + 0x70;
44 else
45 return false;
46
47 cli();
48
49 if (pxe_need_poll) {
50 sti();
51 return false;
52 }
53
54 entry = (far_ptr_t *)(vec << 2);
55 *old = *entry;
56 entry->ptr = (uint32_t)isr;
57
58 /* Enable this interrupt at the PIC level, just in case... */
59 mymask = ~(1 << (irq & 7));
60 if (irq >= 8) {
61 mask = inb(0x21);
62 mask &= ~(1 << 2); /* Enable cascade */
63 outb(mask, 0x21);
64 mask = inb(0xa1);
65 mask &= mymask;
66 outb(mask, 0xa1);
67 } else {
68 mask = inb(0x21);
69 mask &= mymask;
70 outb(mask, 0x21);
71 }
72
73 sti();
74
75 now = jiffies();
76
77 /* Some time to watch for stuck interrupts */
78 while (jiffies() - now < 4 && (ok = !pxe_need_poll))
79 hlt();
80
81 if (!ok)
82 *entry = *old; /* Restore the old vector */
83
84 ddprintf("UNDI: IRQ %d(0x%02x): %04x:%04x -> %04x:%04x\n", irq, vec,
85 old->seg, old->offs, entry->seg, entry->offs);
86
87 return ok;
88 }
89
uninstall_irq_vector(uint8_t irq,void (* isr),far_ptr_t * old)90 static bool uninstall_irq_vector(uint8_t irq, void (*isr), far_ptr_t *old)
91 {
92 far_ptr_t *entry;
93 unsigned int vec;
94 bool rv;
95
96 if (!irq)
97 return true; /* Nothing to uninstall */
98
99 if (irq < 8)
100 vec = irq + 0x08;
101 else if (irq < 16)
102 vec = (irq - 8) + 0x70;
103 else
104 return false;
105
106 cli();
107
108 entry = (far_ptr_t *)(vec << 2);
109
110 if (entry->ptr != (uint32_t)isr) {
111 rv = false;
112 } else {
113 *entry = *old;
114 rv = true;
115 }
116
117 sti();
118 return rv;
119 }
120
pxe_poll_wakeups(void)121 static void pxe_poll_wakeups(void)
122 {
123 static jiffies_t last_jiffies = 0;
124 jiffies_t now = jiffies();
125
126 if (pxe_need_poll == 1) {
127 /* If we need polling now, activate polling */
128 pxe_need_poll = 3;
129 sem_up(&pxe_poll_thread_sem);
130 }
131
132 if (now != last_jiffies) {
133 last_jiffies = now;
134 __thread_process_timeouts();
135 }
136
137 if (pxe_irq_pending) {
138 pxe_irq_pending = 0;
139 sem_up(&pxe_receive_thread_sem);
140 }
141 }
142
pxe_process_irq(void)143 static void pxe_process_irq(void)
144 {
145 static __lowmem t_PXENV_UNDI_ISR isr;
146
147 uint16_t func = PXENV_UNDI_ISR_IN_PROCESS; /* First time */
148 bool done = false;
149
150 while (!done) {
151 memset(&isr, 0, sizeof isr);
152 isr.FuncFlag = func;
153 func = PXENV_UNDI_ISR_IN_GET_NEXT; /* Next time */
154
155 pxe_call(PXENV_UNDI_ISR, &isr);
156
157 switch (isr.FuncFlag) {
158 case PXENV_UNDI_ISR_OUT_DONE:
159 done = true;
160 break;
161
162 case PXENV_UNDI_ISR_OUT_TRANSMIT:
163 /* Transmit complete - nothing for us to do */
164 break;
165
166 case PXENV_UNDI_ISR_OUT_RECEIVE:
167 undiif_input(&isr);
168 break;
169
170 case PXENV_UNDI_ISR_OUT_BUSY:
171 /* ISR busy, this should not happen */
172 done = true;
173 break;
174
175 default:
176 /* Invalid return code, this should not happen */
177 done = true;
178 break;
179 }
180 }
181 }
182
pxe_receive_thread(void * dummy)183 static void pxe_receive_thread(void *dummy)
184 {
185 (void)dummy;
186
187 for (;;) {
188 sem_down(&pxe_receive_thread_sem, 0);
189 pxe_process_irq();
190 }
191 }
192
pxe_isr_poll(void)193 static bool pxe_isr_poll(void)
194 {
195 static __lowmem t_PXENV_UNDI_ISR isr;
196
197 isr.FuncFlag = PXENV_UNDI_ISR_IN_START;
198 pxe_call(PXENV_UNDI_ISR, &isr);
199
200 return isr.FuncFlag == PXENV_UNDI_ISR_OUT_OURS;
201 }
202
pxe_poll_thread(void * dummy)203 static void pxe_poll_thread(void *dummy)
204 {
205 (void)dummy;
206
207 /* Block indefinitely unless activated */
208 sem_down(&pxe_poll_thread_sem, 0);
209
210 for (;;) {
211 cli();
212 if (pxe_receive_thread_sem.count < 0 && pxe_isr_poll())
213 sem_up(&pxe_receive_thread_sem);
214 else
215 __schedule();
216 sti();
217 cpu_relax();
218 }
219 }
220
221 /*
222 * This does preparations and enables the PXE thread
223 */
pxe_init_isr(void)224 void pxe_init_isr(void)
225 {
226 start_idle_thread();
227 sched_hook_func = pxe_poll_wakeups;
228 /*
229 * Run the pxe receive thread at elevated priority, since the UNDI
230 * stack is likely to have very limited memory available; therefore to
231 * avoid packet loss we need to move it into memory that we ourselves
232 * manage, as soon as possible.
233 */
234 core_pm_hook = __schedule;
235
236 pxe_thread = start_thread("pxe receive", 16384, -20,
237 pxe_receive_thread, NULL);
238 }
239
240 /*
241 * Actually start the interrupt routine inside the UNDI stack
242 */
pxe_start_isr(void)243 void pxe_start_isr(void)
244 {
245 int irq = pxe_undi_info.IntNumber;
246
247 if (irq == 2)
248 irq = 9; /* IRQ 2 is really IRQ 9 */
249 else if (irq > 15)
250 irq = 0; /* Invalid IRQ */
251
252 pxe_irq_vector = irq;
253
254 if (irq) {
255 if (!install_irq_vector(irq, pxe_isr, &pxe_irq_chain))
256 irq = 0; /* Install failed or stuck interrupt */
257 }
258
259 poll_thread = start_thread("pxe poll", 4096, POLL_THREAD_PRIORITY,
260 pxe_poll_thread, NULL);
261
262 if (!irq || !(pxe_undi_iface.ServiceFlags & PXE_UNDI_IFACE_FLAG_IRQ)) {
263 asm volatile("orb $1,%0" : "+m" (pxe_need_poll));
264 dprintf("pxe_start_isr: forcing pxe_need_poll\n");
265 } else if (PXE_POLL_BY_MODEL) {
266 dprintf("pxe_start_isr: trying poll by model\n");
267 int hwad = ((int)MAC[0] << 16) + ((int)MAC[1] << 8) + MAC[2];
268 dprintf("pxe_start_isr: got %06x %04x\n", hwad, pxe_undi_iface.ServiceFlags);
269 if ((hwad == 0x000023ae) && (pxe_undi_iface.ServiceFlags == 0xdc1b) ||
270 (hwad == 0x005c260a) && (pxe_undi_iface.ServiceFlags == 0xdc1b) ||
271 (hwad == 0x00180373) && (pxe_undi_iface.ServiceFlags == 0xdc1b)) {
272 asm volatile("orb $1,%0" : "+m" (pxe_need_poll));
273 dprintf("pxe_start_isr: forcing pxe_need_poll by model\n");
274 }
275 }
276 }
277
reset_pxe(void)278 int reset_pxe(void)
279 {
280 static __lowmem struct s_PXENV_UNDI_CLOSE undi_close;
281
282 sched_hook_func = NULL;
283 core_pm_hook = core_pm_null_hook;
284 kill_thread(pxe_thread);
285
286 memset(&undi_close, 0, sizeof(undi_close));
287 pxe_call(PXENV_UNDI_CLOSE, &undi_close);
288
289 if (undi_close.Status)
290 printf("PXENV_UNDI_CLOSE failed: 0x%x\n", undi_close.Status);
291
292 if (pxe_irq_vector)
293 uninstall_irq_vector(pxe_irq_vector, pxe_isr, &pxe_irq_chain);
294 if (poll_thread)
295 kill_thread(poll_thread);
296
297 return undi_close.Status;
298 }
299