blob: 738664d73ba4bd191a18d6a360f947ea1fdc6530 [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>
Lei YU0bf1b782019-08-29 16:02:30 +080025#include <phosphor-logging/log.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;
Lei YU0bf1b782019-08-29 16:02:30 +080033using namespace phosphor::logging;
Faisal Awada5dce1a72024-08-19 15:51:44 -050034using namespace phosphor::power::util;
Lei YU0bf1b782019-08-29 16:02:30 +080035
Faisal Awada5dce1a72024-08-19 15:51:44 -050036namespace version
37{
Shawn McCarney14572cf2024-11-06 12:17:57 -060038
39namespace internal
Lei YU0bf1b782019-08-29 16:02:30 +080040{
Faisal Awada5dce1a72024-08-19 15:51:44 -050041
Shawn McCarney23dee382024-11-11 18:41:49 -060042// PsuInfo contains the device path, PMBus access type, and sysfs file name
43using 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 */
Lei YU0bf1b782019-08-29 16:02:30 +080053PsuVersionInfo getVersionInfo(const std::string& psuInventoryPath)
54{
Shawn McCarney14572cf2024-11-06 12:17:57 -060055 auto data = loadJSONFromFile(PSU_JSON_PATH);
Lei YU0bf1b782019-08-29 16:02:30 +080056
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
Shawn McCarney14572cf2024-11-06 12:17:57 -060076 auto type = getPMBusAccessType(data);
Lei YU0bf1b782019-08-29 16:02:30 +080077
Shawn McCarney23dee382024-11-11 18:41:49 -060078 std::string fileName;
Lei YU0bf1b782019-08-29 16:02:30 +080079 for (const auto& fru : data["fruConfigs"])
80 {
Shawn McCarney23dee382024-11-11 18:41:49 -060081 if (fru.contains("propertyName") &&
82 (fru["propertyName"] == "Version") && fru.contains("fileName"))
Lei YU0bf1b782019-08-29 16:02:30 +080083 {
Shawn McCarney23dee382024-11-11 18:41:49 -060084 fileName = fru["fileName"];
Lei YU0bf1b782019-08-29 16:02:30 +080085 break;
86 }
87 }
Shawn McCarney23dee382024-11-11 18:41:49 -060088 if (fileName.empty())
Lei YU0bf1b782019-08-29 16:02:30 +080089 {
90 log<level::WARNING>("Unable to find Version file");
91 return {};
92 }
Shawn McCarney23dee382024-11-11 18:41:49 -060093 return std::make_tuple(*devicePath, type, fileName);
Lei YU0bf1b782019-08-29 16:02:30 +080094}
Lei YU093b5912019-10-22 15:28:51 +080095
Shawn McCarney23dee382024-11-11 18:41:49 -060096/**
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 */
107std::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 */
134std::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 */
Lei YU093b5912019-10-22 15:28:51 +0800155std::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
Shawn McCarney14572cf2024-11-06 12:17:57 -0600168} // namespace internal
Lei YU0bf1b782019-08-29 16:02:30 +0800169
Faisal Awada5dce1a72024-08-19 15:51:44 -0500170std::string getVersion(sdbusplus::bus_t& bus,
171 const std::string& psuInventoryPath)
172{
Shawn McCarney37c26122024-10-24 13:58:31 -0500173 std::string version;
Faisal Awada5dce1a72024-08-19 15:51:44 -0500174 try
175 {
Shawn McCarney23dee382024-11-11 18:41:49 -0600176 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 }
Faisal Awada5dce1a72024-08-19 15:51:44 -0500186 }
187 catch (const std::exception& e)
188 {
189 log<level::ERR>(std::format("Error: {}", e.what()).c_str());
Faisal Awada5dce1a72024-08-19 15:51:44 -0500190 }
Shawn McCarney37c26122024-10-24 13:58:31 -0500191 return version;
Faisal Awada5dce1a72024-08-19 15:51:44 -0500192}
193
Lei YU093b5912019-10-22 15:28:51 +0800194std::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
Shawn McCarney14572cf2024-11-06 12:17:57 -0600212 return internal::getLatestDefault(versions);
Lei YU093b5912019-10-22 15:28:51 +0800213}
Shawn McCarney23dee382024-11-11 18:41:49 -0600214
Lei YU0bf1b782019-08-29 16:02:30 +0800215} // namespace version