Browse Source

[hyperv] Add support for NetVSC paravirtual network devices

Signed-off-by: Michael Brown <mcb30@ipxe.org>
tags/v1.20.1
Michael Brown 10 years ago
parent
commit
ef16d0d2b3
4 changed files with 1207 additions and 0 deletions
  1. 3
    0
      src/arch/x86/drivers/hyperv/hyperv.c
  2. 838
    0
      src/drivers/net/netvsc.c
  3. 365
    0
      src/drivers/net/netvsc.h
  4. 1
    0
      src/include/ipxe/errfile.h

+ 3
- 0
src/arch/x86/drivers/hyperv/hyperv.c View File

@@ -559,3 +559,6 @@ struct root_device hv_root_device __root_device = {
559 559
 	.dev = { .name = "Hyper-V" },
560 560
 	.driver = &hv_root_driver,
561 561
 };
562
+
563
+/* Drag in netvsc driver */
564
+REQUIRE_OBJECT ( netvsc );

+ 838
- 0
src/drivers/net/netvsc.c View File

@@ -0,0 +1,838 @@
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
+
20
+FILE_LICENCE ( GPL2_OR_LATER );
21
+
22
+/** @file
23
+ *
24
+ * Hyper-V network virtual service client
25
+ *
26
+ * The network virtual service client (NetVSC) connects to the network
27
+ * virtual service provider (NetVSP) via the Hyper-V virtual machine
28
+ * bus (VMBus).  It provides a transport layer for RNDIS packets.
29
+ */
30
+
31
+#include <errno.h>
32
+#include <unistd.h>
33
+#include <byteswap.h>
34
+#include <ipxe/umalloc.h>
35
+#include <ipxe/rndis.h>
36
+#include <ipxe/vmbus.h>
37
+#include "netvsc.h"
38
+
39
+/**
40
+ * Send control message and wait for completion
41
+ *
42
+ * @v netvsc		NetVSC device
43
+ * @v xrid		Relative transaction ID
44
+ * @v data		Data
45
+ * @v len		Length of data
46
+ * @ret rc		Return status code
47
+ */
48
+static int netvsc_control ( struct netvsc_device *netvsc, unsigned int xrid,
49
+			    const void *data, size_t len ) {
50
+	uint64_t xid = ( NETVSC_BASE_XID + xrid );
51
+	unsigned int i;
52
+	int rc;
53
+
54
+	/* Send control message */
55
+	if ( ( rc = vmbus_send_control ( netvsc->vmdev, xid, data, len ) ) !=0){
56
+		DBGC ( netvsc, "NETVSC %s could not send control message: %s\n",
57
+		       netvsc->name, strerror ( rc ) );
58
+		return rc;
59
+	}
60
+
61
+	/* Record transaction ID */
62
+	netvsc->wait_xrid = xrid;
63
+
64
+	/* Wait for operation to complete */
65
+	for ( i = 0 ; i < NETVSC_MAX_WAIT_MS ; i++ ) {
66
+
67
+		/* Check for completion */
68
+		if ( ! netvsc->wait_xrid )
69
+			return netvsc->wait_rc;
70
+
71
+		/* Poll VMBus device */
72
+		vmbus_poll ( netvsc->vmdev );
73
+
74
+		/* Delay for 1ms */
75
+		mdelay ( 1 );
76
+	}
77
+
78
+	DBGC ( netvsc, "NETVSC %s timed out waiting for XRID %d\n",
79
+	       netvsc->name, xrid );
80
+	vmbus_dump_channel ( netvsc->vmdev );
81
+	return -ETIMEDOUT;
82
+}
83
+
84
+/**
85
+ * Handle generic completion
86
+ *
87
+ * @v netvsc		NetVSC device
88
+ * @v data		Data
89
+ * @v len		Length of data
90
+ * @ret rc		Return status code
91
+ */
92
+static int netvsc_completed ( struct netvsc_device *netvsc __unused,
93
+			      const void *data __unused, size_t len __unused ) {
94
+	return 0;
95
+}
96
+
97
+/**
98
+ * Initialise communication
99
+ *
100
+ * @v netvsc		NetVSC device
101
+ * @ret rc		Return status code
102
+ */
103
+static int netvsc_initialise ( struct netvsc_device *netvsc ) {
104
+	struct netvsc_init_message msg;
105
+	int rc;
106
+
107
+	/* Construct message */
108
+	memset ( &msg, 0, sizeof ( msg ) );
109
+	msg.header.type = cpu_to_le32 ( NETVSC_INIT_MSG );
110
+	msg.min = cpu_to_le32 ( NETVSC_VERSION_1 );
111
+	msg.max = cpu_to_le32 ( NETVSC_VERSION_1 );
112
+
113
+	/* Send message and wait for completion */
114
+	if ( ( rc = netvsc_control ( netvsc, NETVSC_INIT_XRID, &msg,
115
+				     sizeof ( msg ) ) ) != 0 ) {
116
+		DBGC ( netvsc, "NETVSC %s could not initialise: %s\n",
117
+		       netvsc->name, strerror ( rc ) );
118
+		return rc;
119
+	}
120
+
121
+	return 0;
122
+}
123
+
124
+/**
125
+ * Handle initialisation completion
126
+ *
127
+ * @v netvsc		NetVSC device
128
+ * @v data		Data
129
+ * @v len		Length of data
130
+ * @ret rc		Return status code
131
+ */
132
+static int
133
+netvsc_initialised ( struct netvsc_device *netvsc, const void *data,
134
+		     size_t len ) {
135
+	const struct netvsc_init_completion *cmplt = data;
136
+
137
+	/* Check completion */
138
+	if ( len < sizeof ( *cmplt ) ) {
139
+		DBGC ( netvsc, "NETVSC %s underlength initialisation "
140
+		       "completion (%zd bytes)\n", netvsc->name, len );
141
+		return -EINVAL;
142
+	}
143
+	if ( cmplt->header.type != cpu_to_le32 ( NETVSC_INIT_CMPLT ) ) {
144
+		DBGC ( netvsc, "NETVSC %s unexpected initialisation completion "
145
+		       "type %d\n", netvsc->name,
146
+		       le32_to_cpu ( cmplt->header.type ) );
147
+		return -EPROTO;
148
+	}
149
+	if ( cmplt->status != cpu_to_le32 ( NETVSC_OK ) ) {
150
+		DBGC ( netvsc, "NETVSC %s initialisation failure status %d\n",
151
+		       netvsc->name, le32_to_cpu ( cmplt->status ) );
152
+		return -EPROTO;
153
+	}
154
+
155
+	return 0;
156
+}
157
+
158
+/**
159
+ * Set NDIS version
160
+ *
161
+ * @v netvsc		NetVSC device
162
+ * @ret rc		Return status code
163
+ */
164
+static int netvsc_ndis_version ( struct netvsc_device *netvsc ) {
165
+	struct netvsc_ndis_version_message msg;
166
+	int rc;
167
+
168
+	/* Construct message */
169
+	memset ( &msg, 0, sizeof ( msg ) );
170
+	msg.header.type = cpu_to_le32 ( NETVSC_NDIS_VERSION_MSG );
171
+	msg.major = cpu_to_le32 ( NETVSC_NDIS_MAJOR );
172
+	msg.minor = cpu_to_le32 ( NETVSC_NDIS_MINOR );
173
+
174
+	/* Send message and wait for completion */
175
+	if ( ( rc = netvsc_control ( netvsc, NETVSC_NDIS_VERSION_XRID,
176
+				     &msg, sizeof ( msg ) ) ) != 0 ) {
177
+		DBGC ( netvsc, "NETVSC %s could not set NDIS version: %s\n",
178
+		       netvsc->name, strerror ( rc ) );
179
+		return rc;
180
+	}
181
+
182
+	return 0;
183
+}
184
+
185
+/**
186
+ * Establish data buffer
187
+ *
188
+ * @v netvsc		NetVSC device
189
+ * @v buffer		Data buffer
190
+ * @ret rc		Return status code
191
+ */
192
+static int netvsc_establish_buffer ( struct netvsc_device *netvsc,
193
+				     struct netvsc_buffer *buffer ) {
194
+	struct netvsc_establish_buffer_message msg;
195
+	int rc;
196
+
197
+	/* Construct message */
198
+	memset ( &msg, 0, sizeof ( msg ) );
199
+	msg.header.type = cpu_to_le32 ( buffer->establish_type );
200
+	msg.gpadl = cpu_to_le32 ( buffer->gpadl );
201
+	msg.pageset = buffer->pages.pageset; /* Already protocol-endian */
202
+
203
+	/* Send message and wait for completion */
204
+	if ( ( rc = netvsc_control ( netvsc, buffer->establish_xrid, &msg,
205
+				     sizeof ( msg ) ) ) != 0 ) {
206
+		DBGC ( netvsc, "NETVSC %s could not establish buffer: %s\n",
207
+		       netvsc->name, strerror ( rc ) );
208
+		return rc;
209
+	}
210
+
211
+	return 0;
212
+}
213
+
214
+/**
215
+ * Handle establish receive data buffer completion
216
+ *
217
+ * @v netvsc		NetVSC device
218
+ * @v data		Data
219
+ * @v len		Length of data
220
+ * @ret rc		Return status code
221
+ */
222
+static int netvsc_rx_established_buffer ( struct netvsc_device *netvsc,
223
+					  const void *data, size_t len ) {
224
+	const struct netvsc_rx_establish_buffer_completion *cmplt = data;
225
+
226
+	/* Check completion */
227
+	if ( len < sizeof ( *cmplt ) ) {
228
+		DBGC ( netvsc, "NETVSC %s underlength buffer completion (%zd "
229
+		       "bytes)\n", netvsc->name, len );
230
+		return -EINVAL;
231
+	}
232
+	if ( cmplt->header.type != cpu_to_le32 ( NETVSC_RX_ESTABLISH_CMPLT ) ) {
233
+		DBGC ( netvsc, "NETVSC %s unexpected buffer completion type "
234
+		       "%d\n", netvsc->name, le32_to_cpu ( cmplt->header.type));
235
+		return -EPROTO;
236
+	}
237
+	if ( cmplt->status != cpu_to_le32 ( NETVSC_OK ) ) {
238
+		DBGC ( netvsc, "NETVSC %s buffer failure status %d\n",
239
+		       netvsc->name, le32_to_cpu ( cmplt->status ) );
240
+		return -EPROTO;
241
+	}
242
+
243
+	return 0;
244
+}
245
+
246
+/**
247
+ * Revoke data buffer
248
+ *
249
+ * @v netvsc		NetVSC device
250
+ * @v buffer		Data buffer
251
+ * @ret rc		Return status code
252
+ */
253
+static int netvsc_revoke_buffer ( struct netvsc_device *netvsc,
254
+				  struct netvsc_buffer *buffer ) {
255
+	struct netvsc_revoke_buffer_message msg;
256
+	int rc;
257
+
258
+	/* Construct message */
259
+	memset ( &msg, 0, sizeof ( msg ) );
260
+	msg.header.type = cpu_to_le32 ( buffer->revoke_type );
261
+	msg.pageset = buffer->pages.pageset; /* Already protocol-endian */
262
+
263
+	/* Send message and wait for completion */
264
+	if ( ( rc = netvsc_control ( netvsc, buffer->revoke_xrid,
265
+				     &msg, sizeof ( msg ) ) ) != 0 )
266
+		return rc;
267
+
268
+	return 0;
269
+}
270
+
271
+/**
272
+ * Handle received control packet
273
+ *
274
+ * @v vmdev		VMBus device
275
+ * @v xid		Transaction ID
276
+ * @v data		Data
277
+ * @v len		Length of data
278
+ * @ret rc		Return status code
279
+ */
280
+static int netvsc_recv_control ( struct vmbus_device *vmdev, uint64_t xid,
281
+				 const void *data, size_t len ) {
282
+	struct rndis_device *rndis = vmbus_get_drvdata ( vmdev );
283
+	struct netvsc_device *netvsc = rndis->priv;
284
+
285
+	DBGC ( netvsc, "NETVSC %s received unsupported control packet "
286
+	       "(%08llx):\n", netvsc->name, xid );
287
+	DBGC_HDA ( netvsc, 0, data, len );
288
+	return -ENOTSUP;
289
+}
290
+
291
+/**
292
+ * Handle received data packet
293
+ *
294
+ * @v vmdev		VMBus device
295
+ * @v xid		Transaction ID
296
+ * @v data		Data
297
+ * @v len		Length of data
298
+ * @v iobuf		I/O buffer, or NULL if allocation failed
299
+ * @ret rc		Return status code
300
+ */
301
+static int netvsc_recv_data ( struct vmbus_device *vmdev, uint64_t xid,
302
+			      const void *data, size_t len,
303
+			      struct io_buffer *iobuf ) {
304
+	struct rndis_device *rndis = vmbus_get_drvdata ( vmdev );
305
+	struct netvsc_device *netvsc = rndis->priv;
306
+	const struct netvsc_rndis_message *msg = data;
307
+	int rc;
308
+
309
+	/* Sanity check */
310
+	if ( len < sizeof ( *msg ) ) {
311
+		DBGC ( netvsc, "NETVSC %s received underlength RNDIS packet "
312
+		       "(%zd bytes)\n", netvsc->name, len );
313
+		rc = -EINVAL;
314
+		goto err_sanity;
315
+	}
316
+	if ( msg->header.type != cpu_to_le32 ( NETVSC_RNDIS_MSG ) ) {
317
+		DBGC ( netvsc, "NETVSC %s received unexpected RNDIS packet "
318
+		       "type %d\n", netvsc->name,
319
+		       le32_to_cpu ( msg->header.type ) );
320
+		rc = -EINVAL;
321
+		goto err_sanity;
322
+	}
323
+
324
+	/* Send completion back to host (even if I/O buffer was missing) */
325
+	if ( ( rc = vmbus_send_completion ( vmdev, xid, NULL, 0 ) ) != 0 ) {
326
+		DBGC ( netvsc, "NETVSC %s could not send completion: %s\n",
327
+		       netvsc->name, strerror ( rc ) );
328
+		goto err_completion;
329
+	}
330
+
331
+	/* Hand off to RNDIS (even if I/O buffer was missing) */
332
+	rndis_rx ( rndis, iob_disown ( iobuf ) );
333
+
334
+	return 0;
335
+
336
+ err_completion:
337
+ err_sanity:
338
+	free_iob ( iobuf );
339
+	return rc;
340
+}
341
+
342
+/**
343
+ * Handle received completion packet
344
+ *
345
+ * @v vmdev		VMBus device
346
+ * @v xid		Transaction ID
347
+ * @v data		Data
348
+ * @v len		Length of data
349
+ * @ret rc		Return status code
350
+ */
351
+static int netvsc_recv_completion ( struct vmbus_device *vmdev, uint64_t xid,
352
+				    const void *data, size_t len ) {
353
+	struct rndis_device *rndis = vmbus_get_drvdata ( vmdev );
354
+	struct netvsc_device *netvsc = rndis->priv;
355
+	struct io_buffer *iobuf;
356
+	int ( * completion ) ( struct netvsc_device *netvsc,
357
+			       const void *data, size_t len );
358
+	unsigned int xrid = ( xid - NETVSC_BASE_XID );
359
+	unsigned int tx_id;
360
+	int rc;
361
+
362
+	/* Handle transmit completion, if applicable */
363
+	tx_id = ( xrid - NETVSC_TX_BASE_XRID );
364
+	if ( ( tx_id < NETVSC_TX_NUM_DESC ) &&
365
+	     ( ( iobuf = netvsc->tx.iobufs[tx_id] ) != NULL ) ) {
366
+
367
+		/* Free buffer ID */
368
+		netvsc->tx.iobufs[tx_id] = NULL;
369
+		netvsc->tx.ids[ ( netvsc->tx.id_cons++ ) &
370
+				( netvsc->tx.count - 1 ) ] = tx_id;
371
+
372
+		/* Hand back to RNDIS */
373
+		rndis_tx_complete ( rndis, iobuf );
374
+		return 0;
375
+	}
376
+
377
+	/* Otherwise determine completion handler */
378
+	if ( xrid == NETVSC_INIT_XRID ) {
379
+		completion = netvsc_initialised;
380
+	} else if ( xrid == NETVSC_RX_ESTABLISH_XRID ) {
381
+		completion = netvsc_rx_established_buffer;
382
+	} else if ( ( netvsc->wait_xrid != 0 ) &&
383
+		    ( xrid == netvsc->wait_xrid ) ) {
384
+		completion = netvsc_completed;
385
+	} else {
386
+		DBGC ( netvsc, "NETVSC %s received unexpected completion "
387
+		       "(%08llx)\n", netvsc->name, xid );
388
+		return -EPIPE;
389
+	}
390
+
391
+	/* Hand off to completion handler */
392
+	rc = completion ( netvsc, data, len );
393
+
394
+	/* Record completion handler result if applicable */
395
+	if ( xrid == netvsc->wait_xrid ) {
396
+		netvsc->wait_xrid = 0;
397
+		netvsc->wait_rc = rc;
398
+	}
399
+
400
+	return rc;
401
+}
402
+
403
+/**
404
+ * Handle received cancellation packet
405
+ *
406
+ * @v vmdev		VMBus device
407
+ * @v xid		Transaction ID
408
+ * @ret rc		Return status code
409
+ */
410
+static int netvsc_recv_cancellation ( struct vmbus_device *vmdev,
411
+				      uint64_t xid ) {
412
+	struct rndis_device *rndis = vmbus_get_drvdata ( vmdev );
413
+	struct netvsc_device *netvsc = rndis->priv;
414
+
415
+	DBGC ( netvsc, "NETVSC %s received unsupported cancellation packet "
416
+	       "(%08llx):\n", netvsc->name, xid );
417
+	return -ENOTSUP;
418
+}
419
+
420
+/** VMBus channel operations */
421
+static struct vmbus_channel_operations netvsc_channel_operations = {
422
+	.recv_control = netvsc_recv_control,
423
+	.recv_data = netvsc_recv_data,
424
+	.recv_completion = netvsc_recv_completion,
425
+	.recv_cancellation = netvsc_recv_cancellation,
426
+};
427
+
428
+/**
429
+ * Poll for completed and received packets
430
+ *
431
+ * @v rndis		RNDIS device
432
+ */
433
+static void netvsc_poll ( struct rndis_device *rndis ) {
434
+	struct netvsc_device *netvsc = rndis->priv;
435
+
436
+	/* Poll VMBus device */
437
+	vmbus_poll ( netvsc->vmdev );
438
+}
439
+
440
+/**
441
+ * Transmit packet
442
+ *
443
+ * @v rndis		RNDIS device
444
+ * @v iobuf		I/O buffer
445
+ * @ret rc		Return status code
446
+ *
447
+ * If this method returns success then the RNDIS device must
448
+ * eventually report completion via rndis_tx_complete().
449
+ */
450
+static int netvsc_transmit ( struct rndis_device *rndis,
451
+			     struct io_buffer *iobuf ) {
452
+	struct netvsc_device *netvsc = rndis->priv;
453
+	struct rndis_header *header = iobuf->data;
454
+	struct netvsc_rndis_message msg;
455
+	unsigned int tx_id;
456
+	unsigned int xrid;
457
+	uint64_t xid;
458
+	int rc;
459
+
460
+	/* Sanity check */
461
+	assert ( iob_len ( iobuf ) >= sizeof ( *header ) );
462
+	assert ( iob_len ( iobuf ) == le32_to_cpu ( header->len ) );
463
+
464
+	/* Check that we have space in the transmit ring */
465
+	if ( netvsc_ring_is_full ( &netvsc->tx ) )
466
+		return rndis_tx_defer ( rndis, iobuf );
467
+
468
+	/* Allocate buffer ID and calculate transaction ID */
469
+	tx_id = netvsc->tx.ids[ netvsc->tx.id_prod & ( netvsc->tx.count - 1 ) ];
470
+	assert ( netvsc->tx.iobufs[tx_id] == NULL );
471
+	xrid = ( NETVSC_TX_BASE_XRID + tx_id );
472
+	xid = ( NETVSC_BASE_XID + xrid );
473
+
474
+	/* Construct message */
475
+	memset ( &msg, 0, sizeof ( msg ) );
476
+	msg.header.type = cpu_to_le32 ( NETVSC_RNDIS_MSG );
477
+	msg.channel = ( ( header->type == cpu_to_le32 ( RNDIS_PACKET_MSG ) ) ?
478
+			NETVSC_RNDIS_DATA : NETVSC_RNDIS_CONTROL );
479
+	msg.buffer = cpu_to_le32 ( NETVSC_RNDIS_NO_BUFFER );
480
+
481
+	/* Send message */
482
+	if ( ( rc = vmbus_send_data ( netvsc->vmdev, xid, &msg, sizeof ( msg ),
483
+				      iobuf ) ) != 0 ) {
484
+		DBGC ( netvsc, "NETVSC %s could not send RNDIS message: %s\n",
485
+		       netvsc->name, strerror ( rc ) );
486
+		return rc;
487
+	}
488
+
489
+	/* Store I/O buffer and consume buffer ID */
490
+	netvsc->tx.iobufs[tx_id] = iobuf;
491
+	netvsc->tx.id_prod++;
492
+
493
+	return 0;
494
+}
495
+
496
+/**
497
+ * Cancel transmission
498
+ *
499
+ * @v netvsc		NetVSC device
500
+ * @v iobuf		I/O buffer
501
+ * @v tx_id		Transmission ID
502
+ */
503
+static void netvsc_cancel_transmit ( struct netvsc_device *netvsc,
504
+				     struct io_buffer *iobuf,
505
+				     unsigned int tx_id ) {
506
+	unsigned int xrid;
507
+	uint64_t xid;
508
+
509
+	/* Send cancellation */
510
+	xrid = ( NETVSC_TX_BASE_XRID + tx_id );
511
+	xid = ( NETVSC_BASE_XID + xrid );
512
+	DBGC ( netvsc, "NETVSC %s cancelling transmission %#x\n",
513
+	       netvsc->name, tx_id );
514
+	vmbus_send_cancellation ( netvsc->vmdev, xid );
515
+
516
+	/* Report back to RNDIS */
517
+	rndis_tx_complete_err ( netvsc->rndis, iobuf, -ECANCELED );
518
+}
519
+
520
+/**
521
+ * Create descriptor ring
522
+ *
523
+ * @v netvsc		NetVSC device
524
+ * @v ring		Descriptor ring
525
+ * @ret rc		Return status code
526
+ */
527
+static int netvsc_create_ring ( struct netvsc_device *netvsc __unused,
528
+				struct netvsc_ring *ring ) {
529
+	unsigned int i;
530
+
531
+	/* Initialise buffer ID ring */
532
+	for ( i = 0 ; i < ring->count ; i++ ) {
533
+		ring->ids[i] = i;
534
+		assert ( ring->iobufs[i] == NULL );
535
+	}
536
+	ring->id_prod = 0;
537
+	ring->id_cons = 0;
538
+
539
+	return 0;
540
+}
541
+
542
+/**
543
+ * Destroy descriptor ring
544
+ *
545
+ * @v netvsc		NetVSC device
546
+ * @v ring		Descriptor ring
547
+ * @v discard		Method used to discard outstanding buffer, or NULL
548
+ */
549
+static void netvsc_destroy_ring ( struct netvsc_device *netvsc,
550
+				  struct netvsc_ring *ring,
551
+				  void ( * discard ) ( struct netvsc_device *,
552
+						       struct io_buffer *,
553
+						       unsigned int ) ) {
554
+	struct io_buffer *iobuf;
555
+	unsigned int i;
556
+
557
+	/* Flush any outstanding buffers */
558
+	for ( i = 0 ; i < ring->count ; i++ ) {
559
+		iobuf = ring->iobufs[i];
560
+		if ( ! iobuf )
561
+			continue;
562
+		ring->iobufs[i] = NULL;
563
+		ring->ids[ ( ring->id_cons++ ) & ( ring->count - 1 ) ] = i;
564
+		if ( discard )
565
+			discard ( netvsc, iobuf, i );
566
+	}
567
+
568
+	/* Sanity check */
569
+	assert ( netvsc_ring_is_empty ( ring ) );
570
+}
571
+
572
+/**
573
+ * Copy data from data buffer
574
+ *
575
+ * @v pages		Transfer page set
576
+ * @v data		Data buffer
577
+ * @v offset		Offset within page set
578
+ * @v len		Length within page set
579
+ * @ret rc		Return status code
580
+ */
581
+static int netvsc_buffer_copy ( struct vmbus_xfer_pages *pages, void *data,
582
+				size_t offset, size_t len ) {
583
+	struct netvsc_buffer *buffer =
584
+		container_of ( pages, struct netvsc_buffer, pages );
585
+
586
+	/* Sanity check */
587
+	if ( ( offset > buffer->len ) || ( len > ( buffer->len - offset ) ) )
588
+		return -ERANGE;
589
+
590
+	/* Copy data from buffer */
591
+	copy_from_user ( data, buffer->data, offset, len );
592
+
593
+	return 0;
594
+}
595
+
596
+/** Transfer page set operations */
597
+static struct vmbus_xfer_pages_operations netvsc_xfer_pages_operations = {
598
+	.copy = netvsc_buffer_copy,
599
+};
600
+
601
+/**
602
+ * Create data buffer
603
+ *
604
+ * @v netvsc		NetVSC device
605
+ * @v buffer		Data buffer
606
+ * @ret rc		Return status code
607
+ */
608
+static int netvsc_create_buffer ( struct netvsc_device *netvsc,
609
+				  struct netvsc_buffer *buffer ) {
610
+	struct vmbus_device *vmdev = netvsc->vmdev;
611
+	int gpadl;
612
+	int rc;
613
+
614
+	/* Allocate receive buffer */
615
+	buffer->data = umalloc ( buffer->len );
616
+	if ( ! buffer->data ) {
617
+		DBGC ( netvsc, "NETVSC %s could not allocate %zd-byte buffer\n",
618
+		       netvsc->name, buffer->len );
619
+		rc = -ENOMEM;
620
+		goto err_alloc;
621
+	}
622
+
623
+	/* Establish GPA descriptor list */
624
+	gpadl = vmbus_establish_gpadl ( vmdev, buffer->data, buffer->len );
625
+	if ( gpadl < 0 ) {
626
+		rc = gpadl;
627
+		DBGC ( netvsc, "NETVSC %s could not establish GPADL: %s\n",
628
+		       netvsc->name, strerror ( rc ) );
629
+		goto err_establish_gpadl;
630
+	}
631
+	buffer->gpadl = gpadl;
632
+
633
+	/* Register transfer page set */
634
+	if ( ( rc = vmbus_register_pages ( vmdev, &buffer->pages ) ) != 0 ) {
635
+		DBGC ( netvsc, "NETVSC %s could not register transfer pages: "
636
+		       "%s\n", netvsc->name, strerror ( rc ) );
637
+		goto err_register_pages;
638
+	}
639
+
640
+	/* Establish data buffer */
641
+	if ( ( rc = netvsc_establish_buffer ( netvsc, buffer ) ) != 0 )
642
+		goto err_establish_buffer;
643
+
644
+	return 0;
645
+
646
+	netvsc_revoke_buffer ( netvsc, buffer );
647
+ err_establish_buffer:
648
+	vmbus_unregister_pages ( vmdev, &buffer->pages );
649
+ err_register_pages:
650
+	vmbus_gpadl_teardown ( vmdev, gpadl );
651
+ err_establish_gpadl:
652
+	ufree ( buffer->data );
653
+ err_alloc:
654
+	return rc;
655
+}
656
+
657
+/**
658
+ * Destroy data buffer
659
+ *
660
+ * @v netvsc		NetVSC device
661
+ * @v buffer		Data buffer
662
+ */
663
+static void netvsc_destroy_buffer ( struct netvsc_device *netvsc,
664
+				    struct netvsc_buffer *buffer ) {
665
+	struct vmbus_device *vmdev = netvsc->vmdev;
666
+	int rc;
667
+
668
+	/* Revoke buffer */
669
+	if ( ( rc = netvsc_revoke_buffer ( netvsc, buffer ) ) != 0 ) {
670
+		DBGC ( netvsc, "NETVSC %s could not revoke buffer: %s\n",
671
+		       netvsc->name, strerror ( rc ) );
672
+		/* Continue to attempt to tear down the GPA descriptor
673
+		 * list, which should forcibly prevent the host from
674
+		 * subsequently accessing this memory.
675
+		 */
676
+	}
677
+
678
+	/* Unregister transfer pages */
679
+	vmbus_unregister_pages ( vmdev, &buffer->pages );
680
+
681
+	/* Tear down GPA descriptor list */
682
+	if ( ( rc = vmbus_gpadl_teardown ( vmdev, buffer->gpadl ) ) != 0 ) {
683
+		DBGC ( netvsc, "NETVSC %s could not tear down GPADL: %s\n",
684
+		       netvsc->name, strerror ( rc ) );
685
+		/* Death is imminent.  The host may well continue to
686
+		 * write to the data buffer.  The best we can do is
687
+		 * leak memory for now and hope that the host doesn't
688
+		 * write to this region after we load an OS.
689
+		 */
690
+		return;
691
+	}
692
+
693
+	/* Free buffer */
694
+	ufree ( buffer->data );
695
+}
696
+
697
+/**
698
+ * Open device
699
+ *
700
+ * @v rndis		RNDIS device
701
+ * @ret rc		Return status code
702
+ */
703
+static int netvsc_open ( struct rndis_device *rndis ) {
704
+	struct netvsc_device *netvsc = rndis->priv;
705
+	int rc;
706
+
707
+	/* Open channel */
708
+	if ( ( rc = vmbus_open ( netvsc->vmdev, &netvsc_channel_operations,
709
+				 PAGE_SIZE, PAGE_SIZE, NETVSC_MTU ) ) != 0 ) {
710
+		DBGC ( netvsc, "NETVSC %s could not open VMBus: %s\n",
711
+		       netvsc->name, strerror ( rc ) );
712
+		goto err_vmbus_open;
713
+	}
714
+
715
+	/* Initialise communication with NetVSP */
716
+	if ( ( rc = netvsc_initialise ( netvsc ) ) != 0 )
717
+		goto err_initialise;
718
+	if ( ( rc = netvsc_ndis_version ( netvsc ) ) != 0 )
719
+		goto err_ndis_version;
720
+
721
+	/* Initialise transmit ring */
722
+	if ( ( rc = netvsc_create_ring ( netvsc, &netvsc->tx ) ) != 0 )
723
+		goto err_create_tx;
724
+
725
+	/* Initialise receive buffer */
726
+	if ( ( rc = netvsc_create_buffer ( netvsc, &netvsc->rx ) ) != 0 )
727
+		goto err_create_rx;
728
+
729
+	return 0;
730
+
731
+	netvsc_destroy_buffer ( netvsc, &netvsc->rx );
732
+ err_create_rx:
733
+	netvsc_destroy_ring ( netvsc, &netvsc->tx, NULL );
734
+ err_create_tx:
735
+ err_ndis_version:
736
+ err_initialise:
737
+	vmbus_close ( netvsc->vmdev );
738
+ err_vmbus_open:
739
+	return rc;
740
+}
741
+
742
+/**
743
+ * Close device
744
+ *
745
+ * @v rndis		RNDIS device
746
+ */
747
+static void netvsc_close ( struct rndis_device *rndis ) {
748
+	struct netvsc_device *netvsc = rndis->priv;
749
+
750
+	/* Destroy receive buffer */
751
+	netvsc_destroy_buffer ( netvsc, &netvsc->rx );
752
+
753
+	/* Destroy transmit ring */
754
+	netvsc_destroy_ring ( netvsc, &netvsc->tx, netvsc_cancel_transmit );
755
+
756
+	/* Close channel */
757
+	vmbus_close ( netvsc->vmdev );
758
+}
759
+
760
+/** RNDIS operations */
761
+static struct rndis_operations netvsc_operations = {
762
+	.open = netvsc_open,
763
+	.close = netvsc_close,
764
+	.transmit = netvsc_transmit,
765
+	.poll = netvsc_poll,
766
+};
767
+
768
+/**
769
+ * Probe device
770
+ *
771
+ * @v vmdev		VMBus device
772
+ * @ret rc		Return status code
773
+ */
774
+static int netvsc_probe ( struct vmbus_device *vmdev ) {
775
+	struct netvsc_device *netvsc;
776
+	struct rndis_device *rndis;
777
+	int rc;
778
+
779
+	/* Allocate and initialise structure */
780
+	rndis = alloc_rndis ( sizeof ( *netvsc ) );
781
+	if ( ! rndis ) {
782
+		rc = -ENOMEM;
783
+		goto err_alloc;
784
+	}
785
+	rndis_init ( rndis, &netvsc_operations );
786
+	rndis->netdev->dev = &vmdev->dev;
787
+	netvsc = rndis->priv;
788
+	netvsc->vmdev = vmdev;
789
+	netvsc->rndis = rndis;
790
+	netvsc->name = vmdev->dev.name;
791
+	netvsc_init_ring ( &netvsc->tx, NETVSC_TX_NUM_DESC,
792
+			   netvsc->tx_iobufs, netvsc->tx_ids );
793
+	netvsc_init_buffer ( &netvsc->rx, NETVSC_RX_BUF_PAGESET,
794
+			     &netvsc_xfer_pages_operations,
795
+			     NETVSC_RX_ESTABLISH_MSG, NETVSC_RX_ESTABLISH_XRID,
796
+			     NETVSC_RX_REVOKE_MSG, NETVSC_RX_REVOKE_XRID,
797
+			     NETVSC_RX_BUF_LEN );
798
+	vmbus_set_drvdata ( vmdev, rndis );
799
+
800
+	/* Register RNDIS device */
801
+	if ( ( rc = register_rndis ( rndis ) ) != 0 ) {
802
+		DBGC ( netvsc, "NETVSC %s could not register: %s\n",
803
+		       netvsc->name, strerror ( rc ) );
804
+		goto err_register;
805
+	}
806
+
807
+	return 0;
808
+
809
+	unregister_rndis ( rndis );
810
+ err_register:
811
+	free_rndis ( rndis );
812
+ err_alloc:
813
+	return rc;
814
+}
815
+
816
+/**
817
+ * Remove device
818
+ *
819
+ * @v vmdev		VMBus device
820
+ */
821
+static void netvsc_remove ( struct vmbus_device *vmdev ) {
822
+	struct rndis_device *rndis = vmbus_get_drvdata ( vmdev );
823
+
824
+	/* Unregister RNDIS device */
825
+	unregister_rndis ( rndis );
826
+
827
+	/* Free RNDIS device */
828
+	free_rndis ( rndis );
829
+}
830
+
831
+/** NetVSC driver */
832
+struct vmbus_driver netvsc_driver __vmbus_driver = {
833
+	.name = "netvsc",
834
+	.type = VMBUS_TYPE ( 0xf8615163, 0xdf3e, 0x46c5, 0x913f,
835
+			     0xf2, 0xd2, 0xf9, 0x65, 0xed, 0x0e ),
836
+	.probe = netvsc_probe,
837
+	.remove = netvsc_remove,
838
+};

