From edc4648c398bfd089a34033983a10d572a6d3d57 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 10 Jul 2007 04:34:53 +0100 Subject: [PATCH] Protect ISR against failure to unhook. --- src/arch/i386/drivers/net/undiisr.S | 4 +++ src/arch/i386/drivers/net/undinet.c | 49 +++++++++-------------------- 2 files changed, 19 insertions(+), 34 deletions(-) diff --git a/src/arch/i386/drivers/net/undiisr.S b/src/arch/i386/drivers/net/undiisr.S index b5838693..f0b61932 100644 --- a/src/arch/i386/drivers/net/undiisr.S +++ b/src/arch/i386/drivers/net/undiisr.S @@ -22,6 +22,10 @@ undiisr: pushw %ds pushw %es pusha + + /* Check that we have an UNDI entry point */ + cmpw $0, undinet_entry_point + je chain /* Issue UNDI API call */ movw %cs:rm_ds, %ax diff --git a/src/arch/i386/drivers/net/undinet.c b/src/arch/i386/drivers/net/undinet.c index c9a610ae..d985f904 100644 --- a/src/arch/i386/drivers/net/undinet.c +++ b/src/arch/i386/drivers/net/undinet.c @@ -39,8 +39,6 @@ /** An UNDI NIC */ struct undi_nic { - /** Entry point */ - SEGOFF16_t entry; /** Assigned IRQ number */ unsigned int irq; /** Currently processing ISR */ @@ -167,7 +165,6 @@ static int undinet_call ( struct undi_nic *undinic, unsigned int function, /* Copy parameter block and entry point */ assert ( params_len <= sizeof ( undinet_params ) ); memcpy ( &undinet_params, params, params_len ); - undinet_entry_point = undinic->entry; /* Call real-mode entry point. This calling convention will * work with both the !PXE and the PXENV+ entry points. @@ -222,7 +219,8 @@ static int undinet_call ( struct undi_nic *undinic, unsigned int function, DBGC ( undinic, "UNDINIC %p parameters at %04x:%04x length " "%#02x, entry point at %04x:%04x\n", undinic, rm_params.segment, rm_params.offset, params_len, - undinic->entry.segment, undinic->entry.offset ); + undinet_entry_point.segment, + undinet_entry_point.offset ); DBGC ( undinic, "UNDINIC %p parameters provided:\n", undinic ); DBGC_HDA ( undinic, rm_params, params, params_len ); DBGC ( undinic, "UNDINIC %p parameters returned:\n", undinic ); @@ -268,13 +266,6 @@ static unsigned int last_trigger_count = 0; * Hook UNDI interrupt service routine * * @v irq IRQ number - * - * The UNDI ISR specifically does @b not chain to the previous - * interrupt handler. BIOSes seem to install somewhat perverse - * default interrupt handlers; some do nothing other than an iret (and - * so will cause a screaming interrupt if there really is another - * interrupting device) and some disable the interrupt at the PIC (and - * so will bring our own interrupts to a shuddering halt). */ static void undinet_hook_isr ( unsigned int irq ) { @@ -411,6 +402,15 @@ static int undinet_transmit ( struct net_device *netdev, * so instead of doing it the easy way we have to go to all the hassle * of installing a genuine interrupt service routine and dealing with * the wonderful 8259 Programmable Interrupt Controller. Joy. + * + * Addendum (10/07/07). When doing things such as iSCSI boot, in + * which we have to co-operate with a running OS, we can't get away + * with the "ISR-just-increments-a-counter-and-returns" trick at all, + * because it involves tying up the PIC for far too long, and other + * interrupt-dependent components (e.g. local disks) start breaking. + * We therefore implement a "proper" ISR which calls PXENV_UNDI_ISR + * from within interrupt context in order to deassert the device + * interrupt, and sends EOI if applicable. */ static void undinet_poll ( struct net_device *netdev ) { struct undi_nic *undinic = netdev->priv; @@ -425,28 +425,6 @@ static void undinet_poll ( struct net_device *netdev ) { if ( ! undinet_isr_triggered() ) return; -#if 0 - /* See if this was our interrupt */ - memset ( &undi_isr, 0, sizeof ( undi_isr ) ); - undi_isr.FuncFlag = PXENV_UNDI_ISR_IN_START; - if ( ( rc = undinet_call ( undinic, PXENV_UNDI_ISR, &undi_isr, - sizeof ( undi_isr ) ) ) != 0 ) - return; - - /* Send EOI to the PIC. In an ideal world, we'd do - * this only for interrupts which the UNDI stack - * reports as "ours". However, since we don't (can't) - * chain to the previous interrupt handler, we have to - * acknowledge all interrupts. See undinet_hook_isr() - * for more background. - */ - send_eoi ( undinic->irq ); - - /* If this wasn't our interrupt, exit now */ - if ( undi_isr.FuncFlag != PXENV_UNDI_ISR_OUT_OURS ) - return; -#endif - /* Start ISR processing */ undinic->isr_processing = 1; undi_isr.FuncFlag = PXENV_UNDI_ISR_IN_PROCESS; @@ -651,7 +629,7 @@ int undinet_probe ( struct undi_device *undi ) { undi_set_drvdata ( undi, netdev ); netdev->dev = &undi->dev; memset ( undinic, 0, sizeof ( *undinic ) ); - undinic->entry = undi->entry; + undinet_entry_point = undi->entry; DBGC ( undinic, "UNDINIC %p using UNDI %p\n", undinic, undi ); /* Hook in UNDI stack */ @@ -770,6 +748,9 @@ void undinet_remove ( struct undi_device *undi ) { sizeof ( stop_undi ) ); undi->flags &= ~UNDI_FL_STARTED; + /* Clear entry point */ + memset ( &undinet_entry_point, 0, sizeof ( undinet_entry_point ) ); + /* Free network device */ netdev_nullify ( netdev ); netdev_put ( netdev );