浏览代码

[myri10ge] Add NonVolatile Option (nvo) support

Add NonVolatile Option (nvo) and NonVolatile Storage (nvs) support to
the myri10ge driver using the EEPROM read/write mechanism provided by
the NIC's Vendor Specific PCI capability.

The myri10ge NIC is capabile of storing 64KB or more of nonvolatile
options, but this patch advertises only 512 bytes of nvo storage
because iPXE malloc's a buffer matching the total size we advertise.
512 is plenty without wasting malloc'd memory.  (The 2 other drivers
currently supporting nvo advertise 256 bytes or less.)

Signed-off-by: Stefan Hajnoczi <stefanha@gmail.com>
Signed-off-by: Michael Brown <mcb30@ipxe.org>
tags/v1.20.1
Glenn Brown 15 年前
父节点
当前提交
66df967cb4
共有 1 个文件被更改,包括 314 次插入11 次删除
  1. 314
    11
      src/drivers/net/myri10ge.c

+ 314
- 11
src/drivers/net/myri10ge.c 查看文件

55
  * myri10ge_net_poll() polls for these receive notifications, posts
55
  * myri10ge_net_poll() polls for these receive notifications, posts
56
  * replacement receive buffers to the NIC, and passes received frames
56
  * replacement receive buffers to the NIC, and passes received frames
57
  * to netdev_rx().
57
  * to netdev_rx().
58
+ *
59
+ * NonVolatile Storage
60
+ *
61
+ * This driver supports NonVolatile Storage (nvs) in the NIC EEPROM.
62
+ * If the last EEPROM block is not otherwise filled, we tell
63
+ * iPXE it may store NonVolatile Options (nvo) there.
58
  */
64
  */
59
 
65
 
60
 /*
66
 /*
75
 #include <ipxe/iobuf.h>
81
 #include <ipxe/iobuf.h>
76
 #include <ipxe/malloc.h>
82
 #include <ipxe/malloc.h>
77
 #include <ipxe/netdevice.h>
83
 #include <ipxe/netdevice.h>
84
+#include <ipxe/nvo.h>
85
+#include <ipxe/nvs.h>
78
 #include <ipxe/pci.h>
86
 #include <ipxe/pci.h>
79
 #include <ipxe/timer.h>
87
 #include <ipxe/timer.h>
80
 
88
 
169
 	   BEWARE: the value must be written 32 bits at a time. */
177
 	   BEWARE: the value must be written 32 bits at a time. */
170
 
178
 
171
 	mcp_cmd_t	*command;
179
 	mcp_cmd_t	*command;
180
+
181
+	/*
182
+	 * Nonvolatile Storage for configuration options.
183
+	 */
184
+
185
+	struct nvs_device	nvs;
186
+	struct nvo_fragment	nvo_fragment[2];
187
+	struct nvo_block	nvo;
188
+
189
+	/* Cached PCI capability locations. */
190
+
191
+	uint8			pci_cap_vs;
172
 };
192
 };
173
 
193
 
174
 /****************************************************************
194
 /****************************************************************
198
 	return ( struct myri10ge_private * ) ( nd + 1 );
218
 	return ( struct myri10ge_private * ) ( nd + 1 );
199
 }
219
 }
200
 
220
 
221
+/*
222
+ * Convert a Myri10ge driver private data pointer to a netdev pointer.
223
+ *
224
+ * @v p		Myri10ge device private data.
225
+ * @ret r	The corresponding network device.
226
+ */
227
+static inline struct net_device *myri10ge_netdev ( struct myri10ge_private *p )
228
+{
229
+	return ( ( struct net_device * ) p ) - 1;
230
+}
231
+
232
+/*
233
+ * Convert a network device pointer to a PCI device pointer.
234
+ *
235
+ * @v netdev	A Network Device.
236
+ * @ret r	The corresponding PCI device.
237
+ */
238
+static inline struct pci_device *myri10ge_pcidev ( struct net_device *netdev )
239
+{
240
+	return container_of (netdev->dev, struct pci_device, dev);
241
+}
242
+
201
 /*
243
 /*
202
  * Pass a receive buffer to the NIC to be filled.
244
  * Pass a receive buffer to the NIC to be filled.
203
  *
245
  *
381
 /* Constants for reading the STRING_SPECS via the Myricom
423
 /* Constants for reading the STRING_SPECS via the Myricom
382
    Vendor Specific PCI configuration space capability. */
