mctp: fix E-M configuration reading for nvmed and mctpreactor

Fixed a mismatch in how Entity Manager configurations are read for MCTP
devices in both nvmesensor and mctpreactor. The previous
implementation failed to handle different schema structures (e.g.,
nested BusInfo vs flat properties directly on the interface).

To resolve this, the bus information extraction was refactored:
- Added AbstractBusInfo and MctpReactorBusInfo in MctpUtil to target
  mctpreactor expectations (USB/I2C/I3C).
- Created a dedicated extractNVMeBusInfo in NVMeSensorMain.cpp to
  handle the nested NVME1000.BusInfo0 structure.

This ensures both applications can correctly parse their respective
configurations from Entity Manager. Tests and mock server were updated
to reflect these changes.

Tested: All tests passed in Docker container.
Change-Id: I28d5b196c5316974d689689304f52148d4617099
Google-Bug-Id: 490106522
Signed-off-by: Hao Jiang <jianghao@google.com>
diff --git a/src/MctpReactorDevice.cpp b/src/MctpReactorDevice.cpp
index 6ed3d88..e6afac5 100644
--- a/src/MctpReactorDevice.cpp
+++ b/src/MctpReactorDevice.cpp
@@ -173,8 +173,9 @@
 
         for (const auto& [epPath, epConfig] : mctpEndpointConfigMap)
         {
-            BusInfo epBusInfo = extractBusInfo(epConfig);
-            if (epBusInfo == targetBusInfo)
+            MctpReactorBusInfo epBusInfo(epConfig);
+            if (epBusInfo.isValid() &&
+                epBusInfo.getProperties() == targetBusInfo)
             {
                 finaliseEndpoint(epPath, std::move(cb));
                 return;
diff --git a/src/MctpUtil.cpp b/src/MctpUtil.cpp
index 8b19eae..1d2aa96 100644
--- a/src/MctpUtil.cpp
+++ b/src/MctpUtil.cpp
@@ -471,13 +471,13 @@
     });
 }
 
-BusInfo extractBusInfo(const SensorData& configData)
+MctpReactorBusInfo::MctpReactorBusInfo(const SensorData& configData)
 {
-    BusInfo busInfo;
-    auto extractProp = [](const auto& properties, BusInfo& info,
+    auto extractProp = [](const auto& dbusProps,
+                          std::map<std::string, std::string>& info,
                           const std::string& key) {
-        auto it = properties.find(key);
-        if (it != properties.end())
+        auto it = dbusProps.find(key);
+        if (it != dbusProps.end())
         {
             info[key] = std::visit(VariantToStringVisitor(), it->second);
         }
@@ -485,39 +485,42 @@
 
     for (const auto& [intf, props] : configData)
     {
-        if (absl::StrContains(intf,
-                              "xyz.openbmc_project.Configuration.BusInfo"))
+        if (absl::StrContains(
+                intf, "xyz.openbmc_project.Configuration.MCTPUSBDevice"))
         {
-            std::string busType;
-            auto it = props.find("BusType");
-            if (it != props.end())
-            {
-                busType = std::visit(VariantToStringVisitor(), it->second);
-            }
+            type = "USB";
+            valid = true;
+            properties["BusType"] = "USB";
 
-            if (busType == "USB")
-            {
-                extractProp(props, busInfo, "RootHubPath");
-                extractProp(props, busInfo, "Port");
-                extractProp(props, busInfo, "Configuration");
-                extractProp(props, busInfo, "InterfaceNum");
+            extractProp(props, properties, "RootHubPath");
+            extractProp(props, properties, "Port");
+            extractProp(props, properties, "InterfaceNum");
+            extractProp(props, properties, "Configuration");
+            break;
+        }
+        if (absl::StrContains(
+                intf, "xyz.openbmc_project.Configuration.MCTPI2CTarget"))
+        {
+            type = "I2C";
+            valid = true;
+            properties["BusType"] = "I2C";
 
-                busInfo["BusType"] = busType;
-            }
-            else if (busType == "I2C")
-            {
-                extractProp(props, busInfo, "Bus");
-                extractProp(props, busInfo, "Address");
+            extractProp(props, properties, "Bus");
+            extractProp(props, properties, "Address");
+            break;
+        }
+        if (absl::StrContains(
+                intf, "xyz.openbmc_project.Configuration.MCTPI3CTarget"))
+        {
+            type = "I3C";
+            valid = true;
+            properties["BusType"] = "I3C";
 
-                busInfo["BusType"] = busType;
-            }
-            else if (!busType.empty())
-            {
-                lg2::warning("Unsupported BusType {TYPE}", "TYPE", busType);
-            }
+            extractProp(props, properties, "Bus");
+            extractProp(props, properties, "Address");
+            break;
         }
     }
-    return busInfo;
 }
 
 void cleanupMctpEndpointListener()
diff --git a/src/MctpUtil.hpp b/src/MctpUtil.hpp
index 5180e6b..4d63c3f 100644
--- a/src/MctpUtil.hpp
+++ b/src/MctpUtil.hpp
@@ -1,7 +1,10 @@
 #pragma once
 
+#include "Utils.hpp"
+
 #include <sdbusplus/asio/connection.hpp>
 
+#include <functional>
 #include <map>
 #include <memory>
 #include <string>
@@ -20,9 +23,28 @@
 
 // Global maps (declared extern)
 
-#include "Utils.hpp"
+class MctpReactorBusInfo
+{
+  public:
+    explicit MctpReactorBusInfo(const SensorData& configData);
+    std::string getType() const
+    {
+        return type;
+    }
+    bool isValid() const
+    {
+        return valid;
+    }
+    const std::map<std::string, std::string>& getProperties() const
+    {
+        return properties;
+    }
 
-#include <functional>
+  private:
+    std::string type;
+    bool valid = false;
+    std::map<std::string, std::string> properties;
+};
 
 extern std::map<std::string, SensorData> mctpEndpointConfigMap;
 
@@ -39,5 +61,3 @@
     const std::shared_ptr<sdbusplus::asio::connection>& conn,
     MctpMessageType msgType);
 void cleanupMctpEndpointListener();
-
-BusInfo extractBusInfo(const SensorData& configData);
diff --git a/src/NVMeSensorMain.cpp b/src/NVMeSensorMain.cpp
index 2821207..0b082a4 100644
--- a/src/NVMeSensorMain.cpp
+++ b/src/NVMeSensorMain.cpp
@@ -200,6 +200,55 @@
     return false;
 }
 
