FruDevice: try to parse Fru even it a bit broken
It was discovered that some of Delta PSUs have FRU which is not properly
formatted: there was no endOfFields mark in product area. This FRU
actually is not valid but we still can show useful information to user.
This change fixes some problems in out of area control and make decoder
not fail if endOfFields was not found but all general fields are
present.
Tested: tested with several different Frus
Signed-off-by: Andrei Kartashev <a.kartashev@yadro.com>
Change-Id: If5a386e3c2cb9412a13cc43cdf5a622c2250082d
diff --git a/src/FruDevice.cpp b/src/FruDevice.cpp
index 7ee7f3b..b92116e 100644
--- a/src/FruDevice.cpp
+++ b/src/FruDevice.cpp
@@ -909,7 +909,7 @@
/* we should have at least len bytes of data available overall */
if (iter + len > end)
{
- std::cerr << "FRU data field extends past end of FRU data\n";
+ std::cerr << "FRU data field extends past end of FRU area data\n";
return make_pair(DecodeState::err, value);
}
@@ -971,50 +971,59 @@
{
if (fruBytes.size() <= fruBlockSize)
{
+ std::cerr << "Error: trying to parse empty FRU \n";
return false;
}
- std::vector<uint8_t>::const_iterator fruAreaOffsetField = fruBytes.begin();
result["Common_Format_Version"] =
- std::to_string(static_cast<int>(*fruAreaOffsetField));
+ std::to_string(static_cast<int>(*fruBytes.begin()));
const std::vector<std::string>* fruAreaFieldNames;
- for (const std::string& area : FRU_AREA_NAMES)
+ // Don't parse Internal and Multirecord areas
+ for (fruAreas area = fruAreas::fruAreaChassis;
+ area <= fruAreas::fruAreaProduct; ++area)
{
- ++fruAreaOffsetField;
- if (fruAreaOffsetField >= fruBytes.end())
+
+ size_t offset = *(fruBytes.begin() + getHeaderAreaFieldOffset(area));
+ if (offset == 0)
{
+ continue;
+ }
+ offset *= fruBlockSize;
+ std::vector<uint8_t>::const_iterator fruBytesIter =
+ fruBytes.begin() + offset;
+ if (fruBytesIter + fruBlockSize >= fruBytes.end())
+ {
+ std::cerr << "Not enough data to parse \n";
return false;
}
- size_t offset = (*fruAreaOffsetField) * fruBlockSize;
-
- if (offset > 1)
+ // check for format version 1
+ if (*fruBytesIter != 0x01)
{
- // +2 to skip format and length
- std::vector<uint8_t>::const_iterator fruBytesIter =
- fruBytes.begin() + offset + 2;
+ std::cerr << "Unexpected version " << *fruBytesIter << "\n";
+ return false;
+ }
+ ++fruBytesIter;
+ uint8_t fruAreaSize = *fruBytesIter * fruBlockSize;
+ std::vector<uint8_t>::const_iterator fruBytesIterEndArea =
+ fruBytes.begin() + offset + fruAreaSize - 1;
+ ++fruBytesIter;
- if (fruBytesIter >= fruBytes.end())
- {
- return false;
- }
-
- if (area == "CHASSIS")
+ switch (area)
+ {
+ case fruAreas::fruAreaChassis:
{
result["CHASSIS_TYPE"] =
std::to_string(static_cast<int>(*fruBytesIter));
fruBytesIter += 1;
fruAreaFieldNames = &CHASSIS_FRU_AREAS;
+ break;
}
- else if (area == "BOARD")
+ case fruAreas::fruAreaBoard:
{
result["BOARD_LANGUAGE_CODE"] =
std::to_string(static_cast<int>(*fruBytesIter));
fruBytesIter += 1;
- if (fruBytesIter >= fruBytes.end())
- {
- return false;
- }
unsigned int minutes = *fruBytesIter |
*(fruBytesIter + 1) << 8 |
@@ -1037,48 +1046,46 @@
result["BOARD_MANUFACTURE_DATE"] = std::string(timeString);
fruBytesIter += 3;
fruAreaFieldNames = &BOARD_FRU_AREAS;
+ break;
}
- else if (area == "PRODUCT")
+ case fruAreas::fruAreaProduct:
{
result["PRODUCT_LANGUAGE_CODE"] =
std::to_string(static_cast<int>(*fruBytesIter));
fruBytesIter += 1;
fruAreaFieldNames = &PRODUCT_FRU_AREAS;
+ break;
+ }
+ default:
+ {
+ std::cerr << "Internal error: unexpected FRU area index: "
+ << static_cast<int>(area) << " \n";
+ return false;
+ }
+ }
+ size_t fieldIndex = 0;
+ DecodeState state;
+ do
+ {
+ auto res = decodeFRUData(fruBytesIter, fruBytesIterEndArea);
+ state = res.first;
+ std::string value = res.second;
+ std::string name;
+ if (fieldIndex < fruAreaFieldNames->size())
+ {
+ name = std::string(getFruAreaName(area)) + "_" +
+ fruAreaFieldNames->at(fieldIndex);
}
else
{
- continue;
+ name =
+ std::string(getFruAreaName(area)) + "_" +
+ FRU_CUSTOM_FIELD_NAME +
+ std::to_string(fieldIndex - fruAreaFieldNames->size() + 1);
}
- size_t fieldIndex = 0;
- DecodeState state = DecodeState::ok;
- while (state == DecodeState::ok)
+
+ if (state == DecodeState::ok)
{
- auto res = decodeFRUData(fruBytesIter, fruBytes.end());
- std::string name;
- if (fieldIndex < fruAreaFieldNames->size())
- {
- name = area + "_" + fruAreaFieldNames->at(fieldIndex);
- }
- else
- {
- name = area + "_" + FRU_CUSTOM_FIELD_NAME +
- std::to_string(fieldIndex -
- fruAreaFieldNames->size() + 1);
- }
-
- state = res.first;
- if (state == DecodeState::end)
- {
- break;
- }
- else if (state == DecodeState::err)
- {
- std::cerr << "Error while parsing " << name << "\n";
- return false;
- }
-
- std::string value = res.second;
-
// Strip non null characters from the end
value.erase(std::find_if(value.rbegin(), value.rend(),
[](char ch) { return ch != 0; })
@@ -1088,7 +1095,18 @@
result[name] = std::move(value);
++fieldIndex;
}
- }
+ else if (state == DecodeState::err)
+ {
+ std::cerr << "Error while parsing " << name << "\n";
+ // Cancel decoding if failed to parse any of mandatory
+ // fields
+ if (fieldIndex < fruAreaFieldNames->size())
+ {
+ std::cerr << "Failed to parse mandatory field \n";
+ return false;
+ }
+ }
+ } while (state == DecodeState::ok);
}
return true;