blob: 0c8eb9229e820a7d3ca1fb3c6ee272dda28688f1 [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>
Rajashekar Gade Reddye7023922019-07-10 16:54:55 +000023#include <ipmid/sessiondef.hpp>
24#include <ipmid/sessionhelper.hpp>
Vernon Mauery33250242019-03-12 16:49:26 -070025#include <ipmid/types.hpp>
Vernon Mauery6a98fe72019-03-11 15:57:48 -070026#include <ipmid/utils.hpp>
Patrick Venture3a5071a2018-09-12 13:27:42 -070027#include <memory>
Patrick Venture46470a32018-09-07 19:26:25 -070028#include <nlohmann/json.hpp>
Patrick Venture3a5071a2018-09-12 13:27:42 -070029#include <phosphor-logging/elog-errors.hpp>
30#include <phosphor-logging/log.hpp>
William A. Kennington III4c008022018-10-12 17:18:14 -070031#include <sdbusplus/message/types.hpp>
Patrick Venture3a5071a2018-09-12 13:27:42 -070032#include <string>
Vernon Mauery0120b682019-03-25 13:08:54 -070033#include <sys_info_param.hpp>
34#include <transporthandler.hpp>
Patrick Venture3a5071a2018-09-12 13:27:42 -070035#include <tuple>
36#include <vector>
37#include <xyz/openbmc_project/Common/error.hpp>
Yong Li18d77262018-10-09 01:59:45 +080038#include <xyz/openbmc_project/Control/Power/ACPIPowerState/server.hpp>
Patrick Venture3a5071a2018-09-12 13:27:42 -070039#include <xyz/openbmc_project/Software/Activation/server.hpp>
40#include <xyz/openbmc_project/Software/Version/server.hpp>
41#include <xyz/openbmc_project/State/BMC/server.hpp>
Ratan Guptab8e99552017-07-27 07:07:48 +053042
Patrick Venture0b02be92018-08-31 11:55:55 -070043extern sd_bus* bus;
vishwabmcba0bd5f2015-09-30 16:50:23 +053044
Alexander Amelkinba19c182018-09-04 15:49:36 +030045constexpr auto bmc_state_interface = "xyz.openbmc_project.State.BMC";
46constexpr auto bmc_state_property = "CurrentBMCState";
Marri Devender Rao5e007a52018-01-08 06:18:36 -060047
Nagaraju Goruganti744398d2018-02-20 09:52:00 -060048static constexpr auto redundancyIntf =
49 "xyz.openbmc_project.Software.RedundancyPriority";
Patrick Venture0b02be92018-08-31 11:55:55 -070050static constexpr auto versionIntf = "xyz.openbmc_project.Software.Version";
Nagaraju Goruganti744398d2018-02-20 09:52:00 -060051static constexpr auto activationIntf =
52 "xyz.openbmc_project.Software.Activation";
53static constexpr auto softwareRoot = "/xyz/openbmc_project/software";
54
Chris Austen6caf28b2015-10-13 12:40:40 -050055void register_netfn_app_functions() __attribute__((constructor));
vishwabmcba0bd5f2015-09-30 16:50:23 +053056
Ratan Guptab8e99552017-07-27 07:07:48 +053057using namespace phosphor::logging;
58using namespace sdbusplus::xyz::openbmc_project::Common::Error;
Nagaraju Goruganti744398d2018-02-20 09:52:00 -060059using Version = sdbusplus::xyz::openbmc_project::Software::server::Version;
60using Activation =
61 sdbusplus::xyz::openbmc_project::Software::server::Activation;
Alexander Amelkinba19c182018-09-04 15:49:36 +030062using BMC = sdbusplus::xyz::openbmc_project::State::server::BMC;
Vernon Mauery185b9f82018-07-20 10:52:36 -070063namespace fs = std::filesystem;
Ratan Guptab8e99552017-07-27 07:07:48 +053064
Yong Libd0503a2019-08-22 17:17:17 +080065#ifdef ENABLE_I2C_WHITELIST_CHECK
Richard Marian Thomaiyar84bf9be2019-04-26 22:59:16 +053066typedef struct
67{
68 uint8_t busId;
69 uint8_t slaveAddr;
70 uint8_t slaveAddrMask;
71 std::vector<uint8_t> data;
72 std::vector<uint8_t> dataMask;
73} i2cMasterWRWhitelist;
74
75static std::vector<i2cMasterWRWhitelist>& getWRWhitelist()
76{
77 static std::vector<i2cMasterWRWhitelist> wrWhitelist;
78 return wrWhitelist;
79}
80
81static constexpr const char* i2cMasterWRWhitelistFile =
82 "/usr/share/ipmi-providers/master_write_read_white_list.json";
83
Richard Marian Thomaiyar84bf9be2019-04-26 22:59:16 +053084static constexpr const char* filtersStr = "filters";
85static constexpr const char* busIdStr = "busId";
86static constexpr const char* slaveAddrStr = "slaveAddr";
87static constexpr const char* slaveAddrMaskStr = "slaveAddrMask";
88static constexpr const char* cmdStr = "command";
89static constexpr const char* cmdMaskStr = "commandMask";
90static constexpr int base_16 = 16;
Yong Libd0503a2019-08-22 17:17:17 +080091#endif // ENABLE_I2C_WHITELIST_CHECK
92static constexpr uint8_t maxIPMIWriteReadSize = 144;
Richard Marian Thomaiyar84bf9be2019-04-26 22:59:16 +053093
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 */
Vernon Maueryea1c4012019-05-24 13:26:16 -0700105std::string getActiveSoftwareVersionInfo(ipmi::Context::ptr ctx)
Nagaraju Goruganti744398d2018-02-20 09:52:00 -0600106{
Nagaraju Goruganti744398d2018-02-20 09:52:00 -0600107 std::string revision{};
Vernon Mauery86a50822019-03-25 13:11:36 -0700108 ipmi::ObjectTree objectTree;
109 try
Nagaraju Goruganti744398d2018-02-20 09:52:00 -0600110 {
Vernon Mauery86a50822019-03-25 13:11:36 -0700111 objectTree =
Vernon Maueryea1c4012019-05-24 13:26:16 -0700112 ipmi::getAllDbusObjects(*ctx->bus, softwareRoot, redundancyIntf);
Vernon Mauery86a50822019-03-25 13:11:36 -0700113 }
114 catch (sdbusplus::exception::SdBusError& e)
115 {
116 log<level::ERR>("Failed to fetch redundancy object from dbus",
117 entry("INTERFACE=%s", redundancyIntf),
118 entry("ERRMSG=%s", e.what()));
Nagaraju Goruganti744398d2018-02-20 09:52:00 -0600119 elog<InternalFailure>();
120 }
121
122 auto objectFound = false;
123 for (auto& softObject : objectTree)
124 {
Vernon Mauery86a50822019-03-25 13:11:36 -0700125 auto service =
Vernon Maueryea1c4012019-05-24 13:26:16 -0700126 ipmi::getService(*ctx->bus, redundancyIntf, softObject.first);
Vernon Mauery86a50822019-03-25 13:11:36 -0700127 auto objValueTree =
Vernon Maueryea1c4012019-05-24 13:26:16 -0700128 ipmi::getManagedObjects(*ctx->bus, service, softwareRoot);
Nagaraju Goruganti744398d2018-02-20 09:52:00 -0600129
130 auto minPriority = 0xFF;
131 for (const auto& objIter : objValueTree)
132 {
133 try
134 {
135 auto& intfMap = objIter.second;
136 auto& redundancyPriorityProps = intfMap.at(redundancyIntf);
137 auto& versionProps = intfMap.at(versionIntf);
138 auto& activationProps = intfMap.at(activationIntf);
Vernon Maueryf442e112019-04-09 11:44:36 -0700139 auto priority =
140 std::get<uint8_t>(redundancyPriorityProps.at("Priority"));
William A. Kennington III4c008022018-10-12 17:18:14 -0700141 auto purpose =
Vernon Maueryf442e112019-04-09 11:44:36 -0700142 std::get<std::string>(versionProps.at("Purpose"));
143 auto activation =
144 std::get<std::string>(activationProps.at("Activation"));
William A. Kennington III4c008022018-10-12 17:18:14 -0700145 auto version =
Vernon Maueryf442e112019-04-09 11:44:36 -0700146 std::get<std::string>(versionProps.at("Version"));
Nagaraju Goruganti744398d2018-02-20 09:52:00 -0600147 if ((Version::convertVersionPurposeFromString(purpose) ==
148 Version::VersionPurpose::BMC) &&
149 (Activation::convertActivationsFromString(activation) ==
150 Activation::Activations::Active))
151 {
152 if (priority < minPriority)
153 {
154 minPriority = priority;
155 objectFound = true;
156 revision = std::move(version);
157 }
158 }
159 }
160 catch (const std::exception& e)
161 {
162 log<level::ERR>(e.what());
163 }
164 }
165 }
166
167 if (!objectFound)
168 {
169 log<level::ERR>("Could not found an BMC software Object");
170 elog<InternalFailure>();
171 }
172
173 return revision;
174}
175
Alexander Amelkinba19c182018-09-04 15:49:36 +0300176bool getCurrentBmcState()
177{
178 sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()};
179
180 // Get the Inventory object implementing the BMC interface
181 ipmi::DbusObjectInfo bmcObject =
182 ipmi::getDbusObject(bus, bmc_state_interface);
183 auto variant =
184 ipmi::getDbusProperty(bus, bmcObject.second, bmcObject.first,
185 bmc_state_interface, bmc_state_property);
186
Vernon Maueryf442e112019-04-09 11:44:36 -0700187 return std::holds_alternative<std::string>(variant) &&
188 BMC::convertBMCStateFromString(std::get<std::string>(variant)) ==
189 BMC::BMCState::Ready;
Alexander Amelkinba19c182018-09-04 15:49:36 +0300190}
191
Patrick Venture94930a12019-04-30 10:01:58 -0700192bool getCurrentBmcStateWithFallback(const bool fallbackAvailability)
193{
194 try
195 {
196 return getCurrentBmcState();
197 }
198 catch (...)
199 {
200 // Nothing provided the BMC interface, therefore return whatever was
201 // configured as the default.
202 return fallbackAvailability;
203 }
204}
205
Yong Li18d77262018-10-09 01:59:45 +0800206namespace acpi_state
207{
208using namespace sdbusplus::xyz::openbmc_project::Control::Power::server;
209
210const static constexpr char* acpiObjPath =
211 "/xyz/openbmc_project/control/host0/acpi_power_state";
212const static constexpr char* acpiInterface =
213 "xyz.openbmc_project.Control.Power.ACPIPowerState";
214const static constexpr char* sysACPIProp = "SysACPIStatus";
215const static constexpr char* devACPIProp = "DevACPIStatus";
216
217enum class PowerStateType : uint8_t
218{
219 sysPowerState = 0x00,
220 devPowerState = 0x01,
221};
222
223// Defined in 20.6 of ipmi doc
224enum class PowerState : uint8_t
225{
226 s0G0D0 = 0x00,
227 s1D1 = 0x01,
228 s2D2 = 0x02,
229 s3D3 = 0x03,
230 s4 = 0x04,
231 s5G2 = 0x05,
232 s4S5 = 0x06,
233 g3 = 0x07,
234 sleep = 0x08,
235 g1Sleep = 0x09,
236 override = 0x0a,
237 legacyOn = 0x20,
238 legacyOff = 0x21,
239 unknown = 0x2a,
240 noChange = 0x7f,
241};
242
243static constexpr uint8_t stateChanged = 0x80;
244
245struct ACPIState
246{
247 uint8_t sysACPIState;
248 uint8_t devACPIState;
249} __attribute__((packed));
250
251std::map<ACPIPowerState::ACPI, PowerState> dbusToIPMI = {
252 {ACPIPowerState::ACPI::S0_G0_D0, PowerState::s0G0D0},
253 {ACPIPowerState::ACPI::S1_D1, PowerState::s1D1},
254 {ACPIPowerState::ACPI::S2_D2, PowerState::s2D2},
255 {ACPIPowerState::ACPI::S3_D3, PowerState::s3D3},
256 {ACPIPowerState::ACPI::S4, PowerState::s4},
257 {ACPIPowerState::ACPI::S5_G2, PowerState::s5G2},
258 {ACPIPowerState::ACPI::S4_S5, PowerState::s4S5},
259 {ACPIPowerState::ACPI::G3, PowerState::g3},
260 {ACPIPowerState::ACPI::SLEEP, PowerState::sleep},
261 {ACPIPowerState::ACPI::G1_SLEEP, PowerState::g1Sleep},
262 {ACPIPowerState::ACPI::OVERRIDE, PowerState::override},
263 {ACPIPowerState::ACPI::LEGACY_ON, PowerState::legacyOn},
264 {ACPIPowerState::ACPI::LEGACY_OFF, PowerState::legacyOff},
265 {ACPIPowerState::ACPI::Unknown, PowerState::unknown}};
266
267bool isValidACPIState(acpi_state::PowerStateType type, uint8_t state)
268{
269 if (type == acpi_state::PowerStateType::sysPowerState)
270 {
271 if ((state <= static_cast<uint8_t>(acpi_state::PowerState::override)) ||
272 (state == static_cast<uint8_t>(acpi_state::PowerState::legacyOn)) ||
273 (state ==
274 static_cast<uint8_t>(acpi_state::PowerState::legacyOff)) ||
275 (state == static_cast<uint8_t>(acpi_state::PowerState::unknown)) ||
276 (state == static_cast<uint8_t>(acpi_state::PowerState::noChange)))
277 {
278 return true;
279 }
280 else
281 {
282 return false;
283 }
284 }
285 else if (type == acpi_state::PowerStateType::devPowerState)
286 {
287 if ((state <= static_cast<uint8_t>(acpi_state::PowerState::s3D3)) ||
288 (state == static_cast<uint8_t>(acpi_state::PowerState::unknown)) ||
289 (state == static_cast<uint8_t>(acpi_state::PowerState::noChange)))
290 {
291 return true;
292 }
293 else
294 {
295 return false;
296 }
297 }
298 else
299 {
300 return false;
301 }
302 return false;
303}
304} // namespace acpi_state
305
Adriana Kobylak3a552e12015-10-19 16:11:00 -0500306ipmi_ret_t ipmi_app_set_acpi_power_state(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
Patrick Venture0b02be92018-08-31 11:55:55 -0700307 ipmi_request_t request,
308 ipmi_response_t response,
309 ipmi_data_len_t data_len,
310 ipmi_context_t context)
Chris Austen6caf28b2015-10-13 12:40:40 -0500311{
Yong Li18d77262018-10-09 01:59:45 +0800312 auto s = static_cast<uint8_t>(acpi_state::PowerState::unknown);
Chris Austen6caf28b2015-10-13 12:40:40 -0500313 ipmi_ret_t rc = IPMI_CC_OK;
Yong Li18d77262018-10-09 01:59:45 +0800314
315 sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()};
316
317 auto value = acpi_state::ACPIPowerState::ACPI::Unknown;
318
319 auto* req = reinterpret_cast<acpi_state::ACPIState*>(request);
320
321 if (*data_len != sizeof(acpi_state::ACPIState))
322 {
323 log<level::ERR>("set_acpi invalid len");
324 *data_len = 0;
325 return IPMI_CC_REQ_DATA_LEN_INVALID;
326 }
327
Chris Austen6caf28b2015-10-13 12:40:40 -0500328 *data_len = 0;
329
Yong Li18d77262018-10-09 01:59:45 +0800330 if (req->sysACPIState & acpi_state::stateChanged)
331 {
332 // set system power state
333 s = req->sysACPIState & ~acpi_state::stateChanged;
334
335 if (!acpi_state::isValidACPIState(
336 acpi_state::PowerStateType::sysPowerState, s))
337 {
338 log<level::ERR>("set_acpi_power sys invalid input",
339 entry("S=%x", s));
340 return IPMI_CC_PARM_OUT_OF_RANGE;
341 }
342
343 // valid input
344 if (s == static_cast<uint8_t>(acpi_state::PowerState::noChange))
345 {
346 log<level::DEBUG>("No change for system power state");
347 }
348 else
349 {
350 auto found = std::find_if(
351 acpi_state::dbusToIPMI.begin(), acpi_state::dbusToIPMI.end(),
352 [&s](const auto& iter) {
353 return (static_cast<uint8_t>(iter.second) == s);
354 });
355
356 value = found->first;
357
358 try
359 {
360 auto acpiObject =
361 ipmi::getDbusObject(bus, acpi_state::acpiInterface);
362 ipmi::setDbusProperty(bus, acpiObject.second, acpiObject.first,
363 acpi_state::acpiInterface,
364 acpi_state::sysACPIProp,
365 convertForMessage(value));
366 }
367 catch (const InternalFailure& e)
368 {
369 log<level::ERR>("Failed in set ACPI system property",
370 entry("EXCEPTION=%s", e.what()));
371 return IPMI_CC_UNSPECIFIED_ERROR;
372 }
373 }
374 }
375 else
376 {
377 log<level::DEBUG>("Do not change system power state");
378 }
379
380 if (req->devACPIState & acpi_state::stateChanged)
381 {
382 // set device power state
383 s = req->devACPIState & ~acpi_state::stateChanged;
384 if (!acpi_state::isValidACPIState(
385 acpi_state::PowerStateType::devPowerState, s))
386 {
387 log<level::ERR>("set_acpi_power dev invalid input",
388 entry("S=%x", s));
389 return IPMI_CC_PARM_OUT_OF_RANGE;
390 }
391
392 // valid input
393 if (s == static_cast<uint8_t>(acpi_state::PowerState::noChange))
394 {
395 log<level::DEBUG>("No change for device power state");
396 }
397 else
398 {
399 auto found = std::find_if(
400 acpi_state::dbusToIPMI.begin(), acpi_state::dbusToIPMI.end(),
401 [&s](const auto& iter) {
402 return (static_cast<uint8_t>(iter.second) == s);
403 });
404
405 value = found->first;
406
407 try
408 {
409 auto acpiObject =
410 ipmi::getDbusObject(bus, acpi_state::acpiInterface);
411 ipmi::setDbusProperty(bus, acpiObject.second, acpiObject.first,
412 acpi_state::acpiInterface,
413 acpi_state::devACPIProp,
414 convertForMessage(value));
415 }
416 catch (const InternalFailure& e)
417 {
418 log<level::ERR>("Failed in set ACPI device property",
419 entry("EXCEPTION=%s", e.what()));
420 return IPMI_CC_UNSPECIFIED_ERROR;
421 }
422 }
423 }
424 else
425 {
426 log<level::DEBUG>("Do not change device power state");
427 }
428
429 return rc;
430}
431
Deepak Kumar Sahu4e6d2572019-05-07 19:53:37 +0000432/**
433 * @brief implements the get ACPI power state command
434 *
435 * @return IPMI completion code plus response data on success.
436 * - ACPI system power state
437 * - ACPI device power state
438 **/
439ipmi::RspType<uint8_t, // acpiSystemPowerState
440 uint8_t // acpiDevicePowerState
441 >
442 ipmiGetAcpiPowerState()
Yong Li18d77262018-10-09 01:59:45 +0800443{
Deepak Kumar Sahu4e6d2572019-05-07 19:53:37 +0000444 uint8_t sysAcpiState;
445 uint8_t devAcpiState;
Yong Li18d77262018-10-09 01:59:45 +0800446
447 sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()};
448
Yong Li18d77262018-10-09 01:59:45 +0800449 try
450 {
451 auto acpiObject = ipmi::getDbusObject(bus, acpi_state::acpiInterface);
452
453 auto sysACPIVal = ipmi::getDbusProperty(
454 bus, acpiObject.second, acpiObject.first, acpi_state::acpiInterface,
455 acpi_state::sysACPIProp);
456 auto sysACPI = acpi_state::ACPIPowerState::convertACPIFromString(
Vernon Maueryf442e112019-04-09 11:44:36 -0700457 std::get<std::string>(sysACPIVal));
Deepak Kumar Sahu4e6d2572019-05-07 19:53:37 +0000458 sysAcpiState = static_cast<uint8_t>(acpi_state::dbusToIPMI.at(sysACPI));
Yong Li18d77262018-10-09 01:59:45 +0800459
460 auto devACPIVal = ipmi::getDbusProperty(
461 bus, acpiObject.second, acpiObject.first, acpi_state::acpiInterface,
462 acpi_state::devACPIProp);
463 auto devACPI = acpi_state::ACPIPowerState::convertACPIFromString(
Vernon Maueryf442e112019-04-09 11:44:36 -0700464 std::get<std::string>(devACPIVal));
Deepak Kumar Sahu4e6d2572019-05-07 19:53:37 +0000465 devAcpiState = static_cast<uint8_t>(acpi_state::dbusToIPMI.at(devACPI));
Yong Li18d77262018-10-09 01:59:45 +0800466 }
467 catch (const InternalFailure& e)
468 {
Deepak Kumar Sahu4e6d2572019-05-07 19:53:37 +0000469 return ipmi::responseUnspecifiedError();
Yong Li18d77262018-10-09 01:59:45 +0800470 }
Deepak Kumar Sahu4e6d2572019-05-07 19:53:37 +0000471
472 return ipmi::responseSuccess(sysAcpiState, devAcpiState);
Chris Austen6caf28b2015-10-13 12:40:40 -0500473}
474
Chris Austen7303bdc2016-04-17 11:50:54 -0500475typedef struct
476{
477 char major;
478 char minor;
Chris Austen176c9652016-04-30 16:32:17 -0500479 uint16_t d[2];
Vernon Mauery86a50822019-03-25 13:11:36 -0700480} Revision;
Chris Austen7303bdc2016-04-17 11:50:54 -0500481
Dinesh Chinari2b7e07d2017-11-08 15:38:50 -0600482/* Currently supports the vx.x-x-[-x] and v1.x.x-x-[-x] format. It will */
483/* return -1 if not in those formats, this routine knows how to parse */
Chris Austen7303bdc2016-04-17 11:50:54 -0500484/* version = v0.6-19-gf363f61-dirty */
485/* ^ ^ ^^ ^ */
486/* | | |----------|-- additional details */
487/* | |---------------- Minor */
488/* |------------------ Major */
Dinesh Chinari2b7e07d2017-11-08 15:38:50 -0600489/* and version = v1.99.10-113-g65edf7d-r3-0-g9e4f715 */
490/* ^ ^ ^^ ^ */
491/* | | |--|---------- additional details */
492/* | |---------------- Minor */
493/* |------------------ Major */
Chris Austen7303bdc2016-04-17 11:50:54 -0500494/* Additional details : If the option group exists it will force Auxiliary */
495/* Firmware Revision Information 4th byte to 1 indicating the build was */
496/* derived with additional edits */
Vernon Mauery86a50822019-03-25 13:11:36 -0700497int convertVersion(std::string s, Revision& rev)
Chris Austen7303bdc2016-04-17 11:50:54 -0500498{
Dinesh Chinari2b7e07d2017-11-08 15:38:50 -0600499 std::string token;
Chris Austen176c9652016-04-30 16:32:17 -0500500 uint16_t commits;
Chris Austen7303bdc2016-04-17 11:50:54 -0500501
Patrick Venture0b02be92018-08-31 11:55:55 -0700502 auto location = s.find_first_of('v');
Dinesh Chinari2b7e07d2017-11-08 15:38:50 -0600503 if (location != std::string::npos)
504 {
Patrick Venture0b02be92018-08-31 11:55:55 -0700505 s = s.substr(location + 1);
Chris Austen176c9652016-04-30 16:32:17 -0500506 }
Chris Austen7303bdc2016-04-17 11:50:54 -0500507
Dinesh Chinari2b7e07d2017-11-08 15:38:50 -0600508 if (!s.empty())
509 {
510 location = s.find_first_of(".");
511 if (location != std::string::npos)
512 {
Vernon Mauery86a50822019-03-25 13:11:36 -0700513 rev.major =
Patrick Venture0b02be92018-08-31 11:55:55 -0700514 static_cast<char>(std::stoi(s.substr(0, location), 0, 16));
515 token = s.substr(location + 1);
Dinesh Chinari2b7e07d2017-11-08 15:38:50 -0600516 }
Chris Austen176c9652016-04-30 16:32:17 -0500517
Dinesh Chinari2b7e07d2017-11-08 15:38:50 -0600518 if (!token.empty())
519 {
520 location = token.find_first_of(".-");
521 if (location != std::string::npos)
522 {
Vernon Mauery86a50822019-03-25 13:11:36 -0700523 rev.minor = static_cast<char>(
Patrick Venture0b02be92018-08-31 11:55:55 -0700524 std::stoi(token.substr(0, location), 0, 16));
525 token = token.substr(location + 1);
Dinesh Chinari2b7e07d2017-11-08 15:38:50 -0600526 }
527 }
Chris Austen7303bdc2016-04-17 11:50:54 -0500528
Dinesh Chinari2b7e07d2017-11-08 15:38:50 -0600529 // Capture the number of commits on top of the minor tag.
530 // I'm using BE format like the ipmi spec asked for
531 location = token.find_first_of(".-");
532 if (!token.empty())
533 {
534 commits = std::stoi(token.substr(0, location), 0, 16);
Vernon Mauery86a50822019-03-25 13:11:36 -0700535 rev.d[0] = (commits >> 8) | (commits << 8);
Dinesh Chinari2b7e07d2017-11-08 15:38:50 -0600536
537 // commit number we skip
538 location = token.find_first_of(".-");
539 if (location != std::string::npos)
540 {
Patrick Venture0b02be92018-08-31 11:55:55 -0700541 token = token.substr(location + 1);
Dinesh Chinari2b7e07d2017-11-08 15:38:50 -0600542 }
543 }
Patrick Venture0b02be92018-08-31 11:55:55 -0700544 else
545 {
Vernon Mauery86a50822019-03-25 13:11:36 -0700546 rev.d[0] = 0;
Dinesh Chinari2b7e07d2017-11-08 15:38:50 -0600547 }
548
549 if (location != std::string::npos)
550 {
Patrick Venture0b02be92018-08-31 11:55:55 -0700551 token = token.substr(location + 1);
Dinesh Chinari2b7e07d2017-11-08 15:38:50 -0600552 }
553
554 // Any value of the optional parameter forces it to 1
555 location = token.find_first_of(".-");
556 if (location != std::string::npos)
557 {
Patrick Venture0b02be92018-08-31 11:55:55 -0700558 token = token.substr(location + 1);
Dinesh Chinari2b7e07d2017-11-08 15:38:50 -0600559 }
560 commits = (!token.empty()) ? 1 : 0;
561
Patrick Venture0b02be92018-08-31 11:55:55 -0700562 // We do this operation to get this displayed in least significant bytes
563 // of ipmitool device id command.
Vernon Mauery86a50822019-03-25 13:11:36 -0700564 rev.d[1] = (commits >> 8) | (commits << 8);
Dinesh Chinari2b7e07d2017-11-08 15:38:50 -0600565 }
566
Chris Austen7303bdc2016-04-17 11:50:54 -0500567 return 0;
568}
569
Vernon Maueryea1c4012019-05-24 13:26:16 -0700570/* @brief: Implement the Get Device ID IPMI command per the IPMI spec
571 * @param[in] ctx - shared_ptr to an IPMI context struct
572 *
573 * @returns IPMI completion code plus response data
574 * - Device ID (manufacturer defined)
575 * - Device revision[4 bits]; reserved[3 bits]; SDR support[1 bit]
576 * - FW revision major[7 bits] (binary encoded); available[1 bit]
577 * - FW Revision minor (BCD encoded)
578 * - IPMI version (0x02 for IPMI 2.0)
579 * - device support (bitfield of supported options)
580 * - MFG IANA ID (3 bytes)
581 * - product ID (2 bytes)
582 * - AUX info (4 bytes)
583 */
584ipmi::RspType<uint8_t, // Device ID
585 uint8_t, // Device Revision
586 uint8_t, // Firmware Revision Major
587 uint8_t, // Firmware Revision minor
588 uint8_t, // IPMI version
589 uint8_t, // Additional device support
590 uint24_t, // MFG ID
591 uint16_t, // Product ID
592 uint32_t // AUX info
593 >
594 ipmiAppGetDeviceId(ipmi::Context::ptr ctx)
Chris Austen6caf28b2015-10-13 12:40:40 -0500595{
Nagaraju Goruganti744398d2018-02-20 09:52:00 -0600596 int r = -1;
Vernon Mauery86a50822019-03-25 13:11:36 -0700597 Revision rev = {0};
598 static struct
599 {
600 uint8_t id;
601 uint8_t revision;
602 uint8_t fw[2];
603 uint8_t ipmiVer;
604 uint8_t addnDevSupport;
605 uint24_t manufId;
606 uint16_t prodId;
607 uint32_t aux;
608 } devId;
David Cobbleya1adb072017-11-21 15:58:13 -0800609 static bool dev_id_initialized = false;
Patrick Venture94930a12019-04-30 10:01:58 -0700610 static bool defaultActivationSetting = true;
David Cobbleya1adb072017-11-21 15:58:13 -0800611 const char* filename = "/usr/share/ipmi-providers/dev_id.json";
Alexander Amelkinba19c182018-09-04 15:49:36 +0300612 constexpr auto ipmiDevIdStateShift = 7;
613 constexpr auto ipmiDevIdFw1Mask = ~(1 << ipmiDevIdStateShift);
Chris Austen6caf28b2015-10-13 12:40:40 -0500614
David Cobbleya1adb072017-11-21 15:58:13 -0800615 if (!dev_id_initialized)
616 {
Nagaraju Goruganti744398d2018-02-20 09:52:00 -0600617 try
618 {
Vernon Maueryea1c4012019-05-24 13:26:16 -0700619 auto version = getActiveSoftwareVersionInfo(ctx);
Vernon Mauery86a50822019-03-25 13:11:36 -0700620 r = convertVersion(version, rev);
David Cobbleya1adb072017-11-21 15:58:13 -0800621 }
Nagaraju Goruganti744398d2018-02-20 09:52:00 -0600622 catch (const std::exception& e)
623 {
624 log<level::ERR>(e.what());
625 }
Nan Liee0cb902016-07-11 15:38:03 +0800626
Patrick Venture0b02be92018-08-31 11:55:55 -0700627 if (r >= 0)
628 {
Nagaraju Goruganti744398d2018-02-20 09:52:00 -0600629 // bit7 identifies if the device is available
630 // 0=normal operation
631 // 1=device firmware, SDR update,
632 // or self-initialization in progress.
Alexander Amelkinba19c182018-09-04 15:49:36 +0300633 // The availability may change in run time, so mask here
634 // and initialize later.
Vernon Mauery86a50822019-03-25 13:11:36 -0700635 devId.fw[0] = rev.major & ipmiDevIdFw1Mask;
Nagaraju Goruganti744398d2018-02-20 09:52:00 -0600636
637 rev.minor = (rev.minor > 99 ? 99 : rev.minor);
Vernon Mauery86a50822019-03-25 13:11:36 -0700638 devId.fw[1] = rev.minor % 10 + (rev.minor / 10) * 16;
639 std::memcpy(&devId.aux, rev.d, 4);
David Cobbleya1adb072017-11-21 15:58:13 -0800640 }
Nan Liee0cb902016-07-11 15:38:03 +0800641
David Cobbleya1adb072017-11-21 15:58:13 -0800642 // IPMI Spec version 2.0
Vernon Mauery86a50822019-03-25 13:11:36 -0700643 devId.ipmiVer = 2;
Adriana Kobylak0e912642016-06-22 16:54:39 -0500644
Vernon Mauery86a50822019-03-25 13:11:36 -0700645 std::ifstream devIdFile(filename);
646 if (devIdFile.is_open())
David Cobbleya1adb072017-11-21 15:58:13 -0800647 {
Vernon Mauery86a50822019-03-25 13:11:36 -0700648 auto data = nlohmann::json::parse(devIdFile, nullptr, false);
David Cobbleya1adb072017-11-21 15:58:13 -0800649 if (!data.is_discarded())
650 {
Vernon Mauery86a50822019-03-25 13:11:36 -0700651 devId.id = data.value("id", 0);
652 devId.revision = data.value("revision", 0);
653 devId.addnDevSupport = data.value("addn_dev_support", 0);
654 devId.manufId = data.value("manuf_id", 0);
655 devId.prodId = data.value("prod_id", 0);
656 devId.aux = data.value("aux", 0);
David Cobbleya1adb072017-11-21 15:58:13 -0800657
Patrick Venture94930a12019-04-30 10:01:58 -0700658 // Set the availablitity of the BMC.
659 defaultActivationSetting = data.value("availability", true);
660
Patrick Venture0b02be92018-08-31 11:55:55 -0700661 // Don't read the file every time if successful
David Cobbleya1adb072017-11-21 15:58:13 -0800662 dev_id_initialized = true;
663 }
664 else
665 {
666 log<level::ERR>("Device ID JSON parser failure");
Vernon Maueryf2587fc2019-04-09 14:28:15 -0700667 return ipmi::responseUnspecifiedError();
David Cobbleya1adb072017-11-21 15:58:13 -0800668 }
669 }
670 else
671 {
672 log<level::ERR>("Device ID file not found");
Vernon Maueryf2587fc2019-04-09 14:28:15 -0700673 return ipmi::responseUnspecifiedError();
Chris Austen7303bdc2016-04-17 11:50:54 -0500674 }
675 }
Chris Austen6caf28b2015-10-13 12:40:40 -0500676
Alexander Amelkinba19c182018-09-04 15:49:36 +0300677 // Set availability to the actual current BMC state
Vernon Mauery86a50822019-03-25 13:11:36 -0700678 devId.fw[0] &= ipmiDevIdFw1Mask;
Patrick Venture94930a12019-04-30 10:01:58 -0700679 if (!getCurrentBmcStateWithFallback(defaultActivationSetting))
Alexander Amelkinba19c182018-09-04 15:49:36 +0300680 {
Vernon Mauery86a50822019-03-25 13:11:36 -0700681 devId.fw[0] |= (1 << ipmiDevIdStateShift);
Alexander Amelkinba19c182018-09-04 15:49:36 +0300682 }
683
Vernon Mauery86a50822019-03-25 13:11:36 -0700684 return ipmi::responseSuccess(
685 devId.id, devId.revision, devId.fw[0], devId.fw[1], devId.ipmiVer,
686 devId.addnDevSupport, devId.manufId, devId.prodId, devId.aux);
Chris Austen6caf28b2015-10-13 12:40:40 -0500687}
688
Vernon Maueryb84a5282019-03-25 13:39:03 -0700689auto ipmiAppGetSelfTestResults() -> ipmi::RspType<uint8_t, uint8_t>
Nan Li41fa24a2016-11-10 20:12:37 +0800690{
Nan Li41fa24a2016-11-10 20:12:37 +0800691 // Byte 2:
692 // 55h - No error.
Gunnar Mills8991dd62017-10-25 17:11:29 -0500693 // 56h - Self Test function not implemented in this controller.
Nan Li41fa24a2016-11-10 20:12:37 +0800694 // 57h - Corrupted or inaccesssible data or devices.
695 // 58h - Fatal hardware error.
696 // FFh - reserved.
697 // all other: Device-specific 'internal failure'.
698 // Byte 3:
699 // For byte 2 = 55h, 56h, FFh: 00h
700 // For byte 2 = 58h, all other: Device-specific
701 // For byte 2 = 57h: self-test error bitfield.
702 // Note: returning 57h does not imply that all test were run.
703 // [7] 1b = Cannot access SEL device.
704 // [6] 1b = Cannot access SDR Repository.
705 // [5] 1b = Cannot access BMC FRU device.
706 // [4] 1b = IPMB signal lines do not respond.
707 // [3] 1b = SDR Repository empty.
708 // [2] 1b = Internal Use Area of BMC FRU corrupted.
709 // [1] 1b = controller update 'boot block' firmware corrupted.
710 // [0] 1b = controller operational firmware corrupted.
Vernon Maueryb84a5282019-03-25 13:39:03 -0700711 constexpr uint8_t notImplemented = 0x56;
712 constexpr uint8_t zero = 0;
713 return ipmi::responseSuccess(notImplemented, zero);
Nan Li41fa24a2016-11-10 20:12:37 +0800714}
715
Vernon Mauery15541322019-03-25 13:33:03 -0700716static constexpr size_t uuidBinaryLength = 16;
717static std::array<uint8_t, uuidBinaryLength> rfc4122ToIpmi(std::string rfc4122)
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500718{
Vernon Mauery15541322019-03-25 13:33:03 -0700719 using Argument = xyz::openbmc_project::Common::InvalidArgument;
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500720 // UUID is in RFC4122 format. Ex: 61a39523-78f2-11e5-9862-e6402cfc3223
Patrick Ventured2117022018-02-06 08:54:37 -0800721 // Per IPMI Spec 2.0 need to convert to 16 hex bytes and reverse the byte
722 // order
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500723 // Ex: 0x2332fc2c40e66298e511f2782395a361
Vernon Mauery15541322019-03-25 13:33:03 -0700724 constexpr size_t uuidHexLength = (2 * uuidBinaryLength);
725 constexpr size_t uuidRfc4122Length = (uuidHexLength + 4);
726 std::array<uint8_t, uuidBinaryLength> uuid;
727 if (rfc4122.size() == uuidRfc4122Length)
Patrick Venture0b02be92018-08-31 11:55:55 -0700728 {
Vernon Mauery15541322019-03-25 13:33:03 -0700729 rfc4122.erase(std::remove(rfc4122.begin(), rfc4122.end(), '-'),
730 rfc4122.end());
Sergey Solomineb9b8142016-08-23 09:07:28 -0500731 }
Vernon Mauery15541322019-03-25 13:33:03 -0700732 if (rfc4122.size() != uuidHexLength)
vishwa1eaea4f2016-02-26 11:57:40 -0600733 {
Vernon Mauery15541322019-03-25 13:33:03 -0700734 elog<InvalidArgument>(Argument::ARGUMENT_NAME("rfc4122"),
735 Argument::ARGUMENT_VALUE(rfc4122.c_str()));
vishwa1eaea4f2016-02-26 11:57:40 -0600736 }
Vernon Mauery15541322019-03-25 13:33:03 -0700737 for (size_t ind = 0; ind < uuidHexLength; ind += 2)
vishwa1eaea4f2016-02-26 11:57:40 -0600738 {
Vernon Mauery15541322019-03-25 13:33:03 -0700739 char v[3];
740 v[0] = rfc4122[ind];
741 v[1] = rfc4122[ind + 1];
742 v[2] = 0;
743 size_t err;
744 long b;
745 try
Emily Shafferedb8bb02018-09-27 14:50:15 -0700746 {
Vernon Mauery15541322019-03-25 13:33:03 -0700747 b = std::stoul(v, &err, 16);
Emily Shafferedb8bb02018-09-27 14:50:15 -0700748 }
Vernon Mauery15541322019-03-25 13:33:03 -0700749 catch (std::exception& e)
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500750 {
Vernon Mauery15541322019-03-25 13:33:03 -0700751 elog<InvalidArgument>(Argument::ARGUMENT_NAME("rfc4122"),
752 Argument::ARGUMENT_VALUE(rfc4122.c_str()));
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500753 }
Vernon Mauery15541322019-03-25 13:33:03 -0700754 // check that exactly two ascii bytes were converted
755 if (err != 2)
756 {
757 elog<InvalidArgument>(Argument::ARGUMENT_NAME("rfc4122"),
758 Argument::ARGUMENT_VALUE(rfc4122.c_str()));
759 }
760 uuid[uuidBinaryLength - (ind / 2) - 1] = static_cast<uint8_t>(b);
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500761 }
Vernon Mauery15541322019-03-25 13:33:03 -0700762 return uuid;
763}
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500764
Vernon Mauery15541322019-03-25 13:33:03 -0700765auto ipmiAppGetDeviceGuid()
766 -> ipmi::RspType<std::array<uint8_t, uuidBinaryLength>>
767{
768 // return a fixed GUID based on /etc/machine-id
769 // This should match the /redfish/v1/Managers/bmc's UUID data
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500770
Vernon Mauery15541322019-03-25 13:33:03 -0700771 // machine specific application ID (for BMC ID)
772 // generated by systemd-id128 -p new as per man page
773 static constexpr sd_id128_t bmcUuidAppId = SD_ID128_MAKE(
774 e0, e1, 73, 76, 64, 61, 47, da, a5, 0c, d0, cc, 64, 12, 45, 78);
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500775
Vernon Mauery15541322019-03-25 13:33:03 -0700776 sd_id128_t bmcUuid;
777 // create the UUID from /etc/machine-id via the systemd API
778 sd_id128_get_machine_app_specific(bmcUuidAppId, &bmcUuid);
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500779
Vernon Mauery15541322019-03-25 13:33:03 -0700780 char bmcUuidCstr[SD_ID128_STRING_MAX];
781 std::string systemUuid = sd_id128_to_string(bmcUuid, bmcUuidCstr);
782
783 std::array<uint8_t, uuidBinaryLength> uuid = rfc4122ToIpmi(systemUuid);
784 return ipmi::responseSuccess(uuid);
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500785}
Chris Austen6caf28b2015-10-13 12:40:40 -0500786
Vernon Mauerycc99ba42019-03-25 13:40:11 -0700787auto ipmiAppGetBtCapabilities()
788 -> ipmi::RspType<uint8_t, uint8_t, uint8_t, uint8_t, uint8_t>
vishwabmcba0bd5f2015-09-30 16:50:23 +0530789{
Adriana Kobylak88ad8152016-12-13 10:09:08 -0600790 // Per IPMI 2.0 spec, the input and output buffer size must be the max
791 // buffer size minus one byte to allocate space for the length byte.
Vernon Mauerycc99ba42019-03-25 13:40:11 -0700792 constexpr uint8_t nrOutstanding = 0x01;
793 constexpr uint8_t inputBufferSize = MAX_IPMI_BUFFER - 1;
794 constexpr uint8_t outputBufferSize = MAX_IPMI_BUFFER - 1;
795 constexpr uint8_t transactionTime = 0x0A;
796 constexpr uint8_t nrRetries = 0x01;
vishwabmcba0bd5f2015-09-30 16:50:23 +0530797
Vernon Mauerycc99ba42019-03-25 13:40:11 -0700798 return ipmi::responseSuccess(nrOutstanding, inputBufferSize,
799 outputBufferSize, transactionTime, nrRetries);
vishwabmcba0bd5f2015-09-30 16:50:23 +0530800}
801
Vernon Maueryb90a5322019-03-25 13:36:55 -0700802auto ipmiAppGetSystemGuid() -> ipmi::RspType<std::array<uint8_t, 16>>
Marri Devender Rao5e007a52018-01-08 06:18:36 -0600803{
Vernon Maueryb90a5322019-03-25 13:36:55 -0700804 static constexpr auto bmcInterface =
805 "xyz.openbmc_project.Inventory.Item.Bmc";
806 static constexpr auto uuidInterface = "xyz.openbmc_project.Common.UUID";
807 static constexpr auto uuidProperty = "UUID";
Marri Devender Rao5e007a52018-01-08 06:18:36 -0600808
Vernon Maueryb90a5322019-03-25 13:36:55 -0700809 ipmi::Value propValue;
Marri Devender Rao5e007a52018-01-08 06:18:36 -0600810 try
811 {
812 // Get the Inventory object implementing BMC interface
Vernon Maueryb90a5322019-03-25 13:36:55 -0700813 auto busPtr = getSdBus();
814 auto objectInfo = ipmi::getDbusObject(*busPtr, bmcInterface);
Marri Devender Rao5e007a52018-01-08 06:18:36 -0600815
816 // Read UUID property value from bmcObject
817 // UUID is in RFC4122 format Ex: 61a39523-78f2-11e5-9862-e6402cfc3223
Vernon Maueryb90a5322019-03-25 13:36:55 -0700818 propValue =
819 ipmi::getDbusProperty(*busPtr, objectInfo.second, objectInfo.first,
820 uuidInterface, uuidProperty);
Marri Devender Rao5e007a52018-01-08 06:18:36 -0600821 }
822 catch (const InternalFailure& e)
823 {
824 log<level::ERR>("Failed in reading BMC UUID property",
Vernon Maueryb90a5322019-03-25 13:36:55 -0700825 entry("INTERFACE=%s", uuidInterface),
826 entry("PROPERTY=%s", uuidProperty));
827 return ipmi::responseUnspecifiedError();
Marri Devender Rao5e007a52018-01-08 06:18:36 -0600828 }
Vernon Maueryb90a5322019-03-25 13:36:55 -0700829 std::array<uint8_t, 16> uuid;
830 std::string rfc4122Uuid = std::get<std::string>(propValue);
831 try
832 {
833 // convert to IPMI format
834 uuid = rfc4122ToIpmi(rfc4122Uuid);
835 }
836 catch (const InvalidArgument& e)
837 {
838 log<level::ERR>("Failed in parsing BMC UUID property",
839 entry("INTERFACE=%s", uuidInterface),
840 entry("PROPERTY=%s", uuidProperty),
841 entry("VALUE=%s", rfc4122Uuid.c_str()));
842 return ipmi::responseUnspecifiedError();
843 }
844 return ipmi::responseSuccess(uuid);
Marri Devender Rao5e007a52018-01-08 06:18:36 -0600845}
846
Rajashekar Gade Reddye7023922019-07-10 16:54:55 +0000847/**
848 * @brief set the session state as teardown
849 *
850 * This function is to set the session state to tear down in progress if the
851 * state is active.
852 *
853 * @param[in] busp - Dbus obj
854 * @param[in] service - service name
855 * @param[in] obj - object path
856 *
857 * @return success completion code if it sets the session state to
858 * tearDownInProgress else return the corresponding error completion code.
859 **/
860uint8_t setSessionState(std::shared_ptr<sdbusplus::asio::connection>& busp,
861 const std::string& service, const std::string& obj)
862{
863 try
864 {
865 uint8_t sessionState = std::get<uint8_t>(ipmi::getDbusProperty(
866 *busp, service, obj, session::sessionIntf, "State"));
867
868 if (sessionState == static_cast<uint8_t>(session::State::active))
869 {
870 ipmi::setDbusProperty(
871 *busp, service, obj, session::sessionIntf, "State",
872 static_cast<uint8_t>(session::State::tearDownInProgress));
873 return ipmi::ccSuccess;
874 }
875 }
876 catch (std::exception& e)
877 {
878 log<level::ERR>("Failed in getting session state property",
879 entry("service=%s", service.c_str()),
880 entry("object path=%s", obj.c_str()),
881 entry("interface=%s", session::sessionIntf));
882 return ipmi::ccUnspecifiedError;
883 }
884
885 return ipmi::ccInvalidFieldRequest;
886}
887
888ipmi::RspType<> ipmiAppCloseSession(uint32_t reqSessionId,
889 std::optional<uint8_t> requestSessionHandle)
890{
891 auto busp = getSdBus();
892 uint8_t reqSessionHandle =
893 requestSessionHandle.value_or(session::defaultSessionHandle);
894
895 if (reqSessionId == session::sessionZero &&
896 reqSessionHandle == session::defaultSessionHandle)
897 {
898 return ipmi::response(session::ccInvalidSessionId);
899 }
900
901 if (reqSessionId == session::sessionZero &&
902 reqSessionHandle == session::invalidSessionHandle)
903 {
904 return ipmi::response(session::ccInvalidSessionHandle);
905 }
906
907 if (reqSessionId != session::sessionZero &&
908 reqSessionHandle != session::defaultSessionHandle)
909 {
910 return ipmi::response(ipmi::ccInvalidFieldRequest);
911 }
912
913 try
914 {
915 ipmi::ObjectTree objectTree = ipmi::getAllDbusObjects(
916 *busp, session::sessionManagerRootPath, session::sessionIntf);
917
918 for (auto& objectTreeItr : objectTree)
919 {
920 const std::string obj = objectTreeItr.first;
921
922 if (isSessionObjectMatched(obj, reqSessionId, reqSessionHandle))
923 {
924 auto& serviceMap = objectTreeItr.second;
925
926 // Session id and session handle are unique for each session.
927 // Session id and handler are retrived from the object path and
928 // object path will be unique for each session. Checking if
929 // multiple objects exist with same object path under multiple
930 // services.
931 if (serviceMap.size() != 1)
932 {
933 return ipmi::responseUnspecifiedError();
934 }
935
936 auto itr = serviceMap.begin();
937 const std::string service = itr->first;
938 return ipmi::response(setSessionState(busp, service, obj));
939 }
940 }
941 }
942 catch (sdbusplus::exception::SdBusError& e)
943 {
944 log<level::ERR>("Failed to fetch object from dbus",
945 entry("INTERFACE=%s", session::sessionIntf),
946 entry("ERRMSG=%s", e.what()));
947 return ipmi::responseUnspecifiedError();
948 }
949
950 return ipmi::responseInvalidFieldRequest();
951}
952
Xo Wangf542e8b2017-08-09 15:34:16 -0700953static std::unique_ptr<SysInfoParamStore> sysInfoParamStore;
954
Xo Wang87651332017-08-11 10:17:59 -0700955static std::string sysInfoReadSystemName()
956{
957 // Use the BMC hostname as the "System Name."
958 char hostname[HOST_NAME_MAX + 1] = {};
959 if (gethostname(hostname, HOST_NAME_MAX) != 0)
960 {
961 perror("System info parameter: system name");
962 }
963 return hostname;
964}
965
Jia, chunhui98b43ba2019-09-05 15:54:23 +0800966static constexpr uint8_t revisionOnly = 0x80;
967static constexpr uint8_t paramRevision = 0x11;
968static constexpr size_t configParameterLength = 16;
Xo Wangf542e8b2017-08-09 15:34:16 -0700969
Jia, chunhui98b43ba2019-09-05 15:54:23 +0800970static constexpr size_t smallChunkSize = 14;
971static constexpr size_t fullChunkSize = 16;
Xo Wangf542e8b2017-08-09 15:34:16 -0700972
Jia, chunhui98b43ba2019-09-05 15:54:23 +0800973static constexpr uint8_t setComplete = 0x0;
974static constexpr uint8_t setInProgress = 0x1;
975static constexpr uint8_t commitWrite = 0x2;
976static uint8_t transferStatus = setComplete;
977
978namespace ipmi
979{
980constexpr Cc ccParmNotSupported = 0x80;
981
982static inline auto responseParmNotSupported()
983{
984 return response(ccParmNotSupported);
Xo Wangf542e8b2017-08-09 15:34:16 -0700985}
Jia, chunhui98b43ba2019-09-05 15:54:23 +0800986} // namespace ipmi
Xo Wangf542e8b2017-08-09 15:34:16 -0700987
Jia, chunhui98b43ba2019-09-05 15:54:23 +0800988ipmi::RspType<
989 uint8_t, // Parameter revision
990 std::optional<uint8_t>, // data1 / setSelector / ProgressStatus
991 std::optional<std::array<uint8_t, configParameterLength>>> // data2-17
992 ipmiAppGetSystemInfo(uint8_t getRevision, uint8_t paramSelector,
993 uint8_t setSelector, uint8_t BlockSelector)
Xo Wangf542e8b2017-08-09 15:34:16 -0700994{
Jia, chunhui98b43ba2019-09-05 15:54:23 +0800995 if (getRevision & revisionOnly)
Xo Wangf542e8b2017-08-09 15:34:16 -0700996 {
Jia, chunhui98b43ba2019-09-05 15:54:23 +0800997 return ipmi::responseSuccess(paramRevision, std::nullopt, std::nullopt);
Xo Wangf542e8b2017-08-09 15:34:16 -0700998 }
999
Jia, chunhui98b43ba2019-09-05 15:54:23 +08001000 if (paramSelector == 0)
Xo Wangf542e8b2017-08-09 15:34:16 -07001001 {
Jia, chunhui98b43ba2019-09-05 15:54:23 +08001002 return ipmi::responseSuccess(paramRevision, transferStatus,
1003 std::nullopt);
Xo Wangf542e8b2017-08-09 15:34:16 -07001004 }
1005
Jia, chunhui98b43ba2019-09-05 15:54:23 +08001006 if (BlockSelector != 0) // 00h if parameter does not require a block number
Xo Wangf542e8b2017-08-09 15:34:16 -07001007 {
Jia, chunhui98b43ba2019-09-05 15:54:23 +08001008 return ipmi::responseParmNotSupported();
Xo Wangf542e8b2017-08-09 15:34:16 -07001009 }
1010
1011 if (sysInfoParamStore == nullptr)
1012 {
1013 sysInfoParamStore = std::make_unique<SysInfoParamStore>();
Xo Wang87651332017-08-11 10:17:59 -07001014 sysInfoParamStore->update(IPMI_SYSINFO_SYSTEM_NAME,
1015 sysInfoReadSystemName);
Xo Wangf542e8b2017-08-09 15:34:16 -07001016 }
1017
1018 // Parameters other than Set In Progress are assumed to be strings.
Jia, chunhui98b43ba2019-09-05 15:54:23 +08001019 std::tuple<bool, std::string> ret =
1020 sysInfoParamStore->lookup(paramSelector);
1021 bool found = std::get<0>(ret);
Xo Wangf542e8b2017-08-09 15:34:16 -07001022 if (!found)
1023 {
Jia, chunhui98b43ba2019-09-05 15:54:23 +08001024 return ipmi::responseParmNotSupported();
Xo Wangf542e8b2017-08-09 15:34:16 -07001025 }
Jia, chunhui98b43ba2019-09-05 15:54:23 +08001026 std::string& paramString = std::get<1>(ret);
1027 std::array<uint8_t, configParameterLength> configData;
1028 size_t count = 0;
1029 if (setSelector == 0)
1030 { // First chunk has only 14 bytes.
1031 configData.at(0) = 0; // encoding
1032 configData.at(1) = paramString.length(); // string length
1033 count = (paramString.length() > smallChunkSize) ? smallChunkSize
1034 : paramString.length();
1035 std::copy_n(paramString.begin(), count,
1036 configData.begin() + 2); // 14 bytes thunk
1037 }
1038 else
Xo Wangf542e8b2017-08-09 15:34:16 -07001039 {
Jia, chunhui98b43ba2019-09-05 15:54:23 +08001040 size_t offset = (setSelector * fullChunkSize) - 2;
1041 if (offset >= paramString.length())
1042 {
1043 return ipmi::responseParmOutOfRange();
1044 }
1045 count = ((paramString.length() - offset) > fullChunkSize)
1046 ? fullChunkSize
1047 : (paramString.length() - offset);
1048 std::copy_n(paramString.begin() + offset, count,
1049 configData.begin()); // 16 bytes chunk
Xo Wangf542e8b2017-08-09 15:34:16 -07001050 }
Jia, chunhui98b43ba2019-09-05 15:54:23 +08001051 return ipmi::responseSuccess(paramRevision, setSelector, configData);
Xo Wangf542e8b2017-08-09 15:34:16 -07001052}
1053
Yong Libd0503a2019-08-22 17:17:17 +08001054#ifdef ENABLE_I2C_WHITELIST_CHECK
Richard Marian Thomaiyar84bf9be2019-04-26 22:59:16 +05301055inline std::vector<uint8_t> convertStringToData(const std::string& command)
1056{
1057 std::istringstream iss(command);
1058 std::string token;
1059 std::vector<uint8_t> dataValue;
1060 while (std::getline(iss, token, ' '))
1061 {
1062 dataValue.emplace_back(
1063 static_cast<uint8_t>(std::stoul(token, nullptr, base_16)));
1064 }
1065 return dataValue;
1066}
1067
1068static bool populateI2CMasterWRWhitelist()
1069{
1070 nlohmann::json data = nullptr;
1071 std::ifstream jsonFile(i2cMasterWRWhitelistFile);
1072
1073 if (!jsonFile.good())
1074 {
1075 log<level::WARNING>("i2c white list file not found!",
1076 entry("FILE_NAME: %s", i2cMasterWRWhitelistFile));
1077 return false;
1078 }
1079
1080 try
1081 {
1082 data = nlohmann::json::parse(jsonFile, nullptr, false);
1083 }
1084 catch (nlohmann::json::parse_error& e)
1085 {
1086 log<level::ERR>("Corrupted i2c white list config file",
1087 entry("FILE_NAME: %s", i2cMasterWRWhitelistFile),
1088 entry("MSG: %s", e.what()));
1089 return false;
1090 }
1091
1092 try
1093 {
1094 // Example JSON Structure format
1095 // "filters": [
1096 // {
1097 // "Description": "Allow full read - ignore first byte write value
1098 // for 0x40 to 0x4F",
1099 // "busId": "0x01",
1100 // "slaveAddr": "0x40",
1101 // "slaveAddrMask": "0x0F",
1102 // "command": "0x00",
1103 // "commandMask": "0xFF"
1104 // },
1105 // {
1106 // "Description": "Allow full read - first byte match 0x05 and
1107 // ignore second byte",
1108 // "busId": "0x01",
1109 // "slaveAddr": "0x57",
1110 // "slaveAddrMask": "0x00",
1111 // "command": "0x05 0x00",
1112 // "commandMask": "0x00 0xFF"
1113 // },]
1114
1115 nlohmann::json filters = data[filtersStr].get<nlohmann::json>();
1116 std::vector<i2cMasterWRWhitelist>& whitelist = getWRWhitelist();
1117 for (const auto& it : filters.items())
1118 {
1119 nlohmann::json filter = it.value();
1120 if (filter.is_null())
1121 {
1122 log<level::ERR>(
1123 "Corrupted I2C master write read whitelist config file",
1124 entry("FILE_NAME: %s", i2cMasterWRWhitelistFile));
1125 return false;
1126 }
1127 const std::vector<uint8_t>& writeData =
1128 convertStringToData(filter[cmdStr].get<std::string>());
1129 const std::vector<uint8_t>& writeDataMask =
1130 convertStringToData(filter[cmdMaskStr].get<std::string>());
1131 if (writeDataMask.size() != writeData.size())
1132 {
1133 log<level::ERR>("I2C master write read whitelist filter "
1134 "mismatch for command & mask size");
1135 return false;
1136 }
1137 whitelist.push_back(
1138 {static_cast<uint8_t>(std::stoul(
1139 filter[busIdStr].get<std::string>(), nullptr, base_16)),
1140 static_cast<uint8_t>(
1141 std::stoul(filter[slaveAddrStr].get<std::string>(),
1142 nullptr, base_16)),
1143 static_cast<uint8_t>(
1144 std::stoul(filter[slaveAddrMaskStr].get<std::string>(),
1145 nullptr, base_16)),
1146 writeData, writeDataMask});
1147 }
1148 if (whitelist.size() != filters.size())
1149 {
1150 log<level::ERR>(
1151 "I2C master write read whitelist filter size mismatch");
1152 return false;
1153 }
1154 }
1155 catch (std::exception& e)
1156 {
1157 log<level::ERR>("I2C master write read whitelist unexpected exception",
1158 entry("ERROR=%s", e.what()));
1159 return false;
1160 }
1161 return true;
1162}
1163
1164static inline bool isWriteDataWhitelisted(const std::vector<uint8_t>& data,
1165 const std::vector<uint8_t>& dataMask,
1166 const std::vector<uint8_t>& writeData)
1167{
1168 std::vector<uint8_t> processedDataBuf(data.size());
1169 std::vector<uint8_t> processedReqBuf(dataMask.size());
1170 std::transform(writeData.begin(), writeData.end(), dataMask.begin(),
1171 processedReqBuf.begin(), std::bit_or<uint8_t>());
1172 std::transform(data.begin(), data.end(), dataMask.begin(),
1173 processedDataBuf.begin(), std::bit_or<uint8_t>());
1174
1175 return (processedDataBuf == processedReqBuf);
1176}
1177
1178static bool isCmdWhitelisted(uint8_t busId, uint8_t slaveAddr,
1179 std::vector<uint8_t>& writeData)
1180{
1181 std::vector<i2cMasterWRWhitelist>& whiteList = getWRWhitelist();
1182 for (const auto& wlEntry : whiteList)
1183 {
1184 if ((busId == wlEntry.busId) &&
1185 ((slaveAddr | wlEntry.slaveAddrMask) ==
1186 (wlEntry.slaveAddr | wlEntry.slaveAddrMask)))
1187 {
1188 const std::vector<uint8_t>& dataMask = wlEntry.dataMask;
1189 // Skip as no-match, if requested write data is more than the
1190 // write data mask size
1191 if (writeData.size() > dataMask.size())
1192 {
1193 continue;
1194 }
1195 if (isWriteDataWhitelisted(wlEntry.data, dataMask, writeData))
1196 {
1197 return true;
1198 }
1199 }
1200 }
1201 return false;
1202}
Yong Libd0503a2019-08-22 17:17:17 +08001203#else
1204static bool populateI2CMasterWRWhitelist()
1205{
1206 log<level::INFO>(
1207 "I2C_WHITELIST_CHECK is disabled, do not populate whitelist");
1208 return true;
1209}
1210#endif // ENABLE_I2C_WHITELIST_CHECK
Richard Marian Thomaiyar84bf9be2019-04-26 22:59:16 +05301211
1212/** @brief implements master write read IPMI command which can be used for
1213 * low-level I2C/SMBus write, read or write-read access
1214 * @param isPrivateBus -to indicate private bus usage
1215 * @param busId - bus id
1216 * @param channelNum - channel number
1217 * @param reserved - skip 1 bit
1218 * @param slaveAddr - slave address
1219 * @param read count - number of bytes to be read
1220 * @param writeData - data to be written
1221 *
1222 * @returns IPMI completion code plus response data
1223 * - readData - i2c response data
1224 */
1225ipmi::RspType<std::vector<uint8_t>>
1226 ipmiMasterWriteRead(bool isPrivateBus, uint3_t busId, uint4_t channelNum,
1227 bool reserved, uint7_t slaveAddr, uint8_t readCount,
1228 std::vector<uint8_t> writeData)
1229{
Richard Marian Thomaiyar84bf9be2019-04-26 22:59:16 +05301230 if (readCount > maxIPMIWriteReadSize)
1231 {
1232 log<level::ERR>("Master write read command: Read count exceeds limit");
1233 return ipmi::responseParmOutOfRange();
1234 }
1235 const size_t writeCount = writeData.size();
1236 if (!readCount && !writeCount)
1237 {
1238 log<level::ERR>("Master write read command: Read & write count are 0");
1239 return ipmi::responseInvalidFieldRequest();
1240 }
Yong Libd0503a2019-08-22 17:17:17 +08001241#ifdef ENABLE_I2C_WHITELIST_CHECK
Richard Marian Thomaiyar84bf9be2019-04-26 22:59:16 +05301242 if (!isCmdWhitelisted(static_cast<uint8_t>(busId),
1243 static_cast<uint8_t>(slaveAddr), writeData))
1244 {
1245 log<level::ERR>("Master write read request blocked!",
1246 entry("BUS=%d", static_cast<uint8_t>(busId)),
1247 entry("ADDR=0x%x", static_cast<uint8_t>(slaveAddr)));
1248 return ipmi::responseInvalidFieldRequest();
1249 }
Yong Libd0503a2019-08-22 17:17:17 +08001250#endif // ENABLE_I2C_WHITELIST_CHECK
Richard Marian Thomaiyar84bf9be2019-04-26 22:59:16 +05301251 std::vector<uint8_t> readBuf(readCount);
1252 std::string i2cBus =
1253 "/dev/i2c-" + std::to_string(static_cast<uint8_t>(busId));
1254
Yong Li7dc4ac02019-08-23 17:44:32 +08001255 ipmi::Cc ret = ipmi::i2cWriteRead(i2cBus, static_cast<uint8_t>(slaveAddr),
1256 writeData, readBuf);
1257 if (ret != ipmi::ccSuccess)
Richard Marian Thomaiyar84bf9be2019-04-26 22:59:16 +05301258 {
Yong Li7dc4ac02019-08-23 17:44:32 +08001259 return ipmi::response(ret);
Richard Marian Thomaiyar84bf9be2019-04-26 22:59:16 +05301260 }
1261 return ipmi::responseSuccess(readBuf);
1262}
1263
Chris Austen6caf28b2015-10-13 12:40:40 -05001264void register_netfn_app_functions()
vishwabmcba0bd5f2015-09-30 16:50:23 +05301265{
Vernon Mauery86a50822019-03-25 13:11:36 -07001266 // <Get Device ID>
1267 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp,
1268 ipmi::app::cmdGetDeviceId, ipmi::Privilege::User,
1269 ipmiAppGetDeviceId);
1270
Tom05732372016-09-06 17:21:23 +05301271 // <Get BT Interface Capabilities>
Vernon Mauerycc99ba42019-03-25 13:40:11 -07001272 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp,
1273 ipmi::app::cmdGetBtIfaceCapabilities,
1274 ipmi::Privilege::User, ipmiAppGetBtCapabilities);
Chris Austen6caf28b2015-10-13 12:40:40 -05001275
Tom05732372016-09-06 17:21:23 +05301276 // <Reset Watchdog Timer>
Vernon Mauery11df4f62019-03-25 14:17:54 -07001277 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp,
1278 ipmi::app::cmdResetWatchdogTimer,
1279 ipmi::Privilege::Operator, ipmiAppResetWatchdogTimer);
Chris Austen6caf28b2015-10-13 12:40:40 -05001280
Tom05732372016-09-06 17:21:23 +05301281 // <Set Watchdog Timer>
Deepak Kumar Sahucfae9482019-05-20 14:58:58 +00001282 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp,
1283 ipmi::app::cmdSetWatchdogTimer,
1284 ipmi::Privilege::Operator, ipmiSetWatchdogTimer);
Chris Austen6caf28b2015-10-13 12:40:40 -05001285
Rajashekar Gade Reddye7023922019-07-10 16:54:55 +00001286 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp,
1287 ipmi::app::cmdCloseSession, ipmi::Privilege::Callback,
1288 ipmiAppCloseSession);
1289
William A. Kennington III73f44512018-02-09 15:28:46 -08001290 // <Get Watchdog Timer>
Deepak Kumar Sahucfae9482019-05-20 14:58:58 +00001291 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp,
1292 ipmi::app::cmdGetWatchdogTimer,
1293 ipmi::Privilege::Operator, ipmiGetWatchdogTimer);
William A. Kennington III73f44512018-02-09 15:28:46 -08001294
Tom05732372016-09-06 17:21:23 +05301295 // <Get Self Test Results>
Vernon Maueryb84a5282019-03-25 13:39:03 -07001296 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp,
1297 ipmi::app::cmdGetSelfTestResults,
1298 ipmi::Privilege::User, ipmiAppGetSelfTestResults);
Nan Li41fa24a2016-11-10 20:12:37 +08001299
Tom05732372016-09-06 17:21:23 +05301300 // <Get Device GUID>
Vernon Mauery15541322019-03-25 13:33:03 -07001301 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp,
1302 ipmi::app::cmdGetDeviceGuid, ipmi::Privilege::User,
1303 ipmiAppGetDeviceGuid);
Adriana Kobylakd100ee52015-10-20 17:02:37 -05001304
Tom05732372016-09-06 17:21:23 +05301305 // <Set ACPI Power State>
Patrick Venture0b02be92018-08-31 11:55:55 -07001306 ipmi_register_callback(NETFUN_APP, IPMI_CMD_SET_ACPI, NULL,
1307 ipmi_app_set_acpi_power_state, PRIVILEGE_ADMIN);
Chris Austen6caf28b2015-10-13 12:40:40 -05001308
Yong Li18d77262018-10-09 01:59:45 +08001309 // <Get ACPI Power State>
Deepak Kumar Sahu4e6d2572019-05-07 19:53:37 +00001310 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp,
1311 ipmi::app::cmdGetAcpiPowerState,
1312 ipmi::Privilege::Admin, ipmiGetAcpiPowerState);
Yong Li18d77262018-10-09 01:59:45 +08001313
Richard Marian Thomaiyar84bf9be2019-04-26 22:59:16 +05301314 // Note: For security reason, this command will be registered only when
1315 // there are proper I2C Master write read whitelist
1316 if (populateI2CMasterWRWhitelist())
1317 {
1318 // Note: For security reasons, registering master write read as admin
1319 // privilege command, even though IPMI 2.0 specification allows it as
1320 // operator privilege.
1321 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp,
1322 ipmi::app::cmdMasterWriteRead,
1323 ipmi::Privilege::Admin, ipmiMasterWriteRead);
1324 }
1325
Marri Devender Rao5e007a52018-01-08 06:18:36 -06001326 // <Get System GUID Command>
Vernon Maueryb90a5322019-03-25 13:36:55 -07001327 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp,
1328 ipmi::app::cmdGetSystemGuid, ipmi::Privilege::User,
1329 ipmiAppGetSystemGuid);
Tom Joseph7cbe2282018-03-21 21:17:33 +05301330
1331 // <Get Channel Cipher Suites Command>
Ayushi Smriti5c3b72c2019-08-30 13:47:31 +00001332 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp,
1333 ipmi::app::cmdGetChannelCipherSuites,
1334 ipmi::Privilege::Callback, getChannelCipherSuites);
Vernon Maueryd2a57de2019-03-25 12:45:28 -07001335
Xo Wangf542e8b2017-08-09 15:34:16 -07001336 // <Get System Info Command>
Jia, chunhui98b43ba2019-09-05 15:54:23 +08001337 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp,
1338 ipmi::app::cmdGetSystemInfoParameters,
1339 ipmi::Privilege::User, ipmiAppGetSystemInfo);
vishwabmcba0bd5f2015-09-30 16:50:23 +05301340 return;
1341}