blob: 485422d7a27f5b2c4fa8b69481eae10180747a63 [file] [log] [blame]
Vernon Maueryce14e4d2019-06-25 11:38:20 -07001/*
2// Copyright (c) 2019 Intel 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*/
AppaRao Pulie99e7ed2020-01-17 12:27:10 +053016#include <byteswap.h>
Vernon Maueryce14e4d2019-06-25 11:38:20 -070017
Chen Yugang7a04f3a2019-10-08 11:12:35 +080018#include <appcommands.hpp>
Vernon Maueryce14e4d2019-06-25 11:38:20 -070019#include <fstream>
20#include <ipmid/api.hpp>
21#include <ipmid/utils.hpp>
22#include <nlohmann/json.hpp>
23#include <phosphor-logging/log.hpp>
24#include <regex>
Vernon Maueryce14e4d2019-06-25 11:38:20 -070025#include <xyz/openbmc_project/State/BMC/server.hpp>
26
27namespace ipmi
28{
29
30static void registerAPPFunctions() __attribute__((constructor));
31
AppaRao Pulie99e7ed2020-01-17 12:27:10 +053032static constexpr const char* bmcStateIntf = "xyz.openbmc_project.State.BMC";
33static constexpr const char* softwareVerIntf =
34 "xyz.openbmc_project.Software.Version";
35static constexpr const char* softwareActivationIntf =
36 "xyz.openbmc_project.Software.Activation";
37static constexpr const char* associationIntf =
38 "xyz.openbmc_project.Association";
39static constexpr const char* softwareFunctionalPath =
40 "/xyz/openbmc_project/software/functional";
Vernon Maueryce14e4d2019-06-25 11:38:20 -070041
AppaRao Pulie99e7ed2020-01-17 12:27:10 +053042using BMC = sdbusplus::xyz::openbmc_project::State::server::BMC;
Vernon Maueryce14e4d2019-06-25 11:38:20 -070043constexpr auto bmc_state_interface = "xyz.openbmc_project.State.BMC";
44constexpr auto bmc_state_property = "CurrentBMCState";
45
Vernon Maueryce14e4d2019-06-25 11:38:20 -070046bool getCurrentBmcState()
47{
48 sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()};
49
50 // Get the Inventory object implementing the BMC interface
51 ipmi::DbusObjectInfo bmcObject =
52 ipmi::getDbusObject(bus, bmc_state_interface);
53 auto variant =
54 ipmi::getDbusProperty(bus, bmcObject.second, bmcObject.first,
55 bmc_state_interface, bmc_state_property);
56
57 return std::holds_alternative<std::string>(variant) &&
58 BMC::convertBMCStateFromString(std::get<std::string>(variant)) ==
59 BMC::BMCState::Ready;
60}
61
62bool getCurrentBmcStateWithFallback(const bool fallbackAvailability)
63{
64 try
65 {
66 return getCurrentBmcState();
67 }
68 catch (...)
69 {
70 // Nothing provided the BMC interface, therefore return whatever was
71 // configured as the default.
72 return fallbackAvailability;
73 }
74}
Jia, chunhui82d178c2019-06-27 16:48:14 +080075
Vernon Maueryce14e4d2019-06-25 11:38:20 -070076/**
AppaRao Pulie99e7ed2020-01-17 12:27:10 +053077 * @brief Returns the functional firmware version information.
Vernon Maueryce14e4d2019-06-25 11:38:20 -070078 *
AppaRao Pulie99e7ed2020-01-17 12:27:10 +053079 * It reads the active firmware versions by checking functional
80 * endpoints association and matching the input version purpose string.
81 * ctx[in] - ipmi context.
82 * reqVersionPurpose[in] - Version purpose which need to be read.
83 * version[out] - Output Version string.
Vernon Maueryce14e4d2019-06-25 11:38:20 -070084 *
AppaRao Pulie99e7ed2020-01-17 12:27:10 +053085 * @return Returns '0' on success and '-1' on failure.
Vernon Maueryce14e4d2019-06-25 11:38:20 -070086 *
87 */
AppaRao Pulie99e7ed2020-01-17 12:27:10 +053088int getActiveSoftwareVersionInfo(ipmi::Context::ptr ctx,
89 const std::string& reqVersionPurpose,
90 std::string& version)
Vernon Maueryce14e4d2019-06-25 11:38:20 -070091{
AppaRao Pulie99e7ed2020-01-17 12:27:10 +053092 std::vector<std::string> activeEndPoints;
93 boost::system::error_code ec = ipmi::getDbusProperty(
94 ctx, ipmi::MAPPER_BUS_NAME, softwareFunctionalPath, associationIntf,
95 "endpoints", activeEndPoints);
96 if (ec)
Vernon Maueryce14e4d2019-06-25 11:38:20 -070097 {
AppaRao Pulie99e7ed2020-01-17 12:27:10 +053098 phosphor::logging::log<phosphor::logging::level::ERR>(
99 "Failed to get Active firmware version endpoints.");
100 return -1;
Vernon Maueryce14e4d2019-06-25 11:38:20 -0700101 }
102
AppaRao Pulie99e7ed2020-01-17 12:27:10 +0530103 for (auto& activeEndPoint : activeEndPoints)
Vernon Maueryce14e4d2019-06-25 11:38:20 -0700104 {
AppaRao Pulie99e7ed2020-01-17 12:27:10 +0530105 std::string serviceName;
106 ec = ipmi::getService(ctx, softwareActivationIntf, activeEndPoint,
107 serviceName);
108 if (ec)
Vernon Maueryce14e4d2019-06-25 11:38:20 -0700109 {
AppaRao Pulie99e7ed2020-01-17 12:27:10 +0530110 phosphor::logging::log<phosphor::logging::level::ERR>(
111 "Failed to perform getService.",
112 phosphor::logging::entry("OBJPATH=%s", activeEndPoint.c_str()));
113 continue;
114 }
115
116 PropertyMap propMap;
117 ec = ipmi::getAllDbusProperties(ctx, serviceName, activeEndPoint,
118 softwareVerIntf, propMap);
119 if (ec)
120 {
121 phosphor::logging::log<phosphor::logging::level::ERR>(
122 "Failed to perform GetAll on Version interface.",
123 phosphor::logging::entry("SERVICE=%s", serviceName.c_str()),
124 phosphor::logging::entry("PATH=%s", activeEndPoint.c_str()));
125 continue;
126 }
127
128 std::string* purposeProp =
129 std::get_if<std::string>(&propMap["Purpose"]);
130 std::string* versionProp =
131 std::get_if<std::string>(&propMap["Version"]);
132 if (!purposeProp || !versionProp)
133 {
134 phosphor::logging::log<phosphor::logging::level::ERR>(
135 "Failed to get version or purpose property");
136 continue;
137 }
138
139 // Check for requested version information and return if found.
140 if (*purposeProp == reqVersionPurpose)
141 {
142 version = *versionProp;
143 return 0;
Vernon Maueryce14e4d2019-06-25 11:38:20 -0700144 }
145 }
146
AppaRao Pulie99e7ed2020-01-17 12:27:10 +0530147 phosphor::logging::log<phosphor::logging::level::INFO>(
148 "Failed to find version information.",
149 phosphor::logging::entry("PURPOSE=%s", reqVersionPurpose.c_str()));
150 return -1;
Vernon Maueryce14e4d2019-06-25 11:38:20 -0700151}
152
Vernon Maueryce14e4d2019-06-25 11:38:20 -0700153// Support both 2 solutions:
154// 1.Current solution 2.7.0-dev-533-g14dc00e79-5e7d997
155// openbmcTag 2.7.0-dev
156// BuildNo 533
Jia, chunhui82d178c2019-06-27 16:48:14 +0800157// openbmcHash 14dc00e79
Vernon Maueryce14e4d2019-06-25 11:38:20 -0700158// MetaHasg 5e7d997
159//
Jia, chunhui82d178c2019-06-27 16:48:14 +0800160// 2.New solution wht-0.2-3-gab3500-38384ac
161// IdStr wht
Vernon Maueryce14e4d2019-06-25 11:38:20 -0700162// Major 0
Jia, chunhui82d178c2019-06-27 16:48:14 +0800163// Minor 2
164// buildNo 3
165// MetaHash ab3500
166// openbmcHash 38384ac
Vernon Maueryce14e4d2019-06-25 11:38:20 -0700167std::optional<MetaRevision> convertIntelVersion(std::string& s)
168{
169 std::smatch results;
170 MetaRevision rev;
Jia, chunhui82d178c2019-06-27 16:48:14 +0800171 std::regex pattern1("(\\d+?).(\\d+?).\\d+?-\\w*?-(\\d+?)-g(\\w+?)-(\\w+?)");
Vernon Maueryce14e4d2019-06-25 11:38:20 -0700172 constexpr size_t matchedPhosphor = 6;
173 if (std::regex_match(s, results, pattern1))
174 {
175 if (results.size() == matchedPhosphor)
176 {
177 rev.platform = "whtref";
178 rev.major = static_cast<uint8_t>(std::stoi(results[1]));
179 rev.minor = static_cast<uint8_t>(std::stoi(results[2]));
180 rev.buildNo = static_cast<uint32_t>(std::stoi(results[3]));
181 rev.openbmcHash = results[4];
182 rev.metaHash = results[5];
Vernon Maueryce14e4d2019-06-25 11:38:20 -0700183 std::string versionString =
184 rev.platform + ":" + std::to_string(rev.major) + ":" +
185 std::to_string(rev.minor) + ":" + std::to_string(rev.buildNo) +
Jia, chunhui82d178c2019-06-27 16:48:14 +0800186 ":" + rev.openbmcHash + ":" + rev.metaHash;
AppaRao Pulie99e7ed2020-01-17 12:27:10 +0530187 phosphor::logging::log<phosphor::logging::level::INFO>(
Jia, chunhui82d178c2019-06-27 16:48:14 +0800188 "Get BMC version",
AppaRao Pulie99e7ed2020-01-17 12:27:10 +0530189 phosphor::logging::entry("VERSION=%s", versionString.c_str()));
Vernon Maueryce14e4d2019-06-25 11:38:20 -0700190 return rev;
191 }
192 }
Jia, chunhui82d178c2019-06-27 16:48:14 +0800193 constexpr size_t matchedIntel = 7;
194 std::regex pattern2("(\\w+?)-(\\d+?).(\\d+?)-(\\d+?)-g(\\w+?)-(\\w+?)");
Vernon Maueryce14e4d2019-06-25 11:38:20 -0700195 if (std::regex_match(s, results, pattern2))
196 {
197 if (results.size() == matchedIntel)
198 {
199 rev.platform = results[1];
200 rev.major = static_cast<uint8_t>(std::stoi(results[2]));
201 rev.minor = static_cast<uint8_t>(std::stoi(results[3]));
202 rev.buildNo = static_cast<uint32_t>(std::stoi(results[4]));
Jia, chunhui82d178c2019-06-27 16:48:14 +0800203 rev.openbmcHash = results[6];
204 rev.metaHash = results[5];
Vernon Maueryce14e4d2019-06-25 11:38:20 -0700205 std::string versionString =
206 rev.platform + ":" + std::to_string(rev.major) + ":" +
207 std::to_string(rev.minor) + ":" + std::to_string(rev.buildNo) +
Jia, chunhui82d178c2019-06-27 16:48:14 +0800208 ":" + rev.openbmcHash + ":" + rev.metaHash;
AppaRao Pulie99e7ed2020-01-17 12:27:10 +0530209 phosphor::logging::log<phosphor::logging::level::INFO>(
Jia, chunhui82d178c2019-06-27 16:48:14 +0800210 "Get BMC version",
AppaRao Pulie99e7ed2020-01-17 12:27:10 +0530211 phosphor::logging::entry("VERSION=%s", versionString.c_str()));
Vernon Maueryce14e4d2019-06-25 11:38:20 -0700212 return rev;
213 }
214 }
215
216 return std::nullopt;
217}
Chen Yugang7a04f3a2019-10-08 11:12:35 +0800218
Vernon Mauery98bbf692019-09-16 11:14:59 -0700219RspType<uint8_t, // Device ID
220 uint8_t, // Device Revision
AppaRao Pulie99e7ed2020-01-17 12:27:10 +0530221 uint7_t, // Firmware Revision Major
222 bool, // Device available(0=NormalMode,1=DeviceFirmware)
Vernon Mauery98bbf692019-09-16 11:14:59 -0700223 uint8_t, // Firmware Revision minor
224 uint8_t, // IPMI version
225 uint8_t, // Additional device support
226 uint24_t, // MFG ID
227 uint16_t, // Product ID
228 uint32_t // AUX info
229 >
AppaRao Pulie99e7ed2020-01-17 12:27:10 +0530230 ipmiAppGetDeviceId(ipmi::Context::ptr ctx)
Vernon Maueryce14e4d2019-06-25 11:38:20 -0700231{
Vernon Maueryce14e4d2019-06-25 11:38:20 -0700232 static struct
233 {
234 uint8_t id;
235 uint8_t revision;
AppaRao Pulie99e7ed2020-01-17 12:27:10 +0530236 uint7_t fwMajor;
237 bool devBusy;
238 uint8_t fwMinor;
Johnathan Mantey3c8af652019-12-19 11:29:15 -0800239 uint8_t ipmiVer = 2;
Vernon Maueryce14e4d2019-06-25 11:38:20 -0700240 uint8_t addnDevSupport;
241 uint24_t manufId;
242 uint16_t prodId;
243 uint32_t aux;
244 } devId;
AppaRao Pulicb6386b2020-01-16 14:53:30 +0530245 static bool fwVerInitialized = false;
246 static bool devIdInitialized = false;
Johnathan Mantey3c8af652019-12-19 11:29:15 -0800247 static bool defaultActivationSetting = false;
Vernon Maueryce14e4d2019-06-25 11:38:20 -0700248 const char* filename = "/usr/share/ipmi-providers/dev_id.json";
Johnathan Mantey3c8af652019-12-19 11:29:15 -0800249 const char* prodIdFilename = "/var/cache/private/prodID";
AppaRao Pulicb6386b2020-01-16 14:53:30 +0530250 if (!fwVerInitialized)
Vernon Maueryce14e4d2019-06-25 11:38:20 -0700251 {
AppaRao Pulie99e7ed2020-01-17 12:27:10 +0530252 std::string versionString;
253 if (!getActiveSoftwareVersionInfo(ctx, versionPurposeBMC,
254 versionString))
Vernon Maueryce14e4d2019-06-25 11:38:20 -0700255 {
AppaRao Pulie99e7ed2020-01-17 12:27:10 +0530256 std::optional<MetaRevision> rev =
257 convertIntelVersion(versionString);
258 if (rev.has_value())
Jia, chunhui82d178c2019-06-27 16:48:14 +0800259 {
AppaRao Pulie99e7ed2020-01-17 12:27:10 +0530260 MetaRevision revision = rev.value();
261 devId.fwMajor = static_cast<uint7_t>(revision.major);
262
263 revision.minor = (revision.minor > 99 ? 99 : revision.minor);
264 devId.fwMinor =
265 revision.minor % 10 + (revision.minor / 10) * 16;
266 try
267 {
268 uint32_t hash = std::stoul(revision.metaHash, 0, 16);
269 hash = bswap_32(hash);
270 devId.aux = (revision.buildNo & 0xFF) + (hash & 0xFFFFFF00);
271 fwVerInitialized = true;
272 }
273 catch (const std::exception& e)
274 {
275 phosphor::logging::log<phosphor::logging::level::ERR>(
276 "Failed to convert git hash",
277 phosphor::logging::entry("ERROR=%s", e.what()));
278 }
Jia, chunhui82d178c2019-06-27 16:48:14 +0800279 }
Vernon Maueryce14e4d2019-06-25 11:38:20 -0700280 }
AppaRao Pulicb6386b2020-01-16 14:53:30 +0530281 }
Vernon Maueryce14e4d2019-06-25 11:38:20 -0700282
AppaRao Pulicb6386b2020-01-16 14:53:30 +0530283 if (!devIdInitialized)
284 {
Vernon Maueryce14e4d2019-06-25 11:38:20 -0700285 std::ifstream devIdFile(filename);
286 if (devIdFile.is_open())
287 {
288 auto data = nlohmann::json::parse(devIdFile, nullptr, false);
289 if (!data.is_discarded())
290 {
291 devId.id = data.value("id", 0);
292 devId.revision = data.value("revision", 0);
293 devId.addnDevSupport = data.value("addn_dev_support", 0);
294 devId.manufId = data.value("manuf_id", 0);
Vernon Maueryce14e4d2019-06-25 11:38:20 -0700295 }
296 else
297 {
AppaRao Pulie99e7ed2020-01-17 12:27:10 +0530298 phosphor::logging::log<phosphor::logging::level::ERR>(
299 "Device ID JSON parser failure");
Vernon Maueryce14e4d2019-06-25 11:38:20 -0700300 return ipmi::responseUnspecifiedError();
301 }
302 }
303 else
304 {
AppaRao Pulie99e7ed2020-01-17 12:27:10 +0530305 phosphor::logging::log<phosphor::logging::level::ERR>(
306 "Device ID file not found");
Vernon Maueryce14e4d2019-06-25 11:38:20 -0700307 return ipmi::responseUnspecifiedError();
308 }
Johnathan Mantey3c8af652019-12-19 11:29:15 -0800309
310 // Determine the Product ID. Using the DBus system is painfully slow at
311 // boot time. Avoid using DBus to get the Product ID. The Product ID is
312 // stored in a non-volatile file now. The /usr/bin/checkFru.sh script,
313 // run during bootup, will populate the productIdFile.
314 std::fstream prodIdFile(prodIdFilename);
315 if (prodIdFile.is_open())
316 {
317 std::string id = "0x00";
318 char* end;
319 prodIdFile.getline(&id[0], id.size() + 1);
320 devId.prodId = std::strtol(&id[0], &end, 0);
AppaRao Pulicb6386b2020-01-16 14:53:30 +0530321 devIdInitialized = true;
Johnathan Mantey3c8af652019-12-19 11:29:15 -0800322 }
323 else
324 {
325 // For any exception send out platform id as 0,
326 // and make sure to re-query the device id.
AppaRao Pulicb6386b2020-01-16 14:53:30 +0530327 devIdInitialized = false;
Johnathan Mantey3c8af652019-12-19 11:29:15 -0800328 devId.prodId = 0;
329 }
Vernon Maueryce14e4d2019-06-25 11:38:20 -0700330 }
331
AppaRao Pulie99e7ed2020-01-17 12:27:10 +0530332 // Set availability to the actual current BMC state.
333 // The availability may change in run time so don't cache.
334 bool bmcDevBusy = getCurrentBmcStateWithFallback(defaultActivationSetting);
Vernon Maueryce14e4d2019-06-25 11:38:20 -0700335
AppaRao Pulie99e7ed2020-01-17 12:27:10 +0530336 return ipmi::responseSuccess(devId.id, devId.revision, devId.fwMajor,
337 bmcDevBusy, devId.fwMinor, devId.ipmiVer,
338 devId.addnDevSupport, devId.manufId,
339 devId.prodId, devId.aux);
Vernon Maueryce14e4d2019-06-25 11:38:20 -0700340}
341
342static void registerAPPFunctions(void)
343{
Vernon Maueryce14e4d2019-06-25 11:38:20 -0700344 // <Get Device ID>
Vernon Mauery98bbf692019-09-16 11:14:59 -0700345 registerHandler(prioOemBase, netFnApp, app::cmdGetDeviceId, Privilege::User,
346 ipmiAppGetDeviceId);
Vernon Maueryce14e4d2019-06-25 11:38:20 -0700347}
348
349} // namespace ipmi