summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJames Feist <james.feist@linux.intel.com>2019-09-27 20:38:08 +0300
committerJames Feist <james.feist@linux.intel.com>2019-10-03 02:46:39 +0300
commit4d8c6046b4fa508b9693a8fbc8d567358f55891f (patch)
treee26c05c8dd3f20d85597bab23ed22fc25a9efd45
parent86e30d6c5088cce8cb315617615c7f1698d1c995 (diff)
downloadprovingground-4d8c6046b4fa508b9693a8fbc8d567358f55891f.tar.xz
hsbp-manager: add drive presence and software version
This adds support for reading the sw version and drive presence from the hsbp cpld. Tested: Version is available in redfish root@intel-obmc:~# busctl tree xyz.openbmc_project.HsbpManager --no-pager `-/xyz `-/xyz/openbmc_project |-/xyz/openbmc_project/inventory | `-/xyz/openbmc_project/inventory/item | |-/xyz/openbmc_project/inventory/item/drive | | |-/xyz/openbmc_project/inventory/item/drive/Drive_1 | | |-/xyz/openbmc_project/inventory/item/drive/Drive_2 | | |-/xyz/openbmc_project/inventory/item/drive/Drive_3 | | |-/xyz/openbmc_project/inventory/item/drive/Drive_4 | | |-/xyz/openbmc_project/inventory/item/drive/Drive_5 | | |-/xyz/openbmc_project/inventory/item/drive/Drive_6 | | |-/xyz/openbmc_project/inventory/item/drive/Drive_7 | | `-/xyz/openbmc_project/inventory/item/drive/Drive_8 | `-/xyz/openbmc_project/inventory/item/hsbp | `-/xyz/openbmc_project/inventory/item/hsbp/J85894_HSBP_1 root@intel-obmc:~# busctl introspect xyz.openbmc_project.HsbpManager /xyz/openbmc_project/inventory/item/hsbp/J85894_HSBP_1 --no-pager NAME TYPE SIGNATURE RESULT/VALUE FLAGS org.freedesktop.DBus.Introspectable interface - - - .Introspect method - s - org.freedesktop.DBus.Peer interface - - - .GetMachineId method - s - .Ping method - - - org.freedesktop.DBus.Properties interface - - - .Get method ss v - .GetAll method s a{sv} - .Set method ssv - - .PropertiesChanged signal sa{sv}as - - xyz.openbmc_project.Inventory.Item interface - - - .Present property b true emits-change .PrettyName property s "J85894 HSBP 1" emits-change xyz.openbmc_project.Software.Version interface - - - .Version property s "00.02.01" emits-change .Purpose property s "xyz.openbmc_project.Software.Version... emits-change Change-Id: I73fd2669f4a0e9b8e499107a960f2169f63873e3 Signed-off-by: James Feist <james.feist@linux.intel.com>
-rw-r--r--hsbp-manager/src/hsbp_manager.cpp301
1 files changed, 283 insertions, 18 deletions
diff --git a/hsbp-manager/src/hsbp_manager.cpp b/hsbp-manager/src/hsbp_manager.cpp
index 110d32d..e02b76a 100644
--- a/hsbp-manager/src/hsbp_manager.cpp
+++ b/hsbp-manager/src/hsbp_manager.cpp
@@ -23,6 +23,7 @@
#include <sdbusplus/asio/object_server.hpp>
#include <sdbusplus/bus/match.hpp>
#include <string>
+#include <utility>
extern "C" {
#include <i2c/smbus.h>
@@ -32,18 +33,60 @@ extern "C" {
constexpr const char* configType =
"xyz.openbmc_project.Configuration.Intel_HSBP_CPLD";
+constexpr size_t scanRateSeconds = 5;
+constexpr size_t maxDrives = 8; // only 1 byte alloted
+
boost::asio::io_context io;
auto conn = std::make_shared<sdbusplus::asio::connection>(io);
sdbusplus::asio::object_server objServer(conn);
-struct Backplane
+static std::string zeroPad(const uint8_t val)
{
+ std::ostringstream version;
+ version << std::setw(2) << std::setfill('0') << static_cast<size_t>(val);
+ return version.str();
+}
- Backplane(size_t busIn, size_t addressIn, const std::string& nameIn) :
- bus(busIn), address(addressIn), name(nameIn)
+struct Drive
+{
+ Drive(size_t driveIndex, bool isPresent, bool isOperational, bool nvme) :
+ isNvme(nvme)
+ {
+ constexpr const char* basePath =
+ "/xyz/openbmc_project/inventory/item/drive/Drive_";
+ itemIface = objServer.add_interface(
+ basePath + std::to_string(driveIndex), inventory::interface);
+ itemIface->register_property("Present", isPresent);
+ itemIface->register_property("PrettyName",
+ "Drive " + std::to_string(driveIndex));
+ itemIface->initialize();
+ operationalIface = objServer.add_interface(
+ basePath + std::to_string(driveIndex),
+ "xyz.openbmc_project.State.Decorator.OperationalStatus");
+ operationalIface->register_property("Functional", isOperational);
+ operationalIface->initialize();
+ }
+ ~Drive()
{
+ objServer.remove_interface(itemIface);
+ objServer.remove_interface(operationalIface);
}
+ std::shared_ptr<sdbusplus::asio::dbus_interface> itemIface;
+ std::shared_ptr<sdbusplus::asio::dbus_interface> operationalIface;
+ bool isNvme;
+};
+
+struct Backplane
+{
+
+ Backplane(size_t busIn, size_t addressIn, size_t backplaneIndexIn,
+ const std::string& nameIn) :
+ bus(busIn),
+ address(addressIn), backplaneIndex(backplaneIndexIn - 1), name(nameIn),
+ timer(std::make_shared<boost::asio::steady_timer>(io))
+ {
+ }
void run()
{
file = open(("/dev/i2c-" + std::to_string(bus)).c_str(), O_RDWR);
@@ -59,33 +102,235 @@ struct Backplane
return;
}
+ if (!getPresent())
+ {
+ std::cerr << "Cannot detect CPLD\n";
+ return;
+ }
+
+ getBootVer(bootVer);
+ getFPGAVer(fpgaVer);
+ getSecurityRev(securityRev);
+ std::string dbusName = boost::replace_all_copy(name, " ", "_");
hsbpItemIface = objServer.add_interface(
- "/xyz/openbmc_project/inventory/item/hsbp/" +
- boost::replace_all_copy(name, " ", "_"),
+ "/xyz/openbmc_project/inventory/item/hsbp/" + dbusName,
inventory::interface);
- hsbpItemIface->register_property("Present", present(true));
+ hsbpItemIface->register_property("Present", true);
hsbpItemIface->register_property("PrettyName", name);
hsbpItemIface->initialize();
- if (!present())
+ versionIface =
+ objServer.add_interface(hsbpItemIface->get_object_path(),
+ "xyz.openbmc_project.Software.Version");
+ versionIface->register_property("Version", zeroPad(bootVer) + "." +
+ zeroPad(fpgaVer) + "." +
+ zeroPad(securityRev));
+ versionIface->register_property(
+ "Purpose",
+ std::string(
+ "xyz.openbmc_project.Software.Version.VersionPurpose.HSBP"));
+ versionIface->initialize();
+ getPresence(presence);
+ getIFDET(ifdet);
+
+ createDrives();
+
+ runTimer();
+ }
+
+ void runTimer()
+ {
+ timer->expires_after(std::chrono::seconds(scanRateSeconds));
+ timer->async_wait([this](boost::system::error_code ec) {
+ if (ec == boost::asio::error::operation_aborted)
+ {
+ // we're being destroyed
+ return;
+ }
+ else if (ec)
+ {
+ std::cerr << "timer error " << ec.message() << "\n";
+ return;
+ }
+ uint8_t curPresence = 0;
+ uint8_t curIFDET = 0;
+ uint8_t curFailed = 0;
+
+ getPresence(curPresence);
+ getIFDET(curIFDET);
+ getFailed(curFailed);
+
+ if (curPresence != presence || curIFDET != ifdet ||
+ curFailed != failed)
+ {
+ presence = curPresence;
+ ifdet = curIFDET;
+ failed = curFailed;
+ updateDrives();
+ }
+ runTimer();
+ });
+ }
+
+ void createDrives()
+ {
+ uint8_t nvme = ifdet ^ presence;
+ for (size_t ii = 0; ii < maxDrives; ii++)
{
- // backplane isn't there
- return;
+ bool isNvme = nvme & (1 << ii);
+ bool isPresent = isNvme || (presence & (1 << ii));
+ bool isFailed = !isPresent || failed & (1 << ii);
+
+ // +1 to convert from 0 based to 1 based
+ size_t driveIndex = (backplaneIndex * maxDrives) + ii + 1;
+ drives.emplace_back(driveIndex, isPresent, !isFailed, isNvme);
}
}
- bool present(bool update = false)
+ void updateDrives()
{
- static bool present = false;
- if (update)
+
+ uint8_t nvme = ifdet ^ presence;
+ for (size_t ii = 0; ii < maxDrives; ii++)
{
- present = i2c_smbus_read_byte(file) >= 0;
+ bool isNvme = nvme & (1 << ii);
+ bool isPresent = isNvme || (presence & (1 << ii));
+ bool isFailed = !isPresent || failed & (1 << ii);
+
+ Drive& drive = drives[ii];
+ drive.isNvme = isNvme;
+ drive.itemIface->set_property("Present", isPresent);
+ drive.operationalIface->set_property("Functional", !isFailed);
}
+ }
+
+ bool getPresent()
+ {
+ present = i2c_smbus_read_byte(file) >= 0;
return present;
}
+
+ bool getTypeID(uint8_t& val)
+ {
+ constexpr uint8_t addr = 2;
+ int ret = i2c_smbus_read_byte_data(file, addr);
+ if (ret < 0)
+ {
+ std::cerr << "Error " << __FUNCTION__ << "\n";
+ return false;
+ }
+ val = static_cast<uint8_t>(ret);
+ return true;
+ }
+
+ bool getBootVer(uint8_t& val)
+ {
+ constexpr uint8_t addr = 3;
+ int ret = i2c_smbus_read_byte_data(file, addr);
+ if (ret < 0)
+ {
+ std::cerr << "Error " << __FUNCTION__ << "\n";
+ return false;
+ }
+ val = static_cast<uint8_t>(ret);
+ return true;
+ }
+
+ bool getFPGAVer(uint8_t& val)
+ {
+ constexpr uint8_t addr = 4;
+ int ret = i2c_smbus_read_byte_data(file, addr);
+ if (ret < 0)
+ {
+ std::cerr << "Error " << __FUNCTION__ << "\n";
+ return false;
+ }
+ val = static_cast<uint8_t>(ret);
+ return true;
+ }
+
+ bool getSecurityRev(uint8_t& val)
+ {
+ constexpr uint8_t addr = 5;
+ int ret = i2c_smbus_read_byte_data(file, addr);
+ if (ret < 0)
+ {
+ std::cerr << "Error " << __FUNCTION__ << "\n";
+ return false;
+ }
+ val = static_cast<uint8_t>(ret);
+ return true;
+ }
+
+ bool getPresence(uint8_t& val)
+ {
+ // NVMe drives do not assert PRSNTn, and as such do not get reported as
+ // PRESENT in this register
+
+ constexpr uint8_t addr = 8;
+
+ int ret = i2c_smbus_read_byte_data(file, addr);
+ if (ret < 0)
+ {
+ std::cerr << "Error " << __FUNCTION__ << "\n";
+ return false;
+ }
+ // presence is inverted
+ val = static_cast<uint8_t>(~ret);
+ return true;
+ }
+
+ bool getIFDET(uint8_t& val)
+ {
+ // This register is a bitmap of parallel GPIO pins connected to the
+ // IFDETn pin of a drive slot. SATA, SAS, and NVMe drives all assert
+ // IFDETn low when they are inserted into the HSBP.This register, in
+ // combination with the PRESENCE register, are used by the BMC to detect
+ // the presence of NVMe drives.
+
+ constexpr uint8_t addr = 9;
+
+ int ret = i2c_smbus_read_byte_data(file, addr);
+ if (ret < 0)
+ {
+ std::cerr << "Error " << __FUNCTION__ << "\n";
+ return false;
+ }
+ // ifdet is inverted
+ val = static_cast<uint8_t>(~ret);
+ return true;
+ }
+
+ bool getFailed(uint8_t& val)
+ {
+ constexpr uint8_t addr = 0xC;
+ int ret = i2c_smbus_read_byte_data(file, addr);
+ if (ret < 0)
+ {
+ std::cerr << "Error " << __FUNCTION__ << "\n";
+ return false;
+ }
+ val = static_cast<uint8_t>(ret);
+ return true;
+ }
+
+ bool getRebuild(uint8_t& val)
+ {
+ constexpr uint8_t addr = 0xD;
+ int ret = i2c_smbus_read_byte_data(file, addr);
+ if (ret < 0)
+ {
+ std::cerr << "Error " << __FUNCTION__ << "\n";
+ return false;
+ }
+ val = static_cast<uint8_t>(ret);
+ return true;
+ }
+
~Backplane()
{
objServer.remove_interface(hsbpItemIface);
+ objServer.remove_interface(versionIface);
if (file >= 0)
{
close(file);
@@ -94,13 +339,27 @@ struct Backplane
size_t bus;
size_t address;
- int file = -1;
+ size_t backplaneIndex;
std::string name;
+ std::shared_ptr<boost::asio::steady_timer> timer;
+ bool present = false;
+ uint8_t typeId = 0;
+ uint8_t bootVer = 0;
+ uint8_t fpgaVer = 0;
+ uint8_t securityRev = 0;
+ uint8_t funSupported = 0;
+ uint8_t presence = 0;
+ uint8_t ifdet = 0;
+ uint8_t failed = 0;
+
+ int file = -1;
+
std::string type;
std::shared_ptr<sdbusplus::asio::dbus_interface> hsbpItemIface;
- std::vector<std::shared_ptr<sdbusplus::asio::dbus_interface>>
- driveItemIfaces;
+ std::shared_ptr<sdbusplus::asio::dbus_interface> versionIface;
+
+ std::vector<Drive> drives;
};
std::unordered_map<std::string, Backplane> backplanes;
@@ -135,6 +394,7 @@ void populate()
backplanes.clear();
std::optional<size_t> bus;
std::optional<size_t> address;
+ std::optional<size_t> backplaneIndex;
std::optional<std::string> name;
for (const auto& [key, value] : resp)
{
@@ -146,19 +406,24 @@ void populate()
{
address = std::get<uint64_t>(value);
}
+ else if (key == "Index")
+ {
+ backplaneIndex = std::get<uint64_t>(value);
+ }
else if (key == "Name")
{
name = std::get<std::string>(value);
}
}
- if (!bus || !address || !name)
+ if (!bus || !address || !name || !backplaneIndex)
{
std::cerr << "Illegal configuration at " << path
<< "\n";
return;
}
const auto& [backplane, status] = backplanes.emplace(
- *name, Backplane(*bus, *address, *name));
+ *name,
+ Backplane(*bus, *address, *backplaneIndex, *name));
backplane->second.run();
},
owner, path, "org.freedesktop.DBus.Properties", "GetAll",