blob: 42e9e4bd6f04fb6858d5564a7bc902e767c63791 [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
434ipmi_ret_t ipmi_app_get_acpi_power_state(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
435 ipmi_request_t request,
436 ipmi_response_t response,
437 ipmi_data_len_t data_len,
438 ipmi_context_t context)
439{
440 ipmi_ret_t rc = IPMI_CC_OK;
441
442 auto* res = reinterpret_cast<acpi_state::ACPIState*>(response);
443
444 sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()};
445
446 *data_len = 0;
447
448 try
449 {
450 auto acpiObject = ipmi::getDbusObject(bus, acpi_state::acpiInterface);
451
452 auto sysACPIVal = ipmi::getDbusProperty(
453 bus, acpiObject.second, acpiObject.first, acpi_state::acpiInterface,
454 acpi_state::sysACPIProp);
455 auto sysACPI = acpi_state::ACPIPowerState::convertACPIFromString(
Vernon Maueryf442e112019-04-09 11:44:36 -0700456 std::get<std::string>(sysACPIVal));
Yong Li18d77262018-10-09 01:59:45 +0800457 res->sysACPIState =
458 static_cast<uint8_t>(acpi_state::dbusToIPMI.at(sysACPI));
459
460 auto devACPIVal = ipmi::getDbusProperty(
461 bus, acpiObject.second, acpiObject.first, acpi_state::acpiInterface,
462 acpi_state::devACPIProp);
463 auto devACPI = acpi_state::ACPIPowerState::convertACPIFromString(
Vernon Maueryf442e112019-04-09 11:44:36 -0700464 std::get<std::string>(devACPIVal));
Yong Li18d77262018-10-09 01:59:45 +0800465 res->devACPIState =
466 static_cast<uint8_t>(acpi_state::dbusToIPMI.at(devACPI));
467
468 *data_len = sizeof(acpi_state::ACPIState);
469 }
470 catch (const InternalFailure& e)
471 {
472 log<level::ERR>("Failed in get ACPI property");
473 return IPMI_CC_UNSPECIFIED_ERROR;
474 }
Chris Austen6caf28b2015-10-13 12:40:40 -0500475 return rc;
476}
477
Chris Austen7303bdc2016-04-17 11:50:54 -0500478typedef struct
479{
480 char major;
481 char minor;
Chris Austen176c9652016-04-30 16:32:17 -0500482 uint16_t d[2];
Vernon Mauery86a50822019-03-25 13:11:36 -0700483} Revision;
Chris Austen7303bdc2016-04-17 11:50:54 -0500484
Dinesh Chinari2b7e07d2017-11-08 15:38:50 -0600485/* Currently supports the vx.x-x-[-x] and v1.x.x-x-[-x] format. It will */
486/* return -1 if not in those formats, this routine knows how to parse */
Chris Austen7303bdc2016-04-17 11:50:54 -0500487/* version = v0.6-19-gf363f61-dirty */
488/* ^ ^ ^^ ^ */
489/* | | |----------|-- additional details */
490/* | |---------------- Minor */
491/* |------------------ Major */
Dinesh Chinari2b7e07d2017-11-08 15:38:50 -0600492/* and version = v1.99.10-113-g65edf7d-r3-0-g9e4f715 */
493/* ^ ^ ^^ ^ */
494/* | | |--|---------- additional details */
495/* | |---------------- Minor */
496/* |------------------ Major */
Chris Austen7303bdc2016-04-17 11:50:54 -0500497/* Additional details : If the option group exists it will force Auxiliary */
498/* Firmware Revision Information 4th byte to 1 indicating the build was */
499/* derived with additional edits */
Vernon Mauery86a50822019-03-25 13:11:36 -0700500int convertVersion(std::string s, Revision& rev)
Chris Austen7303bdc2016-04-17 11:50:54 -0500501{
Dinesh Chinari2b7e07d2017-11-08 15:38:50 -0600502 std::string token;
Chris Austen176c9652016-04-30 16:32:17 -0500503 uint16_t commits;
Chris Austen7303bdc2016-04-17 11:50:54 -0500504
Patrick Venture0b02be92018-08-31 11:55:55 -0700505 auto location = s.find_first_of('v');
Dinesh Chinari2b7e07d2017-11-08 15:38:50 -0600506 if (location != std::string::npos)
507 {
Patrick Venture0b02be92018-08-31 11:55:55 -0700508 s = s.substr(location + 1);
Chris Austen176c9652016-04-30 16:32:17 -0500509 }
Chris Austen7303bdc2016-04-17 11:50:54 -0500510
Dinesh Chinari2b7e07d2017-11-08 15:38:50 -0600511 if (!s.empty())
512 {
513 location = s.find_first_of(".");
514 if (location != std::string::npos)
515 {
Vernon Mauery86a50822019-03-25 13:11:36 -0700516 rev.major =
Patrick Venture0b02be92018-08-31 11:55:55 -0700517 static_cast<char>(std::stoi(s.substr(0, location), 0, 16));
518 token = s.substr(location + 1);
Dinesh Chinari2b7e07d2017-11-08 15:38:50 -0600519 }
Chris Austen176c9652016-04-30 16:32:17 -0500520
Dinesh Chinari2b7e07d2017-11-08 15:38:50 -0600521 if (!token.empty())
522 {
523 location = token.find_first_of(".-");
524 if (location != std::string::npos)
525 {
Vernon Mauery86a50822019-03-25 13:11:36 -0700526 rev.minor = static_cast<char>(
Patrick Venture0b02be92018-08-31 11:55:55 -0700527 std::stoi(token.substr(0, location), 0, 16));
528 token = token.substr(location + 1);
Dinesh Chinari2b7e07d2017-11-08 15:38:50 -0600529 }
530 }
Chris Austen7303bdc2016-04-17 11:50:54 -0500531
Dinesh Chinari2b7e07d2017-11-08 15:38:50 -0600532 // Capture the number of commits on top of the minor tag.
533 // I'm using BE format like the ipmi spec asked for
534 location = token.find_first_of(".-");
535 if (!token.empty())
536 {
537 commits = std::stoi(token.substr(0, location), 0, 16);
Vernon Mauery86a50822019-03-25 13:11:36 -0700538 rev.d[0] = (commits >> 8) | (commits << 8);
Dinesh Chinari2b7e07d2017-11-08 15:38:50 -0600539
540 // commit number we skip
541 location = token.find_first_of(".-");
542 if (location != std::string::npos)
543 {
Patrick Venture0b02be92018-08-31 11:55:55 -0700544 token = token.substr(location + 1);
Dinesh Chinari2b7e07d2017-11-08 15:38:50 -0600545 }
546 }
Patrick Venture0b02be92018-08-31 11:55:55 -0700547 else
548 {
Vernon Mauery86a50822019-03-25 13:11:36 -0700549 rev.d[0] = 0;
Dinesh Chinari2b7e07d2017-11-08 15:38:50 -0600550 }
551
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
557 // Any value of the optional parameter forces it to 1
558 location = token.find_first_of(".-");
559 if (location != std::string::npos)
560 {
Patrick Venture0b02be92018-08-31 11:55:55 -0700561 token = token.substr(location + 1);
Dinesh Chinari2b7e07d2017-11-08 15:38:50 -0600562 }
563 commits = (!token.empty()) ? 1 : 0;
564
Patrick Venture0b02be92018-08-31 11:55:55 -0700565 // We do this operation to get this displayed in least significant bytes
566 // of ipmitool device id command.
Vernon Mauery86a50822019-03-25 13:11:36 -0700567 rev.d[1] = (commits >> 8) | (commits << 8);
Dinesh Chinari2b7e07d2017-11-08 15:38:50 -0600568 }
569
Chris Austen7303bdc2016-04-17 11:50:54 -0500570 return 0;
571}
572
Vernon Mauery86a50822019-03-25 13:11:36 -0700573auto ipmiAppGetDeviceId() -> ipmi::RspType<uint8_t, // Device ID
574 uint8_t, // Device Revision
575 uint8_t, // Firmware Revision Major
576 uint8_t, // Firmware Revision minor
577 uint8_t, // IPMI version
578 uint8_t, // Additional device support
579 uint24_t, // MFG ID
580 uint16_t, // Product ID
581 uint32_t // AUX info
582 >
Chris Austen6caf28b2015-10-13 12:40:40 -0500583{
Nagaraju Goruganti744398d2018-02-20 09:52:00 -0600584 int r = -1;
Vernon Mauery86a50822019-03-25 13:11:36 -0700585 Revision rev = {0};
586 static struct
587 {
588 uint8_t id;
589 uint8_t revision;
590 uint8_t fw[2];
591 uint8_t ipmiVer;
592 uint8_t addnDevSupport;
593 uint24_t manufId;
594 uint16_t prodId;
595 uint32_t aux;
596 } devId;
David Cobbleya1adb072017-11-21 15:58:13 -0800597 static bool dev_id_initialized = false;
Patrick Venture94930a12019-04-30 10:01:58 -0700598 static bool defaultActivationSetting = true;
David Cobbleya1adb072017-11-21 15:58:13 -0800599 const char* filename = "/usr/share/ipmi-providers/dev_id.json";
Alexander Amelkinba19c182018-09-04 15:49:36 +0300600 constexpr auto ipmiDevIdStateShift = 7;
601 constexpr auto ipmiDevIdFw1Mask = ~(1 << ipmiDevIdStateShift);
Chris Austen6caf28b2015-10-13 12:40:40 -0500602
David Cobbleya1adb072017-11-21 15:58:13 -0800603 if (!dev_id_initialized)
604 {
Nagaraju Goruganti744398d2018-02-20 09:52:00 -0600605 try
606 {
607 auto version = getActiveSoftwareVersionInfo();
Vernon Mauery86a50822019-03-25 13:11:36 -0700608 r = convertVersion(version, rev);
David Cobbleya1adb072017-11-21 15:58:13 -0800609 }
Nagaraju Goruganti744398d2018-02-20 09:52:00 -0600610 catch (const std::exception& e)
611 {
612 log<level::ERR>(e.what());
613 }
Nan Liee0cb902016-07-11 15:38:03 +0800614
Patrick Venture0b02be92018-08-31 11:55:55 -0700615 if (r >= 0)
616 {
Nagaraju Goruganti744398d2018-02-20 09:52:00 -0600617 // bit7 identifies if the device is available
618 // 0=normal operation
619 // 1=device firmware, SDR update,
620 // or self-initialization in progress.
Alexander Amelkinba19c182018-09-04 15:49:36 +0300621 // The availability may change in run time, so mask here
622 // and initialize later.
Vernon Mauery86a50822019-03-25 13:11:36 -0700623 devId.fw[0] = rev.major & ipmiDevIdFw1Mask;
Nagaraju Goruganti744398d2018-02-20 09:52:00 -0600624
625 rev.minor = (rev.minor > 99 ? 99 : rev.minor);
Vernon Mauery86a50822019-03-25 13:11:36 -0700626 devId.fw[1] = rev.minor % 10 + (rev.minor / 10) * 16;
627 std::memcpy(&devId.aux, rev.d, 4);
David Cobbleya1adb072017-11-21 15:58:13 -0800628 }
Nan Liee0cb902016-07-11 15:38:03 +0800629
David Cobbleya1adb072017-11-21 15:58:13 -0800630 // IPMI Spec version 2.0
Vernon Mauery86a50822019-03-25 13:11:36 -0700631 devId.ipmiVer = 2;
Adriana Kobylak0e912642016-06-22 16:54:39 -0500632
Vernon Mauery86a50822019-03-25 13:11:36 -0700633 std::ifstream devIdFile(filename);
634 if (devIdFile.is_open())
David Cobbleya1adb072017-11-21 15:58:13 -0800635 {
Vernon Mauery86a50822019-03-25 13:11:36 -0700636 auto data = nlohmann::json::parse(devIdFile, nullptr, false);
David Cobbleya1adb072017-11-21 15:58:13 -0800637 if (!data.is_discarded())
638 {
Vernon Mauery86a50822019-03-25 13:11:36 -0700639 devId.id = data.value("id", 0);
640 devId.revision = data.value("revision", 0);
641 devId.addnDevSupport = data.value("addn_dev_support", 0);
642 devId.manufId = data.value("manuf_id", 0);
643 devId.prodId = data.value("prod_id", 0);
644 devId.aux = data.value("aux", 0);
David Cobbleya1adb072017-11-21 15:58:13 -0800645
Patrick Venture94930a12019-04-30 10:01:58 -0700646 // Set the availablitity of the BMC.
647 defaultActivationSetting = data.value("availability", true);
648
Patrick Venture0b02be92018-08-31 11:55:55 -0700649 // Don't read the file every time if successful
David Cobbleya1adb072017-11-21 15:58:13 -0800650 dev_id_initialized = true;
651 }
652 else
653 {
654 log<level::ERR>("Device ID JSON parser failure");
Vernon Maueryf2587fc2019-04-09 14:28:15 -0700655 return ipmi::responseUnspecifiedError();
David Cobbleya1adb072017-11-21 15:58:13 -0800656 }
657 }
658 else
659 {
660 log<level::ERR>("Device ID file not found");
Vernon Maueryf2587fc2019-04-09 14:28:15 -0700661 return ipmi::responseUnspecifiedError();
Chris Austen7303bdc2016-04-17 11:50:54 -0500662 }
663 }
Chris Austen6caf28b2015-10-13 12:40:40 -0500664
Alexander Amelkinba19c182018-09-04 15:49:36 +0300665 // Set availability to the actual current BMC state
Vernon Mauery86a50822019-03-25 13:11:36 -0700666 devId.fw[0] &= ipmiDevIdFw1Mask;
Patrick Venture94930a12019-04-30 10:01:58 -0700667 if (!getCurrentBmcStateWithFallback(defaultActivationSetting))
Alexander Amelkinba19c182018-09-04 15:49:36 +0300668 {
Vernon Mauery86a50822019-03-25 13:11:36 -0700669 devId.fw[0] |= (1 << ipmiDevIdStateShift);
Alexander Amelkinba19c182018-09-04 15:49:36 +0300670 }
671
Vernon Mauery86a50822019-03-25 13:11:36 -0700672 return ipmi::responseSuccess(
673 devId.id, devId.revision, devId.fw[0], devId.fw[1], devId.ipmiVer,
674 devId.addnDevSupport, devId.manufId, devId.prodId, devId.aux);
Chris Austen6caf28b2015-10-13 12:40:40 -0500675}
676
Vernon Maueryb84a5282019-03-25 13:39:03 -0700677auto ipmiAppGetSelfTestResults() -> ipmi::RspType<uint8_t, uint8_t>
Nan Li41fa24a2016-11-10 20:12:37 +0800678{
Nan Li41fa24a2016-11-10 20:12:37 +0800679 // Byte 2:
680 // 55h - No error.
Gunnar Mills8991dd62017-10-25 17:11:29 -0500681 // 56h - Self Test function not implemented in this controller.
Nan Li41fa24a2016-11-10 20:12:37 +0800682 // 57h - Corrupted or inaccesssible data or devices.
683 // 58h - Fatal hardware error.
684 // FFh - reserved.
685 // all other: Device-specific 'internal failure'.
686 // Byte 3:
687 // For byte 2 = 55h, 56h, FFh: 00h
688 // For byte 2 = 58h, all other: Device-specific
689 // For byte 2 = 57h: self-test error bitfield.
690 // Note: returning 57h does not imply that all test were run.
691 // [7] 1b = Cannot access SEL device.
692 // [6] 1b = Cannot access SDR Repository.
693 // [5] 1b = Cannot access BMC FRU device.
694 // [4] 1b = IPMB signal lines do not respond.
695 // [3] 1b = SDR Repository empty.
696 // [2] 1b = Internal Use Area of BMC FRU corrupted.
697 // [1] 1b = controller update 'boot block' firmware corrupted.
698 // [0] 1b = controller operational firmware corrupted.
Vernon Maueryb84a5282019-03-25 13:39:03 -0700699 constexpr uint8_t notImplemented = 0x56;
700 constexpr uint8_t zero = 0;
701 return ipmi::responseSuccess(notImplemented, zero);
Nan Li41fa24a2016-11-10 20:12:37 +0800702}
703
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500704ipmi_ret_t ipmi_app_get_device_guid(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
Patrick Venture0b02be92018-08-31 11:55:55 -0700705 ipmi_request_t request,
706 ipmi_response_t response,
707 ipmi_data_len_t data_len,
708 ipmi_context_t context)
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500709{
Patrick Venture0b02be92018-08-31 11:55:55 -0700710 const char* objname = "/org/openbmc/control/chassis0";
711 const char* iface = "org.freedesktop.DBus.Properties";
712 const char* chassis_iface = "org.openbmc.control.Chassis";
713 sd_bus_message* reply = NULL;
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500714 sd_bus_error error = SD_BUS_ERROR_NULL;
715 int r = 0;
Patrick Venture0b02be92018-08-31 11:55:55 -0700716 char* uuid = NULL;
717 char* busname = NULL;
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500718
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500719 // UUID is in RFC4122 format. Ex: 61a39523-78f2-11e5-9862-e6402cfc3223
Patrick Ventured2117022018-02-06 08:54:37 -0800720 // Per IPMI Spec 2.0 need to convert to 16 hex bytes and reverse the byte
721 // order
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500722 // Ex: 0x2332fc2c40e66298e511f2782395a361
723
Patrick Venture0b02be92018-08-31 11:55:55 -0700724 const int resp_size = 16; // Response is 16 hex bytes per IPMI Spec
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500725 uint8_t resp_uuid[resp_size]; // Array to hold the formatted response
Patrick Ventured2117022018-02-06 08:54:37 -0800726 // Point resp end of array to save in reverse order
Patrick Venture0b02be92018-08-31 11:55:55 -0700727 int resp_loc = resp_size - 1;
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500728 int i = 0;
Patrick Venture0b02be92018-08-31 11:55:55 -0700729 char* tokptr = NULL;
730 char* id_octet = NULL;
Emily Shafferedb8bb02018-09-27 14:50:15 -0700731 size_t total_uuid_size = 0;
732 // 1 byte of resp is built from 2 chars of uuid.
733 constexpr size_t max_uuid_size = 2 * resp_size;
vishwa1eaea4f2016-02-26 11:57:40 -0600734
735 // Status code.
736 ipmi_ret_t rc = IPMI_CC_OK;
737 *data_len = 0;
738
vishwa1eaea4f2016-02-26 11:57:40 -0600739 // Call Get properties method with the interface and property name
Sergey Solomineb9b8142016-08-23 09:07:28 -0500740 r = mapper_get_service(bus, objname, &busname);
Patrick Venture0b02be92018-08-31 11:55:55 -0700741 if (r < 0)
742 {
743 log<level::ERR>("Failed to get bus name", entry("BUS=%s", objname),
Aditya Saripalli5fb14602017-11-09 14:46:27 +0530744 entry("ERRNO=0x%X", -r));
Sergey Solomineb9b8142016-08-23 09:07:28 -0500745 goto finish;
746 }
Patrick Venture0b02be92018-08-31 11:55:55 -0700747 r = sd_bus_call_method(bus, busname, objname, iface, "Get", &error, &reply,
748 "ss", chassis_iface, "uuid");
vishwa1eaea4f2016-02-26 11:57:40 -0600749 if (r < 0)
750 {
Patrick Venture0b02be92018-08-31 11:55:55 -0700751 log<level::ERR>("Failed to call Get Method", entry("ERRNO=0x%X", -r));
vishwa1eaea4f2016-02-26 11:57:40 -0600752 rc = IPMI_CC_UNSPECIFIED_ERROR;
753 goto finish;
754 }
755
756 r = sd_bus_message_read(reply, "v", "s", &uuid);
757 if (r < 0 || uuid == NULL)
758 {
Patrick Venture0b02be92018-08-31 11:55:55 -0700759 log<level::ERR>("Failed to get a response", entry("ERRNO=0x%X", -r));
vishwa1eaea4f2016-02-26 11:57:40 -0600760 rc = IPMI_CC_RESPONSE_ERROR;
761 goto finish;
762 }
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500763
764 // Traverse the UUID
Patrick Ventured2117022018-02-06 08:54:37 -0800765 // Get the UUID octects separated by dash
766 id_octet = strtok_r(uuid, "-", &tokptr);
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500767
768 if (id_octet == NULL)
vishwa1eaea4f2016-02-26 11:57:40 -0600769 {
770 // Error
Patrick Venture0b02be92018-08-31 11:55:55 -0700771 log<level::ERR>("Unexpected UUID format", entry("UUID=%s", uuid));
vishwa1eaea4f2016-02-26 11:57:40 -0600772 rc = IPMI_CC_RESPONSE_ERROR;
773 goto finish;
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500774 }
775
776 while (id_octet != NULL)
777 {
778 // Calculate the octet string size since it varies
779 // Divide it by 2 for the array size since 1 byte is built from 2 chars
Patrick Venture0b02be92018-08-31 11:55:55 -0700780 int tmp_size = strlen(id_octet) / 2;
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500781
Emily Shafferedb8bb02018-09-27 14:50:15 -0700782 // Check if total UUID size has been exceeded
783 if ((total_uuid_size += strlen(id_octet)) > max_uuid_size)
784 {
785 // Error - UUID too long to store
786 log<level::ERR>("UUID too long", entry("UUID=%s", uuid));
787 rc = IPMI_CC_RESPONSE_ERROR;
788 goto finish;
789 }
790
Patrick Venture0b02be92018-08-31 11:55:55 -0700791 for (i = 0; i < tmp_size; i++)
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500792 {
Patrick Ventured2117022018-02-06 08:54:37 -0800793 // Holder of the 2 chars that will become a byte
794 char tmp_array[3] = {0};
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500795 strncpy(tmp_array, id_octet, 2); // 2 chars at a time
796
797 int resp_byte = strtoul(tmp_array, NULL, 16); // Convert to hex byte
Patrick Ventured2117022018-02-06 08:54:37 -0800798 // Copy end to first
Patrick Ventureb51bf9c2018-09-10 15:53:14 -0700799 std::memcpy((void*)&resp_uuid[resp_loc], &resp_byte, 1);
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500800 resp_loc--;
Patrick Venture0b02be92018-08-31 11:55:55 -0700801 id_octet += 2; // Finished with the 2 chars, advance
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500802 }
Patrick Venture0b02be92018-08-31 11:55:55 -0700803 id_octet = strtok_r(NULL, "-", &tokptr); // Get next octet
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500804 }
805
806 // Data length
807 *data_len = resp_size;
808
809 // Pack the actual response
Patrick Ventureb51bf9c2018-09-10 15:53:14 -0700810 std::memcpy(response, &resp_uuid, *data_len);
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500811
vishwa1eaea4f2016-02-26 11:57:40 -0600812finish:
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500813 sd_bus_error_free(&error);
vishwa1eaea4f2016-02-26 11:57:40 -0600814 reply = sd_bus_message_unref(reply);
Sergey Solomineb9b8142016-08-23 09:07:28 -0500815 free(busname);
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500816
817 return rc;
818}
Chris Austen6caf28b2015-10-13 12:40:40 -0500819
Vernon Mauerycc99ba42019-03-25 13:40:11 -0700820auto ipmiAppGetBtCapabilities()
821 -> ipmi::RspType<uint8_t, uint8_t, uint8_t, uint8_t, uint8_t>
vishwabmcba0bd5f2015-09-30 16:50:23 +0530822{
Adriana Kobylak88ad8152016-12-13 10:09:08 -0600823 // Per IPMI 2.0 spec, the input and output buffer size must be the max
824 // buffer size minus one byte to allocate space for the length byte.
Vernon Mauerycc99ba42019-03-25 13:40:11 -0700825 constexpr uint8_t nrOutstanding = 0x01;
826 constexpr uint8_t inputBufferSize = MAX_IPMI_BUFFER - 1;
827 constexpr uint8_t outputBufferSize = MAX_IPMI_BUFFER - 1;
828 constexpr uint8_t transactionTime = 0x0A;
829 constexpr uint8_t nrRetries = 0x01;
vishwabmcba0bd5f2015-09-30 16:50:23 +0530830
Vernon Mauerycc99ba42019-03-25 13:40:11 -0700831 return ipmi::responseSuccess(nrOutstanding, inputBufferSize,
832 outputBufferSize, transactionTime, nrRetries);
vishwabmcba0bd5f2015-09-30 16:50:23 +0530833}
834
Marri Devender Rao5e007a52018-01-08 06:18:36 -0600835ipmi_ret_t ipmi_app_get_sys_guid(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
Patrick Venture0b02be92018-08-31 11:55:55 -0700836 ipmi_request_t request,
837 ipmi_response_t response,
838 ipmi_data_len_t data_len,
839 ipmi_context_t context)
Marri Devender Rao5e007a52018-01-08 06:18:36 -0600840
841{
842 ipmi_ret_t rc = IPMI_CC_OK;
843 sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()};
844
845 try
846 {
847 // Get the Inventory object implementing BMC interface
848 ipmi::DbusObjectInfo bmcObject =
849 ipmi::getDbusObject(bus, bmc_interface);
850
851 // Read UUID property value from bmcObject
852 // UUID is in RFC4122 format Ex: 61a39523-78f2-11e5-9862-e6402cfc3223
Patrick Venture0b02be92018-08-31 11:55:55 -0700853 auto variant =
854 ipmi::getDbusProperty(bus, bmcObject.second, bmcObject.first,
855 bmc_guid_interface, bmc_guid_property);
Vernon Maueryf442e112019-04-09 11:44:36 -0700856 std::string guidProp = std::get<std::string>(variant);
Marri Devender Rao5e007a52018-01-08 06:18:36 -0600857
858 // Erase "-" characters from the property value
859 guidProp.erase(std::remove(guidProp.begin(), guidProp.end(), '-'),
Patrick Venture0b02be92018-08-31 11:55:55 -0700860 guidProp.end());
Marri Devender Rao5e007a52018-01-08 06:18:36 -0600861
862 auto guidPropLen = guidProp.length();
863 // Validate UUID data
864 // Divide by 2 as 1 byte is built from 2 chars
Patrick Venture0b02be92018-08-31 11:55:55 -0700865 if ((guidPropLen <= 0) || ((guidPropLen / 2) != bmc_guid_len))
Marri Devender Rao5e007a52018-01-08 06:18:36 -0600866
867 {
868 log<level::ERR>("Invalid UUID property value",
Patrick Venture0b02be92018-08-31 11:55:55 -0700869 entry("UUID_LENGTH=%d", guidPropLen));
Marri Devender Rao5e007a52018-01-08 06:18:36 -0600870 return IPMI_CC_RESPONSE_ERROR;
871 }
872
873 // Convert data in RFC4122(MSB) format to LSB format
874 // Get 2 characters at a time as 1 byte is built from 2 chars and
875 // convert to hex byte
876 // TODO: Data printed for GUID command is not as per the
877 // GUID format defined in IPMI specification 2.0 section 20.8
878 // Ticket raised: https://sourceforge.net/p/ipmitool/bugs/501/
879 uint8_t respGuid[bmc_guid_len];
880 for (size_t i = 0, respLoc = (bmc_guid_len - 1);
Patrick Venture0b02be92018-08-31 11:55:55 -0700881 i < guidPropLen && respLoc >= 0; i += 2, respLoc--)
Marri Devender Rao5e007a52018-01-08 06:18:36 -0600882 {
883 auto value = static_cast<uint8_t>(
Patrick Venture0b02be92018-08-31 11:55:55 -0700884 std::stoi(guidProp.substr(i, 2).c_str(), NULL, 16));
Marri Devender Rao5e007a52018-01-08 06:18:36 -0600885 respGuid[respLoc] = value;
886 }
887
888 *data_len = bmc_guid_len;
Patrick Ventureb51bf9c2018-09-10 15:53:14 -0700889 std::memcpy(response, &respGuid, bmc_guid_len);
Marri Devender Rao5e007a52018-01-08 06:18:36 -0600890 }
891 catch (const InternalFailure& e)
892 {
893 log<level::ERR>("Failed in reading BMC UUID property",
894 entry("INTERFACE=%s", bmc_interface),
895 entry("PROPERTY_INTERFACE=%s", bmc_guid_interface),
896 entry("PROPERTY=%s", bmc_guid_property));
897 return IPMI_CC_UNSPECIFIED_ERROR;
898 }
899 return rc;
900}
901
Xo Wangf542e8b2017-08-09 15:34:16 -0700902static std::unique_ptr<SysInfoParamStore> sysInfoParamStore;
903
Xo Wang87651332017-08-11 10:17:59 -0700904static std::string sysInfoReadSystemName()
905{
906 // Use the BMC hostname as the "System Name."
907 char hostname[HOST_NAME_MAX + 1] = {};
908 if (gethostname(hostname, HOST_NAME_MAX) != 0)
909 {
910 perror("System info parameter: system name");
911 }
912 return hostname;
913}
914
Xo Wangf542e8b2017-08-09 15:34:16 -0700915struct IpmiSysInfoResp
916{
917 uint8_t paramRevision;
918 uint8_t setSelector;
919 union
920 {
921 struct
922 {
923 uint8_t encoding;
924 uint8_t stringLen;
925 uint8_t stringData0[14];
926 } __attribute__((packed));
927 uint8_t stringDataN[16];
928 uint8_t byteData;
929 };
930} __attribute__((packed));
931
932/**
933 * Split a string into (up to) 16-byte chunks as expected in response for get
934 * system info parameter.
935 *
936 * @param[in] fullString: Input string to be split
937 * @param[in] chunkIndex: Index of the chunk to be written out
938 * @param[in,out] chunk: Output data buffer; must have 14 byte capacity if
939 * chunk_index = 0 and 16-byte capacity otherwise
940 * @return the number of bytes written into the output buffer, or -EINVAL for
941 * invalid arguments.
942 */
943static int splitStringParam(const std::string& fullString, int chunkIndex,
944 uint8_t* chunk)
945{
946 constexpr int maxChunk = 255;
947 constexpr int smallChunk = 14;
948 constexpr int chunkSize = 16;
949 if (chunkIndex > maxChunk || chunk == nullptr)
950 {
951 return -EINVAL;
952 }
953 try
954 {
955 std::string output;
956 if (chunkIndex == 0)
957 {
958 // Output must have 14 byte capacity.
959 output = fullString.substr(0, smallChunk);
960 }
961 else
962 {
963 // Output must have 16 byte capacity.
964 output = fullString.substr((chunkIndex * chunkSize) - 2, chunkSize);
965 }
966
967 std::memcpy(chunk, output.c_str(), output.length());
968 return output.length();
969 }
970 catch (const std::out_of_range& e)
971 {
972 // The position was beyond the end.
973 return -EINVAL;
974 }
975}
976
977/**
978 * Packs the Get Sys Info Request Item into the response.
979 *
980 * @param[in] paramString - the parameter.
981 * @param[in] setSelector - the selector
982 * @param[in,out] resp - the System info response.
983 * @return The number of bytes packed or failure from splitStringParam().
984 */
985static int packGetSysInfoResp(const std::string& paramString,
986 uint8_t setSelector, IpmiSysInfoResp* resp)
987{
988 uint8_t* dataBuffer = resp->stringDataN;
989 resp->setSelector = setSelector;
990 if (resp->setSelector == 0) // First chunk has only 14 bytes.
991 {
992 resp->encoding = 0;
993 resp->stringLen = paramString.length();
994 dataBuffer = resp->stringData0;
995 }
996 return splitStringParam(paramString, resp->setSelector, dataBuffer);
997}
998
999ipmi_ret_t ipmi_app_get_system_info(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
1000 ipmi_request_t request,
1001 ipmi_response_t response,
1002 ipmi_data_len_t dataLen,
1003 ipmi_context_t context)
1004{
1005 IpmiSysInfoResp resp = {};
1006 size_t respLen = 0;
1007 uint8_t* const reqData = static_cast<uint8_t*>(request);
1008 std::string paramString;
1009 bool found;
1010 std::tuple<bool, std::string> ret;
1011 constexpr int minRequestSize = 4;
1012 constexpr int paramSelector = 1;
1013 constexpr uint8_t revisionOnly = 0x80;
1014 const uint8_t paramRequested = reqData[paramSelector];
1015 int rc;
1016
1017 if (*dataLen < minRequestSize)
1018 {
1019 return IPMI_CC_REQ_DATA_LEN_INVALID;
1020 }
1021
1022 *dataLen = 0; // default to 0.
1023
1024 // Parameters revision as of IPMI spec v2.0 rev. 1.1 (Feb 11, 2014 E6)
1025 resp.paramRevision = 0x11;
1026 if (reqData[0] & revisionOnly) // Get parameter revision only
1027 {
1028 respLen = 1;
1029 goto writeResponse;
1030 }
1031
1032 // The "Set In Progress" parameter can be used for rollback of parameter
1033 // data and is not implemented.
1034 if (paramRequested == 0)
1035 {
1036 resp.byteData = 0;
1037 respLen = 2;
1038 goto writeResponse;
1039 }
1040
1041 if (sysInfoParamStore == nullptr)
1042 {
1043 sysInfoParamStore = std::make_unique<SysInfoParamStore>();
Xo Wang87651332017-08-11 10:17:59 -07001044 sysInfoParamStore->update(IPMI_SYSINFO_SYSTEM_NAME,
1045 sysInfoReadSystemName);
Xo Wangf542e8b2017-08-09 15:34:16 -07001046 }
1047
1048 // Parameters other than Set In Progress are assumed to be strings.
1049 ret = sysInfoParamStore->lookup(paramRequested);
1050 found = std::get<0>(ret);
1051 paramString = std::get<1>(ret);
1052 if (!found)
1053 {
1054 return IPMI_CC_SYSTEM_INFO_PARAMETER_NOT_SUPPORTED;
1055 }
1056 // TODO: Cache each parameter across multiple calls, until the whole string
1057 // has been read out. Otherwise, it's possible for a parameter to change
1058 // between requests for its chunks, returning chunks incoherent with each
1059 // other. For now, the parameter store is simply required to have only
1060 // idempotent callbacks.
1061 rc = packGetSysInfoResp(paramString, reqData[2], &resp);
1062 if (rc == -EINVAL)
1063 {
1064 return IPMI_CC_RESPONSE_ERROR;
1065 }
1066
1067 respLen = sizeof(resp); // Write entire string data chunk in response.
1068
1069writeResponse:
1070 std::memcpy(response, &resp, sizeof(resp));
1071 *dataLen = respLen;
1072 return IPMI_CC_OK;
1073}
1074
Richard Marian Thomaiyar84bf9be2019-04-26 22:59:16 +05301075inline std::vector<uint8_t> convertStringToData(const std::string& command)
1076{
1077 std::istringstream iss(command);
1078 std::string token;
1079 std::vector<uint8_t> dataValue;
1080 while (std::getline(iss, token, ' '))
1081 {
1082 dataValue.emplace_back(
1083 static_cast<uint8_t>(std::stoul(token, nullptr, base_16)));
1084 }
1085 return dataValue;
1086}
1087
1088static bool populateI2CMasterWRWhitelist()
1089{
1090 nlohmann::json data = nullptr;
1091 std::ifstream jsonFile(i2cMasterWRWhitelistFile);
1092
1093 if (!jsonFile.good())
1094 {
1095 log<level::WARNING>("i2c white list file not found!",
1096 entry("FILE_NAME: %s", i2cMasterWRWhitelistFile));
1097 return false;
1098 }
1099
1100 try
1101 {
1102 data = nlohmann::json::parse(jsonFile, nullptr, false);
1103 }
1104 catch (nlohmann::json::parse_error& e)
1105 {
1106 log<level::ERR>("Corrupted i2c white list config file",
1107 entry("FILE_NAME: %s", i2cMasterWRWhitelistFile),
1108 entry("MSG: %s", e.what()));
1109 return false;
1110 }
1111
1112 try
1113 {
1114 // Example JSON Structure format
1115 // "filters": [
1116 // {
1117 // "Description": "Allow full read - ignore first byte write value
1118 // for 0x40 to 0x4F",
1119 // "busId": "0x01",
1120 // "slaveAddr": "0x40",
1121 // "slaveAddrMask": "0x0F",
1122 // "command": "0x00",
1123 // "commandMask": "0xFF"
1124 // },
1125 // {
1126 // "Description": "Allow full read - first byte match 0x05 and
1127 // ignore second byte",
1128 // "busId": "0x01",
1129 // "slaveAddr": "0x57",
1130 // "slaveAddrMask": "0x00",
1131 // "command": "0x05 0x00",
1132 // "commandMask": "0x00 0xFF"
1133 // },]
1134
1135 nlohmann::json filters = data[filtersStr].get<nlohmann::json>();
1136 std::vector<i2cMasterWRWhitelist>& whitelist = getWRWhitelist();
1137 for (const auto& it : filters.items())
1138 {
1139 nlohmann::json filter = it.value();
1140 if (filter.is_null())
1141 {
1142 log<level::ERR>(
1143 "Corrupted I2C master write read whitelist config file",
1144 entry("FILE_NAME: %s", i2cMasterWRWhitelistFile));
1145 return false;
1146 }
1147 const std::vector<uint8_t>& writeData =
1148 convertStringToData(filter[cmdStr].get<std::string>());
1149 const std::vector<uint8_t>& writeDataMask =
1150 convertStringToData(filter[cmdMaskStr].get<std::string>());
1151 if (writeDataMask.size() != writeData.size())
1152 {
1153 log<level::ERR>("I2C master write read whitelist filter "
1154 "mismatch for command & mask size");
1155 return false;
1156 }
1157 whitelist.push_back(
1158 {static_cast<uint8_t>(std::stoul(
1159 filter[busIdStr].get<std::string>(), nullptr, base_16)),
1160 static_cast<uint8_t>(
1161 std::stoul(filter[slaveAddrStr].get<std::string>(),
1162 nullptr, base_16)),
1163 static_cast<uint8_t>(
1164 std::stoul(filter[slaveAddrMaskStr].get<std::string>(),
1165 nullptr, base_16)),
1166 writeData, writeDataMask});
1167 }
1168 if (whitelist.size() != filters.size())
1169 {
1170 log<level::ERR>(
1171 "I2C master write read whitelist filter size mismatch");
1172 return false;
1173 }
1174 }
1175 catch (std::exception& e)
1176 {
1177 log<level::ERR>("I2C master write read whitelist unexpected exception",
1178 entry("ERROR=%s", e.what()));
1179 return false;
1180 }
1181 return true;
1182}
1183
1184static inline bool isWriteDataWhitelisted(const std::vector<uint8_t>& data,
1185 const std::vector<uint8_t>& dataMask,
1186 const std::vector<uint8_t>& writeData)
1187{
1188 std::vector<uint8_t> processedDataBuf(data.size());
1189 std::vector<uint8_t> processedReqBuf(dataMask.size());
1190 std::transform(writeData.begin(), writeData.end(), dataMask.begin(),
1191 processedReqBuf.begin(), std::bit_or<uint8_t>());
1192 std::transform(data.begin(), data.end(), dataMask.begin(),
1193 processedDataBuf.begin(), std::bit_or<uint8_t>());
1194
1195 return (processedDataBuf == processedReqBuf);
1196}
1197
1198static bool isCmdWhitelisted(uint8_t busId, uint8_t slaveAddr,
1199 std::vector<uint8_t>& writeData)
1200{
1201 std::vector<i2cMasterWRWhitelist>& whiteList = getWRWhitelist();
1202 for (const auto& wlEntry : whiteList)
1203 {
1204 if ((busId == wlEntry.busId) &&
1205 ((slaveAddr | wlEntry.slaveAddrMask) ==
1206 (wlEntry.slaveAddr | wlEntry.slaveAddrMask)))
1207 {
1208 const std::vector<uint8_t>& dataMask = wlEntry.dataMask;
1209 // Skip as no-match, if requested write data is more than the
1210 // write data mask size
1211 if (writeData.size() > dataMask.size())
1212 {
1213 continue;
1214 }
1215 if (isWriteDataWhitelisted(wlEntry.data, dataMask, writeData))
1216 {
1217 return true;
1218 }
1219 }
1220 }
1221 return false;
1222}
1223
1224/** @brief implements master write read IPMI command which can be used for
1225 * low-level I2C/SMBus write, read or write-read access
1226 * @param isPrivateBus -to indicate private bus usage
1227 * @param busId - bus id
1228 * @param channelNum - channel number
1229 * @param reserved - skip 1 bit
1230 * @param slaveAddr - slave address
1231 * @param read count - number of bytes to be read
1232 * @param writeData - data to be written
1233 *
1234 * @returns IPMI completion code plus response data
1235 * - readData - i2c response data
1236 */
1237ipmi::RspType<std::vector<uint8_t>>
1238 ipmiMasterWriteRead(bool isPrivateBus, uint3_t busId, uint4_t channelNum,
1239 bool reserved, uint7_t slaveAddr, uint8_t readCount,
1240 std::vector<uint8_t> writeData)
1241{
1242 i2c_rdwr_ioctl_data msgReadWrite = {0};
1243 i2c_msg i2cmsg[2] = {0};
1244
1245 if (readCount > maxIPMIWriteReadSize)
1246 {
1247 log<level::ERR>("Master write read command: Read count exceeds limit");
1248 return ipmi::responseParmOutOfRange();
1249 }
1250 const size_t writeCount = writeData.size();
1251 if (!readCount && !writeCount)
1252 {
1253 log<level::ERR>("Master write read command: Read & write count are 0");
1254 return ipmi::responseInvalidFieldRequest();
1255 }
1256 if (!isCmdWhitelisted(static_cast<uint8_t>(busId),
1257 static_cast<uint8_t>(slaveAddr), writeData))
1258 {
1259 log<level::ERR>("Master write read request blocked!",
1260 entry("BUS=%d", static_cast<uint8_t>(busId)),
1261 entry("ADDR=0x%x", static_cast<uint8_t>(slaveAddr)));
1262 return ipmi::responseInvalidFieldRequest();
1263 }
1264 std::vector<uint8_t> readBuf(readCount);
1265 std::string i2cBus =
1266 "/dev/i2c-" + std::to_string(static_cast<uint8_t>(busId));
1267
1268 int i2cDev = ::open(i2cBus.c_str(), O_RDWR | O_CLOEXEC);
1269 if (i2cDev < 0)
1270 {
1271 log<level::ERR>("Failed to open i2c bus",
1272 entry("BUS=%s", i2cBus.c_str()));
1273 return ipmi::responseInvalidFieldRequest();
1274 }
1275
1276 int msgCount = 0;
1277 if (writeCount)
1278 {
1279 i2cmsg[msgCount].addr = static_cast<uint8_t>(slaveAddr);
1280 i2cmsg[msgCount].flags = 0x00;
1281 i2cmsg[msgCount].len = writeCount;
1282 i2cmsg[msgCount].buf = writeData.data();
1283 msgCount++;
1284 }
1285 if (readCount)
1286 {
1287 i2cmsg[msgCount].addr = static_cast<uint8_t>(slaveAddr);
1288 i2cmsg[msgCount].flags = I2C_M_RD;
1289 i2cmsg[msgCount].len = readCount;
1290 i2cmsg[msgCount].buf = readBuf.data();
1291 msgCount++;
1292 }
1293
1294 msgReadWrite.msgs = i2cmsg;
1295 msgReadWrite.nmsgs = msgCount;
1296
1297 int ret = ::ioctl(i2cDev, I2C_RDWR, &msgReadWrite);
1298 ::close(i2cDev);
1299
1300 if (ret < 0)
1301 {
1302 log<level::ERR>("Master write read: Failed", entry("RET=%d", ret));
1303 return ipmi::responseUnspecifiedError();
1304 }
1305 if (readCount)
1306 {
1307 readBuf.resize(msgReadWrite.msgs[msgCount - 1].len);
1308 }
1309 return ipmi::responseSuccess(readBuf);
1310}
1311
Chris Austen6caf28b2015-10-13 12:40:40 -05001312void register_netfn_app_functions()
vishwabmcba0bd5f2015-09-30 16:50:23 +05301313{
Vernon Mauery86a50822019-03-25 13:11:36 -07001314 // <Get Device ID>
1315 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp,
1316 ipmi::app::cmdGetDeviceId, ipmi::Privilege::User,
1317 ipmiAppGetDeviceId);
1318
Tom05732372016-09-06 17:21:23 +05301319 // <Get BT Interface Capabilities>
Vernon Mauerycc99ba42019-03-25 13:40:11 -07001320 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp,
1321 ipmi::app::cmdGetBtIfaceCapabilities,
1322 ipmi::Privilege::User, ipmiAppGetBtCapabilities);
Chris Austen6caf28b2015-10-13 12:40:40 -05001323
Tom05732372016-09-06 17:21:23 +05301324 // <Reset Watchdog Timer>
Vernon Mauery11df4f62019-03-25 14:17:54 -07001325 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp,
1326 ipmi::app::cmdResetWatchdogTimer,
1327 ipmi::Privilege::Operator, ipmiAppResetWatchdogTimer);
Chris Austen6caf28b2015-10-13 12:40:40 -05001328
Tom05732372016-09-06 17:21:23 +05301329 // <Set Watchdog Timer>
Patrick Venture0b02be92018-08-31 11:55:55 -07001330 ipmi_register_callback(NETFUN_APP, IPMI_CMD_SET_WD, NULL,
1331 ipmi_app_watchdog_set, PRIVILEGE_OPERATOR);
Chris Austen6caf28b2015-10-13 12:40:40 -05001332
William A. Kennington III73f44512018-02-09 15:28:46 -08001333 // <Get Watchdog Timer>
Patrick Venture0b02be92018-08-31 11:55:55 -07001334 ipmi_register_callback(NETFUN_APP, IPMI_CMD_GET_WD, NULL,
1335 ipmi_app_watchdog_get, PRIVILEGE_OPERATOR);
William A. Kennington III73f44512018-02-09 15:28:46 -08001336
Tom05732372016-09-06 17:21:23 +05301337 // <Get Self Test Results>
Vernon Maueryb84a5282019-03-25 13:39:03 -07001338 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp,
1339 ipmi::app::cmdGetSelfTestResults,
1340 ipmi::Privilege::User, ipmiAppGetSelfTestResults);
Nan Li41fa24a2016-11-10 20:12:37 +08001341
Tom05732372016-09-06 17:21:23 +05301342 // <Get Device GUID>
Patrick Venture0b02be92018-08-31 11:55:55 -07001343 ipmi_register_callback(NETFUN_APP, IPMI_CMD_GET_DEVICE_GUID, NULL,
1344 ipmi_app_get_device_guid, PRIVILEGE_USER);
Adriana Kobylakd100ee52015-10-20 17:02:37 -05001345
Tom05732372016-09-06 17:21:23 +05301346 // <Set ACPI Power State>
Patrick Venture0b02be92018-08-31 11:55:55 -07001347 ipmi_register_callback(NETFUN_APP, IPMI_CMD_SET_ACPI, NULL,
1348 ipmi_app_set_acpi_power_state, PRIVILEGE_ADMIN);
Chris Austen6caf28b2015-10-13 12:40:40 -05001349
Yong Li18d77262018-10-09 01:59:45 +08001350 // <Get ACPI Power State>
1351 ipmi_register_callback(NETFUN_APP, IPMI_CMD_GET_ACPI, NULL,
1352 ipmi_app_get_acpi_power_state, PRIVILEGE_ADMIN);
1353
Richard Marian Thomaiyar84bf9be2019-04-26 22:59:16 +05301354 // Note: For security reason, this command will be registered only when
1355 // there are proper I2C Master write read whitelist
1356 if (populateI2CMasterWRWhitelist())
1357 {
1358 // Note: For security reasons, registering master write read as admin
1359 // privilege command, even though IPMI 2.0 specification allows it as
1360 // operator privilege.
1361 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp,
1362 ipmi::app::cmdMasterWriteRead,
1363 ipmi::Privilege::Admin, ipmiMasterWriteRead);
1364 }
1365
Marri Devender Rao5e007a52018-01-08 06:18:36 -06001366 // <Get System GUID Command>
Patrick Venture0b02be92018-08-31 11:55:55 -07001367 ipmi_register_callback(NETFUN_APP, IPMI_CMD_GET_SYS_GUID, NULL,
1368 ipmi_app_get_sys_guid, PRIVILEGE_USER);
Tom Joseph7cbe2282018-03-21 21:17:33 +05301369
1370 // <Get Channel Cipher Suites Command>
Patrick Venture0b02be92018-08-31 11:55:55 -07001371 ipmi_register_callback(NETFUN_APP, IPMI_CMD_GET_CHAN_CIPHER_SUITES, NULL,
1372 getChannelCipherSuites, PRIVILEGE_CALLBACK);
Vernon Maueryd2a57de2019-03-25 12:45:28 -07001373
Xo Wangf542e8b2017-08-09 15:34:16 -07001374 // <Get System Info Command>
1375 ipmi_register_callback(NETFUN_APP, IPMI_CMD_GET_SYSTEM_INFO, NULL,
1376 ipmi_app_get_system_info, PRIVILEGE_USER);
vishwabmcba0bd5f2015-09-30 16:50:23 +05301377 return;
1378}