blob: fad727b1428eedebded7a9db8a755f4430810bb4 [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
Nagaraju Goruganti744398d2018-02-20 09:52:00 -060064/**
65 * @brief Returns the Version info from primary s/w object
66 *
67 * Get the Version info from the active s/w object which is having high
68 * "Priority" value(a smaller number is a higher priority) and "Purpose"
69 * is "BMC" from the list of all s/w objects those are implementing
70 * RedundancyPriority interface from the given softwareRoot path.
71 *
72 * @return On success returns the Version info from primary s/w object.
73 *
74 */
75std::string getActiveSoftwareVersionInfo()
76{
Vernon Mauery86a50822019-03-25 13:11:36 -070077 auto busp = getSdBus();
Nagaraju Goruganti744398d2018-02-20 09:52:00 -060078
79 std::string revision{};
Vernon Mauery86a50822019-03-25 13:11:36 -070080 ipmi::ObjectTree objectTree;
81 try
Nagaraju Goruganti744398d2018-02-20 09:52:00 -060082 {
Vernon Mauery86a50822019-03-25 13:11:36 -070083 objectTree =
84 ipmi::getAllDbusObjects(*busp, softwareRoot, redundancyIntf);
85 }
86 catch (sdbusplus::exception::SdBusError& e)
87 {
88 log<level::ERR>("Failed to fetch redundancy object from dbus",
89 entry("INTERFACE=%s", redundancyIntf),
90 entry("ERRMSG=%s", e.what()));
Nagaraju Goruganti744398d2018-02-20 09:52:00 -060091 elog<InternalFailure>();
92 }
93
94 auto objectFound = false;
95 for (auto& softObject : objectTree)
96 {
Vernon Mauery86a50822019-03-25 13:11:36 -070097 auto service =
98 ipmi::getService(*busp, redundancyIntf, softObject.first);
99 auto objValueTree =
100 ipmi::getManagedObjects(*busp, service, softwareRoot);
Nagaraju Goruganti744398d2018-02-20 09:52:00 -0600101
102 auto minPriority = 0xFF;
103 for (const auto& objIter : objValueTree)
104 {
105 try
106 {
107 auto& intfMap = objIter.second;
108 auto& redundancyPriorityProps = intfMap.at(redundancyIntf);
109 auto& versionProps = intfMap.at(versionIntf);
110 auto& activationProps = intfMap.at(activationIntf);
William A. Kennington III4c008022018-10-12 17:18:14 -0700111 auto priority = variant_ns::get<uint8_t>(
112 redundancyPriorityProps.at("Priority"));
113 auto purpose =
114 variant_ns::get<std::string>(versionProps.at("Purpose"));
115 auto activation = variant_ns::get<std::string>(
116 activationProps.at("Activation"));
117 auto version =
118 variant_ns::get<std::string>(versionProps.at("Version"));
Nagaraju Goruganti744398d2018-02-20 09:52:00 -0600119 if ((Version::convertVersionPurposeFromString(purpose) ==
120 Version::VersionPurpose::BMC) &&
121 (Activation::convertActivationsFromString(activation) ==
122 Activation::Activations::Active))
123 {
124 if (priority < minPriority)
125 {
126 minPriority = priority;
127 objectFound = true;
128 revision = std::move(version);
129 }
130 }
131 }
132 catch (const std::exception& e)
133 {
134 log<level::ERR>(e.what());
135 }
136 }
137 }
138
139 if (!objectFound)
140 {
141 log<level::ERR>("Could not found an BMC software Object");
142 elog<InternalFailure>();
143 }
144
145 return revision;
146}
147
Alexander Amelkinba19c182018-09-04 15:49:36 +0300148bool getCurrentBmcState()
149{
150 sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()};
151
152 // Get the Inventory object implementing the BMC interface
153 ipmi::DbusObjectInfo bmcObject =
154 ipmi::getDbusObject(bus, bmc_state_interface);
155 auto variant =
156 ipmi::getDbusProperty(bus, bmcObject.second, bmcObject.first,
157 bmc_state_interface, bmc_state_property);
158
William A. Kennington III4c008022018-10-12 17:18:14 -0700159 return variant_ns::holds_alternative<std::string>(variant) &&
160 BMC::convertBMCStateFromString(
161 variant_ns::get<std::string>(variant)) == BMC::BMCState::Ready;
Alexander Amelkinba19c182018-09-04 15:49:36 +0300162}
163
Yong Li18d77262018-10-09 01:59:45 +0800164namespace acpi_state
165{
166using namespace sdbusplus::xyz::openbmc_project::Control::Power::server;
167
168const static constexpr char* acpiObjPath =
169 "/xyz/openbmc_project/control/host0/acpi_power_state";
170const static constexpr char* acpiInterface =
171 "xyz.openbmc_project.Control.Power.ACPIPowerState";
172const static constexpr char* sysACPIProp = "SysACPIStatus";
173const static constexpr char* devACPIProp = "DevACPIStatus";
174
175enum class PowerStateType : uint8_t
176{
177 sysPowerState = 0x00,
178 devPowerState = 0x01,
179};
180
181// Defined in 20.6 of ipmi doc
182enum class PowerState : uint8_t
183{
184 s0G0D0 = 0x00,
185 s1D1 = 0x01,
186 s2D2 = 0x02,
187 s3D3 = 0x03,
188 s4 = 0x04,
189 s5G2 = 0x05,
190 s4S5 = 0x06,
191 g3 = 0x07,
192 sleep = 0x08,
193 g1Sleep = 0x09,
194 override = 0x0a,
195 legacyOn = 0x20,
196 legacyOff = 0x21,
197 unknown = 0x2a,
198 noChange = 0x7f,
199};
200
201static constexpr uint8_t stateChanged = 0x80;
202
203struct ACPIState
204{
205 uint8_t sysACPIState;
206 uint8_t devACPIState;
207} __attribute__((packed));
208
209std::map<ACPIPowerState::ACPI, PowerState> dbusToIPMI = {
210 {ACPIPowerState::ACPI::S0_G0_D0, PowerState::s0G0D0},
211 {ACPIPowerState::ACPI::S1_D1, PowerState::s1D1},
212 {ACPIPowerState::ACPI::S2_D2, PowerState::s2D2},
213 {ACPIPowerState::ACPI::S3_D3, PowerState::s3D3},
214 {ACPIPowerState::ACPI::S4, PowerState::s4},
215 {ACPIPowerState::ACPI::S5_G2, PowerState::s5G2},
216 {ACPIPowerState::ACPI::S4_S5, PowerState::s4S5},
217 {ACPIPowerState::ACPI::G3, PowerState::g3},
218 {ACPIPowerState::ACPI::SLEEP, PowerState::sleep},
219 {ACPIPowerState::ACPI::G1_SLEEP, PowerState::g1Sleep},
220 {ACPIPowerState::ACPI::OVERRIDE, PowerState::override},
221 {ACPIPowerState::ACPI::LEGACY_ON, PowerState::legacyOn},
222 {ACPIPowerState::ACPI::LEGACY_OFF, PowerState::legacyOff},
223 {ACPIPowerState::ACPI::Unknown, PowerState::unknown}};
224
225bool isValidACPIState(acpi_state::PowerStateType type, uint8_t state)
226{
227 if (type == acpi_state::PowerStateType::sysPowerState)
228 {
229 if ((state <= static_cast<uint8_t>(acpi_state::PowerState::override)) ||
230 (state == static_cast<uint8_t>(acpi_state::PowerState::legacyOn)) ||
231 (state ==
232 static_cast<uint8_t>(acpi_state::PowerState::legacyOff)) ||
233 (state == static_cast<uint8_t>(acpi_state::PowerState::unknown)) ||
234 (state == static_cast<uint8_t>(acpi_state::PowerState::noChange)))
235 {
236 return true;
237 }
238 else
239 {
240 return false;
241 }
242 }
243 else if (type == acpi_state::PowerStateType::devPowerState)
244 {
245 if ((state <= static_cast<uint8_t>(acpi_state::PowerState::s3D3)) ||
246 (state == static_cast<uint8_t>(acpi_state::PowerState::unknown)) ||
247 (state == static_cast<uint8_t>(acpi_state::PowerState::noChange)))
248 {
249 return true;
250 }
251 else
252 {
253 return false;
254 }
255 }
256 else
257 {
258 return false;
259 }
260 return false;
261}
262} // namespace acpi_state
263
Adriana Kobylak3a552e12015-10-19 16:11:00 -0500264ipmi_ret_t ipmi_app_set_acpi_power_state(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
Patrick Venture0b02be92018-08-31 11:55:55 -0700265 ipmi_request_t request,
266 ipmi_response_t response,
267 ipmi_data_len_t data_len,
268 ipmi_context_t context)
Chris Austen6caf28b2015-10-13 12:40:40 -0500269{
Yong Li18d77262018-10-09 01:59:45 +0800270 auto s = static_cast<uint8_t>(acpi_state::PowerState::unknown);
Chris Austen6caf28b2015-10-13 12:40:40 -0500271 ipmi_ret_t rc = IPMI_CC_OK;
Yong Li18d77262018-10-09 01:59:45 +0800272
273 sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()};
274
275 auto value = acpi_state::ACPIPowerState::ACPI::Unknown;
276
277 auto* req = reinterpret_cast<acpi_state::ACPIState*>(request);
278
279 if (*data_len != sizeof(acpi_state::ACPIState))
280 {
281 log<level::ERR>("set_acpi invalid len");
282 *data_len = 0;
283 return IPMI_CC_REQ_DATA_LEN_INVALID;
284 }
285
Chris Austen6caf28b2015-10-13 12:40:40 -0500286 *data_len = 0;
287
Yong Li18d77262018-10-09 01:59:45 +0800288 if (req->sysACPIState & acpi_state::stateChanged)
289 {
290 // set system power state
291 s = req->sysACPIState & ~acpi_state::stateChanged;
292
293 if (!acpi_state::isValidACPIState(
294 acpi_state::PowerStateType::sysPowerState, s))
295 {
296 log<level::ERR>("set_acpi_power sys invalid input",
297 entry("S=%x", s));
298 return IPMI_CC_PARM_OUT_OF_RANGE;
299 }
300
301 // valid input
302 if (s == static_cast<uint8_t>(acpi_state::PowerState::noChange))
303 {
304 log<level::DEBUG>("No change for system power state");
305 }
306 else
307 {
308 auto found = std::find_if(
309 acpi_state::dbusToIPMI.begin(), acpi_state::dbusToIPMI.end(),
310 [&s](const auto& iter) {
311 return (static_cast<uint8_t>(iter.second) == s);
312 });
313
314 value = found->first;
315
316 try
317 {
318 auto acpiObject =
319 ipmi::getDbusObject(bus, acpi_state::acpiInterface);
320 ipmi::setDbusProperty(bus, acpiObject.second, acpiObject.first,
321 acpi_state::acpiInterface,
322 acpi_state::sysACPIProp,
323 convertForMessage(value));
324 }
325 catch (const InternalFailure& e)
326 {
327 log<level::ERR>("Failed in set ACPI system property",
328 entry("EXCEPTION=%s", e.what()));
329 return IPMI_CC_UNSPECIFIED_ERROR;
330 }
331 }
332 }
333 else
334 {
335 log<level::DEBUG>("Do not change system power state");
336 }
337
338 if (req->devACPIState & acpi_state::stateChanged)
339 {
340 // set device power state
341 s = req->devACPIState & ~acpi_state::stateChanged;
342 if (!acpi_state::isValidACPIState(
343 acpi_state::PowerStateType::devPowerState, s))
344 {
345 log<level::ERR>("set_acpi_power dev invalid input",
346 entry("S=%x", s));
347 return IPMI_CC_PARM_OUT_OF_RANGE;
348 }
349
350 // valid input
351 if (s == static_cast<uint8_t>(acpi_state::PowerState::noChange))
352 {
353 log<level::DEBUG>("No change for device power state");
354 }
355 else
356 {
357 auto found = std::find_if(
358 acpi_state::dbusToIPMI.begin(), acpi_state::dbusToIPMI.end(),
359 [&s](const auto& iter) {
360 return (static_cast<uint8_t>(iter.second) == s);
361 });
362
363 value = found->first;
364
365 try
366 {
367 auto acpiObject =
368 ipmi::getDbusObject(bus, acpi_state::acpiInterface);
369 ipmi::setDbusProperty(bus, acpiObject.second, acpiObject.first,
370 acpi_state::acpiInterface,
371 acpi_state::devACPIProp,
372 convertForMessage(value));
373 }
374 catch (const InternalFailure& e)
375 {
376 log<level::ERR>("Failed in set ACPI device property",
377 entry("EXCEPTION=%s", e.what()));
378 return IPMI_CC_UNSPECIFIED_ERROR;
379 }
380 }
381 }
382 else
383 {
384 log<level::DEBUG>("Do not change device power state");
385 }
386
387 return rc;
388}
389
390ipmi_ret_t ipmi_app_get_acpi_power_state(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
391 ipmi_request_t request,
392 ipmi_response_t response,
393 ipmi_data_len_t data_len,
394 ipmi_context_t context)
395{
396 ipmi_ret_t rc = IPMI_CC_OK;
397
398 auto* res = reinterpret_cast<acpi_state::ACPIState*>(response);
399
400 sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()};
401
402 *data_len = 0;
403
404 try
405 {
406 auto acpiObject = ipmi::getDbusObject(bus, acpi_state::acpiInterface);
407
408 auto sysACPIVal = ipmi::getDbusProperty(
409 bus, acpiObject.second, acpiObject.first, acpi_state::acpiInterface,
410 acpi_state::sysACPIProp);
411 auto sysACPI = acpi_state::ACPIPowerState::convertACPIFromString(
William A. Kennington IIIdfad4862018-11-19 17:45:35 -0800412 variant_ns::get<std::string>(sysACPIVal));
Yong Li18d77262018-10-09 01:59:45 +0800413 res->sysACPIState =
414 static_cast<uint8_t>(acpi_state::dbusToIPMI.at(sysACPI));
415
416 auto devACPIVal = ipmi::getDbusProperty(
417 bus, acpiObject.second, acpiObject.first, acpi_state::acpiInterface,
418 acpi_state::devACPIProp);
419 auto devACPI = acpi_state::ACPIPowerState::convertACPIFromString(
William A. Kennington IIIdfad4862018-11-19 17:45:35 -0800420 variant_ns::get<std::string>(devACPIVal));
Yong Li18d77262018-10-09 01:59:45 +0800421 res->devACPIState =
422 static_cast<uint8_t>(acpi_state::dbusToIPMI.at(devACPI));
423
424 *data_len = sizeof(acpi_state::ACPIState);
425 }
426 catch (const InternalFailure& e)
427 {
428 log<level::ERR>("Failed in get ACPI property");
429 return IPMI_CC_UNSPECIFIED_ERROR;
430 }
Chris Austen6caf28b2015-10-13 12:40:40 -0500431 return rc;
432}
433
Chris Austen7303bdc2016-04-17 11:50:54 -0500434typedef struct
435{
436 char major;
437 char minor;
Chris Austen176c9652016-04-30 16:32:17 -0500438 uint16_t d[2];
Vernon Mauery86a50822019-03-25 13:11:36 -0700439} Revision;
Chris Austen7303bdc2016-04-17 11:50:54 -0500440
Dinesh Chinari2b7e07d2017-11-08 15:38:50 -0600441/* Currently supports the vx.x-x-[-x] and v1.x.x-x-[-x] format. It will */
442/* return -1 if not in those formats, this routine knows how to parse */
Chris Austen7303bdc2016-04-17 11:50:54 -0500443/* version = v0.6-19-gf363f61-dirty */
444/* ^ ^ ^^ ^ */
445/* | | |----------|-- additional details */
446/* | |---------------- Minor */
447/* |------------------ Major */
Dinesh Chinari2b7e07d2017-11-08 15:38:50 -0600448/* and version = v1.99.10-113-g65edf7d-r3-0-g9e4f715 */
449/* ^ ^ ^^ ^ */
450/* | | |--|---------- additional details */
451/* | |---------------- Minor */
452/* |------------------ Major */
Chris Austen7303bdc2016-04-17 11:50:54 -0500453/* Additional details : If the option group exists it will force Auxiliary */
454/* Firmware Revision Information 4th byte to 1 indicating the build was */
455/* derived with additional edits */
Vernon Mauery86a50822019-03-25 13:11:36 -0700456int convertVersion(std::string s, Revision& rev)
Chris Austen7303bdc2016-04-17 11:50:54 -0500457{
Dinesh Chinari2b7e07d2017-11-08 15:38:50 -0600458 std::string token;
Chris Austen176c9652016-04-30 16:32:17 -0500459 uint16_t commits;
Chris Austen7303bdc2016-04-17 11:50:54 -0500460
Patrick Venture0b02be92018-08-31 11:55:55 -0700461 auto location = s.find_first_of('v');
Dinesh Chinari2b7e07d2017-11-08 15:38:50 -0600462 if (location != std::string::npos)
463 {
Patrick Venture0b02be92018-08-31 11:55:55 -0700464 s = s.substr(location + 1);
Chris Austen176c9652016-04-30 16:32:17 -0500465 }
Chris Austen7303bdc2016-04-17 11:50:54 -0500466
Dinesh Chinari2b7e07d2017-11-08 15:38:50 -0600467 if (!s.empty())
468 {
469 location = s.find_first_of(".");
470 if (location != std::string::npos)
471 {
Vernon Mauery86a50822019-03-25 13:11:36 -0700472 rev.major =
Patrick Venture0b02be92018-08-31 11:55:55 -0700473 static_cast<char>(std::stoi(s.substr(0, location), 0, 16));
474 token = s.substr(location + 1);
Dinesh Chinari2b7e07d2017-11-08 15:38:50 -0600475 }
Chris Austen176c9652016-04-30 16:32:17 -0500476
Dinesh Chinari2b7e07d2017-11-08 15:38:50 -0600477 if (!token.empty())
478 {
479 location = token.find_first_of(".-");
480 if (location != std::string::npos)
481 {
Vernon Mauery86a50822019-03-25 13:11:36 -0700482 rev.minor = static_cast<char>(
Patrick Venture0b02be92018-08-31 11:55:55 -0700483 std::stoi(token.substr(0, location), 0, 16));
484 token = token.substr(location + 1);
Dinesh Chinari2b7e07d2017-11-08 15:38:50 -0600485 }
486 }
Chris Austen7303bdc2016-04-17 11:50:54 -0500487
Dinesh Chinari2b7e07d2017-11-08 15:38:50 -0600488 // Capture the number of commits on top of the minor tag.
489 // I'm using BE format like the ipmi spec asked for
490 location = token.find_first_of(".-");
491 if (!token.empty())
492 {
493 commits = std::stoi(token.substr(0, location), 0, 16);
Vernon Mauery86a50822019-03-25 13:11:36 -0700494 rev.d[0] = (commits >> 8) | (commits << 8);
Dinesh Chinari2b7e07d2017-11-08 15:38:50 -0600495
496 // commit number we skip
497 location = token.find_first_of(".-");
498 if (location != std::string::npos)
499 {
Patrick Venture0b02be92018-08-31 11:55:55 -0700500 token = token.substr(location + 1);
Dinesh Chinari2b7e07d2017-11-08 15:38:50 -0600501 }
502 }
Patrick Venture0b02be92018-08-31 11:55:55 -0700503 else
504 {
Vernon Mauery86a50822019-03-25 13:11:36 -0700505 rev.d[0] = 0;
Dinesh Chinari2b7e07d2017-11-08 15:38:50 -0600506 }
507
508 if (location != std::string::npos)
509 {
Patrick Venture0b02be92018-08-31 11:55:55 -0700510 token = token.substr(location + 1);
Dinesh Chinari2b7e07d2017-11-08 15:38:50 -0600511 }
512
513 // Any value of the optional parameter forces it to 1
514 location = token.find_first_of(".-");
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 commits = (!token.empty()) ? 1 : 0;
520
Patrick Venture0b02be92018-08-31 11:55:55 -0700521 // We do this operation to get this displayed in least significant bytes
522 // of ipmitool device id command.
Vernon Mauery86a50822019-03-25 13:11:36 -0700523 rev.d[1] = (commits >> 8) | (commits << 8);
Dinesh Chinari2b7e07d2017-11-08 15:38:50 -0600524 }
525
Chris Austen7303bdc2016-04-17 11:50:54 -0500526 return 0;
527}
528
Vernon Mauery86a50822019-03-25 13:11:36 -0700529auto ipmiAppGetDeviceId() -> ipmi::RspType<uint8_t, // Device ID
530 uint8_t, // Device Revision
531 uint8_t, // Firmware Revision Major
532 uint8_t, // Firmware Revision minor
533 uint8_t, // IPMI version
534 uint8_t, // Additional device support
535 uint24_t, // MFG ID
536 uint16_t, // Product ID
537 uint32_t // AUX info
538 >
Chris Austen6caf28b2015-10-13 12:40:40 -0500539{
Nagaraju Goruganti744398d2018-02-20 09:52:00 -0600540 int r = -1;
Vernon Mauery86a50822019-03-25 13:11:36 -0700541 Revision rev = {0};
542 static struct
543 {
544 uint8_t id;
545 uint8_t revision;
546 uint8_t fw[2];
547 uint8_t ipmiVer;
548 uint8_t addnDevSupport;
549 uint24_t manufId;
550 uint16_t prodId;
551 uint32_t aux;
552 } devId;
David Cobbleya1adb072017-11-21 15:58:13 -0800553 static bool dev_id_initialized = false;
554 const char* filename = "/usr/share/ipmi-providers/dev_id.json";
Alexander Amelkinba19c182018-09-04 15:49:36 +0300555 constexpr auto ipmiDevIdStateShift = 7;
556 constexpr auto ipmiDevIdFw1Mask = ~(1 << ipmiDevIdStateShift);
Chris Austen6caf28b2015-10-13 12:40:40 -0500557
David Cobbleya1adb072017-11-21 15:58:13 -0800558 if (!dev_id_initialized)
559 {
Nagaraju Goruganti744398d2018-02-20 09:52:00 -0600560 try
561 {
562 auto version = getActiveSoftwareVersionInfo();
Vernon Mauery86a50822019-03-25 13:11:36 -0700563 r = convertVersion(version, rev);
David Cobbleya1adb072017-11-21 15:58:13 -0800564 }
Nagaraju Goruganti744398d2018-02-20 09:52:00 -0600565 catch (const std::exception& e)
566 {
567 log<level::ERR>(e.what());
568 }
Nan Liee0cb902016-07-11 15:38:03 +0800569
Patrick Venture0b02be92018-08-31 11:55:55 -0700570 if (r >= 0)
571 {
Nagaraju Goruganti744398d2018-02-20 09:52:00 -0600572 // bit7 identifies if the device is available
573 // 0=normal operation
574 // 1=device firmware, SDR update,
575 // or self-initialization in progress.
Alexander Amelkinba19c182018-09-04 15:49:36 +0300576 // The availability may change in run time, so mask here
577 // and initialize later.
Vernon Mauery86a50822019-03-25 13:11:36 -0700578 devId.fw[0] = rev.major & ipmiDevIdFw1Mask;
Nagaraju Goruganti744398d2018-02-20 09:52:00 -0600579
580 rev.minor = (rev.minor > 99 ? 99 : rev.minor);
Vernon Mauery86a50822019-03-25 13:11:36 -0700581 devId.fw[1] = rev.minor % 10 + (rev.minor / 10) * 16;
582 std::memcpy(&devId.aux, rev.d, 4);
David Cobbleya1adb072017-11-21 15:58:13 -0800583 }
Nan Liee0cb902016-07-11 15:38:03 +0800584
David Cobbleya1adb072017-11-21 15:58:13 -0800585 // IPMI Spec version 2.0
Vernon Mauery86a50822019-03-25 13:11:36 -0700586 devId.ipmiVer = 2;
Adriana Kobylak0e912642016-06-22 16:54:39 -0500587
Vernon Mauery86a50822019-03-25 13:11:36 -0700588 std::ifstream devIdFile(filename);
589 if (devIdFile.is_open())
David Cobbleya1adb072017-11-21 15:58:13 -0800590 {
Vernon Mauery86a50822019-03-25 13:11:36 -0700591 auto data = nlohmann::json::parse(devIdFile, nullptr, false);
David Cobbleya1adb072017-11-21 15:58:13 -0800592 if (!data.is_discarded())
593 {
Vernon Mauery86a50822019-03-25 13:11:36 -0700594 devId.id = data.value("id", 0);
595 devId.revision = data.value("revision", 0);
596 devId.addnDevSupport = data.value("addn_dev_support", 0);
597 devId.manufId = data.value("manuf_id", 0);
598 devId.prodId = data.value("prod_id", 0);
599 devId.aux = data.value("aux", 0);
David Cobbleya1adb072017-11-21 15:58:13 -0800600
Patrick Venture0b02be92018-08-31 11:55:55 -0700601 // Don't read the file every time if successful
David Cobbleya1adb072017-11-21 15:58:13 -0800602 dev_id_initialized = true;
603 }
604 else
605 {
606 log<level::ERR>("Device ID JSON parser failure");
Vernon Mauery86a50822019-03-25 13:11:36 -0700607 return ipmi::response(ipmi::ccUnspecifiedError);
David Cobbleya1adb072017-11-21 15:58:13 -0800608 }
609 }
610 else
611 {
612 log<level::ERR>("Device ID file not found");
Vernon Mauery86a50822019-03-25 13:11:36 -0700613 return ipmi::response(ipmi::ccUnspecifiedError);
Chris Austen7303bdc2016-04-17 11:50:54 -0500614 }
615 }
Chris Austen6caf28b2015-10-13 12:40:40 -0500616
Alexander Amelkinba19c182018-09-04 15:49:36 +0300617 // Set availability to the actual current BMC state
Vernon Mauery86a50822019-03-25 13:11:36 -0700618 devId.fw[0] &= ipmiDevIdFw1Mask;
Alexander Amelkinba19c182018-09-04 15:49:36 +0300619 if (!getCurrentBmcState())
620 {
Vernon Mauery86a50822019-03-25 13:11:36 -0700621 devId.fw[0] |= (1 << ipmiDevIdStateShift);
Alexander Amelkinba19c182018-09-04 15:49:36 +0300622 }
623
Vernon Mauery86a50822019-03-25 13:11:36 -0700624 return ipmi::responseSuccess(
625 devId.id, devId.revision, devId.fw[0], devId.fw[1], devId.ipmiVer,
626 devId.addnDevSupport, devId.manufId, devId.prodId, devId.aux);
Chris Austen6caf28b2015-10-13 12:40:40 -0500627}
628
Vernon Maueryb84a5282019-03-25 13:39:03 -0700629auto ipmiAppGetSelfTestResults() -> ipmi::RspType<uint8_t, uint8_t>
Nan Li41fa24a2016-11-10 20:12:37 +0800630{
Nan Li41fa24a2016-11-10 20:12:37 +0800631 // Byte 2:
632 // 55h - No error.
Gunnar Mills8991dd62017-10-25 17:11:29 -0500633 // 56h - Self Test function not implemented in this controller.
Nan Li41fa24a2016-11-10 20:12:37 +0800634 // 57h - Corrupted or inaccesssible data or devices.
635 // 58h - Fatal hardware error.
636 // FFh - reserved.
637 // all other: Device-specific 'internal failure'.
638 // Byte 3:
639 // For byte 2 = 55h, 56h, FFh: 00h
640 // For byte 2 = 58h, all other: Device-specific
641 // For byte 2 = 57h: self-test error bitfield.
642 // Note: returning 57h does not imply that all test were run.
643 // [7] 1b = Cannot access SEL device.
644 // [6] 1b = Cannot access SDR Repository.
645 // [5] 1b = Cannot access BMC FRU device.
646 // [4] 1b = IPMB signal lines do not respond.
647 // [3] 1b = SDR Repository empty.
648 // [2] 1b = Internal Use Area of BMC FRU corrupted.
649 // [1] 1b = controller update 'boot block' firmware corrupted.
650 // [0] 1b = controller operational firmware corrupted.
Vernon Maueryb84a5282019-03-25 13:39:03 -0700651 constexpr uint8_t notImplemented = 0x56;
652 constexpr uint8_t zero = 0;
653 return ipmi::responseSuccess(notImplemented, zero);
Nan Li41fa24a2016-11-10 20:12:37 +0800654}
655
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500656ipmi_ret_t ipmi_app_get_device_guid(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
Patrick Venture0b02be92018-08-31 11:55:55 -0700657 ipmi_request_t request,
658 ipmi_response_t response,
659 ipmi_data_len_t data_len,
660 ipmi_context_t context)
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500661{
Patrick Venture0b02be92018-08-31 11:55:55 -0700662 const char* objname = "/org/openbmc/control/chassis0";
663 const char* iface = "org.freedesktop.DBus.Properties";
664 const char* chassis_iface = "org.openbmc.control.Chassis";
665 sd_bus_message* reply = NULL;
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500666 sd_bus_error error = SD_BUS_ERROR_NULL;
667 int r = 0;
Patrick Venture0b02be92018-08-31 11:55:55 -0700668 char* uuid = NULL;
669 char* busname = NULL;
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500670
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500671 // UUID is in RFC4122 format. Ex: 61a39523-78f2-11e5-9862-e6402cfc3223
Patrick Ventured2117022018-02-06 08:54:37 -0800672 // Per IPMI Spec 2.0 need to convert to 16 hex bytes and reverse the byte
673 // order
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500674 // Ex: 0x2332fc2c40e66298e511f2782395a361
675
Patrick Venture0b02be92018-08-31 11:55:55 -0700676 const int resp_size = 16; // Response is 16 hex bytes per IPMI Spec
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500677 uint8_t resp_uuid[resp_size]; // Array to hold the formatted response
Patrick Ventured2117022018-02-06 08:54:37 -0800678 // Point resp end of array to save in reverse order
Patrick Venture0b02be92018-08-31 11:55:55 -0700679 int resp_loc = resp_size - 1;
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500680 int i = 0;
Patrick Venture0b02be92018-08-31 11:55:55 -0700681 char* tokptr = NULL;
682 char* id_octet = NULL;
Emily Shafferedb8bb02018-09-27 14:50:15 -0700683 size_t total_uuid_size = 0;
684 // 1 byte of resp is built from 2 chars of uuid.
685 constexpr size_t max_uuid_size = 2 * resp_size;
vishwa1eaea4f2016-02-26 11:57:40 -0600686
687 // Status code.
688 ipmi_ret_t rc = IPMI_CC_OK;
689 *data_len = 0;
690
vishwa1eaea4f2016-02-26 11:57:40 -0600691 // Call Get properties method with the interface and property name
Sergey Solomineb9b8142016-08-23 09:07:28 -0500692 r = mapper_get_service(bus, objname, &busname);
Patrick Venture0b02be92018-08-31 11:55:55 -0700693 if (r < 0)
694 {
695 log<level::ERR>("Failed to get bus name", entry("BUS=%s", objname),
Aditya Saripalli5fb14602017-11-09 14:46:27 +0530696 entry("ERRNO=0x%X", -r));
Sergey Solomineb9b8142016-08-23 09:07:28 -0500697 goto finish;
698 }
Patrick Venture0b02be92018-08-31 11:55:55 -0700699 r = sd_bus_call_method(bus, busname, objname, iface, "Get", &error, &reply,
700 "ss", chassis_iface, "uuid");
vishwa1eaea4f2016-02-26 11:57:40 -0600701 if (r < 0)
702 {
Patrick Venture0b02be92018-08-31 11:55:55 -0700703 log<level::ERR>("Failed to call Get Method", entry("ERRNO=0x%X", -r));
vishwa1eaea4f2016-02-26 11:57:40 -0600704 rc = IPMI_CC_UNSPECIFIED_ERROR;
705 goto finish;
706 }
707
708 r = sd_bus_message_read(reply, "v", "s", &uuid);
709 if (r < 0 || uuid == NULL)
710 {
Patrick Venture0b02be92018-08-31 11:55:55 -0700711 log<level::ERR>("Failed to get a response", entry("ERRNO=0x%X", -r));
vishwa1eaea4f2016-02-26 11:57:40 -0600712 rc = IPMI_CC_RESPONSE_ERROR;
713 goto finish;
714 }
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500715
716 // Traverse the UUID
Patrick Ventured2117022018-02-06 08:54:37 -0800717 // Get the UUID octects separated by dash
718 id_octet = strtok_r(uuid, "-", &tokptr);
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500719
720 if (id_octet == NULL)
vishwa1eaea4f2016-02-26 11:57:40 -0600721 {
722 // Error
Patrick Venture0b02be92018-08-31 11:55:55 -0700723 log<level::ERR>("Unexpected UUID format", entry("UUID=%s", uuid));
vishwa1eaea4f2016-02-26 11:57:40 -0600724 rc = IPMI_CC_RESPONSE_ERROR;
725 goto finish;
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500726 }
727
728 while (id_octet != NULL)
729 {
730 // Calculate the octet string size since it varies
731 // Divide it by 2 for the array size since 1 byte is built from 2 chars
Patrick Venture0b02be92018-08-31 11:55:55 -0700732 int tmp_size = strlen(id_octet) / 2;
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500733
Emily Shafferedb8bb02018-09-27 14:50:15 -0700734 // Check if total UUID size has been exceeded
735 if ((total_uuid_size += strlen(id_octet)) > max_uuid_size)
736 {
737 // Error - UUID too long to store
738 log<level::ERR>("UUID too long", entry("UUID=%s", uuid));
739 rc = IPMI_CC_RESPONSE_ERROR;
740 goto finish;
741 }
742
Patrick Venture0b02be92018-08-31 11:55:55 -0700743 for (i = 0; i < tmp_size; i++)
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500744 {
Patrick Ventured2117022018-02-06 08:54:37 -0800745 // Holder of the 2 chars that will become a byte
746 char tmp_array[3] = {0};
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500747 strncpy(tmp_array, id_octet, 2); // 2 chars at a time
748
749 int resp_byte = strtoul(tmp_array, NULL, 16); // Convert to hex byte
Patrick Ventured2117022018-02-06 08:54:37 -0800750 // Copy end to first
Patrick Ventureb51bf9c2018-09-10 15:53:14 -0700751 std::memcpy((void*)&resp_uuid[resp_loc], &resp_byte, 1);
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500752 resp_loc--;
Patrick Venture0b02be92018-08-31 11:55:55 -0700753 id_octet += 2; // Finished with the 2 chars, advance
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500754 }
Patrick Venture0b02be92018-08-31 11:55:55 -0700755 id_octet = strtok_r(NULL, "-", &tokptr); // Get next octet
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500756 }
757
758 // Data length
759 *data_len = resp_size;
760
761 // Pack the actual response
Patrick Ventureb51bf9c2018-09-10 15:53:14 -0700762 std::memcpy(response, &resp_uuid, *data_len);
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500763
vishwa1eaea4f2016-02-26 11:57:40 -0600764finish:
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500765 sd_bus_error_free(&error);
vishwa1eaea4f2016-02-26 11:57:40 -0600766 reply = sd_bus_message_unref(reply);
Sergey Solomineb9b8142016-08-23 09:07:28 -0500767 free(busname);
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500768
769 return rc;
770}
Chris Austen6caf28b2015-10-13 12:40:40 -0500771
Vernon Mauerycc99ba42019-03-25 13:40:11 -0700772auto ipmiAppGetBtCapabilities()
773 -> ipmi::RspType<uint8_t, uint8_t, uint8_t, uint8_t, uint8_t>
vishwabmcba0bd5f2015-09-30 16:50:23 +0530774{
Adriana Kobylak88ad8152016-12-13 10:09:08 -0600775 // Per IPMI 2.0 spec, the input and output buffer size must be the max
776 // buffer size minus one byte to allocate space for the length byte.
Vernon Mauerycc99ba42019-03-25 13:40:11 -0700777 constexpr uint8_t nrOutstanding = 0x01;
778 constexpr uint8_t inputBufferSize = MAX_IPMI_BUFFER - 1;
779 constexpr uint8_t outputBufferSize = MAX_IPMI_BUFFER - 1;
780 constexpr uint8_t transactionTime = 0x0A;
781 constexpr uint8_t nrRetries = 0x01;
vishwabmcba0bd5f2015-09-30 16:50:23 +0530782
Vernon Mauerycc99ba42019-03-25 13:40:11 -0700783 return ipmi::responseSuccess(nrOutstanding, inputBufferSize,
784 outputBufferSize, transactionTime, nrRetries);
vishwabmcba0bd5f2015-09-30 16:50:23 +0530785}
786
Adriana Kobylak3a552e12015-10-19 16:11:00 -0500787ipmi_ret_t ipmi_app_wildcard_handler(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
Patrick Venture0b02be92018-08-31 11:55:55 -0700788 ipmi_request_t request,
789 ipmi_response_t response,
790 ipmi_data_len_t data_len,
791 ipmi_context_t context)
vishwabmcba0bd5f2015-09-30 16:50:23 +0530792{
vishwabmcba0bd5f2015-09-30 16:50:23 +0530793 // Status code.
Nan Li70aa8d92016-08-29 00:11:10 +0800794 ipmi_ret_t rc = IPMI_CC_INVALID;
vishwabmcba0bd5f2015-09-30 16:50:23 +0530795
796 *data_len = strlen("THIS IS WILDCARD");
797
798 // Now pack actual response
Patrick Ventureb51bf9c2018-09-10 15:53:14 -0700799 std::memcpy(response, "THIS IS WILDCARD", *data_len);
vishwabmcba0bd5f2015-09-30 16:50:23 +0530800
801 return rc;
802}
803
Marri Devender Rao5e007a52018-01-08 06:18:36 -0600804ipmi_ret_t ipmi_app_get_sys_guid(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
Patrick Venture0b02be92018-08-31 11:55:55 -0700805 ipmi_request_t request,
806 ipmi_response_t response,
807 ipmi_data_len_t data_len,
808 ipmi_context_t context)
Marri Devender Rao5e007a52018-01-08 06:18:36 -0600809
810{
811 ipmi_ret_t rc = IPMI_CC_OK;
812 sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()};
813
814 try
815 {
816 // Get the Inventory object implementing BMC interface
817 ipmi::DbusObjectInfo bmcObject =
818 ipmi::getDbusObject(bus, bmc_interface);
819
820 // Read UUID property value from bmcObject
821 // UUID is in RFC4122 format Ex: 61a39523-78f2-11e5-9862-e6402cfc3223
Patrick Venture0b02be92018-08-31 11:55:55 -0700822 auto variant =
823 ipmi::getDbusProperty(bus, bmcObject.second, bmcObject.first,
824 bmc_guid_interface, bmc_guid_property);
William A. Kennington III4c008022018-10-12 17:18:14 -0700825 std::string guidProp = variant_ns::get<std::string>(variant);
Marri Devender Rao5e007a52018-01-08 06:18:36 -0600826
827 // Erase "-" characters from the property value
828 guidProp.erase(std::remove(guidProp.begin(), guidProp.end(), '-'),
Patrick Venture0b02be92018-08-31 11:55:55 -0700829 guidProp.end());
Marri Devender Rao5e007a52018-01-08 06:18:36 -0600830
831 auto guidPropLen = guidProp.length();
832 // Validate UUID data
833 // Divide by 2 as 1 byte is built from 2 chars
Patrick Venture0b02be92018-08-31 11:55:55 -0700834 if ((guidPropLen <= 0) || ((guidPropLen / 2) != bmc_guid_len))
Marri Devender Rao5e007a52018-01-08 06:18:36 -0600835
836 {
837 log<level::ERR>("Invalid UUID property value",
Patrick Venture0b02be92018-08-31 11:55:55 -0700838 entry("UUID_LENGTH=%d", guidPropLen));
Marri Devender Rao5e007a52018-01-08 06:18:36 -0600839 return IPMI_CC_RESPONSE_ERROR;
840 }
841
842 // Convert data in RFC4122(MSB) format to LSB format
843 // Get 2 characters at a time as 1 byte is built from 2 chars and
844 // convert to hex byte
845 // TODO: Data printed for GUID command is not as per the
846 // GUID format defined in IPMI specification 2.0 section 20.8
847 // Ticket raised: https://sourceforge.net/p/ipmitool/bugs/501/
848 uint8_t respGuid[bmc_guid_len];
849 for (size_t i = 0, respLoc = (bmc_guid_len - 1);
Patrick Venture0b02be92018-08-31 11:55:55 -0700850 i < guidPropLen && respLoc >= 0; i += 2, respLoc--)
Marri Devender Rao5e007a52018-01-08 06:18:36 -0600851 {
852 auto value = static_cast<uint8_t>(
Patrick Venture0b02be92018-08-31 11:55:55 -0700853 std::stoi(guidProp.substr(i, 2).c_str(), NULL, 16));
Marri Devender Rao5e007a52018-01-08 06:18:36 -0600854 respGuid[respLoc] = value;
855 }
856
857 *data_len = bmc_guid_len;
Patrick Ventureb51bf9c2018-09-10 15:53:14 -0700858 std::memcpy(response, &respGuid, bmc_guid_len);
Marri Devender Rao5e007a52018-01-08 06:18:36 -0600859 }
860 catch (const InternalFailure& e)
861 {
862 log<level::ERR>("Failed in reading BMC UUID property",
863 entry("INTERFACE=%s", bmc_interface),
864 entry("PROPERTY_INTERFACE=%s", bmc_guid_interface),
865 entry("PROPERTY=%s", bmc_guid_property));
866 return IPMI_CC_UNSPECIFIED_ERROR;
867 }
868 return rc;
869}
870
Xo Wangf542e8b2017-08-09 15:34:16 -0700871static std::unique_ptr<SysInfoParamStore> sysInfoParamStore;
872
Xo Wang87651332017-08-11 10:17:59 -0700873static std::string sysInfoReadSystemName()
874{
875 // Use the BMC hostname as the "System Name."
876 char hostname[HOST_NAME_MAX + 1] = {};
877 if (gethostname(hostname, HOST_NAME_MAX) != 0)
878 {
879 perror("System info parameter: system name");
880 }
881 return hostname;
882}
883
Xo Wangf542e8b2017-08-09 15:34:16 -0700884struct IpmiSysInfoResp
885{
886 uint8_t paramRevision;
887 uint8_t setSelector;
888 union
889 {
890 struct
891 {
892 uint8_t encoding;
893 uint8_t stringLen;
894 uint8_t stringData0[14];
895 } __attribute__((packed));
896 uint8_t stringDataN[16];
897 uint8_t byteData;
898 };
899} __attribute__((packed));
900
901/**
902 * Split a string into (up to) 16-byte chunks as expected in response for get
903 * system info parameter.
904 *
905 * @param[in] fullString: Input string to be split
906 * @param[in] chunkIndex: Index of the chunk to be written out
907 * @param[in,out] chunk: Output data buffer; must have 14 byte capacity if
908 * chunk_index = 0 and 16-byte capacity otherwise
909 * @return the number of bytes written into the output buffer, or -EINVAL for
910 * invalid arguments.
911 */
912static int splitStringParam(const std::string& fullString, int chunkIndex,
913 uint8_t* chunk)
914{
915 constexpr int maxChunk = 255;
916 constexpr int smallChunk = 14;
917 constexpr int chunkSize = 16;
918 if (chunkIndex > maxChunk || chunk == nullptr)
919 {
920 return -EINVAL;
921 }
922 try
923 {
924 std::string output;
925 if (chunkIndex == 0)
926 {
927 // Output must have 14 byte capacity.
928 output = fullString.substr(0, smallChunk);
929 }
930 else
931 {
932 // Output must have 16 byte capacity.
933 output = fullString.substr((chunkIndex * chunkSize) - 2, chunkSize);
934 }
935
936 std::memcpy(chunk, output.c_str(), output.length());
937 return output.length();
938 }
939 catch (const std::out_of_range& e)
940 {
941 // The position was beyond the end.
942 return -EINVAL;
943 }
944}
945
946/**
947 * Packs the Get Sys Info Request Item into the response.
948 *
949 * @param[in] paramString - the parameter.
950 * @param[in] setSelector - the selector
951 * @param[in,out] resp - the System info response.
952 * @return The number of bytes packed or failure from splitStringParam().
953 */
954static int packGetSysInfoResp(const std::string& paramString,
955 uint8_t setSelector, IpmiSysInfoResp* resp)
956{
957 uint8_t* dataBuffer = resp->stringDataN;
958 resp->setSelector = setSelector;
959 if (resp->setSelector == 0) // First chunk has only 14 bytes.
960 {
961 resp->encoding = 0;
962 resp->stringLen = paramString.length();
963 dataBuffer = resp->stringData0;
964 }
965 return splitStringParam(paramString, resp->setSelector, dataBuffer);
966}
967
968ipmi_ret_t ipmi_app_get_system_info(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
969 ipmi_request_t request,
970 ipmi_response_t response,
971 ipmi_data_len_t dataLen,
972 ipmi_context_t context)
973{
974 IpmiSysInfoResp resp = {};
975 size_t respLen = 0;
976 uint8_t* const reqData = static_cast<uint8_t*>(request);
977 std::string paramString;
978 bool found;
979 std::tuple<bool, std::string> ret;
980 constexpr int minRequestSize = 4;
981 constexpr int paramSelector = 1;
982 constexpr uint8_t revisionOnly = 0x80;
983 const uint8_t paramRequested = reqData[paramSelector];
984 int rc;
985
986 if (*dataLen < minRequestSize)
987 {
988 return IPMI_CC_REQ_DATA_LEN_INVALID;
989 }
990
991 *dataLen = 0; // default to 0.
992
993 // Parameters revision as of IPMI spec v2.0 rev. 1.1 (Feb 11, 2014 E6)
994 resp.paramRevision = 0x11;
995 if (reqData[0] & revisionOnly) // Get parameter revision only
996 {
997 respLen = 1;
998 goto writeResponse;
999 }
1000
1001 // The "Set In Progress" parameter can be used for rollback of parameter
1002 // data and is not implemented.
1003 if (paramRequested == 0)
1004 {
1005 resp.byteData = 0;
1006 respLen = 2;
1007 goto writeResponse;
1008 }
1009
1010 if (sysInfoParamStore == nullptr)
1011 {
1012 sysInfoParamStore = std::make_unique<SysInfoParamStore>();
Xo Wang87651332017-08-11 10:17:59 -07001013 sysInfoParamStore->update(IPMI_SYSINFO_SYSTEM_NAME,
1014 sysInfoReadSystemName);
Xo Wangf542e8b2017-08-09 15:34:16 -07001015 }
1016
1017 // Parameters other than Set In Progress are assumed to be strings.
1018 ret = sysInfoParamStore->lookup(paramRequested);
1019 found = std::get<0>(ret);
1020 paramString = std::get<1>(ret);
1021 if (!found)
1022 {
1023 return IPMI_CC_SYSTEM_INFO_PARAMETER_NOT_SUPPORTED;
1024 }
1025 // TODO: Cache each parameter across multiple calls, until the whole string
1026 // has been read out. Otherwise, it's possible for a parameter to change
1027 // between requests for its chunks, returning chunks incoherent with each
1028 // other. For now, the parameter store is simply required to have only
1029 // idempotent callbacks.
1030 rc = packGetSysInfoResp(paramString, reqData[2], &resp);
1031 if (rc == -EINVAL)
1032 {
1033 return IPMI_CC_RESPONSE_ERROR;
1034 }
1035
1036 respLen = sizeof(resp); // Write entire string data chunk in response.
1037
1038writeResponse:
1039 std::memcpy(response, &resp, sizeof(resp));
1040 *dataLen = respLen;
1041 return IPMI_CC_OK;
1042}
1043
Chris Austen6caf28b2015-10-13 12:40:40 -05001044void register_netfn_app_functions()
vishwabmcba0bd5f2015-09-30 16:50:23 +05301045{
Vernon Mauery86a50822019-03-25 13:11:36 -07001046 // <Get Device ID>
1047 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp,
1048 ipmi::app::cmdGetDeviceId, ipmi::Privilege::User,
1049 ipmiAppGetDeviceId);
1050
Tom05732372016-09-06 17:21:23 +05301051 // <Get BT Interface Capabilities>
Vernon Mauerycc99ba42019-03-25 13:40:11 -07001052 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp,
1053 ipmi::app::cmdGetBtIfaceCapabilities,
1054 ipmi::Privilege::User, ipmiAppGetBtCapabilities);
Chris Austen6caf28b2015-10-13 12:40:40 -05001055
Tom05732372016-09-06 17:21:23 +05301056 // <Wildcard Command>
Patrick Venture0b02be92018-08-31 11:55:55 -07001057 ipmi_register_callback(NETFUN_APP, IPMI_CMD_WILDCARD, NULL,
1058 ipmi_app_wildcard_handler, PRIVILEGE_USER);
Chris Austen6caf28b2015-10-13 12:40:40 -05001059
Tom05732372016-09-06 17:21:23 +05301060 // <Reset Watchdog Timer>
Patrick Venture0b02be92018-08-31 11:55:55 -07001061 ipmi_register_callback(NETFUN_APP, IPMI_CMD_RESET_WD, NULL,
1062 ipmi_app_watchdog_reset, PRIVILEGE_OPERATOR);
Chris Austen6caf28b2015-10-13 12:40:40 -05001063
Tom05732372016-09-06 17:21:23 +05301064 // <Set Watchdog Timer>
Patrick Venture0b02be92018-08-31 11:55:55 -07001065 ipmi_register_callback(NETFUN_APP, IPMI_CMD_SET_WD, NULL,
1066 ipmi_app_watchdog_set, PRIVILEGE_OPERATOR);
Chris Austen6caf28b2015-10-13 12:40:40 -05001067
William A. Kennington III73f44512018-02-09 15:28:46 -08001068 // <Get Watchdog Timer>
Patrick Venture0b02be92018-08-31 11:55:55 -07001069 ipmi_register_callback(NETFUN_APP, IPMI_CMD_GET_WD, NULL,
1070 ipmi_app_watchdog_get, PRIVILEGE_OPERATOR);
William A. Kennington III73f44512018-02-09 15:28:46 -08001071
Tom05732372016-09-06 17:21:23 +05301072 // <Get Self Test Results>
Vernon Maueryb84a5282019-03-25 13:39:03 -07001073 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp,
1074 ipmi::app::cmdGetSelfTestResults,
1075 ipmi::Privilege::User, ipmiAppGetSelfTestResults);
Nan Li41fa24a2016-11-10 20:12:37 +08001076
Tom05732372016-09-06 17:21:23 +05301077 // <Get Device GUID>
Patrick Venture0b02be92018-08-31 11:55:55 -07001078 ipmi_register_callback(NETFUN_APP, IPMI_CMD_GET_DEVICE_GUID, NULL,
1079 ipmi_app_get_device_guid, PRIVILEGE_USER);
Adriana Kobylakd100ee52015-10-20 17:02:37 -05001080
Tom05732372016-09-06 17:21:23 +05301081 // <Set ACPI Power State>
Patrick Venture0b02be92018-08-31 11:55:55 -07001082 ipmi_register_callback(NETFUN_APP, IPMI_CMD_SET_ACPI, NULL,
1083 ipmi_app_set_acpi_power_state, PRIVILEGE_ADMIN);
Chris Austen6caf28b2015-10-13 12:40:40 -05001084
Yong Li18d77262018-10-09 01:59:45 +08001085 // <Get ACPI Power State>
1086 ipmi_register_callback(NETFUN_APP, IPMI_CMD_GET_ACPI, NULL,
1087 ipmi_app_get_acpi_power_state, PRIVILEGE_ADMIN);
1088
Marri Devender Rao5e007a52018-01-08 06:18:36 -06001089 // <Get System GUID Command>
Patrick Venture0b02be92018-08-31 11:55:55 -07001090 ipmi_register_callback(NETFUN_APP, IPMI_CMD_GET_SYS_GUID, NULL,
1091 ipmi_app_get_sys_guid, PRIVILEGE_USER);
Tom Joseph7cbe2282018-03-21 21:17:33 +05301092
1093 // <Get Channel Cipher Suites Command>
Patrick Venture0b02be92018-08-31 11:55:55 -07001094 ipmi_register_callback(NETFUN_APP, IPMI_CMD_GET_CHAN_CIPHER_SUITES, NULL,
1095 getChannelCipherSuites, PRIVILEGE_CALLBACK);
Vernon Maueryd2a57de2019-03-25 12:45:28 -07001096
Xo Wangf542e8b2017-08-09 15:34:16 -07001097 // <Get System Info Command>
1098 ipmi_register_callback(NETFUN_APP, IPMI_CMD_GET_SYSTEM_INFO, NULL,
1099 ipmi_app_get_system_info, PRIVILEGE_USER);
vishwabmcba0bd5f2015-09-30 16:50:23 +05301100 return;
1101}