Pārlūkot izejas kodu

[virtio] Add virtio-net 1.0 support

This commit makes virtio-net support devices with VEN 0x1af4 and DEV
0x1041, which is how non-transitional (modern-only) virtio-net devices
are exposed on the PCI bus.

Transitional devices supporting both the old 0.9.5 and new 1.0 version
of the virtio spec are driven using the new protocol.  Legacy devices
are driven using the old protocol, same as before this commit.

Signed-off-by: Ladi Prosek <lprosek@redhat.com>
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
Signed-off-by: Michael Brown <mcb30@ipxe.org>
tags/v1.20.1
Ladi Prosek 8 gadus atpakaļ
vecāks
revīzija
988243c93f
1 mainītis faili ar 251 papildinājumiem un 11 dzēšanām
  1. 251
    11
      src/drivers/net/virtio-net.c

+ 251
- 11
src/drivers/net/virtio-net.c Parādīt failu

@@ -89,6 +89,12 @@ struct virtnet_nic {
89 89
 	/** Base pio register address */
90 90
 	unsigned long ioaddr;
91 91
 
92
+	/** 0 for legacy, 1 for virtio 1.0 */
93
+	int virtio_version;
94
+
95
+	/** Virtio 1.0 device data */
96
+	struct virtio_pci_modern_device vdev;
97
+
92 98
 	/** RX/TX virtqueues */
93 99
 	struct vring_virtqueue *virtqueue;
94 100
 
@@ -99,7 +105,7 @@ struct virtnet_nic {
99 105
 	unsigned int rx_num_iobufs;
100 106
 
101 107
 	/** Virtio net packet header, we only need one */
102
-	struct virtio_net_hdr empty_header;
108
+	struct virtio_net_hdr_modern empty_header;
103 109
 };
104 110
 
105 111
 /** Add an iobuf to a virtqueue
@@ -116,6 +122,9 @@ static void virtnet_enqueue_iob ( struct net_device *netdev,
116 122
 	struct vring_virtqueue *vq = &virtnet->virtqueue[vq_idx];
117 123
 	unsigned int out = ( vq_idx == TX_INDEX ) ? 2 : 0;
118 124
 	unsigned int in = ( vq_idx == TX_INDEX ) ? 0 : 2;
125
+	size_t header_len = virtnet->virtio_version
126
+		? sizeof ( virtnet->empty_header )
127
+		: sizeof ( virtnet->empty_header.legacy );
119 128
 	struct vring_list list[] = {
120 129
 		{
121 130
 			/* Share a single zeroed virtio net header between all
@@ -124,7 +133,7 @@ static void virtnet_enqueue_iob ( struct net_device *netdev,
124 133
 			 * header fields get used.
125 134
 			 */
126 135
 			.addr = ( char* ) &virtnet->empty_header,
127
-			.length = sizeof ( virtnet->empty_header ),
136
+			.length = header_len,
128 137
 		},
129 138
 		{
130 139
 			.addr = ( char* ) iobuf->data,
@@ -136,7 +145,8 @@ static void virtnet_enqueue_iob ( struct net_device *netdev,
136 145
 		virtnet, iobuf, vq_idx );
137 146
 
138 147
 	vring_add_buf ( vq, list, out, in, iobuf, 0 );
139
-	vring_kick ( NULL, virtnet->ioaddr, vq, 1 );
148
+	vring_kick ( virtnet->virtio_version ? &virtnet->vdev : NULL,
149
+		     virtnet->ioaddr, vq, 1 );
140 150
 }
141 151
 
142 152
 /** Try to keep rx virtqueue filled with iobufs
@@ -165,12 +175,12 @@ static void virtnet_refill_rx_virtqueue ( struct net_device *netdev ) {
165 175
 	}
166 176
 }
167 177
 
168
-/** Open network device
178
+/** Open network device, legacy virtio 0.9.5
169 179
  *
170 180
  * @v netdev	Network device
171 181
  * @ret rc	Return status code
172 182
  */
