blob: cbbd5f46650eb7821736c816185247782fd7a42c [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>
Patrick Venture0b02be92018-08-31 11:55:55 -070015#include <systemd/sd-bus.h>
Xo Wang87651332017-08-11 10:17:59 -070016#include <unistd.h>
Patrick Venture0b02be92018-08-31 11:55:55 -070017
Patrick Venture3a5071a2018-09-12 13:27:42 -070018#include <algorithm>
19#include <array>
20#include <cstddef>
21#include <fstream>
22#include <memory>
Patrick Venture46470a32018-09-07 19:26:25 -070023#include <nlohmann/json.hpp>
Patrick Venture3a5071a2018-09-12 13:27:42 -070024#include <phosphor-logging/elog-errors.hpp>
25#include <phosphor-logging/log.hpp>
26#include <string>
27#include <tuple>
28#include <vector>
29#include <xyz/openbmc_project/Common/error.hpp>
30#include <xyz/openbmc_project/Software/Activation/server.hpp>
31#include <xyz/openbmc_project/Software/Version/server.hpp>
32#include <xyz/openbmc_project/State/BMC/server.hpp>
Ratan Guptab8e99552017-07-27 07:07:48 +053033
Vernon Mauery185b9f82018-07-20 10:52:36 -070034#if __has_include(<filesystem>)
35#include <filesystem>
36#elif __has_include(<experimental/filesystem>)
37#include <experimental/filesystem>
Patrick Venture0b02be92018-08-31 11:55:55 -070038namespace std
39{
40// splice experimental::filesystem into std
41namespace filesystem = std::experimental::filesystem;
42} // namespace std
Vernon Mauery185b9f82018-07-20 10:52:36 -070043#else
Patrick Venture0b02be92018-08-31 11:55:55 -070044#error filesystem not available
Vernon Mauery185b9f82018-07-20 10:52:36 -070045#endif
Ratan Gupta62736ec2017-09-02 12:02:47 +053046
Patrick Venture0b02be92018-08-31 11:55:55 -070047extern sd_bus* bus;
vishwabmcba0bd5f2015-09-30 16:50:23 +053048
Alexander Amelkinba19c182018-09-04 15:49:36 +030049constexpr auto bmc_state_interface = "xyz.openbmc_project.State.BMC";
50constexpr auto bmc_state_property = "CurrentBMCState";
Marri Devender Rao5e007a52018-01-08 06:18:36 -060051constexpr auto bmc_interface = "xyz.openbmc_project.Inventory.Item.Bmc";
52constexpr auto bmc_guid_interface = "xyz.openbmc_project.Common.UUID";
53constexpr auto bmc_guid_property = "UUID";
54constexpr auto bmc_guid_len = 16;
55
Nagaraju Goruganti744398d2018-02-20 09:52:00 -060056static constexpr auto redundancyIntf =
57 "xyz.openbmc_project.Software.RedundancyPriority";
Patrick Venture0b02be92018-08-31 11:55:55 -070058static constexpr auto versionIntf = "xyz.openbmc_project.Software.Version";
Nagaraju Goruganti744398d2018-02-20 09:52:00 -060059static constexpr auto activationIntf =
60 "xyz.openbmc_project.Software.Activation";
61static constexpr auto softwareRoot = "/xyz/openbmc_project/software";
62
Chris Austen6caf28b2015-10-13 12:40:40 -050063void register_netfn_app_functions() __attribute__((constructor));
vishwabmcba0bd5f2015-09-30 16:50:23 +053064
Ratan Guptab8e99552017-07-27 07:07:48 +053065using namespace phosphor::logging;
66using namespace sdbusplus::xyz::openbmc_project::Common::Error;
Nagaraju Goruganti744398d2018-02-20 09:52:00 -060067using Version = sdbusplus::xyz::openbmc_project::Software::server::Version;
68using Activation =
69 sdbusplus::xyz::openbmc_project::Software::server::Activation;
Alexander Amelkinba19c182018-09-04 15:49:36 +030070using BMC = sdbusplus::xyz::openbmc_project::State::server::BMC;
Vernon Mauery185b9f82018-07-20 10:52:36 -070071namespace fs = std::filesystem;
Ratan Guptab8e99552017-07-27 07:07:48 +053072
Nan Liee0cb902016-07-11 15:38:03 +080073// Offset in get device id command.
74typedef struct
75{
Patrick Venture0b02be92018-08-31 11:55:55 -070076 uint8_t id;
77 uint8_t revision;
78 uint8_t fw[2];
79 uint8_t ipmi_ver;
80 uint8_t addn_dev_support;
81 uint8_t manuf_id[3];
82 uint8_t prod_id[2];
83 uint8_t aux[4];
84} __attribute__((packed)) ipmi_device_id_t;
Chris Austen7303bdc2016-04-17 11:50:54 -050085
Nagaraju Goruganti744398d2018-02-20 09:52:00 -060086/**
87 * @brief Returns the Version info from primary s/w object
88 *
89 * Get the Version info from the active s/w object which is having high
90 * "Priority" value(a smaller number is a higher priority) and "Purpose"
91 * is "BMC" from the list of all s/w objects those are implementing
92 * RedundancyPriority interface from the given softwareRoot path.
93 *
94 * @return On success returns the Version info from primary s/w object.
95 *
96 */
97std::string getActiveSoftwareVersionInfo()
98{
99 sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()};
100
101 std::string revision{};
Patrick Venture0b02be92018-08-31 11:55:55 -0700102 auto objectTree =
103 ipmi::getAllDbusObjects(bus, softwareRoot, redundancyIntf, "");
Nagaraju Goruganti744398d2018-02-20 09:52:00 -0600104 if (objectTree.empty())
105 {
106 log<level::ERR>("No Obj has implemented the s/w redundancy interface",
107 entry("INTERFACE=%s", redundancyIntf));
108 elog<InternalFailure>();
109 }
110
111 auto objectFound = false;
112 for (auto& softObject : objectTree)
113 {
114 auto service = ipmi::getService(bus, redundancyIntf, softObject.first);
115 auto objValueTree = ipmi::getManagedObjects(bus, service, softwareRoot);
116
117 auto minPriority = 0xFF;
118 for (const auto& objIter : objValueTree)
119 {
120 try
121 {
122 auto& intfMap = objIter.second;
123 auto& redundancyPriorityProps = intfMap.at(redundancyIntf);
124 auto& versionProps = intfMap.at(versionIntf);
125 auto& activationProps = intfMap.at(activationIntf);
126 auto priority =
127 redundancyPriorityProps.at("Priority").get<uint8_t>();
128 auto purpose = versionProps.at("Purpose").get<std::string>();
129 auto activation =
130 activationProps.at("Activation").get<std::string>();
131 auto version = versionProps.at("Version").get<std::string>();
132 if ((Version::convertVersionPurposeFromString(purpose) ==
133 Version::VersionPurpose::BMC) &&
134 (Activation::convertActivationsFromString(activation) ==
135 Activation::Activations::Active))
136 {
137 if (priority < minPriority)
138 {
139 minPriority = priority;
140 objectFound = true;
141 revision = std::move(version);
142 }
143 }
144 }
145 catch (const std::exception& e)
146 {
147 log<level::ERR>(e.what());
148 }
149 }
150 }
151
152 if (!objectFound)
153 {
154 log<level::ERR>("Could not found an BMC software Object");
155 elog<InternalFailure>();
156 }
157
158 return revision;
159}
160
Alexander Amelkinba19c182018-09-04 15:49:36 +0300161bool getCurrentBmcState()
162{
163 sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()};
164
165 // Get the Inventory object implementing the BMC interface
166 ipmi::DbusObjectInfo bmcObject =
167 ipmi::getDbusObject(bus, bmc_state_interface);
168 auto variant =
169 ipmi::getDbusProperty(bus, bmcObject.second, bmcObject.first,
170 bmc_state_interface, bmc_state_property);
171
172 return variant.is<std::string>() &&
173 BMC::convertBMCStateFromString(variant.get<std::string>()) ==
174 BMC::BMCState::Ready;
175}
176
Adriana Kobylak3a552e12015-10-19 16:11:00 -0500177ipmi_ret_t ipmi_app_set_acpi_power_state(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
Patrick Venture0b02be92018-08-31 11:55:55 -0700178 ipmi_request_t request,
179 ipmi_response_t response,
180 ipmi_data_len_t data_len,
181 ipmi_context_t context)
Chris Austen6caf28b2015-10-13 12:40:40 -0500182{
183 ipmi_ret_t rc = IPMI_CC_OK;
184 *data_len = 0;
185
Aditya Saripalli5fb14602017-11-09 14:46:27 +0530186 log<level::DEBUG>("IPMI SET ACPI STATE Ignoring for now\n");
Chris Austen6caf28b2015-10-13 12:40:40 -0500187 return rc;
188}
189
Chris Austen7303bdc2016-04-17 11:50:54 -0500190typedef struct
191{
192 char major;
193 char minor;
Chris Austen176c9652016-04-30 16:32:17 -0500194 uint16_t d[2];
Chris Austen7303bdc2016-04-17 11:50:54 -0500195} rev_t;
196
Dinesh Chinari2b7e07d2017-11-08 15:38:50 -0600197/* Currently supports the vx.x-x-[-x] and v1.x.x-x-[-x] format. It will */
198/* return -1 if not in those formats, this routine knows how to parse */
Chris Austen7303bdc2016-04-17 11:50:54 -0500199/* version = v0.6-19-gf363f61-dirty */
200/* ^ ^ ^^ ^ */
201/* | | |----------|-- additional details */
202/* | |---------------- Minor */
203/* |------------------ Major */
Dinesh Chinari2b7e07d2017-11-08 15:38:50 -0600204/* and version = v1.99.10-113-g65edf7d-r3-0-g9e4f715 */
205/* ^ ^ ^^ ^ */
206/* | | |--|---------- additional details */
207/* | |---------------- Minor */
208/* |------------------ Major */
Chris Austen7303bdc2016-04-17 11:50:54 -0500209/* Additional details : If the option group exists it will force Auxiliary */
210/* Firmware Revision Information 4th byte to 1 indicating the build was */
211/* derived with additional edits */
Patrick Venture0b02be92018-08-31 11:55:55 -0700212int convert_version(const char* p, rev_t* rev)
Chris Austen7303bdc2016-04-17 11:50:54 -0500213{
Dinesh Chinari2b7e07d2017-11-08 15:38:50 -0600214 std::string s(p);
215 std::string token;
Chris Austen176c9652016-04-30 16:32:17 -0500216 uint16_t commits;
Chris Austen7303bdc2016-04-17 11:50:54 -0500217
Patrick Venture0b02be92018-08-31 11:55:55 -0700218 auto location = s.find_first_of('v');
Dinesh Chinari2b7e07d2017-11-08 15:38:50 -0600219 if (location != std::string::npos)
220 {
Patrick Venture0b02be92018-08-31 11:55:55 -0700221 s = s.substr(location + 1);
Chris Austen176c9652016-04-30 16:32:17 -0500222 }
Chris Austen7303bdc2016-04-17 11:50:54 -0500223
Dinesh Chinari2b7e07d2017-11-08 15:38:50 -0600224 if (!s.empty())
225 {
226 location = s.find_first_of(".");
227 if (location != std::string::npos)
228 {
Patrick Venture0b02be92018-08-31 11:55:55 -0700229 rev->major =
230 static_cast<char>(std::stoi(s.substr(0, location), 0, 16));
231 token = s.substr(location + 1);
Dinesh Chinari2b7e07d2017-11-08 15:38:50 -0600232 }
Chris Austen176c9652016-04-30 16:32:17 -0500233
Dinesh Chinari2b7e07d2017-11-08 15:38:50 -0600234 if (!token.empty())
235 {
236 location = token.find_first_of(".-");
237 if (location != std::string::npos)
238 {
Patrick Ventured2117022018-02-06 08:54:37 -0800239 rev->minor = static_cast<char>(
Patrick Venture0b02be92018-08-31 11:55:55 -0700240 std::stoi(token.substr(0, location), 0, 16));
241 token = token.substr(location + 1);
Dinesh Chinari2b7e07d2017-11-08 15:38:50 -0600242 }
243 }
Chris Austen7303bdc2016-04-17 11:50:54 -0500244
Dinesh Chinari2b7e07d2017-11-08 15:38:50 -0600245 // Capture the number of commits on top of the minor tag.
246 // I'm using BE format like the ipmi spec asked for
247 location = token.find_first_of(".-");
248 if (!token.empty())
249 {
250 commits = std::stoi(token.substr(0, location), 0, 16);
Patrick Venture0b02be92018-08-31 11:55:55 -0700251 rev->d[0] = (commits >> 8) | (commits << 8);
Dinesh Chinari2b7e07d2017-11-08 15:38:50 -0600252
253 // commit number we skip
254 location = token.find_first_of(".-");
255 if (location != std::string::npos)
256 {
Patrick Venture0b02be92018-08-31 11:55:55 -0700257 token = token.substr(location + 1);
Dinesh Chinari2b7e07d2017-11-08 15:38:50 -0600258 }
259 }
Patrick Venture0b02be92018-08-31 11:55:55 -0700260 else
261 {
Dinesh Chinari2b7e07d2017-11-08 15:38:50 -0600262 rev->d[0] = 0;
263 }
264
265 if (location != std::string::npos)
266 {
Patrick Venture0b02be92018-08-31 11:55:55 -0700267 token = token.substr(location + 1);
Dinesh Chinari2b7e07d2017-11-08 15:38:50 -0600268 }
269
270 // Any value of the optional parameter forces it to 1
271 location = token.find_first_of(".-");
272 if (location != std::string::npos)
273 {
Patrick Venture0b02be92018-08-31 11:55:55 -0700274 token = token.substr(location + 1);
Dinesh Chinari2b7e07d2017-11-08 15:38:50 -0600275 }
276 commits = (!token.empty()) ? 1 : 0;
277
Patrick Venture0b02be92018-08-31 11:55:55 -0700278 // We do this operation to get this displayed in least significant bytes
279 // of ipmitool device id command.
280 rev->d[1] = (commits >> 8) | (commits << 8);
Dinesh Chinari2b7e07d2017-11-08 15:38:50 -0600281 }
282
Chris Austen7303bdc2016-04-17 11:50:54 -0500283 return 0;
284}
285
Adriana Kobylak3a552e12015-10-19 16:11:00 -0500286ipmi_ret_t ipmi_app_get_device_id(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
Patrick Venture0b02be92018-08-31 11:55:55 -0700287 ipmi_request_t request,
288 ipmi_response_t response,
289 ipmi_data_len_t data_len,
290 ipmi_context_t context)
Chris Austen6caf28b2015-10-13 12:40:40 -0500291{
292 ipmi_ret_t rc = IPMI_CC_OK;
Nagaraju Goruganti744398d2018-02-20 09:52:00 -0600293 int r = -1;
Chris Austen7303bdc2016-04-17 11:50:54 -0500294 rev_t rev = {0};
David Cobbleya1adb072017-11-21 15:58:13 -0800295 static ipmi_device_id_t dev_id{};
296 static bool dev_id_initialized = false;
297 const char* filename = "/usr/share/ipmi-providers/dev_id.json";
Alexander Amelkinba19c182018-09-04 15:49:36 +0300298 constexpr auto ipmiDevIdStateShift = 7;
299 constexpr auto ipmiDevIdFw1Mask = ~(1 << ipmiDevIdStateShift);
Chris Austen6caf28b2015-10-13 12:40:40 -0500300
301 // Data length
Chris Austen7303bdc2016-04-17 11:50:54 -0500302 *data_len = sizeof(dev_id);
303
David Cobbleya1adb072017-11-21 15:58:13 -0800304 if (!dev_id_initialized)
305 {
Nagaraju Goruganti744398d2018-02-20 09:52:00 -0600306 try
307 {
308 auto version = getActiveSoftwareVersionInfo();
309 r = convert_version(version.c_str(), &rev);
David Cobbleya1adb072017-11-21 15:58:13 -0800310 }
Nagaraju Goruganti744398d2018-02-20 09:52:00 -0600311 catch (const std::exception& e)
312 {
313 log<level::ERR>(e.what());
314 }
Nan Liee0cb902016-07-11 15:38:03 +0800315
Patrick Venture0b02be92018-08-31 11:55:55 -0700316 if (r >= 0)
317 {
Nagaraju Goruganti744398d2018-02-20 09:52:00 -0600318 // bit7 identifies if the device is available
319 // 0=normal operation
320 // 1=device firmware, SDR update,
321 // or self-initialization in progress.
Alexander Amelkinba19c182018-09-04 15:49:36 +0300322 // The availability may change in run time, so mask here
323 // and initialize later.
324 dev_id.fw[0] = rev.major & ipmiDevIdFw1Mask;
Nagaraju Goruganti744398d2018-02-20 09:52:00 -0600325
326 rev.minor = (rev.minor > 99 ? 99 : rev.minor);
327 dev_id.fw[1] = rev.minor % 10 + (rev.minor / 10) * 16;
Patrick Ventureb51bf9c2018-09-10 15:53:14 -0700328 std::memcpy(&dev_id.aux, rev.d, 4);
David Cobbleya1adb072017-11-21 15:58:13 -0800329 }
Nan Liee0cb902016-07-11 15:38:03 +0800330
David Cobbleya1adb072017-11-21 15:58:13 -0800331 // IPMI Spec version 2.0
332 dev_id.ipmi_ver = 2;
Adriana Kobylak0e912642016-06-22 16:54:39 -0500333
David Cobbleya1adb072017-11-21 15:58:13 -0800334 std::ifstream dev_id_file(filename);
335 if (dev_id_file.is_open())
336 {
337 auto data = nlohmann::json::parse(dev_id_file, nullptr, false);
338 if (!data.is_discarded())
339 {
340 dev_id.id = data.value("id", 0);
341 dev_id.revision = data.value("revision", 0);
342 dev_id.addn_dev_support = data.value("addn_dev_support", 0);
343 dev_id.manuf_id[2] = data.value("manuf_id", 0) >> 16;
344 dev_id.manuf_id[1] = data.value("manuf_id", 0) >> 8;
345 dev_id.manuf_id[0] = data.value("manuf_id", 0);
346 dev_id.prod_id[1] = data.value("prod_id", 0) >> 8;
347 dev_id.prod_id[0] = data.value("prod_id", 0);
Tom Josephaf8a0982018-03-09 07:54:02 -0600348 dev_id.aux[3] = data.value("aux", 0);
349 dev_id.aux[2] = data.value("aux", 0) >> 8;
350 dev_id.aux[1] = data.value("aux", 0) >> 16;
351 dev_id.aux[0] = data.value("aux", 0) >> 24;
David Cobbleya1adb072017-11-21 15:58:13 -0800352
Patrick Venture0b02be92018-08-31 11:55:55 -0700353 // Don't read the file every time if successful
David Cobbleya1adb072017-11-21 15:58:13 -0800354 dev_id_initialized = true;
355 }
356 else
357 {
358 log<level::ERR>("Device ID JSON parser failure");
359 rc = IPMI_CC_UNSPECIFIED_ERROR;
360 }
361 }
362 else
363 {
364 log<level::ERR>("Device ID file not found");
365 rc = IPMI_CC_UNSPECIFIED_ERROR;
Chris Austen7303bdc2016-04-17 11:50:54 -0500366 }
367 }
Chris Austen6caf28b2015-10-13 12:40:40 -0500368
Alexander Amelkinba19c182018-09-04 15:49:36 +0300369 // Set availability to the actual current BMC state
370 dev_id.fw[0] &= ipmiDevIdFw1Mask;
371 if (!getCurrentBmcState())
372 {
373 dev_id.fw[0] |= (1 << ipmiDevIdStateShift);
374 }
375
Chris Austen6caf28b2015-10-13 12:40:40 -0500376 // Pack the actual response
Patrick Ventureb51bf9c2018-09-10 15:53:14 -0700377 std::memcpy(response, &dev_id, *data_len);
Nagaraju Goruganti744398d2018-02-20 09:52:00 -0600378
Chris Austen6caf28b2015-10-13 12:40:40 -0500379 return rc;
380}
381
Nan Li41fa24a2016-11-10 20:12:37 +0800382ipmi_ret_t ipmi_app_get_self_test_results(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
Patrick Venture0b02be92018-08-31 11:55:55 -0700383 ipmi_request_t request,
384 ipmi_response_t response,
385 ipmi_data_len_t data_len,
386 ipmi_context_t context)
Nan Li41fa24a2016-11-10 20:12:37 +0800387{
388 ipmi_ret_t rc = IPMI_CC_OK;
389
390 // Byte 2:
391 // 55h - No error.
Gunnar Mills8991dd62017-10-25 17:11:29 -0500392 // 56h - Self Test function not implemented in this controller.
Nan Li41fa24a2016-11-10 20:12:37 +0800393 // 57h - Corrupted or inaccesssible data or devices.
394 // 58h - Fatal hardware error.
395 // FFh - reserved.
396 // all other: Device-specific 'internal failure'.
397 // Byte 3:
398 // For byte 2 = 55h, 56h, FFh: 00h
399 // For byte 2 = 58h, all other: Device-specific
400 // For byte 2 = 57h: self-test error bitfield.
401 // Note: returning 57h does not imply that all test were run.
402 // [7] 1b = Cannot access SEL device.
403 // [6] 1b = Cannot access SDR Repository.
404 // [5] 1b = Cannot access BMC FRU device.
405 // [4] 1b = IPMB signal lines do not respond.
406 // [3] 1b = SDR Repository empty.
407 // [2] 1b = Internal Use Area of BMC FRU corrupted.
408 // [1] 1b = controller update 'boot block' firmware corrupted.
409 // [0] 1b = controller operational firmware corrupted.
410
411 char selftestresults[2] = {0};
412
413 *data_len = 2;
414
415 selftestresults[0] = 0x56;
416 selftestresults[1] = 0;
417
Patrick Ventureb51bf9c2018-09-10 15:53:14 -0700418 std::memcpy(response, selftestresults, *data_len);
Nan Li41fa24a2016-11-10 20:12:37 +0800419
420 return rc;
421}
422
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500423ipmi_ret_t ipmi_app_get_device_guid(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
Patrick Venture0b02be92018-08-31 11:55:55 -0700424 ipmi_request_t request,
425 ipmi_response_t response,
426 ipmi_data_len_t data_len,
427 ipmi_context_t context)
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500428{
Patrick Venture0b02be92018-08-31 11:55:55 -0700429 const char* objname = "/org/openbmc/control/chassis0";
430 const char* iface = "org.freedesktop.DBus.Properties";
431 const char* chassis_iface = "org.openbmc.control.Chassis";
432 sd_bus_message* reply = NULL;
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500433 sd_bus_error error = SD_BUS_ERROR_NULL;
434 int r = 0;
Patrick Venture0b02be92018-08-31 11:55:55 -0700435 char* uuid = NULL;
436 char* busname = NULL;
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500437
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500438 // UUID is in RFC4122 format. Ex: 61a39523-78f2-11e5-9862-e6402cfc3223
Patrick Ventured2117022018-02-06 08:54:37 -0800439 // Per IPMI Spec 2.0 need to convert to 16 hex bytes and reverse the byte
440 // order
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500441 // Ex: 0x2332fc2c40e66298e511f2782395a361
442
Patrick Venture0b02be92018-08-31 11:55:55 -0700443 const int resp_size = 16; // Response is 16 hex bytes per IPMI Spec
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500444 uint8_t resp_uuid[resp_size]; // Array to hold the formatted response
Patrick Ventured2117022018-02-06 08:54:37 -0800445 // Point resp end of array to save in reverse order
Patrick Venture0b02be92018-08-31 11:55:55 -0700446 int resp_loc = resp_size - 1;
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500447 int i = 0;
Patrick Venture0b02be92018-08-31 11:55:55 -0700448 char* tokptr = NULL;
449 char* id_octet = NULL;
vishwa1eaea4f2016-02-26 11:57:40 -0600450
451 // Status code.
452 ipmi_ret_t rc = IPMI_CC_OK;
453 *data_len = 0;
454
vishwa1eaea4f2016-02-26 11:57:40 -0600455 // Call Get properties method with the interface and property name
Sergey Solomineb9b8142016-08-23 09:07:28 -0500456 r = mapper_get_service(bus, objname, &busname);
Patrick Venture0b02be92018-08-31 11:55:55 -0700457 if (r < 0)
458 {
459 log<level::ERR>("Failed to get bus name", entry("BUS=%s", objname),
Aditya Saripalli5fb14602017-11-09 14:46:27 +0530460 entry("ERRNO=0x%X", -r));
Sergey Solomineb9b8142016-08-23 09:07:28 -0500461 goto finish;
462 }
Patrick Venture0b02be92018-08-31 11:55:55 -0700463 r = sd_bus_call_method(bus, busname, objname, iface, "Get", &error, &reply,
464 "ss", chassis_iface, "uuid");
vishwa1eaea4f2016-02-26 11:57:40 -0600465 if (r < 0)
466 {
Patrick Venture0b02be92018-08-31 11:55:55 -0700467 log<level::ERR>("Failed to call Get Method", entry("ERRNO=0x%X", -r));
vishwa1eaea4f2016-02-26 11:57:40 -0600468 rc = IPMI_CC_UNSPECIFIED_ERROR;
469 goto finish;
470 }
471
472 r = sd_bus_message_read(reply, "v", "s", &uuid);
473 if (r < 0 || uuid == NULL)
474 {
Patrick Venture0b02be92018-08-31 11:55:55 -0700475 log<level::ERR>("Failed to get a response", entry("ERRNO=0x%X", -r));
vishwa1eaea4f2016-02-26 11:57:40 -0600476 rc = IPMI_CC_RESPONSE_ERROR;
477 goto finish;
478 }
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500479
480 // Traverse the UUID
Patrick Ventured2117022018-02-06 08:54:37 -0800481 // Get the UUID octects separated by dash
482 id_octet = strtok_r(uuid, "-", &tokptr);
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500483
484 if (id_octet == NULL)
vishwa1eaea4f2016-02-26 11:57:40 -0600485 {
486 // Error
Patrick Venture0b02be92018-08-31 11:55:55 -0700487 log<level::ERR>("Unexpected UUID format", entry("UUID=%s", uuid));
vishwa1eaea4f2016-02-26 11:57:40 -0600488 rc = IPMI_CC_RESPONSE_ERROR;
489 goto finish;
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500490 }
491
492 while (id_octet != NULL)
493 {
494 // Calculate the octet string size since it varies
495 // Divide it by 2 for the array size since 1 byte is built from 2 chars
Patrick Venture0b02be92018-08-31 11:55:55 -0700496 int tmp_size = strlen(id_octet) / 2;
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500497
Patrick Venture0b02be92018-08-31 11:55:55 -0700498 for (i = 0; i < tmp_size; i++)
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500499 {
Patrick Ventured2117022018-02-06 08:54:37 -0800500 // Holder of the 2 chars that will become a byte
501 char tmp_array[3] = {0};
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500502 strncpy(tmp_array, id_octet, 2); // 2 chars at a time
503
504 int resp_byte = strtoul(tmp_array, NULL, 16); // Convert to hex byte
Patrick Ventured2117022018-02-06 08:54:37 -0800505 // Copy end to first
Patrick Ventureb51bf9c2018-09-10 15:53:14 -0700506 std::memcpy((void*)&resp_uuid[resp_loc], &resp_byte, 1);
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500507 resp_loc--;
Patrick Venture0b02be92018-08-31 11:55:55 -0700508 id_octet += 2; // Finished with the 2 chars, advance
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500509 }
Patrick Venture0b02be92018-08-31 11:55:55 -0700510 id_octet = strtok_r(NULL, "-", &tokptr); // Get next octet
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500511 }
512
513 // Data length
514 *data_len = resp_size;
515
516 // Pack the actual response
Patrick Ventureb51bf9c2018-09-10 15:53:14 -0700517 std::memcpy(response, &resp_uuid, *data_len);
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500518
vishwa1eaea4f2016-02-26 11:57:40 -0600519finish:
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500520 sd_bus_error_free(&error);
vishwa1eaea4f2016-02-26 11:57:40 -0600521 reply = sd_bus_message_unref(reply);
Sergey Solomineb9b8142016-08-23 09:07:28 -0500522 free(busname);
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500523
524 return rc;
525}
Chris Austen6caf28b2015-10-13 12:40:40 -0500526
Adriana Kobylak3a552e12015-10-19 16:11:00 -0500527ipmi_ret_t ipmi_app_get_bt_capabilities(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
Patrick Venture0b02be92018-08-31 11:55:55 -0700528 ipmi_request_t request,
529 ipmi_response_t response,
530 ipmi_data_len_t data_len,
531 ipmi_context_t context)
vishwabmcba0bd5f2015-09-30 16:50:23 +0530532{
vishwabmcba0bd5f2015-09-30 16:50:23 +0530533
534 // Status code.
535 ipmi_ret_t rc = IPMI_CC_OK;
536
Adriana Kobylak88ad8152016-12-13 10:09:08 -0600537 // Per IPMI 2.0 spec, the input and output buffer size must be the max
538 // buffer size minus one byte to allocate space for the length byte.
Patrick Venture0b02be92018-08-31 11:55:55 -0700539 uint8_t str[] = {0x01, MAX_IPMI_BUFFER - 1, MAX_IPMI_BUFFER - 1, 0x0A,
540 0x01};
vishwabmcba0bd5f2015-09-30 16:50:23 +0530541
542 // Data length
543 *data_len = sizeof(str);
544
545 // Pack the actual response
Patrick Ventureb51bf9c2018-09-10 15:53:14 -0700546 std::memcpy(response, &str, *data_len);
vishwabmcba0bd5f2015-09-30 16:50:23 +0530547
548 return rc;
549}
550
Adriana Kobylak3a552e12015-10-19 16:11:00 -0500551ipmi_ret_t ipmi_app_wildcard_handler(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
Patrick Venture0b02be92018-08-31 11:55:55 -0700552 ipmi_request_t request,
553 ipmi_response_t response,
554 ipmi_data_len_t data_len,
555 ipmi_context_t context)
vishwabmcba0bd5f2015-09-30 16:50:23 +0530556{
vishwabmcba0bd5f2015-09-30 16:50:23 +0530557 // Status code.
Nan Li70aa8d92016-08-29 00:11:10 +0800558 ipmi_ret_t rc = IPMI_CC_INVALID;
vishwabmcba0bd5f2015-09-30 16:50:23 +0530559
560 *data_len = strlen("THIS IS WILDCARD");
561
562 // Now pack actual response
Patrick Ventureb51bf9c2018-09-10 15:53:14 -0700563 std::memcpy(response, "THIS IS WILDCARD", *data_len);
vishwabmcba0bd5f2015-09-30 16:50:23 +0530564
565 return rc;
566}
567
Marri Devender Rao5e007a52018-01-08 06:18:36 -0600568ipmi_ret_t ipmi_app_get_sys_guid(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
Patrick Venture0b02be92018-08-31 11:55:55 -0700569 ipmi_request_t request,
570 ipmi_response_t response,
571 ipmi_data_len_t data_len,
572 ipmi_context_t context)
Marri Devender Rao5e007a52018-01-08 06:18:36 -0600573
574{
575 ipmi_ret_t rc = IPMI_CC_OK;
576 sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()};
577
578 try
579 {
580 // Get the Inventory object implementing BMC interface
581 ipmi::DbusObjectInfo bmcObject =
582 ipmi::getDbusObject(bus, bmc_interface);
583
584 // Read UUID property value from bmcObject
585 // UUID is in RFC4122 format Ex: 61a39523-78f2-11e5-9862-e6402cfc3223
Patrick Venture0b02be92018-08-31 11:55:55 -0700586 auto variant =
587 ipmi::getDbusProperty(bus, bmcObject.second, bmcObject.first,
588 bmc_guid_interface, bmc_guid_property);
Marri Devender Rao5e007a52018-01-08 06:18:36 -0600589 std::string guidProp = variant.get<std::string>();
590
591 // Erase "-" characters from the property value
592 guidProp.erase(std::remove(guidProp.begin(), guidProp.end(), '-'),
Patrick Venture0b02be92018-08-31 11:55:55 -0700593 guidProp.end());
Marri Devender Rao5e007a52018-01-08 06:18:36 -0600594
595 auto guidPropLen = guidProp.length();
596 // Validate UUID data
597 // Divide by 2 as 1 byte is built from 2 chars
Patrick Venture0b02be92018-08-31 11:55:55 -0700598 if ((guidPropLen <= 0) || ((guidPropLen / 2) != bmc_guid_len))
Marri Devender Rao5e007a52018-01-08 06:18:36 -0600599
600 {
601 log<level::ERR>("Invalid UUID property value",
Patrick Venture0b02be92018-08-31 11:55:55 -0700602 entry("UUID_LENGTH=%d", guidPropLen));
Marri Devender Rao5e007a52018-01-08 06:18:36 -0600603 return IPMI_CC_RESPONSE_ERROR;
604 }
605
606 // Convert data in RFC4122(MSB) format to LSB format
607 // Get 2 characters at a time as 1 byte is built from 2 chars and
608 // convert to hex byte
609 // TODO: Data printed for GUID command is not as per the
610 // GUID format defined in IPMI specification 2.0 section 20.8
611 // Ticket raised: https://sourceforge.net/p/ipmitool/bugs/501/
612 uint8_t respGuid[bmc_guid_len];
613 for (size_t i = 0, respLoc = (bmc_guid_len - 1);
Patrick Venture0b02be92018-08-31 11:55:55 -0700614 i < guidPropLen && respLoc >= 0; i += 2, respLoc--)
Marri Devender Rao5e007a52018-01-08 06:18:36 -0600615 {
616 auto value = static_cast<uint8_t>(
Patrick Venture0b02be92018-08-31 11:55:55 -0700617 std::stoi(guidProp.substr(i, 2).c_str(), NULL, 16));
Marri Devender Rao5e007a52018-01-08 06:18:36 -0600618 respGuid[respLoc] = value;
619 }
620
621 *data_len = bmc_guid_len;
Patrick Ventureb51bf9c2018-09-10 15:53:14 -0700622 std::memcpy(response, &respGuid, bmc_guid_len);
Marri Devender Rao5e007a52018-01-08 06:18:36 -0600623 }
624 catch (const InternalFailure& e)
625 {
626 log<level::ERR>("Failed in reading BMC UUID property",
627 entry("INTERFACE=%s", bmc_interface),
628 entry("PROPERTY_INTERFACE=%s", bmc_guid_interface),
629 entry("PROPERTY=%s", bmc_guid_property));
630 return IPMI_CC_UNSPECIFIED_ERROR;
631 }
632 return rc;
633}
634
Xo Wangf542e8b2017-08-09 15:34:16 -0700635static std::unique_ptr<SysInfoParamStore> sysInfoParamStore;
636
Xo Wang87651332017-08-11 10:17:59 -0700637static std::string sysInfoReadSystemName()
638{
639 // Use the BMC hostname as the "System Name."
640 char hostname[HOST_NAME_MAX + 1] = {};
641 if (gethostname(hostname, HOST_NAME_MAX) != 0)
642 {
643 perror("System info parameter: system name");
644 }
645 return hostname;
646}
647
Xo Wangf542e8b2017-08-09 15:34:16 -0700648struct IpmiSysInfoResp
649{
650 uint8_t paramRevision;
651 uint8_t setSelector;
652 union
653 {
654 struct
655 {
656 uint8_t encoding;
657 uint8_t stringLen;
658 uint8_t stringData0[14];
659 } __attribute__((packed));
660 uint8_t stringDataN[16];
661 uint8_t byteData;
662 };
663} __attribute__((packed));
664
665/**
666 * Split a string into (up to) 16-byte chunks as expected in response for get
667 * system info parameter.
668 *
669 * @param[in] fullString: Input string to be split
670 * @param[in] chunkIndex: Index of the chunk to be written out
671 * @param[in,out] chunk: Output data buffer; must have 14 byte capacity if
672 * chunk_index = 0 and 16-byte capacity otherwise
673 * @return the number of bytes written into the output buffer, or -EINVAL for
674 * invalid arguments.
675 */
676static int splitStringParam(const std::string& fullString, int chunkIndex,
677 uint8_t* chunk)
678{
679 constexpr int maxChunk = 255;
680 constexpr int smallChunk = 14;
681 constexpr int chunkSize = 16;
682 if (chunkIndex > maxChunk || chunk == nullptr)
683 {
684 return -EINVAL;
685 }
686 try
687 {
688 std::string output;
689 if (chunkIndex == 0)
690 {
691 // Output must have 14 byte capacity.
692 output = fullString.substr(0, smallChunk);
693 }
694 else
695 {
696 // Output must have 16 byte capacity.
697 output = fullString.substr((chunkIndex * chunkSize) - 2, chunkSize);
698 }
699
700 std::memcpy(chunk, output.c_str(), output.length());
701 return output.length();
702 }
703 catch (const std::out_of_range& e)
704 {
705 // The position was beyond the end.
706 return -EINVAL;
707 }
708}
709
710/**
711 * Packs the Get Sys Info Request Item into the response.
712 *
713 * @param[in] paramString - the parameter.
714 * @param[in] setSelector - the selector
715 * @param[in,out] resp - the System info response.
716 * @return The number of bytes packed or failure from splitStringParam().
717 */
718static int packGetSysInfoResp(const std::string& paramString,
719 uint8_t setSelector, IpmiSysInfoResp* resp)
720{
721 uint8_t* dataBuffer = resp->stringDataN;
722 resp->setSelector = setSelector;
723 if (resp->setSelector == 0) // First chunk has only 14 bytes.
724 {
725 resp->encoding = 0;
726 resp->stringLen = paramString.length();
727 dataBuffer = resp->stringData0;
728 }
729 return splitStringParam(paramString, resp->setSelector, dataBuffer);
730}
731
732ipmi_ret_t ipmi_app_get_system_info(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
733 ipmi_request_t request,
734 ipmi_response_t response,
735 ipmi_data_len_t dataLen,
736 ipmi_context_t context)
737{
738 IpmiSysInfoResp resp = {};
739 size_t respLen = 0;
740 uint8_t* const reqData = static_cast<uint8_t*>(request);
741 std::string paramString;
742 bool found;
743 std::tuple<bool, std::string> ret;
744 constexpr int minRequestSize = 4;
745 constexpr int paramSelector = 1;
746 constexpr uint8_t revisionOnly = 0x80;
747 const uint8_t paramRequested = reqData[paramSelector];
748 int rc;
749
750 if (*dataLen < minRequestSize)
751 {
752 return IPMI_CC_REQ_DATA_LEN_INVALID;
753 }
754
755 *dataLen = 0; // default to 0.
756
757 // Parameters revision as of IPMI spec v2.0 rev. 1.1 (Feb 11, 2014 E6)
758 resp.paramRevision = 0x11;
759 if (reqData[0] & revisionOnly) // Get parameter revision only
760 {
761 respLen = 1;
762 goto writeResponse;
763 }
764
765 // The "Set In Progress" parameter can be used for rollback of parameter
766 // data and is not implemented.
767 if (paramRequested == 0)
768 {
769 resp.byteData = 0;
770 respLen = 2;
771 goto writeResponse;
772 }
773
774 if (sysInfoParamStore == nullptr)
775 {
776 sysInfoParamStore = std::make_unique<SysInfoParamStore>();
Xo Wang87651332017-08-11 10:17:59 -0700777 sysInfoParamStore->update(IPMI_SYSINFO_SYSTEM_NAME,
778 sysInfoReadSystemName);
Xo Wangf542e8b2017-08-09 15:34:16 -0700779 }
780
781 // Parameters other than Set In Progress are assumed to be strings.
782 ret = sysInfoParamStore->lookup(paramRequested);
783 found = std::get<0>(ret);
784 paramString = std::get<1>(ret);
785 if (!found)
786 {
787 return IPMI_CC_SYSTEM_INFO_PARAMETER_NOT_SUPPORTED;
788 }
789 // TODO: Cache each parameter across multiple calls, until the whole string
790 // has been read out. Otherwise, it's possible for a parameter to change
791 // between requests for its chunks, returning chunks incoherent with each
792 // other. For now, the parameter store is simply required to have only
793 // idempotent callbacks.
794 rc = packGetSysInfoResp(paramString, reqData[2], &resp);
795 if (rc == -EINVAL)
796 {
797 return IPMI_CC_RESPONSE_ERROR;
798 }
799
800 respLen = sizeof(resp); // Write entire string data chunk in response.
801
802writeResponse:
803 std::memcpy(response, &resp, sizeof(resp));
804 *dataLen = respLen;
805 return IPMI_CC_OK;
806}
807
Chris Austen6caf28b2015-10-13 12:40:40 -0500808void register_netfn_app_functions()
vishwabmcba0bd5f2015-09-30 16:50:23 +0530809{
Tom05732372016-09-06 17:21:23 +0530810 // <Get BT Interface Capabilities>
Patrick Venture0b02be92018-08-31 11:55:55 -0700811 ipmi_register_callback(NETFUN_APP, IPMI_CMD_GET_CAP_BIT, NULL,
812 ipmi_app_get_bt_capabilities, PRIVILEGE_USER);
Chris Austen6caf28b2015-10-13 12:40:40 -0500813
Tom05732372016-09-06 17:21:23 +0530814 // <Wildcard Command>
Patrick Venture0b02be92018-08-31 11:55:55 -0700815 ipmi_register_callback(NETFUN_APP, IPMI_CMD_WILDCARD, NULL,
816 ipmi_app_wildcard_handler, PRIVILEGE_USER);
Chris Austen6caf28b2015-10-13 12:40:40 -0500817
Tom05732372016-09-06 17:21:23 +0530818 // <Reset Watchdog Timer>
Patrick Venture0b02be92018-08-31 11:55:55 -0700819 ipmi_register_callback(NETFUN_APP, IPMI_CMD_RESET_WD, NULL,
820 ipmi_app_watchdog_reset, PRIVILEGE_OPERATOR);
Chris Austen6caf28b2015-10-13 12:40:40 -0500821
Tom05732372016-09-06 17:21:23 +0530822 // <Set Watchdog Timer>
Patrick Venture0b02be92018-08-31 11:55:55 -0700823 ipmi_register_callback(NETFUN_APP, IPMI_CMD_SET_WD, NULL,
824 ipmi_app_watchdog_set, PRIVILEGE_OPERATOR);
Chris Austen6caf28b2015-10-13 12:40:40 -0500825
William A. Kennington III73f44512018-02-09 15:28:46 -0800826 // <Get Watchdog Timer>
Patrick Venture0b02be92018-08-31 11:55:55 -0700827 ipmi_register_callback(NETFUN_APP, IPMI_CMD_GET_WD, NULL,
828 ipmi_app_watchdog_get, PRIVILEGE_OPERATOR);
William A. Kennington III73f44512018-02-09 15:28:46 -0800829
Tom05732372016-09-06 17:21:23 +0530830 // <Get Device ID>
Patrick Venture0b02be92018-08-31 11:55:55 -0700831 ipmi_register_callback(NETFUN_APP, IPMI_CMD_GET_DEVICE_ID, NULL,
832 ipmi_app_get_device_id, PRIVILEGE_USER);
Chris Austen6caf28b2015-10-13 12:40:40 -0500833
Tom05732372016-09-06 17:21:23 +0530834 // <Get Self Test Results>
Patrick Venture0b02be92018-08-31 11:55:55 -0700835 ipmi_register_callback(NETFUN_APP, IPMI_CMD_GET_SELF_TEST_RESULTS, NULL,
836 ipmi_app_get_self_test_results, PRIVILEGE_USER);
Nan Li41fa24a2016-11-10 20:12:37 +0800837
Tom05732372016-09-06 17:21:23 +0530838 // <Get Device GUID>
Patrick Venture0b02be92018-08-31 11:55:55 -0700839 ipmi_register_callback(NETFUN_APP, IPMI_CMD_GET_DEVICE_GUID, NULL,
840 ipmi_app_get_device_guid, PRIVILEGE_USER);
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500841
Tom05732372016-09-06 17:21:23 +0530842 // <Set ACPI Power State>
Patrick Venture0b02be92018-08-31 11:55:55 -0700843 ipmi_register_callback(NETFUN_APP, IPMI_CMD_SET_ACPI, NULL,
844 ipmi_app_set_acpi_power_state, PRIVILEGE_ADMIN);
Chris Austen6caf28b2015-10-13 12:40:40 -0500845
Tom Joseph69fabfe2017-08-04 10:15:01 +0530846 // <Get Channel Access>
Patrick Venture0b02be92018-08-31 11:55:55 -0700847 ipmi_register_callback(NETFUN_APP, IPMI_CMD_GET_CHANNEL_ACCESS, NULL,
848 ipmi_get_channel_access, PRIVILEGE_USER);
Tom Joseph69fabfe2017-08-04 10:15:01 +0530849
Tom05732372016-09-06 17:21:23 +0530850 // <Get Channel Info Command>
Patrick Venture0b02be92018-08-31 11:55:55 -0700851 ipmi_register_callback(NETFUN_APP, IPMI_CMD_GET_CHAN_INFO, NULL,
852 ipmi_app_channel_info, PRIVILEGE_USER);
Chris Austenc2cd29d2016-02-05 20:02:29 -0600853
Marri Devender Rao5e007a52018-01-08 06:18:36 -0600854 // <Get System GUID Command>
Patrick Venture0b02be92018-08-31 11:55:55 -0700855 ipmi_register_callback(NETFUN_APP, IPMI_CMD_GET_SYS_GUID, NULL,
856 ipmi_app_get_sys_guid, PRIVILEGE_USER);
Tom Joseph7cbe2282018-03-21 21:17:33 +0530857
858 // <Get Channel Cipher Suites Command>
Patrick Venture0b02be92018-08-31 11:55:55 -0700859 ipmi_register_callback(NETFUN_APP, IPMI_CMD_GET_CHAN_CIPHER_SUITES, NULL,
860 getChannelCipherSuites, PRIVILEGE_CALLBACK);
Tom Joseph13227682018-08-10 01:05:21 +0530861
862 // <Set Channel Access Command>
863 ipmi_register_callback(NETFUN_APP, IPMI_CMD_SET_CHAN_ACCESS, NULL,
864 ipmi_set_channel_access, PRIVILEGE_ADMIN);
Xo Wangf542e8b2017-08-09 15:34:16 -0700865
866 // <Get System Info Command>
867 ipmi_register_callback(NETFUN_APP, IPMI_CMD_GET_SYSTEM_INFO, NULL,
868 ipmi_app_get_system_info, PRIVILEGE_USER);
vishwabmcba0bd5f2015-09-30 16:50:23 +0530869 return;
870}