blob: 0de30af98c591004c3b86e370600723968a0e1be [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}
81/**
82 * @brief Returns the Version info from primary s/w object
83 *
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 *
89 * @return On success returns the Version info from primary s/w object.
90 *
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 {
157 Log::log<Log::level::ERR>("Could not found an BMC software Object");
158 }
159
160 return revision;
161}
162
163typedef struct
164{
165 std::string platform;
166 uint8_t major;
167 uint8_t minor;
168 uint32_t buildNo;
169 std::string openbmcHash;
170 std::string metaHash;
171 std::string buildType;
172} 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
178// openbmcHash g14dc00e79
179// MetaHasg 5e7d997
180//
181// 2.New solution whtref-0.1-45-023125-a1295e-release
182// IdStr whtref
183// Major 0
184// Minor 1
185// buildNo 45
186// openbmcHash 023125
187// MetaHash a1295e
188// BuildType release/CI/<devloperId>
189std::optional<MetaRevision> convertIntelVersion(std::string& s)
190{
191 std::smatch results;
192 MetaRevision rev;
193 std::regex pattern1("(\\d+?).(\\d+?).\\d+?-\\w*?-(\\d+?)-(\\w+?)-(\\w+?)");
194 constexpr size_t matchedPhosphor = 6;
195 if (std::regex_match(s, results, pattern1))
196 {
197 if (results.size() == matchedPhosphor)
198 {
199 rev.platform = "whtref";
200 rev.major = static_cast<uint8_t>(std::stoi(results[1]));
201 rev.minor = static_cast<uint8_t>(std::stoi(results[2]));
202 rev.buildNo = static_cast<uint32_t>(std::stoi(results[3]));
203 rev.openbmcHash = results[4];
204 rev.metaHash = results[5];
205 rev.buildType = "release";
206 std::string versionString =
207 rev.platform + ":" + std::to_string(rev.major) + ":" +
208 std::to_string(rev.minor) + ":" + std::to_string(rev.buildNo) +
209 ":" + rev.openbmcHash + ":" + rev.metaHash + ":" +
210 rev.buildType;
211 phosphor::logging::log<phosphor::logging::level::INFO>(
212 versionString.c_str());
213 return rev;
214 }
215 }
216 constexpr size_t matchedIntel = 8;
217 std::regex pattern2(
218 "(\\w+?)-(\\d+?).(\\d+?)-(\\d+?)-(\\w+?)-(\\w+?)-(\\w+?)");
219 if (std::regex_match(s, results, pattern2))
220 {
221 if (results.size() == matchedIntel)
222 {
223 rev.platform = results[1];
224 rev.major = static_cast<uint8_t>(std::stoi(results[2]));
225 rev.minor = static_cast<uint8_t>(std::stoi(results[3]));
226 rev.buildNo = static_cast<uint32_t>(std::stoi(results[4]));
227 rev.openbmcHash = results[5];
228 rev.metaHash = results[6];
229 rev.buildType = results[7];
230 std::string versionString =
231 rev.platform + ":" + std::to_string(rev.major) + ":" +
232 std::to_string(rev.minor) + ":" + std::to_string(rev.buildNo) +
233 ":" + rev.openbmcHash + ":" + rev.metaHash + ":" +
234 rev.buildType;
235 phosphor::logging::log<phosphor::logging::level::INFO>(
236 versionString.c_str());
237 return rev;
238 }
239 }
240
241 return std::nullopt;
242}
243} // namespace
244auto ipmiAppGetDeviceId() -> ipmi::RspType<uint8_t, // Device ID
245 uint8_t, // Device Revision
246 uint8_t, // Firmware Revision Major
247 uint8_t, // Firmware Revision minor
248 uint8_t, // IPMI version
249 uint8_t, // Additional device support
250 uint24_t, // MFG ID
251 uint16_t, // Product ID
252 uint32_t // AUX info
253 >
254{
255 std::optional<MetaRevision> rev;
256 static struct
257 {
258 uint8_t id;
259 uint8_t revision;
260 uint8_t fw[2];
261 uint8_t ipmiVer;
262 uint8_t addnDevSupport;
263 uint24_t manufId;
264 uint16_t prodId;
265 uint32_t aux;
266 } devId;
267 static bool dev_id_initialized = false;
268 static bool defaultActivationSetting = true;
269 const char* filename = "/usr/share/ipmi-providers/dev_id.json";
270 constexpr auto ipmiDevIdStateShift = 7;
271 constexpr auto ipmiDevIdFw1Mask = ~(1 << ipmiDevIdStateShift);
272
273 if (!dev_id_initialized)
274 {
275 try
276 {
277 auto version = getActiveSoftwareVersionInfo();
278 rev = convertIntelVersion(version);
279 }
280 catch (const std::exception& e)
281 {
282 Log::log<Log::level::ERR>(e.what());
283 }
284
285 if (rev.has_value())
286 {
287 // bit7 identifies if the device is available
288 // 0=normal operation
289 // 1=device firmware, SDR update,
290 // or self-initialization in progress.
291 // The availability may change in run time, so mask here
292 // and initialize later.
293 MetaRevision revision = rev.value();
294 devId.fw[0] = revision.major & ipmiDevIdFw1Mask;
295
296 revision.minor = (revision.minor > 99 ? 99 : revision.minor);
297 devId.fw[1] = revision.minor % 10 + (revision.minor / 10) * 16;
298 devId.aux = revision.buildNo;
299 }
300
301 // IPMI Spec version 2.0
302 devId.ipmiVer = 2;
303
304 std::ifstream devIdFile(filename);
305 if (devIdFile.is_open())
306 {
307 auto data = nlohmann::json::parse(devIdFile, nullptr, false);
308 if (!data.is_discarded())
309 {
310 devId.id = data.value("id", 0);
311 devId.revision = data.value("revision", 0);
312 devId.addnDevSupport = data.value("addn_dev_support", 0);
313 devId.manufId = data.value("manuf_id", 0);
314
315 try
316 {
317 auto busp = getSdBus();
318 const ipmi::DbusObjectInfo& object = ipmi::getDbusObject(
319 *busp, "xyz.openbmc_project.Inventory.Item.Board",
320 "/xyz/openbmc_project/inventory/system/board/",
321 "Baseboard");
322 const ipmi::Value& propValue = ipmi::getDbusProperty(
323 *busp, object.second, object.first,
324 "xyz.openbmc_project.Inventory.Item.Board",
325 "ProductId");
326 devId.prodId =
327 static_cast<uint8_t>(std::get<uint64_t>(propValue));
328 }
329 catch (std::exception& e)
330 {
331 devId.prodId = data.value("prod_id", 0);
332 }
333
334 // Set the availablitity of the BMC.
335 defaultActivationSetting = data.value("availability", true);
336
337 // Don't read the file every time if successful
338 dev_id_initialized = true;
339 }
340 else
341 {
342 Log::log<Log::level::ERR>("Device ID JSON parser failure");
343 return ipmi::responseUnspecifiedError();
344 }
345 }
346 else
347 {
348 Log::log<Log::level::ERR>("Device ID file not found");
349 return ipmi::responseUnspecifiedError();
350 }
351 }
352
353 // Set availability to the actual current BMC state
354 devId.fw[0] &= ipmiDevIdFw1Mask;
355 if (!getCurrentBmcStateWithFallback(defaultActivationSetting))
356 {
357 devId.fw[0] |= (1 << ipmiDevIdStateShift);
358 }
359
360 return ipmi::responseSuccess(
361 devId.id, devId.revision, devId.fw[0], devId.fw[1], devId.ipmiVer,
362 devId.addnDevSupport, devId.manufId, devId.prodId, devId.aux);
363}
364
365static void registerAPPFunctions(void)
366{
367 Log::log<Log::level::INFO>("Registering App commands");
368 // <Get Device ID>
369 ipmi::registerHandler(ipmi::prioOemBase, ipmi::netFnApp,
370 ipmi::app::cmdGetDeviceId, ipmi::Privilege::User,
371 ipmiAppGetDeviceId);
372 return;
373}
374
375} // namespace ipmi