blob: 8b37a1f78d2b5d2fbc5b98c7f9edcdaa4ce09137 [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
26namespace ipmi
27{
28
29static void registerAPPFunctions() __attribute__((constructor));
30
AppaRao Pulie99e7ed2020-01-17 12:27:10 +053031static constexpr const char* bmcStateIntf = "xyz.openbmc_project.State.BMC";
32static constexpr const char* softwareVerIntf =
33 "xyz.openbmc_project.Software.Version";
34static constexpr const char* softwareActivationIntf =
35 "xyz.openbmc_project.Software.Activation";
36static constexpr const char* associationIntf =
37 "xyz.openbmc_project.Association";
38static constexpr const char* softwareFunctionalPath =
39 "/xyz/openbmc_project/software/functional";
Vernon Maueryce14e4d2019-06-25 11:38:20 -070040
AppaRao Puli6c6cec42020-02-26 11:56:47 +053041static constexpr const char* currentBmcStateProp = "CurrentBMCState";
42static constexpr const char* bmcStateReadyStr =
43 "xyz.openbmc_project.State.BMC.BMCState.Ready";
Vernon Maueryce14e4d2019-06-25 11:38:20 -070044
AppaRao Puli6c6cec42020-02-26 11:56:47 +053045static std::unique_ptr<sdbusplus::bus::match::match> bmcStateChangedSignal;
46static uint8_t bmcDeviceBusy = true;
47
48int initBMCDeviceState(ipmi::Context::ptr ctx)
Vernon Maueryce14e4d2019-06-25 11:38:20 -070049{
AppaRao Puli6c6cec42020-02-26 11:56:47 +053050 DbusObjectInfo objInfo;
51 boost::system::error_code ec =
52 ipmi::getDbusObject(ctx, bmcStateIntf, "/", "bmc0", objInfo);
53 if (ec)
Vernon Maueryce14e4d2019-06-25 11:38:20 -070054 {
AppaRao Puli6c6cec42020-02-26 11:56:47 +053055 phosphor::logging::log<phosphor::logging::level::ERR>(
56 "initBMCDeviceState: Failed to perform GetSubTree action",
57 phosphor::logging::entry("ERROR=%s", ec.message().c_str()),
58 phosphor::logging::entry("INTERFACE=%s", bmcStateIntf));
59 return -1;
Vernon Maueryce14e4d2019-06-25 11:38:20 -070060 }
AppaRao Puli6c6cec42020-02-26 11:56:47 +053061
62 std::string bmcState;
63 ec = ipmi::getDbusProperty(ctx, objInfo.second, objInfo.first, bmcStateIntf,
64 currentBmcStateProp, bmcState);
65 if (ec)
Vernon Maueryce14e4d2019-06-25 11:38:20 -070066 {
AppaRao Puli6c6cec42020-02-26 11:56:47 +053067 phosphor::logging::log<phosphor::logging::level::ERR>(
68 "initBMCDeviceState: Failed to get CurrentBMCState property",
69 phosphor::logging::entry("ERROR=%s", ec.message().c_str()));
70 return -1;
Vernon Maueryce14e4d2019-06-25 11:38:20 -070071 }
AppaRao Puli6c6cec42020-02-26 11:56:47 +053072
73 bmcDeviceBusy = (bmcState != bmcStateReadyStr);
74
75 phosphor::logging::log<phosphor::logging::level::INFO>(
76 "BMC device state updated");
77
78 // BMC state may change runtime while doing firmware udpate.
79 // Register for property change signal to update state.
80 bmcStateChangedSignal = std::make_unique<sdbusplus::bus::match::match>(
81 *(ctx->bus),
82 sdbusplus::bus::match::rules::propertiesChanged(objInfo.first,
83 bmcStateIntf),
84 [](sdbusplus::message::message& msg) {
Patrick Williams886a48a2020-05-13 17:44:37 -050085 std::map<std::string, std::variant<std::string>> props;
AppaRao Puli6c6cec42020-02-26 11:56:47 +053086 std::vector<std::string> inVal;
87 std::string iface;
88 try
89 {
90 msg.read(iface, props, inVal);
91 }
92 catch (const std::exception& e)
93 {
94 phosphor::logging::log<phosphor::logging::level::ERR>(
95 "Exception caught in Get CurrentBMCState");
96 return;
97 }
98
99 auto it = props.find(currentBmcStateProp);
100 if (it != props.end())
101 {
102 std::string* state = std::get_if<std::string>(&it->second);
103 if (state)
104 {
105 bmcDeviceBusy = (*state != bmcStateReadyStr);
106 phosphor::logging::log<phosphor::logging::level::INFO>(
107 "BMC device state updated");
108 }
109 }
110 });
111
112 return 0;
Vernon Maueryce14e4d2019-06-25 11:38:20 -0700113}
Jia, chunhui82d178c2019-06-27 16:48:14 +0800114
Vernon Maueryce14e4d2019-06-25 11:38:20 -0700115/**
AppaRao Pulie99e7ed2020-01-17 12:27:10 +0530116 * @brief Returns the functional firmware version information.
Vernon Maueryce14e4d2019-06-25 11:38:20 -0700117 *
AppaRao Pulie99e7ed2020-01-17 12:27:10 +0530118 * It reads the active firmware versions by checking functional
119 * endpoints association and matching the input version purpose string.
120 * ctx[in] - ipmi context.
121 * reqVersionPurpose[in] - Version purpose which need to be read.
122 * version[out] - Output Version string.
Vernon Maueryce14e4d2019-06-25 11:38:20 -0700123 *
AppaRao Pulie99e7ed2020-01-17 12:27:10 +0530124 * @return Returns '0' on success and '-1' on failure.
Vernon Maueryce14e4d2019-06-25 11:38:20 -0700125 *
126 */
AppaRao Pulie99e7ed2020-01-17 12:27:10 +0530127int getActiveSoftwareVersionInfo(ipmi::Context::ptr ctx,
128 const std::string& reqVersionPurpose,
129 std::string& version)
Vernon Maueryce14e4d2019-06-25 11:38:20 -0700130{
AppaRao Pulie99e7ed2020-01-17 12:27:10 +0530131 std::vector<std::string> activeEndPoints;
132 boost::system::error_code ec = ipmi::getDbusProperty(
133 ctx, ipmi::MAPPER_BUS_NAME, softwareFunctionalPath, associationIntf,
134 "endpoints", activeEndPoints);
135 if (ec)
Vernon Maueryce14e4d2019-06-25 11:38:20 -0700136 {
AppaRao Pulie99e7ed2020-01-17 12:27:10 +0530137 phosphor::logging::log<phosphor::logging::level::ERR>(
138 "Failed to get Active firmware version endpoints.");
139 return -1;
Vernon Maueryce14e4d2019-06-25 11:38:20 -0700140 }
141
AppaRao Pulie99e7ed2020-01-17 12:27:10 +0530142 for (auto& activeEndPoint : activeEndPoints)
Vernon Maueryce14e4d2019-06-25 11:38:20 -0700143 {
AppaRao Pulie99e7ed2020-01-17 12:27:10 +0530144 std::string serviceName;
145 ec = ipmi::getService(ctx, softwareActivationIntf, activeEndPoint,
146 serviceName);
147 if (ec)
Vernon Maueryce14e4d2019-06-25 11:38:20 -0700148 {
AppaRao Pulie99e7ed2020-01-17 12:27:10 +0530149 phosphor::logging::log<phosphor::logging::level::ERR>(
150 "Failed to perform getService.",
151 phosphor::logging::entry("OBJPATH=%s", activeEndPoint.c_str()));
152 continue;
153 }
154
155 PropertyMap propMap;
156 ec = ipmi::getAllDbusProperties(ctx, serviceName, activeEndPoint,
157 softwareVerIntf, propMap);
158 if (ec)
159 {
160 phosphor::logging::log<phosphor::logging::level::ERR>(
161 "Failed to perform GetAll on Version interface.",
162 phosphor::logging::entry("SERVICE=%s", serviceName.c_str()),
163 phosphor::logging::entry("PATH=%s", activeEndPoint.c_str()));
164 continue;
165 }
166
167 std::string* purposeProp =
168 std::get_if<std::string>(&propMap["Purpose"]);
169 std::string* versionProp =
170 std::get_if<std::string>(&propMap["Version"]);
171 if (!purposeProp || !versionProp)
172 {
173 phosphor::logging::log<phosphor::logging::level::ERR>(
174 "Failed to get version or purpose property");
175 continue;
176 }
177
178 // Check for requested version information and return if found.
179 if (*purposeProp == reqVersionPurpose)
180 {
181 version = *versionProp;
182 return 0;
Vernon Maueryce14e4d2019-06-25 11:38:20 -0700183 }
184 }
185
AppaRao Pulie99e7ed2020-01-17 12:27:10 +0530186 phosphor::logging::log<phosphor::logging::level::INFO>(
187 "Failed to find version information.",
188 phosphor::logging::entry("PURPOSE=%s", reqVersionPurpose.c_str()));
189 return -1;
Vernon Maueryce14e4d2019-06-25 11:38:20 -0700190}
191
Vernon Maueryce14e4d2019-06-25 11:38:20 -0700192// Support both 2 solutions:
193// 1.Current solution 2.7.0-dev-533-g14dc00e79-5e7d997
194// openbmcTag 2.7.0-dev
195// BuildNo 533
Jia, chunhui82d178c2019-06-27 16:48:14 +0800196// openbmcHash 14dc00e79
Vernon Maueryce14e4d2019-06-25 11:38:20 -0700197// MetaHasg 5e7d997
198//
Jia, chunhui82d178c2019-06-27 16:48:14 +0800199// 2.New solution wht-0.2-3-gab3500-38384ac
200// IdStr wht
Vernon Maueryce14e4d2019-06-25 11:38:20 -0700201// Major 0
Jia, chunhui82d178c2019-06-27 16:48:14 +0800202// Minor 2
203// buildNo 3
204// MetaHash ab3500
205// openbmcHash 38384ac
Vernon Maueryce14e4d2019-06-25 11:38:20 -0700206std::optional<MetaRevision> convertIntelVersion(std::string& s)
207{
208 std::smatch results;
209 MetaRevision rev;
Jia, chunhui82d178c2019-06-27 16:48:14 +0800210 std::regex pattern1("(\\d+?).(\\d+?).\\d+?-\\w*?-(\\d+?)-g(\\w+?)-(\\w+?)");
Vernon Maueryce14e4d2019-06-25 11:38:20 -0700211 constexpr size_t matchedPhosphor = 6;
212 if (std::regex_match(s, results, pattern1))
213 {
214 if (results.size() == matchedPhosphor)
215 {
216 rev.platform = "whtref";
217 rev.major = static_cast<uint8_t>(std::stoi(results[1]));
218 rev.minor = static_cast<uint8_t>(std::stoi(results[2]));
219 rev.buildNo = static_cast<uint32_t>(std::stoi(results[3]));
220 rev.openbmcHash = results[4];
221 rev.metaHash = results[5];
Vernon Maueryce14e4d2019-06-25 11:38:20 -0700222 std::string versionString =
223 rev.platform + ":" + std::to_string(rev.major) + ":" +
224 std::to_string(rev.minor) + ":" + std::to_string(rev.buildNo) +
Jia, chunhui82d178c2019-06-27 16:48:14 +0800225 ":" + rev.openbmcHash + ":" + rev.metaHash;
AppaRao Pulie99e7ed2020-01-17 12:27:10 +0530226 phosphor::logging::log<phosphor::logging::level::INFO>(
Jia, chunhui82d178c2019-06-27 16:48:14 +0800227 "Get BMC version",
AppaRao Pulie99e7ed2020-01-17 12:27:10 +0530228 phosphor::logging::entry("VERSION=%s", versionString.c_str()));
Vernon Maueryce14e4d2019-06-25 11:38:20 -0700229 return rev;
230 }
231 }
Jia, chunhui82d178c2019-06-27 16:48:14 +0800232 constexpr size_t matchedIntel = 7;
233 std::regex pattern2("(\\w+?)-(\\d+?).(\\d+?)-(\\d+?)-g(\\w+?)-(\\w+?)");
Vernon Maueryce14e4d2019-06-25 11:38:20 -0700234 if (std::regex_match(s, results, pattern2))
235 {
236 if (results.size() == matchedIntel)
237 {
238 rev.platform = results[1];
239 rev.major = static_cast<uint8_t>(std::stoi(results[2]));
240 rev.minor = static_cast<uint8_t>(std::stoi(results[3]));
241 rev.buildNo = static_cast<uint32_t>(std::stoi(results[4]));
Jia, chunhui82d178c2019-06-27 16:48:14 +0800242 rev.openbmcHash = results[6];
243 rev.metaHash = results[5];
Vernon Maueryce14e4d2019-06-25 11:38:20 -0700244 std::string versionString =
245 rev.platform + ":" + std::to_string(rev.major) + ":" +
246 std::to_string(rev.minor) + ":" + std::to_string(rev.buildNo) +
Jia, chunhui82d178c2019-06-27 16:48:14 +0800247 ":" + rev.openbmcHash + ":" + rev.metaHash;
AppaRao Pulie99e7ed2020-01-17 12:27:10 +0530248 phosphor::logging::log<phosphor::logging::level::INFO>(
Jia, chunhui82d178c2019-06-27 16:48:14 +0800249 "Get BMC version",
AppaRao Pulie99e7ed2020-01-17 12:27:10 +0530250 phosphor::logging::entry("VERSION=%s", versionString.c_str()));
Vernon Maueryce14e4d2019-06-25 11:38:20 -0700251 return rev;
252 }
253 }
254
255 return std::nullopt;
256}
Chen Yugang7a04f3a2019-10-08 11:12:35 +0800257
Vernon Mauery98bbf692019-09-16 11:14:59 -0700258RspType<uint8_t, // Device ID
259 uint8_t, // Device Revision
AppaRao Pulie99e7ed2020-01-17 12:27:10 +0530260 uint7_t, // Firmware Revision Major
261 bool, // Device available(0=NormalMode,1=DeviceFirmware)
Vernon Mauery98bbf692019-09-16 11:14:59 -0700262 uint8_t, // Firmware Revision minor
263 uint8_t, // IPMI version
264 uint8_t, // Additional device support
265 uint24_t, // MFG ID
266 uint16_t, // Product ID
267 uint32_t // AUX info
268 >
AppaRao Pulie99e7ed2020-01-17 12:27:10 +0530269 ipmiAppGetDeviceId(ipmi::Context::ptr ctx)
Vernon Maueryce14e4d2019-06-25 11:38:20 -0700270{
Vernon Maueryce14e4d2019-06-25 11:38:20 -0700271 static struct
272 {
273 uint8_t id;
274 uint8_t revision;
AppaRao Pulie99e7ed2020-01-17 12:27:10 +0530275 uint7_t fwMajor;
276 bool devBusy;
277 uint8_t fwMinor;
Johnathan Mantey3c8af652019-12-19 11:29:15 -0800278 uint8_t ipmiVer = 2;
Vernon Maueryce14e4d2019-06-25 11:38:20 -0700279 uint8_t addnDevSupport;
280 uint24_t manufId;
281 uint16_t prodId;
282 uint32_t aux;
283 } devId;
AppaRao Pulicb6386b2020-01-16 14:53:30 +0530284 static bool fwVerInitialized = false;
285 static bool devIdInitialized = false;
AppaRao Puli6c6cec42020-02-26 11:56:47 +0530286 static bool bmcStateInitialized = false;
Vernon Maueryce14e4d2019-06-25 11:38:20 -0700287 const char* filename = "/usr/share/ipmi-providers/dev_id.json";
Johnathan Mantey3c8af652019-12-19 11:29:15 -0800288 const char* prodIdFilename = "/var/cache/private/prodID";
AppaRao Pulicb6386b2020-01-16 14:53:30 +0530289 if (!fwVerInitialized)
Vernon Maueryce14e4d2019-06-25 11:38:20 -0700290 {
AppaRao Pulie99e7ed2020-01-17 12:27:10 +0530291 std::string versionString;
292 if (!getActiveSoftwareVersionInfo(ctx, versionPurposeBMC,
293 versionString))
Vernon Maueryce14e4d2019-06-25 11:38:20 -0700294 {
AppaRao Pulie99e7ed2020-01-17 12:27:10 +0530295 std::optional<MetaRevision> rev =
296 convertIntelVersion(versionString);
297 if (rev.has_value())
Jia, chunhui82d178c2019-06-27 16:48:14 +0800298 {
AppaRao Pulie99e7ed2020-01-17 12:27:10 +0530299 MetaRevision revision = rev.value();
300 devId.fwMajor = static_cast<uint7_t>(revision.major);
301
302 revision.minor = (revision.minor > 99 ? 99 : revision.minor);
303 devId.fwMinor =
304 revision.minor % 10 + (revision.minor / 10) * 16;
305 try
306 {
307 uint32_t hash = std::stoul(revision.metaHash, 0, 16);
308 hash = bswap_32(hash);
309 devId.aux = (revision.buildNo & 0xFF) + (hash & 0xFFFFFF00);
310 fwVerInitialized = true;
311 }
312 catch (const std::exception& e)
313 {
314 phosphor::logging::log<phosphor::logging::level::ERR>(
315 "Failed to convert git hash",
316 phosphor::logging::entry("ERROR=%s", e.what()));
317 }
Jia, chunhui82d178c2019-06-27 16:48:14 +0800318 }
Vernon Maueryce14e4d2019-06-25 11:38:20 -0700319 }
AppaRao Pulicb6386b2020-01-16 14:53:30 +0530320 }
Vernon Maueryce14e4d2019-06-25 11:38:20 -0700321
AppaRao Pulicb6386b2020-01-16 14:53:30 +0530322 if (!devIdInitialized)
323 {
Vernon Maueryce14e4d2019-06-25 11:38:20 -0700324 std::ifstream devIdFile(filename);
325 if (devIdFile.is_open())
326 {
327 auto data = nlohmann::json::parse(devIdFile, nullptr, false);
328 if (!data.is_discarded())
329 {
330 devId.id = data.value("id", 0);
331 devId.revision = data.value("revision", 0);
332 devId.addnDevSupport = data.value("addn_dev_support", 0);
333 devId.manufId = data.value("manuf_id", 0);
Vernon Maueryce14e4d2019-06-25 11:38:20 -0700334 }
335 else
336 {
AppaRao Pulie99e7ed2020-01-17 12:27:10 +0530337 phosphor::logging::log<phosphor::logging::level::ERR>(
338 "Device ID JSON parser failure");
Vernon Maueryce14e4d2019-06-25 11:38:20 -0700339 return ipmi::responseUnspecifiedError();
340 }
341 }
342 else
343 {
AppaRao Pulie99e7ed2020-01-17 12:27:10 +0530344 phosphor::logging::log<phosphor::logging::level::ERR>(
345 "Device ID file not found");
Vernon Maueryce14e4d2019-06-25 11:38:20 -0700346 return ipmi::responseUnspecifiedError();
347 }
Johnathan Mantey3c8af652019-12-19 11:29:15 -0800348
349 // Determine the Product ID. Using the DBus system is painfully slow at
350 // boot time. Avoid using DBus to get the Product ID. The Product ID is
351 // stored in a non-volatile file now. The /usr/bin/checkFru.sh script,
352 // run during bootup, will populate the productIdFile.
353 std::fstream prodIdFile(prodIdFilename);
354 if (prodIdFile.is_open())
355 {
356 std::string id = "0x00";
357 char* end;
358 prodIdFile.getline(&id[0], id.size() + 1);
359 devId.prodId = std::strtol(&id[0], &end, 0);
AppaRao Pulicb6386b2020-01-16 14:53:30 +0530360 devIdInitialized = true;
Johnathan Mantey3c8af652019-12-19 11:29:15 -0800361 }
362 else
363 {
364 // For any exception send out platform id as 0,
365 // and make sure to re-query the device id.
AppaRao Pulicb6386b2020-01-16 14:53:30 +0530366 devIdInitialized = false;
Johnathan Mantey3c8af652019-12-19 11:29:15 -0800367 devId.prodId = 0;
368 }
Vernon Maueryce14e4d2019-06-25 11:38:20 -0700369 }
370
AppaRao Puli6c6cec42020-02-26 11:56:47 +0530371 if (!bmcStateInitialized)
372 {
373 if (!initBMCDeviceState(ctx))
374 {
375 bmcStateInitialized = true;
376 }
377 }
Vernon Maueryce14e4d2019-06-25 11:38:20 -0700378
AppaRao Pulie99e7ed2020-01-17 12:27:10 +0530379 return ipmi::responseSuccess(devId.id, devId.revision, devId.fwMajor,
AppaRao Puli6c6cec42020-02-26 11:56:47 +0530380 bmcDeviceBusy, devId.fwMinor, devId.ipmiVer,
AppaRao Pulie99e7ed2020-01-17 12:27:10 +0530381 devId.addnDevSupport, devId.manufId,
382 devId.prodId, devId.aux);
Vernon Maueryce14e4d2019-06-25 11:38:20 -0700383}
384
385static void registerAPPFunctions(void)
386{
Vernon Maueryce14e4d2019-06-25 11:38:20 -0700387 // <Get Device ID>
Vernon Mauery98bbf692019-09-16 11:14:59 -0700388 registerHandler(prioOemBase, netFnApp, app::cmdGetDeviceId, Privilege::User,
389 ipmiAppGetDeviceId);
Vernon Maueryce14e4d2019-06-25 11:38:20 -0700390}
391
392} // namespace ipmi