blob: 09b1c2f4445de480f90da21431ab82135dd0b893 [file] [log] [blame]
Patrick Venture0b02be92018-08-31 11:55:55 -07001#include <arpa/inet.h>
Richard Marian Thomaiyar84bf9be2019-04-26 22:59:16 +05302#include <fcntl.h>
Xo Wang87651332017-08-11 10:17:59 -07003#include <limits.h>
Richard Marian Thomaiyar84bf9be2019-04-26 22:59:16 +05304#include <linux/i2c-dev.h>
5#include <linux/i2c.h>
Patrick Venture0b02be92018-08-31 11:55:55 -07006#include <mapper.h>
Richard Marian Thomaiyar84bf9be2019-04-26 22:59:16 +05307#include <sys/ioctl.h>
8#include <sys/stat.h>
9#include <sys/types.h>
Patrick Venture0b02be92018-08-31 11:55:55 -070010#include <systemd/sd-bus.h>
Xo Wang87651332017-08-11 10:17:59 -070011#include <unistd.h>
Patrick Venture0b02be92018-08-31 11:55:55 -070012
Patrick Venture3a5071a2018-09-12 13:27:42 -070013#include <algorithm>
Vernon Mauery0120b682019-03-25 13:08:54 -070014#include <app/channel.hpp>
15#include <app/watchdog.hpp>
16#include <apphandler.hpp>
Patrick Venture3a5071a2018-09-12 13:27:42 -070017#include <array>
18#include <cstddef>
Vernon Mauery0120b682019-03-25 13:08:54 -070019#include <cstdint>
Vernon Mauerybdda8002019-02-26 10:18:51 -080020#include <filesystem>
Patrick Venture3a5071a2018-09-12 13:27:42 -070021#include <fstream>
Vernon Mauery0120b682019-03-25 13:08:54 -070022#include <ipmid/api.hpp>
Vernon Mauery33250242019-03-12 16:49:26 -070023#include <ipmid/types.hpp>
Vernon Mauery6a98fe72019-03-11 15:57:48 -070024#include <ipmid/utils.hpp>
Patrick Venture3a5071a2018-09-12 13:27:42 -070025#include <memory>
Patrick Venture46470a32018-09-07 19:26:25 -070026#include <nlohmann/json.hpp>
Patrick Venture3a5071a2018-09-12 13:27:42 -070027#include <phosphor-logging/elog-errors.hpp>
28#include <phosphor-logging/log.hpp>
William A. Kennington III4c008022018-10-12 17:18:14 -070029#include <sdbusplus/message/types.hpp>
Patrick Venture3a5071a2018-09-12 13:27:42 -070030#include <string>
Vernon Mauery0120b682019-03-25 13:08:54 -070031#include <sys_info_param.hpp>
32#include <transporthandler.hpp>
Patrick Venture3a5071a2018-09-12 13:27:42 -070033#include <tuple>
34#include <vector>
35#include <xyz/openbmc_project/Common/error.hpp>
Yong Li18d77262018-10-09 01:59:45 +080036#include <xyz/openbmc_project/Control/Power/ACPIPowerState/server.hpp>
Patrick Venture3a5071a2018-09-12 13:27:42 -070037#include <xyz/openbmc_project/Software/Activation/server.hpp>
38#include <xyz/openbmc_project/Software/Version/server.hpp>
39#include <xyz/openbmc_project/State/BMC/server.hpp>
Ratan Guptab8e99552017-07-27 07:07:48 +053040
Patrick Venture0b02be92018-08-31 11:55:55 -070041extern sd_bus* bus;
vishwabmcba0bd5f2015-09-30 16:50:23 +053042
Alexander Amelkinba19c182018-09-04 15:49:36 +030043constexpr auto bmc_state_interface = "xyz.openbmc_project.State.BMC";
44constexpr auto bmc_state_property = "CurrentBMCState";
Marri Devender Rao5e007a52018-01-08 06:18:36 -060045
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;
Ratan Guptab8e99552017-07-27 07:07:48 +053062
Richard Marian Thomaiyar84bf9be2019-04-26 22:59:16 +053063typedef struct
64{
65 uint8_t busId;
66 uint8_t slaveAddr;
67 uint8_t slaveAddrMask;
68 std::vector<uint8_t> data;
69 std::vector<uint8_t> dataMask;
70} i2cMasterWRWhitelist;
71
72static std::vector<i2cMasterWRWhitelist>& getWRWhitelist()
73{
74 static std::vector<i2cMasterWRWhitelist> wrWhitelist;
75 return wrWhitelist;
76}
77
78static constexpr const char* i2cMasterWRWhitelistFile =
79 "/usr/share/ipmi-providers/master_write_read_white_list.json";
80
81static constexpr uint8_t maxIPMIWriteReadSize = 144;
82static constexpr const char* filtersStr = "filters";
83static constexpr const char* busIdStr = "busId";
84static constexpr const char* slaveAddrStr = "slaveAddr";
85static constexpr const char* slaveAddrMaskStr = "slaveAddrMask";
86static constexpr const char* cmdStr = "command";
87static constexpr const char* cmdMaskStr = "commandMask";
88static constexpr int base_16 = 16;
89
Nagaraju Goruganti744398d2018-02-20 09:52:00 -060090/**
91 * @brief Returns the Version info from primary s/w object
92 *
93 * Get the Version info from the active s/w object which is having high
94 * "Priority" value(a smaller number is a higher priority) and "Purpose"
95 * is "BMC" from the list of all s/w objects those are implementing
96 * RedundancyPriority interface from the given softwareRoot path.
97 *
98 * @return On success returns the Version info from primary s/w object.
99 *
100 */
Vernon Maueryea1c4012019-05-24 13:26:16 -0700101std::string getActiveSoftwareVersionInfo(ipmi::Context::ptr ctx)
Nagaraju Goruganti744398d2018-02-20 09:52:00 -0600102{
Nagaraju Goruganti744398d2018-02-20 09:52:00 -0600103 std::string revision{};
Vernon Mauery86a50822019-03-25 13:11:36 -0700104 ipmi::ObjectTree objectTree;
105 try
Nagaraju Goruganti744398d2018-02-20 09:52:00 -0600106 {
Vernon Mauery86a50822019-03-25 13:11:36 -0700107 objectTree =
Vernon Maueryea1c4012019-05-24 13:26:16 -0700108 ipmi::getAllDbusObjects(*ctx->bus, softwareRoot, redundancyIntf);
Vernon Mauery86a50822019-03-25 13:11:36 -0700109 }
110 catch (sdbusplus::exception::SdBusError& e)
111 {
112 log<level::ERR>("Failed to fetch redundancy object from dbus",
113 entry("INTERFACE=%s", redundancyIntf),
114 entry("ERRMSG=%s", e.what()));
Nagaraju Goruganti744398d2018-02-20 09:52:00 -0600115 elog<InternalFailure>();
116 }
117
118 auto objectFound = false;
119 for (auto& softObject : objectTree)
120 {
Vernon Mauery86a50822019-03-25 13:11:36 -0700121 auto service =
Vernon Maueryea1c4012019-05-24 13:26:16 -0700122 ipmi::getService(*ctx->bus, redundancyIntf, softObject.first);
Vernon Mauery86a50822019-03-25 13:11:36 -0700123 auto objValueTree =
Vernon Maueryea1c4012019-05-24 13:26:16 -0700124 ipmi::getManagedObjects(*ctx->bus, service, softwareRoot);
Nagaraju Goruganti744398d2018-02-20 09:52:00 -0600125
126 auto minPriority = 0xFF;
127 for (const auto& objIter : objValueTree)
128 {
129 try
130 {
131 auto& intfMap = objIter.second;
132 auto& redundancyPriorityProps = intfMap.at(redundancyIntf);
133 auto& versionProps = intfMap.at(versionIntf);
134 auto& activationProps = intfMap.at(activationIntf);
Vernon Maueryf442e112019-04-09 11:44:36 -0700135 auto priority =
136 std::get<uint8_t>(redundancyPriorityProps.at("Priority"));
William A. Kennington III4c008022018-10-12 17:18:14 -0700137 auto purpose =
Vernon Maueryf442e112019-04-09 11:44:36 -0700138 std::get<std::string>(versionProps.at("Purpose"));
139 auto activation =
140 std::get<std::string>(activationProps.at("Activation"));
William A. Kennington III4c008022018-10-12 17:18:14 -0700141 auto version =
Vernon Maueryf442e112019-04-09 11:44:36 -0700142 std::get<std::string>(versionProps.at("Version"));
Nagaraju Goruganti744398d2018-02-20 09:52:00 -0600143 if ((Version::convertVersionPurposeFromString(purpose) ==
144 Version::VersionPurpose::BMC) &&
145 (Activation::convertActivationsFromString(activation) ==
146 Activation::Activations::Active))
147 {
148 if (priority < minPriority)
149 {
150 minPriority = priority;
151 objectFound = true;
152 revision = std::move(version);
153 }
154 }
155 }
156 catch (const std::exception& e)
157 {
158 log<level::ERR>(e.what());
159 }
160 }
161 }
162
163 if (!objectFound)
164 {
165 log<level::ERR>("Could not found an BMC software Object");
166 elog<InternalFailure>();
167 }
168
169 return revision;
170}
171
Alexander Amelkinba19c182018-09-04 15:49:36 +0300172bool getCurrentBmcState()
173{
174 sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()};
175
176 // Get the Inventory object implementing the BMC interface
177 ipmi::DbusObjectInfo bmcObject =
178 ipmi::getDbusObject(bus, bmc_state_interface);
179 auto variant =
180 ipmi::getDbusProperty(bus, bmcObject.second, bmcObject.first,
181 bmc_state_interface, bmc_state_property);
182
Vernon Maueryf442e112019-04-09 11:44:36 -0700183 return std::holds_alternative<std::string>(variant) &&
184 BMC::convertBMCStateFromString(std::get<std::string>(variant)) ==
185 BMC::BMCState::Ready;
Alexander Amelkinba19c182018-09-04 15:49:36 +0300186}
187
Patrick Venture94930a12019-04-30 10:01:58 -0700188bool getCurrentBmcStateWithFallback(const bool fallbackAvailability)
189{
190 try
191 {
192 return getCurrentBmcState();
193 }
194 catch (...)
195 {
196 // Nothing provided the BMC interface, therefore return whatever was
197 // configured as the default.
198 return fallbackAvailability;
199 }
200}
201
Yong Li18d77262018-10-09 01:59:45 +0800202namespace acpi_state
203{
204using namespace sdbusplus::xyz::openbmc_project::Control::Power::server;
205
206const static constexpr char* acpiObjPath =
207 "/xyz/openbmc_project/control/host0/acpi_power_state";
208const static constexpr char* acpiInterface =
209 "xyz.openbmc_project.Control.Power.ACPIPowerState";
210const static constexpr char* sysACPIProp = "SysACPIStatus";
211const static constexpr char* devACPIProp = "DevACPIStatus";
212
213enum class PowerStateType : uint8_t
214{
215 sysPowerState = 0x00,
216 devPowerState = 0x01,
217};
218
219// Defined in 20.6 of ipmi doc
220enum class PowerState : uint8_t
221{
222 s0G0D0 = 0x00,
223 s1D1 = 0x01,
224 s2D2 = 0x02,
225 s3D3 = 0x03,
226 s4 = 0x04,
227 s5G2 = 0x05,
228 s4S5 = 0x06,
229 g3 = 0x07,
230 sleep = 0x08,
231 g1Sleep = 0x09,
232 override = 0x0a,
233 legacyOn = 0x20,
234 legacyOff = 0x21,
235 unknown = 0x2a,
236 noChange = 0x7f,
237};
238
239static constexpr uint8_t stateChanged = 0x80;
240
241struct ACPIState
242{
243 uint8_t sysACPIState;
244 uint8_t devACPIState;
245} __attribute__((packed));
246
247std::map<ACPIPowerState::ACPI, PowerState> dbusToIPMI = {
248 {ACPIPowerState::ACPI::S0_G0_D0, PowerState::s0G0D0},
249 {ACPIPowerState::ACPI::S1_D1, PowerState::s1D1},
250 {ACPIPowerState::ACPI::S2_D2, PowerState::s2D2},
251 {ACPIPowerState::ACPI::S3_D3, PowerState::s3D3},
252 {ACPIPowerState::ACPI::S4, PowerState::s4},
253 {ACPIPowerState::ACPI::S5_G2, PowerState::s5G2},
254 {ACPIPowerState::ACPI::S4_S5, PowerState::s4S5},
255 {ACPIPowerState::ACPI::G3, PowerState::g3},
256 {ACPIPowerState::ACPI::SLEEP, PowerState::sleep},
257 {ACPIPowerState::ACPI::G1_SLEEP, PowerState::g1Sleep},
258 {ACPIPowerState::ACPI::OVERRIDE, PowerState::override},
259 {ACPIPowerState::ACPI::LEGACY_ON, PowerState::legacyOn},
260 {ACPIPowerState::ACPI::LEGACY_OFF, PowerState::legacyOff},
261 {ACPIPowerState::ACPI::Unknown, PowerState::unknown}};
262
263bool isValidACPIState(acpi_state::PowerStateType type, uint8_t state)
264{
265 if (type == acpi_state::PowerStateType::sysPowerState)
266 {
267 if ((state <= static_cast<uint8_t>(acpi_state::PowerState::override)) ||
268 (state == static_cast<uint8_t>(acpi_state::PowerState::legacyOn)) ||
269 (state ==
270 static_cast<uint8_t>(acpi_state::PowerState::legacyOff)) ||
271 (state == static_cast<uint8_t>(acpi_state::PowerState::unknown)) ||
272 (state == static_cast<uint8_t>(acpi_state::PowerState::noChange)))
273 {
274 return true;
275 }
276 else
277 {
278 return false;
279 }
280 }
281 else if (type == acpi_state::PowerStateType::devPowerState)
282 {
283 if ((state <= static_cast<uint8_t>(acpi_state::PowerState::s3D3)) ||
284 (state == static_cast<uint8_t>(acpi_state::PowerState::unknown)) ||
285 (state == static_cast<uint8_t>(acpi_state::PowerState::noChange)))
286 {
287 return true;
288 }
289 else
290 {
291 return false;
292 }
293 }
294 else
295 {
296 return false;
297 }
298 return false;
299}
300} // namespace acpi_state
301
Adriana Kobylak3a552e12015-10-19 16:11:00 -0500302ipmi_ret_t ipmi_app_set_acpi_power_state(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
Patrick Venture0b02be92018-08-31 11:55:55 -0700303 ipmi_request_t request,
304 ipmi_response_t response,
305 ipmi_data_len_t data_len,
306 ipmi_context_t context)
Chris Austen6caf28b2015-10-13 12:40:40 -0500307{
Yong Li18d77262018-10-09 01:59:45 +0800308 auto s = static_cast<uint8_t>(acpi_state::PowerState::unknown);
Chris Austen6caf28b2015-10-13 12:40:40 -0500309 ipmi_ret_t rc = IPMI_CC_OK;
Yong Li18d77262018-10-09 01:59:45 +0800310
311 sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()};
312
313 auto value = acpi_state::ACPIPowerState::ACPI::Unknown;
314
315 auto* req = reinterpret_cast<acpi_state::ACPIState*>(request);
316
317 if (*data_len != sizeof(acpi_state::ACPIState))
318 {
319 log<level::ERR>("set_acpi invalid len");
320 *data_len = 0;
321 return IPMI_CC_REQ_DATA_LEN_INVALID;
322 }
323
Chris Austen6caf28b2015-10-13 12:40:40 -0500324 *data_len = 0;
325
Yong Li18d77262018-10-09 01:59:45 +0800326 if (req->sysACPIState & acpi_state::stateChanged)
327 {
328 // set system power state
329 s = req->sysACPIState & ~acpi_state::stateChanged;
330
331 if (!acpi_state::isValidACPIState(
332 acpi_state::PowerStateType::sysPowerState, s))
333 {
334 log<level::ERR>("set_acpi_power sys invalid input",
335 entry("S=%x", s));
336 return IPMI_CC_PARM_OUT_OF_RANGE;
337 }
338
339 // valid input
340 if (s == static_cast<uint8_t>(acpi_state::PowerState::noChange))
341 {
342 log<level::DEBUG>("No change for system power state");
343 }
344 else
345 {
346 auto found = std::find_if(
347 acpi_state::dbusToIPMI.begin(), acpi_state::dbusToIPMI.end(),
348 [&s](const auto& iter) {
349 return (static_cast<uint8_t>(iter.second) == s);
350 });
351
352 value = found->first;
353
354 try
355 {
356 auto acpiObject =
357 ipmi::getDbusObject(bus, acpi_state::acpiInterface);
358 ipmi::setDbusProperty(bus, acpiObject.second, acpiObject.first,
359 acpi_state::acpiInterface,
360 acpi_state::sysACPIProp,
361 convertForMessage(value));
362 }
363 catch (const InternalFailure& e)
364 {
365 log<level::ERR>("Failed in set ACPI system property",
366 entry("EXCEPTION=%s", e.what()));
367 return IPMI_CC_UNSPECIFIED_ERROR;
368 }
369 }
370 }
371 else
372 {
373 log<level::DEBUG>("Do not change system power state");
374 }
375
376 if (req->devACPIState & acpi_state::stateChanged)
377 {
378 // set device power state
379 s = req->devACPIState & ~acpi_state::stateChanged;
380 if (!acpi_state::isValidACPIState(
381 acpi_state::PowerStateType::devPowerState, s))
382 {
383 log<level::ERR>("set_acpi_power dev invalid input",
384 entry("S=%x", s));
385 return IPMI_CC_PARM_OUT_OF_RANGE;
386 }
387
388 // valid input
389 if (s == static_cast<uint8_t>(acpi_state::PowerState::noChange))
390 {
391 log<level::DEBUG>("No change for device power state");
392 }
393 else
394 {
395 auto found = std::find_if(
396 acpi_state::dbusToIPMI.begin(), acpi_state::dbusToIPMI.end(),
397 [&s](const auto& iter) {
398 return (static_cast<uint8_t>(iter.second) == s);
399 });
400
401 value = found->first;
402
403 try
404 {
405 auto acpiObject =
406 ipmi::getDbusObject(bus, acpi_state::acpiInterface);
407 ipmi::setDbusProperty(bus, acpiObject.second, acpiObject.first,
408 acpi_state::acpiInterface,
409 acpi_state::devACPIProp,
410 convertForMessage(value));
411 }
412 catch (const InternalFailure& e)
413 {
414 log<level::ERR>("Failed in set ACPI device property",
415 entry("EXCEPTION=%s", e.what()));
416 return IPMI_CC_UNSPECIFIED_ERROR;
417 }
418 }
419 }
420 else
421 {
422 log<level::DEBUG>("Do not change device power state");
423 }
424
425 return rc;
426}
427
Deepak Kumar Sahu4e6d2572019-05-07 19:53:37 +0000428/**
429 * @brief implements the get ACPI power state command
430 *
431 * @return IPMI completion code plus response data on success.
432 * - ACPI system power state
433 * - ACPI device power state
434 **/
435ipmi::RspType<uint8_t, // acpiSystemPowerState
436 uint8_t // acpiDevicePowerState
437 >
438 ipmiGetAcpiPowerState()
Yong Li18d77262018-10-09 01:59:45 +0800439{
Deepak Kumar Sahu4e6d2572019-05-07 19:53:37 +0000440 uint8_t sysAcpiState;
441 uint8_t devAcpiState;
Yong Li18d77262018-10-09 01:59:45 +0800442
443 sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()};
444
Yong Li18d77262018-10-09 01:59:45 +0800445 try
446 {
447 auto acpiObject = ipmi::getDbusObject(bus, acpi_state::acpiInterface);
448
449 auto sysACPIVal = ipmi::getDbusProperty(
450 bus, acpiObject.second, acpiObject.first, acpi_state::acpiInterface,
451 acpi_state::sysACPIProp);
452 auto sysACPI = acpi_state::ACPIPowerState::convertACPIFromString(
Vernon Maueryf442e112019-04-09 11:44:36 -0700453 std::get<std::string>(sysACPIVal));
Deepak Kumar Sahu4e6d2572019-05-07 19:53:37 +0000454 sysAcpiState = static_cast<uint8_t>(acpi_state::dbusToIPMI.at(sysACPI));
Yong Li18d77262018-10-09 01:59:45 +0800455
456 auto devACPIVal = ipmi::getDbusProperty(
457 bus, acpiObject.second, acpiObject.first, acpi_state::acpiInterface,
458 acpi_state::devACPIProp);
459 auto devACPI = acpi_state::ACPIPowerState::convertACPIFromString(
Vernon Maueryf442e112019-04-09 11:44:36 -0700460 std::get<std::string>(devACPIVal));
Deepak Kumar Sahu4e6d2572019-05-07 19:53:37 +0000461 devAcpiState = static_cast<uint8_t>(acpi_state::dbusToIPMI.at(devACPI));
Yong Li18d77262018-10-09 01:59:45 +0800462 }
463 catch (const InternalFailure& e)
464 {
Deepak Kumar Sahu4e6d2572019-05-07 19:53:37 +0000465 return ipmi::responseUnspecifiedError();
Yong Li18d77262018-10-09 01:59:45 +0800466 }
Deepak Kumar Sahu4e6d2572019-05-07 19:53:37 +0000467
468 return ipmi::responseSuccess(sysAcpiState, devAcpiState);
Chris Austen6caf28b2015-10-13 12:40:40 -0500469}
470
Chris Austen7303bdc2016-04-17 11:50:54 -0500471typedef struct
472{
473 char major;
474 char minor;
Chris Austen176c9652016-04-30 16:32:17 -0500475 uint16_t d[2];
Vernon Mauery86a50822019-03-25 13:11:36 -0700476} Revision;
Chris Austen7303bdc2016-04-17 11:50:54 -0500477
Dinesh Chinari2b7e07d2017-11-08 15:38:50 -0600478/* Currently supports the vx.x-x-[-x] and v1.x.x-x-[-x] format. It will */
479/* return -1 if not in those formats, this routine knows how to parse */
Chris Austen7303bdc2016-04-17 11:50:54 -0500480/* version = v0.6-19-gf363f61-dirty */
481/* ^ ^ ^^ ^ */
482/* | | |----------|-- additional details */
483/* | |---------------- Minor */
484/* |------------------ Major */
Dinesh Chinari2b7e07d2017-11-08 15:38:50 -0600485/* and version = v1.99.10-113-g65edf7d-r3-0-g9e4f715 */
486/* ^ ^ ^^ ^ */
487/* | | |--|---------- additional details */
488/* | |---------------- Minor */
489/* |------------------ Major */
Chris Austen7303bdc2016-04-17 11:50:54 -0500490/* Additional details : If the option group exists it will force Auxiliary */
491/* Firmware Revision Information 4th byte to 1 indicating the build was */
492/* derived with additional edits */
Vernon Mauery86a50822019-03-25 13:11:36 -0700493int convertVersion(std::string s, Revision& rev)
Chris Austen7303bdc2016-04-17 11:50:54 -0500494{
Dinesh Chinari2b7e07d2017-11-08 15:38:50 -0600495 std::string token;
Chris Austen176c9652016-04-30 16:32:17 -0500496 uint16_t commits;
Chris Austen7303bdc2016-04-17 11:50:54 -0500497
Patrick Venture0b02be92018-08-31 11:55:55 -0700498 auto location = s.find_first_of('v');
Dinesh Chinari2b7e07d2017-11-08 15:38:50 -0600499 if (location != std::string::npos)
500 {
Patrick Venture0b02be92018-08-31 11:55:55 -0700501 s = s.substr(location + 1);
Chris Austen176c9652016-04-30 16:32:17 -0500502 }
Chris Austen7303bdc2016-04-17 11:50:54 -0500503
Dinesh Chinari2b7e07d2017-11-08 15:38:50 -0600504 if (!s.empty())
505 {
506 location = s.find_first_of(".");
507 if (location != std::string::npos)
508 {
Vernon Mauery86a50822019-03-25 13:11:36 -0700509 rev.major =
Patrick Venture0b02be92018-08-31 11:55:55 -0700510 static_cast<char>(std::stoi(s.substr(0, location), 0, 16));
511 token = s.substr(location + 1);
Dinesh Chinari2b7e07d2017-11-08 15:38:50 -0600512 }
Chris Austen176c9652016-04-30 16:32:17 -0500513
Dinesh Chinari2b7e07d2017-11-08 15:38:50 -0600514 if (!token.empty())
515 {
516 location = token.find_first_of(".-");
517 if (location != std::string::npos)
518 {
Vernon Mauery86a50822019-03-25 13:11:36 -0700519 rev.minor = static_cast<char>(
Patrick Venture0b02be92018-08-31 11:55:55 -0700520 std::stoi(token.substr(0, location), 0, 16));
521 token = token.substr(location + 1);
Dinesh Chinari2b7e07d2017-11-08 15:38:50 -0600522 }
523 }
Chris Austen7303bdc2016-04-17 11:50:54 -0500524
Dinesh Chinari2b7e07d2017-11-08 15:38:50 -0600525 // Capture the number of commits on top of the minor tag.
526 // I'm using BE format like the ipmi spec asked for
527 location = token.find_first_of(".-");
528 if (!token.empty())
529 {
530 commits = std::stoi(token.substr(0, location), 0, 16);
Vernon Mauery86a50822019-03-25 13:11:36 -0700531 rev.d[0] = (commits >> 8) | (commits << 8);
Dinesh Chinari2b7e07d2017-11-08 15:38:50 -0600532
533 // commit number we skip
534 location = token.find_first_of(".-");
535 if (location != std::string::npos)
536 {
Patrick Venture0b02be92018-08-31 11:55:55 -0700537 token = token.substr(location + 1);
Dinesh Chinari2b7e07d2017-11-08 15:38:50 -0600538 }
539 }
Patrick Venture0b02be92018-08-31 11:55:55 -0700540 else
541 {
Vernon Mauery86a50822019-03-25 13:11:36 -0700542 rev.d[0] = 0;
Dinesh Chinari2b7e07d2017-11-08 15:38:50 -0600543 }
544
545 if (location != std::string::npos)
546 {
Patrick Venture0b02be92018-08-31 11:55:55 -0700547 token = token.substr(location + 1);
Dinesh Chinari2b7e07d2017-11-08 15:38:50 -0600548 }
549
550 // Any value of the optional parameter forces it to 1
551 location = token.find_first_of(".-");
552 if (location != std::string::npos)
553 {
Patrick Venture0b02be92018-08-31 11:55:55 -0700554 token = token.substr(location + 1);
Dinesh Chinari2b7e07d2017-11-08 15:38:50 -0600555 }
556 commits = (!token.empty()) ? 1 : 0;
557
Patrick Venture0b02be92018-08-31 11:55:55 -0700558 // We do this operation to get this displayed in least significant bytes
559 // of ipmitool device id command.
Vernon Mauery86a50822019-03-25 13:11:36 -0700560 rev.d[1] = (commits >> 8) | (commits << 8);
Dinesh Chinari2b7e07d2017-11-08 15:38:50 -0600561 }
562
Chris Austen7303bdc2016-04-17 11:50:54 -0500563 return 0;
564}
565
Vernon Maueryea1c4012019-05-24 13:26:16 -0700566/* @brief: Implement the Get Device ID IPMI command per the IPMI spec
567 * @param[in] ctx - shared_ptr to an IPMI context struct
568 *
569 * @returns IPMI completion code plus response data
570 * - Device ID (manufacturer defined)
571 * - Device revision[4 bits]; reserved[3 bits]; SDR support[1 bit]
572 * - FW revision major[7 bits] (binary encoded); available[1 bit]
573 * - FW Revision minor (BCD encoded)
574 * - IPMI version (0x02 for IPMI 2.0)
575 * - device support (bitfield of supported options)
576 * - MFG IANA ID (3 bytes)
577 * - product ID (2 bytes)
578 * - AUX info (4 bytes)
579 */
580ipmi::RspType<uint8_t, // Device ID
581 uint8_t, // Device Revision
582 uint8_t, // Firmware Revision Major
583 uint8_t, // Firmware Revision minor
584 uint8_t, // IPMI version
585 uint8_t, // Additional device support
586 uint24_t, // MFG ID
587 uint16_t, // Product ID
588 uint32_t // AUX info
589 >
590 ipmiAppGetDeviceId(ipmi::Context::ptr ctx)
Chris Austen6caf28b2015-10-13 12:40:40 -0500591{
Nagaraju Goruganti744398d2018-02-20 09:52:00 -0600592 int r = -1;
Vernon Mauery86a50822019-03-25 13:11:36 -0700593 Revision rev = {0};
594 static struct
595 {
596 uint8_t id;
597 uint8_t revision;
598 uint8_t fw[2];
599 uint8_t ipmiVer;
600 uint8_t addnDevSupport;
601 uint24_t manufId;
602 uint16_t prodId;
603 uint32_t aux;
604 } devId;
David Cobbleya1adb072017-11-21 15:58:13 -0800605 static bool dev_id_initialized = false;
Patrick Venture94930a12019-04-30 10:01:58 -0700606 static bool defaultActivationSetting = true;
David Cobbleya1adb072017-11-21 15:58:13 -0800607 const char* filename = "/usr/share/ipmi-providers/dev_id.json";
Alexander Amelkinba19c182018-09-04 15:49:36 +0300608 constexpr auto ipmiDevIdStateShift = 7;
609 constexpr auto ipmiDevIdFw1Mask = ~(1 << ipmiDevIdStateShift);
Chris Austen6caf28b2015-10-13 12:40:40 -0500610
David Cobbleya1adb072017-11-21 15:58:13 -0800611 if (!dev_id_initialized)
612 {
Nagaraju Goruganti744398d2018-02-20 09:52:00 -0600613 try
614 {
Vernon Maueryea1c4012019-05-24 13:26:16 -0700615 auto version = getActiveSoftwareVersionInfo(ctx);
Vernon Mauery86a50822019-03-25 13:11:36 -0700616 r = convertVersion(version, rev);
David Cobbleya1adb072017-11-21 15:58:13 -0800617 }
Nagaraju Goruganti744398d2018-02-20 09:52:00 -0600618 catch (const std::exception& e)
619 {
620 log<level::ERR>(e.what());
621 }
Nan Liee0cb902016-07-11 15:38:03 +0800622
Patrick Venture0b02be92018-08-31 11:55:55 -0700623 if (r >= 0)
624 {
Nagaraju Goruganti744398d2018-02-20 09:52:00 -0600625 // bit7 identifies if the device is available
626 // 0=normal operation
627 // 1=device firmware, SDR update,
628 // or self-initialization in progress.
Alexander Amelkinba19c182018-09-04 15:49:36 +0300629 // The availability may change in run time, so mask here
630 // and initialize later.
Vernon Mauery86a50822019-03-25 13:11:36 -0700631 devId.fw[0] = rev.major & ipmiDevIdFw1Mask;
Nagaraju Goruganti744398d2018-02-20 09:52:00 -0600632
633 rev.minor = (rev.minor > 99 ? 99 : rev.minor);
Vernon Mauery86a50822019-03-25 13:11:36 -0700634 devId.fw[1] = rev.minor % 10 + (rev.minor / 10) * 16;
635 std::memcpy(&devId.aux, rev.d, 4);
David Cobbleya1adb072017-11-21 15:58:13 -0800636 }
Nan Liee0cb902016-07-11 15:38:03 +0800637
David Cobbleya1adb072017-11-21 15:58:13 -0800638 // IPMI Spec version 2.0
Vernon Mauery86a50822019-03-25 13:11:36 -0700639 devId.ipmiVer = 2;
Adriana Kobylak0e912642016-06-22 16:54:39 -0500640
Vernon Mauery86a50822019-03-25 13:11:36 -0700641 std::ifstream devIdFile(filename);
642 if (devIdFile.is_open())
David Cobbleya1adb072017-11-21 15:58:13 -0800643 {
Vernon Mauery86a50822019-03-25 13:11:36 -0700644 auto data = nlohmann::json::parse(devIdFile, nullptr, false);
David Cobbleya1adb072017-11-21 15:58:13 -0800645 if (!data.is_discarded())
646 {
Vernon Mauery86a50822019-03-25 13:11:36 -0700647 devId.id = data.value("id", 0);
648 devId.revision = data.value("revision", 0);
649 devId.addnDevSupport = data.value("addn_dev_support", 0);
650 devId.manufId = data.value("manuf_id", 0);
651 devId.prodId = data.value("prod_id", 0);
652 devId.aux = data.value("aux", 0);
David Cobbleya1adb072017-11-21 15:58:13 -0800653
Patrick Venture94930a12019-04-30 10:01:58 -0700654 // Set the availablitity of the BMC.
655 defaultActivationSetting = data.value("availability", true);
656
Patrick Venture0b02be92018-08-31 11:55:55 -0700657 // Don't read the file every time if successful
David Cobbleya1adb072017-11-21 15:58:13 -0800658 dev_id_initialized = true;
659 }
660 else
661 {
662 log<level::ERR>("Device ID JSON parser failure");
Vernon Maueryf2587fc2019-04-09 14:28:15 -0700663 return ipmi::responseUnspecifiedError();
David Cobbleya1adb072017-11-21 15:58:13 -0800664 }
665 }
666 else
667 {
668 log<level::ERR>("Device ID file not found");
Vernon Maueryf2587fc2019-04-09 14:28:15 -0700669 return ipmi::responseUnspecifiedError();
Chris Austen7303bdc2016-04-17 11:50:54 -0500670 }
671 }
Chris Austen6caf28b2015-10-13 12:40:40 -0500672
Alexander Amelkinba19c182018-09-04 15:49:36 +0300673 // Set availability to the actual current BMC state
Vernon Mauery86a50822019-03-25 13:11:36 -0700674 devId.fw[0] &= ipmiDevIdFw1Mask;
Patrick Venture94930a12019-04-30 10:01:58 -0700675 if (!getCurrentBmcStateWithFallback(defaultActivationSetting))
Alexander Amelkinba19c182018-09-04 15:49:36 +0300676 {
Vernon Mauery86a50822019-03-25 13:11:36 -0700677 devId.fw[0] |= (1 << ipmiDevIdStateShift);
Alexander Amelkinba19c182018-09-04 15:49:36 +0300678 }
679
Vernon Mauery86a50822019-03-25 13:11:36 -0700680 return ipmi::responseSuccess(
681 devId.id, devId.revision, devId.fw[0], devId.fw[1], devId.ipmiVer,
682 devId.addnDevSupport, devId.manufId, devId.prodId, devId.aux);
Chris Austen6caf28b2015-10-13 12:40:40 -0500683}
684
Vernon Maueryb84a5282019-03-25 13:39:03 -0700685auto ipmiAppGetSelfTestResults() -> ipmi::RspType<uint8_t, uint8_t>
Nan Li41fa24a2016-11-10 20:12:37 +0800686{
Nan Li41fa24a2016-11-10 20:12:37 +0800687 // Byte 2:
688 // 55h - No error.
Gunnar Mills8991dd62017-10-25 17:11:29 -0500689 // 56h - Self Test function not implemented in this controller.
Nan Li41fa24a2016-11-10 20:12:37 +0800690 // 57h - Corrupted or inaccesssible data or devices.
691 // 58h - Fatal hardware error.
692 // FFh - reserved.
693 // all other: Device-specific 'internal failure'.
694 // Byte 3:
695 // For byte 2 = 55h, 56h, FFh: 00h
696 // For byte 2 = 58h, all other: Device-specific
697 // For byte 2 = 57h: self-test error bitfield.
698 // Note: returning 57h does not imply that all test were run.
699 // [7] 1b = Cannot access SEL device.
700 // [6] 1b = Cannot access SDR Repository.
701 // [5] 1b = Cannot access BMC FRU device.
702 // [4] 1b = IPMB signal lines do not respond.
703 // [3] 1b = SDR Repository empty.
704 // [2] 1b = Internal Use Area of BMC FRU corrupted.
705 // [1] 1b = controller update 'boot block' firmware corrupted.
706 // [0] 1b = controller operational firmware corrupted.
Vernon Maueryb84a5282019-03-25 13:39:03 -0700707 constexpr uint8_t notImplemented = 0x56;
708 constexpr uint8_t zero = 0;
709 return ipmi::responseSuccess(notImplemented, zero);
Nan Li41fa24a2016-11-10 20:12:37 +0800710}
711
Vernon Mauery15541322019-03-25 13:33:03 -0700712static constexpr size_t uuidBinaryLength = 16;
713static std::array<uint8_t, uuidBinaryLength> rfc4122ToIpmi(std::string rfc4122)
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500714{
Vernon Mauery15541322019-03-25 13:33:03 -0700715 using Argument = xyz::openbmc_project::Common::InvalidArgument;
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500716 // UUID is in RFC4122 format. Ex: 61a39523-78f2-11e5-9862-e6402cfc3223
Patrick Ventured2117022018-02-06 08:54:37 -0800717 // Per IPMI Spec 2.0 need to convert to 16 hex bytes and reverse the byte
718 // order
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500719 // Ex: 0x2332fc2c40e66298e511f2782395a361
Vernon Mauery15541322019-03-25 13:33:03 -0700720 constexpr size_t uuidHexLength = (2 * uuidBinaryLength);
721 constexpr size_t uuidRfc4122Length = (uuidHexLength + 4);
722 std::array<uint8_t, uuidBinaryLength> uuid;
723 if (rfc4122.size() == uuidRfc4122Length)
Patrick Venture0b02be92018-08-31 11:55:55 -0700724 {
Vernon Mauery15541322019-03-25 13:33:03 -0700725 rfc4122.erase(std::remove(rfc4122.begin(), rfc4122.end(), '-'),
726 rfc4122.end());
Sergey Solomineb9b8142016-08-23 09:07:28 -0500727 }
Vernon Mauery15541322019-03-25 13:33:03 -0700728 if (rfc4122.size() != uuidHexLength)
vishwa1eaea4f2016-02-26 11:57:40 -0600729 {
Vernon Mauery15541322019-03-25 13:33:03 -0700730 elog<InvalidArgument>(Argument::ARGUMENT_NAME("rfc4122"),
731 Argument::ARGUMENT_VALUE(rfc4122.c_str()));
vishwa1eaea4f2016-02-26 11:57:40 -0600732 }
Vernon Mauery15541322019-03-25 13:33:03 -0700733 for (size_t ind = 0; ind < uuidHexLength; ind += 2)
vishwa1eaea4f2016-02-26 11:57:40 -0600734 {
Vernon Mauery15541322019-03-25 13:33:03 -0700735 char v[3];
736 v[0] = rfc4122[ind];
737 v[1] = rfc4122[ind + 1];
738 v[2] = 0;
739 size_t err;
740 long b;
741 try
Emily Shafferedb8bb02018-09-27 14:50:15 -0700742 {
Vernon Mauery15541322019-03-25 13:33:03 -0700743 b = std::stoul(v, &err, 16);
Emily Shafferedb8bb02018-09-27 14:50:15 -0700744 }
Vernon Mauery15541322019-03-25 13:33:03 -0700745 catch (std::exception& e)
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500746 {
Vernon Mauery15541322019-03-25 13:33:03 -0700747 elog<InvalidArgument>(Argument::ARGUMENT_NAME("rfc4122"),
748 Argument::ARGUMENT_VALUE(rfc4122.c_str()));
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500749 }
Vernon Mauery15541322019-03-25 13:33:03 -0700750 // check that exactly two ascii bytes were converted
751 if (err != 2)
752 {
753 elog<InvalidArgument>(Argument::ARGUMENT_NAME("rfc4122"),
754 Argument::ARGUMENT_VALUE(rfc4122.c_str()));
755 }
756 uuid[uuidBinaryLength - (ind / 2) - 1] = static_cast<uint8_t>(b);
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500757 }
Vernon Mauery15541322019-03-25 13:33:03 -0700758 return uuid;
759}
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500760
Vernon Mauery15541322019-03-25 13:33:03 -0700761auto ipmiAppGetDeviceGuid()
762 -> ipmi::RspType<std::array<uint8_t, uuidBinaryLength>>
763{
764 // return a fixed GUID based on /etc/machine-id
765 // This should match the /redfish/v1/Managers/bmc's UUID data
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500766
Vernon Mauery15541322019-03-25 13:33:03 -0700767 // machine specific application ID (for BMC ID)
768 // generated by systemd-id128 -p new as per man page
769 static constexpr sd_id128_t bmcUuidAppId = SD_ID128_MAKE(
770 e0, e1, 73, 76, 64, 61, 47, da, a5, 0c, d0, cc, 64, 12, 45, 78);
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500771
Vernon Mauery15541322019-03-25 13:33:03 -0700772 sd_id128_t bmcUuid;
773 // create the UUID from /etc/machine-id via the systemd API
774 sd_id128_get_machine_app_specific(bmcUuidAppId, &bmcUuid);
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500775
Vernon Mauery15541322019-03-25 13:33:03 -0700776 char bmcUuidCstr[SD_ID128_STRING_MAX];
777 std::string systemUuid = sd_id128_to_string(bmcUuid, bmcUuidCstr);
778
779 std::array<uint8_t, uuidBinaryLength> uuid = rfc4122ToIpmi(systemUuid);
780 return ipmi::responseSuccess(uuid);
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500781}
Chris Austen6caf28b2015-10-13 12:40:40 -0500782
Vernon Mauerycc99ba42019-03-25 13:40:11 -0700783auto ipmiAppGetBtCapabilities()
784 -> ipmi::RspType<uint8_t, uint8_t, uint8_t, uint8_t, uint8_t>
vishwabmcba0bd5f2015-09-30 16:50:23 +0530785{
Adriana Kobylak88ad8152016-12-13 10:09:08 -0600786 // Per IPMI 2.0 spec, the input and output buffer size must be the max
787 // buffer size minus one byte to allocate space for the length byte.
Vernon Mauerycc99ba42019-03-25 13:40:11 -0700788 constexpr uint8_t nrOutstanding = 0x01;
789 constexpr uint8_t inputBufferSize = MAX_IPMI_BUFFER - 1;
790 constexpr uint8_t outputBufferSize = MAX_IPMI_BUFFER - 1;
791 constexpr uint8_t transactionTime = 0x0A;
792 constexpr uint8_t nrRetries = 0x01;
vishwabmcba0bd5f2015-09-30 16:50:23 +0530793
Vernon Mauerycc99ba42019-03-25 13:40:11 -0700794 return ipmi::responseSuccess(nrOutstanding, inputBufferSize,
795 outputBufferSize, transactionTime, nrRetries);
vishwabmcba0bd5f2015-09-30 16:50:23 +0530796}
797
Vernon Maueryb90a5322019-03-25 13:36:55 -0700798auto ipmiAppGetSystemGuid() -> ipmi::RspType<std::array<uint8_t, 16>>
Marri Devender Rao5e007a52018-01-08 06:18:36 -0600799{
Vernon Maueryb90a5322019-03-25 13:36:55 -0700800 static constexpr auto bmcInterface =
801 "xyz.openbmc_project.Inventory.Item.Bmc";
802 static constexpr auto uuidInterface = "xyz.openbmc_project.Common.UUID";
803 static constexpr auto uuidProperty = "UUID";
Marri Devender Rao5e007a52018-01-08 06:18:36 -0600804
Vernon Maueryb90a5322019-03-25 13:36:55 -0700805 ipmi::Value propValue;
Marri Devender Rao5e007a52018-01-08 06:18:36 -0600806 try
807 {
808 // Get the Inventory object implementing BMC interface
Vernon Maueryb90a5322019-03-25 13:36:55 -0700809 auto busPtr = getSdBus();
810 auto objectInfo = ipmi::getDbusObject(*busPtr, bmcInterface);
Marri Devender Rao5e007a52018-01-08 06:18:36 -0600811
812 // Read UUID property value from bmcObject
813 // UUID is in RFC4122 format Ex: 61a39523-78f2-11e5-9862-e6402cfc3223
Vernon Maueryb90a5322019-03-25 13:36:55 -0700814 propValue =
815 ipmi::getDbusProperty(*busPtr, objectInfo.second, objectInfo.first,
816 uuidInterface, uuidProperty);
Marri Devender Rao5e007a52018-01-08 06:18:36 -0600817 }
818 catch (const InternalFailure& e)
819 {
820 log<level::ERR>("Failed in reading BMC UUID property",
Vernon Maueryb90a5322019-03-25 13:36:55 -0700821 entry("INTERFACE=%s", uuidInterface),
822 entry("PROPERTY=%s", uuidProperty));
823 return ipmi::responseUnspecifiedError();
Marri Devender Rao5e007a52018-01-08 06:18:36 -0600824 }
Vernon Maueryb90a5322019-03-25 13:36:55 -0700825 std::array<uint8_t, 16> uuid;
826 std::string rfc4122Uuid = std::get<std::string>(propValue);
827 try
828 {
829 // convert to IPMI format
830 uuid = rfc4122ToIpmi(rfc4122Uuid);
831 }
832 catch (const InvalidArgument& e)
833 {
834 log<level::ERR>("Failed in parsing BMC UUID property",
835 entry("INTERFACE=%s", uuidInterface),
836 entry("PROPERTY=%s", uuidProperty),
837 entry("VALUE=%s", rfc4122Uuid.c_str()));
838 return ipmi::responseUnspecifiedError();
839 }
840 return ipmi::responseSuccess(uuid);
Marri Devender Rao5e007a52018-01-08 06:18:36 -0600841}
842
Xo Wangf542e8b2017-08-09 15:34:16 -0700843static std::unique_ptr<SysInfoParamStore> sysInfoParamStore;
844
Xo Wang87651332017-08-11 10:17:59 -0700845static std::string sysInfoReadSystemName()
846{
847 // Use the BMC hostname as the "System Name."
848 char hostname[HOST_NAME_MAX + 1] = {};
849 if (gethostname(hostname, HOST_NAME_MAX) != 0)
850 {
851 perror("System info parameter: system name");
852 }
853 return hostname;
854}
855
Xo Wangf542e8b2017-08-09 15:34:16 -0700856struct IpmiSysInfoResp
857{
858 uint8_t paramRevision;
859 uint8_t setSelector;
860 union
861 {
862 struct
863 {
864 uint8_t encoding;
865 uint8_t stringLen;
866 uint8_t stringData0[14];
867 } __attribute__((packed));
868 uint8_t stringDataN[16];
869 uint8_t byteData;
870 };
871} __attribute__((packed));
872
873/**
874 * Split a string into (up to) 16-byte chunks as expected in response for get
875 * system info parameter.
876 *
877 * @param[in] fullString: Input string to be split
878 * @param[in] chunkIndex: Index of the chunk to be written out
879 * @param[in,out] chunk: Output data buffer; must have 14 byte capacity if
880 * chunk_index = 0 and 16-byte capacity otherwise
881 * @return the number of bytes written into the output buffer, or -EINVAL for
882 * invalid arguments.
883 */
884static int splitStringParam(const std::string& fullString, int chunkIndex,
885 uint8_t* chunk)
886{
887 constexpr int maxChunk = 255;
888 constexpr int smallChunk = 14;
889 constexpr int chunkSize = 16;
890 if (chunkIndex > maxChunk || chunk == nullptr)
891 {
892 return -EINVAL;
893 }
894 try
895 {
896 std::string output;
897 if (chunkIndex == 0)
898 {
899 // Output must have 14 byte capacity.
900 output = fullString.substr(0, smallChunk);
901 }
902 else
903 {
904 // Output must have 16 byte capacity.
905 output = fullString.substr((chunkIndex * chunkSize) - 2, chunkSize);
906 }
907
908 std::memcpy(chunk, output.c_str(), output.length());
909 return output.length();
910 }
911 catch (const std::out_of_range& e)
912 {
913 // The position was beyond the end.
914 return -EINVAL;
915 }
916}
917
918/**
919 * Packs the Get Sys Info Request Item into the response.
920 *
921 * @param[in] paramString - the parameter.
922 * @param[in] setSelector - the selector
923 * @param[in,out] resp - the System info response.
924 * @return The number of bytes packed or failure from splitStringParam().
925 */
926static int packGetSysInfoResp(const std::string& paramString,
927 uint8_t setSelector, IpmiSysInfoResp* resp)
928{
929 uint8_t* dataBuffer = resp->stringDataN;
930 resp->setSelector = setSelector;
931 if (resp->setSelector == 0) // First chunk has only 14 bytes.
932 {
933 resp->encoding = 0;
934 resp->stringLen = paramString.length();
935 dataBuffer = resp->stringData0;
936 }
937 return splitStringParam(paramString, resp->setSelector, dataBuffer);
938}
939
940ipmi_ret_t ipmi_app_get_system_info(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
941 ipmi_request_t request,
942 ipmi_response_t response,
943 ipmi_data_len_t dataLen,
944 ipmi_context_t context)
945{
946 IpmiSysInfoResp resp = {};
947 size_t respLen = 0;
948 uint8_t* const reqData = static_cast<uint8_t*>(request);
949 std::string paramString;
950 bool found;
951 std::tuple<bool, std::string> ret;
952 constexpr int minRequestSize = 4;
953 constexpr int paramSelector = 1;
954 constexpr uint8_t revisionOnly = 0x80;
955 const uint8_t paramRequested = reqData[paramSelector];
956 int rc;
957
958 if (*dataLen < minRequestSize)
959 {
960 return IPMI_CC_REQ_DATA_LEN_INVALID;
961 }
962
963 *dataLen = 0; // default to 0.
964
965 // Parameters revision as of IPMI spec v2.0 rev. 1.1 (Feb 11, 2014 E6)
966 resp.paramRevision = 0x11;
967 if (reqData[0] & revisionOnly) // Get parameter revision only
968 {
969 respLen = 1;
970 goto writeResponse;
971 }
972
973 // The "Set In Progress" parameter can be used for rollback of parameter
974 // data and is not implemented.
975 if (paramRequested == 0)
976 {
977 resp.byteData = 0;
978 respLen = 2;
979 goto writeResponse;
980 }
981
982 if (sysInfoParamStore == nullptr)
983 {
984 sysInfoParamStore = std::make_unique<SysInfoParamStore>();
Xo Wang87651332017-08-11 10:17:59 -0700985 sysInfoParamStore->update(IPMI_SYSINFO_SYSTEM_NAME,
986 sysInfoReadSystemName);
Xo Wangf542e8b2017-08-09 15:34:16 -0700987 }
988
989 // Parameters other than Set In Progress are assumed to be strings.
990 ret = sysInfoParamStore->lookup(paramRequested);
991 found = std::get<0>(ret);
992 paramString = std::get<1>(ret);
993 if (!found)
994 {
995 return IPMI_CC_SYSTEM_INFO_PARAMETER_NOT_SUPPORTED;
996 }
997 // TODO: Cache each parameter across multiple calls, until the whole string
998 // has been read out. Otherwise, it's possible for a parameter to change
999 // between requests for its chunks, returning chunks incoherent with each
1000 // other. For now, the parameter store is simply required to have only
1001 // idempotent callbacks.
1002 rc = packGetSysInfoResp(paramString, reqData[2], &resp);
1003 if (rc == -EINVAL)
1004 {
1005 return IPMI_CC_RESPONSE_ERROR;
1006 }
1007
1008 respLen = sizeof(resp); // Write entire string data chunk in response.
1009
1010writeResponse:
1011 std::memcpy(response, &resp, sizeof(resp));
1012 *dataLen = respLen;
1013 return IPMI_CC_OK;
1014}
1015
Richard Marian Thomaiyar84bf9be2019-04-26 22:59:16 +05301016inline std::vector<uint8_t> convertStringToData(const std::string& command)
1017{
1018 std::istringstream iss(command);
1019 std::string token;
1020 std::vector<uint8_t> dataValue;
1021 while (std::getline(iss, token, ' '))
1022 {
1023 dataValue.emplace_back(
1024 static_cast<uint8_t>(std::stoul(token, nullptr, base_16)));
1025 }
1026 return dataValue;
1027}
1028
1029static bool populateI2CMasterWRWhitelist()
1030{
1031 nlohmann::json data = nullptr;
1032 std::ifstream jsonFile(i2cMasterWRWhitelistFile);
1033
1034 if (!jsonFile.good())
1035 {
1036 log<level::WARNING>("i2c white list file not found!",
1037 entry("FILE_NAME: %s", i2cMasterWRWhitelistFile));
1038 return false;
1039 }
1040
1041 try
1042 {
1043 data = nlohmann::json::parse(jsonFile, nullptr, false);
1044 }
1045 catch (nlohmann::json::parse_error& e)
1046 {
1047 log<level::ERR>("Corrupted i2c white list config file",
1048 entry("FILE_NAME: %s", i2cMasterWRWhitelistFile),
1049 entry("MSG: %s", e.what()));
1050 return false;
1051 }
1052
1053 try
1054 {
1055 // Example JSON Structure format
1056 // "filters": [
1057 // {
1058 // "Description": "Allow full read - ignore first byte write value
1059 // for 0x40 to 0x4F",
1060 // "busId": "0x01",
1061 // "slaveAddr": "0x40",
1062 // "slaveAddrMask": "0x0F",
1063 // "command": "0x00",
1064 // "commandMask": "0xFF"
1065 // },
1066 // {
1067 // "Description": "Allow full read - first byte match 0x05 and
1068 // ignore second byte",
1069 // "busId": "0x01",
1070 // "slaveAddr": "0x57",
1071 // "slaveAddrMask": "0x00",
1072 // "command": "0x05 0x00",
1073 // "commandMask": "0x00 0xFF"
1074 // },]
1075
1076 nlohmann::json filters = data[filtersStr].get<nlohmann::json>();
1077 std::vector<i2cMasterWRWhitelist>& whitelist = getWRWhitelist();
1078 for (const auto& it : filters.items())
1079 {
1080 nlohmann::json filter = it.value();
1081 if (filter.is_null())
1082 {
1083 log<level::ERR>(
1084 "Corrupted I2C master write read whitelist config file",
1085 entry("FILE_NAME: %s", i2cMasterWRWhitelistFile));
1086 return false;
1087 }
1088 const std::vector<uint8_t>& writeData =
1089 convertStringToData(filter[cmdStr].get<std::string>());
1090 const std::vector<uint8_t>& writeDataMask =
1091 convertStringToData(filter[cmdMaskStr].get<std::string>());
1092 if (writeDataMask.size() != writeData.size())
1093 {
1094 log<level::ERR>("I2C master write read whitelist filter "
1095 "mismatch for command & mask size");
1096 return false;
1097 }
1098 whitelist.push_back(
1099 {static_cast<uint8_t>(std::stoul(
1100 filter[busIdStr].get<std::string>(), nullptr, base_16)),
1101 static_cast<uint8_t>(
1102 std::stoul(filter[slaveAddrStr].get<std::string>(),
1103 nullptr, base_16)),
1104 static_cast<uint8_t>(
1105 std::stoul(filter[slaveAddrMaskStr].get<std::string>(),
1106 nullptr, base_16)),
1107 writeData, writeDataMask});
1108 }
1109 if (whitelist.size() != filters.size())
1110 {
1111 log<level::ERR>(
1112 "I2C master write read whitelist filter size mismatch");
1113 return false;
1114 }
1115 }
1116 catch (std::exception& e)
1117 {
1118 log<level::ERR>("I2C master write read whitelist unexpected exception",
1119 entry("ERROR=%s", e.what()));
1120 return false;
1121 }
1122 return true;
1123}
1124
1125static inline bool isWriteDataWhitelisted(const std::vector<uint8_t>& data,
1126 const std::vector<uint8_t>& dataMask,
1127 const std::vector<uint8_t>& writeData)
1128{
1129 std::vector<uint8_t> processedDataBuf(data.size());
1130 std::vector<uint8_t> processedReqBuf(dataMask.size());
1131 std::transform(writeData.begin(), writeData.end(), dataMask.begin(),
1132 processedReqBuf.begin(), std::bit_or<uint8_t>());
1133 std::transform(data.begin(), data.end(), dataMask.begin(),
1134 processedDataBuf.begin(), std::bit_or<uint8_t>());
1135
1136 return (processedDataBuf == processedReqBuf);
1137}
1138
1139static bool isCmdWhitelisted(uint8_t busId, uint8_t slaveAddr,
1140 std::vector<uint8_t>& writeData)
1141{
1142 std::vector<i2cMasterWRWhitelist>& whiteList = getWRWhitelist();
1143 for (const auto& wlEntry : whiteList)
1144 {
1145 if ((busId == wlEntry.busId) &&
1146 ((slaveAddr | wlEntry.slaveAddrMask) ==
1147 (wlEntry.slaveAddr | wlEntry.slaveAddrMask)))
1148 {
1149 const std::vector<uint8_t>& dataMask = wlEntry.dataMask;
1150 // Skip as no-match, if requested write data is more than the
1151 // write data mask size
1152 if (writeData.size() > dataMask.size())
1153 {
1154 continue;
1155 }
1156 if (isWriteDataWhitelisted(wlEntry.data, dataMask, writeData))
1157 {
1158 return true;
1159 }
1160 }
1161 }
1162 return false;
1163}
1164
1165/** @brief implements master write read IPMI command which can be used for
1166 * low-level I2C/SMBus write, read or write-read access
1167 * @param isPrivateBus -to indicate private bus usage
1168 * @param busId - bus id
1169 * @param channelNum - channel number
1170 * @param reserved - skip 1 bit
1171 * @param slaveAddr - slave address
1172 * @param read count - number of bytes to be read
1173 * @param writeData - data to be written
1174 *
1175 * @returns IPMI completion code plus response data
1176 * - readData - i2c response data
1177 */
1178ipmi::RspType<std::vector<uint8_t>>
1179 ipmiMasterWriteRead(bool isPrivateBus, uint3_t busId, uint4_t channelNum,
1180 bool reserved, uint7_t slaveAddr, uint8_t readCount,
1181 std::vector<uint8_t> writeData)
1182{
1183 i2c_rdwr_ioctl_data msgReadWrite = {0};
1184 i2c_msg i2cmsg[2] = {0};
1185
1186 if (readCount > maxIPMIWriteReadSize)
1187 {
1188 log<level::ERR>("Master write read command: Read count exceeds limit");
1189 return ipmi::responseParmOutOfRange();
1190 }
1191 const size_t writeCount = writeData.size();
1192 if (!readCount && !writeCount)
1193 {
1194 log<level::ERR>("Master write read command: Read & write count are 0");
1195 return ipmi::responseInvalidFieldRequest();
1196 }
1197 if (!isCmdWhitelisted(static_cast<uint8_t>(busId),
1198 static_cast<uint8_t>(slaveAddr), writeData))
1199 {
1200 log<level::ERR>("Master write read request blocked!",
1201 entry("BUS=%d", static_cast<uint8_t>(busId)),
1202 entry("ADDR=0x%x", static_cast<uint8_t>(slaveAddr)));
1203 return ipmi::responseInvalidFieldRequest();
1204 }
1205 std::vector<uint8_t> readBuf(readCount);
1206 std::string i2cBus =
1207 "/dev/i2c-" + std::to_string(static_cast<uint8_t>(busId));
1208
1209 int i2cDev = ::open(i2cBus.c_str(), O_RDWR | O_CLOEXEC);
1210 if (i2cDev < 0)
1211 {
1212 log<level::ERR>("Failed to open i2c bus",
1213 entry("BUS=%s", i2cBus.c_str()));
1214 return ipmi::responseInvalidFieldRequest();
1215 }
1216
1217 int msgCount = 0;
1218 if (writeCount)
1219 {
1220 i2cmsg[msgCount].addr = static_cast<uint8_t>(slaveAddr);
1221 i2cmsg[msgCount].flags = 0x00;
1222 i2cmsg[msgCount].len = writeCount;
1223 i2cmsg[msgCount].buf = writeData.data();
1224 msgCount++;
1225 }
1226 if (readCount)
1227 {
1228 i2cmsg[msgCount].addr = static_cast<uint8_t>(slaveAddr);
1229 i2cmsg[msgCount].flags = I2C_M_RD;
1230 i2cmsg[msgCount].len = readCount;
1231 i2cmsg[msgCount].buf = readBuf.data();
1232 msgCount++;
1233 }
1234
1235 msgReadWrite.msgs = i2cmsg;
1236 msgReadWrite.nmsgs = msgCount;
1237
1238 int ret = ::ioctl(i2cDev, I2C_RDWR, &msgReadWrite);
1239 ::close(i2cDev);
1240
1241 if (ret < 0)
1242 {
1243 log<level::ERR>("Master write read: Failed", entry("RET=%d", ret));
1244 return ipmi::responseUnspecifiedError();
1245 }
1246 if (readCount)
1247 {
1248 readBuf.resize(msgReadWrite.msgs[msgCount - 1].len);
1249 }
1250 return ipmi::responseSuccess(readBuf);
1251}
1252
Chris Austen6caf28b2015-10-13 12:40:40 -05001253void register_netfn_app_functions()
vishwabmcba0bd5f2015-09-30 16:50:23 +05301254{
Vernon Mauery86a50822019-03-25 13:11:36 -07001255 // <Get Device ID>
1256 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp,
1257 ipmi::app::cmdGetDeviceId, ipmi::Privilege::User,
1258 ipmiAppGetDeviceId);
1259
Tom05732372016-09-06 17:21:23 +05301260 // <Get BT Interface Capabilities>
Vernon Mauerycc99ba42019-03-25 13:40:11 -07001261 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp,
1262 ipmi::app::cmdGetBtIfaceCapabilities,
1263 ipmi::Privilege::User, ipmiAppGetBtCapabilities);
Chris Austen6caf28b2015-10-13 12:40:40 -05001264
Tom05732372016-09-06 17:21:23 +05301265 // <Reset Watchdog Timer>
Vernon Mauery11df4f62019-03-25 14:17:54 -07001266 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp,
1267 ipmi::app::cmdResetWatchdogTimer,
1268 ipmi::Privilege::Operator, ipmiAppResetWatchdogTimer);
Chris Austen6caf28b2015-10-13 12:40:40 -05001269
Tom05732372016-09-06 17:21:23 +05301270 // <Set Watchdog Timer>
Patrick Venture0b02be92018-08-31 11:55:55 -07001271 ipmi_register_callback(NETFUN_APP, IPMI_CMD_SET_WD, NULL,
1272 ipmi_app_watchdog_set, PRIVILEGE_OPERATOR);
Chris Austen6caf28b2015-10-13 12:40:40 -05001273
William A. Kennington III73f44512018-02-09 15:28:46 -08001274 // <Get Watchdog Timer>
Patrick Venture0b02be92018-08-31 11:55:55 -07001275 ipmi_register_callback(NETFUN_APP, IPMI_CMD_GET_WD, NULL,
1276 ipmi_app_watchdog_get, PRIVILEGE_OPERATOR);
William A. Kennington III73f44512018-02-09 15:28:46 -08001277
Tom05732372016-09-06 17:21:23 +05301278 // <Get Self Test Results>
Vernon Maueryb84a5282019-03-25 13:39:03 -07001279 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp,
1280 ipmi::app::cmdGetSelfTestResults,
1281 ipmi::Privilege::User, ipmiAppGetSelfTestResults);
Nan Li41fa24a2016-11-10 20:12:37 +08001282
Tom05732372016-09-06 17:21:23 +05301283 // <Get Device GUID>
Vernon Mauery15541322019-03-25 13:33:03 -07001284 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp,
1285 ipmi::app::cmdGetDeviceGuid, ipmi::Privilege::User,
1286 ipmiAppGetDeviceGuid);
Adriana Kobylakd100ee52015-10-20 17:02:37 -05001287
Tom05732372016-09-06 17:21:23 +05301288 // <Set ACPI Power State>
Patrick Venture0b02be92018-08-31 11:55:55 -07001289 ipmi_register_callback(NETFUN_APP, IPMI_CMD_SET_ACPI, NULL,
1290 ipmi_app_set_acpi_power_state, PRIVILEGE_ADMIN);
Chris Austen6caf28b2015-10-13 12:40:40 -05001291
Yong Li18d77262018-10-09 01:59:45 +08001292 // <Get ACPI Power State>
Deepak Kumar Sahu4e6d2572019-05-07 19:53:37 +00001293 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp,
1294 ipmi::app::cmdGetAcpiPowerState,
1295 ipmi::Privilege::Admin, ipmiGetAcpiPowerState);
Yong Li18d77262018-10-09 01:59:45 +08001296
Richard Marian Thomaiyar84bf9be2019-04-26 22:59:16 +05301297 // Note: For security reason, this command will be registered only when
1298 // there are proper I2C Master write read whitelist
1299 if (populateI2CMasterWRWhitelist())
1300 {
1301 // Note: For security reasons, registering master write read as admin
1302 // privilege command, even though IPMI 2.0 specification allows it as
1303 // operator privilege.
1304 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp,
1305 ipmi::app::cmdMasterWriteRead,
1306 ipmi::Privilege::Admin, ipmiMasterWriteRead);
1307 }
1308
Marri Devender Rao5e007a52018-01-08 06:18:36 -06001309 // <Get System GUID Command>
Vernon Maueryb90a5322019-03-25 13:36:55 -07001310 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp,
1311 ipmi::app::cmdGetSystemGuid, ipmi::Privilege::User,
1312 ipmiAppGetSystemGuid);
Tom Joseph7cbe2282018-03-21 21:17:33 +05301313
1314 // <Get Channel Cipher Suites Command>
Patrick Venture0b02be92018-08-31 11:55:55 -07001315 ipmi_register_callback(NETFUN_APP, IPMI_CMD_GET_CHAN_CIPHER_SUITES, NULL,
1316 getChannelCipherSuites, PRIVILEGE_CALLBACK);
Vernon Maueryd2a57de2019-03-25 12:45:28 -07001317
Xo Wangf542e8b2017-08-09 15:34:16 -07001318 // <Get System Info Command>
1319 ipmi_register_callback(NETFUN_APP, IPMI_CMD_GET_SYSTEM_INFO, NULL,
1320 ipmi_app_get_system_info, PRIVILEGE_USER);
vishwabmcba0bd5f2015-09-30 16:50:23 +05301321 return;
1322}