+static BusInfo extractNVMeBusInfo(const SensorData& configData)
+{
+    BusInfo busInfo;
+    for (const auto& pair : configData)
+    {
+        const std::string& intf = pair.first;
+        const SensorBaseConfigMap& props = pair.second;
+        if (absl::StrContains(
+                intf, "xyz.openbmc_project.Configuration.NVME1000.BusInfo"))
+        {
+            std::string busType;
+            auto it = props.find("BusType");
+            if (it != props.end())
+            {
+                busType = std::visit(VariantToStringVisitor(), it->second);
+                busInfo["BusType"] = busType;
+            }
+
+            auto extractProp = [&](const std::string& key) {
+                auto it = props.find(key);
+                if (it != props.end())
+                {
+                    busInfo[key] = std::visit(VariantToStringVisitor(),
+                                              it->second);
+                }
+            };
+
+            if (busType == "USB")
+            {
+                extractProp("RootHubPath");
+                extractProp("Port");
+                extractProp("InterfaceNum");
+                extractProp("Configuration");
+            }
+            else if (busType == "I2C")
+            {
+                extractProp("Bus");
+                extractProp("Address");
+            }
+            else if (!busType.empty())
+            {
+                lg2::warning("Unsupported BusType {TYPE} in NVME1000 config",
+                             "TYPE", busType);
+            }
+        }
+    }
+    return busInfo;
+}
+
 static void handleConfigurations(
     boost::asio::io_context& io, sdbusplus::asio::object_server& objectServer,
     std::shared_ptr<sdbusplus::asio::connection>& dbusConnection,
@@ -241,7 +290,7 @@
         }
         else if (*nvmeProtocol == "mi_mctp")
         {
-            busInfo = extractBusInfo(configData);
+            busInfo = extractNVMeBusInfo(configData);
             if (busInfo.empty())
             {
                 lg2::error("Missing BusInfo for mi_mctp device {PATH}", "PATH",
diff --git a/tests/mock_mctpd.py b/tests/mock_mctpd.py
index 65801e7..965c4c6 100644
--- a/tests/mock_mctpd.py
+++ b/tests/mock_mctpd.py
@@ -149,10 +149,27 @@
         self.QueryCompleted()
         return {
             dbus.ObjectPath('/xyz/openbmc_project/inventory/system/board/MockBoard/mctp_device'): {
-                'xyz.openbmc_project.Configuration.BusInfo': {
+                'xyz.openbmc_project.Configuration.MCTPUSBDevice': {
                     'BusType': 'USB',
-                    'Port': '1.2.5',
-                    'Configuration': dbus.UInt32(1)
+                    'Port': '1.5.1',
+                    'Configuration': dbus.UInt64(1),
+                    'InterfaceNum': dbus.UInt64(4),
+                    'Name': 'config',
+                    'RootHubPath': '/sys/devices/platform/ahb/f0806000.usb',
+                    'Type': 'MCTPUSBDevice'
+                }
+            },
+            dbus.ObjectPath('/xyz/openbmc_project/inventory/system/board/Ironheart/Ironheart_i2c_static_NVMe_2'): {
+                'xyz.openbmc_project.Configuration.NVME1000': {
+                    'Name': 'Ironheart_i2c_static_NVMe_2',
+                    'Protocol': 'mi_mctp',
+                    'Type': 'NVME1000'
+                },
+                'xyz.openbmc_project.Configuration.NVME1000.BusInfo0': {
+                    'BusType': 'USB',
+                    'InterfaceNum': dbus.UInt64(3),
+                    'Port': '1.5.1',
+                    'RootHubPath': '/sys/devices/platform/ahb/f0806000.usb'
                 }
             }
         }
diff --git a/tests/test_MctpUtil.cpp b/tests/test_MctpUtil.cpp
index 05a4a50..5a0cc3c 100644
--- a/tests/test_MctpUtil.cpp
+++ b/tests/test_MctpUtil.cpp
@@ -1041,8 +1041,10 @@
     // USB)
     BusInfo targetBusInfo;
     targetBusInfo["BusType"] = "USB";
-    targetBusInfo["Port"] = "1.2.5";
+    targetBusInfo["Port"] = "1.5.1";
     targetBusInfo["Configuration"] = "1";
+    targetBusInfo["InterfaceNum"] = "4";
+    targetBusInfo["RootHubPath"] = "/sys/devices/platform/ahb/f0806000.usb";
 
     bool setupCompleted = false;
 
@@ -1115,8 +1117,10 @@
     // Create BusInfo for target device
     BusInfo targetBusInfo;
     targetBusInfo["BusType"] = "USB";
-    targetBusInfo["Port"] = "1.2.5";
+    targetBusInfo["Port"] = "1.5.1";
     targetBusInfo["Configuration"] = "1";
+    targetBusInfo["InterfaceNum"] = "4";
+    targetBusInfo["RootHubPath"] = "/sys/devices/platform/ahb/f0806000.usb";
 
     bool setupCompleted = false;
 
@@ -1171,8 +1175,10 @@
     // Create BusInfo for target device
     BusInfo targetBusInfo;
     targetBusInfo["BusType"] = "USB";
-    targetBusInfo["Port"] = "1.2.5";
+    targetBusInfo["Port"] = "1.5.1";
     targetBusInfo["Configuration"] = "1";
+    targetBusInfo["InterfaceNum"] = "4";
+    targetBusInfo["RootHubPath"] = "/sys/devices/platform/ahb/f0806000.usb";
 
     bool setupCompleted = false;
     std::shared_ptr<MctpEndpoint> endpoint;
@@ -1227,8 +1233,10 @@
     // Create BusInfo for target device
     BusInfo targetBusInfo;
     targetBusInfo["BusType"] = "USB";
-    targetBusInfo["Port"] = "1.2.5";
+    targetBusInfo["Port"] = "1.5.1";
     targetBusInfo["Configuration"] = "1";
+    targetBusInfo["InterfaceNum"] = "4";
+    targetBusInfo["RootHubPath"] = "/sys/devices/platform/ahb/f0806000.usb";
 
     bool setupCompleted = false;
     std::error_code errorResult;
@@ -1289,8 +1297,10 @@
     // Create BusInfo for target device
     BusInfo targetBusInfo;
     targetBusInfo["BusType"] = "USB";
-    targetBusInfo["Port"] = "1.2.5";
+    targetBusInfo["Port"] = "1.5.1";
     targetBusInfo["Configuration"] = "1";
+    targetBusInfo["InterfaceNum"] = "4";
+    targetBusInfo["RootHubPath"] = "/sys/devices/platform/ahb/f0806000.usb";
 
     bool sensorNotified = false;
     auto cb = [&](const std::shared_ptr<MctpEndpoint>&) {