]> git.ipfire.org Git - thirdparty/ipxe.git/commitdiff
[rndis] Send RNDIS_INITIALISE_MSG
authorMichael Brown <mcb30@ipxe.org>
Fri, 19 Dec 2014 17:04:25 +0000 (17:04 +0000)
committerMichael Brown <mcb30@ipxe.org>
Fri, 19 Dec 2014 17:05:56 +0000 (17:05 +0000)
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>
src/include/ipxe/rndis.h
src/net/rndis.c

index 60ffd10f5459f76f0fcc68471682928469ab2f14..4e4ab7f8f36aa6bbd5b8d31331b31c0118c6bfd8 100644 (file)
@@ -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 */
@@ -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
 
index 4ad7b81454588ff10896101b36ad5e58ac34ea9a..0de3d3a669df9a16155529be35cbc73785276cbf 100644 (file)
@@ -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
  *
@@ -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
  *
@@ -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;
 }
 
 /**
@@ -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;
@@ -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;
@@ -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 |
@@ -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;
@@ -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 )
@@ -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 );