aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorH. Peter Anvin <hpa@linux.intel.com>2012-05-29 16:06:23 -0700
committerH. Peter Anvin <hpa@linux.intel.com>2012-05-29 16:06:23 -0700
commit624432a3429aa6c57eb8d2b03faf64c4edfdf0cc (patch)
treea0162ed9a92e3b6944645582aa8d713ab5b96bbc
parentd493d0416f038f80d73b897e317e6d2c251c1775 (diff)
downloadsyslinux-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.c42
-rw-r--r--core/pxeisr.inc42
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