xref: /openbmc/phosphor-power/tools/power-utils/version.cpp (revision f8e8bc19fa720b6b348a3dddefd8eca7650b3e26)
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/lg2.hpp>
26 
27 #include <exception>
28 #include <tuple>
29 
30 using json = nlohmann::json;
31 
32 using namespace utils;
33 using namespace phosphor::power::util;
34 
35 namespace version
36 {
37 
38 namespace internal
39 {
40 
41 // PsuInfo contains the device path, PMBus access type, and sysfs file name
42 using PsuVersionInfo =
43     std::tuple<std::string, phosphor::pmbus::Type, std::string>;
44 
45 /**
46  * @brief Get PSU version information
47  *
48  * @param[in] psuInventoryPath - The PSU inventory path.
49  *
50  * @return tuple - device path, PMBus access type, and sysfs file name
51  */
getVersionInfo(const std::string & psuInventoryPath)52 PsuVersionInfo getVersionInfo(const std::string& psuInventoryPath)
53 {
54     auto data = loadJSONFromFile(PSU_JSON_PATH);
55 
56     if (data == nullptr)
57     {
58         return {};
59     }
60 
61     auto devices = data.find("psuDevices");
62     if (devices == data.end())
63     {
64         lg2::warning("Unable to find psuDevices");
65         return {};
66     }
67     auto devicePath = devices->find(psuInventoryPath);
68     if (devicePath == devices->end())
69     {
70         lg2::warning("Unable to find path for PSU PATH={PATH}", "PATH",
71                      psuInventoryPath);
72         return {};
73     }
74 
75     auto type = getPMBusAccessType(data);
76 
77     std::string fileName;
78     for (const auto& fru : data["fruConfigs"])
79     {
80         if (fru.contains("propertyName") &&
81             (fru["propertyName"] == "Version") && fru.contains("fileName"))
82         {
83             fileName = fru["fileName"];
84             break;
85         }
86     }
87     if (fileName.empty())
88     {
89         lg2::warning("Unable to find Version file");
90         return {};
91     }
92     return std::make_tuple(*devicePath, type, fileName);
93 }
94 
95 /**
96  * @brief Get the PSU version from sysfs.
97  *
98  * Obtain PSU information from the PSU JSON file.
99  *
100  * Throws an exception if an error occurs.
101  *
102  * @param[in] psuInventoryPath - PSU D-Bus inventory path
103  *
104  * @return PSU version, or "" if none found
105  */
getVersionJson(const std::string & psuInventoryPath)106 std::string getVersionJson(const std::string& psuInventoryPath)
107 {
108     // Get PSU device path, PMBus access type, and sysfs file name from JSON
109     const auto [devicePath, type, fileName] = getVersionInfo(psuInventoryPath);
110 
111     // Read version from sysfs file
112     std::string version;
113     if (!devicePath.empty() && !fileName.empty())
114     {
115         phosphor::pmbus::PMBus pmbus(devicePath);
116         version = pmbus.readString(fileName, type);
117     }
118     return version;
119 }
120 
121 /**
122  * @brief Get the PSU version from sysfs.
123  *
124  * Obtain PSU information from D-Bus.
125  *
126  * Throws an exception if an error occurs.
127  *
128  * @param[in] bus - D-Bus connection
129  * @param[in] psuInventoryPath - PSU D-Bus inventory path
130  *
131  * @return PSU version, or "" if none found
132  */
getVersionDbus(sdbusplus::bus_t & bus,const std::string & psuInventoryPath)133 std::string getVersionDbus(sdbusplus::bus_t& bus,
134                            const std::string& psuInventoryPath)
135 {
136     // Get PSU I2C bus/address and create PMBus interface
137     const auto [i2cbus, i2caddr] = getPsuI2c(bus, psuInventoryPath);
138     auto pmbus = getPmbusIntf(i2cbus, i2caddr);
139 
140     // Read version from sysfs file
141     std::string name = "fw_version";
142     auto type = phosphor::pmbus::Type::HwmonDeviceDebug;
143     std::string version = pmbus->readString(name, type);
144     return version;
145 }
146 
147 /**
148  * @brief Get firmware latest version
149  *
150  * @param[in] versions - String of versions
151  *
152  * @return version - latest firmware level
153  */
getLatestDefault(const std::vector<std::string> & versions)154 std::string getLatestDefault(const std::vector<std::string>& versions)
155 {
156     std::string latest;
157     for (const auto& version : versions)
158     {
159         if (latest < version)
160         {
161             latest = version;
162         }
163     }
164     return latest;
165 }
166 
167 } // namespace internal
168 
getVersion(sdbusplus::bus_t & bus,const std::string & psuInventoryPath)169 std::string getVersion(sdbusplus::bus_t& bus,
170                        const std::string& psuInventoryPath)
171 {
172     std::string version;
173     try
174     {
175         if (usePsuJsonFile())
176         {
177             // Obtain PSU information from JSON file
178             version = internal::getVersionJson(psuInventoryPath);
179         }
180         else
181         {
182             // Obtain PSU information from D-Bus
183             version = internal::getVersionDbus(bus, psuInventoryPath);
184         }
185     }
186     catch (const std::exception& e)
187     {
188         lg2::error("Error in getVersion: {ERROR}", "ERROR", e);
189     }
190     return version;
191 }
192 
getLatest(const std::vector<std::string> & versions)193 std::string getLatest(const std::vector<std::string>& versions)
194 {
195     // TODO: when multiple PSU/Machines are supported, add configuration options
196     // to implement machine-specific logic.
197     // For now IBM AC Servers and Inspur FP5280G2 are supported.
198     //
199     // IBM AC servers' PSU version has two types:
200     // * XXXXYYYYZZZZ: XXXX is the primary version
201     //                 YYYY is the secondary version
202     //                 ZZZZ is the communication version
203     //
204     // * XXXXYYYY:     XXXX is the primary version
205     //                 YYYY is the seconday version
206     //
207     // Inspur FP5280G2 PSU version is human readable text and a larger string
208     // means a newer version.
209     //
210     // So just compare by strings is OK for these cases
211     return internal::getLatestDefault(versions);
212 }
213 
214 } // namespace version
215