blob: c77f014ac491bf6a14c7b49fd62bf3ab3bdaf37c [file] [log] [blame]
Patrick Venture46470a32018-09-07 19:26:25 -07001#include "apphandler.hpp"
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"
Xo Wangf542e8b2017-08-09 15:34:16 -07006#include "sys_info_param.hpp"
7#include "transporthandler.hpp"
8#include "types.hpp"
9#include "utils.hpp"
10
Patrick Venture0b02be92018-08-31 11:55:55 -070011#include <arpa/inet.h>
Patrick Venture46470a32018-09-07 19:26:25 -070012#include <host-ipmid/ipmid-api.h>
Xo Wang87651332017-08-11 10:17:59 -070013#include <limits.h>
Patrick Venture0b02be92018-08-31 11:55:55 -070014#include <mapper.h>
15#include <stdint.h>
16#include <stdio.h>
17#include <systemd/sd-bus.h>
Xo Wang87651332017-08-11 10:17:59 -070018#include <unistd.h>
Patrick Venture0b02be92018-08-31 11:55:55 -070019
Patrick Venture46470a32018-09-07 19:26:25 -070020#include <nlohmann/json.hpp>
Ratan Guptab8e99552017-07-27 07:07:48 +053021
Vernon Mauery185b9f82018-07-20 10:52:36 -070022#if __has_include(<filesystem>)
23#include <filesystem>
24#elif __has_include(<experimental/filesystem>)
25#include <experimental/filesystem>
Patrick Venture0b02be92018-08-31 11:55:55 -070026namespace std
27{
28// splice experimental::filesystem into std
29namespace filesystem = std::experimental::filesystem;
30} // namespace std
Vernon Mauery185b9f82018-07-20 10:52:36 -070031#else
Patrick Venture0b02be92018-08-31 11:55:55 -070032#error filesystem not available
Vernon Mauery185b9f82018-07-20 10:52:36 -070033#endif
Ratan Gupta62736ec2017-09-02 12:02:47 +053034
Xo Wangf542e8b2017-08-09 15:34:16 -070035#include <algorithm>
Patrick Venture0b02be92018-08-31 11:55:55 -070036#include <array>
37#include <cstddef>
38#include <fstream>
Xo Wangf542e8b2017-08-09 15:34:16 -070039#include <memory>
Ratan Guptab8e99552017-07-27 07:07:48 +053040#include <phosphor-logging/elog-errors.hpp>
Patrick Venture0b02be92018-08-31 11:55:55 -070041#include <phosphor-logging/log.hpp>
42#include <string>
Xo Wangf542e8b2017-08-09 15:34:16 -070043#include <tuple>
Patrick Venture0b02be92018-08-31 11:55:55 -070044#include <vector>
45#include <xyz/openbmc_project/Common/error.hpp>
46#include <xyz/openbmc_project/Software/Activation/server.hpp>
47#include <xyz/openbmc_project/Software/Version/server.hpp>
Alexander Amelkinba19c182018-09-04 15:49:36 +030048#include <xyz/openbmc_project/State/BMC/server.hpp>
Ratan Guptab8e99552017-07-27 07:07:48 +053049
Patrick Venture0b02be92018-08-31 11:55:55 -070050extern sd_bus* bus;
vishwabmcba0bd5f2015-09-30 16:50:23 +053051
Alexander Amelkinba19c182018-09-04 15:49:36 +030052constexpr auto bmc_state_interface = "xyz.openbmc_project.State.BMC";
53constexpr auto bmc_state_property = "CurrentBMCState";
Marri Devender Rao5e007a52018-01-08 06:18:36 -060054constexpr auto bmc_interface = "xyz.openbmc_project.Inventory.Item.Bmc";
55constexpr auto bmc_guid_interface = "xyz.openbmc_project.Common.UUID";
56constexpr auto bmc_guid_property = "UUID";
57constexpr auto bmc_guid_len = 16;
58
Nagaraju Goruganti744398d2018-02-20 09:52:00 -060059static constexpr auto redundancyIntf =
60 "xyz.openbmc_project.Software.RedundancyPriority";
Patrick Venture0b02be92018-08-31 11:55:55 -070061static constexpr auto versionIntf = "xyz.openbmc_project.Software.Version";
Nagaraju Goruganti744398d2018-02-20 09:52:00 -060062static constexpr auto activationIntf =
63 "xyz.openbmc_project.Software.Activation";
64static constexpr auto softwareRoot = "/xyz/openbmc_project/software";
65
Chris Austen6caf28b2015-10-13 12:40:40 -050066void register_netfn_app_functions() __attribute__((constructor));
vishwabmcba0bd5f2015-09-30 16:50:23 +053067
Ratan Guptab8e99552017-07-27 07:07:48 +053068using namespace phosphor::logging;
69using namespace sdbusplus::xyz::openbmc_project::Common::Error;
Nagaraju Goruganti744398d2018-02-20 09:52:00 -060070using Version = sdbusplus::xyz::openbmc_project::Software::server::Version;
71using Activation =
72 sdbusplus::xyz::openbmc_project::Software::server::Activation;
Alexander Amelkinba19c182018-09-04 15:49:36 +030073using BMC = sdbusplus::xyz::openbmc_project::State::server::BMC;
Vernon Mauery185b9f82018-07-20 10:52:36 -070074namespace fs = std::filesystem;
Ratan Guptab8e99552017-07-27 07:07:48 +053075
Nan Liee0cb902016-07-11 15:38:03 +080076// Offset in get device id command.
77typedef struct
78{
Patrick Venture0b02be92018-08-31 11:55:55 -070079 uint8_t id;
80 uint8_t revision;
81 uint8_t fw[2];
82 uint8_t ipmi_ver;
83 uint8_t addn_dev_support;
84 uint8_t manuf_id[3];
85 uint8_t prod_id[2];
86 uint8_t aux[4];
87} __attribute__((packed)) ipmi_device_id_t;
Chris Austen7303bdc2016-04-17 11:50:54 -050088
Nagaraju Goruganti744398d2018-02-20 09:52:00 -060089/**
90 * @brief Returns the Version info from primary s/w object
91 *
92 * Get the Version info from the active s/w object which is having high
93 * "Priority" value(a smaller number is a higher priority) and "Purpose"
94 * is "BMC" from the list of all s/w objects those are implementing
95 * RedundancyPriority interface from the given softwareRoot path.
96 *
97 * @return On success returns the Version info from primary s/w object.
98 *
99 */
100std::string getActiveSoftwareVersionInfo()
101{
102 sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()};
103
104 std::string revision{};
Patrick Venture0b02be92018-08-31 11:55:55 -0700105 auto objectTree =
106 ipmi::getAllDbusObjects(bus, softwareRoot, redundancyIntf, "");
Nagaraju Goruganti744398d2018-02-20 09:52:00 -0600107 if (objectTree.empty())
108 {
109 log<level::ERR>("No Obj has implemented the s/w redundancy interface",
110 entry("INTERFACE=%s", redundancyIntf));
111 elog<InternalFailure>();
112 }
113
114 auto objectFound = false;
115 for (auto& softObject : objectTree)
116 {
117 auto service = ipmi::getService(bus, redundancyIntf, softObject.first);
118 auto objValueTree = ipmi::getManagedObjects(bus, service, softwareRoot);
119
120 auto minPriority = 0xFF;
121 for (const auto& objIter : objValueTree)
122 {
123 try
124 {
125 auto& intfMap = objIter.second;
126 auto& redundancyPriorityProps = intfMap.at(redundancyIntf);
127 auto& versionProps = intfMap.at(versionIntf);
128 auto& activationProps = intfMap.at(activationIntf);
129 auto priority =
130 redundancyPriorityProps.at("Priority").get<uint8_t>();
131 auto purpose = versionProps.at("Purpose").get<std::string>();
132 auto activation =
133 activationProps.at("Activation").get<std::string>();
134 auto version = versionProps.at("Version").get<std::string>();
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<level::ERR>(e.what());
151 }
152 }
153 }
154
155 if (!objectFound)
156 {
157 log<level::ERR>("Could not found an BMC software Object");
158 elog<InternalFailure>();
159 }
160
161 return revision;
162}
163
Alexander Amelkinba19c182018-09-04 15:49:36 +0300164bool getCurrentBmcState()
165{
166 sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()};
167
168 // Get the Inventory object implementing the BMC interface
169 ipmi::DbusObjectInfo bmcObject =
170 ipmi::getDbusObject(bus, bmc_state_interface);
171 auto variant =
172 ipmi::getDbusProperty(bus, bmcObject.second, bmcObject.first,
173 bmc_state_interface, bmc_state_property);
174
175 return variant.is<std::string>() &&
176 BMC::convertBMCStateFromString(variant.get<std::string>()) ==
177 BMC::BMCState::Ready;
178}
179
Adriana Kobylak3a552e12015-10-19 16:11:00 -0500180ipmi_ret_t ipmi_app_set_acpi_power_state(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
Patrick Venture0b02be92018-08-31 11:55:55 -0700181 ipmi_request_t request,
182 ipmi_response_t response,
183 ipmi_data_len_t data_len,
184 ipmi_context_t context)
Chris Austen6caf28b2015-10-13 12:40:40 -0500185{
186 ipmi_ret_t rc = IPMI_CC_OK;
187 *data_len = 0;
188
Aditya Saripalli5fb14602017-11-09 14:46:27 +0530189 log<level::DEBUG>("IPMI SET ACPI STATE Ignoring for now\n");
Chris Austen6caf28b2015-10-13 12:40:40 -0500190 return rc;
191}
192
Chris Austen7303bdc2016-04-17 11:50:54 -0500193typedef struct
194{
195 char major;
196 char minor;
Chris Austen176c9652016-04-30 16:32:17 -0500197 uint16_t d[2];
Chris Austen7303bdc2016-04-17 11:50:54 -0500198} rev_t;
199
Dinesh Chinari2b7e07d2017-11-08 15:38:50 -0600200/* Currently supports the vx.x-x-[-x] and v1.x.x-x-[-x] format. It will */
201/* return -1 if not in those formats, this routine knows how to parse */
Chris Austen7303bdc2016-04-17 11:50:54 -0500202/* version = v0.6-19-gf363f61-dirty */
203/* ^ ^ ^^ ^ */
204/* | | |----------|-- additional details */
205/* | |---------------- Minor */
206/* |------------------ Major */
Dinesh Chinari2b7e07d2017-11-08 15:38:50 -0600207/* and version = v1.99.10-113-g65edf7d-r3-0-g9e4f715 */
208/* ^ ^ ^^ ^ */
209/* | | |--|---------- additional details */
210/* | |---------------- Minor */
211/* |------------------ Major */
Chris Austen7303bdc2016-04-17 11:50:54 -0500212/* Additional details : If the option group exists it will force Auxiliary */
213/* Firmware Revision Information 4th byte to 1 indicating the build was */
214/* derived with additional edits */
Patrick Venture0b02be92018-08-31 11:55:55 -0700215int convert_version(const char* p, rev_t* rev)
Chris Austen7303bdc2016-04-17 11:50:54 -0500216{
Dinesh Chinari2b7e07d2017-11-08 15:38:50 -0600217 std::string s(p);
218 std::string token;
Chris Austen176c9652016-04-30 16:32:17 -0500219 uint16_t commits;
Chris Austen7303bdc2016-04-17 11:50:54 -0500220
Patrick Venture0b02be92018-08-31 11:55:55 -0700221 auto location = s.find_first_of('v');
Dinesh Chinari2b7e07d2017-11-08 15:38:50 -0600222 if (location != std::string::npos)
223 {
Patrick Venture0b02be92018-08-31 11:55:55 -0700224 s = s.substr(location + 1);
Chris Austen176c9652016-04-30 16:32:17 -0500225 }
Chris Austen7303bdc2016-04-17 11:50:54 -0500226
Dinesh Chinari2b7e07d2017-11-08 15:38:50 -0600227 if (!s.empty())
228 {
229 location = s.find_first_of(".");
230 if (location != std::string::npos)
231 {
Patrick Venture0b02be92018-08-31 11:55:55 -0700232 rev->major =
233 static_cast<char>(std::stoi(s.substr(0, location), 0, 16));
234 token = s.substr(location + 1);
Dinesh Chinari2b7e07d2017-11-08 15:38:50 -0600235 }
Chris Austen176c9652016-04-30 16:32:17 -0500236
Dinesh Chinari2b7e07d2017-11-08 15:38:50 -0600237 if (!token.empty())
238 {
239 location = token.find_first_of(".-");
240 if (location != std::string::npos)
241 {
Patrick Ventured2117022018-02-06 08:54:37 -0800242 rev->minor = static_cast<char>(
Patrick Venture0b02be92018-08-31 11:55:55 -0700243 std::stoi(token.substr(0, location), 0, 16));
244 token = token.substr(location + 1);
Dinesh Chinari2b7e07d2017-11-08 15:38:50 -0600245 }
246 }
Chris Austen7303bdc2016-04-17 11:50:54 -0500247
Dinesh Chinari2b7e07d2017-11-08 15:38:50 -0600248 // Capture the number of commits on top of the minor tag.
249 // I'm using BE format like the ipmi spec asked for
250 location = token.find_first_of(".-");
251 if (!token.empty())
252 {
253 commits = std::stoi(token.substr(0, location), 0, 16);
Patrick Venture0b02be92018-08-31 11:55:55 -0700254 rev->d[0] = (commits >> 8) | (commits << 8);
Dinesh Chinari2b7e07d2017-11-08 15:38:50 -0600255
256 // commit number we skip
257 location = token.find_first_of(".-");
258 if (location != std::string::npos)
259 {
Patrick Venture0b02be92018-08-31 11:55:55 -0700260 token = token.substr(location + 1);
Dinesh Chinari2b7e07d2017-11-08 15:38:50 -0600261 }
262 }
Patrick Venture0b02be92018-08-31 11:55:55 -0700263 else
264 {
Dinesh Chinari2b7e07d2017-11-08 15:38:50 -0600265 rev->d[0] = 0;
266 }
267
268 if (location != std::string::npos)
269 {
Patrick Venture0b02be92018-08-31 11:55:55 -0700270 token = token.substr(location + 1);
Dinesh Chinari2b7e07d2017-11-08 15:38:50 -0600271 }
272
273 // Any value of the optional parameter forces it to 1
274 location = token.find_first_of(".-");
275 if (location != std::string::npos)
276 {
Patrick Venture0b02be92018-08-31 11:55:55 -0700277 token = token.substr(location + 1);
Dinesh Chinari2b7e07d2017-11-08 15:38:50 -0600278 }
279 commits = (!token.empty()) ? 1 : 0;
280
Patrick Venture0b02be92018-08-31 11:55:55 -0700281 // We do this operation to get this displayed in least significant bytes
282 // of ipmitool device id command.
283 rev->d[1] = (commits >> 8) | (commits << 8);
Dinesh Chinari2b7e07d2017-11-08 15:38:50 -0600284 }
285
Chris Austen7303bdc2016-04-17 11:50:54 -0500286 return 0;
287}
288
Adriana Kobylak3a552e12015-10-19 16:11:00 -0500289ipmi_ret_t ipmi_app_get_device_id(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
Patrick Venture0b02be92018-08-31 11:55:55 -0700290 ipmi_request_t request,
291 ipmi_response_t response,
292 ipmi_data_len_t data_len,
293 ipmi_context_t context)
Chris Austen6caf28b2015-10-13 12:40:40 -0500294{
295 ipmi_ret_t rc = IPMI_CC_OK;
Nagaraju Goruganti744398d2018-02-20 09:52:00 -0600296 int r = -1;
Chris Austen7303bdc2016-04-17 11:50:54 -0500297 rev_t rev = {0};
David Cobbleya1adb072017-11-21 15:58:13 -0800298 static ipmi_device_id_t dev_id{};
299 static bool dev_id_initialized = false;
300 const char* filename = "/usr/share/ipmi-providers/dev_id.json";
Alexander Amelkinba19c182018-09-04 15:49:36 +0300301 constexpr auto ipmiDevIdStateShift = 7;
302 constexpr auto ipmiDevIdFw1Mask = ~(1 << ipmiDevIdStateShift);
Chris Austen6caf28b2015-10-13 12:40:40 -0500303
304 // Data length
Chris Austen7303bdc2016-04-17 11:50:54 -0500305 *data_len = sizeof(dev_id);
306
David Cobbleya1adb072017-11-21 15:58:13 -0800307 if (!dev_id_initialized)
308 {
Nagaraju Goruganti744398d2018-02-20 09:52:00 -0600309 try
310 {
311 auto version = getActiveSoftwareVersionInfo();
312 r = convert_version(version.c_str(), &rev);
David Cobbleya1adb072017-11-21 15:58:13 -0800313 }
Nagaraju Goruganti744398d2018-02-20 09:52:00 -0600314 catch (const std::exception& e)
315 {
316 log<level::ERR>(e.what());
317 }
Nan Liee0cb902016-07-11 15:38:03 +0800318
Patrick Venture0b02be92018-08-31 11:55:55 -0700319 if (r >= 0)
320 {
Nagaraju Goruganti744398d2018-02-20 09:52:00 -0600321 // bit7 identifies if the device is available
322 // 0=normal operation
323 // 1=device firmware, SDR update,
324 // or self-initialization in progress.
Alexander Amelkinba19c182018-09-04 15:49:36 +0300325 // The availability may change in run time, so mask here
326 // and initialize later.
327 dev_id.fw[0] = rev.major & ipmiDevIdFw1Mask;
Nagaraju Goruganti744398d2018-02-20 09:52:00 -0600328
329 rev.minor = (rev.minor > 99 ? 99 : rev.minor);
330 dev_id.fw[1] = rev.minor % 10 + (rev.minor / 10) * 16;
331 memcpy(&dev_id.aux, rev.d, 4);
David Cobbleya1adb072017-11-21 15:58:13 -0800332 }
Nan Liee0cb902016-07-11 15:38:03 +0800333
David Cobbleya1adb072017-11-21 15:58:13 -0800334 // IPMI Spec version 2.0
335 dev_id.ipmi_ver = 2;
Adriana Kobylak0e912642016-06-22 16:54:39 -0500336
David Cobbleya1adb072017-11-21 15:58:13 -0800337 std::ifstream dev_id_file(filename);
338 if (dev_id_file.is_open())
339 {
340 auto data = nlohmann::json::parse(dev_id_file, nullptr, false);
341 if (!data.is_discarded())
342 {
343 dev_id.id = data.value("id", 0);
344 dev_id.revision = data.value("revision", 0);
345 dev_id.addn_dev_support = data.value("addn_dev_support", 0);
346 dev_id.manuf_id[2] = data.value("manuf_id", 0) >> 16;
347 dev_id.manuf_id[1] = data.value("manuf_id", 0) >> 8;
348 dev_id.manuf_id[0] = data.value("manuf_id", 0);
349 dev_id.prod_id[1] = data.value("prod_id", 0) >> 8;
350 dev_id.prod_id[0] = data.value("prod_id", 0);
Tom Josephaf8a0982018-03-09 07:54:02 -0600351 dev_id.aux[3] = data.value("aux", 0);
352 dev_id.aux[2] = data.value("aux", 0) >> 8;
353 dev_id.aux[1] = data.value("aux", 0) >> 16;
354 dev_id.aux[0] = data.value("aux", 0) >> 24;
David Cobbleya1adb072017-11-21 15:58:13 -0800355
Patrick Venture0b02be92018-08-31 11:55:55 -0700356 // Don't read the file every time if successful
David Cobbleya1adb072017-11-21 15:58:13 -0800357 dev_id_initialized = true;
358 }
359 else
360 {
361 log<level::ERR>("Device ID JSON parser failure");
362 rc = IPMI_CC_UNSPECIFIED_ERROR;
363 }
364 }
365 else
366 {
367 log<level::ERR>("Device ID file not found");
368 rc = IPMI_CC_UNSPECIFIED_ERROR;
Chris Austen7303bdc2016-04-17 11:50:54 -0500369 }
370 }
Chris Austen6caf28b2015-10-13 12:40:40 -0500371
Alexander Amelkinba19c182018-09-04 15:49:36 +0300372 // Set availability to the actual current BMC state
373 dev_id.fw[0] &= ipmiDevIdFw1Mask;
374 if (!getCurrentBmcState())
375 {
376 dev_id.fw[0] |= (1 << ipmiDevIdStateShift);
377 }
378
Chris Austen6caf28b2015-10-13 12:40:40 -0500379 // Pack the actual response
Chris Austen7303bdc2016-04-17 11:50:54 -0500380 memcpy(response, &dev_id, *data_len);
Nagaraju Goruganti744398d2018-02-20 09:52:00 -0600381
Chris Austen6caf28b2015-10-13 12:40:40 -0500382 return rc;
383}
384
Nan Li41fa24a2016-11-10 20:12:37 +0800385ipmi_ret_t ipmi_app_get_self_test_results(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
Patrick Venture0b02be92018-08-31 11:55:55 -0700386 ipmi_request_t request,
387 ipmi_response_t response,
388 ipmi_data_len_t data_len,
389 ipmi_context_t context)
Nan Li41fa24a2016-11-10 20:12:37 +0800390{
391 ipmi_ret_t rc = IPMI_CC_OK;
392
393 // Byte 2:
394 // 55h - No error.
Gunnar Mills8991dd62017-10-25 17:11:29 -0500395 // 56h - Self Test function not implemented in this controller.
Nan Li41fa24a2016-11-10 20:12:37 +0800396 // 57h - Corrupted or inaccesssible data or devices.
397 // 58h - Fatal hardware error.
398 // FFh - reserved.
399 // all other: Device-specific 'internal failure'.
400 // Byte 3:
401 // For byte 2 = 55h, 56h, FFh: 00h
402 // For byte 2 = 58h, all other: Device-specific
403 // For byte 2 = 57h: self-test error bitfield.
404 // Note: returning 57h does not imply that all test were run.
405 // [7] 1b = Cannot access SEL device.
406 // [6] 1b = Cannot access SDR Repository.
407 // [5] 1b = Cannot access BMC FRU device.
408 // [4] 1b = IPMB signal lines do not respond.
409 // [3] 1b = SDR Repository empty.
410 // [2] 1b = Internal Use Area of BMC FRU corrupted.
411 // [1] 1b = controller update 'boot block' firmware corrupted.
412 // [0] 1b = controller operational firmware corrupted.
413
414 char selftestresults[2] = {0};
415
416 *data_len = 2;
417
418 selftestresults[0] = 0x56;
419 selftestresults[1] = 0;
420
421 memcpy(response, selftestresults, *data_len);
422
423 return rc;
424}
425
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500426ipmi_ret_t ipmi_app_get_device_guid(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
Patrick Venture0b02be92018-08-31 11:55:55 -0700427 ipmi_request_t request,
428 ipmi_response_t response,
429 ipmi_data_len_t data_len,
430 ipmi_context_t context)
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500431{
Patrick Venture0b02be92018-08-31 11:55:55 -0700432 const char* objname = "/org/openbmc/control/chassis0";
433 const char* iface = "org.freedesktop.DBus.Properties";
434 const char* chassis_iface = "org.openbmc.control.Chassis";
435 sd_bus_message* reply = NULL;
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500436 sd_bus_error error = SD_BUS_ERROR_NULL;
437 int r = 0;
Patrick Venture0b02be92018-08-31 11:55:55 -0700438 char* uuid = NULL;
439 char* busname = NULL;
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500440
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500441 // UUID is in RFC4122 format. Ex: 61a39523-78f2-11e5-9862-e6402cfc3223
Patrick Ventured2117022018-02-06 08:54:37 -0800442 // Per IPMI Spec 2.0 need to convert to 16 hex bytes and reverse the byte
443 // order
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500444 // Ex: 0x2332fc2c40e66298e511f2782395a361
445
Patrick Venture0b02be92018-08-31 11:55:55 -0700446 const int resp_size = 16; // Response is 16 hex bytes per IPMI Spec
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500447 uint8_t resp_uuid[resp_size]; // Array to hold the formatted response
Patrick Ventured2117022018-02-06 08:54:37 -0800448 // Point resp end of array to save in reverse order
Patrick Venture0b02be92018-08-31 11:55:55 -0700449 int resp_loc = resp_size - 1;
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500450 int i = 0;
Patrick Venture0b02be92018-08-31 11:55:55 -0700451 char* tokptr = NULL;
452 char* id_octet = NULL;
vishwa1eaea4f2016-02-26 11:57:40 -0600453
454 // Status code.
455 ipmi_ret_t rc = IPMI_CC_OK;
456 *data_len = 0;
457
vishwa1eaea4f2016-02-26 11:57:40 -0600458 // Call Get properties method with the interface and property name
Sergey Solomineb9b8142016-08-23 09:07:28 -0500459 r = mapper_get_service(bus, objname, &busname);
Patrick Venture0b02be92018-08-31 11:55:55 -0700460 if (r < 0)
461 {
462 log<level::ERR>("Failed to get bus name", entry("BUS=%s", objname),
Aditya Saripalli5fb14602017-11-09 14:46:27 +0530463 entry("ERRNO=0x%X", -r));
Sergey Solomineb9b8142016-08-23 09:07:28 -0500464 goto finish;
465 }
Patrick Venture0b02be92018-08-31 11:55:55 -0700466 r = sd_bus_call_method(bus, busname, objname, iface, "Get", &error, &reply,
467 "ss", chassis_iface, "uuid");
vishwa1eaea4f2016-02-26 11:57:40 -0600468 if (r < 0)
469 {
Patrick Venture0b02be92018-08-31 11:55:55 -0700470 log<level::ERR>("Failed to call Get Method", entry("ERRNO=0x%X", -r));
vishwa1eaea4f2016-02-26 11:57:40 -0600471 rc = IPMI_CC_UNSPECIFIED_ERROR;
472 goto finish;
473 }
474
475 r = sd_bus_message_read(reply, "v", "s", &uuid);
476 if (r < 0 || uuid == NULL)
477 {
Patrick Venture0b02be92018-08-31 11:55:55 -0700478 log<level::ERR>("Failed to get a response", entry("ERRNO=0x%X", -r));
vishwa1eaea4f2016-02-26 11:57:40 -0600479 rc = IPMI_CC_RESPONSE_ERROR;
480 goto finish;
481 }
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500482
483 // Traverse the UUID
Patrick Ventured2117022018-02-06 08:54:37 -0800484 // Get the UUID octects separated by dash
485 id_octet = strtok_r(uuid, "-", &tokptr);
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500486
487 if (id_octet == NULL)
vishwa1eaea4f2016-02-26 11:57:40 -0600488 {
489 // Error
Patrick Venture0b02be92018-08-31 11:55:55 -0700490 log<level::ERR>("Unexpected UUID format", entry("UUID=%s", uuid));
vishwa1eaea4f2016-02-26 11:57:40 -0600491 rc = IPMI_CC_RESPONSE_ERROR;
492 goto finish;
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500493 }
494
495 while (id_octet != NULL)
496 {
497 // Calculate the octet string size since it varies
498 // Divide it by 2 for the array size since 1 byte is built from 2 chars
Patrick Venture0b02be92018-08-31 11:55:55 -0700499 int tmp_size = strlen(id_octet) / 2;
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500500
Patrick Venture0b02be92018-08-31 11:55:55 -0700501 for (i = 0; i < tmp_size; i++)
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500502 {
Patrick Ventured2117022018-02-06 08:54:37 -0800503 // Holder of the 2 chars that will become a byte
504 char tmp_array[3] = {0};
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500505 strncpy(tmp_array, id_octet, 2); // 2 chars at a time
506
507 int resp_byte = strtoul(tmp_array, NULL, 16); // Convert to hex byte
Patrick Ventured2117022018-02-06 08:54:37 -0800508 // Copy end to first
509 memcpy((void*)&resp_uuid[resp_loc], &resp_byte, 1);
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500510 resp_loc--;
Patrick Venture0b02be92018-08-31 11:55:55 -0700511 id_octet += 2; // Finished with the 2 chars, advance
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500512 }
Patrick Venture0b02be92018-08-31 11:55:55 -0700513 id_octet = strtok_r(NULL, "-", &tokptr); // Get next octet
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500514 }
515
516 // Data length
517 *data_len = resp_size;
518
519 // Pack the actual response
520 memcpy(response, &resp_uuid, *data_len);
521
vishwa1eaea4f2016-02-26 11:57:40 -0600522finish:
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500523 sd_bus_error_free(&error);
vishwa1eaea4f2016-02-26 11:57:40 -0600524 reply = sd_bus_message_unref(reply);
Sergey Solomineb9b8142016-08-23 09:07:28 -0500525 free(busname);
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500526
527 return rc;
528}
Chris Austen6caf28b2015-10-13 12:40:40 -0500529
Adriana Kobylak3a552e12015-10-19 16:11:00 -0500530ipmi_ret_t ipmi_app_get_bt_capabilities(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
Patrick Venture0b02be92018-08-31 11:55:55 -0700531 ipmi_request_t request,
532 ipmi_response_t response,
533 ipmi_data_len_t data_len,
534 ipmi_context_t context)
vishwabmcba0bd5f2015-09-30 16:50:23 +0530535{
vishwabmcba0bd5f2015-09-30 16:50:23 +0530536
537 // Status code.
538 ipmi_ret_t rc = IPMI_CC_OK;
539
Adriana Kobylak88ad8152016-12-13 10:09:08 -0600540 // Per IPMI 2.0 spec, the input and output buffer size must be the max
541 // buffer size minus one byte to allocate space for the length byte.
Patrick Venture0b02be92018-08-31 11:55:55 -0700542 uint8_t str[] = {0x01, MAX_IPMI_BUFFER - 1, MAX_IPMI_BUFFER - 1, 0x0A,
543 0x01};
vishwabmcba0bd5f2015-09-30 16:50:23 +0530544
545 // Data length
546 *data_len = sizeof(str);
547
548 // Pack the actual response
549 memcpy(response, &str, *data_len);
550
551 return rc;
552}
553
Adriana Kobylak3a552e12015-10-19 16:11:00 -0500554ipmi_ret_t ipmi_app_wildcard_handler(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
Patrick Venture0b02be92018-08-31 11:55:55 -0700555 ipmi_request_t request,
556 ipmi_response_t response,
557 ipmi_data_len_t data_len,
558 ipmi_context_t context)
vishwabmcba0bd5f2015-09-30 16:50:23 +0530559{
vishwabmcba0bd5f2015-09-30 16:50:23 +0530560 // Status code.
Nan Li70aa8d92016-08-29 00:11:10 +0800561 ipmi_ret_t rc = IPMI_CC_INVALID;
vishwabmcba0bd5f2015-09-30 16:50:23 +0530562
563 *data_len = strlen("THIS IS WILDCARD");
564
565 // Now pack actual response
566 memcpy(response, "THIS IS WILDCARD", *data_len);
567
568 return rc;
569}
570
Marri Devender Rao5e007a52018-01-08 06:18:36 -0600571ipmi_ret_t ipmi_app_get_sys_guid(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
Patrick Venture0b02be92018-08-31 11:55:55 -0700572 ipmi_request_t request,
573 ipmi_response_t response,
574 ipmi_data_len_t data_len,
575 ipmi_context_t context)
Marri Devender Rao5e007a52018-01-08 06:18:36 -0600576
577{
578 ipmi_ret_t rc = IPMI_CC_OK;
579 sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()};
580
581 try
582 {
583 // Get the Inventory object implementing BMC interface
584 ipmi::DbusObjectInfo bmcObject =
585 ipmi::getDbusObject(bus, bmc_interface);
586
587 // Read UUID property value from bmcObject
588 // UUID is in RFC4122 format Ex: 61a39523-78f2-11e5-9862-e6402cfc3223
Patrick Venture0b02be92018-08-31 11:55:55 -0700589 auto variant =
590 ipmi::getDbusProperty(bus, bmcObject.second, bmcObject.first,
591 bmc_guid_interface, bmc_guid_property);
Marri Devender Rao5e007a52018-01-08 06:18:36 -0600592 std::string guidProp = variant.get<std::string>();
593
594 // Erase "-" characters from the property value
595 guidProp.erase(std::remove(guidProp.begin(), guidProp.end(), '-'),
Patrick Venture0b02be92018-08-31 11:55:55 -0700596 guidProp.end());
Marri Devender Rao5e007a52018-01-08 06:18:36 -0600597
598 auto guidPropLen = guidProp.length();
599 // Validate UUID data
600 // Divide by 2 as 1 byte is built from 2 chars
Patrick Venture0b02be92018-08-31 11:55:55 -0700601 if ((guidPropLen <= 0) || ((guidPropLen / 2) != bmc_guid_len))
Marri Devender Rao5e007a52018-01-08 06:18:36 -0600602
603 {
604 log<level::ERR>("Invalid UUID property value",
Patrick Venture0b02be92018-08-31 11:55:55 -0700605 entry("UUID_LENGTH=%d", guidPropLen));
Marri Devender Rao5e007a52018-01-08 06:18:36 -0600606 return IPMI_CC_RESPONSE_ERROR;
607 }
608
609 // Convert data in RFC4122(MSB) format to LSB format
610 // Get 2 characters at a time as 1 byte is built from 2 chars and
611 // convert to hex byte
612 // TODO: Data printed for GUID command is not as per the
613 // GUID format defined in IPMI specification 2.0 section 20.8
614 // Ticket raised: https://sourceforge.net/p/ipmitool/bugs/501/
615 uint8_t respGuid[bmc_guid_len];
616 for (size_t i = 0, respLoc = (bmc_guid_len - 1);
Patrick Venture0b02be92018-08-31 11:55:55 -0700617 i < guidPropLen && respLoc >= 0; i += 2, respLoc--)
Marri Devender Rao5e007a52018-01-08 06:18:36 -0600618 {
619 auto value = static_cast<uint8_t>(
Patrick Venture0b02be92018-08-31 11:55:55 -0700620 std::stoi(guidProp.substr(i, 2).c_str(), NULL, 16));
Marri Devender Rao5e007a52018-01-08 06:18:36 -0600621 respGuid[respLoc] = value;
622 }
623
624 *data_len = bmc_guid_len;
625 memcpy(response, &respGuid, bmc_guid_len);
626 }
627 catch (const InternalFailure& e)
628 {
629 log<level::ERR>("Failed in reading BMC UUID property",
630 entry("INTERFACE=%s", bmc_interface),
631 entry("PROPERTY_INTERFACE=%s", bmc_guid_interface),
632 entry("PROPERTY=%s", bmc_guid_property));
633 return IPMI_CC_UNSPECIFIED_ERROR;
634 }
635 return rc;
636}
637
Xo Wangf542e8b2017-08-09 15:34:16 -0700638static std::unique_ptr<SysInfoParamStore> sysInfoParamStore;
639
Xo Wang87651332017-08-11 10:17:59 -0700640static std::string sysInfoReadSystemName()
641{
642 // Use the BMC hostname as the "System Name."
643 char hostname[HOST_NAME_MAX + 1] = {};
644 if (gethostname(hostname, HOST_NAME_MAX) != 0)
645 {
646 perror("System info parameter: system name");
647 }
648 return hostname;
649}
650
Xo Wangf542e8b2017-08-09 15:34:16 -0700651struct IpmiSysInfoResp
652{
653 uint8_t paramRevision;
654 uint8_t setSelector;
655 union
656 {
657 struct
658 {
659 uint8_t encoding;
660 uint8_t stringLen;
661 uint8_t stringData0[14];
662 } __attribute__((packed));
663 uint8_t stringDataN[16];
664 uint8_t byteData;
665 };
666} __attribute__((packed));
667
668/**
669 * Split a string into (up to) 16-byte chunks as expected in response for get
670 * system info parameter.
671 *
672 * @param[in] fullString: Input string to be split
673 * @param[in] chunkIndex: Index of the chunk to be written out
674 * @param[in,out] chunk: Output data buffer; must have 14 byte capacity if
675 * chunk_index = 0 and 16-byte capacity otherwise
676 * @return the number of bytes written into the output buffer, or -EINVAL for
677 * invalid arguments.
678 */
679static int splitStringParam(const std::string& fullString, int chunkIndex,
680 uint8_t* chunk)
681{
682 constexpr int maxChunk = 255;
683 constexpr int smallChunk = 14;
684 constexpr int chunkSize = 16;
685 if (chunkIndex > maxChunk || chunk == nullptr)
686 {
687 return -EINVAL;
688 }
689 try
690 {
691 std::string output;
692 if (chunkIndex == 0)
693 {
694 // Output must have 14 byte capacity.
695 output = fullString.substr(0, smallChunk);
696 }
697 else
698 {
699 // Output must have 16 byte capacity.
700 output = fullString.substr((chunkIndex * chunkSize) - 2, chunkSize);
701 }
702
703 std::memcpy(chunk, output.c_str(), output.length());
704 return output.length();
705 }
706 catch (const std::out_of_range& e)
707 {
708 // The position was beyond the end.
709 return -EINVAL;
710 }
711}
712
713/**
714 * Packs the Get Sys Info Request Item into the response.
715 *
716 * @param[in] paramString - the parameter.
717 * @param[in] setSelector - the selector
718 * @param[in,out] resp - the System info response.
719 * @return The number of bytes packed or failure from splitStringParam().
720 */
721static int packGetSysInfoResp(const std::string& paramString,
722 uint8_t setSelector, IpmiSysInfoResp* resp)
723{
724 uint8_t* dataBuffer = resp->stringDataN;
725 resp->setSelector = setSelector;
726 if (resp->setSelector == 0) // First chunk has only 14 bytes.
727 {
728 resp->encoding = 0;
729 resp->stringLen = paramString.length();
730 dataBuffer = resp->stringData0;
731 }
732 return splitStringParam(paramString, resp->setSelector, dataBuffer);
733}
734
735ipmi_ret_t ipmi_app_get_system_info(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
736 ipmi_request_t request,
737 ipmi_response_t response,
738 ipmi_data_len_t dataLen,
739 ipmi_context_t context)
740{
741 IpmiSysInfoResp resp = {};
742 size_t respLen = 0;
743 uint8_t* const reqData = static_cast<uint8_t*>(request);
744 std::string paramString;
745 bool found;
746 std::tuple<bool, std::string> ret;
747 constexpr int minRequestSize = 4;
748 constexpr int paramSelector = 1;
749 constexpr uint8_t revisionOnly = 0x80;
750 const uint8_t paramRequested = reqData[paramSelector];
751 int rc;
752
753 if (*dataLen < minRequestSize)
754 {
755 return IPMI_CC_REQ_DATA_LEN_INVALID;
756 }
757
758 *dataLen = 0; // default to 0.
759
760 // Parameters revision as of IPMI spec v2.0 rev. 1.1 (Feb 11, 2014 E6)
761 resp.paramRevision = 0x11;
762 if (reqData[0] & revisionOnly) // Get parameter revision only
763 {
764 respLen = 1;
765 goto writeResponse;
766 }
767
768 // The "Set In Progress" parameter can be used for rollback of parameter
769 // data and is not implemented.
770 if (paramRequested == 0)
771 {
772 resp.byteData = 0;
773 respLen = 2;
774 goto writeResponse;
775 }
776
777 if (sysInfoParamStore == nullptr)
778 {
779 sysInfoParamStore = std::make_unique<SysInfoParamStore>();
Xo Wang87651332017-08-11 10:17:59 -0700780 sysInfoParamStore->update(IPMI_SYSINFO_SYSTEM_NAME,
781 sysInfoReadSystemName);
Xo Wangf542e8b2017-08-09 15:34:16 -0700782 }
783
784 // Parameters other than Set In Progress are assumed to be strings.
785 ret = sysInfoParamStore->lookup(paramRequested);
786 found = std::get<0>(ret);
787 paramString = std::get<1>(ret);
788 if (!found)
789 {
790 return IPMI_CC_SYSTEM_INFO_PARAMETER_NOT_SUPPORTED;
791 }
792 // TODO: Cache each parameter across multiple calls, until the whole string
793 // has been read out. Otherwise, it's possible for a parameter to change
794 // between requests for its chunks, returning chunks incoherent with each
795 // other. For now, the parameter store is simply required to have only
796 // idempotent callbacks.
797 rc = packGetSysInfoResp(paramString, reqData[2], &resp);
798 if (rc == -EINVAL)
799 {
800 return IPMI_CC_RESPONSE_ERROR;
801 }
802
803 respLen = sizeof(resp); // Write entire string data chunk in response.
804
805writeResponse:
806 std::memcpy(response, &resp, sizeof(resp));
807 *dataLen = respLen;
808 return IPMI_CC_OK;
809}
810
Chris Austen6caf28b2015-10-13 12:40:40 -0500811void register_netfn_app_functions()
vishwabmcba0bd5f2015-09-30 16:50:23 +0530812{
Tom05732372016-09-06 17:21:23 +0530813 // <Get BT Interface Capabilities>
Patrick Venture0b02be92018-08-31 11:55:55 -0700814 ipmi_register_callback(NETFUN_APP, IPMI_CMD_GET_CAP_BIT, NULL,
815 ipmi_app_get_bt_capabilities, PRIVILEGE_USER);
Chris Austen6caf28b2015-10-13 12:40:40 -0500816
Tom05732372016-09-06 17:21:23 +0530817 // <Wildcard Command>
Patrick Venture0b02be92018-08-31 11:55:55 -0700818 ipmi_register_callback(NETFUN_APP, IPMI_CMD_WILDCARD, NULL,
819 ipmi_app_wildcard_handler, PRIVILEGE_USER);
Chris Austen6caf28b2015-10-13 12:40:40 -0500820
Tom05732372016-09-06 17:21:23 +0530821 // <Reset Watchdog Timer>
Patrick Venture0b02be92018-08-31 11:55:55 -0700822 ipmi_register_callback(NETFUN_APP, IPMI_CMD_RESET_WD, NULL,
823 ipmi_app_watchdog_reset, PRIVILEGE_OPERATOR);
Chris Austen6caf28b2015-10-13 12:40:40 -0500824
Tom05732372016-09-06 17:21:23 +0530825 // <Set Watchdog Timer>
Patrick Venture0b02be92018-08-31 11:55:55 -0700826 ipmi_register_callback(NETFUN_APP, IPMI_CMD_SET_WD, NULL,
827 ipmi_app_watchdog_set, PRIVILEGE_OPERATOR);
Chris Austen6caf28b2015-10-13 12:40:40 -0500828
William A. Kennington III73f44512018-02-09 15:28:46 -0800829 // <Get Watchdog Timer>
Patrick Venture0b02be92018-08-31 11:55:55 -0700830 ipmi_register_callback(NETFUN_APP, IPMI_CMD_GET_WD, NULL,
831 ipmi_app_watchdog_get, PRIVILEGE_OPERATOR);
William A. Kennington III73f44512018-02-09 15:28:46 -0800832
Tom05732372016-09-06 17:21:23 +0530833 // <Get Device ID>
Patrick Venture0b02be92018-08-31 11:55:55 -0700834 ipmi_register_callback(NETFUN_APP, IPMI_CMD_GET_DEVICE_ID, NULL,
835 ipmi_app_get_device_id, PRIVILEGE_USER);
Chris Austen6caf28b2015-10-13 12:40:40 -0500836
Tom05732372016-09-06 17:21:23 +0530837 // <Get Self Test Results>
Patrick Venture0b02be92018-08-31 11:55:55 -0700838 ipmi_register_callback(NETFUN_APP, IPMI_CMD_GET_SELF_TEST_RESULTS, NULL,
839 ipmi_app_get_self_test_results, PRIVILEGE_USER);
Nan Li41fa24a2016-11-10 20:12:37 +0800840
Tom05732372016-09-06 17:21:23 +0530841 // <Get Device GUID>
Patrick Venture0b02be92018-08-31 11:55:55 -0700842 ipmi_register_callback(NETFUN_APP, IPMI_CMD_GET_DEVICE_GUID, NULL,
843 ipmi_app_get_device_guid, PRIVILEGE_USER);
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500844
Tom05732372016-09-06 17:21:23 +0530845 // <Set ACPI Power State>
Patrick Venture0b02be92018-08-31 11:55:55 -0700846 ipmi_register_callback(NETFUN_APP, IPMI_CMD_SET_ACPI, NULL,
847 ipmi_app_set_acpi_power_state, PRIVILEGE_ADMIN);
Chris Austen6caf28b2015-10-13 12:40:40 -0500848
Tom Joseph69fabfe2017-08-04 10:15:01 +0530849 // <Get Channel Access>
Patrick Venture0b02be92018-08-31 11:55:55 -0700850 ipmi_register_callback(NETFUN_APP, IPMI_CMD_GET_CHANNEL_ACCESS, NULL,
851 ipmi_get_channel_access, PRIVILEGE_USER);
Tom Joseph69fabfe2017-08-04 10:15:01 +0530852
Tom05732372016-09-06 17:21:23 +0530853 // <Get Channel Info Command>
Patrick Venture0b02be92018-08-31 11:55:55 -0700854 ipmi_register_callback(NETFUN_APP, IPMI_CMD_GET_CHAN_INFO, NULL,
855 ipmi_app_channel_info, PRIVILEGE_USER);
Chris Austenc2cd29d2016-02-05 20:02:29 -0600856
Marri Devender Rao5e007a52018-01-08 06:18:36 -0600857 // <Get System GUID Command>
Patrick Venture0b02be92018-08-31 11:55:55 -0700858 ipmi_register_callback(NETFUN_APP, IPMI_CMD_GET_SYS_GUID, NULL,
859 ipmi_app_get_sys_guid, PRIVILEGE_USER);
Tom Joseph7cbe2282018-03-21 21:17:33 +0530860
861 // <Get Channel Cipher Suites Command>
Patrick Venture0b02be92018-08-31 11:55:55 -0700862 ipmi_register_callback(NETFUN_APP, IPMI_CMD_GET_CHAN_CIPHER_SUITES, NULL,
863 getChannelCipherSuites, PRIVILEGE_CALLBACK);
Tom Joseph13227682018-08-10 01:05:21 +0530864
865 // <Set Channel Access Command>
866 ipmi_register_callback(NETFUN_APP, IPMI_CMD_SET_CHAN_ACCESS, NULL,
867 ipmi_set_channel_access, PRIVILEGE_ADMIN);
Xo Wangf542e8b2017-08-09 15:34:16 -0700868
869 // <Get System Info Command>
870 ipmi_register_callback(NETFUN_APP, IPMI_CMD_GET_SYSTEM_INFO, NULL,
871 ipmi_app_get_system_info, PRIVILEGE_USER);
vishwabmcba0bd5f2015-09-30 16:50:23 +0530872 return;
873}