patch-2.4.21 linux-2.4.21/arch/ia64/sn/io/sn1/eeprom.c

Next file: linux-2.4.21/arch/ia64/sn/io/sn1/efi-rtc.c
Previous file: linux-2.4.21/arch/ia64/sn/io/sn1/Makefile
Back to the patch index
Back to the overall index

diff -urN linux-2.4.20/arch/ia64/sn/io/sn1/eeprom.c linux-2.4.21/arch/ia64/sn/io/sn1/eeprom.c
@@ -0,0 +1,1421 @@
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (C) 1999-2002 Silicon Graphics, Inc. All rights reserved.
+ */
+
+/*
+ * WARNING:     There is more than one copy of this file in different isms.
+ *              All copies must be kept exactly in sync.
+ *              Do not modify this file without also updating the following:
+ *
+ *              irix/kern/io/eeprom.c
+ *              stand/arcs/lib/libsk/ml/eeprom.c
+ *		stand/arcs/lib/libkl/io/eeprom.c
+ *
+ *      (from time to time they might not be in sync but that's due to bringup
+ *       activity - this comment is to remind us that they eventually have to
+ *       get back together)
+ *
+ * eeprom.c
+ *
+ * access to board-mounted EEPROMs via the L1 system controllers
+ *
+ */
+
+#include <linux/types.h>
+#include <linux/slab.h>
+#include <asm/sn/sgi.h>
+#include <asm/sn/io.h>
+#include <asm/sn/iograph.h>
+#include <asm/sn/invent.h>
+#include <asm/sn/hcl.h>
+#include <asm/sn/hcl_util.h>
+#include <asm/sn/labelcl.h>
+#include <asm/sn/eeprom.h>
+#include <asm/sn/router.h>
+#include <asm/sn/module.h>
+#include <asm/sn/ksys/l1.h>
+#include <asm/sn/nodepda.h>
+#include <asm/sn/clksupport.h>
+#include <asm/sn/sn_cpuid.h>
+#include <asm/sn/simulator.h>
+
+#if defined(EEPROM_DEBUG)
+#define db_printf(x) printk x
+#else
+#define db_printf(x) printk x
+#endif
+
+#define BCOPY(x,y,z)	memcpy(y,x,z)
+
+#define UNDERSCORE	0	/* don't convert underscores to hyphens */
+#define HYPHEN		1	/* convert underscores to hyphens */
+
+void		copy_ascii_field( char *to, char *from, int length,
+			          int change_underscore );
+uint64_t	generate_unique_id( char *sn, int sn_len );
+uchar_t		char_to_base36( char c );
+int		nicify( char *dst, eeprom_brd_record_t *src );
+static void	int64_to_hex_string( char *out, uint64_t val );
+
+// extern int router_lock( net_vec_t, int, int );
+// extern int router_unlock( net_vec_t );
+#define ROUTER_LOCK(p) 	// router_lock(p, 10000, 3000000)
+#define ROUTER_UNLOCK(p) 	// router_unlock(p)
+
+#define IP27LOG_OVNIC           "OverrideNIC"
+
+
+/* the following function converts an EEPROM record to a close facsimile
+ * of the string returned by reading a Dallas Semiconductor NIC (see
+ * one of the many incarnations of nic.c for details on that driver)
+ */
+int nicify( char *dst, eeprom_brd_record_t *src )
+{
+    int field_len;
+    uint64_t unique_id;
+    char *cur_dst = dst;
+    eeprom_board_ia_t   *board;
+
+    board   = src->board_ia;
+    ASSERT( board );  /* there should always be a board info area */
+
+    /* copy part number */
+    strcpy( cur_dst, "Part:" );
+    cur_dst += strlen( cur_dst );
+    ASSERT( (board->part_num_tl & FIELD_FORMAT_MASK)
+	    == FIELD_FORMAT_ASCII );
+    field_len = board->part_num_tl & FIELD_LENGTH_MASK;
+    copy_ascii_field( cur_dst, board->part_num, field_len, HYPHEN );
+    cur_dst += field_len;
+
+    /* copy product name */
+    strcpy( cur_dst, ";Name:" );
+    cur_dst += strlen( cur_dst );
+    ASSERT( (board->product_tl & FIELD_FORMAT_MASK) == FIELD_FORMAT_ASCII );
+    field_len = board->product_tl & FIELD_LENGTH_MASK;
+    copy_ascii_field( cur_dst, board->product, field_len, UNDERSCORE );
+    cur_dst += field_len;
+
+    /* copy serial number */
+    strcpy( cur_dst, ";Serial:" );
+    cur_dst += strlen( cur_dst );
+    ASSERT( (board->serial_num_tl & FIELD_FORMAT_MASK)
+	    == FIELD_FORMAT_ASCII );
+    field_len = board->serial_num_tl & FIELD_LENGTH_MASK;
+    copy_ascii_field( cur_dst, board->serial_num, field_len,
+		      HYPHEN);
+
+    cur_dst += field_len;
+
+    /* copy revision */
+    strcpy( cur_dst, ";Revision:");
+    cur_dst += strlen( cur_dst );
+    ASSERT( (board->board_rev_tl & FIELD_FORMAT_MASK)
+	    == FIELD_FORMAT_ASCII );
+    field_len = board->board_rev_tl & FIELD_LENGTH_MASK;
+    copy_ascii_field( cur_dst, board->board_rev, field_len, HYPHEN );
+    cur_dst += field_len;
+
+    /* EEPROMs don't have equivalents for the Group, Capability and
+     * Variety fields, so we pad these with 0's
+     */
+    strcpy( cur_dst, ";Group:ff;Capability:ffffffff;Variety:ff" );
+    cur_dst += strlen( cur_dst );
+
+    /* use the board serial number to "fake" a laser id */
+    strcpy( cur_dst, ";Laser:" );
+    cur_dst += strlen( cur_dst );
+    unique_id = generate_unique_id( board->serial_num,
+				    board->serial_num_tl & FIELD_LENGTH_MASK );
+    int64_to_hex_string( cur_dst, unique_id );
+    strcat( dst, ";" );
+
+    return 1;
+}
+
+
+/* These functions borrow heavily from chars2* in nic.c
+ */
+void copy_ascii_field( char *to, char *from, int length,
+		       int change_underscore )
+{
+    int i;
+    for( i = 0; i < length; i++ ) {
+
+	/* change underscores to hyphens if requested */
+	if( from[i] == '_' && change_underscore == HYPHEN )
+	    to[i] = '-';
+
+	/* ; and ; are separators, so mustn't appear within
+	 * a field */
+	else if( from[i] == ':' || from[i] == ';' )
+	    to[i] = '?';
+
+	/* I'm not sure why or if ASCII character 0xff would
+	 * show up in an EEPROM field, but the NIC parsing
+	 * routines wouldn't like it if it did... so we
+	 * get rid of it, just in case. */
+	else if( (unsigned char)from[i] == (unsigned char)0xff )
+	    to[i] = ' ';
+	
+	/* unprintable characters are replaced with . */
+	else if( from[i] < ' ' || from[i] >= 0x7f )
+	    to[i] = '.';
+
+	/* otherwise, just copy the character */
+	else
+	    to[i] = from[i];
+    }
+
+    if( i == 0 ) {
+	to[i] = ' '; /* return at least a space... */
+	i++;
+    }
+    to[i] = 0;	     /* terminating null */
+}
+
+/* Note that int64_to_hex_string currently only has a big-endian
+ * implementation.
+ */
+#ifdef _MIPSEB
+static void int64_to_hex_string( char *out, uint64_t val )
+{
+    int i;
+    uchar_t table[] = "0123456789abcdef";
+    uchar_t *byte_ptr = (uchar_t *)&val;
+    for( i = 0; i < sizeof(uint64_t); i++ ) {
+	out[i*2] = table[ ((*byte_ptr) >> 4) & 0x0f ];
+	out[i*2+1] = table[ (*byte_ptr) & 0x0f ];
+	byte_ptr++;
+    }
+    out[i*2] = '\0';
+}
+
+#else /* little endian */
+
+static void int64_to_hex_string( char *out, uint64_t val )
+{
+
+
+	printk("int64_to_hex_string needs a little-endian implementation.\n");
+}
+#endif /* _MIPSEB */
+
+/* Convert a standard ASCII serial number to a unique integer
+ * id number by treating the serial number string as though
+ * it were a base 36 number
+ */
+uint64_t generate_unique_id( char *sn, int sn_len )
+{
+    int uid = 0;
+    int i;
+
+    #define VALID_BASE36(c)	((c >= '0' && c <='9') \
+			    ||   (c >= 'A' && c <='Z') \
+			    ||   (c >= 'a' && c <='z'))
+
+    for( i = 0; i < sn_len; i++ ) {
+	if( !VALID_BASE36(sn[i]) )
+	    continue;
+	uid *= 36;
+	uid += char_to_base36( sn[i] );
+    }
+
+    if( uid == 0 )
+	return rtc_time();
+
+    return uid;
+}
+
+uchar_t char_to_base36( char c )
+{
+    uchar_t val;
+
+    if( c >= '0' && c <= '9' )
+	val = (c - '0');
+
+    else if( c >= 'A' && c <= 'Z' )
+	val = (c - 'A' + 10);
+
+    else if( c >= 'a' && c <= 'z' )
+	val = (c - 'a' + 10);
+
+    else val = 0;
+
+    return val;
+}
+
+
+/* given a pointer to the three-byte little-endian EEPROM representation
+ * of date-of-manufacture, this function translates to a big-endian
+ * integer format
+ */
+int eeprom_xlate_board_mfr_date( uchar_t *src )
+{
+    int rval = 0;
+    rval += *src; src++;
+    rval += ((int)(*src) << 8); src ++;
+    rval += ((int)(*src) << 16);
+    return rval;
+}
+
+
+int eeprom_str( char *nic_str, nasid_t nasid, int component )
+{
+    eeprom_brd_record_t eep;
+    eeprom_board_ia_t board;
+    eeprom_chassis_ia_t chassis;
+    int r;
+
+    if( (component & C_DIMM) == C_DIMM ) {
+	/* this function isn't applicable to DIMMs */
+	return EEP_PARAM;
+    }
+    else {
+	eep.board_ia = &board;
+	eep.spd = NULL;
+	if( !(component & SUBORD_MASK) )
+	    eep.chassis_ia = &chassis;  /* only main boards have a chassis
+					 * info area */
+	else
+	    eep.chassis_ia = NULL;
+    }
+    
+    switch( component & BRICK_MASK ) {
+      case C_BRICK:
+	r = cbrick_eeprom_read( &eep, nasid, component );
+	break;
+      case IO_BRICK:
+	r = iobrick_eeprom_read( &eep, nasid, component );
+	break;
+      default:
+	return EEP_PARAM;  /* must be an invalid component */
+    }
+    if( r )
+	return r;
+    if( !nicify( nic_str, &eep ) )
+	return EEP_NICIFY;
+
+    return EEP_OK;
+}
+
+int vector_eeprom_str( char *nic_str, nasid_t nasid,
+		       int component, net_vec_t path )
+{
+    eeprom_brd_record_t eep;
+    eeprom_board_ia_t board;
+    eeprom_chassis_ia_t chassis;
+    int r;
+
+    eep.board_ia = &board;
+    if( !(component & SUBORD_MASK) )
+        eep.chassis_ia = &chassis;  /* only main boards have a chassis
+                                     * info area */
+    else
+        eep.chassis_ia = NULL;
+
+    if( !(component & VECTOR) )
+	return EEP_PARAM;
+
+    if( (r = vector_eeprom_read( &eep, nasid, path, component )) )
+	return r;
+
+    if( !nicify( nic_str, &eep ) )
+        return EEP_NICIFY;
+
+    return EEP_OK;
+}
+
+
+int is_iobrick( int nasid, int widget_num )
+{
+    uint32_t wid_reg;
+    int part_num, mfg_num;
+
+    /* Read the widget's WIDGET_ID register to get
+     * its part number and mfg number
+     */
+    wid_reg = *(volatile int32_t *)
+        (NODE_SWIN_BASE( nasid, widget_num ) + WIDGET_ID);
+
+    part_num = (wid_reg & WIDGET_PART_NUM) >> WIDGET_PART_NUM_SHFT;
+    mfg_num = (wid_reg & WIDGET_MFG_NUM) >> WIDGET_MFG_NUM_SHFT;
+
+    /* Is this the "xbow part" of an XBridge?  If so, this
+     * widget is definitely part of an I/O brick.
+     */
+    if( part_num == XXBOW_WIDGET_PART_NUM &&
+	mfg_num == XXBOW_WIDGET_MFGR_NUM )
+
+	return 1;
+
+    /* Is this a "bridge part" of an XBridge?  If so, once
+     * again, we know this widget is part of an I/O brick.
+     */
+    if( part_num == XBRIDGE_WIDGET_PART_NUM &&
+	mfg_num == XBRIDGE_WIDGET_MFGR_NUM )
+
+	return 1;
+
+    return 0;
+}
+
+
+int cbrick_uid_get( nasid_t nasid, uint64_t *uid )
+{
+    char uid_str[32];
+    char msg[BRL1_QSIZE];
+    int subch, len;
+    l1sc_t sc;
+    l1sc_t *scp;
+    int local = (nasid == get_nasid());
+
+    if ( IS_RUNNING_ON_SIMULATOR() )
+	return EEP_L1;
+
+    /* If the promlog variable pointed to by IP27LOG_OVNIC is set,
+     * use that value for the cbrick UID rather than the EEPROM
+     * serial number.
+     */
+#ifdef LOG_GETENV
+    if( ip27log_getenv( nasid, IP27LOG_OVNIC, uid_str, NULL, 0 ) >= 0 )
+    {
+	/* We successfully read IP27LOG_OVNIC, so return it as the UID. */
+	db_printf(( "cbrick_uid_get:"
+		    "Overriding UID with environment variable %s\n", 
+		    IP27LOG_OVNIC ));
+	*uid = strtoull( uid_str, NULL, 0 );
+	return EEP_OK;
+    }
+#endif
+
+    /* If this brick is retrieving its own uid, use the local l1sc_t to
+     * arbitrate access to the l1; otherwise, set up a new one.
+     */
+    if( local ) {
+	scp = get_l1sc();
+    }
+    else {
+	scp = &sc;
+	sc_init( &sc, nasid, BRL1_LOCALHUB_UART );
+    }
+
+    /* fill in msg with the opcode & params */
+    BZERO( msg, BRL1_QSIZE );
+    if( (subch = sc_open( scp, L1_ADDR_LOCAL )) < 0 )
+	return EEP_L1;
+
+    if( (len = sc_construct_msg( scp, subch, msg, BRL1_QSIZE,
+				 L1_ADDR_TASK_GENERAL,
+				 L1_REQ_SER_NUM, 0 )) < 0 )
+    {
+	sc_close( scp, subch );
+	return( EEP_L1 );
+    }
+
+    /* send the request to the L1 */
+    if( sc_command( scp, subch, msg, msg, &len ) ) {
+	sc_close( scp, subch );
+	return( EEP_L1 );
+    }
+
+    /* free up subchannel */
+    sc_close(scp, subch);
+
+    /* check response */
+    if( sc_interpret_resp( msg, 2, L1_ARG_ASCII, uid_str ) < 0 )
+    {
+	return( EEP_L1 );
+    }
+
+    *uid = generate_unique_id( uid_str, strlen( uid_str ) );
+
+    return EEP_OK;
+}
+
+
+int rbrick_uid_get( nasid_t nasid, net_vec_t path, uint64_t *uid )
+{
+    char uid_str[32];
+    char msg[BRL1_QSIZE];
+    int subch, len;
+    l1sc_t sc;
+
+    if ( IS_RUNNING_ON_SIMULATOR() )
+	return EEP_L1;
+
+#define FAIL								\
+    {									\
+	*uid = rtc_time();						\
+	printk( "rbrick_uid_get failed; using current time as uid\n" );	\
+	return EEP_OK;							\
+    }
+
+    ROUTER_LOCK(path);
+    sc_init( &sc, nasid, path );
+
+    /* fill in msg with the opcode & params */
+    BZERO( msg, BRL1_QSIZE );
+    if( (subch = sc_open( &sc, L1_ADDR_LOCAL )) < 0 ) {
+	ROUTER_UNLOCK(path);
+	FAIL;
+    }
+
+    if( (len = sc_construct_msg( &sc, subch, msg, BRL1_QSIZE,
+				 L1_ADDR_TASK_GENERAL,
+				 L1_REQ_SER_NUM, 0 )) < 0 )
+    {
+	ROUTER_UNLOCK(path);
+	sc_close( &sc, subch );
+	FAIL;
+    }
+
+    /* send the request to the L1 */
+    if( sc_command( &sc, subch, msg, msg, &len ) ) {
+	ROUTER_UNLOCK(path);
+	sc_close( &sc, subch );
+	FAIL;
+    }
+
+    /* free up subchannel */
+    ROUTER_UNLOCK(path);
+    sc_close(&sc, subch);
+
+    /* check response */
+    if( sc_interpret_resp( msg, 2, L1_ARG_ASCII, uid_str ) < 0 )
+    {
+	FAIL;
+    }
+
+    *uid = generate_unique_id( uid_str, strlen( uid_str ) );
+
+    return EEP_OK;
+}
+
+int iobrick_uid_get( nasid_t nasid, uint64_t *uid )
+{
+    eeprom_brd_record_t eep;
+    eeprom_board_ia_t board;
+    eeprom_chassis_ia_t chassis;
+    int r;
+
+    eep.board_ia = &board;
+    eep.chassis_ia = &chassis;
+    eep.spd = NULL;
+
+    r = iobrick_eeprom_read( &eep, nasid, IO_BRICK );
+    if( r != EEP_OK ) {
+        *uid = rtc_time();
+        return r;
+    }
+
+    *uid = generate_unique_id( board.serial_num,
+                               board.serial_num_tl & FIELD_LENGTH_MASK );
+
+    return EEP_OK;
+}
+
+
+int ibrick_mac_addr_get( nasid_t nasid, char *eaddr )
+{
+    eeprom_brd_record_t eep;
+    eeprom_board_ia_t board;
+    eeprom_chassis_ia_t chassis;
+    int r;
+    char *tmp;
+
+    eep.board_ia = &board;
+    eep.chassis_ia = &chassis;
+    eep.spd = NULL;
+
+    r = iobrick_eeprom_read( &eep, nasid, IO_BRICK );
+    if( (r != EEP_OK) || (board.mac_addr[0] == '\0') ) {
+	db_printf(( "ibrick_mac_addr_get: "
+		    "Couldn't read MAC address from EEPROM\n" ));
+	return EEP_L1;
+    }
+    else {
+	/* successfully read info area */
+	int ix;
+	tmp = board.mac_addr;
+	for( ix = 0; ix < (board.mac_addr_tl & FIELD_LENGTH_MASK); ix++ )
+	{
+	    *eaddr++ = *tmp++;
+	}
+	*eaddr = '\0';
+    }
+
+    return EEP_OK;
+}
+
+
+/* 
+ * eeprom_vertex_info_set
+ *
+ * Given a vertex handle, a component designation, a starting nasid
+ * and (in the case of a router) a vector path to the component, this
+ * function will read the EEPROM and attach the resulting information
+ * to the vertex in the same string format as that provided by the
+ * Dallas Semiconductor NIC drivers.  If the vertex already has the
+ * string, this function just returns the string.
+ */
+
+extern char *nic_vertex_info_get( devfs_handle_t );
+extern void nic_vmc_check( devfs_handle_t, char * );
+/* the following were lifted from nic.c - change later? */
+#define MAX_INFO 2048
+#define NEWSZ(ptr,sz)   ((ptr) = kern_malloc((sz)))
+#define DEL(ptr) (kern_free((ptr)))
+
+char *eeprom_vertex_info_set( int component, int nasid, devfs_handle_t v,
+                              net_vec_t path )
+{
+        char *info_tmp;
+        int info_len;
+        char *info;
+
+        /* see if this vertex is already marked */
+        info_tmp = nic_vertex_info_get(v);
+        if (info_tmp) return info_tmp;
+
+        /* get a temporary place for the data */
+        NEWSZ(info_tmp, MAX_INFO);
+        if (!info_tmp) return NULL;
+
+        /* read the EEPROM */
+	if( component & R_BRICK ) {
+	    if( RBRICK_EEPROM_STR( info_tmp, nasid, path ) != EEP_OK )
+		return NULL;
+	}
+	else {
+            if( eeprom_str( info_tmp, nasid, component ) != EEP_OK )
+	        return NULL;
+	}
+
+        /* allocate a smaller final place */
+        info_len = strlen(info_tmp)+1;
+        NEWSZ(info, info_len);
+        if (info) {
+                strcpy(info, info_tmp);
+                DEL(info_tmp);
+        } else {
+                info = info_tmp;
+        }
+
+        /* add info to the vertex */
+        hwgraph_info_add_LBL(v, INFO_LBL_NIC,
+                             (arbitrary_info_t) info);
+
+        /* see if someone else got there first */
+        info_tmp = nic_vertex_info_get(v);
+        if (info != info_tmp) {
+            DEL(info);
+            return info_tmp;
+        }
+
+        /* export the data */
+        hwgraph_info_export_LBL(v, INFO_LBL_NIC, info_len);
+
+        /* trigger all matching callbacks */
+        nic_vmc_check(v, info);
+
+        return info;
+}
+
+
+/*********************************************************************
+ *
+ * stubs for use until the Bedrock/L1 link is available
+ *
+ */
+
+#include <asm/sn/nic.h>
+
+/* #define EEPROM_TEST */
+
+/* fake eeprom reading functions (replace when the BR/L1 communication
+ * channel is in working order)
+ */
+
+
+/* generate a charater in [0-9A-Z]; if an "extra" character is
+ * specified (such as '_'), include it as one of the possibilities.
+ */
+char random_eeprom_ch( char extra ) 
+{
+    char ch;
+    int modval = 36;
+    if( extra )
+	modval++;
+    
+    ch = rtc_time() % modval;
+
+    if( ch < 10 )
+        ch += '0';
+    else if( ch >= 10 && ch < 36 )
+	ch += ('A' - 10);
+    else
+	ch = extra;
+
+    return ch;
+}
+
+/* create a part number of the form xxx-xxxx-xxx.
+ * It may be important later to generate different
+ * part numbers depending on the component we're
+ * supposed to be "reading" from, so the component
+ * paramter is provided.
+ */
+void fake_a_part_number( char *buf, int component )
+{
+    int i;
+    switch( component ) {
+
+    /* insert component-specific routines here */
+
+    case C_BRICK:
+	strcpy( buf, "030-1266-001" );
+	break;
+    default:
+        for( i = 0; i < 12; i++ ) {
+	    if( i == 3 || i == 8 )
+	        buf[i] = '-';
+	    else
+	        buf[i] = random_eeprom_ch(0);
+        }
+    }
+}
+
+
+/* create a six-character serial number */
+void fake_a_serial_number( char *buf, uint64_t ser )
+{
+    int i;
+    static const char hexchars[] = "0123456789ABCDEF";
+
+    if (ser) {
+	for( i = 5; i >=0; i-- ) {
+	    buf[i] = hexchars[ser & 0xf];
+	    ser >>= 4;
+	}
+    }
+    else {
+	for( i = 0; i < 6; i++ )
+	    buf[i] = random_eeprom_ch(0);
+    }
+}
+
+
+void fake_a_product_name( uchar_t *format, char* buf, int component )
+{
+    switch( component & BRICK_MASK ) {
+
+    case C_BRICK:
+	if( component & SUBORD_MASK ) {
+	    strcpy( buf, "C_BRICK_SUB" );
+	    *format = 0xCB;
+	}
+	else {
+	    strcpy( buf, "IP35" );
+	    *format = 0xC4;
+	}
+	break;
+
+    case R_BRICK:
+        if( component & SUBORD_MASK ) {
+            strcpy( buf, "R_BRICK_SUB" );
+            *format = 0xCB;
+        }
+        else {
+            strcpy( buf, "R_BRICK" );
+            *format = 0xC7;
+        }
+        break;
+
+    case IO_BRICK:
+        if( component & SUBORD_MASK ) {
+            strcpy( buf, "IO_BRICK_SUB" );
+            *format = 0xCC;
+        }
+        else {
+            strcpy( buf, "IO_BRICK" );
+            *format = 0xC8;
+        }
+        break;
+
+    default:
+	strcpy( buf, "UNK_DEVICE" );
+	*format = 0xCA;
+    }
+}
+
+
+
+int fake_an_eeprom_record( eeprom_brd_record_t *buf, int component, 
+			   uint64_t ser )
+{
+    eeprom_board_ia_t *board;
+    eeprom_chassis_ia_t *chassis;
+    int i, cs;
+
+    board = buf->board_ia;
+    chassis = buf->chassis_ia;
+
+    if( !(component & SUBORD_MASK) ) {
+	if( !chassis )
+	    return EEP_PARAM;
+	chassis->format = 0;
+	chassis->length = 5;
+	chassis->type = 0x17;
+
+	chassis->part_num_tl = 0xCC;
+	fake_a_part_number( chassis->part_num, component );
+	chassis->serial_num_tl = 0xC6;
+	fake_a_serial_number( chassis->serial_num, ser );
+
+	cs = chassis->format + chassis->length + chassis->type
+	    + chassis->part_num_tl + chassis->serial_num_tl;
+	for( i = 0; i < (chassis->part_num_tl & FIELD_LENGTH_MASK); i++ )
+	    cs += chassis->part_num[i];
+	for( i = 0; i < (chassis->serial_num_tl & FIELD_LENGTH_MASK); i++ )
+	    cs += chassis->serial_num[i];
+	chassis->checksum = 256 - (cs % 256);
+    }
+
+    if( !board )
+	return EEP_PARAM;
+    board->format = 0;
+    board->length = 10;
+    board->language = 0;
+    board->mfg_date = 1789200; /* noon, 5/26/99 */
+    board->manuf_tl = 0xC3;
+    strcpy( board->manuf, "SGI" );
+
+    fake_a_product_name( &(board->product_tl), board->product, component );
+
+    board->serial_num_tl = 0xC6;
+    fake_a_serial_number( board->serial_num, ser );
+
+    board->part_num_tl = 0xCC;
+    fake_a_part_number( board->part_num, component );
+
+    board->board_rev_tl = 0xC2;
+    board->board_rev[0] = '0';
+    board->board_rev[1] = '1';
+
+    board->eeprom_size_tl = 0x01;
+    board->eeprom_size = 1;
+
+    board->temp_waiver_tl = 0xC2;
+    board->temp_waiver[0] = '0';
+    board->temp_waiver[1] = '1';
+
+    cs = board->format + board->length + board->language
+	+ (board->mfg_date & 0xFF)
+	+ (board->mfg_date & 0xFF00)
+	+ (board->mfg_date & 0xFF0000)
+	+ board->manuf_tl + board->product_tl + board->serial_num_tl
+	+ board->part_num_tl + board->board_rev_tl
+	+ board->board_rev[0] + board->board_rev[1]
+	+ board->eeprom_size_tl + board->eeprom_size + board->temp_waiver_tl
+	+ board->temp_waiver[0] + board->temp_waiver[1];
+    for( i = 0; i < (board->manuf_tl & FIELD_LENGTH_MASK); i++ )
+	cs += board->manuf[i];
+    for( i = 0; i < (board->product_tl & FIELD_LENGTH_MASK); i++ )
+	cs += board->product[i];
+    for( i = 0; i < (board->serial_num_tl & FIELD_LENGTH_MASK); i++ )
+	cs += board->serial_num[i];
+    for( i = 0; i < (board->part_num_tl & FIELD_LENGTH_MASK); i++ )
+	cs += board->part_num[i];
+    
+    board->checksum = 256 - (cs % 256);
+
+    return EEP_OK;
+}
+
+#define EEPROM_CHUNKSIZE	64
+
+#if defined(EEPROM_DEBUG)
+#define RETURN_ERROR							\
+{									\
+    printk( "read_ia error return, component 0x%x, line %d"		\
+	    ", address 0x%x, ia code 0x%x\n",				\
+	    l1_compt, __LINE__, sc->subch[subch].target, ia_code );	\
+    return EEP_L1;							\
+}
+
+#else
+#define RETURN_ERROR	return(EEP_L1)
+#endif
+
+int read_ia( l1sc_t *sc, int subch, int l1_compt, 
+	     int ia_code, char *eep_record )
+{
+    char msg[BRL1_QSIZE]; 	   /* message buffer */
+    int len;              	   /* number of bytes used in message buffer */
+    int ia_len = EEPROM_CHUNKSIZE; /* remaining bytes in info area */
+    int offset = 0;                /* current offset into info area */
+
+    if ( IS_RUNNING_ON_SIMULATOR() )
+	return EEP_L1;
+
+    BZERO( msg, BRL1_QSIZE );
+
+    /* retrieve EEPROM data in 64-byte chunks
+     */
+
+    while( ia_len )
+    {
+	/* fill in msg with opcode & params */
+	if( (len = sc_construct_msg( sc, subch, msg, BRL1_QSIZE,
+				     L1_ADDR_TASK_GENERAL,
+				     L1_REQ_EEPROM, 8,
+				     L1_ARG_INT, l1_compt,
+				     L1_ARG_INT, ia_code,
+				     L1_ARG_INT, offset,
+				     L1_ARG_INT, ia_len )) < 0 )
+	{
+	    RETURN_ERROR;
+	}
+
+	/* send the request to the L1 */
+
+	if( sc_command( sc, subch, msg, msg, &len ) ) {
+	    RETURN_ERROR;
+	}
+
+	/* check response */
+	if( sc_interpret_resp( msg, 5, 
+			       L1_ARG_INT, &ia_len,
+			       L1_ARG_UNKNOWN, &len, eep_record ) < 0 )
+	{
+	    RETURN_ERROR;
+	}
+
+	if( ia_len > EEPROM_CHUNKSIZE )
+	    ia_len = EEPROM_CHUNKSIZE;
+
+	eep_record += EEPROM_CHUNKSIZE;
+	offset += EEPROM_CHUNKSIZE;
+    }
+
+    return EEP_OK;
+}
+
+
+int read_spd( l1sc_t *sc, int subch, int l1_compt,
+	      eeprom_spd_u *spd )
+{
+    char msg[BRL1_QSIZE]; 	    /* message buffer */
+    int len;              	    /* number of bytes used in message buffer */
+    int resp;                       /* l1 response code */
+    int spd_len = EEPROM_CHUNKSIZE; /* remaining bytes in spd record */
+    int offset = 0;		    /* current offset into spd record */
+    char *spd_p = spd->bytes;	    /* "thumb" for writing to spd */
+
+    if ( IS_RUNNING_ON_SIMULATOR() )
+	return EEP_L1;
+
+    BZERO( msg, BRL1_QSIZE );
+
+    /* retrieve EEPROM data in 64-byte chunks
+     */
+
+    while( spd_len )
+    {
+	/* fill in msg with opcode & params */
+	if( (len = sc_construct_msg( sc, subch, msg, BRL1_QSIZE,
+				     L1_ADDR_TASK_GENERAL,
+				     L1_REQ_EEPROM, 8,
+				     L1_ARG_INT, l1_compt,
+				     L1_ARG_INT, L1_EEP_SPD,
+				     L1_ARG_INT, offset,
+				     L1_ARG_INT, spd_len )) < 0 )
+	{
+	    return( EEP_L1 );
+	}
+
+	/* send the request to the L1 */
+	if( sc_command( sc, subch, msg, msg, &len ) ) {
+	    return( EEP_L1 );
+	}
+
+	/* check response */
+	if( (resp = sc_interpret_resp( msg, 5, 
+			       L1_ARG_INT, &spd_len,
+			       L1_ARG_UNKNOWN, &len, spd_p )) < 0 )
+	{
+            /*
+             * translate l1 response code to eeprom.c error codes:
+             * The L1 response will be L1_RESP_NAVAIL if the spd
+             * can't be read (i.e. the spd isn't physically there). It will
+             * return L1_RESP_INVAL if the spd exists, but fails the checksum
+             * test because the eeprom wasn't programmed, programmed incorrectly,
+             * or corrupted. L1_RESP_NAVAIL indicates the eeprom is likely not present,
+             * whereas L1_RESP_INVAL indicates the eeprom is present, but the data is
+             * invalid.
+             */
+            if(resp == L1_RESP_INVAL) {
+                resp = EEP_BAD_CHECKSUM;
+            } else {
+                resp = EEP_L1;
+            }
+            return( resp );
+	}
+
+	if( spd_len > EEPROM_CHUNKSIZE )
+	    spd_len = EEPROM_CHUNKSIZE;
+
+	spd_p += EEPROM_CHUNKSIZE;
+	offset += EEPROM_CHUNKSIZE;
+    }
+    return EEP_OK;
+}
+
+
+int read_chassis_ia( l1sc_t *sc, int subch, int l1_compt,
+		     eeprom_chassis_ia_t *ia )
+{
+    char eep_record[512];          /* scratch area for building up info area */
+    char *eep_rec_p = eep_record;  /* thumb for moving through eep_record */
+    int checksum = 0;              /* use to verify eeprom record checksum */
+    int i;
+
+    /* Read in info area record from the L1.
+     */
+    if( read_ia( sc, subch, l1_compt, L1_EEP_CHASSIS, eep_record )
+	!= EEP_OK )
+    {
+	return EEP_L1;
+    }
+
+    /* Now we've got the whole info area.  Transfer it to the data structure.
+     */
+
+    eep_rec_p = eep_record;
+    ia->format = *eep_rec_p++;
+    ia->length = *eep_rec_p++;
+    if( ia->length == 0 ) {
+	/* since we're using 8*ia->length-1 as an array index later, make
+	 * sure it's sane.
+	 */
+	db_printf(( "read_chassis_ia: eeprom length byte of ZERO\n" ));
+	return EEP_L1;
+    }
+    ia->type = *eep_rec_p++;
+   
+    ia->part_num_tl = *eep_rec_p++;
+
+    (void)BCOPY( eep_rec_p, ia->part_num, (ia->part_num_tl & FIELD_LENGTH_MASK) );
+    eep_rec_p += (ia->part_num_tl & FIELD_LENGTH_MASK);
+
+    ia->serial_num_tl = *eep_rec_p++;
+
+    BCOPY( eep_rec_p, ia->serial_num, 
+	   (ia->serial_num_tl & FIELD_LENGTH_MASK) );
+    eep_rec_p += (ia->serial_num_tl & FIELD_LENGTH_MASK);
+
+    ia->checksum = eep_record[(8 * ia->length) - 1];
+
+    /* verify checksum */
+    eep_rec_p = eep_record;
+    checksum = 0;
+    for( i = 0; i < (8 * ia->length); i++ ) {
+	checksum += *eep_rec_p++;
+    }
+
+    if( (checksum & 0xff) != 0 )
+    {
+	db_printf(( "read_chassis_ia: bad checksum\n" ));
+	db_printf(( "read_chassis_ia: target 0x%x  uart 0x%lx\n",
+			   sc->subch[subch].target, sc->uart ));
+	return EEP_BAD_CHECKSUM;
+    }
+
+    return EEP_OK;
+}
+
+
+int read_board_ia( l1sc_t *sc, int subch, int l1_compt,
+		   eeprom_board_ia_t *ia )
+{
+    char eep_record[512];          /* scratch area for building up info area */
+    char *eep_rec_p = eep_record;  /* thumb for moving through eep_record */
+    int checksum = 0;              /* running checksum total */
+    int i;
+
+    BZERO( ia, sizeof( eeprom_board_ia_t ) );
+
+    /* Read in info area record from the L1.
+     */
+    if( read_ia( sc, subch, l1_compt, L1_EEP_BOARD, eep_record )
+	!= EEP_OK )
+    {
+	db_printf(( "read_board_ia: error reading info area from L1\n" ));
+	return EEP_L1;
+    }
+
+     /* Now we've got the whole info area.  Transfer it to the data structure.
+      */
+
+    eep_rec_p = eep_record;
+    ia->format = *eep_rec_p++;
+    ia->length = *eep_rec_p++;
+    if( ia->length == 0 ) {
+	/* since we're using 8*ia->length-1 as an array index later, make
+	 * sure it's sane.
+	 */
+	db_printf(( "read_board_ia: eeprom length byte of ZERO\n" ));
+	return EEP_L1;
+    }
+    ia->language = *eep_rec_p++;
+    
+    ia->mfg_date = eeprom_xlate_board_mfr_date( (uchar_t *)eep_rec_p );
+    eep_rec_p += 3;
+
+    ia->manuf_tl = *eep_rec_p++;
+    
+    BCOPY( eep_rec_p, ia->manuf, (ia->manuf_tl & FIELD_LENGTH_MASK) );
+    eep_rec_p += (ia->manuf_tl & FIELD_LENGTH_MASK);
+
+    ia->product_tl = *eep_rec_p++;
+    
+    BCOPY( eep_rec_p, ia->product, (ia->product_tl & FIELD_LENGTH_MASK) );
+    eep_rec_p += (ia->product_tl & FIELD_LENGTH_MASK);
+
+    ia->serial_num_tl = *eep_rec_p++;
+    
+    BCOPY(eep_rec_p, ia->serial_num, (ia->serial_num_tl & FIELD_LENGTH_MASK));
+    eep_rec_p += (ia->serial_num_tl & FIELD_LENGTH_MASK);
+
+    ia->part_num_tl = *eep_rec_p++;
+
+    BCOPY( eep_rec_p, ia->part_num, (ia->part_num_tl & FIELD_LENGTH_MASK) );
+    eep_rec_p += (ia->part_num_tl & FIELD_LENGTH_MASK);
+
+    eep_rec_p++; /* we do not use the FRU file id */
+    
+    ia->board_rev_tl = *eep_rec_p++;
+    
+    BCOPY( eep_rec_p, ia->board_rev, (ia->board_rev_tl & FIELD_LENGTH_MASK) );
+    eep_rec_p += (ia->board_rev_tl & FIELD_LENGTH_MASK);
+
+    ia->eeprom_size_tl = *eep_rec_p++;
+    ia->eeprom_size = *eep_rec_p++;
+
+    ia->temp_waiver_tl = *eep_rec_p++;
+    
+    BCOPY( eep_rec_p, ia->temp_waiver, 
+	   (ia->temp_waiver_tl & FIELD_LENGTH_MASK) );
+    eep_rec_p += (ia->temp_waiver_tl & FIELD_LENGTH_MASK);
+
+    /* if there's more, we must be reading a main board; get
+     * additional fields
+     */
+    if( ((unsigned char)*eep_rec_p != (unsigned char)EEPROM_EOF) ) {
+
+	ia->ekey_G_tl = *eep_rec_p++;
+	BCOPY( eep_rec_p, (char *)&ia->ekey_G, 
+	       ia->ekey_G_tl & FIELD_LENGTH_MASK );
+	eep_rec_p += (ia->ekey_G_tl & FIELD_LENGTH_MASK);
+	
+	ia->ekey_P_tl = *eep_rec_p++;
+	BCOPY( eep_rec_p, (char *)&ia->ekey_P, 
+	       ia->ekey_P_tl & FIELD_LENGTH_MASK );
+	eep_rec_p += (ia->ekey_P_tl & FIELD_LENGTH_MASK);
+	
+	ia->ekey_Y_tl = *eep_rec_p++;
+	BCOPY( eep_rec_p, (char *)&ia->ekey_Y, 
+	       ia->ekey_Y_tl & FIELD_LENGTH_MASK );
+	eep_rec_p += (ia->ekey_Y_tl & FIELD_LENGTH_MASK);
+	
+	/* 
+	 * need to get a couple more fields if this is an I brick 
+	 */
+	if( ((unsigned char)*eep_rec_p != (unsigned char)EEPROM_EOF) ) {
+
+	    ia->mac_addr_tl = *eep_rec_p++;
+	    BCOPY( eep_rec_p, ia->mac_addr, 
+		   ia->mac_addr_tl & FIELD_LENGTH_MASK );
+	    eep_rec_p += (ia->mac_addr_tl & FIELD_LENGTH_MASK);
+	    
+	    ia->ieee1394_cfg_tl = *eep_rec_p++;
+	    BCOPY( eep_rec_p, ia->ieee1394_cfg,
+		   ia->ieee1394_cfg_tl & FIELD_LENGTH_MASK );
+	    
+	}
+    }
+
+    ia->checksum = eep_record[(ia->length * 8) - 1];
+
+    /* verify checksum */
+    eep_rec_p = eep_record;
+    checksum = 0;
+    for( i = 0; i < (8 * ia->length); i++ ) {
+	checksum += *eep_rec_p++;
+    }
+
+    if( (checksum & 0xff) != 0 )
+    {
+	db_printf(( "read_board_ia: bad checksum\n" ));
+	db_printf(( "read_board_ia: target 0x%x  uart 0x%lx\n",
+		    sc->subch[subch].target, sc->uart ));
+	return EEP_BAD_CHECKSUM;
+    }
+
+    return EEP_OK;
+}
+
+
+int _cbrick_eeprom_read( eeprom_brd_record_t *buf, l1sc_t *scp,
+			 int component )
+{
+    int r;
+    uint64_t uid = 0;
+#ifdef LOG_GETENV
+    char uid_str[32];
+#endif
+    int l1_compt, subch;
+
+    if ( IS_RUNNING_ON_SIMULATOR() )
+	return EEP_L1;
+
+    /* make sure we're targeting a cbrick */
+    if( !(component & C_BRICK) )
+	return EEP_PARAM;
+
+    /* If the promlog variable pointed to by IP27LOG_OVNIC is set,
+     * use that value for the cbrick UID rather than the EEPROM
+     * serial number.
+     */
+#ifdef LOG_GETENV
+    if( ip27log_getenv( scp->nasid, IP27LOG_OVNIC, uid_str, "0", 0 ) >= 0 )
+    {
+	db_printf(( "_cbrick_eeprom_read: "
+		    "Overriding UID with environment variable %s\n", 
+		    IP27LOG_OVNIC ));
+	uid = strtoull( uid_str, NULL, 0 );
+    }
+#endif
+
+    if( (subch = sc_open( scp, L1_ADDR_LOCAL )) < 0 )
+	return EEP_L1;
+
+    if((component & C_DIMM) == C_DIMM) {
+        l1_compt = L1_EEP_DIMM(component & COMPT_MASK);
+        r = read_spd(scp,subch,l1_compt, buf->spd);
+        sc_close(scp,subch);
+        return(r);
+    }
+
+    switch( component )
+    {
+      case C_BRICK:
+	/* c-brick motherboard */
+	l1_compt = L1_EEP_NODE;
+	r = read_chassis_ia( scp, subch, l1_compt, buf->chassis_ia );
+	if( r != EEP_OK ) {
+	    sc_close( scp, subch );
+	    db_printf(( "_cbrick_eeprom_read: using a fake eeprom record\n" ));
+	    return fake_an_eeprom_record( buf, component, uid );
+	}
+	if( uid ) {
+	    /* If IP27LOG_OVNIC is set, we want to put that value
+	     * in as our UID. */
+	    fake_a_serial_number( buf->chassis_ia->serial_num, uid );
+	    buf->chassis_ia->serial_num_tl = 6;
+	}
+	break;
+
+      case C_PIMM:
+	/* one of the PIMM boards */
+	l1_compt = L1_EEP_PIMM( component & COMPT_MASK );
+	break;
+
+      default:
+	/* unsupported board type */
+	sc_close( scp, subch );
+	return EEP_PARAM;
+    }
+	      
+    r = read_board_ia( scp, subch, l1_compt, buf->board_ia );
+    sc_close( scp, subch );
+    if( r != EEP_OK ) 
+    {
+	db_printf(( "_cbrick_eeprom_read: using a fake eeprom record\n" ));
+	return fake_an_eeprom_record( buf, component, uid );
+    }
+    return EEP_OK;
+}
+
+
+int cbrick_eeprom_read( eeprom_brd_record_t *buf, nasid_t nasid,
+    		        int component )
+{
+    l1sc_t *scp;
+    int local = (nasid == get_nasid());
+
+    if ( IS_RUNNING_ON_SIMULATOR() )
+	return EEP_L1;
+
+    /* If this brick is retrieving its own uid, use the local l1sc_t to
+     * arbitrate access to the l1; otherwise, set up a new one (prom) or
+     * use an existing remote l1sc_t (kernel)
+     */
+    if( local ) {
+	scp = get_l1sc();
+    }
+    else {
+	scp = &NODEPDA( NASID_TO_COMPACT_NODEID(nasid) )->module->elsc;
+    }
+
+    return _cbrick_eeprom_read( buf, scp, component );
+}
+
+
+int iobrick_eeprom_read( eeprom_brd_record_t *buf, nasid_t nasid,
+			 int component )
+{
+    int r;
+    int l1_compt, subch;
+    l1sc_t *scp;
+    int local = (nasid == get_nasid());
+
+    if ( IS_RUNNING_ON_SIMULATOR() )
+	return EEP_L1;
+
+    /* make sure we're talking to an applicable brick */
+    if( !(component & IO_BRICK) ) {
+	return EEP_PARAM;
+    }
+
+    /* If we're talking to this c-brick's attached io brick, use
+     * the local l1sc_t; otherwise, set up a new one (prom) or
+     * use an existing remote l1sc_t (kernel)
+     */
+    if( local ) {
+	scp = get_l1sc();
+    }
+    else {
+	scp = &NODEPDA( NASID_TO_COMPACT_NODEID(nasid) )->module->elsc;
+    }
+
+    if( (subch = sc_open( scp, L1_ADDR_LOCALIO )) < 0 )
+	return EEP_L1;
+
+
+    switch( component )
+    {
+      case IO_BRICK:
+	/* IO brick motherboard */
+	l1_compt = L1_EEP_LOGIC;
+	r = read_chassis_ia( scp, subch, l1_compt, buf->chassis_ia );
+
+	if( r != EEP_OK ) {
+	    sc_close( scp, subch );
+	/*
+	 * Whenever we no longer need to test on hardware
+	 * that does not have EEPROMS, then this can be removed.
+	 */
+	    r = fake_an_eeprom_record( buf, component, rtc_time() );
+	    return r;
+	}
+	break;
+
+      case IO_POWER:
+	/* IO brick power board */
+	l1_compt = L1_EEP_POWER;
+	break;
+
+      default:
+	/* unsupported board type */
+	sc_close( scp, subch );
+	return EEP_PARAM;
+    }
+
+    r = read_board_ia( scp, subch, l1_compt, buf->board_ia );
+    sc_close( scp, subch );
+    if( r != EEP_OK ) {
+	return r;
+    }
+    return EEP_OK;
+}
+
+
+int vector_eeprom_read( eeprom_brd_record_t *buf, nasid_t nasid,
+			net_vec_t path, int component )
+{
+    int r;
+    uint64_t uid = 0;
+    int l1_compt, subch;
+    l1sc_t sc;
+
+    if ( IS_RUNNING_ON_SIMULATOR() )
+	return EEP_L1;
+
+    /* make sure we're targeting an applicable brick */
+    if( !(component & VECTOR) )
+	return EEP_PARAM;
+
+    switch( component & BRICK_MASK )
+    {
+      case R_BRICK:
+	ROUTER_LOCK( path );
+	sc_init( &sc, nasid, path );
+
+	if( (subch = sc_open( &sc, L1_ADDR_LOCAL )) < 0 )
+	{
+	    db_printf(( "vector_eeprom_read: couldn't open subch\n" ));
+	    ROUTER_UNLOCK(path);
+	    return EEP_L1;
+	}
+	switch( component )
+	{
+	  case R_BRICK:
+	    /* r-brick motherboard */
+	    l1_compt = L1_EEP_LOGIC;
+    	    r = read_chassis_ia( &sc, subch, l1_compt, buf->chassis_ia );
+	    if( r != EEP_OK ) {
+		sc_close( &sc, subch );
+		ROUTER_UNLOCK( path );
+		printk( "vector_eeprom_read: couldn't get rbrick eeprom info;"
+			" using current time as uid\n" );
+		uid = rtc_time();
+		db_printf(("vector_eeprom_read: using a fake eeprom record\n"));
+		return fake_an_eeprom_record( buf, component, uid );
+	    }
+	    break;
+
+	  case R_POWER:
+	    /* r-brick power board */
+	    l1_compt = L1_EEP_POWER;
+	    break;
+
+	  default:
+	    /* unsupported board type */
+	    sc_close( &sc, subch );
+	    ROUTER_UNLOCK( path );
+	    return EEP_PARAM;
+	}
+	r = read_board_ia( &sc, subch, l1_compt, buf->board_ia );
+	sc_close( &sc, subch );
+	ROUTER_UNLOCK( path );
+	if( r != EEP_OK ) {
+	    db_printf(( "vector_eeprom_read: using a fake eeprom record\n" ));
+	    return fake_an_eeprom_record( buf, component, uid );
+	}
+	return EEP_OK;
+
+      case C_BRICK:
+	sc_init( &sc, nasid, path );
+	return _cbrick_eeprom_read( buf, &sc, component );
+
+      default:
+	/* unsupported brick type */
+	return EEP_PARAM;
+    }
+}

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)