#include "impl.hpp" #include "vpdecc/vpdecc.h" #include "const.hpp" #include "defines.hpp" #include "ibm_vpd_utils.hpp" #include "types.hpp" #include "vpd_exceptions.hpp" #include #include #include #include #include #include #include #include namespace openpower { namespace vpd { namespace parser { using namespace openpower::vpd::constants; using namespace openpower::vpd::exceptions; static const std::unordered_map supportedRecords = { {"VINI", Record::VINI}, {"OPFR", Record::OPFR}, {"OSYS", Record::OSYS}}; static const std::unordered_map supportedKeywords = { {"DR", std::make_tuple(record::Keyword::DR, keyword::Encoding::ASCII)}, {"PN", std::make_tuple(record::Keyword::PN, keyword::Encoding::ASCII)}, {"SN", std::make_tuple(record::Keyword::SN, keyword::Encoding::ASCII)}, {"CC", std::make_tuple(record::Keyword::CC, keyword::Encoding::ASCII)}, {"HW", std::make_tuple(record::Keyword::HW, keyword::Encoding::RAW)}, {"B1", std::make_tuple(record::Keyword::B1, keyword::Encoding::B1)}, {"VN", std::make_tuple(record::Keyword::VN, keyword::Encoding::ASCII)}, {"MB", std::make_tuple(record::Keyword::MB, keyword::Encoding::MB)}, {"MM", std::make_tuple(record::Keyword::MM, keyword::Encoding::ASCII)}, {"UD", std::make_tuple(record::Keyword::UD, keyword::Encoding::UD)}, {"VP", std::make_tuple(record::Keyword::VP, keyword::Encoding::ASCII)}, {"VS", std::make_tuple(record::Keyword::VS, keyword::Encoding::ASCII)}, }; namespace { constexpr auto toHex(size_t c) { constexpr auto map = "0123456789abcdef"; return map[c]; } } // namespace /*readUInt16LE: Read 2 bytes LE data*/ static LE2ByteData readUInt16LE(Binary::const_iterator iterator) { LE2ByteData lowByte = *iterator; LE2ByteData highByte = *(iterator + 1); lowByte |= (highByte << 8); return lowByte; } RecordOffset Impl::getVtocOffset() const { auto vpdPtr = vpd.cbegin(); std::advance(vpdPtr, offsets::VTOC_PTR); // Get VTOC Offset auto vtocOffset = readUInt16LE(vpdPtr); return vtocOffset; } #ifdef IPZ_PARSER int Impl::vhdrEccCheck() { int rc = eccStatus::SUCCESS; auto vpdPtr = vpd.cbegin(); auto l_status = vpdecc_check_data( const_cast(&vpdPtr[offsets::VHDR_RECORD]), lengths::VHDR_RECORD_LENGTH, const_cast(&vpdPtr[offsets::VHDR_ECC]), lengths::VHDR_ECC_LENGTH); if (l_status == VPD_ECC_CORRECTABLE_DATA) { try { if (vpdFileStream.is_open()) { vpdFileStream.seekp(vpdStartOffset + offsets::VHDR_RECORD, std::ios::beg); vpdFileStream.write( reinterpret_cast(&vpd[offsets::VHDR_RECORD]), lengths::VHDR_RECORD_LENGTH); } else { std::cerr << "File not open"; rc = eccStatus::FAILED; } } catch (const std::fstream::failure& e) { std::cout << "Error while operating on file with exception:" << e.what(); rc = eccStatus::FAILED; } } else if (l_status != VPD_ECC_OK) { rc = eccStatus::FAILED; } return rc; } int Impl::vtocEccCheck() { int rc = eccStatus::SUCCESS; // Use another pointer to get ECC information from VHDR, // actual pointer is pointing to VTOC data auto vpdPtr = vpd.cbegin(); // Get VTOC Offset auto vtocOffset = getVtocOffset(); // Get the VTOC Length std::advance(vpdPtr, offsets::VTOC_PTR + sizeof(RecordOffset)); auto vtocLength = readUInt16LE(vpdPtr); // Get the ECC Offset std::advance(vpdPtr, sizeof(RecordLength)); auto vtocECCOffset = readUInt16LE(vpdPtr); // Get the ECC length std::advance(vpdPtr, sizeof(ECCOffset)); auto vtocECCLength = readUInt16LE(vpdPtr); // Reset pointer to start of the vpd, // so that Offset will point to correct address vpdPtr = vpd.cbegin(); auto l_status = vpdecc_check_data( const_cast(&vpdPtr[vtocOffset]), vtocLength, const_cast(&vpdPtr[vtocECCOffset]), vtocECCLength); if (l_status == VPD_ECC_CORRECTABLE_DATA) { try { if (vpdFileStream.is_open()) { vpdFileStream.seekp(vpdStartOffset + vtocOffset, std::ios::beg); vpdFileStream.write( reinterpret_cast(&vpdPtr[vtocOffset]), vtocLength); } else { std::cerr << "File not open"; rc = eccStatus::FAILED; } } catch (const std::fstream::failure& e) { std::cout << "Error while operating on file with exception " << e.what(); rc = eccStatus::FAILED; } } else if (l_status != VPD_ECC_OK) { rc = eccStatus::FAILED; } return rc; } int Impl::recordEccCheck(Binary::const_iterator iterator) { int rc = eccStatus::SUCCESS; auto recordOffset = readUInt16LE(iterator); std::advance(iterator, sizeof(RecordOffset)); auto recordLength = readUInt16LE(iterator); std::advance(iterator, sizeof(RecordLength)); auto eccOffset = readUInt16LE(iterator); std::advance(iterator, sizeof(ECCOffset)); auto eccLength = readUInt16LE(iterator); if (eccLength == 0 || eccOffset == 0) { throw(VpdEccException( "Could not find ECC's offset or Length for Record:")); } if (recordOffset == 0 || recordLength == 0) { throw(VpdDataException("Could not find VPD record offset or VPD record " "length for Record:")); } auto vpdPtr = vpd.cbegin(); auto l_status = vpdecc_check_data( const_cast(&vpdPtr[recordOffset]), recordLength, const_cast(&vpdPtr[eccOffset]), eccLength); if (l_status == VPD_ECC_CORRECTABLE_DATA) { try { if (vpdFileStream.is_open()) { vpdFileStream.seekp(vpdStartOffset + recordOffset, std::ios::beg); vpdFileStream.write( reinterpret_cast(&vpdPtr[recordOffset]), recordLength); } else { std::cerr << "File not open"; rc = eccStatus::FAILED; } } catch (const std::fstream::failure& e) { std::cout << "Error while operating on file with exception " << e.what(); rc = eccStatus::FAILED; } } else if (l_status != VPD_ECC_OK) { rc = eccStatus::FAILED; } return rc; } #endif void Impl::checkHeader() { if (vpd.empty() || (lengths::RECORD_MIN > vpd.size())) { throw(VpdDataException("Malformed VPD")); } else { auto iterator = vpd.cbegin(); std::advance(iterator, offsets::VHDR); auto stop = std::next(iterator, lengths::RECORD_NAME); std::string record(iterator, stop); if ("VHDR" != record) { throw(VpdDataException("VHDR record not found")); } #ifdef IPZ_PARSER // Check ECC int rc = eccStatus::FAILED; rc = vhdrEccCheck(); if (rc != eccStatus::SUCCESS) { throw(VpdEccException("ERROR: VHDR ECC check Failed")); } #endif } } std::size_t Impl::readTOC(Binary::const_iterator& iterator) { // The offset to VTOC could be 1 or 2 bytes long RecordOffset vtocOffset = getVtocOffset(); // Got the offset to VTOC, skip past record header and keyword header // to get to the record name. std::advance(iterator, vtocOffset + sizeof(RecordId) + sizeof(RecordSize) + // Skip past the RT keyword, which contains // the record name. lengths::KW_NAME + sizeof(KwSize)); auto stop = std::next(iterator, lengths::RECORD_NAME); std::string record(iterator, stop); if ("VTOC" != record) { throw(VpdDataException("VTOC record not found")); } #ifdef IPZ_PARSER // Check ECC int rc = eccStatus::FAILED; rc = vtocEccCheck(); if (rc != eccStatus::SUCCESS) { throw(VpdEccException("ERROR: VTOC ECC check Failed")); } #endif // VTOC record name is good, now read through the TOC, stored in the PT // PT keyword; vpdBuffer is now pointing at the first character of the // name 'VTOC', jump to PT data. // Skip past record name and KW name, 'PT' std::advance(iterator, lengths::RECORD_NAME + lengths::KW_NAME); // Note size of PT std::size_t ptLen = *iterator; // Skip past PT size std::advance(iterator, sizeof(KwSize)); // length of PT keyword return ptLen; } internal::OffsetList Impl::readPT(Binary::const_iterator iterator, std::size_t ptLength) { internal::OffsetList offsets{}; auto end = iterator; std::advance(end, ptLength); // Look at each entry in the PT keyword. In the entry, // we care only about the record offset information. while (iterator < end) { #ifdef IPZ_PARSER auto iteratorToRecName = iterator; #endif // Skip record name and record type std::advance(iterator, lengths::RECORD_NAME + sizeof(RecordType)); // Get record offset auto offset = readUInt16LE(iterator); offsets.push_back(offset); #ifdef IPZ_PARSER std::string recordName(iteratorToRecName, iteratorToRecName + lengths::RECORD_NAME); try { // Verify the ECC for this Record int rc = recordEccCheck(iterator); if (rc != eccStatus::SUCCESS) { std::string errorMsg = std::string( "ERROR: ECC check did not pass for the " "Record:"); throw(VpdEccException(errorMsg)); } } catch (const VpdEccException& ex) { inventory::PelAdditionalData additionalData{}; additionalData.emplace("DESCRIPTION", std::string{ex.what()} + recordName); additionalData.emplace("CALLOUT_INVENTORY_PATH", inventoryPath); createPEL(additionalData, PelSeverity::WARNING, errIntfForEccCheckFail, nullptr); } catch (const VpdDataException& ex) { inventory::PelAdditionalData additionalData{}; additionalData.emplace("DESCRIPTION", std::string{ex.what()} + recordName); additionalData.emplace("CALLOUT_INVENTORY_PATH", inventoryPath); createPEL(additionalData, PelSeverity::WARNING, errIntfForInvalidVPD, nullptr); } #endif // Jump record size, record length, ECC offset and ECC length std::advance(iterator, sizeof(RecordOffset) + sizeof(RecordLength) + sizeof(ECCOffset) + sizeof(ECCLength)); } return offsets; } void Impl::processRecord(std::size_t recordOffset) { // Jump to record name auto nameOffset = recordOffset + sizeof(RecordId) + sizeof(RecordSize) + // Skip past the RT keyword, which contains // the record name. lengths::KW_NAME + sizeof(KwSize); // Get record name auto iterator = vpd.cbegin(); std::advance(iterator, nameOffset); std::string name(iterator, iterator + lengths::RECORD_NAME); #ifndef IPZ_PARSER if (supportedRecords.end() != supportedRecords.find(name)) { #endif // If it's a record we're interested in, proceed to find // contained keywords and their values. std::advance(iterator, lengths::RECORD_NAME); #ifdef IPZ_PARSER // Reverse back to RT Kw, in ipz vpd, to Read RT KW & value std::advance(iterator, -(lengths::KW_NAME + sizeof(KwSize) + lengths::RECORD_NAME)); #endif auto kwMap = readKeywords(iterator); // Add entry for this record (and contained keyword:value pairs) // to the parsed vpd output. out.emplace(std::move(name), std::move(kwMap)); #ifndef IPZ_PARSER } #endif } std::string Impl::readKwData(const internal::KeywordInfo& keyword, std::size_t dataLength, Binary::const_iterator iterator) { using namespace openpower::vpd; switch (std::get(keyword)) { case keyword::Encoding::ASCII: { auto stop = std::next(iterator, dataLength); return std::string(iterator, stop); } case keyword::Encoding::RAW: { auto stop = std::next(iterator, dataLength); std::string data(iterator, stop); std::string result{}; std::for_each(data.cbegin(), data.cend(), [&result](size_t c) { result += toHex(c >> 4); result += toHex(c & 0x0F); }); return result; } case keyword::Encoding::MB: { // MB is BuildDate, represent as // 1997-01-01-08:30:00 // ---:: auto stop = std::next(iterator, MB_LEN_BYTES); std::string data(iterator, stop); std::string result; result.reserve(MB_LEN_BYTES); auto strItr = data.cbegin(); std::advance(strItr, 1); std::for_each(strItr, data.cend(), [&result](size_t c) { result += toHex(c >> 4); result += toHex(c & 0x0F); }); result.insert(MB_YEAR_END, 1, '-'); result.insert(MB_MONTH_END, 1, '-'); result.insert(MB_DAY_END, 1, '-'); result.insert(MB_HOUR_END, 1, ':'); result.insert(MB_MIN_END, 1, ':'); return result; } case keyword::Encoding::B1: { // B1 is MAC address, represent as AA:BB:CC:DD:EE:FF auto stop = std::next(iterator, MAC_ADDRESS_LEN_BYTES); std::string data(iterator, stop); std::string result{}; auto strItr = data.cbegin(); size_t firstDigit = *strItr; result += toHex(firstDigit >> 4); result += toHex(firstDigit & 0x0F); std::advance(strItr, 1); std::for_each(strItr, data.cend(), [&result](size_t c) { result += ":"; result += toHex(c >> 4); result += toHex(c & 0x0F); }); return result; } case keyword::Encoding::UD: { // UD, the UUID info, represented as // 123e4567-e89b-12d3-a456-426655440000 //--