summaryrefslogtreecommitdiffstats
path: root/qemu/roms/ipxe/src/drivers/net/efi/nii.c
diff options
context:
space:
mode:
Diffstat (limited to 'qemu/roms/ipxe/src/drivers/net/efi/nii.c')
-rw-r--r--qemu/roms/ipxe/src/drivers/net/efi/nii.c150
1 files changed, 117 insertions, 33 deletions
diff --git a/qemu/roms/ipxe/src/drivers/net/efi/nii.c b/qemu/roms/ipxe/src/drivers/net/efi/nii.c
index d0d7da95a..b91848f5c 100644
--- a/qemu/roms/ipxe/src/drivers/net/efi/nii.c
+++ b/qemu/roms/ipxe/src/drivers/net/efi/nii.c
@@ -15,9 +15,13 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
+ *
+ * You can also choose to distribute this program under the terms of
+ * the Unmodified Binary Distribution Licence (as given in the file
+ * COPYING.UBDL), provided that you have satisfied its requirements.
*/
-FILE_LICENCE ( GPL2_OR_LATER );
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <string.h>
#include <strings.h>
@@ -168,6 +172,9 @@ struct nii_nic {
/** Saved task priority level */
EFI_TPL saved_tpl;
+ /** Media status is supported */
+ int media;
+
/** Current transmit buffer */
struct io_buffer *txbuf;
/** Current receive buffer */
@@ -408,6 +415,13 @@ static int nii_issue_cpb_db ( struct nii_nic *nii, unsigned int op, void *cpb,
cdb.IFnum = nii->nii->IfNum;
/* Issue command */
+ DBGC2 ( nii, "NII %s issuing %02x:%04x ifnum %d%s%s\n",
+ nii->dev.name, cdb.OpCode, cdb.OpFlags, cdb.IFnum,
+ ( cpb ? " cpb" : "" ), ( db ? " db" : "" ) );
+ if ( cpb )
+ DBGC2_HD ( nii, cpb, cpb_len );
+ if ( db )
+ DBGC2_HD ( nii, db, db_len );
nii->issue ( ( intptr_t ) &cdb );
/* Check completion status */
@@ -552,6 +566,7 @@ static int nii_get_init_info ( struct nii_nic *nii,
nii->buffer_len = db.MemoryRequired;
nii->mtu = ( db.FrameDataLen + db.MediaHeaderLen );
netdev->max_pkt_len = nii->mtu;
+ nii->media = ( stat & PXE_STATFLAGS_GET_STATUS_NO_MEDIA_SUPPORTED );
return 0;
}
@@ -560,10 +575,12 @@ static int nii_get_init_info ( struct nii_nic *nii,
* Initialise UNDI
*
* @v nii NII NIC
+ * @v flags Flags
* @ret rc Return status code
*/
-static int nii_initialise ( struct nii_nic *nii ) {
+static int nii_initialise_flags ( struct nii_nic *nii, unsigned int flags ) {
PXE_CPB_INITIALIZE cpb;
+ PXE_DB_INITIALIZE db;
unsigned int op;
int stat;
int rc;
@@ -580,10 +597,13 @@ static int nii_initialise ( struct nii_nic *nii ) {
cpb.MemoryAddr = ( ( intptr_t ) nii->buffer );
cpb.MemoryLength = nii->buffer_len;
+ /* Construct data block */
+ memset ( &db, 0, sizeof ( db ) );
+
/* Issue command */
- op = NII_OP ( PXE_OPCODE_INITIALIZE,
- PXE_OPFLAGS_INITIALIZE_DO_NOT_DETECT_CABLE );
- if ( ( stat = nii_issue_cpb ( nii, op, &cpb, sizeof ( cpb ) ) ) < 0 ) {
+ op = NII_OP ( PXE_OPCODE_INITIALIZE, flags );
+ if ( ( stat = nii_issue_cpb_db ( nii, op, &cpb, sizeof ( cpb ),
+ &db, sizeof ( db ) ) ) < 0 ) {
rc = -EIO_STAT ( stat );
DBGC ( nii, "NII %s could not initialise: %s\n",
nii->dev.name, strerror ( rc ) );
@@ -599,6 +619,36 @@ static int nii_initialise ( struct nii_nic *nii ) {
}
/**
+ * Initialise UNDI
+ *
+ * @v nii NII NIC
+ * @ret rc Return status code
+ */
+static int nii_initialise ( struct nii_nic *nii ) {
+ unsigned int flags;
+
+ /* Initialise UNDI */
+ flags = PXE_OPFLAGS_INITIALIZE_DO_NOT_DETECT_CABLE;
+ return nii_initialise_flags ( nii, flags );
+}
+
+/**
+ * Initialise UNDI and detect cable
+ *
+ * @v nii NII NIC
+ * @ret rc Return status code
+ */
+static int nii_initialise_and_detect ( struct nii_nic *nii ) {
+ unsigned int flags;
+
+ /* Initialise UNDI and detect cable. This is required to work
+ * around bugs in some Emulex NII drivers.
+ */
+ flags = PXE_OPFLAGS_INITIALIZE_DETECT_CABLE;
+ return nii_initialise_flags ( nii, flags );
+}
+
+/**
* Shut down UNDI
*
* @v nii NII NIC
@@ -630,6 +680,7 @@ static void nii_shutdown ( struct nii_nic *nii ) {
static int nii_get_station_address ( struct nii_nic *nii,
struct net_device *netdev ) {
PXE_DB_STATION_ADDRESS db;
+ unsigned int op;
int stat;
int rc;
@@ -638,8 +689,9 @@ static int nii_get_station_address ( struct nii_nic *nii,
goto err_initialise;
/* Issue command */
- if ( ( stat = nii_issue_db ( nii, PXE_OPCODE_STATION_ADDRESS, &db,
- sizeof ( db ) ) ) < 0 ) {
+ op = NII_OP ( PXE_OPCODE_STATION_ADDRESS,
+ PXE_OPFLAGS_STATION_ADDRESS_READ );
+ if ( ( stat = nii_issue_db ( nii, op, &db, sizeof ( db ) ) ) < 0 ) {
rc = -EIO_STAT ( stat );
DBGC ( nii, "NII %s could not get station address: %s\n",
nii->dev.name, strerror ( rc ) );
@@ -669,18 +721,25 @@ static int nii_get_station_address ( struct nii_nic *nii,
*/
static int nii_set_station_address ( struct nii_nic *nii,
struct net_device *netdev ) {
+ uint32_t implementation = nii->undi->Implementation;
PXE_CPB_STATION_ADDRESS cpb;
+ unsigned int op;
int stat;
int rc;
+ /* Fail if setting station address is unsupported */
+ if ( ! ( implementation & PXE_ROMID_IMP_STATION_ADDR_SETTABLE ) )
+ return -ENOTSUP;
+
/* Construct parameter block */
memset ( &cpb, 0, sizeof ( cpb ) );
memcpy ( cpb.StationAddr, netdev->ll_addr,
netdev->ll_protocol->ll_addr_len );
/* Issue command */
- if ( ( stat = nii_issue_cpb ( nii, PXE_OPCODE_STATION_ADDRESS,
- &cpb, sizeof ( cpb ) ) ) < 0 ) {
+ op = NII_OP ( PXE_OPCODE_STATION_ADDRESS,
+ PXE_OPFLAGS_STATION_ADDRESS_WRITE );
+ if ( ( stat = nii_issue_cpb ( nii, op, &cpb, sizeof ( cpb ) ) ) < 0 ) {
rc = -EIO_STAT ( stat );
DBGC ( nii, "NII %s could not set station address: %s\n",
nii->dev.name, strerror ( rc ) );
@@ -697,21 +756,28 @@ static int nii_set_station_address ( struct nii_nic *nii,
* @ret rc Return status code
*/
static int nii_set_rx_filters ( struct nii_nic *nii ) {
+ uint32_t implementation = nii->undi->Implementation;
+ unsigned int flags;
unsigned int op;
int stat;
int rc;
+ /* Construct receive filter set */
+ flags = ( PXE_OPFLAGS_RECEIVE_FILTER_ENABLE |
+ PXE_OPFLAGS_RECEIVE_FILTER_UNICAST );
+ if ( implementation & PXE_ROMID_IMP_BROADCAST_RX_SUPPORTED )
+ flags |= PXE_OPFLAGS_RECEIVE_FILTER_BROADCAST;
+ if ( implementation & PXE_ROMID_IMP_PROMISCUOUS_RX_SUPPORTED )
+ flags |= PXE_OPFLAGS_RECEIVE_FILTER_PROMISCUOUS;
+ if ( implementation & PXE_ROMID_IMP_PROMISCUOUS_MULTICAST_RX_SUPPORTED )
+ flags |= PXE_OPFLAGS_RECEIVE_FILTER_ALL_MULTICAST;
+
/* Issue command */
- op = NII_OP ( PXE_OPCODE_RECEIVE_FILTERS,
- ( PXE_OPFLAGS_RECEIVE_FILTER_ENABLE |
- PXE_OPFLAGS_RECEIVE_FILTER_UNICAST |
- PXE_OPFLAGS_RECEIVE_FILTER_BROADCAST |
- PXE_OPFLAGS_RECEIVE_FILTER_PROMISCUOUS |
- PXE_OPFLAGS_RECEIVE_FILTER_ALL_MULTICAST ) );
+ op = NII_OP ( PXE_OPCODE_RECEIVE_FILTERS, flags );
if ( ( stat = nii_issue ( nii, op ) ) < 0 ) {
rc = -EIO_STAT ( stat );
- DBGC ( nii, "NII %s could not set receive filters: %s\n",
- nii->dev.name, strerror ( rc ) );
+ DBGC ( nii, "NII %s could not set receive filters %#04x: %s\n",
+ nii->dev.name, flags, strerror ( rc ) );
return rc;
}
@@ -729,6 +795,7 @@ static int nii_transmit ( struct net_device *netdev,
struct io_buffer *iobuf ) {
struct nii_nic *nii = netdev->priv;
PXE_CPB_TRANSMIT cpb;
+ unsigned int op;
int stat;
int rc;
@@ -745,8 +812,10 @@ static int nii_transmit ( struct net_device *netdev,
cpb.MediaheaderLen = netdev->ll_protocol->ll_header_len;
/* Transmit packet */
- if ( ( stat = nii_issue_cpb ( nii, PXE_OPCODE_TRANSMIT, &cpb,
- sizeof ( cpb ) ) ) < 0 ) {
+ op = NII_OP ( PXE_OPCODE_TRANSMIT,
+ ( PXE_OPFLAGS_TRANSMIT_WHOLE |
+ PXE_OPFLAGS_TRANSMIT_DONT_BLOCK ) );
+ if ( ( stat = nii_issue_cpb ( nii, op, &cpb, sizeof ( cpb ) ) ) < 0 ) {
rc = -EIO_STAT ( stat );
DBGC ( nii, "NII %s could not transmit: %s\n",
nii->dev.name, strerror ( rc ) );
@@ -772,12 +841,7 @@ static void nii_poll_tx ( struct net_device *netdev, unsigned int stat ) {
return;
/* Sanity check */
- if ( ! nii->txbuf ) {
- DBGC ( nii, "NII %s reported spurious TX completion\n",
- nii->dev.name );
- netdev_tx_err ( netdev, NULL, -EPIPE );
- return;
- }
+ assert ( nii->txbuf != NULL );
/* Complete transmission */
iobuf = nii->txbuf;
@@ -869,11 +933,14 @@ static void nii_poll ( struct net_device *netdev ) {
int stat;
int rc;
+ /* Construct data block */
+ memset ( &db, 0, sizeof ( db ) );
+
/* Get status */
op = NII_OP ( PXE_OPCODE_GET_STATUS,
( PXE_OPFLAGS_GET_INTERRUPT_STATUS |
- PXE_OPFLAGS_GET_TRANSMITTED_BUFFERS |
- PXE_OPFLAGS_GET_MEDIA_STATUS ) );
+ ( nii->txbuf ? PXE_OPFLAGS_GET_TRANSMITTED_BUFFERS : 0)|
+ ( nii->media ? PXE_OPFLAGS_GET_MEDIA_STATUS : 0 ) ) );
if ( ( stat = nii_issue_db ( nii, op, &db, sizeof ( db ) ) ) < 0 ) {
rc = -EIO_STAT ( stat );
DBGC ( nii, "NII %s could not get status: %s\n",
@@ -882,13 +949,15 @@ static void nii_poll ( struct net_device *netdev ) {
}
/* Process any TX completions */
- nii_poll_tx ( netdev, stat );
+ if ( nii->txbuf )
+ nii_poll_tx ( netdev, stat );
/* Process any RX completions */
nii_poll_rx ( netdev );
/* Check for link state changes */
- nii_poll_link ( netdev, stat );
+ if ( nii->media )
+ nii_poll_link ( netdev, stat );
}
/**
@@ -901,8 +970,18 @@ static int nii_open ( struct net_device *netdev ) {
struct nii_nic *nii = netdev->priv;
int rc;
- /* Initialise NIC */
- if ( ( rc = nii_initialise ( nii ) ) != 0 )
+ /* Initialise NIC
+ *
+ * Some Emulex NII drivers have a bug which prevents packets
+ * from being sent or received unless we specifically ask it
+ * to detect cable presence during initialisation. Work
+ * around these buggy drivers by requesting cable detection at
+ * this point, even though we don't care about link state here
+ * (and would prefer to have the NIC initialise even if no
+ * cable is present, to match the behaviour of all other iPXE
+ * drivers).
+ */
+ if ( ( rc = nii_initialise_and_detect ( nii ) ) != 0 )
goto err_initialise;
/* Attempt to set station address */
@@ -1023,8 +1102,9 @@ int nii_start ( struct efi_device *efidev ) {
nii->issue = ( ( ( void * ) nii->undi ) +
nii->undi->EntryPoint );
}
- DBGC ( nii, "NII %s using UNDI v%x.%x at %p entry %p\n", nii->dev.name,
- nii->nii->MajorVer, nii->nii->MinorVer, nii->undi, nii->issue );
+ DBGC ( nii, "NII %s using UNDI v%x.%x at %p entry %p impl %#08x\n",
+ nii->dev.name, nii->nii->MajorVer, nii->nii->MinorVer,
+ nii->undi, nii->issue, nii->undi->Implementation );
/* Open PCI I/O protocols and locate BARs */
if ( ( rc = nii_pci_open ( nii ) ) != 0 )
@@ -1048,6 +1128,10 @@ int nii_start ( struct efi_device *efidev ) {
DBGC ( nii, "NII %s registered as %s for %p %s\n", nii->dev.name,
netdev->name, device, efi_handle_name ( device ) );
+ /* Set initial link state (if media detection is not supported) */
+ if ( ! nii->media )
+ netdev_link_up ( netdev );
+
return 0;
unregister_netdev ( netdev );