fru-device: fixup fru edit commit
Adds a number of fixups to review comments and simplifications
to the flow of the previous fru-edit commit.
Tested: Edited FRU property on nvl32-obmc
```
root@nvl32-obmc:/sys/bus/i2c/devices/4-0051# busctl introspect xyz.openbmc_project.FruDevice /xyz/openbmc_project/FruDevice/MSX4_MG1_000
NAME TYPE SIGNATURE RESULT/VALUE FLAGS
org.freedesktop.DBus.Introspectable interface - - -
.Introspect method - s -
org.freedesktop.DBus.Peer interface - - -
.GetMachineId method - s -
.Ping method - - -
org.freedesktop.DBus.Properties interface - - -
.Get method ss v -
.GetAll method s a{sv} -
.Set method ssv - -
.PropertiesChanged signal sa{sv}as - -
xyz.openbmc_project.FruDevice interface - - -
.UpdateFruField method ss b -
.ADDRESS property u 81 emits-change
.BOARD_INFO_AM1 property s "NULL" emits-change writable
.BOARD_LANGUAGE_CODE property s "0" emits-change
.BOARD_MANUFACTURER property s "Giga Computing" emits-change writable
.BOARD_MANUFACTURE_DATE property s "20250101T000800Z" emits-change
.BOARD_PART_NUMBER property s "123456789AB" emits-change writable
.BOARD_PRODUCT_NAME property s "MSX4-MG1-000" emits-change writable
.BOARD_SERIAL_NUMBER property s "S2510200007" emits-change writable
.BUS property u 4 emits-change
.CHASSIS_PART_NUMBER property s "01234567" emits-change writable
.CHASSIS_SERIAL_NUMBER property s "01234567890123456789AB" emits-change writable
.CHASSIS_TYPE property s "23" emits-change
.Common_Format_Version property s "1" emits-change
.PRODUCT_ASSET_TAG property s "01234567890" emits-change writable
.PRODUCT_LANGUAGE_CODE property s "0" emits-change
.PRODUCT_MANUFACTURER property s "Giga Computing" emits-change writable
.PRODUCT_PART_NUMBER property s "000000000001" emits-change writable
.PRODUCT_PRODUCT_NAME property s "XL44-SX2-AAS1-000" emits-change writable
.PRODUCT_SERIAL_NUMBER property s "01234567890123" emits-change writable
.PRODUCT_VERSION property s "0100" emits-change writable
root@nvl32-obmc:/sys/bus/i2c/devices/4-0051# busctl call xyz.openbmc_project.FruDevice /xyz/openbmc_project/FruDevice/MSX4_MG1_000 xyz.openbmc_project.FruDevice UpdateFruField ss "PRODUCT_ASSET_TAG" "12345678901234"
b true
root@nvl32-obmc:/sys/bus/i2c/devices/4-0051# busctl introspect xyz.openbmc_project.FruDevice /xyz/openbmc_project/FruDevice/MSX4_MG1_000
NAME TYPE SIGNATURE RESULT/VALUE FLAGS
org.freedesktop.DBus.Introspectable interface - - -
.Introspect method - s -
org.freedesktop.DBus.Peer interface - - -
.GetMachineId method - s -
.Ping method - - -
org.freedesktop.DBus.Properties interface - - -
.Get method ss v -
.GetAll method s a{sv} -
.Set method ssv - -
.PropertiesChanged signal sa{sv}as - -
xyz.openbmc_project.FruDevice interface - - -
.UpdateFruField method ss b -
.ADDRESS property u 81 emits-change
.BOARD_INFO_AM1 property s "NULL" emits-change writable
.BOARD_LANGUAGE_CODE property s "0" emits-change
.BOARD_MANUFACTURER property s "Giga Computing" emits-change writable
.BOARD_MANUFACTURE_DATE property s "20250101T000800Z" emits-change
.BOARD_PART_NUMBER property s "123456789AB" emits-change writable
.BOARD_PRODUCT_NAME property s "MSX4-MG1-000" emits-change writable
.BOARD_SERIAL_NUMBER property s "S2510200007" emits-change writable
.BUS property u 4 emits-change
.CHASSIS_PART_NUMBER property s "01234567" emits-change writable
.CHASSIS_SERIAL_NUMBER property s "01234567890123456789AB" emits-change writable
.CHASSIS_TYPE property s "23" emits-change
.Common_Format_Version property s "1" emits-change
.PRODUCT_ASSET_TAG property s "12345678901234" emits-change writable
.PRODUCT_LANGUAGE_CODE property s "0" emits-change
.PRODUCT_MANUFACTURER property s "Giga Computing" emits-change writable
.PRODUCT_PART_NUMBER property s "000000000001" emits-change writable
.PRODUCT_PRODUCT_NAME property s "XL44-SX2-AAS1-000" emits-change writable
.PRODUCT_SERIAL_NUMBER property s "01234567890123" emits-change writable
.PRODUCT_VERSION property s "0100" emits-change writable
```
Change-Id: Ic4cd7111cd4f3694faed79793e1abc98f254617b
Signed-off-by: Marc Olberding <molberding@nvidia.com>
diff --git a/src/fru_device/fru_device.cpp b/src/fru_device/fru_device.cpp
index cbd2af0..2678f90 100644
--- a/src/fru_device/fru_device.cpp
+++ b/src/fru_device/fru_device.cpp
@@ -1214,10 +1214,10 @@
// Validate field length: must be 2–63 characters
const size_t len = propertyValue.length();
- if (len <= 1 || len > 63)
+ if (len == 1 || len > 63)
{
- lg2::debug(
- "FRU field data must be between 2 and 63 characters. Invalid Length: {LEN}",
+ lg2::error(
+ "FRU field data must be 0 or between 2 and 63 characters. Invalid Length: {LEN}",
"LEN", len);
return false;
}
@@ -1225,29 +1225,37 @@
std::vector<uint8_t> fruData;
if (!getFruData(fruData, bus, address))
{
- std::cerr << "Failure getting FRU Data from bus " << bus << ", address "
- << address << "\n";
+ lg2::error("Failure getting FRU Data from bus {BUS}, address {ADDRESS}",
+ "BUS", bus, "ADDRESS", address);
return false;
}
if (fruData.empty())
{
- std::cerr << "Empty FRU data\n";
+ lg2::error("Empty FRU data\n");
return false;
}
// Extract area name (prefix before underscore)
- fruAreas fruAreaToUpdate{};
std::string areaName = propertyName.substr(0, propertyName.find('_'));
- if (!getAreaIdx(areaName, fruAreaToUpdate))
+ auto areaIterator =
+ std::find(fruAreaNames.begin(), fruAreaNames.end(), areaName);
+ if (areaIterator == fruAreaNames.end())
{
- lg2::debug("Failed to get FRU area for property: {AREA}", "AREA",
+ lg2::error("Failed to get FRU area for property: {AREA}", "AREA",
areaName);
return false;
}
+ fruAreas fruAreaToUpdate = static_cast<fruAreas>(
+ std::distance(fruAreaNames.begin(), areaIterator));
+
std::vector<std::vector<uint8_t>> areasData;
- disassembleFruData(fruData, areasData);
+ if (!disassembleFruData(fruData, areasData))
+ {
+ lg2::error("Failed to disassemble Fru Data");
+ return false;
+ }
std::vector<uint8_t>& areaData =
areasData[static_cast<size_t>(fruAreaToUpdate)];
@@ -1255,7 +1263,7 @@
{
// If ENABLE_FRU_AREA_RESIZE is not defined then return with failure
#ifndef ENABLE_FRU_AREA_RESIZE
- lg2::debug(
+ lg2::error(
"FRU area {AREA} not present and ENABLE_FRU_AREA_RESIZE is not set. "
"Returning failure.",
"AREA", areaName);
@@ -1263,7 +1271,7 @@
#endif
if (!createDummyArea(fruAreaToUpdate, areaData))
{
- lg2::debug("Failed to create dummy area for {AREA}", "AREA",
+ lg2::error("Failed to create dummy area for {AREA}", "AREA",
areaName);
return false;
}
@@ -1271,22 +1279,27 @@
if (!setField(fruAreaToUpdate, areaData, propertyName, propertyValue))
{
- lg2::debug("Failed to set field value for property: {PROPERTY}",
+ lg2::error("Failed to set field value for property: {PROPERTY}",
"PROPERTY", propertyName);
return false;
}
- assembleFruData(fruData, areasData);
+ if (!assembleFruData(fruData, areasData))
+ {
+ lg2::error("Failed to reassemble FRU data");
+ return false;
+ }
if (fruData.empty())
{
- std::cerr << "FRU data is empty after assembly\n";
+ lg2::error("FRU data is empty after assembly");
return false;
}
if (!writeFRU(static_cast<uint8_t>(bus), static_cast<uint8_t>(address),
fruData))
{
+ lg2::error("Failed to write the FRU");
return false;
}
diff --git a/src/fru_device/fru_utils.cpp b/src/fru_device/fru_utils.cpp
index 67ea9ed..4b01719 100644
--- a/src/fru_device/fru_utils.cpp
+++ b/src/fru_device/fru_utils.cpp
@@ -921,29 +921,30 @@
return ret;
}
-static bool updateHeadercksum(std::vector<uint8_t>& fruData)
+static bool updateHeaderChecksum(std::vector<uint8_t>& fruData)
{
- if (fruData.size() < 8)
+ if (fruData.size() < fruBlockSize)
{
lg2::debug("FRU data is too small to contain a valid header.");
return false;
}
- uint8_t oldcksum = fruData[7];
- std::span<const uint8_t> fruSpan{fruData};
- uint8_t checksum = calculateChecksum(fruSpan.begin(), fruSpan.begin() + 7);
- fruData[7] = checksum;
- if (oldcksum != checksum)
+ uint8_t& checksumInBytes = fruData[7];
+ uint8_t checksum =
+ calculateChecksum({fruData.begin(), fruData.begin() + 7});
+ std::swap(checksumInBytes, checksum);
+
+ if (checksumInBytes != checksum)
{
lg2::debug(
"FRU header checksum updated from {OLD_CHECKSUM} to {NEW_CHECKSUM}",
- "OLD_CHECKSUM", static_cast<int>(oldcksum), "NEW_CHECKSUM",
- static_cast<int>(checksum));
+ "OLD_CHECKSUM", static_cast<int>(checksum), "NEW_CHECKSUM",
+ static_cast<int>(checksumInBytes));
}
return true;
}
-bool updateAreacksum(std::vector<uint8_t>& fruArea)
+bool updateAreaChecksum(std::vector<uint8_t>& fruArea)
{
if (fruArea.size() < fruBlockSize)
{
@@ -973,6 +974,61 @@
return true;
}
+static std::optional<size_t> calculateAreaSize(
+ fruAreas area, std::span<const uint8_t> fruData, size_t areaOffset)
+{
+ switch (area)
+ {
+ case fruAreas::fruAreaChassis:
+ case fruAreas::fruAreaBoard:
+ case fruAreas::fruAreaProduct:
+ if (areaOffset + 1 >= fruData.size())
+ {
+ return std::nullopt;
+ }
+ return fruData[areaOffset + 1] * fruBlockSize; // Area size in bytes
+ case fruAreas::fruAreaInternal:
+ {
+ // Internal area size: It is difference between the next area
+ // offset and current area offset
+ for (fruAreas areaIt = fruAreas::fruAreaChassis;
+ areaIt <= fruAreas::fruAreaMultirecord; ++areaIt)
+ {
+ size_t headerOffset = getHeaderAreaFieldOffset(areaIt);
+ if (headerOffset >= fruData.size())
+ {
+ return std::nullopt;
+ }
+ size_t nextAreaOffset = fruData[headerOffset];
+ if (nextAreaOffset != 0)
+ {
+ return nextAreaOffset * fruBlockSize - areaOffset;
+ }
+ }
+ return std::nullopt;
+ }
+ break;
+ case fruAreas::fruAreaMultirecord:
+ // Multirecord area size.
+ return fruData.size() - areaOffset; // Area size in bytes
+ default:
+ lg2::error("Invalid FRU area: {AREA}", "AREA",
+ static_cast<int>(area));
+ }
+ return std::nullopt;
+}
+
+static size_t getBlockCount(size_t byteCount)
+{
+ size_t blocks = (byteCount + fruBlockSize - 1) / fruBlockSize;
+ // if we're perfectly aligned, we need another block for the checksum
+ if ((byteCount % fruBlockSize) == 0)
+ {
+ blocks++;
+ }
+ return blocks;
+}
+
bool disassembleFruData(std::vector<uint8_t>& fruData,
std::vector<std::vector<uint8_t>>& areasData)
{
@@ -998,80 +1054,42 @@
continue; // Skip areas that are not present
}
areaOffset *= fruBlockSize; // Convert to byte offset
- size_t areaSize = 0;
- switch (area)
+
+ std::optional<size_t> areaSize =
+ calculateAreaSize(area, fruData, areaOffset);
+ if (!areaSize)
{
- case fruAreas::fruAreaChassis:
- case fruAreas::fruAreaBoard:
- case fruAreas::fruAreaProduct:
- areaSize = fruData[areaOffset + 1] *
- fruBlockSize; // Area size in bytes
- break;
- case fruAreas::fruAreaInternal:
- {
- // Internal area size: It is difference between the next area
- // offset and current area offset
- size_t nextAreaOffset = 0;
- for (uint8_t i = 1; i <= 4; i++)
- {
- nextAreaOffset = fruData[getHeaderAreaFieldOffset(
- static_cast<fruAreas>(i))];
- if (nextAreaOffset != 0)
- {
- break; // Found the next area offset
- }
- }
- if (nextAreaOffset == 0)
- {
- return false; // No next area found, invalid FRU data
- }
- areaSize = (nextAreaOffset * fruBlockSize) -
- areaOffset; // Area size in bytes
- }
- break;
- case fruAreas::fruAreaMultirecord:
- // Multirecord area size.
- areaSize = fruData.size() - areaOffset; // Area size in bytes
- break;
- default:
- lg2::error("Invalid FRU area: {AREA}", "AREA",
- static_cast<int>(area));
- return false;
+ return false;
}
- if ((areaOffset + areaSize) > fruData.size())
+ if ((areaOffset + *areaSize) > fruData.size())
{
lg2::error("Area offset + size exceeds FRU data size.");
return false;
}
- std::vector<uint8_t> areaData(fruData.begin() + areaOffset,
- fruData.begin() + areaOffset + areaSize);
- // Update areaData checksum as well
- switch (area)
- {
- case fruAreas::fruAreaChassis:
- case fruAreas::fruAreaBoard:
- case fruAreas::fruAreaProduct:
- updateAreacksum(areaData);
- break;
- default:
- // No checksum update for other areas
- break;
- }
- areasData.push_back(areaData);
+
+ areasData.emplace_back(fruData.begin() + areaOffset,
+ fruData.begin() + areaOffset + *areaSize);
}
+
return true;
}
-bool setField(const fruAreas& fruAreaToUpdate, std::vector<uint8_t>& areaData,
- const std::string& propertyName, const std::string& value)
+struct FieldInfo
{
- const std::vector<std::string>* fruAreaFieldNames = nullptr;
+ size_t length;
+ size_t index;
+};
+
+static std::optional<FieldInfo> findOrCreateField(
+ std::vector<uint8_t>& areaData, const std::string& propertyName,
+ const fruAreas& fruAreaToUpdate)
+{
+ int fieldIndex = 0;
int fieldLength = 0;
- size_t fieldIndex = 0;
- size_t endOfFieldMarker = 0;
std::string areaName = propertyName.substr(0, propertyName.find('_'));
std::string propertyNamePrefix = areaName + "_";
+ const std::vector<std::string>* fruAreaFieldNames = nullptr;
switch (fruAreaToUpdate)
{
@@ -1088,128 +1106,154 @@
fieldIndex = 3;
break;
default:
- lg2::error("Invalid FRU area: {AREA}", "AREA",
- static_cast<int>(fruAreaToUpdate));
- return false;
+ lg2::info("Invalid FRU area: {AREA}", "AREA",
+ static_cast<int>(fruAreaToUpdate));
+ return std::nullopt;
}
- bool found = false;
+
for (const auto& field : *fruAreaFieldNames)
{
fieldLength = getFieldLength(areaData[fieldIndex]);
if (fieldLength < 0)
{
- // This should never happen. Insert dummy field.
areaData.insert(areaData.begin() + fieldIndex, 0xc0);
fieldLength = 0;
}
if (propertyNamePrefix + field == propertyName)
{
- found = true;
- break;
+ return FieldInfo{static_cast<size_t>(fieldLength),
+ static_cast<size_t>(fieldIndex)};
}
fieldIndex += 1 + fieldLength;
}
- if (!found)
- {
- size_t pos = propertyName.find(fruCustomFieldName);
- if (pos != std::string::npos)
- {
- // Get field after pos
- std::string customFieldIdx =
- propertyName.substr(pos + fruCustomFieldName.size());
- // Check if customFieldIdx is a number
- if (std::all_of(customFieldIdx.begin(), customFieldIdx.end(),
- ::isdigit))
- {
- // Convert to integer
- size_t customFieldIndex = std::stoi(customFieldIdx);
- // Loop through the custom fields to find the correct index
- for (size_t i = 0; i < customFieldIndex; i++)
- {
- fieldLength = getFieldLength(areaData[fieldIndex]);
- if (fieldLength < 0)
- {
- // This should never happen. Insert dummy field.
- areaData.insert(areaData.begin() + fieldIndex, 0xc0);
- fieldLength = 0;
- }
- fieldIndex += 1 + fieldLength;
- }
- fieldIndex -= (fieldLength + 1);
- fieldLength = getFieldLength(areaData[fieldIndex]);
- found = true;
- }
- }
+ size_t pos = propertyName.find(fruCustomFieldName);
+ if (pos == std::string::npos)
+ {
+ return std::nullopt;
}
- if (!found)
+ // Get field after pos
+ std::string customFieldIdx =
+ propertyName.substr(pos + fruCustomFieldName.size());
+
+ // Check if customFieldIdx is a number
+ if (!std::all_of(customFieldIdx.begin(), customFieldIdx.end(), ::isdigit))
{
- lg2::debug("Field {FIELD} not found in area {AREA}", "FIELD",
+ return std::nullopt;
+ }
+
+ size_t customFieldIndex = std::stoi(customFieldIdx);
+
+ // insert custom fields up to the index we want
+ for (size_t i = 0; i < customFieldIndex; i++)
+ {
+ fieldLength = getFieldLength(areaData[fieldIndex]);
+ if (fieldLength < 0)
+ {
+ areaData.insert(areaData.begin() + fieldIndex, 0xc0);
+ fieldLength = 0;
+ }
+ fieldIndex += 1 + fieldLength;
+ }
+
+ fieldIndex -= (fieldLength + 1);
+ fieldLength = getFieldLength(areaData[fieldIndex]);
+ return FieldInfo{static_cast<size_t>(fieldLength),
+ static_cast<size_t>(fieldIndex)};
+}
+
+static std::optional<size_t> findEndOfFieldMarker(std::span<uint8_t> bytes)
+{
+ // we're skipping the checksum
+ // this function assumes a properly sized and formatted area
+ static uint8_t constexpr endOfFieldsByte = 0xc1;
+ for (int index = bytes.size() - 2; index >= 0; --index)
+ {
+ if (bytes[index] == endOfFieldsByte)
+ {
+ return index;
+ }
+ }
+ return std::nullopt;
+}
+
+static std::optional<size_t> getNonPaddedSizeOfArea(std::span<uint8_t> bytes)
+{
+ if (auto endOfFields = findEndOfFieldMarker(bytes))
+ {
+ return *endOfFields + 1;
+ }
+ return std::nullopt;
+}
+
+bool setField(const fruAreas& fruAreaToUpdate, std::vector<uint8_t>& areaData,
+ const std::string& propertyName, const std::string& value)
+{
+ if (value.size() == 1 || value.size() > 63)
+ {
+ lg2::error("Invalid value {VALUE} for field {PROP}", "VALUE", value,
+ "PROP", propertyName);
+ return false;
+ }
+
+ // This is inneficient, but the alternative requires
+ // a bunch of complicated indexing and search to
+ // figure out if we cross a block boundary
+ // if we feel that this is too inneficient in the future,
+ // we can implement that.
+ std::vector<uint8_t> tmpBuffer = areaData;
+
+ auto fieldInfo =
+ findOrCreateField(tmpBuffer, propertyName, fruAreaToUpdate);
+
+ if (!fieldInfo)
+ {
+ lg2::error("Field {FIELD} not found in area {AREA}", "FIELD",
propertyName, "AREA", getFruAreaName(fruAreaToUpdate));
return false;
}
- // Reset checksum byte to 0 before recalculating
- areaData[areaData.size() - 1] = 0;
+ auto fieldIt = tmpBuffer.begin() + fieldInfo->index;
// Erase the existing field content.
- areaData.erase(areaData.begin() + fieldIndex,
- areaData.begin() + fieldIndex + fieldLength + 1);
+ tmpBuffer.erase(fieldIt, fieldIt + fieldInfo->length + 1);
// Insert the new field value
- areaData.insert(areaData.begin() + fieldIndex, 0xc0 | value.size());
- areaData.insert(areaData.begin() + fieldIndex + 1, value.begin(),
- value.end());
+ tmpBuffer.insert(fieldIt, 0xc0 | value.size());
+ tmpBuffer.insert_range(fieldIt + 1, value);
- // Locate the end of fields marker
- endOfFieldMarker = fieldIndex + 1 + value.size();
+ auto newSize = getNonPaddedSizeOfArea(tmpBuffer);
+ auto oldSize = getNonPaddedSizeOfArea(areaData);
- for (; endOfFieldMarker < areaData.size();)
+ if (!oldSize || !newSize)
{
- fieldLength = getFieldLength(areaData[endOfFieldMarker]);
- if (fieldLength < 0)
- {
- break;
- }
- endOfFieldMarker += 1 + fieldLength;
- }
- if (endOfFieldMarker >= areaData.size())
- {
- lg2::debug("End of fields marker not found in area {AREA}.", "AREA",
- getFruAreaName(fruAreaToUpdate));
+ lg2::error("Failed to find the size of the area");
return false;
}
- // Resize areaData to endOfFieldMarker
- areaData.resize(endOfFieldMarker + 1, 0); // Fill with zeros
-
- // Calculate number of blocks needed
- uint8_t numOfBlocks = (areaData.size() + fruBlockSize - 1) / fruBlockSize;
- if (areaData.size() % fruBlockSize == 0)
- {
- numOfBlocks++;
- }
-
- // If ENABLE_FRU_AREA_RESIZE is not defined, we do not change the size
- // of the areaData vector & return false.
+ size_t newSizePadded = getBlockCount(*newSize);
#ifndef ENABLE_FRU_AREA_RESIZE
- uint8_t prevNumOfBlocks = areaData[1];
- if (numOfBlocks != prevNumOfBlocks)
+
+ size_t oldSizePadded = getBlockCount(*oldSize);
+
+ if (newSizePadded != oldSizePadded)
{
- lg2::debug(
+ lg2::error(
"FRU area {AREA} resize is disabled, cannot increase size from {OLD_SIZE} to {NEW_SIZE}",
"AREA", getFruAreaName(fruAreaToUpdate), "OLD_SIZE",
- static_cast<int>(prevNumOfBlocks), "NEW_SIZE",
- static_cast<int>(numOfBlocks));
+ static_cast<int>(oldSizePadded), "NEW_SIZE",
+ static_cast<int>(newSizePadded));
return false;
}
-#endif // ENABLE_FRU_AREA_RESIZE
+#endif
+ // Resize the buffer as per numOfBlocks & pad with zeros
+ tmpBuffer.resize(newSizePadded * fruBlockSize, 0);
- // Resize areaData as per numOfBlocks & fill with zeros
- areaData.resize(numOfBlocks * fruBlockSize, 0);
// Update the length field
- areaData[1] = numOfBlocks;
- updateAreacksum(areaData);
+ tmpBuffer[1] = newSizePadded;
+ updateAreaChecksum(tmpBuffer);
+
+ areaData = std::move(tmpBuffer);
return true;
}
@@ -1217,6 +1261,15 @@
bool assembleFruData(std::vector<uint8_t>& fruData,
const std::vector<std::vector<uint8_t>>& areasData)
{
+ for (const auto& area : areasData)
+ {
+ if ((area.size() % fruBlockSize) != 0U)
+ {
+ lg2::error("unaligned area sent to assembleFruData");
+ return false;
+ }
+ }
+
// Clear the existing FRU data
fruData.clear();
fruData.resize(8); // Start with the header size
@@ -1236,11 +1289,9 @@
for (fruAreas area = fruAreas::fruAreaInternal;
area <= fruAreas::fruAreaMultirecord; ++area)
{
- size_t areaBlockSize =
- (areasData[static_cast<size_t>(area)].size() + fruBlockSize - 1) /
- fruBlockSize; // Calculate block size
+ const auto& areaBytes = areasData[static_cast<size_t>(area)];
- if (areasData[static_cast<size_t>(area)].empty())
+ if (areaBytes.empty())
{
lg2::debug("Skipping empty area: {AREA}", "AREA",
getFruAreaName(area));
@@ -1248,25 +1299,18 @@
}
// Set the area offset in the header
- fruData[getHeaderAreaFieldOffset(area)] =
- (writeOffset + fruBlockSize - 1) / fruBlockSize;
-
- // Copy area data back to the main fruData vector
- std::copy(areasData[static_cast<size_t>(area)].begin(),
- areasData[static_cast<size_t>(area)].end(),
- std::back_inserter(fruData));
-
- fruData.resize(writeOffset + areaBlockSize * fruBlockSize,
- 0); // Resize to accommodate the area data
- writeOffset += areaBlockSize * fruBlockSize; // Update write offset
+ fruData[getHeaderAreaFieldOffset(area)] = writeOffset / fruBlockSize;
+ fruData.append_range(areaBytes);
+ writeOffset += areaBytes.size();
}
// Update the header checksum
- if (!updateHeadercksum(fruData))
+ if (!updateHeaderChecksum(fruData))
{
- lg2::debug("Failed to update FRU header checksum.");
+ lg2::error("failed to update header checksum");
return false;
}
+
return true;
}
@@ -1279,8 +1323,8 @@
areaData.clear();
// Set the version, length, and other fields
- areaData.push_back(1); // Version 1
- areaData.push_back(0); // Length (to be updated later)
+ areaData.push_back(fruVersion); // Version 1
+ areaData.push_back(0); // Length (to be updated later)
switch (fruArea)
{
@@ -1316,21 +1360,7 @@
fruBlockSize; // Calculate number of blocks needed
areaData.resize(numOfBlocks * fruBlockSize, 0); // Fill with zeros
areaData[1] = numOfBlocks; // Update length field
- updateAreacksum(areaData);
-
- return true;
-}
-
-bool getAreaIdx(const std::string& areaName, fruAreas& fruAreaToUpdate)
-{
- auto it = std::find(fruAreaNames.begin(), fruAreaNames.end(), areaName);
-
- if (it == fruAreaNames.end())
- {
- lg2::debug("Can't parse index for area name: {AREA}", "AREA", areaName);
- return false;
- }
- fruAreaToUpdate = static_cast<fruAreas>(it - fruAreaNames.begin());
+ updateAreaChecksum(areaData);
return true;
}
diff --git a/src/fru_device/fru_utils.hpp b/src/fru_device/fru_utils.hpp
index 0f8b032..2b32362 100644
--- a/src/fru_device/fru_utils.hpp
+++ b/src/fru_device/fru_utils.hpp
@@ -214,9 +214,7 @@
bool isFieldEditable(std::string_view fieldName);
-bool getAreaIdx(const std::string& areaName, fruAreas& fruAreaToUpdate);
-
-bool updateAreacksum(std::vector<uint8_t>& fruArea);
+bool updateAreaChecksum(std::vector<uint8_t>& fruArea);
bool disassembleFruData(std::vector<uint8_t>& fruData,
std::vector<std::vector<uint8_t>>& areasData);
diff --git a/test/test_fru-utils.cpp b/test/test_fru-utils.cpp
index a4c698c..031b4e4 100644
--- a/test/test_fru-utils.cpp
+++ b/test/test_fru-utils.cpp
@@ -484,37 +484,11 @@
EXPECT_FALSE(isFieldEditable("ABCD_BOARD"));
}
-TEST(GetAreaIdxTest, InvalidAreaReturnsInvalid)
-{
- // Validates that false is returned for an invalid area.
- const std::string invalidArea = "INVALID_AREA";
- fruAreas areaIdx = fruAreas::fruAreaInternal;
- EXPECT_FALSE(getAreaIdx(invalidArea, areaIdx));
-}
-
-TEST(GetAreaIdxTest, ValidAreaReturnsValid)
-{
- const std::vector<std::string> validAreas = {"INTERNAL", "CHASSIS", "BOARD",
- "PRODUCT", "MULTIRECORD"};
-
- const std::array<fruAreas, 5> expectedAreas = {
- fruAreas::fruAreaInternal, fruAreas::fruAreaChassis,
- fruAreas::fruAreaBoard, fruAreas::fruAreaProduct,
- fruAreas::fruAreaMultirecord};
-
- for (size_t i = 0; i < validAreas.size(); ++i)
- {
- fruAreas testArea = fruAreas::fruAreaInternal; // default init
- EXPECT_TRUE(getAreaIdx(validAreas[i], testArea));
- EXPECT_EQ(testArea, expectedAreas[i]);
- }
-}
-
TEST(UpdateAreaChecksumTest, EmptyArea)
{
// Validates that an empty area does not cause any issues.
std::vector<uint8_t> fruArea = {};
- EXPECT_FALSE(updateAreacksum(fruArea));
+ EXPECT_FALSE(updateAreaChecksum(fruArea));
}
TEST(UpdateAreaChecksumTest, ValidArea)
@@ -522,7 +496,7 @@
// Validates that a valid area updates the checksum correctly.
std::vector<uint8_t> fruArea = {0x01, 0x00, 0x01, 0x02,
0x03, 0x04, 0x00, 0x00};
- EXPECT_TRUE(updateAreacksum(fruArea));
+ EXPECT_TRUE(updateAreaChecksum(fruArea));
EXPECT_EQ(fruArea.back(), 0xf5);
}
@@ -531,7 +505,7 @@
// Validates that an invalid area does not update the checksum.
std::vector<uint8_t> fruArea = {0x01, 0x00, 0x01, 0x02, 0x03,
0x04, 0x00, 0x00, 0xAA};
- EXPECT_FALSE(updateAreacksum(fruArea));
+ EXPECT_FALSE(updateAreaChecksum(fruArea));
}
TEST(DisassembleFruDataTest, EmptyData)
@@ -566,7 +540,7 @@
};
std::vector<std::vector<uint8_t>> areasData;
- EXPECT_TRUE(disassembleFruData(fruData, areasData));
+ ASSERT_TRUE(disassembleFruData(fruData, areasData));
EXPECT_GT(areasData.size(), 1);
// Internal area is size is zero
@@ -606,6 +580,33 @@
areasData[static_cast<size_t>(fruAreas::fruAreaProduct)],
"PRODUCT_PRODUCT_NAME", "OpenBMC-test1"));
+ EXPECT_EQ(
+ areasData[static_cast<size_t>(fruAreas::fruAreaProduct)].size() % 8, 0);
+ EXPECT_EQ(areasData[static_cast<size_t>(fruAreas::fruAreaBoard)].size() % 8,
+ 0);
+
std::vector<uint8_t> assembledData;
EXPECT_TRUE(assembleFruData(assembledData, areasData));
+
+ boost::container::flat_map<std::string, std::string> result;
+ auto rescode = formatIPMIFRU(assembledData, result);
+ EXPECT_NE(rescode, resCodes::resErr);
+
+ EXPECT_EQ(result["PRODUCT_ASSET_TAG"], "123");
+ EXPECT_EQ(result["PRODUCT_PART_NUMBER"], "699-13809-0404-600");
+ EXPECT_EQ(result["PRODUCT_PRODUCT_NAME"], "OpenBMC-test1");
+ EXPECT_EQ(result["BOARD_INFO_AM1"], "01");
+ EXPECT_EQ(result["BOARD_INFO_AM2"], "MAC: 3C:6D:66:14:C8:7A");
+}
+
+TEST(ReassembleFruDataTest, UnalignedFails)
+{
+ std::vector<uint8_t> areaOne{0, 35};
+ std::vector<uint8_t> areaTwo{0, 32};
+ std::vector<std::vector<uint8_t>> areas;
+ areas.push_back(areaOne);
+ areas.push_back(areaTwo);
+
+ std::vector<uint8_t> fruData;
+ EXPECT_FALSE(assembleFruData(fruData, areas));
}