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;