/* * 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 #include #include #include #include #include #include #include /* IANA id strings */ #include #include #include #include #if HAVE_CONFIG_H # include #endif #define FRU_MULTIREC_CHUNK_SIZE (255 + sizeof(struct fru_multirec_header)) /* From lib/dimm_spd.c: */ int ipmi_spd_print_fru(struct ipmi_intf * intf, uint8_t id); /* From src/plugins/ipmi_intf.c: */ void ipmi_intf_set_max_request_data_size(struct ipmi_intf * intf, uint16_t size); void ipmi_intf_set_max_response_data_size(struct ipmi_intf * intf, uint16_t size); 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 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 i; struct fru_multirec_header * h; uint32_t last_off, len; i = last_off = offset; 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_data != 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_data != 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_data != 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 ? "); 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 between entries\n", (int)len); /* I can't assign scanf' %x into a single char */ for( i=0;i OEM_KONTRON_SUBCOMMAND_ARG_POS ){ if(strncmp("oem", argv[OEM_KONTRON_SUBCOMMAND_ARG_POS],3)){ printf("usage: fru get \n"); badParams = TRUE; return; } } if( argc \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; 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;blockIndexfield1); 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 \n"); badParams = TRUE; return hasChanged; } } if( argc \n"); printf("usage: oem 15000 3 0 "\ " \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 ){ 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;blockIndexfield1 , 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;indexrecord_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;indexrecord_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= 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; igrouping); 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>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", 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 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? */ intf->target_addr != mc->dev_slave_addr) { /* 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 "); 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 "); 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 field
- edit FRU string"); lprintf(LOG_NOTICE, "fru edit oem iana - 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); if (retStatus != 0) { return retStatus; } 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 i; uint32_t offset= offFruMultiRec; struct fru_multirec_header * h; uint32_t last_off, len; uint8_t error=0; i = last_off = offset; 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 []"); 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 oem iana - limited OEM support"); } /* ipmi_fru_get_help() */ void ipmi_fru_internaluse_help() { lprintf(LOG_NOTICE, "fru internaluse info - get internal use area size"); lprintf(LOG_NOTICE, "fru internaluse print - print internal use area in hex"); lprintf(LOG_NOTICE, "fru internaluse read - read internal use area to file"); lprintf(LOG_NOTICE, "fru internaluse write - 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); if (retStatus != 0) { return retStatus; } 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 i; uint32_t offset= offFruMultiRec; struct fru_multirec_header * h; uint32_t last_off, len; uint8_t error=0; i = last_off = offset; 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 "); 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 = {0}; 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 "); 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 = 0; i < fru_section_len - 1; i++) { checksum += fru_data[i]; } checksum = (~checksum) + 1; fru_data[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) { 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, 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; } /************************* 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; } if ((f_type == 'c' ) || (f_type == 'b' ) || (f_type == 'p' )) { printf("Change multi offset from %d to %d\n", header.offset.multi, header.offset.multi + change_size_by_8); header.offset.multi += 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