Implement redfish LogService

This commit implements an optional LogService endpoint for debugging
CPUs over Redish.  It exposes both the ability to create a new log with
the LogImmediate action, as well as to pull a stored log using the
LogCollection schema.  This feature is disabled by default, and should
be enabled in the meta-x86 layer.

Change-Id: Ied1d5e263a0857e09cd9a24fddec9c11a4066cd2
Signed-off-by: Ed Tanous <ed.tanous@intel.com>
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 97b3aad..dcd174d 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -14,7 +14,16 @@
 option (BMCWEB_ENABLE_STATIC_HOSTING "Enable hosting of static files.
        For example, redfish schema and webui files" ON)
 
-# Insecure options.  Every option that starts with a BMCWEB_INSECURE flag should
+option (
+    BMCWEB_ENABLE_REDFISH_RAW_PECI "Enable PECI transactions through redfish"
+    OFF
+)
+option (
+    BMCWEB_ENABLE_REDFISH_CPU_LOG
+    "Enable CPU log service transactions through redfish" OFF
+)
+
+# Insecure options.Every option that starts with a BMCWEB_INSECURE flag should
 # not be enabled by default for any platform, unless the author fully
 # comprehends the implications of doing so.  In general, enabling these options
 # will cause security problems of varying degrees
@@ -194,7 +203,6 @@
     set_source_files_properties (
         ${CMAKE_BINARY_DIR}/include/bmcweb/blns.hpp PROPERTIES GENERATED TRUE
     )
-
     enable_testing ()
 
     add_executable (webtest ${SRC_FILES} ${UT_FILES})
diff --git a/redfish-core/include/redfish.hpp b/redfish-core/include/redfish.hpp
index 76bed8b..0998634 100644
--- a/redfish-core/include/redfish.hpp
+++ b/redfish-core/include/redfish.hpp
@@ -18,6 +18,7 @@
 #include "../lib/account_service.hpp"
 #include "../lib/chassis.hpp"
 #include "../lib/ethernet.hpp"
+#include "../lib/log_services.hpp"
 #include "../lib/managers.hpp"
 #include "../lib/network_protocol.hpp"
 #include "../lib/redfish_sessions.hpp"
@@ -66,6 +67,18 @@
         nodes.emplace_back(std::make_unique<SoftwareInventory>(app));
         nodes.emplace_back(
             std::make_unique<VlanNetworkInterfaceCollection>(app));
+        nodes.emplace_back(std::make_unique<LogServiceCollection>(app));
+
+#ifdef BMCWEB_ENABLE_REDFISH_CPU_LOG
+        nodes.emplace_back(std::make_unique<CpuLogService>(app));
+        nodes.emplace_back(std::make_unique<CpuLogEntryCollection>(app));
+        nodes.emplace_back(std::make_unique<CpuLogEntry>(app));
+        nodes.emplace_back(std::make_unique<ImmediateCpuLog>(app));
+#ifdef BMCWEB_ENABLE_REDFISH_RAW_PECI
+        nodes.emplace_back(std::make_unique<SendRawPeci>(app));
+#endif // BMCWEB_ENABLE_REDFISH_RAW_PECI
+#endif // BMCWEB_ENABLE_REDFISH_CPU_LOG
+
         nodes.emplace_back(std::make_unique<SystemsCollection>(app));
         nodes.emplace_back(std::make_unique<Systems>(app));
         nodes.emplace_back(std::make_unique<SystemActionsReset>(app));
diff --git a/redfish-core/lib/ethernet.hpp b/redfish-core/lib/ethernet.hpp
index 0c64279..85af636 100644
--- a/redfish-core/lib/ethernet.hpp
+++ b/redfish-core/lib/ethernet.hpp
@@ -368,6 +368,7 @@
         char *endPtr;
         long previousValue = 255;
         bool firstZeroInByteHit;
+
         for (const std::string &byte : bytesInMask)
         {
             if (byte.empty())
diff --git a/redfish-core/lib/log_services.hpp b/redfish-core/lib/log_services.hpp
new file mode 100644
index 0000000..cfe3ae5
--- /dev/null
+++ b/redfish-core/lib/log_services.hpp
@@ -0,0 +1,601 @@
+/*
+// Copyright (c) 2018 Intel Corporation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+*/
+#pragma once
+
+#include "node.hpp"
+
+#include <boost/container/flat_map.hpp>
+#include <experimental/filesystem>
+
+namespace redfish
+{
+
+constexpr char const *CPU_LOG_OBJECT = "com.intel.CpuDebugLog";
+constexpr char const *CPU_LOG_PATH = "/com/intel/CpuDebugLog";
+constexpr char const *CPU_LOG_IMMEDIATE_PATH =
+    "/com/intel/CpuDebugLog/Immediate";
+constexpr char const *CPU_LOG_INTERFACE = "com.intel.CpuDebugLog";
+constexpr char const *CPU_LOG_IMMEDIATE_INTERFACE =
+    "com.intel.CpuDebugLog.Immediate";
+constexpr char const *CPU_LOG_RAW_PECI_INTERFACE =
+    "com.intel.CpuDebugLog.SendRawPeci";
+
+namespace fs = std::experimental::filesystem;
+
+class LogServiceCollection : public Node
+{
+  public:
+    template <typename CrowApp>
+    LogServiceCollection(CrowApp &app) :
+        Node(app, "/redfish/v1/Managers/openbmc/LogServices/")
+    {
+        // Collections use static ID for SubRoute to add to its parent, but only
+        // load dynamic data so the duplicate static members don't get displayed
+        Node::json["@odata.id"] = "/redfish/v1/Managers/openbmc/LogServices";
+        entityPrivileges = {
+            {boost::beast::http::verb::get, {{"ConfigureComponents"}}},
+            {boost::beast::http::verb::head, {{"ConfigureComponents"}}},
+            {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
+            {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
+            {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
+            {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
+    }
+
+  private:
+    /**
+     * Functions triggers appropriate requests on DBus
+     */
+    void doGet(crow::Response &res, const crow::Request &req,
+               const std::vector<std::string> &params) override
+    {
+        // Collections don't include the static data added by SubRoute because
+        // it has a duplicate entry for members
+        res.jsonValue["@odata.type"] =
+            "#LogServiceCollection.LogServiceCollection";
+        res.jsonValue["@odata.context"] =
+            "/redfish/v1/"
+            "$metadata#LogServiceCollection.LogServiceCollection";
+        res.jsonValue["@odata.id"] = "/redfish/v1/Managers/openbmc/LogServices";
+        res.jsonValue["Name"] = "Open BMC Log Services Collection";
+        res.jsonValue["Description"] =
+            "Collection of LogServices for this Manager";
+        nlohmann::json &logserviceArray = res.jsonValue["Members"];
+        logserviceArray = nlohmann::json::array();
+#ifdef BMCWEB_ENABLE_REDFISH_CPU_LOG
+        logserviceArray.push_back(
+            {{"@odata.id", "/redfish/v1/Managers/openbmc/LogServices/CpuLog"}});
+#endif
+        res.jsonValue["Members@odata.count"] = logserviceArray.size();
+        res.end();
+    }
+};
+
+class CpuLogService : public Node
+{
+  public:
+    template <typename CrowApp>
+    CpuLogService(CrowApp &app) :
+        Node(app, "/redfish/v1/Managers/openbmc/LogServices/CpuLog")
+    {
+        // Set the id for SubRoute
+        Node::json["@odata.id"] =
+            "/redfish/v1/Managers/openbmc/LogServices/CpuLog";
+        entityPrivileges = {
+            {boost::beast::http::verb::get, {{"ConfigureComponents"}}},
+            {boost::beast::http::verb::head, {{"ConfigureComponents"}}},
+            {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
+            {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
+            {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
+            {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
+    }
+
+  private:
+    /**
+     * Functions triggers appropriate requests on DBus
+     */
+    void doGet(crow::Response &res, const crow::Request &req,
+               const std::vector<std::string> &params) override
+    {
+        // Copy over the static data to include the entries added by SubRoute
+        res.jsonValue = Node::json;
+        res.jsonValue["@odata.type"] = "#LogService.v1_1_0.LogService";
+        res.jsonValue["@odata.context"] = "/redfish/v1/"
+                                          "$metadata#LogService.LogService";
+        res.jsonValue["Name"] = "Open BMC CPU Log Service";
+        res.jsonValue["Description"] = "CPU Log Service";
+        res.jsonValue["Id"] = "CPU Log";
+        res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
+        res.jsonValue["MaxNumberOfRecords"] = 3;
+        res.jsonValue["Actions"] = {
+            {"Oem",
+             {{"#CpuLog.Immediate",
+               {{"target",
+                 "/redfish/v1/Managers/openbmc/LogServices/CpuLog/Actions/Oem/"
+                 "CpuLog.Immediate"}}}}}};
+
+#ifdef BMCWEB_ENABLE_REDFISH_RAW_PECI
+        res.jsonValue["Actions"]["Oem"].push_back(
+            {"#CpuLog.SendRawPeci",
+             {{"target",
+               "/redfish/v1/Managers/openbmc/LogServices/CpuLog/Actions/Oem/"
+               "CpuLog.SendRawPeci"}}});
+#endif
+        res.end();
+    }
+};
+
+class CpuLogEntryCollection : public Node
+{
+  public:
+    template <typename CrowApp>
+    CpuLogEntryCollection(CrowApp &app) :
+        Node(app, "/redfish/v1/Managers/openbmc/LogServices/CpuLog/Entries")
+    {
+        // Collections use static ID for SubRoute to add to its parent, but only
+        // load dynamic data so the duplicate static members don't get displayed
+        Node::json["@odata.id"] =
+            "/redfish/v1/Managers/openbmc/LogServices/CpuLog/Entries";
+        entityPrivileges = {
+            {boost::beast::http::verb::get, {{"ConfigureComponents"}}},
+            {boost::beast::http::verb::head, {{"ConfigureComponents"}}},
+            {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
+            {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
+            {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
+            {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
+    }
+
+  private:
+    /**
+     * Functions triggers appropriate requests on DBus
+     */
+    void doGet(crow::Response &res, const crow::Request &req,
+               const std::vector<std::string> &params) override
+    {
+        // Collections don't include the static data added by SubRoute because
+        // it has a duplicate entry for members
+        auto getLogEntriesCallback =
+            [&res](const boost::system::error_code ec,
+                   const std::vector<std::string> &resp) {
+                if (ec)
+                {
+                    if (ec.value() !=
+                        boost::system::errc::no_such_file_or_directory)
+                    {
+                        BMCWEB_LOG_DEBUG << "failed to get entries ec: "
+                                         << ec.message();
+                        res.result(
+                            boost::beast::http::status::internal_server_error);
+                        res.end();
+                        return;
+                    }
+                }
+                res.jsonValue["@odata.type"] =
+                    "#LogEntryCollection.LogEntryCollection";
+                res.jsonValue["@odata.context"] =
+                    "/redfish/v1/"
+                    "$metadata#LogEntryCollection.LogEntryCollection";
+                res.jsonValue["Name"] = "Open BMC CPU Log Entries";
+                res.jsonValue["Description"] = "Collection of CPU Log Entries";
+                nlohmann::json &logentry_array = res.jsonValue["Members"];
+                logentry_array = nlohmann::json::array();
+                for (const std::string &objpath : resp)
+                {
+                    // Don't list the immediate log
+                    if (objpath.compare(CPU_LOG_IMMEDIATE_PATH) == 0)
+                    {
+                        continue;
+                    }
+                    std::size_t last_pos = objpath.rfind("/");
+                    if (last_pos != std::string::npos)
+                    {
+                        logentry_array.push_back(
+                            {{"@odata.id", "/redfish/v1/Managers/openbmc/"
+                                           "LogServices/CpuLog/Entries/" +
+                                               objpath.substr(last_pos + 1)}});
+                    }
+                }
+                res.jsonValue["Members@odata.count"] = logentry_array.size();
+                res.end();
+            };
+        crow::connections::systemBus->async_method_call(
+            std::move(getLogEntriesCallback),
+            "xyz.openbmc_project.ObjectMapper",
+            "/xyz/openbmc_project/object_mapper",
+            "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", "", 0,
+            std::array<const char *, 1>{CPU_LOG_INTERFACE});
+    }
+};
+
+std::string getLogCreatedTime(const nlohmann::json &cpuLog)
+{
+    nlohmann::json::const_iterator metaIt = cpuLog.find("metadata");
+    if (metaIt != cpuLog.end())
+    {
+        nlohmann::json::const_iterator tsIt = metaIt->find("timestamp");
+        if (tsIt != metaIt->end())
+        {
+            const std::string *logTime = tsIt->get_ptr<const std::string *>();
+            if (logTime != nullptr)
+            {
+                return *logTime;
+            }
+        }
+    }
+    BMCWEB_LOG_DEBUG << "failed to find log timestamp";
+
+    return std::string();
+}
+
+class CpuLogEntry : public Node
+{
+  public:
+    CpuLogEntry(CrowApp &app) :
+        Node(app,
+             "/redfish/v1/Managers/openbmc/LogServices/CpuLog/Entries/<str>/",
+             std::string())
+    {
+        entityPrivileges = {
+            {boost::beast::http::verb::get, {{"ConfigureComponents"}}},
+            {boost::beast::http::verb::head, {{"ConfigureComponents"}}},
+            {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
+            {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
+            {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
+            {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
+    }
+
+  private:
+    void doGet(crow::Response &res, const crow::Request &req,
+               const std::vector<std::string> &params) override
+    {
+        if (params.size() != 1)
+        {
+            res.result(boost::beast::http::status::internal_server_error);
+            res.end();
+            return;
+        }
+        const uint8_t log_id = std::atoi(params[0].c_str());
+        auto getStoredLogCallback = [&res,
+                                     log_id](const boost::system::error_code ec,
+                                             const sdbusplus::message::variant<
+                                                 std::string> &resp) {
+            if (ec)
+            {
+                BMCWEB_LOG_DEBUG << "failed to get log ec: " << ec.message();
+                res.result(boost::beast::http::status::internal_server_error);
+                res.end();
+                return;
+            }
+            const std::string *log = mapbox::getPtr<const std::string>(resp);
+            if (log == nullptr)
+            {
+                res.result(boost::beast::http::status::internal_server_error);
+                res.end();
+                return;
+            }
+            nlohmann::json j = nlohmann::json::parse(*log, nullptr, false);
+            if (j.is_discarded())
+            {
+                res.result(boost::beast::http::status::internal_server_error);
+                res.end();
+                return;
+            }
+            std::string t = getLogCreatedTime(j);
+            res.jsonValue = {
+                {"@odata.type", "#LogEntry.v1_3_0.LogEntry"},
+                {"@odata.context", "/redfish/v1/$metadata#LogEntry.LogEntry"},
+                {"@odata.id",
+                 "/redfish/v1/Managers/openbmc/LogServices/CpuLog/Entries/" +
+                     std::to_string(log_id)},
+                {"Name", "CPU Debug Log"},
+                {"Id", log_id},
+                {"EntryType", "Oem"},
+                {"OemRecordFormat", "Intel CPU Log"},
+                {"Oem", {{"Intel", std::move(j)}}},
+                {"Created", std::move(t)}};
+            res.end();
+        };
+        crow::connections::systemBus->async_method_call(
+            std::move(getStoredLogCallback), CPU_LOG_OBJECT,
+            CPU_LOG_PATH + std::string("/") + std::to_string(log_id),
+            "org.freedesktop.DBus.Properties", "Get", CPU_LOG_INTERFACE, "Log");
+    }
+};
+
+class ImmediateCpuLog : public Node
+{
+  public:
+    ImmediateCpuLog(CrowApp &app) :
+        Node(app, "/redfish/v1/Managers/openbmc/LogServices/CpuLog/Actions/Oem/"
+                  "CpuLog.Immediate")
+    {
+        entityPrivileges = {
+            {boost::beast::http::verb::get, {{"ConfigureComponents"}}},
+            {boost::beast::http::verb::head, {{"ConfigureComponents"}}},
+            {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
+            {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
+            {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
+            {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
+    }
+
+  private:
+    void doPost(crow::Response &res, const crow::Request &req,
+                const std::vector<std::string> &params) override
+    {
+        static std::unique_ptr<sdbusplus::bus::match::match>
+            immediateLogMatcher;
+
+        // Only allow one Immediate Log request at a time
+        if (immediateLogMatcher != nullptr)
+        {
+            res.addHeader("Retry-After", "30");
+            res.result(boost::beast::http::status::service_unavailable);
+            messages::addMessageToJson(
+                res.jsonValue, messages::serviceTemporarilyUnavailable("30"),
+                "/CpuLog.Immediate");
+            res.end();
+            return;
+        }
+        // Make this static so it survives outside this method
+        static boost::asio::deadline_timer timeout(*req.ioService);
+
+        timeout.expires_from_now(boost::posix_time::seconds(30));
+        timeout.async_wait([&res](const boost::system::error_code &ec) {
+            immediateLogMatcher = nullptr;
+            if (ec)
+            {
+                // operation_aborted is expected if timer is canceled before
+                // completion.
+                if (ec != boost::asio::error::operation_aborted)
+                {
+                    BMCWEB_LOG_ERROR << "Async_wait failed " << ec;
+                }
+                return;
+            }
+            BMCWEB_LOG_ERROR << "Timed out waiting for immediate log";
+
+            res.result(boost::beast::http::status::internal_server_error);
+            res.end();
+        });
+
+        auto immediateLogMatcherCallback = [&res](
+                                               sdbusplus::message::message &m) {
+            BMCWEB_LOG_DEBUG << "Immediate log available match fired";
+            boost::system::error_code ec;
+            timeout.cancel(ec);
+            if (ec)
+            {
+                BMCWEB_LOG_ERROR << "error canceling timer " << ec;
+            }
+            sdbusplus::message::object_path obj_path;
+            boost::container::flat_map<
+                std::string,
+                boost::container::flat_map<
+                    std::string, sdbusplus::message::variant<std::string>>>
+                interfaces_added;
+            m.read(obj_path, interfaces_added);
+            const std::string *log = mapbox::getPtr<const std::string>(
+                interfaces_added[CPU_LOG_INTERFACE]["Log"]);
+            if (log == nullptr)
+            {
+                res.result(boost::beast::http::status::internal_server_error);
+                res.end();
+                // Careful with immediateLogMatcher.  It is a unique_ptr to the
+                // match object inside which this lambda is executing.  Once it
+                // is set to nullptr, the match object will be destroyed and the
+                // lambda will lose its context, including res, so it needs to
+                // be the last thing done.
+                immediateLogMatcher = nullptr;
+                return;
+            }
+            nlohmann::json j = nlohmann::json::parse(*log, nullptr, false);
+            if (j.is_discarded())
+            {
+                res.result(boost::beast::http::status::internal_server_error);
+                res.end();
+                // Careful with immediateLogMatcher.  It is a unique_ptr to the
+                // match object inside which this lambda is executing.  Once it
+                // is set to nullptr, the match object will be destroyed and the
+                // lambda will lose its context, including res, so it needs to
+                // be the last thing done.
+                immediateLogMatcher = nullptr;
+                return;
+            }
+            std::string t = getLogCreatedTime(j);
+            res.jsonValue = {
+                {"@odata.type", "#LogEntry.v1_3_0.LogEntry"},
+                {"@odata.context", "/redfish/v1/$metadata#LogEntry.LogEntry"},
+                {"Name", "CPU Debug Log"},
+                {"EntryType", "Oem"},
+                {"OemRecordFormat", "Intel CPU Log"},
+                {"Oem", {{"Intel", std::move(j)}}},
+                {"Created", std::move(t)}};
+            res.end();
+            // Careful with immediateLogMatcher.  It is a unique_ptr to the
+            // match object inside which this lambda is executing.  Once it is
+            // set to nullptr, the match object will be destroyed and the lambda
+            // will lose its context, including res, so it needs to be the last
+            // thing done.
+            immediateLogMatcher = nullptr;
+        };
+        immediateLogMatcher = std::make_unique<sdbusplus::bus::match::match>(
+            *crow::connections::systemBus,
+            sdbusplus::bus::match::rules::interfacesAdded() +
+                sdbusplus::bus::match::rules::argNpath(0,
+                                                       CPU_LOG_IMMEDIATE_PATH),
+            std::move(immediateLogMatcherCallback));
+
+        auto generateImmediateLogCallback =
+            [&res](const boost::system::error_code ec,
+                   const std::string &resp) {
+                if (ec)
+                {
+                    if (ec.value() ==
+                        boost::system::errc::operation_not_supported)
+                    {
+                        messages::addMessageToJson(
+                            res.jsonValue, messages::resourceInStandby(),
+                            "/CpuLog.Immediate");
+                        res.result(
+                            boost::beast::http::status::service_unavailable);
+                    }
+                    else
+                    {
+                        res.result(
+                            boost::beast::http::status::internal_server_error);
+                    }
+                    res.end();
+                    boost::system::error_code timeoutec;
+                    timeout.cancel(timeoutec);
+                    if (timeoutec)
+                    {
+                        BMCWEB_LOG_ERROR << "error canceling timer "
+                                         << timeoutec;
+                    }
+                    immediateLogMatcher = nullptr;
+                    return;
+                }
+            };
+        crow::connections::systemBus->async_method_call(
+            std::move(generateImmediateLogCallback), CPU_LOG_OBJECT,
+            CPU_LOG_PATH, CPU_LOG_IMMEDIATE_INTERFACE, "GenerateImmediateLog");
+    }
+};
+
+class SendRawPeci : public Node
+{
+  public:
+    SendRawPeci(CrowApp &app) :
+        Node(app, "/redfish/v1/Managers/openbmc/LogServices/CpuLog/Actions/Oem/"
+                  "CpuLog.SendRawPeci")
+    {
+        entityPrivileges = {
+            {boost::beast::http::verb::get, {{"ConfigureComponents"}}},
+            {boost::beast::http::verb::head, {{"ConfigureComponents"}}},
+            {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
+            {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
+            {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
+            {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
+    }
+
+  private:
+    void doPost(crow::Response &res, const crow::Request &req,
+                const std::vector<std::string> &params) override
+    {
+        // Get the Raw PECI command from the request
+        nlohmann::json rawPeciCmd;
+        if (!json_util::processJsonFromRequest(res, req, rawPeciCmd))
+        {
+            return;
+        }
+        // Get the Client Address from the request
+        nlohmann::json::const_iterator caIt = rawPeciCmd.find("ClientAddress");
+        if (caIt == rawPeciCmd.end())
+        {
+            messages::addMessageToJson(
+                res.jsonValue, messages::propertyMissing("ClientAddress"),
+                "/ClientAddress");
+            res.result(boost::beast::http::status::bad_request);
+            res.end();
+            return;
+        }
+        const uint64_t *ca = caIt->get_ptr<const uint64_t *>();
+        if (ca == nullptr)
+        {
+            messages::addMessageToJson(
+                res.jsonValue,
+                messages::propertyValueTypeError(caIt->dump(), "ClientAddress"),
+                "/ClientAddress");
+            res.result(boost::beast::http::status::bad_request);
+            res.end();
+            return;
+        }
+        // Get the Read Length from the request
+        const uint8_t clientAddress = static_cast<uint8_t>(*ca);
+        nlohmann::json::const_iterator rlIt = rawPeciCmd.find("ReadLength");
+        if (rlIt == rawPeciCmd.end())
+        {
+            messages::addMessageToJson(res.jsonValue,
+                                       messages::propertyMissing("ReadLength"),
+                                       "/ReadLength");
+            res.result(boost::beast::http::status::bad_request);
+            res.end();
+            return;
+        }
+        const uint64_t *rl = rlIt->get_ptr<const uint64_t *>();
+        if (rl == nullptr)
+        {
+            messages::addMessageToJson(
+                res.jsonValue,
+                messages::propertyValueTypeError(rlIt->dump(), "ReadLength"),
+                "/ReadLength");
+            res.result(boost::beast::http::status::bad_request);
+            res.end();
+            return;
+        }
+        // Get the PECI Command from the request
+        const uint32_t readLength = static_cast<uint32_t>(*rl);
+        nlohmann::json::const_iterator pcIt = rawPeciCmd.find("PECICommand");
+        if (pcIt == rawPeciCmd.end())
+        {
+            messages::addMessageToJson(res.jsonValue,
+                                       messages::propertyMissing("PECICommand"),
+                                       "/PECICommand");
+            res.result(boost::beast::http::status::bad_request);
+            res.end();
+            return;
+        }
+        std::vector<uint8_t> peciCommand;
+        for (auto pc : *pcIt)
+        {
+            const uint64_t *val = pc.get_ptr<const uint64_t *>();
+            if (val == nullptr)
+            {
+                messages::addMessageToJson(
+                    res.jsonValue,
+                    messages::propertyValueTypeError(
+                        pc.dump(),
+                        "PECICommand/" + std::to_string(peciCommand.size())),
+                    "/PECICommand");
+                res.result(boost::beast::http::status::bad_request);
+                res.end();
+                return;
+            }
+            peciCommand.push_back(static_cast<uint8_t>(*val));
+        }
+        // Callback to return the Raw PECI response
+        auto sendRawPeciCallback = [&res](const boost::system::error_code ec,
+                                          const std::vector<uint8_t> &resp) {
+            if (ec)
+            {
+                BMCWEB_LOG_DEBUG << "failed to send PECI command ec: "
+                                 << ec.message();
+                res.result(boost::beast::http::status::internal_server_error);
+                res.end();
+                return;
+            }
+            res.jsonValue = {{"Name", "PECI Command Response"},
+                             {"PECIResponse", resp}};
+            res.end();
+        };
+        // Call the SendRawPECI command with the provided data
+        crow::connections::systemBus->async_method_call(
+            std::move(sendRawPeciCallback), CPU_LOG_OBJECT, CPU_LOG_PATH,
+            CPU_LOG_RAW_PECI_INTERFACE, "SendRawPeci", clientAddress,
+            readLength, peciCommand);
+    }
+};
+
+} // namespace redfish
diff --git a/settings.hpp.in b/settings.hpp.in
index 897dca5..ecb17ed 100644
--- a/settings.hpp.in
+++ b/settings.hpp.in
@@ -8,3 +8,5 @@
 #cmakedefine BMCWEB_INSECURE_DISABLE_CSRF_PREVENTION
 #cmakedefine BMCWEB_INSECURE_DISABLE_SSL
 #cmakedefine BMCWEB_INSECURE_DISABLE_XSS_PREVENTION
+#cmakedefine BMCWEB_ENABLE_REDFISH_RAW_PECI
+#cmakedefine BMCWEB_ENABLE_REDFISH_CPU_LOG
diff --git a/static/redfish/v1/$metadata/index.xml b/static/redfish/v1/$metadata/index.xml
index c185baf..1d6e17f 100644
--- a/static/redfish/v1/$metadata/index.xml
+++ b/static/redfish/v1/$metadata/index.xml
@@ -44,6 +44,7 @@
         <edmx:Include Namespace="LogEntry"/>
         <edmx:Include Namespace="LogEntry.v1_0_3"/>
         <edmx:Include Namespace="LogEntry.v1_1_1"/>
+        <edmx:Include Namespace="LogEntry.v1_3_0"/>
     </edmx:Reference>
     <edmx:Reference Uri="/redfish/v1/schema/LogEntryCollection_v1.xml">
         <edmx:Include Namespace="LogEntryCollection"/>
@@ -51,6 +52,7 @@
     <edmx:Reference Uri="/redfish/v1/schema/LogService_v1.xml">
         <edmx:Include Namespace="LogService"/>
         <edmx:Include Namespace="LogService.v1_0_3"/>
+        <edmx:Include Namespace="LogService.v1_1_0"/>
     </edmx:Reference>
     <edmx:Reference Uri="/redfish/v1/schema/LogServiceCollection_v1.xml">
         <edmx:Include Namespace="LogServiceCollection"/>