xref: /openbmc/phosphor-power/tools/power-utils/version.cpp (revision 14572cf45978ef61b0fe2627b0d4e1173893e612)
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"
22*14572cf4SShawn McCarney #include "utils.hpp"
230bf1b782SLei YU 
24*14572cf4SShawn 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 
32*14572cf4SShawn 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 {
38*14572cf4SShawn McCarney 
39*14572cf4SShawn McCarney namespace internal
400bf1b782SLei YU {
415dce1a74SFaisal Awada 
420bf1b782SLei YU PsuVersionInfo getVersionInfo(const std::string& psuInventoryPath)
430bf1b782SLei YU {
44*14572cf4SShawn McCarney     auto data = loadJSONFromFile(PSU_JSON_PATH);
450bf1b782SLei YU 
460bf1b782SLei YU     if (data == nullptr)
470bf1b782SLei YU     {
480bf1b782SLei YU         return {};
490bf1b782SLei YU     }
500bf1b782SLei YU 
510bf1b782SLei YU     auto devices = data.find("psuDevices");
520bf1b782SLei YU     if (devices == data.end())
530bf1b782SLei YU     {
540bf1b782SLei YU         log<level::WARNING>("Unable to find psuDevices");
550bf1b782SLei YU         return {};
560bf1b782SLei YU     }
570bf1b782SLei YU     auto devicePath = devices->find(psuInventoryPath);
580bf1b782SLei YU     if (devicePath == devices->end())
590bf1b782SLei YU     {
600bf1b782SLei YU         log<level::WARNING>("Unable to find path for PSU",
610bf1b782SLei YU                             entry("PATH=%s", psuInventoryPath.c_str()));
620bf1b782SLei YU         return {};
630bf1b782SLei YU     }
640bf1b782SLei YU 
65*14572cf4SShawn McCarney     auto type = getPMBusAccessType(data);
660bf1b782SLei YU 
670bf1b782SLei YU     std::string versionStr;
680bf1b782SLei YU     for (const auto& fru : data["fruConfigs"])
690bf1b782SLei YU     {
700bf1b782SLei YU         if (fru["propertyName"] == "Version")
710bf1b782SLei YU         {
72c0b9e9e4SMatt Spinler             versionStr = fru["fileName"].get<std::string>();
730bf1b782SLei YU             break;
740bf1b782SLei YU         }
750bf1b782SLei YU     }
760bf1b782SLei YU     if (versionStr.empty())
770bf1b782SLei YU     {
780bf1b782SLei YU         log<level::WARNING>("Unable to find Version file");
790bf1b782SLei YU         return {};
800bf1b782SLei YU     }
810bf1b782SLei YU     return std::make_tuple(*devicePath, type, versionStr);
820bf1b782SLei YU }
83093b5917SLei YU 
84093b5917SLei YU // A default implemention compare the string itself
85093b5917SLei YU std::string getLatestDefault(const std::vector<std::string>& versions)
86093b5917SLei YU {
87093b5917SLei YU     std::string latest;
88093b5917SLei YU     for (const auto& version : versions)
89093b5917SLei YU     {
90093b5917SLei YU         if (latest < version)
91093b5917SLei YU         {
92093b5917SLei YU             latest = version;
93093b5917SLei YU         }
94093b5917SLei YU     }
95093b5917SLei YU     return latest;
96093b5917SLei YU }
97093b5917SLei YU 
98*14572cf4SShawn McCarney } // namespace internal
990bf1b782SLei YU 
1000bf1b782SLei YU std::string getVersion(const std::string& psuInventoryPath)
1010bf1b782SLei YU {
102f5402197SPatrick Williams     const auto& [devicePath, type, versionStr] =
103*14572cf4SShawn McCarney         internal::getVersionInfo(psuInventoryPath);
1040bf1b782SLei YU     if (devicePath.empty() || versionStr.empty())
1050bf1b782SLei YU     {
1065dce1a74SFaisal Awada         return "";
1070bf1b782SLei YU     }
1080bf1b782SLei YU     std::string version;
1090bf1b782SLei YU     try
1100bf1b782SLei YU     {
1110bf1b782SLei YU         phosphor::pmbus::PMBus pmbus(devicePath);
1120bf1b782SLei YU         version = pmbus.readString(versionStr, type);
1130bf1b782SLei YU     }
1140bf1b782SLei YU     catch (const std::exception& ex)
1150bf1b782SLei YU     {
1160bf1b782SLei YU         log<level::ERR>(ex.what());
1170bf1b782SLei YU     }
1180bf1b782SLei YU     return version;
1190bf1b782SLei YU }
1200bf1b782SLei YU 
1215dce1a74SFaisal Awada std::string getVersion(sdbusplus::bus_t& bus,
1225dce1a74SFaisal Awada                        const std::string& psuInventoryPath)
1235dce1a74SFaisal Awada {
12437c2612bSShawn McCarney     std::string version;
1255dce1a74SFaisal Awada     try
1265dce1a74SFaisal Awada     {
127*14572cf4SShawn McCarney         const auto& [i2cbus, i2caddr] = getPsuI2c(bus, psuInventoryPath);
128*14572cf4SShawn McCarney         auto pmbus = getPmbusIntf(i2cbus, i2caddr);
12937c2612bSShawn McCarney         std::string name = "fw_version";
13037c2612bSShawn McCarney         auto type = phosphor::pmbus::Type::HwmonDeviceDebug;
13137c2612bSShawn McCarney         version = pmbus->readString(name, type);
1325dce1a74SFaisal Awada     }
1335dce1a74SFaisal Awada     catch (const std::exception& e)
1345dce1a74SFaisal Awada     {
1355dce1a74SFaisal Awada         log<level::ERR>(std::format("Error: {}", e.what()).c_str());
1365dce1a74SFaisal Awada     }
13737c2612bSShawn McCarney     return version;
1385dce1a74SFaisal Awada }
1395dce1a74SFaisal Awada 
140093b5917SLei YU std::string getLatest(const std::vector<std::string>& versions)
141093b5917SLei YU {
142093b5917SLei YU     // TODO: when multiple PSU/Machines are supported, add configuration options
143093b5917SLei YU     // to implement machine-specific logic.
144093b5917SLei YU     // For now IBM AC Servers and Inspur FP5280G2 are supported.
145093b5917SLei YU     //
146093b5917SLei YU     // IBM AC servers' PSU version has two types:
147093b5917SLei YU     // * XXXXYYYYZZZZ: XXXX is the primary version
148093b5917SLei YU     //                 YYYY is the secondary version
149093b5917SLei YU     //                 ZZZZ is the communication version
150093b5917SLei YU     //
151093b5917SLei YU     // * XXXXYYYY:     XXXX is the primary version
152093b5917SLei YU     //                 YYYY is the seconday version
153093b5917SLei YU     //
154093b5917SLei YU     // Inspur FP5280G2 PSU version is human readable text and a larger string
155093b5917SLei YU     // means a newer version.
156093b5917SLei YU     //
157093b5917SLei YU     // So just compare by strings is OK for these cases
158*14572cf4SShawn McCarney     return internal::getLatestDefault(versions);
159093b5917SLei YU }
1600bf1b782SLei YU } // namespace version
161