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