diff options
author | H. Peter Anvin <hpa@linux.intel.com> | 2012-05-29 16:06:23 -0700 |
---|---|---|
committer | H. Peter Anvin <hpa@linux.intel.com> | 2012-05-29 16:06:23 -0700 |
commit | 624432a3429aa6c57eb8d2b03faf64c4edfdf0cc (patch) | |
tree | a0162ed9a92e3b6944645582aa8d713ab5b96bbc | |
parent | d493d0416f038f80d73b897e317e6d2c251c1775 (diff) | |
download | syslinux-624432a3429aa6c57eb8d2b03faf64c4edfdf0cc.tar.gz syslinux-624432a3429aa6c57eb8d2b03faf64c4edfdf0cc.tar.xz syslinux-624432a3429aa6c57eb8d2b03faf64c4edfdf0cc.zip |
pxe: Add code to detect a struck interrupt line and disable (go to poll)
Add code to detect a stuck interrupt line and disable it; at that
point we will go to polling mode. Currently we only give ourselves 3
timer ticks after initialization to detect this, but this is good
enough to deal with gPXE on virtio.
Signed-off-by: H. Peter Anvin <hpa@linux.intel.com>
-rw-r--r-- | core/fs/pxe/isr.c | 42 | ||||
-rw-r--r-- | core/pxeisr.inc | 42 |
2 files changed, 74 insertions, 10 deletions
diff --git a/core/fs/pxe/isr.c b/core/fs/pxe/isr.c index c073e032..e2bf2f67 100644 --- a/core/fs/pxe/isr.c +++ b/core/fs/pxe/isr.c @@ -13,15 +13,19 @@ #include <sys/io.h> extern uint8_t pxe_irq_pending; +extern volatile uint8_t pxe_irq_timeout; static DECLARE_INIT_SEMAPHORE(pxe_receive_thread_sem, 0); static struct thread *pxe_thread, *poll_thread; +/* + * 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; - irq_state_t irqstate; + uint32_t now; if (irq < 8) vec = irq + 0x08; @@ -30,7 +34,7 @@ static bool install_irq_vector(uint8_t irq, void (*isr)(void), far_ptr_t *old) else return false; - irqstate = irq_save(); + cli(); entry = (far_ptr_t *)(vec << 2); *old = *entry; @@ -51,18 +55,28 @@ static bool install_irq_vector(uint8_t irq, void (*isr)(void), far_ptr_t *old) outb(mask, 0x21); } - irq_restore(irqstate); + sti(); + + now = jiffies(); + + /* Some time to watch for stuck interrupts */ + while (jiffies() - now < 4 && !pxe_irq_timeout) + hlt(); + + if (pxe_irq_timeout) + *entry = *old; /* Restore the old vector */ printf("UNDI: IRQ %d(0x%02x): %04x:%04x -> %04x:%04x\n", irq, vec, old->seg, old->offs, entry->seg, entry->offs); - return true; + return !pxe_irq_timeout; } 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 */ @@ -74,13 +88,19 @@ static bool uninstall_irq_vector(uint8_t irq, void (*isr), far_ptr_t *old) else return false; + cli(); + entry = (far_ptr_t *)(vec << 2); - if (entry->ptr != (uint32_t)isr) - return false; + if (entry->ptr != (uint32_t)isr) { + rv = false; + } else { + *entry = *old; + rv = true; + } - *entry = *old; - return true; + sti(); + return rv; } static void pxe_poll_wakeups(void) @@ -205,8 +225,10 @@ void pxe_start_isr(void) pxe_irq_vector = irq; - if (irq) - install_irq_vector(irq, pxe_isr, &pxe_irq_chain); + if (irq) { + if (!install_irq_vector(irq, pxe_isr, &pxe_irq_chain)) + irq = 0; /* Install failed or stuck interrupt */ + } if (!irq || !(pxe_undi_iface.ServiceFlags & PXE_UNDI_IFACE_FLAG_IRQ)) poll_thread = start_thread("pxe poll", 4096, POLL_THREAD_PRIORITY, diff --git a/core/pxeisr.inc b/core/pxeisr.inc index a1db334e..b6434f30 100644 --- a/core/pxeisr.inc +++ b/core/pxeisr.inc @@ -3,6 +3,8 @@ ; section .text16 +PXEIRQ_MAX equ 1000 ; Max spurious interrupts in a timer tick + global pxe_isr pxe_isr: cld @@ -27,6 +29,7 @@ pxe_isr: mov byte [pxenv_undi_isr_buf.funcflag],PXENV_UNDI_ISR_IN_START call pxenv + mov ax,[__jiffies] jc .notus cmp word [pxenv_undi_isr_buf.funcflag],PXENV_UNDI_ISR_OUT_OURS @@ -45,6 +48,10 @@ pxe_isr: .pri_pic: out 0x20,al ; Primary PIC + mov [pxeirq_last],ax + mov word [pxeirq_count],PXEIRQ_MAX + +.exit: pop gs pop fs pop es @@ -53,6 +60,12 @@ pxe_isr: iret .notus: + cmp ax,[pxeirq_last] + jne .reset_timeout + dec word [pxeirq_count] + jz .timeout + +.chain: pop gs pop fs pop es @@ -62,6 +75,29 @@ pxe_isr: global pxe_irq_chain pxe_irq_chain equ $-4 +.reset_timeout: + mov [pxeirq_last],ax + mov word [pxeirq_count],PXEIRQ_MAX + jmp .chain + + ; Too many spurious interrupts, shut off the interrupts + ; and go to polling mode +.timeout: + mov al,[pxe_irq_vector] + mov dx,21h + movzx cx,al + shl cx,7-3 + add dx,cx + and al,7 + xchg ax,cx + mov ch,1 + shl ch,cl + in al,dx + or al,ch + out dx,al + mov byte [pxe_irq_timeout],1 + jmp .exit + ; Emulate a PXE interrupt from the polling thread global pxe_poll @@ -118,9 +154,15 @@ pxenv_undi_isr_buf: .pkttype: resb 1 .size equ $-pxenv_undi_isr_buf + alignb 2 +pxeirq_last resw 1 +pxeirq_count resw 1 + global pxe_irq_vector pxe_irq_vector resb 1 ; PXE IRQ vector global pxe_irq_pending pxe_irq_pending resb 1 ; IRQ pending flag + global pxe_irq_timeout +pxe_irq_timeout resb 1 ; Stuck IRQs, disabled timeout section .text16 |