summaryrefslogtreecommitdiff
path: root/util/ifru.c
diff options
context:
space:
mode:
authorJörg Frings-Fürst <debian@jff-webhosting.net>2014-07-06 18:04:32 +0200
committerJörg Frings-Fürst <debian@jff-webhosting.net>2014-07-06 18:04:32 +0200
commita7f89980e5b3f4b9a74c70dbc5ffe8aabd28be28 (patch)
tree41c4deec1fdfbafd7821b4ca7a9772ac0abd92f5 /util/ifru.c
Imported Upstream version 2.9.3upstream/2.9.3
Diffstat (limited to 'util/ifru.c')
-rwxr-xr-xutil/ifru.c2123
1 files changed, 2123 insertions, 0 deletions
diff --git a/util/ifru.c b/util/ifru.c
new file mode 100755
index 0000000..ba834e6
--- /dev/null
+++ b/util/ifru.c
@@ -0,0 +1,2123 @@
+/*
+ * ifru (was fruconfig.c)
+ *
+ * This tool reads the FRU configuration, and optionally sets the asset
+ * tag field in the FRU data. See IPMI v1.5 spec section 28.
+ * It can use either the Intel /dev/imb driver or VALinux /dev/ipmikcs.
+ *
+ * Author: Andy Cress arcress at users.sourceforge.net
+ *
+ * Copyright (c) 2009 Kontron America, Inc.
+ *
+ * 10/28/02 Andy Cress - created
+ * 12/11/02 Andy Cress v0.9 - disable write until checksum fixed.
+ * 12/13/02 Andy Cress v0.91 - don't abort if cc=c9 in load_fru loop.
+ * 01/27/03 Andy Cress v0.92 - more debug, do checksums,
+ * 01/28/03 Andy Cress v1.0 do writes in small chunks, tested w SCB2 & STL2
+ * 02/19/03 Andy Cress v1.1 also get System GUID
+ * 03/10/03 Andy Cress v1.2 do better bounds checking on FRU size
+ * 04/30/03 Andy Cress v1.3 Board Part# & Serial# reversed
+ * 05/13/03 Andy Cress v1.4 Added Chassis fields
+ * 06/19/03 Andy Cress v1.5 added errno.h (email from Travers Carter)
+ * 05/03/04 Andy Cress v1.6 BladeCenter has no product area, only board area
+ * 05/05/04 Andy Cress v1.7 call ipmi_close before exit,
+ * added WIN32 compile options.
+ * 11/01/04 Andy Cress v1.8 add -N / -R for remote nodes
+ * 12/10/04 Andy Cress v1.9 add gnu freeipmi interface
+ * 01/13/05 Andy Cress v1.10 add logic to scan SDRs for all FRU devices,
+ * and interpret them
+ * 01/17/05 Andy Cress v1.11 decode SPD Manufacturer
+ * 01/21/05 Andy Cress v1.12 format SystemGUID display
+ * 02/03/05 Andy Cress v1.13 fixed fwords bit mask in load_fru,
+ * decode DIMM size from SPD also.
+ * 02/04/05 Andy Cress v1.14 decode FRU Board Mfg DateTime
+ * 03/16/05 Andy Cress v1.15 show Asset Tag Length earlier
+ * 05/24/05 Andy Cress v1.16 only do write_asset if successful show_fru
+ * 06/20/05 Andy Cress v1.17 handle Device SDRs also for ATCA
+ * 08/22/05 Andy Cress v1.18 allow setting Product Serial Number also (-s),
+ * also add -b option to show only baseboard data.
+ * 10/31/06 Andy Cress v1.25 handle 1-char asset/serial strings (avoid c1)
+ */
+/*M*
+Copyright (c) 2009 Kontron America, Inc.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ a.. Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+ b.. Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+ c.. Neither the name of Kontron nor the names of its contributors
+ may be used to endorse or promote products derived from this software
+ without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *M*/
+#ifdef WIN32
+#include <windows.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "getopt.h"
+#elif defined(DOS)
+#include <dos.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "getopt.h"
+#else
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+#if defined(HPUX)
+/* getopt is defined in stdio.h */
+#elif defined(MACOS)
+/* getopt is defined in unistd.h */
+#include <unistd.h>
+#else
+#include <errno.h>
+#endif
+#endif
+#include <time.h>
+
+#include "ipmicmd.h"
+#include "ipicmg.h"
+#include "oem_intel.h"
+#include "ifru.h"
+
+#define PICMG_CHILD 1
+#define MIN_SDR_SZ 8
+#ifndef URNLOOPS
+#define URNLOOPS 1000
+#endif
+extern int get_BiosVersion(char *str);
+extern int get_SystemGuid(uchar *guid);
+extern void fmt_time(time_t etime, char *buf, int bufsz); /*see ievents.c*/
+extern int get_LastError( void ); /* ipmilan.c */
+extern int GetSDRRepositoryInfo(int *nret, int *fdev); /*isensor.h*/
+#ifdef METACOMMAND
+extern int ipmi_kontronoem_main(void * intf, int argc, char ** argv);
+#endif
+
+static char *progname = "ifru";
+static char *progver = "2.93";
+static int vend_id = 0;
+static int prod_id = 0;
+static char fdebug = 0;
+static char fpicmg = 0;
+static char fonlybase = 0;
+static char fonlyhsc = 0;
+static int fwritefru = 0;
+static int fdevsdrs = 0;
+static char fshowlen = 0;
+static char ftestshow = 0;
+// static char fgetfru = 0;
+static char fdoanyway = 0;
+static char fprivset = 0;
+static char fset_mc = 0;
+static char fcanonical = 0;
+static char foemkontron = 0;
+static char fbasefru = 1;
+static char fdump = 0;
+static char frestore = 0;
+static char fchild = 0; /* =1 follow child MCs if picmg bladed*/
+static char do_systeminfo = 1;
+static char do_guid = 1;
+static char bdelim = ':';
+static uchar bmc_sa = BMC_SA; /*defaults to 0x20*/
+static uchar guid[17] = "";
+static char *binfile = NULL;
+static uchar *frubuf = NULL;
+#define FIELD_LEN 20
+#define SZ_PRODAREA 520 /* product area max is 8*32 + 3 = 259 mod 8 = 264 */
+static int sfru = 0;
+static int asset_offset = -1;
+static int asset_len = 0;
+static int sernum_offset = -1;
+static int sernum_len = 0;
+static int prodver_offset = -1;
+static int prodver_len = 0;
+static int chassis_offset = -1;
+static int chassis_len = 0;
+static char asset_tag[FIELD_LEN] = {0};
+static char serial_num[FIELD_LEN] = {0};
+static char prod_ver[FIELD_LEN] = {0};
+static char chassis_name[FIELD_LEN] = {0};
+static char ps_prod[FIELD_LEN] = {0};
+static int maxprod = 0;
+static int ctype;
+static uchar g_bus = PUBLIC_BUS;
+static uchar g_sa = 0;
+static uchar g_lun = BMC_LUN;
+static uchar g_addrtype = ADDR_SMI;
+static uchar g_fruid = 0; /* default to fruid 0 */
+static uchar g_frutype = 0;
+static uchar lastfru[3] = {0,0,0};
+/* g_frutype values (detected), see also FruTypeString
+ * --TAG---- FRU IPMB
+ * Baseboard = 0x0c 0x07
+ * PowerSply = 0x0a
+ * PowerCage = 0x15
+ * DIMM = 0x20
+ * HotSwapCt = 0x0f
+ * ME = 0x2e
+ * SysInfo = 0x40
+ * Component = * * (all others)
+ */
+#ifdef METACOMMAND
+extern int verbose;
+extern int sdr_get_reservation(uchar *res_id, int fdev);
+#else
+static int verbose;
+static int sdr_get_reservation(uchar *res_id, int fdev) { return(-1); }
+#endif
+
+#define ERR_LENMAX -7 /*same as LAN_ERR_BADLENGTH */
+#define ERR_LENMIN -10 /*same as LAN_ERR_TOO_SHORT */
+#define ERR_OTHER -13 /*same as LAN_ERR_OTHER */
+
+#define STRING_DATA_TYPE_BINARY 0x00
+#define STRING_DATA_TYPE_BCD_PLUS 0x01
+#define STRING_DATA_TYPE_SIX_BIT_ASCII 0x02
+#define STRING_DATA_TYPE_LANG_DEPENDENT 0x03
+
+#define FRUCHUNK_SZ 16
+#define FRU_END 0xC1
+#define FRU_EMPTY_FIELD 0xC0
+#define FRU_TYPE_MASK 0xC0
+#define FRU_LEN_MASK 0x3F
+
+#define NUM_BOARD_FIELDS 6
+#define NUM_PRODUCT_FIELDS 8
+#define NUM_CHASSIS_FIELDS 3
+#define MAX_CTYPE 26
+char *ctypes[MAX_CTYPE] = { "", "Other", "Unknown", "Desktop",
+ "Low Profile Desktop", "Pizza Box", "Mini-Tower", "Tower",
+ "Portable", "Laptop", "Notebook", "Handheld", "Docking Station",
+ "All-in-One", "Sub-Notebook", "Space-saving", "Lunch Box",
+ "Main Server Chassis", "Expansion Chassis", "SubChassis",
+ "Bus Expansion Chassis", "Peripheral Chassis",
+ "RAID Chassis", /*0x17=23.*/ "Rack-Mount Chassis",
+ "New24" , "New25"};
+ /* what about bladed chassies? */
+char *ctype_hdr =
+"Chassis Type ";
+char *chassis[NUM_CHASSIS_FIELDS] = {
+"Chassis Part Number ",
+"Chassis Serial Num ",
+"Chassis OEM Field " };
+char *board[NUM_BOARD_FIELDS] = {
+"Board Manufacturer ",
+"Board Product Name ",
+"Board Serial Number ",
+"Board Part Number ",
+"Board FRU File ID ",
+"Board OEM Field " };
+char *product[NUM_PRODUCT_FIELDS] = {
+"Product Manufacturer",
+"Product Name ",
+"Product Part Number ",
+"Product Version ",
+"Product Serial Num ",
+"Product Asset Tag ",
+"Product FRU File ID ",
+"Product OEM Field " };
+char *asset_hdr =
+" Asset Tag Length ";
+
+#if 0
+typedef struct {
+ uchar len :6;
+ uchar type:2;
+ } TYPE_LEN; /*old, assumes lo-hi byte order*/
+#else
+typedef struct {
+ uchar len;
+ uchar type;
+ } TYPE_LEN;
+#endif
+
+void
+free_fru(uchar *pfrubuf)
+{
+ if (pfrubuf != NULL) {
+ if (frubuf != NULL) free(frubuf);
+ frubuf = NULL;
+ }
+ return;
+}
+
+int
+load_fru(uchar sa, uchar frudev, uchar frutype, uchar **pfrubuf)
+{
+ int ret = 0;
+ uchar indata[16];
+ uchar resp[18];
+ int sresp;
+ uchar cc;
+ int sz;
+ char fwords;
+ ushort fruoff = 0;
+ int i, rv;
+ int chunk;
+
+ if (pfrubuf == NULL) return(ERR_BAD_PARAM);
+ *pfrubuf = NULL;
+ indata[0] = frudev;
+ sresp = sizeof(resp);
+ if (fdebug) printf("load_fru: sa=%02x, frudev=%02x, addrtype=%d\n",
+ sa,frudev,g_addrtype);
+ ret = ipmi_cmd_mc(GET_FRU_INV_AREA,indata,1,resp,&sresp,&cc,fdebug);
+ if (fdebug) printf("load_fru: inv ret=%d, cc=%x, resp=%02x %02x %02x\n",
+ ret,cc, resp[0], resp[1], resp[2]);
+ if (ret != 0) return(ret);
+ if (cc != 0) { ret = (cc & 0x00ff); return(ret); }
+
+ sz = resp[0] + (resp[1] << 8);
+ if (resp[2] & 0x01) { fwords = 1; sz = sz * 2; }
+ else fwords = 0;
+
+ frubuf = malloc(sz);
+ if (frubuf == NULL) return(get_errno());
+ *pfrubuf = frubuf;
+ sfru = sz;
+
+ /* Loop on READ_FRU_DATA */
+ for (i = 0, chunk=FRUCHUNK_SZ; i < sz; i+=chunk)
+ {
+ if ((i+chunk) >= sz) chunk = sz - i;
+ indata[0] = frudev; /* FRU Device ID */
+ if (fwords) {
+ indata[3] = chunk / 2;
+ fruoff = (i/2);
+ } else {
+ indata[3] = (uchar)chunk;
+ fruoff = (ushort)i;
+ }
+ indata[1] = fruoff & 0x00FF;
+ indata[2] = (fruoff & 0xFF00) >> 8;
+ sresp = sizeof(resp);
+ ret = ipmi_cmd_mc(READ_FRU_DATA,indata,4,resp,&sresp,&cc,fdebug);
+ if (ret != 0) break;
+ else if (cc != 0) {
+ if (i == 0) ret = cc & 0x00ff;
+ if (fdebug) printf("read_fru[%d]: ret = %d cc = %x\n",i,ret,cc);
+ break;
+ }
+ memcpy(&frubuf[i],&resp[1],chunk);
+ }
+
+ if ((frudev == 0) && (sa == bmc_sa) && do_guid)
+ { /*main system fru, so get GUID*/
+ sresp = sizeof(resp);
+ rv = ipmi_cmd_mc(GET_SYSTEM_GUID,indata,0,resp,&sresp,&cc,fdebug);
+ if (fdebug) printf("system_guid: ret = %d, cc = %x\n",rv,cc);
+ if (rv == 0) rv = cc;
+ if ((rv != 0) && !is_remote()) { /* get UUID from SMBIOS */
+ cc = 0; sresp = 16;
+ rv = get_SystemGuid(resp);
+ if (fdebug) printf("get_SystemGuid: ret = %d\n",rv);
+ }
+ if (rv == 0 && cc == 0) {
+ if (fdebug) {
+ printf("system guid (%d): ",sresp);
+ for (i=0; i<16; i++) printf("%02x ",resp[i]);
+ printf("\n");
+ }
+ memcpy(&guid,&resp,16);
+ guid[16] = 0;
+ } else {
+ printf("WARNING: GetSystemGuid error %d, %s\n",rv,decode_rv(rv));
+ /*do not pass this error upstream*/
+ }
+ } /*endif*/
+ return(ret);
+}
+
+static void decode_string(unsigned char type,
+ unsigned char language_code,
+ unsigned char *source,
+ int slen,
+ char *target,
+ int tsize)
+{
+ static const char bcd_plus[] = "0123456789 -.:,_";
+ unsigned char *s = &source[0];
+ int len, i, j, k;
+ union { uint32_t bits; char chars[4]; } u;
+
+ if (slen == 0 || slen == 1) return;
+ switch(type) {
+ case STRING_DATA_TYPE_BINARY: /* 00: binary/unspecified */
+ len = (slen*2); break; /* hex dump -> 2x length */
+ case STRING_DATA_TYPE_SIX_BIT_ASCII: /*type 2 6-bit ASCII*/
+ /* 4 chars per group of 1-3 bytes */
+ len = ((((slen+2)*4)/3) & ~3); break;
+ case STRING_DATA_TYPE_LANG_DEPENDENT: /* 03 language dependent */
+ case STRING_DATA_TYPE_BCD_PLUS: /* 01b: BCD plus */
+ default:
+ len = slen; break;
+ }
+ if (len >= tsize) len = tsize - 1;
+ memset(target, 0, len);
+ if (type == STRING_DATA_TYPE_BCD_PLUS) { /*type 1: BCD plus*/
+ for (k=0; k<len; k++)
+ target[k] = bcd_plus[(s[k] & 0x0f)];
+ target[k] = '\0';
+ } else if (type == STRING_DATA_TYPE_SIX_BIT_ASCII) { /*type 2: 6-bit ASCII*/
+ for (i=j=0; i<slen; i+=3) {
+ u.bits = 0;
+ k = ((slen-i) < 3 ? (slen-i) : 3);
+#if WORDS_BIGENDIAN
+ u.chars[3] = s[i];
+ u.chars[2] = (k > 1 ? s[i+1] : 0);
+ u.chars[1] = (k > 2 ? s[i+2] : 0);
+#define CHAR_IDX 3
+#else
+ memcpy((void *)&u.bits, &s[i], k);
+#define CHAR_IDX 0
+#endif
+ for (k=0; k<4; k++) {
+ target[j++] = ((u.chars[CHAR_IDX] & 0x3f) + 0x20);
+ u.bits >>= 6;
+ }
+ }
+ target[j] = '\0';
+ } else if (type == STRING_DATA_TYPE_LANG_DEPENDENT) { /*type 3*/
+ if ((language_code == 0x00) || (language_code == 0x25) ||
+ (language_code == 0x19)) {
+ memcpy(target, source, len);
+ target[len] = 0;
+ } else {
+ printf("Language 0x%x dependent decode not supported\n",language_code);
+ }
+ } else if (type == STRING_DATA_TYPE_BINARY) { /* 00: binary/unspecified */
+ strncpy(target, buf2str(s, slen), len);
+ target[len] = '\0'; /* add end-of-string char */
+ } else { /* other */
+ printf("Unable to decode type 0x%.2x\n",type);
+ }
+ return;
+}
+
+uchar calc_cksum(uchar *pbuf,int len)
+{
+ int i;
+ uchar cksum;
+ uchar sum = 0;
+
+ for (i = 0; i < len; i++) sum += pbuf[i];
+ cksum = 0 - sum;
+ return(cksum);
+}
+
+static void dumpbuf(uchar *pbuf,int sz)
+{
+ uchar line[17];
+ uchar a;
+ int i, j;
+
+ line[0] = 0; line[16] = 0;
+ j = 0;
+ for (i = 0; i < sz; i++) {
+ if (i % 16 == 0) { j = 0; printf("%s\n %04d: ",line,i); }
+ a = pbuf[i];
+ if (a < 0x20 || a > 0x7f) a = '.';
+ line[j++] = a;
+ printf("%02x ",pbuf[i]);
+ }
+ line[j] = 0;
+ printf("%s\n",line);
+ return;
+}
+
+#define NSPDMFG 10
+static struct {
+ uchar id; char *str;
+} spd_mfg[NSPDMFG] = { /* see JEDEC JEP106 doc */
+{ 0x02, "AMI" },
+{ 0x04, "Fujitsu" },
+{ 0x15, "Philips Semi" },
+{ 0x1c, "Mitsubishi" },
+{ 0x2c, "Micron" },
+{ 0x89, "Intel" },
+{ 0x98, "Kingston" },
+{ 0xA8, "US Modular" },
+{ 0xc1, "Infineon" },
+{ 0xce, "Samsung" }
+};
+
+int ValidTL(uchar typelen)
+{
+ if (vend_id != VENDOR_INTEL) return 1;
+ if ((typelen & 0xC0) == 0xC0) return 1; /* validate C type */
+ if ((typelen & 0x00) == 0x00) return 1; /* validate 0 type too */
+ else return 0;
+}
+
+#define SYS_FRUTYPE 0x40
+char * FruTypeString(uchar frutype, uchar dev)
+{
+ char *pstr;
+ switch (frutype) {
+ case 0x07: /*IPMB*/
+ case 0x0c: /*FRU*/
+ if (dev == 0) pstr = "Baseboard";
+ else pstr = "Board ";
+ break;
+ case 0x0a: pstr = "PowerSply"; break; /*FRU*/
+ case 0x15: pstr = "PowerCage"; break; /*FRU*/
+ case 0x20: pstr = "DIMM "; break; /*FRU*/
+ case 0x0f: pstr = "HotSwapCt"; break; /*IPMB*/
+ case 0x2e: pstr = "ME "; break; /*IPMB*/
+ case SYS_FRUTYPE: pstr = "SysInfo "; break; /*SysInfo*/
+ default: pstr = "Component"; break;
+ }
+ return(pstr);
+}
+
+static int ChkOverflow(int idx, int sz, uchar len)
+{
+ if (idx >= sz) {
+ printf(" ERROR - FRU Buffer Overflow (%d >= %d), last len=%d\n",
+ idx,sz,len);
+ return 1; /*overflow error*/
+ }
+ return 0;
+}
+
+static void
+show_spd(uchar *spd, int lenspd, uchar frudev, uchar frutype)
+{
+ int sz;
+ char *pstr;
+ int sdcap, ranks, busw, sdw, totcap;
+ uchar srev, mfgid, mrev1, mrev2, yr, wk;
+ uchar isdcap, iranks, ibusw, isdw;
+ uchar iparity, iser, ipart, irev;
+ int i;
+ char devstr[24];
+
+ /* Sample SPD Headers:
+ 80 08 07 0c 0a 01 48 00 (DDR SDRAM DIMM)
+ 92 10 0b 01 03 1a 02 00 (DDR3 SDRAM DIMMs) */
+ sz = spd[0]; /* sz should == lenspd */
+ srev = spd[1]; /* SPD Rev: 8 for DDR, 10 for DDR3 SPD spec */
+ if (fcanonical) devstr[0] = 0; /*default is empty string*/
+ else sprintf(devstr,"[%s, %02x] ",FruTypeString(frutype,frudev),frudev);
+ printf("%sMemory SPD Size %c %d\n",
+ devstr,bdelim,lenspd);
+ switch (spd[2]) {
+ case 0x02: pstr = "EDO"; break;
+ case 0x04: pstr = "SDRAM"; break;
+ case 0x05: pstr = "ROM"; break;
+ case 0x06: pstr = "DDR SGRAM"; break;
+ case 0x07: pstr = "DDR SDRAM"; break;
+ case 0x08: pstr = "DDR2 SDRAM"; break;
+ case 0x09: pstr = "DDR2 SDRAM FB"; break;
+ case 0x0A: pstr = "DDR2 SDRAM FB PROBE"; break;
+ case 0x0B: pstr = "DDR3 SDRAM"; break;
+ default: pstr = "DRAM"; break; /*FPM DRAM or other*/
+ }
+ printf("%sMemory Type %c %s\n", devstr,bdelim,pstr);
+ if (srev < 0x10) { /*used rev 0.8 of SPD spec*/
+ printf("%sModule Density %c %d MB per bank\n",
+ devstr,bdelim, (spd[31] * 4));
+ printf("%sModule Banks %c %d banks\n",
+ devstr,bdelim,spd[5]);
+ printf("%sModule Rows, Cols %c %d rows, %d cols\n",
+ devstr,bdelim, spd[3], spd[4]);
+ iparity = spd[11];
+ mfgid = spd[64];
+ } else { /* use 1.0 SPD spec with DDR3 */
+ /* SDRAM CAPACITY = SPD byte 4 bits 3~0 */
+ /* PRIMARY BUS WIDTH = SPD byte 8 bits 2~0 */
+ /* SDRAM WIDTH = SPD byte 7 bits 2~0 */
+ /* RANKS = SPD byte 7 bits 5~3 */
+ isdcap = (spd[4] & 0x0f);
+ iranks = (spd[7] & 0x38) >> 3;
+ isdw = (spd[7] & 0x07);
+ ibusw = (spd[8] & 0x07);
+ iparity = (spd[8] & 0x38) >> 3;
+ mfgid = spd[118];
+ switch(isdcap) {
+ case 0: sdcap = 256; /*MB*/ break;
+ case 1: sdcap = 512; /*MB*/ break;
+ case 2: sdcap = 1024; /*MB*/ break;
+ case 3: sdcap = 2048; /*MB*/ break;
+ case 4: sdcap = 4096; /*MB*/ break;
+ case 5: sdcap = 8192; /*MB*/ break;
+ case 6: sdcap = 16384; /*MB*/ break;
+ default: sdcap = 32768; /*MB*/ break;
+ }
+ switch(iranks) {
+ case 0: ranks = 1; break;
+ case 1: ranks = 2; break;
+ case 2: ranks = 3; break;
+ case 3:
+ default: ranks = 4; break;
+ }
+ switch(isdw) {
+ case 0: sdw = 4; break;
+ case 1: sdw = 8; break;
+ case 2: sdw = 16; break;
+ case 3:
+ default: sdw = 32; break;
+ }
+ switch(ibusw) {
+ case 0: busw = 8; break;
+ case 1: busw = 16; break;
+ case 2: busw = 32; break;
+ case 3:
+ default: busw = 64; break;
+ }
+ totcap = sdcap / 8 * busw / sdw * ranks;
+ printf("%sModule Density %c %d Mbits\n",devstr,bdelim,sdcap);
+ printf("%sModule Ranks %c %d ranks\n",devstr,bdelim,ranks);
+ printf("%sModule Capacity %c %d MB\n",devstr,bdelim,totcap);
+ }
+ if (iparity == 0x00) pstr = "Non-parity";
+ else /* usu 0x02 */ pstr = "ECC";
+ printf("%sDIMM Config Type %c %s\n", devstr,bdelim,pstr);
+ for (i = 0; i < NSPDMFG; i++)
+ if (spd_mfg[i].id == mfgid) break;
+ if (i == NSPDMFG) pstr = ""; /* not found, use null string */
+ else pstr = spd_mfg[i].str;
+ printf("%sManufacturer ID %c %s (0x%02x)\n", devstr,bdelim,pstr,mfgid);
+ if (srev < 0x10) {
+ yr = spd[93];
+ wk = spd[94];
+ iser = 95;
+ ipart = 73;
+ irev = 91;
+ } else { /* 1.0 SPD spec with DDR3 */
+ yr = spd[120];
+ wk = spd[121];
+ iser = 122;
+ ipart = 128;
+ irev = 146;
+ }
+ mrev1 = spd[irev]; /* save this byte for later */
+ mrev2 = spd[irev+1];
+ spd[irev] = 0; /*stringify part number */
+ printf("%sManufacturer Part# %c %s\n",
+ devstr,bdelim,&spd[ipart]);
+ printf("%sManufacturer Rev %c %02x %02x\n",
+ devstr,bdelim,mrev1,mrev2);
+ printf("%sManufacturer Date %c year=%02x week=%02x\n",
+ devstr,bdelim, yr,wk);
+ printf("%sAssembly Serial Num %c %02x%02x%02x%02x\n",
+ devstr,bdelim,spd[iser],spd[iser+1],spd[iser+2],spd[iser+3]);
+ spd[irev] = mrev1; /* restore byte, so ok to repeat later */
+ return;
+}
+
+void show_guid(uchar *pguid)
+{
+ char *s;
+ int i;
+ for (i=0; i<16; i++) {
+ if ((i == 4) || (i == 6) || (i == 8) || (i == 10)) s = "-";
+ else s = "";
+ printf("%s%02x",s,pguid[i]);
+ }
+}
+
+static char *volt_desc(uchar b)
+{
+ char *s;
+ switch(b) {
+ case 0x03: s = "5V"; break;
+ case 0x02: s = "3.3V"; break;
+ case 0x01: s = "-12V"; break;
+ case 0x00:
+ default: s = "12V"; break;
+ }
+ return(s);
+}
+
+static char *mgt_type(uchar b)
+{
+ char *s;
+ switch(b) {
+ case 0x01: s = "SysMgt_URL"; break;
+ case 0x02: s = "SystemName"; break;
+ case 0x03: s = "SysPingAddr"; break;
+ case 0x04: s = "Compon_URL"; break;
+ case 0x05: s = "ComponName"; break;
+ case 0x06: s = "ComponPing"; break;
+ case 0x07:
+ default: s = "SysGUID"; break;
+ }
+ return(s);
+}
+
+
+static
+void show_fru_multi(char *tag, int midx, uchar mtype, uchar *pdata, int dlen)
+{
+ int vend;
+ char mystr[256];
+ char *s1;
+ int v1, v2, v3, v4, v5, v6, v7;
+ uchar b1, b2;
+
+ if (fdebug) dumpbuf(pdata,dlen); /*multi-record area dump*/
+ sprintf(mystr,"%sMulti[%d] ",tag,midx);
+ switch(mtype) {
+ case 0x00: /*Power Supply*/
+ printf("%sPower Supply Record %c \n",mystr,bdelim);
+ v1 = pdata[0] + ((pdata[1] & 0x0f) << 8);
+ printf("\t Capacity \t%c %d W\n",bdelim, v1);
+ v2 = pdata[2] + (pdata[3] << 8);
+ printf("\t Peak VA \t%c %d VA\n",bdelim, v2);
+ printf("\t Inrush Current\t%c %d A\n",bdelim, pdata[4]);
+ printf("\t Inrush Interval\t%c %d ms\n",bdelim, pdata[5]);
+ v3 = pdata[6] + (pdata[7] << 8);
+ v4 = pdata[8] + (pdata[9] << 8);
+ printf("\t Input Voltage Range1\t%c %d-%d V\n",
+ bdelim,v3/100,v4/100);
+ v3 = pdata[10] + (pdata[11] << 8);
+ v4 = pdata[12] + (pdata[13] << 8);
+ printf("\t Input Voltage Range2\t%c %d-%d V\n",
+ bdelim,v3/100,v4/100);
+ printf("\t Input Frequency Range\t%c %d-%d Hz\n",
+ bdelim,pdata[14],pdata[15]);
+ printf("\t AC Dropout Tolerance\t%c %d ms\n",bdelim, pdata[16]);
+ b1 = pdata[17];
+ b2 = (b1 & 0x01);
+ if (b2) { /*predictive fail*/
+ if ((b1 & 0x10) != 0) s1 = "DeassertFail ";
+ else s1 = "AssertFail ";
+ } else {
+ if ((b1 & 0x10) != 0) s1 = "2pulses/rot ";
+ else s1 = "1pulse/rot ";
+ }
+ printf("\t Flags \t%c %s%s%s%s%s\n",bdelim,
+ b2 ? "PredictFail " : "",
+ ((b1 & 0x02) == 0) ? "" : "PowerFactorCorrect ",
+ ((b1 & 0x04) == 0) ? "" : "AutoswitchVolt ",
+ ((b1 & 0x08) == 0) ? "" : "Hotswap ", s1);
+ v5 = pdata[18] + ((pdata[19] & 0x0f) << 8);
+ v6 = (pdata[19] & 0xf0) >> 4;
+ printf("\t Peak Capacity \t%c %d W for %d s\n",bdelim, v5,v6);
+ if (pdata[20] == 0) {
+ printf("\t Combined Capacity\t%c not specified\n",bdelim);
+ } else {
+ b1 = pdata[20] & 0x0f;
+ b2 = (pdata[20] & 0xf0) >> 4;
+ v7 = pdata[21] + (pdata[22] << 8);
+ printf("\t Combined Capacity\t%c %d W (%s and %s)\n",
+ bdelim, v7,volt_desc(b1),volt_desc(b2));
+ }
+ if (b2) /*predictive fail*/
+ printf("\t Fan low threshold\t%c %d RPS\n",bdelim,pdata[23]);
+ break;
+ case 0x01: /*DC Output*/
+ b1 = pdata[0] & 0x0f;
+ b2 = ((pdata[0] & 0x80) != 0);
+ printf("%sDC Output %c number %d\n",mystr,bdelim,b1);
+ printf("\t Standby power \t%c %s\n", bdelim,
+ (b2 ? "Yes" : "No"));
+ v1 = pdata[1] + (pdata[2] << 8);
+ printf("\t Nominal voltage \t%c %.2f V\n", bdelim, v1 / 100);
+ v2 = pdata[3] + (pdata[4] << 8);
+ v3 = pdata[5] + (pdata[6] << 8);
+ printf("\t Voltage deviation \t%c + %.2f V / - %.2f V\n",
+ bdelim, v3/100, v2/100);
+ v4 = pdata[7] + (pdata[8] << 8);
+ printf("\t Ripple and noise pk-pk \t%c %d mV\n", bdelim, v4);
+ v5 = pdata[9] + (pdata[10] << 8);
+ printf("\t Min current draw \t%c %.3f A\n", bdelim, v5/1000);
+ v6 = pdata[11] + (pdata[12] << 8);
+ printf("\t Max current draw \t%c %.3f A\n", bdelim, v6/1000);
+ break;
+ case 0x02: /*DC Load*/
+ b1 = pdata[0] & 0x0f;
+ printf("%sDC Load %c number %d\n",mystr,bdelim,b1);
+ v1 = pdata[1] + (pdata[2] << 8);
+ printf("\t Nominal voltage \t%c %.2f V\n", bdelim, v1 / 100);
+ v2 = pdata[3] + (pdata[4] << 8);
+ v3 = pdata[5] + (pdata[6] << 8);
+ printf("\t Min voltage allowed \t%c %.2f A\n", bdelim, v2);
+ printf("\t Max voltage allowed \t%c %.2f A\n", bdelim, v3);
+ v4 = pdata[7] + (pdata[8] << 8);
+ printf("\t Ripple and noise pk-pk \t%c %d mV\n", bdelim, v4);
+ v5 = pdata[9] + (pdata[10] << 8);
+ printf("\t Min current load \t%c %.3f A\n", bdelim, v5/1000);
+ v6 = pdata[11] + (pdata[12] << 8);
+ printf("\t Max current load \t%c %.3f A\n", bdelim, v6/1000);
+ break;
+ case 0x03: /*Management Access*/
+ b1 = pdata[0];
+ printf("%sManagemt Access %c %s ",mystr,bdelim,mgt_type(b1));
+ memcpy(mystr,&pdata[1],dlen-1);
+ mystr[dlen-1] = 0;
+ printf("%s\n",mystr);
+ break;
+ case 0x04: /*Base Compatibility*/
+ vend = pdata[0] + (pdata[1] << 8) + (pdata[2] << 16);
+ printf("%sBasic Compat %c %06x\n",mystr,bdelim,vend);
+ break;
+ case 0x05: /*Extended Compatibility*/
+ vend = pdata[0] + (pdata[1] << 8) + (pdata[2] << 16);
+ printf("%sExtended Compat %c %06x\n",mystr,bdelim,vend);
+ break;
+ case 0xC0: /*OEM Extension*/
+ vend = pdata[0] + (pdata[1] << 8) + (pdata[2] << 16);
+ if (vend == OEM_PICMG) {
+ printf("%sOEM PICMG %c \n", mystr,bdelim);
+ show_fru_picmg(pdata,dlen);
+ } else
+ printf("%sOEM Ext %c %06x %02x\n",
+ mystr,bdelim, vend, pdata[3]);
+ break;
+ default:
+ printf("%s %02x %c %02x\n", mystr,mtype,bdelim, pdata[0]);
+ break;
+ }
+}
+
+int
+show_fru(uchar sa, uchar frudev, uchar frutype, uchar *pfrubuf)
+{
+ int ret = 0;
+ int i, j, n, sz;
+ uchar *pfru0;
+ uchar *pfru;
+ uchar lang;
+ TYPE_LEN tl;
+ char newstr[64];
+ int iaoff, ialen, bdoff, bdlen;
+ int proff, prlen, choff, chlen;
+ int moff, mlen;
+ char devstr[24];
+ char *pstr;
+
+ if ((pfrubuf[0] & 0x80) == 0x80) { /* 0x80 = type for DIMMs (SPD) */
+ /* FRU Header: 80 08 07 0c 0a 01 48 00 (DIMM) */
+ /* FRU Header: 92 10 0b 01 03 1a 02 00 (DDR3 DIMMs) */
+ sz = pfrubuf[0];
+ if (fdebug) {
+ printf("DIMM SPD Body (size=%d/%d): ",sz,sfru);
+ dumpbuf(pfrubuf,sfru);
+ }
+ show_spd(pfrubuf,sfru, frudev,frutype);
+ return(ret);
+ }
+
+ pstr = FruTypeString(frutype,frudev);
+ if (fcanonical) devstr[0] = 0; /*default is empty string*/
+ else sprintf(devstr,"[%s,%02x,%02x] ",pstr,sa,frudev);
+ printf("%s%s FRU Size %c %d\n",devstr,pstr,bdelim,sfru);
+
+ /*
+ * FRU header:
+ * 0 = format_ver (01 is std, usu 0x80 if DIMM)
+ * 1 = internal_use offset
+ * 2 = chassis_info offset
+ * 3 = board_info offset (usu 6 fields)
+ * 4 = product_info offset (usu 8 fields)
+ * 5 = multirecord offset
+ * 6 = pad (00)
+ * 7 = header checksum (zero checksum)
+ * FRU Header: 01 01 02 09 13 00 00 e0 (BMC)
+ * FRU Header: 01 00 00 00 01 07 00 f7 (Power Cage)
+ */
+ pfru0 = &pfrubuf[0]; /*pointer to fru start*/
+ pfru = &pfrubuf[0];
+ sz = 8; /*minimum for common header*/
+ for (i = 1; i < 6; i++) /* walk thru offsets */
+ if (pfrubuf[i] != 0) sz = pfrubuf[i] * 8;
+ if (sz > 8) { /* if have at least one section */
+ if (pfrubuf[5] != 0) j = 5 + pfrubuf[sz+2]; /*if multi-record area*/
+ else j = pfrubuf[sz+1] * 8; /* else add length of last section */
+ sz += j;
+ }
+
+ /* Now, sz = size used, sfru = total available size */
+ if (sz > sfru) {
+ if (fdebug) {
+ uchar hsum;
+ printf("FRU Header: ");
+ for (i = 0; i < 8; i++) printf("%02x ",pfrubuf[i]);
+ printf("\n");
+ hsum = calc_cksum(&pfrubuf[0],7);
+ if (pfrubuf[7] != hsum)
+ printf("FRU Header checksum mismatch (%x != %x)\n",pfrubuf[7],hsum);
+ }
+ printf("FRU size used=%d > available=%d\n",sz,sfru);
+ if (fpicmg || fdoanyway) sz = sfru; /*do it anyway*/
+ else {
+ printf("Please apply the correct FRU/SDR diskette\n");
+ return(ERR_OTHER);
+ }
+ }
+ /* internal area offset, length */
+ iaoff = pfrubuf[1] * 8;
+ ialen = pfrubuf[iaoff + 1] * 8;
+ /* chassis area offset, length */
+ choff = pfrubuf[2] * 8;
+ chlen = pfrubuf[choff + 1] * 8;
+ /* board area offset, length */
+ bdoff = pfrubuf[3] * 8;
+ bdlen = pfrubuf[bdoff + 1] * 8;
+ /* product area offset, length */
+ proff = pfrubuf[4] * 8;
+ prlen = pfrubuf[proff + 1] * 8;
+ /* multi-record area offset, length */
+ moff = pfrubuf[5] * 8;
+ mlen = 0;
+ if (moff > 0) {
+ for (i = moff; i < sfru; ) {
+ j = 5 + pfrubuf[i+2];
+ mlen += j;
+ if (pfrubuf[i+1] & 0x80) break;
+ i += j;
+ }
+ }
+
+ if (fdebug) {
+ printf("FRU Header: ");
+ for (i = 0; i < 8; i++) printf("%02x ",pfrubuf[i]);
+ printf("\n");
+ printf("FRU Body (size=%d/%d): ",sz,sfru);
+ dumpbuf(pfrubuf,sfru);
+ printf("header, len=%d, cksum0 = %02x, cksum1 = %02x\n",
+ 8,pfrubuf[7],calc_cksum(&pfrubuf[0],7));
+ printf("internal off=%d, len=%d, cksum = %02x\n",
+ iaoff,ialen,calc_cksum(&pfrubuf[iaoff],ialen-1));
+ printf("chassis off=%d, len=%d, cksum = %02x\n",
+ choff,chlen,calc_cksum(&pfrubuf[choff],chlen-1));
+ printf("board off=%d, len=%d, cksum = %02x\n",
+ bdoff,bdlen,calc_cksum(&pfrubuf[bdoff],bdlen-1));
+ printf("prod off=%d, len=%d, cksum = %02x\n",
+ proff,prlen,calc_cksum(&pfrubuf[proff],prlen-1));
+ /* Multi-record area */
+ printf("multi off=%d, len=%d, fru sz=%d\n", moff,mlen,sz);
+ } /*endif fdebug, show header*/
+
+ if (choff != 0) {
+ /* show Chassis area fields */
+ pfru = &pfrubuf[choff];
+ lang = 25; /* English */
+ ctype = pfru[2]; /*chassis type*/
+ if (fdebug) printf("ctype=%x\n",ctype);
+ if (ctype >= MAX_CTYPE) ctype = MAX_CTYPE - 1;
+ printf("%s%s%c %s\n",devstr, ctype_hdr,bdelim,ctypes[ctype]);
+ pfru += 3; /* skip chassis header */
+ tl.len = 0;
+ for (i = 0; i < NUM_CHASSIS_FIELDS; i++)
+ {
+ if (ChkOverflow((int)(pfru - pfru0),sfru,tl.len)) break;
+ if (pfru[0] == FRU_END) break; /*0xC1 = end of FRU area*/
+ if (!ValidTL(pfru[0]))
+ printf(" ERROR - Invalid Type/Length %02x for %s\n",
+ pfru[0],chassis[i]);
+ tl.type = (pfru[0] & FRU_TYPE_MASK) >> 6;
+ tl.len = pfru[0] & FRU_LEN_MASK;
+ if (i == 2) { /* OEM field for chassis_name */
+ chassis_offset = (int)(pfru - pfrubuf);
+ chassis_len = tl.len;
+ if (fdebug) printf("chassis oem dtype=%d lang=%d len=%d\n",
+ tl.type,lang,tl.len);
+ }
+ pfru++;
+ {
+ newstr[0] = 0;
+ decode_string(tl.type,lang,pfru,tl.len,newstr,sizeof(newstr));
+ printf("%s%s%c %s\n",devstr, chassis[i],bdelim,newstr);
+ }
+ pfru += tl.len;
+ }
+ if (fdebug) printf("num Chassis fields = %d\n",i);
+ }
+
+ if (bdoff != 0) {
+ long nMin, nSec;
+ time_t tsec;
+ /* show Board area fields */
+ pfru = &pfrubuf[bdoff];
+ lang = pfru[2];
+ /* Decode board mfg date-time (num minutes since 1/1/96) */
+ nMin = pfru[3] + (pfru[4] << 8) + (pfru[5] << 16);
+ /* 820,454,400 sec from 1/1/70 to 1/1/96 */
+ nSec = (nMin * 60) + 820454400;
+ tsec = (time_t)(nSec & 0x0ffffffff);
+ // fmt_time(tsec,newstr,sizeof(newstr));
+ printf("%sBoard Mfg DateTime %c %s",devstr,bdelim,ctime(&tsec));
+ pfru += 6; /* skip board header */
+ tl.len = 0;
+ for (i = 0; i < NUM_BOARD_FIELDS; i++)
+ {
+ if (ChkOverflow((int)(pfru - pfru0),sfru,tl.len)) break;
+ if (pfru[0] == FRU_END) break; /*0xC1 = end*/
+ if (!ValidTL(pfru[0]))
+ printf(" ERROR - Invalid Type/Length %02x for %s\n",
+ pfru[0],board[i]);
+ tl.type = (pfru[0] & FRU_TYPE_MASK) >> 6;
+ tl.len = pfru[0] & FRU_LEN_MASK;
+ pfru++;
+ {
+ newstr[0] = 0;
+ decode_string(tl.type,lang,pfru,tl.len,newstr,sizeof(newstr));
+ printf("%s%s%c %s\n",devstr, board[i],bdelim,newstr);
+ }
+ pfru += tl.len;
+ }
+ if (fdebug) printf("num Board fields = %d\n",i);
+ }
+
+ if (proff != 0)
+ {
+ /* show Product area fields */
+ pfru = &pfrubuf[proff];
+ maxprod = pfru[1] * 8;
+ lang = pfru[2];
+ pfru += 3; /* skip product header */
+ tl.len = 0;
+ for (i = 0; i < NUM_PRODUCT_FIELDS; i++)
+ {
+ if (ChkOverflow((int)(pfru - pfru0),sfru,tl.len)) break;
+ if (*pfru == FRU_END) { /*0xC1 = end*/
+ /* Wart for known Kontron 1-byte Product Version anomaly. */
+ if (vend_id == VENDOR_KONTRON && i == 3) ;
+ else break;
+ }
+ if (*pfru == 0) *pfru = FRU_EMPTY_FIELD; /* fix a broken table */
+ if (!ValidTL(pfru[0]))
+ printf(" ERROR - Invalid Type/Length %02x for %s\n",
+ pfru[0],product[i]);
+ tl.type = (pfru[0] & FRU_TYPE_MASK) >> 6;
+ tl.len = pfru[0] & FRU_LEN_MASK;
+ if ((frudev == 0) && (sa == bmc_sa)) { /*baseboard FRU*/
+ if (i == 5) { /* asset tag */
+ asset_offset = (int)(pfru - pfrubuf);
+ asset_len = tl.len;
+ if (fdebug) printf("asset off=%d dtype=%d lang=%d len=%d\n",
+ asset_offset,tl.type,lang,tl.len);
+ if (fshowlen) /* show asset tag length for main board */
+ printf("%s%c %d\n",asset_hdr,bdelim,asset_len);
+ } else if (i == 4) { /* serial number */
+ sernum_offset = (int)(pfru - pfrubuf);
+ sernum_len = tl.len;
+ if (fdebug) printf("sernum dtype=%d lang=%d len=%d\n",
+ tl.type, lang, tl.len);
+ } else if (i == 3) { /* product version number */
+ prodver_offset = (int)(pfru - pfrubuf);
+ prodver_len = tl.len;
+ if (fdebug) printf("prodver dtype=%d lang=%d len=%d\n",
+ tl.type, lang, tl.len);
+ }
+ } /*if baseboard sa*/
+ pfru++;
+ {
+ newstr[0] = 0;
+ decode_string(tl.type,lang,pfru,tl.len,newstr,sizeof(newstr));
+ printf("%s%s%c %s\n",devstr, product[i],bdelim,newstr);
+ }
+ pfru += tl.len;
+ }
+ if (fdebug)
+ printf("num Product fields = %d, last=%x, max = %d\n",
+ i,*pfru,maxprod );
+ if (*pfru == 0x00) *pfru = FRU_END; /* insert end char if broken */
+ }
+
+ if (moff != 0)
+ {
+ /* multi-record area may contain several record headers
+ * 0 = record type id
+ * 1 = 0x02 or 0x82 if End-of-List
+ * 2 = record len
+ * 3 = record chksum
+ * 4 = header chksum
+ */
+ pfru = &pfrubuf[moff];
+ j = moff;
+ for (i = 0; j < sz ; i++)
+ {
+ n = pfru[2]; /* len of this record */
+ show_fru_multi(devstr,i,pfru[0],&pfru[5],n);
+ j += (5 + n);
+ if (pfru[1] & 0x80) j = sz; /*0x80 = last in list, break*/
+ pfru += (5 + n);
+ }
+ }
+
+ if ((frudev == 0) && (sa == bmc_sa) && do_guid) {
+ printf("%sSystem GUID %c ",devstr,bdelim);
+ show_guid(guid);
+ printf("\n");
+ }
+ return(ret);
+}
+
+int write_fru_data(uchar id, ushort offset, uchar *data, int dlen, char fdebug)
+{
+ int ret = -1;
+ int chunk;
+ ushort fruoff;
+ uchar req[25];
+ uchar resp[16];
+ int sresp;
+ uchar cc;
+ int i, j;
+
+ /* Write the buffer in small 16-byte (FRUCHUNK_SZ) chunks */
+ req[0] = 0x00; /* override FRU Device ID (fruid) */
+ fruoff = offset;
+ chunk = FRUCHUNK_SZ;
+ for (i = 0; i < dlen; i += chunk) {
+ req[1] = fruoff & 0x00ff;
+ req[2] = (fruoff & 0xff00) >> 8;
+ if ((i + chunk) > dlen) chunk = dlen - i;
+ memcpy(&req[3],&data[i],chunk);
+ if (fdebug) {
+ printf("write_fru_data[%d] (len=%d): ",i,chunk+3);
+ for (j = 0; j < chunk+3; j++) printf("%02x ",req[j]);
+ printf("\n");
+ }
+ sresp = sizeof(resp);
+ ret = ipmi_cmd_mc(WRITE_FRU_DATA,req,(uchar)(chunk+3),resp,&sresp,
+ &cc,fdebug);
+ if ((ret == 0) && (cc != 0)) ret = cc & 0x00ff;
+ if (fdebug && ret == 0)
+ printf("write_fru_data[%d]: %d bytes written\n",i,resp[0]);
+ if (ret != 0) break;
+ fruoff += (ushort)chunk;
+ }
+ return(ret);
+}
+
+/* write_asset updates the FRU Product area only. */
+int
+write_asset(char *tag, char *sernum, char *prodver, int flag, uchar *pfrubuf)
+{
+ int ret = -1;
+ uchar newdata[SZ_PRODAREA];
+ int alen, clen;
+ int snlen, verlen;
+ uchar *pfru0;
+ uchar *pfru;
+ uchar *pnew;
+ int i, j, m, n, newlen, max;
+ int chas_offset;
+ int prod_offset, prod_len;
+ int mult_offset, mult_len;
+ uchar chksum;
+ char fdoasset = 0;
+ char fdosernum = 0;
+ char fdoprodver = 0;
+ char fdochassis = 0;
+
+ if (flag == 0) return ERR_OTHER;
+ if (pfrubuf == NULL) pfrubuf = frubuf;
+ if (pfrubuf == NULL) return ERR_OTHER;
+ if ((flag & 0x01) != 0) {
+ fdoasset = 1;
+ alen = strlen_(tag); /*new*/
+ } else {
+ alen = asset_len; /*old*/
+ }
+ if ((flag & 0x02) != 0) {
+ fdosernum = 1;
+ snlen = strlen_(sernum); /*new*/
+ } else {
+ snlen = sernum_len; /*old*/
+ }
+ if ((flag & 0x200) != 0) {
+ fdochassis = 1;
+ // clen = strlen(chassis_nm); /*new, ignore*/
+ // if (clen <= 1) return ERR_LENMIN;
+ clen = chassis_len;
+ } else {
+ clen = chassis_len; /*old*/
+ }
+ if ((flag & 0x08) != 0) {
+ fdoprodver = 1;
+ verlen = strlen_(prodver); /*new*/
+ } else verlen = prodver_len; /*old*/
+ /*
+ * Abort if offset is negative or if it would clobber
+ * fru header (8 bytes).
+ * Abort if asset tag is too big for fru buffer.
+ */
+ if (fdebug) {
+ printf("write_asset: asset_off=%d asset_len=%d alen=%d sFRU=%d\n",
+ asset_offset,asset_len,alen,sfru);
+ printf(" sernum_off=%d sernm_len=%d snlen=%d maxprod=%d\n",
+ sernum_offset,sernum_len,snlen,maxprod);
+ printf(" prodver=%d prodver_len=%d verlen=%d maxprod=%d\n",
+ prodver_offset,prodver_len,verlen,maxprod);
+ }
+ if (fdoasset && (alen <= 1)) return ERR_LENMIN;
+ if (fdosernum && (snlen <= 1)) return ERR_LENMIN;
+ if (asset_offset < 8) return ERR_LENMIN;
+ if (asset_offset + alen > sfru) return ERR_LENMAX;
+ if (sernum_offset < 8) return ERR_LENMIN;
+ if (sernum_offset + snlen > sfru) return ERR_LENMAX;
+ if (prodver_offset < 8) return ERR_LENMIN;
+ if (prodver_offset + verlen > sfru) return ERR_LENMAX;
+
+ pfru0 = &pfrubuf[0];
+ chas_offset = pfrubuf[2] * 8; // offset of chassis data
+ prod_offset = pfrubuf[4] * 8; // offset of product data
+ prod_len = pfrubuf[prod_offset+1] * 8; // length of product data
+ mult_offset = pfrubuf[5] * 8;
+ mult_len = 0;
+ if (mult_offset > 0) {
+ for (i = mult_offset; i < sfru; ) {
+ mult_len += (5 + pfrubuf[i+2]);
+ if (pfrubuf[i+1] & 0x80) break;
+ i = mult_len;
+ }
+ }
+ /* Check if asset tag will fit in product data area of FRU. */
+ if (fdebug)
+ printf("write_asset: fru[4,p]=[%02x,%02x] prod_off=%d plen=%d veroff=%d\n",
+ pfrubuf[4],pfrubuf[prod_offset+1],prod_offset,prod_len, prodver_offset);
+ /* asset comes after sernum, so this check works for both */
+ if (prod_offset > prodver_offset) return ERR_LENMAX; // offsets wrong
+ if (prod_len > sizeof(newdata)) return ERR_LENMAX; // product > buffer
+
+ memset(newdata,0,prod_len);
+ pnew = &newdata[0];
+ /* Copy fru data from start to chassis OEM */
+ pfru = &pfrubuf[chas_offset];
+
+ /* Copy fru data before serial number */
+ pfru = &pfrubuf[prod_offset];
+ j = prodver_offset - prod_offset;
+ memcpy(pnew,pfru,j);
+ pfru += j;
+ pnew += j;
+ if (fdebug) {
+ printf("write_asset: fru[4,p]=[%02x,%02x] sernum_off=%d snlen=%d plen=%d\n",
+ pfrubuf[4],pfrubuf[sernum_offset+1],sernum_offset,snlen,prod_len);
+ }
+ if (fdoprodver) {
+ pnew[0] = (verlen | FRU_TYPE_MASK); /*add type=3 to snlen*/
+ memcpy(++pnew,prodver,verlen);
+ } else {
+ pnew[0] = (verlen | FRU_TYPE_MASK); /*type/len byte*/
+ memcpy(++pnew,&pfrubuf[prodver_offset+1],verlen);
+ }
+ j += prodver_len + 1;
+ pfru += prodver_len + 1;
+ pnew += verlen; /*already ++pnew above*/
+
+ /* Copy new or old serial number */
+ if (fdebug) printf("pfrubuf[%ld]: %02x %02x %02x, j=%d, snlen=%d/%d\n",
+ (pfru-pfrubuf),pfru[0],pfru[1],pfru[2],j,snlen,sernum_len);
+ if (fdosernum) {
+ pnew[0] = (snlen | FRU_TYPE_MASK); /*add type=3 to snlen*/
+ memcpy(++pnew,sernum,snlen);
+ } else {
+ pnew[0] = (snlen | FRU_TYPE_MASK); /*type/len byte*/
+ memcpy(++pnew,&pfrubuf[sernum_offset+1],snlen);
+ }
+ /* increment past serial number, to asset tag */
+ j += sernum_len + 1;
+ pfru += sernum_len + 1;
+ pnew += snlen; /*already ++pnew above*/
+
+ /* Copy new or old asset tag */
+ if (fdebug) printf("pfrubuf[%ld]: %02x %02x %02x, j=%d, alen=%d/%d\n",
+ (pfru-pfrubuf),pfru[0],pfru[1],pfru[2],j,alen,asset_len);
+ if (fdoasset) {
+ pnew[0] = (alen | FRU_TYPE_MASK); /*add type=3 to len*/
+ memcpy(++pnew,tag,alen);
+ } else {
+ pnew[0] = (alen | FRU_TYPE_MASK); /*type/len byte*/
+ memcpy(++pnew,&pfrubuf[asset_offset+1],alen);
+ }
+ j += asset_len + 1;
+ pfru += asset_len + 1;
+ pnew += alen;
+
+ /* copy trailing fru data from saved copy */
+ n = (int)(pnew - newdata); /* new num bytes used */
+ m = (int)(pfru - pfru0); /* old num bytes used */
+ if (fdebug) printf("pfrubuf[%d]: %02x %02x %02x, j=%d, n=%d, plen=%d\n",
+ m,pfru[0],pfru[1],pfru[2],j,n,prod_len);
+ if (mult_offset > 0) { /* do not expand if multi-record area there */
+ max = prod_len - j;
+ } else /* nothing else, can expand product area, up to sfru */
+ max = sfru - (j + prod_offset);
+ if (fdebug) printf("pfrubuf[%ld]: %02x %02x %02x, j=%d n=%d remainder=%d\n",
+ (pfru-pfrubuf),pfru[0],pfru[1],pfru[2],j,n,max);
+ if (max < 0) max = 0;
+ for (i = 0; i < max; i++) {
+ pnew[i] = pfru[i];
+ if (pfru[i] == FRU_END) { i++; break; }
+ }
+ if (i == max) { /*never found 0xC1 FRU_END*/
+ pnew[0] = FRU_END;
+ i = 1;
+ }
+
+ newlen = n + i;
+ if (fdebug) printf("newbuf[%d]: %02x %02x %02x, j=%d, newlen=%d\n",
+ n,pnew[0],pnew[1],pnew[2],j,newlen);
+ /* round up to next 8-byte boundary */
+ /* need one more byte for checksum, so if mod=0, adds 8 anyway */
+ j = 8 - (newlen % 8);
+ for (i = 0; i < j; i++) newdata[newlen++] = 0;
+
+ if (newlen < prod_len) newlen = prod_len; /* don't shrink product area */
+ newdata[1] = newlen / 8; // set length of product data
+
+ /* include new checksum (calc over Product area) */
+ chksum = calc_cksum(&newdata[0],newlen-1);
+ newdata[newlen-1] = chksum;
+
+#ifdef NEEDED
+ /* This caused a problem on Radisys systems, and is not needed, so skip it.*/
+ if (mult_offset > 0) {
+ /* add on any more data from multi-record area */
+ pnew = &newdata[newlen];
+ pfru = &pfrubuf[mult_offset];
+ memcpy(pnew,pfru,mult_len);
+ newlen += mult_len;
+ }
+#endif
+
+ if (fdebug) {
+ printf("old buffer (%d):",prod_len);
+ dumpbuf(&pfrubuf[prod_offset],prod_len);
+ printf("new buffer (%d):",newlen);
+ dumpbuf(newdata,newlen);
+ }
+ if (prod_offset + newlen >= sfru) return ERR_LENMAX;
+ if ((mult_offset > 0) && (newlen > prod_len)) return ERR_LENMAX;
+#ifdef TEST
+ newlen = 0;
+#endif
+
+ ret = write_fru_data(0, (ushort)prod_offset, newdata, newlen, fdebug);
+ return(ret);
+}
+
+#define SDR_CHUNKSZ 16
+#define LAST_REC 0xffff
+#define SDR_STR_OFF 16
+
+int get_sdr(ushort recid, ushort resid, ushort *recnext,
+ uchar *sdr, int *slen, uchar *pcc)
+{
+ uchar idata[6];
+ uchar rdata[64];
+ int sresp;
+ ushort cmd;
+ uchar cc = 0;
+ int len = 0;
+ int szsdr, thislen;
+ int rc;
+
+ memset(sdr,0,*slen); /*clear sdr */
+ idata[0] = resid & 0x00ff;
+ idata[1] = (resid & 0xff00) >> 8;
+ idata[2] = recid & 0x00ff;
+ idata[3] = (recid & 0xff00) >> 8;
+ idata[4] = 0; /*offset*/
+ idata[5] = SDR_CHUNKSZ; /*bytes to read*/
+ if (fdevsdrs) cmd = GET_DEVICE_SDR;
+ else cmd = GET_SDR;
+ sresp = sizeof(rdata);
+ rc = ipmi_cmd_mc(cmd, idata, 6, rdata, &sresp,&cc, fdebug);
+ if (fdebug) printf("get_sdr[%x] ret = %d cc = %x sresp = %d\n",
+ recid,rc,cc,sresp);
+ if (rc == 0 && cc == 0xCA) { /*cannot read SDR_CHUNKSZ bytes*/
+ idata[5] = 8; /*bytes to read*/
+ sresp = sizeof(rdata);
+ rc = ipmi_cmd_mc(cmd, idata, 6, rdata, &sresp,&cc, fdebug);
+ if (fdebug) printf("get_sdr[%x] ret = %d cc = %x sresp = %d\n",
+ recid,rc,cc,sresp);
+ }
+ *pcc = cc;
+ if (rc == 0 && cc == 0) {
+ *recnext = rdata[0] + (rdata[1] << 8);
+ if (sresp < 2) len = 0;
+ else len = sresp-2;
+ if (len > *slen) len = *slen;
+ memcpy(sdr,&rdata[2],len);
+ szsdr = sdr[4] + 5; /*get actual SDR size*/
+ if (szsdr > *slen) szsdr = *slen;
+ /* if an SDR locator record, get the rest of it. */
+ if (sdr[3] == 0x11 || sdr[3] == 0x12)
+ if (szsdr > SDR_CHUNKSZ) {
+ ushort resid2;
+ rc = sdr_get_reservation((uchar *)&resid2,fdevsdrs);
+ if (fdebug) printf("2nd sdr_get_reservation ret=%d\n",rc);
+ thislen = szsdr - SDR_CHUNKSZ;
+ if (thislen > SDR_CHUNKSZ) thislen = SDR_CHUNKSZ;
+ idata[0] = resid2 & 0x00ff;
+ idata[1] = (resid2 & 0xff00) >> 8;
+ idata[2] = recid & 0x00ff;
+ idata[3] = (recid & 0xff00) >> 8;
+ idata[4] = SDR_CHUNKSZ; /*offset*/
+ idata[5] = (uchar)thislen; /*bytes to read*/
+ sresp = sizeof(rdata);
+ rc = ipmi_cmd_mc(cmd, idata, 6, rdata, &sresp,&cc, fdebug);
+ if (fdebug) printf("get_sdr[%x] 2nd ret=%d cc=%x sresp=%d\n",
+ recid,rc,cc,sresp);
+ if (rc == 0 && cc == 0) {
+ if (sresp < 2) sresp = 0;
+ else sresp -= 2;
+ if (sresp > thislen) sresp = thislen;
+ memcpy(&sdr[len],&rdata[2],sresp);
+ len += sresp;
+ } else rc = 0; /*ok because got first part*/
+ } /*endif got first chunk, need more*/
+ *slen = len;
+ } else *slen = 0;
+ return(rc);
+}
+
+void show_loadfru_error(uchar sa, uchar fruid, int ret)
+{
+ if (ret == 0) return;
+ switch(ret) {
+ case 0x081: printf("\tFRU(%x,%x) device busy\n",sa,fruid); break;
+ case 0x0C3: printf("\tFRU(%x,%x) timeout, not found\n",sa,fruid); break;
+ case 0x0CB: printf("\tFRU(%x,%x) not present\n",sa,fruid); break;
+ default: printf("load_fru(%x,%x) error = %d (0x%x)\n",
+ sa,fruid,ret,ret);
+ break;
+ }
+ return;
+}
+
+int get_show_fru(ushort recid, uchar *sdr, int sdrlen)
+{
+ int ret = 0;
+ int ilen;
+ char idstr[32];
+ uchar sa, fruid =0;
+ uchar frutype = 0;
+ char fgetfru = 0;
+ char fisbase = 0;
+ uchar *pfru;
+
+ if (sdrlen > SDR_STR_OFF) {
+ ilen = sdrlen - SDR_STR_OFF;
+ if (ilen >= sizeof(idstr)) ilen = sizeof(idstr) - 1;
+ memcpy(idstr,&sdr[SDR_STR_OFF],ilen);
+ idstr[ilen] = 0;
+ } else idstr[0] = 0;
+ sa = sdr[5]; /* usu 0x20 for bmc_sa */
+ if (fbasefru) fisbase = 1;
+
+ /* Get its FRU data */
+ if ((sdr[3] == 0x11) && (sdr[7] & 0x80)) { /* FRU SDRs */
+ /* It is a logical FRU device */
+ if (fcanonical)
+ printf("SDR[%04x] FRU %c %s\n", recid, bdelim, idstr);
+ else
+ printf("SDR[%04x] FRU %02x %02x %02x %02x %s\n", recid,
+ sdr[5],sdr[6],sdr[12],sdr[13],idstr);
+ fruid = sdr[6];
+ if (sdrlen > 12) frutype = sdr[12];
+ if (sa == bmc_sa && fruid == 0) { /*dont repeat base fru */
+ frutype = 0x0c;
+ fisbase = 1;
+ if (fbasefru) fgetfru = 1;
+ } else
+ switch(frutype) /*FRU entity id*/
+ {
+ case 0x0a: /*Power Supply*/
+ case 0x20: /*DIMM*/
+ case 0x15: /*Power Cage*/
+ default:
+ fgetfru = 1;
+ break;
+ }
+ } else if (sdr[3] == 0x12) { /* IPMB SDRs (DLRs for MCs) */
+ if (fcanonical)
+ printf("SDR[%04x] IPMB %c %s\n", recid, bdelim, idstr);
+ else
+ printf("SDR[%04x] IPMB %02x %02x %02x %02x %s\n", recid,
+ sdr[5],sdr[6],sdr[12],sdr[13],idstr);
+ fruid = 0; /*every MC must have fruid 0*/
+ if (sdrlen > 12) frutype = sdr[12];
+ if (sa == bmc_sa && fruid == 0) { /*dont repeat base fru */
+ fisbase = 1;
+ frutype = 0x07;
+ if (fdebug) printf("do bmc_sa %02x once\n",sa);
+ g_frutype = frutype;
+ if (fbasefru) fgetfru = 1;
+ } else if (frutype == 0x2e) { /*skip ME*/
+ if (fdebug) printf("skipping ME sa %02x, %02x\n",sa,fruid);
+ } else if (sa == 0x28) { /*do nothing for Bridge Ctlr sa=0x28*/
+ if (fdebug) printf("skipping IPMB sa %02x, %02x\n",sa,fruid);
+ } else if (sa == 0xC0) { /* HotSwap Backplane (sa=0xC0) */
+ /* Note: Loading sa 0xC0 over ipmi lan gives a timeout
+ * error, but it works locally. */
+ fgetfru = 1;
+ } else { /* other misc sa,fruid */
+ fgetfru = 1;
+ }
+ }
+
+ /* Check if matches previous FRU, if so don't repeat */
+ if ((sa == lastfru[0]) && (fruid == lastfru[1]) )
+ fgetfru = 0;
+
+ if (fgetfru) {
+ uchar adrtype;
+ adrtype = g_addrtype;
+ if (fdebug) printf("set_mc %02x:%02x:%02x type=%d fruid=%02x\n",
+ g_bus,sa,g_lun,adrtype,fruid);
+ ipmi_set_mc(g_bus, sa, g_lun,adrtype);
+ ret = load_fru(sa,fruid,frutype,&pfru);
+ if (ret != 0) {
+ show_loadfru_error(sa,fruid,ret);
+ } else {
+ ret = show_fru(sa,fruid,frutype,pfru);
+ if (ret != 0) printf("show_fru error = %d\n",ret);
+ if (sa == bmc_sa && fruid == 0) fbasefru = 0;
+ }
+ free_fru(pfru);
+ pfru = NULL;
+ ipmi_restore_mc();
+ lastfru[0] = sa;
+ lastfru[1] = fruid;
+ lastfru[2] = frutype;
+ }
+ return(ret);
+}
+
+/*
+ * test_show_fru
+ *
+ * Reads the FRU buffer dump from a file to test decoding the FRU.
+ * This file uses the format output from 'ipmiutil fru -x'.
+ *
+[Component,20,00] Component FRU Size : 4096
+FRU Header: 01 0a 00 01 20 00 00 d4
+FRU Body (size=256/4096):
+ 0000: 01 0a 00 01 20 00 00 d4 01 09 00 c0 3b 7e 83 64 .... .......;~.d
+ *
+ */
+static int test_show_fru(char *infile)
+{
+ int rv = -1;
+ FILE *fp;
+ int len, i, idx, sz, off;
+ uchar buff[256];
+ uchar sa = 0x20;
+ uchar fruid =0;
+ uchar frutype = 0;
+ uchar *pfru = NULL;
+ char *p1;
+ char *p2;
+
+ fp = fopen(infile,"r");
+ if (fp == NULL) {
+ printf("Cannot open file %s\n",infile);
+ return(ERR_FILE_OPEN);
+ } else {
+ if (fdebug) printf("decoding text file with FRU buffer bytes\n");
+ idx = 0;
+ off = 0;
+ sz = sizeof(buff);
+ while (fgets(buff, 255, fp)) {
+ len = strlen_(buff);
+ if (fdebug) printf("fgets[%d]: %s",idx,buff); /*has '\n'*/
+ if (idx == 0) { /* the FRU Size line*/
+ p1 = strstr(buff,"FRU Size");
+ if (p1 == NULL) continue;
+ p1 = strchr(buff,',');
+ if (p1 == NULL) continue;
+ p1++;
+ p2 = strchr(p1,',');
+ if (p2 == NULL) p2 = &buff[len];
+ *p2 = 0; /*stringify*/
+ sa = htoi(p1);
+ p1 = p2 + 1;
+ p2 = strchr(p1,']');
+ if (p2 == NULL) p2 = &buff[len];
+ *p2 = 0; /*stringify*/
+ fruid = htoi(p1);
+ frutype = 0;
+ if (fdebug) printf("read[%d] sa=%x fruid=%x\n",idx,sa,fruid);
+ idx++;
+ } else if (idx == 1) { /* the FRU Header line*/
+ p1 = strstr(buff,"FRU Header");
+ // if (p1 == NULL) continue;
+ if (fdebug) printf("read[%d] p1=%p\n",idx,p1);
+ idx++;
+ } else if (idx == 2) { /* the FRU Bodu line*/
+ p1 = strstr(buff,"FRU Body");
+ if (p1 == NULL) continue;
+ p1 = strchr(buff,'=');
+ if (p1 == NULL) continue;
+ ++p1;
+ p2 = strchr(p1,'/');
+ if (p2 == NULL) p2 = &buff[len];
+ *p2 = 0; /*stringify*/
+ sz = atoi(p1);
+ p1 = p2 + 1;
+ p2 = strchr(p1,')');
+ if (p2 == NULL) p2 = &buff[len];
+ *p2 = 0; /*stringify*/
+ sfru = atoi(p1); /*global sfru*/
+ pfru = malloc(sfru);
+ if (fdebug)
+ printf("read[%d] sz=%d/%d pfru=%p\n",idx,sz,sfru,pfru);
+ idx++;
+ } else { /*idx==3, this has the FRU buffer data */
+ p1 = strchr(buff,':');
+ if (p1 == NULL) continue;
+ p1 += 2;
+ for (i = 0; i < 16; i++) {
+ pfru[off+i] = htoi(&p1[i*3]);
+ }
+ off += 16;
+ if (fdebug) printf("read[%d] offset=%d\n",idx,off);
+ } /*endif */
+ if (off >= sz) { rv = 0; break; }
+ } /*end while*/
+
+ fclose(fp);
+ if (rv == 0) {
+ if (pfru == NULL) {
+ printf("Invalid input file format (mode %d)\n",idx);
+ return(ERR_BAD_FORMAT);
+ }
+ rv = show_fru(sa,fruid,frutype,pfru);
+ if (rv != 0) printf("show_fru error = %d\n",rv);
+ }
+ free_fru(pfru);
+ }
+ return(rv);
+}
+
+#ifdef ALONE
+#ifdef WIN32
+int __cdecl
+#else
+int
+#endif
+main(int argc, char **argv)
+#else
+/* METACOMMAND */
+int i_fru(int argc, char **argv)
+#endif
+{
+ int ret, rv;
+ int c;
+ char DevRecord[16];
+ ushort recid;
+ ushort nextid;
+ ushort rsvid;
+ uchar sdr[48];
+ char biosver[80];
+ uchar cc;
+ uchar sa;
+ //uchar fruid = 0;
+ //uchar frutype = 0;
+ int len, i, j;
+ char *s1;
+ uchar *pfru = NULL;
+ FILE *fp;
+ int ipass, npass, nsdrs;
+ char *tag;
+ char do_reserve = 1;
+ char devstr[32];
+
+ printf("%s: version %s\n",progname,progver);
+
+ parse_lan_options('V',"4",0); /*default to admin privilege*/
+ while ( (c = getopt( argc, argv,"a:bcd:efhkl:m:n:i:p:r:s:t:v:xyzT:V:J:EYF:P:N:R:U:Z:?")) != EOF )
+ switch(c) {
+ case 'x': fdebug = 1; break;
+ case 'z': fdebug = 3; break; /*do more LAN debug detail*/
+ case 'b': fonlybase = 1;
+ g_frutype = 0x07; break;
+ case 'c': fcanonical = 1; bdelim = BDELIM; break;
+ case 'd': fdump = 1; /*dump fru to a file*/
+ binfile = optarg;
+ break;
+ case 'e': fchild = 1; break; /*extra child MCs if bladed*/
+ case 'f': fchild = 1; break; /*follow child MCs if bladed*/
+ case 'h': fonlyhsc = 1; /* show HSC FRU, same as -m00c000s */
+ g_frutype = 0x0f; break;
+ case 'k': foemkontron = 1; break;
+ case 'n':
+ fwritefru |= 0x200;
+ if (optarg) {
+ len = strlen_(optarg);
+ if (len >= FIELD_LEN) len = FIELD_LEN - 1;
+ strncpy(chassis_name,optarg,len);
+ if (len == 1) { /* add a space */
+ chassis_name[1] = ' ';
+ chassis_name[2] = 0;
+ }
+ }
+ break;
+ case 'p': /*Power Supply Product */
+ // fwritefru |= 0x10;
+ if (optarg) {
+ len = strlen_(optarg);
+ if (len >= FIELD_LEN) len = FIELD_LEN - 1;
+ strncpy(ps_prod,optarg,len);
+ if (len == 1) { /* add a space */
+ ps_prod[1] = ' ';
+ ps_prod[2] = 0;
+ }
+ }
+ break;
+ case 'r': frestore = 1; /*restore fru from a file*/
+ fwritefru = 0x100;
+ binfile = optarg;
+ break;
+ case 't': ftestshow = 1; /*test show_fru from a file*/
+ binfile = optarg;
+ break;
+ case 'a':
+ fwritefru |= 0x01;
+ if (optarg) {
+ len = strlen_(optarg);
+ if (len >= FIELD_LEN) len = FIELD_LEN - 1;
+ strncpy(asset_tag,optarg,len);
+ if (len == 1) { /* add a space */
+ asset_tag[1] = ' ';
+ asset_tag[2] = 0;
+ }
+ }
+ break;
+ case 's':
+ fwritefru |= 0x02;
+ if (optarg) {
+ len = strlen_(optarg);
+ if (len >= FIELD_LEN) len = FIELD_LEN - 1;
+ strncpy(serial_num,optarg,len);
+ if (len == 1) { /* add a space */
+ serial_num[1] = ' ';
+ serial_num[2] = 0;
+ }
+ }
+ break;
+ case 'v':
+ fwritefru |= 0x08;
+ if (optarg) {
+ len = strlen_(optarg);
+ if (len >= FIELD_LEN) len = FIELD_LEN - 1;
+ strncpy(prod_ver,optarg,len);
+ if (len == 1) { /* add a space */
+ prod_ver[1] = ' ';
+ prod_ver[2] = 0;
+ }
+ }
+ break;
+ case 'm': /* specific IPMB MC, 3-byte address, e.g. "409600" */
+ g_bus = htoi(&optarg[0]); /*bus/channel*/
+ g_sa = htoi(&optarg[2]); /*device slave address*/
+ g_lun = htoi(&optarg[4]); /*LUN*/
+ g_addrtype = ADDR_IPMB;
+ if (optarg[6] == 's') {
+ g_addrtype = ADDR_SMI; s1 = "SMI";
+ } else { g_addrtype = ADDR_IPMB; s1 = "IPMB"; }
+ fset_mc = 1;
+ printf("set MC at %s bus=%x sa=%x lun=%x\n",
+ s1,g_bus,g_sa,g_lun);
+ break;
+ case 'l': /* set local IPMB MC sa (same as -Z but sets bmc_sa) */
+ sa = htoi(&optarg[0]); /*device slave address*/
+ ipmi_set_mymc(g_bus, sa, g_lun,ADDR_IPMB);
+ bmc_sa = sa;
+ break;
+ case 'i': fonlybase = 1; /*specify a fru id*/
+ if (strncmp(optarg,"0x",2) == 0) g_fruid = htoi(&optarg[2]);
+ else g_fruid = htoi(optarg);
+ printf("Using FRU ID 0x%02x\n",g_fruid);
+ break;
+ case 'y': fdoanyway = 1; break;
+ case 'V': /* priv level */
+ fprivset = 1;
+ case 'N': /* nodename */
+ case 'U': /* remote username */
+ case 'P': /* remote password */
+ case 'R': /* remote password */
+ case 'E': /* get password from IPMI_PASSWORD environment var */
+ case 'F': /* force driver type */
+ case 'T': /* auth type */
+ case 'J': /* cipher suite */
+ case 'Y': /* prompt for remote password */
+ case 'Z': /* set local MC address */
+ parse_lan_options(c,optarg,fdebug);
+ break;
+ default:
+ printf("Usage: %s [-bceikmtvx -a asset_tag -s ser_num -NUPREFTVY]\n",
+ progname);
+ printf(" -a tag Sets the Product Asset Tag\n");
+ printf(" -b Only show Baseboard FRU data\n");
+ printf(" -c show canonical, delimited output\n");
+ printf(" -d file Dump the binary FRU data to a file\n");
+ printf(" -e walk Every child FRU, for blade MCs\n");
+ printf(" -i 00 Get a specific FRU ID\n");
+ printf(" -k Kontron setsn, setmfgdate\n");
+ printf(" -m002000 specific MC (bus 00,sa 20,lun 00)\n");
+ printf(" -s snum Sets the Product Serial Number\n");
+ printf(" -v pver Sets the Product Version Number\n");
+ printf(" -x Display extra debug messages\n");
+ print_lan_opt_usage();
+ ret = ERR_USAGE;
+ goto do_exit;
+ }
+
+ if (ftestshow) {
+ ret = test_show_fru(binfile);
+ goto do_exit;
+ }
+
+ if (is_remote() && (!fprivset)) {
+ /* If priv not specified, not writing, and only targetted FRU,
+ can default to request user privilege. */
+ if (!fwritefru && fonlybase) parse_lan_options('V',"2",0);
+ }
+
+ ret = ipmi_getdeviceid( DevRecord, sizeof(DevRecord),fdebug);
+ if (ret == 0) {
+ uchar ipmi_maj, ipmi_min;
+ ipmi_maj = DevRecord[4] & 0x0f;
+ ipmi_min = DevRecord[4] >> 4;
+ vend_id = DevRecord[6] + (DevRecord[7] << 8) + (DevRecord[8] << 16);
+ prod_id = DevRecord[9] + (DevRecord[10] << 8);
+ show_devid( DevRecord[2], DevRecord[3], ipmi_maj, ipmi_min);
+ if (ipmi_maj < 2) do_systeminfo = 0;
+ if ((DevRecord[1] & 0x80) == 0x80) fdevsdrs = 1;
+ if (vend_id == VENDOR_NEC) fdevsdrs = 0;
+ else if (vend_id == VENDOR_SUN) {
+ do_guid = 0; /*Sun doesn't support GUID*/
+ do_systeminfo = 0; /*Sun doesn't support system info*/
+ } else if (vend_id == VENDOR_INTEL) {
+ if (is_thurley(vend_id,prod_id) || is_romley(vend_id,prod_id))
+ set_max_kcs_loops(URNLOOPS); /*longer for cmds (default 300)*/
+ }
+ } else {
+ goto do_exit;
+ }
+
+ ret = ipmi_getpicmg( DevRecord, sizeof(DevRecord),fdebug);
+ if (ret == 0) fpicmg = 1;
+ if (!fpicmg) fdevsdrs = 0; /* override, use SDR repository for FRU */
+ if (fdebug) printf("bmc_sa = %02x, fdevsdrs = %d\n",bmc_sa,fdevsdrs);
+
+ if (fset_mc) {
+ /* target a specific MC via IPMB (usu a picmg blade) */
+ if (fdebug) printf("set_mc: %02x:%02x:%02x type=%d\n",
+ g_bus,g_sa,g_lun,g_addrtype);
+ ipmi_set_mc(g_bus,g_sa,g_lun,g_addrtype);
+ fonlybase = 1; /*only show this MC*/
+ } else {
+ g_sa = bmc_sa; /* BMC_SA = 0x20 */
+ }
+ if (g_frutype == 0) {
+ g_frutype = 0x01; /* other = "Component" */
+ }
+
+ if (foemkontron) {
+#ifdef METACOMMAND
+ for (i = 0; i < optind; i++) { argv++; argc--; }
+ if (fdebug) verbose = 1;
+ ret = ipmi_kontronoem_main(NULL,argc,argv);
+#else
+ ret = LAN_ERR_NOTSUPPORT;
+#endif
+ goto do_exit;
+ }
+ npass = 1;
+
+ if (fonlybase || fonlyhsc) fbasefru = 1;
+ else {
+ /* find FRU devices from SDRs */
+ if (fpicmg && fdevsdrs) {
+ /* npass = 2 will get both SdrRep & DevSdr passes on CMM */
+ npass = 2;
+ g_addrtype = ADDR_IPMB;
+ }
+
+ nsdrs = 0;
+ for (ipass = 0; ipass < npass; ipass++)
+ {
+ ret = GetSDRRepositoryInfo(&j,&fdevsdrs);
+ if (fdebug) printf("GetSDRRepositoryInfo: ret=%x nSDRs=%d fdevsdrs=%d\n",
+ ret,j,fdevsdrs);
+ if (j == 0) {
+ /* this is an error, probably because fdevsdrs is wrong.*/
+ fdevsdrs = (fdevsdrs ^ 1);
+ if (fdebug) printf("nsdrs=0, retrying with fdevsdrs=%d\n",fdevsdrs);
+ j = 60; /*try some default num SDRs*/
+ }
+ nsdrs = j;
+ if (fdevsdrs) tag="Device SDRs";
+ else tag="SDR Repository";
+ printf("--- Scanning %s for %d SDRs ---\n",tag,nsdrs);
+
+ /* loop thru SDRs to find FRU devices */
+ recid = 0;
+ while (recid != LAST_REC)
+ {
+ if (do_reserve) {
+ /* reserve the SDR repository */
+ ret = sdr_get_reservation((uchar *)&rsvid,fdevsdrs);
+ if (fdebug) printf("sdr_get_reservation ret=%d\n",ret);
+ if (ret == 0) do_reserve = 0;
+ }
+
+ len = sizeof(sdr); /*sizeof(sdr); get 32 sdr bytes*/
+ ret = get_sdr(recid,rsvid,&nextid,sdr,&len,&cc);
+ if ((ret != 0) || (cc != 0)) {
+ printf("SDR[%04x] error %d ccode = %x\n",recid,ret,cc);
+ if ((cc == 0xC5) || (cc == 0x83)) ; /*do not stop (ARC)*/
+ else break; /*stop if errors*/
+ }
+ if (len >= MIN_SDR_SZ) {
+ if ((sdr[3] == 0x11) || (sdr[3] == 0x12)) /*SDR FRU or IPMB type*/
+ ret = get_show_fru(recid, sdr,len);
+ do_reserve = 1;
+ } /*endif get_show_fru */
+#ifdef PICMG_CHILD
+ /*
+ * Special logic for blade child MCs in PICMG ATCA systems
+ * if fchild, try all child MCs within the chassis.
+ * SDR type 12 capabilities bits (sdrdata[8]):
+ * 80 = Chassis Device
+ * 40 = Bridge
+ * 20 = IPMB Event Generator
+ * 10 = IPMB Event Receiver
+ * 08 = FRU Device
+ * 04 = SEL Device
+ * 02 = SDR Repository Device
+ * 01 = Sensor Device
+ * But all child MCs use Device SDRs anyway.
+ */
+ if (fpicmg && fchild && (sdr[3] == 0x12)) { /* PICMG MC DLR */
+ ushort _recid, _recnext;
+ int _sz;
+ uchar _sdrdata[MAX_SDR_SIZE];
+ int devsdrs_save;
+ uchar cc;
+
+ /* save the BMC globals, use IPMB MC */
+ devsdrs_save = fdevsdrs;
+ fdevsdrs = 1; /* use Device SDRs for the children*/
+ if (fdebug)
+ printf(" --- IPMB MC (sa=%02x cap=%02x id=%02x devsdrs=%d):\n",
+ sdr[5],sdr[8],sdr[12],fdevsdrs);
+ ipmi_set_mc(PICMG_SLAVE_BUS,sdr[5],sdr[6],g_addrtype);
+
+ _sz = 16;
+ ret = ipmi_cmd_mc(GET_DEVICE_ID,NULL,0,_sdrdata,&_sz,&cc,fdebug);
+ if (ret == 0 && cc == 0) {
+ /* get a new SDR Reservation ID */
+ ret = sdr_get_reservation((uchar *)&rsvid,fdevsdrs);
+ if (fdebug) printf("sdr_get_reservation ret=%d\n",ret);
+ /* Get the SDRs from the IPMB MC */
+ _recid = 0;
+ while (_recid != 0xffff)
+ {
+ _sz = sizeof(_sdrdata);
+ ret = get_sdr(_recid,rsvid,&_recnext,_sdrdata,&_sz,&cc);
+ if (fdebug) printf("get_sdr(%x) rv=%d cc=%x rlen=%d\n",
+ _recid,ret,cc,_sz);
+ if (ret != 0) {
+ printf("%04x get_sdr error %d, rlen=%d\n",_recid,ret,_sz);
+ break;
+ }
+ else if (_sz >= MIN_SDR_SZ) {
+ if ((_sdrdata[3] == 0x11) || (_sdrdata[3] == 0x12))
+ ret = get_show_fru(_recid, _sdrdata, _sz);
+ }
+ if (_recnext == _recid) _recid = 0xffff;
+ else _recid = _recnext;
+ } /*end while*/
+ } /*endif ret==0*/
+
+ /* restore BMC globals */
+ fdevsdrs = devsdrs_save;
+ ipmi_restore_mc();
+ do_reserve = 1; /* get a new SDR Reservation ID */
+ } /*endif fpicmg && fchild*/
+#endif
+
+ recid = nextid;
+ } /*end while sdrs*/
+ if (npass > 1) { /*npass==2 for PICMG child*/
+ /* Switch fdevsdrs from Device to Repository (or vice-versa) */
+ if (fdevsdrs == 0) fdevsdrs = 1;
+ else fdevsdrs = 0;
+ }
+ } /*end ipass loop*/
+ } /*endif not fonlybase*/
+
+ /* load the FRU data for Baseboard (address 0x20) */
+ printf("\n");
+ sa = g_sa; /* bmc_sa = BMC_SA = 0x20 */
+ if (fonlyhsc) { sa = 0xC0; g_addrtype = ADDR_SMI;
+ ipmi_set_mc(g_bus,sa,g_lun,g_addrtype);
+ }
+ if (g_addrtype == ADDR_IPMB)
+ ipmi_set_mc(g_bus,sa,g_lun,g_addrtype);
+
+ if (fbasefru) {
+ /* get and display the Baseboard FRU data */
+ ret = load_fru(sa,g_fruid,g_frutype,&pfru);
+ if (ret != 0) {
+ show_loadfru_error(sa,g_fruid,ret);
+ free_fru(pfru);
+ pfru = NULL;
+ goto do_exit;
+ }
+ ret = show_fru(sa,g_fruid,g_frutype,pfru);
+ if (ret != 0) printf("show_fru error = %d\n",ret);
+ }
+
+ if (fcanonical) devstr[0] = 0; /*default is empty string*/
+ else sprintf(devstr,"[%s,%02x,%02x] ", /*was by g_frutype*/
+ FruTypeString(SYS_FRUTYPE,g_fruid),sa,g_fruid);
+
+ if (!is_remote()) {
+ i = get_BiosVersion(biosver);
+ if (i == 0)
+ printf("%sBIOS Version %c %s\n",devstr,bdelim,biosver);
+ }
+
+ if (do_systeminfo) {
+ char infostr[64];
+ int len;
+ len = sizeof(infostr);
+ rv = get_system_info(1,infostr,&len); /*Firmware Version*/
+ if (rv == 0) {
+ len = sizeof(infostr);
+ rv = get_system_info(2,infostr,&len);
+ if (rv == 0) printf("%sSystem Name %c %s\n",devstr,bdelim,infostr);
+ len = sizeof(infostr);
+ rv = get_system_info(3,infostr,&len);
+ if (rv == 0) printf("%sPri Operating System%c %s\n",devstr,bdelim,infostr);
+ len = sizeof(infostr);
+ rv = get_system_info(4,infostr,&len);
+ if (rv == 0) printf("%sSec Operating System%c %s\n",devstr,bdelim,infostr);
+ } else {
+ if (fdebug && (rv == 0xC1)) /*only supported on later IPMI 2.0 fw */
+ printf("GetSystemInfo not supported on this platform\n");
+ }
+ }
+
+ if (fdump && ret == 0) {
+ /* Dump FRU to a binary file */
+ fp = fopen(binfile,"w");
+ if (fp == NULL) {
+ ret = get_LastError();
+ printf("Cannot open file %s, error %d\n",binfile,ret);
+ } else {
+ printf("Writing FRU size %d to %s ...\n",sfru,binfile);
+ len = (int)fwrite(frubuf, 1, sfru, fp);
+ fclose(fp);
+ if (len <= 0) {
+ ret = get_LastError();
+ printf("Error %d writing file %s\n",ret,binfile);
+ } else ret = 0;
+ }
+ goto do_exit;
+ }
+ else if (frestore) {
+ uchar cksum;
+ /* Restore FRU from a binary file */
+ fp = fopen(binfile,"r");
+ if (fp == NULL) {
+ ret = get_LastError();
+ printf("Cannot open file %s, error %d\n",binfile,ret);
+ } else {
+ ret = 0;
+ /* sfru and frubuf were set from load_fru above. */
+ len = (int)fread(frubuf, 1, sfru, fp);
+ if (len <= 0) {
+ ret = get_LastError();
+ printf("Error %d reading file %s\n",ret,binfile);
+ sfru = 0; /*for safety*/
+ }
+ fclose(fp);
+ if (fdebug) {
+ printf("FRU buffer from file (%d):",sfru);
+ dumpbuf(frubuf,sfru);
+ }
+ /* Do some validation of the FRU buffer header */
+ cksum = calc_cksum(&frubuf[0],7);
+ if (fdebug)
+ printf("header, len=8, cksum0 = %02x, cksum1 = %02x\n",
+ frubuf[7],cksum);
+ if (frubuf[7] != cksum) {
+ printf("Not a valid FRU file\n");
+ ret = ERR_BAD_FORMAT;
+ free_fru(frubuf);
+ }
+ if (ret == 0) { /*successfully read data*/
+ printf("Writing FRU size %d from %s ...\n",sfru,binfile);
+ ret = write_fru_data(g_fruid, 0, frubuf, sfru, fdebug);
+ free_fru(frubuf);
+ if (ret != 0) printf("write_fru error %d (0x%02x)\n",ret,ret);
+ else { /* successful, show new data */
+ ret = load_fru(sa,g_fruid,g_frutype,&pfru);
+ if (ret != 0) show_loadfru_error(sa,g_fruid,ret);
+ else ret = show_fru(sa,g_fruid,g_frutype,pfru);
+ free_fru(pfru);
+ pfru = NULL;
+ }
+ }
+ }
+ } /*end-else frestore */
+ else if ((fwritefru != 0) && ret == 0) {
+ if (fbasefru == 0) {
+ /* if not fbasefru, must reload fru because freed in get_show_fru */
+ ret = load_fru(sa,g_fruid,g_frutype,&pfru);
+ if (ret != 0) {
+ show_loadfru_error(sa,g_fruid,ret);
+ free_fru(pfru);
+ pfru = NULL;
+ goto do_exit;
+ }
+ if (fdebug) printf("Baseboard FRU buffer reloaded (%d):",sfru);
+ }
+ printf("\nWriting new product data (%s,%s,%s) ...\n",
+ prod_ver,serial_num,asset_tag);
+ ret = write_asset(asset_tag,serial_num,prod_ver,fwritefru,pfru);
+ free_fru(pfru);
+ if (ret != 0) printf("write_asset error %d (0x%02x)\n",ret,ret);
+ else { /* successful, show new data */
+ ret = load_fru(sa,g_fruid,g_frutype,&pfru);
+ if (ret != 0) show_loadfru_error(sa,g_fruid,ret);
+ else ret = show_fru(sa,g_fruid,g_frutype,pfru);
+ free_fru(pfru);
+ }
+ pfru = NULL;
+ }
+ else
+ free_fru(pfru);
+
+do_exit:
+ ipmi_close_();
+ // show_outcome(progname,ret);
+ return(ret);
+}
+
+/* end ifru.c */