blob: 7f16b1fc6deb8551ab361867447252038cb3d517 [file] [log] [blame]
vishwabmcba0bd5f2015-09-30 16:50:23 +05301#include "apphandler.h"
Patrick Venture0b02be92018-08-31 11:55:55 -07002
Xo Wangf542e8b2017-08-09 15:34:16 -07003#include "app/channel.hpp"
4#include "app/watchdog.hpp"
5#include "ipmid.hpp"
6#include "nlohmann/json.hpp"
7#include "sys_info_param.hpp"
8#include "transporthandler.hpp"
9#include "types.hpp"
10#include "utils.hpp"
11
Patrick Venture0b02be92018-08-31 11:55:55 -070012#include <arpa/inet.h>
13#include <mapper.h>
14#include <stdint.h>
15#include <stdio.h>
16#include <systemd/sd-bus.h>
17
Patrick Williams37af7332016-09-02 21:21:42 -050018#include "host-ipmid/ipmid-api.h"
Ratan Guptab8e99552017-07-27 07:07:48 +053019
Vernon Mauery185b9f82018-07-20 10:52:36 -070020#if __has_include(<filesystem>)
21#include <filesystem>
22#elif __has_include(<experimental/filesystem>)
23#include <experimental/filesystem>
Patrick Venture0b02be92018-08-31 11:55:55 -070024namespace std
25{
26// splice experimental::filesystem into std
27namespace filesystem = std::experimental::filesystem;
28} // namespace std
Vernon Mauery185b9f82018-07-20 10:52:36 -070029#else
Patrick Venture0b02be92018-08-31 11:55:55 -070030#error filesystem not available
Vernon Mauery185b9f82018-07-20 10:52:36 -070031#endif
Ratan Gupta62736ec2017-09-02 12:02:47 +053032
Xo Wangf542e8b2017-08-09 15:34:16 -070033#include <algorithm>
Patrick Venture0b02be92018-08-31 11:55:55 -070034#include <array>
35#include <cstddef>
36#include <fstream>
Xo Wangf542e8b2017-08-09 15:34:16 -070037#include <memory>
Ratan Guptab8e99552017-07-27 07:07:48 +053038#include <phosphor-logging/elog-errors.hpp>
Patrick Venture0b02be92018-08-31 11:55:55 -070039#include <phosphor-logging/log.hpp>
40#include <string>
Xo Wangf542e8b2017-08-09 15:34:16 -070041#include <tuple>
Patrick Venture0b02be92018-08-31 11:55:55 -070042#include <vector>
43#include <xyz/openbmc_project/Common/error.hpp>
44#include <xyz/openbmc_project/Software/Activation/server.hpp>
45#include <xyz/openbmc_project/Software/Version/server.hpp>
Alexander Amelkinba19c182018-09-04 15:49:36 +030046#include <xyz/openbmc_project/State/BMC/server.hpp>
Ratan Guptab8e99552017-07-27 07:07:48 +053047
Patrick Venture0b02be92018-08-31 11:55:55 -070048extern sd_bus* bus;
vishwabmcba0bd5f2015-09-30 16:50:23 +053049
Alexander Amelkinba19c182018-09-04 15:49:36 +030050constexpr auto bmc_state_interface = "xyz.openbmc_project.State.BMC";
51constexpr auto bmc_state_property = "CurrentBMCState";
Marri Devender Rao5e007a52018-01-08 06:18:36 -060052constexpr auto bmc_interface = "xyz.openbmc_project.Inventory.Item.Bmc";
53constexpr auto bmc_guid_interface = "xyz.openbmc_project.Common.UUID";
54constexpr auto bmc_guid_property = "UUID";
55constexpr auto bmc_guid_len = 16;
56
Nagaraju Goruganti744398d2018-02-20 09:52:00 -060057static constexpr auto redundancyIntf =
58 "xyz.openbmc_project.Software.RedundancyPriority";
Patrick Venture0b02be92018-08-31 11:55:55 -070059static constexpr auto versionIntf = "xyz.openbmc_project.Software.Version";
Nagaraju Goruganti744398d2018-02-20 09:52:00 -060060static constexpr auto activationIntf =
61 "xyz.openbmc_project.Software.Activation";
62static constexpr auto softwareRoot = "/xyz/openbmc_project/software";
63
Chris Austen6caf28b2015-10-13 12:40:40 -050064void register_netfn_app_functions() __attribute__((constructor));
vishwabmcba0bd5f2015-09-30 16:50:23 +053065
Ratan Guptab8e99552017-07-27 07:07:48 +053066using namespace phosphor::logging;
67using namespace sdbusplus::xyz::openbmc_project::Common::Error;
Nagaraju Goruganti744398d2018-02-20 09:52:00 -060068using Version = sdbusplus::xyz::openbmc_project::Software::server::Version;
69using Activation =
70 sdbusplus::xyz::openbmc_project::Software::server::Activation;
Alexander Amelkinba19c182018-09-04 15:49:36 +030071using BMC = sdbusplus::xyz::openbmc_project::State::server::BMC;
Vernon Mauery185b9f82018-07-20 10:52:36 -070072namespace fs = std::filesystem;
Ratan Guptab8e99552017-07-27 07:07:48 +053073
Nan Liee0cb902016-07-11 15:38:03 +080074// Offset in get device id command.
75typedef struct
76{
Patrick Venture0b02be92018-08-31 11:55:55 -070077 uint8_t id;
78 uint8_t revision;
79 uint8_t fw[2];
80 uint8_t ipmi_ver;
81 uint8_t addn_dev_support;
82 uint8_t manuf_id[3];
83 uint8_t prod_id[2];
84 uint8_t aux[4];
85} __attribute__((packed)) ipmi_device_id_t;
Chris Austen7303bdc2016-04-17 11:50:54 -050086
Nagaraju Goruganti744398d2018-02-20 09:52:00 -060087/**
88 * @brief Returns the Version info from primary s/w object
89 *
90 * Get the Version info from the active s/w object which is having high
91 * "Priority" value(a smaller number is a higher priority) and "Purpose"
92 * is "BMC" from the list of all s/w objects those are implementing
93 * RedundancyPriority interface from the given softwareRoot path.
94 *
95 * @return On success returns the Version info from primary s/w object.
96 *
97 */
98std::string getActiveSoftwareVersionInfo()
99{
100 sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()};
101
102 std::string revision{};
Patrick Venture0b02be92018-08-31 11:55:55 -0700103 auto objectTree =
104 ipmi::getAllDbusObjects(bus, softwareRoot, redundancyIntf, "");
Nagaraju Goruganti744398d2018-02-20 09:52:00 -0600105 if (objectTree.empty())
106 {
107 log<level::ERR>("No Obj has implemented the s/w redundancy interface",
108 entry("INTERFACE=%s", redundancyIntf));
109 elog<InternalFailure>();
110 }
111
112 auto objectFound = false;
113 for (auto& softObject : objectTree)
114 {
115 auto service = ipmi::getService(bus, redundancyIntf, softObject.first);
116 auto objValueTree = ipmi::getManagedObjects(bus, 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 redundancyPriorityProps.at("Priority").get<uint8_t>();
129 auto purpose = versionProps.at("Purpose").get<std::string>();
130 auto activation =
131 activationProps.at("Activation").get<std::string>();
132 auto version = versionProps.at("Version").get<std::string>();
133 if ((Version::convertVersionPurposeFromString(purpose) ==
134 Version::VersionPurpose::BMC) &&
135 (Activation::convertActivationsFromString(activation) ==
136 Activation::Activations::Active))
137 {
138 if (priority < minPriority)
139 {
140 minPriority = priority;
141 objectFound = true;
142 revision = std::move(version);
143 }
144 }
145 }
146 catch (const std::exception& e)
147 {
148 log<level::ERR>(e.what());
149 }
150 }
151 }
152
153 if (!objectFound)
154 {
155 log<level::ERR>("Could not found an BMC software Object");
156 elog<InternalFailure>();
157 }
158
159 return revision;
160}
161
Alexander Amelkinba19c182018-09-04 15:49:36 +0300162bool getCurrentBmcState()
163{
164 sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()};
165
166 // Get the Inventory object implementing the BMC interface
167 ipmi::DbusObjectInfo bmcObject =
168 ipmi::getDbusObject(bus, bmc_state_interface);
169 auto variant =
170 ipmi::getDbusProperty(bus, bmcObject.second, bmcObject.first,
171 bmc_state_interface, bmc_state_property);
172
173 return variant.is<std::string>() &&
174 BMC::convertBMCStateFromString(variant.get<std::string>()) ==
175 BMC::BMCState::Ready;
176}
177
Adriana Kobylak3a552e12015-10-19 16:11:00 -0500178ipmi_ret_t ipmi_app_set_acpi_power_state(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
Patrick Venture0b02be92018-08-31 11:55:55 -0700179 ipmi_request_t request,
180 ipmi_response_t response,
181 ipmi_data_len_t data_len,
182 ipmi_context_t context)
Chris Austen6caf28b2015-10-13 12:40:40 -0500183{
184 ipmi_ret_t rc = IPMI_CC_OK;
185 *data_len = 0;
186
Aditya Saripalli5fb14602017-11-09 14:46:27 +0530187 log<level::DEBUG>("IPMI SET ACPI STATE Ignoring for now\n");
Chris Austen6caf28b2015-10-13 12:40:40 -0500188 return rc;
189}
190
Chris Austen7303bdc2016-04-17 11:50:54 -0500191typedef struct
192{
193 char major;
194 char minor;
Chris Austen176c9652016-04-30 16:32:17 -0500195 uint16_t d[2];
Chris Austen7303bdc2016-04-17 11:50:54 -0500196} rev_t;
197
Dinesh Chinari2b7e07d2017-11-08 15:38:50 -0600198/* Currently supports the vx.x-x-[-x] and v1.x.x-x-[-x] format. It will */
199/* return -1 if not in those formats, this routine knows how to parse */
Chris Austen7303bdc2016-04-17 11:50:54 -0500200/* version = v0.6-19-gf363f61-dirty */
201/* ^ ^ ^^ ^ */
202/* | | |----------|-- additional details */
203/* | |---------------- Minor */
204/* |------------------ Major */
Dinesh Chinari2b7e07d2017-11-08 15:38:50 -0600205/* and version = v1.99.10-113-g65edf7d-r3-0-g9e4f715 */
206/* ^ ^ ^^ ^ */
207/* | | |--|---------- additional details */
208/* | |---------------- Minor */
209/* |------------------ Major */
Chris Austen7303bdc2016-04-17 11:50:54 -0500210/* Additional details : If the option group exists it will force Auxiliary */
211/* Firmware Revision Information 4th byte to 1 indicating the build was */
212/* derived with additional edits */
Patrick Venture0b02be92018-08-31 11:55:55 -0700213int convert_version(const char* p, rev_t* rev)
Chris Austen7303bdc2016-04-17 11:50:54 -0500214{
Dinesh Chinari2b7e07d2017-11-08 15:38:50 -0600215 std::string s(p);
216 std::string token;
Chris Austen176c9652016-04-30 16:32:17 -0500217 uint16_t commits;
Chris Austen7303bdc2016-04-17 11:50:54 -0500218
Patrick Venture0b02be92018-08-31 11:55:55 -0700219 auto location = s.find_first_of('v');
Dinesh Chinari2b7e07d2017-11-08 15:38:50 -0600220 if (location != std::string::npos)
221 {
Patrick Venture0b02be92018-08-31 11:55:55 -0700222 s = s.substr(location + 1);
Chris Austen176c9652016-04-30 16:32:17 -0500223 }
Chris Austen7303bdc2016-04-17 11:50:54 -0500224
Dinesh Chinari2b7e07d2017-11-08 15:38:50 -0600225 if (!s.empty())
226 {
227 location = s.find_first_of(".");
228 if (location != std::string::npos)
229 {
Patrick Venture0b02be92018-08-31 11:55:55 -0700230 rev->major =
231 static_cast<char>(std::stoi(s.substr(0, location), 0, 16));
232 token = s.substr(location + 1);
Dinesh Chinari2b7e07d2017-11-08 15:38:50 -0600233 }
Chris Austen176c9652016-04-30 16:32:17 -0500234
Dinesh Chinari2b7e07d2017-11-08 15:38:50 -0600235 if (!token.empty())
236 {
237 location = token.find_first_of(".-");
238 if (location != std::string::npos)
239 {
Patrick Ventured2117022018-02-06 08:54:37 -0800240 rev->minor = static_cast<char>(
Patrick Venture0b02be92018-08-31 11:55:55 -0700241 std::stoi(token.substr(0, location), 0, 16));
242 token = token.substr(location + 1);
Dinesh Chinari2b7e07d2017-11-08 15:38:50 -0600243 }
244 }
Chris Austen7303bdc2016-04-17 11:50:54 -0500245
Dinesh Chinari2b7e07d2017-11-08 15:38:50 -0600246 // Capture the number of commits on top of the minor tag.
247 // I'm using BE format like the ipmi spec asked for
248 location = token.find_first_of(".-");
249 if (!token.empty())
250 {
251 commits = std::stoi(token.substr(0, location), 0, 16);
Patrick Venture0b02be92018-08-31 11:55:55 -0700252 rev->d[0] = (commits >> 8) | (commits << 8);
Dinesh Chinari2b7e07d2017-11-08 15:38:50 -0600253
254 // commit number we skip
255 location = token.find_first_of(".-");
256 if (location != std::string::npos)
257 {
Patrick Venture0b02be92018-08-31 11:55:55 -0700258 token = token.substr(location + 1);
Dinesh Chinari2b7e07d2017-11-08 15:38:50 -0600259 }
260 }
Patrick Venture0b02be92018-08-31 11:55:55 -0700261 else
262 {
Dinesh Chinari2b7e07d2017-11-08 15:38:50 -0600263 rev->d[0] = 0;
264 }
265
266 if (location != std::string::npos)
267 {
Patrick Venture0b02be92018-08-31 11:55:55 -0700268 token = token.substr(location + 1);
Dinesh Chinari2b7e07d2017-11-08 15:38:50 -0600269 }
270
271 // Any value of the optional parameter forces it to 1
272 location = token.find_first_of(".-");
273 if (location != std::string::npos)
274 {
Patrick Venture0b02be92018-08-31 11:55:55 -0700275 token = token.substr(location + 1);
Dinesh Chinari2b7e07d2017-11-08 15:38:50 -0600276 }
277 commits = (!token.empty()) ? 1 : 0;
278
Patrick Venture0b02be92018-08-31 11:55:55 -0700279 // We do this operation to get this displayed in least significant bytes
280 // of ipmitool device id command.
281 rev->d[1] = (commits >> 8) | (commits << 8);
Dinesh Chinari2b7e07d2017-11-08 15:38:50 -0600282 }
283
Chris Austen7303bdc2016-04-17 11:50:54 -0500284 return 0;
285}
286
Adriana Kobylak3a552e12015-10-19 16:11:00 -0500287ipmi_ret_t ipmi_app_get_device_id(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
Patrick Venture0b02be92018-08-31 11:55:55 -0700288 ipmi_request_t request,
289 ipmi_response_t response,
290 ipmi_data_len_t data_len,
291 ipmi_context_t context)
Chris Austen6caf28b2015-10-13 12:40:40 -0500292{
293 ipmi_ret_t rc = IPMI_CC_OK;
Nagaraju Goruganti744398d2018-02-20 09:52:00 -0600294 int r = -1;
Chris Austen7303bdc2016-04-17 11:50:54 -0500295 rev_t rev = {0};
David Cobbleya1adb072017-11-21 15:58:13 -0800296 static ipmi_device_id_t dev_id{};
297 static bool dev_id_initialized = false;
298 const char* filename = "/usr/share/ipmi-providers/dev_id.json";
Alexander Amelkinba19c182018-09-04 15:49:36 +0300299 constexpr auto ipmiDevIdStateShift = 7;
300 constexpr auto ipmiDevIdFw1Mask = ~(1 << ipmiDevIdStateShift);
Chris Austen6caf28b2015-10-13 12:40:40 -0500301
302 // Data length
Chris Austen7303bdc2016-04-17 11:50:54 -0500303 *data_len = sizeof(dev_id);
304
David Cobbleya1adb072017-11-21 15:58:13 -0800305 if (!dev_id_initialized)
306 {
Nagaraju Goruganti744398d2018-02-20 09:52:00 -0600307 try
308 {
309 auto version = getActiveSoftwareVersionInfo();
310 r = convert_version(version.c_str(), &rev);
David Cobbleya1adb072017-11-21 15:58:13 -0800311 }
Nagaraju Goruganti744398d2018-02-20 09:52:00 -0600312 catch (const std::exception& e)
313 {
314 log<level::ERR>(e.what());
315 }
Nan Liee0cb902016-07-11 15:38:03 +0800316
Patrick Venture0b02be92018-08-31 11:55:55 -0700317 if (r >= 0)
318 {
Nagaraju Goruganti744398d2018-02-20 09:52:00 -0600319 // bit7 identifies if the device is available
320 // 0=normal operation
321 // 1=device firmware, SDR update,
322 // or self-initialization in progress.
Alexander Amelkinba19c182018-09-04 15:49:36 +0300323 // The availability may change in run time, so mask here
324 // and initialize later.
325 dev_id.fw[0] = rev.major & ipmiDevIdFw1Mask;
Nagaraju Goruganti744398d2018-02-20 09:52:00 -0600326
327 rev.minor = (rev.minor > 99 ? 99 : rev.minor);
328 dev_id.fw[1] = rev.minor % 10 + (rev.minor / 10) * 16;
329 memcpy(&dev_id.aux, rev.d, 4);
David Cobbleya1adb072017-11-21 15:58:13 -0800330 }
Nan Liee0cb902016-07-11 15:38:03 +0800331
David Cobbleya1adb072017-11-21 15:58:13 -0800332 // IPMI Spec version 2.0
333 dev_id.ipmi_ver = 2;
Adriana Kobylak0e912642016-06-22 16:54:39 -0500334
David Cobbleya1adb072017-11-21 15:58:13 -0800335 std::ifstream dev_id_file(filename);
336 if (dev_id_file.is_open())
337 {
338 auto data = nlohmann::json::parse(dev_id_file, nullptr, false);
339 if (!data.is_discarded())
340 {
341 dev_id.id = data.value("id", 0);
342 dev_id.revision = data.value("revision", 0);
343 dev_id.addn_dev_support = data.value("addn_dev_support", 0);
344 dev_id.manuf_id[2] = data.value("manuf_id", 0) >> 16;
345 dev_id.manuf_id[1] = data.value("manuf_id", 0) >> 8;
346 dev_id.manuf_id[0] = data.value("manuf_id", 0);
347 dev_id.prod_id[1] = data.value("prod_id", 0) >> 8;
348 dev_id.prod_id[0] = data.value("prod_id", 0);
Tom Josephaf8a0982018-03-09 07:54:02 -0600349 dev_id.aux[3] = data.value("aux", 0);
350 dev_id.aux[2] = data.value("aux", 0) >> 8;
351 dev_id.aux[1] = data.value("aux", 0) >> 16;
352 dev_id.aux[0] = data.value("aux", 0) >> 24;
David Cobbleya1adb072017-11-21 15:58:13 -0800353
Patrick Venture0b02be92018-08-31 11:55:55 -0700354 // Don't read the file every time if successful
David Cobbleya1adb072017-11-21 15:58:13 -0800355 dev_id_initialized = true;
356 }
357 else
358 {
359 log<level::ERR>("Device ID JSON parser failure");
360 rc = IPMI_CC_UNSPECIFIED_ERROR;
361 }
362 }
363 else
364 {
365 log<level::ERR>("Device ID file not found");
366 rc = IPMI_CC_UNSPECIFIED_ERROR;
Chris Austen7303bdc2016-04-17 11:50:54 -0500367 }
368 }
Chris Austen6caf28b2015-10-13 12:40:40 -0500369
Alexander Amelkinba19c182018-09-04 15:49:36 +0300370 // Set availability to the actual current BMC state
371 dev_id.fw[0] &= ipmiDevIdFw1Mask;
372 if (!getCurrentBmcState())
373 {
374 dev_id.fw[0] |= (1 << ipmiDevIdStateShift);
375 }
376
Chris Austen6caf28b2015-10-13 12:40:40 -0500377 // Pack the actual response
Chris Austen7303bdc2016-04-17 11:50:54 -0500378 memcpy(response, &dev_id, *data_len);
Nagaraju Goruganti744398d2018-02-20 09:52:00 -0600379
Chris Austen6caf28b2015-10-13 12:40:40 -0500380 return rc;
381}
382
Nan Li41fa24a2016-11-10 20:12:37 +0800383ipmi_ret_t ipmi_app_get_self_test_results(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
Patrick Venture0b02be92018-08-31 11:55:55 -0700384 ipmi_request_t request,
385 ipmi_response_t response,
386 ipmi_data_len_t data_len,
387 ipmi_context_t context)
Nan Li41fa24a2016-11-10 20:12:37 +0800388{
389 ipmi_ret_t rc = IPMI_CC_OK;
390
391 // Byte 2:
392 // 55h - No error.
Gunnar Mills8991dd62017-10-25 17:11:29 -0500393 // 56h - Self Test function not implemented in this controller.
Nan Li41fa24a2016-11-10 20:12:37 +0800394 // 57h - Corrupted or inaccesssible data or devices.
395 // 58h - Fatal hardware error.
396 // FFh - reserved.
397 // all other: Device-specific 'internal failure'.
398 // Byte 3:
399 // For byte 2 = 55h, 56h, FFh: 00h
400 // For byte 2 = 58h, all other: Device-specific
401 // For byte 2 = 57h: self-test error bitfield.
402 // Note: returning 57h does not imply that all test were run.
403 // [7] 1b = Cannot access SEL device.
404 // [6] 1b = Cannot access SDR Repository.
405 // [5] 1b = Cannot access BMC FRU device.
406 // [4] 1b = IPMB signal lines do not respond.
407 // [3] 1b = SDR Repository empty.
408 // [2] 1b = Internal Use Area of BMC FRU corrupted.
409 // [1] 1b = controller update 'boot block' firmware corrupted.
410 // [0] 1b = controller operational firmware corrupted.
411
412 char selftestresults[2] = {0};
413
414 *data_len = 2;
415
416 selftestresults[0] = 0x56;
417 selftestresults[1] = 0;
418
419 memcpy(response, selftestresults, *data_len);
420
421 return rc;
422}
423
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500424ipmi_ret_t ipmi_app_get_device_guid(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
Patrick Venture0b02be92018-08-31 11:55:55 -0700425 ipmi_request_t request,
426 ipmi_response_t response,
427 ipmi_data_len_t data_len,
428 ipmi_context_t context)
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500429{
Patrick Venture0b02be92018-08-31 11:55:55 -0700430 const char* objname = "/org/openbmc/control/chassis0";
431 const char* iface = "org.freedesktop.DBus.Properties";
432 const char* chassis_iface = "org.openbmc.control.Chassis";
433 sd_bus_message* reply = NULL;
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500434 sd_bus_error error = SD_BUS_ERROR_NULL;
435 int r = 0;
Patrick Venture0b02be92018-08-31 11:55:55 -0700436 char* uuid = NULL;
437 char* busname = NULL;
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500438
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500439 // UUID is in RFC4122 format. Ex: 61a39523-78f2-11e5-9862-e6402cfc3223
Patrick Ventured2117022018-02-06 08:54:37 -0800440 // Per IPMI Spec 2.0 need to convert to 16 hex bytes and reverse the byte
441 // order
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500442 // Ex: 0x2332fc2c40e66298e511f2782395a361
443
Patrick Venture0b02be92018-08-31 11:55:55 -0700444 const int resp_size = 16; // Response is 16 hex bytes per IPMI Spec
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500445 uint8_t resp_uuid[resp_size]; // Array to hold the formatted response
Patrick Ventured2117022018-02-06 08:54:37 -0800446 // Point resp end of array to save in reverse order
Patrick Venture0b02be92018-08-31 11:55:55 -0700447 int resp_loc = resp_size - 1;
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500448 int i = 0;
Patrick Venture0b02be92018-08-31 11:55:55 -0700449 char* tokptr = NULL;
450 char* id_octet = NULL;
vishwa1eaea4f2016-02-26 11:57:40 -0600451
452 // Status code.
453 ipmi_ret_t rc = IPMI_CC_OK;
454 *data_len = 0;
455
vishwa1eaea4f2016-02-26 11:57:40 -0600456 // Call Get properties method with the interface and property name
Sergey Solomineb9b8142016-08-23 09:07:28 -0500457 r = mapper_get_service(bus, objname, &busname);
Patrick Venture0b02be92018-08-31 11:55:55 -0700458 if (r < 0)
459 {
460 log<level::ERR>("Failed to get bus name", entry("BUS=%s", objname),
Aditya Saripalli5fb14602017-11-09 14:46:27 +0530461 entry("ERRNO=0x%X", -r));
Sergey Solomineb9b8142016-08-23 09:07:28 -0500462 goto finish;
463 }
Patrick Venture0b02be92018-08-31 11:55:55 -0700464 r = sd_bus_call_method(bus, busname, objname, iface, "Get", &error, &reply,
465 "ss", chassis_iface, "uuid");
vishwa1eaea4f2016-02-26 11:57:40 -0600466 if (r < 0)
467 {
Patrick Venture0b02be92018-08-31 11:55:55 -0700468 log<level::ERR>("Failed to call Get Method", entry("ERRNO=0x%X", -r));
vishwa1eaea4f2016-02-26 11:57:40 -0600469 rc = IPMI_CC_UNSPECIFIED_ERROR;
470 goto finish;
471 }
472
473 r = sd_bus_message_read(reply, "v", "s", &uuid);
474 if (r < 0 || uuid == NULL)
475 {
Patrick Venture0b02be92018-08-31 11:55:55 -0700476 log<level::ERR>("Failed to get a response", entry("ERRNO=0x%X", -r));
vishwa1eaea4f2016-02-26 11:57:40 -0600477 rc = IPMI_CC_RESPONSE_ERROR;
478 goto finish;
479 }
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500480
481 // Traverse the UUID
Patrick Ventured2117022018-02-06 08:54:37 -0800482 // Get the UUID octects separated by dash
483 id_octet = strtok_r(uuid, "-", &tokptr);
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500484
485 if (id_octet == NULL)
vishwa1eaea4f2016-02-26 11:57:40 -0600486 {
487 // Error
Patrick Venture0b02be92018-08-31 11:55:55 -0700488 log<level::ERR>("Unexpected UUID format", entry("UUID=%s", uuid));
vishwa1eaea4f2016-02-26 11:57:40 -0600489 rc = IPMI_CC_RESPONSE_ERROR;
490 goto finish;
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500491 }
492
493 while (id_octet != NULL)
494 {
495 // Calculate the octet string size since it varies
496 // Divide it by 2 for the array size since 1 byte is built from 2 chars
Patrick Venture0b02be92018-08-31 11:55:55 -0700497 int tmp_size = strlen(id_octet) / 2;
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500498
Patrick Venture0b02be92018-08-31 11:55:55 -0700499 for (i = 0; i < tmp_size; i++)
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500500 {
Patrick Ventured2117022018-02-06 08:54:37 -0800501 // Holder of the 2 chars that will become a byte
502 char tmp_array[3] = {0};
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500503 strncpy(tmp_array, id_octet, 2); // 2 chars at a time
504
505 int resp_byte = strtoul(tmp_array, NULL, 16); // Convert to hex byte
Patrick Ventured2117022018-02-06 08:54:37 -0800506 // Copy end to first
507 memcpy((void*)&resp_uuid[resp_loc], &resp_byte, 1);
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500508 resp_loc--;
Patrick Venture0b02be92018-08-31 11:55:55 -0700509 id_octet += 2; // Finished with the 2 chars, advance
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500510 }
Patrick Venture0b02be92018-08-31 11:55:55 -0700511 id_octet = strtok_r(NULL, "-", &tokptr); // Get next octet
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500512 }
513
514 // Data length
515 *data_len = resp_size;
516
517 // Pack the actual response
518 memcpy(response, &resp_uuid, *data_len);
519
vishwa1eaea4f2016-02-26 11:57:40 -0600520finish:
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500521 sd_bus_error_free(&error);
vishwa1eaea4f2016-02-26 11:57:40 -0600522 reply = sd_bus_message_unref(reply);
Sergey Solomineb9b8142016-08-23 09:07:28 -0500523 free(busname);
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500524
525 return rc;
526}
Chris Austen6caf28b2015-10-13 12:40:40 -0500527
Adriana Kobylak3a552e12015-10-19 16:11:00 -0500528ipmi_ret_t ipmi_app_get_bt_capabilities(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
Patrick Venture0b02be92018-08-31 11:55:55 -0700529 ipmi_request_t request,
530 ipmi_response_t response,
531 ipmi_data_len_t data_len,
532 ipmi_context_t context)
vishwabmcba0bd5f2015-09-30 16:50:23 +0530533{
vishwabmcba0bd5f2015-09-30 16:50:23 +0530534
535 // Status code.
536 ipmi_ret_t rc = IPMI_CC_OK;
537
Adriana Kobylak88ad8152016-12-13 10:09:08 -0600538 // Per IPMI 2.0 spec, the input and output buffer size must be the max
539 // buffer size minus one byte to allocate space for the length byte.
Patrick Venture0b02be92018-08-31 11:55:55 -0700540 uint8_t str[] = {0x01, MAX_IPMI_BUFFER - 1, MAX_IPMI_BUFFER - 1, 0x0A,
541 0x01};
vishwabmcba0bd5f2015-09-30 16:50:23 +0530542
543 // Data length
544 *data_len = sizeof(str);
545
546 // Pack the actual response
547 memcpy(response, &str, *data_len);
548
549 return rc;
550}
551
Adriana Kobylak3a552e12015-10-19 16:11:00 -0500552ipmi_ret_t ipmi_app_wildcard_handler(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
Patrick Venture0b02be92018-08-31 11:55:55 -0700553 ipmi_request_t request,
554 ipmi_response_t response,
555 ipmi_data_len_t data_len,
556 ipmi_context_t context)
vishwabmcba0bd5f2015-09-30 16:50:23 +0530557{
vishwabmcba0bd5f2015-09-30 16:50:23 +0530558 // Status code.
Nan Li70aa8d92016-08-29 00:11:10 +0800559 ipmi_ret_t rc = IPMI_CC_INVALID;
vishwabmcba0bd5f2015-09-30 16:50:23 +0530560
561 *data_len = strlen("THIS IS WILDCARD");
562
563 // Now pack actual response
564 memcpy(response, "THIS IS WILDCARD", *data_len);
565
566 return rc;
567}
568
Marri Devender Rao5e007a52018-01-08 06:18:36 -0600569ipmi_ret_t ipmi_app_get_sys_guid(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
Patrick Venture0b02be92018-08-31 11:55:55 -0700570 ipmi_request_t request,
571 ipmi_response_t response,
572 ipmi_data_len_t data_len,
573 ipmi_context_t context)
Marri Devender Rao5e007a52018-01-08 06:18:36 -0600574
575{
576 ipmi_ret_t rc = IPMI_CC_OK;
577 sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()};
578
579 try
580 {
581 // Get the Inventory object implementing BMC interface
582 ipmi::DbusObjectInfo bmcObject =
583 ipmi::getDbusObject(bus, bmc_interface);
584
585 // Read UUID property value from bmcObject
586 // UUID is in RFC4122 format Ex: 61a39523-78f2-11e5-9862-e6402cfc3223
Patrick Venture0b02be92018-08-31 11:55:55 -0700587 auto variant =
588 ipmi::getDbusProperty(bus, bmcObject.second, bmcObject.first,
589 bmc_guid_interface, bmc_guid_property);
Marri Devender Rao5e007a52018-01-08 06:18:36 -0600590 std::string guidProp = variant.get<std::string>();
591
592 // Erase "-" characters from the property value
593 guidProp.erase(std::remove(guidProp.begin(), guidProp.end(), '-'),
Patrick Venture0b02be92018-08-31 11:55:55 -0700594 guidProp.end());
Marri Devender Rao5e007a52018-01-08 06:18:36 -0600595
596 auto guidPropLen = guidProp.length();
597 // Validate UUID data
598 // Divide by 2 as 1 byte is built from 2 chars
Patrick Venture0b02be92018-08-31 11:55:55 -0700599 if ((guidPropLen <= 0) || ((guidPropLen / 2) != bmc_guid_len))
Marri Devender Rao5e007a52018-01-08 06:18:36 -0600600
601 {
602 log<level::ERR>("Invalid UUID property value",
Patrick Venture0b02be92018-08-31 11:55:55 -0700603 entry("UUID_LENGTH=%d", guidPropLen));
Marri Devender Rao5e007a52018-01-08 06:18:36 -0600604 return IPMI_CC_RESPONSE_ERROR;
605 }
606
607 // Convert data in RFC4122(MSB) format to LSB format
608 // Get 2 characters at a time as 1 byte is built from 2 chars and
609 // convert to hex byte
610 // TODO: Data printed for GUID command is not as per the
611 // GUID format defined in IPMI specification 2.0 section 20.8
612 // Ticket raised: https://sourceforge.net/p/ipmitool/bugs/501/
613 uint8_t respGuid[bmc_guid_len];
614 for (size_t i = 0, respLoc = (bmc_guid_len - 1);
Patrick Venture0b02be92018-08-31 11:55:55 -0700615 i < guidPropLen && respLoc >= 0; i += 2, respLoc--)
Marri Devender Rao5e007a52018-01-08 06:18:36 -0600616 {
617 auto value = static_cast<uint8_t>(
Patrick Venture0b02be92018-08-31 11:55:55 -0700618 std::stoi(guidProp.substr(i, 2).c_str(), NULL, 16));
Marri Devender Rao5e007a52018-01-08 06:18:36 -0600619 respGuid[respLoc] = value;
620 }
621
622 *data_len = bmc_guid_len;
623 memcpy(response, &respGuid, bmc_guid_len);
624 }
625 catch (const InternalFailure& e)
626 {
627 log<level::ERR>("Failed in reading BMC UUID property",
628 entry("INTERFACE=%s", bmc_interface),
629 entry("PROPERTY_INTERFACE=%s", bmc_guid_interface),
630 entry("PROPERTY=%s", bmc_guid_property));
631 return IPMI_CC_UNSPECIFIED_ERROR;
632 }
633 return rc;
634}
635
Xo Wangf542e8b2017-08-09 15:34:16 -0700636static std::unique_ptr<SysInfoParamStore> sysInfoParamStore;
637
638struct IpmiSysInfoResp
639{
640 uint8_t paramRevision;
641 uint8_t setSelector;
642 union
643 {
644 struct
645 {
646 uint8_t encoding;
647 uint8_t stringLen;
648 uint8_t stringData0[14];
649 } __attribute__((packed));
650 uint8_t stringDataN[16];
651 uint8_t byteData;
652 };
653} __attribute__((packed));
654
655/**
656 * Split a string into (up to) 16-byte chunks as expected in response for get
657 * system info parameter.
658 *
659 * @param[in] fullString: Input string to be split
660 * @param[in] chunkIndex: Index of the chunk to be written out
661 * @param[in,out] chunk: Output data buffer; must have 14 byte capacity if
662 * chunk_index = 0 and 16-byte capacity otherwise
663 * @return the number of bytes written into the output buffer, or -EINVAL for
664 * invalid arguments.
665 */
666static int splitStringParam(const std::string& fullString, int chunkIndex,
667 uint8_t* chunk)
668{
669 constexpr int maxChunk = 255;
670 constexpr int smallChunk = 14;
671 constexpr int chunkSize = 16;
672 if (chunkIndex > maxChunk || chunk == nullptr)
673 {
674 return -EINVAL;
675 }
676 try
677 {
678 std::string output;
679 if (chunkIndex == 0)
680 {
681 // Output must have 14 byte capacity.
682 output = fullString.substr(0, smallChunk);
683 }
684 else
685 {
686 // Output must have 16 byte capacity.
687 output = fullString.substr((chunkIndex * chunkSize) - 2, chunkSize);
688 }
689
690 std::memcpy(chunk, output.c_str(), output.length());
691 return output.length();
692 }
693 catch (const std::out_of_range& e)
694 {
695 // The position was beyond the end.
696 return -EINVAL;
697 }
698}
699
700/**
701 * Packs the Get Sys Info Request Item into the response.
702 *
703 * @param[in] paramString - the parameter.
704 * @param[in] setSelector - the selector
705 * @param[in,out] resp - the System info response.
706 * @return The number of bytes packed or failure from splitStringParam().
707 */
708static int packGetSysInfoResp(const std::string& paramString,
709 uint8_t setSelector, IpmiSysInfoResp* resp)
710{
711 uint8_t* dataBuffer = resp->stringDataN;
712 resp->setSelector = setSelector;
713 if (resp->setSelector == 0) // First chunk has only 14 bytes.
714 {
715 resp->encoding = 0;
716 resp->stringLen = paramString.length();
717 dataBuffer = resp->stringData0;
718 }
719 return splitStringParam(paramString, resp->setSelector, dataBuffer);
720}
721
722ipmi_ret_t ipmi_app_get_system_info(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
723 ipmi_request_t request,
724 ipmi_response_t response,
725 ipmi_data_len_t dataLen,
726 ipmi_context_t context)
727{
728 IpmiSysInfoResp resp = {};
729 size_t respLen = 0;
730 uint8_t* const reqData = static_cast<uint8_t*>(request);
731 std::string paramString;
732 bool found;
733 std::tuple<bool, std::string> ret;
734 constexpr int minRequestSize = 4;
735 constexpr int paramSelector = 1;
736 constexpr uint8_t revisionOnly = 0x80;
737 const uint8_t paramRequested = reqData[paramSelector];
738 int rc;
739
740 if (*dataLen < minRequestSize)
741 {
742 return IPMI_CC_REQ_DATA_LEN_INVALID;
743 }
744
745 *dataLen = 0; // default to 0.
746
747 // Parameters revision as of IPMI spec v2.0 rev. 1.1 (Feb 11, 2014 E6)
748 resp.paramRevision = 0x11;
749 if (reqData[0] & revisionOnly) // Get parameter revision only
750 {
751 respLen = 1;
752 goto writeResponse;
753 }
754
755 // The "Set In Progress" parameter can be used for rollback of parameter
756 // data and is not implemented.
757 if (paramRequested == 0)
758 {
759 resp.byteData = 0;
760 respLen = 2;
761 goto writeResponse;
762 }
763
764 if (sysInfoParamStore == nullptr)
765 {
766 sysInfoParamStore = std::make_unique<SysInfoParamStore>();
767 }
768
769 // Parameters other than Set In Progress are assumed to be strings.
770 ret = sysInfoParamStore->lookup(paramRequested);
771 found = std::get<0>(ret);
772 paramString = std::get<1>(ret);
773 if (!found)
774 {
775 return IPMI_CC_SYSTEM_INFO_PARAMETER_NOT_SUPPORTED;
776 }
777 // TODO: Cache each parameter across multiple calls, until the whole string
778 // has been read out. Otherwise, it's possible for a parameter to change
779 // between requests for its chunks, returning chunks incoherent with each
780 // other. For now, the parameter store is simply required to have only
781 // idempotent callbacks.
782 rc = packGetSysInfoResp(paramString, reqData[2], &resp);
783 if (rc == -EINVAL)
784 {
785 return IPMI_CC_RESPONSE_ERROR;
786 }
787
788 respLen = sizeof(resp); // Write entire string data chunk in response.
789
790writeResponse:
791 std::memcpy(response, &resp, sizeof(resp));
792 *dataLen = respLen;
793 return IPMI_CC_OK;
794}
795
Chris Austen6caf28b2015-10-13 12:40:40 -0500796void register_netfn_app_functions()
vishwabmcba0bd5f2015-09-30 16:50:23 +0530797{
Tom05732372016-09-06 17:21:23 +0530798 // <Get BT Interface Capabilities>
Patrick Venture0b02be92018-08-31 11:55:55 -0700799 ipmi_register_callback(NETFUN_APP, IPMI_CMD_GET_CAP_BIT, NULL,
800 ipmi_app_get_bt_capabilities, PRIVILEGE_USER);
Chris Austen6caf28b2015-10-13 12:40:40 -0500801
Tom05732372016-09-06 17:21:23 +0530802 // <Wildcard Command>
Patrick Venture0b02be92018-08-31 11:55:55 -0700803 ipmi_register_callback(NETFUN_APP, IPMI_CMD_WILDCARD, NULL,
804 ipmi_app_wildcard_handler, PRIVILEGE_USER);
Chris Austen6caf28b2015-10-13 12:40:40 -0500805
Tom05732372016-09-06 17:21:23 +0530806 // <Reset Watchdog Timer>
Patrick Venture0b02be92018-08-31 11:55:55 -0700807 ipmi_register_callback(NETFUN_APP, IPMI_CMD_RESET_WD, NULL,
808 ipmi_app_watchdog_reset, PRIVILEGE_OPERATOR);
Chris Austen6caf28b2015-10-13 12:40:40 -0500809
Tom05732372016-09-06 17:21:23 +0530810 // <Set Watchdog Timer>
Patrick Venture0b02be92018-08-31 11:55:55 -0700811 ipmi_register_callback(NETFUN_APP, IPMI_CMD_SET_WD, NULL,
812 ipmi_app_watchdog_set, PRIVILEGE_OPERATOR);
Chris Austen6caf28b2015-10-13 12:40:40 -0500813
William A. Kennington III73f44512018-02-09 15:28:46 -0800814 // <Get Watchdog Timer>
Patrick Venture0b02be92018-08-31 11:55:55 -0700815 ipmi_register_callback(NETFUN_APP, IPMI_CMD_GET_WD, NULL,
816 ipmi_app_watchdog_get, PRIVILEGE_OPERATOR);
William A. Kennington III73f44512018-02-09 15:28:46 -0800817
Tom05732372016-09-06 17:21:23 +0530818 // <Get Device ID>
Patrick Venture0b02be92018-08-31 11:55:55 -0700819 ipmi_register_callback(NETFUN_APP, IPMI_CMD_GET_DEVICE_ID, NULL,
820 ipmi_app_get_device_id, PRIVILEGE_USER);
Chris Austen6caf28b2015-10-13 12:40:40 -0500821
Tom05732372016-09-06 17:21:23 +0530822 // <Get Self Test Results>
Patrick Venture0b02be92018-08-31 11:55:55 -0700823 ipmi_register_callback(NETFUN_APP, IPMI_CMD_GET_SELF_TEST_RESULTS, NULL,
824 ipmi_app_get_self_test_results, PRIVILEGE_USER);
Nan Li41fa24a2016-11-10 20:12:37 +0800825
Tom05732372016-09-06 17:21:23 +0530826 // <Get Device GUID>
Patrick Venture0b02be92018-08-31 11:55:55 -0700827 ipmi_register_callback(NETFUN_APP, IPMI_CMD_GET_DEVICE_GUID, NULL,
828 ipmi_app_get_device_guid, PRIVILEGE_USER);
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500829
Tom05732372016-09-06 17:21:23 +0530830 // <Set ACPI Power State>
Patrick Venture0b02be92018-08-31 11:55:55 -0700831 ipmi_register_callback(NETFUN_APP, IPMI_CMD_SET_ACPI, NULL,
832 ipmi_app_set_acpi_power_state, PRIVILEGE_ADMIN);
Chris Austen6caf28b2015-10-13 12:40:40 -0500833
Tom Joseph69fabfe2017-08-04 10:15:01 +0530834 // <Get Channel Access>
Patrick Venture0b02be92018-08-31 11:55:55 -0700835 ipmi_register_callback(NETFUN_APP, IPMI_CMD_GET_CHANNEL_ACCESS, NULL,
836 ipmi_get_channel_access, PRIVILEGE_USER);
Tom Joseph69fabfe2017-08-04 10:15:01 +0530837
Tom05732372016-09-06 17:21:23 +0530838 // <Get Channel Info Command>
Patrick Venture0b02be92018-08-31 11:55:55 -0700839 ipmi_register_callback(NETFUN_APP, IPMI_CMD_GET_CHAN_INFO, NULL,
840 ipmi_app_channel_info, PRIVILEGE_USER);
Chris Austenc2cd29d2016-02-05 20:02:29 -0600841
Marri Devender Rao5e007a52018-01-08 06:18:36 -0600842 // <Get System GUID Command>
Patrick Venture0b02be92018-08-31 11:55:55 -0700843 ipmi_register_callback(NETFUN_APP, IPMI_CMD_GET_SYS_GUID, NULL,
844 ipmi_app_get_sys_guid, PRIVILEGE_USER);
Tom Joseph7cbe2282018-03-21 21:17:33 +0530845
846 // <Get Channel Cipher Suites Command>
Patrick Venture0b02be92018-08-31 11:55:55 -0700847 ipmi_register_callback(NETFUN_APP, IPMI_CMD_GET_CHAN_CIPHER_SUITES, NULL,
848 getChannelCipherSuites, PRIVILEGE_CALLBACK);
Tom Joseph13227682018-08-10 01:05:21 +0530849
850 // <Set Channel Access Command>
851 ipmi_register_callback(NETFUN_APP, IPMI_CMD_SET_CHAN_ACCESS, NULL,
852 ipmi_set_channel_access, PRIVILEGE_ADMIN);
Xo Wangf542e8b2017-08-09 15:34:16 -0700853
854 // <Get System Info Command>
855 ipmi_register_callback(NETFUN_APP, IPMI_CMD_GET_SYSTEM_INFO, NULL,
856 ipmi_app_get_system_info, PRIVILEGE_USER);
vishwabmcba0bd5f2015-09-30 16:50:23 +0530857 return;
858}