Link PCIeDevice to Processor schema

This commit is to populate link(s) to processor associated with given
PCIeDevice using the association of `{connecting/connected_to}` via
 - https://gerrit.openbmc.org/c/openbmc/phosphor-dbus-interfaces/+/81914
   (This PDI needs to go first).

This also updates the PCIeDevice schema version for `Links/Processors`
type in PCIeDevice schema.

An example commit is:
 - https://gerrit.openbmc.org/c/openbmc/openbmc/+/81919

Tested:
- Validator passes
- GET on PCIe devices which have links to processors

Sample output:
```
curl -k -X GET https://${bmc}/redfish/v1/Systems/system/PCIeDevices/pcie_card10
{
  ...
  "Links": {
    "Processors": [
      {
        "@odata.id": "/redfish/v1/Systems/system/Processors/cpu0"
      }
    ],
    "Processors@odata.count": 1
  },
  ...
}
```

Change-Id: I5cb29087cdb9feedac6cc0d5662e2a6903c3228c
Signed-off-by: Myung Bae <myungbae@us.ibm.com>
diff --git a/redfish-core/lib/pcie.hpp b/redfish-core/lib/pcie.hpp
index 3860216..917da89 100644
--- a/redfish-core/lib/pcie.hpp
+++ b/redfish-core/lib/pcie.hpp
@@ -25,6 +25,7 @@
 
 #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/unpack_properties.hpp>
 
@@ -161,6 +162,66 @@
             std::bind_front(handlePCIeDeviceCollectionGet, std::ref(app)));
 }
 
+inline void afterGetAssociatedSubTreePaths(
+    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
+    const boost::system::error_code& ec,
+    const dbus::utility::MapperGetSubTreePathsResponse& processorPaths)
+{
+    if (ec)
+    {
+        if (ec.value() == EBADR)
+        {
+            BMCWEB_LOG_DEBUG("No processor association found");
+            return;
+        }
+        BMCWEB_LOG_ERROR("DBUS response error {}", ec.value());
+        messages::internalError(asyncResp->res);
+        return;
+    }
+
+    if (processorPaths.empty())
+    {
+        BMCWEB_LOG_DEBUG("No association found for processor");
+        return;
+    }
+
+    nlohmann::json& processorList =
+        asyncResp->res.jsonValue["Links"]["Processors"];
+    for (const std::string& processorPath : processorPaths)
+    {
+        std::string processorName =
+            sdbusplus::message::object_path(processorPath).filename();
+        if (processorName.empty())
+        {
+            continue;
+        }
+
+        nlohmann::json item = nlohmann::json::object();
+        item["@odata.id"] =
+            boost::urls::format("/redfish/v1/Systems/{}/Processors/{}",
+                                BMCWEB_REDFISH_SYSTEM_URI_NAME, processorName);
+        processorList.emplace_back(std::move(item));
+    }
+
+    asyncResp->res.jsonValue["Links"]["Processors@odata.count"] =
+        processorList.size();
+}
+
+inline void linkAssociatedProcessor(
+    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
+    const std::string& pcieDevicePath)
+{
+    constexpr std::array<std::string_view, 2> processorInterfaces = {
+        "xyz.openbmc_project.Inventory.Item.Cpu",
+        "xyz.openbmc_project.Inventory.Item.Accelerator"};
+
+    dbus::utility::getAssociatedSubTreePaths(
+        pcieDevicePath + "/connected_to",
+        sdbusplus::message::object_path("/xyz/openbmc_project/inventory"), 0,
+        processorInterfaces,
+        std::bind_front(afterGetAssociatedSubTreePaths, asyncResp));
+}
+
 inline void addPCIeSlotProperties(
     crow::Response& res, const boost::system::error_code& ec,
     const dbus::utility::DBusPropertiesMap& pcieSlotProperties)
@@ -490,7 +551,7 @@
     asyncResp->res.addHeader(
         boost::beast::http::field::link,
         "</redfish/v1/JsonSchemas/PCIeDevice/PCIeDevice.json>; rel=describedby");
-    asyncResp->res.jsonValue["@odata.type"] = "#PCIeDevice.v1_9_0.PCIeDevice";
+    asyncResp->res.jsonValue["@odata.type"] = "#PCIeDevice.v1_19_0.PCIeDevice";
     asyncResp->res.jsonValue["@odata.id"] =
         boost::urls::format("/redfish/v1/Systems/{}/PCIeDevices/{}",
                             BMCWEB_REDFISH_SYSTEM_URI_NAME, pcieDeviceId);
@@ -513,6 +574,7 @@
     getPCIeDeviceProperties(
         asyncResp, pcieDevicePath, service,
         std::bind_front(addPCIeDeviceProperties, asyncResp, pcieDeviceId));
+    linkAssociatedProcessor(asyncResp, pcieDevicePath);
     getPCIeDeviceSlotPath(
         pcieDevicePath, asyncResp,
         std::bind_front(afterGetPCIeDeviceSlotPath, asyncResp));