xref: /openbmc/phosphor-power/tools/power-utils/version.cpp (revision 23dee383ca3a45889fbb4f7bbae65fe3325c5805)
1 /**
2  * Copyright © 2019 IBM Corporation
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 #include "config.h"
17 
18 #include "version.hpp"
19 
20 #include "pmbus.hpp"
21 #include "utility.hpp"
22 #include "utils.hpp"
23 
24 #include <nlohmann/json.hpp>
25 #include <phosphor-logging/log.hpp>
26 
27 #include <exception>
28 #include <tuple>
29 
30 using json = nlohmann::json;
31 
32 using namespace utils;
33 using namespace phosphor::logging;
34 using namespace phosphor::power::util;
35 
36 namespace version
37 {
38 
39 namespace internal
40 {
41 
42 // PsuInfo contains the device path, PMBus access type, and sysfs file name
43 using PsuVersionInfo =
44     std::tuple<std::string, phosphor::pmbus::Type, std::string>;
45 
46 /**
47  * @brief Get PSU version information
48  *
49  * @param[in] psuInventoryPath - The PSU inventory path.
50  *
51  * @return tuple - device path, PMBus access type, and sysfs file name
52  */
getVersionInfo(const std::string & psuInventoryPath)53 PsuVersionInfo getVersionInfo(const std::string& psuInventoryPath)
54 {
55     auto data = loadJSONFromFile(PSU_JSON_PATH);
56 
57     if (data == nullptr)
58     {
59         return {};
60     }
61 
62     auto devices = data.find("psuDevices");
63     if (devices == data.end())
64     {
65         log<level::WARNING>("Unable to find psuDevices");
66         return {};
67     }
68     auto devicePath = devices->find(psuInventoryPath);
69     if (devicePath == devices->end())
70     {
71         log<level::WARNING>("Unable to find path for PSU",
72                             entry("PATH=%s", psuInventoryPath.c_str()));
73         return {};
74     }
75 
76     auto type = getPMBusAccessType(data);
77 
78     std::string fileName;
79     for (const auto& fru : data["fruConfigs"])
80     {
81         if (fru.contains("propertyName") &&
82             (fru["propertyName"] == "Version") && fru.contains("fileName"))
83         {
84             fileName = fru["fileName"];
85             break;
86         }
87     }
88     if (fileName.empty())
89     {
90         log<level::WARNING>("Unable to find Version file");
91         return {};
92     }
93     return std::make_tuple(*devicePath, type, fileName);
94 }
95 
96 /**
97  * @brief Get the PSU version from sysfs.
98  *
99  * Obtain PSU information from the PSU JSON file.
100  *
101  * Throws an exception if an error occurs.
102  *
103  * @param[in] psuInventoryPath - PSU D-Bus inventory path
104  *
105  * @return PSU version, or "" if none found
106  */
getVersionJson(const std::string & psuInventoryPath)107 std::string getVersionJson(const std::string& psuInventoryPath)
108 {
109     // Get PSU device path, PMBus access type, and sysfs file name from JSON
110     const auto [devicePath, type, fileName] = getVersionInfo(psuInventoryPath);
111 
112     // Read version from sysfs file
113     std::string version;
114     if (!devicePath.empty() && !fileName.empty())
115     {
116         phosphor::pmbus::PMBus pmbus(devicePath);
117         version = pmbus.readString(fileName, type);
118     }
119     return version;
120 }
121 
122 /**
123  * @brief Get the PSU version from sysfs.
124  *
125  * Obtain PSU information from D-Bus.
126  *
127  * Throws an exception if an error occurs.
128  *
129  * @param[in] bus - D-Bus connection
130  * @param[in] psuInventoryPath - PSU D-Bus inventory path
131  *
132  * @return PSU version, or "" if none found
133  */
getVersionDbus(sdbusplus::bus_t & bus,const std::string & psuInventoryPath)134 std::string getVersionDbus(sdbusplus::bus_t& bus,
135                            const std::string& psuInventoryPath)
136 {
137     // Get PSU I2C bus/address and create PMBus interface
138     const auto [i2cbus, i2caddr] = getPsuI2c(bus, psuInventoryPath);
139     auto pmbus = getPmbusIntf(i2cbus, i2caddr);
140 
141     // Read version from sysfs file
142     std::string name = "fw_version";
143     auto type = phosphor::pmbus::Type::HwmonDeviceDebug;
144     std::string version = pmbus->readString(name, type);
145     return version;
146 }
147 
148 /**
149  * @brief Get firmware latest version
150  *
151  * @param[in] versions - String of versions
152  *
153  * @return version - latest firmware level
154  */
getLatestDefault(const std::vector<std::string> & versions)155 std::string getLatestDefault(const std::vector<std::string>& versions)
156 {
157     std::string latest;
158     for (const auto& version : versions)
159     {
160         if (latest < version)
161         {
162             latest = version;
163         }
164     }
165     return latest;
166 }
167 
168 } // namespace internal
169 
getVersion(sdbusplus::bus_t & bus,const std::string & psuInventoryPath)170 std::string getVersion(sdbusplus::bus_t& bus,
171                        const std::string& psuInventoryPath)
172 {
173     std::string version;
174     try
175     {
176         if (usePsuJsonFile())
177         {
178             // Obtain PSU information from JSON file
179             version = internal::getVersionJson(psuInventoryPath);
180         }
181         else
182         {
183             // Obtain PSU information from D-Bus
184             version = internal::getVersionDbus(bus, psuInventoryPath);
185         }
186     }
187     catch (const std::exception& e)
188     {
189         log<level::ERR>(std::format("Error: {}", e.what()).c_str());
190     }
191     return version;
192 }
193 
getLatest(const std::vector<std::string> & versions)194 std::string getLatest(const std::vector<std::string>& versions)
195 {
196     // TODO: when multiple PSU/Machines are supported, add configuration options
197     // to implement machine-specific logic.
198     // For now IBM AC Servers and Inspur FP5280G2 are supported.
199     //
200     // IBM AC servers' PSU version has two types:
201     // * XXXXYYYYZZZZ: XXXX is the primary version
202     //                 YYYY is the secondary version
203     //                 ZZZZ is the communication version
204     //
205     // * XXXXYYYY:     XXXX is the primary version
206     //                 YYYY is the seconday version
207     //
208     // Inspur FP5280G2 PSU version is human readable text and a larger string
209     // means a newer version.
210     //
211     // So just compare by strings is OK for these cases
212     return internal::getLatestDefault(versions);
213 }
214 
215 } // namespace version
216