/* * core/fs/pxe/isr.c * * Stub invoked on return from real mode including from an interrupt. * Interrupts are locked out on entry. */ #include "core.h" #include "thread.h" #include "pxe.h" #include #include #include extern uint8_t pxe_irq_pending; extern volatile uint8_t pxe_need_poll; static DECLARE_INIT_SEMAPHORE(pxe_receive_thread_sem, 0); static DECLARE_INIT_SEMAPHORE(pxe_poll_thread_sem, 0); static struct thread *pxe_thread, *poll_thread; #ifndef PXE_POLL_FORCE # define PXE_POLL_FORCE 0 #endif #ifndef PXE_POLL_BY_MODEL # define PXE_POLL_BY_MODEL 1 #endif /* * Note: this *must* be called with interrupts enabled. */ static bool install_irq_vector(uint8_t irq, void (*isr)(void), far_ptr_t *old) { far_ptr_t *entry; unsigned int vec; uint8_t mask, mymask; uint32_t now; bool ok; if (irq < 8) vec = irq + 0x08; else if (irq < 16) vec = (irq - 8) + 0x70; else return false; cli(); if (pxe_need_poll) { sti(); return false; } entry = (far_ptr_t *)(vec << 2); *old = *entry; entry->ptr = (uint32_t)isr; /* Enable this interrupt at the PIC level, just in case... */ mymask = ~(1 << (irq & 7)); if (irq >= 8) { mask = inb(0x21); mask &= ~(1 << 2); /* Enable cascade */ outb(mask, 0x21); mask = inb(0xa1); mask &= mymask; outb(mask, 0xa1); } else { mask = inb(0x21); mask &= mymask; outb(mask, 0x21); } sti(); now = jiffies(); /* Some time to watch for stuck interrupts */ while (jiffies() - now < 4 && (ok = !pxe_need_poll)) hlt(); if (!ok) *entry = *old; /* Restore the old vector */ ddprintf("UNDI: IRQ %d(0x%02x): %04x:%04x -> %04x:%04x\n", irq, vec, old->seg, old->offs, entry->seg, entry->offs); return ok; } static bool uninstall_irq_vector(uint8_t irq, void (*isr), far_ptr_t *old) { far_ptr_t *entry; unsigned int vec; bool rv; if (!irq) return true; /* Nothing to uninstall */ if (irq < 8) vec = irq + 0x08; else if (irq < 16) vec = (irq - 8) + 0x70; else return false; cli(); entry = (far_ptr_t *)(vec << 2); if (entry->ptr != (uint32_t)isr) { rv = false; } else { *entry = *old; rv = true; } sti(); return rv; } static void pxe_poll_wakeups(void) { static jiffies_t last_jiffies = 0; jiffies_t now = jiffies(); if (pxe_need_poll == 1) { /* If we need polling now, activate polling */ pxe_need_poll = 3; sem_up(&pxe_poll_thread_sem); } if (now != last_jiffies) { last_jiffies = now; __thread_process_timeouts(); } if (pxe_irq_pending) { pxe_irq_pending = 0; sem_up(&pxe_receive_thread_sem); } } static void pxe_process_irq(void) { static __lowmem t_PXENV_UNDI_ISR isr; uint16_t func = PXENV_UNDI_ISR_IN_PROCESS; /* First time */ bool done = false; while (!done) { memset(&isr, 0, sizeof isr); isr.FuncFlag = func; func = PXENV_UNDI_ISR_IN_GET_NEXT; /* Next time */ pxe_call(PXENV_UNDI_ISR, &isr); switch (isr.FuncFlag) { case PXENV_UNDI_ISR_OUT_DONE: done = true; break; case PXENV_UNDI_ISR_OUT_TRANSMIT: /* Transmit complete - nothing for us to do */ break; case PXENV_UNDI_ISR_OUT_RECEIVE: undiif_input(&isr); break; case PXENV_UNDI_ISR_OUT_BUSY: /* ISR busy, this should not happen */ done = true; break; default: /* Invalid return code, this should not happen */ done = true; break; } } } static void pxe_receive_thread(void *dummy) { (void)dummy; for (;;) { sem_down(&pxe_receive_thread_sem, 0); pxe_process_irq(); } } static bool pxe_isr_poll(void) { static __lowmem t_PXENV_UNDI_ISR isr; isr.FuncFlag = PXENV_UNDI_ISR_IN_START; pxe_call(PXENV_UNDI_ISR, &isr); return isr.FuncFlag == PXENV_UNDI_ISR_OUT_OURS; } static void pxe_poll_thread(void *dummy) { (void)dummy; /* Block indefinitely unless activated */ sem_down(&pxe_poll_thread_sem, 0); for (;;) { cli(); if (pxe_receive_thread_sem.count < 0 && pxe_isr_poll()) sem_up(&pxe_receive_thread_sem); else __schedule(); sti(); cpu_relax(); } } /* * This does preparations and enables the PXE thread */ void pxe_init_isr(void) { start_idle_thread(); sched_hook_func = pxe_poll_wakeups; /* * Run the pxe receive thread at elevated priority, since the UNDI * stack is likely to have very limited memory available; therefore to * avoid packet loss we need to move it into memory that we ourselves * manage, as soon as possible. */ core_pm_hook = __schedule; pxe_thread = start_thread("pxe receive", 16384, -20, pxe_receive_thread, NULL); } /* * Actually start the interrupt routine inside the UNDI stack */ void pxe_start_isr(void) { int irq = pxe_undi_info.IntNumber; if (irq == 2) irq = 9; /* IRQ 2 is really IRQ 9 */ else if (irq > 15) irq = 0; /* Invalid IRQ */ pxe_irq_vector = irq; if (irq) { if (!install_irq_vector(irq, pxe_isr, &pxe_irq_chain)) irq = 0; /* Install failed or stuck interrupt */ } poll_thread = start_thread("pxe poll", 4096, POLL_THREAD_PRIORITY, pxe_poll_thread, NULL); if (!irq || !(pxe_undi_iface.ServiceFlags & PXE_UNDI_IFACE_FLAG_IRQ)) { asm volatile("orb $1,%0" : "+m" (pxe_need_poll)); dprintf("pxe_start_isr: forcing pxe_need_poll\n"); } else if (PXE_POLL_BY_MODEL) { dprintf("pxe_start_isr: trying poll by model\n"); int hwad = ((int)MAC[0] << 16) + ((int)MAC[1] << 8) + MAC[2]; dprintf("pxe_start_isr: got %06x %04x\n", hwad, pxe_undi_iface.ServiceFlags); if ((hwad == 0x000023ae) && (pxe_undi_iface.ServiceFlags == 0xdc1b) || (hwad == 0x005c260a) && (pxe_undi_iface.ServiceFlags == 0xdc1b) || (hwad == 0x00180373) && (pxe_undi_iface.ServiceFlags == 0xdc1b)) { asm volatile("orb $1,%0" : "+m" (pxe_need_poll)); dprintf("pxe_start_isr: forcing pxe_need_poll by model\n"); } } } int reset_pxe(void) { static __lowmem struct s_PXENV_UNDI_CLOSE undi_close; sched_hook_func = NULL; core_pm_hook = core_pm_null_hook; kill_thread(pxe_thread); memset(&undi_close, 0, sizeof(undi_close)); pxe_call(PXENV_UNDI_CLOSE, &undi_close); if (undi_close.Status) printf("PXENV_UNDI_CLOSE failed: 0x%x\n", undi_close.Status); if (pxe_irq_vector) uninstall_irq_vector(pxe_irq_vector, pxe_isr, &pxe_irq_chain); if (poll_thread) kill_thread(poll_thread); return undi_close.Status; }