FruDevice: improve updateFRUProperty function
updateFRUProperty function is used to write ASSET field but it
pretended to be generic.
This change fixes number of issues like
* wrong size check
* no control of data overflow
* erase FRU Area next to updated area
* wrong handle of first/last fields in area
and some other
This change also implements functionality to resize FRU Area, but it is
disabled by default.
Tested: Tested by setting all properties as writable ad then trying to
change them using "set-property" busctl call to FruDevice.
Signed-off-by: Andrei Kartashev <a.kartashev@yadro.com>
Change-Id: Ic049bbf591878b4af344a352a2bec296e70cfb6e
diff --git a/meson_options.txt b/meson_options.txt
index a9aeeda..770451d 100644
--- a/meson_options.txt
+++ b/meson_options.txt
@@ -4,3 +4,6 @@
option(
'fru-device', type: 'boolean', description: 'Build fru-device.',
)
+option(
+ 'fru-device-resizefru', value : false, type: 'boolean', description: 'Allow FruDevice to resize FRU areas.',
+)
diff --git a/src/FruDevice.cpp b/src/FruDevice.cpp
index f4f904d..f502d63 100644
--- a/src/FruDevice.cpp
+++ b/src/FruDevice.cpp
@@ -70,6 +70,14 @@
const static constexpr char* I2C_DEV_LOCATION = "/dev";
+enum
+{
+ fruAreaInternal = 1,
+ fruAreaChassis,
+ fruAreaBoard,
+ fruAreaProduct,
+ fruAreaMultirecord
+};
static const std::array<std::string, 5> FRU_AREAS = {
"INTERNAL", "CHASSIS", "BOARD", "PRODUCT", "MULTIRECORD"};
const static std::regex NON_ASCII_REGEX("[^\x01-\x7f]");
@@ -1482,14 +1490,17 @@
// Update new fru area length &
// Update checksum at new checksum location
-static void updateFRUAreaLenAndChecksum(std::vector<uint8_t>& fruData,
- size_t fruAreaStart,
- size_t fruAreaEndOfFieldsOffset)
+// Return the offset of the area checksum byte
+static unsigned int updateFRUAreaLenAndChecksum(std::vector<uint8_t>& fruData,
+ size_t fruAreaStart,
+ size_t fruAreaEndOfFieldsOffset,
+ size_t fruAreaEndOffset)
{
size_t traverseFRUAreaIndex = fruAreaEndOfFieldsOffset - fruAreaStart;
// fill zeros for any remaining unused space
- std::fill(fruData.begin() + fruAreaEndOfFieldsOffset, fruData.end(), 0);
+ std::fill(fruData.begin() + fruAreaEndOfFieldsOffset,
+ fruData.begin() + fruAreaEndOffset, 0);
size_t mod = traverseFRUAreaIndex % fruBlockSize;
size_t checksumLoc;
@@ -1517,12 +1528,21 @@
size_t checksumVal = calculateChecksum(finalFRUData);
fruData[checksumLoc] = static_cast<uint8_t>(checksumVal);
+ return checksumLoc;
}
-static size_t getFieldLength(uint8_t fruFieldTypeLenValue)
+static ssize_t getFieldLength(uint8_t fruFieldTypeLenValue)
{
constexpr uint8_t typeLenMask = 0x3F;
- return fruFieldTypeLenValue & typeLenMask;
+ constexpr uint8_t endOfFields = 0xC1;
+ if (fruFieldTypeLenValue == endOfFields)
+ {
+ return -1;
+ }
+ else
+ {
+ return fruFieldTypeLenValue & typeLenMask;
+ }
}
// Details with example of Asset Tag Update
@@ -1572,7 +1592,7 @@
const std::vector<std::string>* fruAreaFieldNames;
- uint8_t fruAreaOffsetFieldValue = 1;
+ uint8_t fruAreaOffsetFieldValue = 0;
size_t offset = 0;
if (propertyName.find("CHASSIS") == 0)
@@ -1606,15 +1626,23 @@
}
else
{
- std::cerr << "ProperyName doesn't exist in FRU Area Vector \n";
+ std::cerr << "Don't know how to handle property " << propertyName
+ << " \n";
+ return false;
+ }
+ if (fruAreaOffsetFieldValue == 0)
+ {
+ std::cerr << "FRU Area for " << propertyName << " not present \n";
return false;
}
size_t fruAreaStart = fruAreaOffsetFieldValue * fruBlockSize;
+ size_t fruAreaSize = fruData[fruAreaStart + 1] * fruBlockSize;
+ size_t fruAreaEnd = fruAreaStart + fruAreaSize;
size_t fruDataIter = fruAreaStart + offset;
- size_t fruUpdateFieldLoc = 0;
- size_t fieldLength;
+ size_t fruUpdateFieldLoc = fruDataIter;
size_t skipToFRUUpdateField = 0;
+ ssize_t fieldLength;
for (auto& field : *fruAreaFieldNames)
{
@@ -1628,37 +1656,85 @@
for (size_t i = 1; i < skipToFRUUpdateField; i++)
{
fieldLength = getFieldLength(fruData[fruDataIter]);
- fruUpdateFieldLoc = fruDataIter + fieldLength;
- fruDataIter = ++fruUpdateFieldLoc;
+ if (fieldLength < 0)
+ {
+ break;
+ }
+ fruDataIter += 1 + fieldLength;
}
+ fruUpdateFieldLoc = fruDataIter;
// Push post update fru field bytes to a vector
fieldLength = getFieldLength(fruData[fruUpdateFieldLoc]);
- size_t restFRUFieldsLoc = fruUpdateFieldLoc + fieldLength;
- restFRUFieldsLoc++;
- skipToFRUUpdateField++;
- fruDataIter = restFRUFieldsLoc;
- size_t endOfFieldsLoc = 0;
- size_t fruFieldLoc = 0;
- constexpr uint8_t endOfFields = 0xC1;
- for (size_t i = fruDataIter; i < fruData.size(); i++)
+ if (fieldLength < 0)
{
- fieldLength = getFieldLength(fruData[fruDataIter]);
- fruFieldLoc = fruDataIter + fieldLength;
- fruDataIter = ++fruFieldLoc;
- if (fruData[fruDataIter] == endOfFields)
+ std::cerr << "Property " << propertyName << " not present \n";
+ return false;
+ }
+ fruDataIter += 1 + fieldLength;
+ size_t restFRUFieldsLoc = fruDataIter;
+ size_t endOfFieldsLoc = 0;
+ while ((fieldLength = getFieldLength(fruData[fruDataIter])) >= 0)
+ {
+ if (fruDataIter >= (fruAreaStart + fruAreaSize))
{
- endOfFieldsLoc = fruDataIter;
+ fruDataIter = fruAreaStart + fruAreaSize;
break;
}
+ fruDataIter += 1 + fieldLength;
}
- endOfFieldsLoc++;
+ endOfFieldsLoc = fruDataIter;
std::vector<uint8_t> restFRUAreaFieldsData;
std::copy_n(fruData.begin() + restFRUFieldsLoc,
- endOfFieldsLoc - restFRUFieldsLoc,
+ endOfFieldsLoc - restFRUFieldsLoc + 1,
std::back_inserter(restFRUAreaFieldsData));
+ // Push post update fru areas if any
+ unsigned int nextFRUAreaLoc = 0;
+ for (unsigned int nextFRUAreaOffsetField = fruAreaInternal;
+ nextFRUAreaOffsetField <= fruAreaMultirecord; nextFRUAreaOffsetField++)
+ {
+ unsigned int fruAreaLoc =
+ fruData[nextFRUAreaOffsetField] * fruBlockSize;
+ if ((fruAreaLoc > endOfFieldsLoc) &&
+ ((nextFRUAreaLoc == 0) || (fruAreaLoc < nextFRUAreaLoc)))
+ {
+ nextFRUAreaLoc = fruAreaLoc;
+ }
+ }
+ std::vector<uint8_t> restFRUAreasData;
+ if (nextFRUAreaLoc)
+ {
+ std::copy_n(fruData.begin() + nextFRUAreaLoc,
+ fruData.size() - nextFRUAreaLoc,
+ std::back_inserter(restFRUAreasData));
+ }
+
+ // check FRU area size
+ size_t fruAreaDataSize =
+ ((fruUpdateFieldLoc - fruAreaStart + 1) + restFRUAreaFieldsData.size());
+ size_t fruAreaAvailableSize = fruAreaSize - fruAreaDataSize;
+ if ((updatePropertyReqLen + 1) > fruAreaAvailableSize)
+ {
+
+#ifdef ENABLE_FRU_AREA_RESIZE
+ size_t newFRUAreaSize = fruAreaDataSize + updatePropertyReqLen + 1;
+ // round size to 8-byte blocks
+ newFRUAreaSize =
+ ((newFRUAreaSize - 1) / fruBlockSize + 1) * fruBlockSize;
+ size_t newFRUDataSize = fruData.size() + newFRUAreaSize - fruAreaSize;
+ fruData.resize(newFRUDataSize);
+ fruAreaSize = newFRUAreaSize;
+ fruAreaEnd = fruAreaStart + fruAreaSize;
+#else
+ std::cerr << "FRU field length: " << updatePropertyReqLen + 1
+ << " should not be greater than available FRU area size: "
+ << fruAreaAvailableSize << "\n";
+ return false;
+#endif // ENABLE_FRU_AREA_RESIZE
+ }
+
// write new requested property field length and data
constexpr uint8_t newTypeLenMask = 0xC0;
fruData[fruUpdateFieldLoc] =
@@ -1670,20 +1746,51 @@
// Copy remaining data to main fru area - post updated fru field vector
restFRUFieldsLoc = fruUpdateFieldLoc + updatePropertyReqLen;
size_t fruAreaDataEnd = restFRUFieldsLoc + restFRUAreaFieldsData.size();
- if (fruAreaDataEnd > fruData.size())
- {
- std::cerr << "FRU area offset: " << fruAreaDataEnd
- << "should not be greater than FRU data size: "
- << fruData.size() << std::endl;
- throw std::invalid_argument(
- "FRU area offset should not be greater than FRU data size");
- }
std::copy(restFRUAreaFieldsData.begin(), restFRUAreaFieldsData.end(),
fruData.begin() + restFRUFieldsLoc);
// Update final fru with new fru area length and checksum
- updateFRUAreaLenAndChecksum(fruData, fruAreaStart, fruAreaDataEnd);
+ unsigned int nextFRUAreaNewLoc = updateFRUAreaLenAndChecksum(
+ fruData, fruAreaStart, fruAreaDataEnd, fruAreaEnd);
+#ifdef ENABLE_FRU_AREA_RESIZE
+ ++nextFRUAreaNewLoc;
+ ssize_t nextFRUAreaOffsetDiff =
+ (nextFRUAreaNewLoc - nextFRUAreaLoc) / fruBlockSize;
+ // Append rest FRU Areas if size changed and there were other sections after
+ // updated one
+ if (nextFRUAreaOffsetDiff && nextFRUAreaLoc)
+ {
+ std::copy(restFRUAreasData.begin(), restFRUAreasData.end(),
+ fruData.begin() + nextFRUAreaNewLoc);
+ // Update Common Header
+ for (size_t fruAreaOffsetField = FRU_AREA_INTERNAL;
+ fruAreaOffsetField <= FRU_AREA_MULTIRECORD; fruAreaOffsetField++)
+ {
+ size_t curFRUAreaOffset = fruData[fruAreaOffsetField];
+ if (curFRUAreaOffset > fruAreaOffsetFieldValue)
+ {
+ fruData[fruAreaOffsetField] = static_cast<int8_t>(
+ curFRUAreaOffset + nextFRUAreaOffsetDiff);
+ }
+ }
+ // Calculate new checksum
+ std::vector<uint8_t> headerFRUData;
+ std::copy_n(fruData.begin(), 7, std::back_inserter(headerFRUData));
+ size_t checksumVal = calculateChecksum(headerFRUData);
+ fruData[7] = static_cast<uint8_t>(checksumVal);
+ // fill zeros if FRU Area size decreased
+ if (nextFRUAreaOffsetDiff < 0)
+ {
+ std::fill(fruData.begin() + nextFRUAreaNewLoc +
+ restFRUAreasData.size(),
+ fruData.end(), 0);
+ }
+ }
+#else
+ // this is to avoid "unused variable" warning
+ (void)nextFRUAreaNewLoc;
+#endif // ENABLE_FRU_AREA_RESIZE
if (fruData.empty())
{
return false;
diff --git a/src/meson.build b/src/meson.build
index fc85d9b..4966ed2 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -18,11 +18,15 @@
)
if get_option('fru-device')
+ cpp_args_fd = cpp_args
+ if get_option('fru-device-resizefru')
+ cpp_args_fd = cpp_args_fd + ['-DENABLE_FRU_AREA_RESIZE']
+ endif
executable(
'fru-device',
'FruDevice.cpp',
'Utils.cpp',
- cpp_args: cpp_args,
+ cpp_args: cpp_args_fd,
dependencies: [
boost,
i2c,