424
    Vendor Specific PCI configuration space capability. */
383
 
425
 
426
+#define VS_EEPROM_READ_ADDR ( vs + 0x04 )
427
+#define VS_EEPROM_READ_DATA ( vs + 0x08 )
428
+#define VS_EEPROM_WRITE     ( vs + 0x0C )
384
 #define VS_ADDR ( vs + 0x18 )
429
 #define VS_ADDR ( vs + 0x18 )
385
 #define VS_DATA ( vs + 0x14 )
430
 #define VS_DATA ( vs + 0x14 )
386
 #define VS_MODE ( vs + 0x10 )
431
 #define VS_MODE ( vs + 0x10 )
387
 #define 	VS_MODE_READ32 0x3
432
 #define 	VS_MODE_READ32 0x3
388
 #define 	VS_MODE_LOCATE 0x8
433
 #define 	VS_MODE_LOCATE 0x8
389
 #define 		VS_LOCATE_STRING_SPECS 0x3
434
 #define 		VS_LOCATE_STRING_SPECS 0x3
435
+#define		VS_MODE_EEPROM_STREAM_WRITE 0xB
390
 
436
 
391
 /*
437
 /*
392
  * Read MAC address from its 'string specs' via the vendor-specific
438
  * Read MAC address from its 'string specs' via the vendor-specific
394
  * before it is mapped.)
440
  * before it is mapped.)
395
  *
441
  *
396
  * @v pci		The device.
442
  * @v pci		The device.
443
+ * @v vs		Offset of the PCI Vendor-Specific Capability.
397
  * @v mac		Buffer to store the MAC address.
444
  * @v mac		Buffer to store the MAC address.
398
  * @ret rc		Returns 0 on success, else an error code.
445
  * @ret rc		Returns 0 on success, else an error code.
399
  */
446
  */
400
 static int mac_address_from_string_specs ( struct pci_device *pci,
447
 static int mac_address_from_string_specs ( struct pci_device *pci,
401
-						   uint8 mac[ETH_ALEN] )
448
+					   unsigned int vs,
449
+					   uint8 mac[ETH_ALEN] )
402
 {
450
 {
403
 	char string_specs[256];
451
 	char string_specs[256];
404
 	char *ptr, *limit;
452
 	char *ptr, *limit;
405
 	char *to = string_specs;
453
 	char *to = string_specs;
406
 	uint32 addr;
454
 	uint32 addr;
407
 	uint32 len;
455
 	uint32 len;
408
-	unsigned int vs;
409
 	int mac_set = 0;
456
 	int mac_set = 0;
410
 
457
 
411
-	/* Find the "vendor specific" capability. */
412
-
413
-	vs = pci_find_capability ( pci, 9 );
414
-	if ( vs == 0 ) {
415
-		DBG ( "no VS\n" );
416
-		return -ENOTSUP;
417
-	}
418
-
419
 	/* Locate the String specs in LANai SRAM. */
458
 	/* Locate the String specs in LANai SRAM. */
420
 
459
 
421
 	pci_write_config_byte ( pci, VS_MODE, VS_MODE_LOCATE );
460
 	pci_write_config_byte ( pci, VS_MODE, VS_MODE_LOCATE );
427
 	/* Copy in the string specs.  Use 32-bit reads for performance. */
466
 	/* Copy in the string specs.  Use 32-bit reads for performance. */
428
 
467
 
429
 	if ( len > sizeof ( string_specs ) || ( len & 3 ) ) {
468
 	if ( len > sizeof ( string_specs ) || ( len & 3 ) ) {
469
+		pci_write_config_byte ( pci, VS_MODE, 0 );
430
 		DBG ( "SS too big\n" );
470
 		DBG ( "SS too big\n" );
431
 		return -ENOTSUP;
471
 		return -ENOTSUP;
432
 	}
472
 	}
483
 	return 0;
523
 	return 0;
484
 }
524
 }
485
 
525
 
