xref: /openbmc/phosphor-power/tools/power-utils/version.cpp (revision 23dee383ca3a45889fbb4f7bbae65fe3325c5805)
10bf1b782SLei YU /**
20bf1b782SLei YU  * Copyright © 2019 IBM Corporation
30bf1b782SLei YU  *
40bf1b782SLei YU  * Licensed under the Apache License, Version 2.0 (the "License");
50bf1b782SLei YU  * you may not use this file except in compliance with the License.
60bf1b782SLei YU  * You may obtain a copy of the License at
70bf1b782SLei YU  *
80bf1b782SLei YU  *     http://www.apache.org/licenses/LICENSE-2.0
90bf1b782SLei YU  *
100bf1b782SLei YU  * Unless required by applicable law or agreed to in writing, software
110bf1b782SLei YU  * distributed under the License is distributed on an "AS IS" BASIS,
120bf1b782SLei YU  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
130bf1b782SLei YU  * See the License for the specific language governing permissions and
140bf1b782SLei YU  * limitations under the License.
150bf1b782SLei YU  */
160bf1b782SLei YU #include "config.h"
170bf1b782SLei YU 
180bf1b782SLei YU #include "version.hpp"
190bf1b782SLei YU 
200bf1b782SLei YU #include "pmbus.hpp"
210bf1b782SLei YU #include "utility.hpp"
2214572cf4SShawn McCarney #include "utils.hpp"
230bf1b782SLei YU 
2414572cf4SShawn McCarney #include <nlohmann/json.hpp>
250bf1b782SLei YU #include <phosphor-logging/log.hpp>
26d1bc4cecSBrandon Wyman 
275dce1a74SFaisal Awada #include <exception>
280bf1b782SLei YU #include <tuple>
290bf1b782SLei YU 
300bf1b782SLei YU using json = nlohmann::json;
310bf1b782SLei YU 
3214572cf4SShawn McCarney using namespace utils;
330bf1b782SLei YU using namespace phosphor::logging;
345dce1a74SFaisal Awada using namespace phosphor::power::util;
350bf1b782SLei YU 
365dce1a74SFaisal Awada namespace version
375dce1a74SFaisal Awada {
3814572cf4SShawn McCarney 
3914572cf4SShawn McCarney namespace internal
400bf1b782SLei YU {
415dce1a74SFaisal Awada 
42*23dee383SShawn McCarney // PsuInfo contains the device path, PMBus access type, and sysfs file name
43*23dee383SShawn McCarney using PsuVersionInfo =
44*23dee383SShawn McCarney     std::tuple<std::string, phosphor::pmbus::Type, std::string>;
45*23dee383SShawn McCarney 
46*23dee383SShawn McCarney /**
47*23dee383SShawn McCarney  * @brief Get PSU version information
48*23dee383SShawn McCarney  *
49*23dee383SShawn McCarney  * @param[in] psuInventoryPath - The PSU inventory path.
50*23dee383SShawn McCarney  *
51*23dee383SShawn McCarney  * @return tuple - device path, PMBus access type, and sysfs file name
52*23dee383SShawn McCarney  */
getVersionInfo(const std::string & psuInventoryPath)530bf1b782SLei YU PsuVersionInfo getVersionInfo(const std::string& psuInventoryPath)
540bf1b782SLei YU {
5514572cf4SShawn McCarney     auto data = loadJSONFromFile(PSU_JSON_PATH);
560bf1b782SLei YU 
570bf1b782SLei YU     if (data == nullptr)
580bf1b782SLei YU     {
590bf1b782SLei YU         return {};
600bf1b782SLei YU     }
610bf1b782SLei YU 
620bf1b782SLei YU     auto devices = data.find("psuDevices");
630bf1b782SLei YU     if (devices == data.end())
640bf1b782SLei YU     {
650bf1b782SLei YU         log<level::WARNING>("Unable to find psuDevices");
660bf1b782SLei YU         return {};
670bf1b782SLei YU     }
680bf1b782SLei YU     auto devicePath = devices->find(psuInventoryPath);
690bf1b782SLei YU     if (devicePath == devices->end())
700bf1b782SLei YU     {
710bf1b782SLei YU         log<level::WARNING>("Unable to find path for PSU",
720bf1b782SLei YU                             entry("PATH=%s", psuInventoryPath.c_str()));
730bf1b782SLei YU         return {};
740bf1b782SLei YU     }
750bf1b782SLei YU 
7614572cf4SShawn McCarney     auto type = getPMBusAccessType(data);
770bf1b782SLei YU 
78*23dee383SShawn McCarney     std::string fileName;
790bf1b782SLei YU     for (const auto& fru : data["fruConfigs"])
800bf1b782SLei YU     {
81*23dee383SShawn McCarney         if (fru.contains("propertyName") &&
82*23dee383SShawn McCarney             (fru["propertyName"] == "Version") && fru.contains("fileName"))
830bf1b782SLei YU         {
84*23dee383SShawn McCarney             fileName = fru["fileName"];
850bf1b782SLei YU             break;
860bf1b782SLei YU         }
870bf1b782SLei YU     }
88*23dee383SShawn McCarney     if (fileName.empty())
890bf1b782SLei YU     {
900bf1b782SLei YU         log<level::WARNING>("Unable to find Version file");
910bf1b782SLei YU         return {};
920bf1b782SLei YU     }
93*23dee383SShawn McCarney     return std::make_tuple(*devicePath, type, fileName);
940bf1b782SLei YU }
95093b5917SLei YU 
96*23dee383SShawn McCarney /**
97*23dee383SShawn McCarney  * @brief Get the PSU version from sysfs.
98*23dee383SShawn McCarney  *
99*23dee383SShawn McCarney  * Obtain PSU information from the PSU JSON file.
100*23dee383SShawn McCarney  *
101*23dee383SShawn McCarney  * Throws an exception if an error occurs.
102*23dee383SShawn McCarney  *
103*23dee383SShawn McCarney  * @param[in] psuInventoryPath - PSU D-Bus inventory path
104*23dee383SShawn McCarney  *
105*23dee383SShawn McCarney  * @return PSU version, or "" if none found
106*23dee383SShawn McCarney  */
getVersionJson(const std::string & psuInventoryPath)107*23dee383SShawn McCarney std::string getVersionJson(const std::string& psuInventoryPath)
108*23dee383SShawn McCarney {
109*23dee383SShawn McCarney     // Get PSU device path, PMBus access type, and sysfs file name from JSON
110*23dee383SShawn McCarney     const auto [devicePath, type, fileName] = getVersionInfo(psuInventoryPath);
111*23dee383SShawn McCarney 
112*23dee383SShawn McCarney     // Read version from sysfs file
113*23dee383SShawn McCarney     std::string version;
114*23dee383SShawn McCarney     if (!devicePath.empty() && !fileName.empty())
115*23dee383SShawn McCarney     {
116*23dee383SShawn McCarney         phosphor::pmbus::PMBus pmbus(devicePath);
117*23dee383SShawn McCarney         version = pmbus.readString(fileName, type);
118*23dee383SShawn McCarney     }
119*23dee383SShawn McCarney     return version;
120*23dee383SShawn McCarney }
121*23dee383SShawn McCarney 
122*23dee383SShawn McCarney /**
123*23dee383SShawn McCarney  * @brief Get the PSU version from sysfs.
124*23dee383SShawn McCarney  *
125*23dee383SShawn McCarney  * Obtain PSU information from D-Bus.
126*23dee383SShawn McCarney  *
127*23dee383SShawn McCarney  * Throws an exception if an error occurs.
128*23dee383SShawn McCarney  *
129*23dee383SShawn McCarney  * @param[in] bus - D-Bus connection
130*23dee383SShawn McCarney  * @param[in] psuInventoryPath - PSU D-Bus inventory path
131*23dee383SShawn McCarney  *
132*23dee383SShawn McCarney  * @return PSU version, or "" if none found
133*23dee383SShawn McCarney  */
getVersionDbus(sdbusplus::bus_t & bus,const std::string & psuInventoryPath)134*23dee383SShawn McCarney std::string getVersionDbus(sdbusplus::bus_t& bus,
135*23dee383SShawn McCarney                            const std::string& psuInventoryPath)
136*23dee383SShawn McCarney {
137*23dee383SShawn McCarney     // Get PSU I2C bus/address and create PMBus interface
138*23dee383SShawn McCarney     const auto [i2cbus, i2caddr] = getPsuI2c(bus, psuInventoryPath);
139*23dee383SShawn McCarney     auto pmbus = getPmbusIntf(i2cbus, i2caddr);
140*23dee383SShawn McCarney 
141*23dee383SShawn McCarney     // Read version from sysfs file
142*23dee383SShawn McCarney     std::string name = "fw_version";
143*23dee383SShawn McCarney     auto type = phosphor::pmbus::Type::HwmonDeviceDebug;
144*23dee383SShawn McCarney     std::string version = pmbus->readString(name, type);
145*23dee383SShawn McCarney     return version;
146*23dee383SShawn McCarney }
147*23dee383SShawn McCarney 
148*23dee383SShawn McCarney /**
149*23dee383SShawn McCarney  * @brief Get firmware latest version
150*23dee383SShawn McCarney  *
151*23dee383SShawn McCarney  * @param[in] versions - String of versions
152*23dee383SShawn McCarney  *
153*23dee383SShawn McCarney  * @return version - latest firmware level
154*23dee383SShawn McCarney  */
getLatestDefault(const std::vector<std::string> & versions)155093b5917SLei YU std::string getLatestDefault(const std::vector<std::string>& versions)
156093b5917SLei YU {
157093b5917SLei YU     std::string latest;
158093b5917SLei YU     for (const auto& version : versions)
159093b5917SLei YU     {
160093b5917SLei YU         if (latest < version)
161093b5917SLei YU         {
162093b5917SLei YU             latest = version;
163093b5917SLei YU         }
164093b5917SLei YU     }
165093b5917SLei YU     return latest;
166093b5917SLei YU }
167093b5917SLei YU 
16814572cf4SShawn McCarney } // namespace internal
1690bf1b782SLei YU 
getVersion(sdbusplus::bus_t & bus,const std::string & psuInventoryPath)1705dce1a74SFaisal Awada std::string getVersion(sdbusplus::bus_t& bus,
1715dce1a74SFaisal Awada                        const std::string& psuInventoryPath)
1725dce1a74SFaisal Awada {
17337c2612bSShawn McCarney     std::string version;
1745dce1a74SFaisal Awada     try
1755dce1a74SFaisal Awada     {
176*23dee383SShawn McCarney         if (usePsuJsonFile())
177*23dee383SShawn McCarney         {
178*23dee383SShawn McCarney             // Obtain PSU information from JSON file
179*23dee383SShawn McCarney             version = internal::getVersionJson(psuInventoryPath);
180*23dee383SShawn McCarney         }
181*23dee383SShawn McCarney         else
182*23dee383SShawn McCarney         {
183*23dee383SShawn McCarney             // Obtain PSU information from D-Bus
184*23dee383SShawn McCarney             version = internal::getVersionDbus(bus, psuInventoryPath);
185*23dee383SShawn McCarney         }
1865dce1a74SFaisal Awada     }
1875dce1a74SFaisal Awada     catch (const std::exception& e)
1885dce1a74SFaisal Awada     {
1895dce1a74SFaisal Awada         log<level::ERR>(std::format("Error: {}", e.what()).c_str());
1905dce1a74SFaisal Awada     }
19137c2612bSShawn McCarney     return version;
1925dce1a74SFaisal Awada }
1935dce1a74SFaisal Awada 
getLatest(const std::vector<std::string> & versions)194093b5917SLei YU std::string getLatest(const std::vector<std::string>& versions)
195093b5917SLei YU {
196093b5917SLei YU     // TODO: when multiple PSU/Machines are supported, add configuration options
197093b5917SLei YU     // to implement machine-specific logic.
198093b5917SLei YU     // For now IBM AC Servers and Inspur FP5280G2 are supported.
199093b5917SLei YU     //
200093b5917SLei YU     // IBM AC servers' PSU version has two types:
201093b5917SLei YU     // * XXXXYYYYZZZZ: XXXX is the primary version
202093b5917SLei YU     //                 YYYY is the secondary version
203093b5917SLei YU     //                 ZZZZ is the communication version
204093b5917SLei YU     //
205093b5917SLei YU     // * XXXXYYYY:     XXXX is the primary version
206093b5917SLei YU     //                 YYYY is the seconday version
207093b5917SLei YU     //
208093b5917SLei YU     // Inspur FP5280G2 PSU version is human readable text and a larger string
209093b5917SLei YU     // means a newer version.
210093b5917SLei YU     //
211093b5917SLei YU     // So just compare by strings is OK for these cases
21214572cf4SShawn McCarney     return internal::getLatestDefault(versions);
213093b5917SLei YU }
214*23dee383SShawn McCarney 
2150bf1b782SLei YU } // namespace version
216