blob: 9a45beb321d2253f69e9b143e5f0410fe22d1783 [file] [log] [blame]
Lei YU0bf1b782019-08-29 16:02:30 +08001/**
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"
Shawn McCarney14572cf2024-11-06 12:17:57 -060022#include "utils.hpp"
Lei YU0bf1b782019-08-29 16:02:30 +080023
Shawn McCarney14572cf2024-11-06 12:17:57 -060024#include <nlohmann/json.hpp>
Anwaar Hadi72e584c2025-05-20 22:02:14 +000025#include <phosphor-logging/lg2.hpp>
Brandon Wymand1bc4ce2019-12-13 14:20:34 -060026
Faisal Awada5dce1a72024-08-19 15:51:44 -050027#include <exception>
Lei YU0bf1b782019-08-29 16:02:30 +080028#include <tuple>
29
30using json = nlohmann::json;
31
Shawn McCarney14572cf2024-11-06 12:17:57 -060032using namespace utils;
Faisal Awada5dce1a72024-08-19 15:51:44 -050033using namespace phosphor::power::util;
Lei YU0bf1b782019-08-29 16:02:30 +080034
Faisal Awada5dce1a72024-08-19 15:51:44 -050035namespace version
36{
Shawn McCarney14572cf2024-11-06 12:17:57 -060037
38namespace internal
Lei YU0bf1b782019-08-29 16:02:30 +080039{
Faisal Awada5dce1a72024-08-19 15:51:44 -050040
Shawn McCarney23dee382024-11-11 18:41:49 -060041// PsuInfo contains the device path, PMBus access type, and sysfs file name
42using 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 */
Lei YU0bf1b782019-08-29 16:02:30 +080052PsuVersionInfo getVersionInfo(const std::string& psuInventoryPath)
53{
Shawn McCarney14572cf2024-11-06 12:17:57 -060054 auto data = loadJSONFromFile(PSU_JSON_PATH);
Lei YU0bf1b782019-08-29 16:02:30 +080055
56 if (data == nullptr)
57 {
58 return {};
59 }
60
61 auto devices = data.find("psuDevices");
62 if (devices == data.end())
63 {
Anwaar Hadi72e584c2025-05-20 22:02:14 +000064 lg2::warning("Unable to find psuDevices");
Lei YU0bf1b782019-08-29 16:02:30 +080065 return {};
66 }
67 auto devicePath = devices->find(psuInventoryPath);
68 if (devicePath == devices->end())
69 {
Anwaar Hadi72e584c2025-05-20 22:02:14 +000070 lg2::warning("Unable to find path for PSU PATH={PATH}", "PATH",
71 psuInventoryPath);
Lei YU0bf1b782019-08-29 16:02:30 +080072 return {};
73 }
74
Shawn McCarney14572cf2024-11-06 12:17:57 -060075 auto type = getPMBusAccessType(data);
Lei YU0bf1b782019-08-29 16:02:30 +080076
Shawn McCarney23dee382024-11-11 18:41:49 -060077 std::string fileName;
Lei YU0bf1b782019-08-29 16:02:30 +080078 for (const auto& fru : data["fruConfigs"])
79 {
Shawn McCarney23dee382024-11-11 18:41:49 -060080 if (fru.contains("propertyName") &&
81 (fru["propertyName"] == "Version") && fru.contains("fileName"))
Lei YU0bf1b782019-08-29 16:02:30 +080082 {
Shawn McCarney23dee382024-11-11 18:41:49 -060083 fileName = fru["fileName"];
Lei YU0bf1b782019-08-29 16:02:30 +080084 break;
85 }
86 }
Shawn McCarney23dee382024-11-11 18:41:49 -060087 if (fileName.empty())
Lei YU0bf1b782019-08-29 16:02:30 +080088 {
Anwaar Hadi72e584c2025-05-20 22:02:14 +000089 lg2::warning("Unable to find Version file");
Lei YU0bf1b782019-08-29 16:02:30 +080090 return {};
91 }
Shawn McCarney23dee382024-11-11 18:41:49 -060092 return std::make_tuple(*devicePath, type, fileName);
Lei YU0bf1b782019-08-29 16:02:30 +080093}
Lei YU093b5912019-10-22 15:28:51 +080094
Shawn McCarney23dee382024-11-11 18:41:49 -060095/**
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 */
106std::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 */
133std::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 */
Lei YU093b5912019-10-22 15:28:51 +0800154std::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
Shawn McCarney14572cf2024-11-06 12:17:57 -0600167} // namespace internal
Lei YU0bf1b782019-08-29 16:02:30 +0800168
Faisal Awada5dce1a72024-08-19 15:51:44 -0500169std::string getVersion(sdbusplus::bus_t& bus,
170 const std::string& psuInventoryPath)
171{
Shawn McCarney37c26122024-10-24 13:58:31 -0500172 std::string version;
Faisal Awada5dce1a72024-08-19 15:51:44 -0500173 try
174 {
Shawn McCarney23dee382024-11-11 18:41:49 -0600175 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 }
Faisal Awada5dce1a72024-08-19 15:51:44 -0500185 }
186 catch (const std::exception& e)
187 {
Anwaar Hadi72e584c2025-05-20 22:02:14 +0000188 lg2::error("Error in getVersion: {ERROR}", "ERROR", e);
Faisal Awada5dce1a72024-08-19 15:51:44 -0500189 }
Shawn McCarney37c26122024-10-24 13:58:31 -0500190 return version;
Faisal Awada5dce1a72024-08-19 15:51:44 -0500191}
192
Lei YU093b5912019-10-22 15:28:51 +0800193std::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
Shawn McCarney14572cf2024-11-06 12:17:57 -0600211 return internal::getLatestDefault(versions);
Lei YU093b5912019-10-22 15:28:51 +0800212}
Shawn McCarney23dee382024-11-11 18:41:49 -0600213
Lei YU0bf1b782019-08-29 16:02:30 +0800214} // namespace version