gPXE fellows:

The attached patch set does the following, in 4 distinct patches:
  * Adds a PCI_CAP_ID_VS definition to pci.h.
  * Adds jump-scrolling to the settings_ui, since gPXE now
    has more settings that can fit on screen.
  * Adds setting-deletion support to the settings UI.
  * Adds nonvolatile option support to the myri10ge driver.

The attached .gif shows the appearance of the modified settings UI,
with "..." to suggest scrolling and with "Ctrl-D" instructions. The patches have been tested with diskless DHCP-less iSCSI boot over a myri10ge NIC.

Would someone please sponsor this patch set?

Thank you,
--Glenn Brown



>From 49fa1ab0f33157ef0115738d83228c427a97067a Mon Sep 17 00:00:00 2001
From: Glenn Brown <gl...@myri.com>
Date: Mon, 14 Jun 2010 16:30:00 -0700
Subject: [PATCH 1/4] [pci] Define Vendor Specific capability ID.

Add a PCI_CAP_ID_VS definition for the PCI standard "Vendor Specific"
capability ID.

Signed-off-by: Glenn Brown <gl...@myri.com>
---
 src/include/gpxe/pci.h |    1 +
 1 files changed, 1 insertions(+), 0 deletions(-)

diff --git a/src/include/gpxe/pci.h b/src/include/gpxe/pci.h
index 8bcf1e0..d8c693d 100644
--- a/src/include/gpxe/pci.h
+++ b/src/include/gpxe/pci.h
@@ -159,6 +159,7 @@ FILE_LICENCE ( GPL2_ONLY );
 #define  PCI_CAP_ID_SLOTID     0x04    /* Slot Identification */
 #define  PCI_CAP_ID_MSI                0x05    /* Message Signalled Interrupts 
*/
 #define  PCI_CAP_ID_CHSWP      0x06    /* CompactPCI HotSwap */
+#define  PCI_CAP_ID_VS         0x09    /* Vendor Specific */
 #define  PCI_CAP_ID_EXP                0x10    /* PCI Express */
 #define PCI_CAP_LIST_NEXT      1       /* Next capability in the list */
 #define PCI_CAP_FLAGS          2       /* Capability defined flags (16 bits) */
-- 
1.7.0.4


>From 0e67712cc5ae36a3e9058233eb7e58c87a1f6593 Mon Sep 17 00:00:00 2001
From: Glenn Brown <gl...@myri.com>
Date: Sun, 20 Jun 2010 22:46:37 -0700
Subject: [PATCH 2/4] [settings_ui] Jump scroll.

Implement jump scrolling with "..." displayed where the settings list
continues off-screen, because there are now too many settings to fit on
screen in the "config ..." text user interface.

Signed-off-by: Glenn Brown <gl...@myri.com>
---
 src/hci/tui/settings_ui.c |  115 ++++++++++++++++++++++++++++++---------------
 1 files changed, 77 insertions(+), 38 deletions(-)

diff --git a/src/hci/tui/settings_ui.c b/src/hci/tui/settings_ui.c
index 74ce6af..61ea3e4 100644
--- a/src/hci/tui/settings_ui.c
+++ b/src/hci/tui/settings_ui.c
@@ -45,6 +45,7 @@ FILE_LICENCE ( GPL2_OR_LATER );
 #define TITLE_ROW              1
 #define SETTINGS_LIST_ROW      3
 #define SETTINGS_LIST_COL      1
+#define SETTINGS_LIST_ROWS     16
 #define INFO_ROW               20
 #define ALERT_ROW              20
 #define INSTRUCTION_ROW                22
