///----------------------------------------------------- // Accepts the filename and validates per IPMI FRU spec //---------------------------------------------------- int ipmi_validate_fru_area(const uint8_t fruid, const char *fru_file_name, sd_bus *bus_type, const bool bmc_fru) { size_t data_len = 0; size_t bytes_read = 0; int rc = -1; // Vector that holds individual IPMI FRU AREAs. Although MULTI and INTERNAL // are not used, keeping it here for completeness. fru_area_vec_t fru_area_vec; std::vector<std::string> defined_fru_area; // BMC defines fru areas that should be present in Skeleton rc = get_defined_fru_area(bus_type, fruid, defined_fru_area); if(rc < 0) { fprintf(stderr, "ERROR: cannot get defined fru area\n"); return rc; } for(uint8_t fru_entry = IPMI_FRU_INTERNAL_OFFSET; fru_entry < (sizeof(struct common_header) -2); fru_entry++) { // Create an object and push onto a vector. std::unique_ptr<ipmi_fru> fru_area = std::make_unique<ipmi_fru> (fruid, get_fru_area_type(fru_entry), bus_type, bmc_fru); // Physically being present bool present = access(fru_file_name, F_OK) == 0; fru_area->set_present(present); // Only setup dbus path for areas defined in BMC. // Otherwise Skeleton will report 'not found' error std::string fru_area_name = fru_area->get_name() + std::to_string(fruid); auto iter = std::find(defined_fru_area.begin(), defined_fru_area.end(), fru_area_name); if (iter != defined_fru_area.end()) { fru_area->setup_sd_bus_paths(); } fru_area_vec.emplace_back(std::move(fru_area)); } FILE *fru_fp = fopen(fru_file_name,"rb"); if(fru_fp == NULL) { fprintf(stderr, "ERROR: opening:[%s]\n",fru_file_name); perror("Error:"); return cleanup_error(fru_fp, fru_area_vec); } // Get the size of the file to see if it meets minimum requirement if(fseek(fru_fp, 0, SEEK_END)) { perror("Error:"); return cleanup_error(fru_fp, fru_area_vec); } // Allocate a buffer to hold entire file content data_len = ftell(fru_fp); uint8_t fru_data[data_len] = {0}; rewind(fru_fp); bytes_read = fread(fru_data, data_len, 1, fru_fp); if(bytes_read != 1) { fprintf(stderr, "Failed reading fru data. Bytes_read=[%d]\n",bytes_read); perror("Error:"); return cleanup_error(fru_fp, fru_area_vec); } // We are done reading. fclose(fru_fp); fru_fp = NULL; rc = ipmi_validate_common_hdr(fru_data, data_len); if(rc < 0) { return cleanup_error(fru_fp, fru_area_vec); } // Now that we validated the common header, populate various fru sections if we have them here. rc = ipmi_populate_fru_areas(fru_data, data_len, fru_area_vec); if(rc < 0) { fprintf(stderr,"Populating FRU areas failed for:[%d]\n",fruid); return cleanup_error(fru_fp, fru_area_vec); } else { printf("SUCCESS: Populated FRU areas for:[%s]\n",fru_file_name); } #ifdef __IPMI_DEBUG__ for(auto& iter : fru_area_vec) { printf("FRU ID : [%d]\n",(iter)->get_fruid()); printf("AREA NAME : [%s]\n",(iter)->get_name()); printf("TYPE : [%d]\n",(iter)->get_type()); printf("LEN : [%d]\n",(iter)->get_len()); printf("BUS NAME : [%s]\n", (iter)->get_bus_name()); printf("OBJ PATH : [%s]\n", (iter)->get_obj_path()); printf("INTF NAME :[%s]\n", (iter)->get_intf_name()); } #endif // If the vector is populated with everything, then go ahead and update the // inventory. if(!(fru_area_vec.empty())) { #ifdef __IPMI_DEBUG__ printf("\n SIZE of vector is : [%d] \n",fru_area_vec.size()); #endif rc = ipmi_update_inventory(fru_area_vec); if(rc <0) { fprintf(stderr, "Error updating inventory\n"); } } // we are done with all that we wanted to do. This will do the job of // calling any destructors too. fru_area_vec.clear(); return rc; }
//------------------------------------------------------------------------- // Validates the CRC and if found good, calls fru areas parser and calls // Inventory Dbus with the dictionary of Name:Value for updating. //------------------------------------------------------------------------- int ipmi_validate_and_update_inventory(const uint8_t fruid, const uint8_t *fru_data) { // Used for generic checksum calculation uint8_t checksum = 0; // This can point to any FRU entry. uint8_t fru_entry; // A generic offset locator for any FRU record. uint8_t area_offset = 0; // First 2 bytes in the record. uint8_t fru_area_hdr[2] = {0}; // To hold info about individual FRU record areas. fru_area_t fru_area; // For parsing and updating Inventory. fru_area_vec_t fru_area_vec; int rc = 0; uint8_t common_hdr[sizeof(struct common_header)] = {0}; memset(common_hdr, 0x0, sizeof(common_hdr)); // Copy first 8 bytes to verify common header memcpy(common_hdr, fru_data, sizeof(common_hdr)); // Validate for first byte to always have a value of [1] if(common_hdr[0] != IPMI_FRU_HDR_BYTE_ZERO) { fprintf(stderr, "ERROR: Common Header entry_1:[0x%X] Invalid.\n",common_hdr[0]); return -1; } else { printf("SUCCESS: Validated [0x%X] in common header\n",common_hdr[0]); } // Validate the header checskum that is at last byte ( Offset: 7 ) checksum = calculate_crc(common_hdr, sizeof(common_hdr)-1); if(checksum != common_hdr[IPMI_FRU_HDR_CRC_OFFSET]) { #ifdef __IPMI__DEBUG__ fprintf(stderr, "ERROR: Common Header checksum mismatch." " Calculated:[0x%X], Embedded:[0x%X]\n", checksum, common_hdr[IPMI_FRU_HDR_CRC_OFFSET]); #endif return -1; } else { printf("SUCCESS: Common Header checksum MATCH:[0x%X]\n",checksum); } //------------------------------------------- // TODO: Add support for Multi Record later //------------------------------------------- // Now start walking the common_hdr array that has offsets into other FRU // record areas and validate those. Starting with second entry since the // first one is always a [0x01] for(fru_entry = IPMI_FRU_INTERNAL_OFFSET; fru_entry < (sizeof(struct common_header) -2); fru_entry++) { // Offset is 'value given in' internal_offset * 8 from the START of // common header. So an an example, 01 00 00 00 01 00 00 fe has // product area set at the offset 01 * 8 --> 8 bytes from the START of // common header. That means, soon after the header checksum. area_offset = common_hdr[fru_entry] * IPMI_EIGHT_BYTES; if(area_offset) { memset((void *)&fru_area, 0x0, sizeof(fru_area_t)); // Enumerated FRU area. fru_area.type = get_fru_area_type(fru_entry); // From start of fru header + record offset, copy 2 bytes. fru_area.offset = &((uint8_t *)fru_data)[area_offset]; memcpy(fru_area_hdr, fru_area.offset, sizeof(fru_area_hdr)); // A NON zero value means that the vpd packet has the data for that // area. err if first element in the record header is _not_ a [0x01]. if(fru_area_hdr[0] != IPMI_FRU_HDR_BYTE_ZERO) { fprintf(stderr, "ERROR: Unexpected :[0x%X] found at Record header\n", fru_area_hdr[0]); // This vector by now may have had some entries. Since this is a // failure now, clear the state data. fru_area_vec.clear(); return -1; } else { printf("SUCCESS: Validated [0x%X] in fru record:[%d] header\n", fru_area_hdr[0],fru_entry); } // Read Length bytes ( makes a complete record read now ) fru_area.len = fru_area_hdr[1] * IPMI_EIGHT_BYTES; #ifdef __IPMI_DEBUG__ printf("AREA NO[%d], SIZE = [%d]\n",fru_entry, fru_area.len); #endif uint8_t fru_area_data[fru_area.len]; memset(fru_area_data, 0x0, sizeof(fru_area_data)); memmove(fru_area_data, fru_area.offset, sizeof(fru_area_data)); // Calculate checksum (from offset -> (Length-1)). // All the bytes except the last byte( which is CRC :) ) will // participate in calculating the checksum. checksum = calculate_crc(fru_area_data, sizeof(fru_area_data)-1); // Verify the embedded checksum in last byte with calculated checksum // record_len -1 since length is some N but numbering is 0..N-1 if(checksum != fru_area_data[fru_area.len-1]) { #ifdef __IPMI_DEBUG__ fprintf(stderr, "ERROR: FRU Header checksum mismatch. " " Calculated:[0x%X], Embedded:[0x%X]\n", checksum, fru_area_data[fru_area.len - 1]); #endif // This vector by now may have had some entries. Since this is a // failure now, clear the state data. fru_area_vec.clear(); return -1; } else { printf("SUCCESS: FRU Header checksum MATCH:[0x%X]\n",checksum); } // Everything is rihgt about this particular FRU record, fru_area_vec.push_back(fru_area); // Update the internal structure with info about this entry that is // needed while handling each areas. } // If the packet has data for a particular data record. } // End walking all the fru records. // If we reach here, then we have validated the crc for all the records and // time to call FRU area parser to get a Name:Value pair dictionary. // This will start iterating all over again on the buffer -BUT- now with the // job of taking each areas, getting it parsed and then updating the // DBUS. if(!(fru_area_vec.empty())) { rc = ipmi_update_inventory(fruid, fru_data, fru_area_vec); } // We are done with this FRU write packet. fru_area_vec.clear(); return rc; }