iPXE
netvsc.c
Go to the documentation of this file.
1 /*
2  * Copyright (C) 2014 Michael Brown <mbrown@fensystems.co.uk>.
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License as
6  * published by the Free Software Foundation; either version 2 of the
7  * License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12  * General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
17  * 02110-1301, USA.
18  *
19  * You can also choose to distribute this program under the terms of
20  * the Unmodified Binary Distribution Licence (as given in the file
21  * COPYING.UBDL), provided that you have satisfied its requirements.
22  */
23 
24 FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
25 
26 /** @file
27  *
28  * Hyper-V network virtual service client
29  *
30  * The network virtual service client (NetVSC) connects to the network
31  * virtual service provider (NetVSP) via the Hyper-V virtual machine
32  * bus (VMBus). It provides a transport layer for RNDIS packets.
33  */
34 
35 #include <errno.h>
36 #include <unistd.h>
37 #include <byteswap.h>
38 #include <ipxe/umalloc.h>
39 #include <ipxe/rndis.h>
40 #include <ipxe/vmbus.h>
41 #include "netvsc.h"
42 
43 /**
44  * Send control message and wait for completion
45  *
46  * @v netvsc NetVSC device
47  * @v xrid Relative transaction ID
48  * @v data Data
49  * @v len Length of data
50  * @ret rc Return status code
51  */
52 static int netvsc_control ( struct netvsc_device *netvsc, unsigned int xrid,
53  const void *data, size_t len ) {
54  uint64_t xid = ( NETVSC_BASE_XID + xrid );
55  unsigned int i;
56  int rc;
57 
58  /* Send control message */
59  if ( ( rc = vmbus_send_control ( netvsc->vmdev, xid, data, len ) ) !=0){
60  DBGC ( netvsc, "NETVSC %s could not send control message: %s\n",
61  netvsc->name, strerror ( rc ) );
62  return rc;
63  }
64 
65  /* Record transaction ID */
66  netvsc->wait_xrid = xrid;
67 
68  /* Wait for operation to complete */
69  for ( i = 0 ; i < NETVSC_MAX_WAIT_MS ; i++ ) {
70 
71  /* Check for completion */
72  if ( ! netvsc->wait_xrid )
73  return netvsc->wait_rc;
74 
75  /* Poll VMBus device */
76  vmbus_poll ( netvsc->vmdev );
77 
78  /* Delay for 1ms */
79  mdelay ( 1 );
80  }
81 
82  DBGC ( netvsc, "NETVSC %s timed out waiting for XRID %d\n",
83  netvsc->name, xrid );
84  vmbus_dump_channel ( netvsc->vmdev );
85  return -ETIMEDOUT;
86 }
87 
88 /**
89  * Handle generic completion
90  *
91  * @v netvsc NetVSC device
92  * @v data Data
93  * @v len Length of data
94  * @ret rc Return status code
95  */
96 static int netvsc_completed ( struct netvsc_device *netvsc __unused,
97  const void *data __unused, size_t len __unused ) {
98  return 0;
99 }
100 
101 /**
102  * Initialise communication
103  *
104  * @v netvsc NetVSC device
105  * @ret rc Return status code
106  */
107 static int netvsc_initialise ( struct netvsc_device *netvsc ) {
108  struct netvsc_init_message msg;
109  int rc;
110 
111  /* Construct message */
112  memset ( &msg, 0, sizeof ( msg ) );
113  msg.header.type = cpu_to_le32 ( NETVSC_INIT_MSG );
114  msg.min = cpu_to_le32 ( NETVSC_VERSION_1 );
115  msg.max = cpu_to_le32 ( NETVSC_VERSION_1 );
116 
117  /* Send message and wait for completion */
118  if ( ( rc = netvsc_control ( netvsc, NETVSC_INIT_XRID, &msg,
119  sizeof ( msg ) ) ) != 0 ) {
120  DBGC ( netvsc, "NETVSC %s could not initialise: %s\n",
121  netvsc->name, strerror ( rc ) );
122  return rc;
123  }
124 
125  return 0;
126 }
127 
128 /**
129  * Handle initialisation completion
130  *
131  * @v netvsc NetVSC device
132  * @v data Data
133  * @v len Length of data
134  * @ret rc Return status code
135  */
136 static int
137 netvsc_initialised ( struct netvsc_device *netvsc, const void *data,
138  size_t len ) {
139  const struct netvsc_init_completion *cmplt = data;
140 
141  /* Check completion */
142  if ( len < sizeof ( *cmplt ) ) {
143  DBGC ( netvsc, "NETVSC %s underlength initialisation "
144  "completion (%zd bytes)\n", netvsc->name, len );
145  return -EINVAL;
146  }
147  if ( cmplt->header.type != cpu_to_le32 ( NETVSC_INIT_CMPLT ) ) {
148  DBGC ( netvsc, "NETVSC %s unexpected initialisation completion "
149  "type %d\n", netvsc->name,
150  le32_to_cpu ( cmplt->header.type ) );
151  return -EPROTO;
152  }
153  if ( cmplt->status != cpu_to_le32 ( NETVSC_OK ) ) {
154  DBGC ( netvsc, "NETVSC %s initialisation failure status %d\n",
155  netvsc->name, le32_to_cpu ( cmplt->status ) );
156  return -EPROTO;
157  }
158 
159  return 0;
160 }
161 
162 /**
163  * Set NDIS version
164  *
165  * @v netvsc NetVSC device
166  * @ret rc Return status code
167  */
168 static int netvsc_ndis_version ( struct netvsc_device *netvsc ) {
170  int rc;
171 
172  /* Construct message */
173  memset ( &msg, 0, sizeof ( msg ) );
174  msg.header.type = cpu_to_le32 ( NETVSC_NDIS_VERSION_MSG );
175  msg.major = cpu_to_le32 ( NETVSC_NDIS_MAJOR );
176  msg.minor = cpu_to_le32 ( NETVSC_NDIS_MINOR );
177 
178  /* Send message and wait for completion */
179  if ( ( rc = netvsc_control ( netvsc, NETVSC_NDIS_VERSION_XRID,
180  &msg, sizeof ( msg ) ) ) != 0 ) {
181  DBGC ( netvsc, "NETVSC %s could not set NDIS version: %s\n",
182  netvsc->name, strerror ( rc ) );
183  return rc;
184  }
185 
186  return 0;
187 }
188 
189 /**
190  * Establish data buffer
191  *
192  * @v netvsc NetVSC device
193  * @v buffer Data buffer
194  * @ret rc Return status code
195  */
196 static int netvsc_establish_buffer ( struct netvsc_device *netvsc,
197  struct netvsc_buffer *buffer ) {
199  int rc;
200 
201  /* Construct message */
202  memset ( &msg, 0, sizeof ( msg ) );
203  msg.header.type = cpu_to_le32 ( buffer->establish_type );
204  msg.gpadl = cpu_to_le32 ( buffer->gpadl );
205  msg.pageset = buffer->pages.pageset; /* Already protocol-endian */
206 
207  /* Send message and wait for completion */
208  if ( ( rc = netvsc_control ( netvsc, buffer->establish_xrid, &msg,
209  sizeof ( msg ) ) ) != 0 ) {
210  DBGC ( netvsc, "NETVSC %s could not establish buffer: %s\n",
211  netvsc->name, strerror ( rc ) );
212  return rc;
213  }
214 
215  return 0;
216 }
217 
218 /**
219  * Handle establish receive data buffer completion
220  *
221  * @v netvsc NetVSC device
222  * @v data Data
223  * @v len Length of data
224  * @ret rc Return status code
225  */
226 static int netvsc_rx_established_buffer ( struct netvsc_device *netvsc,
227  const void *data, size_t len ) {
228  const struct netvsc_rx_establish_buffer_completion *cmplt = data;
229 
230  /* Check completion */
231  if ( len < sizeof ( *cmplt ) ) {
232  DBGC ( netvsc, "NETVSC %s underlength buffer completion (%zd "
233  "bytes)\n", netvsc->name, len );
234  return -EINVAL;
235  }
236  if ( cmplt->header.type != cpu_to_le32 ( NETVSC_RX_ESTABLISH_CMPLT ) ) {
237  DBGC ( netvsc, "NETVSC %s unexpected buffer completion type "
238  "%d\n", netvsc->name, le32_to_cpu ( cmplt->header.type));
239  return -EPROTO;
240  }
241  if ( cmplt->status != cpu_to_le32 ( NETVSC_OK ) ) {
242  DBGC ( netvsc, "NETVSC %s buffer failure status %d\n",
243  netvsc->name, le32_to_cpu ( cmplt->status ) );
244  return -EPROTO;
245  }
246 
247  return 0;
248 }
249 
250 /**
251  * Revoke data buffer
252  *
253  * @v netvsc NetVSC device
254  * @v buffer Data buffer
255  * @ret rc Return status code
256  */
257 static int netvsc_revoke_buffer ( struct netvsc_device *netvsc,
258  struct netvsc_buffer *buffer ) {
260  int rc;
261 
262  /* If the buffer's GPADL is obsolete (i.e. was created before
263  * the most recent Hyper-V reset), then we will never receive
264  * a response to the revoke message. Since the GPADL is
265  * already destroyed as far as the hypervisor is concerned, no
266  * further action is required.
267  */
268  if ( netvsc_is_obsolete ( netvsc ) )
269  return 0;
270 
271  /* Construct message */
272  memset ( &msg, 0, sizeof ( msg ) );
273  msg.header.type = cpu_to_le32 ( buffer->revoke_type );
274  msg.pageset = buffer->pages.pageset; /* Already protocol-endian */
275 
276  /* Send message and wait for completion */
277  if ( ( rc = netvsc_control ( netvsc, buffer->revoke_xrid,
278  &msg, sizeof ( msg ) ) ) != 0 ) {
279  DBGC ( netvsc, "NETVSC %s could not revoke buffer: %s\n",
280  netvsc->name, strerror ( rc ) );
281  return rc;
282  }
283 
284  return 0;
285 }
286 
287 /**
288  * Handle received control packet
289  *
290  * @v vmdev VMBus device
291  * @v xid Transaction ID
292  * @v data Data
293  * @v len Length of data
294  * @ret rc Return status code
295  */
296 static int netvsc_recv_control ( struct vmbus_device *vmdev, uint64_t xid,
297  const void *data, size_t len ) {
298  struct rndis_device *rndis = vmbus_get_drvdata ( vmdev );
299  struct netvsc_device *netvsc = rndis->priv;
300 
301  DBGC ( netvsc, "NETVSC %s received unsupported control packet "
302  "(%08llx):\n", netvsc->name, xid );
303  DBGC_HDA ( netvsc, 0, data, len );
304  return -ENOTSUP;
305 }
306 
307 /**
308  * Handle received data packet
309  *
310  * @v vmdev VMBus device
311  * @v xid Transaction ID
312  * @v data Data
313  * @v len Length of data
314  * @v list List of I/O buffers
315  * @ret rc Return status code
316  */
317 static int netvsc_recv_data ( struct vmbus_device *vmdev, uint64_t xid,
318  const void *data, size_t len,
319  struct list_head *list ) {
320  struct rndis_device *rndis = vmbus_get_drvdata ( vmdev );
321  struct netvsc_device *netvsc = rndis->priv;
322  const struct netvsc_rndis_message *msg = data;
323  struct io_buffer *iobuf;
324  struct io_buffer *tmp;
325  int rc;
326 
327  /* Sanity check */
328  if ( len < sizeof ( *msg ) ) {
329  DBGC ( netvsc, "NETVSC %s received underlength RNDIS packet "
330  "(%zd bytes)\n", netvsc->name, len );
331  rc = -EINVAL;
332  goto err_sanity;
333  }
334  if ( msg->header.type != cpu_to_le32 ( NETVSC_RNDIS_MSG ) ) {
335  DBGC ( netvsc, "NETVSC %s received unexpected RNDIS packet "
336  "type %d\n", netvsc->name,
337  le32_to_cpu ( msg->header.type ) );
338  rc = -EINVAL;
339  goto err_sanity;
340  }
341 
342  /* Send completion back to host */
343  if ( ( rc = vmbus_send_completion ( vmdev, xid, NULL, 0 ) ) != 0 ) {
344  DBGC ( netvsc, "NETVSC %s could not send completion: %s\n",
345  netvsc->name, strerror ( rc ) );
346  goto err_completion;
347  }
348 
349  /* Hand off to RNDIS */
350  list_for_each_entry_safe ( iobuf, tmp, list, list ) {
351  list_del ( &iobuf->list );
352  rndis_rx ( rndis, iob_disown ( iobuf ) );
353  }
354 
355  return 0;
356 
357  err_completion:
358  err_sanity:
359  list_for_each_entry_safe ( iobuf, tmp, list, list ) {
360  list_del ( &iobuf->list );
361  free_iob ( iobuf );
362  }
363  return rc;
364 }
365 
366 /**
367  * Handle received completion packet
368  *
369  * @v vmdev VMBus device
370  * @v xid Transaction ID
371  * @v data Data
372  * @v len Length of data
373  * @ret rc Return status code
374  */
375 static int netvsc_recv_completion ( struct vmbus_device *vmdev, uint64_t xid,
376  const void *data, size_t len ) {
377  struct rndis_device *rndis = vmbus_get_drvdata ( vmdev );
378  struct netvsc_device *netvsc = rndis->priv;
379  struct io_buffer *iobuf;
380  int ( * completion ) ( struct netvsc_device *netvsc,
381  const void *data, size_t len );
382  unsigned int xrid = ( xid - NETVSC_BASE_XID );
383  unsigned int tx_id;
384  int rc;
385 
386  /* Handle transmit completion, if applicable */
387  tx_id = ( xrid - NETVSC_TX_BASE_XRID );
388  if ( ( tx_id < NETVSC_TX_NUM_DESC ) &&
389  ( ( iobuf = netvsc->tx.iobufs[tx_id] ) != NULL ) ) {
390 
391  /* Free buffer ID */
392  netvsc->tx.iobufs[tx_id] = NULL;
393  netvsc->tx.ids[ ( netvsc->tx.id_cons++ ) &
394  ( netvsc->tx.count - 1 ) ] = tx_id;
395 
396  /* Hand back to RNDIS */
397  rndis_tx_complete ( rndis, iobuf );
398  return 0;
399  }
400 
401  /* Otherwise determine completion handler */
402  if ( xrid == NETVSC_INIT_XRID ) {
404  } else if ( xrid == NETVSC_RX_ESTABLISH_XRID ) {
406  } else if ( ( netvsc->wait_xrid != 0 ) &&
407  ( xrid == netvsc->wait_xrid ) ) {
409  } else {
410  DBGC ( netvsc, "NETVSC %s received unexpected completion "
411  "(%08llx)\n", netvsc->name, xid );
412  return -EPIPE;
413  }
414 
415  /* Hand off to completion handler */
416  rc = completion ( netvsc, data, len );
417 
418  /* Record completion handler result if applicable */
419  if ( xrid == netvsc->wait_xrid ) {
420  netvsc->wait_xrid = 0;
421  netvsc->wait_rc = rc;
422  }
423 
424  return rc;
425 }
426 
427 /**
428  * Handle received cancellation packet
429  *
430  * @v vmdev VMBus device
431  * @v xid Transaction ID
432  * @ret rc Return status code
433  */
435  uint64_t xid ) {
436  struct rndis_device *rndis = vmbus_get_drvdata ( vmdev );
437  struct netvsc_device *netvsc = rndis->priv;
438 
439  DBGC ( netvsc, "NETVSC %s received unsupported cancellation packet "
440  "(%08llx):\n", netvsc->name, xid );
441  return -ENOTSUP;
442 }
443 
444 /** VMBus channel operations */
447  .recv_data = netvsc_recv_data,
448  .recv_completion = netvsc_recv_completion,
449  .recv_cancellation = netvsc_recv_cancellation,
450 };
451 
452 /**
453  * Poll for completed and received packets
454  *
455  * @v rndis RNDIS device
456  */
457 static void netvsc_poll ( struct rndis_device *rndis ) {
458  struct netvsc_device *netvsc = rndis->priv;
459  struct vmbus_device *vmdev = netvsc->vmdev;
460 
461  /* Poll VMBus device */
462  while ( vmbus_has_data ( vmdev ) )
463  vmbus_poll ( vmdev );
464 }
465 
466 /**
467  * Transmit packet
468  *
469  * @v rndis RNDIS device
470  * @v iobuf I/O buffer
471  * @ret rc Return status code
472  *
473  * If this method returns success then the RNDIS device must
474  * eventually report completion via rndis_tx_complete().
475  */
476 static int netvsc_transmit ( struct rndis_device *rndis,
477  struct io_buffer *iobuf ) {
478  struct netvsc_device *netvsc = rndis->priv;
479  struct rndis_header *header = iobuf->data;
480  struct netvsc_rndis_message msg;
481  unsigned int tx_id;
482  unsigned int xrid;
483  uint64_t xid;
484  int rc;
485 
486  /* If the device is obsolete (i.e. was opened before the most
487  * recent Hyper-V reset), then we will never receive transmit
488  * completions. Fail transmissions immediately to minimise
489  * the delay in closing and reopening the device.
490  */
491  if ( netvsc_is_obsolete ( netvsc ) )
492  return -EPIPE;
493 
494  /* Sanity check */
495  assert ( iob_len ( iobuf ) >= sizeof ( *header ) );
496  assert ( iob_len ( iobuf ) == le32_to_cpu ( header->len ) );
497 
498  /* Check that we have space in the transmit ring */
499  if ( netvsc_ring_is_full ( &netvsc->tx ) )
500  return rndis_tx_defer ( rndis, iobuf );
501 
502  /* Allocate buffer ID and calculate transaction ID */
503  tx_id = netvsc->tx.ids[ netvsc->tx.id_prod & ( netvsc->tx.count - 1 ) ];
504  assert ( netvsc->tx.iobufs[tx_id] == NULL );
505  xrid = ( NETVSC_TX_BASE_XRID + tx_id );
506  xid = ( NETVSC_BASE_XID + xrid );
507 
508  /* Construct message */
509  memset ( &msg, 0, sizeof ( msg ) );
510  msg.header.type = cpu_to_le32 ( NETVSC_RNDIS_MSG );
511  msg.channel = ( ( header->type == cpu_to_le32 ( RNDIS_PACKET_MSG ) ) ?
514 
515  /* Send message */
516  if ( ( rc = vmbus_send_data ( netvsc->vmdev, xid, &msg, sizeof ( msg ),
517  iobuf ) ) != 0 ) {
518  DBGC ( netvsc, "NETVSC %s could not send RNDIS message: %s\n",
519  netvsc->name, strerror ( rc ) );
520  return rc;
521  }
522 
523  /* Store I/O buffer and consume buffer ID */
524  netvsc->tx.iobufs[tx_id] = iobuf;
525  netvsc->tx.id_prod++;
526 
527  return 0;
528 }
529 
530 /**
531  * Cancel transmission
532  *
533  * @v netvsc NetVSC device
534  * @v iobuf I/O buffer
535  * @v tx_id Transmission ID
536  */
537 static void netvsc_cancel_transmit ( struct netvsc_device *netvsc,
538  struct io_buffer *iobuf,
539  unsigned int tx_id ) {
540  unsigned int xrid;
541  uint64_t xid;
542 
543  /* Send cancellation */
544  xrid = ( NETVSC_TX_BASE_XRID + tx_id );
545  xid = ( NETVSC_BASE_XID + xrid );
546  DBGC ( netvsc, "NETVSC %s cancelling transmission %#x\n",
547  netvsc->name, tx_id );
548  vmbus_send_cancellation ( netvsc->vmdev, xid );
549 
550  /* Report back to RNDIS */
551  rndis_tx_complete_err ( netvsc->rndis, iobuf, -ECANCELED );
552 }
553 
554 /**
555  * Create descriptor ring
556  *
557  * @v netvsc NetVSC device
558  * @v ring Descriptor ring
559  * @ret rc Return status code
560  */
561 static int netvsc_create_ring ( struct netvsc_device *netvsc __unused,
562  struct netvsc_ring *ring ) {
563  unsigned int i;
564 
565  /* Initialise buffer ID ring */
566  for ( i = 0 ; i < ring->count ; i++ ) {
567  ring->ids[i] = i;
568  assert ( ring->iobufs[i] == NULL );
569  }
570  ring->id_prod = 0;
571  ring->id_cons = 0;
572 
573  return 0;
574 }
575 
576 /**
577  * Destroy descriptor ring
578  *
579  * @v netvsc NetVSC device
580  * @v ring Descriptor ring
581  * @v discard Method used to discard outstanding buffer, or NULL
582  */
583 static void netvsc_destroy_ring ( struct netvsc_device *netvsc,
584  struct netvsc_ring *ring,
585  void ( * discard ) ( struct netvsc_device *,
586  struct io_buffer *,
587  unsigned int ) ) {
588  struct io_buffer *iobuf;
589  unsigned int i;
590 
591  /* Flush any outstanding buffers */
592  for ( i = 0 ; i < ring->count ; i++ ) {
593  iobuf = ring->iobufs[i];
594  if ( ! iobuf )
595  continue;
596  ring->iobufs[i] = NULL;
597  ring->ids[ ( ring->id_cons++ ) & ( ring->count - 1 ) ] = i;
598  if ( discard )
599  discard ( netvsc, iobuf, i );
600  }
601 
602  /* Sanity check */
603  assert ( netvsc_ring_is_empty ( ring ) );
604 }
605 
606 /**
607  * Copy data from data buffer
608  *
609  * @v pages Transfer page set
610  * @v data Data buffer
611  * @v offset Offset within page set
612  * @v len Length within page set
613  * @ret rc Return status code
614  */
615 static int netvsc_buffer_copy ( struct vmbus_xfer_pages *pages, void *data,
616  size_t offset, size_t len ) {
617  struct netvsc_buffer *buffer =
618  container_of ( pages, struct netvsc_buffer, pages );
619 
620  /* Sanity check */
621  if ( ( offset > buffer->len ) || ( len > ( buffer->len - offset ) ) )
622  return -ERANGE;
623 
624  /* Copy data from buffer */
625  copy_from_user ( data, buffer->data, offset, len );
626 
627  return 0;
628 }
629 
630 /** Transfer page set operations */
633 };
634 
635 /**
636  * Create data buffer
637  *
638  * @v netvsc NetVSC device
639  * @v buffer Data buffer
640  * @ret rc Return status code
641  */
642 static int netvsc_create_buffer ( struct netvsc_device *netvsc,
643  struct netvsc_buffer *buffer ) {
644  struct vmbus_device *vmdev = netvsc->vmdev;
645  int gpadl;
646  int rc;
647 
648  /* Allocate receive buffer */
649  buffer->data = umalloc ( buffer->len );
650  if ( ! buffer->data ) {
651  DBGC ( netvsc, "NETVSC %s could not allocate %zd-byte buffer\n",
652  netvsc->name, buffer->len );
653  rc = -ENOMEM;
654  goto err_alloc;
655  }
656 
657  /* Establish GPA descriptor list */
658  gpadl = vmbus_establish_gpadl ( vmdev, buffer->data, buffer->len );
659  if ( gpadl < 0 ) {
660  rc = gpadl;
661  DBGC ( netvsc, "NETVSC %s could not establish GPADL: %s\n",
662  netvsc->name, strerror ( rc ) );
663  goto err_establish_gpadl;
664  }
665  buffer->gpadl = gpadl;
666 
667  /* Register transfer page set */
668  if ( ( rc = vmbus_register_pages ( vmdev, &buffer->pages ) ) != 0 ) {
669  DBGC ( netvsc, "NETVSC %s could not register transfer pages: "
670  "%s\n", netvsc->name, strerror ( rc ) );
671  goto err_register_pages;
672  }
673 
674  return 0;
675 
676  vmbus_unregister_pages ( vmdev, &buffer->pages );
677  err_register_pages:
678  vmbus_gpadl_teardown ( vmdev, gpadl );
679  err_establish_gpadl:
680  ufree ( buffer->data );
681  err_alloc:
682  return rc;
683 }
684 
685 /**
686  * Destroy data buffer
687  *
688  * @v netvsc NetVSC device
689  * @v buffer Data buffer
690  */
691 static void netvsc_destroy_buffer ( struct netvsc_device *netvsc,
692  struct netvsc_buffer *buffer ) {
693  struct vmbus_device *vmdev = netvsc->vmdev;
694  int rc;
695 
696  /* Unregister transfer pages */
697  vmbus_unregister_pages ( vmdev, &buffer->pages );
698 
699  /* Tear down GPA descriptor list */
700  if ( ( rc = vmbus_gpadl_teardown ( vmdev, buffer->gpadl ) ) != 0 ) {
701  DBGC ( netvsc, "NETVSC %s could not tear down GPADL: %s\n",
702  netvsc->name, strerror ( rc ) );
703  /* Death is imminent. The host may well continue to
704  * write to the data buffer. The best we can do is
705  * leak memory for now and hope that the host doesn't
706  * write to this region after we load an OS.
707  */
708  return;
709  }
710 
711  /* Free buffer */
712  ufree ( buffer->data );
713 }
714 
715 /**
716  * Open device
717  *
718  * @v rndis RNDIS device
719  * @ret rc Return status code
720  */
721 static int netvsc_open ( struct rndis_device *rndis ) {
722  struct netvsc_device *netvsc = rndis->priv;
723  int rc;
724 
725  /* Initialise receive buffer */
726  if ( ( rc = netvsc_create_buffer ( netvsc, &netvsc->rx ) ) != 0 )
727  goto err_create_rx;
728 
729  /* Open channel */
730  if ( ( rc = vmbus_open ( netvsc->vmdev, &netvsc_channel_operations,
731  PAGE_SIZE, PAGE_SIZE, NETVSC_MTU ) ) != 0 ) {
732  DBGC ( netvsc, "NETVSC %s could not open VMBus: %s\n",
733  netvsc->name, strerror ( rc ) );
734  goto err_vmbus_open;
735  }
736 
737  /* Initialise communication with NetVSP */
738  if ( ( rc = netvsc_initialise ( netvsc ) ) != 0 )
739  goto err_initialise;
740  if ( ( rc = netvsc_ndis_version ( netvsc ) ) != 0 )
741  goto err_ndis_version;
742 
743  /* Initialise transmit ring */
744  if ( ( rc = netvsc_create_ring ( netvsc, &netvsc->tx ) ) != 0 )
745  goto err_create_tx;
746 
747  /* Establish receive buffer */
748  if ( ( rc = netvsc_establish_buffer ( netvsc, &netvsc->rx ) ) != 0 )
749  goto err_establish_rx;
750 
751  return 0;
752 
753  netvsc_revoke_buffer ( netvsc, &netvsc->rx );
754  err_establish_rx:
755  netvsc_destroy_ring ( netvsc, &netvsc->tx, NULL );
756  err_create_tx:
757  err_ndis_version:
758  err_initialise:
759  vmbus_close ( netvsc->vmdev );
760  err_vmbus_open:
761  netvsc_destroy_buffer ( netvsc, &netvsc->rx );
762  err_create_rx:
763  return rc;
764 }
765 
766 /**
767  * Close device
768  *
769  * @v rndis RNDIS device
770  */
771 static void netvsc_close ( struct rndis_device *rndis ) {
772  struct netvsc_device *netvsc = rndis->priv;
773 
774  /* Revoke receive buffer */
775  netvsc_revoke_buffer ( netvsc, &netvsc->rx );
776 
777  /* Destroy transmit ring */
778  netvsc_destroy_ring ( netvsc, &netvsc->tx, netvsc_cancel_transmit );
779 
780  /* Close channel */
781  vmbus_close ( netvsc->vmdev );
782 
783  /* Destroy receive buffer */
784  netvsc_destroy_buffer ( netvsc, &netvsc->rx );
785 }
786 
787 /** RNDIS operations */
789  .open = netvsc_open,
790  .close = netvsc_close,
791  .transmit = netvsc_transmit,
792  .poll = netvsc_poll,
793 };
794 
795 /**
796  * Probe device
797  *
798  * @v vmdev VMBus device
799  * @ret rc Return status code
800  */
801 static int netvsc_probe ( struct vmbus_device *vmdev ) {
802  struct netvsc_device *netvsc;
803  struct rndis_device *rndis;
804  int rc;
805 
806  /* Allocate and initialise structure */
807  rndis = alloc_rndis ( sizeof ( *netvsc ) );
808  if ( ! rndis ) {
809  rc = -ENOMEM;
810  goto err_alloc;
811  }
812  rndis_init ( rndis, &netvsc_operations );
813  rndis->netdev->dev = &vmdev->dev;
814  netvsc = rndis->priv;
815  netvsc->vmdev = vmdev;
816  netvsc->rndis = rndis;
817  netvsc->name = vmdev->dev.name;
818  netvsc_init_ring ( &netvsc->tx, NETVSC_TX_NUM_DESC,
819  netvsc->tx_iobufs, netvsc->tx_ids );
820  netvsc_init_buffer ( &netvsc->rx, NETVSC_RX_BUF_PAGESET,
825  vmbus_set_drvdata ( vmdev, rndis );
826 
827  /* Register RNDIS device */
828  if ( ( rc = register_rndis ( rndis ) ) != 0 ) {
829  DBGC ( netvsc, "NETVSC %s could not register: %s\n",
830  netvsc->name, strerror ( rc ) );
831  goto err_register;
832  }
833 
834  return 0;
835 
836  unregister_rndis ( rndis );
837  err_register:
838  free_rndis ( rndis );
839  err_alloc:
840  return rc;
841 }
842 
843 /**
844  * Reset device
845  *
846  * @v vmdev VMBus device
847  * @ret rc Return status code
848  */
849 static int netvsc_reset ( struct vmbus_device *vmdev ) {
850  struct rndis_device *rndis = vmbus_get_drvdata ( vmdev );
851  struct netvsc_device *netvsc = rndis->priv;
852  struct net_device *netdev = rndis->netdev;
853  int rc;
854 
855  /* A closed device holds no NetVSC (or RNDIS) state, so there
856  * is nothing to reset.
857  */
858  if ( ! netdev_is_open ( netdev ) )
859  return 0;
860 
861  /* Close and reopen device to reset any stale state */
862  netdev_close ( netdev );
863  if ( ( rc = netdev_open ( netdev ) ) != 0 ) {
864  DBGC ( netvsc, "NETVSC %s could not reopen: %s\n",
865  netvsc->name, strerror ( rc ) );
866  return rc;
867  }
868 
869  return 0;
870 }
871 
872 /**
873  * Remove device
874  *
875  * @v vmdev VMBus device
876  */
877 static void netvsc_remove ( struct vmbus_device *vmdev ) {
878  struct rndis_device *rndis = vmbus_get_drvdata ( vmdev );
879 
880  /* Unregister RNDIS device */
881  unregister_rndis ( rndis );
882 
883  /* Free RNDIS device */
884  free_rndis ( rndis );
885 }
886 
887 /** NetVSC driver */
888 struct vmbus_driver netvsc_driver __vmbus_driver = {
889  .name = "netvsc",
890  .type = VMBUS_TYPE ( 0xf8615163, 0xdf3e, 0x46c5, 0x913f,
891  0xf2, 0xd2, 0xf9, 0x65, 0xed, 0x0e ),
892  .probe = netvsc_probe,
893  .reset = netvsc_reset,
894  .remove = netvsc_remove,
895 };
struct rndis_device * alloc_rndis(size_t priv_len)
Allocate RNDIS device.
Definition: rndis.c:1000
unsigned int count
Number of descriptors.
Definition: netvsc.h:238
#define EINVAL
Invalid argument.
Definition: errno.h:428
NDIS version.
Definition: netvsc.h:54
static int netvsc_create_ring(struct netvsc_device *netvsc __unused, struct netvsc_ring *ring)
Create descriptor ring.
Definition: netvsc.c:561
static int netvsc_transmit(struct rndis_device *rndis, struct io_buffer *iobuf)
Transmit packet.
Definition: netvsc.c:476
NetVSC initialisation message.
Definition: netvsc.h:83
const char * name
Name.
Definition: vmbus.h:523
struct arbelprm_rc_send_wqe rc
Definition: arbel.h:14
int vmbus_send_completion(struct vmbus_device *vmdev, uint64_t xid, const void *data, size_t len)
Send completion packet via ring buffer.
Definition: vmbus.c:834
#define NETVSC_RX_ESTABLISH_CMPLT
NetVSC establish receive data buffer completion.
Definition: netvsc.h:139
static struct vmbus_channel_operations netvsc_channel_operations
VMBus channel operations.
Definition: netvsc.c:445
struct device dev
Generic iPXE device.
Definition: vmbus.h:478
static int vmbus_has_data(struct vmbus_device *vmdev)
Check if data is present in ring buffer.
Definition: vmbus.h:586
int(* open)(struct rndis_device *rndis)
Open RNDIS device.
Definition: rndis.h:289
#define NETVSC_RNDIS_DATA
RNDIS data channel (for RNDIS_PACKET_MSG only)
Definition: netvsc.h:227
static void netvsc_poll(struct rndis_device *rndis)
Poll for completed and received packets.
Definition: netvsc.c:457
pseudo_bit_t completion[0x00001]
Definition: arbel.h:13
static void netvsc_close(struct rndis_device *rndis)
Close device.
Definition: netvsc.c:771
static void netvsc_cancel_transmit(struct netvsc_device *netvsc, struct io_buffer *iobuf, unsigned int tx_id)
Cancel transmission.
Definition: netvsc.c:537
#define le32_to_cpu(value)
Definition: byteswap.h:113
int(* recv_control)(struct vmbus_device *vmdev, uint64_t xid, const void *data, size_t len)
Handle received control packet.
Definition: vmbus.h:407
Error codes.
static void netvsc_destroy_ring(struct netvsc_device *netvsc, struct netvsc_ring *ring, void(*discard)(struct netvsc_device *, struct io_buffer *, unsigned int))
Destroy descriptor ring.
Definition: netvsc.c:583
struct vmbus_driver netvsc_driver __vmbus_driver
NetVSC driver.
Definition: netvsc.c:888
static void rndis_tx_complete(struct rndis_device *rndis, struct io_buffer *iobuf)
Complete message transmission.
Definition: rndis.h:364
void free_iob(struct io_buffer *iobuf)
Free I/O buffer.
Definition: iobuf.c:146
int vmbus_open(struct vmbus_device *vmdev, struct vmbus_channel_operations *op, size_t out_len, size_t in_len, size_t mtu)
Open VMBus channel.
Definition: vmbus.c:403
#define EPIPE
Broken pipe.
Definition: errno.h:619
static __always_inline void copy_from_user(void *dest, userptr_t src, off_t src_off, size_t len)
Copy data from user buffer.
Definition: uaccess.h:337
void unregister_rndis(struct rndis_device *rndis)
Unregister RNDIS device.
Definition: rndis.c:1054
NetVSC RNDIS message.
Definition: netvsc.h:213
#define DBGC(...)
Definition: compiler.h:505
#define NETVSC_VERSION_1
Oldest known NetVSC protocol version.
Definition: netvsc.h:95
char name[40]
Name.
Definition: device.h:75
static int netvsc_establish_buffer(struct netvsc_device *netvsc, struct netvsc_buffer *buffer)
Establish data buffer.
Definition: netvsc.c:196
A VMBus device.
Definition: vmbus.h:476
unsigned long long uint64_t
Definition: stdint.h:13
A NetVSC data buffer.
Definition: netvsc.h:294
#define NETVSC_RNDIS_MSG
NetVSC RNDIS message.
Definition: netvsc.h:210
Establish receive buffer.
Definition: netvsc.h:56
unsigned int id_cons
Buffer ID consumer counter.
Definition: netvsc.h:246
uint32_t buffer
Buffer index (or NETVSC_RNDIS_NO_BUFFER)
Definition: netvsc.h:16
static int netvsc_recv_cancellation(struct vmbus_device *vmdev, uint64_t xid)
Handle received cancellation packet.
Definition: netvsc.c:434
#define PAGE_SIZE
Page size.
Definition: io.h:27
const char * name
Name.
Definition: netvsc.h:347
struct netvsc_header header
Message header.
Definition: netvsc.h:103
NetVSC establish receive data buffer completion.
Definition: netvsc.h:178
static void netvsc_remove(struct vmbus_device *vmdev)
Remove device.
Definition: netvsc.c:877
#define NETVSC_RX_REVOKE_MSG
NetVSC revoke receive data buffer message.
Definition: netvsc.h:142
#define ECANCELED
Operation canceled.
Definition: errno.h:343
#define ENOTSUP
Operation not supported.
Definition: errno.h:589
#define NETVSC_RX_BUF_PAGESET
RX data buffer page set ID.
Definition: netvsc.h:33
A doubly-linked list entry (or list head)
Definition: list.h:18
static int vmbus_register_pages(struct vmbus_device *vmdev, struct vmbus_xfer_pages *pages)
Register transfer page set.
Definition: vmbus.h:599
Hyper-V network virtual service client.
#define NETVSC_NDIS_MINOR
NetVSC NDIS minor version.
Definition: netvsc.h:133
Hyper-V virtual machine bus.
unsigned long tmp
Definition: linux_pci.h:53
static int netvsc_revoke_buffer(struct netvsc_device *netvsc, struct netvsc_buffer *buffer)
Revoke data buffer.
Definition: netvsc.c:257
#define list_del(list)
Delete an entry from a list.
Definition: list.h:119
#define ENOMEM
Not enough space.
Definition: errno.h:534
#define iob_disown(iobuf)
Disown an I/O buffer.
Definition: iobuf.h:212
struct io_buffer ** iobufs
I/O buffers, indexed by buffer ID.
Definition: netvsc.h:240
static int netdev_is_open(struct net_device *netdev)
Check whether or not network device is open.
Definition: netdevice.h:658
assert((readw(&hdr->flags) &(GTF_reading|GTF_writing))==0)
unsigned int id_prod
Buffer ID producer counter.
Definition: netvsc.h:244
#define NETVSC_MAX_WAIT_MS
Maximum time to wait for a transaction to complete.
Definition: netvsc.h:19
#define container_of(ptr, type, field)
Get containing structure.
Definition: stddef.h:35
struct net_device * netdev
Network device.
Definition: rndis.h:319
NetVSC establish data buffer message.
Definition: netvsc.h:154
static int netvsc_recv_completion(struct vmbus_device *vmdev, uint64_t xid, const void *data, size_t len)
Handle received completion packet.
Definition: netvsc.c:375
#define NETVSC_RNDIS_NO_BUFFER
"No buffer used" index
Definition: netvsc.h:233
#define DBGC_HDA(...)
Definition: compiler.h:506
#define NETVSC_RX_ESTABLISH_MSG
NetVSC establish receive data buffer message.
Definition: netvsc.h:136
static int netvsc_recv_control(struct vmbus_device *vmdev, uint64_t xid, const void *data, size_t len)
Handle received control packet.
Definition: netvsc.c:296
An RNDIS device.
Definition: rndis.h:317
static userptr_t size_t offset
Offset of the first segment within the content.
Definition: deflate.h:259
static struct net_device * netdev
Definition: gdbudp.c:52
struct netvsc_ring tx
Transmit ring.
Definition: netvsc.h:350
#define NETVSC_INIT_CMPLT
NetVSC initialisation completion.
Definition: netvsc.h:98
static int netvsc_buffer_copy(struct vmbus_xfer_pages *pages, void *data, size_t offset, size_t len)
Copy data from data buffer.
Definition: netvsc.c:615
#define list_for_each_entry_safe(pos, tmp, head, member)
Iterate over entries in a list, safe against deletion of the current entry.
Definition: list.h:458
static struct rndis_operations netvsc_operations
RNDIS operations.
Definition: netvsc.c:788
#define cpu_to_le32(value)
Definition: byteswap.h:107
#define EPROTO
Protocol error.
Definition: errno.h:624
A NetVSC device.
Definition: netvsc.h:341
Revoke receive buffer.
Definition: netvsc.h:58
#define ERANGE
Result too large.
Definition: errno.h:639
char * strerror(int errno)
Retrieve string representation of error number.
Definition: strerror.c:78
int register_rndis(struct rndis_device *rndis)
Register RNDIS device.
Definition: rndis.c:1026
static void * vmbus_get_drvdata(struct vmbus_device *vmdev)
Get VMBus device driver-private data.
Definition: vmbus.h:567
static size_t iob_len(struct io_buffer *iobuf)
Calculate length of data in an I/O buffer.
Definition: iobuf.h:155
A VMBus device driver.
Definition: vmbus.h:521
User memory allocation.
A network device.
Definition: netdevice.h:352
static int netvsc_open(struct rndis_device *rndis)
Open device.
Definition: netvsc.c:721
#define NETVSC_MTU
Maximum supported NetVSC message length.
Definition: netvsc.h:13
static int netvsc_ndis_version(struct netvsc_device *netvsc)
Set NDIS version.
Definition: netvsc.c:168
static int netvsc_completed(struct netvsc_device *netvsc __unused, const void *data __unused, size_t len __unused)
Handle generic completion.
Definition: netvsc.c:96
uint32_t type
Type.
Definition: netvsc.h:76
uint32_t status
Status.
Definition: netvsc.h:109
Remote Network Driver Interface Specification.
uint32_t gpadl
GPADL ID.
Definition: netvsc.h:14
static void vmbus_unregister_pages(struct vmbus_device *vmdev, struct vmbus_xfer_pages *pages)
Unregister transfer page set.
Definition: vmbus.h:613
RNDIS device operations.
Definition: rndis.h:282
int vmbus_gpadl_teardown(struct vmbus_device *vmdev, unsigned int gpadl)
Tear down GPA descriptor list.
Definition: vmbus.c:347
#define NETVSC_NDIS_MAJOR
NetVSC NDIS major version.
Definition: netvsc.h:130
void rndis_rx(struct rndis_device *rndis, struct io_buffer *iobuf)
Receive packet from underlying transport layer.
Definition: rndis.c:829
struct vmbus_xfer_pages pages
Transfer page set.
Definition: netvsc.h:296
static int netvsc_rx_established_buffer(struct netvsc_device *netvsc, const void *data, size_t len)
Handle establish receive data buffer completion.
Definition: netvsc.c:226
static void rndis_init(struct rndis_device *rndis, struct rndis_operations *op)
Initialise an RNDIS device.
Definition: rndis.h:339
struct io_buffer * tx_iobufs[NETVSC_TX_NUM_DESC]
Transmit I/O buffers.
Definition: netvsc.h:354
#define NETVSC_NDIS_VERSION_MSG
NetVSC NDIS version message.
Definition: netvsc.h:115
#define NETVSC_TX_NUM_DESC
Number of transmit ring entries.
Definition: netvsc.h:27
int(* copy)(struct vmbus_xfer_pages *pages, void *data, size_t offset, size_t len)
Copy data from transfer page.
Definition: vmbus.h:461
int rndis_tx_defer(struct rndis_device *rndis, struct io_buffer *iobuf)
Defer transmitted packet.
Definition: rndis.c:190
struct device * dev
Underlying hardware device.
Definition: netdevice.h:364
int vmbus_send_cancellation(struct vmbus_device *vmdev, uint64_t xid)
Send cancellation packet via ring buffer.
Definition: vmbus.c:857
void netdev_close(struct net_device *netdev)
Close network device.
Definition: netdevice.c:895
int vmbus_poll(struct vmbus_device *vmdev)
Poll ring buffer.
Definition: vmbus.c:972
uint8_t tx_ids[NETVSC_TX_NUM_DESC]
Transmit buffer IDs.
Definition: netvsc.h:352
unsigned int wait_xrid
Relative transaction ID for current blocking transaction.
Definition: netvsc.h:360
#define __unused
Declare a variable or data structure as unused.
Definition: compiler.h:573
RNDIS message header.
Definition: rndis.h:23
A NetVSC descriptor ring.
Definition: netvsc.h:236
#define VMBUS_TYPE(a, b, c, d, e0, e1, e2, e3, e4, e5)
Construct VMBus type.
Definition: vmbus.h:572
void mdelay(unsigned long msecs)
Delay for a fixed number of milliseconds.
Definition: timer.c:78
VMBus transfer page set operations.
Definition: vmbus.h:451
FILE_LICENCE(GPL2_OR_LATER_OR_UBDL)
int vmbus_send_control(struct vmbus_device *vmdev, uint64_t xid, const void *data, size_t len)
Send control packet via ring buffer.
Definition: vmbus.c:765
VMBus transfer page set.
Definition: vmbus.h:466
struct vmbus_device * vmdev
VMBus device.
Definition: netvsc.h:343
struct list_head list
List of which this buffer is a member.
Definition: iobuf.h:40
static __always_inline void ufree(userptr_t userptr)
Free external memory.
Definition: umalloc.h:65
uint32_t len
Length.
Definition: ena.h:14
static __always_inline userptr_t umalloc(size_t size)
Allocate external memory.
Definition: umalloc.h:54
static int netvsc_reset(struct vmbus_device *vmdev)
Reset device.
Definition: netvsc.c:849
static void netvsc_destroy_buffer(struct netvsc_device *netvsc, struct netvsc_buffer *buffer)
Destroy data buffer.
Definition: netvsc.c:691
VMBus channel operations.
Definition: vmbus.h:397
void * data
Start of data.
Definition: iobuf.h:48
void * priv
Driver private data.
Definition: rndis.h:325
struct ena_aq_header header
Header.
Definition: ena.h:12
#define RNDIS_PACKET_MSG
RNDIS packet message.
Definition: rndis.h:219
uint8_t data[48]
Additional event data.
Definition: ena.h:22
NetVSC NDIS version message.
Definition: netvsc.h:118
static int netvsc_initialised(struct netvsc_device *netvsc, const void *data, size_t len)
Handle initialisation completion.
Definition: netvsc.c:137
Initialisation.
Definition: netvsc.h:52
static int netvsc_probe(struct vmbus_device *vmdev)
Probe device.
Definition: netvsc.c:801
#define NETVSC_BASE_XID
Base transaction ID.
Definition: netvsc.h:45
static int netvsc_control(struct netvsc_device *netvsc, unsigned int xrid, const void *data, size_t len)
Send control message and wait for completion.
Definition: netvsc.c:52
NetVSC revoke data buffer message.
Definition: netvsc.h:200
VMBus "GPADL teardown" message.
Definition: vmbus.h:194
void vmbus_close(struct vmbus_device *vmdev)
Close VMBus channel.
Definition: vmbus.c:524
Transmit descriptors (one per transmit buffer ID)
Definition: netvsc.h:50
#define NETVSC_RX_BUF_LEN
RX data buffer length.
Definition: netvsc.h:39
#define NETVSC_INIT_MSG
NetVSC initialisation message.
Definition: netvsc.h:80
static void vmbus_set_drvdata(struct vmbus_device *vmdev, void *priv)
Set VMBus device driver-private data.
Definition: vmbus.h:557
int vmbus_establish_gpadl(struct vmbus_device *vmdev, userptr_t data, size_t len)
Establish GPA descriptor list.
Definition: vmbus.c:276
static struct vmbus_xfer_pages_operations netvsc_xfer_pages_operations
Transfer page set operations.
Definition: netvsc.c:631
void vmbus_dump_channel(struct vmbus_device *vmdev)
Dump channel status (for debugging)
Definition: vmbus.c:1089
static int netvsc_initialise(struct netvsc_device *netvsc)
Initialise communication.
Definition: netvsc.c:107
struct rndis_device * rndis
RNDIS device.
Definition: netvsc.h:345
void free_rndis(struct rndis_device *rndis)
Free RNDIS device.
Definition: rndis.c:1066
void rndis_tx_complete_err(struct rndis_device *rndis, struct io_buffer *iobuf, int rc)
Complete message transmission.
Definition: rndis.c:127
#define NULL
NULL pointer (VOID *)
Definition: Base.h:321
int vmbus_send_data(struct vmbus_device *vmdev, uint64_t xid, const void *data, size_t len, struct io_buffer *iobuf)
Send data packet via ring buffer.
Definition: vmbus.c:794
struct netvsc_header header
Message header.
Definition: netvsc.h:180
#define ETIMEDOUT
Connection timed out.
Definition: errno.h:669
NetVSC initialisation completion.
Definition: netvsc.h:101
struct netvsc_buffer rx
Receive buffer.
Definition: netvsc.h:357
int netdev_open(struct net_device *netdev)
Open network device.
Definition: netdevice.c:861
int wait_rc
Return status code for current blocking transaction.
Definition: netvsc.h:362
#define NETVSC_RNDIS_CONTROL
RNDIS control channel (for all other RNDIS messages)
Definition: netvsc.h:230
static int netvsc_recv_data(struct vmbus_device *vmdev, uint64_t xid, const void *data, size_t len, struct list_head *list)
Handle received data packet.
Definition: netvsc.c:317
static int netvsc_create_buffer(struct netvsc_device *netvsc, struct netvsc_buffer *buffer)
Create data buffer.
Definition: netvsc.c:642
uint8_t * ids
Buffer ID ring.
Definition: netvsc.h:242
void * memset(void *dest, int character, size_t len) __nonnull
A persistent I/O buffer.
Definition: iobuf.h:33
static void msg(unsigned int row, const char *fmt,...)
Print message centred on specified row.
Definition: settings_ui.c:285