From 2dc8ed1eb8015c2810fb01f524c8e8c46751ee9b Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 3 Jul 2007 15:53:29 +0100 Subject: [PATCH] Work around Etherboot 5.4 bug when multiple packets are received. --- src/arch/i386/drivers/net/undinet.c | 39 ++++++++++++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-) diff --git a/src/arch/i386/drivers/net/undinet.c b/src/arch/i386/drivers/net/undinet.c index dc4ef98f..46a759cc 100644 --- a/src/arch/i386/drivers/net/undinet.c +++ b/src/arch/i386/drivers/net/undinet.c @@ -45,8 +45,20 @@ struct undi_nic { unsigned int irq; /** Currently processing ISR */ int isr_processing; + /** Bug workarounds */ + int hacks; }; +/** + * @defgroup undi_hacks UNDI workarounds + * @{ + */ + +/** Work around Etherboot 5.4 bugs */ +#define UNDI_HACK_EB54 0x0001 + +/** @} */ + static void undinet_close ( struct net_device *netdev ); /***************************************************************************** @@ -245,6 +257,9 @@ static struct segoff prev_handler[ IRQ_MAX + 1 ]; static volatile uint8_t __text16 ( trigger_count ) = 0; #define trigger_count __use_text16 ( trigger_count ) +/** Last observed trigger count */ +static unsigned int last_trigger_count = 0; + /** * Hook UNDI interrupt service routine * @@ -292,7 +307,6 @@ static void undinet_unhook_isr ( unsigned int irq ) { * @ret triggered ISR has been triggered since last check */ static int undinet_isr_triggered ( void ) { - static unsigned int last_trigger_count = 0; unsigned int this_trigger_count; /* Read trigger_count. Do this only once; it is volatile */ @@ -470,9 +484,15 @@ static void undinet_poll ( struct net_device *netdev, unsigned int rx_quota ) { undi_isr.Frame.segment, undi_isr.Frame.offset, frag_len ); if ( iob_len ( iobuf ) == len ) { + /* Whole packet received; deliver it */ netdev_rx ( netdev, iobuf ); iobuf = NULL; --rx_quota; + /* Etherboot 5.4 fails to return all packets + * under mild load; pretend it retriggered. + */ + if ( undinic->hacks & UNDI_HACK_EB54 ) + --last_trigger_count; } break; case PXENV_UNDI_ISR_OUT_DONE: @@ -592,6 +612,7 @@ int undinet_probe ( struct undi_device *undi ) { struct s_PXENV_UNDI_STARTUP undi_startup; struct s_PXENV_UNDI_INITIALIZE undi_initialize; struct s_PXENV_UNDI_GET_INFORMATION undi_info; + struct s_PXENV_UNDI_GET_IFACE_INFO undi_iface; struct s_PXENV_UNDI_SHUTDOWN undi_shutdown; struct s_PXENV_UNDI_CLEANUP undi_cleanup; struct s_PXENV_STOP_UNDI stop_undi; @@ -649,6 +670,21 @@ int undinet_probe ( struct undi_device *undi ) { DBGC ( undinic, "UNDINIC %p is %s on IRQ %d\n", undinic, eth_ntoa ( netdev->ll_addr ), undinic->irq ); + /* Get interface information */ + memset ( &undi_iface, 0, sizeof ( undi_iface ) ); + if ( ( rc = undinet_call ( undinic, PXENV_UNDI_GET_IFACE_INFO, + &undi_iface, + sizeof ( undi_iface ) ) ) != 0 ) + goto err_undi_get_iface_info; + DBGC ( undinic, "UNDINIC %p has type %s and link speed %ld\n", + undinic, undi_iface.IfaceType, undi_iface.LinkSpeed ); + if ( strncmp ( ( ( char * ) undi_iface.IfaceType ), "Etherboot", + sizeof ( undi_iface.IfaceType ) ) == 0 ) { + DBGC ( undinic, "UNDINIC %p Etherboot 5.4 workaround enabled\n", + undinic ); + undinic->hacks |= UNDI_HACK_EB54; + } + /* Point to NIC specific routines */ netdev->open = undinet_open; netdev->close = undinet_close; @@ -663,6 +699,7 @@ int undinet_probe ( struct undi_device *undi ) { return 0; err_register: + err_undi_get_iface_info: err_bad_irq: err_undi_get_information: err_undi_initialize: