blob: 0d124fe434a190d1390f8275fd3137af5648cbff [file] [log] [blame]
Patrick Venture0b02be92018-08-31 11:55:55 -07001#include <arpa/inet.h>
William A. Kennington III194375f2018-12-14 02:14:33 -08002#include <ipmid/api.h>
Xo Wang87651332017-08-11 10:17:59 -07003#include <limits.h>
Patrick Venture0b02be92018-08-31 11:55:55 -07004#include <mapper.h>
Patrick Venture0b02be92018-08-31 11:55:55 -07005#include <systemd/sd-bus.h>
Xo Wang87651332017-08-11 10:17:59 -07006#include <unistd.h>
Patrick Venture0b02be92018-08-31 11:55:55 -07007
Patrick Venture3a5071a2018-09-12 13:27:42 -07008#include <algorithm>
Vernon Mauery0120b682019-03-25 13:08:54 -07009#include <app/channel.hpp>
10#include <app/watchdog.hpp>
11#include <apphandler.hpp>
Patrick Venture3a5071a2018-09-12 13:27:42 -070012#include <array>
13#include <cstddef>
Vernon Mauery0120b682019-03-25 13:08:54 -070014#include <cstdint>
Vernon Mauerybdda8002019-02-26 10:18:51 -080015#include <filesystem>
Patrick Venture3a5071a2018-09-12 13:27:42 -070016#include <fstream>
Vernon Mauery0120b682019-03-25 13:08:54 -070017#include <ipmid/api.hpp>
18#include <ipmid/registration.hpp>
Vernon Mauery33250242019-03-12 16:49:26 -070019#include <ipmid/types.hpp>
Vernon Mauery6a98fe72019-03-11 15:57:48 -070020#include <ipmid/utils.hpp>
Patrick Venture3a5071a2018-09-12 13:27:42 -070021#include <memory>
Patrick Venture46470a32018-09-07 19:26:25 -070022#include <nlohmann/json.hpp>
Patrick Venture3a5071a2018-09-12 13:27:42 -070023#include <phosphor-logging/elog-errors.hpp>
24#include <phosphor-logging/log.hpp>
William A. Kennington III4c008022018-10-12 17:18:14 -070025#include <sdbusplus/message/types.hpp>
Patrick Venture3a5071a2018-09-12 13:27:42 -070026#include <string>
Vernon Mauery0120b682019-03-25 13:08:54 -070027#include <sys_info_param.hpp>
28#include <transporthandler.hpp>
Patrick Venture3a5071a2018-09-12 13:27:42 -070029#include <tuple>
30#include <vector>
31#include <xyz/openbmc_project/Common/error.hpp>
Yong Li18d77262018-10-09 01:59:45 +080032#include <xyz/openbmc_project/Control/Power/ACPIPowerState/server.hpp>
Patrick Venture3a5071a2018-09-12 13:27:42 -070033#include <xyz/openbmc_project/Software/Activation/server.hpp>
34#include <xyz/openbmc_project/Software/Version/server.hpp>
35#include <xyz/openbmc_project/State/BMC/server.hpp>
Ratan Guptab8e99552017-07-27 07:07:48 +053036
Patrick Venture0b02be92018-08-31 11:55:55 -070037extern sd_bus* bus;
vishwabmcba0bd5f2015-09-30 16:50:23 +053038
Alexander Amelkinba19c182018-09-04 15:49:36 +030039constexpr auto bmc_state_interface = "xyz.openbmc_project.State.BMC";
40constexpr auto bmc_state_property = "CurrentBMCState";
Marri Devender Rao5e007a52018-01-08 06:18:36 -060041constexpr auto bmc_interface = "xyz.openbmc_project.Inventory.Item.Bmc";
42constexpr auto bmc_guid_interface = "xyz.openbmc_project.Common.UUID";
43constexpr auto bmc_guid_property = "UUID";
44constexpr auto bmc_guid_len = 16;
45
Nagaraju Goruganti744398d2018-02-20 09:52:00 -060046static constexpr auto redundancyIntf =
47 "xyz.openbmc_project.Software.RedundancyPriority";
Patrick Venture0b02be92018-08-31 11:55:55 -070048static constexpr auto versionIntf = "xyz.openbmc_project.Software.Version";
Nagaraju Goruganti744398d2018-02-20 09:52:00 -060049static constexpr auto activationIntf =
50 "xyz.openbmc_project.Software.Activation";
51static constexpr auto softwareRoot = "/xyz/openbmc_project/software";
52
Chris Austen6caf28b2015-10-13 12:40:40 -050053void register_netfn_app_functions() __attribute__((constructor));
vishwabmcba0bd5f2015-09-30 16:50:23 +053054
Ratan Guptab8e99552017-07-27 07:07:48 +053055using namespace phosphor::logging;
56using namespace sdbusplus::xyz::openbmc_project::Common::Error;
Nagaraju Goruganti744398d2018-02-20 09:52:00 -060057using Version = sdbusplus::xyz::openbmc_project::Software::server::Version;
58using Activation =
59 sdbusplus::xyz::openbmc_project::Software::server::Activation;
Alexander Amelkinba19c182018-09-04 15:49:36 +030060using BMC = sdbusplus::xyz::openbmc_project::State::server::BMC;
Vernon Mauery185b9f82018-07-20 10:52:36 -070061namespace fs = std::filesystem;
William A. Kennington III4c008022018-10-12 17:18:14 -070062namespace variant_ns = sdbusplus::message::variant_ns;
Ratan Guptab8e99552017-07-27 07:07:48 +053063
Nan Liee0cb902016-07-11 15:38:03 +080064// Offset in get device id command.
65typedef struct
66{
Patrick Venture0b02be92018-08-31 11:55:55 -070067 uint8_t id;
68 uint8_t revision;
69 uint8_t fw[2];
70 uint8_t ipmi_ver;
71 uint8_t addn_dev_support;
72 uint8_t manuf_id[3];
73 uint8_t prod_id[2];
74 uint8_t aux[4];
75} __attribute__((packed)) ipmi_device_id_t;
Chris Austen7303bdc2016-04-17 11:50:54 -050076
Nagaraju Goruganti744398d2018-02-20 09:52:00 -060077/**
78 * @brief Returns the Version info from primary s/w object
79 *
80 * Get the Version info from the active s/w object which is having high
81 * "Priority" value(a smaller number is a higher priority) and "Purpose"
82 * is "BMC" from the list of all s/w objects those are implementing
83 * RedundancyPriority interface from the given softwareRoot path.
84 *
85 * @return On success returns the Version info from primary s/w object.
86 *
87 */
88std::string getActiveSoftwareVersionInfo()
89{
90 sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()};
91
92 std::string revision{};
Patrick Venture0b02be92018-08-31 11:55:55 -070093 auto objectTree =
94 ipmi::getAllDbusObjects(bus, softwareRoot, redundancyIntf, "");
Nagaraju Goruganti744398d2018-02-20 09:52:00 -060095 if (objectTree.empty())
96 {
97 log<level::ERR>("No Obj has implemented the s/w redundancy interface",
98 entry("INTERFACE=%s", redundancyIntf));
99 elog<InternalFailure>();
100 }
101
102 auto objectFound = false;
103 for (auto& softObject : objectTree)
104 {
105 auto service = ipmi::getService(bus, redundancyIntf, softObject.first);
106 auto objValueTree = ipmi::getManagedObjects(bus, service, softwareRoot);
107
108 auto minPriority = 0xFF;
109 for (const auto& objIter : objValueTree)
110 {
111 try
112 {
113 auto& intfMap = objIter.second;
114 auto& redundancyPriorityProps = intfMap.at(redundancyIntf);
115 auto& versionProps = intfMap.at(versionIntf);
116 auto& activationProps = intfMap.at(activationIntf);
William A. Kennington III4c008022018-10-12 17:18:14 -0700117 auto priority = variant_ns::get<uint8_t>(
118 redundancyPriorityProps.at("Priority"));
119 auto purpose =
120 variant_ns::get<std::string>(versionProps.at("Purpose"));
121 auto activation = variant_ns::get<std::string>(
122 activationProps.at("Activation"));
123 auto version =
124 variant_ns::get<std::string>(versionProps.at("Version"));
Nagaraju Goruganti744398d2018-02-20 09:52:00 -0600125 if ((Version::convertVersionPurposeFromString(purpose) ==
126 Version::VersionPurpose::BMC) &&
127 (Activation::convertActivationsFromString(activation) ==
128 Activation::Activations::Active))
129 {
130 if (priority < minPriority)
131 {
132 minPriority = priority;
133 objectFound = true;
134 revision = std::move(version);
135 }
136 }
137 }
138 catch (const std::exception& e)
139 {
140 log<level::ERR>(e.what());
141 }
142 }
143 }
144
145 if (!objectFound)
146 {
147 log<level::ERR>("Could not found an BMC software Object");
148 elog<InternalFailure>();
149 }
150
151 return revision;
152}
153
Alexander Amelkinba19c182018-09-04 15:49:36 +0300154bool getCurrentBmcState()
155{
156 sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()};
157
158 // Get the Inventory object implementing the BMC interface
159 ipmi::DbusObjectInfo bmcObject =
160 ipmi::getDbusObject(bus, bmc_state_interface);
161 auto variant =
162 ipmi::getDbusProperty(bus, bmcObject.second, bmcObject.first,
163 bmc_state_interface, bmc_state_property);
164
William A. Kennington III4c008022018-10-12 17:18:14 -0700165 return variant_ns::holds_alternative<std::string>(variant) &&
166 BMC::convertBMCStateFromString(
167 variant_ns::get<std::string>(variant)) == BMC::BMCState::Ready;
Alexander Amelkinba19c182018-09-04 15:49:36 +0300168}
169
Yong Li18d77262018-10-09 01:59:45 +0800170namespace acpi_state
171{
172using namespace sdbusplus::xyz::openbmc_project::Control::Power::server;
173
174const static constexpr char* acpiObjPath =
175 "/xyz/openbmc_project/control/host0/acpi_power_state";
176const static constexpr char* acpiInterface =
177 "xyz.openbmc_project.Control.Power.ACPIPowerState";
178const static constexpr char* sysACPIProp = "SysACPIStatus";
179const static constexpr char* devACPIProp = "DevACPIStatus";
180
181enum class PowerStateType : uint8_t
182{
183 sysPowerState = 0x00,
184 devPowerState = 0x01,
185};
186
187// Defined in 20.6 of ipmi doc
188enum class PowerState : uint8_t
189{
190 s0G0D0 = 0x00,
191 s1D1 = 0x01,
192 s2D2 = 0x02,
193 s3D3 = 0x03,
194 s4 = 0x04,
195 s5G2 = 0x05,
196 s4S5 = 0x06,
197 g3 = 0x07,
198 sleep = 0x08,
199 g1Sleep = 0x09,
200 override = 0x0a,
201 legacyOn = 0x20,
202 legacyOff = 0x21,
203 unknown = 0x2a,
204 noChange = 0x7f,
205};
206
207static constexpr uint8_t stateChanged = 0x80;
208
209struct ACPIState
210{
211 uint8_t sysACPIState;
212 uint8_t devACPIState;
213} __attribute__((packed));
214
215std::map<ACPIPowerState::ACPI, PowerState> dbusToIPMI = {
216 {ACPIPowerState::ACPI::S0_G0_D0, PowerState::s0G0D0},
217 {ACPIPowerState::ACPI::S1_D1, PowerState::s1D1},
218 {ACPIPowerState::ACPI::S2_D2, PowerState::s2D2},
219 {ACPIPowerState::ACPI::S3_D3, PowerState::s3D3},
220 {ACPIPowerState::ACPI::S4, PowerState::s4},
221 {ACPIPowerState::ACPI::S5_G2, PowerState::s5G2},
222 {ACPIPowerState::ACPI::S4_S5, PowerState::s4S5},
223 {ACPIPowerState::ACPI::G3, PowerState::g3},
224 {ACPIPowerState::ACPI::SLEEP, PowerState::sleep},
225 {ACPIPowerState::ACPI::G1_SLEEP, PowerState::g1Sleep},
226 {ACPIPowerState::ACPI::OVERRIDE, PowerState::override},
227 {ACPIPowerState::ACPI::LEGACY_ON, PowerState::legacyOn},
228 {ACPIPowerState::ACPI::LEGACY_OFF, PowerState::legacyOff},
229 {ACPIPowerState::ACPI::Unknown, PowerState::unknown}};
230
231bool isValidACPIState(acpi_state::PowerStateType type, uint8_t state)
232{
233 if (type == acpi_state::PowerStateType::sysPowerState)
234 {
235 if ((state <= static_cast<uint8_t>(acpi_state::PowerState::override)) ||
236 (state == static_cast<uint8_t>(acpi_state::PowerState::legacyOn)) ||
237 (state ==
238 static_cast<uint8_t>(acpi_state::PowerState::legacyOff)) ||
239 (state == static_cast<uint8_t>(acpi_state::PowerState::unknown)) ||
240 (state == static_cast<uint8_t>(acpi_state::PowerState::noChange)))
241 {
242 return true;
243 }
244 else
245 {
246 return false;
247 }
248 }
249 else if (type == acpi_state::PowerStateType::devPowerState)
250 {
251 if ((state <= static_cast<uint8_t>(acpi_state::PowerState::s3D3)) ||
252 (state == static_cast<uint8_t>(acpi_state::PowerState::unknown)) ||
253 (state == static_cast<uint8_t>(acpi_state::PowerState::noChange)))
254 {
255 return true;
256 }
257 else
258 {
259 return false;
260 }
261 }
262 else
263 {
264 return false;
265 }
266 return false;
267}
268} // namespace acpi_state
269
Adriana Kobylak3a552e12015-10-19 16:11:00 -0500270ipmi_ret_t ipmi_app_set_acpi_power_state(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
Patrick Venture0b02be92018-08-31 11:55:55 -0700271 ipmi_request_t request,
272 ipmi_response_t response,
273 ipmi_data_len_t data_len,
274 ipmi_context_t context)
Chris Austen6caf28b2015-10-13 12:40:40 -0500275{
Yong Li18d77262018-10-09 01:59:45 +0800276 auto s = static_cast<uint8_t>(acpi_state::PowerState::unknown);
Chris Austen6caf28b2015-10-13 12:40:40 -0500277 ipmi_ret_t rc = IPMI_CC_OK;
Yong Li18d77262018-10-09 01:59:45 +0800278
279 sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()};
280
281 auto value = acpi_state::ACPIPowerState::ACPI::Unknown;
282
283 auto* req = reinterpret_cast<acpi_state::ACPIState*>(request);
284
285 if (*data_len != sizeof(acpi_state::ACPIState))
286 {
287 log<level::ERR>("set_acpi invalid len");
288 *data_len = 0;
289 return IPMI_CC_REQ_DATA_LEN_INVALID;
290 }
291
Chris Austen6caf28b2015-10-13 12:40:40 -0500292 *data_len = 0;
293
Yong Li18d77262018-10-09 01:59:45 +0800294 if (req->sysACPIState & acpi_state::stateChanged)
295 {
296 // set system power state
297 s = req->sysACPIState & ~acpi_state::stateChanged;
298
299 if (!acpi_state::isValidACPIState(
300 acpi_state::PowerStateType::sysPowerState, s))
301 {
302 log<level::ERR>("set_acpi_power sys invalid input",
303 entry("S=%x", s));
304 return IPMI_CC_PARM_OUT_OF_RANGE;
305 }
306
307 // valid input
308 if (s == static_cast<uint8_t>(acpi_state::PowerState::noChange))
309 {
310 log<level::DEBUG>("No change for system power state");
311 }
312 else
313 {
314 auto found = std::find_if(
315 acpi_state::dbusToIPMI.begin(), acpi_state::dbusToIPMI.end(),
316 [&s](const auto& iter) {
317 return (static_cast<uint8_t>(iter.second) == s);
318 });
319
320 value = found->first;
321
322 try
323 {
324 auto acpiObject =
325 ipmi::getDbusObject(bus, acpi_state::acpiInterface);
326 ipmi::setDbusProperty(bus, acpiObject.second, acpiObject.first,
327 acpi_state::acpiInterface,
328 acpi_state::sysACPIProp,
329 convertForMessage(value));
330 }
331 catch (const InternalFailure& e)
332 {
333 log<level::ERR>("Failed in set ACPI system property",
334 entry("EXCEPTION=%s", e.what()));
335 return IPMI_CC_UNSPECIFIED_ERROR;
336 }
337 }
338 }
339 else
340 {
341 log<level::DEBUG>("Do not change system power state");
342 }
343
344 if (req->devACPIState & acpi_state::stateChanged)
345 {
346 // set device power state
347 s = req->devACPIState & ~acpi_state::stateChanged;
348 if (!acpi_state::isValidACPIState(
349 acpi_state::PowerStateType::devPowerState, s))
350 {
351 log<level::ERR>("set_acpi_power dev invalid input",
352 entry("S=%x", s));
353 return IPMI_CC_PARM_OUT_OF_RANGE;
354 }
355
356 // valid input
357 if (s == static_cast<uint8_t>(acpi_state::PowerState::noChange))
358 {
359 log<level::DEBUG>("No change for device power state");
360 }
361 else
362 {
363 auto found = std::find_if(
364 acpi_state::dbusToIPMI.begin(), acpi_state::dbusToIPMI.end(),
365 [&s](const auto& iter) {
366 return (static_cast<uint8_t>(iter.second) == s);
367 });
368
369 value = found->first;
370
371 try
372 {
373 auto acpiObject =
374 ipmi::getDbusObject(bus, acpi_state::acpiInterface);
375 ipmi::setDbusProperty(bus, acpiObject.second, acpiObject.first,
376 acpi_state::acpiInterface,
377 acpi_state::devACPIProp,
378 convertForMessage(value));
379 }
380 catch (const InternalFailure& e)
381 {
382 log<level::ERR>("Failed in set ACPI device property",
383 entry("EXCEPTION=%s", e.what()));
384 return IPMI_CC_UNSPECIFIED_ERROR;
385 }
386 }
387 }
388 else
389 {
390 log<level::DEBUG>("Do not change device power state");
391 }
392
393 return rc;
394}
395
396ipmi_ret_t ipmi_app_get_acpi_power_state(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
397 ipmi_request_t request,
398 ipmi_response_t response,
399 ipmi_data_len_t data_len,
400 ipmi_context_t context)
401{
402 ipmi_ret_t rc = IPMI_CC_OK;
403
404 auto* res = reinterpret_cast<acpi_state::ACPIState*>(response);
405
406 sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()};
407
408 *data_len = 0;
409
410 try
411 {
412 auto acpiObject = ipmi::getDbusObject(bus, acpi_state::acpiInterface);
413
414 auto sysACPIVal = ipmi::getDbusProperty(
415 bus, acpiObject.second, acpiObject.first, acpi_state::acpiInterface,
416 acpi_state::sysACPIProp);
417 auto sysACPI = acpi_state::ACPIPowerState::convertACPIFromString(
William A. Kennington IIIdfad4862018-11-19 17:45:35 -0800418 variant_ns::get<std::string>(sysACPIVal));
Yong Li18d77262018-10-09 01:59:45 +0800419 res->sysACPIState =
420 static_cast<uint8_t>(acpi_state::dbusToIPMI.at(sysACPI));
421
422 auto devACPIVal = ipmi::getDbusProperty(
423 bus, acpiObject.second, acpiObject.first, acpi_state::acpiInterface,
424 acpi_state::devACPIProp);
425 auto devACPI = acpi_state::ACPIPowerState::convertACPIFromString(
William A. Kennington IIIdfad4862018-11-19 17:45:35 -0800426 variant_ns::get<std::string>(devACPIVal));
Yong Li18d77262018-10-09 01:59:45 +0800427 res->devACPIState =
428 static_cast<uint8_t>(acpi_state::dbusToIPMI.at(devACPI));
429
430 *data_len = sizeof(acpi_state::ACPIState);
431 }
432 catch (const InternalFailure& e)
433 {
434 log<level::ERR>("Failed in get ACPI property");
435 return IPMI_CC_UNSPECIFIED_ERROR;
436 }
Chris Austen6caf28b2015-10-13 12:40:40 -0500437 return rc;
438}
439
Chris Austen7303bdc2016-04-17 11:50:54 -0500440typedef struct
441{
442 char major;
443 char minor;
Chris Austen176c9652016-04-30 16:32:17 -0500444 uint16_t d[2];
Chris Austen7303bdc2016-04-17 11:50:54 -0500445} rev_t;
446
Dinesh Chinari2b7e07d2017-11-08 15:38:50 -0600447/* Currently supports the vx.x-x-[-x] and v1.x.x-x-[-x] format. It will */
448/* return -1 if not in those formats, this routine knows how to parse */
Chris Austen7303bdc2016-04-17 11:50:54 -0500449/* version = v0.6-19-gf363f61-dirty */
450/* ^ ^ ^^ ^ */
451/* | | |----------|-- additional details */
452/* | |---------------- Minor */
453/* |------------------ Major */
Dinesh Chinari2b7e07d2017-11-08 15:38:50 -0600454/* and version = v1.99.10-113-g65edf7d-r3-0-g9e4f715 */
455/* ^ ^ ^^ ^ */
456/* | | |--|---------- additional details */
457/* | |---------------- Minor */
458/* |------------------ Major */
Chris Austen7303bdc2016-04-17 11:50:54 -0500459/* Additional details : If the option group exists it will force Auxiliary */
460/* Firmware Revision Information 4th byte to 1 indicating the build was */
461/* derived with additional edits */
Patrick Venture0b02be92018-08-31 11:55:55 -0700462int convert_version(const char* p, rev_t* rev)
Chris Austen7303bdc2016-04-17 11:50:54 -0500463{
Dinesh Chinari2b7e07d2017-11-08 15:38:50 -0600464 std::string s(p);
465 std::string token;
Chris Austen176c9652016-04-30 16:32:17 -0500466 uint16_t commits;
Chris Austen7303bdc2016-04-17 11:50:54 -0500467
Patrick Venture0b02be92018-08-31 11:55:55 -0700468 auto location = s.find_first_of('v');
Dinesh Chinari2b7e07d2017-11-08 15:38:50 -0600469 if (location != std::string::npos)
470 {
Patrick Venture0b02be92018-08-31 11:55:55 -0700471 s = s.substr(location + 1);
Chris Austen176c9652016-04-30 16:32:17 -0500472 }
Chris Austen7303bdc2016-04-17 11:50:54 -0500473
Dinesh Chinari2b7e07d2017-11-08 15:38:50 -0600474 if (!s.empty())
475 {
476 location = s.find_first_of(".");
477 if (location != std::string::npos)
478 {
Patrick Venture0b02be92018-08-31 11:55:55 -0700479 rev->major =
480 static_cast<char>(std::stoi(s.substr(0, location), 0, 16));
481 token = s.substr(location + 1);
Dinesh Chinari2b7e07d2017-11-08 15:38:50 -0600482 }
Chris Austen176c9652016-04-30 16:32:17 -0500483
Dinesh Chinari2b7e07d2017-11-08 15:38:50 -0600484 if (!token.empty())
485 {
486 location = token.find_first_of(".-");
487 if (location != std::string::npos)
488 {
Patrick Ventured2117022018-02-06 08:54:37 -0800489 rev->minor = static_cast<char>(
Patrick Venture0b02be92018-08-31 11:55:55 -0700490 std::stoi(token.substr(0, location), 0, 16));
491 token = token.substr(location + 1);
Dinesh Chinari2b7e07d2017-11-08 15:38:50 -0600492 }
493 }
Chris Austen7303bdc2016-04-17 11:50:54 -0500494
Dinesh Chinari2b7e07d2017-11-08 15:38:50 -0600495 // Capture the number of commits on top of the minor tag.
496 // I'm using BE format like the ipmi spec asked for
497 location = token.find_first_of(".-");
498 if (!token.empty())
499 {
500 commits = std::stoi(token.substr(0, location), 0, 16);
Patrick Venture0b02be92018-08-31 11:55:55 -0700501 rev->d[0] = (commits >> 8) | (commits << 8);
Dinesh Chinari2b7e07d2017-11-08 15:38:50 -0600502
503 // commit number we skip
504 location = token.find_first_of(".-");
505 if (location != std::string::npos)
506 {
Patrick Venture0b02be92018-08-31 11:55:55 -0700507 token = token.substr(location + 1);
Dinesh Chinari2b7e07d2017-11-08 15:38:50 -0600508 }
509 }
Patrick Venture0b02be92018-08-31 11:55:55 -0700510 else
511 {
Dinesh Chinari2b7e07d2017-11-08 15:38:50 -0600512 rev->d[0] = 0;
513 }
514
515 if (location != std::string::npos)
516 {
Patrick Venture0b02be92018-08-31 11:55:55 -0700517 token = token.substr(location + 1);
Dinesh Chinari2b7e07d2017-11-08 15:38:50 -0600518 }
519
520 // Any value of the optional parameter forces it to 1
521 location = token.find_first_of(".-");
522 if (location != std::string::npos)
523 {
Patrick Venture0b02be92018-08-31 11:55:55 -0700524 token = token.substr(location + 1);
Dinesh Chinari2b7e07d2017-11-08 15:38:50 -0600525 }
526 commits = (!token.empty()) ? 1 : 0;
527
Patrick Venture0b02be92018-08-31 11:55:55 -0700528 // We do this operation to get this displayed in least significant bytes
529 // of ipmitool device id command.
530 rev->d[1] = (commits >> 8) | (commits << 8);
Dinesh Chinari2b7e07d2017-11-08 15:38:50 -0600531 }
532
Chris Austen7303bdc2016-04-17 11:50:54 -0500533 return 0;
534}
535
Adriana Kobylak3a552e12015-10-19 16:11:00 -0500536ipmi_ret_t ipmi_app_get_device_id(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
Patrick Venture0b02be92018-08-31 11:55:55 -0700537 ipmi_request_t request,
538 ipmi_response_t response,
539 ipmi_data_len_t data_len,
540 ipmi_context_t context)
Chris Austen6caf28b2015-10-13 12:40:40 -0500541{
542 ipmi_ret_t rc = IPMI_CC_OK;
Nagaraju Goruganti744398d2018-02-20 09:52:00 -0600543 int r = -1;
Chris Austen7303bdc2016-04-17 11:50:54 -0500544 rev_t rev = {0};
David Cobbleya1adb072017-11-21 15:58:13 -0800545 static ipmi_device_id_t dev_id{};
546 static bool dev_id_initialized = false;
547 const char* filename = "/usr/share/ipmi-providers/dev_id.json";
Alexander Amelkinba19c182018-09-04 15:49:36 +0300548 constexpr auto ipmiDevIdStateShift = 7;
549 constexpr auto ipmiDevIdFw1Mask = ~(1 << ipmiDevIdStateShift);
Chris Austen6caf28b2015-10-13 12:40:40 -0500550
551 // Data length
Chris Austen7303bdc2016-04-17 11:50:54 -0500552 *data_len = sizeof(dev_id);
553
David Cobbleya1adb072017-11-21 15:58:13 -0800554 if (!dev_id_initialized)
555 {
Nagaraju Goruganti744398d2018-02-20 09:52:00 -0600556 try
557 {
558 auto version = getActiveSoftwareVersionInfo();
559 r = convert_version(version.c_str(), &rev);
David Cobbleya1adb072017-11-21 15:58:13 -0800560 }
Nagaraju Goruganti744398d2018-02-20 09:52:00 -0600561 catch (const std::exception& e)
562 {
563 log<level::ERR>(e.what());
564 }
Nan Liee0cb902016-07-11 15:38:03 +0800565
Patrick Venture0b02be92018-08-31 11:55:55 -0700566 if (r >= 0)
567 {
Nagaraju Goruganti744398d2018-02-20 09:52:00 -0600568 // bit7 identifies if the device is available
569 // 0=normal operation
570 // 1=device firmware, SDR update,
571 // or self-initialization in progress.
Alexander Amelkinba19c182018-09-04 15:49:36 +0300572 // The availability may change in run time, so mask here
573 // and initialize later.
574 dev_id.fw[0] = rev.major & ipmiDevIdFw1Mask;
Nagaraju Goruganti744398d2018-02-20 09:52:00 -0600575
576 rev.minor = (rev.minor > 99 ? 99 : rev.minor);
577 dev_id.fw[1] = rev.minor % 10 + (rev.minor / 10) * 16;
Patrick Ventureb51bf9c2018-09-10 15:53:14 -0700578 std::memcpy(&dev_id.aux, rev.d, 4);
David Cobbleya1adb072017-11-21 15:58:13 -0800579 }
Nan Liee0cb902016-07-11 15:38:03 +0800580
David Cobbleya1adb072017-11-21 15:58:13 -0800581 // IPMI Spec version 2.0
582 dev_id.ipmi_ver = 2;
Adriana Kobylak0e912642016-06-22 16:54:39 -0500583
David Cobbleya1adb072017-11-21 15:58:13 -0800584 std::ifstream dev_id_file(filename);
585 if (dev_id_file.is_open())
586 {
587 auto data = nlohmann::json::parse(dev_id_file, nullptr, false);
588 if (!data.is_discarded())
589 {
590 dev_id.id = data.value("id", 0);
591 dev_id.revision = data.value("revision", 0);
592 dev_id.addn_dev_support = data.value("addn_dev_support", 0);
593 dev_id.manuf_id[2] = data.value("manuf_id", 0) >> 16;
594 dev_id.manuf_id[1] = data.value("manuf_id", 0) >> 8;
595 dev_id.manuf_id[0] = data.value("manuf_id", 0);
596 dev_id.prod_id[1] = data.value("prod_id", 0) >> 8;
597 dev_id.prod_id[0] = data.value("prod_id", 0);
Tom Josephaf8a0982018-03-09 07:54:02 -0600598 dev_id.aux[3] = data.value("aux", 0);
599 dev_id.aux[2] = data.value("aux", 0) >> 8;
600 dev_id.aux[1] = data.value("aux", 0) >> 16;
601 dev_id.aux[0] = data.value("aux", 0) >> 24;
David Cobbleya1adb072017-11-21 15:58:13 -0800602
Patrick Venture0b02be92018-08-31 11:55:55 -0700603 // Don't read the file every time if successful
David Cobbleya1adb072017-11-21 15:58:13 -0800604 dev_id_initialized = true;
605 }
606 else
607 {
608 log<level::ERR>("Device ID JSON parser failure");
609 rc = IPMI_CC_UNSPECIFIED_ERROR;
610 }
611 }
612 else
613 {
614 log<level::ERR>("Device ID file not found");
615 rc = IPMI_CC_UNSPECIFIED_ERROR;
Chris Austen7303bdc2016-04-17 11:50:54 -0500616 }
617 }
Chris Austen6caf28b2015-10-13 12:40:40 -0500618
Alexander Amelkinba19c182018-09-04 15:49:36 +0300619 // Set availability to the actual current BMC state
620 dev_id.fw[0] &= ipmiDevIdFw1Mask;
621 if (!getCurrentBmcState())
622 {
623 dev_id.fw[0] |= (1 << ipmiDevIdStateShift);
624 }
625
Chris Austen6caf28b2015-10-13 12:40:40 -0500626 // Pack the actual response
Patrick Ventureb51bf9c2018-09-10 15:53:14 -0700627 std::memcpy(response, &dev_id, *data_len);
Nagaraju Goruganti744398d2018-02-20 09:52:00 -0600628
Chris Austen6caf28b2015-10-13 12:40:40 -0500629 return rc;
630}
631
Nan Li41fa24a2016-11-10 20:12:37 +0800632ipmi_ret_t ipmi_app_get_self_test_results(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
Patrick Venture0b02be92018-08-31 11:55:55 -0700633 ipmi_request_t request,
634 ipmi_response_t response,
635 ipmi_data_len_t data_len,
636 ipmi_context_t context)
Nan Li41fa24a2016-11-10 20:12:37 +0800637{
638 ipmi_ret_t rc = IPMI_CC_OK;
639
640 // Byte 2:
641 // 55h - No error.
Gunnar Mills8991dd62017-10-25 17:11:29 -0500642 // 56h - Self Test function not implemented in this controller.
Nan Li41fa24a2016-11-10 20:12:37 +0800643 // 57h - Corrupted or inaccesssible data or devices.
644 // 58h - Fatal hardware error.
645 // FFh - reserved.
646 // all other: Device-specific 'internal failure'.
647 // Byte 3:
648 // For byte 2 = 55h, 56h, FFh: 00h
649 // For byte 2 = 58h, all other: Device-specific
650 // For byte 2 = 57h: self-test error bitfield.
651 // Note: returning 57h does not imply that all test were run.
652 // [7] 1b = Cannot access SEL device.
653 // [6] 1b = Cannot access SDR Repository.
654 // [5] 1b = Cannot access BMC FRU device.
655 // [4] 1b = IPMB signal lines do not respond.
656 // [3] 1b = SDR Repository empty.
657 // [2] 1b = Internal Use Area of BMC FRU corrupted.
658 // [1] 1b = controller update 'boot block' firmware corrupted.
659 // [0] 1b = controller operational firmware corrupted.
660
661 char selftestresults[2] = {0};
662
663 *data_len = 2;
664
665 selftestresults[0] = 0x56;
666 selftestresults[1] = 0;
667
Patrick Ventureb51bf9c2018-09-10 15:53:14 -0700668 std::memcpy(response, selftestresults, *data_len);
Nan Li41fa24a2016-11-10 20:12:37 +0800669
670 return rc;
671}
672
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500673ipmi_ret_t ipmi_app_get_device_guid(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
Patrick Venture0b02be92018-08-31 11:55:55 -0700674 ipmi_request_t request,
675 ipmi_response_t response,
676 ipmi_data_len_t data_len,
677 ipmi_context_t context)
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500678{
Patrick Venture0b02be92018-08-31 11:55:55 -0700679 const char* objname = "/org/openbmc/control/chassis0";
680 const char* iface = "org.freedesktop.DBus.Properties";
681 const char* chassis_iface = "org.openbmc.control.Chassis";
682 sd_bus_message* reply = NULL;
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500683 sd_bus_error error = SD_BUS_ERROR_NULL;
684 int r = 0;
Patrick Venture0b02be92018-08-31 11:55:55 -0700685 char* uuid = NULL;
686 char* busname = NULL;
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500687
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500688 // UUID is in RFC4122 format. Ex: 61a39523-78f2-11e5-9862-e6402cfc3223
Patrick Ventured2117022018-02-06 08:54:37 -0800689 // Per IPMI Spec 2.0 need to convert to 16 hex bytes and reverse the byte
690 // order
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500691 // Ex: 0x2332fc2c40e66298e511f2782395a361
692
Patrick Venture0b02be92018-08-31 11:55:55 -0700693 const int resp_size = 16; // Response is 16 hex bytes per IPMI Spec
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500694 uint8_t resp_uuid[resp_size]; // Array to hold the formatted response
Patrick Ventured2117022018-02-06 08:54:37 -0800695 // Point resp end of array to save in reverse order
Patrick Venture0b02be92018-08-31 11:55:55 -0700696 int resp_loc = resp_size - 1;
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500697 int i = 0;
Patrick Venture0b02be92018-08-31 11:55:55 -0700698 char* tokptr = NULL;
699 char* id_octet = NULL;
Emily Shafferedb8bb02018-09-27 14:50:15 -0700700 size_t total_uuid_size = 0;
701 // 1 byte of resp is built from 2 chars of uuid.
702 constexpr size_t max_uuid_size = 2 * resp_size;
vishwa1eaea4f2016-02-26 11:57:40 -0600703
704 // Status code.
705 ipmi_ret_t rc = IPMI_CC_OK;
706 *data_len = 0;
707
vishwa1eaea4f2016-02-26 11:57:40 -0600708 // Call Get properties method with the interface and property name
Sergey Solomineb9b8142016-08-23 09:07:28 -0500709 r = mapper_get_service(bus, objname, &busname);
Patrick Venture0b02be92018-08-31 11:55:55 -0700710 if (r < 0)
711 {
712 log<level::ERR>("Failed to get bus name", entry("BUS=%s", objname),
Aditya Saripalli5fb14602017-11-09 14:46:27 +0530713 entry("ERRNO=0x%X", -r));
Sergey Solomineb9b8142016-08-23 09:07:28 -0500714 goto finish;
715 }
Patrick Venture0b02be92018-08-31 11:55:55 -0700716 r = sd_bus_call_method(bus, busname, objname, iface, "Get", &error, &reply,
717 "ss", chassis_iface, "uuid");
vishwa1eaea4f2016-02-26 11:57:40 -0600718 if (r < 0)
719 {
Patrick Venture0b02be92018-08-31 11:55:55 -0700720 log<level::ERR>("Failed to call Get Method", entry("ERRNO=0x%X", -r));
vishwa1eaea4f2016-02-26 11:57:40 -0600721 rc = IPMI_CC_UNSPECIFIED_ERROR;
722 goto finish;
723 }
724
725 r = sd_bus_message_read(reply, "v", "s", &uuid);
726 if (r < 0 || uuid == NULL)
727 {
Patrick Venture0b02be92018-08-31 11:55:55 -0700728 log<level::ERR>("Failed to get a response", entry("ERRNO=0x%X", -r));
vishwa1eaea4f2016-02-26 11:57:40 -0600729 rc = IPMI_CC_RESPONSE_ERROR;
730 goto finish;
731 }
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500732
733 // Traverse the UUID
Patrick Ventured2117022018-02-06 08:54:37 -0800734 // Get the UUID octects separated by dash
735 id_octet = strtok_r(uuid, "-", &tokptr);
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500736
737 if (id_octet == NULL)
vishwa1eaea4f2016-02-26 11:57:40 -0600738 {
739 // Error
Patrick Venture0b02be92018-08-31 11:55:55 -0700740 log<level::ERR>("Unexpected UUID format", entry("UUID=%s", uuid));
vishwa1eaea4f2016-02-26 11:57:40 -0600741 rc = IPMI_CC_RESPONSE_ERROR;
742 goto finish;
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500743 }
744
745 while (id_octet != NULL)
746 {
747 // Calculate the octet string size since it varies
748 // Divide it by 2 for the array size since 1 byte is built from 2 chars
Patrick Venture0b02be92018-08-31 11:55:55 -0700749 int tmp_size = strlen(id_octet) / 2;
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500750
Emily Shafferedb8bb02018-09-27 14:50:15 -0700751 // Check if total UUID size has been exceeded
752 if ((total_uuid_size += strlen(id_octet)) > max_uuid_size)
753 {
754 // Error - UUID too long to store
755 log<level::ERR>("UUID too long", entry("UUID=%s", uuid));
756 rc = IPMI_CC_RESPONSE_ERROR;
757 goto finish;
758 }
759
Patrick Venture0b02be92018-08-31 11:55:55 -0700760 for (i = 0; i < tmp_size; i++)
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500761 {
Patrick Ventured2117022018-02-06 08:54:37 -0800762 // Holder of the 2 chars that will become a byte
763 char tmp_array[3] = {0};
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500764 strncpy(tmp_array, id_octet, 2); // 2 chars at a time
765
766 int resp_byte = strtoul(tmp_array, NULL, 16); // Convert to hex byte
Patrick Ventured2117022018-02-06 08:54:37 -0800767 // Copy end to first
Patrick Ventureb51bf9c2018-09-10 15:53:14 -0700768 std::memcpy((void*)&resp_uuid[resp_loc], &resp_byte, 1);
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500769 resp_loc--;
Patrick Venture0b02be92018-08-31 11:55:55 -0700770 id_octet += 2; // Finished with the 2 chars, advance
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500771 }
Patrick Venture0b02be92018-08-31 11:55:55 -0700772 id_octet = strtok_r(NULL, "-", &tokptr); // Get next octet
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500773 }
774
775 // Data length
776 *data_len = resp_size;
777
778 // Pack the actual response
Patrick Ventureb51bf9c2018-09-10 15:53:14 -0700779 std::memcpy(response, &resp_uuid, *data_len);
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500780
vishwa1eaea4f2016-02-26 11:57:40 -0600781finish:
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500782 sd_bus_error_free(&error);
vishwa1eaea4f2016-02-26 11:57:40 -0600783 reply = sd_bus_message_unref(reply);
Sergey Solomineb9b8142016-08-23 09:07:28 -0500784 free(busname);
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500785
786 return rc;
787}
Chris Austen6caf28b2015-10-13 12:40:40 -0500788
Adriana Kobylak3a552e12015-10-19 16:11:00 -0500789ipmi_ret_t ipmi_app_get_bt_capabilities(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
Patrick Venture0b02be92018-08-31 11:55:55 -0700790 ipmi_request_t request,
791 ipmi_response_t response,
792 ipmi_data_len_t data_len,
793 ipmi_context_t context)
vishwabmcba0bd5f2015-09-30 16:50:23 +0530794{
vishwabmcba0bd5f2015-09-30 16:50:23 +0530795
796 // Status code.
797 ipmi_ret_t rc = IPMI_CC_OK;
798
Adriana Kobylak88ad8152016-12-13 10:09:08 -0600799 // Per IPMI 2.0 spec, the input and output buffer size must be the max
800 // buffer size minus one byte to allocate space for the length byte.
Patrick Venture0b02be92018-08-31 11:55:55 -0700801 uint8_t str[] = {0x01, MAX_IPMI_BUFFER - 1, MAX_IPMI_BUFFER - 1, 0x0A,
802 0x01};
vishwabmcba0bd5f2015-09-30 16:50:23 +0530803
804 // Data length
805 *data_len = sizeof(str);
806
807 // Pack the actual response
Patrick Ventureb51bf9c2018-09-10 15:53:14 -0700808 std::memcpy(response, &str, *data_len);
vishwabmcba0bd5f2015-09-30 16:50:23 +0530809
810 return rc;
811}
812
Adriana Kobylak3a552e12015-10-19 16:11:00 -0500813ipmi_ret_t ipmi_app_wildcard_handler(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
Patrick Venture0b02be92018-08-31 11:55:55 -0700814 ipmi_request_t request,
815 ipmi_response_t response,
816 ipmi_data_len_t data_len,
817 ipmi_context_t context)
vishwabmcba0bd5f2015-09-30 16:50:23 +0530818{
vishwabmcba0bd5f2015-09-30 16:50:23 +0530819 // Status code.
Nan Li70aa8d92016-08-29 00:11:10 +0800820 ipmi_ret_t rc = IPMI_CC_INVALID;
vishwabmcba0bd5f2015-09-30 16:50:23 +0530821
822 *data_len = strlen("THIS IS WILDCARD");
823
824 // Now pack actual response
Patrick Ventureb51bf9c2018-09-10 15:53:14 -0700825 std::memcpy(response, "THIS IS WILDCARD", *data_len);
vishwabmcba0bd5f2015-09-30 16:50:23 +0530826
827 return rc;
828}
829
Marri Devender Rao5e007a52018-01-08 06:18:36 -0600830ipmi_ret_t ipmi_app_get_sys_guid(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
Patrick Venture0b02be92018-08-31 11:55:55 -0700831 ipmi_request_t request,
832 ipmi_response_t response,
833 ipmi_data_len_t data_len,
834 ipmi_context_t context)
Marri Devender Rao5e007a52018-01-08 06:18:36 -0600835
836{
837 ipmi_ret_t rc = IPMI_CC_OK;
838 sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()};
839
840 try
841 {
842 // Get the Inventory object implementing BMC interface
843 ipmi::DbusObjectInfo bmcObject =
844 ipmi::getDbusObject(bus, bmc_interface);
845
846 // Read UUID property value from bmcObject
847 // UUID is in RFC4122 format Ex: 61a39523-78f2-11e5-9862-e6402cfc3223
Patrick Venture0b02be92018-08-31 11:55:55 -0700848 auto variant =
849 ipmi::getDbusProperty(bus, bmcObject.second, bmcObject.first,
850 bmc_guid_interface, bmc_guid_property);
William A. Kennington III4c008022018-10-12 17:18:14 -0700851 std::string guidProp = variant_ns::get<std::string>(variant);
Marri Devender Rao5e007a52018-01-08 06:18:36 -0600852
853 // Erase "-" characters from the property value
854 guidProp.erase(std::remove(guidProp.begin(), guidProp.end(), '-'),
Patrick Venture0b02be92018-08-31 11:55:55 -0700855 guidProp.end());
Marri Devender Rao5e007a52018-01-08 06:18:36 -0600856
857 auto guidPropLen = guidProp.length();
858 // Validate UUID data
859 // Divide by 2 as 1 byte is built from 2 chars
Patrick Venture0b02be92018-08-31 11:55:55 -0700860 if ((guidPropLen <= 0) || ((guidPropLen / 2) != bmc_guid_len))
Marri Devender Rao5e007a52018-01-08 06:18:36 -0600861
862 {
863 log<level::ERR>("Invalid UUID property value",
Patrick Venture0b02be92018-08-31 11:55:55 -0700864 entry("UUID_LENGTH=%d", guidPropLen));
Marri Devender Rao5e007a52018-01-08 06:18:36 -0600865 return IPMI_CC_RESPONSE_ERROR;
866 }
867
868 // Convert data in RFC4122(MSB) format to LSB format
869 // Get 2 characters at a time as 1 byte is built from 2 chars and
870 // convert to hex byte
871 // TODO: Data printed for GUID command is not as per the
872 // GUID format defined in IPMI specification 2.0 section 20.8
873 // Ticket raised: https://sourceforge.net/p/ipmitool/bugs/501/
874 uint8_t respGuid[bmc_guid_len];
875 for (size_t i = 0, respLoc = (bmc_guid_len - 1);
Patrick Venture0b02be92018-08-31 11:55:55 -0700876 i < guidPropLen && respLoc >= 0; i += 2, respLoc--)
Marri Devender Rao5e007a52018-01-08 06:18:36 -0600877 {
878 auto value = static_cast<uint8_t>(
Patrick Venture0b02be92018-08-31 11:55:55 -0700879 std::stoi(guidProp.substr(i, 2).c_str(), NULL, 16));
Marri Devender Rao5e007a52018-01-08 06:18:36 -0600880 respGuid[respLoc] = value;
881 }
882
883 *data_len = bmc_guid_len;
Patrick Ventureb51bf9c2018-09-10 15:53:14 -0700884 std::memcpy(response, &respGuid, bmc_guid_len);
Marri Devender Rao5e007a52018-01-08 06:18:36 -0600885 }
886 catch (const InternalFailure& e)
887 {
888 log<level::ERR>("Failed in reading BMC UUID property",
889 entry("INTERFACE=%s", bmc_interface),
890 entry("PROPERTY_INTERFACE=%s", bmc_guid_interface),
891 entry("PROPERTY=%s", bmc_guid_property));
892 return IPMI_CC_UNSPECIFIED_ERROR;
893 }
894 return rc;
895}
896
Xo Wangf542e8b2017-08-09 15:34:16 -0700897static std::unique_ptr<SysInfoParamStore> sysInfoParamStore;
898
Xo Wang87651332017-08-11 10:17:59 -0700899static std::string sysInfoReadSystemName()
900{
901 // Use the BMC hostname as the "System Name."
902 char hostname[HOST_NAME_MAX + 1] = {};
903 if (gethostname(hostname, HOST_NAME_MAX) != 0)
904 {
905 perror("System info parameter: system name");
906 }
907 return hostname;
908}
909
Xo Wangf542e8b2017-08-09 15:34:16 -0700910struct IpmiSysInfoResp
911{
912 uint8_t paramRevision;
913 uint8_t setSelector;
914 union
915 {
916 struct
917 {
918 uint8_t encoding;
919 uint8_t stringLen;
920 uint8_t stringData0[14];
921 } __attribute__((packed));
922 uint8_t stringDataN[16];
923 uint8_t byteData;
924 };
925} __attribute__((packed));
926
927/**
928 * Split a string into (up to) 16-byte chunks as expected in response for get
929 * system info parameter.
930 *
931 * @param[in] fullString: Input string to be split
932 * @param[in] chunkIndex: Index of the chunk to be written out
933 * @param[in,out] chunk: Output data buffer; must have 14 byte capacity if
934 * chunk_index = 0 and 16-byte capacity otherwise
935 * @return the number of bytes written into the output buffer, or -EINVAL for
936 * invalid arguments.
937 */
938static int splitStringParam(const std::string& fullString, int chunkIndex,
939 uint8_t* chunk)
940{
941 constexpr int maxChunk = 255;
942 constexpr int smallChunk = 14;
943 constexpr int chunkSize = 16;
944 if (chunkIndex > maxChunk || chunk == nullptr)
945 {
946 return -EINVAL;
947 }
948 try
949 {
950 std::string output;
951 if (chunkIndex == 0)
952 {
953 // Output must have 14 byte capacity.
954 output = fullString.substr(0, smallChunk);
955 }
956 else
957 {
958 // Output must have 16 byte capacity.
959 output = fullString.substr((chunkIndex * chunkSize) - 2, chunkSize);
960 }
961
962 std::memcpy(chunk, output.c_str(), output.length());
963 return output.length();
964 }
965 catch (const std::out_of_range& e)
966 {
967 // The position was beyond the end.
968 return -EINVAL;
969 }
970}
971
972/**
973 * Packs the Get Sys Info Request Item into the response.
974 *
975 * @param[in] paramString - the parameter.
976 * @param[in] setSelector - the selector
977 * @param[in,out] resp - the System info response.
978 * @return The number of bytes packed or failure from splitStringParam().
979 */
980static int packGetSysInfoResp(const std::string& paramString,
981 uint8_t setSelector, IpmiSysInfoResp* resp)
982{
983 uint8_t* dataBuffer = resp->stringDataN;
984 resp->setSelector = setSelector;
985 if (resp->setSelector == 0) // First chunk has only 14 bytes.
986 {
987 resp->encoding = 0;
988 resp->stringLen = paramString.length();
989 dataBuffer = resp->stringData0;
990 }
991 return splitStringParam(paramString, resp->setSelector, dataBuffer);
992}
993
994ipmi_ret_t ipmi_app_get_system_info(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
995 ipmi_request_t request,
996 ipmi_response_t response,
997 ipmi_data_len_t dataLen,
998 ipmi_context_t context)
999{
1000 IpmiSysInfoResp resp = {};
1001 size_t respLen = 0;
1002 uint8_t* const reqData = static_cast<uint8_t*>(request);
1003 std::string paramString;
1004 bool found;
1005 std::tuple<bool, std::string> ret;
1006 constexpr int minRequestSize = 4;
1007 constexpr int paramSelector = 1;
1008 constexpr uint8_t revisionOnly = 0x80;
1009 const uint8_t paramRequested = reqData[paramSelector];
1010 int rc;
1011
1012 if (*dataLen < minRequestSize)
1013 {
1014 return IPMI_CC_REQ_DATA_LEN_INVALID;
1015 }
1016
1017 *dataLen = 0; // default to 0.
1018
1019 // Parameters revision as of IPMI spec v2.0 rev. 1.1 (Feb 11, 2014 E6)
1020 resp.paramRevision = 0x11;
1021 if (reqData[0] & revisionOnly) // Get parameter revision only
1022 {
1023 respLen = 1;
1024 goto writeResponse;
1025 }
1026
1027 // The "Set In Progress" parameter can be used for rollback of parameter
1028 // data and is not implemented.
1029 if (paramRequested == 0)
1030 {
1031 resp.byteData = 0;
1032 respLen = 2;
1033 goto writeResponse;
1034 }
1035
1036 if (sysInfoParamStore == nullptr)
1037 {
1038 sysInfoParamStore = std::make_unique<SysInfoParamStore>();
Xo Wang87651332017-08-11 10:17:59 -07001039 sysInfoParamStore->update(IPMI_SYSINFO_SYSTEM_NAME,
1040 sysInfoReadSystemName);
Xo Wangf542e8b2017-08-09 15:34:16 -07001041 }
1042
1043 // Parameters other than Set In Progress are assumed to be strings.
1044 ret = sysInfoParamStore->lookup(paramRequested);
1045 found = std::get<0>(ret);
1046 paramString = std::get<1>(ret);
1047 if (!found)
1048 {
1049 return IPMI_CC_SYSTEM_INFO_PARAMETER_NOT_SUPPORTED;
1050 }
1051 // TODO: Cache each parameter across multiple calls, until the whole string
1052 // has been read out. Otherwise, it's possible for a parameter to change
1053 // between requests for its chunks, returning chunks incoherent with each
1054 // other. For now, the parameter store is simply required to have only
1055 // idempotent callbacks.
1056 rc = packGetSysInfoResp(paramString, reqData[2], &resp);
1057 if (rc == -EINVAL)
1058 {
1059 return IPMI_CC_RESPONSE_ERROR;
1060 }
1061
1062 respLen = sizeof(resp); // Write entire string data chunk in response.
1063
1064writeResponse:
1065 std::memcpy(response, &resp, sizeof(resp));
1066 *dataLen = respLen;
1067 return IPMI_CC_OK;
1068}
1069
Chris Austen6caf28b2015-10-13 12:40:40 -05001070void register_netfn_app_functions()
vishwabmcba0bd5f2015-09-30 16:50:23 +05301071{
Tom05732372016-09-06 17:21:23 +05301072 // <Get BT Interface Capabilities>
Patrick Venture0b02be92018-08-31 11:55:55 -07001073 ipmi_register_callback(NETFUN_APP, IPMI_CMD_GET_CAP_BIT, NULL,
1074 ipmi_app_get_bt_capabilities, PRIVILEGE_USER);
Chris Austen6caf28b2015-10-13 12:40:40 -05001075
Tom05732372016-09-06 17:21:23 +05301076 // <Wildcard Command>
Patrick Venture0b02be92018-08-31 11:55:55 -07001077 ipmi_register_callback(NETFUN_APP, IPMI_CMD_WILDCARD, NULL,
1078 ipmi_app_wildcard_handler, PRIVILEGE_USER);
Chris Austen6caf28b2015-10-13 12:40:40 -05001079
Tom05732372016-09-06 17:21:23 +05301080 // <Reset Watchdog Timer>
Patrick Venture0b02be92018-08-31 11:55:55 -07001081 ipmi_register_callback(NETFUN_APP, IPMI_CMD_RESET_WD, NULL,
1082 ipmi_app_watchdog_reset, PRIVILEGE_OPERATOR);
Chris Austen6caf28b2015-10-13 12:40:40 -05001083
Tom05732372016-09-06 17:21:23 +05301084 // <Set Watchdog Timer>
Patrick Venture0b02be92018-08-31 11:55:55 -07001085 ipmi_register_callback(NETFUN_APP, IPMI_CMD_SET_WD, NULL,
1086 ipmi_app_watchdog_set, PRIVILEGE_OPERATOR);
Chris Austen6caf28b2015-10-13 12:40:40 -05001087
William A. Kennington III73f44512018-02-09 15:28:46 -08001088 // <Get Watchdog Timer>
Patrick Venture0b02be92018-08-31 11:55:55 -07001089 ipmi_register_callback(NETFUN_APP, IPMI_CMD_GET_WD, NULL,
1090 ipmi_app_watchdog_get, PRIVILEGE_OPERATOR);
William A. Kennington III73f44512018-02-09 15:28:46 -08001091
Tom05732372016-09-06 17:21:23 +05301092 // <Get Device ID>
Patrick Venture0b02be92018-08-31 11:55:55 -07001093 ipmi_register_callback(NETFUN_APP, IPMI_CMD_GET_DEVICE_ID, NULL,
1094 ipmi_app_get_device_id, PRIVILEGE_USER);
Chris Austen6caf28b2015-10-13 12:40:40 -05001095
Tom05732372016-09-06 17:21:23 +05301096 // <Get Self Test Results>
Patrick Venture0b02be92018-08-31 11:55:55 -07001097 ipmi_register_callback(NETFUN_APP, IPMI_CMD_GET_SELF_TEST_RESULTS, NULL,
1098 ipmi_app_get_self_test_results, PRIVILEGE_USER);
Nan Li41fa24a2016-11-10 20:12:37 +08001099
Tom05732372016-09-06 17:21:23 +05301100 // <Get Device GUID>
Patrick Venture0b02be92018-08-31 11:55:55 -07001101 ipmi_register_callback(NETFUN_APP, IPMI_CMD_GET_DEVICE_GUID, NULL,
1102 ipmi_app_get_device_guid, PRIVILEGE_USER);
Adriana Kobylakd100ee52015-10-20 17:02:37 -05001103
Tom05732372016-09-06 17:21:23 +05301104 // <Set ACPI Power State>
Patrick Venture0b02be92018-08-31 11:55:55 -07001105 ipmi_register_callback(NETFUN_APP, IPMI_CMD_SET_ACPI, NULL,
1106 ipmi_app_set_acpi_power_state, PRIVILEGE_ADMIN);
Chris Austen6caf28b2015-10-13 12:40:40 -05001107
Yong Li18d77262018-10-09 01:59:45 +08001108 // <Get ACPI Power State>
1109 ipmi_register_callback(NETFUN_APP, IPMI_CMD_GET_ACPI, NULL,
1110 ipmi_app_get_acpi_power_state, PRIVILEGE_ADMIN);
1111
Marri Devender Rao5e007a52018-01-08 06:18:36 -06001112 // <Get System GUID Command>
Patrick Venture0b02be92018-08-31 11:55:55 -07001113 ipmi_register_callback(NETFUN_APP, IPMI_CMD_GET_SYS_GUID, NULL,
1114 ipmi_app_get_sys_guid, PRIVILEGE_USER);
Tom Joseph7cbe2282018-03-21 21:17:33 +05301115
1116 // <Get Channel Cipher Suites Command>
Patrick Venture0b02be92018-08-31 11:55:55 -07001117 ipmi_register_callback(NETFUN_APP, IPMI_CMD_GET_CHAN_CIPHER_SUITES, NULL,
1118 getChannelCipherSuites, PRIVILEGE_CALLBACK);
Vernon Maueryd2a57de2019-03-25 12:45:28 -07001119
Xo Wangf542e8b2017-08-09 15:34:16 -07001120 // <Get System Info Command>
1121 ipmi_register_callback(NETFUN_APP, IPMI_CMD_GET_SYSTEM_INFO, NULL,
1122 ipmi_app_get_system_info, PRIVILEGE_USER);
vishwabmcba0bd5f2015-09-30 16:50:23 +05301123 return;
1124}