blob: 648a390bf23ddcebf07e6eb43410a75b07b00424 [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
Yong Li18d77262018-10-09 01:59:45 +0800194namespace acpi_state
195{
196using namespace sdbusplus::xyz::openbmc_project::Control::Power::server;
197
198const static constexpr char* acpiObjPath =
199 "/xyz/openbmc_project/control/host0/acpi_power_state";
200const static constexpr char* acpiInterface =
201 "xyz.openbmc_project.Control.Power.ACPIPowerState";
202const static constexpr char* sysACPIProp = "SysACPIStatus";
203const static constexpr char* devACPIProp = "DevACPIStatus";
204
205enum class PowerStateType : uint8_t
206{
207 sysPowerState = 0x00,
208 devPowerState = 0x01,
209};
210
211// Defined in 20.6 of ipmi doc
212enum class PowerState : uint8_t
213{
214 s0G0D0 = 0x00,
215 s1D1 = 0x01,
216 s2D2 = 0x02,
217 s3D3 = 0x03,
218 s4 = 0x04,
219 s5G2 = 0x05,
220 s4S5 = 0x06,
221 g3 = 0x07,
222 sleep = 0x08,
223 g1Sleep = 0x09,
224 override = 0x0a,
225 legacyOn = 0x20,
226 legacyOff = 0x21,
227 unknown = 0x2a,
228 noChange = 0x7f,
229};
230
231static constexpr uint8_t stateChanged = 0x80;
232
233struct ACPIState
234{
235 uint8_t sysACPIState;
236 uint8_t devACPIState;
237} __attribute__((packed));
238
239std::map<ACPIPowerState::ACPI, PowerState> dbusToIPMI = {
240 {ACPIPowerState::ACPI::S0_G0_D0, PowerState::s0G0D0},
241 {ACPIPowerState::ACPI::S1_D1, PowerState::s1D1},
242 {ACPIPowerState::ACPI::S2_D2, PowerState::s2D2},
243 {ACPIPowerState::ACPI::S3_D3, PowerState::s3D3},
244 {ACPIPowerState::ACPI::S4, PowerState::s4},
245 {ACPIPowerState::ACPI::S5_G2, PowerState::s5G2},
246 {ACPIPowerState::ACPI::S4_S5, PowerState::s4S5},
247 {ACPIPowerState::ACPI::G3, PowerState::g3},
248 {ACPIPowerState::ACPI::SLEEP, PowerState::sleep},
249 {ACPIPowerState::ACPI::G1_SLEEP, PowerState::g1Sleep},
250 {ACPIPowerState::ACPI::OVERRIDE, PowerState::override},
251 {ACPIPowerState::ACPI::LEGACY_ON, PowerState::legacyOn},
252 {ACPIPowerState::ACPI::LEGACY_OFF, PowerState::legacyOff},
253 {ACPIPowerState::ACPI::Unknown, PowerState::unknown}};
254
255bool isValidACPIState(acpi_state::PowerStateType type, uint8_t state)
256{
257 if (type == acpi_state::PowerStateType::sysPowerState)
258 {
259 if ((state <= static_cast<uint8_t>(acpi_state::PowerState::override)) ||
260 (state == static_cast<uint8_t>(acpi_state::PowerState::legacyOn)) ||
261 (state ==
262 static_cast<uint8_t>(acpi_state::PowerState::legacyOff)) ||
263 (state == static_cast<uint8_t>(acpi_state::PowerState::unknown)) ||
264 (state == static_cast<uint8_t>(acpi_state::PowerState::noChange)))
265 {
266 return true;
267 }
268 else
269 {
270 return false;
271 }
272 }
273 else if (type == acpi_state::PowerStateType::devPowerState)
274 {
275 if ((state <= static_cast<uint8_t>(acpi_state::PowerState::s3D3)) ||
276 (state == static_cast<uint8_t>(acpi_state::PowerState::unknown)) ||
277 (state == static_cast<uint8_t>(acpi_state::PowerState::noChange)))
278 {
279 return true;
280 }
281 else
282 {
283 return false;
284 }
285 }
286 else
287 {
288 return false;
289 }
290 return false;
291}
292} // namespace acpi_state
293
Adriana Kobylak3a552e12015-10-19 16:11:00 -0500294ipmi_ret_t ipmi_app_set_acpi_power_state(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
Patrick Venture0b02be92018-08-31 11:55:55 -0700295 ipmi_request_t request,
296 ipmi_response_t response,
297 ipmi_data_len_t data_len,
298 ipmi_context_t context)
Chris Austen6caf28b2015-10-13 12:40:40 -0500299{
Yong Li18d77262018-10-09 01:59:45 +0800300 auto s = static_cast<uint8_t>(acpi_state::PowerState::unknown);
Chris Austen6caf28b2015-10-13 12:40:40 -0500301 ipmi_ret_t rc = IPMI_CC_OK;
Yong Li18d77262018-10-09 01:59:45 +0800302
303 sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()};
304
305 auto value = acpi_state::ACPIPowerState::ACPI::Unknown;
306
307 auto* req = reinterpret_cast<acpi_state::ACPIState*>(request);
308
309 if (*data_len != sizeof(acpi_state::ACPIState))
310 {
311 log<level::ERR>("set_acpi invalid len");
312 *data_len = 0;
313 return IPMI_CC_REQ_DATA_LEN_INVALID;
314 }
315
Chris Austen6caf28b2015-10-13 12:40:40 -0500316 *data_len = 0;
317
Yong Li18d77262018-10-09 01:59:45 +0800318 if (req->sysACPIState & acpi_state::stateChanged)
319 {
320 // set system power state
321 s = req->sysACPIState & ~acpi_state::stateChanged;
322
323 if (!acpi_state::isValidACPIState(
324 acpi_state::PowerStateType::sysPowerState, s))
325 {
326 log<level::ERR>("set_acpi_power sys invalid input",
327 entry("S=%x", s));
328 return IPMI_CC_PARM_OUT_OF_RANGE;
329 }
330
331 // valid input
332 if (s == static_cast<uint8_t>(acpi_state::PowerState::noChange))
333 {
334 log<level::DEBUG>("No change for system power state");
335 }
336 else
337 {
338 auto found = std::find_if(
339 acpi_state::dbusToIPMI.begin(), acpi_state::dbusToIPMI.end(),
340 [&s](const auto& iter) {
341 return (static_cast<uint8_t>(iter.second) == s);
342 });
343
344 value = found->first;
345
346 try
347 {
348 auto acpiObject =
349 ipmi::getDbusObject(bus, acpi_state::acpiInterface);
350 ipmi::setDbusProperty(bus, acpiObject.second, acpiObject.first,
351 acpi_state::acpiInterface,
352 acpi_state::sysACPIProp,
353 convertForMessage(value));
354 }
355 catch (const InternalFailure& e)
356 {
357 log<level::ERR>("Failed in set ACPI system property",
358 entry("EXCEPTION=%s", e.what()));
359 return IPMI_CC_UNSPECIFIED_ERROR;
360 }
361 }
362 }
363 else
364 {
365 log<level::DEBUG>("Do not change system power state");
366 }
367
368 if (req->devACPIState & acpi_state::stateChanged)
369 {
370 // set device power state
371 s = req->devACPIState & ~acpi_state::stateChanged;
372 if (!acpi_state::isValidACPIState(
373 acpi_state::PowerStateType::devPowerState, s))
374 {
375 log<level::ERR>("set_acpi_power dev invalid input",
376 entry("S=%x", s));
377 return IPMI_CC_PARM_OUT_OF_RANGE;
378 }
379
380 // valid input
381 if (s == static_cast<uint8_t>(acpi_state::PowerState::noChange))
382 {
383 log<level::DEBUG>("No change for device power state");
384 }
385 else
386 {
387 auto found = std::find_if(
388 acpi_state::dbusToIPMI.begin(), acpi_state::dbusToIPMI.end(),
389 [&s](const auto& iter) {
390 return (static_cast<uint8_t>(iter.second) == s);
391 });
392
393 value = found->first;
394
395 try
396 {
397 auto acpiObject =
398 ipmi::getDbusObject(bus, acpi_state::acpiInterface);
399 ipmi::setDbusProperty(bus, acpiObject.second, acpiObject.first,
400 acpi_state::acpiInterface,
401 acpi_state::devACPIProp,
402 convertForMessage(value));
403 }
404 catch (const InternalFailure& e)
405 {
406 log<level::ERR>("Failed in set ACPI device property",
407 entry("EXCEPTION=%s", e.what()));
408 return IPMI_CC_UNSPECIFIED_ERROR;
409 }
410 }
411 }
412 else
413 {
414 log<level::DEBUG>("Do not change device power state");
415 }
416
417 return rc;
418}
419
420ipmi_ret_t ipmi_app_get_acpi_power_state(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
421 ipmi_request_t request,
422 ipmi_response_t response,
423 ipmi_data_len_t data_len,
424 ipmi_context_t context)
425{
426 ipmi_ret_t rc = IPMI_CC_OK;
427
428 auto* res = reinterpret_cast<acpi_state::ACPIState*>(response);
429
430 sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()};
431
432 *data_len = 0;
433
434 try
435 {
436 auto acpiObject = ipmi::getDbusObject(bus, acpi_state::acpiInterface);
437
438 auto sysACPIVal = ipmi::getDbusProperty(
439 bus, acpiObject.second, acpiObject.first, acpi_state::acpiInterface,
440 acpi_state::sysACPIProp);
441 auto sysACPI = acpi_state::ACPIPowerState::convertACPIFromString(
Vernon Maueryf442e112019-04-09 11:44:36 -0700442 std::get<std::string>(sysACPIVal));
Yong Li18d77262018-10-09 01:59:45 +0800443 res->sysACPIState =
444 static_cast<uint8_t>(acpi_state::dbusToIPMI.at(sysACPI));
445
446 auto devACPIVal = ipmi::getDbusProperty(
447 bus, acpiObject.second, acpiObject.first, acpi_state::acpiInterface,
448 acpi_state::devACPIProp);
449 auto devACPI = acpi_state::ACPIPowerState::convertACPIFromString(
Vernon Maueryf442e112019-04-09 11:44:36 -0700450 std::get<std::string>(devACPIVal));
Yong Li18d77262018-10-09 01:59:45 +0800451 res->devACPIState =
452 static_cast<uint8_t>(acpi_state::dbusToIPMI.at(devACPI));
453
454 *data_len = sizeof(acpi_state::ACPIState);
455 }
456 catch (const InternalFailure& e)
457 {
458 log<level::ERR>("Failed in get ACPI property");
459 return IPMI_CC_UNSPECIFIED_ERROR;
460 }
Chris Austen6caf28b2015-10-13 12:40:40 -0500461 return rc;
462}
463
Chris Austen7303bdc2016-04-17 11:50:54 -0500464typedef struct
465{
466 char major;
467 char minor;
Chris Austen176c9652016-04-30 16:32:17 -0500468 uint16_t d[2];
Vernon Mauery86a50822019-03-25 13:11:36 -0700469} Revision;
Chris Austen7303bdc2016-04-17 11:50:54 -0500470
Dinesh Chinari2b7e07d2017-11-08 15:38:50 -0600471/* Currently supports the vx.x-x-[-x] and v1.x.x-x-[-x] format. It will */
472/* return -1 if not in those formats, this routine knows how to parse */
Chris Austen7303bdc2016-04-17 11:50:54 -0500473/* version = v0.6-19-gf363f61-dirty */
474/* ^ ^ ^^ ^ */
475/* | | |----------|-- additional details */
476/* | |---------------- Minor */
477/* |------------------ Major */
Dinesh Chinari2b7e07d2017-11-08 15:38:50 -0600478/* and version = v1.99.10-113-g65edf7d-r3-0-g9e4f715 */
479/* ^ ^ ^^ ^ */
480/* | | |--|---------- additional details */
481/* | |---------------- Minor */
482/* |------------------ Major */
Chris Austen7303bdc2016-04-17 11:50:54 -0500483/* Additional details : If the option group exists it will force Auxiliary */
484/* Firmware Revision Information 4th byte to 1 indicating the build was */
485/* derived with additional edits */
Vernon Mauery86a50822019-03-25 13:11:36 -0700486int convertVersion(std::string s, Revision& rev)
Chris Austen7303bdc2016-04-17 11:50:54 -0500487{
Dinesh Chinari2b7e07d2017-11-08 15:38:50 -0600488 std::string token;
Chris Austen176c9652016-04-30 16:32:17 -0500489 uint16_t commits;
Chris Austen7303bdc2016-04-17 11:50:54 -0500490
Patrick Venture0b02be92018-08-31 11:55:55 -0700491 auto location = s.find_first_of('v');
Dinesh Chinari2b7e07d2017-11-08 15:38:50 -0600492 if (location != std::string::npos)
493 {
Patrick Venture0b02be92018-08-31 11:55:55 -0700494 s = s.substr(location + 1);
Chris Austen176c9652016-04-30 16:32:17 -0500495 }
Chris Austen7303bdc2016-04-17 11:50:54 -0500496
Dinesh Chinari2b7e07d2017-11-08 15:38:50 -0600497 if (!s.empty())
498 {
499 location = s.find_first_of(".");
500 if (location != std::string::npos)
501 {
Vernon Mauery86a50822019-03-25 13:11:36 -0700502 rev.major =
Patrick Venture0b02be92018-08-31 11:55:55 -0700503 static_cast<char>(std::stoi(s.substr(0, location), 0, 16));
504 token = s.substr(location + 1);
Dinesh Chinari2b7e07d2017-11-08 15:38:50 -0600505 }
Chris Austen176c9652016-04-30 16:32:17 -0500506
Dinesh Chinari2b7e07d2017-11-08 15:38:50 -0600507 if (!token.empty())
508 {
509 location = token.find_first_of(".-");
510 if (location != std::string::npos)
511 {
Vernon Mauery86a50822019-03-25 13:11:36 -0700512 rev.minor = static_cast<char>(
Patrick Venture0b02be92018-08-31 11:55:55 -0700513 std::stoi(token.substr(0, location), 0, 16));
514 token = token.substr(location + 1);
Dinesh Chinari2b7e07d2017-11-08 15:38:50 -0600515 }
516 }
Chris Austen7303bdc2016-04-17 11:50:54 -0500517
Dinesh Chinari2b7e07d2017-11-08 15:38:50 -0600518 // Capture the number of commits on top of the minor tag.
519 // I'm using BE format like the ipmi spec asked for
520 location = token.find_first_of(".-");
521 if (!token.empty())
522 {
523 commits = std::stoi(token.substr(0, location), 0, 16);
Vernon Mauery86a50822019-03-25 13:11:36 -0700524 rev.d[0] = (commits >> 8) | (commits << 8);
Dinesh Chinari2b7e07d2017-11-08 15:38:50 -0600525
526 // commit number we skip
527 location = token.find_first_of(".-");
528 if (location != std::string::npos)
529 {
Patrick Venture0b02be92018-08-31 11:55:55 -0700530 token = token.substr(location + 1);
Dinesh Chinari2b7e07d2017-11-08 15:38:50 -0600531 }
532 }
Patrick Venture0b02be92018-08-31 11:55:55 -0700533 else
534 {
Vernon Mauery86a50822019-03-25 13:11:36 -0700535 rev.d[0] = 0;
Dinesh Chinari2b7e07d2017-11-08 15:38:50 -0600536 }
537
538 if (location != std::string::npos)
539 {
Patrick Venture0b02be92018-08-31 11:55:55 -0700540 token = token.substr(location + 1);
Dinesh Chinari2b7e07d2017-11-08 15:38:50 -0600541 }
542
543 // Any value of the optional parameter forces it to 1
544 location = token.find_first_of(".-");
545 if (location != std::string::npos)
546 {
Patrick Venture0b02be92018-08-31 11:55:55 -0700547 token = token.substr(location + 1);
Dinesh Chinari2b7e07d2017-11-08 15:38:50 -0600548 }
549 commits = (!token.empty()) ? 1 : 0;
550
Patrick Venture0b02be92018-08-31 11:55:55 -0700551 // We do this operation to get this displayed in least significant bytes
552 // of ipmitool device id command.
Vernon Mauery86a50822019-03-25 13:11:36 -0700553 rev.d[1] = (commits >> 8) | (commits << 8);
Dinesh Chinari2b7e07d2017-11-08 15:38:50 -0600554 }
555
Chris Austen7303bdc2016-04-17 11:50:54 -0500556 return 0;
557}
558
Vernon Mauery86a50822019-03-25 13:11:36 -0700559auto ipmiAppGetDeviceId() -> ipmi::RspType<uint8_t, // Device ID
560 uint8_t, // Device Revision
561 uint8_t, // Firmware Revision Major
562 uint8_t, // Firmware Revision minor
563 uint8_t, // IPMI version
564 uint8_t, // Additional device support
565 uint24_t, // MFG ID
566 uint16_t, // Product ID
567 uint32_t // AUX info
568 >
Chris Austen6caf28b2015-10-13 12:40:40 -0500569{
Nagaraju Goruganti744398d2018-02-20 09:52:00 -0600570 int r = -1;
Vernon Mauery86a50822019-03-25 13:11:36 -0700571 Revision rev = {0};
572 static struct
573 {
574 uint8_t id;
575 uint8_t revision;
576 uint8_t fw[2];
577 uint8_t ipmiVer;
578 uint8_t addnDevSupport;
579 uint24_t manufId;
580 uint16_t prodId;
581 uint32_t aux;
582 } devId;
David Cobbleya1adb072017-11-21 15:58:13 -0800583 static bool dev_id_initialized = false;
584 const char* filename = "/usr/share/ipmi-providers/dev_id.json";
Alexander Amelkinba19c182018-09-04 15:49:36 +0300585 constexpr auto ipmiDevIdStateShift = 7;
586 constexpr auto ipmiDevIdFw1Mask = ~(1 << ipmiDevIdStateShift);
Chris Austen6caf28b2015-10-13 12:40:40 -0500587
David Cobbleya1adb072017-11-21 15:58:13 -0800588 if (!dev_id_initialized)
589 {
Nagaraju Goruganti744398d2018-02-20 09:52:00 -0600590 try
591 {
592 auto version = getActiveSoftwareVersionInfo();
Vernon Mauery86a50822019-03-25 13:11:36 -0700593 r = convertVersion(version, rev);
David Cobbleya1adb072017-11-21 15:58:13 -0800594 }
Nagaraju Goruganti744398d2018-02-20 09:52:00 -0600595 catch (const std::exception& e)
596 {
597 log<level::ERR>(e.what());
598 }
Nan Liee0cb902016-07-11 15:38:03 +0800599
Patrick Venture0b02be92018-08-31 11:55:55 -0700600 if (r >= 0)
601 {
Nagaraju Goruganti744398d2018-02-20 09:52:00 -0600602 // bit7 identifies if the device is available
603 // 0=normal operation
604 // 1=device firmware, SDR update,
605 // or self-initialization in progress.
Alexander Amelkinba19c182018-09-04 15:49:36 +0300606 // The availability may change in run time, so mask here
607 // and initialize later.
Vernon Mauery86a50822019-03-25 13:11:36 -0700608 devId.fw[0] = rev.major & ipmiDevIdFw1Mask;
Nagaraju Goruganti744398d2018-02-20 09:52:00 -0600609
610 rev.minor = (rev.minor > 99 ? 99 : rev.minor);
Vernon Mauery86a50822019-03-25 13:11:36 -0700611 devId.fw[1] = rev.minor % 10 + (rev.minor / 10) * 16;
612 std::memcpy(&devId.aux, rev.d, 4);
David Cobbleya1adb072017-11-21 15:58:13 -0800613 }
Nan Liee0cb902016-07-11 15:38:03 +0800614
David Cobbleya1adb072017-11-21 15:58:13 -0800615 // IPMI Spec version 2.0
Vernon Mauery86a50822019-03-25 13:11:36 -0700616 devId.ipmiVer = 2;
Adriana Kobylak0e912642016-06-22 16:54:39 -0500617
Vernon Mauery86a50822019-03-25 13:11:36 -0700618 std::ifstream devIdFile(filename);
619 if (devIdFile.is_open())
David Cobbleya1adb072017-11-21 15:58:13 -0800620 {
Vernon Mauery86a50822019-03-25 13:11:36 -0700621 auto data = nlohmann::json::parse(devIdFile, nullptr, false);
David Cobbleya1adb072017-11-21 15:58:13 -0800622 if (!data.is_discarded())
623 {
Vernon Mauery86a50822019-03-25 13:11:36 -0700624 devId.id = data.value("id", 0);
625 devId.revision = data.value("revision", 0);
626 devId.addnDevSupport = data.value("addn_dev_support", 0);
627 devId.manufId = data.value("manuf_id", 0);
628 devId.prodId = data.value("prod_id", 0);
629 devId.aux = data.value("aux", 0);
David Cobbleya1adb072017-11-21 15:58:13 -0800630
Patrick Venture0b02be92018-08-31 11:55:55 -0700631 // Don't read the file every time if successful
David Cobbleya1adb072017-11-21 15:58:13 -0800632 dev_id_initialized = true;
633 }
634 else
635 {
636 log<level::ERR>("Device ID JSON parser failure");
Vernon Maueryf2587fc2019-04-09 14:28:15 -0700637 return ipmi::responseUnspecifiedError();
David Cobbleya1adb072017-11-21 15:58:13 -0800638 }
639 }
640 else
641 {
642 log<level::ERR>("Device ID file not found");
Vernon Maueryf2587fc2019-04-09 14:28:15 -0700643 return ipmi::responseUnspecifiedError();
Chris Austen7303bdc2016-04-17 11:50:54 -0500644 }
645 }
Chris Austen6caf28b2015-10-13 12:40:40 -0500646
Alexander Amelkinba19c182018-09-04 15:49:36 +0300647 // Set availability to the actual current BMC state
Vernon Mauery86a50822019-03-25 13:11:36 -0700648 devId.fw[0] &= ipmiDevIdFw1Mask;
Alexander Amelkinba19c182018-09-04 15:49:36 +0300649 if (!getCurrentBmcState())
650 {
Vernon Mauery86a50822019-03-25 13:11:36 -0700651 devId.fw[0] |= (1 << ipmiDevIdStateShift);
Alexander Amelkinba19c182018-09-04 15:49:36 +0300652 }
653
Vernon Mauery86a50822019-03-25 13:11:36 -0700654 return ipmi::responseSuccess(
655 devId.id, devId.revision, devId.fw[0], devId.fw[1], devId.ipmiVer,
656 devId.addnDevSupport, devId.manufId, devId.prodId, devId.aux);
Chris Austen6caf28b2015-10-13 12:40:40 -0500657}
658
Vernon Maueryb84a5282019-03-25 13:39:03 -0700659auto ipmiAppGetSelfTestResults() -> ipmi::RspType<uint8_t, uint8_t>
Nan Li41fa24a2016-11-10 20:12:37 +0800660{
Nan Li41fa24a2016-11-10 20:12:37 +0800661 // Byte 2:
662 // 55h - No error.
Gunnar Mills8991dd62017-10-25 17:11:29 -0500663 // 56h - Self Test function not implemented in this controller.
Nan Li41fa24a2016-11-10 20:12:37 +0800664 // 57h - Corrupted or inaccesssible data or devices.
665 // 58h - Fatal hardware error.
666 // FFh - reserved.
667 // all other: Device-specific 'internal failure'.
668 // Byte 3:
669 // For byte 2 = 55h, 56h, FFh: 00h
670 // For byte 2 = 58h, all other: Device-specific
671 // For byte 2 = 57h: self-test error bitfield.
672 // Note: returning 57h does not imply that all test were run.
673 // [7] 1b = Cannot access SEL device.
674 // [6] 1b = Cannot access SDR Repository.
675 // [5] 1b = Cannot access BMC FRU device.
676 // [4] 1b = IPMB signal lines do not respond.
677 // [3] 1b = SDR Repository empty.
678 // [2] 1b = Internal Use Area of BMC FRU corrupted.
679 // [1] 1b = controller update 'boot block' firmware corrupted.
680 // [0] 1b = controller operational firmware corrupted.
Vernon Maueryb84a5282019-03-25 13:39:03 -0700681 constexpr uint8_t notImplemented = 0x56;
682 constexpr uint8_t zero = 0;
683 return ipmi::responseSuccess(notImplemented, zero);
Nan Li41fa24a2016-11-10 20:12:37 +0800684}
685
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500686ipmi_ret_t ipmi_app_get_device_guid(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
Patrick Venture0b02be92018-08-31 11:55:55 -0700687 ipmi_request_t request,
688 ipmi_response_t response,
689 ipmi_data_len_t data_len,
690 ipmi_context_t context)
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500691{
Patrick Venture0b02be92018-08-31 11:55:55 -0700692 const char* objname = "/org/openbmc/control/chassis0";
693 const char* iface = "org.freedesktop.DBus.Properties";
694 const char* chassis_iface = "org.openbmc.control.Chassis";
695 sd_bus_message* reply = NULL;
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500696 sd_bus_error error = SD_BUS_ERROR_NULL;
697 int r = 0;
Patrick Venture0b02be92018-08-31 11:55:55 -0700698 char* uuid = NULL;
699 char* busname = NULL;
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500700
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500701 // UUID is in RFC4122 format. Ex: 61a39523-78f2-11e5-9862-e6402cfc3223
Patrick Ventured2117022018-02-06 08:54:37 -0800702 // Per IPMI Spec 2.0 need to convert to 16 hex bytes and reverse the byte
703 // order
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500704 // Ex: 0x2332fc2c40e66298e511f2782395a361
705
Patrick Venture0b02be92018-08-31 11:55:55 -0700706 const int resp_size = 16; // Response is 16 hex bytes per IPMI Spec
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500707 uint8_t resp_uuid[resp_size]; // Array to hold the formatted response
Patrick Ventured2117022018-02-06 08:54:37 -0800708 // Point resp end of array to save in reverse order
Patrick Venture0b02be92018-08-31 11:55:55 -0700709 int resp_loc = resp_size - 1;
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500710 int i = 0;
Patrick Venture0b02be92018-08-31 11:55:55 -0700711 char* tokptr = NULL;
712 char* id_octet = NULL;
Emily Shafferedb8bb02018-09-27 14:50:15 -0700713 size_t total_uuid_size = 0;
714 // 1 byte of resp is built from 2 chars of uuid.
715 constexpr size_t max_uuid_size = 2 * resp_size;
vishwa1eaea4f2016-02-26 11:57:40 -0600716
717 // Status code.
718 ipmi_ret_t rc = IPMI_CC_OK;
719 *data_len = 0;
720
vishwa1eaea4f2016-02-26 11:57:40 -0600721 // Call Get properties method with the interface and property name
Sergey Solomineb9b8142016-08-23 09:07:28 -0500722 r = mapper_get_service(bus, objname, &busname);
Patrick Venture0b02be92018-08-31 11:55:55 -0700723 if (r < 0)
724 {
725 log<level::ERR>("Failed to get bus name", entry("BUS=%s", objname),
Aditya Saripalli5fb14602017-11-09 14:46:27 +0530726 entry("ERRNO=0x%X", -r));
Sergey Solomineb9b8142016-08-23 09:07:28 -0500727 goto finish;
728 }
Patrick Venture0b02be92018-08-31 11:55:55 -0700729 r = sd_bus_call_method(bus, busname, objname, iface, "Get", &error, &reply,
730 "ss", chassis_iface, "uuid");
vishwa1eaea4f2016-02-26 11:57:40 -0600731 if (r < 0)
732 {
Patrick Venture0b02be92018-08-31 11:55:55 -0700733 log<level::ERR>("Failed to call Get Method", entry("ERRNO=0x%X", -r));
vishwa1eaea4f2016-02-26 11:57:40 -0600734 rc = IPMI_CC_UNSPECIFIED_ERROR;
735 goto finish;
736 }
737
738 r = sd_bus_message_read(reply, "v", "s", &uuid);
739 if (r < 0 || uuid == NULL)
740 {
Patrick Venture0b02be92018-08-31 11:55:55 -0700741 log<level::ERR>("Failed to get a response", entry("ERRNO=0x%X", -r));
vishwa1eaea4f2016-02-26 11:57:40 -0600742 rc = IPMI_CC_RESPONSE_ERROR;
743 goto finish;
744 }
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500745
746 // Traverse the UUID
Patrick Ventured2117022018-02-06 08:54:37 -0800747 // Get the UUID octects separated by dash
748 id_octet = strtok_r(uuid, "-", &tokptr);
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500749
750 if (id_octet == NULL)
vishwa1eaea4f2016-02-26 11:57:40 -0600751 {
752 // Error
Patrick Venture0b02be92018-08-31 11:55:55 -0700753 log<level::ERR>("Unexpected UUID format", entry("UUID=%s", uuid));
vishwa1eaea4f2016-02-26 11:57:40 -0600754 rc = IPMI_CC_RESPONSE_ERROR;
755 goto finish;
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500756 }
757
758 while (id_octet != NULL)
759 {
760 // Calculate the octet string size since it varies
761 // Divide it by 2 for the array size since 1 byte is built from 2 chars
Patrick Venture0b02be92018-08-31 11:55:55 -0700762 int tmp_size = strlen(id_octet) / 2;
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500763
Emily Shafferedb8bb02018-09-27 14:50:15 -0700764 // Check if total UUID size has been exceeded
765 if ((total_uuid_size += strlen(id_octet)) > max_uuid_size)
766 {
767 // Error - UUID too long to store
768 log<level::ERR>("UUID too long", entry("UUID=%s", uuid));
769 rc = IPMI_CC_RESPONSE_ERROR;
770 goto finish;
771 }
772
Patrick Venture0b02be92018-08-31 11:55:55 -0700773 for (i = 0; i < tmp_size; i++)
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500774 {
Patrick Ventured2117022018-02-06 08:54:37 -0800775 // Holder of the 2 chars that will become a byte
776 char tmp_array[3] = {0};
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500777 strncpy(tmp_array, id_octet, 2); // 2 chars at a time
778
779 int resp_byte = strtoul(tmp_array, NULL, 16); // Convert to hex byte
Patrick Ventured2117022018-02-06 08:54:37 -0800780 // Copy end to first
Patrick Ventureb51bf9c2018-09-10 15:53:14 -0700781 std::memcpy((void*)&resp_uuid[resp_loc], &resp_byte, 1);
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500782 resp_loc--;
Patrick Venture0b02be92018-08-31 11:55:55 -0700783 id_octet += 2; // Finished with the 2 chars, advance
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500784 }
Patrick Venture0b02be92018-08-31 11:55:55 -0700785 id_octet = strtok_r(NULL, "-", &tokptr); // Get next octet
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500786 }
787
788 // Data length
789 *data_len = resp_size;
790
791 // Pack the actual response
Patrick Ventureb51bf9c2018-09-10 15:53:14 -0700792 std::memcpy(response, &resp_uuid, *data_len);
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500793
vishwa1eaea4f2016-02-26 11:57:40 -0600794finish:
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500795 sd_bus_error_free(&error);
vishwa1eaea4f2016-02-26 11:57:40 -0600796 reply = sd_bus_message_unref(reply);
Sergey Solomineb9b8142016-08-23 09:07:28 -0500797 free(busname);
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500798
799 return rc;
800}
Chris Austen6caf28b2015-10-13 12:40:40 -0500801
Vernon Mauerycc99ba42019-03-25 13:40:11 -0700802auto ipmiAppGetBtCapabilities()
803 -> ipmi::RspType<uint8_t, uint8_t, uint8_t, uint8_t, uint8_t>
vishwabmcba0bd5f2015-09-30 16:50:23 +0530804{
Adriana Kobylak88ad8152016-12-13 10:09:08 -0600805 // Per IPMI 2.0 spec, the input and output buffer size must be the max
806 // buffer size minus one byte to allocate space for the length byte.
Vernon Mauerycc99ba42019-03-25 13:40:11 -0700807 constexpr uint8_t nrOutstanding = 0x01;
808 constexpr uint8_t inputBufferSize = MAX_IPMI_BUFFER - 1;
809 constexpr uint8_t outputBufferSize = MAX_IPMI_BUFFER - 1;
810 constexpr uint8_t transactionTime = 0x0A;
811 constexpr uint8_t nrRetries = 0x01;
vishwabmcba0bd5f2015-09-30 16:50:23 +0530812
Vernon Mauerycc99ba42019-03-25 13:40:11 -0700813 return ipmi::responseSuccess(nrOutstanding, inputBufferSize,
814 outputBufferSize, transactionTime, nrRetries);
vishwabmcba0bd5f2015-09-30 16:50:23 +0530815}
816
Marri Devender Rao5e007a52018-01-08 06:18:36 -0600817ipmi_ret_t ipmi_app_get_sys_guid(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
Patrick Venture0b02be92018-08-31 11:55:55 -0700818 ipmi_request_t request,
819 ipmi_response_t response,
820 ipmi_data_len_t data_len,
821 ipmi_context_t context)
Marri Devender Rao5e007a52018-01-08 06:18:36 -0600822
823{
824 ipmi_ret_t rc = IPMI_CC_OK;
825 sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()};
826
827 try
828 {
829 // Get the Inventory object implementing BMC interface
830 ipmi::DbusObjectInfo bmcObject =
831 ipmi::getDbusObject(bus, bmc_interface);
832
833 // Read UUID property value from bmcObject
834 // UUID is in RFC4122 format Ex: 61a39523-78f2-11e5-9862-e6402cfc3223
Patrick Venture0b02be92018-08-31 11:55:55 -0700835 auto variant =
836 ipmi::getDbusProperty(bus, bmcObject.second, bmcObject.first,
837 bmc_guid_interface, bmc_guid_property);
Vernon Maueryf442e112019-04-09 11:44:36 -0700838 std::string guidProp = std::get<std::string>(variant);
Marri Devender Rao5e007a52018-01-08 06:18:36 -0600839
840 // Erase "-" characters from the property value
841 guidProp.erase(std::remove(guidProp.begin(), guidProp.end(), '-'),
Patrick Venture0b02be92018-08-31 11:55:55 -0700842 guidProp.end());
Marri Devender Rao5e007a52018-01-08 06:18:36 -0600843
844 auto guidPropLen = guidProp.length();
845 // Validate UUID data
846 // Divide by 2 as 1 byte is built from 2 chars
Patrick Venture0b02be92018-08-31 11:55:55 -0700847 if ((guidPropLen <= 0) || ((guidPropLen / 2) != bmc_guid_len))
Marri Devender Rao5e007a52018-01-08 06:18:36 -0600848
849 {
850 log<level::ERR>("Invalid UUID property value",
Patrick Venture0b02be92018-08-31 11:55:55 -0700851 entry("UUID_LENGTH=%d", guidPropLen));
Marri Devender Rao5e007a52018-01-08 06:18:36 -0600852 return IPMI_CC_RESPONSE_ERROR;
853 }
854
855 // Convert data in RFC4122(MSB) format to LSB format
856 // Get 2 characters at a time as 1 byte is built from 2 chars and
857 // convert to hex byte
858 // TODO: Data printed for GUID command is not as per the
859 // GUID format defined in IPMI specification 2.0 section 20.8
860 // Ticket raised: https://sourceforge.net/p/ipmitool/bugs/501/
861 uint8_t respGuid[bmc_guid_len];
862 for (size_t i = 0, respLoc = (bmc_guid_len - 1);
Patrick Venture0b02be92018-08-31 11:55:55 -0700863 i < guidPropLen && respLoc >= 0; i += 2, respLoc--)
Marri Devender Rao5e007a52018-01-08 06:18:36 -0600864 {
865 auto value = static_cast<uint8_t>(
Patrick Venture0b02be92018-08-31 11:55:55 -0700866 std::stoi(guidProp.substr(i, 2).c_str(), NULL, 16));
Marri Devender Rao5e007a52018-01-08 06:18:36 -0600867 respGuid[respLoc] = value;
868 }
869
870 *data_len = bmc_guid_len;
Patrick Ventureb51bf9c2018-09-10 15:53:14 -0700871 std::memcpy(response, &respGuid, bmc_guid_len);
Marri Devender Rao5e007a52018-01-08 06:18:36 -0600872 }
873 catch (const InternalFailure& e)
874 {
875 log<level::ERR>("Failed in reading BMC UUID property",
876 entry("INTERFACE=%s", bmc_interface),
877 entry("PROPERTY_INTERFACE=%s", bmc_guid_interface),
878 entry("PROPERTY=%s", bmc_guid_property));
879 return IPMI_CC_UNSPECIFIED_ERROR;
880 }
881 return rc;
882}
883
Xo Wangf542e8b2017-08-09 15:34:16 -0700884static std::unique_ptr<SysInfoParamStore> sysInfoParamStore;
885
Xo Wang87651332017-08-11 10:17:59 -0700886static std::string sysInfoReadSystemName()
887{
888 // Use the BMC hostname as the "System Name."
889 char hostname[HOST_NAME_MAX + 1] = {};
890 if (gethostname(hostname, HOST_NAME_MAX) != 0)
891 {
892 perror("System info parameter: system name");
893 }
894 return hostname;
895}
896
Xo Wangf542e8b2017-08-09 15:34:16 -0700897struct IpmiSysInfoResp
898{
899 uint8_t paramRevision;
900 uint8_t setSelector;
901 union
902 {
903 struct
904 {
905 uint8_t encoding;
906 uint8_t stringLen;
907 uint8_t stringData0[14];
908 } __attribute__((packed));
909 uint8_t stringDataN[16];
910 uint8_t byteData;
911 };
912} __attribute__((packed));
913
914/**
915 * Split a string into (up to) 16-byte chunks as expected in response for get
916 * system info parameter.
917 *
918 * @param[in] fullString: Input string to be split
919 * @param[in] chunkIndex: Index of the chunk to be written out
920 * @param[in,out] chunk: Output data buffer; must have 14 byte capacity if
921 * chunk_index = 0 and 16-byte capacity otherwise
922 * @return the number of bytes written into the output buffer, or -EINVAL for
923 * invalid arguments.
924 */
925static int splitStringParam(const std::string& fullString, int chunkIndex,
926 uint8_t* chunk)
927{
928 constexpr int maxChunk = 255;
929 constexpr int smallChunk = 14;
930 constexpr int chunkSize = 16;
931 if (chunkIndex > maxChunk || chunk == nullptr)
932 {
933 return -EINVAL;
934 }
935 try
936 {
937 std::string output;
938 if (chunkIndex == 0)
939 {
940 // Output must have 14 byte capacity.
941 output = fullString.substr(0, smallChunk);
942 }
943 else
944 {
945 // Output must have 16 byte capacity.
946 output = fullString.substr((chunkIndex * chunkSize) - 2, chunkSize);
947 }
948
949 std::memcpy(chunk, output.c_str(), output.length());
950 return output.length();
951 }
952 catch (const std::out_of_range& e)
953 {
954 // The position was beyond the end.
955 return -EINVAL;
956 }
957}
958
959/**
960 * Packs the Get Sys Info Request Item into the response.
961 *
962 * @param[in] paramString - the parameter.
963 * @param[in] setSelector - the selector
964 * @param[in,out] resp - the System info response.
965 * @return The number of bytes packed or failure from splitStringParam().
966 */
967static int packGetSysInfoResp(const std::string& paramString,
968 uint8_t setSelector, IpmiSysInfoResp* resp)
969{
970 uint8_t* dataBuffer = resp->stringDataN;
971 resp->setSelector = setSelector;
972 if (resp->setSelector == 0) // First chunk has only 14 bytes.
973 {
974 resp->encoding = 0;
975 resp->stringLen = paramString.length();
976 dataBuffer = resp->stringData0;
977 }
978 return splitStringParam(paramString, resp->setSelector, dataBuffer);
979}
980
981ipmi_ret_t ipmi_app_get_system_info(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
982 ipmi_request_t request,
983 ipmi_response_t response,
984 ipmi_data_len_t dataLen,
985 ipmi_context_t context)
986{
987 IpmiSysInfoResp resp = {};
988 size_t respLen = 0;
989 uint8_t* const reqData = static_cast<uint8_t*>(request);
990 std::string paramString;
991 bool found;
992 std::tuple<bool, std::string> ret;
993 constexpr int minRequestSize = 4;
994 constexpr int paramSelector = 1;
995 constexpr uint8_t revisionOnly = 0x80;
996 const uint8_t paramRequested = reqData[paramSelector];
997 int rc;
998
999 if (*dataLen < minRequestSize)
1000 {
1001 return IPMI_CC_REQ_DATA_LEN_INVALID;
1002 }
1003
1004 *dataLen = 0; // default to 0.
1005
1006 // Parameters revision as of IPMI spec v2.0 rev. 1.1 (Feb 11, 2014 E6)
1007 resp.paramRevision = 0x11;
1008 if (reqData[0] & revisionOnly) // Get parameter revision only
1009 {
1010 respLen = 1;
1011 goto writeResponse;
1012 }
1013
1014 // The "Set In Progress" parameter can be used for rollback of parameter
1015 // data and is not implemented.
1016 if (paramRequested == 0)
1017 {
1018 resp.byteData = 0;
1019 respLen = 2;
1020 goto writeResponse;
1021 }
1022
1023 if (sysInfoParamStore == nullptr)
1024 {
1025 sysInfoParamStore = std::make_unique<SysInfoParamStore>();
Xo Wang87651332017-08-11 10:17:59 -07001026 sysInfoParamStore->update(IPMI_SYSINFO_SYSTEM_NAME,
1027 sysInfoReadSystemName);
Xo Wangf542e8b2017-08-09 15:34:16 -07001028 }
1029
1030 // Parameters other than Set In Progress are assumed to be strings.
1031 ret = sysInfoParamStore->lookup(paramRequested);
1032 found = std::get<0>(ret);
1033 paramString = std::get<1>(ret);
1034 if (!found)
1035 {
1036 return IPMI_CC_SYSTEM_INFO_PARAMETER_NOT_SUPPORTED;
1037 }
1038 // TODO: Cache each parameter across multiple calls, until the whole string
1039 // has been read out. Otherwise, it's possible for a parameter to change
1040 // between requests for its chunks, returning chunks incoherent with each
1041 // other. For now, the parameter store is simply required to have only
1042 // idempotent callbacks.
1043 rc = packGetSysInfoResp(paramString, reqData[2], &resp);
1044 if (rc == -EINVAL)
1045 {
1046 return IPMI_CC_RESPONSE_ERROR;
1047 }
1048
1049 respLen = sizeof(resp); // Write entire string data chunk in response.
1050
1051writeResponse:
1052 std::memcpy(response, &resp, sizeof(resp));
1053 *dataLen = respLen;
1054 return IPMI_CC_OK;
1055}
1056
Richard Marian Thomaiyar84bf9be2019-04-26 22:59:16 +05301057inline std::vector<uint8_t> convertStringToData(const std::string& command)
1058{
1059 std::istringstream iss(command);
1060 std::string token;
1061 std::vector<uint8_t> dataValue;
1062 while (std::getline(iss, token, ' '))
1063 {
1064 dataValue.emplace_back(
1065 static_cast<uint8_t>(std::stoul(token, nullptr, base_16)));
1066 }
1067 return dataValue;
1068}
1069
1070static bool populateI2CMasterWRWhitelist()
1071{
1072 nlohmann::json data = nullptr;
1073 std::ifstream jsonFile(i2cMasterWRWhitelistFile);
1074
1075 if (!jsonFile.good())
1076 {
1077 log<level::WARNING>("i2c white list file not found!",
1078 entry("FILE_NAME: %s", i2cMasterWRWhitelistFile));
1079 return false;
1080 }
1081
1082 try
1083 {
1084 data = nlohmann::json::parse(jsonFile, nullptr, false);
1085 }
1086 catch (nlohmann::json::parse_error& e)
1087 {
1088 log<level::ERR>("Corrupted i2c white list config file",
1089 entry("FILE_NAME: %s", i2cMasterWRWhitelistFile),
1090 entry("MSG: %s", e.what()));
1091 return false;
1092 }
1093
1094 try
1095 {
1096 // Example JSON Structure format
1097 // "filters": [
1098 // {
1099 // "Description": "Allow full read - ignore first byte write value
1100 // for 0x40 to 0x4F",
1101 // "busId": "0x01",
1102 // "slaveAddr": "0x40",
1103 // "slaveAddrMask": "0x0F",
1104 // "command": "0x00",
1105 // "commandMask": "0xFF"
1106 // },
1107 // {
1108 // "Description": "Allow full read - first byte match 0x05 and
1109 // ignore second byte",
1110 // "busId": "0x01",
1111 // "slaveAddr": "0x57",
1112 // "slaveAddrMask": "0x00",
1113 // "command": "0x05 0x00",
1114 // "commandMask": "0x00 0xFF"
1115 // },]
1116
1117 nlohmann::json filters = data[filtersStr].get<nlohmann::json>();
1118 std::vector<i2cMasterWRWhitelist>& whitelist = getWRWhitelist();
1119 for (const auto& it : filters.items())
1120 {
1121 nlohmann::json filter = it.value();
1122 if (filter.is_null())
1123 {
1124 log<level::ERR>(
1125 "Corrupted I2C master write read whitelist config file",
1126 entry("FILE_NAME: %s", i2cMasterWRWhitelistFile));
1127 return false;
1128 }
1129 const std::vector<uint8_t>& writeData =
1130 convertStringToData(filter[cmdStr].get<std::string>());
1131 const std::vector<uint8_t>& writeDataMask =
1132 convertStringToData(filter[cmdMaskStr].get<std::string>());
1133 if (writeDataMask.size() != writeData.size())
1134 {
1135 log<level::ERR>("I2C master write read whitelist filter "
1136 "mismatch for command & mask size");
1137 return false;
1138 }
1139 whitelist.push_back(
1140 {static_cast<uint8_t>(std::stoul(
1141 filter[busIdStr].get<std::string>(), nullptr, base_16)),
1142 static_cast<uint8_t>(
1143 std::stoul(filter[slaveAddrStr].get<std::string>(),
1144 nullptr, base_16)),
1145 static_cast<uint8_t>(
1146 std::stoul(filter[slaveAddrMaskStr].get<std::string>(),
1147 nullptr, base_16)),
1148 writeData, writeDataMask});
1149 }
1150 if (whitelist.size() != filters.size())
1151 {
1152 log<level::ERR>(
1153 "I2C master write read whitelist filter size mismatch");
1154 return false;
1155 }
1156 }
1157 catch (std::exception& e)
1158 {
1159 log<level::ERR>("I2C master write read whitelist unexpected exception",
1160 entry("ERROR=%s", e.what()));
1161 return false;
1162 }
1163 return true;
1164}
1165
1166static inline bool isWriteDataWhitelisted(const std::vector<uint8_t>& data,
1167 const std::vector<uint8_t>& dataMask,
1168 const std::vector<uint8_t>& writeData)
1169{
1170 std::vector<uint8_t> processedDataBuf(data.size());
1171 std::vector<uint8_t> processedReqBuf(dataMask.size());
1172 std::transform(writeData.begin(), writeData.end(), dataMask.begin(),
1173 processedReqBuf.begin(), std::bit_or<uint8_t>());
1174 std::transform(data.begin(), data.end(), dataMask.begin(),
1175 processedDataBuf.begin(), std::bit_or<uint8_t>());
1176
1177 return (processedDataBuf == processedReqBuf);
1178}
1179
1180static bool isCmdWhitelisted(uint8_t busId, uint8_t slaveAddr,
1181 std::vector<uint8_t>& writeData)
1182{
1183 std::vector<i2cMasterWRWhitelist>& whiteList = getWRWhitelist();
1184 for (const auto& wlEntry : whiteList)
1185 {
1186 if ((busId == wlEntry.busId) &&
1187 ((slaveAddr | wlEntry.slaveAddrMask) ==
1188 (wlEntry.slaveAddr | wlEntry.slaveAddrMask)))
1189 {
1190 const std::vector<uint8_t>& dataMask = wlEntry.dataMask;
1191 // Skip as no-match, if requested write data is more than the
1192 // write data mask size
1193 if (writeData.size() > dataMask.size())
1194 {
1195 continue;
1196 }
1197 if (isWriteDataWhitelisted(wlEntry.data, dataMask, writeData))
1198 {
1199 return true;
1200 }
1201 }
1202 }
1203 return false;
1204}
1205
1206/** @brief implements master write read IPMI command which can be used for
1207 * low-level I2C/SMBus write, read or write-read access
1208 * @param isPrivateBus -to indicate private bus usage
1209 * @param busId - bus id
1210 * @param channelNum - channel number
1211 * @param reserved - skip 1 bit
1212 * @param slaveAddr - slave address
1213 * @param read count - number of bytes to be read
1214 * @param writeData - data to be written
1215 *
1216 * @returns IPMI completion code plus response data
1217 * - readData - i2c response data
1218 */
1219ipmi::RspType<std::vector<uint8_t>>
1220 ipmiMasterWriteRead(bool isPrivateBus, uint3_t busId, uint4_t channelNum,
1221 bool reserved, uint7_t slaveAddr, uint8_t readCount,
1222 std::vector<uint8_t> writeData)
1223{
1224 i2c_rdwr_ioctl_data msgReadWrite = {0};
1225 i2c_msg i2cmsg[2] = {0};
1226
1227 if (readCount > maxIPMIWriteReadSize)
1228 {
1229 log<level::ERR>("Master write read command: Read count exceeds limit");
1230 return ipmi::responseParmOutOfRange();
1231 }
1232 const size_t writeCount = writeData.size();
1233 if (!readCount && !writeCount)
1234 {
1235 log<level::ERR>("Master write read command: Read & write count are 0");
1236 return ipmi::responseInvalidFieldRequest();
1237 }
1238 if (!isCmdWhitelisted(static_cast<uint8_t>(busId),
1239 static_cast<uint8_t>(slaveAddr), writeData))
1240 {
1241 log<level::ERR>("Master write read request blocked!",
1242 entry("BUS=%d", static_cast<uint8_t>(busId)),
1243 entry("ADDR=0x%x", static_cast<uint8_t>(slaveAddr)));
1244 return ipmi::responseInvalidFieldRequest();
1245 }
1246 std::vector<uint8_t> readBuf(readCount);
1247 std::string i2cBus =
1248 "/dev/i2c-" + std::to_string(static_cast<uint8_t>(busId));
1249
1250 int i2cDev = ::open(i2cBus.c_str(), O_RDWR | O_CLOEXEC);
1251 if (i2cDev < 0)
1252 {
1253 log<level::ERR>("Failed to open i2c bus",
1254 entry("BUS=%s", i2cBus.c_str()));
1255 return ipmi::responseInvalidFieldRequest();
1256 }
1257
1258 int msgCount = 0;
1259 if (writeCount)
1260 {
1261 i2cmsg[msgCount].addr = static_cast<uint8_t>(slaveAddr);
1262 i2cmsg[msgCount].flags = 0x00;
1263 i2cmsg[msgCount].len = writeCount;
1264 i2cmsg[msgCount].buf = writeData.data();
1265 msgCount++;
1266 }
1267 if (readCount)
1268 {
1269 i2cmsg[msgCount].addr = static_cast<uint8_t>(slaveAddr);
1270 i2cmsg[msgCount].flags = I2C_M_RD;
1271 i2cmsg[msgCount].len = readCount;
1272 i2cmsg[msgCount].buf = readBuf.data();
1273 msgCount++;
1274 }
1275
1276 msgReadWrite.msgs = i2cmsg;
1277 msgReadWrite.nmsgs = msgCount;
1278
1279 int ret = ::ioctl(i2cDev, I2C_RDWR, &msgReadWrite);
1280 ::close(i2cDev);
1281
1282 if (ret < 0)
1283 {
1284 log<level::ERR>("Master write read: Failed", entry("RET=%d", ret));
1285 return ipmi::responseUnspecifiedError();
1286 }
1287 if (readCount)
1288 {
1289 readBuf.resize(msgReadWrite.msgs[msgCount - 1].len);
1290 }
1291 return ipmi::responseSuccess(readBuf);
1292}
1293
Chris Austen6caf28b2015-10-13 12:40:40 -05001294void register_netfn_app_functions()
vishwabmcba0bd5f2015-09-30 16:50:23 +05301295{
Vernon Mauery86a50822019-03-25 13:11:36 -07001296 // <Get Device ID>
1297 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp,
1298 ipmi::app::cmdGetDeviceId, ipmi::Privilege::User,
1299 ipmiAppGetDeviceId);
1300
Tom05732372016-09-06 17:21:23 +05301301 // <Get BT Interface Capabilities>
Vernon Mauerycc99ba42019-03-25 13:40:11 -07001302 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp,
1303 ipmi::app::cmdGetBtIfaceCapabilities,
1304 ipmi::Privilege::User, ipmiAppGetBtCapabilities);
Chris Austen6caf28b2015-10-13 12:40:40 -05001305
Tom05732372016-09-06 17:21:23 +05301306 // <Reset Watchdog Timer>
Vernon Mauery11df4f62019-03-25 14:17:54 -07001307 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp,
1308 ipmi::app::cmdResetWatchdogTimer,
1309 ipmi::Privilege::Operator, ipmiAppResetWatchdogTimer);
Chris Austen6caf28b2015-10-13 12:40:40 -05001310
Tom05732372016-09-06 17:21:23 +05301311 // <Set Watchdog Timer>
Patrick Venture0b02be92018-08-31 11:55:55 -07001312 ipmi_register_callback(NETFUN_APP, IPMI_CMD_SET_WD, NULL,
1313 ipmi_app_watchdog_set, PRIVILEGE_OPERATOR);
Chris Austen6caf28b2015-10-13 12:40:40 -05001314
William A. Kennington III73f44512018-02-09 15:28:46 -08001315 // <Get Watchdog Timer>
Patrick Venture0b02be92018-08-31 11:55:55 -07001316 ipmi_register_callback(NETFUN_APP, IPMI_CMD_GET_WD, NULL,
1317 ipmi_app_watchdog_get, PRIVILEGE_OPERATOR);
William A. Kennington III73f44512018-02-09 15:28:46 -08001318
Tom05732372016-09-06 17:21:23 +05301319 // <Get Self Test Results>
Vernon Maueryb84a5282019-03-25 13:39:03 -07001320 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp,
1321 ipmi::app::cmdGetSelfTestResults,
1322 ipmi::Privilege::User, ipmiAppGetSelfTestResults);
Nan Li41fa24a2016-11-10 20:12:37 +08001323
Tom05732372016-09-06 17:21:23 +05301324 // <Get Device GUID>
Patrick Venture0b02be92018-08-31 11:55:55 -07001325 ipmi_register_callback(NETFUN_APP, IPMI_CMD_GET_DEVICE_GUID, NULL,
1326 ipmi_app_get_device_guid, PRIVILEGE_USER);
Adriana Kobylakd100ee52015-10-20 17:02:37 -05001327
Tom05732372016-09-06 17:21:23 +05301328 // <Set ACPI Power State>
Patrick Venture0b02be92018-08-31 11:55:55 -07001329 ipmi_register_callback(NETFUN_APP, IPMI_CMD_SET_ACPI, NULL,
1330 ipmi_app_set_acpi_power_state, PRIVILEGE_ADMIN);
Chris Austen6caf28b2015-10-13 12:40:40 -05001331
Yong Li18d77262018-10-09 01:59:45 +08001332 // <Get ACPI Power State>
1333 ipmi_register_callback(NETFUN_APP, IPMI_CMD_GET_ACPI, NULL,
1334 ipmi_app_get_acpi_power_state, PRIVILEGE_ADMIN);
1335
Richard Marian Thomaiyar84bf9be2019-04-26 22:59:16 +05301336 // Note: For security reason, this command will be registered only when
1337 // there are proper I2C Master write read whitelist
1338 if (populateI2CMasterWRWhitelist())
1339 {
1340 // Note: For security reasons, registering master write read as admin
1341 // privilege command, even though IPMI 2.0 specification allows it as
1342 // operator privilege.
1343 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp,
1344 ipmi::app::cmdMasterWriteRead,
1345 ipmi::Privilege::Admin, ipmiMasterWriteRead);
1346 }
1347
Marri Devender Rao5e007a52018-01-08 06:18:36 -06001348 // <Get System GUID Command>
Patrick Venture0b02be92018-08-31 11:55:55 -07001349 ipmi_register_callback(NETFUN_APP, IPMI_CMD_GET_SYS_GUID, NULL,
1350 ipmi_app_get_sys_guid, PRIVILEGE_USER);
Tom Joseph7cbe2282018-03-21 21:17:33 +05301351
1352 // <Get Channel Cipher Suites Command>
Patrick Venture0b02be92018-08-31 11:55:55 -07001353 ipmi_register_callback(NETFUN_APP, IPMI_CMD_GET_CHAN_CIPHER_SUITES, NULL,
1354 getChannelCipherSuites, PRIVILEGE_CALLBACK);
Vernon Maueryd2a57de2019-03-25 12:45:28 -07001355
Xo Wangf542e8b2017-08-09 15:34:16 -07001356 // <Get System Info Command>
1357 ipmi_register_callback(NETFUN_APP, IPMI_CMD_GET_SYSTEM_INFO, NULL,
1358 ipmi_app_get_system_info, PRIVILEGE_USER);
vishwabmcba0bd5f2015-09-30 16:50:23 +05301359 return;
1360}