summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMarty Connor2007-07-03 19:20:54 +0200
committerMarty Connor2007-07-03 19:20:54 +0200
commit4bcfe7507b322bb5ed2faf11e3b6f1cf1abc83ae (patch)
tree38fdcad21781be96a85713753594acab6aa88224
parentWarnings purge: src/{crypto,hci,net} (diff)
parentImplemented (untested) PXENV_START_UNDI. (diff)
downloadipxe-4bcfe7507b322bb5ed2faf11e3b6f1cf1abc83ae.tar.gz
ipxe-4bcfe7507b322bb5ed2faf11e3b6f1cf1abc83ae.tar.xz
ipxe-4bcfe7507b322bb5ed2faf11e3b6f1cf1abc83ae.zip
Merge branch 'master' of /pub/scm/gpxe
-rw-r--r--src/arch/i386/drivers/net/undinet.c49
-rw-r--r--src/drivers/bus/isapnp.c2
-rw-r--r--src/drivers/net/legacy.c11
-rw-r--r--src/drivers/net/pnic.c23
-rw-r--r--src/drivers/net/rtl8139.c31
-rw-r--r--src/include/gpxe/isapnp.h7
-rw-r--r--src/include/gpxe/netdevice.h11
-rw-r--r--src/interface/pxe/pxe_preboot.c73
-rw-r--r--src/interface/pxe/pxe_undi.c13
-rw-r--r--src/net/ipv4.c8
-rw-r--r--src/net/netdevice.c10
11 files changed, 130 insertions, 108 deletions
diff --git a/src/arch/i386/drivers/net/undinet.c b/src/arch/i386/drivers/net/undinet.c
index 6bc0fc36..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 */
@@ -333,6 +347,16 @@ static int undinet_transmit ( struct net_device *netdev,
size_t len = iob_len ( iobuf );
int rc;
+ /* Technically, we ought to make sure that the previous
+ * transmission has completed before we re-use the buffer.
+ * However, many PXE stacks (including at least some Intel PXE
+ * stacks and Etherboot 5.4) fail to generate TX completions.
+ * In practice this won't be a problem, since our TX datapath
+ * has a very low packet volume and we can get away with
+ * assuming that a TX will be complete by the time we want to
+ * transmit the next packet.
+ */
+
/* Copy packet to UNDI I/O buffer */
if ( len > sizeof ( basemem_packet ) )
len = sizeof ( basemem_packet );
@@ -460,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:
@@ -582,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;
@@ -639,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;
@@ -653,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:
diff --git a/src/drivers/bus/isapnp.c b/src/drivers/bus/isapnp.c
index 79268d44..b34108c7 100644
--- a/src/drivers/bus/isapnp.c
+++ b/src/drivers/bus/isapnp.c
@@ -529,7 +529,7 @@ static int isapnp_try_isolate ( void ) {
*
*/
static void isapnp_isolate ( void ) {
- for ( isapnp_read_port = ISAPNP_READ_PORT_MIN ;
+ for ( isapnp_read_port = ISAPNP_READ_PORT_START ;
isapnp_read_port <= ISAPNP_READ_PORT_MAX ;
isapnp_read_port += ISAPNP_READ_PORT_STEP ) {
/* Avoid problematic locations such as the NE2000
diff --git a/src/drivers/net/legacy.c b/src/drivers/net/legacy.c
index 22ddfe66..2d4633d4 100644
--- a/src/drivers/net/legacy.c
+++ b/src/drivers/net/legacy.c
@@ -57,12 +57,17 @@ static void legacy_poll ( struct net_device *netdev, unsigned int rx_quota ) {
}
}
-static int legacy_open ( struct net_device *netdev __unused ) {
+static int legacy_open ( struct net_device *netdev ) {
+ struct nic *nic = netdev->priv;
+
+ nic->nic_op->irq ( nic, ENABLE );
return 0;
}
-static void legacy_close ( struct net_device *netdev __unused ) {
- /* Nothing to do */
+static void legacy_close ( struct net_device *netdev ) {
+ struct nic *nic = netdev->priv;
+
+ nic->nic_op->irq ( nic, DISABLE );
}
int legacy_probe ( void *hwdev,
diff --git a/src/drivers/net/pnic.c b/src/drivers/net/pnic.c
index 63b4195f..f614a073 100644
--- a/src/drivers/net/pnic.c
+++ b/src/drivers/net/pnic.c
@@ -161,29 +161,6 @@ static int pnic_transmit ( struct net_device *netdev, struct io_buffer *iobuf )
}
/**************************************************************************
-IRQ - Handle card interrupt status
-***************************************************************************/
-#if 0
-static void pnic_irq ( struct net_device *netdev, irq_action_t action ) {
- struct pnic *pnic = netdev->priv;
- uint8_t enabled;
-
- switch ( action ) {
- case DISABLE :
- case ENABLE :
- enabled = ( action == ENABLE ? 1 : 0 );
- pnic_command ( pnic, PNIC_CMD_MASK_IRQ,
- &enabled, sizeof ( enabled ), NULL, 0, NULL );
- break;
- case FORCE :
- pnic_command ( pnic, PNIC_CMD_FORCE_IRQ,
- NULL, 0, NULL, 0, NULL );
- break;
- }
-}
-#endif
-
-/**************************************************************************
OPEN - Open network device
***************************************************************************/
static int pnic_open ( struct net_device *netdev ) {
diff --git a/src/drivers/net/rtl8139.c b/src/drivers/net/rtl8139.c
index 6acffd9b..90a7182b 100644
--- a/src/drivers/net/rtl8139.c
+++ b/src/drivers/net/rtl8139.c
@@ -380,8 +380,7 @@ static int rtl_transmit ( struct net_device *netdev, struct io_buffer *iobuf ) {
/* Check for space in TX ring */
if ( rtl->tx.iobuf[rtl->tx.next] != NULL ) {
- printf ( "TX overflow\n" );
- free_iob ( iobuf );
+ DBG ( "TX overflow\n" );
return -ENOBUFS;
}
@@ -472,34 +471,6 @@ static void rtl_poll ( struct net_device *netdev, unsigned int rx_quota ) {
}
}
-#if 0
-static void rtl_irq(struct nic *nic, irq_action_t action)
-{
- unsigned int mask;
- /* Bit of a guess as to which interrupts we should allow */
- unsigned int interested = ROK | RER | RXOVW | FOVW | SERR;
-
- switch ( action ) {
- case DISABLE :
- case ENABLE :
- mask = inw(rtl->ioaddr + IntrMask);
- mask = mask & ~interested;
- if ( action == ENABLE ) mask = mask | interested;
- outw(mask, rtl->ioaddr + IntrMask);
- break;
- case FORCE :
- /* Apparently writing a 1 to this read-only bit of a
- * read-only and otherwise unrelated register will
- * force an interrupt. If you ever want to see how
- * not to write a datasheet, read the one for the
- * RTL8139...
- */
- outb(EROK, rtl->ioaddr + RxEarlyStatus);
- break;
- }
-}
-#endif
-
/**
* Probe PCI device
*
diff --git a/src/include/gpxe/isapnp.h b/src/include/gpxe/isapnp.h
index 72ce0a6f..07797a99 100644
--- a/src/include/gpxe/isapnp.h
+++ b/src/include/gpxe/isapnp.h
@@ -49,7 +49,8 @@
/* Port addresses */
#define ISAPNP_ADDRESS 0x279
#define ISAPNP_WRITE_DATA 0xa79
-#define ISAPNP_READ_PORT_MIN 0x213 /* ISAPnP spec says 0x203, but
+#define ISAPNP_READ_PORT_MIN 0x203
+#define ISAPNP_READ_PORT_START 0x213 /* ISAPnP spec says 0x203, but
* Linux ISAPnP starts at
* 0x213 with no explanatory
* comment. 0x203 probably
@@ -63,6 +64,10 @@
* any value less than 16.
*/
+/* Card select numbers */
+#define ISAPNP_CSN_MIN 0x01
+#define ISAPNP_CSN_MAX 0x0f
+
/* Registers */
#define ISAPNP_READPORT 0x00
#define ISAPNP_SERIALISOLATION 0x01
diff --git a/src/include/gpxe/netdevice.h b/src/include/gpxe/netdevice.h
index c0df7c96..0bc5311c 100644
--- a/src/include/gpxe/netdevice.h
+++ b/src/include/gpxe/netdevice.h
@@ -289,8 +289,9 @@ netdev_put ( struct net_device *netdev ) {
}
extern int netdev_tx ( struct net_device *netdev, struct io_buffer *iobuf );
-void netdev_tx_complete ( struct net_device *netdev, struct io_buffer *iobuf );
-void netdev_tx_complete_next ( struct net_device *netdev );
+extern void netdev_tx_complete ( struct net_device *netdev,
+ struct io_buffer *iobuf );
+extern void netdev_tx_complete_next ( struct net_device *netdev );
extern void netdev_rx ( struct net_device *netdev, struct io_buffer *iobuf );
extern int netdev_poll ( struct net_device *netdev, unsigned int rx_quota );
extern struct io_buffer * netdev_rx_dequeue ( struct net_device *netdev );
@@ -299,9 +300,9 @@ extern int register_netdev ( struct net_device *netdev );
extern int netdev_open ( struct net_device *netdev );
extern void netdev_close ( struct net_device *netdev );
extern void unregister_netdev ( struct net_device *netdev );
-struct net_device * find_netdev ( const char *name );
-struct net_device * find_pci_netdev ( unsigned int busdevfn );
-
+extern struct net_device * find_netdev ( const char *name );
+extern struct net_device * find_netdev_by_location ( unsigned int bus_type,
+ unsigned int location );
extern int net_tx ( struct io_buffer *iobuf, struct net_device *netdev,
struct net_protocol *net_protocol, const void *ll_dest );
extern int net_rx ( struct io_buffer *iobuf, struct net_device *netdev,
diff --git a/src/interface/pxe/pxe_preboot.c b/src/interface/pxe/pxe_preboot.c
index 15752b23..a74e58af 100644
--- a/src/interface/pxe/pxe_preboot.c
+++ b/src/interface/pxe/pxe_preboot.c
@@ -28,6 +28,9 @@
#include <stdlib.h>
#include <gpxe/uaccess.h>
#include <gpxe/dhcp.h>
+#include <gpxe/device.h>
+#include <gpxe/netdevice.h>
+#include <gpxe/isapnp.h>
#include <basemem_packet.h>
#include "pxe.h"
#include "pxe_call.h"
@@ -196,41 +199,47 @@ PXENV_EXIT_t pxenv_restart_tftp ( struct s_PXENV_TFTP_READ_FILE
* Status: working
*/
PXENV_EXIT_t pxenv_start_undi ( struct s_PXENV_START_UNDI *start_undi ) {
+ unsigned int isapnp_read_port;
+ unsigned int isapnp_csn;
+ unsigned int pci_busdevfn;
+ unsigned int bus_type;
+ unsigned int location;
+ struct net_device *netdev;
+
+ DBG ( "PXENV_START_UNDI %04x:%04x:%04x",
+ start_undi->AX, start_undi->BX, start_undi->DX );
+
+ /* Determine bus type and location */
+ isapnp_read_port = start_undi->DX;
+ isapnp_csn = start_undi->BX;
+ pci_busdevfn = start_undi->AX;
+
+ /* Use a heuristic to decide whether we are PCI or ISAPnP */
+ if ( ( isapnp_read_port >= ISAPNP_READ_PORT_MIN ) &&
+ ( isapnp_read_port <= ISAPNP_READ_PORT_MAX ) &&
+ ( isapnp_csn >= ISAPNP_CSN_MIN ) &&
+ ( isapnp_csn <= ISAPNP_CSN_MAX ) ) {
+ bus_type = BUS_TYPE_ISAPNP;
+ location = isapnp_csn;
+ } else {
+ bus_type = BUS_TYPE_PCI;
+ location = pci_busdevfn;
+ }
- DBG ( "PXENV_START_UNDI" );
-
-#if 0
- /* Record PCI bus & devfn passed by caller, so we know which
- * NIC they want to use.
- *
- * If they don't match our already-existing NIC structure, set
- * values to ensure that the specified NIC is used at the next
- * call to pxe_intialise_nic().
- */
- bus = ( start_undi->AX >> 8 ) & 0xff;
- devfn = start_undi->AX & 0xff;
-
-#warning "device probing mechanism has completely changed"
-#if 0
- if ( ( pci->dev.driver == NULL ) ||
- ( pci->dev.bus != bus ) || ( pci->dev.devfn != devfn ) ) {
- /* This is quite a bit of a hack and relies on
- * knowledge of the internal operation of Etherboot's
- * probe mechanism.
- */
- DBG ( " set PCI %hhx:%hhx.%hhx",
- bus, PCI_SLOT(devfn), PCI_FUNC(devfn) );
- dev->type = BOOT_NIC;
- dev->to_probe = PROBE_PCI;
- memset ( &dev->state, 0, sizeof(dev->state) );
- pci->advance = 1;
- pci->dev.use_specified = 1;
- pci->dev.bus = bus;
- pci->dev.devfn = devfn;
+ /* Look for a matching net device */
+ netdev = find_netdev_by_location ( bus_type, location );
+ if ( ! netdev ) {
+ DBG ( " no net device found" );
+ start_undi->Status = PXENV_STATUS_UNDI_CANNOT_INITIALIZE_NIC;
+ return PXENV_EXIT_FAILURE;
}
-#endif
+ DBG ( " using netdev %s", netdev->name );
-#endif
+ /* Save as PXE net device */
+ pxe_set_netdev ( netdev );
+
+ /* Hook INT 1A */
+ pxe_hook_int1a();
start_undi->Status = PXENV_STATUS_SUCCESS;
return PXENV_EXIT_SUCCESS;
diff --git a/src/interface/pxe/pxe_undi.c b/src/interface/pxe/pxe_undi.c
index 0ce4b290..33de8117 100644
--- a/src/interface/pxe/pxe_undi.c
+++ b/src/interface/pxe/pxe_undi.c
@@ -394,18 +394,15 @@ PXENV_EXIT_t pxenv_undi_initiate_diags ( struct s_PXENV_UNDI_INITIATE_DIAGS
/* PXENV_UNDI_FORCE_INTERRUPT
*
- * Status: working
+ * Status: won't implement (would require driver API changes for no
+ * perceptible benefit)
*/
PXENV_EXIT_t pxenv_undi_force_interrupt ( struct s_PXENV_UNDI_FORCE_INTERRUPT
*undi_force_interrupt ) {
DBG ( "PXENV_UNDI_FORCE_INTERRUPT" );
-#if 0
- eth_irq ( FORCE );
-#endif
-
- undi_force_interrupt->Status = PXENV_STATUS_SUCCESS;
- return PXENV_EXIT_SUCCESS;
+ undi_force_interrupt->Status = PXENV_STATUS_UNSUPPORTED;
+ return PXENV_EXIT_FAILURE;
}
/* PXENV_UNDI_GET_MCAST_ADDRESS
@@ -483,7 +480,7 @@ PXENV_EXIT_t pxenv_undi_get_iface_info ( struct s_PXENV_UNDI_GET_IFACE_INFO
* Most PXE stacks seem to take this approach.
*/
snprintf ( ( char * ) undi_get_iface_info->IfaceType,
- sizeof ( undi_get_iface_info->IfaceType ), "Etherboot" );
+ sizeof ( undi_get_iface_info->IfaceType ), "gPXE" );
undi_get_iface_info->LinkSpeed = 10000000; /* 10 Mbps */
undi_get_iface_info->ServiceFlags = 0;
memset ( undi_get_iface_info->Reserved, 0,
diff --git a/src/net/ipv4.c b/src/net/ipv4.c
index e1219edb..35341b3d 100644
--- a/src/net/ipv4.c
+++ b/src/net/ipv4.c
@@ -141,12 +141,20 @@ void del_ipv4_address ( struct net_device *netdev ) {
* @v dest Final destination address
* @ret dest Next hop destination address
* @ret miniroute Routing table entry to use, or NULL if no route
+ *
+ * If the route requires use of a gateway, the next hop destination
+ * address will be overwritten with the gateway address.
*/
static struct ipv4_miniroute * ipv4_route ( struct in_addr *dest ) {
struct ipv4_miniroute *miniroute;
int local;
int has_gw;
+ /* Never attempt to route the broadcast address */
+ if ( dest->s_addr == INADDR_BROADCAST )
+ return NULL;
+
+ /* Find first usable route in routing table */
list_for_each_entry ( miniroute, &ipv4_miniroutes, list ) {
local = ( ( ( dest->s_addr ^ miniroute->address.s_addr )
& miniroute->netmask.s_addr ) == 0 );
diff --git a/src/net/netdevice.c b/src/net/netdevice.c
index 8a099107..971830d9 100644
--- a/src/net/netdevice.c
+++ b/src/net/netdevice.c
@@ -356,15 +356,17 @@ struct net_device * find_netdev ( const char *name ) {
/**
* Get network device by PCI bus:dev.fn address
*
- * @v busdevfn PCI bus:dev.fn address
+ * @v bus_type Bus type
+ * @v location Bus location
* @ret netdev Network device, or NULL
*/
-struct net_device * find_pci_netdev ( unsigned int busdevfn ) {
+struct net_device * find_netdev_by_location ( unsigned int bus_type,
+ unsigned int location ) {
struct net_device *netdev;
list_for_each_entry ( netdev, &net_devices, list ) {
- if ( ( netdev->dev->desc.bus_type == BUS_TYPE_PCI ) &&
- ( netdev->dev->desc.location == busdevfn ) )
+ if ( ( netdev->dev->desc.bus_type == bus_type ) &&
+ ( netdev->dev->desc.location == location ) )
return netdev;
}