blob: 315b581d13b8d5b0855b733ba0c18f41b1833c40 [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>
Jason M. Bills0748c692022-09-08 15:34:08 -070023#include <types.hpp>
James Feistfcd2d3a2020-05-28 10:38:15 -070024
25#include <fstream>
Vernon Maueryce14e4d2019-06-25 11:38:20 -070026#include <regex>
Vernon Maueryce14e4d2019-06-25 11:38:20 -070027
28namespace ipmi
29{
30
31static void registerAPPFunctions() __attribute__((constructor));
32
AppaRao Pulie99e7ed2020-01-17 12:27:10 +053033static constexpr const char* bmcStateIntf = "xyz.openbmc_project.State.BMC";
34static constexpr const char* softwareVerIntf =
35 "xyz.openbmc_project.Software.Version";
36static constexpr const char* softwareActivationIntf =
37 "xyz.openbmc_project.Software.Activation";
38static constexpr const char* associationIntf =
39 "xyz.openbmc_project.Association";
40static constexpr const char* softwareFunctionalPath =
41 "/xyz/openbmc_project/software/functional";
Vernon Maueryce14e4d2019-06-25 11:38:20 -070042
AppaRao Puli6c6cec42020-02-26 11:56:47 +053043static constexpr const char* currentBmcStateProp = "CurrentBMCState";
44static constexpr const char* bmcStateReadyStr =
45 "xyz.openbmc_project.State.BMC.BMCState.Ready";
Vernon Maueryce14e4d2019-06-25 11:38:20 -070046
Patrick Williamsf944d2e2022-07-22 19:26:52 -050047static std::unique_ptr<sdbusplus::bus::match_t> bmcStateChangedSignal;
AppaRao Puli6c6cec42020-02-26 11:56:47 +053048static uint8_t bmcDeviceBusy = true;
49
50int initBMCDeviceState(ipmi::Context::ptr ctx)
Vernon Maueryce14e4d2019-06-25 11:38:20 -070051{
AppaRao Puli6c6cec42020-02-26 11:56:47 +053052 DbusObjectInfo objInfo;
Patrick Williamsb37abfb2023-05-10 07:50:33 -050053 boost::system::error_code ec = ipmi::getDbusObject(ctx, bmcStateIntf, "/",
54 "bmc0", objInfo);
AppaRao Puli6c6cec42020-02-26 11:56:47 +053055 if (ec)
Vernon Maueryce14e4d2019-06-25 11:38:20 -070056 {
AppaRao Puli6c6cec42020-02-26 11:56:47 +053057 phosphor::logging::log<phosphor::logging::level::ERR>(
58 "initBMCDeviceState: Failed to perform GetSubTree action",
59 phosphor::logging::entry("ERROR=%s", ec.message().c_str()),
60 phosphor::logging::entry("INTERFACE=%s", bmcStateIntf));
61 return -1;
Vernon Maueryce14e4d2019-06-25 11:38:20 -070062 }
AppaRao Puli6c6cec42020-02-26 11:56:47 +053063
64 std::string bmcState;
65 ec = ipmi::getDbusProperty(ctx, objInfo.second, objInfo.first, bmcStateIntf,
66 currentBmcStateProp, bmcState);
67 if (ec)
Vernon Maueryce14e4d2019-06-25 11:38:20 -070068 {
AppaRao Puli6c6cec42020-02-26 11:56:47 +053069 phosphor::logging::log<phosphor::logging::level::ERR>(
70 "initBMCDeviceState: Failed to get CurrentBMCState property",
71 phosphor::logging::entry("ERROR=%s", ec.message().c_str()));
72 return -1;
Vernon Maueryce14e4d2019-06-25 11:38:20 -070073 }
AppaRao Puli6c6cec42020-02-26 11:56:47 +053074
75 bmcDeviceBusy = (bmcState != bmcStateReadyStr);
76
77 phosphor::logging::log<phosphor::logging::level::INFO>(
78 "BMC device state updated");
79
80 // BMC state may change runtime while doing firmware udpate.
81 // Register for property change signal to update state.
Patrick Williamsf944d2e2022-07-22 19:26:52 -050082 bmcStateChangedSignal = std::make_unique<sdbusplus::bus::match_t>(
AppaRao Puli6c6cec42020-02-26 11:56:47 +053083 *(ctx->bus),
84 sdbusplus::bus::match::rules::propertiesChanged(objInfo.first,
85 bmcStateIntf),
Patrick Williamsf944d2e2022-07-22 19:26:52 -050086 [](sdbusplus::message_t& msg) {
Patrick Williamsb37abfb2023-05-10 07:50:33 -050087 std::map<std::string, ipmi::DbusVariant> props;
88 std::vector<std::string> inVal;
89 std::string iface;
90 try
91 {
92 msg.read(iface, props, inVal);
93 }
94 catch (const std::exception& e)
95 {
96 phosphor::logging::log<phosphor::logging::level::ERR>(
97 "Exception caught in Get CurrentBMCState");
98 return;
99 }
AppaRao Puli6c6cec42020-02-26 11:56:47 +0530100
Patrick Williamsb37abfb2023-05-10 07:50:33 -0500101 auto it = props.find(currentBmcStateProp);
102 if (it != props.end())
103 {
104 std::string* state = std::get_if<std::string>(&it->second);
105 if (state)
AppaRao Puli6c6cec42020-02-26 11:56:47 +0530106 {
Patrick Williamsb37abfb2023-05-10 07:50:33 -0500107 bmcDeviceBusy = (*state != bmcStateReadyStr);
108 phosphor::logging::log<phosphor::logging::level::INFO>(
109 "BMC device state updated");
AppaRao Puli6c6cec42020-02-26 11:56:47 +0530110 }
Patrick Williamsb37abfb2023-05-10 07:50:33 -0500111 }
AppaRao Puli6c6cec42020-02-26 11:56:47 +0530112 });
113
114 return 0;
Vernon Maueryce14e4d2019-06-25 11:38:20 -0700115}
Jia, chunhui82d178c2019-06-27 16:48:14 +0800116
Vernon Maueryce14e4d2019-06-25 11:38:20 -0700117/**
AppaRao Pulie99e7ed2020-01-17 12:27:10 +0530118 * @brief Returns the functional firmware version information.
Vernon Maueryce14e4d2019-06-25 11:38:20 -0700119 *
AppaRao Pulie99e7ed2020-01-17 12:27:10 +0530120 * It reads the active firmware versions by checking functional
121 * endpoints association and matching the input version purpose string.
122 * ctx[in] - ipmi context.
123 * reqVersionPurpose[in] - Version purpose which need to be read.
124 * version[out] - Output Version string.
Vernon Maueryce14e4d2019-06-25 11:38:20 -0700125 *
AppaRao Pulie99e7ed2020-01-17 12:27:10 +0530126 * @return Returns '0' on success and '-1' on failure.
Vernon Maueryce14e4d2019-06-25 11:38:20 -0700127 *
128 */
AppaRao Pulie99e7ed2020-01-17 12:27:10 +0530129int getActiveSoftwareVersionInfo(ipmi::Context::ptr ctx,
130 const std::string& reqVersionPurpose,
131 std::string& version)
Vernon Maueryce14e4d2019-06-25 11:38:20 -0700132{
AppaRao Pulie99e7ed2020-01-17 12:27:10 +0530133 std::vector<std::string> activeEndPoints;
134 boost::system::error_code ec = ipmi::getDbusProperty(
135 ctx, ipmi::MAPPER_BUS_NAME, softwareFunctionalPath, associationIntf,
136 "endpoints", activeEndPoints);
137 if (ec)
Vernon Maueryce14e4d2019-06-25 11:38:20 -0700138 {
AppaRao Pulie99e7ed2020-01-17 12:27:10 +0530139 phosphor::logging::log<phosphor::logging::level::ERR>(
140 "Failed to get Active firmware version endpoints.");
141 return -1;
Vernon Maueryce14e4d2019-06-25 11:38:20 -0700142 }
143
AppaRao Pulie99e7ed2020-01-17 12:27:10 +0530144 for (auto& activeEndPoint : activeEndPoints)
Vernon Maueryce14e4d2019-06-25 11:38:20 -0700145 {
AppaRao Pulie99e7ed2020-01-17 12:27:10 +0530146 std::string serviceName;
147 ec = ipmi::getService(ctx, softwareActivationIntf, activeEndPoint,
148 serviceName);
149 if (ec)
Vernon Maueryce14e4d2019-06-25 11:38:20 -0700150 {
AppaRao Pulie99e7ed2020-01-17 12:27:10 +0530151 phosphor::logging::log<phosphor::logging::level::ERR>(
152 "Failed to perform getService.",
153 phosphor::logging::entry("OBJPATH=%s", activeEndPoint.c_str()));
154 continue;
155 }
156
157 PropertyMap propMap;
158 ec = ipmi::getAllDbusProperties(ctx, serviceName, activeEndPoint,
159 softwareVerIntf, propMap);
160 if (ec)
161 {
162 phosphor::logging::log<phosphor::logging::level::ERR>(
163 "Failed to perform GetAll on Version interface.",
164 phosphor::logging::entry("SERVICE=%s", serviceName.c_str()),
165 phosphor::logging::entry("PATH=%s", activeEndPoint.c_str()));
166 continue;
167 }
168
169 std::string* purposeProp =
170 std::get_if<std::string>(&propMap["Purpose"]);
171 std::string* versionProp =
172 std::get_if<std::string>(&propMap["Version"]);
173 if (!purposeProp || !versionProp)
174 {
175 phosphor::logging::log<phosphor::logging::level::ERR>(
176 "Failed to get version or purpose property");
177 continue;
178 }
179
180 // Check for requested version information and return if found.
181 if (*purposeProp == reqVersionPurpose)
182 {
183 version = *versionProp;
184 return 0;
Vernon Maueryce14e4d2019-06-25 11:38:20 -0700185 }
186 }
187
AppaRao Pulie99e7ed2020-01-17 12:27:10 +0530188 phosphor::logging::log<phosphor::logging::level::INFO>(
189 "Failed to find version information.",
190 phosphor::logging::entry("PURPOSE=%s", reqVersionPurpose.c_str()));
191 return -1;
Vernon Maueryce14e4d2019-06-25 11:38:20 -0700192}
193
Vernon Maueryce14e4d2019-06-25 11:38:20 -0700194// Support both 2 solutions:
195// 1.Current solution 2.7.0-dev-533-g14dc00e79-5e7d997
196// openbmcTag 2.7.0-dev
197// BuildNo 533
Jia, chunhui82d178c2019-06-27 16:48:14 +0800198// openbmcHash 14dc00e79
Vernon Maueryce14e4d2019-06-25 11:38:20 -0700199// MetaHasg 5e7d997
200//
Jia, chunhui82d178c2019-06-27 16:48:14 +0800201// 2.New solution wht-0.2-3-gab3500-38384ac
202// IdStr wht
Vernon Maueryce14e4d2019-06-25 11:38:20 -0700203// Major 0
Jia, chunhui82d178c2019-06-27 16:48:14 +0800204// Minor 2
205// buildNo 3
206// MetaHash ab3500
207// openbmcHash 38384ac
Vernon Maueryce14e4d2019-06-25 11:38:20 -0700208std::optional<MetaRevision> convertIntelVersion(std::string& s)
209{
210 std::smatch results;
211 MetaRevision rev;
Jia, chunhui82d178c2019-06-27 16:48:14 +0800212 std::regex pattern1("(\\d+?).(\\d+?).\\d+?-\\w*?-(\\d+?)-g(\\w+?)-(\\w+?)");
Vernon Maueryce14e4d2019-06-25 11:38:20 -0700213 constexpr size_t matchedPhosphor = 6;
214 if (std::regex_match(s, results, pattern1))
215 {
216 if (results.size() == matchedPhosphor)
217 {
218 rev.platform = "whtref";
219 rev.major = static_cast<uint8_t>(std::stoi(results[1]));
220 rev.minor = static_cast<uint8_t>(std::stoi(results[2]));
221 rev.buildNo = static_cast<uint32_t>(std::stoi(results[3]));
222 rev.openbmcHash = results[4];
223 rev.metaHash = results[5];
Vernon Maueryce14e4d2019-06-25 11:38:20 -0700224 std::string versionString =
225 rev.platform + ":" + std::to_string(rev.major) + ":" +
226 std::to_string(rev.minor) + ":" + std::to_string(rev.buildNo) +
Jia, chunhui82d178c2019-06-27 16:48:14 +0800227 ":" + rev.openbmcHash + ":" + rev.metaHash;
AppaRao Pulie99e7ed2020-01-17 12:27:10 +0530228 phosphor::logging::log<phosphor::logging::level::INFO>(
Jia, chunhui82d178c2019-06-27 16:48:14 +0800229 "Get BMC version",
AppaRao Pulie99e7ed2020-01-17 12:27:10 +0530230 phosphor::logging::entry("VERSION=%s", versionString.c_str()));
Vernon Maueryce14e4d2019-06-25 11:38:20 -0700231 return rev;
232 }
233 }
Jia, chunhui82d178c2019-06-27 16:48:14 +0800234 constexpr size_t matchedIntel = 7;
235 std::regex pattern2("(\\w+?)-(\\d+?).(\\d+?)-(\\d+?)-g(\\w+?)-(\\w+?)");
Vernon Maueryce14e4d2019-06-25 11:38:20 -0700236 if (std::regex_match(s, results, pattern2))
237 {
238 if (results.size() == matchedIntel)
239 {
240 rev.platform = results[1];
241 rev.major = static_cast<uint8_t>(std::stoi(results[2]));
242 rev.minor = static_cast<uint8_t>(std::stoi(results[3]));
243 rev.buildNo = static_cast<uint32_t>(std::stoi(results[4]));
Jia, chunhui82d178c2019-06-27 16:48:14 +0800244 rev.openbmcHash = results[6];
245 rev.metaHash = results[5];
Vernon Maueryce14e4d2019-06-25 11:38:20 -0700246 std::string versionString =
247 rev.platform + ":" + std::to_string(rev.major) + ":" +
248 std::to_string(rev.minor) + ":" + std::to_string(rev.buildNo) +
Jia, chunhui82d178c2019-06-27 16:48:14 +0800249 ":" + rev.openbmcHash + ":" + rev.metaHash;
AppaRao Pulie99e7ed2020-01-17 12:27:10 +0530250 phosphor::logging::log<phosphor::logging::level::INFO>(
Jia, chunhui82d178c2019-06-27 16:48:14 +0800251 "Get BMC version",
AppaRao Pulie99e7ed2020-01-17 12:27:10 +0530252 phosphor::logging::entry("VERSION=%s", versionString.c_str()));
Vernon Maueryce14e4d2019-06-25 11:38:20 -0700253 return rev;
254 }
255 }
256
257 return std::nullopt;
258}
Chen Yugang7a04f3a2019-10-08 11:12:35 +0800259
Vernon Mauery98bbf692019-09-16 11:14:59 -0700260RspType<uint8_t, // Device ID
261 uint8_t, // Device Revision
AppaRao Pulie99e7ed2020-01-17 12:27:10 +0530262 uint7_t, // Firmware Revision Major
263 bool, // Device available(0=NormalMode,1=DeviceFirmware)
Vernon Mauery98bbf692019-09-16 11:14:59 -0700264 uint8_t, // Firmware Revision minor
265 uint8_t, // IPMI version
266 uint8_t, // Additional device support
267 uint24_t, // MFG ID
268 uint16_t, // Product ID
269 uint32_t // AUX info
270 >
AppaRao Pulie99e7ed2020-01-17 12:27:10 +0530271 ipmiAppGetDeviceId(ipmi::Context::ptr ctx)
Vernon Maueryce14e4d2019-06-25 11:38:20 -0700272{
Vernon Maueryce14e4d2019-06-25 11:38:20 -0700273 static struct
274 {
275 uint8_t id;
276 uint8_t revision;
AppaRao Pulie99e7ed2020-01-17 12:27:10 +0530277 uint7_t fwMajor;
278 bool devBusy;
279 uint8_t fwMinor;
Johnathan Mantey3c8af652019-12-19 11:29:15 -0800280 uint8_t ipmiVer = 2;
Vernon Maueryce14e4d2019-06-25 11:38:20 -0700281 uint8_t addnDevSupport;
282 uint24_t manufId;
283 uint16_t prodId;
284 uint32_t aux;
285 } devId;
AppaRao Pulicb6386b2020-01-16 14:53:30 +0530286 static bool fwVerInitialized = false;
287 static bool devIdInitialized = false;
AppaRao Puli6c6cec42020-02-26 11:56:47 +0530288 static bool bmcStateInitialized = false;
Vernon Maueryce14e4d2019-06-25 11:38:20 -0700289 const char* filename = "/usr/share/ipmi-providers/dev_id.json";
Johnathan Mantey3c8af652019-12-19 11:29:15 -0800290 const char* prodIdFilename = "/var/cache/private/prodID";
AppaRao Pulicb6386b2020-01-16 14:53:30 +0530291 if (!fwVerInitialized)
Vernon Maueryce14e4d2019-06-25 11:38:20 -0700292 {
AppaRao Pulie99e7ed2020-01-17 12:27:10 +0530293 std::string versionString;
294 if (!getActiveSoftwareVersionInfo(ctx, versionPurposeBMC,
295 versionString))
Vernon Maueryce14e4d2019-06-25 11:38:20 -0700296 {
AppaRao Pulie99e7ed2020-01-17 12:27:10 +0530297 std::optional<MetaRevision> rev =
298 convertIntelVersion(versionString);
299 if (rev.has_value())
Jia, chunhui82d178c2019-06-27 16:48:14 +0800300 {
AppaRao Pulie99e7ed2020-01-17 12:27:10 +0530301 MetaRevision revision = rev.value();
302 devId.fwMajor = static_cast<uint7_t>(revision.major);
303
304 revision.minor = (revision.minor > 99 ? 99 : revision.minor);
Patrick Williamsb37abfb2023-05-10 07:50:33 -0500305 devId.fwMinor = revision.minor % 10 +
306 (revision.minor / 10) * 16;
AppaRao Pulie99e7ed2020-01-17 12:27:10 +0530307 try
308 {
309 uint32_t hash = std::stoul(revision.metaHash, 0, 16);
310 hash = bswap_32(hash);
311 devId.aux = (revision.buildNo & 0xFF) + (hash & 0xFFFFFF00);
312 fwVerInitialized = true;
313 }
314 catch (const std::exception& e)
315 {
316 phosphor::logging::log<phosphor::logging::level::ERR>(
317 "Failed to convert git hash",
318 phosphor::logging::entry("ERROR=%s", e.what()));
319 }
Jia, chunhui82d178c2019-06-27 16:48:14 +0800320 }
Vernon Maueryce14e4d2019-06-25 11:38:20 -0700321 }
AppaRao Pulicb6386b2020-01-16 14:53:30 +0530322 }
Vernon Maueryce14e4d2019-06-25 11:38:20 -0700323
AppaRao Pulicb6386b2020-01-16 14:53:30 +0530324 if (!devIdInitialized)
325 {
Vernon Maueryce14e4d2019-06-25 11:38:20 -0700326 std::ifstream devIdFile(filename);
327 if (devIdFile.is_open())
328 {
329 auto data = nlohmann::json::parse(devIdFile, nullptr, false);
330 if (!data.is_discarded())
331 {
332 devId.id = data.value("id", 0);
333 devId.revision = data.value("revision", 0);
334 devId.addnDevSupport = data.value("addn_dev_support", 0);
335 devId.manufId = data.value("manuf_id", 0);
Vernon Maueryce14e4d2019-06-25 11:38:20 -0700336 }
337 else
338 {
AppaRao Pulie99e7ed2020-01-17 12:27:10 +0530339 phosphor::logging::log<phosphor::logging::level::ERR>(
340 "Device ID JSON parser failure");
Vernon Maueryce14e4d2019-06-25 11:38:20 -0700341 return ipmi::responseUnspecifiedError();
342 }
343 }
344 else
345 {
AppaRao Pulie99e7ed2020-01-17 12:27:10 +0530346 phosphor::logging::log<phosphor::logging::level::ERR>(
347 "Device ID file not found");
Vernon Maueryce14e4d2019-06-25 11:38:20 -0700348 return ipmi::responseUnspecifiedError();
349 }
Johnathan Mantey3c8af652019-12-19 11:29:15 -0800350
351 // Determine the Product ID. Using the DBus system is painfully slow at
352 // boot time. Avoid using DBus to get the Product ID. The Product ID is
353 // stored in a non-volatile file now. The /usr/bin/checkFru.sh script,
354 // run during bootup, will populate the productIdFile.
355 std::fstream prodIdFile(prodIdFilename);
356 if (prodIdFile.is_open())
357 {
358 std::string id = "0x00";
359 char* end;
360 prodIdFile.getline(&id[0], id.size() + 1);
361 devId.prodId = std::strtol(&id[0], &end, 0);
AppaRao Pulicb6386b2020-01-16 14:53:30 +0530362 devIdInitialized = true;
Johnathan Mantey3c8af652019-12-19 11:29:15 -0800363 }
364 else
365 {
366 // For any exception send out platform id as 0,
367 // and make sure to re-query the device id.
AppaRao Pulicb6386b2020-01-16 14:53:30 +0530368 devIdInitialized = false;
Johnathan Mantey3c8af652019-12-19 11:29:15 -0800369 devId.prodId = 0;
370 }
Vernon Maueryce14e4d2019-06-25 11:38:20 -0700371 }
372
AppaRao Puli6c6cec42020-02-26 11:56:47 +0530373 if (!bmcStateInitialized)
374 {
375 if (!initBMCDeviceState(ctx))
376 {
377 bmcStateInitialized = true;
378 }
379 }
Vernon Maueryce14e4d2019-06-25 11:38:20 -0700380
AppaRao Pulie99e7ed2020-01-17 12:27:10 +0530381 return ipmi::responseSuccess(devId.id, devId.revision, devId.fwMajor,
AppaRao Puli6c6cec42020-02-26 11:56:47 +0530382 bmcDeviceBusy, devId.fwMinor, devId.ipmiVer,
AppaRao Pulie99e7ed2020-01-17 12:27:10 +0530383 devId.addnDevSupport, devId.manufId,
384 devId.prodId, devId.aux);
Vernon Maueryce14e4d2019-06-25 11:38:20 -0700385}
386
387static void registerAPPFunctions(void)
388{
Vernon Maueryce14e4d2019-06-25 11:38:20 -0700389 // <Get Device ID>
Vernon Mauery98bbf692019-09-16 11:14:59 -0700390 registerHandler(prioOemBase, netFnApp, app::cmdGetDeviceId, Privilege::User,
391 ipmiAppGetDeviceId);
Vernon Maueryce14e4d2019-06-25 11:38:20 -0700392}
393
394} // namespace ipmi