/** * Copyright © 2019 IBM Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "config.h" #include "version.hpp" #include "pmbus.hpp" #include "utility.hpp" #include "utils.hpp" #include #include #include #include using json = nlohmann::json; using namespace utils; using namespace phosphor::logging; using namespace phosphor::power::util; namespace version { namespace internal { // PsuInfo contains the device path, PMBus access type, and sysfs file name using PsuVersionInfo = std::tuple; /** * @brief Get PSU version information * * @param[in] psuInventoryPath - The PSU inventory path. * * @return tuple - device path, PMBus access type, and sysfs file name */ PsuVersionInfo getVersionInfo(const std::string& psuInventoryPath) { auto data = loadJSONFromFile(PSU_JSON_PATH); if (data == nullptr) { return {}; } auto devices = data.find("psuDevices"); if (devices == data.end()) { log("Unable to find psuDevices"); return {}; } auto devicePath = devices->find(psuInventoryPath); if (devicePath == devices->end()) { log("Unable to find path for PSU", entry("PATH=%s", psuInventoryPath.c_str())); return {}; } auto type = getPMBusAccessType(data); std::string fileName; for (const auto& fru : data["fruConfigs"]) { if (fru.contains("propertyName") && (fru["propertyName"] == "Version") && fru.contains("fileName")) { fileName = fru["fileName"]; break; } } if (fileName.empty()) { log("Unable to find Version file"); return {}; } return std::make_tuple(*devicePath, type, fileName); } /** * @brief Get the PSU version from sysfs. * * Obtain PSU information from the PSU JSON file. * * Throws an exception if an error occurs. * * @param[in] psuInventoryPath - PSU D-Bus inventory path * * @return PSU version, or "" if none found */ std::string getVersionJson(const std::string& psuInventoryPath) { // Get PSU device path, PMBus access type, and sysfs file name from JSON const auto [devicePath, type, fileName] = getVersionInfo(psuInventoryPath); // Read version from sysfs file std::string version; if (!devicePath.empty() && !fileName.empty()) { phosphor::pmbus::PMBus pmbus(devicePath); version = pmbus.readString(fileName, type); } return version; } /** * @brief Get the PSU version from sysfs. * * Obtain PSU information from D-Bus. * * Throws an exception if an error occurs. * * @param[in] bus - D-Bus connection * @param[in] psuInventoryPath - PSU D-Bus inventory path * * @return PSU version, or "" if none found */ std::string getVersionDbus(sdbusplus::bus_t& bus, const std::string& psuInventoryPath) { // Get PSU I2C bus/address and create PMBus interface const auto [i2cbus, i2caddr] = getPsuI2c(bus, psuInventoryPath); auto pmbus = getPmbusIntf(i2cbus, i2caddr); // Read version from sysfs file std::string name = "fw_version"; auto type = phosphor::pmbus::Type::HwmonDeviceDebug; std::string version = pmbus->readString(name, type); return version; } /** * @brief Get firmware latest version * * @param[in] versions - String of versions * * @return version - latest firmware level */ std::string getLatestDefault(const std::vector& versions) { std::string latest; for (const auto& version : versions) { if (latest < version) { latest = version; } } return latest; } } // namespace internal std::string getVersion(sdbusplus::bus_t& bus, const std::string& psuInventoryPath) { std::string version; try { if (usePsuJsonFile()) { // Obtain PSU information from JSON file version = internal::getVersionJson(psuInventoryPath); } else { // Obtain PSU information from D-Bus version = internal::getVersionDbus(bus, psuInventoryPath); } } catch (const std::exception& e) { log(std::format("Error: {}", e.what()).c_str()); } return version; } std::string getLatest(const std::vector& versions) { // TODO: when multiple PSU/Machines are supported, add configuration options // to implement machine-specific logic. // For now IBM AC Servers and Inspur FP5280G2 are supported. // // IBM AC servers' PSU version has two types: // * XXXXYYYYZZZZ: XXXX is the primary version // YYYY is the secondary version // ZZZZ is the communication version // // * XXXXYYYY: XXXX is the primary version // YYYY is the seconday version // // Inspur FP5280G2 PSU version is human readable text and a larger string // means a newer version. // // So just compare by strings is OK for these cases return internal::getLatestDefault(versions); } } // namespace version