Entity-Manager: Update product AssetTag inside Fru
Enable product assetTag property to writable in FruDevice.
Added update AssetTag method to write new assetTag.
Tested:
- Tested using "set-property" busctl call to FruDevice Service
and checked that PRODUCT_ASSET_TAG property is getting updated.
Change-Id: Iff492b6d53467246a16515c4a766fcc99f4d0f20
Signed-off-by: Joshi-Mansi <mansi.joshi@linux.intel.com>
diff --git a/src/FruDevice.cpp b/src/FruDevice.cpp
index 0ef0269..e1e95f8 100644
--- a/src/FruDevice.cpp
+++ b/src/FruDevice.cpp
@@ -89,7 +89,24 @@
auto systemBus = std::make_shared<sdbusplus::asio::connection>(io);
auto objServer = sdbusplus::asio::object_server(systemBus);
+static const std::vector<const char*> CHASSIS_FRU_AREAS = {
+ "PART_NUMBER", "SERIAL_NUMBER", "INFO_AM1", "INFO_AM2"};
+
+static const std::vector<const char*> BOARD_FRU_AREAS = {
+ "MANUFACTURER", "PRODUCT_NAME", "SERIAL_NUMBER", "PART_NUMBER",
+ "FRU_VERSION_ID", "INFO_AM1", "INFO_AM2"};
+
+static const std::vector<const char*> PRODUCT_FRU_AREAS = {
+ "MANUFACTURER", "PRODUCT_NAME", "PART_NUMBER", "VERSION", "SERIAL_NUMBER",
+ "ASSET_TAG", "FRU_VERSION_ID", "INFO_AM1", "INFO_AM2"};
+
bool validateHeader(const std::array<uint8_t, I2C_SMBUS_BLOCK_MAX>& blockData);
+bool updateFruProperty(
+ const std::string& assetTag, uint32_t bus, uint32_t address,
+ std::string propertyName,
+ boost::container::flat_map<
+ std::pair<size_t, size_t>,
+ std::shared_ptr<sdbusplus::asio::dbus_interface>>& dbusInterfaceMap);
using ReadBlockFunc =
std::function<int64_t(int flag, int file, uint16_t address, uint16_t offset,
@@ -912,18 +929,6 @@
bool formatFru(const std::vector<char>& fruBytes,
boost::container::flat_map<std::string, std::string>& result)
{
- static const std::vector<const char*> CHASSIS_FRU_AREAS = {
- "PART_NUMBER", "SERIAL_NUMBER", "INFO_AM1", "INFO_AM2"};
-
- static const std::vector<const char*> BOARD_FRU_AREAS = {
- "MANUFACTURER", "PRODUCT_NAME", "SERIAL_NUMBER", "PART_NUMBER",
- "FRU_VERSION_ID", "INFO_AM1", "INFO_AM2"};
-
- static const std::vector<const char*> PRODUCT_FRU_AREAS = {
- "MANUFACTURER", "PRODUCT_NAME", "PART_NUMBER",
- "VERSION", "SERIAL_NUMBER", "ASSET_TAG",
- "FRU_VERSION_ID", "INFO_AM1", "INFO_AM2"};
-
if (fruBytes.size() <= 8)
{
return false;
@@ -1151,13 +1156,38 @@
std::regex_replace(property.second.begin(), property.second.begin(),
property.second.end(), NON_ASCII_REGEX, "_");
- if (property.second.empty())
+ if (property.second.empty() && property.first != "PRODUCT_ASSET_TAG")
{
continue;
}
std::string key =
std::regex_replace(property.first, NON_ASCII_REGEX, "_");
- if (!iface->register_property(key, property.second + '\0'))
+
+ if (property.first == "PRODUCT_ASSET_TAG")
+ {
+ std::string propertyName = property.first;
+ iface->register_property(
+ key, property.second + '\0',
+ [bus, address, propertyName,
+ &dbusInterfaceMap](const std::string& req, std::string& resp) {
+ if (strcmp(req.c_str(), resp.c_str()))
+ {
+ // call the method which will update
+ if (updateFruProperty(req, bus, address, propertyName,
+ dbusInterfaceMap))
+ {
+ resp = req;
+ }
+ else
+ {
+ throw std::invalid_argument(
+ "Fru property update failed.");
+ }
+ }
+ return 1;
+ });
+ }
+ else if (!iface->register_property(key, property.second + '\0'))
{
std::cerr << "illegal key: " << key << "\n";
}
@@ -1427,6 +1457,240 @@
});
}
+// Calculate new checksum for fru info area
+size_t calculateChecksum(std::vector<uint8_t>& fruAreaData)
+{
+ constexpr int checksumMod = 256;
+ constexpr uint8_t modVal = 0xFF;
+ int sum = std::accumulate(fruAreaData.begin(), fruAreaData.end(), 0);
+ return (checksumMod - sum) & modVal;
+}
+
+// Update new fru area length &
+// Update checksum at new checksum location
+static void updateFruAreaLenAndChecksum(std::vector<uint8_t>& fruData,
+ size_t fruAreaStartOffset,
+ size_t fruAreaNoMoreFieldOffset)
+{
+ constexpr size_t fruAreaMultipleBytes = 8;
+ size_t traverseFruAreaIndex = fruAreaNoMoreFieldOffset - fruAreaStartOffset;
+
+ // fill zeros for any remaining unused space
+ std::fill(fruData.begin() + fruAreaNoMoreFieldOffset, fruData.end(), 0);
+
+ size_t mod = traverseFruAreaIndex % fruAreaMultipleBytes;
+ size_t checksumLoc;
+ if (!mod)
+ {
+ traverseFruAreaIndex += (fruAreaMultipleBytes);
+ checksumLoc = fruAreaNoMoreFieldOffset + (fruAreaMultipleBytes - 1);
+ }
+ else
+ {
+ traverseFruAreaIndex += (fruAreaMultipleBytes - mod);
+ checksumLoc =
+ fruAreaNoMoreFieldOffset + (fruAreaMultipleBytes - mod - 1);
+ }
+
+ size_t newFruAreaLen = (traverseFruAreaIndex / fruAreaMultipleBytes) +
+ ((traverseFruAreaIndex % fruAreaMultipleBytes) != 0);
+ size_t fruAreaLengthLoc = fruAreaStartOffset + 1;
+ fruData[fruAreaLengthLoc] = static_cast<uint8_t>(newFruAreaLen);
+
+ // Calculate new checksum
+ std::vector<uint8_t> finalFruData;
+ std::copy_n(fruData.begin() + fruAreaStartOffset,
+ checksumLoc - fruAreaStartOffset,
+ std::back_inserter(finalFruData));
+
+ size_t checksumVal = calculateChecksum(finalFruData);
+
+ fruData[checksumLoc] = static_cast<uint8_t>(checksumVal);
+}
+
+static size_t getTypeLength(uint8_t fruFieldTypeLenValue)
+{
+ constexpr uint8_t typeLenMask = 0x3F;
+ return fruFieldTypeLenValue & typeLenMask;
+}
+
+// Details with example of Asset Tag Update
+// To find location of Product Info Area asset tag as per FRU specification
+// 1. Find product Info area starting offset (*8 - as header will be in
+// multiple of 8 bytes).
+// 2. Skip 3 bytes of product info area (like format version, area length,
+// and language code).
+// 3. Traverse manufacturer name, product name, product version, & product
+// serial number, by reading type/length code to reach the Asset Tag.
+// 4. Update the Asset Tag, reposition the product Info area in multiple of
+// 8 bytes. Update the Product area length and checksum.
+
+bool updateFruProperty(
+ const std::string& updatePropertyReq, uint32_t bus, uint32_t address,
+ std::string propertyName,
+ boost::container::flat_map<
+ std::pair<size_t, size_t>,
+ std::shared_ptr<sdbusplus::asio::dbus_interface>>& dbusInterfaceMap)
+{
+ size_t updatePropertyReqLen = updatePropertyReq.length();
+ if (updatePropertyReqLen == 1 || updatePropertyReqLen > 63)
+ {
+ std::cerr
+ << "Fru field data cannot be of 1 char or more than 63 chars. "
+ "Invalid Length "
+ << updatePropertyReqLen << "\n";
+ return false;
+ }
+
+ std::vector<uint8_t> fruData;
+ try
+ {
+ fruData = getFruInfo(static_cast<uint8_t>(bus),
+ static_cast<uint8_t>(address));
+ }
+ catch (std::invalid_argument& e)
+ {
+ std::cerr << "Failure getting Fru Info" << e.what() << "\n";
+ return false;
+ }
+
+ if (fruData.empty())
+ {
+ return false;
+ }
+
+ const std::vector<const char*>* fieldData;
+
+ constexpr size_t commonHeaderMultipleBytes = 8; // in multiple of 8 bytes
+ uint8_t fruAreaOffsetFieldValue = 1;
+ size_t offset = 0;
+
+ if (propertyName.find("CHASSIS") == 0)
+ {
+ constexpr size_t fruAreaOffsetField = 2;
+ fruAreaOffsetFieldValue = fruData[fruAreaOffsetField];
+
+ offset = 3; // chassis part number offset. Skip fixed first 3 bytes
+
+ fieldData = &CHASSIS_FRU_AREAS;
+ }
+ else if (propertyName.find("BOARD") == 0)
+ {
+ constexpr size_t fruAreaOffsetField = 3;
+ fruAreaOffsetFieldValue = fruData[fruAreaOffsetField];
+
+ offset = 6; // board manufacturer offset. Skip fixed first 6 bytes
+
+ fieldData = &BOARD_FRU_AREAS;
+ }
+ else if (propertyName.find("PRODUCT") == 0)
+ {
+ constexpr size_t fruAreaOffsetField = 4;
+ fruAreaOffsetFieldValue = fruData[fruAreaOffsetField];
+
+ offset = 3;
+ // Manufacturer name offset. Skip fixed first 3 product fru bytes i.e.
+ // version, area length and language code
+
+ fieldData = &PRODUCT_FRU_AREAS;
+ }
+ else
+ {
+ std::cerr << "ProperyName doesn't exist in Fru Area Vector \n";
+ return false;
+ }
+
+ size_t fruAreaStartOffset =
+ fruAreaOffsetFieldValue * commonHeaderMultipleBytes;
+ size_t fruDataIter = fruAreaStartOffset + offset;
+ size_t fruUpdateFieldLoc = 0;
+ size_t typeLength;
+ size_t skipToFruUpdateField = 0;
+
+ for (auto& field : *fieldData)
+ {
+ skipToFruUpdateField++;
+ if (propertyName.find(field) != std::string::npos)
+ {
+ break;
+ }
+ }
+
+ for (size_t i = 1; i < skipToFruUpdateField; i++)
+ {
+ typeLength = getTypeLength(fruData[fruDataIter]);
+ fruUpdateFieldLoc = fruDataIter + typeLength;
+ fruDataIter = ++fruUpdateFieldLoc;
+ }
+
+ // Push post update fru field bytes to a vector
+ typeLength = getTypeLength(fruData[fruUpdateFieldLoc]);
+ size_t postFruUpdateFieldLoc = fruUpdateFieldLoc + typeLength;
+ postFruUpdateFieldLoc++;
+ skipToFruUpdateField++;
+ fruDataIter = postFruUpdateFieldLoc;
+ size_t endLengthLoc = 0;
+ size_t fruFieldLoc = 0;
+ constexpr uint8_t endLength = 0xC1;
+ for (size_t i = fruDataIter; i < fruData.size(); i++)
+ {
+ typeLength = getTypeLength(fruData[fruDataIter]);
+ fruFieldLoc = fruDataIter + typeLength;
+ fruDataIter = ++fruFieldLoc;
+ if (fruData[fruDataIter] == endLength)
+ {
+ endLengthLoc = fruDataIter;
+ break;
+ }
+ }
+ endLengthLoc++;
+
+ std::vector<uint8_t> postUpdatedFruData;
+ std::copy_n(fruData.begin() + postFruUpdateFieldLoc,
+ endLengthLoc - postFruUpdateFieldLoc,
+ std::back_inserter(postUpdatedFruData));
+
+ // write new requested property field length and data
+ constexpr uint8_t newTypeLenMask = 0xC0;
+ fruData[fruUpdateFieldLoc] =
+ static_cast<uint8_t>(updatePropertyReqLen | newTypeLenMask);
+ fruUpdateFieldLoc++;
+ std::copy(updatePropertyReq.begin(), updatePropertyReq.end(),
+ fruData.begin() + fruUpdateFieldLoc);
+
+ // Copy remaining data to main fru area - post updated fru field vector
+ postFruUpdateFieldLoc = fruUpdateFieldLoc + updatePropertyReqLen;
+ size_t fruAreaEndOffset = postFruUpdateFieldLoc + postUpdatedFruData.size();
+ if (fruAreaEndOffset > fruData.size())
+ {
+ std::cerr << "Fru area offset: " << fruAreaEndOffset
+ << "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(postUpdatedFruData.begin(), postUpdatedFruData.end(),
+ fruData.begin() + postFruUpdateFieldLoc);
+
+ // Update final fru with new fru area length and checksum
+ updateFruAreaLenAndChecksum(fruData, fruAreaStartOffset, fruAreaEndOffset);
+
+ if (fruData.empty())
+ {
+ return false;
+ }
+
+ if (!writeFru(static_cast<uint8_t>(bus), static_cast<uint8_t>(address),
+ fruData))
+ {
+ return false;
+ }
+
+ // Rescan the bus so that GetRawFru dbus-call fetches updated values
+ rescanBusses(busMap, dbusInterfaceMap);
+ return true;
+}
+
int main()
{
auto devDir = fs::path("/dev/");