173
-static int virtnet_open ( struct net_device *netdev ) {
183
+static int virtnet_open_legacy ( struct net_device *netdev ) {
174 184
 	struct virtnet_nic *virtnet = netdev->priv;
175 185
 	unsigned long ioaddr = virtnet->ioaddr;
176 186
 	u32 features;
@@ -211,6 +221,81 @@ static int virtnet_open ( struct net_device *netdev ) {
211 221
 	return 0;
212 222
 }
213 223
 
224
+/** Open network device, modern virtio 1.0
225
+ *
226
+ * @v netdev	Network device
227
+ * @ret rc	Return status code
228
+ */
229
+static int virtnet_open_modern ( struct net_device *netdev ) {
230
+	struct virtnet_nic *virtnet = netdev->priv;
231
+	u64 features;
232
+	u8 status;
233
+
234
+	/* Negotiate features */
235
+	features = vpm_get_features ( &virtnet->vdev );
236
+	if ( ! ( features & VIRTIO_F_VERSION_1 ) ) {
237
+		vpm_add_status ( &virtnet->vdev, VIRTIO_CONFIG_S_FAILED );
238
+		return -EINVAL;
239
+	}
240
+	vpm_set_features ( &virtnet->vdev, features & (
241
+		( 1ULL << VIRTIO_NET_F_MAC ) |
242
+		( 1ULL << VIRTIO_F_VERSION_1 ) |
243
+		( 1ULL << VIRTIO_F_ANY_LAYOUT ) ) );
244
+	vpm_add_status ( &virtnet->vdev, VIRTIO_CONFIG_S_FEATURES_OK );
245
+
246
+	status = vpm_get_status ( &virtnet->vdev );
247
+	if ( ! ( status & VIRTIO_CONFIG_S_FEATURES_OK ) ) {
248
+		DBGC ( virtnet, "VIRTIO-NET %p device didn't accept features\n",
249
+		       virtnet );
250
+		vpm_add_status ( &virtnet->vdev, VIRTIO_CONFIG_S_FAILED );
251
+		return -EINVAL;
252
+	}
253
+
254
+	/* Allocate virtqueues */
255
+	virtnet->virtqueue = zalloc ( QUEUE_NB *
256
+				      sizeof ( *virtnet->virtqueue ) );
257
+	if ( ! virtnet->virtqueue ) {
258
+		vpm_add_status ( &virtnet->vdev, VIRTIO_CONFIG_S_FAILED );
259
+		return -ENOMEM;
260
+	}
261
+
262
+	/* Initialize rx/tx virtqueues */
263
+	if ( vpm_find_vqs ( &virtnet->vdev, QUEUE_NB, virtnet->virtqueue ) ) {
264
+		DBGC ( virtnet, "VIRTIO-NET %p cannot register queues\n",
265
+		       virtnet );
266
+		free ( virtnet->virtqueue );
267
+		virtnet->virtqueue = NULL;
268
+		vpm_add_status ( &virtnet->vdev, VIRTIO_CONFIG_S_FAILED );
269
+		return -ENOENT;
270
+	}
271
+
272
+	/* Disable interrupts before starting */
273
+	netdev_irq ( netdev, 0 );
274
+
275
+	vpm_add_status ( &virtnet->vdev, VIRTIO_CONFIG_S_DRIVER_OK );
276
+
277
+	/* Initialize rx packets */
278
+	INIT_LIST_HEAD ( &virtnet->rx_iobufs );
279
+	virtnet->rx_num_iobufs = 0;
280
+	virtnet_refill_rx_virtqueue ( netdev );
281
+	return 0;
282
+}
283
+
284
+/** Open network device
285
+ *
286
+ * @v netdev	Network device
287
+ * @ret rc	Return status code
288
+ */
289
+static int virtnet_open ( struct net_device *netdev ) {
290
+	struct virtnet_nic *virtnet = netdev->priv;
291
+
292
+	if ( virtnet->virtio_version ) {
293
+		return virtnet_open_modern ( netdev );
294
+	} else {
295
+		return virtnet_open_legacy ( netdev );
296
+	}
297
+}
298
+
214 299
 /** Close network device
215 300
  *
216 301
  * @v netdev	Network device
@@ -219,10 +304,19 @@ static void virtnet_close ( struct net_device *netdev ) {
219 304
 	struct virtnet_nic *virtnet = netdev->priv;
220 305
 	struct io_buffer *iobuf;
221 306
 	struct io_buffer *next_iobuf;
307
+	int i;
222 308
 
223
-	vp_reset ( virtnet->ioaddr );
309
+	if ( virtnet->virtio_version ) {
310
+		vpm_reset ( &virtnet->vdev );
311
+	} else {
312
+		vp_reset ( virtnet->ioaddr );
313
+	}
224 314
 
225 315
 	/* Virtqueues can be freed now that NIC is reset */
316
+	for ( i = 0 ; i < QUEUE_NB ; i++ ) {
317
+		virtio_pci_unmap_capability ( &virtnet->virtqueue[i].notification );
318
+	}
319
+
226 320
 	free ( virtnet->virtqueue );
227 321
 	virtnet->virtqueue = NULL;
228 322
 
@@ -303,10 +397,14 @@ static void virtnet_poll ( struct net_device *netdev ) {
303 397
 
304 398
 	/* Acknowledge interrupt.  This is necessary for UNDI operation and
305 399
 	 * interrupts that are raised despite VRING_AVAIL_F_NO_INTERRUPT being
306
-	 * set (that flag is just a hint and the hypervisor not not have to
400
+	 * set (that flag is just a hint and the hypervisor does not have to
307 401
 	 * honor it).
308 402
 	 */
309
-	vp_get_isr ( virtnet->ioaddr );
403
+	if ( virtnet->virtio_version ) {
404
+		vpm_get_isr ( &virtnet->vdev );
405
+	} else {
406
+		vp_get_isr ( virtnet->ioaddr );
407
+	}
310 408
 
311 409
 	virtnet_process_tx_packets ( netdev );
312 410
 	virtnet_process_rx_packets ( netdev );
@@ -339,13 +437,12 @@ static struct net_device_operations virtnet_operations = {
339 437
 };
340 438
 
341 439
 /**
342
- * Probe PCI device
440
+ * Probe PCI device, legacy virtio 0.9.5
343 441
  *
344 442
  * @v pci	PCI device
345
- * @v id	PCI ID
346 443
  * @ret rc	Return status code
347 444
  */
348
-static int virtnet_probe ( struct pci_device *pci ) {
445
+static int virtnet_probe_legacy ( struct pci_device *pci ) {
349 446
 	unsigned long ioaddr = pci->ioaddr;
350 447
 	struct net_device *netdev;
351 448
 	struct virtnet_nic *virtnet;
@@ -395,6 +492,143 @@ static int virtnet_probe ( struct pci_device *pci ) {
395 492
 	return rc;
396 493
 }
397 494
 
495
+/**
496
+ * Probe PCI device, modern virtio 1.0
497
+ *
498
+ * @v pci	PCI device
499
+ * @v found_dev	Set to non-zero if modern device was found (probe may still fail)
500
+ * @ret rc	Return status code
501
+ */
502
+static int virtnet_probe_modern ( struct pci_device *pci, int *found_dev ) {
503
+	struct net_device *netdev;
504
+	struct virtnet_nic *virtnet;
505
+	u64 features;
506
+	int rc, common, isr, notify, config, device;
507
+
508
+	common = virtio_pci_find_capability ( pci, VIRTIO_PCI_CAP_COMMON_CFG );
509
+	if ( ! common ) {
510
+		DBG ( "Common virtio capability not found!\n" );
511
+		return -ENODEV;
512
+	}
513
+	*found_dev = 1;
514
+
515
+	isr = virtio_pci_find_capability ( pci, VIRTIO_PCI_CAP_ISR_CFG );
516
+	notify = virtio_pci_find_capability ( pci, VIRTIO_PCI_CAP_NOTIFY_CFG );
517
+	config = virtio_pci_find_capability ( pci, VIRTIO_PCI_CAP_PCI_CFG );
518
+	if ( ! isr || ! notify || ! config ) {
519
+		DBG ( "Missing virtio capabilities %i/%i/%i/%i\n",
520
+		      common, isr, notify, config );
521
+		return -EINVAL;
522
+	}
523
+	device = virtio_pci_find_capability ( pci, VIRTIO_PCI_CAP_DEVICE_CFG );
524
+
525
+	/* Allocate and hook up net device */
526
+	netdev = alloc_etherdev ( sizeof ( *virtnet ) );
527
+	if ( ! netdev )
528
+		return -ENOMEM;
529
+	netdev_init ( netdev, &virtnet_operations );
530
+	virtnet = netdev->priv;
531
+
532
+	pci_set_drvdata ( pci, netdev );
533
+	netdev->dev = &pci->dev;
534
+
535
+	DBGC ( virtnet, "VIRTIO-NET modern %p busaddr=%s irq=%d\n",
536
+	       virtnet, pci->dev.name, pci->irq );
537
+
538
+	virtnet->vdev.pci = pci;
539
+	rc = virtio_pci_map_capability ( pci, common,
540
+		sizeof ( struct virtio_pci_common_cfg ), 4,
541
+		0, sizeof ( struct virtio_pci_common_cfg ),
542
+		&virtnet->vdev.common );
543
+	if ( rc )
544
+		goto err_map_common;
545
+
546
+	rc = virtio_pci_map_capability ( pci, isr, sizeof ( u8 ), 1,
547
+		0, 1,
548
+		&virtnet->vdev.isr );
549
+	if ( rc )
550
+		goto err_map_isr;
551
+
552
+	virtnet->vdev.notify_cap_pos = notify;
553
+	virtnet->vdev.cfg_cap_pos = config;
554
+
555
+	/* Map the device capability */
556
+	if ( device ) {
557
+		rc = virtio_pci_map_capability ( pci, device,
558
+			0, 4, 0, sizeof ( struct virtio_net_config ),
559
+			&virtnet->vdev.device );
560
+		if ( rc )
561
+			goto err_map_device;
562
+	}
563
+
564
+	/* Enable the PCI device */
565
+	adjust_pci_device ( pci );
566
+
567
+	/* Reset the device and set initial status bits */
568
+	vpm_reset ( &virtnet->vdev );
569
+	vpm_add_status ( &virtnet->vdev, VIRTIO_CONFIG_S_ACKNOWLEDGE );
570
+	vpm_add_status ( &virtnet->vdev, VIRTIO_CONFIG_S_DRIVER );
571
+
572
+	/* Load MAC address */
573
+	if ( device ) {
574
+		features = vpm_get_features ( &virtnet->vdev );
575
+		if ( features & ( 1ULL << VIRTIO_NET_F_MAC ) ) {
576
+			vpm_get ( &virtnet->vdev,
577
+				  offsetof ( struct virtio_net_config, mac ),
578
+				  netdev->hw_addr, ETH_ALEN );
579
+			DBGC ( virtnet, "VIRTIO-NET %p mac=%s\n", virtnet,
580
+			       eth_ntoa ( netdev->hw_addr ) );
581
+		}
582
+	}
583
+
584
+	/* We need a valid MAC address */
585
+	if ( ! is_valid_ether_addr ( netdev->hw_addr ) ) {
586
+		rc = -EADDRNOTAVAIL;
587
+		goto err_mac_address;
588
+	}
589
+
590
+	/* Register network device */
591
+	if ( ( rc = register_netdev ( netdev ) ) != 0 )
592
+		goto err_register_netdev;
593
+
594
+	/* Mark link as up, control virtqueue is not used */
595
+	netdev_link_up ( netdev );
596
+
597
+	virtnet->virtio_version = 1;
598
+	return 0;
599
+
600
+	unregister_netdev ( netdev );
601
+err_register_netdev:
602
+err_mac_address:
603
+	vpm_reset ( &virtnet->vdev );
604
+	netdev_nullify ( netdev );
605
+	netdev_put ( netdev );
606
+
607
+	virtio_pci_unmap_capability ( &virtnet->vdev.device );
608
+err_map_device:
609
+	virtio_pci_unmap_capability ( &virtnet->vdev.isr );
610
+err_map_isr:
611
+	virtio_pci_unmap_capability ( &virtnet->vdev.common );
612
+err_map_common:
613
+	return rc;
614
+}
615
+
616
+/**
617
+ * Probe PCI device
618
+ *
619
+ * @v pci	PCI device
620
+ * @ret rc	Return status code
621
+ */
622
+static int virtnet_probe ( struct pci_device *pci ) {
623
+	int found_modern = 0;
624
+	int rc = virtnet_probe_modern ( pci, &found_modern );
625
+	if ( ! found_modern && pci->device < 0x1040 ) {
626
+		/* fall back to the legacy probe */
627
+		rc = virtnet_probe_legacy ( pci );
628
+	}
629
+	return rc;
630
+}
631
+
398 632
 /**
399 633
  * Remove device
400 634
  *
@@ -402,6 +636,11 @@ static int virtnet_probe ( struct pci_device *pci ) {
402 636
  */
403 637
 static void virtnet_remove ( struct pci_device *pci ) {
404 638
 	struct net_device *netdev = pci_get_drvdata ( pci );
639
+	struct virtnet_nic *virtnet = netdev->priv;
640
+
641
+	virtio_pci_unmap_capability ( &virtnet->vdev.device );
642
+	virtio_pci_unmap_capability ( &virtnet->vdev.isr );
643
+	virtio_pci_unmap_capability ( &virtnet->vdev.common );
405 644
 
406 645
 	unregister_netdev ( netdev );
407 646
 	netdev_nullify ( netdev );
@@ -410,6 +649,7 @@ static void virtnet_remove ( struct pci_device *pci ) {
410 649
 
411 650
 static struct pci_device_id virtnet_nics[] = {
412 651
 PCI_ROM(0x1af4, 0x1000, "virtio-net", "Virtio Network Interface", 0),
652
+PCI_ROM(0x1af4, 0x1041, "virtio-net", "Virtio Network Interface 1.0", 0),
413 653
 };
414 654
 
415 655
 struct pci_driver virtnet_driver __pci_driver = {

Notiek ielāde…
Atcelt
Saglabāt