lib/processor.hpp: Clean up

Clean up route generation and remove large lambda functions.
Also separate processor from processor operating configuration
functions into two files.

Change-Id: I2b72f8663105b6a89612e6e7bc1e7e0812b81eae
Signed-off-by: Christopher Meis <christopher.meis@9elements.com>
diff --git a/redfish-core/lib/processor.hpp b/redfish-core/lib/processor.hpp
index 7fe858f..3479909 100644
--- a/redfish-core/lib/processor.hpp
+++ b/redfish-core/lib/processor.hpp
@@ -889,111 +889,6 @@
 }
 
 /**
- * Request all the properties for the given D-Bus object and fill out the
- * related entries in the Redfish OperatingConfig response.
- *
- * @param[in,out]   asyncResp       Async HTTP response.
- * @param[in]       service     D-Bus service name to query.
- * @param[in]       objPath     D-Bus object to query.
- */
-inline void getOperatingConfigData(
-    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
-    const std::string& service, const std::string& objPath)
-{
-    dbus::utility::getAllProperties(
-        service, objPath,
-        "xyz.openbmc_project.Inventory.Item.Cpu.OperatingConfig",
-        [asyncResp](const boost::system::error_code& ec,
-                    const dbus::utility::DBusPropertiesMap& properties) {
-            if (ec)
-            {
-                BMCWEB_LOG_WARNING("D-Bus error: {}, {}", ec, ec.message());
-                messages::internalError(asyncResp->res);
-                return;
-            }
-
-            const size_t* availableCoreCount = nullptr;
-            const uint32_t* baseSpeed = nullptr;
-            const uint32_t* maxJunctionTemperature = nullptr;
-            const uint32_t* maxSpeed = nullptr;
-            const uint32_t* powerLimit = nullptr;
-            const TurboProfileProperty* turboProfile = nullptr;
-            const BaseSpeedPrioritySettingsProperty* baseSpeedPrioritySettings =
-                nullptr;
-
-            const bool success = sdbusplus::unpackPropertiesNoThrow(
-                dbus_utils::UnpackErrorPrinter(), properties,
-                "AvailableCoreCount", availableCoreCount, "BaseSpeed",
-                baseSpeed, "MaxJunctionTemperature", maxJunctionTemperature,
-                "MaxSpeed", maxSpeed, "PowerLimit", powerLimit, "TurboProfile",
-                turboProfile, "BaseSpeedPrioritySettings",
-                baseSpeedPrioritySettings);
-
-            if (!success)
-            {
-                messages::internalError(asyncResp->res);
-                return;
-            }
-
-            nlohmann::json& json = asyncResp->res.jsonValue;
-
-            if (availableCoreCount != nullptr)
-            {
-                json["TotalAvailableCoreCount"] = *availableCoreCount;
-            }
-
-            if (baseSpeed != nullptr)
-            {
-                json["BaseSpeedMHz"] = *baseSpeed;
-            }
-
-            if (maxJunctionTemperature != nullptr)
-            {
-                json["MaxJunctionTemperatureCelsius"] = *maxJunctionTemperature;
-            }
-
-            if (maxSpeed != nullptr)
-            {
-                json["MaxSpeedMHz"] = *maxSpeed;
-            }
-
-            if (powerLimit != nullptr)
-            {
-                json["TDPWatts"] = *powerLimit;
-            }
-
-            if (turboProfile != nullptr)
-            {
-                nlohmann::json& turboArray = json["TurboProfile"];
-                turboArray = nlohmann::json::array();
-                for (const auto& [turboSpeed, coreCount] : *turboProfile)
-                {
-                    nlohmann::json::object_t turbo;
-                    turbo["ActiveCoreCount"] = coreCount;
-                    turbo["MaxSpeedMHz"] = turboSpeed;
-                    turboArray.emplace_back(std::move(turbo));
-                }
-            }
-
-            if (baseSpeedPrioritySettings != nullptr)
-            {
-                nlohmann::json& baseSpeedArray =
-                    json["BaseSpeedPrioritySettings"];
-                baseSpeedArray = nlohmann::json::array();
-                for (const auto& [baseSpeedMhz, coreList] :
-                     *baseSpeedPrioritySettings)
-                {
-                    nlohmann::json::object_t speed;
-                    speed["CoreCount"] = coreList.size();
-                    speed["CoreIDs"] = coreList;
-                    speed["BaseSpeedMHz"] = baseSpeedMhz;
-                    baseSpeedArray.emplace_back(std::move(speed));
-                }
-            }
-        });
-}
-
-/**
  * Handle the PATCH operation of the AppliedOperatingConfig property. Do basic
  * validation of the input data, and then set the D-Bus property.
  *
@@ -1177,170 +1072,49 @@
                         appliedConfigUri, locationIndicatorActive));
 }
 
-inline void requestRoutesOperatingConfigCollection(App& app)
+inline void handleProcessorCollectionGet(
+    App& app, const crow::Request& req,
+    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
+    const std::string& systemName)
 {
-    BMCWEB_ROUTE(app,
-                 "/redfish/v1/Systems/<str>/Processors/<str>/OperatingConfigs/")
-        .privileges(redfish::privileges::getOperatingConfigCollection)
-        .methods(
-            boost::beast::http::verb::
-                get)([&app](const crow::Request& req,
-                            const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
-                            const std::string& systemName,
-                            const std::string& cpuName) {
-            if (!redfish::setUpRedfishRoute(app, req, asyncResp))
-            {
-                return;
-            }
+    if (!redfish::setUpRedfishRoute(app, req, asyncResp))
+    {
+        return;
+    }
+    if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM)
+    {
+        // Option currently returns no systems.  TBD
+        messages::resourceNotFound(asyncResp->res, "ComputerSystem",
+                                   systemName);
+        return;
+    }
 
-            if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM)
-            {
-                // Option currently returns no systems.  TBD
-                messages::resourceNotFound(asyncResp->res, "ComputerSystem",
-                                           systemName);
-                return;
-            }
+    if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME)
+    {
+        messages::resourceNotFound(asyncResp->res, "ComputerSystem",
+                                   systemName);
+        return;
+    }
 
-            if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME)
-            {
-                messages::resourceNotFound(asyncResp->res, "ComputerSystem",
-                                           systemName);
-                return;
-            }
-            asyncResp->res.jsonValue["@odata.type"] =
-                "#OperatingConfigCollection.OperatingConfigCollection";
-            asyncResp->res.jsonValue["@odata.id"] = boost::urls::format(
-                "/redfish/v1/Systems/{}/Processors/{}/OperatingConfigs",
-                BMCWEB_REDFISH_SYSTEM_URI_NAME, cpuName);
-            asyncResp->res.jsonValue["Name"] = "Operating Config Collection";
+    asyncResp->res.addHeader(
+        boost::beast::http::field::link,
+        "</redfish/v1/JsonSchemas/ProcessorCollection/ProcessorCollection.json>; rel=describedby");
 
-            // First find the matching CPU object so we know how to
-            // constrain our search for related Config objects.
-            const std::array<std::string_view, 1> interfaces = {
-                "xyz.openbmc_project.Control.Processor.CurrentOperatingConfig"};
-            dbus::utility::getSubTreePaths(
-                "/xyz/openbmc_project/inventory", 0, interfaces,
-                [asyncResp,
-                 cpuName](const boost::system::error_code& ec,
-                          const dbus::utility::MapperGetSubTreePathsResponse&
-                              objects) {
-                    if (ec)
-                    {
-                        BMCWEB_LOG_WARNING("D-Bus error: {}, {}", ec,
-                                           ec.message());
-                        messages::internalError(asyncResp->res);
-                        return;
-                    }
+    asyncResp->res.jsonValue["@odata.type"] =
+        "#ProcessorCollection.ProcessorCollection";
+    asyncResp->res.jsonValue["Name"] = "Processor Collection";
 
-                    for (const std::string& object : objects)
-                    {
-                        if (!object.ends_with(cpuName))
-                        {
-                            continue;
-                        }
+    asyncResp->res.jsonValue["@odata.id"] = std::format(
+        "/redfish/v1/Systems/{}/Processors", BMCWEB_REDFISH_SYSTEM_URI_NAME);
 
-                        // Not expected that there will be multiple matching
-                        // CPU objects, but if there are just use the first
-                        // one.
-
-                        // Use the common search routine to construct the
-                        // Collection of all Config objects under this CPU.
-                        constexpr std::array<std::string_view, 1> interface{
-                            "xyz.openbmc_project.Inventory.Item.Cpu.OperatingConfig"};
-                        collection_util::getCollectionMembers(
-                            asyncResp,
-                            boost::urls::format(
-                                "/redfish/v1/Systems/{}/Processors/{}/OperatingConfigs",
-                                BMCWEB_REDFISH_SYSTEM_URI_NAME, cpuName),
-                            interface, object);
-                        return;
-                    }
-                });
-        });
+    collection_util::getCollectionMembers(
+        asyncResp,
+        boost::urls::format("/redfish/v1/Systems/{}/Processors",
+                            BMCWEB_REDFISH_SYSTEM_URI_NAME),
+        processorInterfaces, "/xyz/openbmc_project/inventory");
 }
 
-inline void requestRoutesOperatingConfig(App& app)
-{
-    BMCWEB_ROUTE(
-        app,
-        "/redfish/v1/Systems/<str>/Processors/<str>/OperatingConfigs/<str>/")
-        .privileges(redfish::privileges::getOperatingConfig)
-        .methods(
-            boost::beast::http::verb::
-                get)([&app](const crow::Request& req,
-                            const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
-                            const std::string& systemName,
-                            const std::string& cpuName,
-                            const std::string& configName) {
-            if (!redfish::setUpRedfishRoute(app, req, asyncResp))
-            {
-                return;
-            }
-            if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM)
-            {
-                // Option currently returns no systems.  TBD
-                messages::resourceNotFound(asyncResp->res, "ComputerSystem",
-                                           systemName);
-                return;
-            }
-
-            if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME)
-            {
-                messages::resourceNotFound(asyncResp->res, "ComputerSystem",
-                                           systemName);
-                return;
-            }
-            // Ask for all objects implementing OperatingConfig so we can search
-            // for one with a matching name
-            constexpr std::array<std::string_view, 1> interfaces = {
-                "xyz.openbmc_project.Inventory.Item.Cpu.OperatingConfig"};
-            dbus::utility::getSubTree(
-                "/xyz/openbmc_project/inventory", 0, interfaces,
-                [asyncResp, cpuName, configName](
-                    const boost::system::error_code& ec,
-                    const dbus::utility::MapperGetSubTreeResponse& subtree) {
-                    if (ec)
-                    {
-                        BMCWEB_LOG_WARNING("D-Bus error: {}, {}", ec,
-                                           ec.message());
-                        messages::internalError(asyncResp->res);
-                        return;
-                    }
-                    const std::string expectedEnding =
-                        cpuName + '/' + configName;
-                    for (const auto& [objectPath, serviceMap] : subtree)
-                    {
-                        // Ignore any configs without matching cpuX/configY
-                        if (!objectPath.ends_with(expectedEnding) ||
-                            serviceMap.empty())
-                        {
-                            continue;
-                        }
-
-                        nlohmann::json& json = asyncResp->res.jsonValue;
-                        json["@odata.type"] =
-                            "#OperatingConfig.v1_0_0.OperatingConfig";
-                        json["@odata.id"] = boost::urls::format(
-                            "/redfish/v1/Systems/{}/Processors/{}/OperatingConfigs/{}",
-                            BMCWEB_REDFISH_SYSTEM_URI_NAME, cpuName,
-                            configName);
-                        json["Name"] = "Processor Profile";
-                        json["Id"] = configName;
-
-                        // Just use the first implementation of the object - not
-                        // expected that there would be multiple matching
-                        // services
-                        getOperatingConfigData(
-                            asyncResp, serviceMap.begin()->first, objectPath);
-                        return;
-                    }
-                    messages::resourceNotFound(asyncResp->res,
-                                               "OperatingConfig", configName);
-                });
-        });
-}
-
-inline void requestRoutesProcessorCollection(App& app)
+inline void requestRoutesProcessor(App& app)
 {
     /**
      * Functions triggers appropriate requests on DBus
@@ -1352,55 +1126,8 @@
 
     BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/Processors/")
         .privileges(redfish::privileges::getProcessorCollection)
-        .methods(
-            boost::beast::http::verb::
-                get)([&app](const crow::Request& req,
-                            const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
-                            const std::string& systemName) {
-            if (!redfish::setUpRedfishRoute(app, req, asyncResp))
-            {
-                return;
-            }
-            if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM)
-            {
-                // Option currently returns no systems.  TBD
-                messages::resourceNotFound(asyncResp->res, "ComputerSystem",
-                                           systemName);
-                return;
-            }
-
-            if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME)
-            {
-                messages::resourceNotFound(asyncResp->res, "ComputerSystem",
-                                           systemName);
-                return;
-            }
-
-            asyncResp->res.addHeader(
-                boost::beast::http::field::link,
-                "</redfish/v1/JsonSchemas/ProcessorCollection/ProcessorCollection.json>; rel=describedby");
-
-            asyncResp->res.jsonValue["@odata.type"] =
-                "#ProcessorCollection.ProcessorCollection";
-            asyncResp->res.jsonValue["Name"] = "Processor Collection";
-
-            asyncResp->res.jsonValue["@odata.id"] =
-                std::format("/redfish/v1/Systems/{}/Processors",
-                            BMCWEB_REDFISH_SYSTEM_URI_NAME);
-
-            collection_util::getCollectionMembers(
-                asyncResp,
-                boost::urls::format("/redfish/v1/Systems/{}/Processors",
-                                    BMCWEB_REDFISH_SYSTEM_URI_NAME),
-                processorInterfaces, "/xyz/openbmc_project/inventory");
-        });
-}
-
-inline void requestRoutesProcessor(App& app)
-{
-    /**
-     * Functions triggers appropriate requests on DBus
-     */
+        .methods(boost::beast::http::verb::get)(
+            std::bind_front(handleProcessorCollectionGet, std::ref(app)));
 
     BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/Processors/<str>/")
         .privileges(redfish::privileges::headProcessor)
