From 4d8c6046b4fa508b9693a8fbc8d567358f55891f Mon Sep 17 00:00:00 2001 From: James Feist Date: Fri, 27 Sep 2019 10:38:08 -0700 Subject: 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 --- hsbp-manager/src/hsbp_manager.cpp | 301 +++++++++++++++++++++++++++++++++++--- 1 file 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 #include #include +#include extern "C" { #include @@ -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(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(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 itemIface; + std::shared_ptr 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(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(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(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(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(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(~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(~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(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(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 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 hsbpItemIface; - std::vector> - driveItemIfaces; + std::shared_ptr versionIface; + + std::vector drives; }; std::unordered_map backplanes; @@ -135,6 +394,7 @@ void populate() backplanes.clear(); std::optional bus; std::optional address; + std::optional backplaneIndex; std::optional name; for (const auto& [key, value] : resp) { @@ -146,19 +406,24 @@ void populate() { address = std::get(value); } + else if (key == "Index") + { + backplaneIndex = std::get(value); + } else if (key == "Name") { name = std::get(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", -- cgit v1.2.3