Support dimm loop detection for the AMD platform

Description:
 - Support detection of the DIMM loop pattern for the AMD platform

Design:
 - The ipmi handler netfn=0x38,cmd=0x33 handles BIC incoming
   postcode and call the detection function to check if this
   is the DIMM loop pattern of AMD platform and store those
   patterns in an in-memory space.

   Reference from Meta_BIOS_Requirement_Spec_v0.80
   For AMD platform, the POST code looping pattern format should be:
   (each group has 4 bytes)
   ●Group #0: [DDEE0000]
   ●Group #1: [DDEE] + Total Error Count
   ●Group #2: [DDEE] + Number of Error DIMM
   ●Group #3: [DDEE] + Dimm location
   ●Group #4: [DDEE] + major code
   ●Group #5: [DDEE] + minor code

Change-Id: I9598f71775936cea9a860a091bc74aefea8701f3
Signed-off-by: Delphine CC Chiu <Delphine_CC_Chiu@wiwynn.com>
diff --git a/include/biccommands.hpp b/include/biccommands.hpp
index dfce3a4..8466692 100644
--- a/include/biccommands.hpp
+++ b/include/biccommands.hpp
@@ -1,3 +1,5 @@
+#include <phosphor-logging/lg2.hpp>
+
 #include <cstdint>
 
 enum class fb_bic_cmds : uint8_t
@@ -8,6 +10,7 @@
     CMD_OEM_SET_HOST_POWER_STATE = 0x0C,
     CMD_OEM_GET_FLASH_SIZE = 0x19,
     CMD_OEM_CLEAR_CMOS = 0x25,
+    CMD_OEM_1S_4BYTE_POST_BUF = 0x33,
 };
 
 // Flash size response length
diff --git a/include/types.hpp b/include/types.hpp
new file mode 100644
index 0000000..f6c0076
--- /dev/null
+++ b/include/types.hpp
@@ -0,0 +1,24 @@
+#pragma once
+
+#include <iostream>
+#include <variant>
+#include <vector>
+
+namespace ipmi
+{
+
+constexpr size_t amdFourBytesPostCode = 4;
+
+namespace dimm
+{
+using hostId = size_t;
+struct dimmLoop
+{
+    size_t totalErrorCount;
+    bool gotPattern;  // It gets the whole pattern success.
+    bool startDetect; // Dimm loop detection to use. After getting the anchor
+                      // tag start to detected.
+    std::vector<std::vector<uint8_t>> postCode;
+};
+} // namespace dimm
+} // namespace ipmi
diff --git a/src/biccommands.cpp b/src/biccommands.cpp
index 49ae97f..ed3f6f8 100644
--- a/src/biccommands.cpp
+++ b/src/biccommands.cpp
@@ -20,6 +20,7 @@
 #include <ipmid/api-types.hpp>
 #include <ipmid/api.hpp>
 #include <phosphor-logging/log.hpp>
+#include <types.hpp>
 
 #include <iostream>
 #include <variant>
@@ -42,6 +43,13 @@
 int sendBicCmd(uint8_t, uint8_t, uint8_t, std::vector<uint8_t>&,
                std::vector<uint8_t>&);
 
+constexpr std::array<uint8_t, 2> amdDimmLoopPrefix = {0xDD, 0xEE};
+
+namespace dimm
+{
+std::unordered_map<hostId, dimmLoop> dimmLoops;
+} // namespace dimm
+
 //----------------------------------------------------------------------
 // ipmiOemBicHandler (IPMI/Section - ) (CMD_OEM_BIC_INFO)
 // This Function will handle BIC request for netfn=0x38 and cmd=1
@@ -71,10 +79,79 @@
                                  res->cc, res->payload);
 }
 
