123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836 |
-
-
- FILE_LICENCE ( GPL2_OR_LATER );
-
-
-
- #include <errno.h>
- #include <unistd.h>
- #include <byteswap.h>
- #include <ipxe/umalloc.h>
- #include <ipxe/rndis.h>
- #include <ipxe/vmbus.h>
- #include "netvsc.h"
-
-
- static int netvsc_control ( struct netvsc_device *netvsc, unsigned int xrid,
- const void *data, size_t len ) {
- uint64_t xid = ( NETVSC_BASE_XID + xrid );
- unsigned int i;
- int rc;
-
-
- if ( ( rc = vmbus_send_control ( netvsc->vmdev, xid, data, len ) ) !=0){
- DBGC ( netvsc, "NETVSC %s could not send control message: %s\n",
- netvsc->name, strerror ( rc ) );
- return rc;
- }
-
-
- netvsc->wait_xrid = xrid;
-
-
- for ( i = 0 ; i < NETVSC_MAX_WAIT_MS ; i++ ) {
-
-
- if ( ! netvsc->wait_xrid )
- return netvsc->wait_rc;
-
-
- vmbus_poll ( netvsc->vmdev );
-
-
- mdelay ( 1 );
- }
-
- DBGC ( netvsc, "NETVSC %s timed out waiting for XRID %d\n",
- netvsc->name, xrid );
- vmbus_dump_channel ( netvsc->vmdev );
- return -ETIMEDOUT;
- }
-
-
- static int netvsc_completed ( struct netvsc_device *netvsc __unused,
- const void *data __unused, size_t len __unused ) {
- return 0;
- }
-
-
- static int netvsc_initialise ( struct netvsc_device *netvsc ) {
- struct netvsc_init_message msg;
- int rc;
-
-
- memset ( &msg, 0, sizeof ( msg ) );
- msg.header.type = cpu_to_le32 ( NETVSC_INIT_MSG );
- msg.min = cpu_to_le32 ( NETVSC_VERSION_1 );
- msg.max = cpu_to_le32 ( NETVSC_VERSION_1 );
-
-
- if ( ( rc = netvsc_control ( netvsc, NETVSC_INIT_XRID, &msg,
- sizeof ( msg ) ) ) != 0 ) {
- DBGC ( netvsc, "NETVSC %s could not initialise: %s\n",
- netvsc->name, strerror ( rc ) );
- return rc;
- }
-
- return 0;
- }
-
-
- static int
- netvsc_initialised ( struct netvsc_device *netvsc, const void *data,
- size_t len ) {
- const struct netvsc_init_completion *cmplt = data;
-
-
- if ( len < sizeof ( *cmplt ) ) {
- DBGC ( netvsc, "NETVSC %s underlength initialisation "
- "completion (%zd bytes)\n", netvsc->name, len );
- return -EINVAL;
- }
- if ( cmplt->header.type != cpu_to_le32 ( NETVSC_INIT_CMPLT ) ) {
- DBGC ( netvsc, "NETVSC %s unexpected initialisation completion "
- "type %d\n", netvsc->name,
- le32_to_cpu ( cmplt->header.type ) );
- return -EPROTO;
- }
- if ( cmplt->status != cpu_to_le32 ( NETVSC_OK ) ) {
- DBGC ( netvsc, "NETVSC %s initialisation failure status %d\n",
- netvsc->name, le32_to_cpu ( cmplt->status ) );
- return -EPROTO;
- }
-
- return 0;
- }
-
-
- static int netvsc_ndis_version ( struct netvsc_device *netvsc ) {
- struct netvsc_ndis_version_message msg;
- int rc;
-
-
- memset ( &msg, 0, sizeof ( msg ) );
- msg.header.type = cpu_to_le32 ( NETVSC_NDIS_VERSION_MSG );
- msg.major = cpu_to_le32 ( NETVSC_NDIS_MAJOR );
- msg.minor = cpu_to_le32 ( NETVSC_NDIS_MINOR );
-
-
- if ( ( rc = netvsc_control ( netvsc, NETVSC_NDIS_VERSION_XRID,
- &msg, sizeof ( msg ) ) ) != 0 ) {
- DBGC ( netvsc, "NETVSC %s could not set NDIS version: %s\n",
- netvsc->name, strerror ( rc ) );
- return rc;
- }
-
- return 0;
- }
-
-
- static int netvsc_establish_buffer ( struct netvsc_device *netvsc,
- struct netvsc_buffer *buffer ) {
- struct netvsc_establish_buffer_message msg;
- int rc;
-
-
- memset ( &msg, 0, sizeof ( msg ) );
- msg.header.type = cpu_to_le32 ( buffer->establish_type );
- msg.gpadl = cpu_to_le32 ( buffer->gpadl );
- msg.pageset = buffer->pages.pageset;
-
-
- if ( ( rc = netvsc_control ( netvsc, buffer->establish_xrid, &msg,
- sizeof ( msg ) ) ) != 0 ) {
- DBGC ( netvsc, "NETVSC %s could not establish buffer: %s\n",
- netvsc->name, strerror ( rc ) );
- return rc;
- }
-
- return 0;
- }
-
-
- static int netvsc_rx_established_buffer ( struct netvsc_device *netvsc,
- const void *data, size_t len ) {
- const struct netvsc_rx_establish_buffer_completion *cmplt = data;
-
-
- if ( len < sizeof ( *cmplt ) ) {
- DBGC ( netvsc, "NETVSC %s underlength buffer completion (%zd "
- "bytes)\n", netvsc->name, len );
- return -EINVAL;
- }
- if ( cmplt->header.type != cpu_to_le32 ( NETVSC_RX_ESTABLISH_CMPLT ) ) {
- DBGC ( netvsc, "NETVSC %s unexpected buffer completion type "
- "%d\n", netvsc->name, le32_to_cpu ( cmplt->header.type));
- return -EPROTO;
- }
- if ( cmplt->status != cpu_to_le32 ( NETVSC_OK ) ) {
- DBGC ( netvsc, "NETVSC %s buffer failure status %d\n",
- netvsc->name, le32_to_cpu ( cmplt->status ) );
- return -EPROTO;
- }
-
- return 0;
- }
-
-
- static int netvsc_revoke_buffer ( struct netvsc_device *netvsc,
- struct netvsc_buffer *buffer ) {
- struct netvsc_revoke_buffer_message msg;
- int rc;
-
-
- memset ( &msg, 0, sizeof ( msg ) );
- msg.header.type = cpu_to_le32 ( buffer->revoke_type );
- msg.pageset = buffer->pages.pageset;
-
-
- if ( ( rc = netvsc_control ( netvsc, buffer->revoke_xrid,
- &msg, sizeof ( msg ) ) ) != 0 ) {
- DBGC ( netvsc, "NETVSC %s could not revoke buffer: %s\n",
- netvsc->name, strerror ( rc ) );
- return rc;
- }
-
- return 0;
- }
-
-
- static int netvsc_recv_control ( struct vmbus_device *vmdev, uint64_t xid,
- const void *data, size_t len ) {
- struct rndis_device *rndis = vmbus_get_drvdata ( vmdev );
- struct netvsc_device *netvsc = rndis->priv;
-
- DBGC ( netvsc, "NETVSC %s received unsupported control packet "
- "(%08llx):\n", netvsc->name, xid );
- DBGC_HDA ( netvsc, 0, data, len );
- return -ENOTSUP;
- }
-
-
- static int netvsc_recv_data ( struct vmbus_device *vmdev, uint64_t xid,
- const void *data, size_t len,
- struct io_buffer *iobuf ) {
- struct rndis_device *rndis = vmbus_get_drvdata ( vmdev );
- struct netvsc_device *netvsc = rndis->priv;
- const struct netvsc_rndis_message *msg = data;
- int rc;
-
-
- if ( len < sizeof ( *msg ) ) {
- DBGC ( netvsc, "NETVSC %s received underlength RNDIS packet "
- "(%zd bytes)\n", netvsc->name, len );
- rc = -EINVAL;
- goto err_sanity;
- }
- if ( msg->header.type != cpu_to_le32 ( NETVSC_RNDIS_MSG ) ) {
- DBGC ( netvsc, "NETVSC %s received unexpected RNDIS packet "
- "type %d\n", netvsc->name,
- le32_to_cpu ( msg->header.type ) );
- rc = -EINVAL;
- goto err_sanity;
- }
-
-
- if ( ( rc = vmbus_send_completion ( vmdev, xid, NULL, 0 ) ) != 0 ) {
- DBGC ( netvsc, "NETVSC %s could not send completion: %s\n",
- netvsc->name, strerror ( rc ) );
- goto err_completion;
- }
-
-
- rndis_rx ( rndis, iob_disown ( iobuf ) );
-
- return 0;
-
- err_completion:
- err_sanity:
- free_iob ( iobuf );
- return rc;
- }
-
-
- static int netvsc_recv_completion ( struct vmbus_device *vmdev, uint64_t xid,
- const void *data, size_t len ) {
- struct rndis_device *rndis = vmbus_get_drvdata ( vmdev );
- struct netvsc_device *netvsc = rndis->priv;
- struct io_buffer *iobuf;
- int ( * completion ) ( struct netvsc_device *netvsc,
- const void *data, size_t len );
- unsigned int xrid = ( xid - NETVSC_BASE_XID );
- unsigned int tx_id;
- int rc;
-
-
- tx_id = ( xrid - NETVSC_TX_BASE_XRID );
- if ( ( tx_id < NETVSC_TX_NUM_DESC ) &&
- ( ( iobuf = netvsc->tx.iobufs[tx_id] ) != NULL ) ) {
-
-
- netvsc->tx.iobufs[tx_id] = NULL;
- netvsc->tx.ids[ ( netvsc->tx.id_cons++ ) &
- ( netvsc->tx.count - 1 ) ] = tx_id;
-
-
- rndis_tx_complete ( rndis, iobuf );
- return 0;
- }
-
-
- if ( xrid == NETVSC_INIT_XRID ) {
- completion = netvsc_initialised;
- } else if ( xrid == NETVSC_RX_ESTABLISH_XRID ) {
- completion = netvsc_rx_established_buffer;
- } else if ( ( netvsc->wait_xrid != 0 ) &&
- ( xrid == netvsc->wait_xrid ) ) {
- completion = netvsc_completed;
- } else {
- DBGC ( netvsc, "NETVSC %s received unexpected completion "
- "(%08llx)\n", netvsc->name, xid );
- return -EPIPE;
- }
-
-
- rc = completion ( netvsc, data, len );
-
-
- if ( xrid == netvsc->wait_xrid ) {
- netvsc->wait_xrid = 0;
- netvsc->wait_rc = rc;
- }
-
- return rc;
- }
-
-
- static int netvsc_recv_cancellation ( struct vmbus_device *vmdev,
- uint64_t xid ) {
- struct rndis_device *rndis = vmbus_get_drvdata ( vmdev );
- struct netvsc_device *netvsc = rndis->priv;
-
- DBGC ( netvsc, "NETVSC %s received unsupported cancellation packet "
- "(%08llx):\n", netvsc->name, xid );
- return -ENOTSUP;
- }
-
-
- static struct vmbus_channel_operations netvsc_channel_operations = {
- .recv_control = netvsc_recv_control,
- .recv_data = netvsc_recv_data,
- .recv_completion = netvsc_recv_completion,
- .recv_cancellation = netvsc_recv_cancellation,
- };
-
-
- static void netvsc_poll ( struct rndis_device *rndis ) {
- struct netvsc_device *netvsc = rndis->priv;
- struct vmbus_device *vmdev = netvsc->vmdev;
-
-
- while ( vmbus_has_data ( vmdev ) )
- vmbus_poll ( vmdev );
- }
-
-
- static int netvsc_transmit ( struct rndis_device *rndis,
- struct io_buffer *iobuf ) {
- struct netvsc_device *netvsc = rndis->priv;
- struct rndis_header *header = iobuf->data;
- struct netvsc_rndis_message msg;
- unsigned int tx_id;
- unsigned int xrid;
- uint64_t xid;
- int rc;
-
-
- assert ( iob_len ( iobuf ) >= sizeof ( *header ) );
- assert ( iob_len ( iobuf ) == le32_to_cpu ( header->len ) );
-
-
- if ( netvsc_ring_is_full ( &netvsc->tx ) )
- return rndis_tx_defer ( rndis, iobuf );
-
-
- tx_id = netvsc->tx.ids[ netvsc->tx.id_prod & ( netvsc->tx.count - 1 ) ];
- assert ( netvsc->tx.iobufs[tx_id] == NULL );
- xrid = ( NETVSC_TX_BASE_XRID + tx_id );
- xid = ( NETVSC_BASE_XID + xrid );
-
-
- memset ( &msg, 0, sizeof ( msg ) );
- msg.header.type = cpu_to_le32 ( NETVSC_RNDIS_MSG );
- msg.channel = ( ( header->type == cpu_to_le32 ( RNDIS_PACKET_MSG ) ) ?
- NETVSC_RNDIS_DATA : NETVSC_RNDIS_CONTROL );
- msg.buffer = cpu_to_le32 ( NETVSC_RNDIS_NO_BUFFER );
-
-
- if ( ( rc = vmbus_send_data ( netvsc->vmdev, xid, &msg, sizeof ( msg ),
- iobuf ) ) != 0 ) {
- DBGC ( netvsc, "NETVSC %s could not send RNDIS message: %s\n",
- netvsc->name, strerror ( rc ) );
- return rc;
- }
-
-
- netvsc->tx.iobufs[tx_id] = iobuf;
- netvsc->tx.id_prod++;
-
- return 0;
- }
-
-
- static void netvsc_cancel_transmit ( struct netvsc_device *netvsc,
- struct io_buffer *iobuf,
- unsigned int tx_id ) {
- unsigned int xrid;
- uint64_t xid;
-
-
- xrid = ( NETVSC_TX_BASE_XRID + tx_id );
- xid = ( NETVSC_BASE_XID + xrid );
- DBGC ( netvsc, "NETVSC %s cancelling transmission %#x\n",
- netvsc->name, tx_id );
- vmbus_send_cancellation ( netvsc->vmdev, xid );
-
-
- rndis_tx_complete_err ( netvsc->rndis, iobuf, -ECANCELED );
- }
-
-
- static int netvsc_create_ring ( struct netvsc_device *netvsc __unused,
- struct netvsc_ring *ring ) {
- unsigned int i;
-
-
- for ( i = 0 ; i < ring->count ; i++ ) {
- ring->ids[i] = i;
- assert ( ring->iobufs[i] == NULL );
- }
- ring->id_prod = 0;
- ring->id_cons = 0;
-
- return 0;
- }
-
-
- static void netvsc_destroy_ring ( struct netvsc_device *netvsc,
- struct netvsc_ring *ring,
- void ( * discard ) ( struct netvsc_device *,
- struct io_buffer *,
- unsigned int ) ) {
- struct io_buffer *iobuf;
- unsigned int i;
-
-
- for ( i = 0 ; i < ring->count ; i++ ) {
- iobuf = ring->iobufs[i];
- if ( ! iobuf )
- continue;
- ring->iobufs[i] = NULL;
- ring->ids[ ( ring->id_cons++ ) & ( ring->count - 1 ) ] = i;
- if ( discard )
- discard ( netvsc, iobuf, i );
- }
-
-
- assert ( netvsc_ring_is_empty ( ring ) );
- }
-
-
- static int netvsc_buffer_copy ( struct vmbus_xfer_pages *pages, void *data,
- size_t offset, size_t len ) {
- struct netvsc_buffer *buffer =
- container_of ( pages, struct netvsc_buffer, pages );
-
-
- if ( ( offset > buffer->len ) || ( len > ( buffer->len - offset ) ) )
- return -ERANGE;
-
-
- copy_from_user ( data, buffer->data, offset, len );
-
- return 0;
- }
-
-
- static struct vmbus_xfer_pages_operations netvsc_xfer_pages_operations = {
- .copy = netvsc_buffer_copy,
- };
-
-
- static int netvsc_create_buffer ( struct netvsc_device *netvsc,
- struct netvsc_buffer *buffer ) {
- struct vmbus_device *vmdev = netvsc->vmdev;
- int gpadl;
- int rc;
-
-
- buffer->data = umalloc ( buffer->len );
- if ( ! buffer->data ) {
- DBGC ( netvsc, "NETVSC %s could not allocate %zd-byte buffer\n",
- netvsc->name, buffer->len );
- rc = -ENOMEM;
- goto err_alloc;
- }
-
-
- gpadl = vmbus_establish_gpadl ( vmdev, buffer->data, buffer->len );
- if ( gpadl < 0 ) {
- rc = gpadl;
- DBGC ( netvsc, "NETVSC %s could not establish GPADL: %s\n",
- netvsc->name, strerror ( rc ) );
- goto err_establish_gpadl;
- }
- buffer->gpadl = gpadl;
-
-
- if ( ( rc = vmbus_register_pages ( vmdev, &buffer->pages ) ) != 0 ) {
- DBGC ( netvsc, "NETVSC %s could not register transfer pages: "
- "%s\n", netvsc->name, strerror ( rc ) );
- goto err_register_pages;
- }
-
- return 0;
-
- vmbus_unregister_pages ( vmdev, &buffer->pages );
- err_register_pages:
- vmbus_gpadl_teardown ( vmdev, gpadl );
- err_establish_gpadl:
- ufree ( buffer->data );
- err_alloc:
- return rc;
- }
-
-
- static void netvsc_destroy_buffer ( struct netvsc_device *netvsc,
- struct netvsc_buffer *buffer ) {
- struct vmbus_device *vmdev = netvsc->vmdev;
- int rc;
-
-
- vmbus_unregister_pages ( vmdev, &buffer->pages );
-
-
- if ( ( rc = vmbus_gpadl_teardown ( vmdev, buffer->gpadl ) ) != 0 ) {
- DBGC ( netvsc, "NETVSC %s could not tear down GPADL: %s\n",
- netvsc->name, strerror ( rc ) );
-
-
- return;
- }
-
-
- ufree ( buffer->data );
- }
-
-
- static int netvsc_open ( struct rndis_device *rndis ) {
- struct netvsc_device *netvsc = rndis->priv;
- int rc;
-
-
- if ( ( rc = netvsc_create_buffer ( netvsc, &netvsc->rx ) ) != 0 )
- goto err_create_rx;
-
-
- if ( ( rc = vmbus_open ( netvsc->vmdev, &netvsc_channel_operations,
- PAGE_SIZE, PAGE_SIZE, NETVSC_MTU ) ) != 0 ) {
- DBGC ( netvsc, "NETVSC %s could not open VMBus: %s\n",
- netvsc->name, strerror ( rc ) );
- goto err_vmbus_open;
- }
-
-
- if ( ( rc = netvsc_initialise ( netvsc ) ) != 0 )
- goto err_initialise;
- if ( ( rc = netvsc_ndis_version ( netvsc ) ) != 0 )
- goto err_ndis_version;
-
-
- if ( ( rc = netvsc_create_ring ( netvsc, &netvsc->tx ) ) != 0 )
- goto err_create_tx;
-
-
- if ( ( rc = netvsc_establish_buffer ( netvsc, &netvsc->rx ) ) != 0 )
- goto err_establish_rx;
-
- return 0;
-
- netvsc_revoke_buffer ( netvsc, &netvsc->rx );
- err_establish_rx:
- netvsc_destroy_ring ( netvsc, &netvsc->tx, NULL );
- err_create_tx:
- err_ndis_version:
- err_initialise:
- vmbus_close ( netvsc->vmdev );
- err_vmbus_open:
- netvsc_destroy_buffer ( netvsc, &netvsc->rx );
- err_create_rx:
- return rc;
- }
-
-
- static void netvsc_close ( struct rndis_device *rndis ) {
- struct netvsc_device *netvsc = rndis->priv;
-
-
- netvsc_revoke_buffer ( netvsc, &netvsc->rx );
-
-
- netvsc_destroy_ring ( netvsc, &netvsc->tx, netvsc_cancel_transmit );
-
-
- vmbus_close ( netvsc->vmdev );
-
-
- netvsc_destroy_buffer ( netvsc, &netvsc->rx );
- }
-
-
- static struct rndis_operations netvsc_operations = {
- .open = netvsc_open,
- .close = netvsc_close,
- .transmit = netvsc_transmit,
- .poll = netvsc_poll,
- };
-
-
- static int netvsc_probe ( struct vmbus_device *vmdev ) {
- struct netvsc_device *netvsc;
- struct rndis_device *rndis;
- int rc;
-
-
- rndis = alloc_rndis ( sizeof ( *netvsc ) );
- if ( ! rndis ) {
- rc = -ENOMEM;
- goto err_alloc;
- }
- rndis_init ( rndis, &netvsc_operations );
- rndis->netdev->dev = &vmdev->dev;
- netvsc = rndis->priv;
- netvsc->vmdev = vmdev;
- netvsc->rndis = rndis;
- netvsc->name = vmdev->dev.name;
- netvsc_init_ring ( &netvsc->tx, NETVSC_TX_NUM_DESC,
- netvsc->tx_iobufs, netvsc->tx_ids );
- netvsc_init_buffer ( &netvsc->rx, NETVSC_RX_BUF_PAGESET,
- &netvsc_xfer_pages_operations,
- NETVSC_RX_ESTABLISH_MSG, NETVSC_RX_ESTABLISH_XRID,
- NETVSC_RX_REVOKE_MSG, NETVSC_RX_REVOKE_XRID,
- NETVSC_RX_BUF_LEN );
- vmbus_set_drvdata ( vmdev, rndis );
-
-
- if ( ( rc = register_rndis ( rndis ) ) != 0 ) {
- DBGC ( netvsc, "NETVSC %s could not register: %s\n",
- netvsc->name, strerror ( rc ) );
- goto err_register;
- }
-
- return 0;
-
- unregister_rndis ( rndis );
- err_register:
- free_rndis ( rndis );
- err_alloc:
- return rc;
- }
-
-
- static void netvsc_remove ( struct vmbus_device *vmdev ) {
- struct rndis_device *rndis = vmbus_get_drvdata ( vmdev );
-
-
- unregister_rndis ( rndis );
-
-
- free_rndis ( rndis );
- }
-
-
- struct vmbus_driver netvsc_driver __vmbus_driver = {
- .name = "netvsc",
- .type = VMBUS_TYPE ( 0xf8615163, 0xdf3e, 0x46c5, 0x913f,
- 0xf2, 0xd2, 0xf9, 0x65, 0xed, 0x0e ),
- .probe = netvsc_probe,
- .remove = netvsc_remove,
- };
|