526
+/****************************************************************
527
+ * NonVolatile Storage support
528
+ ****************************************************************/
529
+
530
+/*
531
+ * Fill a buffer with data read from nonvolatile storage.
532
+ *
533
+ * @v nvs	The NonVolatile Storage device to be read.
534
+ * @v addr      The first NonVolatile Storage address to be read.
535
+ * @v _buf	Pointer to the data buffer to be filled.
536
+ * @v len	The number of bytes to copy.
537
+ * @ret rc	0 on success, else nonzero.
538
+ */
539
+static int myri10ge_nvs_read ( struct nvs_device *nvs,
540
+			       unsigned int addr,
541
+			       void *_buf,
542
+			       size_t len )
543
+{
544
+	struct myri10ge_private *priv =
545
+		container_of (nvs, struct myri10ge_private, nvs);
546
+	struct pci_device *pci = myri10ge_pcidev ( myri10ge_netdev ( priv ) );
547
+	unsigned int vs = priv->pci_cap_vs;
548
+	unsigned char *buf = (unsigned char *) _buf;
549
+	unsigned int data;
550
+	unsigned int i, j;
551
+
552
+	DBGP ( "myri10ge_nvs_read\n" );
553
+
554
+	/* Issue the first read address. */
555
+
556
+	pci_write_config_byte ( pci, VS_EEPROM_READ_ADDR + 3, addr>>16 );
557
+	pci_write_config_byte ( pci, VS_EEPROM_READ_ADDR + 2, addr>>8 );
558
+	pci_write_config_byte ( pci, VS_EEPROM_READ_ADDR + 1, addr );
559
+	addr++;
560
+
561
+	/* Issue all the reads, and harvest the results every 4th issue. */
562
+
563
+	for ( i=0; i<len; ++i,addr++ ) {
564
+
565
+		/* Issue the next read address, updating only the
566
+		   bytes that need updating.  We always update the
567
+		   LSB, which triggers the read. */
568
+
569
+		if ( ( addr & 0xff ) == 0 ) {
570
+			if ( ( addr & 0xffff ) == 0 ) {
571
+				pci_write_config_byte ( pci,
572
+							VS_EEPROM_READ_ADDR + 3,
573
+							addr >> 16 );
574
+			}
575
+			pci_write_config_byte ( pci,
576
+					        VS_EEPROM_READ_ADDR + 2,
577
+						addr >> 8 );
578
+		}
579
+		pci_write_config_byte ( pci, VS_EEPROM_READ_ADDR + 1, addr );
580
+
581
+		/* If 4 data bytes are available, read them with a single read. */
582
+
583
+		if ( ( i & 3 ) == 3 ) {
584
+			pci_read_config_dword ( pci,
585
+						VS_EEPROM_READ_DATA,
586
+						&data );
587
+			for ( j=0; j<4; j++ ) {
588
+				buf[i-j] = data;
589
+				data >>= 8;
590
+			}
591
+		}
592
+	}
593
+
594
+	/* Harvest any remaining results. */
595
+
596
+	if ( ( i & 3 ) != 0 ) {
597
+		pci_read_config_dword ( pci, VS_EEPROM_READ_DATA, &data );
598
+		for ( j=1; j<=(i&3); j++ ) {
599
+			buf[i-j] = data;
600
+			data >>= 8;
601
+		}
602
+	}
603
+
604
+	DBGP_HDA ( addr - len, _buf, len );
605
+	return 0;
606
+}
607
+
608
+/*
609
+ * Write a buffer into nonvolatile storage.
610
+ *
611
+ * @v nvs	The NonVolatile Storage device to be written.
612
+ * @v address   The NonVolatile Storage address to be written.
613
+ * @v _buf	Pointer to the data to be written.
614
+ * @v len	Length of the buffer to be written.
615
+ * @ret rc	0 on success, else nonzero.
616
+ */
617
+static int myri10ge_nvs_write ( struct nvs_device *nvs,
618
+				unsigned int addr,
619
+				const void *_buf,
620
+				size_t len )
621
+{
622
+	struct myri10ge_private *priv =
623
+		container_of (nvs, struct myri10ge_private, nvs);
624
+	struct pci_device *pci = myri10ge_pcidev ( myri10ge_netdev ( priv ) );
625
+	unsigned int vs = priv->pci_cap_vs;
626
+	const unsigned char *buf = (const unsigned char *)_buf;
627
+	unsigned int i;
628
+	uint8 verify;
629
+
630
+	DBGP ( "nvs_write " );
631
+	DBGP_HDA ( addr, _buf, len );
632
+
633
+	/* Start erase of the NonVolatile Options block. */
634
+
635
+	DBGP ( "erasing " );
636
+	pci_write_config_dword ( pci, VS_EEPROM_WRITE, ( addr << 8 ) | 0xff );
637
+
638
+	/* Wait for erase to complete. */
639
+
640
+	DBGP ( "waiting " );
641
+	pci_read_config_byte ( pci, VS_EEPROM_READ_DATA, &verify );
642
+	while ( verify != 0xff ) {
643
+		pci_write_config_byte ( pci, VS_EEPROM_READ_ADDR + 1, addr );
644
+		pci_read_config_byte ( pci, VS_EEPROM_READ_DATA, &verify );
645
+	}
646
+
647
+	/* Write the data one byte at a time. */
648
+
649
+	DBGP ( "writing " );
650
+	pci_write_config_byte ( pci, VS_MODE, VS_MODE_EEPROM_STREAM_WRITE );
651
+	pci_write_config_dword ( pci, VS_ADDR, addr );
652
+	for (i=0; i<len; i++, addr++)
653
+		pci_write_config_byte ( pci, VS_DATA, buf[i] );
654
+	pci_write_config_dword ( pci, VS_ADDR, 0xffffffff );
655
+	pci_write_config_byte ( pci, VS_MODE, 0 );
656
+
657
+	DBGP ( "done\n" );
658
+	return 0;
659
+}
660
+
661
+/*
662
+ * Initialize NonVolatile storage support for a device.
663
+ *
664
+ * @v priv	Device private data for the device.
665
+ * @ret rc	0 on success, else an error code.
666
+ */
667
+
668
+static int myri10ge_nv_init ( struct myri10ge_private *priv )
669
+{
670
+	int rc;
671
+	struct myri10ge_eeprom_header
672
+	{
673
+		uint8 __jump[8];
674
+		uint32 eeprom_len;
675
+		uint32 eeprom_segment_len;
676
+		uint32 mcp1_offset;
677
+		uint32 mcp2_offset;
678
+		uint32 version;
679
+	} hdr;
680
+	uint32 mcp2_len;
681
+	unsigned int nvo_fragment_pos;
682
+
683
+	DBGP ( "myri10ge_nv_init\n" );
684
+
685
+	/* Read the EEPROM header, and byteswap the fields we will use.
686
+	   This is safe even though priv->nvs is not yet initialized. */
687
+
688
+	rc = myri10ge_nvs_read ( &priv->nvs, 0, &hdr, sizeof ( hdr ) );
689
+	if ( rc ) {
690
+		DBG ( "EEPROM header unreadable\n" );
691
+		return rc;
692
+	}
693
+	hdr.eeprom_len	       = ntohl ( hdr.eeprom_len );
694
+	hdr.eeprom_segment_len = ntohl ( hdr.eeprom_segment_len );
695
+	hdr.mcp2_offset	       = ntohl ( hdr.mcp2_offset );
696
+	hdr.version	       = ntohl ( hdr.version );
697
+	DBG2 ( "eelen:%xh seglen:%xh mcp2@%xh ver%d\n", hdr.eeprom_len,
698
+	       hdr.eeprom_segment_len, hdr.mcp2_offset, hdr.version );
699
+
700
+	/* If the firmware does not support EEPROM writes, simply return. */
701
+
702
+	if ( hdr.version < 1 ) {
703
+		DBG ( "No EEPROM write support\n" );
704
+		return 0;
705
+	}
706
+
707
+	/* Read the length of MCP2. */
708
+
709
+	rc = myri10ge_nvs_read ( &priv->nvs, hdr.mcp2_offset, &mcp2_len, 4 );
710
+	mcp2_len = ntohl ( mcp2_len );
711
+	DBG2 ( "mcp2len:%xh\n", mcp2_len );
712
+
713
+	/* Determine the position of the NonVolatile Options fragment and
714
+	   simply return if it overlaps other data. */
715
+
716
+	nvo_fragment_pos = hdr.eeprom_len -  hdr.eeprom_segment_len;
717
+	if ( hdr.mcp2_offset + mcp2_len > nvo_fragment_pos ) {
718
+		DBG ( "EEPROM full\n" );
719
+		return 0;
720
+	}
721
+
722
+	/* Initilize NonVolatile Storage state. */
723
+
724
+	priv->nvs.word_len_log2 = 0;
725
+	priv->nvs.size		= hdr.eeprom_len;
726
+	priv->nvs.block_size	= hdr.eeprom_segment_len;
727
+	priv->nvs.read		= myri10ge_nvs_read;
728
+	priv->nvs.write		= myri10ge_nvs_write;
729
+
730
+	/* Build the NonVolatile storage fragment list.  We would like
731
+	   to use the whole last EEPROM block for this, but we must
732
+	   reduce the block size lest malloc fail in
733
+	   src/core/nvo.o. */
734
+
735
+	priv->nvo_fragment[0].address = nvo_fragment_pos;
736
+	priv->nvo_fragment[0].len     = 0x200;
737
+
738
+	/* Register the NonVolatile Options storage. */
739
+
740
+	nvo_init ( &priv->nvo,
741
+		   &priv->nvs,
742
+		   priv->nvo_fragment,
743
+		   & myri10ge_netdev (priv) -> refcnt );
744
+	rc = register_nvo ( &priv->nvo,
745
+			    netdev_settings ( myri10ge_netdev ( priv ) ) );
746
+	if ( rc ) {
747
+		DBG ("register_nvo failed");
748
+		priv->nvo_fragment[0].len = 0;
749
+		return rc;
750
+	}
751
+
752
+	DBG2 ( "NVO supported\n" );
753
+	return 0;
754
+}
755
+
756
+void
757
+myri10ge_nv_fini ( struct myri10ge_private *priv )
758
+{
759
+	/* Simply return if nonvolatile access is not supported. */
760
+
761
+	if ( 0 == priv->nvo_fragment[0].len )
762
+		return;
763
+
764
+	unregister_nvo ( &priv->nvo );
765
+}
766
+
486
 /****************************************************************
767
 /****************************************************************
487
  * iPXE PCI Device Driver API functions
768
  * iPXE PCI Device Driver API functions
488
  ****************************************************************/
