blob: e1779bb66f4c4cc29d52b509828190d1692f1aa2 [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;
William A. Kennington III4c008022018-10-12 17:18:14 -070060namespace variant_ns = sdbusplus::message::variant_ns;
Ratan Guptab8e99552017-07-27 07:07:48 +053061
Nagaraju Goruganti744398d2018-02-20 09:52:00 -060062/**
63 * @brief Returns the Version info from primary s/w object
64 *
65 * Get the Version info from the active s/w object which is having high
66 * "Priority" value(a smaller number is a higher priority) and "Purpose"
67 * is "BMC" from the list of all s/w objects those are implementing
68 * RedundancyPriority interface from the given softwareRoot path.
69 *
70 * @return On success returns the Version info from primary s/w object.
71 *
72 */
73std::string getActiveSoftwareVersionInfo()
74{
Vernon Mauery86a50822019-03-25 13:11:36 -070075 auto busp = getSdBus();
Nagaraju Goruganti744398d2018-02-20 09:52:00 -060076
77 std::string revision{};
Vernon Mauery86a50822019-03-25 13:11:36 -070078 ipmi::ObjectTree objectTree;
79 try
Nagaraju Goruganti744398d2018-02-20 09:52:00 -060080 {
Vernon Mauery86a50822019-03-25 13:11:36 -070081 objectTree =
82 ipmi::getAllDbusObjects(*busp, softwareRoot, redundancyIntf);
83 }
84 catch (sdbusplus::exception::SdBusError& e)
85 {
86 log<level::ERR>("Failed to fetch redundancy object from dbus",
87 entry("INTERFACE=%s", redundancyIntf),
88 entry("ERRMSG=%s", e.what()));
Nagaraju Goruganti744398d2018-02-20 09:52:00 -060089 elog<InternalFailure>();
90 }
91
92 auto objectFound = false;
93 for (auto& softObject : objectTree)
94 {
Vernon Mauery86a50822019-03-25 13:11:36 -070095 auto service =
96 ipmi::getService(*busp, redundancyIntf, softObject.first);
97 auto objValueTree =
98 ipmi::getManagedObjects(*busp, service, softwareRoot);
Nagaraju Goruganti744398d2018-02-20 09:52:00 -060099
100 auto minPriority = 0xFF;
101 for (const auto& objIter : objValueTree)
102 {
103 try
104 {
105 auto& intfMap = objIter.second;
106 auto& redundancyPriorityProps = intfMap.at(redundancyIntf);
107 auto& versionProps = intfMap.at(versionIntf);
108 auto& activationProps = intfMap.at(activationIntf);
William A. Kennington III4c008022018-10-12 17:18:14 -0700109 auto priority = variant_ns::get<uint8_t>(
110 redundancyPriorityProps.at("Priority"));
111 auto purpose =
112 variant_ns::get<std::string>(versionProps.at("Purpose"));
113 auto activation = variant_ns::get<std::string>(
114 activationProps.at("Activation"));
115 auto version =
116 variant_ns::get<std::string>(versionProps.at("Version"));
Nagaraju Goruganti744398d2018-02-20 09:52:00 -0600117 if ((Version::convertVersionPurposeFromString(purpose) ==
118 Version::VersionPurpose::BMC) &&
119 (Activation::convertActivationsFromString(activation) ==
120 Activation::Activations::Active))
121 {
122 if (priority < minPriority)
123 {
124 minPriority = priority;
125 objectFound = true;
126 revision = std::move(version);
127 }
128 }
129 }
130 catch (const std::exception& e)
131 {
132 log<level::ERR>(e.what());
133 }
134 }
135 }
136
137 if (!objectFound)
138 {
139 log<level::ERR>("Could not found an BMC software Object");
140 elog<InternalFailure>();
141 }
142
143 return revision;
144}
145
Alexander Amelkinba19c182018-09-04 15:49:36 +0300146bool getCurrentBmcState()
147{
148 sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()};
149
150 // Get the Inventory object implementing the BMC interface
151 ipmi::DbusObjectInfo bmcObject =
152 ipmi::getDbusObject(bus, bmc_state_interface);
153 auto variant =
154 ipmi::getDbusProperty(bus, bmcObject.second, bmcObject.first,
155 bmc_state_interface, bmc_state_property);
156
William A. Kennington III4c008022018-10-12 17:18:14 -0700157 return variant_ns::holds_alternative<std::string>(variant) &&
158 BMC::convertBMCStateFromString(
159 variant_ns::get<std::string>(variant)) == BMC::BMCState::Ready;
Alexander Amelkinba19c182018-09-04 15:49:36 +0300160}
161
Yong Li18d77262018-10-09 01:59:45 +0800162namespace acpi_state
163{
164using namespace sdbusplus::xyz::openbmc_project::Control::Power::server;
165
166const static constexpr char* acpiObjPath =
167 "/xyz/openbmc_project/control/host0/acpi_power_state";
168const static constexpr char* acpiInterface =
169 "xyz.openbmc_project.Control.Power.ACPIPowerState";
170const static constexpr char* sysACPIProp = "SysACPIStatus";
171const static constexpr char* devACPIProp = "DevACPIStatus";
172
173enum class PowerStateType : uint8_t
174{
175 sysPowerState = 0x00,
176 devPowerState = 0x01,
177};
178
179// Defined in 20.6 of ipmi doc
180enum class PowerState : uint8_t
181{
182 s0G0D0 = 0x00,
183 s1D1 = 0x01,
184 s2D2 = 0x02,
185 s3D3 = 0x03,
186 s4 = 0x04,
187 s5G2 = 0x05,
188 s4S5 = 0x06,
189 g3 = 0x07,
190 sleep = 0x08,
191 g1Sleep = 0x09,
192 override = 0x0a,
193 legacyOn = 0x20,
194 legacyOff = 0x21,
195 unknown = 0x2a,
196 noChange = 0x7f,
197};
198
199static constexpr uint8_t stateChanged = 0x80;
200
201struct ACPIState
202{
203 uint8_t sysACPIState;
204 uint8_t devACPIState;
205} __attribute__((packed));
206
207std::map<ACPIPowerState::ACPI, PowerState> dbusToIPMI = {
208 {ACPIPowerState::ACPI::S0_G0_D0, PowerState::s0G0D0},
209 {ACPIPowerState::ACPI::S1_D1, PowerState::s1D1},
210 {ACPIPowerState::ACPI::S2_D2, PowerState::s2D2},
211 {ACPIPowerState::ACPI::S3_D3, PowerState::s3D3},
212 {ACPIPowerState::ACPI::S4, PowerState::s4},
213 {ACPIPowerState::ACPI::S5_G2, PowerState::s5G2},
214 {ACPIPowerState::ACPI::S4_S5, PowerState::s4S5},
215 {ACPIPowerState::ACPI::G3, PowerState::g3},
216 {ACPIPowerState::ACPI::SLEEP, PowerState::sleep},
217 {ACPIPowerState::ACPI::G1_SLEEP, PowerState::g1Sleep},
218 {ACPIPowerState::ACPI::OVERRIDE, PowerState::override},
219 {ACPIPowerState::ACPI::LEGACY_ON, PowerState::legacyOn},
220 {ACPIPowerState::ACPI::LEGACY_OFF, PowerState::legacyOff},
221 {ACPIPowerState::ACPI::Unknown, PowerState::unknown}};
222
223bool isValidACPIState(acpi_state::PowerStateType type, uint8_t state)
224{
225 if (type == acpi_state::PowerStateType::sysPowerState)
226 {
227 if ((state <= static_cast<uint8_t>(acpi_state::PowerState::override)) ||
228 (state == static_cast<uint8_t>(acpi_state::PowerState::legacyOn)) ||
229 (state ==
230 static_cast<uint8_t>(acpi_state::PowerState::legacyOff)) ||
231 (state == static_cast<uint8_t>(acpi_state::PowerState::unknown)) ||
232 (state == static_cast<uint8_t>(acpi_state::PowerState::noChange)))
233 {
234 return true;
235 }
236 else
237 {
238 return false;
239 }
240 }
241 else if (type == acpi_state::PowerStateType::devPowerState)
242 {
243 if ((state <= static_cast<uint8_t>(acpi_state::PowerState::s3D3)) ||
244 (state == static_cast<uint8_t>(acpi_state::PowerState::unknown)) ||
245 (state == static_cast<uint8_t>(acpi_state::PowerState::noChange)))
246 {
247 return true;
248 }
249 else
250 {
251 return false;
252 }
253 }
254 else
255 {
256 return false;
257 }
258 return false;
259}
260} // namespace acpi_state
261
Adriana Kobylak3a552e12015-10-19 16:11:00 -0500262ipmi_ret_t ipmi_app_set_acpi_power_state(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
Patrick Venture0b02be92018-08-31 11:55:55 -0700263 ipmi_request_t request,
264 ipmi_response_t response,
265 ipmi_data_len_t data_len,
266 ipmi_context_t context)
Chris Austen6caf28b2015-10-13 12:40:40 -0500267{
Yong Li18d77262018-10-09 01:59:45 +0800268 auto s = static_cast<uint8_t>(acpi_state::PowerState::unknown);
Chris Austen6caf28b2015-10-13 12:40:40 -0500269 ipmi_ret_t rc = IPMI_CC_OK;
Yong Li18d77262018-10-09 01:59:45 +0800270
271 sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()};
272
273 auto value = acpi_state::ACPIPowerState::ACPI::Unknown;
274
275 auto* req = reinterpret_cast<acpi_state::ACPIState*>(request);
276
277 if (*data_len != sizeof(acpi_state::ACPIState))
278 {
279 log<level::ERR>("set_acpi invalid len");
280 *data_len = 0;
281 return IPMI_CC_REQ_DATA_LEN_INVALID;
282 }
283
Chris Austen6caf28b2015-10-13 12:40:40 -0500284 *data_len = 0;
285
Yong Li18d77262018-10-09 01:59:45 +0800286 if (req->sysACPIState & acpi_state::stateChanged)
287 {
288 // set system power state
289 s = req->sysACPIState & ~acpi_state::stateChanged;
290
291 if (!acpi_state::isValidACPIState(
292 acpi_state::PowerStateType::sysPowerState, s))
293 {
294 log<level::ERR>("set_acpi_power sys invalid input",
295 entry("S=%x", s));
296 return IPMI_CC_PARM_OUT_OF_RANGE;
297 }
298
299 // valid input
300 if (s == static_cast<uint8_t>(acpi_state::PowerState::noChange))
301 {
302 log<level::DEBUG>("No change for system power state");
303 }
304 else
305 {
306 auto found = std::find_if(
307 acpi_state::dbusToIPMI.begin(), acpi_state::dbusToIPMI.end(),
308 [&s](const auto& iter) {
309 return (static_cast<uint8_t>(iter.second) == s);
310 });
311
312 value = found->first;
313
314 try
315 {
316 auto acpiObject =
317 ipmi::getDbusObject(bus, acpi_state::acpiInterface);
318 ipmi::setDbusProperty(bus, acpiObject.second, acpiObject.first,
319 acpi_state::acpiInterface,
320 acpi_state::sysACPIProp,
321 convertForMessage(value));
322 }
323 catch (const InternalFailure& e)
324 {
325 log<level::ERR>("Failed in set ACPI system property",
326 entry("EXCEPTION=%s", e.what()));
327 return IPMI_CC_UNSPECIFIED_ERROR;
328 }
329 }
330 }
331 else
332 {
333 log<level::DEBUG>("Do not change system power state");
334 }
335
336 if (req->devACPIState & acpi_state::stateChanged)
337 {
338 // set device power state
339 s = req->devACPIState & ~acpi_state::stateChanged;
340 if (!acpi_state::isValidACPIState(
341 acpi_state::PowerStateType::devPowerState, s))
342 {
343 log<level::ERR>("set_acpi_power dev invalid input",
344 entry("S=%x", s));
345 return IPMI_CC_PARM_OUT_OF_RANGE;
346 }
347
348 // valid input
349 if (s == static_cast<uint8_t>(acpi_state::PowerState::noChange))
350 {
351 log<level::DEBUG>("No change for device power state");
352 }
353 else
354 {
355 auto found = std::find_if(
356 acpi_state::dbusToIPMI.begin(), acpi_state::dbusToIPMI.end(),
357 [&s](const auto& iter) {
358 return (static_cast<uint8_t>(iter.second) == s);
359 });
360
361 value = found->first;
362
363 try
364 {
365 auto acpiObject =
366 ipmi::getDbusObject(bus, acpi_state::acpiInterface);
367 ipmi::setDbusProperty(bus, acpiObject.second, acpiObject.first,
368 acpi_state::acpiInterface,
369 acpi_state::devACPIProp,
370 convertForMessage(value));
371 }
372 catch (const InternalFailure& e)
373 {
374 log<level::ERR>("Failed in set ACPI device property",
375 entry("EXCEPTION=%s", e.what()));
376 return IPMI_CC_UNSPECIFIED_ERROR;
377 }
378 }
379 }
380 else
381 {
382 log<level::DEBUG>("Do not change device power state");
383 }
384
385 return rc;
386}
387
388ipmi_ret_t ipmi_app_get_acpi_power_state(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
389 ipmi_request_t request,
390 ipmi_response_t response,
391 ipmi_data_len_t data_len,
392 ipmi_context_t context)
393{
394 ipmi_ret_t rc = IPMI_CC_OK;
395
396 auto* res = reinterpret_cast<acpi_state::ACPIState*>(response);
397
398 sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()};
399
400 *data_len = 0;
401
402 try
403 {
404 auto acpiObject = ipmi::getDbusObject(bus, acpi_state::acpiInterface);
405
406 auto sysACPIVal = ipmi::getDbusProperty(
407 bus, acpiObject.second, acpiObject.first, acpi_state::acpiInterface,
408 acpi_state::sysACPIProp);
409 auto sysACPI = acpi_state::ACPIPowerState::convertACPIFromString(
William A. Kennington IIIdfad4862018-11-19 17:45:35 -0800410 variant_ns::get<std::string>(sysACPIVal));
Yong Li18d77262018-10-09 01:59:45 +0800411 res->sysACPIState =
412 static_cast<uint8_t>(acpi_state::dbusToIPMI.at(sysACPI));
413
414 auto devACPIVal = ipmi::getDbusProperty(
415 bus, acpiObject.second, acpiObject.first, acpi_state::acpiInterface,
416 acpi_state::devACPIProp);
417 auto devACPI = acpi_state::ACPIPowerState::convertACPIFromString(
William A. Kennington IIIdfad4862018-11-19 17:45:35 -0800418 variant_ns::get<std::string>(devACPIVal));
Yong Li18d77262018-10-09 01:59:45 +0800419 res->devACPIState =
420 static_cast<uint8_t>(acpi_state::dbusToIPMI.at(devACPI));
421
422 *data_len = sizeof(acpi_state::ACPIState);
423 }
424 catch (const InternalFailure& e)
425 {
426 log<level::ERR>("Failed in get ACPI property");
427 return IPMI_CC_UNSPECIFIED_ERROR;
428 }
Chris Austen6caf28b2015-10-13 12:40:40 -0500429 return rc;
430}
431
Chris Austen7303bdc2016-04-17 11:50:54 -0500432typedef struct
433{
434 char major;
435 char minor;
Chris Austen176c9652016-04-30 16:32:17 -0500436 uint16_t d[2];
Vernon Mauery86a50822019-03-25 13:11:36 -0700437} Revision;
Chris Austen7303bdc2016-04-17 11:50:54 -0500438
Dinesh Chinari2b7e07d2017-11-08 15:38:50 -0600439/* Currently supports the vx.x-x-[-x] and v1.x.x-x-[-x] format. It will */
440/* return -1 if not in those formats, this routine knows how to parse */
Chris Austen7303bdc2016-04-17 11:50:54 -0500441/* version = v0.6-19-gf363f61-dirty */
442/* ^ ^ ^^ ^ */
443/* | | |----------|-- additional details */
444/* | |---------------- Minor */
445/* |------------------ Major */
Dinesh Chinari2b7e07d2017-11-08 15:38:50 -0600446/* and version = v1.99.10-113-g65edf7d-r3-0-g9e4f715 */
447/* ^ ^ ^^ ^ */
448/* | | |--|---------- additional details */
449/* | |---------------- Minor */
450/* |------------------ Major */
Chris Austen7303bdc2016-04-17 11:50:54 -0500451/* Additional details : If the option group exists it will force Auxiliary */
452/* Firmware Revision Information 4th byte to 1 indicating the build was */
453/* derived with additional edits */
Vernon Mauery86a50822019-03-25 13:11:36 -0700454int convertVersion(std::string s, Revision& rev)
Chris Austen7303bdc2016-04-17 11:50:54 -0500455{
Dinesh Chinari2b7e07d2017-11-08 15:38:50 -0600456 std::string token;
Chris Austen176c9652016-04-30 16:32:17 -0500457 uint16_t commits;
Chris Austen7303bdc2016-04-17 11:50:54 -0500458
Patrick Venture0b02be92018-08-31 11:55:55 -0700459 auto location = s.find_first_of('v');
Dinesh Chinari2b7e07d2017-11-08 15:38:50 -0600460 if (location != std::string::npos)
461 {
Patrick Venture0b02be92018-08-31 11:55:55 -0700462 s = s.substr(location + 1);
Chris Austen176c9652016-04-30 16:32:17 -0500463 }
Chris Austen7303bdc2016-04-17 11:50:54 -0500464
Dinesh Chinari2b7e07d2017-11-08 15:38:50 -0600465 if (!s.empty())
466 {
467 location = s.find_first_of(".");
468 if (location != std::string::npos)
469 {
Vernon Mauery86a50822019-03-25 13:11:36 -0700470 rev.major =
Patrick Venture0b02be92018-08-31 11:55:55 -0700471 static_cast<char>(std::stoi(s.substr(0, location), 0, 16));
472 token = s.substr(location + 1);
Dinesh Chinari2b7e07d2017-11-08 15:38:50 -0600473 }
Chris Austen176c9652016-04-30 16:32:17 -0500474
Dinesh Chinari2b7e07d2017-11-08 15:38:50 -0600475 if (!token.empty())
476 {
477 location = token.find_first_of(".-");
478 if (location != std::string::npos)
479 {
Vernon Mauery86a50822019-03-25 13:11:36 -0700480 rev.minor = static_cast<char>(
Patrick Venture0b02be92018-08-31 11:55:55 -0700481 std::stoi(token.substr(0, location), 0, 16));
482 token = token.substr(location + 1);
Dinesh Chinari2b7e07d2017-11-08 15:38:50 -0600483 }
484 }
Chris Austen7303bdc2016-04-17 11:50:54 -0500485
Dinesh Chinari2b7e07d2017-11-08 15:38:50 -0600486 // Capture the number of commits on top of the minor tag.
487 // I'm using BE format like the ipmi spec asked for
488 location = token.find_first_of(".-");
489 if (!token.empty())
490 {
491 commits = std::stoi(token.substr(0, location), 0, 16);
Vernon Mauery86a50822019-03-25 13:11:36 -0700492 rev.d[0] = (commits >> 8) | (commits << 8);
Dinesh Chinari2b7e07d2017-11-08 15:38:50 -0600493
494 // commit number we skip
495 location = token.find_first_of(".-");
496 if (location != std::string::npos)
497 {
Patrick Venture0b02be92018-08-31 11:55:55 -0700498 token = token.substr(location + 1);
Dinesh Chinari2b7e07d2017-11-08 15:38:50 -0600499 }
500 }
Patrick Venture0b02be92018-08-31 11:55:55 -0700501 else
502 {
Vernon Mauery86a50822019-03-25 13:11:36 -0700503 rev.d[0] = 0;
Dinesh Chinari2b7e07d2017-11-08 15:38:50 -0600504 }
505
506 if (location != std::string::npos)
507 {
Patrick Venture0b02be92018-08-31 11:55:55 -0700508 token = token.substr(location + 1);
Dinesh Chinari2b7e07d2017-11-08 15:38:50 -0600509 }
510
511 // Any value of the optional parameter forces it to 1
512 location = token.find_first_of(".-");
513 if (location != std::string::npos)
514 {
Patrick Venture0b02be92018-08-31 11:55:55 -0700515 token = token.substr(location + 1);
Dinesh Chinari2b7e07d2017-11-08 15:38:50 -0600516 }
517 commits = (!token.empty()) ? 1 : 0;
518
Patrick Venture0b02be92018-08-31 11:55:55 -0700519 // We do this operation to get this displayed in least significant bytes
520 // of ipmitool device id command.
Vernon Mauery86a50822019-03-25 13:11:36 -0700521 rev.d[1] = (commits >> 8) | (commits << 8);
Dinesh Chinari2b7e07d2017-11-08 15:38:50 -0600522 }
523
Chris Austen7303bdc2016-04-17 11:50:54 -0500524 return 0;
525}
526
Vernon Mauery86a50822019-03-25 13:11:36 -0700527auto ipmiAppGetDeviceId() -> ipmi::RspType<uint8_t, // Device ID
528 uint8_t, // Device Revision
529 uint8_t, // Firmware Revision Major
530 uint8_t, // Firmware Revision minor
531 uint8_t, // IPMI version
532 uint8_t, // Additional device support
533 uint24_t, // MFG ID
534 uint16_t, // Product ID
535 uint32_t // AUX info
536 >
Chris Austen6caf28b2015-10-13 12:40:40 -0500537{
Nagaraju Goruganti744398d2018-02-20 09:52:00 -0600538 int r = -1;
Vernon Mauery86a50822019-03-25 13:11:36 -0700539 Revision rev = {0};
540 static struct
541 {
542 uint8_t id;
543 uint8_t revision;
544 uint8_t fw[2];
545 uint8_t ipmiVer;
546 uint8_t addnDevSupport;
547 uint24_t manufId;
548 uint16_t prodId;
549 uint32_t aux;
550 } devId;
David Cobbleya1adb072017-11-21 15:58:13 -0800551 static bool dev_id_initialized = false;
552 const char* filename = "/usr/share/ipmi-providers/dev_id.json";
Alexander Amelkinba19c182018-09-04 15:49:36 +0300553 constexpr auto ipmiDevIdStateShift = 7;
554 constexpr auto ipmiDevIdFw1Mask = ~(1 << ipmiDevIdStateShift);
Chris Austen6caf28b2015-10-13 12:40:40 -0500555
David Cobbleya1adb072017-11-21 15:58:13 -0800556 if (!dev_id_initialized)
557 {
Nagaraju Goruganti744398d2018-02-20 09:52:00 -0600558 try
559 {
560 auto version = getActiveSoftwareVersionInfo();
Vernon Mauery86a50822019-03-25 13:11:36 -0700561 r = convertVersion(version, rev);
David Cobbleya1adb072017-11-21 15:58:13 -0800562 }
Nagaraju Goruganti744398d2018-02-20 09:52:00 -0600563 catch (const std::exception& e)
564 {
565 log<level::ERR>(e.what());
566 }
Nan Liee0cb902016-07-11 15:38:03 +0800567
Patrick Venture0b02be92018-08-31 11:55:55 -0700568 if (r >= 0)
569 {
Nagaraju Goruganti744398d2018-02-20 09:52:00 -0600570 // bit7 identifies if the device is available
571 // 0=normal operation
572 // 1=device firmware, SDR update,
573 // or self-initialization in progress.
Alexander Amelkinba19c182018-09-04 15:49:36 +0300574 // The availability may change in run time, so mask here
575 // and initialize later.
Vernon Mauery86a50822019-03-25 13:11:36 -0700576 devId.fw[0] = rev.major & ipmiDevIdFw1Mask;
Nagaraju Goruganti744398d2018-02-20 09:52:00 -0600577
578 rev.minor = (rev.minor > 99 ? 99 : rev.minor);
Vernon Mauery86a50822019-03-25 13:11:36 -0700579 devId.fw[1] = rev.minor % 10 + (rev.minor / 10) * 16;
580 std::memcpy(&devId.aux, rev.d, 4);
David Cobbleya1adb072017-11-21 15:58:13 -0800581 }
Nan Liee0cb902016-07-11 15:38:03 +0800582
David Cobbleya1adb072017-11-21 15:58:13 -0800583 // IPMI Spec version 2.0
Vernon Mauery86a50822019-03-25 13:11:36 -0700584 devId.ipmiVer = 2;
Adriana Kobylak0e912642016-06-22 16:54:39 -0500585
Vernon Mauery86a50822019-03-25 13:11:36 -0700586 std::ifstream devIdFile(filename);
587 if (devIdFile.is_open())
David Cobbleya1adb072017-11-21 15:58:13 -0800588 {
Vernon Mauery86a50822019-03-25 13:11:36 -0700589 auto data = nlohmann::json::parse(devIdFile, nullptr, false);
David Cobbleya1adb072017-11-21 15:58:13 -0800590 if (!data.is_discarded())
591 {
Vernon Mauery86a50822019-03-25 13:11:36 -0700592 devId.id = data.value("id", 0);
593 devId.revision = data.value("revision", 0);
594 devId.addnDevSupport = data.value("addn_dev_support", 0);
595 devId.manufId = data.value("manuf_id", 0);
596 devId.prodId = data.value("prod_id", 0);
597 devId.aux = data.value("aux", 0);
David Cobbleya1adb072017-11-21 15:58:13 -0800598
Patrick Venture0b02be92018-08-31 11:55:55 -0700599 // Don't read the file every time if successful
David Cobbleya1adb072017-11-21 15:58:13 -0800600 dev_id_initialized = true;
601 }
602 else
603 {
604 log<level::ERR>("Device ID JSON parser failure");
Vernon Mauery86a50822019-03-25 13:11:36 -0700605 return ipmi::response(ipmi::ccUnspecifiedError);
David Cobbleya1adb072017-11-21 15:58:13 -0800606 }
607 }
608 else
609 {
610 log<level::ERR>("Device ID file not found");
Vernon Mauery86a50822019-03-25 13:11:36 -0700611 return ipmi::response(ipmi::ccUnspecifiedError);
Chris Austen7303bdc2016-04-17 11:50:54 -0500612 }
613 }
Chris Austen6caf28b2015-10-13 12:40:40 -0500614
Alexander Amelkinba19c182018-09-04 15:49:36 +0300615 // Set availability to the actual current BMC state
Vernon Mauery86a50822019-03-25 13:11:36 -0700616 devId.fw[0] &= ipmiDevIdFw1Mask;
Alexander Amelkinba19c182018-09-04 15:49:36 +0300617 if (!getCurrentBmcState())
618 {
Vernon Mauery86a50822019-03-25 13:11:36 -0700619 devId.fw[0] |= (1 << ipmiDevIdStateShift);
Alexander Amelkinba19c182018-09-04 15:49:36 +0300620 }
621
Vernon Mauery86a50822019-03-25 13:11:36 -0700622 return ipmi::responseSuccess(
623 devId.id, devId.revision, devId.fw[0], devId.fw[1], devId.ipmiVer,
624 devId.addnDevSupport, devId.manufId, devId.prodId, devId.aux);
Chris Austen6caf28b2015-10-13 12:40:40 -0500625}
626
Vernon Maueryb84a5282019-03-25 13:39:03 -0700627auto ipmiAppGetSelfTestResults() -> ipmi::RspType<uint8_t, uint8_t>
Nan Li41fa24a2016-11-10 20:12:37 +0800628{
Nan Li41fa24a2016-11-10 20:12:37 +0800629 // Byte 2:
630 // 55h - No error.
Gunnar Mills8991dd62017-10-25 17:11:29 -0500631 // 56h - Self Test function not implemented in this controller.
Nan Li41fa24a2016-11-10 20:12:37 +0800632 // 57h - Corrupted or inaccesssible data or devices.
633 // 58h - Fatal hardware error.
634 // FFh - reserved.
635 // all other: Device-specific 'internal failure'.
636 // Byte 3:
637 // For byte 2 = 55h, 56h, FFh: 00h
638 // For byte 2 = 58h, all other: Device-specific
639 // For byte 2 = 57h: self-test error bitfield.
640 // Note: returning 57h does not imply that all test were run.
641 // [7] 1b = Cannot access SEL device.
642 // [6] 1b = Cannot access SDR Repository.
643 // [5] 1b = Cannot access BMC FRU device.
644 // [4] 1b = IPMB signal lines do not respond.
645 // [3] 1b = SDR Repository empty.
646 // [2] 1b = Internal Use Area of BMC FRU corrupted.
647 // [1] 1b = controller update 'boot block' firmware corrupted.
648 // [0] 1b = controller operational firmware corrupted.
Vernon Maueryb84a5282019-03-25 13:39:03 -0700649 constexpr uint8_t notImplemented = 0x56;
650 constexpr uint8_t zero = 0;
651 return ipmi::responseSuccess(notImplemented, zero);
Nan Li41fa24a2016-11-10 20:12:37 +0800652}
653
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500654ipmi_ret_t ipmi_app_get_device_guid(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
Patrick Venture0b02be92018-08-31 11:55:55 -0700655 ipmi_request_t request,
656 ipmi_response_t response,
657 ipmi_data_len_t data_len,
658 ipmi_context_t context)
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500659{
Patrick Venture0b02be92018-08-31 11:55:55 -0700660 const char* objname = "/org/openbmc/control/chassis0";
661 const char* iface = "org.freedesktop.DBus.Properties";
662 const char* chassis_iface = "org.openbmc.control.Chassis";
663 sd_bus_message* reply = NULL;
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500664 sd_bus_error error = SD_BUS_ERROR_NULL;
665 int r = 0;
Patrick Venture0b02be92018-08-31 11:55:55 -0700666 char* uuid = NULL;
667 char* busname = NULL;
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500668
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500669 // UUID is in RFC4122 format. Ex: 61a39523-78f2-11e5-9862-e6402cfc3223
Patrick Ventured2117022018-02-06 08:54:37 -0800670 // Per IPMI Spec 2.0 need to convert to 16 hex bytes and reverse the byte
671 // order
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500672 // Ex: 0x2332fc2c40e66298e511f2782395a361
673
Patrick Venture0b02be92018-08-31 11:55:55 -0700674 const int resp_size = 16; // Response is 16 hex bytes per IPMI Spec
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500675 uint8_t resp_uuid[resp_size]; // Array to hold the formatted response
Patrick Ventured2117022018-02-06 08:54:37 -0800676 // Point resp end of array to save in reverse order
Patrick Venture0b02be92018-08-31 11:55:55 -0700677 int resp_loc = resp_size - 1;
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500678 int i = 0;
Patrick Venture0b02be92018-08-31 11:55:55 -0700679 char* tokptr = NULL;
680 char* id_octet = NULL;
Emily Shafferedb8bb02018-09-27 14:50:15 -0700681 size_t total_uuid_size = 0;
682 // 1 byte of resp is built from 2 chars of uuid.
683 constexpr size_t max_uuid_size = 2 * resp_size;
vishwa1eaea4f2016-02-26 11:57:40 -0600684
685 // Status code.
686 ipmi_ret_t rc = IPMI_CC_OK;
687 *data_len = 0;
688
vishwa1eaea4f2016-02-26 11:57:40 -0600689 // Call Get properties method with the interface and property name
Sergey Solomineb9b8142016-08-23 09:07:28 -0500690 r = mapper_get_service(bus, objname, &busname);
Patrick Venture0b02be92018-08-31 11:55:55 -0700691 if (r < 0)
692 {
693 log<level::ERR>("Failed to get bus name", entry("BUS=%s", objname),
Aditya Saripalli5fb14602017-11-09 14:46:27 +0530694 entry("ERRNO=0x%X", -r));
Sergey Solomineb9b8142016-08-23 09:07:28 -0500695 goto finish;
696 }
Patrick Venture0b02be92018-08-31 11:55:55 -0700697 r = sd_bus_call_method(bus, busname, objname, iface, "Get", &error, &reply,
698 "ss", chassis_iface, "uuid");
vishwa1eaea4f2016-02-26 11:57:40 -0600699 if (r < 0)
700 {
Patrick Venture0b02be92018-08-31 11:55:55 -0700701 log<level::ERR>("Failed to call Get Method", entry("ERRNO=0x%X", -r));
vishwa1eaea4f2016-02-26 11:57:40 -0600702 rc = IPMI_CC_UNSPECIFIED_ERROR;
703 goto finish;
704 }
705
706 r = sd_bus_message_read(reply, "v", "s", &uuid);
707 if (r < 0 || uuid == NULL)
708 {
Patrick Venture0b02be92018-08-31 11:55:55 -0700709 log<level::ERR>("Failed to get a response", entry("ERRNO=0x%X", -r));
vishwa1eaea4f2016-02-26 11:57:40 -0600710 rc = IPMI_CC_RESPONSE_ERROR;
711 goto finish;
712 }
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500713
714 // Traverse the UUID
Patrick Ventured2117022018-02-06 08:54:37 -0800715 // Get the UUID octects separated by dash
716 id_octet = strtok_r(uuid, "-", &tokptr);
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500717
718 if (id_octet == NULL)
vishwa1eaea4f2016-02-26 11:57:40 -0600719 {
720 // Error
Patrick Venture0b02be92018-08-31 11:55:55 -0700721 log<level::ERR>("Unexpected UUID format", entry("UUID=%s", uuid));
vishwa1eaea4f2016-02-26 11:57:40 -0600722 rc = IPMI_CC_RESPONSE_ERROR;
723 goto finish;
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500724 }
725
726 while (id_octet != NULL)
727 {
728 // Calculate the octet string size since it varies
729 // Divide it by 2 for the array size since 1 byte is built from 2 chars
Patrick Venture0b02be92018-08-31 11:55:55 -0700730 int tmp_size = strlen(id_octet) / 2;
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500731
Emily Shafferedb8bb02018-09-27 14:50:15 -0700732 // Check if total UUID size has been exceeded
733 if ((total_uuid_size += strlen(id_octet)) > max_uuid_size)
734 {
735 // Error - UUID too long to store
736 log<level::ERR>("UUID too long", entry("UUID=%s", uuid));
737 rc = IPMI_CC_RESPONSE_ERROR;
738 goto finish;
739 }
740
Patrick Venture0b02be92018-08-31 11:55:55 -0700741 for (i = 0; i < tmp_size; i++)
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500742 {
Patrick Ventured2117022018-02-06 08:54:37 -0800743 // Holder of the 2 chars that will become a byte
744 char tmp_array[3] = {0};
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500745 strncpy(tmp_array, id_octet, 2); // 2 chars at a time
746
747 int resp_byte = strtoul(tmp_array, NULL, 16); // Convert to hex byte
Patrick Ventured2117022018-02-06 08:54:37 -0800748 // Copy end to first
Patrick Ventureb51bf9c2018-09-10 15:53:14 -0700749 std::memcpy((void*)&resp_uuid[resp_loc], &resp_byte, 1);
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500750 resp_loc--;
Patrick Venture0b02be92018-08-31 11:55:55 -0700751 id_octet += 2; // Finished with the 2 chars, advance
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500752 }
Patrick Venture0b02be92018-08-31 11:55:55 -0700753 id_octet = strtok_r(NULL, "-", &tokptr); // Get next octet
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500754 }
755
756 // Data length
757 *data_len = resp_size;
758
759 // Pack the actual response
Patrick Ventureb51bf9c2018-09-10 15:53:14 -0700760 std::memcpy(response, &resp_uuid, *data_len);
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500761
vishwa1eaea4f2016-02-26 11:57:40 -0600762finish:
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500763 sd_bus_error_free(&error);
vishwa1eaea4f2016-02-26 11:57:40 -0600764 reply = sd_bus_message_unref(reply);
Sergey Solomineb9b8142016-08-23 09:07:28 -0500765 free(busname);
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500766
767 return rc;
768}
Chris Austen6caf28b2015-10-13 12:40:40 -0500769
Vernon Mauerycc99ba42019-03-25 13:40:11 -0700770auto ipmiAppGetBtCapabilities()
771 -> ipmi::RspType<uint8_t, uint8_t, uint8_t, uint8_t, uint8_t>
vishwabmcba0bd5f2015-09-30 16:50:23 +0530772{
Adriana Kobylak88ad8152016-12-13 10:09:08 -0600773 // Per IPMI 2.0 spec, the input and output buffer size must be the max
774 // buffer size minus one byte to allocate space for the length byte.
Vernon Mauerycc99ba42019-03-25 13:40:11 -0700775 constexpr uint8_t nrOutstanding = 0x01;
776 constexpr uint8_t inputBufferSize = MAX_IPMI_BUFFER - 1;
777 constexpr uint8_t outputBufferSize = MAX_IPMI_BUFFER - 1;
778 constexpr uint8_t transactionTime = 0x0A;
779 constexpr uint8_t nrRetries = 0x01;
vishwabmcba0bd5f2015-09-30 16:50:23 +0530780
Vernon Mauerycc99ba42019-03-25 13:40:11 -0700781 return ipmi::responseSuccess(nrOutstanding, inputBufferSize,
782 outputBufferSize, transactionTime, nrRetries);
vishwabmcba0bd5f2015-09-30 16:50:23 +0530783}
784
Marri Devender Rao5e007a52018-01-08 06:18:36 -0600785ipmi_ret_t ipmi_app_get_sys_guid(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
Patrick Venture0b02be92018-08-31 11:55:55 -0700786 ipmi_request_t request,
787 ipmi_response_t response,
788 ipmi_data_len_t data_len,
789 ipmi_context_t context)
Marri Devender Rao5e007a52018-01-08 06:18:36 -0600790
791{
792 ipmi_ret_t rc = IPMI_CC_OK;
793 sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()};
794
795 try
796 {
797 // Get the Inventory object implementing BMC interface
798 ipmi::DbusObjectInfo bmcObject =
799 ipmi::getDbusObject(bus, bmc_interface);
800
801 // Read UUID property value from bmcObject
802 // UUID is in RFC4122 format Ex: 61a39523-78f2-11e5-9862-e6402cfc3223
Patrick Venture0b02be92018-08-31 11:55:55 -0700803 auto variant =
804 ipmi::getDbusProperty(bus, bmcObject.second, bmcObject.first,
805 bmc_guid_interface, bmc_guid_property);
William A. Kennington III4c008022018-10-12 17:18:14 -0700806 std::string guidProp = variant_ns::get<std::string>(variant);
Marri Devender Rao5e007a52018-01-08 06:18:36 -0600807
808 // Erase "-" characters from the property value
809 guidProp.erase(std::remove(guidProp.begin(), guidProp.end(), '-'),
Patrick Venture0b02be92018-08-31 11:55:55 -0700810 guidProp.end());
Marri Devender Rao5e007a52018-01-08 06:18:36 -0600811
812 auto guidPropLen = guidProp.length();
813 // Validate UUID data
814 // Divide by 2 as 1 byte is built from 2 chars
Patrick Venture0b02be92018-08-31 11:55:55 -0700815 if ((guidPropLen <= 0) || ((guidPropLen / 2) != bmc_guid_len))
Marri Devender Rao5e007a52018-01-08 06:18:36 -0600816
817 {
818 log<level::ERR>("Invalid UUID property value",
Patrick Venture0b02be92018-08-31 11:55:55 -0700819 entry("UUID_LENGTH=%d", guidPropLen));
Marri Devender Rao5e007a52018-01-08 06:18:36 -0600820 return IPMI_CC_RESPONSE_ERROR;
821 }
822
823 // Convert data in RFC4122(MSB) format to LSB format
824 // Get 2 characters at a time as 1 byte is built from 2 chars and
825 // convert to hex byte
826 // TODO: Data printed for GUID command is not as per the
827 // GUID format defined in IPMI specification 2.0 section 20.8
828 // Ticket raised: https://sourceforge.net/p/ipmitool/bugs/501/
829 uint8_t respGuid[bmc_guid_len];
830 for (size_t i = 0, respLoc = (bmc_guid_len - 1);
Patrick Venture0b02be92018-08-31 11:55:55 -0700831 i < guidPropLen && respLoc >= 0; i += 2, respLoc--)
Marri Devender Rao5e007a52018-01-08 06:18:36 -0600832 {
833 auto value = static_cast<uint8_t>(
Patrick Venture0b02be92018-08-31 11:55:55 -0700834 std::stoi(guidProp.substr(i, 2).c_str(), NULL, 16));
Marri Devender Rao5e007a52018-01-08 06:18:36 -0600835 respGuid[respLoc] = value;
836 }
837
838 *data_len = bmc_guid_len;
Patrick Ventureb51bf9c2018-09-10 15:53:14 -0700839 std::memcpy(response, &respGuid, bmc_guid_len);
Marri Devender Rao5e007a52018-01-08 06:18:36 -0600840 }
841 catch (const InternalFailure& e)
842 {
843 log<level::ERR>("Failed in reading BMC UUID property",
844 entry("INTERFACE=%s", bmc_interface),
845 entry("PROPERTY_INTERFACE=%s", bmc_guid_interface),
846 entry("PROPERTY=%s", bmc_guid_property));
847 return IPMI_CC_UNSPECIFIED_ERROR;
848 }
849 return rc;
850}
851
Xo Wangf542e8b2017-08-09 15:34:16 -0700852static std::unique_ptr<SysInfoParamStore> sysInfoParamStore;
853
Xo Wang87651332017-08-11 10:17:59 -0700854static std::string sysInfoReadSystemName()
855{
856 // Use the BMC hostname as the "System Name."
857 char hostname[HOST_NAME_MAX + 1] = {};
858 if (gethostname(hostname, HOST_NAME_MAX) != 0)
859 {
860 perror("System info parameter: system name");
861 }
862 return hostname;
863}
864
Xo Wangf542e8b2017-08-09 15:34:16 -0700865struct IpmiSysInfoResp
866{
867 uint8_t paramRevision;
868 uint8_t setSelector;
869 union
870 {
871 struct
872 {
873 uint8_t encoding;
874 uint8_t stringLen;
875 uint8_t stringData0[14];
876 } __attribute__((packed));
877 uint8_t stringDataN[16];
878 uint8_t byteData;
879 };
880} __attribute__((packed));
881
882/**
883 * Split a string into (up to) 16-byte chunks as expected in response for get
884 * system info parameter.
885 *
886 * @param[in] fullString: Input string to be split
887 * @param[in] chunkIndex: Index of the chunk to be written out
888 * @param[in,out] chunk: Output data buffer; must have 14 byte capacity if
889 * chunk_index = 0 and 16-byte capacity otherwise
890 * @return the number of bytes written into the output buffer, or -EINVAL for
891 * invalid arguments.
892 */
893static int splitStringParam(const std::string& fullString, int chunkIndex,
894 uint8_t* chunk)
895{
896 constexpr int maxChunk = 255;
897 constexpr int smallChunk = 14;
898 constexpr int chunkSize = 16;
899 if (chunkIndex > maxChunk || chunk == nullptr)
900 {
901 return -EINVAL;
902 }
903 try
904 {
905 std::string output;
906 if (chunkIndex == 0)
907 {
908 // Output must have 14 byte capacity.
909 output = fullString.substr(0, smallChunk);
910 }
911 else
912 {
913 // Output must have 16 byte capacity.
914 output = fullString.substr((chunkIndex * chunkSize) - 2, chunkSize);
915 }
916
917 std::memcpy(chunk, output.c_str(), output.length());
918 return output.length();
919 }
920 catch (const std::out_of_range& e)
921 {
922 // The position was beyond the end.
923 return -EINVAL;
924 }
925}
926
927/**
928 * Packs the Get Sys Info Request Item into the response.
929 *
930 * @param[in] paramString - the parameter.
931 * @param[in] setSelector - the selector
932 * @param[in,out] resp - the System info response.
933 * @return The number of bytes packed or failure from splitStringParam().
934 */
935static int packGetSysInfoResp(const std::string& paramString,
936 uint8_t setSelector, IpmiSysInfoResp* resp)
937{
938 uint8_t* dataBuffer = resp->stringDataN;
939 resp->setSelector = setSelector;
940 if (resp->setSelector == 0) // First chunk has only 14 bytes.
941 {
942 resp->encoding = 0;
943 resp->stringLen = paramString.length();
944 dataBuffer = resp->stringData0;
945 }
946 return splitStringParam(paramString, resp->setSelector, dataBuffer);
947}
948
949ipmi_ret_t ipmi_app_get_system_info(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
950 ipmi_request_t request,
951 ipmi_response_t response,
952 ipmi_data_len_t dataLen,
953 ipmi_context_t context)
954{
955 IpmiSysInfoResp resp = {};
956 size_t respLen = 0;
957 uint8_t* const reqData = static_cast<uint8_t*>(request);
958 std::string paramString;
959 bool found;
960 std::tuple<bool, std::string> ret;
961 constexpr int minRequestSize = 4;
962 constexpr int paramSelector = 1;
963 constexpr uint8_t revisionOnly = 0x80;
964 const uint8_t paramRequested = reqData[paramSelector];
965 int rc;
966
967 if (*dataLen < minRequestSize)
968 {
969 return IPMI_CC_REQ_DATA_LEN_INVALID;
970 }
971
972 *dataLen = 0; // default to 0.
973
974 // Parameters revision as of IPMI spec v2.0 rev. 1.1 (Feb 11, 2014 E6)
975 resp.paramRevision = 0x11;
976 if (reqData[0] & revisionOnly) // Get parameter revision only
977 {
978 respLen = 1;
979 goto writeResponse;
980 }
981
982 // The "Set In Progress" parameter can be used for rollback of parameter
983 // data and is not implemented.
984 if (paramRequested == 0)
985 {
986 resp.byteData = 0;
987 respLen = 2;
988 goto writeResponse;
989 }
990
991 if (sysInfoParamStore == nullptr)
992 {
993 sysInfoParamStore = std::make_unique<SysInfoParamStore>();
Xo Wang87651332017-08-11 10:17:59 -0700994 sysInfoParamStore->update(IPMI_SYSINFO_SYSTEM_NAME,
995 sysInfoReadSystemName);
Xo Wangf542e8b2017-08-09 15:34:16 -0700996 }
997
998 // Parameters other than Set In Progress are assumed to be strings.
999 ret = sysInfoParamStore->lookup(paramRequested);
1000 found = std::get<0>(ret);
1001 paramString = std::get<1>(ret);
1002 if (!found)
1003 {
1004 return IPMI_CC_SYSTEM_INFO_PARAMETER_NOT_SUPPORTED;
1005 }
1006 // TODO: Cache each parameter across multiple calls, until the whole string
1007 // has been read out. Otherwise, it's possible for a parameter to change
1008 // between requests for its chunks, returning chunks incoherent with each
1009 // other. For now, the parameter store is simply required to have only
1010 // idempotent callbacks.
1011 rc = packGetSysInfoResp(paramString, reqData[2], &resp);
1012 if (rc == -EINVAL)
1013 {
1014 return IPMI_CC_RESPONSE_ERROR;
1015 }
1016
1017 respLen = sizeof(resp); // Write entire string data chunk in response.
1018
1019writeResponse:
1020 std::memcpy(response, &resp, sizeof(resp));
1021 *dataLen = respLen;
1022 return IPMI_CC_OK;
1023}
1024
Chris Austen6caf28b2015-10-13 12:40:40 -05001025void register_netfn_app_functions()
vishwabmcba0bd5f2015-09-30 16:50:23 +05301026{
Vernon Mauery86a50822019-03-25 13:11:36 -07001027 // <Get Device ID>
1028 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp,
1029 ipmi::app::cmdGetDeviceId, ipmi::Privilege::User,
1030 ipmiAppGetDeviceId);
1031
Tom05732372016-09-06 17:21:23 +05301032 // <Get BT Interface Capabilities>
Vernon Mauerycc99ba42019-03-25 13:40:11 -07001033 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp,
1034 ipmi::app::cmdGetBtIfaceCapabilities,
1035 ipmi::Privilege::User, ipmiAppGetBtCapabilities);
Chris Austen6caf28b2015-10-13 12:40:40 -05001036
Tom05732372016-09-06 17:21:23 +05301037 // <Reset Watchdog Timer>
Patrick Venture0b02be92018-08-31 11:55:55 -07001038 ipmi_register_callback(NETFUN_APP, IPMI_CMD_RESET_WD, NULL,
1039 ipmi_app_watchdog_reset, PRIVILEGE_OPERATOR);
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}