blob: 10e3d13cf6e492f78143e3f6bcbad4d8486bb59b [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 <ipmid/api.hpp>
20#include <ipmid/utils.hpp>
21#include <nlohmann/json.hpp>
22#include <phosphor-logging/log.hpp>
James Feistfcd2d3a2020-05-28 10:38:15 -070023
24#include <fstream>
Vernon Maueryce14e4d2019-06-25 11:38:20 -070025#include <regex>
Vernon Maueryce14e4d2019-06-25 11:38:20 -070026
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 Puli6c6cec42020-02-26 11:56:47 +053042static constexpr const char* currentBmcStateProp = "CurrentBMCState";
43static constexpr const char* bmcStateReadyStr =
44 "xyz.openbmc_project.State.BMC.BMCState.Ready";
Vernon Maueryce14e4d2019-06-25 11:38:20 -070045
AppaRao Puli6c6cec42020-02-26 11:56:47 +053046static std::unique_ptr<sdbusplus::bus::match::match> bmcStateChangedSignal;
47static uint8_t bmcDeviceBusy = true;
48
49int initBMCDeviceState(ipmi::Context::ptr ctx)
Vernon Maueryce14e4d2019-06-25 11:38:20 -070050{
AppaRao Puli6c6cec42020-02-26 11:56:47 +053051 DbusObjectInfo objInfo;
52 boost::system::error_code ec =
53 ipmi::getDbusObject(ctx, bmcStateIntf, "/", "bmc0", objInfo);
54 if (ec)
Vernon Maueryce14e4d2019-06-25 11:38:20 -070055 {
AppaRao Puli6c6cec42020-02-26 11:56:47 +053056 phosphor::logging::log<phosphor::logging::level::ERR>(
57 "initBMCDeviceState: Failed to perform GetSubTree action",
58 phosphor::logging::entry("ERROR=%s", ec.message().c_str()),
59 phosphor::logging::entry("INTERFACE=%s", bmcStateIntf));
60 return -1;
Vernon Maueryce14e4d2019-06-25 11:38:20 -070061 }
AppaRao Puli6c6cec42020-02-26 11:56:47 +053062
63 std::string bmcState;
64 ec = ipmi::getDbusProperty(ctx, objInfo.second, objInfo.first, bmcStateIntf,
65 currentBmcStateProp, bmcState);
66 if (ec)
Vernon Maueryce14e4d2019-06-25 11:38:20 -070067 {
AppaRao Puli6c6cec42020-02-26 11:56:47 +053068 phosphor::logging::log<phosphor::logging::level::ERR>(
69 "initBMCDeviceState: Failed to get CurrentBMCState property",
70 phosphor::logging::entry("ERROR=%s", ec.message().c_str()));
71 return -1;
Vernon Maueryce14e4d2019-06-25 11:38:20 -070072 }
AppaRao Puli6c6cec42020-02-26 11:56:47 +053073
74 bmcDeviceBusy = (bmcState != bmcStateReadyStr);
75
76 phosphor::logging::log<phosphor::logging::level::INFO>(
77 "BMC device state updated");
78
79 // BMC state may change runtime while doing firmware udpate.
80 // Register for property change signal to update state.
81 bmcStateChangedSignal = std::make_unique<sdbusplus::bus::match::match>(
82 *(ctx->bus),
83 sdbusplus::bus::match::rules::propertiesChanged(objInfo.first,
84 bmcStateIntf),
85 [](sdbusplus::message::message& msg) {
Patrick Williams886a48a2020-05-13 17:44:37 -050086 std::map<std::string, std::variant<std::string>> props;
AppaRao Puli6c6cec42020-02-26 11:56:47 +053087 std::vector<std::string> inVal;
88 std::string iface;
89 try
90 {
91 msg.read(iface, props, inVal);
92 }
93 catch (const std::exception& e)
94 {
95 phosphor::logging::log<phosphor::logging::level::ERR>(
96 "Exception caught in Get CurrentBMCState");
97 return;
98 }
99
100 auto it = props.find(currentBmcStateProp);
101 if (it != props.end())
102 {
103 std::string* state = std::get_if<std::string>(&it->second);
104 if (state)
105 {
106 bmcDeviceBusy = (*state != bmcStateReadyStr);
107 phosphor::logging::log<phosphor::logging::level::INFO>(
108 "BMC device state updated");
109 }
110 }
111 });
112
113 return 0;
Vernon Maueryce14e4d2019-06-25 11:38:20 -0700114}
Jia, chunhui82d178c2019-06-27 16:48:14 +0800115
Vernon Maueryce14e4d2019-06-25 11:38:20 -0700116/**
AppaRao Pulie99e7ed2020-01-17 12:27:10 +0530117 * @brief Returns the functional firmware version information.
Vernon Maueryce14e4d2019-06-25 11:38:20 -0700118 *
AppaRao Pulie99e7ed2020-01-17 12:27:10 +0530119 * It reads the active firmware versions by checking functional
120 * endpoints association and matching the input version purpose string.
121 * ctx[in] - ipmi context.
122 * reqVersionPurpose[in] - Version purpose which need to be read.
123 * version[out] - Output Version string.
Vernon Maueryce14e4d2019-06-25 11:38:20 -0700124 *
AppaRao Pulie99e7ed2020-01-17 12:27:10 +0530125 * @return Returns '0' on success and '-1' on failure.
Vernon Maueryce14e4d2019-06-25 11:38:20 -0700126 *
127 */
AppaRao Pulie99e7ed2020-01-17 12:27:10 +0530128int getActiveSoftwareVersionInfo(ipmi::Context::ptr ctx,
129 const std::string& reqVersionPurpose,
130 std::string& version)
Vernon Maueryce14e4d2019-06-25 11:38:20 -0700131{
AppaRao Pulie99e7ed2020-01-17 12:27:10 +0530132 std::vector<std::string> activeEndPoints;
133 boost::system::error_code ec = ipmi::getDbusProperty(
134 ctx, ipmi::MAPPER_BUS_NAME, softwareFunctionalPath, associationIntf,
135 "endpoints", activeEndPoints);
136 if (ec)
Vernon Maueryce14e4d2019-06-25 11:38:20 -0700137 {
AppaRao Pulie99e7ed2020-01-17 12:27:10 +0530138 phosphor::logging::log<phosphor::logging::level::ERR>(
139 "Failed to get Active firmware version endpoints.");
140 return -1;
Vernon Maueryce14e4d2019-06-25 11:38:20 -0700141 }
142
AppaRao Pulie99e7ed2020-01-17 12:27:10 +0530143 for (auto& activeEndPoint : activeEndPoints)
Vernon Maueryce14e4d2019-06-25 11:38:20 -0700144 {
AppaRao Pulie99e7ed2020-01-17 12:27:10 +0530145 std::string serviceName;
146 ec = ipmi::getService(ctx, softwareActivationIntf, activeEndPoint,
147 serviceName);
148 if (ec)
Vernon Maueryce14e4d2019-06-25 11:38:20 -0700149 {
AppaRao Pulie99e7ed2020-01-17 12:27:10 +0530150 phosphor::logging::log<phosphor::logging::level::ERR>(
151 "Failed to perform getService.",
152 phosphor::logging::entry("OBJPATH=%s", activeEndPoint.c_str()));
153 continue;
154 }
155
156 PropertyMap propMap;
157 ec = ipmi::getAllDbusProperties(ctx, serviceName, activeEndPoint,
158 softwareVerIntf, propMap);
159 if (ec)
160 {
161 phosphor::logging::log<phosphor::logging::level::ERR>(
162 "Failed to perform GetAll on Version interface.",
163 phosphor::logging::entry("SERVICE=%s", serviceName.c_str()),
164 phosphor::logging::entry("PATH=%s", activeEndPoint.c_str()));
165 continue;
166 }
167
168 std::string* purposeProp =
169 std::get_if<std::string>(&propMap["Purpose"]);
170 std::string* versionProp =
171 std::get_if<std::string>(&propMap["Version"]);
172 if (!purposeProp || !versionProp)
173 {
174 phosphor::logging::log<phosphor::logging::level::ERR>(
175 "Failed to get version or purpose property");
176 continue;
177 }
178
179 // Check for requested version information and return if found.
180 if (*purposeProp == reqVersionPurpose)
181 {
182 version = *versionProp;
183 return 0;
Vernon Maueryce14e4d2019-06-25 11:38:20 -0700184 }
185 }
186
AppaRao Pulie99e7ed2020-01-17 12:27:10 +0530187 phosphor::logging::log<phosphor::logging::level::INFO>(
188 "Failed to find version information.",
189 phosphor::logging::entry("PURPOSE=%s", reqVersionPurpose.c_str()));
190 return -1;
Vernon Maueryce14e4d2019-06-25 11:38:20 -0700191}
192
Vernon Maueryce14e4d2019-06-25 11:38:20 -0700193// Support both 2 solutions:
194// 1.Current solution 2.7.0-dev-533-g14dc00e79-5e7d997
195// openbmcTag 2.7.0-dev
196// BuildNo 533
Jia, chunhui82d178c2019-06-27 16:48:14 +0800197// openbmcHash 14dc00e79
Vernon Maueryce14e4d2019-06-25 11:38:20 -0700198// MetaHasg 5e7d997
199//
Jia, chunhui82d178c2019-06-27 16:48:14 +0800200// 2.New solution wht-0.2-3-gab3500-38384ac
201// IdStr wht
Vernon Maueryce14e4d2019-06-25 11:38:20 -0700202// Major 0
Jia, chunhui82d178c2019-06-27 16:48:14 +0800203// Minor 2
204// buildNo 3
205// MetaHash ab3500
206// openbmcHash 38384ac
Vernon Maueryce14e4d2019-06-25 11:38:20 -0700207std::optional<MetaRevision> convertIntelVersion(std::string& s)
208{
209 std::smatch results;
210 MetaRevision rev;
Jia, chunhui82d178c2019-06-27 16:48:14 +0800211 std::regex pattern1("(\\d+?).(\\d+?).\\d+?-\\w*?-(\\d+?)-g(\\w+?)-(\\w+?)");
Vernon Maueryce14e4d2019-06-25 11:38:20 -0700212 constexpr size_t matchedPhosphor = 6;
213 if (std::regex_match(s, results, pattern1))
214 {
215 if (results.size() == matchedPhosphor)
216 {
217 rev.platform = "whtref";
218 rev.major = static_cast<uint8_t>(std::stoi(results[1]));
219 rev.minor = static_cast<uint8_t>(std::stoi(results[2]));
220 rev.buildNo = static_cast<uint32_t>(std::stoi(results[3]));
221 rev.openbmcHash = results[4];
222 rev.metaHash = results[5];
Vernon Maueryce14e4d2019-06-25 11:38:20 -0700223 std::string versionString =
224 rev.platform + ":" + std::to_string(rev.major) + ":" +
225 std::to_string(rev.minor) + ":" + std::to_string(rev.buildNo) +
Jia, chunhui82d178c2019-06-27 16:48:14 +0800226 ":" + rev.openbmcHash + ":" + rev.metaHash;
AppaRao Pulie99e7ed2020-01-17 12:27:10 +0530227 phosphor::logging::log<phosphor::logging::level::INFO>(
Jia, chunhui82d178c2019-06-27 16:48:14 +0800228 "Get BMC version",
AppaRao Pulie99e7ed2020-01-17 12:27:10 +0530229 phosphor::logging::entry("VERSION=%s", versionString.c_str()));
Vernon Maueryce14e4d2019-06-25 11:38:20 -0700230 return rev;
231 }
232 }
Jia, chunhui82d178c2019-06-27 16:48:14 +0800233 constexpr size_t matchedIntel = 7;
234 std::regex pattern2("(\\w+?)-(\\d+?).(\\d+?)-(\\d+?)-g(\\w+?)-(\\w+?)");
Vernon Maueryce14e4d2019-06-25 11:38:20 -0700235 if (std::regex_match(s, results, pattern2))
236 {
237 if (results.size() == matchedIntel)
238 {
239 rev.platform = results[1];
240 rev.major = static_cast<uint8_t>(std::stoi(results[2]));
241 rev.minor = static_cast<uint8_t>(std::stoi(results[3]));
242 rev.buildNo = static_cast<uint32_t>(std::stoi(results[4]));
Jia, chunhui82d178c2019-06-27 16:48:14 +0800243 rev.openbmcHash = results[6];
244 rev.metaHash = results[5];
Vernon Maueryce14e4d2019-06-25 11:38:20 -0700245 std::string versionString =
246 rev.platform + ":" + std::to_string(rev.major) + ":" +
247 std::to_string(rev.minor) + ":" + std::to_string(rev.buildNo) +
Jia, chunhui82d178c2019-06-27 16:48:14 +0800248 ":" + rev.openbmcHash + ":" + rev.metaHash;
AppaRao Pulie99e7ed2020-01-17 12:27:10 +0530249 phosphor::logging::log<phosphor::logging::level::INFO>(
Jia, chunhui82d178c2019-06-27 16:48:14 +0800250 "Get BMC version",
AppaRao Pulie99e7ed2020-01-17 12:27:10 +0530251 phosphor::logging::entry("VERSION=%s", versionString.c_str()));
Vernon Maueryce14e4d2019-06-25 11:38:20 -0700252 return rev;
253 }
254 }
255
256 return std::nullopt;
257}
Chen Yugang7a04f3a2019-10-08 11:12:35 +0800258
Vernon Mauery98bbf692019-09-16 11:14:59 -0700259RspType<uint8_t, // Device ID
260 uint8_t, // Device Revision
AppaRao Pulie99e7ed2020-01-17 12:27:10 +0530261 uint7_t, // Firmware Revision Major
262 bool, // Device available(0=NormalMode,1=DeviceFirmware)
Vernon Mauery98bbf692019-09-16 11:14:59 -0700263 uint8_t, // Firmware Revision minor
264 uint8_t, // IPMI version
265 uint8_t, // Additional device support
266 uint24_t, // MFG ID
267 uint16_t, // Product ID
268 uint32_t // AUX info
269 >
AppaRao Pulie99e7ed2020-01-17 12:27:10 +0530270 ipmiAppGetDeviceId(ipmi::Context::ptr ctx)
Vernon Maueryce14e4d2019-06-25 11:38:20 -0700271{
Vernon Maueryce14e4d2019-06-25 11:38:20 -0700272 static struct
273 {
274 uint8_t id;
275 uint8_t revision;
AppaRao Pulie99e7ed2020-01-17 12:27:10 +0530276 uint7_t fwMajor;
277 bool devBusy;
278 uint8_t fwMinor;
Johnathan Mantey3c8af652019-12-19 11:29:15 -0800279 uint8_t ipmiVer = 2;
Vernon Maueryce14e4d2019-06-25 11:38:20 -0700280 uint8_t addnDevSupport;
281 uint24_t manufId;
282 uint16_t prodId;
283 uint32_t aux;
284 } devId;
AppaRao Pulicb6386b2020-01-16 14:53:30 +0530285 static bool fwVerInitialized = false;
286 static bool devIdInitialized = false;
AppaRao Puli6c6cec42020-02-26 11:56:47 +0530287 static bool bmcStateInitialized = false;
Vernon Maueryce14e4d2019-06-25 11:38:20 -0700288 const char* filename = "/usr/share/ipmi-providers/dev_id.json";
Johnathan Mantey3c8af652019-12-19 11:29:15 -0800289 const char* prodIdFilename = "/var/cache/private/prodID";
AppaRao Pulicb6386b2020-01-16 14:53:30 +0530290 if (!fwVerInitialized)
Vernon Maueryce14e4d2019-06-25 11:38:20 -0700291 {
AppaRao Pulie99e7ed2020-01-17 12:27:10 +0530292 std::string versionString;
293 if (!getActiveSoftwareVersionInfo(ctx, versionPurposeBMC,
294 versionString))
Vernon Maueryce14e4d2019-06-25 11:38:20 -0700295 {
AppaRao Pulie99e7ed2020-01-17 12:27:10 +0530296 std::optional<MetaRevision> rev =
297 convertIntelVersion(versionString);
298 if (rev.has_value())
Jia, chunhui82d178c2019-06-27 16:48:14 +0800299 {
AppaRao Pulie99e7ed2020-01-17 12:27:10 +0530300 MetaRevision revision = rev.value();
301 devId.fwMajor = static_cast<uint7_t>(revision.major);
302
303 revision.minor = (revision.minor > 99 ? 99 : revision.minor);
304 devId.fwMinor =
305 revision.minor % 10 + (revision.minor / 10) * 16;
306 try
307 {
308 uint32_t hash = std::stoul(revision.metaHash, 0, 16);
309 hash = bswap_32(hash);
310 devId.aux = (revision.buildNo & 0xFF) + (hash & 0xFFFFFF00);
311 fwVerInitialized = true;
312 }
313 catch (const std::exception& e)
314 {
315 phosphor::logging::log<phosphor::logging::level::ERR>(
316 "Failed to convert git hash",
317 phosphor::logging::entry("ERROR=%s", e.what()));
318 }
Jia, chunhui82d178c2019-06-27 16:48:14 +0800319 }
Vernon Maueryce14e4d2019-06-25 11:38:20 -0700320 }
AppaRao Pulicb6386b2020-01-16 14:53:30 +0530321 }
Vernon Maueryce14e4d2019-06-25 11:38:20 -0700322
AppaRao Pulicb6386b2020-01-16 14:53:30 +0530323 if (!devIdInitialized)
324 {
Vernon Maueryce14e4d2019-06-25 11:38:20 -0700325 std::ifstream devIdFile(filename);
326 if (devIdFile.is_open())
327 {
328 auto data = nlohmann::json::parse(devIdFile, nullptr, false);
329 if (!data.is_discarded())
330 {
331 devId.id = data.value("id", 0);
332 devId.revision = data.value("revision", 0);
333 devId.addnDevSupport = data.value("addn_dev_support", 0);
334 devId.manufId = data.value("manuf_id", 0);
Vernon Maueryce14e4d2019-06-25 11:38:20 -0700335 }
336 else
337 {
AppaRao Pulie99e7ed2020-01-17 12:27:10 +0530338 phosphor::logging::log<phosphor::logging::level::ERR>(
339 "Device ID JSON parser failure");
Vernon Maueryce14e4d2019-06-25 11:38:20 -0700340 return ipmi::responseUnspecifiedError();
341 }
342 }
343 else
344 {
AppaRao Pulie99e7ed2020-01-17 12:27:10 +0530345 phosphor::logging::log<phosphor::logging::level::ERR>(
346 "Device ID file not found");
Vernon Maueryce14e4d2019-06-25 11:38:20 -0700347 return ipmi::responseUnspecifiedError();
348 }
Johnathan Mantey3c8af652019-12-19 11:29:15 -0800349
350 // Determine the Product ID. Using the DBus system is painfully slow at
351 // boot time. Avoid using DBus to get the Product ID. The Product ID is
352 // stored in a non-volatile file now. The /usr/bin/checkFru.sh script,
353 // run during bootup, will populate the productIdFile.
354 std::fstream prodIdFile(prodIdFilename);
355 if (prodIdFile.is_open())
356 {
357 std::string id = "0x00";
358 char* end;
359 prodIdFile.getline(&id[0], id.size() + 1);
360 devId.prodId = std::strtol(&id[0], &end, 0);
AppaRao Pulicb6386b2020-01-16 14:53:30 +0530361 devIdInitialized = true;
Johnathan Mantey3c8af652019-12-19 11:29:15 -0800362 }
363 else
364 {
365 // For any exception send out platform id as 0,
366 // and make sure to re-query the device id.
AppaRao Pulicb6386b2020-01-16 14:53:30 +0530367 devIdInitialized = false;
Johnathan Mantey3c8af652019-12-19 11:29:15 -0800368 devId.prodId = 0;
369 }
Vernon Maueryce14e4d2019-06-25 11:38:20 -0700370 }
371
AppaRao Puli6c6cec42020-02-26 11:56:47 +0530372 if (!bmcStateInitialized)
373 {
374 if (!initBMCDeviceState(ctx))
375 {
376 bmcStateInitialized = true;
377 }
378 }
Vernon Maueryce14e4d2019-06-25 11:38:20 -0700379
AppaRao Pulie99e7ed2020-01-17 12:27:10 +0530380 return ipmi::responseSuccess(devId.id, devId.revision, devId.fwMajor,
AppaRao Puli6c6cec42020-02-26 11:56:47 +0530381 bmcDeviceBusy, devId.fwMinor, devId.ipmiVer,
AppaRao Pulie99e7ed2020-01-17 12:27:10 +0530382 devId.addnDevSupport, devId.manufId,
383 devId.prodId, devId.aux);
Vernon Maueryce14e4d2019-06-25 11:38:20 -0700384}
385
386static void registerAPPFunctions(void)
387{
Vernon Maueryce14e4d2019-06-25 11:38:20 -0700388 // <Get Device ID>
Vernon Mauery98bbf692019-09-16 11:14:59 -0700389 registerHandler(prioOemBase, netFnApp, app::cmdGetDeviceId, Privilege::User,
390 ipmiAppGetDeviceId);
Vernon Maueryce14e4d2019-06-25 11:38:20 -0700391}
392
393} // namespace ipmi