blob: 87f45d2b81aab3582ef3c51835eb6ff70115e44f [file] [log] [blame]
Patrick Venture0b02be92018-08-31 11:55:55 -07001#include <arpa/inet.h>
Xo Wang87651332017-08-11 10:17:59 -07002#include <limits.h>
Patrick Venture0b02be92018-08-31 11:55:55 -07003#include <mapper.h>
Patrick Venture0b02be92018-08-31 11:55:55 -07004#include <systemd/sd-bus.h>
Xo Wang87651332017-08-11 10:17:59 -07005#include <unistd.h>
Patrick Venture0b02be92018-08-31 11:55:55 -07006
Patrick Venture3a5071a2018-09-12 13:27:42 -07007#include <algorithm>
Vernon Mauery0120b682019-03-25 13:08:54 -07008#include <app/channel.hpp>
9#include <app/watchdog.hpp>
10#include <apphandler.hpp>
Patrick Venture3a5071a2018-09-12 13:27:42 -070011#include <array>
12#include <cstddef>
Vernon Mauery0120b682019-03-25 13:08:54 -070013#include <cstdint>
Vernon Mauerybdda8002019-02-26 10:18:51 -080014#include <filesystem>
Patrick Venture3a5071a2018-09-12 13:27:42 -070015#include <fstream>
Vernon Mauery0120b682019-03-25 13:08:54 -070016#include <ipmid/api.hpp>
Vernon Mauery33250242019-03-12 16:49:26 -070017#include <ipmid/types.hpp>
Vernon Mauery6a98fe72019-03-11 15:57:48 -070018#include <ipmid/utils.hpp>
Patrick Venture3a5071a2018-09-12 13:27:42 -070019#include <memory>
Patrick Venture46470a32018-09-07 19:26:25 -070020#include <nlohmann/json.hpp>
Patrick Venture3a5071a2018-09-12 13:27:42 -070021#include <phosphor-logging/elog-errors.hpp>
22#include <phosphor-logging/log.hpp>
William A. Kennington III4c008022018-10-12 17:18:14 -070023#include <sdbusplus/message/types.hpp>
Patrick Venture3a5071a2018-09-12 13:27:42 -070024#include <string>
Vernon Mauery0120b682019-03-25 13:08:54 -070025#include <sys_info_param.hpp>
26#include <transporthandler.hpp>
Patrick Venture3a5071a2018-09-12 13:27:42 -070027#include <tuple>
28#include <vector>
29#include <xyz/openbmc_project/Common/error.hpp>
Yong Li18d77262018-10-09 01:59:45 +080030#include <xyz/openbmc_project/Control/Power/ACPIPowerState/server.hpp>
Patrick Venture3a5071a2018-09-12 13:27:42 -070031#include <xyz/openbmc_project/Software/Activation/server.hpp>
32#include <xyz/openbmc_project/Software/Version/server.hpp>
33#include <xyz/openbmc_project/State/BMC/server.hpp>
Ratan Guptab8e99552017-07-27 07:07:48 +053034
Patrick Venture0b02be92018-08-31 11:55:55 -070035extern sd_bus* bus;
vishwabmcba0bd5f2015-09-30 16:50:23 +053036
Alexander Amelkinba19c182018-09-04 15:49:36 +030037constexpr auto bmc_state_interface = "xyz.openbmc_project.State.BMC";
38constexpr auto bmc_state_property = "CurrentBMCState";
Marri Devender Rao5e007a52018-01-08 06:18:36 -060039constexpr auto bmc_interface = "xyz.openbmc_project.Inventory.Item.Bmc";
40constexpr auto bmc_guid_interface = "xyz.openbmc_project.Common.UUID";
41constexpr auto bmc_guid_property = "UUID";
42constexpr auto bmc_guid_len = 16;
43
Nagaraju Goruganti744398d2018-02-20 09:52:00 -060044static constexpr auto redundancyIntf =
45 "xyz.openbmc_project.Software.RedundancyPriority";
Patrick Venture0b02be92018-08-31 11:55:55 -070046static constexpr auto versionIntf = "xyz.openbmc_project.Software.Version";
Nagaraju Goruganti744398d2018-02-20 09:52:00 -060047static constexpr auto activationIntf =
48 "xyz.openbmc_project.Software.Activation";
49static constexpr auto softwareRoot = "/xyz/openbmc_project/software";
50
Chris Austen6caf28b2015-10-13 12:40:40 -050051void register_netfn_app_functions() __attribute__((constructor));
vishwabmcba0bd5f2015-09-30 16:50:23 +053052
Ratan Guptab8e99552017-07-27 07:07:48 +053053using namespace phosphor::logging;
54using namespace sdbusplus::xyz::openbmc_project::Common::Error;
Nagaraju Goruganti744398d2018-02-20 09:52:00 -060055using Version = sdbusplus::xyz::openbmc_project::Software::server::Version;
56using Activation =
57 sdbusplus::xyz::openbmc_project::Software::server::Activation;
Alexander Amelkinba19c182018-09-04 15:49:36 +030058using BMC = sdbusplus::xyz::openbmc_project::State::server::BMC;
Vernon Mauery185b9f82018-07-20 10:52:36 -070059namespace fs = std::filesystem;
Ratan Guptab8e99552017-07-27 07:07:48 +053060
Nagaraju Goruganti744398d2018-02-20 09:52:00 -060061/**
62 * @brief Returns the Version info from primary s/w object
63 *
64 * Get the Version info from the active s/w object which is having high
65 * "Priority" value(a smaller number is a higher priority) and "Purpose"
66 * is "BMC" from the list of all s/w objects those are implementing
67 * RedundancyPriority interface from the given softwareRoot path.
68 *
69 * @return On success returns the Version info from primary s/w object.
70 *
71 */
72std::string getActiveSoftwareVersionInfo()
73{
Vernon Mauery86a50822019-03-25 13:11:36 -070074 auto busp = getSdBus();
Nagaraju Goruganti744398d2018-02-20 09:52:00 -060075
76 std::string revision{};
Vernon Mauery86a50822019-03-25 13:11:36 -070077 ipmi::ObjectTree objectTree;
78 try
Nagaraju Goruganti744398d2018-02-20 09:52:00 -060079 {
Vernon Mauery86a50822019-03-25 13:11:36 -070080 objectTree =
81 ipmi::getAllDbusObjects(*busp, softwareRoot, redundancyIntf);
82 }
83 catch (sdbusplus::exception::SdBusError& e)
84 {
85 log<level::ERR>("Failed to fetch redundancy object from dbus",
86 entry("INTERFACE=%s", redundancyIntf),
87 entry("ERRMSG=%s", e.what()));
Nagaraju Goruganti744398d2018-02-20 09:52:00 -060088 elog<InternalFailure>();
89 }
90
91 auto objectFound = false;
92 for (auto& softObject : objectTree)
93 {
Vernon Mauery86a50822019-03-25 13:11:36 -070094 auto service =
95 ipmi::getService(*busp, redundancyIntf, softObject.first);
96 auto objValueTree =
97 ipmi::getManagedObjects(*busp, service, softwareRoot);
Nagaraju Goruganti744398d2018-02-20 09:52:00 -060098
99 auto minPriority = 0xFF;
100 for (const auto& objIter : objValueTree)
101 {
102 try
103 {
104 auto& intfMap = objIter.second;
105 auto& redundancyPriorityProps = intfMap.at(redundancyIntf);
106 auto& versionProps = intfMap.at(versionIntf);
107 auto& activationProps = intfMap.at(activationIntf);
Vernon Maueryf442e112019-04-09 11:44:36 -0700108 auto priority =
109 std::get<uint8_t>(redundancyPriorityProps.at("Priority"));
William A. Kennington III4c008022018-10-12 17:18:14 -0700110 auto purpose =
Vernon Maueryf442e112019-04-09 11:44:36 -0700111 std::get<std::string>(versionProps.at("Purpose"));
112 auto activation =
113 std::get<std::string>(activationProps.at("Activation"));
William A. Kennington III4c008022018-10-12 17:18:14 -0700114 auto version =
Vernon Maueryf442e112019-04-09 11:44:36 -0700115 std::get<std::string>(versionProps.at("Version"));
Nagaraju Goruganti744398d2018-02-20 09:52:00 -0600116 if ((Version::convertVersionPurposeFromString(purpose) ==
117 Version::VersionPurpose::BMC) &&
118 (Activation::convertActivationsFromString(activation) ==
119 Activation::Activations::Active))
120 {
121 if (priority < minPriority)
122 {
123 minPriority = priority;
124 objectFound = true;
125 revision = std::move(version);
126 }
127 }
128 }
129 catch (const std::exception& e)
130 {
131 log<level::ERR>(e.what());
132 }
133 }
134 }
135
136 if (!objectFound)
137 {
138 log<level::ERR>("Could not found an BMC software Object");
139 elog<InternalFailure>();
140 }
141
142 return revision;
143}
144
Alexander Amelkinba19c182018-09-04 15:49:36 +0300145bool getCurrentBmcState()
146{
147 sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()};
148
149 // Get the Inventory object implementing the BMC interface
150 ipmi::DbusObjectInfo bmcObject =
151 ipmi::getDbusObject(bus, bmc_state_interface);
152 auto variant =
153 ipmi::getDbusProperty(bus, bmcObject.second, bmcObject.first,
154 bmc_state_interface, bmc_state_property);
155
Vernon Maueryf442e112019-04-09 11:44:36 -0700156 return std::holds_alternative<std::string>(variant) &&
157 BMC::convertBMCStateFromString(std::get<std::string>(variant)) ==
158 BMC::BMCState::Ready;
Alexander Amelkinba19c182018-09-04 15:49:36 +0300159}
160
Yong Li18d77262018-10-09 01:59:45 +0800161namespace acpi_state
162{
163using namespace sdbusplus::xyz::openbmc_project::Control::Power::server;
164
165const static constexpr char* acpiObjPath =
166 "/xyz/openbmc_project/control/host0/acpi_power_state";
167const static constexpr char* acpiInterface =
168 "xyz.openbmc_project.Control.Power.ACPIPowerState";
169const static constexpr char* sysACPIProp = "SysACPIStatus";
170const static constexpr char* devACPIProp = "DevACPIStatus";
171
172enum class PowerStateType : uint8_t
173{
174 sysPowerState = 0x00,
175 devPowerState = 0x01,
176};
177
178// Defined in 20.6 of ipmi doc
179enum class PowerState : uint8_t
180{
181 s0G0D0 = 0x00,
182 s1D1 = 0x01,
183 s2D2 = 0x02,
184 s3D3 = 0x03,
185 s4 = 0x04,
186 s5G2 = 0x05,
187 s4S5 = 0x06,
188 g3 = 0x07,
189 sleep = 0x08,
190 g1Sleep = 0x09,
191 override = 0x0a,
192 legacyOn = 0x20,
193 legacyOff = 0x21,
194 unknown = 0x2a,
195 noChange = 0x7f,
196};
197
198static constexpr uint8_t stateChanged = 0x80;
199
200struct ACPIState
201{
202 uint8_t sysACPIState;
203 uint8_t devACPIState;
204} __attribute__((packed));
205
206std::map<ACPIPowerState::ACPI, PowerState> dbusToIPMI = {
207 {ACPIPowerState::ACPI::S0_G0_D0, PowerState::s0G0D0},
208 {ACPIPowerState::ACPI::S1_D1, PowerState::s1D1},
209 {ACPIPowerState::ACPI::S2_D2, PowerState::s2D2},
210 {ACPIPowerState::ACPI::S3_D3, PowerState::s3D3},
211 {ACPIPowerState::ACPI::S4, PowerState::s4},
212 {ACPIPowerState::ACPI::S5_G2, PowerState::s5G2},
213 {ACPIPowerState::ACPI::S4_S5, PowerState::s4S5},
214 {ACPIPowerState::ACPI::G3, PowerState::g3},
215 {ACPIPowerState::ACPI::SLEEP, PowerState::sleep},
216 {ACPIPowerState::ACPI::G1_SLEEP, PowerState::g1Sleep},
217 {ACPIPowerState::ACPI::OVERRIDE, PowerState::override},
218 {ACPIPowerState::ACPI::LEGACY_ON, PowerState::legacyOn},
219 {ACPIPowerState::ACPI::LEGACY_OFF, PowerState::legacyOff},
220 {ACPIPowerState::ACPI::Unknown, PowerState::unknown}};
221
222bool isValidACPIState(acpi_state::PowerStateType type, uint8_t state)
223{
224 if (type == acpi_state::PowerStateType::sysPowerState)
225 {
226 if ((state <= static_cast<uint8_t>(acpi_state::PowerState::override)) ||
227 (state == static_cast<uint8_t>(acpi_state::PowerState::legacyOn)) ||
228 (state ==
229 static_cast<uint8_t>(acpi_state::PowerState::legacyOff)) ||
230 (state == static_cast<uint8_t>(acpi_state::PowerState::unknown)) ||
231 (state == static_cast<uint8_t>(acpi_state::PowerState::noChange)))
232 {
233 return true;
234 }
235 else
236 {
237 return false;
238 }
239 }
240 else if (type == acpi_state::PowerStateType::devPowerState)
241 {
242 if ((state <= static_cast<uint8_t>(acpi_state::PowerState::s3D3)) ||
243 (state == static_cast<uint8_t>(acpi_state::PowerState::unknown)) ||
244 (state == static_cast<uint8_t>(acpi_state::PowerState::noChange)))
245 {
246 return true;
247 }
248 else
249 {
250 return false;
251 }
252 }
253 else
254 {
255 return false;
256 }
257 return false;
258}
259} // namespace acpi_state
260
Adriana Kobylak3a552e12015-10-19 16:11:00 -0500261ipmi_ret_t ipmi_app_set_acpi_power_state(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
Patrick Venture0b02be92018-08-31 11:55:55 -0700262 ipmi_request_t request,
263 ipmi_response_t response,
264 ipmi_data_len_t data_len,
265 ipmi_context_t context)
Chris Austen6caf28b2015-10-13 12:40:40 -0500266{
Yong Li18d77262018-10-09 01:59:45 +0800267 auto s = static_cast<uint8_t>(acpi_state::PowerState::unknown);
Chris Austen6caf28b2015-10-13 12:40:40 -0500268 ipmi_ret_t rc = IPMI_CC_OK;
Yong Li18d77262018-10-09 01:59:45 +0800269
270 sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()};
271
272 auto value = acpi_state::ACPIPowerState::ACPI::Unknown;
273
274 auto* req = reinterpret_cast<acpi_state::ACPIState*>(request);
275
276 if (*data_len != sizeof(acpi_state::ACPIState))
277 {
278 log<level::ERR>("set_acpi invalid len");
279 *data_len = 0;
280 return IPMI_CC_REQ_DATA_LEN_INVALID;
281 }
282
Chris Austen6caf28b2015-10-13 12:40:40 -0500283 *data_len = 0;
284
Yong Li18d77262018-10-09 01:59:45 +0800285 if (req->sysACPIState & acpi_state::stateChanged)
286 {
287 // set system power state
288 s = req->sysACPIState & ~acpi_state::stateChanged;
289
290 if (!acpi_state::isValidACPIState(
291 acpi_state::PowerStateType::sysPowerState, s))
292 {
293 log<level::ERR>("set_acpi_power sys invalid input",
294 entry("S=%x", s));
295 return IPMI_CC_PARM_OUT_OF_RANGE;
296 }
297
298 // valid input
299 if (s == static_cast<uint8_t>(acpi_state::PowerState::noChange))
300 {
301 log<level::DEBUG>("No change for system power state");
302 }
303 else
304 {
305 auto found = std::find_if(
306 acpi_state::dbusToIPMI.begin(), acpi_state::dbusToIPMI.end(),
307 [&s](const auto& iter) {
308 return (static_cast<uint8_t>(iter.second) == s);
309 });
310
311 value = found->first;
312
313 try
314 {
315 auto acpiObject =
316 ipmi::getDbusObject(bus, acpi_state::acpiInterface);
317 ipmi::setDbusProperty(bus, acpiObject.second, acpiObject.first,
318 acpi_state::acpiInterface,
319 acpi_state::sysACPIProp,
320 convertForMessage(value));
321 }
322 catch (const InternalFailure& e)
323 {
324 log<level::ERR>("Failed in set ACPI system property",
325 entry("EXCEPTION=%s", e.what()));
326 return IPMI_CC_UNSPECIFIED_ERROR;
327 }
328 }
329 }
330 else
331 {
332 log<level::DEBUG>("Do not change system power state");
333 }
334
335 if (req->devACPIState & acpi_state::stateChanged)
336 {
337 // set device power state
338 s = req->devACPIState & ~acpi_state::stateChanged;
339 if (!acpi_state::isValidACPIState(
340 acpi_state::PowerStateType::devPowerState, s))
341 {
342 log<level::ERR>("set_acpi_power dev invalid input",
343 entry("S=%x", s));
344 return IPMI_CC_PARM_OUT_OF_RANGE;
345 }
346
347 // valid input
348 if (s == static_cast<uint8_t>(acpi_state::PowerState::noChange))
349 {
350 log<level::DEBUG>("No change for device power state");
351 }
352 else
353 {
354 auto found = std::find_if(
355 acpi_state::dbusToIPMI.begin(), acpi_state::dbusToIPMI.end(),
356 [&s](const auto& iter) {
357 return (static_cast<uint8_t>(iter.second) == s);
358 });
359
360 value = found->first;
361
362 try
363 {
364 auto acpiObject =
365 ipmi::getDbusObject(bus, acpi_state::acpiInterface);
366 ipmi::setDbusProperty(bus, acpiObject.second, acpiObject.first,
367 acpi_state::acpiInterface,
368 acpi_state::devACPIProp,
369 convertForMessage(value));
370 }
371 catch (const InternalFailure& e)
372 {
373 log<level::ERR>("Failed in set ACPI device property",
374 entry("EXCEPTION=%s", e.what()));
375 return IPMI_CC_UNSPECIFIED_ERROR;
376 }
377 }
378 }
379 else
380 {
381 log<level::DEBUG>("Do not change device power state");
382 }
383
384 return rc;
385}
386
387ipmi_ret_t ipmi_app_get_acpi_power_state(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
388 ipmi_request_t request,
389 ipmi_response_t response,
390 ipmi_data_len_t data_len,
391 ipmi_context_t context)
392{
393 ipmi_ret_t rc = IPMI_CC_OK;
394
395 auto* res = reinterpret_cast<acpi_state::ACPIState*>(response);
396
397 sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()};
398
399 *data_len = 0;
400
401 try
402 {
403 auto acpiObject = ipmi::getDbusObject(bus, acpi_state::acpiInterface);
404
405 auto sysACPIVal = ipmi::getDbusProperty(
406 bus, acpiObject.second, acpiObject.first, acpi_state::acpiInterface,
407 acpi_state::sysACPIProp);
408 auto sysACPI = acpi_state::ACPIPowerState::convertACPIFromString(
Vernon Maueryf442e112019-04-09 11:44:36 -0700409 std::get<std::string>(sysACPIVal));
Yong Li18d77262018-10-09 01:59:45 +0800410 res->sysACPIState =
411 static_cast<uint8_t>(acpi_state::dbusToIPMI.at(sysACPI));
412
413 auto devACPIVal = ipmi::getDbusProperty(
414 bus, acpiObject.second, acpiObject.first, acpi_state::acpiInterface,
415 acpi_state::devACPIProp);
416 auto devACPI = acpi_state::ACPIPowerState::convertACPIFromString(
Vernon Maueryf442e112019-04-09 11:44:36 -0700417 std::get<std::string>(devACPIVal));
Yong Li18d77262018-10-09 01:59:45 +0800418 res->devACPIState =
419 static_cast<uint8_t>(acpi_state::dbusToIPMI.at(devACPI));
420
421 *data_len = sizeof(acpi_state::ACPIState);
422 }
423 catch (const InternalFailure& e)
424 {
425 log<level::ERR>("Failed in get ACPI property");
426 return IPMI_CC_UNSPECIFIED_ERROR;
427 }
Chris Austen6caf28b2015-10-13 12:40:40 -0500428 return rc;
429}
430
Chris Austen7303bdc2016-04-17 11:50:54 -0500431typedef struct
432{
433 char major;
434 char minor;
Chris Austen176c9652016-04-30 16:32:17 -0500435 uint16_t d[2];
Vernon Mauery86a50822019-03-25 13:11:36 -0700436} Revision;
Chris Austen7303bdc2016-04-17 11:50:54 -0500437
Dinesh Chinari2b7e07d2017-11-08 15:38:50 -0600438/* Currently supports the vx.x-x-[-x] and v1.x.x-x-[-x] format. It will */
439/* return -1 if not in those formats, this routine knows how to parse */
Chris Austen7303bdc2016-04-17 11:50:54 -0500440/* version = v0.6-19-gf363f61-dirty */
441/* ^ ^ ^^ ^ */
442/* | | |----------|-- additional details */
443/* | |---------------- Minor */
444/* |------------------ Major */
Dinesh Chinari2b7e07d2017-11-08 15:38:50 -0600445/* and version = v1.99.10-113-g65edf7d-r3-0-g9e4f715 */
446/* ^ ^ ^^ ^ */
447/* | | |--|---------- additional details */
448/* | |---------------- Minor */
449/* |------------------ Major */
Chris Austen7303bdc2016-04-17 11:50:54 -0500450/* Additional details : If the option group exists it will force Auxiliary */
451/* Firmware Revision Information 4th byte to 1 indicating the build was */
452/* derived with additional edits */
Vernon Mauery86a50822019-03-25 13:11:36 -0700453int convertVersion(std::string s, Revision& rev)
Chris Austen7303bdc2016-04-17 11:50:54 -0500454{
Dinesh Chinari2b7e07d2017-11-08 15:38:50 -0600455 std::string token;
Chris Austen176c9652016-04-30 16:32:17 -0500456 uint16_t commits;
Chris Austen7303bdc2016-04-17 11:50:54 -0500457
Patrick Venture0b02be92018-08-31 11:55:55 -0700458 auto location = s.find_first_of('v');
Dinesh Chinari2b7e07d2017-11-08 15:38:50 -0600459 if (location != std::string::npos)
460 {
Patrick Venture0b02be92018-08-31 11:55:55 -0700461 s = s.substr(location + 1);
Chris Austen176c9652016-04-30 16:32:17 -0500462 }
Chris Austen7303bdc2016-04-17 11:50:54 -0500463
Dinesh Chinari2b7e07d2017-11-08 15:38:50 -0600464 if (!s.empty())
465 {
466 location = s.find_first_of(".");
467 if (location != std::string::npos)
468 {
Vernon Mauery86a50822019-03-25 13:11:36 -0700469 rev.major =
Patrick Venture0b02be92018-08-31 11:55:55 -0700470 static_cast<char>(std::stoi(s.substr(0, location), 0, 16));
471 token = s.substr(location + 1);
Dinesh Chinari2b7e07d2017-11-08 15:38:50 -0600472 }
Chris Austen176c9652016-04-30 16:32:17 -0500473
Dinesh Chinari2b7e07d2017-11-08 15:38:50 -0600474 if (!token.empty())
475 {
476 location = token.find_first_of(".-");
477 if (location != std::string::npos)
478 {
Vernon Mauery86a50822019-03-25 13:11:36 -0700479 rev.minor = static_cast<char>(
Patrick Venture0b02be92018-08-31 11:55:55 -0700480 std::stoi(token.substr(0, location), 0, 16));
481 token = token.substr(location + 1);
Dinesh Chinari2b7e07d2017-11-08 15:38:50 -0600482 }
483 }
Chris Austen7303bdc2016-04-17 11:50:54 -0500484
Dinesh Chinari2b7e07d2017-11-08 15:38:50 -0600485 // Capture the number of commits on top of the minor tag.
486 // I'm using BE format like the ipmi spec asked for
487 location = token.find_first_of(".-");
488 if (!token.empty())
489 {
490 commits = std::stoi(token.substr(0, location), 0, 16);
Vernon Mauery86a50822019-03-25 13:11:36 -0700491 rev.d[0] = (commits >> 8) | (commits << 8);
Dinesh Chinari2b7e07d2017-11-08 15:38:50 -0600492
493 // commit number we skip
494 location = token.find_first_of(".-");
495 if (location != std::string::npos)
496 {
Patrick Venture0b02be92018-08-31 11:55:55 -0700497 token = token.substr(location + 1);
Dinesh Chinari2b7e07d2017-11-08 15:38:50 -0600498 }
499 }
Patrick Venture0b02be92018-08-31 11:55:55 -0700500 else
501 {
Vernon Mauery86a50822019-03-25 13:11:36 -0700502 rev.d[0] = 0;
Dinesh Chinari2b7e07d2017-11-08 15:38:50 -0600503 }
504
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
510 // Any value of the optional parameter forces it to 1
511 location = token.find_first_of(".-");
512 if (location != std::string::npos)
513 {
Patrick Venture0b02be92018-08-31 11:55:55 -0700514 token = token.substr(location + 1);
Dinesh Chinari2b7e07d2017-11-08 15:38:50 -0600515 }
516 commits = (!token.empty()) ? 1 : 0;
517
Patrick Venture0b02be92018-08-31 11:55:55 -0700518 // We do this operation to get this displayed in least significant bytes
519 // of ipmitool device id command.
Vernon Mauery86a50822019-03-25 13:11:36 -0700520 rev.d[1] = (commits >> 8) | (commits << 8);
Dinesh Chinari2b7e07d2017-11-08 15:38:50 -0600521 }
522
Chris Austen7303bdc2016-04-17 11:50:54 -0500523 return 0;
524}
525
Vernon Mauery86a50822019-03-25 13:11:36 -0700526auto ipmiAppGetDeviceId() -> ipmi::RspType<uint8_t, // Device ID
527 uint8_t, // Device Revision
528 uint8_t, // Firmware Revision Major
529 uint8_t, // Firmware Revision minor
530 uint8_t, // IPMI version
531 uint8_t, // Additional device support
532 uint24_t, // MFG ID
533 uint16_t, // Product ID
534 uint32_t // AUX info
535 >
Chris Austen6caf28b2015-10-13 12:40:40 -0500536{
Nagaraju Goruganti744398d2018-02-20 09:52:00 -0600537 int r = -1;
Vernon Mauery86a50822019-03-25 13:11:36 -0700538 Revision rev = {0};
539 static struct
540 {
541 uint8_t id;
542 uint8_t revision;
543 uint8_t fw[2];
544 uint8_t ipmiVer;
545 uint8_t addnDevSupport;
546 uint24_t manufId;
547 uint16_t prodId;
548 uint32_t aux;
549 } devId;
David Cobbleya1adb072017-11-21 15:58:13 -0800550 static bool dev_id_initialized = false;
551 const char* filename = "/usr/share/ipmi-providers/dev_id.json";
Alexander Amelkinba19c182018-09-04 15:49:36 +0300552 constexpr auto ipmiDevIdStateShift = 7;
553 constexpr auto ipmiDevIdFw1Mask = ~(1 << ipmiDevIdStateShift);
Chris Austen6caf28b2015-10-13 12:40:40 -0500554
David Cobbleya1adb072017-11-21 15:58:13 -0800555 if (!dev_id_initialized)
556 {
Nagaraju Goruganti744398d2018-02-20 09:52:00 -0600557 try
558 {
559 auto version = getActiveSoftwareVersionInfo();
Vernon Mauery86a50822019-03-25 13:11:36 -0700560 r = convertVersion(version, rev);
David Cobbleya1adb072017-11-21 15:58:13 -0800561 }
Nagaraju Goruganti744398d2018-02-20 09:52:00 -0600562 catch (const std::exception& e)
563 {
564 log<level::ERR>(e.what());
565 }
Nan Liee0cb902016-07-11 15:38:03 +0800566
Patrick Venture0b02be92018-08-31 11:55:55 -0700567 if (r >= 0)
568 {
Nagaraju Goruganti744398d2018-02-20 09:52:00 -0600569 // bit7 identifies if the device is available
570 // 0=normal operation
571 // 1=device firmware, SDR update,
572 // or self-initialization in progress.
Alexander Amelkinba19c182018-09-04 15:49:36 +0300573 // The availability may change in run time, so mask here
574 // and initialize later.
Vernon Mauery86a50822019-03-25 13:11:36 -0700575 devId.fw[0] = rev.major & ipmiDevIdFw1Mask;
Nagaraju Goruganti744398d2018-02-20 09:52:00 -0600576
577 rev.minor = (rev.minor > 99 ? 99 : rev.minor);
Vernon Mauery86a50822019-03-25 13:11:36 -0700578 devId.fw[1] = rev.minor % 10 + (rev.minor / 10) * 16;
579 std::memcpy(&devId.aux, rev.d, 4);
David Cobbleya1adb072017-11-21 15:58:13 -0800580 }
Nan Liee0cb902016-07-11 15:38:03 +0800581
David Cobbleya1adb072017-11-21 15:58:13 -0800582 // IPMI Spec version 2.0
Vernon Mauery86a50822019-03-25 13:11:36 -0700583 devId.ipmiVer = 2;
Adriana Kobylak0e912642016-06-22 16:54:39 -0500584
Vernon Mauery86a50822019-03-25 13:11:36 -0700585 std::ifstream devIdFile(filename);
586 if (devIdFile.is_open())
David Cobbleya1adb072017-11-21 15:58:13 -0800587 {
Vernon Mauery86a50822019-03-25 13:11:36 -0700588 auto data = nlohmann::json::parse(devIdFile, nullptr, false);
David Cobbleya1adb072017-11-21 15:58:13 -0800589 if (!data.is_discarded())
590 {
Vernon Mauery86a50822019-03-25 13:11:36 -0700591 devId.id = data.value("id", 0);
592 devId.revision = data.value("revision", 0);
593 devId.addnDevSupport = data.value("addn_dev_support", 0);
594 devId.manufId = data.value("manuf_id", 0);
595 devId.prodId = data.value("prod_id", 0);
596 devId.aux = data.value("aux", 0);
David Cobbleya1adb072017-11-21 15:58:13 -0800597
Patrick Venture0b02be92018-08-31 11:55:55 -0700598 // Don't read the file every time if successful
David Cobbleya1adb072017-11-21 15:58:13 -0800599 dev_id_initialized = true;
600 }
601 else
602 {
603 log<level::ERR>("Device ID JSON parser failure");
Vernon Maueryf2587fc2019-04-09 14:28:15 -0700604 return ipmi::responseUnspecifiedError();
David Cobbleya1adb072017-11-21 15:58:13 -0800605 }
606 }
607 else
608 {
609 log<level::ERR>("Device ID file not found");
Vernon Maueryf2587fc2019-04-09 14:28:15 -0700610 return ipmi::responseUnspecifiedError();
Chris Austen7303bdc2016-04-17 11:50:54 -0500611 }
612 }
Chris Austen6caf28b2015-10-13 12:40:40 -0500613
Alexander Amelkinba19c182018-09-04 15:49:36 +0300614 // Set availability to the actual current BMC state
Vernon Mauery86a50822019-03-25 13:11:36 -0700615 devId.fw[0] &= ipmiDevIdFw1Mask;
Alexander Amelkinba19c182018-09-04 15:49:36 +0300616 if (!getCurrentBmcState())
617 {
Vernon Mauery86a50822019-03-25 13:11:36 -0700618 devId.fw[0] |= (1 << ipmiDevIdStateShift);
Alexander Amelkinba19c182018-09-04 15:49:36 +0300619 }
620
Vernon Mauery86a50822019-03-25 13:11:36 -0700621 return ipmi::responseSuccess(
622 devId.id, devId.revision, devId.fw[0], devId.fw[1], devId.ipmiVer,
623 devId.addnDevSupport, devId.manufId, devId.prodId, devId.aux);
Chris Austen6caf28b2015-10-13 12:40:40 -0500624}
625
Vernon Maueryb84a5282019-03-25 13:39:03 -0700626auto ipmiAppGetSelfTestResults() -> ipmi::RspType<uint8_t, uint8_t>
Nan Li41fa24a2016-11-10 20:12:37 +0800627{
Nan Li41fa24a2016-11-10 20:12:37 +0800628 // Byte 2:
629 // 55h - No error.
Gunnar Mills8991dd62017-10-25 17:11:29 -0500630 // 56h - Self Test function not implemented in this controller.
Nan Li41fa24a2016-11-10 20:12:37 +0800631 // 57h - Corrupted or inaccesssible data or devices.
632 // 58h - Fatal hardware error.
633 // FFh - reserved.
634 // all other: Device-specific 'internal failure'.
635 // Byte 3:
636 // For byte 2 = 55h, 56h, FFh: 00h
637 // For byte 2 = 58h, all other: Device-specific
638 // For byte 2 = 57h: self-test error bitfield.
639 // Note: returning 57h does not imply that all test were run.
640 // [7] 1b = Cannot access SEL device.
641 // [6] 1b = Cannot access SDR Repository.
642 // [5] 1b = Cannot access BMC FRU device.
643 // [4] 1b = IPMB signal lines do not respond.
644 // [3] 1b = SDR Repository empty.
645 // [2] 1b = Internal Use Area of BMC FRU corrupted.
646 // [1] 1b = controller update 'boot block' firmware corrupted.
647 // [0] 1b = controller operational firmware corrupted.
Vernon Maueryb84a5282019-03-25 13:39:03 -0700648 constexpr uint8_t notImplemented = 0x56;
649 constexpr uint8_t zero = 0;
650 return ipmi::responseSuccess(notImplemented, zero);
Nan Li41fa24a2016-11-10 20:12:37 +0800651}
652
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500653ipmi_ret_t ipmi_app_get_device_guid(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
Patrick Venture0b02be92018-08-31 11:55:55 -0700654 ipmi_request_t request,
655 ipmi_response_t response,
656 ipmi_data_len_t data_len,
657 ipmi_context_t context)
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500658{
Patrick Venture0b02be92018-08-31 11:55:55 -0700659 const char* objname = "/org/openbmc/control/chassis0";
660 const char* iface = "org.freedesktop.DBus.Properties";
661 const char* chassis_iface = "org.openbmc.control.Chassis";
662 sd_bus_message* reply = NULL;
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500663 sd_bus_error error = SD_BUS_ERROR_NULL;
664 int r = 0;
Patrick Venture0b02be92018-08-31 11:55:55 -0700665 char* uuid = NULL;
666 char* busname = NULL;
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500667
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500668 // UUID is in RFC4122 format. Ex: 61a39523-78f2-11e5-9862-e6402cfc3223
Patrick Ventured2117022018-02-06 08:54:37 -0800669 // Per IPMI Spec 2.0 need to convert to 16 hex bytes and reverse the byte
670 // order
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500671 // Ex: 0x2332fc2c40e66298e511f2782395a361
672
Patrick Venture0b02be92018-08-31 11:55:55 -0700673 const int resp_size = 16; // Response is 16 hex bytes per IPMI Spec
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500674 uint8_t resp_uuid[resp_size]; // Array to hold the formatted response
Patrick Ventured2117022018-02-06 08:54:37 -0800675 // Point resp end of array to save in reverse order
Patrick Venture0b02be92018-08-31 11:55:55 -0700676 int resp_loc = resp_size - 1;
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500677 int i = 0;
Patrick Venture0b02be92018-08-31 11:55:55 -0700678 char* tokptr = NULL;
679 char* id_octet = NULL;
Emily Shafferedb8bb02018-09-27 14:50:15 -0700680 size_t total_uuid_size = 0;
681 // 1 byte of resp is built from 2 chars of uuid.
682 constexpr size_t max_uuid_size = 2 * resp_size;
vishwa1eaea4f2016-02-26 11:57:40 -0600683
684 // Status code.
685 ipmi_ret_t rc = IPMI_CC_OK;
686 *data_len = 0;
687
vishwa1eaea4f2016-02-26 11:57:40 -0600688 // Call Get properties method with the interface and property name
Sergey Solomineb9b8142016-08-23 09:07:28 -0500689 r = mapper_get_service(bus, objname, &busname);
Patrick Venture0b02be92018-08-31 11:55:55 -0700690 if (r < 0)
691 {
692 log<level::ERR>("Failed to get bus name", entry("BUS=%s", objname),
Aditya Saripalli5fb14602017-11-09 14:46:27 +0530693 entry("ERRNO=0x%X", -r));
Sergey Solomineb9b8142016-08-23 09:07:28 -0500694 goto finish;
695 }
Patrick Venture0b02be92018-08-31 11:55:55 -0700696 r = sd_bus_call_method(bus, busname, objname, iface, "Get", &error, &reply,
697 "ss", chassis_iface, "uuid");
vishwa1eaea4f2016-02-26 11:57:40 -0600698 if (r < 0)
699 {
Patrick Venture0b02be92018-08-31 11:55:55 -0700700 log<level::ERR>("Failed to call Get Method", entry("ERRNO=0x%X", -r));
vishwa1eaea4f2016-02-26 11:57:40 -0600701 rc = IPMI_CC_UNSPECIFIED_ERROR;
702 goto finish;
703 }
704
705 r = sd_bus_message_read(reply, "v", "s", &uuid);
706 if (r < 0 || uuid == NULL)
707 {
Patrick Venture0b02be92018-08-31 11:55:55 -0700708 log<level::ERR>("Failed to get a response", entry("ERRNO=0x%X", -r));
vishwa1eaea4f2016-02-26 11:57:40 -0600709 rc = IPMI_CC_RESPONSE_ERROR;
710 goto finish;
711 }
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500712
713 // Traverse the UUID
Patrick Ventured2117022018-02-06 08:54:37 -0800714 // Get the UUID octects separated by dash
715 id_octet = strtok_r(uuid, "-", &tokptr);
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500716
717 if (id_octet == NULL)
vishwa1eaea4f2016-02-26 11:57:40 -0600718 {
719 // Error
Patrick Venture0b02be92018-08-31 11:55:55 -0700720 log<level::ERR>("Unexpected UUID format", entry("UUID=%s", uuid));
vishwa1eaea4f2016-02-26 11:57:40 -0600721 rc = IPMI_CC_RESPONSE_ERROR;
722 goto finish;
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500723 }
724
725 while (id_octet != NULL)
726 {
727 // Calculate the octet string size since it varies
728 // Divide it by 2 for the array size since 1 byte is built from 2 chars
Patrick Venture0b02be92018-08-31 11:55:55 -0700729 int tmp_size = strlen(id_octet) / 2;
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500730
Emily Shafferedb8bb02018-09-27 14:50:15 -0700731 // Check if total UUID size has been exceeded
732 if ((total_uuid_size += strlen(id_octet)) > max_uuid_size)
733 {
734 // Error - UUID too long to store
735 log<level::ERR>("UUID too long", entry("UUID=%s", uuid));
736 rc = IPMI_CC_RESPONSE_ERROR;
737 goto finish;
738 }
739
Patrick Venture0b02be92018-08-31 11:55:55 -0700740 for (i = 0; i < tmp_size; i++)
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500741 {
Patrick Ventured2117022018-02-06 08:54:37 -0800742 // Holder of the 2 chars that will become a byte
743 char tmp_array[3] = {0};
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500744 strncpy(tmp_array, id_octet, 2); // 2 chars at a time
745
746 int resp_byte = strtoul(tmp_array, NULL, 16); // Convert to hex byte
Patrick Ventured2117022018-02-06 08:54:37 -0800747 // Copy end to first
Patrick Ventureb51bf9c2018-09-10 15:53:14 -0700748 std::memcpy((void*)&resp_uuid[resp_loc], &resp_byte, 1);
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500749 resp_loc--;
Patrick Venture0b02be92018-08-31 11:55:55 -0700750 id_octet += 2; // Finished with the 2 chars, advance
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500751 }
Patrick Venture0b02be92018-08-31 11:55:55 -0700752 id_octet = strtok_r(NULL, "-", &tokptr); // Get next octet
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500753 }
754
755 // Data length
756 *data_len = resp_size;
757
758 // Pack the actual response
Patrick Ventureb51bf9c2018-09-10 15:53:14 -0700759 std::memcpy(response, &resp_uuid, *data_len);
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500760
vishwa1eaea4f2016-02-26 11:57:40 -0600761finish:
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500762 sd_bus_error_free(&error);
vishwa1eaea4f2016-02-26 11:57:40 -0600763 reply = sd_bus_message_unref(reply);
Sergey Solomineb9b8142016-08-23 09:07:28 -0500764 free(busname);
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500765
766 return rc;
767}
Chris Austen6caf28b2015-10-13 12:40:40 -0500768
Vernon Mauerycc99ba42019-03-25 13:40:11 -0700769auto ipmiAppGetBtCapabilities()
770 -> ipmi::RspType<uint8_t, uint8_t, uint8_t, uint8_t, uint8_t>
vishwabmcba0bd5f2015-09-30 16:50:23 +0530771{
Adriana Kobylak88ad8152016-12-13 10:09:08 -0600772 // Per IPMI 2.0 spec, the input and output buffer size must be the max
773 // buffer size minus one byte to allocate space for the length byte.
Vernon Mauerycc99ba42019-03-25 13:40:11 -0700774 constexpr uint8_t nrOutstanding = 0x01;
775 constexpr uint8_t inputBufferSize = MAX_IPMI_BUFFER - 1;
776 constexpr uint8_t outputBufferSize = MAX_IPMI_BUFFER - 1;
777 constexpr uint8_t transactionTime = 0x0A;
778 constexpr uint8_t nrRetries = 0x01;
vishwabmcba0bd5f2015-09-30 16:50:23 +0530779
Vernon Mauerycc99ba42019-03-25 13:40:11 -0700780 return ipmi::responseSuccess(nrOutstanding, inputBufferSize,
781 outputBufferSize, transactionTime, nrRetries);
vishwabmcba0bd5f2015-09-30 16:50:23 +0530782}
783
Marri Devender Rao5e007a52018-01-08 06:18:36 -0600784ipmi_ret_t ipmi_app_get_sys_guid(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
Patrick Venture0b02be92018-08-31 11:55:55 -0700785 ipmi_request_t request,
786 ipmi_response_t response,
787 ipmi_data_len_t data_len,
788 ipmi_context_t context)
Marri Devender Rao5e007a52018-01-08 06:18:36 -0600789
790{
791 ipmi_ret_t rc = IPMI_CC_OK;
792 sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()};
793
794 try
795 {
796 // Get the Inventory object implementing BMC interface
797 ipmi::DbusObjectInfo bmcObject =
798 ipmi::getDbusObject(bus, bmc_interface);
799
800 // Read UUID property value from bmcObject
801 // UUID is in RFC4122 format Ex: 61a39523-78f2-11e5-9862-e6402cfc3223
Patrick Venture0b02be92018-08-31 11:55:55 -0700802 auto variant =
803 ipmi::getDbusProperty(bus, bmcObject.second, bmcObject.first,
804 bmc_guid_interface, bmc_guid_property);
Vernon Maueryf442e112019-04-09 11:44:36 -0700805 std::string guidProp = std::get<std::string>(variant);
Marri Devender Rao5e007a52018-01-08 06:18:36 -0600806
807 // Erase "-" characters from the property value
808 guidProp.erase(std::remove(guidProp.begin(), guidProp.end(), '-'),
Patrick Venture0b02be92018-08-31 11:55:55 -0700809 guidProp.end());
Marri Devender Rao5e007a52018-01-08 06:18:36 -0600810
811 auto guidPropLen = guidProp.length();
812 // Validate UUID data
813 // Divide by 2 as 1 byte is built from 2 chars
Patrick Venture0b02be92018-08-31 11:55:55 -0700814 if ((guidPropLen <= 0) || ((guidPropLen / 2) != bmc_guid_len))
Marri Devender Rao5e007a52018-01-08 06:18:36 -0600815
816 {
817 log<level::ERR>("Invalid UUID property value",
Patrick Venture0b02be92018-08-31 11:55:55 -0700818 entry("UUID_LENGTH=%d", guidPropLen));
Marri Devender Rao5e007a52018-01-08 06:18:36 -0600819 return IPMI_CC_RESPONSE_ERROR;
820 }
821
822 // Convert data in RFC4122(MSB) format to LSB format
823 // Get 2 characters at a time as 1 byte is built from 2 chars and
824 // convert to hex byte
825 // TODO: Data printed for GUID command is not as per the
826 // GUID format defined in IPMI specification 2.0 section 20.8
827 // Ticket raised: https://sourceforge.net/p/ipmitool/bugs/501/
828 uint8_t respGuid[bmc_guid_len];
829 for (size_t i = 0, respLoc = (bmc_guid_len - 1);
Patrick Venture0b02be92018-08-31 11:55:55 -0700830 i < guidPropLen && respLoc >= 0; i += 2, respLoc--)
Marri Devender Rao5e007a52018-01-08 06:18:36 -0600831 {
832 auto value = static_cast<uint8_t>(
Patrick Venture0b02be92018-08-31 11:55:55 -0700833 std::stoi(guidProp.substr(i, 2).c_str(), NULL, 16));
Marri Devender Rao5e007a52018-01-08 06:18:36 -0600834 respGuid[respLoc] = value;
835 }
836
837 *data_len = bmc_guid_len;
Patrick Ventureb51bf9c2018-09-10 15:53:14 -0700838 std::memcpy(response, &respGuid, bmc_guid_len);
Marri Devender Rao5e007a52018-01-08 06:18:36 -0600839 }
840 catch (const InternalFailure& e)
841 {
842 log<level::ERR>("Failed in reading BMC UUID property",
843 entry("INTERFACE=%s", bmc_interface),
844 entry("PROPERTY_INTERFACE=%s", bmc_guid_interface),
845 entry("PROPERTY=%s", bmc_guid_property));
846 return IPMI_CC_UNSPECIFIED_ERROR;
847 }
848 return rc;
849}
850
Xo Wangf542e8b2017-08-09 15:34:16 -0700851static std::unique_ptr<SysInfoParamStore> sysInfoParamStore;
852
Xo Wang87651332017-08-11 10:17:59 -0700853static std::string sysInfoReadSystemName()
854{
855 // Use the BMC hostname as the "System Name."
856 char hostname[HOST_NAME_MAX + 1] = {};
857 if (gethostname(hostname, HOST_NAME_MAX) != 0)
858 {
859 perror("System info parameter: system name");
860 }
861 return hostname;
862}
863
Xo Wangf542e8b2017-08-09 15:34:16 -0700864struct IpmiSysInfoResp
865{
866 uint8_t paramRevision;
867 uint8_t setSelector;
868 union
869 {
870 struct
871 {
872 uint8_t encoding;
873 uint8_t stringLen;
874 uint8_t stringData0[14];
875 } __attribute__((packed));
876 uint8_t stringDataN[16];
877 uint8_t byteData;
878 };
879} __attribute__((packed));
880
881/**
882 * Split a string into (up to) 16-byte chunks as expected in response for get
883 * system info parameter.
884 *
885 * @param[in] fullString: Input string to be split
886 * @param[in] chunkIndex: Index of the chunk to be written out
887 * @param[in,out] chunk: Output data buffer; must have 14 byte capacity if
888 * chunk_index = 0 and 16-byte capacity otherwise
889 * @return the number of bytes written into the output buffer, or -EINVAL for
890 * invalid arguments.
891 */
892static int splitStringParam(const std::string& fullString, int chunkIndex,
893 uint8_t* chunk)
894{
895 constexpr int maxChunk = 255;
896 constexpr int smallChunk = 14;
897 constexpr int chunkSize = 16;
898 if (chunkIndex > maxChunk || chunk == nullptr)
899 {
900 return -EINVAL;
901 }
902 try
903 {
904 std::string output;
905 if (chunkIndex == 0)
906 {
907 // Output must have 14 byte capacity.
908 output = fullString.substr(0, smallChunk);
909 }
910 else
911 {
912 // Output must have 16 byte capacity.
913 output = fullString.substr((chunkIndex * chunkSize) - 2, chunkSize);
914 }
915
916 std::memcpy(chunk, output.c_str(), output.length());
917 return output.length();
918 }
919 catch (const std::out_of_range& e)
920 {
921 // The position was beyond the end.
922 return -EINVAL;
923 }
924}
925
926/**
927 * Packs the Get Sys Info Request Item into the response.
928 *
929 * @param[in] paramString - the parameter.
930 * @param[in] setSelector - the selector
931 * @param[in,out] resp - the System info response.
932 * @return The number of bytes packed or failure from splitStringParam().
933 */
934static int packGetSysInfoResp(const std::string& paramString,
935 uint8_t setSelector, IpmiSysInfoResp* resp)
936{
937 uint8_t* dataBuffer = resp->stringDataN;
938 resp->setSelector = setSelector;
939 if (resp->setSelector == 0) // First chunk has only 14 bytes.
940 {
941 resp->encoding = 0;
942 resp->stringLen = paramString.length();
943 dataBuffer = resp->stringData0;
944 }
945 return splitStringParam(paramString, resp->setSelector, dataBuffer);
946}
947
948ipmi_ret_t ipmi_app_get_system_info(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
949 ipmi_request_t request,
950 ipmi_response_t response,
951 ipmi_data_len_t dataLen,
952 ipmi_context_t context)
953{
954 IpmiSysInfoResp resp = {};
955 size_t respLen = 0;
956 uint8_t* const reqData = static_cast<uint8_t*>(request);
957 std::string paramString;
958 bool found;
959 std::tuple<bool, std::string> ret;
960 constexpr int minRequestSize = 4;
961 constexpr int paramSelector = 1;
962 constexpr uint8_t revisionOnly = 0x80;
963 const uint8_t paramRequested = reqData[paramSelector];
964 int rc;
965
966 if (*dataLen < minRequestSize)
967 {
968 return IPMI_CC_REQ_DATA_LEN_INVALID;
969 }
970
971 *dataLen = 0; // default to 0.
972
973 // Parameters revision as of IPMI spec v2.0 rev. 1.1 (Feb 11, 2014 E6)
974 resp.paramRevision = 0x11;
975 if (reqData[0] & revisionOnly) // Get parameter revision only
976 {
977 respLen = 1;
978 goto writeResponse;
979 }
980
981 // The "Set In Progress" parameter can be used for rollback of parameter
982 // data and is not implemented.
983 if (paramRequested == 0)
984 {
985 resp.byteData = 0;
986 respLen = 2;
987 goto writeResponse;
988 }
989
990 if (sysInfoParamStore == nullptr)
991 {
992 sysInfoParamStore = std::make_unique<SysInfoParamStore>();
Xo Wang87651332017-08-11 10:17:59 -0700993 sysInfoParamStore->update(IPMI_SYSINFO_SYSTEM_NAME,
994 sysInfoReadSystemName);
Xo Wangf542e8b2017-08-09 15:34:16 -0700995 }
996
997 // Parameters other than Set In Progress are assumed to be strings.
998 ret = sysInfoParamStore->lookup(paramRequested);
999 found = std::get<0>(ret);
1000 paramString = std::get<1>(ret);
1001 if (!found)
1002 {
1003 return IPMI_CC_SYSTEM_INFO_PARAMETER_NOT_SUPPORTED;
1004 }
1005 // TODO: Cache each parameter across multiple calls, until the whole string
1006 // has been read out. Otherwise, it's possible for a parameter to change
1007 // between requests for its chunks, returning chunks incoherent with each
1008 // other. For now, the parameter store is simply required to have only
1009 // idempotent callbacks.
1010 rc = packGetSysInfoResp(paramString, reqData[2], &resp);
1011 if (rc == -EINVAL)
1012 {
1013 return IPMI_CC_RESPONSE_ERROR;
1014 }
1015
1016 respLen = sizeof(resp); // Write entire string data chunk in response.
1017
1018writeResponse:
1019 std::memcpy(response, &resp, sizeof(resp));
1020 *dataLen = respLen;
1021 return IPMI_CC_OK;
1022}
1023
Chris Austen6caf28b2015-10-13 12:40:40 -05001024void register_netfn_app_functions()
vishwabmcba0bd5f2015-09-30 16:50:23 +05301025{
Vernon Mauery86a50822019-03-25 13:11:36 -07001026 // <Get Device ID>
1027 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp,
1028 ipmi::app::cmdGetDeviceId, ipmi::Privilege::User,
1029 ipmiAppGetDeviceId);
1030
Tom05732372016-09-06 17:21:23 +05301031 // <Get BT Interface Capabilities>
Vernon Mauerycc99ba42019-03-25 13:40:11 -07001032 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp,
1033 ipmi::app::cmdGetBtIfaceCapabilities,
1034 ipmi::Privilege::User, ipmiAppGetBtCapabilities);
Chris Austen6caf28b2015-10-13 12:40:40 -05001035
Tom05732372016-09-06 17:21:23 +05301036 // <Reset Watchdog Timer>
Vernon Mauery11df4f62019-03-25 14:17:54 -07001037 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp,
1038 ipmi::app::cmdResetWatchdogTimer,
1039 ipmi::Privilege::Operator, ipmiAppResetWatchdogTimer);
Chris Austen6caf28b2015-10-13 12:40:40 -05001040
Tom05732372016-09-06 17:21:23 +05301041 // <Set Watchdog Timer>
Patrick Venture0b02be92018-08-31 11:55:55 -07001042 ipmi_register_callback(NETFUN_APP, IPMI_CMD_SET_WD, NULL,
1043 ipmi_app_watchdog_set, PRIVILEGE_OPERATOR);
Chris Austen6caf28b2015-10-13 12:40:40 -05001044
William A. Kennington III73f44512018-02-09 15:28:46 -08001045 // <Get Watchdog Timer>
Patrick Venture0b02be92018-08-31 11:55:55 -07001046 ipmi_register_callback(NETFUN_APP, IPMI_CMD_GET_WD, NULL,
1047 ipmi_app_watchdog_get, PRIVILEGE_OPERATOR);
William A. Kennington III73f44512018-02-09 15:28:46 -08001048
Tom05732372016-09-06 17:21:23 +05301049 // <Get Self Test Results>
Vernon Maueryb84a5282019-03-25 13:39:03 -07001050 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp,
1051 ipmi::app::cmdGetSelfTestResults,
1052 ipmi::Privilege::User, ipmiAppGetSelfTestResults);
Nan Li41fa24a2016-11-10 20:12:37 +08001053
Tom05732372016-09-06 17:21:23 +05301054 // <Get Device GUID>
Patrick Venture0b02be92018-08-31 11:55:55 -07001055 ipmi_register_callback(NETFUN_APP, IPMI_CMD_GET_DEVICE_GUID, NULL,
1056 ipmi_app_get_device_guid, PRIVILEGE_USER);
Adriana Kobylakd100ee52015-10-20 17:02:37 -05001057
Tom05732372016-09-06 17:21:23 +05301058 // <Set ACPI Power State>
Patrick Venture0b02be92018-08-31 11:55:55 -07001059 ipmi_register_callback(NETFUN_APP, IPMI_CMD_SET_ACPI, NULL,
1060 ipmi_app_set_acpi_power_state, PRIVILEGE_ADMIN);
Chris Austen6caf28b2015-10-13 12:40:40 -05001061
Yong Li18d77262018-10-09 01:59:45 +08001062 // <Get ACPI Power State>
1063 ipmi_register_callback(NETFUN_APP, IPMI_CMD_GET_ACPI, NULL,
1064 ipmi_app_get_acpi_power_state, PRIVILEGE_ADMIN);
1065
Marri Devender Rao5e007a52018-01-08 06:18:36 -06001066 // <Get System GUID Command>
Patrick Venture0b02be92018-08-31 11:55:55 -07001067 ipmi_register_callback(NETFUN_APP, IPMI_CMD_GET_SYS_GUID, NULL,
1068 ipmi_app_get_sys_guid, PRIVILEGE_USER);
Tom Joseph7cbe2282018-03-21 21:17:33 +05301069
1070 // <Get Channel Cipher Suites Command>
Patrick Venture0b02be92018-08-31 11:55:55 -07001071 ipmi_register_callback(NETFUN_APP, IPMI_CMD_GET_CHAN_CIPHER_SUITES, NULL,
1072 getChannelCipherSuites, PRIVILEGE_CALLBACK);
Vernon Maueryd2a57de2019-03-25 12:45:28 -07001073
Xo Wangf542e8b2017-08-09 15:34:16 -07001074 // <Get System Info Command>
1075 ipmi_register_callback(NETFUN_APP, IPMI_CMD_GET_SYSTEM_INFO, NULL,
1076 ipmi_app_get_system_info, PRIVILEGE_USER);
vishwabmcba0bd5f2015-09-30 16:50:23 +05301077 return;
1078}