769
  ****************************************************************/
532
 
813
 
533
 	myri10ge_net_irq ( netdev, 0 );
814
 	myri10ge_net_irq ( netdev, 0 );
534
 
815
 
816
+	/* Find the PCI Vendor-Specific capability. */
817
+
818
+	priv->pci_cap_vs = pci_find_capability ( pci , PCI_CAP_ID_VNDR );
819
+	if ( 0 == priv->pci_cap_vs ) {
820
+		rc = -ENOTSUP;
821
+		dbg = "no_vs";
822
+		goto abort_with_netdev_init;
823
+	}
824
+
535
 	/* Read the NIC HW address. */
825
 	/* Read the NIC HW address. */
536
 
826
 
537
-	rc = mac_address_from_string_specs ( pci, netdev->hw_addr );
827
+	rc = mac_address_from_string_specs ( pci,
828
+					     priv->pci_cap_vs,
829
+					     netdev->hw_addr );
538
 	if ( rc ) {
830
 	if ( rc ) {
539
 		dbg = "mac_from_ss";
831
 		dbg = "mac_from_ss";
540
 		goto abort_with_netdev_init;
832
 		goto abort_with_netdev_init;
554
 		goto abort_with_netdev_init;
846
 		goto abort_with_netdev_init;
555
 	}
847
 	}