+ 365
- 0
src/drivers/net/netvsc.h View File

@@ -0,0 +1,365 @@
1
+#ifndef _NETVSC_H
2
+#define _NETVSC_H
3
+
4
+/** @file
5
+ *
6
+ * Hyper-V network virtual service client
7
+ *
8
+ */
9
+
10
+FILE_LICENCE ( GPL2_OR_LATER );
11
+
12
+/** Maximum supported NetVSC message length */
13
+#define NETVSC_MTU 512
14
+
15
+/** Maximum time to wait for a transaction to complete
16
+ *
17
+ * This is a policy decision.
18
+ */
19
+#define NETVSC_MAX_WAIT_MS 1000
20
+
21
+/** Number of transmit ring entries
22
+ *
23
+ * Must be a power of two.  This is a policy decision.  This value
24
+ * must be sufficiently small to guarantee that we never run out of
25
+ * space in the VMBus outbound ring buffer.
26
+ */
27
+#define NETVSC_TX_NUM_DESC 8
28
+
29
+/** RX data buffer page set ID
30
+ *
31
+ * This is a policy decision.
32
+ */
33
+#define NETVSC_RX_BUF_PAGESET 0xbead
34
+
35
+/** RX data buffer length
36
+ *
37
+ * This is a policy decision.
38
+ */
39
+#define NETVSC_RX_BUF_LEN ( 16 * PAGE_SIZE )
40
+
41
+/** Base transaction ID
42
+ *
43
+ * This is a policy decision.
44
+ */
45
+#define NETVSC_BASE_XID 0x18ae0000UL
46
+
47
+/** Relative transaction IDs */
48
+enum netvsc_xrid {
49
+	/** Transmit descriptors (one per transmit buffer ID) */
50
+	NETVSC_TX_BASE_XRID = 0,
51
+	/** Initialisation */
52
+	NETVSC_INIT_XRID = ( NETVSC_TX_BASE_XRID + NETVSC_TX_NUM_DESC ),
53
+	/** NDIS version */
54
+	NETVSC_NDIS_VERSION_XRID,
55
+	/** Establish receive buffer */
56
+	NETVSC_RX_ESTABLISH_XRID,
57
+	/** Revoke receive buffer */
58
+	NETVSC_RX_REVOKE_XRID,
59
+};
60
+
61
+/** NetVSC status codes */
62
+enum netvsc_status {
63
+	NETVSC_NONE = 0,
64
+	NETVSC_OK = 1,
65
+	NETVSC_FAIL = 2,
66
+	NETVSC_TOO_NEW = 3,
67
+	NETVSC_TOO_OLD = 4,
68
+	NETVSC_BAD_PACKET = 5,
69
+	NETVSC_BUSY = 6,
70
+	NETVSC_UNSUPPORTED = 7,
71
+};
72
+
73
+/** NetVSC message header */
74
+struct netvsc_header {
75
+	/** Type */
76
+	uint32_t type;
77
+} __attribute__ (( packed ));
78
+
79
+/** NetVSC initialisation message */
80
+#define NETVSC_INIT_MSG 1
81
+
82
+/** NetVSC initialisation message */
83
+struct netvsc_init_message {
84
+	/** Message header */
85
+	struct netvsc_header header;
86
+	/** Minimum supported protocol version */
87
+	uint32_t min;
88
+	/** Maximum supported protocol version */
89
+	uint32_t max;
90
+	/** Reserved */
91
+	uint8_t reserved[20];
92
+} __attribute__ (( packed ));
93
+
94
+/** Oldest known NetVSC protocol version */
95
+#define NETVSC_VERSION_1 2 /* sic */
96
+
97
+/** NetVSC initialisation completion */
98
+#define NETVSC_INIT_CMPLT 2
99
+
100
+/** NetVSC initialisation completion */
101
+struct netvsc_init_completion {
102
+	/** Message header */
103
+	struct netvsc_header header;
104
+	/** Protocol version */
105
+	uint32_t version;
106
+	/** Maximum memory descriptor list length */
107
+	uint32_t max_mdl_len;
108
+	/** Status */
109
+	uint32_t status;
110
+	/** Reserved */
111
+	uint8_t reserved[16];
112
+} __attribute__ (( packed ));
113
+
114
+/** NetVSC NDIS version message */
115
+#define NETVSC_NDIS_VERSION_MSG 100
116
+
117
+/** NetVSC NDIS version message */
118
+struct netvsc_ndis_version_message {
119
+	/** Message header */
120
+	struct netvsc_header header;
121
+	/** Major version */
122
+	uint32_t major;
123
+	/** Minor version */
124
+	uint32_t minor;
125
+	/** Reserved */
126
+	uint8_t reserved[20];
127
+} __attribute__ (( packed ));
128
+
129
+/** NetVSC NDIS major version */
130
+#define NETVSC_NDIS_MAJOR 6
131
+
132
+/** NetVSC NDIS minor version */
133
+#define NETVSC_NDIS_MINOR 1
134
+
135
+/** NetVSC establish receive data buffer message */
136
+#define NETVSC_RX_ESTABLISH_MSG 101
137
+
138
+/** NetVSC establish receive data buffer completion */
139
+#define NETVSC_RX_ESTABLISH_CMPLT 102
140
+
141
+/** NetVSC revoke receive data buffer message */
142
+#define NETVSC_RX_REVOKE_MSG 103
143
+
144
+/** NetVSC establish transmit data buffer message */
145
+#define NETVSC_TX_ESTABLISH_MSG 104
146
+
147
+/** NetVSC establish transmit data buffer completion */
148
+#define NETVSC_TX_ESTABLISH_CMPLT 105
149
+
150
+/** NetVSC revoke transmit data buffer message */
151
+#define NETVSC_TX_REVOKE_MSG 106
152
+
153
+/** NetVSC establish data buffer message */
154
+struct netvsc_establish_buffer_message {
155
+	/** Message header */
156
+	struct netvsc_header header;
157
+	/** GPADL ID */
158
+	uint32_t gpadl;
159
+	/** Page set ID */
160
+	uint16_t pageset;
161
+	/** Reserved */
162
+	uint8_t reserved[22];
163
+} __attribute__ (( packed ));
164
+
165
+/** NetVSC receive data buffer section */
166
+struct netvsc_rx_buffer_section {
167
+	/** Starting offset */
168
+	uint32_t start;
169
+	/** Subsection length */
170
+	uint32_t len;
171
+	/** Number of subsections */
172
+	uint32_t count;
173
+	/** Ending offset */
174
+	uint32_t end;
175
+} __attribute__ (( packed ));
176
+
177
+/** NetVSC establish receive data buffer completion */
178
+struct netvsc_rx_establish_buffer_completion {
179
+	/** Message header */
180
+	struct netvsc_header header;
181
+	/** Status */
182
+	uint32_t status;
183
+	/** Number of sections (must be 1) */
184
+	uint32_t count;
185
+	/** Section descriptors */
186
+	struct netvsc_rx_buffer_section section[1];
187
+} __attribute__ (( packed ));
188
+
189
+/** NetVSC establish transmit data buffer completion */
190
+struct netvsc_tx_establish_buffer_completion {
191
+	/** Message header */
192
+	struct netvsc_header header;
193
+	/** Status */
194
+	uint32_t status;
195
+	/** Section length */
196
+	uint32_t len;
197
+} __attribute__ (( packed ));
198
+
199
+/** NetVSC revoke data buffer message */
200
+struct netvsc_revoke_buffer_message {
201
+	/** Message header */
202
+	struct netvsc_header header;
203
+	/** Page set ID */
204
+	uint16_t pageset;
205
+	/** Reserved */
206
+	uint8_t reserved[26];
207
+} __attribute__ (( packed ));
208
+
209
+/** NetVSC RNDIS message */
210
+#define NETVSC_RNDIS_MSG 107
211
+
212
+/** NetVSC RNDIS message */
213
+struct netvsc_rndis_message {
214
+	/** Message header */
215
+	struct netvsc_header header;
216
+	/** RNDIS channel */
217
+	uint32_t channel;
218
+	/** Buffer index (or NETVSC_RNDIS_NO_BUFFER) */
219
+	uint32_t buffer;
220
+	/** Buffer length */
221
+	uint32_t len;
222
+	/** Reserved */
223
+	uint8_t reserved[16];
224
+} __attribute__ (( packed ));
225
+
226
+/** RNDIS data channel (for RNDIS_PACKET_MSG only) */
227
+#define NETVSC_RNDIS_DATA 0
228
+
229
+/** RNDIS control channel (for all other RNDIS messages) */
230
+#define NETVSC_RNDIS_CONTROL 1
231
+
232
+/** "No buffer used" index */
233
+#define NETVSC_RNDIS_NO_BUFFER 0xffffffffUL
234
+
235
+/** A NetVSC descriptor ring */
236
+struct netvsc_ring {
237
+	/** Number of descriptors */
238
+	unsigned int count;
239
+	/** I/O buffers, indexed by buffer ID */
240
+	struct io_buffer **iobufs;
241
+	/** Buffer ID ring */
242
+	uint8_t *ids;
243
+	/** Buffer ID producer counter */
244
+	unsigned int id_prod;
245
+	/** Buffer ID consumer counter */
246
+	unsigned int id_cons;
247
+};
248
+
249
+/**
250
+ * Initialise descriptor ring
251
+ *
252
+ * @v ring		Descriptor ring
253
+ * @v count		Maximum number of used descriptors
254
+ * @v iobufs		I/O buffers
255
+ * @v ids		Buffer IDs
256
+ */
257
+static inline __attribute__ (( always_inline )) void
258
+netvsc_init_ring ( struct netvsc_ring *ring, unsigned int count,
259
+		   struct io_buffer **iobufs, uint8_t *ids ) {
260
+
261
+	ring->count = count;
262
+	ring->iobufs = iobufs;
263
+	ring->ids = ids;
264
+}
265
+
266
+/**
267
+ * Check whether or not descriptor ring is full
268
+ *
269
+ * @v ring		Descriptor ring
270
+ * @v is_full		Ring is full
271
+ */
272
+static inline __attribute__ (( always_inline )) int
273
+netvsc_ring_is_full ( struct netvsc_ring *ring ) {
274
+	unsigned int fill_level;
275
+
276
+	fill_level = ( ring->id_prod - ring->id_cons );
277
+	assert ( fill_level <= ring->count );
278
+	return ( fill_level >= ring->count );
279
+}
280
+
281
+/**
282
+ * Check whether or not descriptor ring is empty
283
+ *
284
+ * @v ring		Descriptor ring
285
+ * @v is_empty		Ring is empty
286
+ */
287
+static inline __attribute__ (( always_inline )) int
288
+netvsc_ring_is_empty ( struct netvsc_ring *ring ) {
289
+
290
+	return ( ring->id_prod == ring->id_cons );
291
+}
292
+
293
+/** A NetVSC data buffer */
294
+struct netvsc_buffer {
295
+	/** Transfer page set */
296
+	struct vmbus_xfer_pages pages;
297
+	/** Establish data buffer message type */
298
+	uint8_t establish_type;
299
+	/** Establish data buffer relative transaction ID */
300
+	uint8_t establish_xrid;
301
+	/** Revoke data buffer message type */
302
+	uint8_t revoke_type;
303
+	/** Revoke data buffer relative transaction ID */
304
+	uint8_t revoke_xrid;
305
+	/** Buffer length */
306
+	size_t len;
307
+	/** Buffer */
308
+	userptr_t data;
309
+	/** GPADL ID */
310
+	unsigned int gpadl;
311
+};
312
+
313
+/**
314
+ * Initialise data buffer
315
+ *
316
+ * @v buffer		Data buffer
317
+ * @v pageset		Page set ID
318
+ * @v op		Page set operations
319
+ * @v establish_type	Establish data buffer message type
320
+ * @v establish_xrid	Establish data buffer relative transaction ID
321
+ * @v revoke_type	Revoke data buffer message type
322
+ * @v revoke_type	Revoke data buffer relative transaction ID
323
+ * @v len		Required length
324
+ */
325
+static inline __attribute__ (( always_inline )) void
326
+netvsc_init_buffer ( struct netvsc_buffer *buffer, uint16_t pageset,
327
+		     struct vmbus_xfer_pages_operations *op,
328
+		     uint8_t establish_type, uint8_t establish_xrid,
329
+		     uint8_t revoke_type, uint8_t revoke_xrid, size_t len ) {
330
+
331
+	buffer->pages.pageset = cpu_to_le16 ( pageset );
332
+	buffer->pages.op = op;
333
+	buffer->establish_type = establish_type;
334
+	buffer->establish_xrid = establish_xrid;
335
+	buffer->revoke_type = revoke_type;
336
+	buffer->revoke_xrid = revoke_xrid;
337
+	buffer->len = len;
338
+}
339
+
340
+/** A NetVSC device */
341
+struct netvsc_device {
342
+	/** VMBus device */
343
+	struct vmbus_device *vmdev;
344
+	/** RNDIS device */
345
+	struct rndis_device *rndis;
346
+	/** Name */
347
+	const char *name;
348
+
349
+	/** Transmit ring */
350
+	struct netvsc_ring tx;
351
+	/** Transmit buffer IDs */
352
+	uint8_t tx_ids[NETVSC_TX_NUM_DESC];
353
+	/** Transmit I/O buffers */
354
+	struct io_buffer *tx_iobufs[NETVSC_TX_NUM_DESC];
355
+
356
+	/** Receive buffer */
357
+	struct netvsc_buffer rx;
358
+
359
+	/** Relative transaction ID for current blocking transaction */
360
+	unsigned int wait_xrid;
361
+	/** Return status code for current blocking transaction */
362
+	int wait_rc;
363
+};
364
+
365
+#endif /* _NETVSC_H */

+ 1
- 0
src/include/ipxe/errfile.h View File

@@ -157,6 +157,7 @@ FILE_LICENCE ( GPL2_OR_LATER );
157 157
 #define ERRFILE_snp		     ( ERRFILE_DRIVER | 0x00680000 )
158 158
 #define ERRFILE_netfront	     ( ERRFILE_DRIVER | 0x00690000 )
159 159
 #define ERRFILE_nii		     ( ERRFILE_DRIVER | 0x006a0000 )
160
+#define ERRFILE_netvsc		     ( ERRFILE_DRIVER | 0x006b0000 )
160 161
 
161 162
 #define ERRFILE_scsi		     ( ERRFILE_DRIVER | 0x00700000 )
162 163
 #define ERRFILE_arbel		     ( ERRFILE_DRIVER | 0x00710000 )

Loading…
Cancel
Save