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
15extern uint8_t pxe_irq_pending;
16extern volatile uint8_t pxe_need_poll;
17static DECLARE_INIT_SEMAPHORE(pxe_receive_thread_sem, 0);
18static DECLARE_INIT_SEMAPHORE(pxe_poll_thread_sem, 0);
19static 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 */
32static 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
90static 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
121static 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
143static 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
183static 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
193static 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
203static 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 */
224void 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 */
243void 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
278int 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