@@ -65,6 +66,8 @@ struct setting_row {
 struct setting_widget {
        /** Settings block */
        struct settings *settings;
+        /** Index of the first visible setting, for scrolling. */
+       unsigned int first_visible;
        /** Configuration setting */
        struct setting *setting;
        /** Screen row */
@@ -84,15 +87,13 @@ struct setting_widget {
 
 static void load_setting ( struct setting_widget *widget ) __nonnull;
 static int save_setting ( struct setting_widget *widget ) __nonnull;
-static void init_setting ( struct setting_widget *widget,
-                           struct settings *settings,
-                           struct setting *setting,
-                           unsigned int row, unsigned int col ) __nonnull;
+static void init_widget ( struct setting_widget *widget,
+                           struct settings *settings ) __nonnull;
 static void draw_setting ( struct setting_widget *widget ) __nonnull;
 static int edit_setting ( struct setting_widget *widget, int key ) __nonnull;
-static void init_setting_index ( struct setting_widget *widget,
-                                 struct settings *settings,
-                                 unsigned int index ) __nonnull;
+static void select_setting ( struct setting_widget *widget,
+                            unsigned int index ) __nonnull;
+static void reveal ( struct setting_widget *widget, unsigned int n) __nonnull;
 static void vmsg ( unsigned int row, const char *fmt, va_list args ) __nonnull;
 static void msg ( unsigned int row, const char *fmt, ... ) __nonnull;
 static void valert ( const char *fmt, va_list args ) __nonnull;
@@ -135,28 +136,17 @@ static int save_setting ( struct setting_widget *widget ) 
{
 }
 
 /**
- * Initialise setting widget
+ * Initialise the scrolling setting widget, drawing initial display.
  *
  * @v widget           Setting widget
  * @v settings         Settings block
- * @v setting          Configuration setting
- * @v row              Screen row
- * @v col              Screen column
  */
-static void init_setting ( struct setting_widget *widget,
-                          struct settings *settings,
-                          struct setting *setting,
-                          unsigned int row, unsigned int col ) {
-
-       /* Initialise widget structure */
+static void init_widget ( struct setting_widget *widget,
+                         struct settings *settings ) {
        memset ( widget, 0, sizeof ( *widget ) );
        widget->settings = settings;
-       widget->setting = setting;
-       widget->row = row;
-       widget->col = col;
-
-       /* Read current setting value */
-       load_setting ( widget );
+       widget->first_visible = SETTINGS_LIST_ROWS;
+       reveal ( widget, 0 );
 }
 
 /**
@@ -212,19 +202,25 @@ static int edit_setting ( struct setting_widget *widget, 
int key ) {
 }
 
 /**
- * Initialise setting widget by index
+ * Select a setting for display updates, by index.
  *
  * @v widget           Setting widget
  * @v settings         Settings block
  * @v index            Index of setting with settings list
  */
-static void init_setting_index ( struct setting_widget *widget,
-                                struct settings *settings,
-                                unsigned int index ) {
+static void select_setting ( struct setting_widget *widget,
+                            unsigned int index ) {
        struct setting *all_settings = table_start ( SETTINGS );
+       unsigned int skip = offsetof ( struct setting_widget, setting );
+
+       /* Reset the widget, preserving static state. */
+       memset ( ( char * ) widget + skip, 0, sizeof ( *widget ) - skip );
+       widget->setting = &all_settings[index];
+       widget->row = SETTINGS_LIST_ROW + index - widget->first_visible;
+       widget->col = SETTINGS_LIST_COL;
 
-       init_setting ( widget, settings, &all_settings[index],
-                      ( SETTINGS_LIST_ROW + index ), SETTINGS_LIST_COL );
+       /* Read current setting value */
+       load_setting ( widget );
 }
 
 /**
@@ -334,22 +330,65 @@ static void draw_instruction_row ( int editing ) {
        }
 }
 
+/**
+ * Reveal a setting by index: Scroll the setting list to reveal the
+ * specified setting.
+ *
+ * @widget     The main loop's display widget.
+ * @n          The index of the setting to reveal.
+ */
+static void reveal ( struct setting_widget *widget, unsigned int n)
+{
+       unsigned int i;
+
+       /* Simply return if setting N is already on-screen. */
+       if ( n - widget->first_visible < SETTINGS_LIST_ROWS )
+               return;
+       
+       /* Jump scroll to make the specified setting visible. */
+       while ( widget->first_visible < n )
+               widget->first_visible += SETTINGS_LIST_ROWS;
+       while ( widget->first_visible > n )
+               widget->first_visible -= SETTINGS_LIST_ROWS;
+       
+       /* Draw elipses before and/or after the settings list to
+          represent any invisible settings. */
+       mvaddstr ( SETTINGS_LIST_ROW - 1,
+                  SETTINGS_LIST_COL + 1,
+                  widget->first_visible > 0 ? "..." : "   " );
+       mvaddstr ( SETTINGS_LIST_ROW + SETTINGS_LIST_ROWS,
+                  SETTINGS_LIST_COL + 1,
+                  ( widget->first_visible + SETTINGS_LIST_ROWS < NUM_SETTINGS
+                    ? "..."
+                    : "   " ) );
+       
+       /* Draw visible settings. */
+       for ( i = 0; i < SETTINGS_LIST_ROWS; i++ ) {
+               if ( widget->first_visible + i < NUM_SETTINGS ) {
+                       select_setting ( widget, widget->first_visible + i );
+                       draw_setting ( widget );
+               } else {
+                       clearmsg ( SETTINGS_LIST_ROW + i );
+               }
+       }
+
+       /* Set the widget to the current row, which will be redrawn
+          appropriately by the main loop. */
+       select_setting ( widget, n );
+}
+
 static int main_loop ( struct settings *settings ) {
        struct setting_widget widget;
        unsigned int current = 0;
        unsigned int next;
-       int i;
        int key;
        int rc;
 
        /* Print initial screen content */
        draw_title_row();
        color_set ( CPAIR_NORMAL, NULL );
-       for ( i = ( NUM_SETTINGS - 1 ) ; i >= 0 ; i-- ) {
-               init_setting_index ( &widget, settings, i );
-               draw_setting ( &widget );
-       }
-
+       init_widget ( &widget, settings );
+       
        while ( 1 ) {
                /* Redraw information and instruction rows */
                draw_info_row ( widget.setting );
@@ -385,11 +424,11 @@ static int main_loop ( struct settings *settings ) {
                        switch ( key ) {
                        case KEY_DOWN:
                                if ( next < ( NUM_SETTINGS - 1 ) )
-                                       next++;
+                                       reveal ( &widget, ++next );
                                break;
                        case KEY_UP:
                                if ( next > 0 )
-                                       next--;
+                                       reveal ( &widget, --next ) ;
                                break;
                        case CTRL_X:
                                return 0;
@@ -399,7 +438,7 @@ static int main_loop ( struct settings *settings ) {
                        }       
                        if ( next != current ) {
                                draw_setting ( &widget );
-                               init_setting_index ( &widget, settings, next );
+                               select_setting ( &widget, next );
                                current = next;
                        }
                }
-- 
1.7.0.4


>From dbdf24ef2d3db34db08460402137bcbe4e58e37d Mon Sep 17 00:00:00 2001
From: Glenn Brown <gl...@myri.com>
Date: Sun, 20 Jun 2010 22:52:07 -0700
Subject: [PATCH 3/4] [settings_ui] Add setting deletion (Ctrl-D) interface

Make Ctrl-D delete a settings, because the Text User Interface (tui)
previously provided no way to delete a setting.  Also, update the
on-screen instructions to describe the new feature.  Deleting settings is
especially important for settings stored in precious nonvolatile storate.

Signed-off-by: Glenn Brown <gl...@myri.com>
---
 src/hci/tui/settings_ui.c |    7 +++++++
 1 files changed, 7 insertions(+), 0 deletions(-)

diff --git a/src/hci/tui/settings_ui.c b/src/hci/tui/settings_ui.c
index 61ea3e4..1915e58 100644
--- a/src/hci/tui/settings_ui.c
+++ b/src/hci/tui/settings_ui.c
@@ -326,6 +326,7 @@ static void draw_instruction_row ( int editing ) {
                      "Ctrl-C - discard changes" );
        } else {
                msg ( INSTRUCTION_ROW,
+                     "Ctrl-D - delete setting" INSTRUCTION_PAD
                      "Ctrl-X - exit configuration utility" );
        }
 }
@@ -430,6 +431,12 @@ static int main_loop ( struct settings *settings ) {
                                if ( next > 0 )
                                        reveal ( &widget, --next ) ;
                                break;
+                       case CTRL_D:
+                               delete_setting ( widget.settings,
+                                                widget.setting );
+                               select_setting ( &widget, next );
+                               draw_setting ( &widget );
+                               break;
                        case CTRL_X:
                                return 0;
                        default:
-- 
1.7.0.4


>From a6697dbc0ab262addd2e9fe57e9820b70b557230 Mon Sep 17 00:00:00 2001
From: Glenn Brown <gl...@myri.com>
Date: Sun, 20 Jun 2010 22:57:41 -0700
Subject: [PATCH 4/4] [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
gPXE 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: Glenn Brown <gl...@myri.com>
---
 src/drivers/net/myri10ge.c |  325 ++++++++++++++++++++++++++++++++++++++++++--
 1 files changed, 314 insertions(+), 11 deletions(-)

diff --git a/src/drivers/net/myri10ge.c b/src/drivers/net/myri10ge.c
index 353158b..99bc272 100644
--- a/src/drivers/net/myri10ge.c
+++ b/src/drivers/net/myri10ge.c
@@ -55,6 +55,12 @@ FILE_LICENCE ( GPL2_ONLY );
  * myri10ge_net_poll() polls for these receive notifications, posts
  * replacement receive buffers to the NIC, and passes received frames
  * to netdev_rx().
+ *
+ * NonVolatile Storage
+ *
+ * This driver supports NonVolatile Storage (nvs) in the NIC EEPROM.
+ * If the last EEPROM block is not otherwise filled, we tell
+ * gPXE it may store NonVolatile Options (nvo) there.
  */
 
 /*
@@ -75,6 +81,8 @@ FILE_LICENCE ( GPL2_ONLY );
 #include <gpxe/iobuf.h>
 #include <gpxe/malloc.h>
 #include <gpxe/netdevice.h>
+#include <gpxe/nvo.h>
+#include <gpxe/nvs.h>
 #include <gpxe/pci.h>
 #include <gpxe/timer.h>
 
@@ -169,6 +177,18 @@ struct myri10ge_private
           BEWARE: the value must be written 32 bits at a time. */
 
        mcp_cmd_t       *command;
+
+       /*
+        * Nonvolatile Storage for configuration options.
+        */
+
+       struct nvs_device       nvs;
+       struct nvo_fragment     nvo_fragment[2];
+       struct nvo_block        nvo;
+
+       /* Cached PCI capability locations. */
+
+       uint8                   pci_cap_vs;
 };
 
 /****************************************************************
@@ -199,6 +219,28 @@ static inline struct myri10ge_private *myri10ge_priv ( 
struct net_device *nd )
 }
 
 /*
+ * Convert a Myri10ge driver private data pointer to a netdev pointer.
+ *
+ * @v p                Myri10ge device private data.
+ * @ret r      The corresponding network device.
+ */
+static inline struct net_device *myri10ge_netdev ( struct myri10ge_private *p )
+{
+       return ( ( struct net_device * ) p ) - 1;
+}
+
+/*
+ * Convert a network device pointer to a PCI device pointer.
+ *
+ * @v netdev   A Network Device.
+ * @ret r      The corresponding PCI device.
+ */
+static inline struct pci_device *myri10ge_pcidev ( struct net_device *netdev )
+{
+       return container_of (netdev->dev, struct pci_device, dev);
+}
+
+/*
  * Pass a receive buffer to the NIC to be filled.
  *
  * @v priv     The network device to receive the buffer.
@@ -381,12 +423,16 @@ static void myri10ge_interrupt_handler ( struct 
net_device *netdev )
 /* Constants for reading the STRING_SPECS via the Myricom
    Vendor Specific PCI configuration space capability. */
 
+#define VS_EEPROM_READ_ADDR ( vs + 0x04 )
+#define VS_EEPROM_READ_DATA ( vs + 0x08 )
+#define VS_EEPROM_WRITE     ( vs + 0x0C )
 #define VS_ADDR ( vs + 0x18 )
 #define VS_DATA ( vs + 0x14 )
 #define VS_MODE ( vs + 0x10 )
 #define        VS_MODE_READ32 0x3
 #define        VS_MODE_LOCATE 0x8
 #define                VS_LOCATE_STRING_SPECS 0x3
+#define                VS_MODE_EEPROM_STREAM_WRITE 0xB
 
 /*
  * Read MAC address from its 'string specs' via the vendor-specific
@@ -394,28 +440,21 @@ static void myri10ge_interrupt_handler ( struct 
net_device *netdev )
  * before it is mapped.)
  *
  * @v pci              The device.
+ * @v vs               Offset of the PCI Vendor-Specific Capability.
  * @v mac              Buffer to store the MAC address.
  * @ret rc             Returns 0 on success, else an error code.
  */
 static int mac_address_from_string_specs ( struct pci_device *pci,
-                                                  uint8 mac[ETH_ALEN] )
+                                          unsigned int vs,
+                                          uint8 mac[ETH_ALEN] )
 {
        char string_specs[256];
        char *ptr, *limit;
        char *to = string_specs;
        uint32 addr;
        uint32 len;
-       unsigned int vs;
        int mac_set = 0;
 
-       /* Find the "vendor specific" capability. */
-
-       vs = pci_find_capability ( pci, 9 );
-       if ( vs == 0 ) {
-               DBG ( "no VS\n" );
-               return -ENOTSUP;
-       }
-
        /* Locate the String specs in LANai SRAM. */
 
        pci_write_config_byte ( pci, VS_MODE, VS_MODE_LOCATE );
@@ -427,6 +466,7 @@ static int mac_address_from_string_specs ( struct 
pci_device *pci,
        /* Copy in the string specs.  Use 32-bit reads for performance. */
 
        if ( len > sizeof ( string_specs ) || ( len & 3 ) ) {
+               pci_write_config_byte ( pci, VS_MODE, 0 );
                DBG ( "SS too big\n" );
                return -ENOTSUP;
        }
@@ -484,6 +524,247 @@ static int mac_address_from_string_specs ( struct 
pci_device *pci,
 }
 
 /****************************************************************
+ * NonVolatile Storage support
+ ****************************************************************/
+
+/*
+ * Fill a buffer with data read from nonvolatile storage.
+ *
+ * @v nvs      The NonVolatile Storage device to be read.
+ * @v addr      The first NonVolatile Storage address to be read.
+ * @v _buf     Pointer to the data buffer to be filled.
+ * @v len      The number of bytes to copy.
+ * @ret rc     0 on success, else nonzero.
+ */
+static int myri10ge_nvs_read ( struct nvs_device *nvs,
+                              unsigned int addr,
+                              void *_buf,
+                              size_t len )
+{
+       struct myri10ge_private *priv =
+               container_of (nvs, struct myri10ge_private, nvs);
+       struct pci_device *pci = myri10ge_pcidev ( myri10ge_netdev ( priv ) );
+       unsigned int vs = priv->pci_cap_vs;
+       unsigned char *buf = (unsigned char *) _buf;
+       unsigned int data;
+       unsigned int i, j;
+
+       DBGP ( "myri10ge_nvs_read\n" );
+
+       /* Issue the first read address. */
+
+       pci_write_config_byte ( pci, VS_EEPROM_READ_ADDR + 3, addr>>16 );
+       pci_write_config_byte ( pci, VS_EEPROM_READ_ADDR + 2, addr>>8 );
+       pci_write_config_byte ( pci, VS_EEPROM_READ_ADDR + 1, addr );
+       addr++;
+
+       /* Issue all the reads, and harvest the results every 4th issue. */
+
+       for ( i=0; i<len; ++i,addr++ ) {
+
+               /* Issue the next read address, updating only the
+                  bytes that need updating.  We always update the
+                  LSB, which triggers the read. */
+
+               if ( ( addr & 0xff ) == 0 ) {
+                       if ( ( addr & 0xffff ) == 0 ) {
+                               pci_write_config_byte ( pci,
+                                                       VS_EEPROM_READ_ADDR + 3,
+                                                       addr >> 16 );
+                       }
+                       pci_write_config_byte ( pci,
+                                               VS_EEPROM_READ_ADDR + 2,
+                                               addr >> 8 );
+               }
+               pci_write_config_byte ( pci, VS_EEPROM_READ_ADDR + 1, addr );
+
+               /* If 4 data bytes are available, read them with a single read. 
*/
+
+               if ( ( i & 3 ) == 3 ) {
+                       pci_read_config_dword ( pci,
+                                               VS_EEPROM_READ_DATA,
+                                               &data );
+                       for ( j=0; j<4; j++ ) {
+                               buf[i-j] = data;
+                               data >>= 8;
+                       }
+               }
+       }
+
+       /* Harvest any remaining results. */
+
+       if ( ( i & 3 ) != 0 ) {
+               pci_read_config_dword ( pci, VS_EEPROM_READ_DATA, &data );
+               for ( j=1; j<=(i&3); j++ ) {
+                       buf[i-j] = data;
+                       data >>= 8;
+               }
+       }
+
+       DBGP_HDA ( addr - len, _buf, len );
+       return 0;
+}
+
+/*
+ * Write a buffer into nonvolatile storage.
+ *
+ * @v nvs      The NonVolatile Storage device to be written.
+ * @v address   The NonVolatile Storage address to be written.
+ * @v _buf     Pointer to the data to be written.
+ * @v len      Length of the buffer to be written.
+ * @ret rc     0 on success, else nonzero.
+ */
+static int myri10ge_nvs_write ( struct nvs_device *nvs,
+                               unsigned int addr,
+                               const void *_buf,
+                               size_t len )
+{
+       struct myri10ge_private *priv =
+               container_of (nvs, struct myri10ge_private, nvs);
+       struct pci_device *pci = myri10ge_pcidev ( myri10ge_netdev ( priv ) );
+       unsigned int vs = priv->pci_cap_vs;
+       const unsigned char *buf = (const unsigned char *)_buf;
+       unsigned int i;
+       uint8 verify;
+
+       DBGP ( "nvs_write " );
+       DBGP_HDA ( addr, _buf, len );
+
+       /* Start erase of the NonVolatile Options block. */
+
+       DBGP ( "erasing " );
+       pci_write_config_dword ( pci, VS_EEPROM_WRITE, ( addr << 8 ) | 0xff );
+
+       /* Wait for erase to complete. */
+
+       DBGP ( "waiting " );
+       pci_read_config_byte ( pci, VS_EEPROM_READ_DATA, &verify );
+       while ( verify != 0xff ) {
+               pci_write_config_byte ( pci, VS_EEPROM_READ_ADDR + 1, addr );
+               pci_read_config_byte ( pci, VS_EEPROM_READ_DATA, &verify );
+       }
+
+       /* Write the data one byte at a time. */
+
+       DBGP ( "writing " );
+       pci_write_config_byte ( pci, VS_MODE, VS_MODE_EEPROM_STREAM_WRITE );
+       pci_write_config_dword ( pci, VS_ADDR, addr );
+       for (i=0; i<len; i++, addr++)
+               pci_write_config_byte ( pci, VS_DATA, buf[i] );
+       pci_write_config_dword ( pci, VS_ADDR, 0xffffffff );
+       pci_write_config_byte ( pci, VS_MODE, 0 );
+
+       DBGP ( "done\n" );
+       return 0;
+}
+
+/*
+ * Initialize NonVolatile storage support for a device.
+ *
+ * @v priv     Device private data for the device.
+ * @ret rc     0 on success, else an error code.
+ */
+
+static int myri10ge_nv_init ( struct myri10ge_private *priv )
+{
+       int rc;
+       struct myri10ge_eeprom_header
+       {
+               uint8 __jump[8];
+               uint32 eeprom_len;
+               uint32 eeprom_segment_len;
+               uint32 mcp1_offset;
+               uint32 mcp2_offset;
+               uint32 version;
+       } hdr;
+       uint32 mcp2_len;
+       unsigned int nvo_fragment_pos;
+
+       DBGP ( "myri10ge_nv_init\n" );
+
+       /* Read the EEPROM header, and byteswap the fields we will use.
+          This is safe even though priv->nvs is not yet initialized. */
+
+       rc = myri10ge_nvs_read ( &priv->nvs, 0, &hdr, sizeof ( hdr ) );
+       if ( rc ) {
+               DBG ( "EEPROM header unreadable\n" );
+               return rc;
+       }
+       hdr.eeprom_len         = bswap_32 ( hdr.eeprom_len );
+       hdr.eeprom_segment_len = bswap_32 ( hdr.eeprom_segment_len );
+       hdr.mcp2_offset        = bswap_32 ( hdr.mcp2_offset );
+       hdr.version            = bswap_32 ( hdr.version );
+       DBG2 ( "eelen:%xh seglen:%xh m...@%xh ver%d\n", hdr.eeprom_len,
+              hdr.eeprom_segment_len, hdr.mcp2_offset, hdr.version );
+
+       /* If the firmware does not support EEPROM writes, simply return. */
+
+       if ( hdr.version < 1 ) {
+               DBG ( "No EEPROM write support\n" );
+               return 0;
+       }
+
+       /* Read the length of MCP2. */
+
+       rc = myri10ge_nvs_read ( &priv->nvs, hdr.mcp2_offset, &mcp2_len, 4 );
+       mcp2_len = bswap_32 ( mcp2_len );
+       DBG2 ( "mcp2len:%xh\n", mcp2_len );
+
+       /* Determine the position of the NonVolatile Options fragment and
+          simply return if it overlaps other data. */
+
+       nvo_fragment_pos = hdr.eeprom_len -  hdr.eeprom_segment_len;
+       if ( hdr.mcp2_offset + mcp2_len > nvo_fragment_pos ) {
+               DBG ( "EEPROM full\n" );
+               return 0;
+       }
+
+       /* Initilize NonVolatile Storage state. */
+
+       priv->nvs.word_len_log2 = 0;
+       priv->nvs.size          = hdr.eeprom_len;
+       priv->nvs.block_size    = hdr.eeprom_segment_len;
+       priv->nvs.read          = myri10ge_nvs_read;
+       priv->nvs.write         = myri10ge_nvs_write;
+
+       /* Build the NonVolatile storage fragment list.  We would like
+          to use the whole last EEPROM block for this, but we must
+          reduce the block size lest malloc fail in
+          src/core/nvo.o. */
+
+       priv->nvo_fragment[0].address = nvo_fragment_pos;
+       priv->nvo_fragment[0].len     = 0x200;
+
+       /* Register the NonVolatile Options storage. */
+
+       nvo_init ( &priv->nvo,
+                  &priv->nvs,
+                  priv->nvo_fragment,
+                  & myri10ge_netdev (priv) -> refcnt );
+       rc = register_nvo ( &priv->nvo,
+                           netdev_settings ( myri10ge_netdev ( priv ) ) );
+       if ( rc ) {
+               DBG ("register_nvo failed");
+               priv->nvo_fragment[0].len = 0;
+               return rc;
+       }
+
+       DBG2 ( "NVO supported\n" );
+       return 0;
+}
+
+void
+myri10ge_nv_fini ( struct myri10ge_private *priv )
+{
+       /* Simply return if nonvolatile access is not supported. */
+
+       if ( 0 == priv->nvo_fragment[0].len )
+               return;
+
+       unregister_nvo ( &priv->nvo );
+}
+
+/****************************************************************
  * gPXE PCI Device Driver API functions
  ****************************************************************/
 
@@ -532,9 +813,20 @@ static int myri10ge_pci_probe ( struct pci_device *pci,
 
        myri10ge_net_irq ( netdev, 0 );
 
+       /* Find the PCI Vendor-Specific capability. */
+
+       priv->pci_cap_vs = pci_find_capability ( pci , PCI_CAP_ID_VS );
+       if ( 0 == priv->pci_cap_vs ) {
+               rc = -ENOTSUP;
+               dbg = "no_vs";
+               goto abort_with_netdev_init;
+       }
+
        /* Read the NIC HW address. */
 
-       rc = mac_address_from_string_specs ( pci, netdev->hw_addr );
+       rc = mac_address_from_string_specs ( pci,
+                                            priv->pci_cap_vs,
+                                            netdev->hw_addr );
        if ( rc ) {
                dbg = "mac_from_ss";
                goto abort_with_netdev_init;
@@ -554,10 +846,20 @@ static int myri10ge_pci_probe ( struct pci_device *pci,
                goto abort_with_netdev_init;
        }
 
+       /* Initialize NonVolatile Storage support. */
+
+       rc = myri10ge_nv_init ( priv );
+       if ( rc ) {
+               dbg = "myri10ge_nv_init";
+               goto abort_with_registered_netdev;
+       }
+
        DBGP ( "done\n" );
 
        return 0;
 
+abort_with_registered_netdev:
+       unregister_netdev ( netdev );
 abort_with_netdev_init:
        netdev_nullify ( netdev );
        netdev_put ( netdev );
@@ -580,6 +882,7 @@ static void myri10ge_pci_remove ( struct pci_device *pci )
        DBGP ( "myri10ge_pci_remove\n" );
        netdev = pci_get_drvdata ( pci );
 
+       myri10ge_nv_fini ( myri10ge_priv ( netdev ) );
        unregister_netdev ( netdev );
        netdev_nullify ( netdev );
        netdev_put ( netdev );
-- 
1.7.0.4

<<attachment: gpxe_config.gif>>

_______________________________________________
gPXE mailing list
gPXE@etherboot.org
http://etherboot.org/mailman/listinfo/gpxe

Reply via email to