blob: ebf95bed57545511fc8623325abd302d04f3b465 [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
18#include <fstream>
19#include <ipmid/api.hpp>
20#include <ipmid/utils.hpp>
21#include <nlohmann/json.hpp>
22#include <phosphor-logging/log.hpp>
23#include <regex>
24#include <xyz/openbmc_project/Software/Activation/server.hpp>
25#include <xyz/openbmc_project/Software/Version/server.hpp>
26#include <xyz/openbmc_project/State/BMC/server.hpp>
27
28namespace ipmi
29{
30
31static void registerAPPFunctions() __attribute__((constructor));
32
33namespace Log = phosphor::logging;
34namespace Error = sdbusplus::xyz::openbmc_project::Common::Error;
35using Version = sdbusplus::xyz::openbmc_project::Software::server::Version;
36using Activation =
37 sdbusplus::xyz::openbmc_project::Software::server::Activation;
38using BMC = sdbusplus::xyz::openbmc_project::State::server::BMC;
39
40constexpr auto bmc_state_interface = "xyz.openbmc_project.State.BMC";
41constexpr auto bmc_state_property = "CurrentBMCState";
42
43namespace
44{
45static constexpr auto redundancyIntf =
46 "xyz.openbmc_project.Software.RedundancyPriority";
47static constexpr auto versionIntf = "xyz.openbmc_project.Software.Version";
48static constexpr auto activationIntf =
49 "xyz.openbmc_project.Software.Activation";
50static constexpr auto softwareRoot = "/xyz/openbmc_project/software";
51
52bool getCurrentBmcState()
53{
54 sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()};
55
56 // Get the Inventory object implementing the BMC interface
57 ipmi::DbusObjectInfo bmcObject =
58 ipmi::getDbusObject(bus, bmc_state_interface);
59 auto variant =
60 ipmi::getDbusProperty(bus, bmcObject.second, bmcObject.first,
61 bmc_state_interface, bmc_state_property);
62
63 return std::holds_alternative<std::string>(variant) &&
64 BMC::convertBMCStateFromString(std::get<std::string>(variant)) ==
65 BMC::BMCState::Ready;
66}
67
68bool getCurrentBmcStateWithFallback(const bool fallbackAvailability)
69{
70 try
71 {
72 return getCurrentBmcState();
73 }
74 catch (...)
75 {
76 // Nothing provided the BMC interface, therefore return whatever was
77 // configured as the default.
78 return fallbackAvailability;
79 }
80}
Jia, chunhui82d178c2019-06-27 16:48:14 +080081
Vernon Maueryce14e4d2019-06-25 11:38:20 -070082/**
Jia, chunhui82d178c2019-06-27 16:48:14 +080083 * @brief Returns the Version info from primary software object
Vernon Maueryce14e4d2019-06-25 11:38:20 -070084 *
85 * Get the Version info from the active s/w object which is having high
86 * "Priority" value(a smaller number is a higher priority) and "Purpose"
87 * is "BMC" from the list of all s/w objects those are implementing
88 * RedundancyPriority interface from the given softwareRoot path.
89 *
Jia, chunhui82d178c2019-06-27 16:48:14 +080090 * @return On success returns the Version info from primary software object.
Vernon Maueryce14e4d2019-06-25 11:38:20 -070091 *
92 */
93std::string getActiveSoftwareVersionInfo()
94{
95 auto busp = getSdBus();
96
97 std::string revision{};
98 ipmi::ObjectTree objectTree;
99 try
100 {
101 objectTree =
102 ipmi::getAllDbusObjects(*busp, softwareRoot, redundancyIntf);
103 }
104 catch (sdbusplus::exception::SdBusError& e)
105 {
106 Log::log<Log::level::ERR>("Failed to fetch redundancy object from dbus",
107 Log::entry("INTERFACE=%s", redundancyIntf),
108 Log::entry("ERRMSG=%s", e.what()));
109 }
110
111 auto objectFound = false;
112 for (auto& softObject : objectTree)
113 {
114 auto service =
115 ipmi::getService(*busp, redundancyIntf, softObject.first);
116 auto objValueTree =
117 ipmi::getManagedObjects(*busp, service, softwareRoot);
118
119 auto minPriority = 0xFF;
120 for (const auto& objIter : objValueTree)
121 {
122 try
123 {
124 auto& intfMap = objIter.second;
125 auto& redundancyPriorityProps = intfMap.at(redundancyIntf);
126 auto& versionProps = intfMap.at(versionIntf);
127 auto& activationProps = intfMap.at(activationIntf);
128 auto priority =
129 std::get<uint8_t>(redundancyPriorityProps.at("Priority"));
130 auto purpose =
131 std::get<std::string>(versionProps.at("Purpose"));
132 auto activation =
133 std::get<std::string>(activationProps.at("Activation"));
134 auto version =
135 std::get<std::string>(versionProps.at("Version"));
136 if ((Version::convertVersionPurposeFromString(purpose) ==
137 Version::VersionPurpose::BMC) &&
138 (Activation::convertActivationsFromString(activation) ==
139 Activation::Activations::Active))
140 {
141 if (priority < minPriority)
142 {
143 minPriority = priority;
144 objectFound = true;
145 revision = std::move(version);
146 }
147 }
148 }
149 catch (const std::exception& e)
150 {
151 Log::log<Log::level::ERR>(e.what());
152 }
153 }
154 }
155
156 if (!objectFound)
157 {
Jia, chunhui82d178c2019-06-27 16:48:14 +0800158 Log::log<Log::level::ERR>("Could not find an BMC software object");
Vernon Maueryce14e4d2019-06-25 11:38:20 -0700159 }
160
161 return revision;
162}
163
164typedef struct
165{
166 std::string platform;
167 uint8_t major;
168 uint8_t minor;
169 uint32_t buildNo;
170 std::string openbmcHash;
171 std::string metaHash;
Vernon Maueryce14e4d2019-06-25 11:38:20 -0700172} MetaRevision;
173
174// Support both 2 solutions:
175// 1.Current solution 2.7.0-dev-533-g14dc00e79-5e7d997
176// openbmcTag 2.7.0-dev
177// BuildNo 533
Jia, chunhui82d178c2019-06-27 16:48:14 +0800178// openbmcHash 14dc00e79
Vernon Maueryce14e4d2019-06-25 11:38:20 -0700179// MetaHasg 5e7d997
180//
Jia, chunhui82d178c2019-06-27 16:48:14 +0800181// 2.New solution wht-0.2-3-gab3500-38384ac
182// IdStr wht
Vernon Maueryce14e4d2019-06-25 11:38:20 -0700183// Major 0
Jia, chunhui82d178c2019-06-27 16:48:14 +0800184// Minor 2
185// buildNo 3
186// MetaHash ab3500
187// openbmcHash 38384ac
Vernon Maueryce14e4d2019-06-25 11:38:20 -0700188std::optional<MetaRevision> convertIntelVersion(std::string& s)
189{
190 std::smatch results;
191 MetaRevision rev;
Jia, chunhui82d178c2019-06-27 16:48:14 +0800192 std::regex pattern1("(\\d+?).(\\d+?).\\d+?-\\w*?-(\\d+?)-g(\\w+?)-(\\w+?)");
Vernon Maueryce14e4d2019-06-25 11:38:20 -0700193 constexpr size_t matchedPhosphor = 6;
194 if (std::regex_match(s, results, pattern1))
195 {
196 if (results.size() == matchedPhosphor)
197 {
198 rev.platform = "whtref";
199 rev.major = static_cast<uint8_t>(std::stoi(results[1]));
200 rev.minor = static_cast<uint8_t>(std::stoi(results[2]));
201 rev.buildNo = static_cast<uint32_t>(std::stoi(results[3]));
202 rev.openbmcHash = results[4];
203 rev.metaHash = results[5];
Vernon Maueryce14e4d2019-06-25 11:38:20 -0700204 std::string versionString =
205 rev.platform + ":" + std::to_string(rev.major) + ":" +
206 std::to_string(rev.minor) + ":" + std::to_string(rev.buildNo) +
Jia, chunhui82d178c2019-06-27 16:48:14 +0800207 ":" + rev.openbmcHash + ":" + rev.metaHash;
208 Log::log<Log::level::INFO>(
209 "Get BMC version",
210 Log::entry("VERSION=%s", versionString.c_str()));
Vernon Maueryce14e4d2019-06-25 11:38:20 -0700211 return rev;
212 }
213 }
Jia, chunhui82d178c2019-06-27 16:48:14 +0800214 constexpr size_t matchedIntel = 7;
215 std::regex pattern2("(\\w+?)-(\\d+?).(\\d+?)-(\\d+?)-g(\\w+?)-(\\w+?)");
Vernon Maueryce14e4d2019-06-25 11:38:20 -0700216 if (std::regex_match(s, results, pattern2))
217 {
218 if (results.size() == matchedIntel)
219 {
220 rev.platform = results[1];
221 rev.major = static_cast<uint8_t>(std::stoi(results[2]));
222 rev.minor = static_cast<uint8_t>(std::stoi(results[3]));
223 rev.buildNo = static_cast<uint32_t>(std::stoi(results[4]));
Jia, chunhui82d178c2019-06-27 16:48:14 +0800224 rev.openbmcHash = results[6];
225 rev.metaHash = results[5];
Vernon Maueryce14e4d2019-06-25 11:38:20 -0700226 std::string versionString =
227 rev.platform + ":" + std::to_string(rev.major) + ":" +
228 std::to_string(rev.minor) + ":" + std::to_string(rev.buildNo) +
Jia, chunhui82d178c2019-06-27 16:48:14 +0800229 ":" + rev.openbmcHash + ":" + rev.metaHash;
230 Log::log<Log::level::INFO>(
231 "Get BMC version",
232 Log::entry("VERSION=%s", versionString.c_str()));
Vernon Maueryce14e4d2019-06-25 11:38:20 -0700233 return rev;
234 }
235 }
236
237 return std::nullopt;
238}
239} // namespace
Vernon Mauery98bbf692019-09-16 11:14:59 -0700240RspType<uint8_t, // Device ID
241 uint8_t, // Device Revision
242 uint8_t, // Firmware Revision Major
243 uint8_t, // Firmware Revision minor
244 uint8_t, // IPMI version
245 uint8_t, // Additional device support
246 uint24_t, // MFG ID
247 uint16_t, // Product ID
248 uint32_t // AUX info
249 >
250 ipmiAppGetDeviceId()
Vernon Maueryce14e4d2019-06-25 11:38:20 -0700251{
Vernon Maueryce14e4d2019-06-25 11:38:20 -0700252 static struct
253 {
254 uint8_t id;
255 uint8_t revision;
256 uint8_t fw[2];
257 uint8_t ipmiVer;
258 uint8_t addnDevSupport;
259 uint24_t manufId;
260 uint16_t prodId;
261 uint32_t aux;
262 } devId;
263 static bool dev_id_initialized = false;
264 static bool defaultActivationSetting = true;
265 const char* filename = "/usr/share/ipmi-providers/dev_id.json";
266 constexpr auto ipmiDevIdStateShift = 7;
267 constexpr auto ipmiDevIdFw1Mask = ~(1 << ipmiDevIdStateShift);
268
269 if (!dev_id_initialized)
270 {
Jia, chunhui82d178c2019-06-27 16:48:14 +0800271 std::optional<MetaRevision> rev;
Vernon Maueryce14e4d2019-06-25 11:38:20 -0700272 try
273 {
274 auto version = getActiveSoftwareVersionInfo();
275 rev = convertIntelVersion(version);
276 }
277 catch (const std::exception& e)
278 {
Jia, chunhui82d178c2019-06-27 16:48:14 +0800279 Log::log<Log::level::ERR>("Failed to get active version info",
280 Log::entry("ERROR=%s", e.what()));
Vernon Maueryce14e4d2019-06-25 11:38:20 -0700281 }
282
283 if (rev.has_value())
284 {
285 // bit7 identifies if the device is available
286 // 0=normal operation
287 // 1=device firmware, SDR update,
288 // or self-initialization in progress.
289 // The availability may change in run time, so mask here
290 // and initialize later.
291 MetaRevision revision = rev.value();
292 devId.fw[0] = revision.major & ipmiDevIdFw1Mask;
293
294 revision.minor = (revision.minor > 99 ? 99 : revision.minor);
295 devId.fw[1] = revision.minor % 10 + (revision.minor / 10) * 16;
Jia, chunhui82d178c2019-06-27 16:48:14 +0800296 try
297 {
298 uint32_t hash = std::stoul(revision.metaHash, 0, 16);
299 hash = ((hash & 0xff000000) >> 24) |
300 ((hash & 0x00FF0000) >> 8) | ((hash & 0x0000FF00) << 8) |
301 ((hash & 0xFF) << 24);
302 devId.aux = (revision.buildNo & 0xFF) + (hash & 0xFFFFFF00);
303 }
304 catch (const std::exception& e)
305 {
306 Log::log<Log::level::ERR>("Failed to convert git hash",
307 Log::entry("ERROR=%s", e.what()));
308 }
Vernon Maueryce14e4d2019-06-25 11:38:20 -0700309 }
310
311 // IPMI Spec version 2.0
312 devId.ipmiVer = 2;
313
314 std::ifstream devIdFile(filename);
315 if (devIdFile.is_open())
316 {
317 auto data = nlohmann::json::parse(devIdFile, nullptr, false);
318 if (!data.is_discarded())
319 {
320 devId.id = data.value("id", 0);
321 devId.revision = data.value("revision", 0);
322 devId.addnDevSupport = data.value("addn_dev_support", 0);
323 devId.manufId = data.value("manuf_id", 0);
324
325 try
326 {
327 auto busp = getSdBus();
328 const ipmi::DbusObjectInfo& object = ipmi::getDbusObject(
329 *busp, "xyz.openbmc_project.Inventory.Item.Board",
330 "/xyz/openbmc_project/inventory/system/board/",
331 "Baseboard");
332 const ipmi::Value& propValue = ipmi::getDbusProperty(
333 *busp, object.second, object.first,
334 "xyz.openbmc_project.Inventory.Item.Board",
335 "ProductId");
336 devId.prodId =
337 static_cast<uint8_t>(std::get<uint64_t>(propValue));
338 }
339 catch (std::exception& e)
340 {
341 devId.prodId = data.value("prod_id", 0);
342 }
343
344 // Set the availablitity of the BMC.
345 defaultActivationSetting = data.value("availability", true);
346
347 // Don't read the file every time if successful
348 dev_id_initialized = true;
349 }
350 else
351 {
352 Log::log<Log::level::ERR>("Device ID JSON parser failure");
353 return ipmi::responseUnspecifiedError();
354 }
355 }
356 else
357 {
358 Log::log<Log::level::ERR>("Device ID file not found");
359 return ipmi::responseUnspecifiedError();
360 }
361 }
362
363 // Set availability to the actual current BMC state
364 devId.fw[0] &= ipmiDevIdFw1Mask;
365 if (!getCurrentBmcStateWithFallback(defaultActivationSetting))
366 {
367 devId.fw[0] |= (1 << ipmiDevIdStateShift);
368 }
369
370 return ipmi::responseSuccess(
371 devId.id, devId.revision, devId.fw[0], devId.fw[1], devId.ipmiVer,
372 devId.addnDevSupport, devId.manufId, devId.prodId, devId.aux);
373}
374
375static void registerAPPFunctions(void)
376{
377 Log::log<Log::level::INFO>("Registering App commands");
378 // <Get Device ID>
Vernon Mauery98bbf692019-09-16 11:14:59 -0700379 registerHandler(prioOemBase, netFnApp, app::cmdGetDeviceId, Privilege::User,
380 ipmiAppGetDeviceId);
Vernon Maueryce14e4d2019-06-25 11:38:20 -0700381}
382
383} // namespace ipmi