blob: ea400609c6e13f47a9a294297143c3bfe3954f5c [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 -060045constexpr auto bmc_interface = "xyz.openbmc_project.Inventory.Item.Bmc";
46constexpr auto bmc_guid_interface = "xyz.openbmc_project.Common.UUID";
47constexpr auto bmc_guid_property = "UUID";
48constexpr auto bmc_guid_len = 16;
49
Nagaraju Goruganti744398d2018-02-20 09:52:00 -060050static constexpr auto redundancyIntf =
51 "xyz.openbmc_project.Software.RedundancyPriority";
Patrick Venture0b02be92018-08-31 11:55:55 -070052static constexpr auto versionIntf = "xyz.openbmc_project.Software.Version";
Nagaraju Goruganti744398d2018-02-20 09:52:00 -060053static constexpr auto activationIntf =
54 "xyz.openbmc_project.Software.Activation";
55static constexpr auto softwareRoot = "/xyz/openbmc_project/software";
56
Chris Austen6caf28b2015-10-13 12:40:40 -050057void register_netfn_app_functions() __attribute__((constructor));
vishwabmcba0bd5f2015-09-30 16:50:23 +053058
Ratan Guptab8e99552017-07-27 07:07:48 +053059using namespace phosphor::logging;
60using namespace sdbusplus::xyz::openbmc_project::Common::Error;
Nagaraju Goruganti744398d2018-02-20 09:52:00 -060061using Version = sdbusplus::xyz::openbmc_project::Software::server::Version;
62using Activation =
63 sdbusplus::xyz::openbmc_project::Software::server::Activation;
Alexander Amelkinba19c182018-09-04 15:49:36 +030064using BMC = sdbusplus::xyz::openbmc_project::State::server::BMC;
Vernon Mauery185b9f82018-07-20 10:52:36 -070065namespace fs = std::filesystem;
Ratan Guptab8e99552017-07-27 07:07:48 +053066
Richard Marian Thomaiyar84bf9be2019-04-26 22:59:16 +053067typedef struct
68{
69 uint8_t busId;
70 uint8_t slaveAddr;
71 uint8_t slaveAddrMask;
72 std::vector<uint8_t> data;
73 std::vector<uint8_t> dataMask;
74} i2cMasterWRWhitelist;
75
76static std::vector<i2cMasterWRWhitelist>& getWRWhitelist()
77{
78 static std::vector<i2cMasterWRWhitelist> wrWhitelist;
79 return wrWhitelist;
80}
81
82static constexpr const char* i2cMasterWRWhitelistFile =
83 "/usr/share/ipmi-providers/master_write_read_white_list.json";
84
85static constexpr uint8_t maxIPMIWriteReadSize = 144;
86static constexpr const char* filtersStr = "filters";
87static constexpr const char* busIdStr = "busId";
88static constexpr const char* slaveAddrStr = "slaveAddr";
89static constexpr const char* slaveAddrMaskStr = "slaveAddrMask";
90static constexpr const char* cmdStr = "command";
91static constexpr const char* cmdMaskStr = "commandMask";
92static constexpr int base_16 = 16;
93
Nagaraju Goruganti744398d2018-02-20 09:52:00 -060094/**
95 * @brief Returns the Version info from primary s/w object
96 *
97 * Get the Version info from the active s/w object which is having high
98 * "Priority" value(a smaller number is a higher priority) and "Purpose"
99 * is "BMC" from the list of all s/w objects those are implementing
100 * RedundancyPriority interface from the given softwareRoot path.
101 *
102 * @return On success returns the Version info from primary s/w object.
103 *
104 */
105std::string getActiveSoftwareVersionInfo()
106{
Vernon Mauery86a50822019-03-25 13:11:36 -0700107 auto busp = getSdBus();
Nagaraju Goruganti744398d2018-02-20 09:52:00 -0600108
109 std::string revision{};
Vernon Mauery86a50822019-03-25 13:11:36 -0700110 ipmi::ObjectTree objectTree;
111 try
Nagaraju Goruganti744398d2018-02-20 09:52:00 -0600112 {
Vernon Mauery86a50822019-03-25 13:11:36 -0700113 objectTree =
114 ipmi::getAllDbusObjects(*busp, softwareRoot, redundancyIntf);
115 }
116 catch (sdbusplus::exception::SdBusError& e)
117 {
118 log<level::ERR>("Failed to fetch redundancy object from dbus",
119 entry("INTERFACE=%s", redundancyIntf),
120 entry("ERRMSG=%s", e.what()));
Nagaraju Goruganti744398d2018-02-20 09:52:00 -0600121 elog<InternalFailure>();
122 }
123
124 auto objectFound = false;
125 for (auto& softObject : objectTree)
126 {
Vernon Mauery86a50822019-03-25 13:11:36 -0700127 auto service =
128 ipmi::getService(*busp, redundancyIntf, softObject.first);
129 auto objValueTree =
130 ipmi::getManagedObjects(*busp, service, softwareRoot);
Nagaraju Goruganti744398d2018-02-20 09:52:00 -0600131
132 auto minPriority = 0xFF;
133 for (const auto& objIter : objValueTree)
134 {
135 try
136 {
137 auto& intfMap = objIter.second;
138 auto& redundancyPriorityProps = intfMap.at(redundancyIntf);
139 auto& versionProps = intfMap.at(versionIntf);
140 auto& activationProps = intfMap.at(activationIntf);
Vernon Maueryf442e112019-04-09 11:44:36 -0700141 auto priority =
142 std::get<uint8_t>(redundancyPriorityProps.at("Priority"));
William A. Kennington III4c008022018-10-12 17:18:14 -0700143 auto purpose =
Vernon Maueryf442e112019-04-09 11:44:36 -0700144 std::get<std::string>(versionProps.at("Purpose"));
145 auto activation =
146 std::get<std::string>(activationProps.at("Activation"));
William A. Kennington III4c008022018-10-12 17:18:14 -0700147 auto version =
Vernon Maueryf442e112019-04-09 11:44:36 -0700148 std::get<std::string>(versionProps.at("Version"));
Nagaraju Goruganti744398d2018-02-20 09:52:00 -0600149 if ((Version::convertVersionPurposeFromString(purpose) ==
150 Version::VersionPurpose::BMC) &&
151 (Activation::convertActivationsFromString(activation) ==
152 Activation::Activations::Active))
153 {
154 if (priority < minPriority)
155 {
156 minPriority = priority;
157 objectFound = true;
158 revision = std::move(version);
159 }
160 }
161 }
162 catch (const std::exception& e)
163 {
164 log<level::ERR>(e.what());
165 }
166 }
167 }
168
169 if (!objectFound)
170 {
171 log<level::ERR>("Could not found an BMC software Object");
172 elog<InternalFailure>();
173 }
174
175 return revision;
176}
177
Alexander Amelkinba19c182018-09-04 15:49:36 +0300178bool getCurrentBmcState()
179{
180 sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()};
181
182 // Get the Inventory object implementing the BMC interface
183 ipmi::DbusObjectInfo bmcObject =
184 ipmi::getDbusObject(bus, bmc_state_interface);
185 auto variant =
186 ipmi::getDbusProperty(bus, bmcObject.second, bmcObject.first,
187 bmc_state_interface, bmc_state_property);
188
Vernon Maueryf442e112019-04-09 11:44:36 -0700189 return std::holds_alternative<std::string>(variant) &&
190 BMC::convertBMCStateFromString(std::get<std::string>(variant)) ==
191 BMC::BMCState::Ready;
Alexander Amelkinba19c182018-09-04 15:49:36 +0300192}
193
Patrick Venture94930a12019-04-30 10:01:58 -0700194bool getCurrentBmcStateWithFallback(const bool fallbackAvailability)
195{
196 try
197 {
198 return getCurrentBmcState();
199 }
200 catch (...)
201 {
202 // Nothing provided the BMC interface, therefore return whatever was
203 // configured as the default.
204 return fallbackAvailability;
205 }
206}
207
Yong Li18d77262018-10-09 01:59:45 +0800208namespace acpi_state
209{
210using namespace sdbusplus::xyz::openbmc_project::Control::Power::server;
211
212const static constexpr char* acpiObjPath =
213 "/xyz/openbmc_project/control/host0/acpi_power_state";
214const static constexpr char* acpiInterface =
215 "xyz.openbmc_project.Control.Power.ACPIPowerState";
216const static constexpr char* sysACPIProp = "SysACPIStatus";
217const static constexpr char* devACPIProp = "DevACPIStatus";
218
219enum class PowerStateType : uint8_t
220{
221 sysPowerState = 0x00,
222 devPowerState = 0x01,
223};
224
225// Defined in 20.6 of ipmi doc
226enum class PowerState : uint8_t
227{
228 s0G0D0 = 0x00,
229 s1D1 = 0x01,
230 s2D2 = 0x02,
231 s3D3 = 0x03,
232 s4 = 0x04,
233 s5G2 = 0x05,
234 s4S5 = 0x06,
235 g3 = 0x07,
236 sleep = 0x08,
237 g1Sleep = 0x09,
238 override = 0x0a,
239 legacyOn = 0x20,
240 legacyOff = 0x21,
241 unknown = 0x2a,
242 noChange = 0x7f,
243};
244
245static constexpr uint8_t stateChanged = 0x80;
246
247struct ACPIState
248{
249 uint8_t sysACPIState;
250 uint8_t devACPIState;
251} __attribute__((packed));
252
253std::map<ACPIPowerState::ACPI, PowerState> dbusToIPMI = {
254 {ACPIPowerState::ACPI::S0_G0_D0, PowerState::s0G0D0},
255 {ACPIPowerState::ACPI::S1_D1, PowerState::s1D1},
256 {ACPIPowerState::ACPI::S2_D2, PowerState::s2D2},
257 {ACPIPowerState::ACPI::S3_D3, PowerState::s3D3},
258 {ACPIPowerState::ACPI::S4, PowerState::s4},
259 {ACPIPowerState::ACPI::S5_G2, PowerState::s5G2},
260 {ACPIPowerState::ACPI::S4_S5, PowerState::s4S5},
261 {ACPIPowerState::ACPI::G3, PowerState::g3},
262 {ACPIPowerState::ACPI::SLEEP, PowerState::sleep},
263 {ACPIPowerState::ACPI::G1_SLEEP, PowerState::g1Sleep},
264 {ACPIPowerState::ACPI::OVERRIDE, PowerState::override},
265 {ACPIPowerState::ACPI::LEGACY_ON, PowerState::legacyOn},
266 {ACPIPowerState::ACPI::LEGACY_OFF, PowerState::legacyOff},
267 {ACPIPowerState::ACPI::Unknown, PowerState::unknown}};
268
269bool isValidACPIState(acpi_state::PowerStateType type, uint8_t state)
270{
271 if (type == acpi_state::PowerStateType::sysPowerState)
272 {
273 if ((state <= static_cast<uint8_t>(acpi_state::PowerState::override)) ||
274 (state == static_cast<uint8_t>(acpi_state::PowerState::legacyOn)) ||
275 (state ==
276 static_cast<uint8_t>(acpi_state::PowerState::legacyOff)) ||
277 (state == static_cast<uint8_t>(acpi_state::PowerState::unknown)) ||
278 (state == static_cast<uint8_t>(acpi_state::PowerState::noChange)))
279 {
280 return true;
281 }
282 else
283 {
284 return false;
285 }
286 }
287 else if (type == acpi_state::PowerStateType::devPowerState)
288 {
289 if ((state <= static_cast<uint8_t>(acpi_state::PowerState::s3D3)) ||
290 (state == static_cast<uint8_t>(acpi_state::PowerState::unknown)) ||
291 (state == static_cast<uint8_t>(acpi_state::PowerState::noChange)))
292 {
293 return true;
294 }
295 else
296 {
297 return false;
298 }
299 }
300 else
301 {
302 return false;
303 }
304 return false;
305}
306} // namespace acpi_state
307
Adriana Kobylak3a552e12015-10-19 16:11:00 -0500308ipmi_ret_t ipmi_app_set_acpi_power_state(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
Patrick Venture0b02be92018-08-31 11:55:55 -0700309 ipmi_request_t request,
310 ipmi_response_t response,
311 ipmi_data_len_t data_len,
312 ipmi_context_t context)
Chris Austen6caf28b2015-10-13 12:40:40 -0500313{
Yong Li18d77262018-10-09 01:59:45 +0800314 auto s = static_cast<uint8_t>(acpi_state::PowerState::unknown);
Chris Austen6caf28b2015-10-13 12:40:40 -0500315 ipmi_ret_t rc = IPMI_CC_OK;
Yong Li18d77262018-10-09 01:59:45 +0800316
317 sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()};
318
319 auto value = acpi_state::ACPIPowerState::ACPI::Unknown;
320
321 auto* req = reinterpret_cast<acpi_state::ACPIState*>(request);
322
323 if (*data_len != sizeof(acpi_state::ACPIState))
324 {
325 log<level::ERR>("set_acpi invalid len");
326 *data_len = 0;
327 return IPMI_CC_REQ_DATA_LEN_INVALID;
328 }
329
Chris Austen6caf28b2015-10-13 12:40:40 -0500330 *data_len = 0;
331
Yong Li18d77262018-10-09 01:59:45 +0800332 if (req->sysACPIState & acpi_state::stateChanged)
333 {
334 // set system power state
335 s = req->sysACPIState & ~acpi_state::stateChanged;
336
337 if (!acpi_state::isValidACPIState(
338 acpi_state::PowerStateType::sysPowerState, s))
339 {
340 log<level::ERR>("set_acpi_power sys invalid input",
341 entry("S=%x", s));
342 return IPMI_CC_PARM_OUT_OF_RANGE;
343 }
344
345 // valid input
346 if (s == static_cast<uint8_t>(acpi_state::PowerState::noChange))
347 {
348 log<level::DEBUG>("No change for system power state");
349 }
350 else
351 {
352 auto found = std::find_if(
353 acpi_state::dbusToIPMI.begin(), acpi_state::dbusToIPMI.end(),
354 [&s](const auto& iter) {
355 return (static_cast<uint8_t>(iter.second) == s);
356 });
357
358 value = found->first;
359
360 try
361 {
362 auto acpiObject =
363 ipmi::getDbusObject(bus, acpi_state::acpiInterface);
364 ipmi::setDbusProperty(bus, acpiObject.second, acpiObject.first,
365 acpi_state::acpiInterface,
366 acpi_state::sysACPIProp,
367 convertForMessage(value));
368 }
369 catch (const InternalFailure& e)
370 {
371 log<level::ERR>("Failed in set ACPI system property",
372 entry("EXCEPTION=%s", e.what()));
373 return IPMI_CC_UNSPECIFIED_ERROR;
374 }
375 }
376 }
377 else
378 {
379 log<level::DEBUG>("Do not change system power state");
380 }
381
382 if (req->devACPIState & acpi_state::stateChanged)
383 {
384 // set device power state
385 s = req->devACPIState & ~acpi_state::stateChanged;
386 if (!acpi_state::isValidACPIState(
387 acpi_state::PowerStateType::devPowerState, s))
388 {
389 log<level::ERR>("set_acpi_power dev invalid input",
390 entry("S=%x", s));
391 return IPMI_CC_PARM_OUT_OF_RANGE;
392 }
393
394 // valid input
395 if (s == static_cast<uint8_t>(acpi_state::PowerState::noChange))
396 {
397 log<level::DEBUG>("No change for device power state");
398 }
399 else
400 {
401 auto found = std::find_if(
402 acpi_state::dbusToIPMI.begin(), acpi_state::dbusToIPMI.end(),
403 [&s](const auto& iter) {
404 return (static_cast<uint8_t>(iter.second) == s);
405 });
406
407 value = found->first;
408
409 try
410 {
411 auto acpiObject =
412 ipmi::getDbusObject(bus, acpi_state::acpiInterface);
413 ipmi::setDbusProperty(bus, acpiObject.second, acpiObject.first,
414 acpi_state::acpiInterface,
415 acpi_state::devACPIProp,
416 convertForMessage(value));
417 }
418 catch (const InternalFailure& e)
419 {
420 log<level::ERR>("Failed in set ACPI device property",
421 entry("EXCEPTION=%s", e.what()));
422 return IPMI_CC_UNSPECIFIED_ERROR;
423 }
424 }
425 }
426 else
427 {
428 log<level::DEBUG>("Do not change device power state");
429 }
430
431 return rc;
432}
433
Deepak Kumar Sahu4e6d2572019-05-07 19:53:37 +0000434/**
435 * @brief implements the get ACPI power state command
436 *
437 * @return IPMI completion code plus response data on success.
438 * - ACPI system power state
439 * - ACPI device power state
440 **/
441ipmi::RspType<uint8_t, // acpiSystemPowerState
442 uint8_t // acpiDevicePowerState
443 >
444 ipmiGetAcpiPowerState()
Yong Li18d77262018-10-09 01:59:45 +0800445{
Deepak Kumar Sahu4e6d2572019-05-07 19:53:37 +0000446 uint8_t sysAcpiState;
447 uint8_t devAcpiState;
Yong Li18d77262018-10-09 01:59:45 +0800448
449 sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()};
450
Yong Li18d77262018-10-09 01:59:45 +0800451 try
452 {
453 auto acpiObject = ipmi::getDbusObject(bus, acpi_state::acpiInterface);
454
455 auto sysACPIVal = ipmi::getDbusProperty(
456 bus, acpiObject.second, acpiObject.first, acpi_state::acpiInterface,
457 acpi_state::sysACPIProp);
458 auto sysACPI = acpi_state::ACPIPowerState::convertACPIFromString(
Vernon Maueryf442e112019-04-09 11:44:36 -0700459 std::get<std::string>(sysACPIVal));
Deepak Kumar Sahu4e6d2572019-05-07 19:53:37 +0000460 sysAcpiState = static_cast<uint8_t>(acpi_state::dbusToIPMI.at(sysACPI));
Yong Li18d77262018-10-09 01:59:45 +0800461
462 auto devACPIVal = ipmi::getDbusProperty(
463 bus, acpiObject.second, acpiObject.first, acpi_state::acpiInterface,
464 acpi_state::devACPIProp);
465 auto devACPI = acpi_state::ACPIPowerState::convertACPIFromString(
Vernon Maueryf442e112019-04-09 11:44:36 -0700466 std::get<std::string>(devACPIVal));
Deepak Kumar Sahu4e6d2572019-05-07 19:53:37 +0000467 devAcpiState = static_cast<uint8_t>(acpi_state::dbusToIPMI.at(devACPI));
Yong Li18d77262018-10-09 01:59:45 +0800468 }
469 catch (const InternalFailure& e)
470 {
Deepak Kumar Sahu4e6d2572019-05-07 19:53:37 +0000471 return ipmi::responseUnspecifiedError();
Yong Li18d77262018-10-09 01:59:45 +0800472 }
Deepak Kumar Sahu4e6d2572019-05-07 19:53:37 +0000473
474 return ipmi::responseSuccess(sysAcpiState, devAcpiState);
Chris Austen6caf28b2015-10-13 12:40:40 -0500475}
476
Chris Austen7303bdc2016-04-17 11:50:54 -0500477typedef struct
478{
479 char major;
480 char minor;
Chris Austen176c9652016-04-30 16:32:17 -0500481 uint16_t d[2];
Vernon Mauery86a50822019-03-25 13:11:36 -0700482} Revision;
Chris Austen7303bdc2016-04-17 11:50:54 -0500483
Dinesh Chinari2b7e07d2017-11-08 15:38:50 -0600484/* Currently supports the vx.x-x-[-x] and v1.x.x-x-[-x] format. It will */
485/* return -1 if not in those formats, this routine knows how to parse */
Chris Austen7303bdc2016-04-17 11:50:54 -0500486/* version = v0.6-19-gf363f61-dirty */
487/* ^ ^ ^^ ^ */
488/* | | |----------|-- additional details */
489/* | |---------------- Minor */
490/* |------------------ Major */
Dinesh Chinari2b7e07d2017-11-08 15:38:50 -0600491/* and version = v1.99.10-113-g65edf7d-r3-0-g9e4f715 */
492/* ^ ^ ^^ ^ */
493/* | | |--|---------- additional details */
494/* | |---------------- Minor */
495/* |------------------ Major */
Chris Austen7303bdc2016-04-17 11:50:54 -0500496/* Additional details : If the option group exists it will force Auxiliary */
497/* Firmware Revision Information 4th byte to 1 indicating the build was */
498/* derived with additional edits */
Vernon Mauery86a50822019-03-25 13:11:36 -0700499int convertVersion(std::string s, Revision& rev)
Chris Austen7303bdc2016-04-17 11:50:54 -0500500{
Dinesh Chinari2b7e07d2017-11-08 15:38:50 -0600501 std::string token;
Chris Austen176c9652016-04-30 16:32:17 -0500502 uint16_t commits;
Chris Austen7303bdc2016-04-17 11:50:54 -0500503
Patrick Venture0b02be92018-08-31 11:55:55 -0700504 auto location = s.find_first_of('v');
Dinesh Chinari2b7e07d2017-11-08 15:38:50 -0600505 if (location != std::string::npos)
506 {
Patrick Venture0b02be92018-08-31 11:55:55 -0700507 s = s.substr(location + 1);
Chris Austen176c9652016-04-30 16:32:17 -0500508 }
Chris Austen7303bdc2016-04-17 11:50:54 -0500509
Dinesh Chinari2b7e07d2017-11-08 15:38:50 -0600510 if (!s.empty())
511 {
512 location = s.find_first_of(".");
513 if (location != std::string::npos)
514 {
Vernon Mauery86a50822019-03-25 13:11:36 -0700515 rev.major =
Patrick Venture0b02be92018-08-31 11:55:55 -0700516 static_cast<char>(std::stoi(s.substr(0, location), 0, 16));
517 token = s.substr(location + 1);
Dinesh Chinari2b7e07d2017-11-08 15:38:50 -0600518 }
Chris Austen176c9652016-04-30 16:32:17 -0500519
Dinesh Chinari2b7e07d2017-11-08 15:38:50 -0600520 if (!token.empty())
521 {
522 location = token.find_first_of(".-");
523 if (location != std::string::npos)
524 {
Vernon Mauery86a50822019-03-25 13:11:36 -0700525 rev.minor = static_cast<char>(
Patrick Venture0b02be92018-08-31 11:55:55 -0700526 std::stoi(token.substr(0, location), 0, 16));
527 token = token.substr(location + 1);
Dinesh Chinari2b7e07d2017-11-08 15:38:50 -0600528 }
529 }
Chris Austen7303bdc2016-04-17 11:50:54 -0500530
Dinesh Chinari2b7e07d2017-11-08 15:38:50 -0600531 // Capture the number of commits on top of the minor tag.
532 // I'm using BE format like the ipmi spec asked for
533 location = token.find_first_of(".-");
534 if (!token.empty())
535 {
536 commits = std::stoi(token.substr(0, location), 0, 16);
Vernon Mauery86a50822019-03-25 13:11:36 -0700537 rev.d[0] = (commits >> 8) | (commits << 8);
Dinesh Chinari2b7e07d2017-11-08 15:38:50 -0600538
539 // commit number we skip
540 location = token.find_first_of(".-");
541 if (location != std::string::npos)
542 {
Patrick Venture0b02be92018-08-31 11:55:55 -0700543 token = token.substr(location + 1);
Dinesh Chinari2b7e07d2017-11-08 15:38:50 -0600544 }
545 }
Patrick Venture0b02be92018-08-31 11:55:55 -0700546 else
547 {
Vernon Mauery86a50822019-03-25 13:11:36 -0700548 rev.d[0] = 0;
Dinesh Chinari2b7e07d2017-11-08 15:38:50 -0600549 }
550
551 if (location != std::string::npos)
552 {
Patrick Venture0b02be92018-08-31 11:55:55 -0700553 token = token.substr(location + 1);
Dinesh Chinari2b7e07d2017-11-08 15:38:50 -0600554 }
555
556 // Any value of the optional parameter forces it to 1
557 location = token.find_first_of(".-");
558 if (location != std::string::npos)
559 {
Patrick Venture0b02be92018-08-31 11:55:55 -0700560 token = token.substr(location + 1);
Dinesh Chinari2b7e07d2017-11-08 15:38:50 -0600561 }
562 commits = (!token.empty()) ? 1 : 0;
563
Patrick Venture0b02be92018-08-31 11:55:55 -0700564 // We do this operation to get this displayed in least significant bytes
565 // of ipmitool device id command.
Vernon Mauery86a50822019-03-25 13:11:36 -0700566 rev.d[1] = (commits >> 8) | (commits << 8);
Dinesh Chinari2b7e07d2017-11-08 15:38:50 -0600567 }
568
Chris Austen7303bdc2016-04-17 11:50:54 -0500569 return 0;
570}
571
Vernon Mauery86a50822019-03-25 13:11:36 -0700572auto ipmiAppGetDeviceId() -> ipmi::RspType<uint8_t, // Device ID
573 uint8_t, // Device Revision
574 uint8_t, // Firmware Revision Major
575 uint8_t, // Firmware Revision minor
576 uint8_t, // IPMI version
577 uint8_t, // Additional device support
578 uint24_t, // MFG ID
579 uint16_t, // Product ID
580 uint32_t // AUX info
581 >
Chris Austen6caf28b2015-10-13 12:40:40 -0500582{
Nagaraju Goruganti744398d2018-02-20 09:52:00 -0600583 int r = -1;
Vernon Mauery86a50822019-03-25 13:11:36 -0700584 Revision rev = {0};
585 static struct
586 {
587 uint8_t id;
588 uint8_t revision;
589 uint8_t fw[2];
590 uint8_t ipmiVer;
591 uint8_t addnDevSupport;
592 uint24_t manufId;
593 uint16_t prodId;
594 uint32_t aux;
595 } devId;
David Cobbleya1adb072017-11-21 15:58:13 -0800596 static bool dev_id_initialized = false;
Patrick Venture94930a12019-04-30 10:01:58 -0700597 static bool defaultActivationSetting = true;
David Cobbleya1adb072017-11-21 15:58:13 -0800598 const char* filename = "/usr/share/ipmi-providers/dev_id.json";
Alexander Amelkinba19c182018-09-04 15:49:36 +0300599 constexpr auto ipmiDevIdStateShift = 7;
600 constexpr auto ipmiDevIdFw1Mask = ~(1 << ipmiDevIdStateShift);
Chris Austen6caf28b2015-10-13 12:40:40 -0500601
David Cobbleya1adb072017-11-21 15:58:13 -0800602 if (!dev_id_initialized)
603 {
Nagaraju Goruganti744398d2018-02-20 09:52:00 -0600604 try
605 {
606 auto version = getActiveSoftwareVersionInfo();
Vernon Mauery86a50822019-03-25 13:11:36 -0700607 r = convertVersion(version, rev);
David Cobbleya1adb072017-11-21 15:58:13 -0800608 }
Nagaraju Goruganti744398d2018-02-20 09:52:00 -0600609 catch (const std::exception& e)
610 {
611 log<level::ERR>(e.what());
612 }
Nan Liee0cb902016-07-11 15:38:03 +0800613
Patrick Venture0b02be92018-08-31 11:55:55 -0700614 if (r >= 0)
615 {
Nagaraju Goruganti744398d2018-02-20 09:52:00 -0600616 // bit7 identifies if the device is available
617 // 0=normal operation
618 // 1=device firmware, SDR update,
619 // or self-initialization in progress.
Alexander Amelkinba19c182018-09-04 15:49:36 +0300620 // The availability may change in run time, so mask here
621 // and initialize later.
Vernon Mauery86a50822019-03-25 13:11:36 -0700622 devId.fw[0] = rev.major & ipmiDevIdFw1Mask;
Nagaraju Goruganti744398d2018-02-20 09:52:00 -0600623
624 rev.minor = (rev.minor > 99 ? 99 : rev.minor);
Vernon Mauery86a50822019-03-25 13:11:36 -0700625 devId.fw[1] = rev.minor % 10 + (rev.minor / 10) * 16;
626 std::memcpy(&devId.aux, rev.d, 4);
David Cobbleya1adb072017-11-21 15:58:13 -0800627 }
Nan Liee0cb902016-07-11 15:38:03 +0800628
David Cobbleya1adb072017-11-21 15:58:13 -0800629 // IPMI Spec version 2.0
Vernon Mauery86a50822019-03-25 13:11:36 -0700630 devId.ipmiVer = 2;
Adriana Kobylak0e912642016-06-22 16:54:39 -0500631
Vernon Mauery86a50822019-03-25 13:11:36 -0700632 std::ifstream devIdFile(filename);
633 if (devIdFile.is_open())
David Cobbleya1adb072017-11-21 15:58:13 -0800634 {
Vernon Mauery86a50822019-03-25 13:11:36 -0700635 auto data = nlohmann::json::parse(devIdFile, nullptr, false);
David Cobbleya1adb072017-11-21 15:58:13 -0800636 if (!data.is_discarded())
637 {
Vernon Mauery86a50822019-03-25 13:11:36 -0700638 devId.id = data.value("id", 0);
639 devId.revision = data.value("revision", 0);
640 devId.addnDevSupport = data.value("addn_dev_support", 0);
641 devId.manufId = data.value("manuf_id", 0);
642 devId.prodId = data.value("prod_id", 0);
643 devId.aux = data.value("aux", 0);
David Cobbleya1adb072017-11-21 15:58:13 -0800644
Patrick Venture94930a12019-04-30 10:01:58 -0700645 // Set the availablitity of the BMC.
646 defaultActivationSetting = data.value("availability", true);
647
Patrick Venture0b02be92018-08-31 11:55:55 -0700648 // Don't read the file every time if successful
David Cobbleya1adb072017-11-21 15:58:13 -0800649 dev_id_initialized = true;
650 }
651 else
652 {
653 log<level::ERR>("Device ID JSON parser failure");
Vernon Maueryf2587fc2019-04-09 14:28:15 -0700654 return ipmi::responseUnspecifiedError();
David Cobbleya1adb072017-11-21 15:58:13 -0800655 }
656 }
657 else
658 {
659 log<level::ERR>("Device ID file not found");
Vernon Maueryf2587fc2019-04-09 14:28:15 -0700660 return ipmi::responseUnspecifiedError();
Chris Austen7303bdc2016-04-17 11:50:54 -0500661 }
662 }
Chris Austen6caf28b2015-10-13 12:40:40 -0500663
Alexander Amelkinba19c182018-09-04 15:49:36 +0300664 // Set availability to the actual current BMC state
Vernon Mauery86a50822019-03-25 13:11:36 -0700665 devId.fw[0] &= ipmiDevIdFw1Mask;
Patrick Venture94930a12019-04-30 10:01:58 -0700666 if (!getCurrentBmcStateWithFallback(defaultActivationSetting))
Alexander Amelkinba19c182018-09-04 15:49:36 +0300667 {
Vernon Mauery86a50822019-03-25 13:11:36 -0700668 devId.fw[0] |= (1 << ipmiDevIdStateShift);
Alexander Amelkinba19c182018-09-04 15:49:36 +0300669 }
670
Vernon Mauery86a50822019-03-25 13:11:36 -0700671 return ipmi::responseSuccess(
672 devId.id, devId.revision, devId.fw[0], devId.fw[1], devId.ipmiVer,
673 devId.addnDevSupport, devId.manufId, devId.prodId, devId.aux);
Chris Austen6caf28b2015-10-13 12:40:40 -0500674}
675
Vernon Maueryb84a5282019-03-25 13:39:03 -0700676auto ipmiAppGetSelfTestResults() -> ipmi::RspType<uint8_t, uint8_t>
Nan Li41fa24a2016-11-10 20:12:37 +0800677{
Nan Li41fa24a2016-11-10 20:12:37 +0800678 // Byte 2:
679 // 55h - No error.
Gunnar Mills8991dd62017-10-25 17:11:29 -0500680 // 56h - Self Test function not implemented in this controller.
Nan Li41fa24a2016-11-10 20:12:37 +0800681 // 57h - Corrupted or inaccesssible data or devices.
682 // 58h - Fatal hardware error.
683 // FFh - reserved.
684 // all other: Device-specific 'internal failure'.
685 // Byte 3:
686 // For byte 2 = 55h, 56h, FFh: 00h
687 // For byte 2 = 58h, all other: Device-specific
688 // For byte 2 = 57h: self-test error bitfield.
689 // Note: returning 57h does not imply that all test were run.
690 // [7] 1b = Cannot access SEL device.
691 // [6] 1b = Cannot access SDR Repository.
692 // [5] 1b = Cannot access BMC FRU device.
693 // [4] 1b = IPMB signal lines do not respond.
694 // [3] 1b = SDR Repository empty.
695 // [2] 1b = Internal Use Area of BMC FRU corrupted.
696 // [1] 1b = controller update 'boot block' firmware corrupted.
697 // [0] 1b = controller operational firmware corrupted.
Vernon Maueryb84a5282019-03-25 13:39:03 -0700698 constexpr uint8_t notImplemented = 0x56;
699 constexpr uint8_t zero = 0;
700 return ipmi::responseSuccess(notImplemented, zero);
Nan Li41fa24a2016-11-10 20:12:37 +0800701}
702
Vernon Mauery15541322019-03-25 13:33:03 -0700703static constexpr size_t uuidBinaryLength = 16;
704static std::array<uint8_t, uuidBinaryLength> rfc4122ToIpmi(std::string rfc4122)
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500705{
Vernon Mauery15541322019-03-25 13:33:03 -0700706 using Argument = xyz::openbmc_project::Common::InvalidArgument;
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500707 // UUID is in RFC4122 format. Ex: 61a39523-78f2-11e5-9862-e6402cfc3223
Patrick Ventured2117022018-02-06 08:54:37 -0800708 // Per IPMI Spec 2.0 need to convert to 16 hex bytes and reverse the byte
709 // order
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500710 // Ex: 0x2332fc2c40e66298e511f2782395a361
Vernon Mauery15541322019-03-25 13:33:03 -0700711 constexpr size_t uuidHexLength = (2 * uuidBinaryLength);
712 constexpr size_t uuidRfc4122Length = (uuidHexLength + 4);
713 std::array<uint8_t, uuidBinaryLength> uuid;
714 if (rfc4122.size() == uuidRfc4122Length)
Patrick Venture0b02be92018-08-31 11:55:55 -0700715 {
Vernon Mauery15541322019-03-25 13:33:03 -0700716 rfc4122.erase(std::remove(rfc4122.begin(), rfc4122.end(), '-'),
717 rfc4122.end());
Sergey Solomineb9b8142016-08-23 09:07:28 -0500718 }
Vernon Mauery15541322019-03-25 13:33:03 -0700719 if (rfc4122.size() != uuidHexLength)
vishwa1eaea4f2016-02-26 11:57:40 -0600720 {
Vernon Mauery15541322019-03-25 13:33:03 -0700721 elog<InvalidArgument>(Argument::ARGUMENT_NAME("rfc4122"),
722 Argument::ARGUMENT_VALUE(rfc4122.c_str()));
vishwa1eaea4f2016-02-26 11:57:40 -0600723 }
Vernon Mauery15541322019-03-25 13:33:03 -0700724 for (size_t ind = 0; ind < uuidHexLength; ind += 2)
vishwa1eaea4f2016-02-26 11:57:40 -0600725 {
Vernon Mauery15541322019-03-25 13:33:03 -0700726 char v[3];
727 v[0] = rfc4122[ind];
728 v[1] = rfc4122[ind + 1];
729 v[2] = 0;
730 size_t err;
731 long b;
732 try
Emily Shafferedb8bb02018-09-27 14:50:15 -0700733 {
Vernon Mauery15541322019-03-25 13:33:03 -0700734 b = std::stoul(v, &err, 16);
Emily Shafferedb8bb02018-09-27 14:50:15 -0700735 }
Vernon Mauery15541322019-03-25 13:33:03 -0700736 catch (std::exception& e)
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500737 {
Vernon Mauery15541322019-03-25 13:33:03 -0700738 elog<InvalidArgument>(Argument::ARGUMENT_NAME("rfc4122"),
739 Argument::ARGUMENT_VALUE(rfc4122.c_str()));
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500740 }
Vernon Mauery15541322019-03-25 13:33:03 -0700741 // check that exactly two ascii bytes were converted
742 if (err != 2)
743 {
744 elog<InvalidArgument>(Argument::ARGUMENT_NAME("rfc4122"),
745 Argument::ARGUMENT_VALUE(rfc4122.c_str()));
746 }
747 uuid[uuidBinaryLength - (ind / 2) - 1] = static_cast<uint8_t>(b);
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500748 }
Vernon Mauery15541322019-03-25 13:33:03 -0700749 return uuid;
750}
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500751
Vernon Mauery15541322019-03-25 13:33:03 -0700752auto ipmiAppGetDeviceGuid()
753 -> ipmi::RspType<std::array<uint8_t, uuidBinaryLength>>
754{
755 // return a fixed GUID based on /etc/machine-id
756 // This should match the /redfish/v1/Managers/bmc's UUID data
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500757
Vernon Mauery15541322019-03-25 13:33:03 -0700758 // machine specific application ID (for BMC ID)
759 // generated by systemd-id128 -p new as per man page
760 static constexpr sd_id128_t bmcUuidAppId = SD_ID128_MAKE(
761 e0, e1, 73, 76, 64, 61, 47, da, a5, 0c, d0, cc, 64, 12, 45, 78);
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500762
Vernon Mauery15541322019-03-25 13:33:03 -0700763 sd_id128_t bmcUuid;
764 // create the UUID from /etc/machine-id via the systemd API
765 sd_id128_get_machine_app_specific(bmcUuidAppId, &bmcUuid);
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500766
Vernon Mauery15541322019-03-25 13:33:03 -0700767 char bmcUuidCstr[SD_ID128_STRING_MAX];
768 std::string systemUuid = sd_id128_to_string(bmcUuid, bmcUuidCstr);
769
770 std::array<uint8_t, uuidBinaryLength> uuid = rfc4122ToIpmi(systemUuid);
771 return ipmi::responseSuccess(uuid);
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500772}
Chris Austen6caf28b2015-10-13 12:40:40 -0500773
Vernon Mauerycc99ba42019-03-25 13:40:11 -0700774auto ipmiAppGetBtCapabilities()
775 -> ipmi::RspType<uint8_t, uint8_t, uint8_t, uint8_t, uint8_t>
vishwabmcba0bd5f2015-09-30 16:50:23 +0530776{
Adriana Kobylak88ad8152016-12-13 10:09:08 -0600777 // Per IPMI 2.0 spec, the input and output buffer size must be the max
778 // buffer size minus one byte to allocate space for the length byte.
Vernon Mauerycc99ba42019-03-25 13:40:11 -0700779 constexpr uint8_t nrOutstanding = 0x01;
780 constexpr uint8_t inputBufferSize = MAX_IPMI_BUFFER - 1;
781 constexpr uint8_t outputBufferSize = MAX_IPMI_BUFFER - 1;
782 constexpr uint8_t transactionTime = 0x0A;
783 constexpr uint8_t nrRetries = 0x01;
vishwabmcba0bd5f2015-09-30 16:50:23 +0530784
Vernon Mauerycc99ba42019-03-25 13:40:11 -0700785 return ipmi::responseSuccess(nrOutstanding, inputBufferSize,
786 outputBufferSize, transactionTime, nrRetries);
vishwabmcba0bd5f2015-09-30 16:50:23 +0530787}
788
Marri Devender Rao5e007a52018-01-08 06:18:36 -0600789ipmi_ret_t ipmi_app_get_sys_guid(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
Patrick Venture0b02be92018-08-31 11:55:55 -0700790 ipmi_request_t request,
791 ipmi_response_t response,
792 ipmi_data_len_t data_len,
793 ipmi_context_t context)
Marri Devender Rao5e007a52018-01-08 06:18:36 -0600794
795{
796 ipmi_ret_t rc = IPMI_CC_OK;
797 sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()};
798
799 try
800 {
801 // Get the Inventory object implementing BMC interface
802 ipmi::DbusObjectInfo bmcObject =
803 ipmi::getDbusObject(bus, bmc_interface);
804
805 // Read UUID property value from bmcObject
806 // UUID is in RFC4122 format Ex: 61a39523-78f2-11e5-9862-e6402cfc3223
Patrick Venture0b02be92018-08-31 11:55:55 -0700807 auto variant =
808 ipmi::getDbusProperty(bus, bmcObject.second, bmcObject.first,
809 bmc_guid_interface, bmc_guid_property);
Vernon Maueryf442e112019-04-09 11:44:36 -0700810 std::string guidProp = std::get<std::string>(variant);
Marri Devender Rao5e007a52018-01-08 06:18:36 -0600811
812 // Erase "-" characters from the property value
813 guidProp.erase(std::remove(guidProp.begin(), guidProp.end(), '-'),
Patrick Venture0b02be92018-08-31 11:55:55 -0700814 guidProp.end());
Marri Devender Rao5e007a52018-01-08 06:18:36 -0600815
816 auto guidPropLen = guidProp.length();
817 // Validate UUID data
818 // Divide by 2 as 1 byte is built from 2 chars
Patrick Venture0b02be92018-08-31 11:55:55 -0700819 if ((guidPropLen <= 0) || ((guidPropLen / 2) != bmc_guid_len))
Marri Devender Rao5e007a52018-01-08 06:18:36 -0600820
821 {
822 log<level::ERR>("Invalid UUID property value",
Patrick Venture0b02be92018-08-31 11:55:55 -0700823 entry("UUID_LENGTH=%d", guidPropLen));
Marri Devender Rao5e007a52018-01-08 06:18:36 -0600824 return IPMI_CC_RESPONSE_ERROR;
825 }
826
827 // Convert data in RFC4122(MSB) format to LSB format
828 // Get 2 characters at a time as 1 byte is built from 2 chars and
829 // convert to hex byte
830 // TODO: Data printed for GUID command is not as per the
831 // GUID format defined in IPMI specification 2.0 section 20.8
832 // Ticket raised: https://sourceforge.net/p/ipmitool/bugs/501/
833 uint8_t respGuid[bmc_guid_len];
834 for (size_t i = 0, respLoc = (bmc_guid_len - 1);
Patrick Venture0b02be92018-08-31 11:55:55 -0700835 i < guidPropLen && respLoc >= 0; i += 2, respLoc--)
Marri Devender Rao5e007a52018-01-08 06:18:36 -0600836 {
837 auto value = static_cast<uint8_t>(
Patrick Venture0b02be92018-08-31 11:55:55 -0700838 std::stoi(guidProp.substr(i, 2).c_str(), NULL, 16));
Marri Devender Rao5e007a52018-01-08 06:18:36 -0600839 respGuid[respLoc] = value;
840 }
841
842 *data_len = bmc_guid_len;
Patrick Ventureb51bf9c2018-09-10 15:53:14 -0700843 std::memcpy(response, &respGuid, bmc_guid_len);
Marri Devender Rao5e007a52018-01-08 06:18:36 -0600844 }
845 catch (const InternalFailure& e)
846 {
847 log<level::ERR>("Failed in reading BMC UUID property",
848 entry("INTERFACE=%s", bmc_interface),
849 entry("PROPERTY_INTERFACE=%s", bmc_guid_interface),
850 entry("PROPERTY=%s", bmc_guid_property));
851 return IPMI_CC_UNSPECIFIED_ERROR;
852 }
853 return rc;
854}
855
Xo Wangf542e8b2017-08-09 15:34:16 -0700856static std::unique_ptr<SysInfoParamStore> sysInfoParamStore;
857
Xo Wang87651332017-08-11 10:17:59 -0700858static std::string sysInfoReadSystemName()
859{
860 // Use the BMC hostname as the "System Name."
861 char hostname[HOST_NAME_MAX + 1] = {};
862 if (gethostname(hostname, HOST_NAME_MAX) != 0)
863 {
864 perror("System info parameter: system name");
865 }
866 return hostname;
867}
868
Xo Wangf542e8b2017-08-09 15:34:16 -0700869struct IpmiSysInfoResp
870{
871 uint8_t paramRevision;
872 uint8_t setSelector;
873 union
874 {
875 struct
876 {
877 uint8_t encoding;
878 uint8_t stringLen;
879 uint8_t stringData0[14];
880 } __attribute__((packed));
881 uint8_t stringDataN[16];
882 uint8_t byteData;
883 };
884} __attribute__((packed));
885
886/**
887 * Split a string into (up to) 16-byte chunks as expected in response for get
888 * system info parameter.
889 *
890 * @param[in] fullString: Input string to be split
891 * @param[in] chunkIndex: Index of the chunk to be written out
892 * @param[in,out] chunk: Output data buffer; must have 14 byte capacity if
893 * chunk_index = 0 and 16-byte capacity otherwise
894 * @return the number of bytes written into the output buffer, or -EINVAL for
895 * invalid arguments.
896 */
897static int splitStringParam(const std::string& fullString, int chunkIndex,
898 uint8_t* chunk)
899{
900 constexpr int maxChunk = 255;
901 constexpr int smallChunk = 14;
902 constexpr int chunkSize = 16;
903 if (chunkIndex > maxChunk || chunk == nullptr)
904 {
905 return -EINVAL;
906 }
907 try
908 {
909 std::string output;
910 if (chunkIndex == 0)
911 {
912 // Output must have 14 byte capacity.
913 output = fullString.substr(0, smallChunk);
914 }
915 else
916 {
917 // Output must have 16 byte capacity.
918 output = fullString.substr((chunkIndex * chunkSize) - 2, chunkSize);
919 }
920
921 std::memcpy(chunk, output.c_str(), output.length());
922 return output.length();
923 }
924 catch (const std::out_of_range& e)
925 {
926 // The position was beyond the end.
927 return -EINVAL;
928 }
929}
930
931/**
932 * Packs the Get Sys Info Request Item into the response.
933 *
934 * @param[in] paramString - the parameter.
935 * @param[in] setSelector - the selector
936 * @param[in,out] resp - the System info response.
937 * @return The number of bytes packed or failure from splitStringParam().
938 */
939static int packGetSysInfoResp(const std::string& paramString,
940 uint8_t setSelector, IpmiSysInfoResp* resp)
941{
942 uint8_t* dataBuffer = resp->stringDataN;
943 resp->setSelector = setSelector;
944 if (resp->setSelector == 0) // First chunk has only 14 bytes.
945 {
946 resp->encoding = 0;
947 resp->stringLen = paramString.length();
948 dataBuffer = resp->stringData0;
949 }
950 return splitStringParam(paramString, resp->setSelector, dataBuffer);
951}
952
953ipmi_ret_t ipmi_app_get_system_info(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
954 ipmi_request_t request,
955 ipmi_response_t response,
956 ipmi_data_len_t dataLen,
957 ipmi_context_t context)
958{
959 IpmiSysInfoResp resp = {};
960 size_t respLen = 0;
961 uint8_t* const reqData = static_cast<uint8_t*>(request);
962 std::string paramString;
963 bool found;
964 std::tuple<bool, std::string> ret;
965 constexpr int minRequestSize = 4;
966 constexpr int paramSelector = 1;
967 constexpr uint8_t revisionOnly = 0x80;
968 const uint8_t paramRequested = reqData[paramSelector];
969 int rc;
970
971 if (*dataLen < minRequestSize)
972 {
973 return IPMI_CC_REQ_DATA_LEN_INVALID;
974 }
975
976 *dataLen = 0; // default to 0.
977
978 // Parameters revision as of IPMI spec v2.0 rev. 1.1 (Feb 11, 2014 E6)
979 resp.paramRevision = 0x11;
980 if (reqData[0] & revisionOnly) // Get parameter revision only
981 {
982 respLen = 1;
983 goto writeResponse;
984 }
985
986 // The "Set In Progress" parameter can be used for rollback of parameter
987 // data and is not implemented.
988 if (paramRequested == 0)
989 {
990 resp.byteData = 0;
991 respLen = 2;
992 goto writeResponse;
993 }
994
995 if (sysInfoParamStore == nullptr)
996 {
997 sysInfoParamStore = std::make_unique<SysInfoParamStore>();
Xo Wang87651332017-08-11 10:17:59 -0700998 sysInfoParamStore->update(IPMI_SYSINFO_SYSTEM_NAME,
999 sysInfoReadSystemName);
Xo Wangf542e8b2017-08-09 15:34:16 -07001000 }
1001
1002 // Parameters other than Set In Progress are assumed to be strings.
1003 ret = sysInfoParamStore->lookup(paramRequested);
1004 found = std::get<0>(ret);
1005 paramString = std::get<1>(ret);
1006 if (!found)
1007 {
1008 return IPMI_CC_SYSTEM_INFO_PARAMETER_NOT_SUPPORTED;
1009 }
1010 // TODO: Cache each parameter across multiple calls, until the whole string
1011 // has been read out. Otherwise, it's possible for a parameter to change
1012 // between requests for its chunks, returning chunks incoherent with each
1013 // other. For now, the parameter store is simply required to have only
1014 // idempotent callbacks.
1015 rc = packGetSysInfoResp(paramString, reqData[2], &resp);
1016 if (rc == -EINVAL)
1017 {
1018 return IPMI_CC_RESPONSE_ERROR;
1019 }
1020
1021 respLen = sizeof(resp); // Write entire string data chunk in response.
1022
1023writeResponse:
1024 std::memcpy(response, &resp, sizeof(resp));
1025 *dataLen = respLen;
1026 return IPMI_CC_OK;
1027}
1028
Richard Marian Thomaiyar84bf9be2019-04-26 22:59:16 +05301029inline std::vector<uint8_t> convertStringToData(const std::string& command)
1030{
1031 std::istringstream iss(command);
1032 std::string token;
1033 std::vector<uint8_t> dataValue;
1034 while (std::getline(iss, token, ' '))
1035 {
1036 dataValue.emplace_back(
1037 static_cast<uint8_t>(std::stoul(token, nullptr, base_16)));
1038 }
1039 return dataValue;
1040}
1041
1042static bool populateI2CMasterWRWhitelist()
1043{
1044 nlohmann::json data = nullptr;
1045 std::ifstream jsonFile(i2cMasterWRWhitelistFile);
1046
1047 if (!jsonFile.good())
1048 {
1049 log<level::WARNING>("i2c white list file not found!",
1050 entry("FILE_NAME: %s", i2cMasterWRWhitelistFile));
1051 return false;
1052 }
1053
1054 try
1055 {
1056 data = nlohmann::json::parse(jsonFile, nullptr, false);
1057 }
1058 catch (nlohmann::json::parse_error& e)
1059 {
1060 log<level::ERR>("Corrupted i2c white list config file",
1061 entry("FILE_NAME: %s", i2cMasterWRWhitelistFile),
1062 entry("MSG: %s", e.what()));
1063 return false;
1064 }
1065
1066 try
1067 {
1068 // Example JSON Structure format
1069 // "filters": [
1070 // {
1071 // "Description": "Allow full read - ignore first byte write value
1072 // for 0x40 to 0x4F",
1073 // "busId": "0x01",
1074 // "slaveAddr": "0x40",
1075 // "slaveAddrMask": "0x0F",
1076 // "command": "0x00",
1077 // "commandMask": "0xFF"
1078 // },
1079 // {
1080 // "Description": "Allow full read - first byte match 0x05 and
1081 // ignore second byte",
1082 // "busId": "0x01",
1083 // "slaveAddr": "0x57",
1084 // "slaveAddrMask": "0x00",
1085 // "command": "0x05 0x00",
1086 // "commandMask": "0x00 0xFF"
1087 // },]
1088
1089 nlohmann::json filters = data[filtersStr].get<nlohmann::json>();
1090 std::vector<i2cMasterWRWhitelist>& whitelist = getWRWhitelist();
1091 for (const auto& it : filters.items())
1092 {
1093 nlohmann::json filter = it.value();
1094 if (filter.is_null())
1095 {
1096 log<level::ERR>(
1097 "Corrupted I2C master write read whitelist config file",
1098 entry("FILE_NAME: %s", i2cMasterWRWhitelistFile));
1099 return false;
1100 }
1101 const std::vector<uint8_t>& writeData =
1102 convertStringToData(filter[cmdStr].get<std::string>());
1103 const std::vector<uint8_t>& writeDataMask =
1104 convertStringToData(filter[cmdMaskStr].get<std::string>());
1105 if (writeDataMask.size() != writeData.size())
1106 {
1107 log<level::ERR>("I2C master write read whitelist filter "
1108 "mismatch for command & mask size");
1109 return false;
1110 }
1111 whitelist.push_back(
1112 {static_cast<uint8_t>(std::stoul(
1113 filter[busIdStr].get<std::string>(), nullptr, base_16)),
1114 static_cast<uint8_t>(
1115 std::stoul(filter[slaveAddrStr].get<std::string>(),
1116 nullptr, base_16)),
1117 static_cast<uint8_t>(
1118 std::stoul(filter[slaveAddrMaskStr].get<std::string>(),
1119 nullptr, base_16)),
1120 writeData, writeDataMask});
1121 }
1122 if (whitelist.size() != filters.size())
1123 {
1124 log<level::ERR>(
1125 "I2C master write read whitelist filter size mismatch");
1126 return false;
1127 }
1128 }
1129 catch (std::exception& e)
1130 {
1131 log<level::ERR>("I2C master write read whitelist unexpected exception",
1132 entry("ERROR=%s", e.what()));
1133 return false;
1134 }
1135 return true;
1136}
1137
1138static inline bool isWriteDataWhitelisted(const std::vector<uint8_t>& data,
1139 const std::vector<uint8_t>& dataMask,
1140 const std::vector<uint8_t>& writeData)
1141{
1142 std::vector<uint8_t> processedDataBuf(data.size());
1143 std::vector<uint8_t> processedReqBuf(dataMask.size());
1144 std::transform(writeData.begin(), writeData.end(), dataMask.begin(),
1145 processedReqBuf.begin(), std::bit_or<uint8_t>());
1146 std::transform(data.begin(), data.end(), dataMask.begin(),
1147 processedDataBuf.begin(), std::bit_or<uint8_t>());
1148
1149 return (processedDataBuf == processedReqBuf);
1150}
1151
1152static bool isCmdWhitelisted(uint8_t busId, uint8_t slaveAddr,
1153 std::vector<uint8_t>& writeData)
1154{
1155 std::vector<i2cMasterWRWhitelist>& whiteList = getWRWhitelist();
1156 for (const auto& wlEntry : whiteList)
1157 {
1158 if ((busId == wlEntry.busId) &&
1159 ((slaveAddr | wlEntry.slaveAddrMask) ==
1160 (wlEntry.slaveAddr | wlEntry.slaveAddrMask)))
1161 {
1162 const std::vector<uint8_t>& dataMask = wlEntry.dataMask;
1163 // Skip as no-match, if requested write data is more than the
1164 // write data mask size
1165 if (writeData.size() > dataMask.size())
1166 {
1167 continue;
1168 }
1169 if (isWriteDataWhitelisted(wlEntry.data, dataMask, writeData))
1170 {
1171 return true;
1172 }
1173 }
1174 }
1175 return false;
1176}
1177
1178/** @brief implements master write read IPMI command which can be used for
1179 * low-level I2C/SMBus write, read or write-read access
1180 * @param isPrivateBus -to indicate private bus usage
1181 * @param busId - bus id
1182 * @param channelNum - channel number
1183 * @param reserved - skip 1 bit
1184 * @param slaveAddr - slave address
1185 * @param read count - number of bytes to be read
1186 * @param writeData - data to be written
1187 *
1188 * @returns IPMI completion code plus response data
1189 * - readData - i2c response data
1190 */
1191ipmi::RspType<std::vector<uint8_t>>
1192 ipmiMasterWriteRead(bool isPrivateBus, uint3_t busId, uint4_t channelNum,
1193 bool reserved, uint7_t slaveAddr, uint8_t readCount,
1194 std::vector<uint8_t> writeData)
1195{
1196 i2c_rdwr_ioctl_data msgReadWrite = {0};
1197 i2c_msg i2cmsg[2] = {0};
1198
1199 if (readCount > maxIPMIWriteReadSize)
1200 {
1201 log<level::ERR>("Master write read command: Read count exceeds limit");
1202 return ipmi::responseParmOutOfRange();
1203 }
1204 const size_t writeCount = writeData.size();
1205 if (!readCount && !writeCount)
1206 {
1207 log<level::ERR>("Master write read command: Read & write count are 0");
1208 return ipmi::responseInvalidFieldRequest();
1209 }
1210 if (!isCmdWhitelisted(static_cast<uint8_t>(busId),
1211 static_cast<uint8_t>(slaveAddr), writeData))
1212 {
1213 log<level::ERR>("Master write read request blocked!",
1214 entry("BUS=%d", static_cast<uint8_t>(busId)),
1215 entry("ADDR=0x%x", static_cast<uint8_t>(slaveAddr)));
1216 return ipmi::responseInvalidFieldRequest();
1217 }
1218 std::vector<uint8_t> readBuf(readCount);
1219 std::string i2cBus =
1220 "/dev/i2c-" + std::to_string(static_cast<uint8_t>(busId));
1221
1222 int i2cDev = ::open(i2cBus.c_str(), O_RDWR | O_CLOEXEC);
1223 if (i2cDev < 0)
1224 {
1225 log<level::ERR>("Failed to open i2c bus",
1226 entry("BUS=%s", i2cBus.c_str()));
1227 return ipmi::responseInvalidFieldRequest();
1228 }
1229
1230 int msgCount = 0;
1231 if (writeCount)
1232 {
1233 i2cmsg[msgCount].addr = static_cast<uint8_t>(slaveAddr);
1234 i2cmsg[msgCount].flags = 0x00;
1235 i2cmsg[msgCount].len = writeCount;
1236 i2cmsg[msgCount].buf = writeData.data();
1237 msgCount++;
1238 }
1239 if (readCount)
1240 {
1241 i2cmsg[msgCount].addr = static_cast<uint8_t>(slaveAddr);
1242 i2cmsg[msgCount].flags = I2C_M_RD;
1243 i2cmsg[msgCount].len = readCount;
1244 i2cmsg[msgCount].buf = readBuf.data();
1245 msgCount++;
1246 }
1247
1248 msgReadWrite.msgs = i2cmsg;
1249 msgReadWrite.nmsgs = msgCount;
1250
1251 int ret = ::ioctl(i2cDev, I2C_RDWR, &msgReadWrite);
1252 ::close(i2cDev);
1253
1254 if (ret < 0)
1255 {
1256 log<level::ERR>("Master write read: Failed", entry("RET=%d", ret));
1257 return ipmi::responseUnspecifiedError();
1258 }
1259 if (readCount)
1260 {
1261 readBuf.resize(msgReadWrite.msgs[msgCount - 1].len);
1262 }
1263 return ipmi::responseSuccess(readBuf);
1264}
1265
Chris Austen6caf28b2015-10-13 12:40:40 -05001266void register_netfn_app_functions()
vishwabmcba0bd5f2015-09-30 16:50:23 +05301267{
Vernon Mauery86a50822019-03-25 13:11:36 -07001268 // <Get Device ID>
1269 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp,
1270 ipmi::app::cmdGetDeviceId, ipmi::Privilege::User,
1271 ipmiAppGetDeviceId);
1272
Tom05732372016-09-06 17:21:23 +05301273 // <Get BT Interface Capabilities>
Vernon Mauerycc99ba42019-03-25 13:40:11 -07001274 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp,
1275 ipmi::app::cmdGetBtIfaceCapabilities,
1276 ipmi::Privilege::User, ipmiAppGetBtCapabilities);
Chris Austen6caf28b2015-10-13 12:40:40 -05001277
Tom05732372016-09-06 17:21:23 +05301278 // <Reset Watchdog Timer>
Vernon Mauery11df4f62019-03-25 14:17:54 -07001279 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp,
1280 ipmi::app::cmdResetWatchdogTimer,
1281 ipmi::Privilege::Operator, ipmiAppResetWatchdogTimer);
Chris Austen6caf28b2015-10-13 12:40:40 -05001282
Tom05732372016-09-06 17:21:23 +05301283 // <Set Watchdog Timer>
Patrick Venture0b02be92018-08-31 11:55:55 -07001284 ipmi_register_callback(NETFUN_APP, IPMI_CMD_SET_WD, NULL,
1285 ipmi_app_watchdog_set, PRIVILEGE_OPERATOR);
Chris Austen6caf28b2015-10-13 12:40:40 -05001286
William A. Kennington III73f44512018-02-09 15:28:46 -08001287 // <Get Watchdog Timer>
Patrick Venture0b02be92018-08-31 11:55:55 -07001288 ipmi_register_callback(NETFUN_APP, IPMI_CMD_GET_WD, NULL,
1289 ipmi_app_watchdog_get, PRIVILEGE_OPERATOR);
William A. Kennington III73f44512018-02-09 15:28:46 -08001290
Tom05732372016-09-06 17:21:23 +05301291 // <Get Self Test Results>
Vernon Maueryb84a5282019-03-25 13:39:03 -07001292 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp,
1293 ipmi::app::cmdGetSelfTestResults,
1294 ipmi::Privilege::User, ipmiAppGetSelfTestResults);
Nan Li41fa24a2016-11-10 20:12:37 +08001295
Tom05732372016-09-06 17:21:23 +05301296 // <Get Device GUID>
Vernon Mauery15541322019-03-25 13:33:03 -07001297 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp,
1298 ipmi::app::cmdGetDeviceGuid, ipmi::Privilege::User,
1299 ipmiAppGetDeviceGuid);
Adriana Kobylakd100ee52015-10-20 17:02:37 -05001300
Tom05732372016-09-06 17:21:23 +05301301 // <Set ACPI Power State>
Patrick Venture0b02be92018-08-31 11:55:55 -07001302 ipmi_register_callback(NETFUN_APP, IPMI_CMD_SET_ACPI, NULL,
1303 ipmi_app_set_acpi_power_state, PRIVILEGE_ADMIN);
Chris Austen6caf28b2015-10-13 12:40:40 -05001304
Yong Li18d77262018-10-09 01:59:45 +08001305 // <Get ACPI Power State>
Deepak Kumar Sahu4e6d2572019-05-07 19:53:37 +00001306 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp,
1307 ipmi::app::cmdGetAcpiPowerState,
1308 ipmi::Privilege::Admin, ipmiGetAcpiPowerState);
Yong Li18d77262018-10-09 01:59:45 +08001309
Richard Marian Thomaiyar84bf9be2019-04-26 22:59:16 +05301310 // Note: For security reason, this command will be registered only when
1311 // there are proper I2C Master write read whitelist
1312 if (populateI2CMasterWRWhitelist())
1313 {
1314 // Note: For security reasons, registering master write read as admin
1315 // privilege command, even though IPMI 2.0 specification allows it as
1316 // operator privilege.
1317 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp,
1318 ipmi::app::cmdMasterWriteRead,
1319 ipmi::Privilege::Admin, ipmiMasterWriteRead);
1320 }
1321
Marri Devender Rao5e007a52018-01-08 06:18:36 -06001322 // <Get System GUID Command>
Patrick Venture0b02be92018-08-31 11:55:55 -07001323 ipmi_register_callback(NETFUN_APP, IPMI_CMD_GET_SYS_GUID, NULL,
1324 ipmi_app_get_sys_guid, PRIVILEGE_USER);
Tom Joseph7cbe2282018-03-21 21:17:33 +05301325
1326 // <Get Channel Cipher Suites Command>
Patrick Venture0b02be92018-08-31 11:55:55 -07001327 ipmi_register_callback(NETFUN_APP, IPMI_CMD_GET_CHAN_CIPHER_SUITES, NULL,
1328 getChannelCipherSuites, PRIVILEGE_CALLBACK);
Vernon Maueryd2a57de2019-03-25 12:45:28 -07001329
Xo Wangf542e8b2017-08-09 15:34:16 -07001330 // <Get System Info Command>
1331 ipmi_register_callback(NETFUN_APP, IPMI_CMD_GET_SYSTEM_INFO, NULL,
1332 ipmi_app_get_system_info, PRIVILEGE_USER);
vishwabmcba0bd5f2015-09-30 16:50:23 +05301333 return;
1334}