blob: 539d1191a06bbf80b927751a31a48c9f0e6f832f [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*/
16#include "xyz/openbmc_project/Common/error.hpp"
17
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>
25#include <xyz/openbmc_project/Software/Activation/server.hpp>
26#include <xyz/openbmc_project/Software/Version/server.hpp>
27#include <xyz/openbmc_project/State/BMC/server.hpp>
28
29namespace ipmi
30{
31
32static void registerAPPFunctions() __attribute__((constructor));
33
34namespace Log = phosphor::logging;
35namespace Error = sdbusplus::xyz::openbmc_project::Common::Error;
36using Version = sdbusplus::xyz::openbmc_project::Software::server::Version;
37using Activation =
38 sdbusplus::xyz::openbmc_project::Software::server::Activation;
39using BMC = sdbusplus::xyz::openbmc_project::State::server::BMC;
40
41constexpr auto bmc_state_interface = "xyz.openbmc_project.State.BMC";
42constexpr auto bmc_state_property = "CurrentBMCState";
43
Vernon Maueryce14e4d2019-06-25 11:38:20 -070044static constexpr auto redundancyIntf =
45 "xyz.openbmc_project.Software.RedundancyPriority";
46static constexpr auto versionIntf = "xyz.openbmc_project.Software.Version";
47static constexpr auto activationIntf =
48 "xyz.openbmc_project.Software.Activation";
49static constexpr auto softwareRoot = "/xyz/openbmc_project/software";
50
51bool getCurrentBmcState()
52{
53 sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()};
54
55 // Get the Inventory object implementing the BMC interface
56 ipmi::DbusObjectInfo bmcObject =
57 ipmi::getDbusObject(bus, bmc_state_interface);
58 auto variant =
59 ipmi::getDbusProperty(bus, bmcObject.second, bmcObject.first,
60 bmc_state_interface, bmc_state_property);
61
62 return std::holds_alternative<std::string>(variant) &&
63 BMC::convertBMCStateFromString(std::get<std::string>(variant)) ==
64 BMC::BMCState::Ready;
65}
66
67bool getCurrentBmcStateWithFallback(const bool fallbackAvailability)
68{
69 try
70 {
71 return getCurrentBmcState();
72 }
73 catch (...)
74 {
75 // Nothing provided the BMC interface, therefore return whatever was
76 // configured as the default.
77 return fallbackAvailability;
78 }
79}
Jia, chunhui82d178c2019-06-27 16:48:14 +080080
Vernon Maueryce14e4d2019-06-25 11:38:20 -070081/**
Jia, chunhui82d178c2019-06-27 16:48:14 +080082 * @brief Returns the Version info from primary software object
Vernon Maueryce14e4d2019-06-25 11:38:20 -070083 *
84 * Get the Version info from the active s/w object which is having high
85 * "Priority" value(a smaller number is a higher priority) and "Purpose"
86 * is "BMC" from the list of all s/w objects those are implementing
87 * RedundancyPriority interface from the given softwareRoot path.
88 *
Jia, chunhui82d178c2019-06-27 16:48:14 +080089 * @return On success returns the Version info from primary software object.
Vernon Maueryce14e4d2019-06-25 11:38:20 -070090 *
91 */
92std::string getActiveSoftwareVersionInfo()
93{
94 auto busp = getSdBus();
95
96 std::string revision{};
97 ipmi::ObjectTree objectTree;
98 try
99 {
100 objectTree =
101 ipmi::getAllDbusObjects(*busp, softwareRoot, redundancyIntf);
102 }
103 catch (sdbusplus::exception::SdBusError& e)
104 {
105 Log::log<Log::level::ERR>("Failed to fetch redundancy object from dbus",
106 Log::entry("INTERFACE=%s", redundancyIntf),
107 Log::entry("ERRMSG=%s", e.what()));
108 }
109
110 auto objectFound = false;
111 for (auto& softObject : objectTree)
112 {
113 auto service =
114 ipmi::getService(*busp, redundancyIntf, softObject.first);
115 auto objValueTree =
116 ipmi::getManagedObjects(*busp, service, softwareRoot);
117
118 auto minPriority = 0xFF;
119 for (const auto& objIter : objValueTree)
120 {
121 try
122 {
123 auto& intfMap = objIter.second;
124 auto& redundancyPriorityProps = intfMap.at(redundancyIntf);
125 auto& versionProps = intfMap.at(versionIntf);
126 auto& activationProps = intfMap.at(activationIntf);
127 auto priority =
128 std::get<uint8_t>(redundancyPriorityProps.at("Priority"));
129 auto purpose =
130 std::get<std::string>(versionProps.at("Purpose"));
131 auto activation =
132 std::get<std::string>(activationProps.at("Activation"));
133 auto version =
134 std::get<std::string>(versionProps.at("Version"));
135 if ((Version::convertVersionPurposeFromString(purpose) ==
136 Version::VersionPurpose::BMC) &&
137 (Activation::convertActivationsFromString(activation) ==
138 Activation::Activations::Active))
139 {
140 if (priority < minPriority)
141 {
142 minPriority = priority;
143 objectFound = true;
144 revision = std::move(version);
145 }
146 }
147 }
148 catch (const std::exception& e)
149 {
150 Log::log<Log::level::ERR>(e.what());
151 }
152 }
153 }
154
155 if (!objectFound)
156 {
Jia, chunhui82d178c2019-06-27 16:48:14 +0800157 Log::log<Log::level::ERR>("Could not find an BMC software object");
Vernon Maueryce14e4d2019-06-25 11:38:20 -0700158 }
159
160 return revision;
161}
162
Vernon Maueryce14e4d2019-06-25 11:38:20 -0700163// Support both 2 solutions:
164// 1.Current solution 2.7.0-dev-533-g14dc00e79-5e7d997
165// openbmcTag 2.7.0-dev
166// BuildNo 533
Jia, chunhui82d178c2019-06-27 16:48:14 +0800167// openbmcHash 14dc00e79
Vernon Maueryce14e4d2019-06-25 11:38:20 -0700168// MetaHasg 5e7d997
169//
Jia, chunhui82d178c2019-06-27 16:48:14 +0800170// 2.New solution wht-0.2-3-gab3500-38384ac
171// IdStr wht
Vernon Maueryce14e4d2019-06-25 11:38:20 -0700172// Major 0
Jia, chunhui82d178c2019-06-27 16:48:14 +0800173// Minor 2
174// buildNo 3
175// MetaHash ab3500
176// openbmcHash 38384ac
Vernon Maueryce14e4d2019-06-25 11:38:20 -0700177std::optional<MetaRevision> convertIntelVersion(std::string& s)
178{
179 std::smatch results;
180 MetaRevision rev;
Jia, chunhui82d178c2019-06-27 16:48:14 +0800181 std::regex pattern1("(\\d+?).(\\d+?).\\d+?-\\w*?-(\\d+?)-g(\\w+?)-(\\w+?)");
Vernon Maueryce14e4d2019-06-25 11:38:20 -0700182 constexpr size_t matchedPhosphor = 6;
183 if (std::regex_match(s, results, pattern1))
184 {
185 if (results.size() == matchedPhosphor)
186 {
187 rev.platform = "whtref";
188 rev.major = static_cast<uint8_t>(std::stoi(results[1]));
189 rev.minor = static_cast<uint8_t>(std::stoi(results[2]));
190 rev.buildNo = static_cast<uint32_t>(std::stoi(results[3]));
191 rev.openbmcHash = results[4];
192 rev.metaHash = results[5];
Vernon Maueryce14e4d2019-06-25 11:38:20 -0700193 std::string versionString =
194 rev.platform + ":" + std::to_string(rev.major) + ":" +
195 std::to_string(rev.minor) + ":" + std::to_string(rev.buildNo) +
Jia, chunhui82d178c2019-06-27 16:48:14 +0800196 ":" + rev.openbmcHash + ":" + rev.metaHash;
197 Log::log<Log::level::INFO>(
198 "Get BMC version",
199 Log::entry("VERSION=%s", versionString.c_str()));
Vernon Maueryce14e4d2019-06-25 11:38:20 -0700200 return rev;
201 }
202 }
Jia, chunhui82d178c2019-06-27 16:48:14 +0800203 constexpr size_t matchedIntel = 7;
204 std::regex pattern2("(\\w+?)-(\\d+?).(\\d+?)-(\\d+?)-g(\\w+?)-(\\w+?)");
Vernon Maueryce14e4d2019-06-25 11:38:20 -0700205 if (std::regex_match(s, results, pattern2))
206 {
207 if (results.size() == matchedIntel)
208 {
209 rev.platform = results[1];
210 rev.major = static_cast<uint8_t>(std::stoi(results[2]));
211 rev.minor = static_cast<uint8_t>(std::stoi(results[3]));
212 rev.buildNo = static_cast<uint32_t>(std::stoi(results[4]));
Jia, chunhui82d178c2019-06-27 16:48:14 +0800213 rev.openbmcHash = results[6];
214 rev.metaHash = results[5];
Vernon Maueryce14e4d2019-06-25 11:38:20 -0700215 std::string versionString =
216 rev.platform + ":" + std::to_string(rev.major) + ":" +
217 std::to_string(rev.minor) + ":" + std::to_string(rev.buildNo) +
Jia, chunhui82d178c2019-06-27 16:48:14 +0800218 ":" + rev.openbmcHash + ":" + rev.metaHash;
219 Log::log<Log::level::INFO>(
220 "Get BMC version",
221 Log::entry("VERSION=%s", versionString.c_str()));
Vernon Maueryce14e4d2019-06-25 11:38:20 -0700222 return rev;
223 }
224 }
225
226 return std::nullopt;
227}
Chen Yugang7a04f3a2019-10-08 11:12:35 +0800228
Vernon Mauery98bbf692019-09-16 11:14:59 -0700229RspType<uint8_t, // Device ID
230 uint8_t, // Device Revision
231 uint8_t, // Firmware Revision Major
232 uint8_t, // Firmware Revision minor
233 uint8_t, // IPMI version
234 uint8_t, // Additional device support
235 uint24_t, // MFG ID
236 uint16_t, // Product ID
237 uint32_t // AUX info
238 >
239 ipmiAppGetDeviceId()
Vernon Maueryce14e4d2019-06-25 11:38:20 -0700240{
Vernon Maueryce14e4d2019-06-25 11:38:20 -0700241 static struct
242 {
243 uint8_t id;
244 uint8_t revision;
245 uint8_t fw[2];
Johnathan Mantey3c8af652019-12-19 11:29:15 -0800246 uint8_t ipmiVer = 2;
Vernon Maueryce14e4d2019-06-25 11:38:20 -0700247 uint8_t addnDevSupport;
248 uint24_t manufId;
249 uint16_t prodId;
250 uint32_t aux;
251 } devId;
AppaRao Pulicb6386b2020-01-16 14:53:30 +0530252 static bool fwVerInitialized = false;
253 static bool devIdInitialized = false;
Johnathan Mantey3c8af652019-12-19 11:29:15 -0800254 static bool defaultActivationSetting = false;
Vernon Maueryce14e4d2019-06-25 11:38:20 -0700255 const char* filename = "/usr/share/ipmi-providers/dev_id.json";
Johnathan Mantey3c8af652019-12-19 11:29:15 -0800256 const char* prodIdFilename = "/var/cache/private/prodID";
Vernon Maueryce14e4d2019-06-25 11:38:20 -0700257 constexpr auto ipmiDevIdStateShift = 7;
258 constexpr auto ipmiDevIdFw1Mask = ~(1 << ipmiDevIdStateShift);
Johnathan Mantey3c8af652019-12-19 11:29:15 -0800259 constexpr auto ipmiDevIdBusy = (1 << ipmiDevIdStateShift);
Vernon Maueryce14e4d2019-06-25 11:38:20 -0700260
AppaRao Pulicb6386b2020-01-16 14:53:30 +0530261 if (!fwVerInitialized)
Vernon Maueryce14e4d2019-06-25 11:38:20 -0700262 {
Jia, chunhui82d178c2019-06-27 16:48:14 +0800263 std::optional<MetaRevision> rev;
Vernon Maueryce14e4d2019-06-25 11:38:20 -0700264 try
265 {
266 auto version = getActiveSoftwareVersionInfo();
267 rev = convertIntelVersion(version);
268 }
269 catch (const std::exception& e)
270 {
Jia, chunhui82d178c2019-06-27 16:48:14 +0800271 Log::log<Log::level::ERR>("Failed to get active version info",
272 Log::entry("ERROR=%s", e.what()));
Vernon Maueryce14e4d2019-06-25 11:38:20 -0700273 }
274
275 if (rev.has_value())
276 {
277 // bit7 identifies if the device is available
278 // 0=normal operation
279 // 1=device firmware, SDR update,
280 // or self-initialization in progress.
281 // The availability may change in run time, so mask here
282 // and initialize later.
283 MetaRevision revision = rev.value();
284 devId.fw[0] = revision.major & ipmiDevIdFw1Mask;
285
286 revision.minor = (revision.minor > 99 ? 99 : revision.minor);
287 devId.fw[1] = revision.minor % 10 + (revision.minor / 10) * 16;
Jia, chunhui82d178c2019-06-27 16:48:14 +0800288 try
289 {
290 uint32_t hash = std::stoul(revision.metaHash, 0, 16);
291 hash = ((hash & 0xff000000) >> 24) |
292 ((hash & 0x00FF0000) >> 8) | ((hash & 0x0000FF00) << 8) |
293 ((hash & 0xFF) << 24);
294 devId.aux = (revision.buildNo & 0xFF) + (hash & 0xFFFFFF00);
AppaRao Pulicb6386b2020-01-16 14:53:30 +0530295 fwVerInitialized = true;
Jia, chunhui82d178c2019-06-27 16:48:14 +0800296 }
297 catch (const std::exception& e)
298 {
299 Log::log<Log::level::ERR>("Failed to convert git hash",
300 Log::entry("ERROR=%s", e.what()));
301 }
Vernon Maueryce14e4d2019-06-25 11:38:20 -0700302 }
AppaRao Pulicb6386b2020-01-16 14:53:30 +0530303 }
Vernon Maueryce14e4d2019-06-25 11:38:20 -0700304
AppaRao Pulicb6386b2020-01-16 14:53:30 +0530305 if (!devIdInitialized)
306 {
Vernon Maueryce14e4d2019-06-25 11:38:20 -0700307 std::ifstream devIdFile(filename);
308 if (devIdFile.is_open())
309 {
310 auto data = nlohmann::json::parse(devIdFile, nullptr, false);
311 if (!data.is_discarded())
312 {
313 devId.id = data.value("id", 0);
314 devId.revision = data.value("revision", 0);
315 devId.addnDevSupport = data.value("addn_dev_support", 0);
316 devId.manufId = data.value("manuf_id", 0);
Vernon Maueryce14e4d2019-06-25 11:38:20 -0700317 }
318 else
319 {
320 Log::log<Log::level::ERR>("Device ID JSON parser failure");
321 return ipmi::responseUnspecifiedError();
322 }
323 }
324 else
325 {
326 Log::log<Log::level::ERR>("Device ID file not found");
327 return ipmi::responseUnspecifiedError();
328 }
Johnathan Mantey3c8af652019-12-19 11:29:15 -0800329
330 // Determine the Product ID. Using the DBus system is painfully slow at
331 // boot time. Avoid using DBus to get the Product ID. The Product ID is
332 // stored in a non-volatile file now. The /usr/bin/checkFru.sh script,
333 // run during bootup, will populate the productIdFile.
334 std::fstream prodIdFile(prodIdFilename);
335 if (prodIdFile.is_open())
336 {
337 std::string id = "0x00";
338 char* end;
339 prodIdFile.getline(&id[0], id.size() + 1);
340 devId.prodId = std::strtol(&id[0], &end, 0);
AppaRao Pulicb6386b2020-01-16 14:53:30 +0530341 devIdInitialized = true;
Johnathan Mantey3c8af652019-12-19 11:29:15 -0800342 }
343 else
344 {
345 // For any exception send out platform id as 0,
346 // and make sure to re-query the device id.
AppaRao Pulicb6386b2020-01-16 14:53:30 +0530347 devIdInitialized = false;
Johnathan Mantey3c8af652019-12-19 11:29:15 -0800348 devId.prodId = 0;
349 }
Vernon Maueryce14e4d2019-06-25 11:38:20 -0700350 }
351
352 // Set availability to the actual current BMC state
353 devId.fw[0] &= ipmiDevIdFw1Mask;
354 if (!getCurrentBmcStateWithFallback(defaultActivationSetting))
355 {
Johnathan Mantey3c8af652019-12-19 11:29:15 -0800356 devId.fw[0] |= ipmiDevIdBusy;
Vernon Maueryce14e4d2019-06-25 11:38:20 -0700357 }
358
359 return ipmi::responseSuccess(
360 devId.id, devId.revision, devId.fw[0], devId.fw[1], devId.ipmiVer,
361 devId.addnDevSupport, devId.manufId, devId.prodId, devId.aux);
362}
363
364static void registerAPPFunctions(void)
365{
366 Log::log<Log::level::INFO>("Registering App commands");
367 // <Get Device ID>
Vernon Mauery98bbf692019-09-16 11:14:59 -0700368 registerHandler(prioOemBase, netFnApp, app::cmdGetDeviceId, Privilege::User,
369 ipmiAppGetDeviceId);
Vernon Maueryce14e4d2019-06-25 11:38:20 -0700370}
371
372} // namespace ipmi