phosphor-bmc-code-mgmt: revise JedFileParser to support LCMXO3 series
checking UFM and ensure checksum calculated correctly.
sync code from:
[1] https://github.com/facebook/openbmc/commit/7c656105a7e36b76adb1a1b5a3eed98b0ada43a2
Tested on YV5 POC:
'''
ken@ken-All-Series:~$ curl --silent $creds https://$bmc/redfish/v1/UpdateService/FirmwareInventory/
{
"@odata.id": "/redfish/v1/UpdateService/FirmwareInventory",
"@odata.type": "#SoftwareInventoryCollection.SoftwareInventoryCollection",
"Members": [
{
"@odata.id": "/redfish/v1/UpdateService/FirmwareInventory/6116e97d"
},
{
"@odata.id": "/redfish/v1/UpdateService/FirmwareInventory/YV5_SCM_CPLD_9224"
}
],
"Members@odata.count": 2,
"Name": "Software Inventory Collection"
}
ken@ken-All-Series:~$curl -k ${creds} -H "Content-Type:multipart/form-data" -X POST -F UpdateParameters="{\"Targets\":[\"/redfish/v1/UpdateService/FirmwareInventory/YV5_SCM_CPLD_9224\"],\"@Redfish.OperationApplyTime\":\"Immediate\"};type=application/json" -F "UpdateFile=@${fwpath};type=application/octet-stream" https://${bmc}/redfish/v1/UpdateService/update-multipart
{
"@odata.id": "/redfish/v1/TaskService/Tasks/0",
"@odata.type": "#Task.v1_4_3.Task",
"HidePayload": false,
"Id": "0",
"Messages": [
{
"@odata.type": "#Message.v1_1_1.Message",
"Message": "The task with Id '0' has started.",
"MessageArgs": [
"0"
],
"MessageId": "TaskEvent.1.0.TaskStarted",
"MessageSeverity": "OK",
"Resolution": "None."
}
],
"Name": "Task 0",
"Payload": {
"HttpHeaders": [],
"HttpOperation": "POST",
"TargetUri": "/redfish/v1/UpdateService/update-multipart"
},
"PercentComplete": 0,
"StartTime": "2025-07-09T07:09:41+00:00",
"TaskMonitor": "/redfish/v1/TaskService/TaskMonitors/0",
"TaskState": "Running",
"TaskStatus": "OK"
}
'''
Change-Id: I4cb953e25e9c09eee2fa511c787b57bfa787e78e
Signed-off-by: Ken Chen <Ken.Chen@quantatw.com>
diff --git a/cpld/lattice/lattice.cpp b/cpld/lattice/lattice.cpp
index 87ae2f0..fea4b36 100644
--- a/cpld/lattice/lattice.cpp
+++ b/cpld/lattice/lattice.cpp
@@ -5,6 +5,7 @@
#include <algorithm>
#include <fstream>
#include <map>
+#include <numeric>
#include <thread>
#include <vector>
@@ -15,17 +16,19 @@
static constexpr std::string_view tagQF = "QF";
static constexpr std::string_view tagUH = "UH";
static constexpr std::string_view tagCFStart = "L000";
+static constexpr std::string_view tagData = "NOTE TAG DATA";
+static constexpr std::string_view tagUFM = "NOTE USER MEMORY DATA";
static constexpr std::string_view tagChecksum = "C";
static constexpr std::string_view tagUserCode = "NOTE User Electronic";
static constexpr std::string_view tagEbrInitData = "NOTE EBR_INIT DATA";
+static constexpr std::string_view tagEndConfig = "NOTE END CONFIG DATA";
+static constexpr std::string_view tagDevName = "NOTE DEVICE NAME";
constexpr uint8_t isOK = 0;
constexpr uint8_t isReady = 0;
constexpr uint8_t busyOrReadyBit = 4;
constexpr uint8_t failOrOKBit = 5;
-constexpr bool enableUpdateEbrInit = false;
-
enum cpldI2cCmd
{
commandEraseFlash = 0x0E,
@@ -49,20 +52,6 @@
return b;
}
-static int findNumberSize(const std::string& end, const std::string& start,
- const std::string& line)
-{
- auto pos1 = line.find(start);
- auto pos2 = line.find(end);
-
- if (pos1 == std::string::npos || pos2 == std::string::npos || pos1 >= pos2)
- {
- return false;
- }
-
- return static_cast<int>(pos2 - pos1 - 1);
-}
-
std::string uint32ToHexStr(uint32_t value)
{
std::ostringstream oss;
@@ -73,13 +62,16 @@
bool CpldLatticeManager::jedFileParser()
{
- bool cfStart = false;
- bool ufmStart = false; // for isLCMXO3D
- bool ufmPrepare = false;
- bool versionStart = false;
- bool checksumStart = false;
- bool ebrInitDataStart = false;
- int numberSize = 0;
+ enum class ParseState
+ {
+ none,
+ cfg,
+ endCfg,
+ ufm,
+ checksum,
+ userCode
+ };
+ ParseState state = ParseState::none;
if (image == nullptr || imageSize == 0)
{
@@ -88,214 +80,143 @@
return false;
}
- // Convert binary data to a string
std::string content(reinterpret_cast<const char*>(image), imageSize);
- // Use stringstream to simulate file reading
std::istringstream iss(content);
std::string line;
- // Parsing JED file
+ auto pushPage = [](std::string& line, std::vector<uint8_t>& sector) {
+ if (line[0] == '0' || line[0] == '1')
+ {
+ while (line.size() >= 8)
+ {
+ try
+ {
+ sector.push_back(static_cast<uint8_t>(
+ std::stoi(line.substr(0, 8), 0, 2)));
+ line.erase(0, 8);
+ }
+ catch (...)
+ {
+ break;
+ }
+ }
+ }
+ };
+
while (getline(iss, line))
{
- if (line.rfind(tagQF, 0) == 0)
+ if (!line.empty() && line.back() == '\r')
{
- numberSize = findNumberSize("*", "F", line);
- if (numberSize <= 0)
- {
- lg2::error("Error in parsing QF tag");
- return false;
- }
- static constexpr auto start = tagQF.length();
- fwInfo.QF = std::stoul(line.substr(start, numberSize));
-
- lg2::debug("QF Size = {QF}", "QF", fwInfo.QF);
+ line.pop_back();
}
- else if (line.rfind(tagCFStart, 0) == 0)
+ if (line.empty())
{
- cfStart = true;
- }
- else if (enableUpdateEbrInit && line.rfind(tagEbrInitData, 0) == 0)
- {
- ebrInitDataStart = true;
- }
- else if (ufmPrepare)
- {
- ufmPrepare = false;
- ufmStart = true;
continue;
}
- else if (line.rfind(tagUserCode, 0) == 0)
- {
- versionStart = true;
- }
- else if (line.rfind(tagChecksum, 0) == 0)
- {
- checksumStart = true;
- }
- if (line.rfind("NOTE DEVICE NAME:", 0) == 0)
+ if (line.starts_with(tagQF))
{
- lg2::error(line.c_str());
- if (line.find(chip) != std::string::npos)
+ ssize_t numberSize = static_cast<ssize_t>(line.find('*')) -
+ static_cast<ssize_t>(line.find('F')) - 1;
+ if (numberSize > 0)
{
- lg2::debug("[OK] The image device name match with chip name");
+ fwInfo.QF = std::stoul(line.substr(tagQF.length(), numberSize));
+ lg2::debug("QF Size = {QFSIZE}", "QFSIZE", fwInfo.QF);
}
- else
+ }
+ else if (line.starts_with(tagCFStart) ||
+ line.starts_with(tagEbrInitData))
+ {
+ state = ParseState::cfg;
+ continue;
+ }
+ else if (line.starts_with(tagEndConfig))
+ {
+ state = ParseState::endCfg;
+ continue;
+ }
+ else if (line.starts_with(tagUFM) || line.starts_with(tagData))
+ {
+ state = ParseState::ufm;
+ continue;
+ }
+ else if (line.starts_with(tagUserCode))
+ {
+ state = ParseState::userCode;
+ continue;
+ }
+ else if (line.starts_with(tagChecksum))
+ {
+ state = ParseState::checksum;
+ }
+ else if (line.starts_with(tagDevName))
+ {
+ lg2::debug("{DEVNAME}", "DEVNAME", line);
+ if (line.find(chip) == std::string::npos)
{
- lg2::debug("Abort update as image doesn't match the chip name");
- return false;
+ lg2::debug("STOP UPDATING: The image does not match the chip.");
+ return -1;
}
}
- if (cfStart)
+ switch (state)
{
- // L000
- if ((line.rfind(tagCFStart, 0)) && (line.size() != 1))
- {
- if ((line.rfind('0', 0) == 0) || (line.rfind('1', 0) == 0))
+ case ParseState::cfg:
+ pushPage(line, fwInfo.cfgData);
+ break;
+ case ParseState::endCfg:
+ pushPage(line, sumOnly);
+ break;
+ case ParseState::ufm:
+ pushPage(line, fwInfo.ufmData);
+ break;
+ case ParseState::checksum:
+ if (line.size() > 1)
{
- while (!line.empty())
- {
- auto binaryStr = line.substr(0, 8);
- try
- {
- fwInfo.cfgData.push_back(
- std::stoi(binaryStr, 0, 2));
- line.erase(0, 8);
- }
- catch (const std::invalid_argument& error)
- {
- break;
- }
- catch (...)
- {
- lg2::error("Error while parsing CF section");
- return false;
- }
- }
- }
- else
- {
- lg2::debug("CF size = {CF}", "CF", fwInfo.cfgData.size());
- cfStart = false;
- if (!ebrInitDataStart)
- {
- ufmPrepare = true;
- }
- }
- }
- }
- else if (enableUpdateEbrInit && ebrInitDataStart)
- {
- // NOTE EBR_INIT DATA
- if ((line.rfind(tagEbrInitData, 0)) && (line.size() != 1))
- {
- if ((line.rfind('L', 0)) && (line.size() != 1))
- {
- if ((line.rfind('0', 0) == 0) || (line.rfind('1', 0) == 0))
- {
- while (!line.empty())
- {
- auto binaryStr = line.substr(0, 8);
- try
- {
- fwInfo.cfgData.push_back(
- std::stoi(binaryStr, 0, 2));
- line.erase(0, 8);
- }
- catch (const std::invalid_argument& error)
- {
- break;
- }
- catch (...)
- {
- lg2::error("Error while parsing CF section");
- return false;
- }
- }
- }
- else
- {
- lg2::debug("CF size with EBR_INIT Data = {CF}", "CF",
- fwInfo.cfgData.size());
- ebrInitDataStart = false;
- ufmPrepare = true;
- }
- }
- }
- }
- else if ((checksumStart) && (line.size() != 1))
- {
- checksumStart = false;
- numberSize = findNumberSize("*", "C", line);
- if (numberSize <= 0)
- {
- lg2::error("Error in parsing checksum");
- return false;
- }
- static constexpr auto start = tagChecksum.length();
- std::istringstream iss(line.substr(start, numberSize));
- iss >> std::hex >> fwInfo.checksum;
-
- lg2::debug("Checksum = {CHECKSUM}", "CHECKSUM", fwInfo.checksum);
- }
- else if (versionStart)
- {
- if ((line.rfind(tagUserCode, 0)) && (line.size() != 1))
- {
- versionStart = false;
-
- if (line.rfind(tagUH, 0) == 0)
- {
- numberSize = findNumberSize("*", "H", line);
+ state = ParseState::none;
+ ssize_t numberSize =
+ static_cast<ssize_t>(line.find('*')) -
+ static_cast<ssize_t>(line.find('C')) - 1;
if (numberSize <= 0)
{
- lg2::error("Error in parsing version");
- return false;
+ lg2::debug("Error in parsing checksum");
+ return -1;
}
- static constexpr auto start = tagUH.length();
+ static constexpr auto start = tagChecksum.length();
std::istringstream iss(line.substr(start, numberSize));
+ iss >> std::hex >> fwInfo.checksum;
+ lg2::debug("Checksum = 0x{CHECKSUM}", "CHECKSUM",
+ fwInfo.checksum);
+ }
+ break;
+ case ParseState::userCode:
+ if (line.starts_with(tagUH))
+ {
+ state = ParseState::none;
+ ssize_t numberSize =
+ static_cast<ssize_t>(line.find('*')) -
+ static_cast<ssize_t>(line.find('H')) - 1;
+ if (numberSize <= 0)
+ {
+ lg2::debug("Error in parsing usercode");
+ return -1;
+ }
+ std::istringstream iss(
+ line.substr(tagUH.length(), numberSize));
iss >> std::hex >> fwInfo.version;
-
- lg2::debug("UserCode = {USERCODE}", "USERCODE",
+ lg2::debug("UserCode = 0x{USERCODE}", "USERCODE",
fwInfo.version);
}
- }
+ break;
+ default:
+ break;
}
- else if (ufmStart)
- {
- if ((line.rfind('L', 0)) && (line.size() != 1))
- {
- if ((line.rfind('0', 0) == 0) || (line.rfind('1', 0) == 0))
- {
- while (!line.empty())
- {
- auto binaryStr = line.substr(0, 8);
- try
- {
- fwInfo.ufmData.push_back(
- std::stoi(binaryStr, 0, 2));
- line.erase(0, 8);
- }
- catch (const std::invalid_argument& error)
- {
- break;
- }
- catch (...)
- {
- lg2::error("Error while parsing UFM section");
- return false;
- }
- }
- }
- else
- {
- lg2::debug("UFM size = {UFM}", "UFM",
- fwInfo.ufmData.size());
- ufmStart = false;
- }
- }
- }
+ }
+
+ lg2::debug("CFG Size = {CFGSIZE}", "CFGSIZE", fwInfo.cfgData.size());
+ if (!fwInfo.ufmData.empty())
+ {
+ lg2::debug("UFM size = {UFMSIZE}", "UFMSIZE", fwInfo.ufmData.size());
}
return true;
@@ -303,28 +224,34 @@
bool CpldLatticeManager::verifyChecksum()
{
- // Compute check sum
- unsigned int jedFileCheckSum = 0;
- for (unsigned i = 0; i < fwInfo.cfgData.size(); i++)
- {
- jedFileCheckSum += reverse_bit(fwInfo.cfgData.at(i));
- }
- for (unsigned i = 0; i < fwInfo.ufmData.size(); i++)
- {
- jedFileCheckSum += reverse_bit(fwInfo.ufmData.at(i));
- }
- lg2::debug("jedFileCheckSum = {JEDFILECHECKSUM}", "JEDFILECHECKSUM",
- jedFileCheckSum);
- jedFileCheckSum = jedFileCheckSum & 0xffff;
+ uint32_t calculated = 0U;
+ auto addByte = [](uint32_t sum, uint8_t byte) {
+ return sum + reverse_bit(byte);
+ };
- if ((fwInfo.checksum != jedFileCheckSum) || (fwInfo.checksum == 0))
+ calculated = std::accumulate(fwInfo.cfgData.begin(), fwInfo.cfgData.end(),
+ calculated, addByte);
+ calculated =
+ std::accumulate(sumOnly.begin(), sumOnly.end(), calculated, addByte);
+ calculated = std::accumulate(fwInfo.ufmData.begin(), fwInfo.ufmData.end(),
+ calculated, addByte);
+
+ lg2::debug("Calculated checksum = {CALCULATED}", "CALCULATED", lg2::hex,
+ calculated);
+ lg2::debug("Checksum from JED file = {JEDFILECHECKSUM}", "JEDFILECHECKSUM",
+ lg2::hex, fwInfo.checksum);
+
+ if (fwInfo.checksum != (calculated & 0xFFFF))
{
- lg2::error("CPLD JED File CheckSum Error = {JEDFILECHECKSUM}",
- "JEDFILECHECKSUM", jedFileCheckSum);
+ lg2::error("JED file checksum compare fail, "
+ "Calculated checksum = {CALCULATED}, "
+ "Checksum from JED file = {JEDFILECHECKSUM}",
+ "CALCULATED", lg2::hex, calculated, "JEDFILECHECKSUM",
+ lg2::hex, fwInfo.checksum);
return false;
}
- lg2::debug("JED File Checksum compare success");
+ lg2::debug("JED file checksum compare success");
return true;
}
diff --git a/cpld/lattice/lattice.hpp b/cpld/lattice/lattice.hpp
index 3c06461..2c11952 100644
--- a/cpld/lattice/lattice.hpp
+++ b/cpld/lattice/lattice.hpp
@@ -18,12 +18,14 @@
{"LCMXO3LF-2100C", {0x61, 0x2b, 0xb0, 0x43}}},
{"LatticeLCMXO3LF_4300CFirmware",
{"LCMXO3LF-4300C", {0x61, 0x2b, 0xc0, 0x43}}},
+ {"LatticeLCMXO3D_4300Firmware", {"LCMXO3D-4300", {0x01, 0x2e, 0x20, 0x43}}},
+ {"LatticeLCMXO3D_9400Firmware", {"LCMXO3D-9400", {0x21, 0x2e, 0x30, 0x43}}},
};
struct cpldI2cInfo
{
- unsigned long int QF; // Quantity of Fuses
- unsigned int* UFM; // User Flash Memory
+ unsigned long int QF;
+ unsigned int* UFM;
unsigned int version;
unsigned int checksum;
std::vector<uint8_t> cfgData;
@@ -52,6 +54,7 @@
size_t imageSize;
std::string chip;
std::string target;
+ std::vector<uint8_t> sumOnly;
bool isLCMXO3D = false;
bool debugMode = false;
phosphor::i2c::I2C i2cInterface;