Enhance sys_info and postcode for multi-host
This patch enhances multi-host platform support in the OCP debug card
display (sys_info and postcode pages) and OEM command modules.
It replaces hard-coded D-Bus paths and single-interface networkqueries
with dynamic lookups based on the current host selector position.
-Sys_Info page now retrieves the correct FRU path.
-POSTCODE page dynamically calls Boot.PostCode<N> service/object for
each host slot.
-Network information Network information now supports fetching from
eth1 when eth0 is unavailable, preventing missing data or crashes.
Motivation:
Original implementation used hardcoded D-Bus paths such as PostCode0
and only checked eth0 for network-related data. On multi-host platforms,
each host has a separate PostCode service, and FRU/FW data may differ
or be present in alternate paths. Moreover, some systems expose IP/MAC
data on eth1, causing failures when only eth0 is queried. These issue
led to incomplete data being shown or crashes in the sys_info display.
Design:
- Introduced logic to dynamically determine the current host position
and fetch the corresponding PostCode service (PostCode0 ~ PostCode7).
- Enhance `getNetworkData` to check both eth0 and eth1 for IP and MAC
addresses, and avoid crashes if a value is not found.
- Updated FRU retrieval to support multiple motherboard paths and
fallback paths, using filtered D-Bus object mapping
via interface matching and predicate.
- Ensured all std::string results are checked before accessing to
prevent null dereferencing and crashes.
Change-Id: I0c88eb58c3b416437a02994c4c202d5c2269d32a
Signed-off-by: Liora Guo <liora.guo.wiwynn@gmail.com>
diff --git a/src/oemcommands.cpp b/src/oemcommands.cpp
index e5cb0f5..99b28c2 100644
--- a/src/oemcommands.cpp
+++ b/src/oemcommands.cpp
@@ -33,8 +33,10 @@
#include <array>
#include <cstring>
#include <fstream>
+#include <functional>
#include <iomanip>
#include <iostream>
+#include <optional>
#include <regex>
#include <sstream>
#include <string>
@@ -432,71 +434,94 @@
return 0;
}
+static std::optional<std::string> findIpAddress(
+ sdbusplus::bus_t& bus, const std::string& protocol,
+ const std::vector<std::string>& ifaces)
+{
+ for (auto& dev : ifaces)
+ {
+ try
+ {
+ auto ipObjectInfo =
+ ipmi::network::getIPObject(bus, ipmi::network::IP_INTERFACE,
+ ipmi::network::ROOT, protocol, dev);
+
+ auto props = ipmi::getAllDbusProperties(
+ bus, ipObjectInfo.second, ipObjectInfo.first,
+ ipmi::network::IP_INTERFACE);
+
+ auto addr = std::get<std::string>(props.at("Address"));
+ if (!addr.empty())
+ return addr;
+ }
+ catch (const sdbusplus::exception::SdBusError&)
+ {}
+ }
+ return std::nullopt;
+}
+static std::optional<std::string> findMacAddress(
+ sdbusplus::bus_t& bus, const std::vector<std::string>& ifaces)
+{
+ for (auto& dev : ifaces)
+ {
+ try
+ {
+ auto [path, obj] = ipmi::getDbusObject(
+ bus, ipmi::network::MAC_INTERFACE, ipmi::network::ROOT, dev);
+
+ auto var = ipmi::getDbusProperty(
+ bus, obj, path, ipmi::network::MAC_INTERFACE, "MACAddress");
+
+ auto mac = std::get<std::string>(var);
+ if (!mac.empty())
+ return mac;
+ }
+ catch (const sdbusplus::exception::SdBusError&)
+ {}
+ }
+ return std::nullopt;
+}
+
ipmi_ret_t getNetworkData(uint8_t lan_param, char* data)
{
- ipmi_ret_t rc = ipmi::ccSuccess;
- sdbusplus::bus_t bus(ipmid_get_sd_bus_connection());
+ static const std::vector<std::string> ifaces{"eth0", "eth1"};
- const std::string ethdevice = "eth0";
+ sdbusplus::bus_t bus(ipmid_get_sd_bus_connection());
+ ipmi_ret_t rc = IPMI_CC_OK;
+ std::optional<std::string> result;
switch (static_cast<LanParam>(lan_param))
{
case LanParam::IP:
- {
- std::string ipaddress;
- auto ipObjectInfo = ipmi::network::getIPObject(
- bus, ipmi::network::IP_INTERFACE, ipmi::network::ROOT,
- ipmi::network::IPV4_PROTOCOL, ethdevice);
-
- auto properties = ipmi::getAllDbusProperties(
- bus, ipObjectInfo.second, ipObjectInfo.first,
- ipmi::network::IP_INTERFACE);
-
- ipaddress = std::get<std::string>(properties["Address"]);
-
- std::strcpy(data, ipaddress.c_str());
- }
- break;
+ result = findIpAddress(bus, ipmi::network::IPV4_PROTOCOL, ifaces);
+ lg2::info("Found IP Address: {ADDR}", "ADDR",
+ result.value_or("Not Found"));
+ break;
case LanParam::IPV6:
- {
- std::string ipaddress;
- auto ipObjectInfo = ipmi::network::getIPObject(
- bus, ipmi::network::IP_INTERFACE, ipmi::network::ROOT,
- ipmi::network::IPV6_PROTOCOL, ethdevice);
-
- auto properties = ipmi::getAllDbusProperties(
- bus, ipObjectInfo.second, ipObjectInfo.first,
- ipmi::network::IP_INTERFACE);
-
- ipaddress = std::get<std::string>(properties["Address"]);
-
- std::strcpy(data, ipaddress.c_str());
- }
- break;
+ result = findIpAddress(bus, ipmi::network::IPV6_PROTOCOL, ifaces);
+ lg2::info("Found IPv6 Address: {ADDR}", "ADDR",
+ result.value_or("Not Found"));
+ break;
case LanParam::MAC:
- {
- std::string macAddress;
- auto macObjectInfo =
- ipmi::getDbusObject(bus, ipmi::network::MAC_INTERFACE,
- ipmi::network::ROOT, ethdevice);
-
- auto variant = ipmi::getDbusProperty(
- bus, macObjectInfo.second, macObjectInfo.first,
- ipmi::network::MAC_INTERFACE, "MACAddress");
-
- macAddress = std::get<std::string>(variant);
-
- sscanf(macAddress.c_str(), ipmi::network::MAC_ADDRESS_FORMAT,
- (data), (data + 1), (data + 2), (data + 3), (data + 4),
- (data + 5));
- std::strcpy(data, macAddress.c_str());
- }
- break;
+ result = findMacAddress(bus, ifaces);
+ lg2::info("Found MAC Address: {ADDR}", "ADDR",
+ result.value_or("Not Found"));
+ break;
default:
- rc = ipmi::ccParmOutOfRange;
+ return IPMI_CC_PARM_OUT_OF_RANGE;
+ }
+
+ if (!result.has_value())
+ {
+ rc = IPMI_CC_UNSPECIFIED_ERROR;
+ data[0] = '\0';
+ }
+ else
+ {
+ std::strcpy(data, result->c_str());
}
return rc;
}
@@ -514,38 +539,78 @@
}
return platform;
}
-
-// return "" equals failed
-std::string getMotherBoardFruPath()
+static std::optional<std::string> findFruPathByInterface(
+ sdbusplus::bus_t& dbus, const std::string& interfaceName,
+ std::function<bool(const std::string&)> predicate)
{
+ const int depth = 0;
std::vector<std::string> paths;
- static constexpr const auto depth = 0;
- sdbusplus::bus_t dbus(ipmid_get_sd_bus_connection());
auto mapperCall = dbus.new_method_call(
"xyz.openbmc_project.ObjectMapper",
"/xyz/openbmc_project/object_mapper",
"xyz.openbmc_project.ObjectMapper", "GetSubTreePaths");
- static constexpr auto interface = {
- "xyz.openbmc_project.Inventory.Item.Board.Motherboard"};
+ mapperCall.append("/xyz/openbmc_project/inventory/", depth,
+ std::array<const char*, 1>{interfaceName.c_str()});
- mapperCall.append("/xyz/openbmc_project/inventory/", depth, interface);
try
{
auto reply = dbus.call(mapperCall);
reply.read(paths);
}
- catch (sdbusplus::exception_t& e)
+ catch (const sdbusplus::exception_t& e)
{
phosphor::logging::log<phosphor::logging::level::ERR>(e.what());
- return "";
+ return std::nullopt;
}
for (const auto& path : paths)
{
- return path;
+ if (predicate(path))
+ {
+ return path;
+ }
+ }
+ return std::nullopt;
+}
+
+// return "" equals failed
+std::string getMotherBoardFruPath()
+{
+ sdbusplus::bus_t dbus(ipmid_get_sd_bus_connection());
+
+ bool platform = isMultiHostPlatform();
+ size_t hostPosition;
+ if (platform)
+ getSelectorPosition(hostPosition);
+
+ if (hostPosition == 0)
+ {
+ if (auto path = findFruPathByInterface(
+ dbus, "xyz.openbmc_project.Inventory.Item.Bmc",
+ [](const std::string&) { return true; }))
+ {
+ lg2::info("[1] Found Motherboard: {PATH}", "PATH", *path);
+ return *path;
+ }
}
+ size_t targetIndex = (hostPosition > 0 ? hostPosition - 1 : 0);
+ auto predNth = [targetIndex,
+ counter = size_t{0}](const std::string& /*path*/) mutable {
+ return (counter++ == targetIndex);
+ };
+
+ if (auto path = findFruPathByInterface(
+ dbus, "xyz.openbmc_project.Inventory.Item.Board.Motherboard",
+ predNth))
+ {
+ lg2::info("[2] Found Motherboard: {PATH}", "PATH", *path);
+ return *path;
+ }
+
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "getMotherBoardFruPath: no valid FRU path found");
return "";
}
diff --git a/src/usb-dbg.cpp b/src/usb-dbg.cpp
index 70b69a0..57c3339 100644
--- a/src/usb-dbg.cpp
+++ b/src/usb-dbg.cpp
@@ -650,19 +650,25 @@
}
getMaxHostPosition(maxPosition);
+ std::string data;
if (hostPosition == BMC_POSITION || hostInstances == "0")
{
- std::string data = "FRU:" + getMotherBoardFruName();
- frame_info.append(data);
+ data = "FRU:" + getMotherBoardFruName();
}
else if (hostPosition != BMC_POSITION && hostPosition <= maxPosition)
{
- std::string data = "FRU:slot" + std::to_string(hostPosition);
- frame_info.append(data);
+ if (getMotherBoardFruName() != "")
+ {
+ data = "FRU:" + getMotherBoardFruName();
+ }
+ else
+ {
+ data = "FRU:slot" + std::to_string(hostPosition);
+ }
}
+ frame_info.append(data);
// FRU
- std::string data;
frame_info.append("SN:");
if (getFruData(data, serialName) != 0)
{
@@ -783,6 +789,8 @@
{
// up to 70 codes can be displayed on 10 pages
static constexpr size_t maxPostcodes = 70;
+ bool platform = isMultiHostPlatform();
+ size_t hostPosition = 0;
if (page == 1)
{
@@ -791,18 +799,24 @@
snprintf(frame_postcode.title, 32, "POST CODE");
frame_postcode.max_page = 10;
+ if (platform)
+ getSelectorPosition(hostPosition);
+
// Synchronously get D-Bus connection
auto bus = sdbusplus::bus::new_default();
+ std::string serviceName =
+ BOOT_POSTCODE_SERVICE + std::to_string(hostPosition);
+ std::string objectPath =
+ BOOT_POSTCODE_OBJECTPATH + std::to_string(hostPosition);
// Build D-Bus method call
auto method = bus.new_method_call(
- "xyz.openbmc_project.State.Boot.PostCode0", // Target service name
- "/xyz/openbmc_project/State/Boot/PostCode0", // Object path
- "xyz.openbmc_project.State.Boot.PostCode", // Interface name
- "GetPostCodes"); // Method name
+ serviceName.c_str(), // Target service name
+ objectPath.c_str(), // Object path
+ BOOT_POSTCODE_INTERFACE, // Interface name
+ "GetPostCodes"); // Method name
- method.append(uint16_t(1)); // Add method parameter, assuming it's page
-
+ method.append(uint16_t(1)); // Add method parameter, assuming it's pag
try
{
auto reply = bus.call(method); // Send synchronous method call