Browse Source

[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 years ago
parent
commit
988243c93f
1 changed files with 251 additions and 11 deletions
  1. 251
    11
      src/drivers/net/virtio-net.c

+ 251
- 11
src/drivers/net/virtio-net.c View File

89
 	/** Base pio register address */
89
 	/** Base pio register address */
90
 	unsigned long ioaddr;
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
 	/** RX/TX virtqueues */
98
 	/** RX/TX virtqueues */
93
 	struct vring_virtqueue *virtqueue;
99
 	struct vring_virtqueue *virtqueue;
94
 
100
 
99
 	unsigned int rx_num_iobufs;
105
 	unsigned int rx_num_iobufs;
100
 
106
 
101
 	/** Virtio net packet header, we only need one */
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
 /** Add an iobuf to a virtqueue
111
 /** Add an iobuf to a virtqueue
116
 	struct vring_virtqueue *vq = &virtnet->virtqueue[vq_idx];
122
 	struct vring_virtqueue *vq = &virtnet->virtqueue[vq_idx];
117
 	unsigned int out = ( vq_idx == TX_INDEX ) ? 2 : 0;
123
 	unsigned int out = ( vq_idx == TX_INDEX ) ? 2 : 0;
118
 	unsigned int in = ( vq_idx == TX_INDEX ) ? 0 : 2;
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
 	struct vring_list list[] = {
128
 	struct vring_list list[] = {
120
 		{
129
 		{
121
 			/* Share a single zeroed virtio net header between all
130
 			/* Share a single zeroed virtio net header between all
124
 			 * header fields get used.
133
 			 * header fields get used.
125
 			 */
134
 			 */
126
 			.addr = ( char* ) &virtnet->empty_header,
135
 			.addr = ( char* ) &virtnet->empty_header,
127
-			.length = sizeof ( virtnet->empty_header ),
136
+			.length = header_len,
128
 		},
137
 		},
129
 		{
138
 		{
130
 			.addr = ( char* ) iobuf->data,
139
 			.addr = ( char* ) iobuf->data,
136
 		virtnet, iobuf, vq_idx );
145
 		virtnet, iobuf, vq_idx );
137
 
146
 
138
 	vring_add_buf ( vq, list, out, in, iobuf, 0 );
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
 /** Try to keep rx virtqueue filled with iobufs
152
 /** Try to keep rx virtqueue filled with iobufs
165
 	}
175
 	}
166
 }
176
 }
167
 
177
 
168
-/** Open network device
178
+/** Open network device, legacy virtio 0.9.5
169
  *
179
  *
170
  * @v netdev	Network device
180
  * @v netdev	Network device
171
  * @ret rc	Return status code
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
 	struct virtnet_nic *virtnet = netdev->priv;
184
 	struct virtnet_nic *virtnet = netdev->priv;
175
 	unsigned long ioaddr = virtnet->ioaddr;
185
 	unsigned long ioaddr = virtnet->ioaddr;
176
 	u32 features;
186
 	u32 features;
211
 	return 0;
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
 /** Close network device
299
 /** Close network device
215
  *
300
  *
216
  * @v netdev	Network device
301
  * @v netdev	Network device
219
 	struct virtnet_nic *virtnet = netdev->priv;
304
 	struct virtnet_nic *virtnet = netdev->priv;
220
 	struct io_buffer *iobuf;
305
 	struct io_buffer *iobuf;
221
 	struct io_buffer *next_iobuf;
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
 	/* Virtqueues can be freed now that NIC is reset */
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
 	free ( virtnet->virtqueue );
320
 	free ( virtnet->virtqueue );
227
 	virtnet->virtqueue = NULL;
321
 	virtnet->virtqueue = NULL;
228
 
322
 
303
 
397
 
304
 	/* Acknowledge interrupt.  This is necessary for UNDI operation and
398
 	/* Acknowledge interrupt.  This is necessary for UNDI operation and
305
 	 * interrupts that are raised despite VRING_AVAIL_F_NO_INTERRUPT being
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
 	 * honor it).
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
 	virtnet_process_tx_packets ( netdev );
409
 	virtnet_process_tx_packets ( netdev );
312
 	virtnet_process_rx_packets ( netdev );
410
 	virtnet_process_rx_packets ( netdev );
339
 };
437
 };
340
 
438
 
341
 /**
439
 /**
342
- * Probe PCI device
440
+ * Probe PCI device, legacy virtio 0.9.5
343
  *
441
  *
344
  * @v pci	PCI device
442
  * @v pci	PCI device
345
- * @v id	PCI ID
346
  * @ret rc	Return status code
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
 	unsigned long ioaddr = pci->ioaddr;
446
 	unsigned long ioaddr = pci->ioaddr;
350
 	struct net_device *netdev;
447
 	struct net_device *netdev;
351
 	struct virtnet_nic *virtnet;
448
 	struct virtnet_nic *virtnet;
395
 	return rc;
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
  * Remove device
633
  * Remove device
400
  *
634
  *
402
  */
636
  */
403
 static void virtnet_remove ( struct pci_device *pci ) {
637
 static void virtnet_remove ( struct pci_device *pci ) {
404
 	struct net_device *netdev = pci_get_drvdata ( pci );
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
 	unregister_netdev ( netdev );
645
 	unregister_netdev ( netdev );
407
 	netdev_nullify ( netdev );
646
 	netdev_nullify ( netdev );
410
 
649
 
411
 static struct pci_device_id virtnet_nics[] = {
650
 static struct pci_device_id virtnet_nics[] = {
412
 PCI_ROM(0x1af4, 0x1000, "virtio-net", "Virtio Network Interface", 0),
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
 struct pci_driver virtnet_driver __pci_driver = {
655
 struct pci_driver virtnet_driver __pci_driver = {

Loading…
Cancel
Save