blob: 91bede5d3d869ca1a8d34053180f5bbe6ac30555 [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
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500703ipmi_ret_t ipmi_app_get_device_guid(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
Patrick Venture0b02be92018-08-31 11:55:55 -0700704 ipmi_request_t request,
705 ipmi_response_t response,
706 ipmi_data_len_t data_len,
707 ipmi_context_t context)
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500708{
Patrick Venture0b02be92018-08-31 11:55:55 -0700709 const char* objname = "/org/openbmc/control/chassis0";
710 const char* iface = "org.freedesktop.DBus.Properties";
711 const char* chassis_iface = "org.openbmc.control.Chassis";
712 sd_bus_message* reply = NULL;
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500713 sd_bus_error error = SD_BUS_ERROR_NULL;
714 int r = 0;
Patrick Venture0b02be92018-08-31 11:55:55 -0700715 char* uuid = NULL;
716 char* busname = NULL;
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500717
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500718 // UUID is in RFC4122 format. Ex: 61a39523-78f2-11e5-9862-e6402cfc3223
Patrick Ventured2117022018-02-06 08:54:37 -0800719 // Per IPMI Spec 2.0 need to convert to 16 hex bytes and reverse the byte
720 // order
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500721 // Ex: 0x2332fc2c40e66298e511f2782395a361
722
Patrick Venture0b02be92018-08-31 11:55:55 -0700723 const int resp_size = 16; // Response is 16 hex bytes per IPMI Spec
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500724 uint8_t resp_uuid[resp_size]; // Array to hold the formatted response
Patrick Ventured2117022018-02-06 08:54:37 -0800725 // Point resp end of array to save in reverse order
Patrick Venture0b02be92018-08-31 11:55:55 -0700726 int resp_loc = resp_size - 1;
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500727 int i = 0;
Patrick Venture0b02be92018-08-31 11:55:55 -0700728 char* tokptr = NULL;
729 char* id_octet = NULL;
Emily Shafferedb8bb02018-09-27 14:50:15 -0700730 size_t total_uuid_size = 0;
731 // 1 byte of resp is built from 2 chars of uuid.
732 constexpr size_t max_uuid_size = 2 * resp_size;
vishwa1eaea4f2016-02-26 11:57:40 -0600733
734 // Status code.
735 ipmi_ret_t rc = IPMI_CC_OK;
736 *data_len = 0;
737
vishwa1eaea4f2016-02-26 11:57:40 -0600738 // Call Get properties method with the interface and property name
Sergey Solomineb9b8142016-08-23 09:07:28 -0500739 r = mapper_get_service(bus, objname, &busname);
Patrick Venture0b02be92018-08-31 11:55:55 -0700740 if (r < 0)
741 {
742 log<level::ERR>("Failed to get bus name", entry("BUS=%s", objname),
Aditya Saripalli5fb14602017-11-09 14:46:27 +0530743 entry("ERRNO=0x%X", -r));
Sergey Solomineb9b8142016-08-23 09:07:28 -0500744 goto finish;
745 }
Patrick Venture0b02be92018-08-31 11:55:55 -0700746 r = sd_bus_call_method(bus, busname, objname, iface, "Get", &error, &reply,
747 "ss", chassis_iface, "uuid");
vishwa1eaea4f2016-02-26 11:57:40 -0600748 if (r < 0)
749 {
Patrick Venture0b02be92018-08-31 11:55:55 -0700750 log<level::ERR>("Failed to call Get Method", entry("ERRNO=0x%X", -r));
vishwa1eaea4f2016-02-26 11:57:40 -0600751 rc = IPMI_CC_UNSPECIFIED_ERROR;
752 goto finish;
753 }
754
755 r = sd_bus_message_read(reply, "v", "s", &uuid);
756 if (r < 0 || uuid == NULL)
757 {
Patrick Venture0b02be92018-08-31 11:55:55 -0700758 log<level::ERR>("Failed to get a response", entry("ERRNO=0x%X", -r));
vishwa1eaea4f2016-02-26 11:57:40 -0600759 rc = IPMI_CC_RESPONSE_ERROR;
760 goto finish;
761 }
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500762
763 // Traverse the UUID
Patrick Ventured2117022018-02-06 08:54:37 -0800764 // Get the UUID octects separated by dash
765 id_octet = strtok_r(uuid, "-", &tokptr);
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500766
767 if (id_octet == NULL)
vishwa1eaea4f2016-02-26 11:57:40 -0600768 {
769 // Error
Patrick Venture0b02be92018-08-31 11:55:55 -0700770 log<level::ERR>("Unexpected UUID format", entry("UUID=%s", uuid));
vishwa1eaea4f2016-02-26 11:57:40 -0600771 rc = IPMI_CC_RESPONSE_ERROR;
772 goto finish;
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500773 }
774
775 while (id_octet != NULL)
776 {
777 // Calculate the octet string size since it varies
778 // Divide it by 2 for the array size since 1 byte is built from 2 chars
Patrick Venture0b02be92018-08-31 11:55:55 -0700779 int tmp_size = strlen(id_octet) / 2;
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500780
Emily Shafferedb8bb02018-09-27 14:50:15 -0700781 // Check if total UUID size has been exceeded
782 if ((total_uuid_size += strlen(id_octet)) > max_uuid_size)
783 {
784 // Error - UUID too long to store
785 log<level::ERR>("UUID too long", entry("UUID=%s", uuid));
786 rc = IPMI_CC_RESPONSE_ERROR;
787 goto finish;
788 }
789
Patrick Venture0b02be92018-08-31 11:55:55 -0700790 for (i = 0; i < tmp_size; i++)
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500791 {
Patrick Ventured2117022018-02-06 08:54:37 -0800792 // Holder of the 2 chars that will become a byte
793 char tmp_array[3] = {0};
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500794 strncpy(tmp_array, id_octet, 2); // 2 chars at a time
795
796 int resp_byte = strtoul(tmp_array, NULL, 16); // Convert to hex byte
Patrick Ventured2117022018-02-06 08:54:37 -0800797 // Copy end to first
Patrick Ventureb51bf9c2018-09-10 15:53:14 -0700798 std::memcpy((void*)&resp_uuid[resp_loc], &resp_byte, 1);
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500799 resp_loc--;
Patrick Venture0b02be92018-08-31 11:55:55 -0700800 id_octet += 2; // Finished with the 2 chars, advance
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500801 }
Patrick Venture0b02be92018-08-31 11:55:55 -0700802 id_octet = strtok_r(NULL, "-", &tokptr); // Get next octet
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500803 }
804
805 // Data length
806 *data_len = resp_size;
807
808 // Pack the actual response
Patrick Ventureb51bf9c2018-09-10 15:53:14 -0700809 std::memcpy(response, &resp_uuid, *data_len);
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500810
vishwa1eaea4f2016-02-26 11:57:40 -0600811finish:
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500812 sd_bus_error_free(&error);
vishwa1eaea4f2016-02-26 11:57:40 -0600813 reply = sd_bus_message_unref(reply);
Sergey Solomineb9b8142016-08-23 09:07:28 -0500814 free(busname);
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500815
816 return rc;
817}
Chris Austen6caf28b2015-10-13 12:40:40 -0500818
Vernon Mauerycc99ba42019-03-25 13:40:11 -0700819auto ipmiAppGetBtCapabilities()
820 -> ipmi::RspType<uint8_t, uint8_t, uint8_t, uint8_t, uint8_t>
vishwabmcba0bd5f2015-09-30 16:50:23 +0530821{
Adriana Kobylak88ad8152016-12-13 10:09:08 -0600822 // Per IPMI 2.0 spec, the input and output buffer size must be the max
823 // buffer size minus one byte to allocate space for the length byte.
Vernon Mauerycc99ba42019-03-25 13:40:11 -0700824 constexpr uint8_t nrOutstanding = 0x01;
825 constexpr uint8_t inputBufferSize = MAX_IPMI_BUFFER - 1;
826 constexpr uint8_t outputBufferSize = MAX_IPMI_BUFFER - 1;
827 constexpr uint8_t transactionTime = 0x0A;
828 constexpr uint8_t nrRetries = 0x01;
vishwabmcba0bd5f2015-09-30 16:50:23 +0530829
Vernon Mauerycc99ba42019-03-25 13:40:11 -0700830 return ipmi::responseSuccess(nrOutstanding, inputBufferSize,
831 outputBufferSize, transactionTime, nrRetries);
vishwabmcba0bd5f2015-09-30 16:50:23 +0530832}
833
Marri Devender Rao5e007a52018-01-08 06:18:36 -0600834ipmi_ret_t ipmi_app_get_sys_guid(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
Patrick Venture0b02be92018-08-31 11:55:55 -0700835 ipmi_request_t request,
836 ipmi_response_t response,
837 ipmi_data_len_t data_len,
838 ipmi_context_t context)
Marri Devender Rao5e007a52018-01-08 06:18:36 -0600839
840{
841 ipmi_ret_t rc = IPMI_CC_OK;
842 sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()};
843
844 try
845 {
846 // Get the Inventory object implementing BMC interface
847 ipmi::DbusObjectInfo bmcObject =
848 ipmi::getDbusObject(bus, bmc_interface);
849
850 // Read UUID property value from bmcObject
851 // UUID is in RFC4122 format Ex: 61a39523-78f2-11e5-9862-e6402cfc3223
Patrick Venture0b02be92018-08-31 11:55:55 -0700852 auto variant =
853 ipmi::getDbusProperty(bus, bmcObject.second, bmcObject.first,
854 bmc_guid_interface, bmc_guid_property);
Vernon Maueryf442e112019-04-09 11:44:36 -0700855 std::string guidProp = std::get<std::string>(variant);
Marri Devender Rao5e007a52018-01-08 06:18:36 -0600856
857 // Erase "-" characters from the property value
858 guidProp.erase(std::remove(guidProp.begin(), guidProp.end(), '-'),
Patrick Venture0b02be92018-08-31 11:55:55 -0700859 guidProp.end());
Marri Devender Rao5e007a52018-01-08 06:18:36 -0600860
861 auto guidPropLen = guidProp.length();
862 // Validate UUID data
863 // Divide by 2 as 1 byte is built from 2 chars
Patrick Venture0b02be92018-08-31 11:55:55 -0700864 if ((guidPropLen <= 0) || ((guidPropLen / 2) != bmc_guid_len))
Marri Devender Rao5e007a52018-01-08 06:18:36 -0600865
866 {
867 log<level::ERR>("Invalid UUID property value",
Patrick Venture0b02be92018-08-31 11:55:55 -0700868 entry("UUID_LENGTH=%d", guidPropLen));
Marri Devender Rao5e007a52018-01-08 06:18:36 -0600869 return IPMI_CC_RESPONSE_ERROR;
870 }
871
872 // Convert data in RFC4122(MSB) format to LSB format
873 // Get 2 characters at a time as 1 byte is built from 2 chars and
874 // convert to hex byte
875 // TODO: Data printed for GUID command is not as per the
876 // GUID format defined in IPMI specification 2.0 section 20.8
877 // Ticket raised: https://sourceforge.net/p/ipmitool/bugs/501/
878 uint8_t respGuid[bmc_guid_len];
879 for (size_t i = 0, respLoc = (bmc_guid_len - 1);
Patrick Venture0b02be92018-08-31 11:55:55 -0700880 i < guidPropLen && respLoc >= 0; i += 2, respLoc--)
Marri Devender Rao5e007a52018-01-08 06:18:36 -0600881 {
882 auto value = static_cast<uint8_t>(
Patrick Venture0b02be92018-08-31 11:55:55 -0700883 std::stoi(guidProp.substr(i, 2).c_str(), NULL, 16));
Marri Devender Rao5e007a52018-01-08 06:18:36 -0600884 respGuid[respLoc] = value;
885 }
886
887 *data_len = bmc_guid_len;
Patrick Ventureb51bf9c2018-09-10 15:53:14 -0700888 std::memcpy(response, &respGuid, bmc_guid_len);
Marri Devender Rao5e007a52018-01-08 06:18:36 -0600889 }
890 catch (const InternalFailure& e)
891 {
892 log<level::ERR>("Failed in reading BMC UUID property",
893 entry("INTERFACE=%s", bmc_interface),
894 entry("PROPERTY_INTERFACE=%s", bmc_guid_interface),
895 entry("PROPERTY=%s", bmc_guid_property));
896 return IPMI_CC_UNSPECIFIED_ERROR;
897 }
898 return rc;
899}
900
Xo Wangf542e8b2017-08-09 15:34:16 -0700901static std::unique_ptr<SysInfoParamStore> sysInfoParamStore;
902
Xo Wang87651332017-08-11 10:17:59 -0700903static std::string sysInfoReadSystemName()
904{
905 // Use the BMC hostname as the "System Name."
906 char hostname[HOST_NAME_MAX + 1] = {};
907 if (gethostname(hostname, HOST_NAME_MAX) != 0)
908 {
909 perror("System info parameter: system name");
910 }
911 return hostname;
912}
913
Xo Wangf542e8b2017-08-09 15:34:16 -0700914struct IpmiSysInfoResp
915{
916 uint8_t paramRevision;
917 uint8_t setSelector;
918 union
919 {
920 struct
921 {
922 uint8_t encoding;
923 uint8_t stringLen;
924 uint8_t stringData0[14];
925 } __attribute__((packed));
926 uint8_t stringDataN[16];
927 uint8_t byteData;
928 };
929} __attribute__((packed));
930
931/**
932 * Split a string into (up to) 16-byte chunks as expected in response for get
933 * system info parameter.
934 *
935 * @param[in] fullString: Input string to be split
936 * @param[in] chunkIndex: Index of the chunk to be written out
937 * @param[in,out] chunk: Output data buffer; must have 14 byte capacity if
938 * chunk_index = 0 and 16-byte capacity otherwise
939 * @return the number of bytes written into the output buffer, or -EINVAL for
940 * invalid arguments.
941 */
942static int splitStringParam(const std::string& fullString, int chunkIndex,
943 uint8_t* chunk)
944{
945 constexpr int maxChunk = 255;
946 constexpr int smallChunk = 14;
947 constexpr int chunkSize = 16;
948 if (chunkIndex > maxChunk || chunk == nullptr)
949 {
950 return -EINVAL;
951 }
952 try
953 {
954 std::string output;
955 if (chunkIndex == 0)
956 {
957 // Output must have 14 byte capacity.
958 output = fullString.substr(0, smallChunk);
959 }
960 else
961 {
962 // Output must have 16 byte capacity.
963 output = fullString.substr((chunkIndex * chunkSize) - 2, chunkSize);
964 }
965
966 std::memcpy(chunk, output.c_str(), output.length());
967 return output.length();
968 }
969 catch (const std::out_of_range& e)
970 {
971 // The position was beyond the end.
972 return -EINVAL;
973 }
974}
975
976/**
977 * Packs the Get Sys Info Request Item into the response.
978 *
979 * @param[in] paramString - the parameter.
980 * @param[in] setSelector - the selector
981 * @param[in,out] resp - the System info response.
982 * @return The number of bytes packed or failure from splitStringParam().
983 */
984static int packGetSysInfoResp(const std::string& paramString,
985 uint8_t setSelector, IpmiSysInfoResp* resp)
986{
987 uint8_t* dataBuffer = resp->stringDataN;
988 resp->setSelector = setSelector;
989 if (resp->setSelector == 0) // First chunk has only 14 bytes.
990 {
991 resp->encoding = 0;
992 resp->stringLen = paramString.length();
993 dataBuffer = resp->stringData0;
994 }
995 return splitStringParam(paramString, resp->setSelector, dataBuffer);
996}
997
998ipmi_ret_t ipmi_app_get_system_info(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
999 ipmi_request_t request,
1000 ipmi_response_t response,
1001 ipmi_data_len_t dataLen,
1002 ipmi_context_t context)
1003{
1004 IpmiSysInfoResp resp = {};
1005 size_t respLen = 0;
1006 uint8_t* const reqData = static_cast<uint8_t*>(request);
1007 std::string paramString;
1008 bool found;
1009 std::tuple<bool, std::string> ret;
1010 constexpr int minRequestSize = 4;
1011 constexpr int paramSelector = 1;
1012 constexpr uint8_t revisionOnly = 0x80;
1013 const uint8_t paramRequested = reqData[paramSelector];
1014 int rc;
1015
1016 if (*dataLen < minRequestSize)
1017 {
1018 return IPMI_CC_REQ_DATA_LEN_INVALID;
1019 }
1020
1021 *dataLen = 0; // default to 0.
1022
1023 // Parameters revision as of IPMI spec v2.0 rev. 1.1 (Feb 11, 2014 E6)
1024 resp.paramRevision = 0x11;
1025 if (reqData[0] & revisionOnly) // Get parameter revision only
1026 {
1027 respLen = 1;
1028 goto writeResponse;
1029 }
1030
1031 // The "Set In Progress" parameter can be used for rollback of parameter
1032 // data and is not implemented.
1033 if (paramRequested == 0)
1034 {
1035 resp.byteData = 0;
1036 respLen = 2;
1037 goto writeResponse;
1038 }
1039
1040 if (sysInfoParamStore == nullptr)
1041 {
1042 sysInfoParamStore = std::make_unique<SysInfoParamStore>();
Xo Wang87651332017-08-11 10:17:59 -07001043 sysInfoParamStore->update(IPMI_SYSINFO_SYSTEM_NAME,
1044 sysInfoReadSystemName);
Xo Wangf542e8b2017-08-09 15:34:16 -07001045 }
1046
1047 // Parameters other than Set In Progress are assumed to be strings.
1048 ret = sysInfoParamStore->lookup(paramRequested);
1049 found = std::get<0>(ret);
1050 paramString = std::get<1>(ret);
1051 if (!found)
1052 {
1053 return IPMI_CC_SYSTEM_INFO_PARAMETER_NOT_SUPPORTED;
1054 }
1055 // TODO: Cache each parameter across multiple calls, until the whole string
1056 // has been read out. Otherwise, it's possible for a parameter to change
1057 // between requests for its chunks, returning chunks incoherent with each
1058 // other. For now, the parameter store is simply required to have only
1059 // idempotent callbacks.
1060 rc = packGetSysInfoResp(paramString, reqData[2], &resp);
1061 if (rc == -EINVAL)
1062 {
1063 return IPMI_CC_RESPONSE_ERROR;
1064 }
1065
1066 respLen = sizeof(resp); // Write entire string data chunk in response.
1067
1068writeResponse:
1069 std::memcpy(response, &resp, sizeof(resp));
1070 *dataLen = respLen;
1071 return IPMI_CC_OK;
1072}
1073
Richard Marian Thomaiyar84bf9be2019-04-26 22:59:16 +05301074inline std::vector<uint8_t> convertStringToData(const std::string& command)
1075{
1076 std::istringstream iss(command);
1077 std::string token;
1078 std::vector<uint8_t> dataValue;
1079 while (std::getline(iss, token, ' '))
1080 {
1081 dataValue.emplace_back(
1082 static_cast<uint8_t>(std::stoul(token, nullptr, base_16)));
1083 }
1084 return dataValue;
1085}
1086
1087static bool populateI2CMasterWRWhitelist()
1088{
1089 nlohmann::json data = nullptr;
1090 std::ifstream jsonFile(i2cMasterWRWhitelistFile);
1091
1092 if (!jsonFile.good())
1093 {
1094 log<level::WARNING>("i2c white list file not found!",
1095 entry("FILE_NAME: %s", i2cMasterWRWhitelistFile));
1096 return false;
1097 }
1098
1099 try
1100 {
1101 data = nlohmann::json::parse(jsonFile, nullptr, false);
1102 }
1103 catch (nlohmann::json::parse_error& e)
1104 {
1105 log<level::ERR>("Corrupted i2c white list config file",
1106 entry("FILE_NAME: %s", i2cMasterWRWhitelistFile),
1107 entry("MSG: %s", e.what()));
1108 return false;
1109 }
1110
1111 try
1112 {
1113 // Example JSON Structure format
1114 // "filters": [
1115 // {
1116 // "Description": "Allow full read - ignore first byte write value
1117 // for 0x40 to 0x4F",
1118 // "busId": "0x01",
1119 // "slaveAddr": "0x40",
1120 // "slaveAddrMask": "0x0F",
1121 // "command": "0x00",
1122 // "commandMask": "0xFF"
1123 // },
1124 // {
1125 // "Description": "Allow full read - first byte match 0x05 and
1126 // ignore second byte",
1127 // "busId": "0x01",
1128 // "slaveAddr": "0x57",
1129 // "slaveAddrMask": "0x00",
1130 // "command": "0x05 0x00",
1131 // "commandMask": "0x00 0xFF"
1132 // },]
1133
1134 nlohmann::json filters = data[filtersStr].get<nlohmann::json>();
1135 std::vector<i2cMasterWRWhitelist>& whitelist = getWRWhitelist();
1136 for (const auto& it : filters.items())
1137 {
1138 nlohmann::json filter = it.value();
1139 if (filter.is_null())
1140 {
1141 log<level::ERR>(
1142 "Corrupted I2C master write read whitelist config file",
1143 entry("FILE_NAME: %s", i2cMasterWRWhitelistFile));
1144 return false;
1145 }
1146 const std::vector<uint8_t>& writeData =
1147 convertStringToData(filter[cmdStr].get<std::string>());
1148 const std::vector<uint8_t>& writeDataMask =
1149 convertStringToData(filter[cmdMaskStr].get<std::string>());
1150 if (writeDataMask.size() != writeData.size())
1151 {
1152 log<level::ERR>("I2C master write read whitelist filter "
1153 "mismatch for command & mask size");
1154 return false;
1155 }
1156 whitelist.push_back(
1157 {static_cast<uint8_t>(std::stoul(
1158 filter[busIdStr].get<std::string>(), nullptr, base_16)),
1159 static_cast<uint8_t>(
1160 std::stoul(filter[slaveAddrStr].get<std::string>(),
1161 nullptr, base_16)),
1162 static_cast<uint8_t>(
1163 std::stoul(filter[slaveAddrMaskStr].get<std::string>(),
1164 nullptr, base_16)),
1165 writeData, writeDataMask});
1166 }
1167 if (whitelist.size() != filters.size())
1168 {
1169 log<level::ERR>(
1170 "I2C master write read whitelist filter size mismatch");
1171 return false;
1172 }
1173 }
1174 catch (std::exception& e)
1175 {
1176 log<level::ERR>("I2C master write read whitelist unexpected exception",
1177 entry("ERROR=%s", e.what()));
1178 return false;
1179 }
1180 return true;
1181}
1182
1183static inline bool isWriteDataWhitelisted(const std::vector<uint8_t>& data,
1184 const std::vector<uint8_t>& dataMask,
1185 const std::vector<uint8_t>& writeData)
1186{
1187 std::vector<uint8_t> processedDataBuf(data.size());
1188 std::vector<uint8_t> processedReqBuf(dataMask.size());
1189 std::transform(writeData.begin(), writeData.end(), dataMask.begin(),
1190 processedReqBuf.begin(), std::bit_or<uint8_t>());
1191 std::transform(data.begin(), data.end(), dataMask.begin(),
1192 processedDataBuf.begin(), std::bit_or<uint8_t>());
1193
1194 return (processedDataBuf == processedReqBuf);
1195}
1196
1197static bool isCmdWhitelisted(uint8_t busId, uint8_t slaveAddr,
1198 std::vector<uint8_t>& writeData)
1199{
1200 std::vector<i2cMasterWRWhitelist>& whiteList = getWRWhitelist();
1201 for (const auto& wlEntry : whiteList)
1202 {
1203 if ((busId == wlEntry.busId) &&
1204 ((slaveAddr | wlEntry.slaveAddrMask) ==
1205 (wlEntry.slaveAddr | wlEntry.slaveAddrMask)))
1206 {
1207 const std::vector<uint8_t>& dataMask = wlEntry.dataMask;
1208 // Skip as no-match, if requested write data is more than the
1209 // write data mask size
1210 if (writeData.size() > dataMask.size())
1211 {
1212 continue;
1213 }
1214 if (isWriteDataWhitelisted(wlEntry.data, dataMask, writeData))
1215 {
1216 return true;
1217 }
1218 }
1219 }
1220 return false;
1221}
1222
1223/** @brief implements master write read IPMI command which can be used for
1224 * low-level I2C/SMBus write, read or write-read access
1225 * @param isPrivateBus -to indicate private bus usage
1226 * @param busId - bus id
1227 * @param channelNum - channel number
1228 * @param reserved - skip 1 bit
1229 * @param slaveAddr - slave address
1230 * @param read count - number of bytes to be read
1231 * @param writeData - data to be written
1232 *
1233 * @returns IPMI completion code plus response data
1234 * - readData - i2c response data
1235 */
1236ipmi::RspType<std::vector<uint8_t>>
1237 ipmiMasterWriteRead(bool isPrivateBus, uint3_t busId, uint4_t channelNum,
1238 bool reserved, uint7_t slaveAddr, uint8_t readCount,
1239 std::vector<uint8_t> writeData)
1240{
1241 i2c_rdwr_ioctl_data msgReadWrite = {0};
1242 i2c_msg i2cmsg[2] = {0};
1243
1244 if (readCount > maxIPMIWriteReadSize)
1245 {
1246 log<level::ERR>("Master write read command: Read count exceeds limit");
1247 return ipmi::responseParmOutOfRange();
1248 }
1249 const size_t writeCount = writeData.size();
1250 if (!readCount && !writeCount)
1251 {
1252 log<level::ERR>("Master write read command: Read & write count are 0");
1253 return ipmi::responseInvalidFieldRequest();
1254 }
1255 if (!isCmdWhitelisted(static_cast<uint8_t>(busId),
1256 static_cast<uint8_t>(slaveAddr), writeData))
1257 {
1258 log<level::ERR>("Master write read request blocked!",
1259 entry("BUS=%d", static_cast<uint8_t>(busId)),
1260 entry("ADDR=0x%x", static_cast<uint8_t>(slaveAddr)));
1261 return ipmi::responseInvalidFieldRequest();
1262 }
1263 std::vector<uint8_t> readBuf(readCount);
1264 std::string i2cBus =
1265 "/dev/i2c-" + std::to_string(static_cast<uint8_t>(busId));
1266
1267 int i2cDev = ::open(i2cBus.c_str(), O_RDWR | O_CLOEXEC);
1268 if (i2cDev < 0)
1269 {
1270 log<level::ERR>("Failed to open i2c bus",
1271 entry("BUS=%s", i2cBus.c_str()));
1272 return ipmi::responseInvalidFieldRequest();
1273 }
1274
1275 int msgCount = 0;
1276 if (writeCount)
1277 {
1278 i2cmsg[msgCount].addr = static_cast<uint8_t>(slaveAddr);
1279 i2cmsg[msgCount].flags = 0x00;
1280 i2cmsg[msgCount].len = writeCount;
1281 i2cmsg[msgCount].buf = writeData.data();
1282 msgCount++;
1283 }
1284 if (readCount)
1285 {
1286 i2cmsg[msgCount].addr = static_cast<uint8_t>(slaveAddr);
1287 i2cmsg[msgCount].flags = I2C_M_RD;
1288 i2cmsg[msgCount].len = readCount;
1289 i2cmsg[msgCount].buf = readBuf.data();
1290 msgCount++;
1291 }
1292
1293 msgReadWrite.msgs = i2cmsg;
1294 msgReadWrite.nmsgs = msgCount;
1295
1296 int ret = ::ioctl(i2cDev, I2C_RDWR, &msgReadWrite);
1297 ::close(i2cDev);
1298
1299 if (ret < 0)
1300 {
1301 log<level::ERR>("Master write read: Failed", entry("RET=%d", ret));
1302 return ipmi::responseUnspecifiedError();
1303 }
1304 if (readCount)
1305 {
1306 readBuf.resize(msgReadWrite.msgs[msgCount - 1].len);
1307 }
1308 return ipmi::responseSuccess(readBuf);
1309}
1310
Chris Austen6caf28b2015-10-13 12:40:40 -05001311void register_netfn_app_functions()
vishwabmcba0bd5f2015-09-30 16:50:23 +05301312{
Vernon Mauery86a50822019-03-25 13:11:36 -07001313 // <Get Device ID>
1314 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp,
1315 ipmi::app::cmdGetDeviceId, ipmi::Privilege::User,
1316 ipmiAppGetDeviceId);
1317
Tom05732372016-09-06 17:21:23 +05301318 // <Get BT Interface Capabilities>
Vernon Mauerycc99ba42019-03-25 13:40:11 -07001319 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp,
1320 ipmi::app::cmdGetBtIfaceCapabilities,
1321 ipmi::Privilege::User, ipmiAppGetBtCapabilities);
Chris Austen6caf28b2015-10-13 12:40:40 -05001322
Tom05732372016-09-06 17:21:23 +05301323 // <Reset Watchdog Timer>
Vernon Mauery11df4f62019-03-25 14:17:54 -07001324 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp,
1325 ipmi::app::cmdResetWatchdogTimer,
1326 ipmi::Privilege::Operator, ipmiAppResetWatchdogTimer);
Chris Austen6caf28b2015-10-13 12:40:40 -05001327
Tom05732372016-09-06 17:21:23 +05301328 // <Set Watchdog Timer>
Patrick Venture0b02be92018-08-31 11:55:55 -07001329 ipmi_register_callback(NETFUN_APP, IPMI_CMD_SET_WD, NULL,
1330 ipmi_app_watchdog_set, PRIVILEGE_OPERATOR);
Chris Austen6caf28b2015-10-13 12:40:40 -05001331
William A. Kennington III73f44512018-02-09 15:28:46 -08001332 // <Get Watchdog Timer>
Patrick Venture0b02be92018-08-31 11:55:55 -07001333 ipmi_register_callback(NETFUN_APP, IPMI_CMD_GET_WD, NULL,
1334 ipmi_app_watchdog_get, PRIVILEGE_OPERATOR);
William A. Kennington III73f44512018-02-09 15:28:46 -08001335
Tom05732372016-09-06 17:21:23 +05301336 // <Get Self Test Results>
Vernon Maueryb84a5282019-03-25 13:39:03 -07001337 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp,
1338 ipmi::app::cmdGetSelfTestResults,
1339 ipmi::Privilege::User, ipmiAppGetSelfTestResults);
Nan Li41fa24a2016-11-10 20:12:37 +08001340
Tom05732372016-09-06 17:21:23 +05301341 // <Get Device GUID>
Patrick Venture0b02be92018-08-31 11:55:55 -07001342 ipmi_register_callback(NETFUN_APP, IPMI_CMD_GET_DEVICE_GUID, NULL,
1343 ipmi_app_get_device_guid, PRIVILEGE_USER);
Adriana Kobylakd100ee52015-10-20 17:02:37 -05001344
Tom05732372016-09-06 17:21:23 +05301345 // <Set ACPI Power State>
Patrick Venture0b02be92018-08-31 11:55:55 -07001346 ipmi_register_callback(NETFUN_APP, IPMI_CMD_SET_ACPI, NULL,
1347 ipmi_app_set_acpi_power_state, PRIVILEGE_ADMIN);
Chris Austen6caf28b2015-10-13 12:40:40 -05001348
Yong Li18d77262018-10-09 01:59:45 +08001349 // <Get ACPI Power State>
Deepak Kumar Sahu4e6d2572019-05-07 19:53:37 +00001350 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp,
1351 ipmi::app::cmdGetAcpiPowerState,
1352 ipmi::Privilege::Admin, ipmiGetAcpiPowerState);
Yong Li18d77262018-10-09 01:59:45 +08001353
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}