• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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