556
 
848
 
849
+	/* Initialize NonVolatile Storage support. */
850
+
851
+	rc = myri10ge_nv_init ( priv );
852
+	if ( rc ) {
853
+		dbg = "myri10ge_nv_init";
854
+		goto abort_with_registered_netdev;
855
+	}
856
+
557
 	DBGP ( "done\n" );
857
 	DBGP ( "done\n" );
558
 
858
 
559
 	return 0;
859
 	return 0;
560
 
860
 
861
+abort_with_registered_netdev:
862
+	unregister_netdev ( netdev );
561
 abort_with_netdev_init:
863
 abort_with_netdev_init:
562
 	netdev_nullify ( netdev );
864
 	netdev_nullify ( netdev );
563
 	netdev_put ( netdev );
865
 	netdev_put ( netdev );
580
 	DBGP ( "myri10ge_pci_remove\n" );
882
 	DBGP ( "myri10ge_pci_remove\n" );
581
 	netdev = pci_get_drvdata ( pci );
883
 	netdev = pci_get_drvdata ( pci );
582
 
884
 
885
+	myri10ge_nv_fini ( myri10ge_priv ( netdev ) );
583
 	unregister_netdev ( netdev );
886
 	unregister_netdev ( netdev );
584
 	netdev_nullify ( netdev );
887
 	netdev_nullify ( netdev );
585
 	netdev_put ( netdev );
888
 	netdev_put ( netdev );

正在加载...
取消
保存