summaryrefslogtreecommitdiff
path: root/lib/ipmi_fru.c
diff options
context:
space:
mode:
authorJörg Frings-Fürst <debian@jff-webhosting.net>2014-07-23 15:03:00 +0200
committerJörg Frings-Fürst <debian@jff-webhosting.net>2014-07-23 15:03:00 +0200
commitb32d92e890caac903491116e9d817aa780c0323b (patch)
tree5a135c37eaa9ac94772819a28ce5beedd18e5c4a /lib/ipmi_fru.c
parentc3445516ecd58e97de483cf4b7fafcc1104890d7 (diff)
Imported Upstream version 1.8.14upstream/1.8.14
Diffstat (limited to 'lib/ipmi_fru.c')
-rw-r--r--lib/ipmi_fru.c5209
1 files changed, 5209 insertions, 0 deletions
diff --git a/lib/ipmi_fru.c b/lib/ipmi_fru.c
new file mode 100644
index 0000000..1b2e0cd
--- /dev/null
+++ b/lib/ipmi_fru.c
@@ -0,0 +1,5209 @@
+/*
+* Copyright (c) 2003 Sun Microsystems, 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:
+*
+* Redistribution of source code must retain the above copyright
+* notice, this list of conditions and the following disclaimer.
+*
+* Redistribution 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.
+*
+* Neither the name of Sun Microsystems, Inc. or the names of
+* contributors may be used to endorse or promote products derived
+* from this software without specific prior written permission.
+*
+* This software is provided "AS IS," without a warranty of any kind.
+* ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES,
+* INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A
+* PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED.
+* SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE
+* FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING
+* OR DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL
+* SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA,
+* OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR
+* PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF
+* LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE,
+* EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+*/
+
+#include <ipmitool/ipmi.h>
+#include <ipmitool/log.h>
+#include <ipmitool/helper.h>
+#include <ipmitool/ipmi_intf.h>
+#include <ipmitool/ipmi_fru.h>
+#include <ipmitool/ipmi_mc.h>
+#include <ipmitool/ipmi_sdr.h>
+#include <ipmitool/ipmi_strings.h> /* IANA id strings */
+
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <errno.h>
+
+#if HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#define FRU_MULTIREC_CHUNK_SIZE (255 + sizeof(struct fru_multirec_header))
+
+extern int verbose;
+
+static void ipmi_fru_read_to_bin(struct ipmi_intf * intf, char * pFileName, uint8_t fruId);
+static void ipmi_fru_write_from_bin(struct ipmi_intf * intf, char * pFileName, uint8_t fruId);
+static int ipmi_fru_upg_ekeying(struct ipmi_intf * intf, char * pFileName, uint8_t fruId);
+static int ipmi_fru_get_multirec_location_from_fru(struct ipmi_intf * intf, uint8_t fruId,
+ struct fru_info *pFruInfo, uint32_t * pRetLocation,
+ uint32_t * pRetSize);
+static int ipmi_fru_get_multirec_from_file(char * pFileName, uint8_t * pBufArea,
+ uint32_t size, uint32_t offset);
+static int ipmi_fru_get_multirec_size_from_file(char * pFileName, uint32_t * pSize, uint32_t * pOffset);
+int ipmi_fru_get_adjust_size_from_buffer(uint8_t *pBufArea, uint32_t *pSize);
+static void ipmi_fru_picmg_ext_print(uint8_t * fru_data, int off, int length);
+
+static int ipmi_fru_set_field_string(struct ipmi_intf * intf, unsigned
+ char fruId, uint8_t f_type, uint8_t f_index, char *f_string);
+static int
+ipmi_fru_set_field_string_rebuild(struct ipmi_intf * intf, uint8_t fruId,
+ struct fru_info fru, struct fru_header header,
+ uint8_t f_type, uint8_t f_index, char *f_string);
+
+static void
+fru_area_print_multirec_bloc(struct ipmi_intf * intf, struct fru_info * fru,
+ uint8_t id, uint32_t offset);
+int
+read_fru_area(struct ipmi_intf * intf, struct fru_info *fru, uint8_t id,
+ uint32_t offset, uint32_t length, uint8_t *frubuf);
+void free_fru_bloc(t_ipmi_fru_bloc *bloc);
+
+/* get_fru_area_str - Parse FRU area string from raw data
+*
+* @data: raw FRU data
+* @offset: offset into data for area
+*
+* returns pointer to FRU area string
+*/
+char * get_fru_area_str(uint8_t * data, uint32_t * offset)
+{
+ static const char bcd_plus[] = "0123456789 -.:,_";
+ char * str;
+ int len, off, size, i, j, k, typecode;
+ union {
+ uint32_t bits;
+ char chars[4];
+ } u;
+
+ size = 0;
+ off = *offset;
+
+ /* bits 6:7 contain format */
+ typecode = ((data[off] & 0xC0) >> 6);
+
+ // printf("Typecode:%i\n", typecode);
+ /* bits 0:5 contain length */
+ len = data[off++];
+ len &= 0x3f;
+
+ switch (typecode) {
+ case 0: /* 00b: binary/unspecified */
+ /* hex dump -> 2x length */
+ size = (len*2);
+ break;
+ case 2: /* 10b: 6-bit ASCII */
+ /* 4 chars per group of 1-3 bytes */
+ size = ((((len+2)*4)/3) & ~3);
+ break;
+ case 3: /* 11b: 8-bit ASCII */
+ case 1: /* 01b: BCD plus */
+ /* no length adjustment */
+ size = len;
+ break;
+ }
+
+ if (size < 1) {
+ *offset = off;
+ return NULL;
+ }
+ str = malloc(size+1);
+ if (str == NULL)
+ return NULL;
+ memset(str, 0, size+1);
+
+ if (len == 0) {
+ str[0] = '\0';
+ *offset = off;
+ return str;
+ }
+
+ switch (typecode) {
+ case 0: /* Binary */
+ strncpy(str, buf2str(&data[off], len), len*2);
+ break;
+
+ case 1: /* BCD plus */
+ for (k=0; k<len; k++)
+ str[k] = bcd_plus[(data[off+k] & 0x0f)];
+ str[k] = '\0';
+ break;
+
+ case 2: /* 6-bit ASCII */
+ for (i=j=0; i<len; i+=3) {
+ u.bits = 0;
+ k = ((len-i) < 3 ? (len-i) : 3);
+#if WORDS_BIGENDIAN
+ u.chars[3] = data[off+i];
+ u.chars[2] = (k > 1 ? data[off+i+1] : 0);
+ u.chars[1] = (k > 2 ? data[off+i+2] : 0);
+#define CHAR_IDX 3
+#else
+ memcpy((void *)&u.bits, &data[off+i], k);
+#define CHAR_IDX 0
+#endif
+ for (k=0; k<4; k++) {
+ str[j++] = ((u.chars[CHAR_IDX] & 0x3f) + 0x20);
+ u.bits >>= 6;
+ }
+ }
+ str[j] = '\0';
+ break;
+
+ case 3:
+ memcpy(str, &data[off], len);
+ str[len] = '\0';
+ break;
+ }
+
+ off += len;
+ *offset = off;
+
+ return str;
+}
+
+/* is_valid_filename - checks file/path supplied by user
+ *
+ * input_filename - user input string
+ *
+ * returns 0 if path is ok
+ * returns (-1) if path is NULL
+ * returns (-2) if path is too short
+ * returns (-3) if path is too long
+ */
+int
+is_valid_filename(const char *input_filename)
+{
+ if (input_filename == NULL) {
+ lprintf(LOG_ERR, "ERROR: NULL pointer passed.");
+ return (-1);
+ }
+
+ if (strlen(input_filename) < 1) {
+ lprintf(LOG_ERR, "File/path is invalid.");
+ return (-2);
+ }
+
+ if (strlen(input_filename) >= 512) {
+ lprintf(LOG_ERR, "File/path must be shorter than 512 bytes.");
+ return (-3);
+ }
+
+ return 0;
+} /* is_valid_filename() */
+
+/* build_fru_bloc - build fru bloc for write protection
+*
+* @intf: ipmi interface
+* @fru_info: information about FRU device
+* @id : Fru id
+* @soffset : Source offset (from buffer)
+* @doffset : Destination offset (in device)
+* @length : Size of data to write (in bytes)
+* @pFrubuf : Pointer on data to write
+*
+* returns 0 on success
+* returns -1 on error
+*/
+#define FRU_NUM_BLOC_COMMON_HEADER 6
+t_ipmi_fru_bloc *
+build_fru_bloc(struct ipmi_intf * intf, struct fru_info *fru, uint8_t id)
+{
+ t_ipmi_fru_bloc * p_first, * p_bloc, * p_new;
+ struct ipmi_rs * rsp;
+ struct ipmi_rq req;
+ struct fru_header header;
+ struct fru_multirec_header rec_hdr;
+ uint8_t msg_data[4];
+ uint32_t off;
+ uint16_t i;
+
+ /*
+ * get COMMON Header format
+ */
+ msg_data[0] = id;
+ msg_data[1] = 0;
+ msg_data[2] = 0;
+ msg_data[3] = 8;
+
+ memset(&req, 0, sizeof(req));
+ req.msg.netfn = IPMI_NETFN_STORAGE;
+ req.msg.cmd = GET_FRU_DATA;
+ req.msg.data = msg_data;
+ req.msg.data_len = 4;
+
+ rsp = intf->sendrecv(intf, &req);
+
+ if (rsp == NULL) {
+ lprintf(LOG_ERR, " Device not present (No Response)");
+ return NULL;
+ }
+
+ if (rsp->ccode > 0) {
+ lprintf(LOG_ERR," Device not present (%s)",
+ val2str(rsp->ccode, completion_code_vals));
+ return NULL;
+ }
+
+ if (verbose > 1) {
+ printbuf(rsp->data, rsp->data_len, "FRU DATA");
+ }
+
+ memcpy(&header, rsp->data + 1, 8);
+
+ /* verify header checksum */
+ if (ipmi_csum((uint8_t *)&header, 8)) {
+ lprintf(LOG_ERR, " Bad header checksum");
+ return NULL;
+ }
+
+ if (header.version != 1) {
+ lprintf(LOG_ERR, " Unknown FRU header version 0x%02x", header.version);
+ return NULL;
+ }
+
+ /******************************************
+ Malloc and fill up the bloc contents
+ *******************************************/
+
+ // Common header
+ p_first = malloc(sizeof(struct ipmi_fru_bloc));
+ if (!p_first) {
+ lprintf(LOG_ERR, "ipmitool: malloc failure");
+ return NULL;
+ }
+
+ p_bloc = p_first;
+ p_bloc->next = NULL;
+ p_bloc->start= 0;
+ p_bloc->size = fru->size;
+ strcpy((char *)p_bloc->blocId, "Common Header Section");
+
+ for (i = 0; i < 4; i++) {
+ if (header.offsets[i]) {
+ p_new = malloc(sizeof(struct ipmi_fru_bloc));
+ if (!p_new) {
+ lprintf(LOG_ERR, "ipmitool: malloc failure");
+ free_fru_bloc(p_first);
+ return NULL;
+ }
+
+ p_new->next = NULL;
+ p_new->start = header.offsets[i] * 8;
+ p_new->size = fru->size - p_new->start;
+
+ strncpy((char *)p_new->blocId, section_id[i], sizeof(p_new->blocId));
+ /* Make sure string is null terminated */
+ p_new->blocId[sizeof(p_new->blocId)-1] = 0;
+
+ p_bloc->next = p_new;
+ p_bloc->size = p_new->start - p_bloc->start;
+ p_bloc = p_new;
+ }
+ }
+
+ // Multi
+ if (header.offset.multi) {
+ off = header.offset.multi * 8;
+
+ do {
+ /*
+ * check for odd offset for the case of fru devices
+ * accessed by words
+ */
+ if (fru->access && (off & 1)) {
+ lprintf(LOG_ERR, " Unaligned offset for a block: %d", off);
+ /* increment offset */
+ off++;
+ break;
+ }
+
+ if (read_fru_area(intf, fru, id, off, 5,
+ (uint8_t *) &rec_hdr) < 0) {
+ break;
+ }
+
+ p_new = malloc(sizeof(struct ipmi_fru_bloc));
+ if (!p_new) {
+ lprintf(LOG_ERR, "ipmitool: malloc failure");
+ free_fru_bloc(p_first);
+ return NULL;
+ }
+
+ p_new->next = NULL;
+ p_new->start = off;
+ p_new->size = fru->size - p_new->start;
+ sprintf((char *)p_new->blocId, "Multi-Rec Area: Type %i",
+ rec_hdr.type);
+
+ p_bloc->next = p_new;
+ p_bloc->size = p_new->start - p_bloc->start;
+ p_bloc = p_new;
+
+ off += rec_hdr.len + sizeof(struct fru_multirec_header);
+
+ /* verify record header */
+ if (ipmi_csum((uint8_t *)&rec_hdr,
+ sizeof(struct fru_multirec_header))) {
+ /* can't reliably judge for the rest space */
+ break;
+ }
+ } while (!(rec_hdr.format & 0x80) && (off < fru->size));
+
+ lprintf(LOG_DEBUG,"Multi-Record area ends at: %i (%xh)", off, off);
+
+ if (fru->size > off) {
+ // Bloc for remaining space
+ p_new = malloc(sizeof(struct ipmi_fru_bloc));
+ if (!p_new) {
+ lprintf(LOG_ERR, "ipmitool: malloc failure");
+ free_fru_bloc(p_first);
+ return NULL;
+ }
+
+ p_new->next = NULL;
+ p_new->start = off;
+ p_new->size = fru->size - p_new->start;
+ strcpy((char *)p_new->blocId, "Unused space");
+
+ p_bloc->next = p_new;
+ p_bloc->size = p_new->start - p_bloc->start;
+ }
+ }
+
+ /* Dump blocs */
+ for(p_bloc = p_first, i = 0; p_bloc; p_bloc = p_bloc->next) {
+ lprintf(LOG_DEBUG ,"Bloc Numb : %i", i++);
+ lprintf(LOG_DEBUG ,"Bloc Id : %s", p_bloc->blocId);
+ lprintf(LOG_DEBUG ,"Bloc Start: %i", p_bloc->start);
+ lprintf(LOG_DEBUG ,"Bloc Size : %i", p_bloc->size);
+ lprintf(LOG_DEBUG ,"");
+ }
+
+ return p_first;
+}
+
+void
+free_fru_bloc(t_ipmi_fru_bloc *bloc)
+{
+ t_ipmi_fru_bloc * del;
+
+ while (bloc) {
+ del = bloc;
+ bloc = bloc->next;
+ free(del);
+ del = NULL;
+ }
+}
+
+/*
+ * write FRU[doffset:length] from the pFrubuf[soffset:length]
+ * rc=1 on success
+**/
+int
+write_fru_area(struct ipmi_intf * intf, struct fru_info *fru, uint8_t id,
+ uint16_t soffset, uint16_t doffset,
+ uint16_t length, uint8_t *pFrubuf)
+{
+ uint16_t tmp, finish;
+ struct ipmi_rs * rsp;
+ struct ipmi_rq req;
+ uint8_t msg_data[255+3];
+ uint16_t writeLength;
+ uint16_t found_bloc = 0;
+
+ finish = doffset + length; /* destination offset */
+ if (finish > fru->size)
+ {
+ lprintf(LOG_ERROR, "Return error");
+ return -1;
+ }
+
+ if (fru->access && ((doffset & 1) || (length & 1))) {
+ lprintf(LOG_ERROR, "Odd offset or length specified");
+ return (-1);
+ }
+
+ t_ipmi_fru_bloc * fru_bloc = build_fru_bloc(intf, fru, id);
+ t_ipmi_fru_bloc * saved_fru_bloc = fru_bloc;
+
+ memset(&req, 0, sizeof(req));
+ req.msg.netfn = IPMI_NETFN_STORAGE;
+ req.msg.cmd = SET_FRU_DATA;
+ req.msg.data = msg_data;
+
+ /* initialize request size only once */
+ if (fru->max_write_size == 0) {
+ uint16_t max_rq_size = ipmi_intf_get_max_request_data_size(intf);
+
+ /* validate lower bound of the maximum request data size */
+ if (max_rq_size <= 3) {
+ lprintf(LOG_ERROR, "Maximum request size is too small to send "
+ "a write request");
+ return -1;
+ }
+
+ /*
+ * Write FRU Info command returns the number of written bytes in
+ * a single byte field.
+ */
+ if (max_rq_size - 3 > 255) {
+ /* Limit the max write size with 255 bytes. */
+ fru->max_write_size = 255;
+ } else {
+ /* subtract 1 byte for FRU ID an 2 bytes for offset */
+ fru->max_write_size = max_rq_size - 3;
+ }
+
+ /* check word access */
+ if (fru->access) {
+ fru->max_write_size &= ~1;
+ }
+ }
+
+ do {
+ uint16_t end_bloc;
+ uint8_t protected_bloc = 0;
+
+ /* Write per bloc, try to find the end of a bloc*/
+ while (fru_bloc && fru_bloc->start + fru_bloc->size <= doffset) {
+ fru_bloc = fru_bloc->next;
+ found_bloc++;
+ }
+
+ if (fru_bloc && fru_bloc->start + fru_bloc->size < finish) {
+ end_bloc = fru_bloc->start + fru_bloc->size;
+ } else {
+ end_bloc = finish;
+ }
+
+ /* calculate write length */
+ tmp = end_bloc - doffset;
+
+ /* check that write length is more than maximum request size */
+ if (tmp > fru->max_write_size) {
+ writeLength = fru->max_write_size;
+ } else {
+ writeLength = tmp;
+ }
+
+ /* copy fru data */
+ memcpy(&msg_data[3], pFrubuf + soffset, writeLength);
+
+ /* check word access */
+ if (fru->access) {
+ writeLength &= ~1;
+ }
+
+ tmp = doffset;
+ if (fru->access) {
+ tmp >>= 1;
+ }
+
+ msg_data[0] = id;
+ msg_data[1] = (uint8_t)tmp;
+ msg_data[2] = (uint8_t)(tmp >> 8);
+ req.msg.data_len = writeLength + 3;
+
+ if(fru_bloc) {
+ lprintf(LOG_INFO,"Writing %d bytes (Bloc #%i: %s)",
+ writeLength, found_bloc, fru_bloc->blocId);
+ } else {
+ lprintf(LOG_INFO,"Writing %d bytes", writeLength);
+ }
+
+ rsp = intf->sendrecv(intf, &req);
+ if (!rsp) {
+ break;
+ }
+
+ if (rsp->ccode == 0xc7 || rsp->ccode == 0xc8 || rsp->ccode == 0xca) {
+ if (fru->max_write_size > 8) {
+ fru->max_write_size -= 8;
+ lprintf(LOG_INFO, "Retrying FRU write with request size %d",
+ fru->max_write_size);
+ continue;
+ }
+ } else if(rsp->ccode == 0x80) {
+ rsp->ccode = 0;
+ // Write protected section
+ protected_bloc = 1;
+ }
+
+ if (rsp->ccode > 0)
+ break;
+
+ if (protected_bloc == 0) {
+ // Write OK, bloc not protected, continue
+ lprintf(LOG_INFO,"Wrote %d bytes", writeLength);
+ doffset += writeLength;
+ soffset += writeLength;
+ } else {
+ if(fru_bloc) {
+ // Bloc protected, advise user and jump over protected bloc
+ lprintf(LOG_INFO,
+ "Bloc [%s] protected at offset: %i (size %i bytes)",
+ fru_bloc->blocId, fru_bloc->start, fru_bloc->size);
+ lprintf(LOG_INFO,"Jumping over this bloc");
+ } else {
+ lprintf(LOG_INFO,
+ "Remaining FRU is protected following offset: %i",
+ doffset);
+ }
+ soffset += end_bloc - doffset;
+ doffset = end_bloc;
+ }
+ } while (doffset < finish);
+
+ if (saved_fru_bloc) {
+ free_fru_bloc(saved_fru_bloc);
+ }
+
+ return doffset >= finish;
+}
+
+/* read_fru_area - fill in frubuf[offset:length] from the FRU[offset:length]
+*
+* @intf: ipmi interface
+* @fru: fru info
+* @id: fru id
+* @offset: offset into buffer
+* @length: how much to read
+* @frubuf: buffer read into
+*
+* returns -1 on error
+* returns 0 if successful
+*/
+int
+read_fru_area(struct ipmi_intf * intf, struct fru_info *fru, uint8_t id,
+ uint32_t offset, uint32_t length, uint8_t *frubuf)
+{
+ uint32_t off = offset, tmp, finish;
+ struct ipmi_rs * rsp;
+ struct ipmi_rq req;
+ uint8_t msg_data[4];
+
+ if (offset > fru->size) {
+ lprintf(LOG_ERR, "Read FRU Area offset incorrect: %d > %d",
+ offset, fru->size);
+ return -1;
+ }
+
+ finish = offset + length;
+ if (finish > fru->size) {
+ finish = fru->size;
+ lprintf(LOG_NOTICE, "Read FRU Area length %d too large, "
+ "Adjusting to %d",
+ offset + length, finish - offset);
+ }
+
+ memset(&req, 0, sizeof(req));
+ req.msg.netfn = IPMI_NETFN_STORAGE;
+ req.msg.cmd = GET_FRU_DATA;
+ req.msg.data = msg_data;
+ req.msg.data_len = 4;
+
+ if (fru->max_read_size == 0) {
+ uint16_t max_rs_size = ipmi_intf_get_max_response_data_size(intf) - 1;
+
+ /* validate lower bound of the maximum response data size */
+ if (max_rs_size <= 1) {
+ lprintf(LOG_ERROR, "Maximum response size is too small to send "
+ "a read request");
+ return -1;
+ }
+
+ /*
+ * Read FRU Info command may read up to 255 bytes of data.
+ */
+ if (max_rs_size - 1 > 255) {
+ /* Limit the max read size with 255 bytes. */
+ fru->max_read_size = 255;
+ } else {
+ /* subtract 1 byte for bytes count */
+ fru->max_read_size = max_rs_size - 1;
+ }
+
+ /* check word access */
+ if (fru->access) {
+ fru->max_read_size &= ~1;
+ }
+ }
+
+ do {
+ tmp = fru->access ? off >> 1 : off;
+ msg_data[0] = id;
+ msg_data[1] = (uint8_t)(tmp & 0xff);
+ msg_data[2] = (uint8_t)(tmp >> 8);
+ tmp = finish - off;
+ if (tmp > fru->max_read_size)
+ msg_data[3] = (uint8_t)fru->max_read_size;
+ else
+ msg_data[3] = (uint8_t)tmp;
+
+ rsp = intf->sendrecv(intf, &req);
+ if (rsp == NULL) {
+ lprintf(LOG_NOTICE, "FRU Read failed");
+ break;
+ }
+ if (rsp->ccode > 0) {
+ /* if we get C8h or CAh completion code then we requested too
+ * many bytes at once so try again with smaller size */
+ if ((rsp->ccode == 0xc8 || rsp->ccode == 0xca)
+ && fru->max_read_size > 8) {
+ if (fru->max_read_size > 32) {
+ /* subtract read length more aggressively */
+ fru->max_read_size -= 8;
+ } else {
+ /* subtract length less aggressively */
+ fru->max_read_size--;
+ }
+
+ lprintf(LOG_INFO, "Retrying FRU read with request size %d",
+ fru->max_read_size);
+ continue;
+ }
+
+ lprintf(LOG_NOTICE, "FRU Read failed: %s",
+ val2str(rsp->ccode, completion_code_vals));
+ break;
+ }
+
+ tmp = fru->access ? rsp->data[0] << 1 : rsp->data[0];
+ memcpy(frubuf, rsp->data + 1, tmp);
+ off += tmp;
+ frubuf += tmp;
+ /* sometimes the size returned in the Info command
+ * is too large. return 0 so higher level function
+ * still attempts to parse what was returned */
+ if (tmp == 0 && off < finish) {
+ return 0;
+ }
+ } while (off < finish);
+
+ if (off < finish) {
+ return -1;
+ }
+
+ return 0;
+}
+
+/* read_fru_area - fill in frubuf[offset:length] from the FRU[offset:length]
+*
+* @intf: ipmi interface
+* @fru: fru info
+* @id: fru id
+* @offset: offset into buffer
+* @length: how much to read
+* @frubuf: buffer read into
+*
+* returns -1 on error
+* returns 0 if successful
+*/
+int
+read_fru_area_section(struct ipmi_intf * intf, struct fru_info *fru, uint8_t id,
+ uint32_t offset, uint32_t length, uint8_t *frubuf)
+{
+ static uint32_t fru_data_rqst_size = 20;
+ uint32_t off = offset, tmp, finish;
+ struct ipmi_rs * rsp;
+ struct ipmi_rq req;
+ uint8_t msg_data[4];
+
+ if (offset > fru->size) {
+ lprintf(LOG_ERR, "Read FRU Area offset incorrect: %d > %d",
+ offset, fru->size);
+ return -1;
+ }
+
+ finish = offset + length;
+ if (finish > fru->size) {
+ finish = fru->size;
+ lprintf(LOG_NOTICE, "Read FRU Area length %d too large, "
+ "Adjusting to %d",
+ offset + length, finish - offset);
+ }
+
+ memset(&req, 0, sizeof(req));
+ req.msg.netfn = IPMI_NETFN_STORAGE;
+ req.msg.cmd = GET_FRU_DATA;
+ req.msg.data = msg_data;
+ req.msg.data_len = 4;
+
+#ifdef LIMIT_ALL_REQUEST_SIZE
+ if (fru_data_rqst_size > 16)
+#else
+ if (fru->access && fru_data_rqst_size > 16)
+#endif
+ fru_data_rqst_size = 16;
+ do {
+ tmp = fru->access ? off >> 1 : off;
+ msg_data[0] = id;
+ msg_data[1] = (uint8_t)(tmp & 0xff);
+ msg_data[2] = (uint8_t)(tmp >> 8);
+ tmp = finish - off;
+ if (tmp > fru_data_rqst_size)
+ msg_data[3] = (uint8_t)fru_data_rqst_size;
+ else
+ msg_data[3] = (uint8_t)tmp;
+
+ rsp = intf->sendrecv(intf, &req);
+ if (rsp == NULL) {
+ lprintf(LOG_NOTICE, "FRU Read failed");
+ break;
+ }
+ if (rsp->ccode > 0) {
+ /* if we get C7 or C8 or CA return code then we requested too
+ * many bytes at once so try again with smaller size */
+ if ((rsp->ccode == 0xc7 || rsp->ccode == 0xc8 || rsp->ccode == 0xca) &&
+ (--fru_data_rqst_size > 8)) {
+ lprintf(LOG_INFO, "Retrying FRU read with request size %d",
+ fru_data_rqst_size);
+ continue;
+ }
+ lprintf(LOG_NOTICE, "FRU Read failed: %s",
+ val2str(rsp->ccode, completion_code_vals));
+ break;
+ }
+
+ tmp = fru->access ? rsp->data[0] << 1 : rsp->data[0];
+ memcpy((frubuf + off)-offset, rsp->data + 1, tmp);
+ off += tmp;
+
+ /* sometimes the size returned in the Info command
+ * is too large. return 0 so higher level function
+ * still attempts to parse what was returned */
+ if (tmp == 0 && off < finish)
+ return 0;
+
+ } while (off < finish);
+
+ if (off < finish)
+ return -1;
+
+ return 0;
+}
+
+
+static void
+fru_area_print_multirec_bloc(struct ipmi_intf * intf, struct fru_info * fru,
+ uint8_t id, uint32_t offset)
+{
+ uint8_t * fru_data = NULL;
+ uint32_t fru_len, i;
+ struct fru_multirec_header * h;
+ uint32_t last_off, len;
+
+ i = last_off = offset;
+ fru_len = 0;
+
+ fru_data = malloc(fru->size + 1);
+ if (fru_data == NULL) {
+ lprintf(LOG_ERR, " Out of memory!");
+ return;
+ }
+
+ memset(fru_data, 0, fru->size + 1);
+
+ do {
+ h = (struct fru_multirec_header *) (fru_data + i);
+
+ // read area in (at most) FRU_MULTIREC_CHUNK_SIZE bytes at a time
+ if ((last_off < (i + sizeof(*h))) || (last_off < (i + h->len)))
+ {
+ len = fru->size - last_off;
+ if (len > FRU_MULTIREC_CHUNK_SIZE)
+ len = FRU_MULTIREC_CHUNK_SIZE;
+
+ if (read_fru_area(intf, fru, id, last_off, len, fru_data) < 0)
+ break;
+
+ last_off += len;
+ }
+
+ //printf("Bloc Numb : %i\n", counter);
+ printf("Bloc Start: %i\n", i);
+ printf("Bloc Size : %i\n", h->len);
+ printf("\n");
+
+ i += h->len + sizeof (struct fru_multirec_header);
+ } while (!(h->format & 0x80));
+
+ i = offset;
+ do {
+ h = (struct fru_multirec_header *) (fru_data + i);
+
+ printf("Bloc Start: %i\n", i);
+ printf("Bloc Size : %i\n", h->len);
+ printf("\n");
+
+ i += h->len + sizeof (struct fru_multirec_header);
+ } while (!(h->format & 0x80));
+
+ lprintf(LOG_DEBUG ,"Multi-Record area ends at: %i (%xh)",i,i);
+
+ free(fru_data);
+ fru_data = NULL;
+}
+
+
+/* fru_area_print_chassis - Print FRU Chassis Area
+*
+* @intf: ipmi interface
+* @fru: fru info
+* @id: fru id
+* @offset: offset pointer
+*/
+static void
+fru_area_print_chassis(struct ipmi_intf * intf, struct fru_info * fru,
+ uint8_t id, uint32_t offset)
+{
+ char * fru_area;
+ uint8_t * fru_data;
+ uint32_t fru_len, i;
+ uint8_t tmp[2];
+
+ fru_len = 0;
+
+ /* read enough to check length field */
+ if (read_fru_area(intf, fru, id, offset, 2, tmp) == 0) {
+ fru_len = 8 * tmp[1];
+ }
+
+ if (fru_len == 0) {
+ return;
+ }
+
+ fru_data = malloc(fru_len);
+ if (fru_data == NULL) {
+ lprintf(LOG_ERR, "ipmitool: malloc failure");
+ return;
+ }
+
+ memset(fru_data, 0, fru_len);
+
+ /* read in the full fru */
+ if (read_fru_area(intf, fru, id, offset, fru_len, fru_data) < 0) {
+ free(fru_data);
+ fru_data = NULL;
+ return;
+ }
+
+ /*
+ * skip first two bytes which specify
+ * fru area version and fru area length
+ */
+ i = 2;
+
+ printf(" Chassis Type : %s\n",
+ chassis_type_desc[fru_data[i] >
+ (sizeof(chassis_type_desc)/sizeof(chassis_type_desc[0])) - 1 ?
+ 2 : fru_data[i]]);
+
+ i++;
+
+ fru_area = get_fru_area_str(fru_data, &i);
+ if (fru_area != NULL) {
+ if (strlen(fru_area) > 0) {
+ printf(" Chassis Part Number : %s\n", fru_area);
+ }
+ free(fru_area);
+ fru_area = NULL;
+ }
+
+ fru_area = get_fru_area_str(fru_data, &i);
+ if (fru_area != NULL) {
+ if (strlen(fru_area) > 0) {
+ printf(" Chassis Serial : %s\n", fru_area);
+ }
+ free(fru_area);
+ fru_area = NULL;
+ }
+
+ /* read any extra fields */
+ while ((fru_data[i] != 0xc1) && (i < fru_len))
+ {
+ int j = i;
+ fru_area = get_fru_area_str(fru_data, &i);
+ if (fru_area != NULL) {
+ if (strlen(fru_area) > 0) {
+ printf(" Chassis Extra : %s\n", fru_area);
+ }
+ free(fru_area);
+ fru_area = NULL;
+ }
+
+ if (i == j) {
+ break;
+ }
+ }
+
+ if (fru_area != NULL) {
+ free(fru_data);
+ fru_data = NULL;
+ }
+}
+
+/* fru_area_print_board - Print FRU Board Area
+*
+* @intf: ipmi interface
+* @fru: fru info
+* @id: fru id
+* @offset: offset pointer
+*/
+static void
+fru_area_print_board(struct ipmi_intf * intf, struct fru_info * fru,
+ uint8_t id, uint32_t offset)
+{
+ char * fru_area;
+ uint8_t * fru_data;
+ uint32_t fru_len;
+ uint32_t i;
+ time_t tval;
+ uint8_t tmp[2];
+
+ fru_len = 0;
+
+ /* read enough to check length field */
+ if (read_fru_area(intf, fru, id, offset, 2, tmp) == 0) {
+ fru_len = 8 * tmp[1];
+ }
+
+ if (fru_len <= 0) {
+ return;
+ }
+
+ fru_data = malloc(fru_len);
+ if (fru_data == NULL) {
+ lprintf(LOG_ERR, "ipmitool: malloc failure");
+ return;
+ }
+
+ memset(fru_data, 0, fru_len);
+
+ /* read in the full fru */
+ if (read_fru_area(intf, fru, id, offset, fru_len, fru_data) < 0) {
+ free(fru_data);
+ fru_data = NULL;
+ return;
+ }
+
+ /*
+ * skip first three bytes which specify
+ * fru area version, fru area length
+ * and fru board language
+ */
+ i = 3;
+
+ tval=((fru_data[i+2] << 16) + (fru_data[i+1] << 8) + (fru_data[i]));
+ tval=tval * 60;
+ tval=tval + secs_from_1970_1996;
+ printf(" Board Mfg Date : %s", asctime(localtime(&tval)));
+ i += 3; /* skip mfg. date time */
+
+ fru_area = get_fru_area_str(fru_data, &i);
+ if (fru_area != NULL) {
+ if (strlen(fru_area) > 0) {
+ printf(" Board Mfg : %s\n", fru_area);
+ }
+ free(fru_area);
+ fru_area = NULL;
+ }
+
+ fru_area = get_fru_area_str(fru_data, &i);
+ if (fru_area != NULL) {
+ if (strlen(fru_area) > 0) {
+ printf(" Board Product : %s\n", fru_area);
+ }
+ free(fru_area);
+ fru_area = NULL;
+ }
+
+ fru_area = get_fru_area_str(fru_data, &i);
+ if (fru_area != NULL) {
+ if (strlen(fru_area) > 0) {
+ printf(" Board Serial : %s\n", fru_area);
+ }
+ free(fru_area);
+ fru_area = NULL;
+ }
+
+ fru_area = get_fru_area_str(fru_data, &i);
+ if (fru_area != NULL) {
+ if (strlen(fru_area) > 0) {
+ printf(" Board Part Number : %s\n", fru_area);
+ }
+ free(fru_area);
+ fru_area = NULL;
+ }
+
+ fru_area = get_fru_area_str(fru_data, &i);
+ if (fru_area != NULL) {
+ if (strlen(fru_area) > 0 && verbose > 0) {
+ printf(" Board FRU ID : %s\n", fru_area);
+ }
+ free(fru_area);
+ fru_area = NULL;
+ }
+
+ /* read any extra fields */
+ while ((fru_data[i] != 0xc1) && (i < fru_len))
+ {
+ int j = i;
+ fru_area = get_fru_area_str(fru_data, &i);
+ if (fru_area != NULL) {
+ if (strlen(fru_area) > 0) {
+ printf(" Board Extra : %s\n", fru_area);
+ }
+ free(fru_area);
+ fru_area = NULL;
+ }
+ if (i == j)
+ break;
+ }
+
+ if (fru_area != NULL) {
+ free(fru_data);
+ fru_data = NULL;
+ }
+}
+
+/* fru_area_print_product - Print FRU Product Area
+*
+* @intf: ipmi interface
+* @fru: fru info
+* @id: fru id
+* @offset: offset pointer
+*/
+static void
+fru_area_print_product(struct ipmi_intf * intf, struct fru_info * fru,
+ uint8_t id, uint32_t offset)
+{
+ char * fru_area;
+ uint8_t * fru_data;
+ uint32_t fru_len, i;
+ uint8_t tmp[2];
+
+ fru_len = 0;
+
+ /* read enough to check length field */
+ if (read_fru_area(intf, fru, id, offset, 2, tmp) == 0) {
+ fru_len = 8 * tmp[1];
+ }
+
+ if (fru_len == 0) {
+ return;
+ }
+
+ fru_data = malloc(fru_len);
+ if (fru_data == NULL) {
+ lprintf(LOG_ERR, "ipmitool: malloc failure");
+ return;
+ }
+
+ memset(fru_data, 0, fru_len);
+
+
+ /* read in the full fru */
+ if (read_fru_area(intf, fru, id, offset, fru_len, fru_data) < 0) {
+ free(fru_data);
+ fru_data = NULL;
+ return;
+ }
+
+ /*
+ * skip first three bytes which specify
+ * fru area version, fru area length
+ * and fru board language
+ */
+ i = 3;
+
+ fru_area = get_fru_area_str(fru_data, &i);
+ if (fru_area != NULL) {
+ if (strlen(fru_area) > 0) {
+ printf(" Product Manufacturer : %s\n", fru_area);
+ }
+ free(fru_area);
+ fru_area = NULL;
+ }
+
+ fru_area = get_fru_area_str(fru_data, &i);
+ if (fru_area != NULL) {
+ if (strlen(fru_area) > 0) {
+ printf(" Product Name : %s\n", fru_area);
+ }
+ free(fru_area);
+ fru_area = NULL;
+ }
+
+ fru_area = get_fru_area_str(fru_data, &i);
+ if (fru_area != NULL) {
+ if (strlen(fru_area) > 0) {
+ printf(" Product Part Number : %s\n", fru_area);
+ }
+ free(fru_area);
+ fru_area = NULL;
+ }
+
+ fru_area = get_fru_area_str(fru_data, &i);
+ if (fru_area != NULL) {
+ if (strlen(fru_area) > 0) {
+ printf(" Product Version : %s\n", fru_area);
+ }
+ free(fru_area);
+ fru_area = NULL;
+ }
+
+ fru_area = get_fru_area_str(fru_data, &i);
+ if (fru_area != NULL) {
+ if (strlen(fru_area) > 0) {
+ printf(" Product Serial : %s\n", fru_area);
+ }
+ free(fru_area);
+ fru_area = NULL;
+ }
+
+ fru_area = get_fru_area_str(fru_data, &i);
+ if (fru_area != NULL) {
+ if (strlen(fru_area) > 0) {
+ printf(" Product Asset Tag : %s\n", fru_area);
+ }
+ free(fru_area);
+ fru_area = NULL;
+ }
+
+ fru_area = get_fru_area_str(fru_data, &i);
+ if (fru_area != NULL) {
+ if (strlen(fru_area) > 0 && verbose > 0) {
+ printf(" Product FRU ID : %s\n", fru_area);
+ }
+ free(fru_area);
+ fru_area = NULL;
+ }
+
+ /* read any extra fields */
+ while ((fru_data[i] != 0xc1) && (i < fru_len))
+ {
+ int j = i;
+ fru_area = get_fru_area_str(fru_data, &i);
+ if (fru_area != NULL) {
+ if (strlen(fru_area) > 0) {
+ printf(" Product Extra : %s\n", fru_area);
+ }
+ free(fru_area);
+ fru_area = NULL;
+ }
+ if (i == j)
+ break;
+ }
+
+ if (fru_area != NULL) {
+ free(fru_data);
+ fru_data = NULL;
+ }
+}
+
+/* fru_area_print_multirec - Print FRU Multi Record Area
+*
+* @intf: ipmi interface
+* @fru: fru info
+* @id: fru id
+* @offset: offset pointer
+*/
+static void
+fru_area_print_multirec(struct ipmi_intf * intf, struct fru_info * fru,
+ uint8_t id, uint32_t offset)
+{
+ uint8_t * fru_data;
+ struct fru_multirec_header * h;
+ struct fru_multirec_powersupply * ps;
+ struct fru_multirec_dcoutput * dc;
+ struct fru_multirec_dcload * dl;
+ uint16_t peak_capacity;
+ uint8_t peak_hold_up_time;
+ uint32_t last_off;
+
+ last_off = offset;
+
+ fru_data = malloc(FRU_MULTIREC_CHUNK_SIZE);
+ if (fru_data == NULL) {
+ lprintf(LOG_ERR, "ipmitool: malloc failure");
+ return;
+ }
+
+ memset(fru_data, 0, FRU_MULTIREC_CHUNK_SIZE);
+
+ h = (struct fru_multirec_header *) (fru_data);
+
+ do {
+ if (read_fru_area(intf, fru, id, last_off, sizeof(*h), fru_data) < 0) {
+ break;
+ }
+
+ if (h->len && read_fru_area(intf, fru, id,
+ last_off + sizeof(*h), h->len, fru_data + sizeof(*h)) < 0) {
+ break;
+ }
+
+ last_off += h->len + sizeof(*h);
+
+ switch (h->type) {
+ case FRU_RECORD_TYPE_POWER_SUPPLY_INFORMATION:
+ ps = (struct fru_multirec_powersupply *)
+ (fru_data + sizeof(struct fru_multirec_header));
+
+#if WORDS_BIGENDIAN
+ ps->capacity = BSWAP_16(ps->capacity);
+ ps->peak_va = BSWAP_16(ps->peak_va);
+ ps->lowend_input1 = BSWAP_16(ps->lowend_input1);
+ ps->highend_input1 = BSWAP_16(ps->highend_input1);
+ ps->lowend_input2 = BSWAP_16(ps->lowend_input2);
+ ps->highend_input2 = BSWAP_16(ps->highend_input2);
+ ps->combined_capacity = BSWAP_16(ps->combined_capacity);
+ ps->peak_cap_ht = BSWAP_16(ps->peak_cap_ht);
+#endif
+ peak_hold_up_time = (ps->peak_cap_ht & 0xf000) >> 12;
+ peak_capacity = ps->peak_cap_ht & 0x0fff;
+
+ printf (" Power Supply Record\n");
+ printf (" Capacity : %d W\n",
+ ps->capacity);
+ printf (" Peak VA : %d VA\n",
+ ps->peak_va);
+ printf (" Inrush Current : %d A\n",
+ ps->inrush_current);
+ printf (" Inrush Interval : %d ms\n",
+ ps->inrush_interval);
+ printf (" Input Voltage Range 1 : %d-%d V\n",
+ ps->lowend_input1 / 100, ps->highend_input1 / 100);
+ printf (" Input Voltage Range 2 : %d-%d V\n",
+ ps->lowend_input2 / 100, ps->highend_input2 / 100);
+ printf (" Input Frequency Range : %d-%d Hz\n",
+ ps->lowend_freq, ps->highend_freq);
+ printf (" A/C Dropout Tolerance : %d ms\n",
+ ps->dropout_tolerance);
+ printf (" Flags : %s%s%s%s%s\n",
+ ps->predictive_fail ? "'Predictive fail' " : "",
+ ps->pfc ? "'Power factor correction' " : "",
+ ps->autoswitch ? "'Autoswitch voltage' " : "",
+ ps->hotswap ? "'Hot swap' " : "",
+ ps->predictive_fail ? ps->rps_threshold ?
+ ps->tach ? "'Two pulses per rotation'" : "'One pulse per rotation'" :
+ ps->tach ? "'Failure on pin de-assertion'" : "'Failure on pin assertion'" : "");
+ printf (" Peak capacity : %d W\n",
+ peak_capacity);
+ printf (" Peak capacity holdup : %d s\n",
+ peak_hold_up_time);
+ if (ps->combined_capacity == 0)
+ printf (" Combined capacity : not specified\n");
+ else
+ printf (" Combined capacity : %d W (%s and %s)\n",
+ ps->combined_capacity,
+ combined_voltage_desc [ps->combined_voltage1],
+ combined_voltage_desc [ps->combined_voltage2]);
+ if (ps->predictive_fail)
+ printf (" Fan lower threshold : %d RPS\n",
+ ps->rps_threshold);
+ break;
+
+ case FRU_RECORD_TYPE_DC_OUTPUT:
+ dc = (struct fru_multirec_dcoutput *)
+ (fru_data + sizeof(struct fru_multirec_header));
+
+#if WORDS_BIGENDIAN
+ dc->nominal_voltage = BSWAP_16(dc->nominal_voltage);
+ dc->max_neg_dev = BSWAP_16(dc->max_neg_dev);
+ dc->max_pos_dev = BSWAP_16(dc->max_pos_dev);
+ dc->ripple_and_noise = BSWAP_16(dc->ripple_and_noise);
+ dc->min_current = BSWAP_16(dc->min_current);
+ dc->max_current = BSWAP_16(dc->max_current);
+#endif
+
+ printf (" DC Output Record\n");
+ printf (" Output Number : %d\n",
+ dc->output_number);
+ printf (" Standby power : %s\n",
+ dc->standby ? "Yes" : "No");
+ printf (" Nominal voltage : %.2f V\n",
+ (double) dc->nominal_voltage / 100);
+ printf (" Max negative deviation : %.2f V\n",
+ (double) dc->max_neg_dev / 100);
+ printf (" Max positive deviation : %.2f V\n",
+ (double) dc->max_pos_dev / 100);
+ printf (" Ripple and noise pk-pk : %d mV\n",
+ dc->ripple_and_noise);
+ printf (" Minimum current draw : %.3f A\n",
+ (double) dc->min_current / 1000);
+ printf (" Maximum current draw : %.3f A\n",
+ (double) dc->max_current / 1000);
+ break;
+
+ case FRU_RECORD_TYPE_DC_LOAD:
+ dl = (struct fru_multirec_dcload *)
+ (fru_data + sizeof(struct fru_multirec_header));
+
+#if WORDS_BIGENDIAN
+ dl->nominal_voltage = BSWAP_16(dl->nominal_voltage);
+ dl->min_voltage = BSWAP_16(dl->min_voltage);
+ dl->max_voltage = BSWAP_16(dl->max_voltage);
+ dl->ripple_and_noise = BSWAP_16(dl->ripple_and_noise);
+ dl->min_current = BSWAP_16(dl->min_current);
+ dl->max_current = BSWAP_16(dl->max_current);
+#endif
+
+ printf (" DC Load Record\n");
+ printf (" Output Number : %d\n",
+ dl->output_number);
+ printf (" Nominal voltage : %.2f V\n",
+ (double) dl->nominal_voltage / 100);
+ printf (" Min voltage allowed : %.2f V\n",
+ (double) dl->min_voltage / 100);
+ printf (" Max voltage allowed : %.2f V\n",
+ (double) dl->max_voltage / 100);
+ printf (" Ripple and noise pk-pk : %d mV\n",
+ dl->ripple_and_noise);
+ printf (" Minimum current load : %.3f A\n",
+ (double) dl->min_current / 1000);
+ printf (" Maximum current load : %.3f A\n",
+ (double) dl->max_current / 1000);
+ break;
+ case FRU_RECORD_TYPE_OEM_EXTENSION:
+ {
+ struct fru_multirec_oem_header *oh=(struct fru_multirec_oem_header *)
+ &fru_data[sizeof(struct fru_multirec_header)];
+ uint32_t iana = oh->mfg_id[0] | oh->mfg_id[1]<<8 | oh->mfg_id[2]<<16;
+
+ /* Now makes sure this is really PICMG record */
+
+ if( iana == IPMI_OEM_PICMG ){
+ printf(" PICMG Extension Record\n");
+ ipmi_fru_picmg_ext_print(fru_data,
+ sizeof(struct fru_multirec_header),
+ h->len);
+ }
+ /* FIXME: Add OEM record support here */
+ else{
+ printf(" OEM (%s) Record\n", val2str( iana, ipmi_oem_info));
+ }
+ }
+ break;
+ }
+ } while (!(h->format & 0x80));
+
+ lprintf(LOG_DEBUG ,"Multi-Record area ends at: %i (%xh)", last_off, last_off);
+
+ free(fru_data);
+}
+
+/* ipmi_fru_query_new_value - Query new values to replace original FRU content
+*
+* @data: FRU data
+* @offset: offset of the bytes to be modified in data
+* @len: size of the modified data
+*
+* returns : TRUE if data changed
+* returns : FALSE if data not changed
+*/
+int ipmi_fru_query_new_value(uint8_t *data,int offset, size_t len)
+{
+ int status=FALSE;
+ int ret;
+ char answer;
+
+ printf("Would you like to change this value <y/n> ? ");
+ ret = scanf("%c", &answer);
+ if (ret != 1) {
+ return FALSE;
+ }
+
+ if( answer == 'y' || answer == 'Y' ){
+ int i;
+ unsigned int *holder;
+
+ holder = malloc(len);
+ printf(
+ "Enter hex values for each of the %d entries (lsb first), "
+ "hit <enter> between entries\n", (int)len);
+
+ /* I can't assign scanf' %x into a single char */
+ for( i=0;i<len;i++ ){
+ ret = scanf("%x", holder+i);
+ if (ret != 1) {
+ free(holder);
+ return FALSE;
+ }
+ }
+ for( i=0;i<len;i++ ){
+ data[offset++] = (unsigned char) *(holder+i);
+ }
+ /* &data[offset++] */
+ free(holder);
+ holder = NULL;
+ status = TRUE;
+ }
+ else{
+ printf("Entered %c\n",answer);
+ }
+
+ return status;
+}
+
+/* ipmi_fru_oemkontron_edit -
+* Query new values to replace original FRU content
+* This is a generic enough to support any type of 'OEM' record
+* because the user supplies 'IANA number' , 'record Id' and 'record' version'
+*
+* However, the parser must have 'apriori' knowledge of the record format
+* The currently supported record is :
+*
+* IANA : 15000 (Kontron)
+* RECORD ID : 3
+* RECORD VERSION: 0 (or 1)
+*
+* I would have like to put that stuff in an OEM specific file, but apart for
+* the record format information, all commands are really standard 'FRU' command
+*
+*
+* @data: FRU data
+* @offset: start of the current multi record (start of header)
+* @len: len of the current record (excluding header)
+* @h: pointer to record header
+* @oh: pointer to OEM /PICMG header
+*
+* returns: TRUE if data changed
+* returns: FALSE if data not changed
+*/
+#define OEM_KONTRON_INFORMATION_RECORD 3
+
+#define EDIT_OEM_KONTRON_COMPLETE_ARG_COUNT 12
+#define GET_OEM_KONTRON_COMPLETE_ARG_COUNT 5
+/*
+./src/ipmitool fru edit 0
+oem 15000 3 0 name instance FIELD1 FIELD2 FIELD3 crc32
+*/
+
+#define OEM_KONTRON_SUBCOMMAND_ARG_POS 2
+#define OEM_KONTRON_IANA_ARG_POS 3
+#define OEM_KONTRON_RECORDID_ARG_POS 4
+#define OEM_KONTRON_FORMAT_ARG_POS 5
+#define OEM_KONTRON_NAME_ARG_POS 6
+#define OEM_KONTRON_INSTANCE_ARG_POS 7
+#define OEM_KONTRON_VERSION_ARG_POS 8
+#define OEM_KONTRON_BUILDDATE_ARG_POS 9
+#define OEM_KONTRON_UPDATEDATE_ARG_POS 10
+#define OEM_KONTRON_CRC32_ARG_POS 11
+
+#define OEM_KONTRON_FIELD_SIZE 8
+#define OEM_KONTRON_VERSION_FIELD_SIZE 10
+
+#ifdef HAVE_PRAGMA_PACK
+#pragma pack(1)
+#endif
+typedef struct OemKontronInformationRecordV0{
+ uint8_t field1TypeLength;
+ uint8_t field1[OEM_KONTRON_FIELD_SIZE];
+ uint8_t field2TypeLength;
+ uint8_t field2[OEM_KONTRON_FIELD_SIZE];
+ uint8_t field3TypeLength;
+ uint8_t field3[OEM_KONTRON_FIELD_SIZE];
+ uint8_t crcTypeLength;
+ uint8_t crc32[OEM_KONTRON_FIELD_SIZE];
+}tOemKontronInformationRecordV0;
+#ifdef HAVE_PRAGMA_PACK
+#pragma pack(0)
+#endif
+
+
+#ifdef HAVE_PRAGMA_PACK
+#pragma pack(1)
+#endif
+typedef struct OemKontronInformationRecordV1{
+ uint8_t field1TypeLength;
+ uint8_t field1[OEM_KONTRON_VERSION_FIELD_SIZE];
+ uint8_t field2TypeLength;
+ uint8_t field2[OEM_KONTRON_FIELD_SIZE];
+ uint8_t field3TypeLength;
+ uint8_t field3[OEM_KONTRON_FIELD_SIZE];
+ uint8_t crcTypeLength;
+ uint8_t crc32[OEM_KONTRON_FIELD_SIZE];
+}tOemKontronInformationRecordV1;
+#ifdef HAVE_PRAGMA_PACK
+#pragma pack(0)
+#endif
+
+/*
+./src/ipmitool fru get 0 oem iana 3
+
+*/
+
+static void ipmi_fru_oemkontron_get( int argc, char ** argv,uint8_t * fru_data,
+ int off,int len,
+ struct fru_multirec_header *h,
+ struct fru_multirec_oem_header *oh)
+{
+ static int badParams=FALSE;
+ int start = off;
+ int offset = start;
+ int length = len;
+ int i;
+ offset += sizeof(struct fru_multirec_oem_header);
+
+ if(!badParams){
+ /* the 'OEM' field is already checked in caller */
+ if( argc > OEM_KONTRON_SUBCOMMAND_ARG_POS ){
+ if(strncmp("oem", argv[OEM_KONTRON_SUBCOMMAND_ARG_POS],3)){
+ printf("usage: fru get <id> <oem>\n");
+ badParams = TRUE;
+ return;
+ }
+ }
+ if( argc<GET_OEM_KONTRON_COMPLETE_ARG_COUNT ){
+ printf("usage: oem <iana> <recordid>\n");
+ printf("usage: oem 15000 3\n");
+ badParams = TRUE;
+ return;
+ }
+ }
+
+ if(!badParams){
+
+ if(oh->record_id == OEM_KONTRON_INFORMATION_RECORD ) {
+
+ uint8_t version;
+
+ printf("Kontron OEM Information Record\n");
+ version = oh->record_version;
+
+ int blockstart;
+ uint8_t blockCount;
+ uint8_t blockIndex=0;
+
+ unsigned int matchInstance = 0;
+ uint8_t instance = 0;
+
+ if (str2uchar(argv[OEM_KONTRON_INSTANCE_ARG_POS], &instance) != 0) {
+ lprintf(LOG_ERR,
+ "Instance argument '%s' is either invalid or out of range.",
+ argv[OEM_KONTRON_INSTANCE_ARG_POS]);
+ badParams = TRUE;
+ return;
+ }
+
+ blockCount = fru_data[offset++];
+
+ for(blockIndex=0;blockIndex<blockCount;blockIndex++){
+ void * pRecordData;
+ uint8_t nameLen;
+
+ blockstart = offset;
+ nameLen = ( fru_data[offset++] &= 0x3F );
+ printf(" Name: %*.*s\n",nameLen, nameLen, (const char *)(fru_data+offset));
+
+ offset+=nameLen;
+
+ pRecordData = &fru_data[offset];
+
+ printf(" Record Version: %d\n", version);
+ if( version == 0 )
+ {
+ printf(" Version: %*.*s\n",
+ OEM_KONTRON_FIELD_SIZE,
+ OEM_KONTRON_FIELD_SIZE,
+ ((tOemKontronInformationRecordV0 *) pRecordData)->field1);
+ printf(" Build Date: %*.*s\n",
+ OEM_KONTRON_FIELD_SIZE,
+ OEM_KONTRON_FIELD_SIZE,
+ ((tOemKontronInformationRecordV0 *) pRecordData)->field2);
+ printf(" Update Date: %*.*s\n",
+ OEM_KONTRON_FIELD_SIZE,
+ OEM_KONTRON_FIELD_SIZE,
+ ((tOemKontronInformationRecordV0 *) pRecordData)->field3);
+ printf(" Checksum: %*.*s\n\n",
+ OEM_KONTRON_FIELD_SIZE,
+ OEM_KONTRON_FIELD_SIZE,
+ ((tOemKontronInformationRecordV0 *) pRecordData)->crc32);
+ matchInstance++;
+ offset+= sizeof(tOemKontronInformationRecordV0);
+ offset++;
+ }
+ else if ( version == 1 )
+ {
+ printf(" Version: %*.*s\n",
+ OEM_KONTRON_VERSION_FIELD_SIZE,
+ OEM_KONTRON_VERSION_FIELD_SIZE,
+ ((tOemKontronInformationRecordV1 *) pRecordData)->field1);
+ printf(" Build Date: %*.*s\n",
+ OEM_KONTRON_FIELD_SIZE,
+ OEM_KONTRON_FIELD_SIZE,
+ ((tOemKontronInformationRecordV1 *) pRecordData)->field2);
+ printf(" Update Date: %*.*s\n",
+ OEM_KONTRON_FIELD_SIZE,
+ OEM_KONTRON_FIELD_SIZE,
+ ((tOemKontronInformationRecordV1 *) pRecordData)->field3);
+ printf(" Checksum: %*.*s\n\n",
+ OEM_KONTRON_FIELD_SIZE,
+ OEM_KONTRON_FIELD_SIZE,
+ ((tOemKontronInformationRecordV1 *) pRecordData)->crc32);
+ matchInstance++;
+ offset+= sizeof(tOemKontronInformationRecordV1);
+ offset++;
+ }
+ else
+ {
+ printf (" Unsupported version %d\n",version);
+ }
+ }
+ }
+ }
+}
+
+static int ipmi_fru_oemkontron_edit( int argc, char ** argv,uint8_t * fru_data,
+ int off,int len,
+ struct fru_multirec_header *h,
+ struct fru_multirec_oem_header *oh)
+{
+ static int badParams=FALSE;
+ int hasChanged = FALSE;
+ int start = off;
+ int offset = start;
+ int length = len;
+ int i;
+ uint8_t record_id = 0;
+ offset += sizeof(struct fru_multirec_oem_header);
+
+ if(!badParams){
+ /* the 'OEM' field is already checked in caller */
+ if( argc > OEM_KONTRON_SUBCOMMAND_ARG_POS ){
+ if(strncmp("oem", argv[OEM_KONTRON_SUBCOMMAND_ARG_POS],3)){
+ printf("usage: fru edit <id> <oem> <args...>\n");
+ badParams = TRUE;
+ return hasChanged;
+ }
+ }
+ if( argc<EDIT_OEM_KONTRON_COMPLETE_ARG_COUNT ){
+ printf("usage: oem <iana> <recordid> <format> <args...>\n");
+ printf("usage: oem 15000 3 0 <name> <instance> <field1>"\
+ " <field2> <field3> <crc32>\n");
+ badParams = TRUE;
+ return hasChanged;
+ }
+ if (str2uchar(argv[OEM_KONTRON_RECORDID_ARG_POS], &record_id) != 0) {
+ lprintf(LOG_ERR,
+ "Record ID argument '%s' is either invalid or out of range.",
+ argv[OEM_KONTRON_RECORDID_ARG_POS]);
+ badParams = TRUE;
+ return hasChanged;
+ }
+ if (record_id == OEM_KONTRON_INFORMATION_RECORD) {
+ for(i=OEM_KONTRON_VERSION_ARG_POS;i<=OEM_KONTRON_CRC32_ARG_POS;i++){
+ if( (strlen(argv[i]) != OEM_KONTRON_FIELD_SIZE) &&
+ (strlen(argv[i]) != OEM_KONTRON_VERSION_FIELD_SIZE)) {
+ printf("error: version fields must have %d characters\n",
+ OEM_KONTRON_FIELD_SIZE);
+ badParams = TRUE;
+ return hasChanged;
+ }
+ }
+ }
+ }
+
+ if(!badParams){
+
+ if(oh->record_id == OEM_KONTRON_INFORMATION_RECORD ) {
+ uint8_t formatVersion = 0;
+ uint8_t version;
+
+ if (str2uchar(argv[OEM_KONTRON_FORMAT_ARG_POS], &formatVersion) != 0) {
+ lprintf(LOG_ERR,
+ "Format argument '%s' is either invalid or out of range.",
+ argv[OEM_KONTRON_FORMAT_ARG_POS]);
+ badParams = TRUE;
+ return hasChanged;
+ }
+
+ printf(" Kontron OEM Information Record\n");
+ version = oh->record_version;
+
+ if( version == formatVersion ){
+ int blockstart;
+ uint8_t blockCount;
+ uint8_t blockIndex=0;
+
+ uint8_t matchInstance = 0;
+ uint8_t instance = 0;
+
+ if (str2uchar(argv[OEM_KONTRON_INSTANCE_ARG_POS], &instance) != 0) {
+ lprintf(LOG_ERR,
+ "Instance argument '%s' is either invalid or out of range.",
+ argv[OEM_KONTRON_INSTANCE_ARG_POS]);
+ badParams = TRUE;
+ return hasChanged;
+ }
+
+ blockCount = fru_data[offset++];
+ printf(" blockCount: %d\n",blockCount);
+
+ for(blockIndex=0;blockIndex<blockCount;blockIndex++){
+ void * pRecordData;
+ uint8_t nameLen;
+
+ blockstart = offset;
+
+ nameLen = ( fru_data[offset++] & 0x3F );
+
+ if( version == 0 || version == 1 )
+ {
+ if(!strncmp((char *)argv[OEM_KONTRON_NAME_ARG_POS],
+ (const char *)(fru_data+offset),nameLen)&& (matchInstance == instance)){
+
+ printf ("Found : %s\n",argv[OEM_KONTRON_NAME_ARG_POS]);
+ offset+=nameLen;
+
+ pRecordData = &fru_data[offset];
+
+ if( version == 0 )
+ {
+ memcpy( ((tOemKontronInformationRecordV0 *)
+ pRecordData)->field1 ,
+ argv[OEM_KONTRON_VERSION_ARG_POS] ,
+ OEM_KONTRON_FIELD_SIZE);
+ memcpy( ((tOemKontronInformationRecordV0 *)
+ pRecordData)->field2 ,
+ argv[OEM_KONTRON_BUILDDATE_ARG_POS],
+ OEM_KONTRON_FIELD_SIZE);
+ memcpy( ((tOemKontronInformationRecordV0 *)
+ pRecordData)->field3 ,
+ argv[OEM_KONTRON_UPDATEDATE_ARG_POS],
+ OEM_KONTRON_FIELD_SIZE);
+ memcpy( ((tOemKontronInformationRecordV0 *)
+ pRecordData)->crc32 ,
+ argv[OEM_KONTRON_CRC32_ARG_POS] ,
+ OEM_KONTRON_FIELD_SIZE);
+ }
+ else
+ {
+ memcpy( ((tOemKontronInformationRecordV1 *)
+ pRecordData)->field1 ,
+ argv[OEM_KONTRON_VERSION_ARG_POS] ,
+ OEM_KONTRON_VERSION_FIELD_SIZE);
+ memcpy( ((tOemKontronInformationRecordV1 *)
+ pRecordData)->field2 ,
+ argv[OEM_KONTRON_BUILDDATE_ARG_POS],
+ OEM_KONTRON_FIELD_SIZE);
+ memcpy( ((tOemKontronInformationRecordV1 *)
+ pRecordData)->field3 ,
+ argv[OEM_KONTRON_UPDATEDATE_ARG_POS],
+ OEM_KONTRON_FIELD_SIZE);
+ memcpy( ((tOemKontronInformationRecordV1 *)
+ pRecordData)->crc32 ,
+ argv[OEM_KONTRON_CRC32_ARG_POS] ,
+ OEM_KONTRON_FIELD_SIZE);
+ }
+
+ matchInstance++;
+ hasChanged = TRUE;
+ }
+ else if(!strncmp((char *)argv[OEM_KONTRON_NAME_ARG_POS],
+ (const char *)(fru_data+offset), nameLen)){
+ printf ("Skipped : %s [instance %d]\n",argv[OEM_KONTRON_NAME_ARG_POS],
+ (unsigned int)matchInstance);
+ matchInstance++;
+ offset+=nameLen;
+ }
+ else {
+ offset+=nameLen;
+ }
+
+ if( version == 0 )
+ {
+ offset+= sizeof(tOemKontronInformationRecordV0);
+ }
+ else
+ {
+ offset+= sizeof(tOemKontronInformationRecordV1);
+ }
+ offset++;
+ }
+ else
+ {
+ printf (" Unsupported version %d\n",version);
+ }
+ }
+ }
+ else{
+ printf(" Version: %d\n",version);
+ }
+ }
+ if( hasChanged ){
+
+ uint8_t record_checksum =0;
+ uint8_t header_checksum =0;
+ int index;
+
+ lprintf(LOG_DEBUG,"Initial record checksum : %x",h->record_checksum);
+ lprintf(LOG_DEBUG,"Initial header checksum : %x",h->header_checksum);
+ for(index=0;index<length;index++){
+ record_checksum+= fru_data[start+index];
+ }
+ /* Update Record checksum */
+ h->record_checksum = ~record_checksum + 1;
+
+
+ for(index=0;index<(sizeof(struct fru_multirec_header) -1);index++){
+ uint8_t data= *( (uint8_t *)h+ index);
+ header_checksum+=data;
+ }
+ /* Update header checksum */
+ h->header_checksum = ~header_checksum + 1;
+
+ lprintf(LOG_DEBUG,"Final record checksum : %x",h->record_checksum);
+ lprintf(LOG_DEBUG,"Final header checksum : %x",h->header_checksum);
+
+ /* write back data */
+ }
+ }
+
+ return hasChanged;
+}
+
+/* ipmi_fru_picmg_ext_edit - Query new values to replace original FRU content
+*
+* @data: FRU data
+* @offset: start of the current multi record (start of header)
+* @len: len of the current record (excluding header)
+* @h: pointer to record header
+* @oh: pointer to OEM /PICMG header
+*
+* returns: TRUE if data changed
+* returns: FALSE if data not changed
+*/
+static int ipmi_fru_picmg_ext_edit(uint8_t * fru_data,
+ int off,int len,
+ struct fru_multirec_header *h,
+ struct fru_multirec_oem_header *oh)
+{
+ int hasChanged = FALSE;
+ int start = off;
+ int offset = start;
+ int length = len;
+ offset += sizeof(struct fru_multirec_oem_header);
+
+ switch (oh->record_id)
+ {
+ case FRU_AMC_ACTIVATION:
+ printf(" FRU_AMC_ACTIVATION\n");
+ {
+ int index=offset;
+ uint16_t max_current;
+
+ max_current = fru_data[offset];
+ max_current |= fru_data[++offset]<<8;
+
+ printf(" Maximum Internal Current(@12V): %.2f A (0x%02x)\n",
+ (float)max_current / 10.0f, max_current);
+
+ if( ipmi_fru_query_new_value(fru_data,index,2) ){
+ max_current = fru_data[index];
+ max_current |= fru_data[++index]<<8;
+ printf(" New Maximum Internal Current(@12V): %.2f A (0x%02x)\n",
+ (float)max_current / 10.0f, max_current);
+ hasChanged = TRUE;
+
+ }
+
+ printf(" Module Activation Readiness: %i sec.\n", fru_data[++offset]);
+ printf(" Descriptor Count: %i\n", fru_data[++offset]);
+ printf("\n");
+
+ for (++offset;
+ offset < (off + length);
+ offset += sizeof(struct fru_picmgext_activation_record)) {
+ struct fru_picmgext_activation_record * a =
+ (struct fru_picmgext_activation_record *) &fru_data[offset];
+
+ printf(" IPMB-Address: 0x%x\n", a->ibmb_addr);
+ printf(" Max. Module Current: %.2f A\n", (float)a->max_module_curr / 10.0f);
+
+ printf("\n");
+ }
+ }
+ break;
+
+ case FRU_AMC_CURRENT:
+ printf(" FRU_AMC_CURRENT\n");
+ {
+ int index=offset;
+ unsigned char current;
+
+ current = fru_data[index];
+
+ printf(" Current draw(@12V): %.2f A (0x%02x)\n",
+ (float)current / 10.0f, current);
+
+ if( ipmi_fru_query_new_value(fru_data, index, 1) ){
+ current = fru_data[index];
+
+ printf(" New Current draw(@12V): %.2f A (0x%02x)\n",
+ (float)current / 10.0f, current);
+ hasChanged = TRUE;
+ }
+ }
+ break;
+ }
+
+ if( hasChanged ){
+
+ uint8_t record_checksum =0;
+ uint8_t header_checksum =0;
+ int index;
+
+ lprintf(LOG_DEBUG,"Initial record checksum : %x",h->record_checksum);
+ lprintf(LOG_DEBUG,"Initial header checksum : %x",h->header_checksum);
+ for(index=0;index<length;index++){
+ record_checksum+= fru_data[start+index];
+ }
+ /* Update Record checksum */
+ h->record_checksum = ~record_checksum + 1;
+
+
+ for(index=0;index<(sizeof(struct fru_multirec_header) -1);index++){
+ uint8_t data= *( (uint8_t *)h+ index);
+ header_checksum+=data;
+ }
+ /* Update header checksum */
+ h->header_checksum = ~header_checksum + 1;
+
+ lprintf(LOG_DEBUG,"Final record checksum : %x",h->record_checksum);
+ lprintf(LOG_DEBUG,"Final header checksum : %x",h->header_checksum);
+
+ /* write back data */
+ }
+
+ return hasChanged;
+}
+
+/* ipmi_fru_picmg_ext_print - prints OEM fru record (PICMG)
+*
+* @fru_data: FRU data
+* @offset: offset of the bytes to be modified in data
+* @length: size of the record
+*
+* returns : n/a
+*/
+static void ipmi_fru_picmg_ext_print(uint8_t * fru_data, int off, int length)
+{
+ struct fru_multirec_oem_header *h;
+ int guid_count;
+ int offset = off;
+ int start_offset = off;
+ int i;
+
+ h = (struct fru_multirec_oem_header *) &fru_data[offset];
+ offset += sizeof(struct fru_multirec_oem_header);
+
+ switch (h->record_id)
+ {
+ case FRU_PICMG_BACKPLANE_P2P:
+ {
+ uint8_t index;
+ unsigned int data;
+ struct fru_picmgext_slot_desc *slot_d;
+
+ slot_d =
+ (struct fru_picmgext_slot_desc*)&fru_data[offset];
+ offset += sizeof(struct fru_picmgext_slot_desc);
+ printf(" FRU_PICMG_BACKPLANE_P2P\n");
+
+ while (offset <= (start_offset+length)) {
+ printf("\n");
+ printf(" Channel Type: ");
+ switch (slot_d->chan_type)
+ {
+ case 0x00:
+ case 0x07:
+ printf("PICMG 2.9\n");
+ break;
+ case 0x08:
+ printf("Single Port Fabric IF\n");
+ break;
+ case 0x09:
+ printf("Double Port Fabric IF\n");
+ break;
+ case 0x0a:
+ printf("Full Channel Fabric IF\n");
+ break;
+ case 0x0b:
+ printf("Base IF\n");
+ break;
+ case 0x0c:
+ printf("Update Channel IF\n");
+ break;
+ case 0x0d:
+ printf("ShMC Cross Connect\n");
+ break;
+ default:
+ printf("Unknown IF (0x%x)\n",
+ slot_d->chan_type);
+ break;
+ }
+ printf(" Slot Addr. : %02x\n",
+ slot_d->slot_addr );
+ printf(" Channel Count: %i\n",
+ slot_d->chn_count);
+
+ for (index = 0;
+ index < (slot_d->chn_count);
+ index++) {
+ struct fru_picmgext_chn_desc *d;
+ data = (fru_data[offset+0]) |
+ (fru_data[offset+1] << 8) |
+ (fru_data[offset+2] << 16);
+ d = (struct fru_picmgext_chn_desc *)&data;
+ if (verbose) {
+ printf( " "
+ "Chn: %02x -> "
+ "Chn: %02x in "
+ "Slot: %02x\n",
+ d->local_chn,
+ d->remote_chn,
+ d->remote_slot);
+ }
+ offset += FRU_PICMGEXT_CHN_DESC_RECORD_SIZE;
+ }
+ slot_d = (struct fru_picmgext_slot_desc*)&fru_data[offset];
+ offset += sizeof(struct fru_picmgext_slot_desc);
+ }
+ }
+ break;
+
+ case FRU_PICMG_ADDRESS_TABLE:
+ {
+ unsigned int hwaddr;
+ unsigned int sitetype;
+ unsigned int sitenum;
+ unsigned int entries;
+ unsigned int i;
+ char *picmg_site_type_strings[] = {
+ "AdvancedTCA Board",
+ "Power Entry",
+ "Shelf FRU Information",
+ "Dedicated ShMC",
+ "Fan Tray",
+ "Fan Filter Tray",
+ "Alarm",
+ "AdvancedMC Module",
+ "PMC",
+ "Rear Transition Module"};
+
+
+ printf(" FRU_PICMG_ADDRESS_TABLE\n");
+ printf(" Type/Len: 0x%02x\n", fru_data[offset++]);
+ printf(" Shelf Addr: ");
+ for (i=0;i<20;i++) {
+ printf("0x%02x ", fru_data[offset++]);
+ }
+ printf("\n");
+
+ entries = fru_data[offset++];
+ printf(" Addr Table Entries: 0x%02x\n", entries);
+
+ for (i=0; i<entries; i++) {
+ hwaddr = fru_data[offset];
+ sitenum = fru_data[offset + 1];
+ sitetype = fru_data[offset + 2];
+ printf(
+ " HWAddr: 0x%02x (0x%02x) SiteNum: %d SiteType: 0x%02x %s\n",
+ hwaddr, hwaddr * 2,
+ sitenum, sitetype,
+ (sitetype < 0xa) ?
+ picmg_site_type_strings[sitetype] :
+ "Reserved");
+ offset += 3;
+ }
+ }
+ break;
+
+ case FRU_PICMG_SHELF_POWER_DIST:
+ {
+ unsigned int entries;
+ unsigned int feeds;
+ unsigned int feedcnt;
+ unsigned int hwaddr;
+ unsigned int i;
+ unsigned int id;
+ unsigned int j;
+ unsigned int maxext;
+ unsigned int maxint;
+ unsigned int minexp;
+
+ printf(" FRU_PICMG_SHELF_POWER_DIST\n");
+
+ feeds = fru_data[offset++];
+ printf(" Number of Power Feeds: 0x%02x\n",
+ feeds);
+
+ for (i=0; i<feeds; i++) {
+ printf(" Feed %d:\n", i);
+ maxext = fru_data[offset] |
+ (fru_data[offset+1] << 8);
+ offset += 2;
+ maxint = fru_data[offset] |
+ (fru_data[offset+1] << 8);
+ offset += 2;
+ minexp = fru_data[offset];
+ offset += 1;
+ entries = fru_data[offset];
+ offset += 1;
+
+ printf(
+ " Max External Current: %d.%d Amps (0x%04x)\n",
+ maxext / 10, maxext % 10, maxext);
+ if (maxint < 0xffff) {
+ printf(
+ " Max Internal Current: %d.%d Amps (0x%04x)\n",
+ maxint / 10, maxint % 10,
+ maxint);
+ } else {
+ printf(
+ " Max Internal Current: Not Specified\n");
+ }
+
+ if (minexp >= 0x48 && minexp <= 0x90) {
+ printf(
+ " Min Expected Voltage: -%02d.%dV\n",
+ minexp / 2, (minexp % 2) * 5);
+ } else {
+ printf(
+ " Min Expected Voltage: -%dV (actual invalid value 0x%x)\n",
+ 36, minexp);
+ }
+ for (j=0; j < entries; j++) {
+ hwaddr = fru_data[offset++];
+ id = fru_data[offset++];
+ printf(
+ " FRU HW Addr: 0x%02x (0x%02x)",
+ hwaddr, hwaddr * 2);
+ printf(
+ " FRU ID: 0x%02x\n",
+ id);
+ }
+ }
+ }
+ break;
+
+ case FRU_PICMG_SHELF_ACTIVATION:
+ {
+ unsigned int i;
+ unsigned int count = 0;
+
+ printf(" FRU_PICMG_SHELF_ACTIVATION\n");
+ printf(
+ " Allowance for FRU Act Readiness: 0x%02x\n",
+ fru_data[offset++]);
+
+ count = fru_data[offset++];
+ printf(
+ " FRU activation and Power Desc Cnt: 0x%02x\n",
+ count);
+
+ for (i=0; i<count; i++) {
+ printf(" HW Addr: 0x%02x ",
+ fru_data[offset++]);
+ printf(" FRU ID: 0x%02x ",
+ fru_data[offset++]);
+ printf(" Max FRU Power: 0x%04x ",
+ fru_data[offset+0] |
+ (fru_data[offset+1]<<8));
+ offset += 2;
+ printf(" Config: 0x%02x \n",
+ fru_data[offset++]);
+ }
+ }
+ break;
+
+ case FRU_PICMG_SHMC_IP_CONN:
+ printf(" FRU_PICMG_SHMC_IP_CONN\n");
+ break;
+
+ case FRU_PICMG_BOARD_P2P:
+ printf(" FRU_PICMG_BOARD_P2P\n");
+
+ guid_count = fru_data[offset++];
+ printf(" GUID count: %2d\n", guid_count);
+ for (i = 0 ; i < guid_count; i++ ) {
+ int j;
+ printf(" GUID [%2d]: 0x", i);
+
+ for (j=0; j < sizeof(struct fru_picmgext_guid);
+ j++) {
+ printf("%02x", fru_data[offset+j]);
+ }
+
+ printf("\n");
+ offset += sizeof(struct fru_picmgext_guid);
+ }
+ printf("\n");
+
+ for (; offset < off + length;
+ offset += sizeof(struct fru_picmgext_link_desc)) {
+
+ /* to solve little endian /big endian problem */
+ struct fru_picmgext_link_desc *d;
+ unsigned int data = (fru_data[offset+0]) |
+ (fru_data[offset+1] << 8) |
+ (fru_data[offset+2] << 16) |
+ (fru_data[offset+3] << 24);
+ d = (struct fru_picmgext_link_desc *) &data;
+
+ printf(" Link Grouping ID: 0x%02x\n",
+ d->grouping);
+ printf(" Link Type Extension: 0x%02x - ",
+ d->ext);
+ if (d->type == FRU_PICMGEXT_LINK_TYPE_BASE) {
+ switch (d->ext)
+ {
+ case 0:
+ printf("10/100/1000BASE-T Link (four-pair)\n");
+ break;
+ case 1:
+ printf("ShMC Cross-connect (two-pair)\n");
+ break;
+ default:
+ printf("Unknwon\n");
+ break;
+ }
+ } else if (d->type == FRU_PICMGEXT_LINK_TYPE_FABRIC_ETHERNET) {
+ switch (d->ext)
+ {
+ case 0:
+ printf("Fixed 1000Base-BX\n");
+ break;
+ case 1:
+ printf("Fixed 10GBASE-BX4 [XAUI]\n");
+ break;
+ case 2:
+ printf("FC-PI\n");
+ break;
+ default:
+ printf("Unknwon\n");
+ break;
+ }
+ } else if (d->type == FRU_PICMGEXT_LINK_TYPE_FABRIC_INFINIBAND) {
+ printf("Unknwon\n");
+ } else if (d->type == FRU_PICMGEXT_LINK_TYPE_FABRIC_STAR) {
+ printf("Unknwon\n");
+ } else if (d->type == FRU_PICMGEXT_LINK_TYPE_PCIE) {
+ printf("Unknwon\n");
+ } else {
+ printf("Unknwon\n");
+ }
+
+ printf(" Link Type: 0x%02x - ",
+ d->type);
+ if (d->type == 0 || d->type == 0xff) {
+ printf("Reserved\n");
+ }
+ else if (d->type >= 0x06 && d->type <= 0xef) {
+ printf("Reserved\n");
+ }
+ else if (d->type >= 0xf0 && d->type <= 0xfe) {
+ printf("OEM GUID Definition\n");
+ }
+ else {
+ switch (d->type)
+ {
+ case FRU_PICMGEXT_LINK_TYPE_BASE:
+ printf("PICMG 3.0 Base Interface 10/100/1000\n");
+ break;
+ case FRU_PICMGEXT_LINK_TYPE_FABRIC_ETHERNET:
+ printf("PICMG 3.1 Ethernet Fabric Interface\n");
+ break;
+ case FRU_PICMGEXT_LINK_TYPE_FABRIC_INFINIBAND:
+ printf("PICMG 3.2 Infiniband Fabric Interface\n");
+ break;
+ case FRU_PICMGEXT_LINK_TYPE_FABRIC_STAR:
+ printf("PICMG 3.3 Star Fabric Interface\n");
+ break;
+ case FRU_PICMGEXT_LINK_TYPE_PCIE:
+ printf("PICMG 3.4 PCI Express Fabric Interface\n");
+ break;
+ default:
+ printf("Invalid\n");
+ break;
+ }
+ }
+ printf(" Link Designator: \n");
+ printf(" Port Flag: 0x%02x\n",
+ d->desig_port);
+ printf(" Interface: 0x%02x - ",
+ d->desig_if);
+ switch (d->desig_if)
+ {
+ case FRU_PICMGEXT_DESIGN_IF_BASE:
+ printf("Base Interface\n");
+ break;
+ case FRU_PICMGEXT_DESIGN_IF_FABRIC:
+ printf("Fabric Interface\n");
+ break;
+ case FRU_PICMGEXT_DESIGN_IF_UPDATE_CHANNEL:
+ printf("Update Channel\n");
+ break;
+ case FRU_PICMGEXT_DESIGN_IF_RESERVED:
+ printf("Reserved\n");
+ break;
+ default:
+ printf("Invalid");
+ break;
+ }
+ printf(" Channel Number: 0x%02x\n",
+ d->desig_channel);
+ printf("\n");
+ }
+
+ break;
+
+ case FRU_AMC_CURRENT:
+ {
+ unsigned char current;
+ printf(" FRU_AMC_CURRENT\n");
+
+ current = fru_data[offset];
+ printf(" Current draw(@12V): %.2f A [ %.2f Watt ]\n",
+ (float)current / 10.0f,
+ (float)current / 10.0f * 12.0f);
+ printf("\n");
+ }
+ break;
+
+ case FRU_AMC_ACTIVATION:
+ printf(" FRU_AMC_ACTIVATION\n");
+ {
+ uint16_t max_current;
+
+ max_current = fru_data[offset];
+ max_current |= fru_data[++offset]<<8;
+ printf(" Maximum Internal Current(@12V): %.2f A [ %.2f Watt ]\n",
+ (float)max_current / 10.0f,
+ (float)max_current / 10.0f * 12.0f);
+
+ printf(" Module Activation Readiness: %i sec.\n", fru_data[++offset]);
+ printf(" Descriptor Count: %i\n", fru_data[++offset]);
+ printf("\n");
+
+ for(++offset; offset < off + length;
+ offset += sizeof(struct fru_picmgext_activation_record))
+ {
+ struct fru_picmgext_activation_record *a;
+ a = (struct fru_picmgext_activation_record *)&fru_data[offset];
+ printf(" IPMB-Address: 0x%x\n",
+ a->ibmb_addr);
+ printf(" Max. Module Current: %.2f A\n",
+ (float)a->max_module_curr / 10.0f);
+ printf("\n");
+ }
+ }
+ break;
+
+ case FRU_AMC_CARRIER_P2P:
+ {
+ uint16_t index;
+ printf(" FRU_CARRIER_P2P\n");
+ for(; offset < off + length; ) {
+ struct fru_picmgext_carrier_p2p_record * h =
+ (struct fru_picmgext_carrier_p2p_record *)&fru_data[offset];
+ printf("\n");
+ printf(" Resource ID: %i",
+ (h->resource_id & 0x07));
+ printf(" Type: ");
+ if ((h->resource_id>>7) == 1) {
+ printf("AMC\n");
+ } else {
+ printf("Local\n");
+ }
+ printf(" Descriptor Count: %i\n",
+ h->p2p_count);
+ offset += sizeof(struct fru_picmgext_carrier_p2p_record);
+ for (index = 0; index < h->p2p_count; index++) {
+ /* to solve little endian /big endian problem */
+ unsigned char data[3];
+ struct fru_picmgext_carrier_p2p_descriptor * desc;
+# ifndef WORDS_BIGENDIAN
+ data[0] = fru_data[offset+0];
+ data[1] = fru_data[offset+1];
+ data[2] = fru_data[offset+2];
+# else
+ data[0] = fru_data[offset+2];
+ data[1] = fru_data[offset+1];
+ data[2] = fru_data[offset+0];
+# endif
+ desc = (struct fru_picmgext_carrier_p2p_descriptor*)&data;
+ printf(" Port: %02d\t-> Remote Port: %02d\t",
+ desc->local_port, desc->remote_port);
+ if ((desc->remote_resource_id >> 7) == 1) {
+ printf("[ AMC ID: %02d ]\n",
+ desc->remote_resource_id & 0x0F);
+ } else {
+ printf("[ local ID: %02d ]\n",
+ desc->remote_resource_id & 0x0F);
+ }
+ offset += sizeof(struct fru_picmgext_carrier_p2p_descriptor);
+ }
+ }
+ }
+ break;
+
+ case FRU_AMC_P2P:
+ {
+ unsigned int index;
+ unsigned char channel_count;
+ struct fru_picmgext_amc_p2p_record * h;
+ printf(" FRU_AMC_P2P\n");
+ guid_count = fru_data[offset];
+ printf(" GUID count: %2d\n", guid_count);
+ for (i = 0 ; i < guid_count; i++) {
+ int j;
+ printf(" GUID %2d: ", i);
+ for (j=0; j < sizeof(struct fru_picmgext_guid);
+ j++) {
+ printf("%02x", fru_data[offset+j]);
+ offset += sizeof(struct fru_picmgext_guid);
+ printf("\n");
+ }
+ h = (struct fru_picmgext_amc_p2p_record *)&fru_data[++offset];
+ printf(" %s",
+ (h->record_type ?
+ "AMC Module:" : "On-Carrier Device"));
+ printf(" Resource ID: %i\n", h->resource_id);
+ offset += sizeof(struct fru_picmgext_amc_p2p_record);
+ channel_count = fru_data[offset++];
+ printf(" Descriptor Count: %i\n",
+ channel_count);
+ for (index = 0; index < channel_count; index++) {
+ unsigned int data;
+ struct fru_picmgext_amc_channel_desc_record *d;
+ /* pack the data in little endian format.
+ * Stupid intel...
+ */
+ data = fru_data[offset] |
+ (fru_data[offset + 1] << 8) |
+ (fru_data[offset + 2] << 16);
+ d = (struct fru_picmgext_amc_channel_desc_record *)&data;
+ printf(" Lane 0 Port: %i\n",
+ d->lane0port);
+ printf(" Lane 1 Port: %i\n",
+ d->lane1port);
+ printf(" Lane 2 Port: %i\n",
+ d->lane2port);
+ printf(" Lane 3 Port: %i\n\n",
+ d->lane3port);
+ offset += FRU_PICMGEXT_AMC_CHANNEL_DESC_RECORD_SIZE;
+ }
+ for (; offset < off + length;) {
+ unsigned int data[2];
+ struct fru_picmgext_amc_link_desc_record *l;
+ l = (struct fru_picmgext_amc_link_desc_record *)&data[0];
+ data[0] = fru_data[offset] |
+ (fru_data[offset + 1] << 8) |
+ (fru_data[offset + 2] << 16) |
+ (fru_data[offset + 3] << 24);
+ data[1] = fru_data[offset + 4];
+ printf( " Link Designator: Channel ID: %i\n"
+ " Port Flag 0: %s%s%s%s\n",
+ l->channel_id,
+ (l->port_flag_0)?"o":"-",
+ (l->port_flag_1)?"o":"-",
+ (l->port_flag_2)?"o":"-",
+ (l->port_flag_3)?"o":"-" );
+ switch (l->type) {
+ case FRU_PICMGEXT_AMC_LINK_TYPE_PCIE:
+ /* AMC.1 */
+ printf( " Link Type: %02x - "
+ "AMC.1 PCI Express\n", l->type);
+ switch (l->type_ext) {
+ case AMC_LINK_TYPE_EXT_PCIE_G1_NSSC:
+ printf( " Link Type Ext: %i - "
+ " Gen 1 capable - non SSC\n",
+ l->type_ext);
+ break;
+ case AMC_LINK_TYPE_EXT_PCIE_G1_SSC:
+ printf( " Link Type Ext: %i - "
+ " Gen 1 capable - SSC\n",
+ l->type_ext);
+ break;
+ case AMC_LINK_TYPE_EXT_PCIE_G2_NSSC:
+ printf( " Link Type Ext: %i - "
+ " Gen 2 capable - non SSC\n",
+ l->type_ext);
+ break;
+ case AMC_LINK_TYPE_EXT_PCIE_G2_SSC:
+ printf( " Link Type Ext: %i - "
+ " Gen 2 capable - SSC\n",
+ l->type_ext);
+ break;
+ default:
+ printf( " Link Type Ext: %i - "
+ " Invalid\n",
+ l->type_ext);
+ break;
+ }
+ break;
+
+ case FRU_PICMGEXT_AMC_LINK_TYPE_PCIE_AS1:
+ case FRU_PICMGEXT_AMC_LINK_TYPE_PCIE_AS2:
+ /* AMC.1 */
+ printf( " Link Type: %02x - "
+ "AMC.1 PCI Express Advanced Switching\n",
+ l->type);
+ printf(" Link Type Ext: %i\n",
+ l->type_ext);
+ break;
+
+ case FRU_PICMGEXT_AMC_LINK_TYPE_ETHERNET:
+ /* AMC.2 */
+ printf( " Link Type: %02x - "
+ "AMC.2 Ethernet\n",
+ l->type);
+ switch (l->type_ext) {
+ case AMC_LINK_TYPE_EXT_ETH_1000_BX:
+ printf( " Link Type Ext: %i - "
+ " 1000Base-Bx (SerDES Gigabit) Ethernet Link\n",
+ l->type_ext);
+ break;
+
+ case AMC_LINK_TYPE_EXT_ETH_10G_XAUI:
+ printf( " Link Type Ext: %i - "
+ " 10Gbit XAUI Ethernet Link\n",
+ l->type_ext);
+ break;
+
+ default:
+ printf( " Link Type Ext: %i - "
+ " Invalid\n",
+ l->type_ext);
+ break;
+ }
+ break;
+
+ case FRU_PICMGEXT_AMC_LINK_TYPE_STORAGE:
+ /* AMC.3 */
+ printf( " Link Type: %02x - "
+ "AMC.3 Storage\n",
+ l->type);
+ switch (l->type_ext) {
+ case AMC_LINK_TYPE_EXT_STORAGE_FC:
+ printf( " Link Type Ext: %i - "
+ " Fibre Channel\n",
+ l->type_ext);
+ break;
+
+ case AMC_LINK_TYPE_EXT_STORAGE_SATA:
+ printf( " Link Type Ext: %i - "
+ " Serial ATA\n",
+ l->type_ext);
+ break;
+
+ case AMC_LINK_TYPE_EXT_STORAGE_SAS:
+ printf( " Link Type Ext: %i - "
+ " Serial Attached SCSI\n",
+ l->type_ext);
+ break;
+
+ default:
+ printf( " Link Type Ext: %i - "
+ " Invalid\n",
+ l->type_ext);
+ break;
+ }
+ break;
+
+ case FRU_PICMGEXT_AMC_LINK_TYPE_RAPIDIO:
+ /* AMC.4 */
+ printf( " Link Type: %02x - "
+ "AMC.4 Serial Rapid IO\n",
+ l->type);
+ printf(" Link Type Ext: %i\n",
+ l->type_ext);
+ break;
+
+ default:
+ printf( " Link Type: %02x - "
+ "reserved or OEM GUID",
+ l->type);
+ printf(" Link Type Ext: %i\n",
+ l->type_ext);
+ break;
+ }
+
+ printf(" Link group Id: %i\n",
+ l->group_id);
+ printf(" Link Asym Match: %i\n\n",
+ l->asym_match);
+ offset += FRU_PICMGEXT_AMC_LINK_DESC_RECORD_SIZE;
+ }
+ }
+ }
+ break;
+
+ case FRU_AMC_CARRIER_INFO:
+ {
+ unsigned char extVersion;
+ unsigned char siteCount;
+
+ printf(" FRU_CARRIER_INFO\n");
+
+ extVersion = fru_data[offset++];
+ siteCount = fru_data[offset++];
+
+ printf(" AMC.0 extension version: R%d.%d\n",
+ (extVersion >> 0)& 0x0F,
+ (extVersion >> 4)& 0x0F );
+ printf(" Carrier Sie Number Cnt: %d\n", siteCount);
+
+ for (i = 0 ; i < siteCount; i++ ){
+ printf(" Site ID: %i \n", fru_data[offset++]);
+ }
+ printf("\n");
+ }
+ break;
+ case FRU_PICMG_CLK_CARRIER_P2P:
+ {
+ unsigned char desc_count;
+ int i,j;
+
+ printf(" FRU_PICMG_CLK_CARRIER_P2P\n");
+
+ desc_count = fru_data[offset++];
+
+ for(i=0; i<desc_count; i++){
+ unsigned char resource_id;
+ unsigned char channel_count;
+
+ resource_id = fru_data[offset++];
+ channel_count = fru_data[offset++];
+
+ printf("\n");
+ printf(" Clock Resource ID: 0x%02x Type: ", resource_id);
+ if((resource_id & 0xC0)>>6 == 0) {printf("On-Carrier-Device\n");}
+ else if((resource_id & 0xC0)>>6 == 1) {printf("AMC slot\n");}
+ else if((resource_id & 0xC0)>>6 == 2) {printf("Backplane\n");}
+ else{ printf("reserved\n");}
+ printf(" Channel Count: 0x%02x\n", channel_count);
+
+ for(j=0; j<channel_count; j++){
+ unsigned char loc_channel, rem_channel, rem_resource;
+
+ loc_channel = fru_data[offset++];
+ rem_channel = fru_data[offset++];
+ rem_resource = fru_data[offset++];
+
+ printf(" CLK-ID: 0x%02x ->", loc_channel);
+ printf(" remote CLKID: 0x%02x ", rem_channel);
+ if((rem_resource & 0xC0)>>6 == 0) {printf("[ Carrier-Dev");}
+ else if((rem_resource & 0xC0)>>6 == 1) {printf("[ AMC slot ");}
+ else if((rem_resource & 0xC0)>>6 == 2) {printf("[ Backplane ");}
+ else{ printf("reserved ");}
+ printf(" 0x%02x ]\n", rem_resource&0xF);
+ }
+ }
+ printf("\n");
+ }
+ break;
+ case FRU_PICMG_CLK_CONFIG:
+ {
+ unsigned char resource_id, descr_count;
+ int i,j;
+
+ printf(" FRU_PICMG_CLK_CONFIG\n");
+
+ resource_id = fru_data[offset++];
+ descr_count = fru_data[offset++];
+
+ printf("\n");
+ printf(" Clock Resource ID: 0x%02x\n", resource_id);
+ printf(" Descr. Count: 0x%02x\n", descr_count);
+
+ for(i=0; i<descr_count; i++){
+ unsigned char channel_id, control;
+ unsigned char indirect_cnt, direct_cnt;
+
+ channel_id = fru_data[offset++];
+ control = fru_data[offset++];
+ printf(" CLK-ID: 0x%02x - ", channel_id);
+ printf("CTRL 0x%02x [ %12s ]\n",
+ control,
+ ((control&0x1)==0)?"Carrier IPMC":"Application");
+
+ indirect_cnt = fru_data[offset++];
+ direct_cnt = fru_data[offset++];
+ printf(" Cnt: Indirect 0x%02x / Direct 0x%02x\n",
+ indirect_cnt,
+ direct_cnt);
+
+ /* indirect desc */
+ for(j=0; j<indirect_cnt; j++){
+ unsigned char feature;
+ unsigned char dep_chn_id;
+
+ feature = fru_data[offset++];
+ dep_chn_id = fru_data[offset++];
+
+ printf(" Feature: 0x%02x [%8s] - ", feature, (feature&0x1)==1?"Source":"Receiver");
+ printf(" Dep. CLK-ID: 0x%02x\n", dep_chn_id);
+ }
+
+ /* direct desc */
+ for(j=0; j<direct_cnt; j++){
+ unsigned char feature, family, accuracy;
+ unsigned int freq, min_freq, max_freq;
+
+ feature = fru_data[offset++];
+ family = fru_data[offset++];
+ accuracy = fru_data[offset++];
+ freq = (fru_data[offset+0] << 0 ) | (fru_data[offset+1] << 8 )
+ | (fru_data[offset+2] << 16) | (fru_data[offset+3] << 24);
+ offset += 4;
+ min_freq = (fru_data[offset+0] << 0 ) | (fru_data[offset+1] << 8 )
+ | (fru_data[offset+2] << 16) | (fru_data[offset+3] << 24);
+ offset += 4;
+ max_freq = (fru_data[offset+0] << 0 ) | (fru_data[offset+1] << 8 )
+ | (fru_data[offset+2] << 16) | (fru_data[offset+3] << 24);
+ offset += 4;
+
+ printf(" - Feature: 0x%02x - PLL: %x / Asym: %s\n",
+ feature,
+ (feature > 1) & 1,
+ (feature&1)?"Source":"Receiver");
+ printf(" Family: 0x%02x - AccLVL: 0x%02x\n", family, accuracy);
+ printf(" FRQ: %-9ld - min: %-9ld - max: %-9ld\n",
+ freq, min_freq, max_freq);
+ }
+ printf("\n");
+ }
+ printf("\n");
+ }
+ break;
+
+ case FRU_UTCA_FRU_INFO_TABLE:
+ case FRU_UTCA_CARRIER_MNG_IP:
+ case FRU_UTCA_CARRIER_INFO:
+ case FRU_UTCA_CARRIER_LOCATION:
+ case FRU_UTCA_SHMC_IP_LINK:
+ case FRU_UTCA_POWER_POLICY:
+ case FRU_UTCA_ACTIVATION:
+ case FRU_UTCA_PM_CAPABILTY:
+ case FRU_UTCA_FAN_GEOGRAPHY:
+ case FRU_UTCA_CLOCK_MAPPING:
+ case FRU_UTCA_MSG_BRIDGE_POLICY:
+ case FRU_UTCA_OEM_MODULE_DESC:
+ printf(" Not implemented yet. uTCA specific record found!!\n");
+ printf(" - Record ID: 0x%02x\n", h->record_id);
+ break;
+
+ default:
+ printf(" Unknown OEM Extension Record ID: %x\n", h->record_id);
+ break;
+
+ }
+}
+
+
+/* __ipmi_fru_print - Do actual work to print a FRU by its ID
+*
+* @intf: ipmi interface
+* @id: fru id
+*
+* returns -1 on error
+* returns 0 if successful
+* returns 1 if device not present
+*/
+static int
+__ipmi_fru_print(struct ipmi_intf * intf, uint8_t id)
+{
+ struct ipmi_rs * rsp;
+ struct ipmi_rq req;
+ struct fru_info fru;
+ struct fru_header header;
+ uint8_t msg_data[4];
+
+ memset(&fru, 0, sizeof(struct fru_info));
+ memset(&header, 0, sizeof(struct fru_header));
+
+ /*
+ * get info about this FRU
+ */
+ memset(msg_data, 0, 4);
+ msg_data[0] = id;
+
+ memset(&req, 0, sizeof(req));
+ req.msg.netfn = IPMI_NETFN_STORAGE;
+ req.msg.cmd = GET_FRU_INFO;
+ req.msg.data = msg_data;
+ req.msg.data_len = 1;
+
+ rsp = intf->sendrecv(intf, &req);
+ if (rsp == NULL) {
+ printf(" Device not present (No Response)\n");
+ return -1;
+ }
+ if (rsp->ccode > 0) {
+ printf(" Device not present (%s)\n",
+ val2str(rsp->ccode, completion_code_vals));
+ return -1;
+ }
+
+ memset(&fru, 0, sizeof(fru));
+ fru.size = (rsp->data[1] << 8) | rsp->data[0];
+ fru.access = rsp->data[2] & 0x1;
+
+ lprintf(LOG_DEBUG, "fru.size = %d bytes (accessed by %s)",
+ fru.size, fru.access ? "words" : "bytes");
+
+ if (fru.size < 1) {
+ lprintf(LOG_ERR, " Invalid FRU size %d", fru.size);
+ return -1;
+ }
+
+ /*
+ * retrieve the FRU header
+ */
+ msg_data[0] = id;
+ msg_data[1] = 0;
+ msg_data[2] = 0;
+ msg_data[3] = 8;
+
+ memset(&req, 0, sizeof(req));
+ req.msg.netfn = IPMI_NETFN_STORAGE;
+ req.msg.cmd = GET_FRU_DATA;
+ req.msg.data = msg_data;
+ req.msg.data_len = 4;
+
+ rsp = intf->sendrecv(intf, &req);
+ if (rsp == NULL) {
+ printf(" Device not present (No Response)\n");
+ return 1;
+ }
+ if (rsp->ccode > 0) {
+ printf(" Device not present (%s)\n",
+ val2str(rsp->ccode, completion_code_vals));
+ return 1;
+ }
+
+ if (verbose > 1)
+ printbuf(rsp->data, rsp->data_len, "FRU DATA");
+
+ memcpy(&header, rsp->data + 1, 8);
+
+ if (header.version != 1) {
+ lprintf(LOG_ERR, " Unknown FRU header version 0x%02x",
+ header.version);
+ return -1;
+ }
+
+ /* offsets need converted to bytes
+ * but that conversion is not done to the structure
+ * because we may end up with offset > 255
+ * which would overflow our 1-byte offset field */
+
+ lprintf(LOG_DEBUG, "fru.header.version: 0x%x",
+ header.version);
+ lprintf(LOG_DEBUG, "fru.header.offset.internal: 0x%x",
+ header.offset.internal * 8);
+ lprintf(LOG_DEBUG, "fru.header.offset.chassis: 0x%x",
+ header.offset.chassis * 8);
+ lprintf(LOG_DEBUG, "fru.header.offset.board: 0x%x",
+ header.offset.board * 8);
+ lprintf(LOG_DEBUG, "fru.header.offset.product: 0x%x",
+ header.offset.product * 8);
+ lprintf(LOG_DEBUG, "fru.header.offset.multi: 0x%x",
+ header.offset.multi * 8);
+
+ /*
+ * rather than reading the entire part
+ * only read the areas we'll format
+ */
+ /* chassis area */
+ if ((header.offset.chassis*8) >= sizeof(struct fru_header))
+ fru_area_print_chassis(intf, &fru, id, header.offset.chassis*8);
+
+ /* board area */
+ if ((header.offset.board*8) >= sizeof(struct fru_header))
+ fru_area_print_board(intf, &fru, id, header.offset.board*8);
+
+ /* product area */
+ if ((header.offset.product*8) >= sizeof(struct fru_header))
+ fru_area_print_product(intf, &fru, id, header.offset.product*8);
+
+ /* multirecord area */
+ if( verbose==0 ) /* scipp parsing multirecord */
+ return 0;
+
+ if ((header.offset.multi*8) >= sizeof(struct fru_header))
+ fru_area_print_multirec(intf, &fru, id, header.offset.multi*8);
+
+ return 0;
+}
+
+/* ipmi_fru_print - Print a FRU from its SDR locator record
+*
+* @intf: ipmi interface
+* @fru: SDR FRU Locator Record
+*
+* returns -1 on error
+*/
+int
+ipmi_fru_print(struct ipmi_intf * intf, struct sdr_record_fru_locator * fru)
+{
+ char desc[17];
+ uint8_t bridged_request = 0;
+ uint32_t save_addr;
+ uint32_t save_channel;
+ int rc = 0;
+
+ if (fru == NULL)
+ return __ipmi_fru_print(intf, 0);
+
+ /* Logical FRU Device
+ * dev_type == 0x10
+ * modifier
+ * 0x00 = IPMI FRU Inventory
+ * 0x01 = DIMM Memory ID
+ * 0x02 = IPMI FRU Inventory
+ * 0x03 = System Processor FRU
+ * 0xff = unspecified
+ *
+ * EEPROM 24C01 or equivalent
+ * dev_type >= 0x08 && dev_type <= 0x0f
+ * modifier
+ * 0x00 = unspecified
+ * 0x01 = DIMM Memory ID
+ * 0x02 = IPMI FRU Inventory
+ * 0x03 = System Processor Cartridge
+ */
+ if (fru->dev_type != 0x10 &&
+ (fru->dev_type_modifier != 0x02 ||
+ fru->dev_type < 0x08 || fru->dev_type > 0x0f))
+ return -1;
+
+ if (fru->dev_slave_addr == IPMI_BMC_SLAVE_ADDR &&
+ fru->device_id == 0)
+ return 0;
+
+ memset(desc, 0, sizeof(desc));
+ memcpy(desc, fru->id_string, fru->id_code & 0x01f);
+ desc[fru->id_code & 0x01f] = 0;
+ printf("FRU Device Description : %s (ID %d)\n", desc, fru->device_id);
+
+ switch (fru->dev_type_modifier) {
+ case 0x00:
+ case 0x02:
+ if (BRIDGE_TO_SENSOR(intf, fru->dev_slave_addr,
+ fru->channel_num)) {
+ bridged_request = 1;
+ save_addr = intf->target_addr;
+ intf->target_addr = fru->dev_slave_addr;
+ save_channel = intf->target_channel;
+ intf->target_channel = fru->channel_num;
+ }
+ /* print FRU */
+ rc = __ipmi_fru_print(intf, fru->device_id);
+ if (bridged_request) {
+ intf->target_addr = save_addr;
+ intf->target_channel = save_channel;
+ }
+ break;
+ case 0x01:
+ rc = ipmi_spd_print_fru(intf, fru->device_id);
+ break;
+ default:
+ if (verbose)
+ printf(" Unsupported device 0x%02x "
+ "type 0x%02x with modifier 0x%02x\n",
+ fru->device_id, fru->dev_type,
+ fru->dev_type_modifier);
+ else
+ printf(" Unsupported device\n");
+ }
+ printf("\n");
+
+ return rc;
+}
+
+/* ipmi_fru_print_all - Print builtin FRU + SDR FRU Locator records
+*
+* @intf: ipmi interface
+*
+* returns -1 on error
+*/
+static int
+ipmi_fru_print_all(struct ipmi_intf * intf)
+{
+ struct ipmi_sdr_iterator * itr;
+ struct sdr_get_rs * header;
+ struct sdr_record_fru_locator * fru;
+ int rc;
+ struct ipmi_rs * rsp;
+ struct ipmi_rq req;
+ struct ipm_devid_rsp *devid;
+ struct sdr_record_mc_locator * mc;
+ uint32_t save_addr;
+
+ printf("FRU Device Description : Builtin FRU Device (ID 0)\n");
+ /* TODO: Figure out if FRU device 0 may show up in SDR records. */
+
+ /* Do a Get Device ID command to determine device support */
+ memset (&req, 0, sizeof(req));
+ req.msg.netfn = IPMI_NETFN_APP;
+ req.msg.cmd = BMC_GET_DEVICE_ID;
+ req.msg.data_len = 0;
+
+ rsp = intf->sendrecv(intf, &req);
+ if (rsp == NULL) {
+ lprintf(LOG_ERR, "Get Device ID command failed");
+ return -1;
+ }
+ if (rsp->ccode > 0) {
+ lprintf(LOG_ERR, "Get Device ID command failed: %s",
+ val2str(rsp->ccode, completion_code_vals));
+ return -1;
+ }
+
+ devid = (struct ipm_devid_rsp *) rsp->data;
+
+ /* Check the FRU inventory device bit to decide whether various */
+ /* FRU commands can be issued to FRU device #0 LUN 0 */
+
+ if (devid->adtl_device_support & 0x08) { /* FRU Inventory Device bit? */
+ rc = ipmi_fru_print(intf, NULL);
+ printf("\n");
+ }
+
+ if ((itr = ipmi_sdr_start(intf, 0)) == NULL)
+ return -1;
+
+ /* Walk the SDRs looking for FRU Devices and Management Controller Devices. */
+ /* For FRU devices, print the FRU from the SDR locator record. */
+ /* For MC devices, issue FRU commands to the satellite controller to print */
+ /* FRU data. */
+ while ((header = ipmi_sdr_get_next_header(intf, itr)) != NULL)
+ {
+ if (header->type == SDR_RECORD_TYPE_MC_DEVICE_LOCATOR ) {
+ /* Check the capabilities of the Management Controller Device */
+ mc = (struct sdr_record_mc_locator *)
+ ipmi_sdr_get_record(intf, header, itr);
+ /* Does this MC device support FRU inventory device? */
+ if (mc && (mc->dev_support & 0x08)) { /* FRU inventory device? */
+ /* Yes. Prepare to issue FRU commands to FRU device #0 LUN 0 */
+ /* using the slave address specified in the MC record. */
+
+ /* save current target address */
+ save_addr = intf->target_addr;
+
+ /* set new target address to satellite controller */
+ intf->target_addr = mc->dev_slave_addr;
+
+ printf("FRU Device Description : %-16s\n", mc->id_string);
+
+ /* print the FRU by issuing FRU commands to the satellite */
+ /* controller. */
+ rc = __ipmi_fru_print(intf, 0);
+
+ printf("\n");
+
+ /* restore previous target */
+ intf->target_addr = save_addr;
+ }
+
+ if (mc) {
+ free(mc);
+ mc = NULL;
+ }
+
+ continue;
+ }
+
+ if (header->type != SDR_RECORD_TYPE_FRU_DEVICE_LOCATOR)
+ continue;
+
+ /* Print the FRU from the SDR locator record. */
+ fru = (struct sdr_record_fru_locator *)
+ ipmi_sdr_get_record(intf, header, itr);
+ if (fru == NULL || !fru->logical) {
+ if (fru) {
+ free(fru);
+ fru = NULL;
+ }
+ continue;
+ }
+ rc = ipmi_fru_print(intf, fru);
+ free(fru);
+ fru = NULL;
+ }
+
+ ipmi_sdr_end(intf, itr);
+
+ return rc;
+}
+
+/* ipmi_fru_read_help() - print help text for 'read'
+ *
+ * returns void
+ */
+void
+ipmi_fru_read_help()
+{
+ lprintf(LOG_NOTICE, "fru read <fru id> <fru file>");
+ lprintf(LOG_NOTICE, "Note: FRU ID and file(incl. full path) must be specified.");
+ lprintf(LOG_NOTICE, "Example: ipmitool fru read 0 /root/fru.bin");
+} /* ipmi_fru_read_help() */
+
+static void
+ipmi_fru_read_to_bin(struct ipmi_intf * intf,
+ char * pFileName,
+ uint8_t fruId)
+{
+ struct ipmi_rs * rsp;
+ struct ipmi_rq req;
+ struct fru_info fru;
+ uint8_t msg_data[4];
+ uint8_t * pFruBuf;
+
+ msg_data[0] = fruId;
+
+ memset(&req, 0, sizeof(req));
+ req.msg.netfn = IPMI_NETFN_STORAGE;
+ req.msg.cmd = GET_FRU_INFO;
+ req.msg.data = msg_data;
+ req.msg.data_len = 1;
+
+ rsp = intf->sendrecv(intf, &req);
+ if (!rsp)
+ return;
+
+ if (rsp->ccode > 0) {
+ if (rsp->ccode == 0xc3)
+ printf (" Timeout accessing FRU info. (Device not present?)\n");
+ return;
+ }
+
+ memset(&fru, 0, sizeof(fru));
+ fru.size = (rsp->data[1] << 8) | rsp->data[0];
+ fru.access = rsp->data[2] & 0x1;
+
+ if (verbose) {
+ printf("Fru Size = %d bytes\n",fru.size);
+ printf("Fru Access = %xh\n", fru.access);
+ }
+
+ pFruBuf = malloc(fru.size);
+ if (pFruBuf != NULL) {
+ printf("Fru Size : %d bytes\n",fru.size);
+ read_fru_area(intf, &fru, fruId, 0, fru.size, pFruBuf);
+ } else {
+ lprintf(LOG_ERR, "Cannot allocate %d bytes\n", fru.size);
+ return;
+ }
+
+ if(pFruBuf != NULL)
+ {
+ FILE * pFile;
+ pFile = fopen(pFileName,"wb");
+ if (pFile) {
+ fwrite(pFruBuf, fru.size, 1, pFile);
+ printf("Done\n");
+ } else {
+ lprintf(LOG_ERR, "Error opening file %s\n", pFileName);
+ free(pFruBuf);
+ pFruBuf = NULL;
+ return;
+ }
+ fclose(pFile);
+ }
+ free(pFruBuf);
+ pFruBuf = NULL;
+}
+
+static void
+ipmi_fru_write_from_bin(struct ipmi_intf * intf,
+ char * pFileName,
+ uint8_t fruId)
+{
+ struct ipmi_rs *rsp;
+ struct ipmi_rq req;
+ struct fru_info fru;
+ uint8_t msg_data[4];
+ uint8_t *pFruBuf;
+ uint16_t len = 0;
+ FILE *pFile;
+
+ msg_data[0] = fruId;
+
+ memset(&req, 0, sizeof (req));
+ req.msg.netfn = IPMI_NETFN_STORAGE;
+ req.msg.cmd = GET_FRU_INFO;
+ req.msg.data = msg_data;
+ req.msg.data_len = 1;
+
+ rsp = intf->sendrecv(intf, &req);
+ if (!rsp)
+ return;
+
+ if (rsp->ccode) {
+ if (rsp->ccode == 0xc3)
+ printf(" Timeout accessing FRU info. (Device not present?)\n");
+ return;
+ }
+
+ memset(&fru, 0, sizeof(fru));
+ fru.size = (rsp->data[1] << 8) | rsp->data[0];
+ fru.access = rsp->data[2] & 0x1;
+
+ if (verbose) {
+ printf("Fru Size = %d bytes\n", fru.size);
+ printf("Fru Access = %xh\n", fru.access);
+ }
+
+ pFruBuf = malloc(fru.size);
+ if (pFruBuf == NULL) {
+ lprintf(LOG_ERR, "Cannot allocate %d bytes\n", fru.size);
+ return;
+ }
+
+ pFile = fopen(pFileName, "rb");
+ if (pFile != NULL) {
+ len = fread(pFruBuf, 1, fru.size, pFile);
+ printf("Fru Size : %d bytes\n", fru.size);
+ printf("Size to Write : %d bytes\n", len);
+ fclose(pFile);
+ } else {
+ lprintf(LOG_ERR, "Error opening file %s\n", pFileName);
+ }
+
+ if (len != 0) {
+ write_fru_area(intf, &fru, fruId,0, 0, len, pFruBuf);
+ lprintf(LOG_INFO,"Done");
+ }
+
+ free(pFruBuf);
+ pFruBuf = NULL;
+}
+
+/* ipmi_fru_write_help() - print help text for 'write'
+ *
+ * retruns void
+ */
+void
+ipmi_fru_write_help()
+{
+ lprintf(LOG_NOTICE, "fru write <fru id> <fru file>");
+ lprintf(LOG_NOTICE, "Note: FRU ID and file(incl. full path) must be specified.");
+ lprintf(LOG_NOTICE, "Example: ipmitool fru write 0 /root/fru.bin");
+} /* ipmi_fru_write_help() */
+
+/* ipmi_fru_edit_help - print help text for 'fru edit' command
+ *
+ * returns void
+ */
+void
+ipmi_fru_edit_help()
+{
+ lprintf(LOG_NOTICE,
+ "fru edit <fruid> field <section> <index> <string> - edit FRU string");
+ lprintf(LOG_NOTICE,
+ "fru edit <fruid> oem iana <record> <format> <args> - limited OEM support");
+} /* ipmi_fru_edit_help() */
+
+/* ipmi_fru_edit_multirec - Query new values to replace original FRU content
+*
+* @intf: interface to use
+* @id: FRU id to work on
+*
+* returns: nothing
+*/
+/* Work in progress, copy paste most of the stuff for other functions in this
+ file ... not elegant yet */
+static int
+ipmi_fru_edit_multirec(struct ipmi_intf * intf, uint8_t id ,
+ int argc, char ** argv)
+{
+
+ struct ipmi_rs * rsp;
+ struct ipmi_rq req;
+ struct fru_info fru;
+ struct fru_header header;
+ uint8_t msg_data[4];
+
+ uint16_t retStatus = 0;
+ uint32_t offFruMultiRec;
+ uint32_t fruMultiRecSize = 0;
+ struct fru_info fruInfo;
+ retStatus = ipmi_fru_get_multirec_location_from_fru(intf, id, &fruInfo,
+ &offFruMultiRec,
+ &fruMultiRecSize);
+
+
+ lprintf(LOG_DEBUG, "FRU Size : %lu\n", fruMultiRecSize);
+ lprintf(LOG_DEBUG, "Multi Rec offset: %lu\n", offFruMultiRec);
+
+ {
+
+
+ memset(&fru, 0, sizeof(struct fru_info));
+ memset(&header, 0, sizeof(struct fru_header));
+
+ /*
+ * get info about this FRU
+ */
+ memset(msg_data, 0, 4);
+ msg_data[0] = id;
+
+ memset(&req, 0, sizeof(req));
+ req.msg.netfn = IPMI_NETFN_STORAGE;
+ req.msg.cmd = GET_FRU_INFO;
+ req.msg.data = msg_data;
+ req.msg.data_len = 1;
+
+ rsp = intf->sendrecv(intf, &req);
+ if (rsp == NULL) {
+ printf(" Device not present (No Response)\n");
+ return -1;
+ }
+ if (rsp->ccode > 0) {
+ printf(" Device not present (%s)\n",
+ val2str(rsp->ccode, completion_code_vals));
+ return -1;
+ }
+
+ memset(&fru, 0, sizeof(fru));
+ fru.size = (rsp->data[1] << 8) | rsp->data[0];
+ fru.access = rsp->data[2] & 0x1;
+
+ lprintf(LOG_DEBUG, "fru.size = %d bytes (accessed by %s)",
+ fru.size, fru.access ? "words" : "bytes");
+
+ if (fru.size < 1) {
+ lprintf(LOG_ERR, " Invalid FRU size %d", fru.size);
+ return -1;
+ }
+ }
+
+ {
+ uint8_t * fru_data;
+ uint32_t fru_len, i;
+ uint32_t offset= offFruMultiRec;
+ struct fru_multirec_header * h;
+ uint32_t last_off, len;
+ uint8_t error=0;
+
+ i = last_off = offset;
+ fru_len = 0;
+
+ memset(&fru, 0, sizeof(fru));
+ fru_data = malloc(fru.size + 1);
+ if (fru_data == NULL) {
+ lprintf(LOG_ERR, " Out of memory!");
+ return -1;
+ }
+ memset(fru_data, 0, fru.size + 1);
+
+ do {
+ h = (struct fru_multirec_header *) (fru_data + i);
+
+ /* read area in (at most) FRU_MULTIREC_CHUNK_SIZE bytes at a time */
+ if ((last_off < (i + sizeof(*h))) || (last_off < (i + h->len)))
+ {
+ len = fru.size - last_off;
+ if (len > FRU_MULTIREC_CHUNK_SIZE)
+ len = FRU_MULTIREC_CHUNK_SIZE;
+
+ if (read_fru_area(intf, &fru, id, last_off, len, fru_data) < 0)
+ break;
+
+ last_off += len;
+ }
+ if( h->type == FRU_RECORD_TYPE_OEM_EXTENSION ){
+
+ struct fru_multirec_oem_header *oh=(struct fru_multirec_oem_header *)
+ &fru_data[i + sizeof(struct fru_multirec_header)];
+ uint32_t iana = oh->mfg_id[0] | oh->mfg_id[1]<<8 | oh->mfg_id[2]<<16;
+
+ uint32_t suppliedIana = 0 ;
+ /* Now makes sure this is really PICMG record */
+
+ /* Default to PICMG for backward compatibility */
+ if( argc <=2 ) {
+ suppliedIana = IPMI_OEM_PICMG;
+ } else {
+ if( !strncmp( argv[2] , "oem" , 3 )) {
+ /* Expect IANA number next */
+ if( argc <= 3 ) {
+ lprintf(LOG_ERR, "oem iana <record> <format> [<args>]");
+ error = 1;
+ } else {
+ if (str2uint(argv[3], &suppliedIana) == 0) {
+ lprintf(LOG_DEBUG,
+ "using iana: %d",
+ suppliedIana);
+ } else {
+ lprintf(LOG_ERR,
+ "Given IANA '%s' is invalid.",
+ argv[3]);
+ error = 1;
+ }
+ }
+ }
+ }
+
+ if( suppliedIana == iana ) {
+ lprintf(LOG_DEBUG, "Matching record found" );
+
+ if( iana == IPMI_OEM_PICMG ){
+ if( ipmi_fru_picmg_ext_edit(fru_data,
+ i + sizeof(struct fru_multirec_header),
+ h->len, h, oh )){
+ /* The fru changed */
+ write_fru_area(intf,&fru,id, i,i,
+ h->len+ sizeof(struct fru_multirec_header), fru_data);
+ }
+ }
+ else if( iana == IPMI_OEM_KONTRON ) {
+ if( ipmi_fru_oemkontron_edit( argc,argv,fru_data,
+ i + sizeof(struct fru_multirec_header),
+ h->len, h, oh )){
+ /* The fru changed */
+ write_fru_area(intf,&fru,id, i,i,
+ h->len+ sizeof(struct fru_multirec_header), fru_data);
+ }
+ }
+ /* FIXME: Add OEM record support here */
+ else{
+ printf(" OEM IANA (%s) Record not support in this mode\n",
+ val2str( iana, ipmi_oem_info));
+ error = 1;
+ }
+ }
+ }
+ i += h->len + sizeof (struct fru_multirec_header);
+ } while (!(h->format & 0x80) && (error != 1));
+
+ free(fru_data);
+ fru_data = NULL;
+ }
+ return 0;
+}
+
+/* ipmi_fru_get_help - print help text for 'fru get'
+ *
+ * returns void
+ */
+void
+ipmi_fru_get_help()
+{
+ lprintf(LOG_NOTICE,
+ "fru get <fruid> oem iana <record> <format> <args> - limited OEM support");
+} /* ipmi_fru_get_help() */
+
+void
+ipmi_fru_internaluse_help()
+{
+ lprintf(LOG_NOTICE,
+ "fru internaluse <fru id> info - get internal use area size");
+ lprintf(LOG_NOTICE,
+ "fru internaluse <fru id> print - print internal use area in hex");
+ lprintf(LOG_NOTICE,
+ "fru internaluse <fru id> read <fru file> - read internal use area to file");
+ lprintf(LOG_NOTICE,
+ "fru internaluse <fru id> write <fru file> - write internal use area from file");
+} /* void ipmi_fru_internaluse_help() */
+
+/* ipmi_fru_get_multirec - Query new values to replace original FRU content
+*
+* @intf: interface to use
+* @id: FRU id to work on
+*
+* returns: nothing
+*/
+/* Work in progress, copy paste most of the stuff for other functions in this
+ file ... not elegant yet */
+static int
+ipmi_fru_get_multirec(struct ipmi_intf * intf, uint8_t id ,
+ int argc, char ** argv)
+{
+
+ struct ipmi_rs * rsp;
+ struct ipmi_rq req;
+ struct fru_info fru;
+ struct fru_header header;
+ uint8_t msg_data[4];
+
+ uint16_t retStatus = 0;
+ uint32_t offFruMultiRec;
+ uint32_t fruMultiRecSize = 0;
+ struct fru_info fruInfo;
+ retStatus = ipmi_fru_get_multirec_location_from_fru(intf, id, &fruInfo,
+ &offFruMultiRec,
+ &fruMultiRecSize);
+
+
+ lprintf(LOG_DEBUG, "FRU Size : %lu\n", fruMultiRecSize);
+ lprintf(LOG_DEBUG, "Multi Rec offset: %lu\n", offFruMultiRec);
+
+ {
+
+
+ memset(&fru, 0, sizeof(struct fru_info));
+ memset(&header, 0, sizeof(struct fru_header));
+
+ /*
+ * get info about this FRU
+ */
+ memset(msg_data, 0, 4);
+ msg_data[0] = id;
+
+ memset(&req, 0, sizeof(req));
+ req.msg.netfn = IPMI_NETFN_STORAGE;
+ req.msg.cmd = GET_FRU_INFO;
+ req.msg.data = msg_data;
+ req.msg.data_len = 1;
+
+ rsp = intf->sendrecv(intf, &req);
+ if (rsp == NULL) {
+ printf(" Device not present (No Response)\n");
+ return -1;
+ }
+ if (rsp->ccode > 0) {
+ printf(" Device not present (%s)\n",
+ val2str(rsp->ccode, completion_code_vals));
+ return -1;
+ }
+
+ memset(&fru, 0, sizeof(fru));
+ fru.size = (rsp->data[1] << 8) | rsp->data[0];
+ fru.access = rsp->data[2] & 0x1;
+
+ lprintf(LOG_DEBUG, "fru.size = %d bytes (accessed by %s)",
+ fru.size, fru.access ? "words" : "bytes");
+
+ if (fru.size < 1) {
+ lprintf(LOG_ERR, " Invalid FRU size %d", fru.size);
+ return -1;
+ }
+ }
+
+ {
+ uint8_t * fru_data;
+ uint32_t fru_len, i;
+ uint32_t offset= offFruMultiRec;
+ struct fru_multirec_header * h;
+ uint32_t last_off, len;
+ uint8_t error=0;
+
+ i = last_off = offset;
+ fru_len = 0;
+
+ fru_data = malloc(fru.size + 1);
+ if (fru_data == NULL) {
+ lprintf(LOG_ERR, " Out of memory!");
+ return -1;
+ }
+ memset(fru_data, 0, fru.size + 1);
+
+ do {
+ h = (struct fru_multirec_header *) (fru_data + i);
+
+ /* read area in (at most) FRU_MULTIREC_CHUNK_SIZE bytes at a time */
+ if ((last_off < (i + sizeof(*h))) || (last_off < (i + h->len)))
+ {
+ len = fru.size - last_off;
+ if (len > FRU_MULTIREC_CHUNK_SIZE)
+ len = FRU_MULTIREC_CHUNK_SIZE;
+
+ if (read_fru_area(intf, &fru, id, last_off, len, fru_data) < 0)
+ break;
+
+ last_off += len;
+ }
+ if( h->type == FRU_RECORD_TYPE_OEM_EXTENSION ){
+
+ struct fru_multirec_oem_header *oh=(struct fru_multirec_oem_header *)
+ &fru_data[i + sizeof(struct fru_multirec_header)];
+ uint32_t iana = oh->mfg_id[0] | oh->mfg_id[1]<<8 | oh->mfg_id[2]<<16;
+
+ uint32_t suppliedIana = 0 ;
+ /* Now makes sure this is really PICMG record */
+ if( !strncmp( argv[2] , "oem" , 3 )) {
+ /* Expect IANA number next */
+ if( argc <= 3 ) {
+ lprintf(LOG_ERR, "oem iana <record> <format>");
+ error = 1;
+ } else {
+ if (str2uint(argv[3], &suppliedIana) == 0) {
+ lprintf(LOG_DEBUG,
+ "using iana: %d",
+ suppliedIana);
+ } else {
+ lprintf(LOG_ERR,
+ "Given IANA '%s' is invalid.",
+ argv[3]);
+ error = 1;
+ }
+ }
+ }
+
+ if( suppliedIana == iana ) {
+ lprintf(LOG_DEBUG, "Matching record found" );
+
+ if( iana == IPMI_OEM_KONTRON ) {
+ ipmi_fru_oemkontron_get( argc,argv,fru_data,
+ i + sizeof(struct fru_multirec_header),
+ h->len, h, oh );
+ }
+ /* FIXME: Add OEM record support here */
+ else{
+ printf(" OEM IANA (%s) Record not supported in this mode\n",
+ val2str( iana, ipmi_oem_info));
+ error = 1;
+ }
+ }
+ }
+ i += h->len + sizeof (struct fru_multirec_header);
+ } while (!(h->format & 0x80) && (error != 1));
+
+ free(fru_data);
+ fru_data = NULL;
+ }
+ return 0;
+}
+
+static int
+ipmi_fru_upg_ekeying(struct ipmi_intf * intf,
+ char * pFileName,
+ uint8_t fruId)
+{
+ struct fru_info fruInfo;
+ uint8_t *buf = NULL;
+ uint32_t offFruMultiRec = 0;
+ uint32_t fruMultiRecSize = 0;
+ uint32_t offFileMultiRec = 0;
+ uint32_t fileMultiRecSize = 0;
+ if (pFileName == NULL) {
+ lprintf(LOG_ERR, "File expected, but none given.");
+ return (-1);
+ }
+ if (ipmi_fru_get_multirec_location_from_fru(intf, fruId, &fruInfo,
+ &offFruMultiRec, &fruMultiRecSize) != 0) {
+ lprintf(LOG_ERR, "Failed to get multirec location from FRU.");
+ return (-1);
+ }
+ lprintf(LOG_DEBUG, "FRU Size : %lu\n", fruMultiRecSize);
+ lprintf(LOG_DEBUG, "Multi Rec offset: %lu\n", offFruMultiRec);
+ if (ipmi_fru_get_multirec_size_from_file(pFileName, &fileMultiRecSize,
+ &offFileMultiRec) != 0) {
+ lprintf(LOG_ERR, "Failed to get multirec size from file '%s'.", pFileName);
+ return (-1);
+ }
+ buf = malloc(fileMultiRecSize);
+ if (buf == NULL) {
+ lprintf(LOG_ERR, "ipmitool: malloc failure");
+ return (-1);
+ }
+ if (ipmi_fru_get_multirec_from_file(pFileName, buf, fileMultiRecSize,
+ offFileMultiRec) != 0) {
+ lprintf(LOG_ERR, "Failed to get multirec from file '%s'.", pFileName);
+ if (buf != NULL) {
+ free(buf);
+ buf = NULL;
+ }
+ return (-1);
+ }
+ if (ipmi_fru_get_adjust_size_from_buffer(buf, &fileMultiRecSize) != 0) {
+ lprintf(LOG_ERR, "Failed to adjust size from buffer.");
+ if (buf != NULL) {
+ free(buf);
+ buf = NULL;
+ }
+ return (-1);
+ }
+ if (write_fru_area(intf, &fruInfo, fruId, 0, offFruMultiRec,
+ fileMultiRecSize, buf) != 0) {
+ lprintf(LOG_ERR, "Failed to write FRU area.");
+ if (buf != NULL) {
+ free(buf);
+ buf = NULL;
+ }
+ return (-1);
+ }
+ if (buf != NULL) {
+ free(buf);
+ buf = NULL;
+ }
+ lprintf(LOG_INFO, "Done upgrading Ekey.");
+ return 0;
+}
+
+/* ipmi_fru_upgekey_help - print help text for 'upgEkey'
+ *
+ * returns void
+ */
+void
+ipmi_fru_upgekey_help()
+{
+ lprintf(LOG_NOTICE, "fru upgEkey <fru id> <fru file>");
+ lprintf(LOG_NOTICE, "Note: FRU ID and file(incl. full path) must be specified.");
+ lprintf(LOG_NOTICE, "Example: ipmitool fru upgEkey 0 /root/fru.bin");
+} /* ipmi_fru_upgekey_help() */
+
+static int
+ipmi_fru_get_multirec_size_from_file(char * pFileName,
+ uint32_t * pSize,
+ uint32_t * pOffset)
+{
+ struct fru_header header;
+ FILE * pFile;
+ uint8_t len = 0;
+ uint32_t end = 0;
+ *pSize = 0;
+
+ pFile = fopen(pFileName,"rb");
+ if (pFile) {
+ rewind(pFile);
+ len = fread(&header, 1, 8, pFile);
+ fseek(pFile, 0, SEEK_END);
+ end = ftell(pFile);
+ fclose(pFile);
+ }
+
+ lprintf(LOG_DEBUG, "File Size = %lu\n", end);
+ lprintf(LOG_DEBUG, "Len = %u\n", len);
+
+ if (len != 8) {
+ printf("Error with file %s in getting size\n", pFileName);
+ return -1;
+ }
+
+ if (header.version != 0x01) {
+ printf ("Unknown FRU header version %02x.\n", header.version);
+ return -1;
+ }
+
+ /* Retreive length */
+ if (((header.offset.internal * 8) > (header.offset.internal * 8)) &&
+ ((header.offset.internal * 8) < end))
+ end = (header.offset.internal * 8);
+
+ if (((header.offset.chassis * 8) > (header.offset.chassis * 8)) &&
+ ((header.offset.chassis * 8) < end))
+ end = (header.offset.chassis * 8);
+
+ if (((header.offset.board * 8) > (header.offset.board * 8)) &&
+ ((header.offset.board * 8) < end))
+ end = (header.offset.board * 8);
+
+ if (((header.offset.product * 8) > (header.offset.product * 8)) &&
+ ((header.offset.product * 8) < end))
+ end = (header.offset.product * 8);
+
+ *pSize = end - (header.offset.multi * 8);
+ *pOffset = (header.offset.multi * 8);
+
+ return 0;
+}
+
+int
+ipmi_fru_get_adjust_size_from_buffer(uint8_t * fru_data, uint32_t *pSize)
+{
+ struct fru_multirec_header * head;
+ int status = 0;
+ uint8_t checksum = 0;
+ uint8_t counter = 0;
+ uint16_t count = 0;
+ do {
+ checksum = 0;
+ head = (struct fru_multirec_header *) (fru_data + count);
+ if (verbose) {
+ printf("Adding (");
+ }
+ for (counter = 0; counter < sizeof(struct fru_multirec_header); counter++) {
+ if (verbose) {
+ printf(" %02X", *(fru_data + count + counter));
+ }
+ checksum += *(fru_data + count + counter);
+ }
+ if (verbose) {
+ printf(")");
+ }
+ if (checksum != 0) {
+ lprintf(LOG_ERR, "Bad checksum in Multi Records");
+ status = (-1);
+ if (verbose) {
+ printf("--> FAIL");
+ }
+ } else if (verbose) {
+ printf("--> OK");
+ }
+ if (verbose > 1 && checksum == 0) {
+ for (counter = 0; counter < head->len; counter++) {
+ printf(" %02X", *(fru_data + count + counter
+ + sizeof(struct fru_multirec_header)));
+ }
+ }
+ if (verbose) {
+ printf("\n");
+ }
+ count += head->len + sizeof (struct fru_multirec_header);
+ } while ((!(head->format & 0x80)) && (status == 0));
+
+ *pSize = count;
+ lprintf(LOG_DEBUG, "Size of multirec: %lu\n", *pSize);
+ return status;
+}
+
+static int
+ipmi_fru_get_multirec_from_file(char * pFileName, uint8_t * pBufArea,
+ uint32_t size, uint32_t offset)
+{
+ FILE * pFile;
+ uint32_t len = 0;
+ if (pFileName == NULL) {
+ lprintf(LOG_ERR, "Invalid file name given.");
+ return (-1);
+ }
+
+ errno = 0;
+ pFile = fopen(pFileName, "rb");
+ if (!pFile) {
+ lprintf(LOG_ERR, "Error opening file '%s': %i -> %s.", pFileName, errno,
+ strerror(errno));
+ return (-1);
+ }
+ errno = 0;
+ if (fseek(pFile, offset, SEEK_SET) != 0) {
+ lprintf(LOG_ERR, "Failed to seek in file '%s': %i -> %s.", pFileName, errno,
+ strerror(errno));
+ fclose(pFile);
+ return (-1);
+ }
+ len = fread(pBufArea, size, 1, pFile);
+ fclose(pFile);
+
+ if (len != 1) {
+ lprintf(LOG_ERR, "Error in file '%s'.", pFileName);
+ return (-1);
+ }
+ return 0;
+}
+
+static int
+ipmi_fru_get_multirec_location_from_fru(struct ipmi_intf * intf,
+ uint8_t fruId,
+ struct fru_info *pFruInfo,
+ uint32_t * pRetLocation,
+ uint32_t * pRetSize)
+{
+ struct ipmi_rs * rsp;
+ struct ipmi_rq req;
+ uint8_t msg_data[4];
+ uint32_t end;
+ struct fru_header header;
+
+ *pRetLocation = 0;
+
+ msg_data[0] = fruId;
+
+ memset(&req, 0, sizeof(req));
+ req.msg.netfn = IPMI_NETFN_STORAGE;
+ req.msg.cmd = GET_FRU_INFO;
+ req.msg.data = msg_data;
+ req.msg.data_len = 1;
+
+ rsp = intf->sendrecv(intf, &req);
+ if (!rsp) {
+ if (verbose > 1)
+ printf("no response\n");
+ return -1;
+ }
+
+ if (rsp->ccode > 0) {
+ if (rsp->ccode == 0xc3)
+ printf (" Timeout accessing FRU info. (Device not present?)\n");
+ else
+ printf (" CCODE = 0x%02x\n", rsp->ccode);
+ return -1;
+ }
+ pFruInfo->size = (rsp->data[1] << 8) | rsp->data[0];
+ pFruInfo->access = rsp->data[2] & 0x1;
+
+ if (verbose > 1)
+ printf("pFruInfo->size = %d bytes (accessed by %s)\n",
+ pFruInfo->size, pFruInfo->access ? "words" : "bytes");
+
+ if (!pFruInfo->size)
+ return -1;
+
+ msg_data[0] = fruId;
+ msg_data[1] = 0;
+ msg_data[2] = 0;
+ msg_data[3] = 8;
+
+ memset(&req, 0, sizeof(req));
+ req.msg.netfn = IPMI_NETFN_STORAGE;
+ req.msg.cmd = GET_FRU_DATA;
+ req.msg.data = msg_data;
+ req.msg.data_len = 4;
+
+ rsp = intf->sendrecv(intf, &req);
+
+ if (!rsp)
+ return -1;
+ if (rsp->ccode > 0) {
+ if (rsp->ccode == 0xc3)
+ printf (" Timeout while reading FRU data. (Device not present?)\n");
+ return -1;
+ }
+
+ if (verbose > 1)
+ printbuf(rsp->data, rsp->data_len, "FRU DATA");
+
+ memcpy(&header, rsp->data + 1, 8);
+
+ if (header.version != 0x01) {
+ printf (" Unknown FRU header version %02x.\n", header.version);
+ return -1;
+ }
+
+ end = pFruInfo->size;
+
+ /* Retreive length */
+ if (((header.offset.internal * 8) > (header.offset.internal * 8)) &&
+ ((header.offset.internal * 8) < end))
+ end = (header.offset.internal * 8);
+
+ if (((header.offset.chassis * 8) > (header.offset.chassis * 8)) &&
+ ((header.offset.chassis * 8) < end))
+ end = (header.offset.chassis * 8);
+
+ if (((header.offset.board * 8) > (header.offset.board * 8)) &&
+ ((header.offset.board * 8) < end))
+ end = (header.offset.board * 8);
+
+ if (((header.offset.product * 8) > (header.offset.product * 8)) &&
+ ((header.offset.product * 8) < end))
+ end = (header.offset.product * 8);
+
+ *pRetSize = end;
+ *pRetLocation = 8 * header.offset.multi;
+
+ return 0;
+}
+
+/* ipmi_fru_get_internal_use_offset - Retreive internal use offset
+*
+* @intf: ipmi interface
+* @id: fru id
+*
+* returns -1 on error
+* returns 0 if successful
+* returns 1 if device not present
+*/
+static int
+ipmi_fru_get_internal_use_info( struct ipmi_intf * intf,
+ uint8_t id,
+ struct fru_info * fru,
+ uint16_t * size,
+ uint16_t * offset)
+{
+ struct ipmi_rs * rsp;
+ struct ipmi_rq req;
+ struct fru_header header;
+ uint8_t msg_data[4];
+
+ // Init output value
+ * offset = 0;
+ * size = 0;
+
+ memset(fru, 0, sizeof(struct fru_info));
+ memset(&header, 0, sizeof(struct fru_header));
+
+ /*
+ * get info about this FRU
+ */
+ memset(msg_data, 0, 4);
+ msg_data[0] = id;
+
+ memset(&req, 0, sizeof(req));
+ req.msg.netfn = IPMI_NETFN_STORAGE;
+ req.msg.cmd = GET_FRU_INFO;
+ req.msg.data = msg_data;
+ req.msg.data_len = 1;
+
+ rsp = intf->sendrecv(intf, &req);
+ if (rsp == NULL) {
+ printf(" Device not present (No Response)\n");
+ return -1;
+ }
+ if (rsp->ccode > 0) {
+ printf(" Device not present (%s)\n",
+ val2str(rsp->ccode, completion_code_vals));
+ return -1;
+ }
+
+ memset(&fru, 0, sizeof(fru));
+ fru->size = (rsp->data[1] << 8) | rsp->data[0];
+ fru->access = rsp->data[2] & 0x1;
+
+ lprintf(LOG_DEBUG, "fru.size = %d bytes (accessed by %s)",
+ fru->size, fru->access ? "words" : "bytes");
+
+ if (fru->size < 1) {
+ lprintf(LOG_ERR, " Invalid FRU size %d", fru->size);
+ return -1;
+ }
+
+ /*
+ * retrieve the FRU header
+ */
+ msg_data[0] = id;
+ msg_data[1] = 0;
+ msg_data[2] = 0;
+ msg_data[3] = 8;
+
+ memset(&req, 0, sizeof(req));
+ req.msg.netfn = IPMI_NETFN_STORAGE;
+ req.msg.cmd = GET_FRU_DATA;
+ req.msg.data = msg_data;
+ req.msg.data_len = 4;
+
+ rsp = intf->sendrecv(intf, &req);
+ if (rsp == NULL) {
+ printf(" Device not present (No Response)\n");
+ return 1;
+ }
+ if (rsp->ccode > 0) {
+ printf(" Device not present (%s)\n",
+ val2str(rsp->ccode, completion_code_vals));
+ return 1;
+ }
+
+ if (verbose > 1)
+ printbuf(rsp->data, rsp->data_len, "FRU DATA");
+
+ memcpy(&header, rsp->data + 1, 8);
+
+ if (header.version != 1) {
+ lprintf(LOG_ERR, " Unknown FRU header version 0x%02x",
+ header.version);
+ return -1;
+ }
+
+ lprintf(LOG_DEBUG, "fru.header.version: 0x%x",
+ header.version);
+ lprintf(LOG_DEBUG, "fru.header.offset.internal: 0x%x",
+ header.offset.internal * 8);
+ lprintf(LOG_DEBUG, "fru.header.offset.chassis: 0x%x",
+ header.offset.chassis * 8);
+ lprintf(LOG_DEBUG, "fru.header.offset.board: 0x%x",
+ header.offset.board * 8);
+ lprintf(LOG_DEBUG, "fru.header.offset.product: 0x%x",
+ header.offset.product * 8);
+ lprintf(LOG_DEBUG, "fru.header.offset.multi: 0x%x",
+ header.offset.multi * 8);
+
+ if((header.offset.internal*8) == 0)
+ {
+ * size = 0;
+ * offset = 0;
+ }
+ else
+ {
+ (* offset) = (header.offset.internal*8);
+
+ if(header.offset.chassis != 0)
+ {
+ (* size) = ((header.offset.chassis*8)-(* offset));
+ }
+ else if(header.offset.board != 0)
+ {
+ (* size) = ((header.offset.board*8)-(* offset));
+ }
+ else if(header.offset.product != 0)
+ {
+ (* size) = ((header.offset.product*8)-(* offset));
+ }
+ else if(header.offset.multi != 0)
+ {
+ (* size) = ((header.offset.multi*8)-(* offset));
+ }
+ else
+ {
+ (* size) = (fru->size - (* offset));
+ }
+ }
+ return 0;
+}
+
+/* ipmi_fru_info_internal_use - print internal use info
+*
+* @intf: ipmi interface
+* @id: fru id
+*
+* returns -1 on error
+* returns 0 if successful
+* returns 1 if device not present
+*/
+static int
+ipmi_fru_info_internal_use(struct ipmi_intf * intf, uint8_t id)
+{
+ struct fru_info fru;
+ uint16_t size;
+ uint16_t offset;
+ int rc = 0;
+
+ rc = ipmi_fru_get_internal_use_info(intf, id, &fru, &size, &offset);
+
+ if(rc == 0)
+ {
+ lprintf(LOG_DEBUG, "Internal Use Area Offset: %i", offset);
+ printf( "Internal Use Area Size : %i\n", size);
+ }
+ else
+ {
+ lprintf(LOG_ERR, "Cannot access internal use area");
+ return -1;
+ }
+ return 0;
+}
+
+/* ipmi_fru_help - print help text for FRU subcommand
+ *
+ * returns void
+ */
+void
+ipmi_fru_help()
+{
+ lprintf(LOG_NOTICE,
+ "FRU Commands: print read write upgEkey edit internaluse get");
+} /* ipmi_fru_help() */
+
+/* ipmi_fru_read_internal_use - print internal use are in hex or file
+*
+* @intf: ipmi interface
+* @id: fru id
+*
+* returns -1 on error
+* returns 0 if successful
+* returns 1 if device not present
+*/
+static int
+ipmi_fru_read_internal_use(struct ipmi_intf * intf, uint8_t id, char * pFileName)
+{
+ struct fru_info fru;
+ uint16_t size;
+ uint16_t offset;
+ int rc = 0;
+
+ rc = ipmi_fru_get_internal_use_info(intf, id, &fru, &size, &offset);
+
+ if(rc == 0)
+ {
+ uint8_t * frubuf;
+
+ lprintf(LOG_DEBUG, "Internal Use Area Offset: %i", offset);
+ printf( "Internal Use Area Size : %i\n", size);
+
+ frubuf = malloc( size );
+ if(frubuf)
+ {
+ rc = read_fru_area_section(intf, &fru, id, offset, size, frubuf);
+
+ if(rc == 0)
+ {
+ if(pFileName == NULL)
+ {
+ uint16_t counter;
+ for(counter = 0; counter < size; counter ++)
+ {
+ if((counter % 16) == 0)
+ printf("\n%02i- ", (counter / 16));
+ printf("%02X ", frubuf[counter]);
+ }
+ }
+ else
+ {
+ FILE * pFile;
+ pFile = fopen(pFileName,"wb");
+ if (pFile)
+ {
+ fwrite(frubuf, size, 1, pFile);
+ printf("Done\n");
+ }
+ else
+ {
+ lprintf(LOG_ERR, "Error opening file %s\n", pFileName);
+ free(frubuf);
+ frubuf = NULL;
+ return -1;
+ }
+ fclose(pFile);
+ }
+ }
+ printf("\n");
+
+ free(frubuf);
+ frubuf = NULL;
+ }
+
+ }
+ else
+ {
+ lprintf(LOG_ERR, "Cannot access internal use area");
+ }
+ return 0;
+}
+
+/* ipmi_fru_write_internal_use - print internal use are in hex or file
+*
+* @intf: ipmi interface
+* @id: fru id
+*
+* returns -1 on error
+* returns 0 if successful
+* returns 1 if device not present
+*/
+static int
+ipmi_fru_write_internal_use(struct ipmi_intf * intf, uint8_t id, char * pFileName)
+{
+ struct fru_info fru;
+ uint16_t size;
+ uint16_t offset;
+ int rc = 0;
+
+ rc = ipmi_fru_get_internal_use_info(intf, id, &fru, &size, &offset);
+
+ if(rc == 0)
+ {
+ uint8_t * frubuf;
+ FILE * fp;
+ uint32_t fileLength = 0;
+
+ lprintf(LOG_DEBUG, "Internal Use Area Offset: %i", offset);
+ printf( "Internal Use Area Size : %i\n", size);
+
+ fp = fopen(pFileName, "r");
+
+ if(fp)
+ {
+ /* Retreive file length, check if it's fits the Eeprom Size */
+ fseek(fp, 0 ,SEEK_END);
+ fileLength = ftell(fp);
+
+ lprintf(LOG_ERR, "File Size: %i", fileLength);
+ lprintf(LOG_ERR, "Area Size: %i", size);
+ if(fileLength != size)
+ {
+ lprintf(LOG_ERR, "File size does not fit Eeprom Size");
+ fclose(fp);
+ fp = NULL;
+ }
+ else
+ {
+ fseek(fp, 0 ,SEEK_SET);
+ }
+ }
+
+ if(fp)
+ {
+ frubuf = malloc( size );
+ if(frubuf)
+ {
+ uint16_t fru_read_size;
+ fru_read_size = fread(frubuf, 1, size, fp);
+
+ if(fru_read_size == size)
+ {
+ rc = write_fru_area(intf, &fru, id, 0, offset, size, frubuf);
+
+ if(rc == 0)
+ {
+ lprintf(LOG_INFO, "Done\n");
+ }
+ }
+ else
+ {
+ lprintf(LOG_ERR, "Unable to read file: %i\n", fru_read_size);
+ }
+
+ free(frubuf);
+ frubuf = NULL;
+ }
+ fclose(fp);
+ fp = NULL;
+ }
+ }
+ else
+ {
+ lprintf(LOG_ERR, "Cannot access internal use area");
+ }
+ return 0;
+}
+
+int
+ipmi_fru_main(struct ipmi_intf * intf, int argc, char ** argv)
+{
+ int rc = 0;
+ uint8_t fru_id = 0;
+
+ if (argc < 1) {
+ rc = ipmi_fru_print_all(intf);
+ }
+ else if (strncmp(argv[0], "help", 4) == 0) {
+ ipmi_fru_help();
+ return 0;
+ }
+ else if (strncmp(argv[0], "print", 5) == 0 ||
+ strncmp(argv[0], "list", 4) == 0) {
+ if (argc > 1) {
+ if (strcmp(argv[1], "help") == 0) {
+ lprintf(LOG_NOTICE, "fru print [fru id] - print information about FRU(s)");
+ return 0;
+ }
+
+ if (is_fru_id(argv[1], &fru_id) != 0)
+ return (-1);
+
+ rc = __ipmi_fru_print(intf, fru_id);
+ } else {
+ rc = ipmi_fru_print_all(intf);
+ }
+ }
+ else if (!strncmp(argv[0], "read", 5)) {
+ if (argc > 1 && strcmp(argv[1], "help") == 0) {
+ ipmi_fru_read_help();
+ return 0;
+ } else if (argc < 3) {
+ lprintf(LOG_ERR, "Not enough parameters given.");
+ ipmi_fru_read_help();
+ return (-1);
+ }
+
+ if (is_fru_id(argv[1], &fru_id) != 0)
+ return (-1);
+
+ /* There is a file name in the parameters */
+ if (is_valid_filename(argv[2]) != 0)
+ return (-1);
+
+ if (verbose) {
+ printf("FRU ID : %d\n", fru_id);
+ printf("FRU File : %s\n", argv[2]);
+ }
+ /* TODO - rc is missing */
+ ipmi_fru_read_to_bin(intf, argv[2], fru_id);
+ }
+ else if (!strncmp(argv[0], "write", 5)) {
+ if (argc > 1 && strcmp(argv[1], "help") == 0) {
+ ipmi_fru_write_help();
+ return 0;
+ } else if (argc < 3) {
+ lprintf(LOG_ERR, "Not enough parameters given.");
+ ipmi_fru_write_help();
+ return (-1);
+ }
+
+ if (is_fru_id(argv[1], &fru_id) != 0)
+ return (-1);
+
+ /* There is a file name in the parameters */
+ if (is_valid_filename(argv[2]) != 0)
+ return (-1);
+
+ if (verbose) {
+ printf("FRU ID : %d\n", fru_id);
+ printf("FRU File : %s\n", argv[2]);
+ }
+ /* TODO - rc is missing */
+ ipmi_fru_write_from_bin(intf, argv[2], fru_id);
+ }
+ else if (!strncmp(argv[0], "upgEkey", 7)) {
+ if (argc > 1 && strcmp(argv[1], "help") == 0) {
+ ipmi_fru_upgekey_help();
+ return 0;
+ } else if (argc < 3) {
+ lprintf(LOG_ERR, "Not enough parameters given.");
+ ipmi_fru_upgekey_help();
+ return (-1);
+ }
+
+ if (is_fru_id(argv[1], &fru_id) != 0)
+ return (-1);
+
+ /* There is a file name in the parameters */
+ if (is_valid_filename(argv[2]) != 0)
+ return (-1);
+
+ rc = ipmi_fru_upg_ekeying(intf, argv[2], fru_id);
+ }
+ else if (!strncmp(argv[0], "internaluse", 11)) {
+ if (argc > 1 && strcmp(argv[1], "help") == 0) {
+ ipmi_fru_internaluse_help();
+ return 0;
+ }
+
+ if ( (argc >= 3) && (!strncmp(argv[2], "info", 4)) ) {
+
+ if (is_fru_id(argv[1], &fru_id) != 0)
+ return (-1);
+
+ rc = ipmi_fru_info_internal_use(intf, fru_id);
+ }
+ else if ( (argc >= 3) && (!strncmp(argv[2], "print", 5)) ) {
+
+ if (is_fru_id(argv[1], &fru_id) != 0)
+ return (-1);
+
+ rc = ipmi_fru_read_internal_use(intf, fru_id, NULL);
+ }
+ else if ( (argc >= 4) && (!strncmp(argv[2], "read", 4)) ) {
+
+ if (is_fru_id(argv[1], &fru_id) != 0)
+ return (-1);
+
+ /* There is a file name in the parameters */
+ if (is_valid_filename(argv[3]) != 0)
+ return (-1);
+
+ lprintf(LOG_DEBUG, "FRU ID : %d", fru_id);
+ lprintf(LOG_DEBUG, "FRU File : %s", argv[3]);
+
+ rc = ipmi_fru_read_internal_use(intf, fru_id, argv[3]);
+ }
+ else if ( (argc >= 4) && (!strncmp(argv[2], "write", 5)) ) {
+
+ if (is_fru_id(argv[1], &fru_id) != 0)
+ return (-1);
+
+ /* There is a file name in the parameters */
+ if (is_valid_filename(argv[3]) != 0)
+ return (-1);
+
+ lprintf(LOG_DEBUG, "FRU ID : %d", fru_id);
+ lprintf(LOG_DEBUG, "FRU File : %s", argv[3]);
+
+ rc = ipmi_fru_write_internal_use(intf, fru_id, argv[3]);
+ } else {
+ lprintf(LOG_ERR,
+ "Either unknown command or not enough parameters given.");
+ ipmi_fru_internaluse_help();
+ return (-1);
+ }
+ }
+ else if (!strncmp(argv[0], "edit", 4)) {
+ if (argc > 1 && strcmp(argv[1], "help") == 0) {
+ ipmi_fru_edit_help();
+ return 0;
+ } else if (argc < 2) {
+ lprintf(LOG_ERR, "Not enough parameters given.");
+ ipmi_fru_edit_help();
+ return (-1);
+ }
+
+ if (argc >= 2) {
+ if (is_fru_id(argv[1], &fru_id) != 0)
+ return (-1);
+
+ if (verbose) {
+ printf("FRU ID : %d\n", fru_id);
+ }
+ } else {
+ printf("Using default FRU ID: %d\n", fru_id);
+ }
+
+ if (argc >= 3) {
+ if (!strncmp(argv[2], "field", 5)) {
+ if (argc != 6) {
+ lprintf(LOG_ERR, "Not enough parameters given.");
+ ipmi_fru_edit_help();
+ return (-1);
+ }
+ rc = ipmi_fru_set_field_string(intf, fru_id, *argv[3], *argv[4],
+ (char *) argv[5]);
+ } else if (!strncmp(argv[2], "oem", 3)) {
+ rc = ipmi_fru_edit_multirec(intf, fru_id, argc, argv);
+ } else {
+ lprintf(LOG_ERR, "Invalid command: %s", argv[2]);
+ ipmi_fru_edit_help();
+ return (-1);
+ }
+ } else {
+ rc = ipmi_fru_edit_multirec(intf, fru_id, argc, argv);
+ }
+ }
+ else if (!strncmp(argv[0], "get", 4)) {
+ if (argc > 1 && (strncmp(argv[1], "help", 4) == 0)) {
+ ipmi_fru_get_help();
+ return 0;
+ } else if (argc < 2) {
+ lprintf(LOG_ERR, "Not enough parameters given.");
+ ipmi_fru_get_help();
+ return (-1);
+ }
+
+ if (argc >= 2) {
+ if (is_fru_id(argv[1], &fru_id) != 0)
+ return (-1);
+
+ if (verbose) {
+ printf("FRU ID : %d\n", fru_id);
+ }
+ } else {
+ printf("Using default FRU ID: %d\n", fru_id);
+ }
+
+ if (argc >= 3) {
+ if (!strncmp(argv[2], "oem", 3)) {
+ rc = ipmi_fru_get_multirec(intf, fru_id, argc, argv);
+ } else {
+ lprintf(LOG_ERR, "Invalid command: %s", argv[2]);
+ ipmi_fru_get_help();
+ return (-1);
+ }
+ } else {
+ rc = ipmi_fru_get_multirec(intf, fru_id, argc, argv);
+ }
+ }
+ else {
+ lprintf(LOG_ERR, "Invalid FRU command: %s", argv[0]);
+ ipmi_fru_help();
+ return (-1);
+ }
+
+ return rc;
+}
+
+/* ipmi_fru_set_field_string - Set a field string to a new value, Need to be the same size. If
+* size if not equal, the function ipmi_fru_set_field_string_rebuild
+* will be called.
+*
+* @intf: ipmi interface
+* @id: fru id
+* @f_type: Type of the Field : c=Chassis b=Board p=Product
+* @f_index: findex of the field, zero indexed.
+* @f_string: NULL terminated string
+*
+* returns -1 on error
+* returns 1 if successful
+*/
+static int
+ipmi_fru_set_field_string(struct ipmi_intf * intf, uint8_t fruId, uint8_t
+f_type, uint8_t f_index, char *f_string)
+{
+ struct ipmi_rs *rsp;
+ struct ipmi_rq req;
+
+ struct fru_info fru;
+ struct fru_header header;
+ uint8_t msg_data[4];
+ uint8_t checksum;
+ int i = 0;
+ int rc = 1;
+ uint8_t *fru_data = NULL;
+ uint8_t *fru_area = NULL;
+ uint32_t fru_field_offset, fru_field_offset_tmp;
+ uint32_t fru_section_len, header_offset;
+
+ memset(msg_data, 0, 4);
+ msg_data[0] = fruId;
+
+ memset(&req, 0, sizeof(req));
+ req.msg.netfn = IPMI_NETFN_STORAGE;
+ req.msg.cmd = GET_FRU_INFO;
+ req.msg.data = msg_data;
+ req.msg.data_len = 1;
+
+ rsp = intf->sendrecv(intf, &req);
+ if (rsp == NULL) {
+ printf(" Device not present (No Response)\n");
+ rc = (-1);
+ goto ipmi_fru_set_field_string_out;
+ }
+ if (rsp->ccode > 0) {
+ printf(" Device not present (%s)\n",
+ val2str(rsp->ccode, completion_code_vals));
+ rc = (-1);
+ goto ipmi_fru_set_field_string_out;
+ }
+
+ memset(&fru, 0, sizeof(fru));
+ fru.size = (rsp->data[1] << 8) | rsp->data[0];
+ fru.access = rsp->data[2] & 0x1;
+
+ if (fru.size < 1) {
+ printf(" Invalid FRU size %d", fru.size);
+ rc = (-1);
+ goto ipmi_fru_set_field_string_out;
+ }
+ /*
+ * retrieve the FRU header
+ */
+ msg_data[0] = fruId;
+ msg_data[1] = 0;
+ msg_data[2] = 0;
+ msg_data[3] = 8;
+
+ memset(&req, 0, sizeof(req));
+ req.msg.netfn = IPMI_NETFN_STORAGE;
+ req.msg.cmd = GET_FRU_DATA;
+ req.msg.data = msg_data;
+ req.msg.data_len = 4;
+
+ rsp = intf->sendrecv(intf, &req);
+ if (rsp == NULL)
+ {
+ printf(" Device not present (No Response)\n");
+ rc = (-1);
+ goto ipmi_fru_set_field_string_out;
+ }
+ if (rsp->ccode > 0)
+ {
+ printf(" Device not present (%s)\n",
+ val2str(rsp->ccode, completion_code_vals));
+ rc = (-1);
+ goto ipmi_fru_set_field_string_out;
+ }
+
+ if (verbose > 1)
+ printbuf(rsp->data, rsp->data_len, "FRU DATA");
+
+ memcpy(&header, rsp->data + 1, 8);
+
+ if (header.version != 1)
+ {
+ printf(" Unknown FRU header version 0x%02x",
+ header.version);
+ rc = (-1);
+ goto ipmi_fru_set_field_string_out;
+ }
+
+ fru_data = malloc( fru.size );
+
+ if( fru_data == NULL )
+ {
+ printf("Out of memory!\n");
+ rc = (-1);
+ goto ipmi_fru_set_field_string_out;
+ }
+
+ /* Setup offset from the field type */
+
+ /* Chassis type field */
+ if (f_type == 'c' ) {
+ header_offset = (header.offset.chassis * 8);
+ read_fru_area(intf ,&fru, fruId, header_offset , 3 , fru_data);
+ fru_field_offset = 3;
+ fru_section_len = *(fru_data + 1) * 8;
+ }
+ /* Board type field */
+ else if (f_type == 'b' ) {
+ header_offset = (header.offset.board * 8);
+ read_fru_area(intf ,&fru, fruId, header_offset , 3 , fru_data);
+ fru_field_offset = 6;
+ fru_section_len = *(fru_data + 1) * 8;
+ }
+ /* Product type field */
+ else if (f_type == 'p' ) {
+ header_offset = (header.offset.product * 8);
+ read_fru_area(intf ,&fru, fruId, header_offset , 3 , fru_data);
+ fru_field_offset = 3;
+ fru_section_len = *(fru_data + 1) * 8;
+ }
+ else
+ {
+ printf("Wrong field type.");
+ rc = (-1);
+ goto ipmi_fru_set_field_string_out;
+ }
+ memset(fru_data, 0, fru.size);
+ if( read_fru_area(intf ,&fru, fruId, header_offset ,
+ fru_section_len , fru_data) < 0 )
+ {
+ rc = (-1);
+ goto ipmi_fru_set_field_string_out;
+ }
+ /* Convert index from character to decimal */
+ f_index= f_index - 0x30;
+
+ /*Seek to field index */
+ for (i=0; i <= f_index; i++) {
+ fru_field_offset_tmp = fru_field_offset;
+ if (fru_area != NULL) {
+ free(fru_area);
+ fru_area = NULL;
+ }
+ fru_area = (uint8_t *) get_fru_area_str(fru_data, &fru_field_offset);
+ }
+
+ if( (fru_area == NULL ) || strlen((const char *)fru_area) == 0 ) {
+ printf("Field not found !\n");
+ rc = (-1);
+ goto ipmi_fru_set_field_string_out;
+ }
+
+ if ( strlen((const char *)fru_area) == strlen((const char *)f_string) )
+ {
+ printf("Updating Field '%s' with '%s' ...\n", fru_area, f_string );
+ memcpy(fru_data + fru_field_offset_tmp + 1,
+ f_string, strlen(f_string));
+
+ checksum = 0;
+ /* Calculate Header Checksum */
+ for( i = header_offset; i < header_offset
+ + fru_section_len - 1; i ++ )
+ {
+ checksum += fru_data[i];
+ }
+ checksum = (~checksum) + 1;
+ fru_data[header_offset + fru_section_len - 1] = checksum;
+
+ /* Write the updated section to the FRU data; source offset => 0 */
+ if( write_fru_area(intf, &fru, fruId, 0,
+ header_offset, fru_section_len, fru_data) < 0 )
+ {
+ printf("Write to FRU data failed.\n");
+ rc = (-1);
+ goto ipmi_fru_set_field_string_out;
+ }
+ }
+ else {
+ printf("String size are not equal, resizing fru to fit new string\n");
+ if(
+ ipmi_fru_set_field_string_rebuild(intf,fruId,fru,header,f_type,f_index,f_string)
+ )
+ {
+ rc = (-1);
+ goto ipmi_fru_set_field_string_out;
+ }
+ }
+
+ ipmi_fru_set_field_string_out:
+ if (fru_data != NULL) {
+ free(fru_data);
+ fru_data = NULL;
+ }
+ if (fru_area != NULL) {
+ free(fru_area);
+ fru_area = NULL;
+ }
+
+ return rc;
+}
+
+/*
+ This function can update a string within of the following section when the size is not equal:
+
+ Chassis
+ Product
+ Board
+*/
+/* ipmi_fru_set_field_string_rebuild - Set a field string to a new value, When size are not
+* the same size.
+*
+* This function can update a string within of the following section when the size is not equal:
+*
+* - Chassis
+* - Product
+* - Board
+*
+* @intf: ipmi interface
+* @fruId: fru id
+* @fru: info about fru
+* @header: contain the header of the FRU
+* @f_type: Type of the Field : c=Chassis b=Board p=Product
+* @f_index: findex of the field, zero indexed.
+* @f_string: NULL terminated string
+*
+* returns -1 on error
+* returns 1 if successful
+*/
+
+#define DBG_RESIZE_FRU
+static int
+ipmi_fru_set_field_string_rebuild(struct ipmi_intf * intf, uint8_t fruId,
+ struct fru_info fru, struct fru_header header,
+ uint8_t f_type, uint8_t f_index, char *f_string)
+{
+ uint8_t msg_data[4];
+ uint8_t checksum;
+ int i = 0;
+ uint8_t *fru_data_old = NULL;
+ uint8_t *fru_data_new = NULL;
+ uint8_t *fru_area = NULL;
+ uint32_t fru_field_offset, fru_field_offset_tmp;
+ uint32_t fru_section_len, old_section_len, header_offset;
+ uint32_t chassis_offset, board_offset, product_offset;
+ uint32_t chassis_len, board_len, product_len, product_len_new;
+ int num_byte_change = 0, padding_len = 0;
+ uint32_t counter;
+ unsigned char cksum;
+ int rc = 1;
+
+ fru_data_old = calloc( fru.size, sizeof(uint8_t) );
+
+ fru_data_new = malloc( fru.size );
+
+ if( fru_data_old == NULL || fru_data_new == NULL )
+ {
+ printf("Out of memory!\n");
+ rc = (-1);
+ goto ipmi_fru_set_field_string_rebuild_out;
+ }
+
+ /*************************
+ 1) Read ALL FRU */
+ printf("Read All FRU area\n");
+ printf("Fru Size : %u bytes\n", fru.size);
+
+ /* Read current fru data */
+ read_fru_area(intf ,&fru, fruId, 0, fru.size , fru_data_old);
+
+ #ifdef DBG_RESIZE_FRU
+ printf("Copy to new FRU\n");
+ #endif
+
+ /*************************
+ 2) Copy all FRU to new FRU */
+ memcpy(fru_data_new, fru_data_old, fru.size);
+
+ /* Build location of all modifiable components */
+ chassis_offset = (header.offset.chassis * 8);
+ board_offset = (header.offset.board * 8);
+ product_offset = (header.offset.product * 8);
+
+ /* Retrieve length of all modifiable components */
+ chassis_len = *(fru_data_old + chassis_offset + 1) * 8;
+ board_len = *(fru_data_old + board_offset + 1) * 8;
+ product_len = *(fru_data_old + product_offset + 1) * 8;
+ product_len_new = product_len;
+
+ /* Chassis type field */
+ if (f_type == 'c' )
+ {
+ header_offset = chassis_offset;
+ fru_field_offset = chassis_offset + 3;
+ fru_section_len = chassis_len;
+ }
+ /* Board type field */
+ else if (f_type == 'b' )
+ {
+ header_offset = board_offset;
+ fru_field_offset = board_offset + 6;
+ fru_section_len = board_len;
+ }
+ /* Product type field */
+ else if (f_type == 'p' )
+ {
+ header_offset = product_offset;
+ fru_field_offset = product_offset + 3;
+ fru_section_len = product_len;
+ }
+ else
+ {
+ printf("Wrong field type.");
+ rc = (-1);
+ goto ipmi_fru_set_field_string_rebuild_out;
+ }
+
+ /* Keep length for future old section display */
+ old_section_len = fru_section_len;
+
+ /*************************
+ 3) Seek to field index */
+ for (i = 0;i <= f_index; i++) {
+ fru_field_offset_tmp = fru_field_offset;
+ if (fru_area != NULL) {
+ free(fru_area);
+ fru_area = NULL;
+ }
+ fru_area = (uint8_t *) get_fru_area_str(fru_data_old, &fru_field_offset);
+ }
+
+ if( (fru_area == NULL ) || strlen((const char *)fru_area) == 0 ) {
+ printf("Field not found (1)!\n");
+ rc = (-1);
+ goto ipmi_fru_set_field_string_rebuild_out;
+ }
+
+ #ifdef DBG_RESIZE_FRU
+ printf("Section Length: %u\n", fru_section_len);
+ #endif
+
+ /*************************
+ 4) Check number of padding bytes and bytes changed */
+ for(counter = 2; counter < fru_section_len; counter ++)
+ {
+ if(*(fru_data_old + (header_offset + fru_section_len - counter)) == 0)
+ padding_len ++;
+ else
+ break;
+ }
+ num_byte_change = strlen(f_string) - strlen(fru_area);
+
+ #ifdef DBG_RESIZE_FRU
+ printf("Padding Length: %u\n", padding_len);
+ printf("NumByte Change: %i\n", num_byte_change);
+ printf("Start SecChnge: %x\n", *(fru_data_old + fru_field_offset_tmp));
+ printf("End SecChnge : %x\n", *(fru_data_old + fru_field_offset_tmp + strlen(f_string) + 1));
+
+ printf("Start Section : %x\n", *(fru_data_old + header_offset));
+ printf("End Sec wo Pad: %x\n", *(fru_data_old + header_offset + fru_section_len - 2 - padding_len));
+ printf("End Section : %x\n", *(fru_data_old + header_offset + fru_section_len - 1));
+ #endif
+
+ /* Calculate New Padding Length */
+ padding_len -= num_byte_change;
+
+ #ifdef DBG_RESIZE_FRU
+ printf("New Padding Length: %i\n", padding_len);
+ #endif
+
+ /*************************
+ 5) Check if section must be resize. This occur when padding length is not between 0 and 7 */
+ if( (padding_len < 0) || (padding_len >= 8))
+ {
+ uint32_t remaining_offset = ((header.offset.product * 8) + product_len);
+ int change_size_by_8;
+
+ if(padding_len >= 8)
+ {
+ /* Section must be set smaller */
+ change_size_by_8 = ((padding_len) / 8) * (-1);
+ }
+ else
+ {
+ /* Section must be set bigger */
+ change_size_by_8 = 1 + (((padding_len+1) / 8) * (-1));
+ }
+
+ /* Recalculate padding and section length base on the section changes */
+ fru_section_len += (change_size_by_8 * 8);
+ padding_len += (change_size_by_8 * 8);
+
+ #ifdef DBG_RESIZE_FRU
+ printf("change_size_by_8: %i\n", change_size_by_8);
+ printf("New Padding Length: %i\n", padding_len);
+ printf("change_size_by_8: %i\n", change_size_by_8);
+ printf("header.offset.board: %i\n", header.offset.board);
+ #endif
+
+ /* Must move sections */
+ /* Section that can be modified are as follow
+ Chassis
+ Board
+ product */
+
+ /* Chassis type field */
+ if (f_type == 'c' )
+ {
+ printf("Moving Section Chassis, from %i to %i\n",
+ ((header.offset.board) * 8),
+ ((header.offset.board + change_size_by_8) * 8)
+ );
+ memcpy(
+ (fru_data_new + ((header.offset.board + change_size_by_8) * 8)),
+ (fru_data_old + (header.offset.board) * 8),
+ board_len
+ );
+ header.offset.board += change_size_by_8;
+ }
+ /* Board type field */
+ if ((f_type == 'c' ) || (f_type == 'b' ))
+ {
+ printf("Moving Section Product, from %i to %i\n",
+ ((header.offset.product) * 8),
+ ((header.offset.product + change_size_by_8) * 8)
+ );
+ memcpy(
+ (fru_data_new + ((header.offset.product + change_size_by_8) * 8)),
+ (fru_data_old + (header.offset.product) * 8),
+ product_len
+ );
+ header.offset.product += change_size_by_8;
+ }
+
+ /* Adjust length of the section */
+ if (f_type == 'c')
+ {
+ *(fru_data_new + chassis_offset + 1) += change_size_by_8;
+ }
+ else if( f_type == 'b')
+ {
+ *(fru_data_new + board_offset + 1) += change_size_by_8;
+ }
+ else if( f_type == 'p')
+ {
+ *(fru_data_new + product_offset + 1) += change_size_by_8;
+ product_len_new = *(fru_data_new + product_offset + 1) * 8;
+ }
+
+ /* Rebuild Header checksum */
+ {
+ unsigned char * pfru_header = (unsigned char *) &header;
+ header.checksum = 0;
+ for(counter = 0; counter < (sizeof(struct fru_header) -1); counter ++)
+ {
+ header.checksum += pfru_header[counter];
+ }
+ header.checksum = (0 - header.checksum);
+ memcpy(fru_data_new, pfru_header, sizeof(struct fru_header));
+ }
+
+ /* Move remaining sections in 1 copy */
+ printf("Moving Remaining Bytes (Multi-Rec , etc..), from %i to %i\n",
+ remaining_offset,
+ ((header.offset.product) * 8) + product_len_new
+ );
+ if(((header.offset.product * 8) + product_len_new - remaining_offset) < 0)
+ {
+ memcpy(
+ fru_data_new + (header.offset.product * 8) + product_len_new,
+ fru_data_old + remaining_offset,
+ fru.size - remaining_offset
+ );
+ }
+ else
+ {
+ memcpy(
+ fru_data_new + (header.offset.product * 8) + product_len_new,
+ fru_data_old + remaining_offset,
+ fru.size - ((header.offset.product * 8) + product_len_new)
+ );
+ }
+ }
+
+ /* Update only if it's fits padding length as defined in the spec, otherwise, it's an internal
+ error */
+ /*************************
+ 6) Update Field and sections */
+ if( (padding_len >=0) && (padding_len < 8))
+ {
+ /* Do not requires any change in other section */
+
+ /* Change field length */
+ printf(
+ "Updating Field : '%s' with '%s' ... (Length from '%d' to '%d')\n",
+ fru_area, f_string,
+ (int)*(fru_data_old + fru_field_offset_tmp),
+ (int)(0xc0 + strlen(f_string)));
+ *(fru_data_new + fru_field_offset_tmp) = (0xc0 + strlen(f_string));
+ memcpy(fru_data_new + fru_field_offset_tmp + 1, f_string, strlen(f_string));
+
+ /* Copy remaing bytes in section */
+#ifdef DBG_RESIZE_FRU
+ printf("Copying remaining of sections: %d \n",
+ (int)((fru_data_old + header_offset + fru_section_len - 1) -
+ (fru_data_old + fru_field_offset_tmp + strlen(f_string) + 1)));
+#endif
+
+ memcpy((fru_data_new + fru_field_offset_tmp + 1 +
+ strlen(f_string)),
+ (fru_data_old + fru_field_offset_tmp + 1 +
+ strlen(fru_area)),
+ ((fru_data_old + header_offset + fru_section_len - 1) -
+ (fru_data_old + fru_field_offset_tmp + strlen(f_string) + 1)));
+
+ /* Add Padding if required */
+ for(counter = 0; counter < padding_len; counter ++)
+ {
+ *(fru_data_new + header_offset + fru_section_len - 1 -
+ padding_len + counter) = 0;
+ }
+
+ /* Calculate New Checksum */
+ cksum = 0;
+ for( counter = 0; counter <fru_section_len-1; counter ++ )
+ {
+ cksum += *(fru_data_new + header_offset + counter);
+ }
+ *(fru_data_new + header_offset + fru_section_len - 1) = (0 - cksum);
+
+ #ifdef DBG_RESIZE_FRU
+ printf("Calculate New Checksum: %x\n", (0 - cksum));
+ #endif
+
+ /****** ENABLE to show section modified before and after ********/
+ #if 0
+ printf("Section: ");
+ for( counter = 0; counter <old_section_len; counter ++ )
+ {
+ if((counter %16) == 0)
+ {
+ printf("\n");
+ }
+ printf( "%02X ", *(fru_data_old + header_offset + counter) );
+ }
+ printf("\n");
+
+ printf("Section: ");
+ for( counter = 0; counter <fru_section_len; counter ++ )
+ {
+ if((counter %16) == 0)
+ {
+ printf("\n");
+ }
+ printf( "%02X ", *(fru_data_new + header_offset + counter) );
+ }
+ printf("\n");
+ #endif
+ }
+ else
+ {
+ printf( "Internal error, padding length %i (must be from 0 to 7) ", padding_len );
+ rc = (-1);
+ goto ipmi_fru_set_field_string_rebuild_out;
+ }
+
+ /*************************
+ 7) Finally, write new FRU */
+ printf("Writing new FRU.\n");
+ if( write_fru_area( intf, &fru, fruId, 0, 0, fru.size, fru_data_new ) < 0 )
+ {
+ printf("Write to FRU data failed.\n");
+ rc = (-1);
+ goto ipmi_fru_set_field_string_rebuild_out;
+ }
+
+ printf("Done.\n");
+
+ ipmi_fru_set_field_string_rebuild_out:
+ if (fru_area != NULL) {
+ free(fru_area);
+ fru_area = NULL;
+ }
+ if (fru_data_new != NULL) {
+ free(fru_data_new);
+ fru_data_new = NULL;
+ }
+ if (fru_data_old != NULL) {
+ free(fru_data_old);
+ fru_data_old = NULL;
+ }
+
+ return rc;
+}