blob: 20104fee232885c958087507b289d1f060628f79 [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"
Xo Wangf542e8b2017-08-09 15:34:16 -07005#include "sys_info_param.hpp"
6#include "transporthandler.hpp"
Xo Wangf542e8b2017-08-09 15:34:16 -07007
Patrick Venture0b02be92018-08-31 11:55:55 -07008#include <arpa/inet.h>
William A. Kennington III194375f2018-12-14 02:14:33 -08009#include <ipmid/api.h>
Xo Wang87651332017-08-11 10:17:59 -070010#include <limits.h>
Patrick Venture0b02be92018-08-31 11:55:55 -070011#include <mapper.h>
Patrick Venture0b02be92018-08-31 11:55:55 -070012#include <systemd/sd-bus.h>
Xo Wang87651332017-08-11 10:17:59 -070013#include <unistd.h>
Patrick Venture0b02be92018-08-31 11:55:55 -070014
Patrick Venture3a5071a2018-09-12 13:27:42 -070015#include <algorithm>
16#include <array>
17#include <cstddef>
Vernon Mauerybdda8002019-02-26 10:18:51 -080018#include <filesystem>
Patrick Venture3a5071a2018-09-12 13:27:42 -070019#include <fstream>
Vernon Mauery33250242019-03-12 16:49:26 -070020#include <ipmid/types.hpp>
Vernon Mauery6a98fe72019-03-11 15:57:48 -070021#include <ipmid/utils.hpp>
Patrick Venture3a5071a2018-09-12 13:27:42 -070022#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>
William A. Kennington III4c008022018-10-12 17:18:14 -070026#include <sdbusplus/message/types.hpp>
Patrick Venture3a5071a2018-09-12 13:27:42 -070027#include <string>
28#include <tuple>
29#include <vector>
30#include <xyz/openbmc_project/Common/error.hpp>
Yong Li18d77262018-10-09 01:59:45 +080031#include <xyz/openbmc_project/Control/Power/ACPIPowerState/server.hpp>
Patrick Venture3a5071a2018-09-12 13:27:42 -070032#include <xyz/openbmc_project/Software/Activation/server.hpp>
33#include <xyz/openbmc_project/Software/Version/server.hpp>
34#include <xyz/openbmc_project/State/BMC/server.hpp>
Ratan Guptab8e99552017-07-27 07:07:48 +053035
Patrick Venture0b02be92018-08-31 11:55:55 -070036extern sd_bus* bus;
vishwabmcba0bd5f2015-09-30 16:50:23 +053037
Alexander Amelkinba19c182018-09-04 15:49:36 +030038constexpr auto bmc_state_interface = "xyz.openbmc_project.State.BMC";
39constexpr auto bmc_state_property = "CurrentBMCState";
Marri Devender Rao5e007a52018-01-08 06:18:36 -060040constexpr auto bmc_interface = "xyz.openbmc_project.Inventory.Item.Bmc";
41constexpr auto bmc_guid_interface = "xyz.openbmc_project.Common.UUID";
42constexpr auto bmc_guid_property = "UUID";
43constexpr auto bmc_guid_len = 16;
44
Nagaraju Goruganti744398d2018-02-20 09:52:00 -060045static constexpr auto redundancyIntf =
46 "xyz.openbmc_project.Software.RedundancyPriority";
Patrick Venture0b02be92018-08-31 11:55:55 -070047static constexpr auto versionIntf = "xyz.openbmc_project.Software.Version";
Nagaraju Goruganti744398d2018-02-20 09:52:00 -060048static constexpr auto activationIntf =
49 "xyz.openbmc_project.Software.Activation";
50static constexpr auto softwareRoot = "/xyz/openbmc_project/software";
51
Chris Austen6caf28b2015-10-13 12:40:40 -050052void register_netfn_app_functions() __attribute__((constructor));
vishwabmcba0bd5f2015-09-30 16:50:23 +053053
Ratan Guptab8e99552017-07-27 07:07:48 +053054using namespace phosphor::logging;
55using namespace sdbusplus::xyz::openbmc_project::Common::Error;
Nagaraju Goruganti744398d2018-02-20 09:52:00 -060056using Version = sdbusplus::xyz::openbmc_project::Software::server::Version;
57using Activation =
58 sdbusplus::xyz::openbmc_project::Software::server::Activation;
Alexander Amelkinba19c182018-09-04 15:49:36 +030059using BMC = sdbusplus::xyz::openbmc_project::State::server::BMC;
Vernon Mauery185b9f82018-07-20 10:52:36 -070060namespace fs = std::filesystem;
William A. Kennington III4c008022018-10-12 17:18:14 -070061namespace variant_ns = sdbusplus::message::variant_ns;
Ratan Guptab8e99552017-07-27 07:07:48 +053062
Nan Liee0cb902016-07-11 15:38:03 +080063// Offset in get device id command.
64typedef struct
65{
Patrick Venture0b02be92018-08-31 11:55:55 -070066 uint8_t id;
67 uint8_t revision;
68 uint8_t fw[2];
69 uint8_t ipmi_ver;
70 uint8_t addn_dev_support;
71 uint8_t manuf_id[3];
72 uint8_t prod_id[2];
73 uint8_t aux[4];
74} __attribute__((packed)) ipmi_device_id_t;
Chris Austen7303bdc2016-04-17 11:50:54 -050075
Nagaraju Goruganti744398d2018-02-20 09:52:00 -060076/**
77 * @brief Returns the Version info from primary s/w object
78 *
79 * Get the Version info from the active s/w object which is having high
80 * "Priority" value(a smaller number is a higher priority) and "Purpose"
81 * is "BMC" from the list of all s/w objects those are implementing
82 * RedundancyPriority interface from the given softwareRoot path.
83 *
84 * @return On success returns the Version info from primary s/w object.
85 *
86 */
87std::string getActiveSoftwareVersionInfo()
88{
89 sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()};
90
91 std::string revision{};
Patrick Venture0b02be92018-08-31 11:55:55 -070092 auto objectTree =
93 ipmi::getAllDbusObjects(bus, softwareRoot, redundancyIntf, "");
Nagaraju Goruganti744398d2018-02-20 09:52:00 -060094 if (objectTree.empty())
95 {
96 log<level::ERR>("No Obj has implemented the s/w redundancy interface",
97 entry("INTERFACE=%s", redundancyIntf));
98 elog<InternalFailure>();
99 }
100
101 auto objectFound = false;
102 for (auto& softObject : objectTree)
103 {
104 auto service = ipmi::getService(bus, redundancyIntf, softObject.first);
105 auto objValueTree = ipmi::getManagedObjects(bus, service, softwareRoot);
106
107 auto minPriority = 0xFF;
108 for (const auto& objIter : objValueTree)
109 {
110 try
111 {
112 auto& intfMap = objIter.second;
113 auto& redundancyPriorityProps = intfMap.at(redundancyIntf);
114 auto& versionProps = intfMap.at(versionIntf);
115 auto& activationProps = intfMap.at(activationIntf);
William A. Kennington III4c008022018-10-12 17:18:14 -0700116 auto priority = variant_ns::get<uint8_t>(
117 redundancyPriorityProps.at("Priority"));
118 auto purpose =
119 variant_ns::get<std::string>(versionProps.at("Purpose"));
120 auto activation = variant_ns::get<std::string>(
121 activationProps.at("Activation"));
122 auto version =
123 variant_ns::get<std::string>(versionProps.at("Version"));
Nagaraju Goruganti744398d2018-02-20 09:52:00 -0600124 if ((Version::convertVersionPurposeFromString(purpose) ==
125 Version::VersionPurpose::BMC) &&
126 (Activation::convertActivationsFromString(activation) ==
127 Activation::Activations::Active))
128 {
129 if (priority < minPriority)
130 {
131 minPriority = priority;
132 objectFound = true;
133 revision = std::move(version);
134 }
135 }
136 }
137 catch (const std::exception& e)
138 {
139 log<level::ERR>(e.what());
140 }
141 }
142 }
143
144 if (!objectFound)
145 {
146 log<level::ERR>("Could not found an BMC software Object");
147 elog<InternalFailure>();
148 }
149
150 return revision;
151}
152
Alexander Amelkinba19c182018-09-04 15:49:36 +0300153bool getCurrentBmcState()
154{
155 sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()};
156
157 // Get the Inventory object implementing the BMC interface
158 ipmi::DbusObjectInfo bmcObject =
159 ipmi::getDbusObject(bus, bmc_state_interface);
160 auto variant =
161 ipmi::getDbusProperty(bus, bmcObject.second, bmcObject.first,
162 bmc_state_interface, bmc_state_property);
163
William A. Kennington III4c008022018-10-12 17:18:14 -0700164 return variant_ns::holds_alternative<std::string>(variant) &&
165 BMC::convertBMCStateFromString(
166 variant_ns::get<std::string>(variant)) == BMC::BMCState::Ready;
Alexander Amelkinba19c182018-09-04 15:49:36 +0300167}
168
Yong Li18d77262018-10-09 01:59:45 +0800169namespace acpi_state
170{
171using namespace sdbusplus::xyz::openbmc_project::Control::Power::server;
172
173const static constexpr char* acpiObjPath =
174 "/xyz/openbmc_project/control/host0/acpi_power_state";
175const static constexpr char* acpiInterface =
176 "xyz.openbmc_project.Control.Power.ACPIPowerState";
177const static constexpr char* sysACPIProp = "SysACPIStatus";
178const static constexpr char* devACPIProp = "DevACPIStatus";
179
180enum class PowerStateType : uint8_t
181{
182 sysPowerState = 0x00,
183 devPowerState = 0x01,
184};
185
186// Defined in 20.6 of ipmi doc
187enum class PowerState : uint8_t
188{
189 s0G0D0 = 0x00,
190 s1D1 = 0x01,
191 s2D2 = 0x02,
192 s3D3 = 0x03,
193 s4 = 0x04,
194 s5G2 = 0x05,
195 s4S5 = 0x06,
196 g3 = 0x07,
197 sleep = 0x08,
198 g1Sleep = 0x09,
199 override = 0x0a,
200 legacyOn = 0x20,
201 legacyOff = 0x21,
202 unknown = 0x2a,
203 noChange = 0x7f,
204};
205
206static constexpr uint8_t stateChanged = 0x80;
207
208struct ACPIState
209{
210 uint8_t sysACPIState;
211 uint8_t devACPIState;
212} __attribute__((packed));
213
214std::map<ACPIPowerState::ACPI, PowerState> dbusToIPMI = {
215 {ACPIPowerState::ACPI::S0_G0_D0, PowerState::s0G0D0},
216 {ACPIPowerState::ACPI::S1_D1, PowerState::s1D1},
217 {ACPIPowerState::ACPI::S2_D2, PowerState::s2D2},
218 {ACPIPowerState::ACPI::S3_D3, PowerState::s3D3},
219 {ACPIPowerState::ACPI::S4, PowerState::s4},
220 {ACPIPowerState::ACPI::S5_G2, PowerState::s5G2},
221 {ACPIPowerState::ACPI::S4_S5, PowerState::s4S5},
222 {ACPIPowerState::ACPI::G3, PowerState::g3},
223 {ACPIPowerState::ACPI::SLEEP, PowerState::sleep},
224 {ACPIPowerState::ACPI::G1_SLEEP, PowerState::g1Sleep},
225 {ACPIPowerState::ACPI::OVERRIDE, PowerState::override},
226 {ACPIPowerState::ACPI::LEGACY_ON, PowerState::legacyOn},
227 {ACPIPowerState::ACPI::LEGACY_OFF, PowerState::legacyOff},
228 {ACPIPowerState::ACPI::Unknown, PowerState::unknown}};
229
230bool isValidACPIState(acpi_state::PowerStateType type, uint8_t state)
231{
232 if (type == acpi_state::PowerStateType::sysPowerState)
233 {
234 if ((state <= static_cast<uint8_t>(acpi_state::PowerState::override)) ||
235 (state == static_cast<uint8_t>(acpi_state::PowerState::legacyOn)) ||
236 (state ==
237 static_cast<uint8_t>(acpi_state::PowerState::legacyOff)) ||
238 (state == static_cast<uint8_t>(acpi_state::PowerState::unknown)) ||
239 (state == static_cast<uint8_t>(acpi_state::PowerState::noChange)))
240 {
241 return true;
242 }
243 else
244 {
245 return false;
246 }
247 }
248 else if (type == acpi_state::PowerStateType::devPowerState)
249 {
250 if ((state <= static_cast<uint8_t>(acpi_state::PowerState::s3D3)) ||
251 (state == static_cast<uint8_t>(acpi_state::PowerState::unknown)) ||
252 (state == static_cast<uint8_t>(acpi_state::PowerState::noChange)))
253 {
254 return true;
255 }
256 else
257 {
258 return false;
259 }
260 }
261 else
262 {
263 return false;
264 }
265 return false;
266}
267} // namespace acpi_state
268
Adriana Kobylak3a552e12015-10-19 16:11:00 -0500269ipmi_ret_t ipmi_app_set_acpi_power_state(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
Patrick Venture0b02be92018-08-31 11:55:55 -0700270 ipmi_request_t request,
271 ipmi_response_t response,
272 ipmi_data_len_t data_len,
273 ipmi_context_t context)
Chris Austen6caf28b2015-10-13 12:40:40 -0500274{
Yong Li18d77262018-10-09 01:59:45 +0800275 auto s = static_cast<uint8_t>(acpi_state::PowerState::unknown);
Chris Austen6caf28b2015-10-13 12:40:40 -0500276 ipmi_ret_t rc = IPMI_CC_OK;
Yong Li18d77262018-10-09 01:59:45 +0800277
278 sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()};
279
280 auto value = acpi_state::ACPIPowerState::ACPI::Unknown;
281
282 auto* req = reinterpret_cast<acpi_state::ACPIState*>(request);
283
284 if (*data_len != sizeof(acpi_state::ACPIState))
285 {
286 log<level::ERR>("set_acpi invalid len");
287 *data_len = 0;
288 return IPMI_CC_REQ_DATA_LEN_INVALID;
289 }
290
Chris Austen6caf28b2015-10-13 12:40:40 -0500291 *data_len = 0;
292
Yong Li18d77262018-10-09 01:59:45 +0800293 if (req->sysACPIState & acpi_state::stateChanged)
294 {
295 // set system power state
296 s = req->sysACPIState & ~acpi_state::stateChanged;
297
298 if (!acpi_state::isValidACPIState(
299 acpi_state::PowerStateType::sysPowerState, s))
300 {
301 log<level::ERR>("set_acpi_power sys invalid input",
302 entry("S=%x", s));
303 return IPMI_CC_PARM_OUT_OF_RANGE;
304 }
305
306 // valid input
307 if (s == static_cast<uint8_t>(acpi_state::PowerState::noChange))
308 {
309 log<level::DEBUG>("No change for system power state");
310 }
311 else
312 {
313 auto found = std::find_if(
314 acpi_state::dbusToIPMI.begin(), acpi_state::dbusToIPMI.end(),
315 [&s](const auto& iter) {
316 return (static_cast<uint8_t>(iter.second) == s);
317 });
318
319 value = found->first;
320
321 try
322 {
323 auto acpiObject =
324 ipmi::getDbusObject(bus, acpi_state::acpiInterface);
325 ipmi::setDbusProperty(bus, acpiObject.second, acpiObject.first,
326 acpi_state::acpiInterface,
327 acpi_state::sysACPIProp,
328 convertForMessage(value));
329 }
330 catch (const InternalFailure& e)
331 {
332 log<level::ERR>("Failed in set ACPI system property",
333 entry("EXCEPTION=%s", e.what()));
334 return IPMI_CC_UNSPECIFIED_ERROR;
335 }
336 }
337 }
338 else
339 {
340 log<level::DEBUG>("Do not change system power state");
341 }
342
343 if (req->devACPIState & acpi_state::stateChanged)
344 {
345 // set device power state
346 s = req->devACPIState & ~acpi_state::stateChanged;
347 if (!acpi_state::isValidACPIState(
348 acpi_state::PowerStateType::devPowerState, s))
349 {
350 log<level::ERR>("set_acpi_power dev invalid input",
351 entry("S=%x", s));
352 return IPMI_CC_PARM_OUT_OF_RANGE;
353 }
354
355 // valid input
356 if (s == static_cast<uint8_t>(acpi_state::PowerState::noChange))
357 {
358 log<level::DEBUG>("No change for device power state");
359 }
360 else
361 {
362 auto found = std::find_if(
363 acpi_state::dbusToIPMI.begin(), acpi_state::dbusToIPMI.end(),
364 [&s](const auto& iter) {
365 return (static_cast<uint8_t>(iter.second) == s);
366 });
367
368 value = found->first;
369
370 try
371 {
372 auto acpiObject =
373 ipmi::getDbusObject(bus, acpi_state::acpiInterface);
374 ipmi::setDbusProperty(bus, acpiObject.second, acpiObject.first,
375 acpi_state::acpiInterface,
376 acpi_state::devACPIProp,
377 convertForMessage(value));
378 }
379 catch (const InternalFailure& e)
380 {
381 log<level::ERR>("Failed in set ACPI device property",
382 entry("EXCEPTION=%s", e.what()));
383 return IPMI_CC_UNSPECIFIED_ERROR;
384 }
385 }
386 }
387 else
388 {
389 log<level::DEBUG>("Do not change device power state");
390 }
391
392 return rc;
393}
394
395ipmi_ret_t ipmi_app_get_acpi_power_state(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
396 ipmi_request_t request,
397 ipmi_response_t response,
398 ipmi_data_len_t data_len,
399 ipmi_context_t context)
400{
401 ipmi_ret_t rc = IPMI_CC_OK;
402
403 auto* res = reinterpret_cast<acpi_state::ACPIState*>(response);
404
405 sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()};
406
407 *data_len = 0;
408
409 try
410 {
411 auto acpiObject = ipmi::getDbusObject(bus, acpi_state::acpiInterface);
412
413 auto sysACPIVal = ipmi::getDbusProperty(
414 bus, acpiObject.second, acpiObject.first, acpi_state::acpiInterface,
415 acpi_state::sysACPIProp);
416 auto sysACPI = acpi_state::ACPIPowerState::convertACPIFromString(
William A. Kennington IIIdfad4862018-11-19 17:45:35 -0800417 variant_ns::get<std::string>(sysACPIVal));
Yong Li18d77262018-10-09 01:59:45 +0800418 res->sysACPIState =
419 static_cast<uint8_t>(acpi_state::dbusToIPMI.at(sysACPI));
420
421 auto devACPIVal = ipmi::getDbusProperty(
422 bus, acpiObject.second, acpiObject.first, acpi_state::acpiInterface,
423 acpi_state::devACPIProp);
424 auto devACPI = acpi_state::ACPIPowerState::convertACPIFromString(
William A. Kennington IIIdfad4862018-11-19 17:45:35 -0800425 variant_ns::get<std::string>(devACPIVal));
Yong Li18d77262018-10-09 01:59:45 +0800426 res->devACPIState =
427 static_cast<uint8_t>(acpi_state::dbusToIPMI.at(devACPI));
428
429 *data_len = sizeof(acpi_state::ACPIState);
430 }
431 catch (const InternalFailure& e)
432 {
433 log<level::ERR>("Failed in get ACPI property");
434 return IPMI_CC_UNSPECIFIED_ERROR;
435 }
Chris Austen6caf28b2015-10-13 12:40:40 -0500436 return rc;
437}
438
Chris Austen7303bdc2016-04-17 11:50:54 -0500439typedef struct
440{
441 char major;
442 char minor;
Chris Austen176c9652016-04-30 16:32:17 -0500443 uint16_t d[2];
Chris Austen7303bdc2016-04-17 11:50:54 -0500444} rev_t;
445
Dinesh Chinari2b7e07d2017-11-08 15:38:50 -0600446/* Currently supports the vx.x-x-[-x] and v1.x.x-x-[-x] format. It will */
447/* return -1 if not in those formats, this routine knows how to parse */
Chris Austen7303bdc2016-04-17 11:50:54 -0500448/* version = v0.6-19-gf363f61-dirty */
449/* ^ ^ ^^ ^ */
450/* | | |----------|-- additional details */
451/* | |---------------- Minor */
452/* |------------------ Major */
Dinesh Chinari2b7e07d2017-11-08 15:38:50 -0600453/* and version = v1.99.10-113-g65edf7d-r3-0-g9e4f715 */
454/* ^ ^ ^^ ^ */
455/* | | |--|---------- additional details */
456/* | |---------------- Minor */
457/* |------------------ Major */
Chris Austen7303bdc2016-04-17 11:50:54 -0500458/* Additional details : If the option group exists it will force Auxiliary */
459/* Firmware Revision Information 4th byte to 1 indicating the build was */
460/* derived with additional edits */
Patrick Venture0b02be92018-08-31 11:55:55 -0700461int convert_version(const char* p, rev_t* rev)
Chris Austen7303bdc2016-04-17 11:50:54 -0500462{
Dinesh Chinari2b7e07d2017-11-08 15:38:50 -0600463 std::string s(p);
464 std::string token;
Chris Austen176c9652016-04-30 16:32:17 -0500465 uint16_t commits;
Chris Austen7303bdc2016-04-17 11:50:54 -0500466
Patrick Venture0b02be92018-08-31 11:55:55 -0700467 auto location = s.find_first_of('v');
Dinesh Chinari2b7e07d2017-11-08 15:38:50 -0600468 if (location != std::string::npos)
469 {
Patrick Venture0b02be92018-08-31 11:55:55 -0700470 s = s.substr(location + 1);
Chris Austen176c9652016-04-30 16:32:17 -0500471 }
Chris Austen7303bdc2016-04-17 11:50:54 -0500472
Dinesh Chinari2b7e07d2017-11-08 15:38:50 -0600473 if (!s.empty())
474 {
475 location = s.find_first_of(".");
476 if (location != std::string::npos)
477 {
Patrick Venture0b02be92018-08-31 11:55:55 -0700478 rev->major =
479 static_cast<char>(std::stoi(s.substr(0, location), 0, 16));
480 token = s.substr(location + 1);
Dinesh Chinari2b7e07d2017-11-08 15:38:50 -0600481 }
Chris Austen176c9652016-04-30 16:32:17 -0500482
Dinesh Chinari2b7e07d2017-11-08 15:38:50 -0600483 if (!token.empty())
484 {
485 location = token.find_first_of(".-");
486 if (location != std::string::npos)
487 {
Patrick Ventured2117022018-02-06 08:54:37 -0800488 rev->minor = static_cast<char>(
Patrick Venture0b02be92018-08-31 11:55:55 -0700489 std::stoi(token.substr(0, location), 0, 16));
490 token = token.substr(location + 1);
Dinesh Chinari2b7e07d2017-11-08 15:38:50 -0600491 }
492 }
Chris Austen7303bdc2016-04-17 11:50:54 -0500493
Dinesh Chinari2b7e07d2017-11-08 15:38:50 -0600494 // Capture the number of commits on top of the minor tag.
495 // I'm using BE format like the ipmi spec asked for
496 location = token.find_first_of(".-");
497 if (!token.empty())
498 {
499 commits = std::stoi(token.substr(0, location), 0, 16);
Patrick Venture0b02be92018-08-31 11:55:55 -0700500 rev->d[0] = (commits >> 8) | (commits << 8);
Dinesh Chinari2b7e07d2017-11-08 15:38:50 -0600501
502 // commit number we skip
503 location = token.find_first_of(".-");
504 if (location != std::string::npos)
505 {
Patrick Venture0b02be92018-08-31 11:55:55 -0700506 token = token.substr(location + 1);
Dinesh Chinari2b7e07d2017-11-08 15:38:50 -0600507 }
508 }
Patrick Venture0b02be92018-08-31 11:55:55 -0700509 else
510 {
Dinesh Chinari2b7e07d2017-11-08 15:38:50 -0600511 rev->d[0] = 0;
512 }
513
514 if (location != std::string::npos)
515 {
Patrick Venture0b02be92018-08-31 11:55:55 -0700516 token = token.substr(location + 1);
Dinesh Chinari2b7e07d2017-11-08 15:38:50 -0600517 }
518
519 // Any value of the optional parameter forces it to 1
520 location = token.find_first_of(".-");
521 if (location != std::string::npos)
522 {
Patrick Venture0b02be92018-08-31 11:55:55 -0700523 token = token.substr(location + 1);
Dinesh Chinari2b7e07d2017-11-08 15:38:50 -0600524 }
525 commits = (!token.empty()) ? 1 : 0;
526
Patrick Venture0b02be92018-08-31 11:55:55 -0700527 // We do this operation to get this displayed in least significant bytes
528 // of ipmitool device id command.
529 rev->d[1] = (commits >> 8) | (commits << 8);
Dinesh Chinari2b7e07d2017-11-08 15:38:50 -0600530 }
531
Chris Austen7303bdc2016-04-17 11:50:54 -0500532 return 0;
533}
534
Adriana Kobylak3a552e12015-10-19 16:11:00 -0500535ipmi_ret_t ipmi_app_get_device_id(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
Patrick Venture0b02be92018-08-31 11:55:55 -0700536 ipmi_request_t request,
537 ipmi_response_t response,
538 ipmi_data_len_t data_len,
539 ipmi_context_t context)
Chris Austen6caf28b2015-10-13 12:40:40 -0500540{
541 ipmi_ret_t rc = IPMI_CC_OK;
Nagaraju Goruganti744398d2018-02-20 09:52:00 -0600542 int r = -1;
Chris Austen7303bdc2016-04-17 11:50:54 -0500543 rev_t rev = {0};
David Cobbleya1adb072017-11-21 15:58:13 -0800544 static ipmi_device_id_t dev_id{};
545 static bool dev_id_initialized = false;
546 const char* filename = "/usr/share/ipmi-providers/dev_id.json";
Alexander Amelkinba19c182018-09-04 15:49:36 +0300547 constexpr auto ipmiDevIdStateShift = 7;
548 constexpr auto ipmiDevIdFw1Mask = ~(1 << ipmiDevIdStateShift);
Chris Austen6caf28b2015-10-13 12:40:40 -0500549
550 // Data length
Chris Austen7303bdc2016-04-17 11:50:54 -0500551 *data_len = sizeof(dev_id);
552
David Cobbleya1adb072017-11-21 15:58:13 -0800553 if (!dev_id_initialized)
554 {
Nagaraju Goruganti744398d2018-02-20 09:52:00 -0600555 try
556 {
557 auto version = getActiveSoftwareVersionInfo();
558 r = convert_version(version.c_str(), &rev);
David Cobbleya1adb072017-11-21 15:58:13 -0800559 }
Nagaraju Goruganti744398d2018-02-20 09:52:00 -0600560 catch (const std::exception& e)
561 {
562 log<level::ERR>(e.what());
563 }
Nan Liee0cb902016-07-11 15:38:03 +0800564
Patrick Venture0b02be92018-08-31 11:55:55 -0700565 if (r >= 0)
566 {
Nagaraju Goruganti744398d2018-02-20 09:52:00 -0600567 // bit7 identifies if the device is available
568 // 0=normal operation
569 // 1=device firmware, SDR update,
570 // or self-initialization in progress.
Alexander Amelkinba19c182018-09-04 15:49:36 +0300571 // The availability may change in run time, so mask here
572 // and initialize later.
573 dev_id.fw[0] = rev.major & ipmiDevIdFw1Mask;
Nagaraju Goruganti744398d2018-02-20 09:52:00 -0600574
575 rev.minor = (rev.minor > 99 ? 99 : rev.minor);
576 dev_id.fw[1] = rev.minor % 10 + (rev.minor / 10) * 16;
Patrick Ventureb51bf9c2018-09-10 15:53:14 -0700577 std::memcpy(&dev_id.aux, rev.d, 4);
David Cobbleya1adb072017-11-21 15:58:13 -0800578 }
Nan Liee0cb902016-07-11 15:38:03 +0800579
David Cobbleya1adb072017-11-21 15:58:13 -0800580 // IPMI Spec version 2.0
581 dev_id.ipmi_ver = 2;
Adriana Kobylak0e912642016-06-22 16:54:39 -0500582
David Cobbleya1adb072017-11-21 15:58:13 -0800583 std::ifstream dev_id_file(filename);
584 if (dev_id_file.is_open())
585 {
586 auto data = nlohmann::json::parse(dev_id_file, nullptr, false);
587 if (!data.is_discarded())
588 {
589 dev_id.id = data.value("id", 0);
590 dev_id.revision = data.value("revision", 0);
591 dev_id.addn_dev_support = data.value("addn_dev_support", 0);
592 dev_id.manuf_id[2] = data.value("manuf_id", 0) >> 16;
593 dev_id.manuf_id[1] = data.value("manuf_id", 0) >> 8;
594 dev_id.manuf_id[0] = data.value("manuf_id", 0);
595 dev_id.prod_id[1] = data.value("prod_id", 0) >> 8;
596 dev_id.prod_id[0] = data.value("prod_id", 0);
Tom Josephaf8a0982018-03-09 07:54:02 -0600597 dev_id.aux[3] = data.value("aux", 0);
598 dev_id.aux[2] = data.value("aux", 0) >> 8;
599 dev_id.aux[1] = data.value("aux", 0) >> 16;
600 dev_id.aux[0] = data.value("aux", 0) >> 24;
David Cobbleya1adb072017-11-21 15:58:13 -0800601
Patrick Venture0b02be92018-08-31 11:55:55 -0700602 // Don't read the file every time if successful
David Cobbleya1adb072017-11-21 15:58:13 -0800603 dev_id_initialized = true;
604 }
605 else
606 {
607 log<level::ERR>("Device ID JSON parser failure");
608 rc = IPMI_CC_UNSPECIFIED_ERROR;
609 }
610 }
611 else
612 {
613 log<level::ERR>("Device ID file not found");
614 rc = IPMI_CC_UNSPECIFIED_ERROR;
Chris Austen7303bdc2016-04-17 11:50:54 -0500615 }
616 }
Chris Austen6caf28b2015-10-13 12:40:40 -0500617
Alexander Amelkinba19c182018-09-04 15:49:36 +0300618 // Set availability to the actual current BMC state
619 dev_id.fw[0] &= ipmiDevIdFw1Mask;
620 if (!getCurrentBmcState())
621 {
622 dev_id.fw[0] |= (1 << ipmiDevIdStateShift);
623 }
624
Chris Austen6caf28b2015-10-13 12:40:40 -0500625 // Pack the actual response
Patrick Ventureb51bf9c2018-09-10 15:53:14 -0700626 std::memcpy(response, &dev_id, *data_len);
Nagaraju Goruganti744398d2018-02-20 09:52:00 -0600627
Chris Austen6caf28b2015-10-13 12:40:40 -0500628 return rc;
629}
630
Nan Li41fa24a2016-11-10 20:12:37 +0800631ipmi_ret_t ipmi_app_get_self_test_results(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
Patrick Venture0b02be92018-08-31 11:55:55 -0700632 ipmi_request_t request,
633 ipmi_response_t response,
634 ipmi_data_len_t data_len,
635 ipmi_context_t context)
Nan Li41fa24a2016-11-10 20:12:37 +0800636{
637 ipmi_ret_t rc = IPMI_CC_OK;
638
639 // Byte 2:
640 // 55h - No error.
Gunnar Mills8991dd62017-10-25 17:11:29 -0500641 // 56h - Self Test function not implemented in this controller.
Nan Li41fa24a2016-11-10 20:12:37 +0800642 // 57h - Corrupted or inaccesssible data or devices.
643 // 58h - Fatal hardware error.
644 // FFh - reserved.
645 // all other: Device-specific 'internal failure'.
646 // Byte 3:
647 // For byte 2 = 55h, 56h, FFh: 00h
648 // For byte 2 = 58h, all other: Device-specific
649 // For byte 2 = 57h: self-test error bitfield.
650 // Note: returning 57h does not imply that all test were run.
651 // [7] 1b = Cannot access SEL device.
652 // [6] 1b = Cannot access SDR Repository.
653 // [5] 1b = Cannot access BMC FRU device.
654 // [4] 1b = IPMB signal lines do not respond.
655 // [3] 1b = SDR Repository empty.
656 // [2] 1b = Internal Use Area of BMC FRU corrupted.
657 // [1] 1b = controller update 'boot block' firmware corrupted.
658 // [0] 1b = controller operational firmware corrupted.
659
660 char selftestresults[2] = {0};
661
662 *data_len = 2;
663
664 selftestresults[0] = 0x56;
665 selftestresults[1] = 0;
666
Patrick Ventureb51bf9c2018-09-10 15:53:14 -0700667 std::memcpy(response, selftestresults, *data_len);
Nan Li41fa24a2016-11-10 20:12:37 +0800668
669 return rc;
670}
671
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500672ipmi_ret_t ipmi_app_get_device_guid(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
Patrick Venture0b02be92018-08-31 11:55:55 -0700673 ipmi_request_t request,
674 ipmi_response_t response,
675 ipmi_data_len_t data_len,
676 ipmi_context_t context)
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500677{
Patrick Venture0b02be92018-08-31 11:55:55 -0700678 const char* objname = "/org/openbmc/control/chassis0";
679 const char* iface = "org.freedesktop.DBus.Properties";
680 const char* chassis_iface = "org.openbmc.control.Chassis";
681 sd_bus_message* reply = NULL;
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500682 sd_bus_error error = SD_BUS_ERROR_NULL;
683 int r = 0;
Patrick Venture0b02be92018-08-31 11:55:55 -0700684 char* uuid = NULL;
685 char* busname = NULL;
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500686
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500687 // UUID is in RFC4122 format. Ex: 61a39523-78f2-11e5-9862-e6402cfc3223
Patrick Ventured2117022018-02-06 08:54:37 -0800688 // Per IPMI Spec 2.0 need to convert to 16 hex bytes and reverse the byte
689 // order
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500690 // Ex: 0x2332fc2c40e66298e511f2782395a361
691
Patrick Venture0b02be92018-08-31 11:55:55 -0700692 const int resp_size = 16; // Response is 16 hex bytes per IPMI Spec
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500693 uint8_t resp_uuid[resp_size]; // Array to hold the formatted response
Patrick Ventured2117022018-02-06 08:54:37 -0800694 // Point resp end of array to save in reverse order
Patrick Venture0b02be92018-08-31 11:55:55 -0700695 int resp_loc = resp_size - 1;
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500696 int i = 0;
Patrick Venture0b02be92018-08-31 11:55:55 -0700697 char* tokptr = NULL;
698 char* id_octet = NULL;
Emily Shafferedb8bb02018-09-27 14:50:15 -0700699 size_t total_uuid_size = 0;
700 // 1 byte of resp is built from 2 chars of uuid.
701 constexpr size_t max_uuid_size = 2 * resp_size;
vishwa1eaea4f2016-02-26 11:57:40 -0600702
703 // Status code.
704 ipmi_ret_t rc = IPMI_CC_OK;
705 *data_len = 0;
706
vishwa1eaea4f2016-02-26 11:57:40 -0600707 // Call Get properties method with the interface and property name
Sergey Solomineb9b8142016-08-23 09:07:28 -0500708 r = mapper_get_service(bus, objname, &busname);
Patrick Venture0b02be92018-08-31 11:55:55 -0700709 if (r < 0)
710 {
711 log<level::ERR>("Failed to get bus name", entry("BUS=%s", objname),
Aditya Saripalli5fb14602017-11-09 14:46:27 +0530712 entry("ERRNO=0x%X", -r));
Sergey Solomineb9b8142016-08-23 09:07:28 -0500713 goto finish;
714 }
Patrick Venture0b02be92018-08-31 11:55:55 -0700715 r = sd_bus_call_method(bus, busname, objname, iface, "Get", &error, &reply,
716 "ss", chassis_iface, "uuid");
vishwa1eaea4f2016-02-26 11:57:40 -0600717 if (r < 0)
718 {
Patrick Venture0b02be92018-08-31 11:55:55 -0700719 log<level::ERR>("Failed to call Get Method", entry("ERRNO=0x%X", -r));
vishwa1eaea4f2016-02-26 11:57:40 -0600720 rc = IPMI_CC_UNSPECIFIED_ERROR;
721 goto finish;
722 }
723
724 r = sd_bus_message_read(reply, "v", "s", &uuid);
725 if (r < 0 || uuid == NULL)
726 {
Patrick Venture0b02be92018-08-31 11:55:55 -0700727 log<level::ERR>("Failed to get a response", entry("ERRNO=0x%X", -r));
vishwa1eaea4f2016-02-26 11:57:40 -0600728 rc = IPMI_CC_RESPONSE_ERROR;
729 goto finish;
730 }
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500731
732 // Traverse the UUID
Patrick Ventured2117022018-02-06 08:54:37 -0800733 // Get the UUID octects separated by dash
734 id_octet = strtok_r(uuid, "-", &tokptr);
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500735
736 if (id_octet == NULL)
vishwa1eaea4f2016-02-26 11:57:40 -0600737 {
738 // Error
Patrick Venture0b02be92018-08-31 11:55:55 -0700739 log<level::ERR>("Unexpected UUID format", entry("UUID=%s", uuid));
vishwa1eaea4f2016-02-26 11:57:40 -0600740 rc = IPMI_CC_RESPONSE_ERROR;
741 goto finish;
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500742 }
743
744 while (id_octet != NULL)
745 {
746 // Calculate the octet string size since it varies
747 // Divide it by 2 for the array size since 1 byte is built from 2 chars
Patrick Venture0b02be92018-08-31 11:55:55 -0700748 int tmp_size = strlen(id_octet) / 2;
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500749
Emily Shafferedb8bb02018-09-27 14:50:15 -0700750 // Check if total UUID size has been exceeded
751 if ((total_uuid_size += strlen(id_octet)) > max_uuid_size)
752 {
753 // Error - UUID too long to store
754 log<level::ERR>("UUID too long", entry("UUID=%s", uuid));
755 rc = IPMI_CC_RESPONSE_ERROR;
756 goto finish;
757 }
758
Patrick Venture0b02be92018-08-31 11:55:55 -0700759 for (i = 0; i < tmp_size; i++)
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500760 {
Patrick Ventured2117022018-02-06 08:54:37 -0800761 // Holder of the 2 chars that will become a byte
762 char tmp_array[3] = {0};
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500763 strncpy(tmp_array, id_octet, 2); // 2 chars at a time
764
765 int resp_byte = strtoul(tmp_array, NULL, 16); // Convert to hex byte
Patrick Ventured2117022018-02-06 08:54:37 -0800766 // Copy end to first
Patrick Ventureb51bf9c2018-09-10 15:53:14 -0700767 std::memcpy((void*)&resp_uuid[resp_loc], &resp_byte, 1);
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500768 resp_loc--;
Patrick Venture0b02be92018-08-31 11:55:55 -0700769 id_octet += 2; // Finished with the 2 chars, advance
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500770 }
Patrick Venture0b02be92018-08-31 11:55:55 -0700771 id_octet = strtok_r(NULL, "-", &tokptr); // Get next octet
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500772 }
773
774 // Data length
775 *data_len = resp_size;
776
777 // Pack the actual response
Patrick Ventureb51bf9c2018-09-10 15:53:14 -0700778 std::memcpy(response, &resp_uuid, *data_len);
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500779
vishwa1eaea4f2016-02-26 11:57:40 -0600780finish:
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500781 sd_bus_error_free(&error);
vishwa1eaea4f2016-02-26 11:57:40 -0600782 reply = sd_bus_message_unref(reply);
Sergey Solomineb9b8142016-08-23 09:07:28 -0500783 free(busname);
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500784
785 return rc;
786}
Chris Austen6caf28b2015-10-13 12:40:40 -0500787
Adriana Kobylak3a552e12015-10-19 16:11:00 -0500788ipmi_ret_t ipmi_app_get_bt_capabilities(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
Patrick Venture0b02be92018-08-31 11:55:55 -0700789 ipmi_request_t request,
790 ipmi_response_t response,
791 ipmi_data_len_t data_len,
792 ipmi_context_t context)
vishwabmcba0bd5f2015-09-30 16:50:23 +0530793{
vishwabmcba0bd5f2015-09-30 16:50:23 +0530794
795 // Status code.
796 ipmi_ret_t rc = IPMI_CC_OK;
797
Adriana Kobylak88ad8152016-12-13 10:09:08 -0600798 // Per IPMI 2.0 spec, the input and output buffer size must be the max
799 // buffer size minus one byte to allocate space for the length byte.
Patrick Venture0b02be92018-08-31 11:55:55 -0700800 uint8_t str[] = {0x01, MAX_IPMI_BUFFER - 1, MAX_IPMI_BUFFER - 1, 0x0A,
801 0x01};
vishwabmcba0bd5f2015-09-30 16:50:23 +0530802
803 // Data length
804 *data_len = sizeof(str);
805
806 // Pack the actual response
Patrick Ventureb51bf9c2018-09-10 15:53:14 -0700807 std::memcpy(response, &str, *data_len);
vishwabmcba0bd5f2015-09-30 16:50:23 +0530808
809 return rc;
810}
811
Adriana Kobylak3a552e12015-10-19 16:11:00 -0500812ipmi_ret_t ipmi_app_wildcard_handler(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
Patrick Venture0b02be92018-08-31 11:55:55 -0700813 ipmi_request_t request,
814 ipmi_response_t response,
815 ipmi_data_len_t data_len,
816 ipmi_context_t context)
vishwabmcba0bd5f2015-09-30 16:50:23 +0530817{
vishwabmcba0bd5f2015-09-30 16:50:23 +0530818 // Status code.
Nan Li70aa8d92016-08-29 00:11:10 +0800819 ipmi_ret_t rc = IPMI_CC_INVALID;
vishwabmcba0bd5f2015-09-30 16:50:23 +0530820
821 *data_len = strlen("THIS IS WILDCARD");
822
823 // Now pack actual response
Patrick Ventureb51bf9c2018-09-10 15:53:14 -0700824 std::memcpy(response, "THIS IS WILDCARD", *data_len);
vishwabmcba0bd5f2015-09-30 16:50:23 +0530825
826 return rc;
827}
828
Marri Devender Rao5e007a52018-01-08 06:18:36 -0600829ipmi_ret_t ipmi_app_get_sys_guid(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
Patrick Venture0b02be92018-08-31 11:55:55 -0700830 ipmi_request_t request,
831 ipmi_response_t response,
832 ipmi_data_len_t data_len,
833 ipmi_context_t context)
Marri Devender Rao5e007a52018-01-08 06:18:36 -0600834
835{
836 ipmi_ret_t rc = IPMI_CC_OK;
837 sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()};
838
839 try
840 {
841 // Get the Inventory object implementing BMC interface
842 ipmi::DbusObjectInfo bmcObject =
843 ipmi::getDbusObject(bus, bmc_interface);
844
845 // Read UUID property value from bmcObject
846 // UUID is in RFC4122 format Ex: 61a39523-78f2-11e5-9862-e6402cfc3223
Patrick Venture0b02be92018-08-31 11:55:55 -0700847 auto variant =
848 ipmi::getDbusProperty(bus, bmcObject.second, bmcObject.first,
849 bmc_guid_interface, bmc_guid_property);
William A. Kennington III4c008022018-10-12 17:18:14 -0700850 std::string guidProp = variant_ns::get<std::string>(variant);
Marri Devender Rao5e007a52018-01-08 06:18:36 -0600851
852 // Erase "-" characters from the property value
853 guidProp.erase(std::remove(guidProp.begin(), guidProp.end(), '-'),
Patrick Venture0b02be92018-08-31 11:55:55 -0700854 guidProp.end());
Marri Devender Rao5e007a52018-01-08 06:18:36 -0600855
856 auto guidPropLen = guidProp.length();
857 // Validate UUID data
858 // Divide by 2 as 1 byte is built from 2 chars
Patrick Venture0b02be92018-08-31 11:55:55 -0700859 if ((guidPropLen <= 0) || ((guidPropLen / 2) != bmc_guid_len))
Marri Devender Rao5e007a52018-01-08 06:18:36 -0600860
861 {
862 log<level::ERR>("Invalid UUID property value",
Patrick Venture0b02be92018-08-31 11:55:55 -0700863 entry("UUID_LENGTH=%d", guidPropLen));
Marri Devender Rao5e007a52018-01-08 06:18:36 -0600864 return IPMI_CC_RESPONSE_ERROR;
865 }
866
867 // Convert data in RFC4122(MSB) format to LSB format
868 // Get 2 characters at a time as 1 byte is built from 2 chars and
869 // convert to hex byte
870 // TODO: Data printed for GUID command is not as per the
871 // GUID format defined in IPMI specification 2.0 section 20.8
872 // Ticket raised: https://sourceforge.net/p/ipmitool/bugs/501/
873 uint8_t respGuid[bmc_guid_len];
874 for (size_t i = 0, respLoc = (bmc_guid_len - 1);
Patrick Venture0b02be92018-08-31 11:55:55 -0700875 i < guidPropLen && respLoc >= 0; i += 2, respLoc--)
Marri Devender Rao5e007a52018-01-08 06:18:36 -0600876 {
877 auto value = static_cast<uint8_t>(
Patrick Venture0b02be92018-08-31 11:55:55 -0700878 std::stoi(guidProp.substr(i, 2).c_str(), NULL, 16));
Marri Devender Rao5e007a52018-01-08 06:18:36 -0600879 respGuid[respLoc] = value;
880 }
881
882 *data_len = bmc_guid_len;
Patrick Ventureb51bf9c2018-09-10 15:53:14 -0700883 std::memcpy(response, &respGuid, bmc_guid_len);
Marri Devender Rao5e007a52018-01-08 06:18:36 -0600884 }
885 catch (const InternalFailure& e)
886 {
887 log<level::ERR>("Failed in reading BMC UUID property",
888 entry("INTERFACE=%s", bmc_interface),
889 entry("PROPERTY_INTERFACE=%s", bmc_guid_interface),
890 entry("PROPERTY=%s", bmc_guid_property));
891 return IPMI_CC_UNSPECIFIED_ERROR;
892 }
893 return rc;
894}
895
Xo Wangf542e8b2017-08-09 15:34:16 -0700896static std::unique_ptr<SysInfoParamStore> sysInfoParamStore;
897
Xo Wang87651332017-08-11 10:17:59 -0700898static std::string sysInfoReadSystemName()
899{
900 // Use the BMC hostname as the "System Name."
901 char hostname[HOST_NAME_MAX + 1] = {};
902 if (gethostname(hostname, HOST_NAME_MAX) != 0)
903 {
904 perror("System info parameter: system name");
905 }
906 return hostname;
907}
908
Xo Wangf542e8b2017-08-09 15:34:16 -0700909struct IpmiSysInfoResp
910{
911 uint8_t paramRevision;
912 uint8_t setSelector;
913 union
914 {
915 struct
916 {
917 uint8_t encoding;
918 uint8_t stringLen;
919 uint8_t stringData0[14];
920 } __attribute__((packed));
921 uint8_t stringDataN[16];
922 uint8_t byteData;
923 };
924} __attribute__((packed));
925
926/**
927 * Split a string into (up to) 16-byte chunks as expected in response for get
928 * system info parameter.
929 *
930 * @param[in] fullString: Input string to be split
931 * @param[in] chunkIndex: Index of the chunk to be written out
932 * @param[in,out] chunk: Output data buffer; must have 14 byte capacity if
933 * chunk_index = 0 and 16-byte capacity otherwise
934 * @return the number of bytes written into the output buffer, or -EINVAL for
935 * invalid arguments.
936 */
937static int splitStringParam(const std::string& fullString, int chunkIndex,
938 uint8_t* chunk)
939{
940 constexpr int maxChunk = 255;
941 constexpr int smallChunk = 14;
942 constexpr int chunkSize = 16;
943 if (chunkIndex > maxChunk || chunk == nullptr)
944 {
945 return -EINVAL;
946 }
947 try
948 {
949 std::string output;
950 if (chunkIndex == 0)
951 {
952 // Output must have 14 byte capacity.
953 output = fullString.substr(0, smallChunk);
954 }
955 else
956 {
957 // Output must have 16 byte capacity.
958 output = fullString.substr((chunkIndex * chunkSize) - 2, chunkSize);
959 }
960
961 std::memcpy(chunk, output.c_str(), output.length());
962 return output.length();
963 }
964 catch (const std::out_of_range& e)
965 {
966 // The position was beyond the end.
967 return -EINVAL;
968 }
969}
970
971/**
972 * Packs the Get Sys Info Request Item into the response.
973 *
974 * @param[in] paramString - the parameter.
975 * @param[in] setSelector - the selector
976 * @param[in,out] resp - the System info response.
977 * @return The number of bytes packed or failure from splitStringParam().
978 */
979static int packGetSysInfoResp(const std::string& paramString,
980 uint8_t setSelector, IpmiSysInfoResp* resp)
981{
982 uint8_t* dataBuffer = resp->stringDataN;
983 resp->setSelector = setSelector;
984 if (resp->setSelector == 0) // First chunk has only 14 bytes.
985 {
986 resp->encoding = 0;
987 resp->stringLen = paramString.length();
988 dataBuffer = resp->stringData0;
989 }
990 return splitStringParam(paramString, resp->setSelector, dataBuffer);
991}
992
993ipmi_ret_t ipmi_app_get_system_info(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
994 ipmi_request_t request,
995 ipmi_response_t response,
996 ipmi_data_len_t dataLen,
997 ipmi_context_t context)
998{
999 IpmiSysInfoResp resp = {};
1000 size_t respLen = 0;
1001 uint8_t* const reqData = static_cast<uint8_t*>(request);
1002 std::string paramString;
1003 bool found;
1004 std::tuple<bool, std::string> ret;
1005 constexpr int minRequestSize = 4;
1006 constexpr int paramSelector = 1;
1007 constexpr uint8_t revisionOnly = 0x80;
1008 const uint8_t paramRequested = reqData[paramSelector];
1009 int rc;
1010
1011 if (*dataLen < minRequestSize)
1012 {
1013 return IPMI_CC_REQ_DATA_LEN_INVALID;
1014 }
1015
1016 *dataLen = 0; // default to 0.
1017
1018 // Parameters revision as of IPMI spec v2.0 rev. 1.1 (Feb 11, 2014 E6)
1019 resp.paramRevision = 0x11;
1020 if (reqData[0] & revisionOnly) // Get parameter revision only
1021 {
1022 respLen = 1;
1023 goto writeResponse;
1024 }
1025
1026 // The "Set In Progress" parameter can be used for rollback of parameter
1027 // data and is not implemented.
1028 if (paramRequested == 0)
1029 {
1030 resp.byteData = 0;
1031 respLen = 2;
1032 goto writeResponse;
1033 }
1034
1035 if (sysInfoParamStore == nullptr)
1036 {
1037 sysInfoParamStore = std::make_unique<SysInfoParamStore>();
Xo Wang87651332017-08-11 10:17:59 -07001038 sysInfoParamStore->update(IPMI_SYSINFO_SYSTEM_NAME,
1039 sysInfoReadSystemName);
Xo Wangf542e8b2017-08-09 15:34:16 -07001040 }
1041
1042 // Parameters other than Set In Progress are assumed to be strings.
1043 ret = sysInfoParamStore->lookup(paramRequested);
1044 found = std::get<0>(ret);
1045 paramString = std::get<1>(ret);
1046 if (!found)
1047 {
1048 return IPMI_CC_SYSTEM_INFO_PARAMETER_NOT_SUPPORTED;
1049 }
1050 // TODO: Cache each parameter across multiple calls, until the whole string
1051 // has been read out. Otherwise, it's possible for a parameter to change
1052 // between requests for its chunks, returning chunks incoherent with each
1053 // other. For now, the parameter store is simply required to have only
1054 // idempotent callbacks.
1055 rc = packGetSysInfoResp(paramString, reqData[2], &resp);
1056 if (rc == -EINVAL)
1057 {
1058 return IPMI_CC_RESPONSE_ERROR;
1059 }
1060
1061 respLen = sizeof(resp); // Write entire string data chunk in response.
1062
1063writeResponse:
1064 std::memcpy(response, &resp, sizeof(resp));
1065 *dataLen = respLen;
1066 return IPMI_CC_OK;
1067}
1068
Chris Austen6caf28b2015-10-13 12:40:40 -05001069void register_netfn_app_functions()
vishwabmcba0bd5f2015-09-30 16:50:23 +05301070{
Tom05732372016-09-06 17:21:23 +05301071 // <Get BT Interface Capabilities>
Patrick Venture0b02be92018-08-31 11:55:55 -07001072 ipmi_register_callback(NETFUN_APP, IPMI_CMD_GET_CAP_BIT, NULL,
1073 ipmi_app_get_bt_capabilities, PRIVILEGE_USER);
Chris Austen6caf28b2015-10-13 12:40:40 -05001074
Tom05732372016-09-06 17:21:23 +05301075 // <Wildcard Command>
Patrick Venture0b02be92018-08-31 11:55:55 -07001076 ipmi_register_callback(NETFUN_APP, IPMI_CMD_WILDCARD, NULL,
1077 ipmi_app_wildcard_handler, PRIVILEGE_USER);
Chris Austen6caf28b2015-10-13 12:40:40 -05001078
Tom05732372016-09-06 17:21:23 +05301079 // <Reset Watchdog Timer>
Patrick Venture0b02be92018-08-31 11:55:55 -07001080 ipmi_register_callback(NETFUN_APP, IPMI_CMD_RESET_WD, NULL,
1081 ipmi_app_watchdog_reset, PRIVILEGE_OPERATOR);
Chris Austen6caf28b2015-10-13 12:40:40 -05001082
Tom05732372016-09-06 17:21:23 +05301083 // <Set Watchdog Timer>
Patrick Venture0b02be92018-08-31 11:55:55 -07001084 ipmi_register_callback(NETFUN_APP, IPMI_CMD_SET_WD, NULL,
1085 ipmi_app_watchdog_set, PRIVILEGE_OPERATOR);
Chris Austen6caf28b2015-10-13 12:40:40 -05001086
William A. Kennington III73f44512018-02-09 15:28:46 -08001087 // <Get Watchdog Timer>
Patrick Venture0b02be92018-08-31 11:55:55 -07001088 ipmi_register_callback(NETFUN_APP, IPMI_CMD_GET_WD, NULL,
1089 ipmi_app_watchdog_get, PRIVILEGE_OPERATOR);
William A. Kennington III73f44512018-02-09 15:28:46 -08001090
Tom05732372016-09-06 17:21:23 +05301091 // <Get Device ID>
Patrick Venture0b02be92018-08-31 11:55:55 -07001092 ipmi_register_callback(NETFUN_APP, IPMI_CMD_GET_DEVICE_ID, NULL,
1093 ipmi_app_get_device_id, PRIVILEGE_USER);
Chris Austen6caf28b2015-10-13 12:40:40 -05001094
Tom05732372016-09-06 17:21:23 +05301095 // <Get Self Test Results>
Patrick Venture0b02be92018-08-31 11:55:55 -07001096 ipmi_register_callback(NETFUN_APP, IPMI_CMD_GET_SELF_TEST_RESULTS, NULL,
1097 ipmi_app_get_self_test_results, PRIVILEGE_USER);
Nan Li41fa24a2016-11-10 20:12:37 +08001098
Tom05732372016-09-06 17:21:23 +05301099 // <Get Device GUID>
Patrick Venture0b02be92018-08-31 11:55:55 -07001100 ipmi_register_callback(NETFUN_APP, IPMI_CMD_GET_DEVICE_GUID, NULL,
1101 ipmi_app_get_device_guid, PRIVILEGE_USER);
Adriana Kobylakd100ee52015-10-20 17:02:37 -05001102
Tom05732372016-09-06 17:21:23 +05301103 // <Set ACPI Power State>
Patrick Venture0b02be92018-08-31 11:55:55 -07001104 ipmi_register_callback(NETFUN_APP, IPMI_CMD_SET_ACPI, NULL,
1105 ipmi_app_set_acpi_power_state, PRIVILEGE_ADMIN);
Chris Austen6caf28b2015-10-13 12:40:40 -05001106
Yong Li18d77262018-10-09 01:59:45 +08001107 // <Get ACPI Power State>
1108 ipmi_register_callback(NETFUN_APP, IPMI_CMD_GET_ACPI, NULL,
1109 ipmi_app_get_acpi_power_state, PRIVILEGE_ADMIN);
1110
Marri Devender Rao5e007a52018-01-08 06:18:36 -06001111 // <Get System GUID Command>
Patrick Venture0b02be92018-08-31 11:55:55 -07001112 ipmi_register_callback(NETFUN_APP, IPMI_CMD_GET_SYS_GUID, NULL,
1113 ipmi_app_get_sys_guid, PRIVILEGE_USER);
Tom Joseph7cbe2282018-03-21 21:17:33 +05301114
1115 // <Get Channel Cipher Suites Command>
Patrick Venture0b02be92018-08-31 11:55:55 -07001116 ipmi_register_callback(NETFUN_APP, IPMI_CMD_GET_CHAN_CIPHER_SUITES, NULL,
1117 getChannelCipherSuites, PRIVILEGE_CALLBACK);
Vernon Maueryd2a57de2019-03-25 12:45:28 -07001118
Xo Wangf542e8b2017-08-09 15:34:16 -07001119 // <Get System Info Command>
1120 ipmi_register_callback(NETFUN_APP, IPMI_CMD_GET_SYSTEM_INFO, NULL,
1121 ipmi_app_get_system_info, PRIVILEGE_USER);
vishwabmcba0bd5f2015-09-30 16:50:23 +05301122 return;
1123}