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 PsuVersionInfo getVersionInfo(const std::string& psuInventoryPath)
43 {
44     auto data = loadJSONFromFile(PSU_JSON_PATH);
45 
46     if (data == nullptr)
47     {
48         return {};
49     }
50 
51     auto devices = data.find("psuDevices");
52     if (devices == data.end())
53     {
54         log<level::WARNING>("Unable to find psuDevices");
55         return {};
56     }
57     auto devicePath = devices->find(psuInventoryPath);
58     if (devicePath == devices->end())
59     {
60         log<level::WARNING>("Unable to find path for PSU",
61                             entry("PATH=%s", psuInventoryPath.c_str()));
62         return {};
63     }
64 
65     auto type = getPMBusAccessType(data);
66 
67     std::string versionStr;
68     for (const auto& fru : data["fruConfigs"])
69     {
70         if (fru["propertyName"] == "Version")
71         {
72             versionStr = fru["fileName"].get<std::string>();
73             break;
74         }
75     }
76     if (versionStr.empty())
77     {
78         log<level::WARNING>("Unable to find Version file");
79         return {};
80     }
81     return std::make_tuple(*devicePath, type, versionStr);
82 }
83 
84 // A default implemention compare the string itself
85 std::string getLatestDefault(const std::vector<std::string>& versions)
86 {
87     std::string latest;
88     for (const auto& version : versions)
89     {
90         if (latest < version)
91         {
92             latest = version;
93         }
94     }
95     return latest;
96 }
97 
98 } // namespace internal
99 
100 std::string getVersion(const std::string& psuInventoryPath)
101 {
102     const auto& [devicePath, type, versionStr] =
103         internal::getVersionInfo(psuInventoryPath);
104     if (devicePath.empty() || versionStr.empty())
105     {
106         return "";
107     }
108     std::string version;
109     try
110     {
111         phosphor::pmbus::PMBus pmbus(devicePath);
112         version = pmbus.readString(versionStr, type);
113     }
114     catch (const std::exception& ex)
115     {
116         log<level::ERR>(ex.what());
117     }
118     return version;
119 }
120 
121 std::string getVersion(sdbusplus::bus_t& bus,
122                        const std::string& psuInventoryPath)
123 {
124     std::string version;
125     try
126     {
127         const auto& [i2cbus, i2caddr] = getPsuI2c(bus, psuInventoryPath);
128         auto pmbus = getPmbusIntf(i2cbus, i2caddr);
129         std::string name = "fw_version";
130         auto type = phosphor::pmbus::Type::HwmonDeviceDebug;
131         version = pmbus->readString(name, type);
132     }
133     catch (const std::exception& e)
134     {
135         log<level::ERR>(std::format("Error: {}", e.what()).c_str());
136     }
137     return version;
138 }
139 
140 std::string getLatest(const std::vector<std::string>& versions)
141 {
142     // TODO: when multiple PSU/Machines are supported, add configuration options
143     // to implement machine-specific logic.
144     // For now IBM AC Servers and Inspur FP5280G2 are supported.
145     //
146     // IBM AC servers' PSU version has two types:
147     // * XXXXYYYYZZZZ: XXXX is the primary version
148     //                 YYYY is the secondary version
149     //                 ZZZZ is the communication version
150     //
151     // * XXXXYYYY:     XXXX is the primary version
152     //                 YYYY is the seconday version
153     //
154     // Inspur FP5280G2 PSU version is human readable text and a larger string
155     // means a newer version.
156     //
157     // So just compare by strings is OK for these cases
158     return internal::getLatestDefault(versions);
159 }
160 } // namespace version
161