Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
[rndis] Send RNDIS_INITIALISE_MSG
The Hyper-V RNDIS implementation on Windows Server 2012 R2 requires
that we send an explicit RNDIS initialisation message in order to get
a working RX datapath.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
  • Loading branch information
mcb30 committed Dec 19, 2014
1 parent ef16d0d commit 1d0ade4
Show file tree
Hide file tree
Showing 2 changed files with 181 additions and 24 deletions.
22 changes: 20 additions & 2 deletions src/include/ipxe/rndis.h
Expand Up @@ -28,10 +28,10 @@ struct rndis_header {
} __attribute__ (( packed ));

/** RNDIS initialise message */
#define RNDIS_INITIALIZE_MSG 0x00000002UL
#define RNDIS_INITIALISE_MSG 0x00000002UL

/** RNDIS initialise message */
struct rndis_initialize_message {
struct rndis_initialise_message {
/** Request ID */
uint32_t id;
/** Major version */
Expand All @@ -42,6 +42,24 @@ struct rndis_initialize_message {
uint32_t mtu;
} __attribute__ (( packed ));

/** Request ID used for initialisation
*
* This is a policy decision.
*/
#define RNDIS_INIT_ID 0xe110e110UL

/** RNDIS major version */
#define RNDIS_VERSION_MAJOR 1

/** RNDIS minor version */
#define RNDIS_VERSION_MINOR 0

/** RNDIS maximum transfer size
*
* This is a policy decision.
*/
#define RNDIS_MTU 2048

/** RNDIS initialise completion */
#define RNDIS_INITIALISE_CMPLT 0x80000002UL

Expand Down
183 changes: 161 additions & 22 deletions src/net/rndis.c
Expand Up @@ -53,6 +53,38 @@ static struct io_buffer * rndis_alloc_iob ( size_t len ) {
return iobuf;
}

/**
* Wait for completion
*
* @v rndis RNDIS device
* @v wait_id Request ID
* @ret rc Return status code
*/
static int rndis_wait ( struct rndis_device *rndis, unsigned int wait_id ) {
unsigned int i;

/* Record query ID */
rndis->wait_id = wait_id;

/* Wait for operation to complete */
for ( i = 0 ; i < RNDIS_MAX_WAIT_MS ; i++ ) {

/* Check for completion */
if ( ! rndis->wait_id )
return rndis->wait_rc;

/* Poll RNDIS device */
rndis->op->poll ( rndis );

/* Delay for 1ms */
mdelay ( 1 );
}

DBGC ( rndis, "RNDIS %s timed out waiting for ID %#08x\n",
rndis->name, wait_id );
return -ETIMEDOUT;
}

/**
* Transmit message
*
Expand Down Expand Up @@ -227,6 +259,114 @@ static void rndis_rx_data ( struct rndis_device *rndis,
netdev_rx_err ( netdev, iob_disown ( iobuf ), rc );
}

/**
* Transmit initialisation message
*
* @v rndis RNDIS device
* @v id Request ID
* @ret rc Return status code
*/
static int rndis_tx_initialise ( struct rndis_device *rndis, unsigned int id ) {
struct io_buffer *iobuf;
struct rndis_initialise_message *msg;
int rc;

/* Allocate I/O buffer */
iobuf = rndis_alloc_iob ( sizeof ( *msg ) );
if ( ! iobuf ) {
rc = -ENOMEM;
goto err_alloc;
}

/* Construct message */
msg = iob_put ( iobuf, sizeof ( *msg ) );
memset ( msg, 0, sizeof ( *msg ) );
msg->id = id; /* Non-endian */
msg->major = cpu_to_le32 ( RNDIS_VERSION_MAJOR );
msg->minor = cpu_to_le32 ( RNDIS_VERSION_MINOR );
msg->mtu = cpu_to_le32 ( RNDIS_MTU );

/* Transmit message */
if ( ( rc = rndis_tx_message ( rndis, iobuf,
RNDIS_INITIALISE_MSG ) ) != 0 )
goto err_tx;

return 0;

err_tx:
free_iob ( iobuf );
err_alloc:
return rc;
}

/**
* Receive initialisation completion
*
* @v rndis RNDIS device
* @v iobuf I/O buffer
*/
static void rndis_rx_initialise ( struct rndis_device *rndis,
struct io_buffer *iobuf ) {
struct rndis_initialise_completion *cmplt;
size_t len = iob_len ( iobuf );
unsigned int id;
int rc;

/* Sanity check */
if ( len < sizeof ( *cmplt ) ) {
DBGC ( rndis, "RNDIS %s received underlength initialisation "
"completion:\n", rndis->name );
DBGC_HDA ( rndis, 0, iobuf->data, len );
rc = -EINVAL;
goto err_len;
}
cmplt = iobuf->data;

/* Extract request ID */
id = cmplt->id; /* Non-endian */

/* Check status */
if ( cmplt->status ) {
DBGC ( rndis, "RNDIS %s received initialisation completion "
"failure %#08x\n", rndis->name,
le32_to_cpu ( cmplt->status ) );
rc = -EIO;
goto err_status;
}

/* Success */
rc = 0;

err_status:
/* Record completion result if applicable */
if ( id == rndis->wait_id ) {
rndis->wait_id = 0;
rndis->wait_rc = rc;
}
err_len:
free_iob ( iobuf );
}

/**
* Initialise RNDIS
*
* @v rndis RNDIS device
* @ret rc Return status code
*/
static int rndis_initialise ( struct rndis_device *rndis ) {
int rc;

/* Transmit initialisation message */
if ( ( rc = rndis_tx_initialise ( rndis, RNDIS_INIT_ID ) ) != 0 )
return rc;

/* Wait for response */
if ( ( rc = rndis_wait ( rndis, RNDIS_INIT_ID ) ) != 0 )
return rc;

return 0;
}

/**
* Transmit OID message
*
Expand Down Expand Up @@ -443,33 +583,17 @@ static void rndis_rx_set_oid ( struct rndis_device *rndis,
*/
static int rndis_oid ( struct rndis_device *rndis, unsigned int oid,
const void *data, size_t len ) {
unsigned int i;
int rc;

/* Transmit query */
if ( ( rc = rndis_tx_oid ( rndis, oid, data, len ) ) != 0 )
return rc;

/* Record query ID */
rndis->wait_id = oid;

/* Wait for operation to complete */
for ( i = 0 ; i < RNDIS_MAX_WAIT_MS ; i++ ) {

/* Check for completion */
if ( ! rndis->wait_id )
return rndis->wait_rc;

/* Poll RNDIS device */
rndis->op->poll ( rndis );

/* Delay for 1ms */
mdelay ( 1 );
}
/* Wait for response */
if ( ( rc = rndis_wait ( rndis, oid ) ) != 0 )
return rc;

DBGC ( rndis, "RNDIS %s timed out waiting for OID %#08x\n",
rndis->name, oid );
return -ETIMEDOUT;
return 0;
}

/**
Expand Down Expand Up @@ -550,6 +674,10 @@ static void rndis_rx_message ( struct rndis_device *rndis,
rndis_rx_data ( rndis, iob_disown ( iobuf ) );
break;

case RNDIS_INITIALISE_CMPLT:
rndis_rx_initialise ( rndis, iob_disown ( iobuf ) );
break;

case RNDIS_QUERY_CMPLT:
rndis_rx_query_oid ( rndis, iob_disown ( iobuf ) );
break;
Expand Down Expand Up @@ -615,8 +743,9 @@ void rndis_rx ( struct rndis_device *rndis, struct io_buffer *iobuf ) {
/* Parse and check header */
type = le32_to_cpu ( header->type );
len = le32_to_cpu ( header->len );
if ( len > iob_len ( iobuf ) ) {
DBGC ( rndis, "RNDIS %s received underlength packet:\n",
if ( ( len < sizeof ( *header ) ) ||
( len > iob_len ( iobuf ) ) ) {
DBGC ( rndis, "RNDIS %s received malformed packet:\n",
rndis->name );
DBGC_HDA ( rndis, 0, iobuf->data, iob_len ( iobuf ) );
rc = -EINVAL;
Expand Down Expand Up @@ -667,6 +796,10 @@ static int rndis_open ( struct net_device *netdev ) {
goto err_open;
}

/* Initialise RNDIS */
if ( ( rc = rndis_initialise ( rndis ) ) != 0 )
goto err_initialise;

/* Set receive filter */
filter = cpu_to_le32 ( RNDIS_FILTER_UNICAST |
RNDIS_FILTER_MULTICAST |
Expand All @@ -689,6 +822,7 @@ static int rndis_open ( struct net_device *netdev ) {

err_query_link:
err_set_filter:
err_initialise:
rndis->op->close ( rndis );
err_open:
return rc;
Expand Down Expand Up @@ -794,6 +928,10 @@ int register_rndis ( struct rndis_device *rndis ) {
goto err_open;
}

/* Initialise RNDIS */
if ( ( rc = rndis_initialise ( rndis ) ) != 0 )
goto err_initialise;

/* Query permanent MAC address */
if ( ( rc = rndis_oid ( rndis, RNDIS_OID_802_3_PERMANENT_ADDRESS,
NULL, 0 ) ) != 0 )
Expand All @@ -817,6 +955,7 @@ int register_rndis ( struct rndis_device *rndis ) {
err_query_link:
err_query_current:
err_query_permanent:
err_initialise:
rndis->op->close ( rndis );
err_open:
unregister_netdev ( netdev );
Expand Down

0 comments on commit 1d0ade4

Please sign in to comment.