Home | History | Annotate | Download | only in pxe
      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  */
     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 
     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 
    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 
    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 
    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 
    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 
    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  */
    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  */
    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 
    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