diff options
author | James Feist <james.feist@linux.intel.com> | 2019-09-27 20:38:08 +0300 |
---|---|---|
committer | James Feist <james.feist@linux.intel.com> | 2019-10-03 02:46:39 +0300 |
commit | 4d8c6046b4fa508b9693a8fbc8d567358f55891f (patch) | |
tree | e26c05c8dd3f20d85597bab23ed22fc25a9efd45 | |
parent | 86e30d6c5088cce8cb315617615c7f1698d1c995 (diff) | |
download | provingground-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.cpp | 301 |
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", |