diff options
author | Jörg Frings-Fürst <debian@jff-webhosting.net> | 2014-07-06 18:04:32 +0200 |
---|---|---|
committer | Jörg Frings-Fürst <debian@jff-webhosting.net> | 2014-07-06 18:04:32 +0200 |
commit | a7f89980e5b3f4b9a74c70dbc5ffe8aabd28be28 (patch) | |
tree | 41c4deec1fdfbafd7821b4ca7a9772ac0abd92f5 /util/ifru.c |
Imported Upstream version 2.9.3upstream/2.9.3
Diffstat (limited to 'util/ifru.c')
-rwxr-xr-x | util/ifru.c | 2123 |
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 */ |