Add multi-host postcode support
These changes add the multiple host postcode implementation.
In addition to the LPC snoop the postcode can be read via D-Bus
interface.The same is displayed in seven segment display thorough
GPIO lines.
The design can be refered at
https://gerrit.openbmc-project.xyz/c/openbmc/docs/+/35065
Tested:
Verified in Facebook Tiogapass & Yosemitev2 platforms.
1.The postcode displayed in the seven segment corresponding to the
host position selected in the frontpanel of Facebook
yosemitev2(4 host).
2.The postcode history is verified in the respective host directory
"/var/lib/phosphor-post-code-manager/hostX(1,2,3,4)".
Signed-off-by: Kumar Thangavel <thangavel.k@hcl.com>
Change-Id: Id79ad00652bfedd319d3b8ccf1aafbdc60b33d86
diff --git a/ipmisnoop/ipmisnoop.hpp b/ipmisnoop/ipmisnoop.hpp
new file mode 100644
index 0000000..efc5378
--- /dev/null
+++ b/ipmisnoop/ipmisnoop.hpp
@@ -0,0 +1,190 @@
+#pragma once
+
+#include "lpcsnoop/snoop.hpp"
+
+#include <boost/asio.hpp>
+#include <filesystem>
+#include <gpiod.hpp>
+#include <iostream>
+#include <sdbusplus/asio/connection.hpp>
+#include <sdbusplus/asio/object_server.hpp>
+#include <sdbusplus/asio/property.hpp>
+#include <sdbusplus/bus.hpp>
+#include <sdbusplus/server.hpp>
+#include <xyz/openbmc_project/Chassis/Buttons/HostSelector/server.hpp>
+#include <xyz/openbmc_project/State/Boot/Raw/server.hpp>
+
+const std::string ipmiSnoopObject = "/xyz/openbmc_project/state/boot/raw";
+
+const int hostParseIdx = 3;
+const int maxPostcode = 255;
+const int maxPosition = 4;
+
+std::vector<gpiod::line> led_lines;
+
+using Selector =
+ sdbusplus::xyz::openbmc_project::Chassis::Buttons::server::HostSelector;
+
+std::unique_ptr<sdbusplus::bus::match_t> matchSignal;
+
+const std::string selectorService = "xyz.openbmc_project.Chassis.Buttons";
+const std::string selectorObject =
+ "/xyz/openbmc_project/Chassis/Buttons/HostSelector";
+const std::string selectorIface =
+ "xyz.openbmc_project.Chassis.Buttons.HostSelector";
+
+const std::string rawObject = "/xyz/openbmc_project/state/boot";
+const std::string rawIface = "xyz.openbmc_project.State.Boot.Raw";
+const std::string rawService = "xyz.openbmc_project.State.Boot.Raw";
+
+uint32_t getSelectorPosition(sdbusplus::bus::bus& bus)
+{
+ const std::string propertyName = "Position";
+
+ auto method =
+ bus.new_method_call(selectorService.c_str(), selectorObject.c_str(),
+ "org.freedesktop.DBus.Properties", "Get");
+ method.append(selectorIface.c_str(), propertyName);
+
+ try
+ {
+ std::variant<uint32_t> value{};
+ auto reply = bus.call(method);
+ reply.read(value);
+ return std::get<uint32_t>(value);
+ }
+ catch (const sdbusplus::exception::exception& ex)
+ {
+ std::cerr << "GetProperty call failed ";
+ throw std::runtime_error("GetProperty call failed");
+ }
+}
+
+struct IpmiPostReporter : PostObject
+{
+ IpmiPostReporter(sdbusplus::bus::bus& bus, const char* objPath) :
+ PostObject(bus, objPath), bus(bus),
+ propertiesChangedSignalRaw(
+ bus,
+ sdbusplus::bus::match::rules::propertiesChanged(objPath, rawIface),
+
+ [this, &bus](sdbusplus::message::message& msg) {
+ using primarycode_t = uint64_t;
+ using secondarycode_t = std::vector<uint8_t>;
+ using postcode_t = std::tuple<primarycode_t, secondarycode_t>;
+
+ std::string objectName;
+ std::string InterfaceName;
+ std::map<std::string, std::variant<postcode_t>> msgData;
+ msg.read(InterfaceName, msgData);
+
+ std::filesystem::path name(msg.get_path());
+ objectName = name.filename();
+
+ std::string hostNumStr = objectName.substr(hostParseIdx);
+ size_t hostNum = std::stoi(hostNumStr);
+
+ size_t position = getSelectorPosition(bus);
+
+ if (position > maxPosition)
+ {
+ std::cerr << "Invalid position. Position should be 1 to 4 "
+ "for all hosts "
+ << std::endl;
+ }
+
+ // Check if it was the Value property that changed.
+ auto valPropMap = msgData.find("Value");
+ if (valPropMap == msgData.end())
+ {
+ std::cerr << "Value property is not found " << std::endl;
+ return;
+ }
+ uint64_t postcode =
+ std::get<0>(std::get<postcode_t>(valPropMap->second));
+
+ if (postcode <= maxPostcode)
+ {
+ if (position == hostNum)
+ {
+ uint8_t postcode_8bit =
+ static_cast<uint8_t>(postcode & 0x0000FF);
+
+ // write postcode into seven segment display
+ if (postCodeDisplay(postcode_8bit) < 0)
+ {
+ fprintf(stderr, "Error in display the postcode\n");
+ }
+ }
+ else
+ {
+ fprintf(stderr, "Host Selector Position and host "
+ "number is not matched..\n");
+ }
+ }
+ else
+ {
+ fprintf(stderr, "invalid postcode value \n");
+ }
+ })
+ {
+ }
+
+ sdbusplus::bus::bus& bus;
+ sdbusplus::bus::match_t propertiesChangedSignalRaw;
+ int postCodeDisplay(uint8_t);
+ void getSelectorPositionSignal(sdbusplus::bus::bus& bus);
+};
+
+// Configure the seven segment display connected GPIOs direction
+int configGPIODirOutput()
+{
+ std::string gpioStr;
+ // Need to define gpio names LED_POST_CODE_0 to 8 in dts file
+ std::string gpioName = "LED_POST_CODE_";
+ const int value = 0;
+
+ for (int iteration = 0; iteration < 8; iteration++)
+ {
+ gpioStr = gpioName + std::to_string(iteration);
+ gpiod::line gpioLine = gpiod::find_line(gpioStr);
+
+ if (!gpioLine)
+ {
+ std::string errMsg = "Failed to find the " + gpioStr + " line";
+ std::cerr << errMsg.c_str() << std::endl;
+ return -1;
+ }
+
+ led_lines.push_back(gpioLine);
+ // Request GPIO output to specified value
+ try
+ {
+ gpioLine.request({__FUNCTION__,
+ gpiod::line_request::DIRECTION_OUTPUT,
+ gpiod::line_request::FLAG_ACTIVE_LOW},
+ value);
+ }
+ catch (std::exception&)
+ {
+ std::string errMsg = "Failed to request " + gpioStr + " output";
+ std::cerr << errMsg.c_str() << std::endl;
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+// Display the received postcode into seven segment display
+int IpmiPostReporter::postCodeDisplay(uint8_t status)
+{
+ for (int iteration = 0; iteration < 8; iteration++)
+ {
+ // split byte to write into GPIOs
+ int value = !((status >> iteration) & 0x01);
+
+ led_lines[iteration].set_value(value);
+ }
+ return 0;
+}
diff --git a/lpcsnoop/snoop.hpp b/lpcsnoop/snoop.hpp
index 34d53b1..dab3334 100644
--- a/lpcsnoop/snoop.hpp
+++ b/lpcsnoop/snoop.hpp
@@ -5,9 +5,9 @@
#include <xyz/openbmc_project/State/Boot/Raw/server.hpp>
/* The LPC snoop on port 80h is mapped to this dbus path. */
-#define SNOOP_OBJECTPATH "/xyz/openbmc_project/state/boot/raw0"
+constexpr char snoopObject[] = "/xyz/openbmc_project/state/boot/raw0";
/* The LPC snoop on port 80h is mapped to this dbus service. */
-#define SNOOP_BUSNAME "xyz.openbmc_project.State.Boot.Raw"
+constexpr char snoopDbus[] = "xyz.openbmc_project.State.Boot.Raw";
template <typename... T>
using ServerObject = typename sdbusplus::server::object::object<T...>;
diff --git a/lpcsnoop/snoop_listen.hpp b/lpcsnoop/snoop_listen.hpp
index 174e664..876c119 100644
--- a/lpcsnoop/snoop_listen.hpp
+++ b/lpcsnoop/snoop_listen.hpp
@@ -30,7 +30,7 @@
using namespace sdbusplus::bus::match::rules;
return type::signal() + interface("org.freedesktop.DBus.Properties") +
- member("PropertiesChanged") + path(SNOOP_OBJECTPATH);
+ member("PropertiesChanged") + path(snoopObject);
}
class SnoopListen
@@ -78,7 +78,7 @@
m.read(messageBusName, messageData);
- if (messageBusName == SNOOP_BUSNAME &&
+ if (messageBusName == snoopDbus &&
messageData.find(propertyKey) != messageData.end())
{
handler(get<postcode_t>(messageData[propertyKey]));
diff --git a/main.cpp b/main.cpp
index 7ba238c..461c5ed 100644
--- a/main.cpp
+++ b/main.cpp
@@ -14,6 +14,10 @@
* limitations under the License.
*/
+#ifdef ENABLE_IPMI_SNOOP
+#include "ipmisnoop/ipmisnoop.hpp"
+#endif
+
#include "lpcsnoop/snoop.hpp"
#include <endian.h>
@@ -32,10 +36,63 @@
#include <sdeventplus/source/event.hpp>
#include <sdeventplus/source/io.hpp>
#include <sdeventplus/source/signal.hpp>
+#include <span>
#include <stdplus/signal.hpp>
#include <thread>
+#ifdef ENABLE_IPMI_SNOOP
+#include <xyz/openbmc_project/State/Boot/Raw/server.hpp>
+#endif
+
static size_t codeSize = 1; /* Size of each POST code in bytes */
+const char* defaultHostInstances = "0";
+const uint8_t minPositionVal = 0;
+const uint8_t maxPositionVal = 5;
+
+#ifdef ENABLE_IPMI_SNOOP
+std::vector<std::unique_ptr<IpmiPostReporter>> reporters;
+#endif
+
+#ifdef ENABLE_IPMI_SNOOP
+void IpmiPostReporter::getSelectorPositionSignal(sdbusplus::bus::bus& bus)
+{
+ size_t posVal = 0;
+
+ matchSignal = std::make_unique<sdbusplus::bus::match_t>(
+ bus,
+ sdbusplus::bus::match::rules::propertiesChanged(selectorObject,
+ selectorIface),
+ [&](sdbusplus::message::message& msg) {
+ std::string objectName;
+ std::map<std::string, Selector::PropertiesVariant> msgData;
+ msg.read(objectName, msgData);
+
+ auto valPropMap = msgData.find("Position");
+ {
+ if (valPropMap == msgData.end())
+ {
+ std::cerr << "Position property not found " << std::endl;
+ return;
+ }
+
+ posVal = std::get<size_t>(valPropMap->second);
+
+ if (posVal > minPositionVal && posVal < maxPositionVal)
+ {
+ std::tuple<uint64_t, secondary_post_code_t> postcodes =
+ reporters[posVal - 1]->value();
+ uint64_t postcode = std::get<uint64_t>(postcodes);
+
+ // write postcode into seven segment display
+ if (postCodeDisplay(postcode) < 0)
+ {
+ fprintf(stderr, "Error in display the postcode\n");
+ }
+ }
+ }
+ });
+}
+#endif
static void usage(const char* name)
{
@@ -44,8 +101,9 @@
" -b, --bytes <SIZE> set POST code length to <SIZE> bytes. "
"Default is %zu\n"
" -d, --device <DEVICE> use <DEVICE> file.\n"
+ " -h, --host <host instances> . Default is '%s'\n"
" -v, --verbose Prints verbose information while running\n\n",
- name, codeSize);
+ name, codeSize, defaultHostInstances);
}
/*
@@ -92,6 +150,53 @@
s.get_event().exit(1);
}
+#ifdef ENABLE_IPMI_SNOOP
+// handle muti-host D-bus
+int postCodeIpmiHandler(const std::string& snoopObject,
+ const std::string& snoopDbus, sdbusplus::bus::bus& bus,
+ std::span<std::string> host)
+{
+ int ret = 0;
+
+ try
+ {
+ for (size_t iteration = 0; iteration < host.size(); iteration++)
+ {
+ std::string objPathInst = snoopObject + host[iteration];
+
+ sdbusplus::server::manager_t m{bus, objPathInst.c_str()};
+
+ /* Create a monitor object and let it do all the rest */
+ reporters.emplace_back(
+ std::make_unique<IpmiPostReporter>(bus, objPathInst.c_str()));
+
+ reporters[iteration]->emit_object_added();
+ }
+
+ bus.request_name(snoopDbus.c_str());
+ reporters[0]->getSelectorPositionSignal(bus);
+ }
+ catch (const std::exception& e)
+ {
+ fprintf(stderr, "%s\n", e.what());
+ }
+
+ // Configure seven segment dsiplay connected to GPIOs as output
+ ret = configGPIODirOutput();
+ if (ret < 0)
+ {
+ fprintf(stderr, "Failed find the gpio line\n");
+ }
+
+ while (true)
+ {
+ bus.process_discard();
+ bus.wait();
+ }
+ exit(EXIT_SUCCESS);
+}
+#endif
+
/*
* TODO(venture): this only listens one of the possible snoop ports, but
* doesn't share the namespace.
@@ -101,10 +206,17 @@
*/
int main(int argc, char* argv[])
{
- int rc = 0;
- int opt;
- int postFd = -1;
+#ifndef ENABLE_IPMI_SNOOP
+ int postFd = -1;
+#endif
+
+ int opt;
+ bool verbose = false;
+
+#ifdef ENABLE_IPMI_SNOOP
+ std::vector<std::string> host;
+#endif
/*
* These string constants are only used in this method within this object
* and this object is the only object feeding into the final binary.
@@ -112,28 +224,42 @@
* If however, another object is added to this binary it would be proper
* to move these declarations to be global and extern to the other object.
*/
- const char* snoopObject = SNOOP_OBJECTPATH;
- const char* snoopDbus = SNOOP_BUSNAME;
-
- bool deferSignals = true;
- bool verbose = false;
// clang-format off
static const struct option long_options[] = {
+ #ifdef ENABLE_IPMI_SNOOP
+ {"host", optional_argument, NULL, 'h'},
+ #endif
{"bytes", required_argument, NULL, 'b'},
+ #ifndef ENABLE_IPMI_SNOOP
{"device", optional_argument, NULL, 'd'},
+ #endif
{"verbose", no_argument, NULL, 'v'},
{0, 0, 0, 0}
};
// clang-format on
- while ((opt = getopt_long(argc, argv, "b:d:v", long_options, NULL)) != -1)
+ while ((opt = getopt_long(argc, argv, "h:b:d:v", long_options, NULL)) != -1)
{
switch (opt)
{
case 0:
break;
- case 'b':
+#ifdef ENABLE_IPMI_SNOOP
+ case 'h': {
+ std::string_view instances = optarg;
+ size_t pos = 0;
+
+ while ((pos = instances.find(" ")) != std::string::npos)
+ {
+ host.emplace_back(instances.substr(0, pos));
+ instances.remove_prefix(pos + 1);
+ }
+ host.emplace_back(instances);
+ break;
+ }
+#endif
+ case 'b': {
codeSize = atoi(optarg);
if (codeSize < 1 || codeSize > 8)
@@ -145,26 +271,44 @@
exit(EXIT_FAILURE);
}
break;
+ }
+#ifndef ENABLE_IPMI_SNOOP
case 'd':
+
postFd = open(optarg, O_NONBLOCK);
if (postFd < 0)
{
fprintf(stderr, "Unable to open: %s\n", optarg);
return -1;
}
-
break;
+#endif
case 'v':
verbose = true;
break;
default:
usage(argv[0]);
- exit(EXIT_FAILURE);
}
}
auto bus = sdbusplus::bus::new_default();
+#ifdef ENABLE_IPMI_SNOOP
+ std::cout << "Verbose = " << verbose << std::endl;
+ int ret = postCodeIpmiHandler(ipmiSnoopObject, snoopDbus, bus, host);
+ if (ret < 0)
+ {
+ fprintf(stderr, "Error in postCodeIpmiHandler\n");
+ return ret;
+ }
+ return 0;
+#endif
+
+#ifndef ENABLE_IPMI_SNOOP
+ int rc = 0;
+
+ bool deferSignals = true;
+
// Add systemd object manager.
sdbusplus::server::manager::manager snoopdManager(bus, snoopObject);
@@ -175,11 +319,11 @@
// Create sdevent and add IO source
try
{
- auto event = sdeventplus::Event::get_default();
- std::optional<sdeventplus::source::IO> reporterSource;
+ sdeventplus::Event event = sdeventplus::Event::get_default();
if (postFd > 0)
{
- reporterSource.emplace(
+
+ sdeventplus::source::IO reporterSource(
event, postFd, EPOLLIN | EPOLLET,
std::bind_front(PostCodeEventHandler, &reporter, verbose));
}
@@ -207,4 +351,5 @@
}
return rc;
+#endif
}
diff --git a/meson.build b/meson.build
index 2bafd48..8959cfb 100644
--- a/meson.build
+++ b/meson.build
@@ -19,12 +19,18 @@
sdbusplus = dependency('sdbusplus')
sdeventplus = dependency('sdeventplus')
systemd = dependency('systemd')
+libgpiodcxx = dependency('libgpiodcxx')
conf_data = configuration_data()
conf_data.set('bindir', get_option('prefix') / get_option('bindir'))
conf_data.set('SYSTEMD_TARGET', get_option('systemd-target'))
snoopd_args = '-b ' + get_option('post-code-bytes').to_string()
+if not get_option('snoop').disabled()
+ add_project_arguments('-DENABLE_IPMI_SNOOP',language:'cpp')
+ snoopd_args += ' -h "' + get_option('host-instances') + '"'
+endif
+
if get_option('snoop-device') != ''
snoopd_args += ' -d /dev/' + get_option('snoop-device')
endif
@@ -44,6 +50,7 @@
sdbusplus,
sdeventplus,
phosphor_dbus_interfaces,
+ libgpiodcxx,
],
install: true,
)
diff --git a/meson_options.txt b/meson_options.txt
index 763c73e..f1c0cf1 100644
--- a/meson_options.txt
+++ b/meson_options.txt
@@ -10,6 +10,17 @@
value: 1,
)
option(
+ 'host-instances',
+ description: 'obmc instances of the host',
+ type: 'string',
+)
+option(
+ 'snoop',
+ type: 'feature',
+ description: 'Compile time flag to enable Ipmi snoop.',
+ value: 'disabled',
+)
+option(
'systemd-target',
description: 'Target for starting this service.',
type: 'string'
diff --git a/test/post_reporter_test.cpp b/test/post_reporter_test.cpp
index 5213593..0a13769 100644
--- a/test/post_reporter_test.cpp
+++ b/test/post_reporter_test.cpp
@@ -35,32 +35,32 @@
{
EXPECT_CALL(bus_mock,
- sd_bus_emit_object_added(IsNull(), StrEq(SNOOP_OBJECTPATH)))
+ sd_bus_emit_object_added(IsNull(), StrEq(snoopObject)))
.WillOnce(Return(0));
- PostReporter testReporter(bus, SNOOP_OBJECTPATH, true);
+ PostReporter testReporter(bus, snoopObject, true);
testReporter.emit_object_added();
}
TEST_F(PostReporterTest, AddsObjectWithExpectedName)
{
EXPECT_CALL(bus_mock,
- sd_bus_add_object_vtable(IsNull(), _, StrEq(SNOOP_OBJECTPATH),
- StrEq(SNOOP_BUSNAME), _, _))
+ sd_bus_add_object_vtable(IsNull(), _, StrEq(snoopObject),
+ StrEq(snoopDbus), _, _))
.WillOnce(Return(0));
- PostReporter testReporter(bus, SNOOP_OBJECTPATH, true);
+ PostReporter testReporter(bus, snoopObject, true);
}
TEST_F(PostReporterTest, ValueReadsDefaultToZero)
{
- PostReporter testReporter(bus, SNOOP_OBJECTPATH, true);
+ PostReporter testReporter(bus, snoopObject, true);
EXPECT_EQ(0, std::get<primary_post_code_t>(testReporter.value()));
}
TEST_F(PostReporterTest, SetValueToPositiveValueWorks)
{
- PostReporter testReporter(bus, SNOOP_OBJECTPATH, true);
+ PostReporter testReporter(bus, snoopObject, true);
secondary_post_code_t secondaryCode = {123, 124, 125};
testReporter.value(std::make_tuple(65537, secondaryCode));
EXPECT_EQ(65537, std::get<primary_post_code_t>(testReporter.value()));
@@ -70,7 +70,7 @@
TEST_F(PostReporterTest, SetValueMultipleTimesWorks)
{
- PostReporter testReporter(bus, SNOOP_OBJECTPATH, true);
+ PostReporter testReporter(bus, snoopObject, true);
secondary_post_code_t secondaryCode = {10, 40, 0, 245, 56};
testReporter.value(std::make_tuple(123, secondaryCode));
EXPECT_EQ(123, std::get<primary_post_code_t>(testReporter.value()));