Added IPMI handler to support core count request

This change introduces a new OEM IPMI command (#30 - SysGetCoreCount)
to allow the BIOS or host to query the number of CPU cores as reported
by the BMC.

The core count is read from /run/cpu_config.json on the BMC. If the file
is not found or invalid, the command will return an error or 0 cores.

This is needed for the BIOS to dynamically adjust settings based on the
CPU configuration .

Tested:
{
  "cpu_core_count": 255
}
root@ddcgq3-nfd01:/# ipmitool raw 0x2e 0x32 0x79 0x2b 0x00 0x1a
 79 2b 00 1a ff 00

 79 2b 00 1a 00 01
{
  "cpu_core_count": 256
}

cat /run/cpu_config.json
{
  "cpu_core_count": 320
}
 79 2b 00 1a 40 01

Change-Id: I946d5ac918a2dae8a94e1b348446075e72007e3e
Signed-off-by: Vikram Gara <vikramgara+bmc@google.com>
diff --git a/handler.cpp b/handler.cpp
index 18c74a1..57a5e5a 100644
--- a/handler.cpp
+++ b/handler.cpp
@@ -12,6 +12,8 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+#include "config.h"
+
 #include "handler.hpp"
 
 #include "bm_config.h"
@@ -42,6 +44,7 @@
 #include <fstream>
 #include <iomanip>
 #include <map>
+#include <optional>
 #include <sstream>
 #include <string>
 #include <string_view>
@@ -807,8 +810,65 @@
         stdplus::print(stderr, "Failed to read: '{}'.\n", opath);
         throw IpmiException(::ipmi::ccUnspecifiedError);
     }
+
     return property;
 }
 
+std::optional<uint16_t> Handler::getCoreCount(const std::string& filePath) const
+{
+    std::error_code ec;
+    if (!this->getFs()->exists(filePath, ec))
+    {
+        log<level::INFO>("CPU config file not found",
+                         entry("PATH=%s", filePath.c_str()));
+        return std::nullopt;
+    }
+
+    std::ifstream ifs(filePath);
+    if (!ifs.is_open())
+    {
+        log<level::ERR>("Failed to open CPU config file",
+                        entry("PATH=%s", filePath.c_str()));
+        return std::nullopt;
+    }
+
+    try
+    {
+        Json data = Json::parse(ifs);
+        if (data.contains("cpu_core_count") &&
+            data["cpu_core_count"].is_number_integer())
+        {
+            int coreCountInt = data["cpu_core_count"].get<int>();
+            if (coreCountInt < 0 || coreCountInt > UINT16_MAX)
+            {
+                log<level::ERR>("Core count out of range for uint16_t",
+                                entry("PATH=%s", filePath.c_str()),
+                                entry("VALUE=%d", coreCountInt));
+                return std::nullopt;
+            }
+            return static_cast<uint16_t>(coreCountInt);
+        }
+        else
+        {
+            log<level::ERR>("Invalid format in CPU config file",
+                            entry("PATH=%s", filePath.c_str()));
+            return std::nullopt;
+        }
+    }
+    catch (Json::parse_error& e)
+    {
+        log<level::ERR>("Failed to parse CPU config file",
+                        entry("PATH=%s", filePath.c_str()),
+                        entry("WHAT=%s", e.what()));
+        return std::nullopt;
+    }
+    catch (...)
+    {
+        log<level::ERR>("Unknown error reading CPU config file",
+                        entry("PATH=%s", filePath.c_str()));
+        return std::nullopt;
+    }
+}
+
 } // namespace ipmi
 } // namespace google