+void dimmLoopPatternDetection(size_t hostId, std::vector<uint8_t> data)
+{
+    if constexpr (postCodeSize != amdFourBytesPostCode)
+    {
+        return;
+    }
+
+    if (data.size() != amdFourBytesPostCode)
+    {
+        return;
+    }
+
+    /*
+    Reference from Meta_BIOS_Requirement_Spec_v0.80
+    For AMD platform, the POST code looping pattern format should be:
+    (each group has 4 bytes)
+    ●Group #0: [DDEE0000]
+    ●Group #1: [DDEE] + Total Error Count
+    ●Group #2: [DDEE] + Number of Error DIMM
+    ●Group #3: [DDEE] + Dimm location
+    ●Group #4: [DDEE] + major code
+    ●Group #5: [DDEE] + minor code
+    */
+    std::array<uint8_t, 2> prefix = {data[3], data[2]};
+
+    if (prefix != amdDimmLoopPrefix)
+    {
+        // Clear all the post code stored before.
+        if (dimm::dimmLoops[hostId].startDetect)
+        {
+            dimm::dimmLoops[hostId].totalErrorCount = 0;
+            dimm::dimmLoops[hostId].postCode.clear();
+
+            dimm::dimmLoops[hostId].startDetect = false;
+        }
+        return;
+    }
+
+    // Which means it already got the dimm loop, stop checking again.
+    if (dimm::dimmLoops[hostId].gotPattern)
+    {
+        return;
+    }
+
+    constexpr std::array<uint8_t, 4> anchorTag = {0x0, 0x0, 0xEE, 0xDD};
+    if (std::ranges::equal(anchorTag, data))
+    {
+        dimm::dimmLoops[hostId].startDetect = true;
+    }
+    if (dimm::dimmLoops[hostId].startDetect)
+    {
+        // The second one is error count
+        if (dimm::dimmLoops[hostId].postCode.size() % 6 == 1)
+        {
+            dimm::dimmLoops[hostId].totalErrorCount = (data[1] << 8) | data[0];
+        }
+
+        dimm::dimmLoops[hostId].postCode.push_back(data);
+
+        // Is the last element of dimmloop then stop to detect
+        if (dimm::dimmLoops[hostId].postCode.size() ==
+            (dimm::dimmLoops[hostId].totalErrorCount * 6))
+        {
+            // Gets whole pattern success
+            dimm::dimmLoops[hostId].gotPattern = true;
+        }
+    }
+}
+
 //----------------------------------------------------------------------
 // ipmiOemPostCodeHandler (CMD_OEM_BIC_POST_BUFFER_INFO)
 // This Function will handle BIC incomming postcode from multi-host for
-// netfn=0x38 and cmd=0x08 send the response back to the sender.
+// netfn=0x38 and cmd=0x08 or 0x33 send the response back to the sender.
 //----------------------------------------------------------------------
 
 ipmi::RspType<IanaType> ipmiOemPostCodeHandler(ipmi::Context::ptr ctx,
@@ -85,6 +162,14 @@
     // creating bus connection
     auto conn = getSdBus();
 
+    auto hostId = findHost(ctx->hostIdx);
+    if (!hostId)
+    {
+        lg2::error("Invalid Host Id received");
+        return ipmi::responseInvalidCommand();
+    }
+    dimmLoopPatternDetection(*hostId, data);
+
     using postcode_t = std::tuple<uint64_t, std::vector<uint8_t>>;
 
     std::string dbusObjStr = dbusObj + std::to_string((ctx->hostIdx + 1));
@@ -317,6 +402,10 @@
         static_cast<Cmd>(fb_bic_cmds::CMD_OEM_SEND_POST_BUFFER_TO_BMC),
         ipmi::Privilege::User, ipmiOemPostCodeHandler);
     ipmi::registerHandler(
+        ipmi::prioOpenBmcBase, ipmi::netFnOemFive,
+        static_cast<Cmd>(fb_bic_cmds::CMD_OEM_1S_4BYTE_POST_BUF),
+        ipmi::Privilege::User, ipmiOemPostCodeHandler);
+    ipmi::registerHandler(
         ipmi::prioOemBase, ipmi::netFnOemFive,
         static_cast<Cmd>(fb_bic_cmds::CMD_OEM_GET_BIC_GPIO_STATE),
         ipmi::Privilege::User, ipmiOemGetBicGpioState);