diff --git a/redfish-core/lib/processor_operating_config.hpp b/redfish-core/lib/processor_operating_config.hpp
new file mode 100644
index 0000000..fb46550
--- /dev/null
+++ b/redfish-core/lib/processor_operating_config.hpp
@@ -0,0 +1,310 @@
+// SPDX-License-Identifier: Apache-2.0
+// SPDX-FileCopyrightText: Copyright OpenBMC Authors
+// SPDX-FileCopyrightText: Copyright 2018 Intel Corporation
+#pragma once
+
+#include "bmcweb_config.h"
+
+#include "app.hpp"
+#include "async_resp.hpp"
+#include "dbus_utility.hpp"
+#include "error_messages.hpp"
+#include "http_request.hpp"
+#include "logging.hpp"
+#include "query.hpp"
+#include "registries/privilege_registry.hpp"
+#include "utils/collection.hpp"
+#include "utils/dbus_utils.hpp"
+
+#include <boost/beast/http/field.hpp>
+#include <boost/beast/http/verb.hpp>
+#include <boost/system/error_code.hpp>
+#include <boost/url/format.hpp>
+#include <sdbusplus/message/native_types.hpp>
+#include <sdbusplus/unpack_properties.hpp>
+
+#include <array>
+#include <cstddef>
+#include <cstdint>
+#include <format>
+#include <functional>
+#include <memory>
+#include <ranges>
+#include <string>
+#include <string_view>
+#include <tuple>
+#include <utility>
+#include <vector>
+
+namespace redfish
+{
+
+// OperatingConfig D-Bus Types
+using TurboProfileProperty = std::vector<std::tuple<uint32_t, size_t>>;
+using BaseSpeedPrioritySettingsProperty =
+    std::vector<std::tuple<uint32_t, std::vector<uint32_t>>>;
+// uint32_t and size_t may or may not be the same type, requiring a dedup'd
+// variant
+
+/**
+ * Request all the properties for the given D-Bus object and fill out the
+ * related entries in the Redfish OperatingConfig response.
+ *
+ * @param[in,out]   asyncResp       Async HTTP response.
+ * @param[in]       service     D-Bus service name to query.
+ * @param[in]       objPath     D-Bus object to query.
+ */
+inline void getOperatingConfigData(
+    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
+    const std::string& service, const std::string& objPath)
+{
+    dbus::utility::getAllProperties(
+        service, objPath,
+        "xyz.openbmc_project.Inventory.Item.Cpu.OperatingConfig",
+        [asyncResp](const boost::system::error_code& ec,
+                    const dbus::utility::DBusPropertiesMap& properties) {
+            if (ec)
+            {
+                BMCWEB_LOG_WARNING("D-Bus error: {}, {}", ec, ec.message());
+                messages::internalError(asyncResp->res);
+                return;
+            }
+
+            const size_t* availableCoreCount = nullptr;
+            const uint32_t* baseSpeed = nullptr;
+            const uint32_t* maxJunctionTemperature = nullptr;
+            const uint32_t* maxSpeed = nullptr;
+            const uint32_t* powerLimit = nullptr;
+            const TurboProfileProperty* turboProfile = nullptr;
+            const BaseSpeedPrioritySettingsProperty* baseSpeedPrioritySettings =
+                nullptr;
+
+            const bool success = sdbusplus::unpackPropertiesNoThrow(
+                dbus_utils::UnpackErrorPrinter(), properties,
+                "AvailableCoreCount", availableCoreCount, "BaseSpeed",
+                baseSpeed, "MaxJunctionTemperature", maxJunctionTemperature,
+                "MaxSpeed", maxSpeed, "PowerLimit", powerLimit, "TurboProfile",
+                turboProfile, "BaseSpeedPrioritySettings",
+                baseSpeedPrioritySettings);
+
+            if (!success)
+            {
+                messages::internalError(asyncResp->res);
+                return;
+            }
+
+            nlohmann::json& json = asyncResp->res.jsonValue;
+
+            if (availableCoreCount != nullptr)
+            {
+                json["TotalAvailableCoreCount"] = *availableCoreCount;
+            }
+
+            if (baseSpeed != nullptr)
+            {
+                json["BaseSpeedMHz"] = *baseSpeed;
+            }
+
+            if (maxJunctionTemperature != nullptr)
+            {
+                json["MaxJunctionTemperatureCelsius"] = *maxJunctionTemperature;
+            }
+
+            if (maxSpeed != nullptr)
+            {
+                json["MaxSpeedMHz"] = *maxSpeed;
+            }
+
+            if (powerLimit != nullptr)
+            {
+                json["TDPWatts"] = *powerLimit;
+            }
+
+            if (turboProfile != nullptr)
+            {
+                nlohmann::json& turboArray = json["TurboProfile"];
+                turboArray = nlohmann::json::array();
+                for (const auto& [turboSpeed, coreCount] : *turboProfile)
+                {
+                    nlohmann::json::object_t turbo;
+                    turbo["ActiveCoreCount"] = coreCount;
+                    turbo["MaxSpeedMHz"] = turboSpeed;
+                    turboArray.emplace_back(std::move(turbo));
+                }
+            }
+
+            if (baseSpeedPrioritySettings != nullptr)
+            {
+                nlohmann::json& baseSpeedArray =
+                    json["BaseSpeedPrioritySettings"];
+                baseSpeedArray = nlohmann::json::array();
+                for (const auto& [baseSpeedMhz, coreList] :
+                     *baseSpeedPrioritySettings)
+                {
+                    nlohmann::json::object_t speed;
+                    speed["CoreCount"] = coreList.size();
+                    speed["CoreIDs"] = coreList;
+                    speed["BaseSpeedMHz"] = baseSpeedMhz;
+                    baseSpeedArray.emplace_back(std::move(speed));
+                }
+            }
+        });
+}
+
+inline void handleOperatingConfigCollectionGet(
+    App& app, const crow::Request& req,
+    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
+    const std::string& systemName, const std::string& cpuName)
+{
+    if (!redfish::setUpRedfishRoute(app, req, asyncResp))
+    {
+        return;
+    }
+
+    if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM)
+    {
+        // Option currently returns no systems.  TBD
+        messages::resourceNotFound(asyncResp->res, "ComputerSystem",
+                                   systemName);
+        return;
+    }
+
+    if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME)
+    {
+        messages::resourceNotFound(asyncResp->res, "ComputerSystem",
+                                   systemName);
+        return;
+    }
+    asyncResp->res.jsonValue["@odata.type"] =
+        "#OperatingConfigCollection.OperatingConfigCollection";
+    asyncResp->res.jsonValue["@odata.id"] = boost::urls::format(
+        "/redfish/v1/Systems/{}/Processors/{}/OperatingConfigs",
+        BMCWEB_REDFISH_SYSTEM_URI_NAME, cpuName);
+    asyncResp->res.jsonValue["Name"] = "Operating Config Collection";
+
+    // First find the matching CPU object so we know how to
+    // constrain our search for related Config objects.
+    const std::array<std::string_view, 1> interfaces = {
+        "xyz.openbmc_project.Control.Processor.CurrentOperatingConfig"};
+    dbus::utility::getSubTreePaths(
+        "/xyz/openbmc_project/inventory", 0, interfaces,
+        [asyncResp,
+         cpuName](const boost::system::error_code& ec,
+                  const dbus::utility::MapperGetSubTreePathsResponse& objects) {
+            if (ec)
+            {
+                BMCWEB_LOG_WARNING("D-Bus error: {}, {}", ec, ec.message());
+                messages::internalError(asyncResp->res);
+                return;
+            }
+
+            for (const std::string& object : objects)
+            {
+                if (!object.ends_with(cpuName))
+                {
+                    continue;
+                }
+
+                // Not expected that there will be multiple matching
+                // CPU objects, but if there are just use the first
+                // one.
+
+                // Use the common search routine to construct the
+                // Collection of all Config objects under this CPU.
+                constexpr std::array<std::string_view, 1> interface{
+                    "xyz.openbmc_project.Inventory.Item.Cpu.OperatingConfig"};
+                collection_util::getCollectionMembers(
+                    asyncResp,
+                    boost::urls::format(
+                        "/redfish/v1/Systems/{}/Processors/{}/OperatingConfigs",
+                        BMCWEB_REDFISH_SYSTEM_URI_NAME, cpuName),
+                    interface, object);
+                return;
+            }
+        });
+}
+
+inline void handleOperationConfigGet(
+    App& app, const crow::Request& req,
+    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
+    const std::string& systemName, const std::string& cpuName,
+    const std::string& configName)
+{
+    if (!redfish::setUpRedfishRoute(app, req, asyncResp))
+    {
+        return;
+    }
+    if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM)
+    {
+        // Option currently returns no systems.  TBD
+        messages::resourceNotFound(asyncResp->res, "ComputerSystem",
+                                   systemName);
+        return;
+    }
+
+    if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME)
+    {
+        messages::resourceNotFound(asyncResp->res, "ComputerSystem",
+                                   systemName);
+        return;
+    }
+    // Ask for all objects implementing OperatingConfig so we can search
+    // for one with a matching name
+    constexpr std::array<std::string_view, 1> interfaces = {
+        "xyz.openbmc_project.Inventory.Item.Cpu.OperatingConfig"};
+    dbus::utility::getSubTree(
+        "/xyz/openbmc_project/inventory", 0, interfaces,
+        [asyncResp, cpuName,
+         configName](const boost::system::error_code& ec,
+                     const dbus::utility::MapperGetSubTreeResponse& subtree) {
+            if (ec)
+            {
+                BMCWEB_LOG_WARNING("D-Bus error: {}, {}", ec, ec.message());
+                messages::internalError(asyncResp->res);
+                return;
+            }
+            const std::string expectedEnding = cpuName + '/' + configName;
+            for (const auto& [objectPath, serviceMap] : subtree)
+            {
+                // Ignore any configs without matching cpuX/configY
+                if (!objectPath.ends_with(expectedEnding) || serviceMap.empty())
+                {
+                    continue;
+                }
+
+                nlohmann::json& json = asyncResp->res.jsonValue;
+                json["@odata.type"] = "#OperatingConfig.v1_0_0.OperatingConfig";
+                json["@odata.id"] = boost::urls::format(
+                    "/redfish/v1/Systems/{}/Processors/{}/OperatingConfigs/{}",
+                    BMCWEB_REDFISH_SYSTEM_URI_NAME, cpuName, configName);
+                json["Name"] = "Processor Profile";
+                json["Id"] = configName;
+
+                // Just use the first implementation of the object - not
+                // expected that there would be multiple matching
+                // services
+                getOperatingConfigData(asyncResp, serviceMap.begin()->first,
+                                       objectPath);
+                return;
+            }
+            messages::resourceNotFound(asyncResp->res, "OperatingConfig",
+                                       configName);
+        });
+}
+
+inline void requestRoutesOperatingConfig(App& app)
+{
+    BMCWEB_ROUTE(app,
+                 "/redfish/v1/Systems/<str>/Processors/<str>/OperatingConfigs/")
+        .privileges(redfish::privileges::getOperatingConfigCollection)
+        .methods(boost::beast::http::verb::get)(
+            std::bind_front(handleOperatingConfigCollectionGet, std::ref(app)));
+
+    BMCWEB_ROUTE(
+        app,
+        "/redfish/v1/Systems/<str>/Processors/<str>/OperatingConfigs/<str>/")
+        .privileges(redfish::privileges::getOperatingConfig)
+        .methods(boost::beast::http::verb::get)(
+            std::bind_front(handleOperationConfigGet, std::ref(app)));
+}
+} // namespace redfish
diff --git a/redfish-core/src/redfish.cpp b/redfish-core/src/redfish.cpp
index 9ee2280..988939f 100644
--- a/redfish-core/src/redfish.cpp
+++ b/redfish-core/src/redfish.cpp
@@ -36,6 +36,7 @@
 #include "power_subsystem.hpp"
 #include "power_supply.hpp"
 #include "processor.hpp"
+#include "processor_operating_config.hpp"
 #include "redfish_sessions.hpp"
 #include "redfish_v1.hpp"
 #include "roles.hpp"
@@ -154,9 +155,7 @@
         requestRoutesCrashdumpCollect(app);
     }
 
-    requestRoutesProcessorCollection(app);
     requestRoutesProcessor(app);
-    requestRoutesOperatingConfigCollection(app);
     requestRoutesOperatingConfig(app);
     requestRoutesMemoryCollection(app);
     requestRoutesMemory(app);