1 #include "config.h"
2
3 #include <arpa/inet.h>
4 #include <fcntl.h>
5 #include <limits.h>
6 #include <linux/i2c-dev.h>
7 #include <linux/i2c.h>
8 #include <sys/ioctl.h>
9 #include <sys/stat.h>
10 #include <sys/types.h>
11 #include <systemd/sd-bus.h>
12 #include <unistd.h>
13
14 #include <app/channel.hpp>
15 #include <app/watchdog.hpp>
16 #include <apphandler.hpp>
17 #include <ipmid/api.hpp>
18 #include <ipmid/sessiondef.hpp>
19 #include <ipmid/sessionhelper.hpp>
20 #include <ipmid/types.hpp>
21 #include <ipmid/utils.hpp>
22 #include <nlohmann/json.hpp>
23 #include <phosphor-logging/elog-errors.hpp>
24 #include <phosphor-logging/lg2.hpp>
25 #include <sdbusplus/message/types.hpp>
26 #include <sys_info_param.hpp>
27 #include <xyz/openbmc_project/Common/error.hpp>
28 #include <xyz/openbmc_project/Control/Power/ACPIPowerState/server.hpp>
29 #include <xyz/openbmc_project/Software/Activation/server.hpp>
30 #include <xyz/openbmc_project/Software/Version/server.hpp>
31 #include <xyz/openbmc_project/State/BMC/server.hpp>
32
33 #include <algorithm>
34 #include <array>
35 #include <charconv>
36 #include <cstddef>
37 #include <cstdint>
38 #include <filesystem>
39 #include <fstream>
40 #include <memory>
41 #include <regex>
42 #include <string>
43 #include <string_view>
44 #include <tuple>
45 #include <vector>
46
47 extern sd_bus* bus;
48
49 constexpr auto versionPurposeHostEnd = ".Host";
50
51 static constexpr auto redundancyIntf =
52 "xyz.openbmc_project.Software.RedundancyPriority";
53 static constexpr auto versionIntf = "xyz.openbmc_project.Software.Version";
54 static constexpr auto activationIntf =
55 "xyz.openbmc_project.Software.Activation";
56 static constexpr auto softwareRoot = "/xyz/openbmc_project/software";
57
58 void registerNetFnAppFunctions() __attribute__((constructor));
59
60 using namespace phosphor::logging;
61 using namespace sdbusplus::error::xyz::openbmc_project::common;
62 using Version = sdbusplus::server::xyz::openbmc_project::software::Version;
63 using Activation =
64 sdbusplus::server::xyz::openbmc_project::software::Activation;
65 using BMCState = sdbusplus::server::xyz::openbmc_project::state::BMC;
66 namespace fs = std::filesystem;
67
68 #ifdef ENABLE_I2C_WHITELIST_CHECK
69 typedef struct
70 {
71 uint8_t busId;
72 uint8_t targetAddr;
73 uint8_t targetAddrMask;
74 std::vector<uint8_t> data;
75 std::vector<uint8_t> dataMask;
76 } i2cControllerWRAllowlist;
77
getWRAllowlist()78 static std::vector<i2cControllerWRAllowlist>& getWRAllowlist()
79 {
80 static std::vector<i2cControllerWRAllowlist> wrAllowlist;
81 return wrAllowlist;
82 }
83
84 static constexpr const char* i2cControllerWRAllowlistFile =
85 "/usr/share/ipmi-providers/master_write_read_white_list.json";
86
87 static constexpr const char* filtersStr = "filters";
88 static constexpr const char* busIdStr = "busId";
89 static constexpr const char* targetAddrStr = "slaveAddr";
90 static constexpr const char* targetAddrMaskStr = "slaveAddrMask";
91 static constexpr const char* cmdStr = "command";
92 static constexpr const char* cmdMaskStr = "commandMask";
93 static constexpr int base_16 = 16;
94 #endif // ENABLE_I2C_WHITELIST_CHECK
95 static constexpr uint8_t oemCmdStart = 192;
96 static constexpr uint8_t invalidParamSelectorStart = 8;
97 static constexpr uint8_t invalidParamSelectorEnd = 191;
98
99 /**
100 * @brief Returns the Version info from primary s/w object
101 *
102 * Get the Version info from the active s/w object which is having high
103 * "Priority" value(a smaller number is a higher priority) and "Purpose"
104 * is "BMC" from the list of all s/w objects those are implementing
105 * RedundancyPriority interface from the given softwareRoot path.
106 *
107 * @return On success returns the Version info from primary s/w object.
108 *
109 */
getActiveSoftwareVersionInfo(ipmi::Context::ptr ctx)110 std::string getActiveSoftwareVersionInfo(ipmi::Context::ptr ctx)
111 {
112 std::string revision{};
113 ipmi::ObjectTree objectTree;
114 try
115 {
116 objectTree =
117 ipmi::getAllDbusObjects(*ctx->bus, softwareRoot, redundancyIntf);
118 }
119 catch (const sdbusplus::exception_t& e)
120 {
121 lg2::error("Failed to fetch redundancy object from dbus, "
122 "interface: {INTERFACE}, error: {ERROR}",
123 "INTERFACE", redundancyIntf, "ERROR", e);
124 elog<InternalFailure>();
125 }
126
127 auto objectFound = false;
128 for (auto& softObject : objectTree)
129 {
130 auto service =
131 ipmi::getService(*ctx->bus, redundancyIntf, softObject.first);
132 auto objValueTree =
133 ipmi::getManagedObjects(*ctx->bus, service, softwareRoot);
134
135 auto minPriority = 0xFF;
136 for (const auto& objIter : objValueTree)
137 {
138 try
139 {
140 auto& intfMap = objIter.second;
141 auto& redundancyPriorityProps = intfMap.at(redundancyIntf);
142 auto& versionProps = intfMap.at(versionIntf);
143 auto& activationProps = intfMap.at(activationIntf);
144 auto priority =
145 std::get<uint8_t>(redundancyPriorityProps.at("Priority"));
146 auto purpose =
147 std::get<std::string>(versionProps.at("Purpose"));
148 auto activation =
149 std::get<std::string>(activationProps.at("Activation"));
150 auto version =
151 std::get<std::string>(versionProps.at("Version"));
152 if ((Version::convertVersionPurposeFromString(purpose) ==
153 Version::VersionPurpose::BMC) &&
154 (Activation::convertActivationsFromString(activation) ==
155 Activation::Activations::Active))
156 {
157 if (priority < minPriority)
158 {
159 minPriority = priority;
160 objectFound = true;
161 revision = std::move(version);
162 }
163 }
164 }
165 catch (const std::exception& e)
166 {
167 lg2::error("error message: {ERROR}", "ERROR", e);
168 }
169 }
170 }
171
172 if (!objectFound)
173 {
174 lg2::error("Could not found an BMC software Object");
175 elog<InternalFailure>();
176 }
177
178 return revision;
179 }
180
getCurrentBmcState()181 bool getCurrentBmcState()
182 {
183 sdbusplus::bus_t bus{ipmid_get_sd_bus_connection()};
184
185 // Get the Inventory object implementing the BMC interface
186 ipmi::DbusObjectInfo bmcObject =
187 ipmi::getDbusObject(bus, BMCState::interface);
188 auto variant = ipmi::getDbusProperty(
189 bus, bmcObject.second, bmcObject.first, BMCState::interface,
190 BMCState::property_names::current_bmc_state);
191
192 return std::holds_alternative<std::string>(variant) &&
193 BMCState::convertBMCStateFromString(
194 std::get<std::string>(variant)) == BMCState::BMCState::Ready;
195 }
196
getCurrentBmcStateWithFallback(const bool fallbackAvailability)197 bool getCurrentBmcStateWithFallback(const bool fallbackAvailability)
198 {
199 try
200 {
201 return getCurrentBmcState();
202 }
203 catch (...)
204 {
205 // Nothing provided the BMC interface, therefore return whatever was
206 // configured as the default.
207 return fallbackAvailability;
208 }
209 }
210
211 namespace acpi_state
212 {
213 using namespace sdbusplus::server::xyz::openbmc_project::control::power;
214
215 const static constexpr char* acpiInterface =
216 "xyz.openbmc_project.Control.Power.ACPIPowerState";
217 const static constexpr char* sysACPIProp = "SysACPIStatus";
218 const static constexpr char* devACPIProp = "DevACPIStatus";
219
220 enum class PowerStateType : uint8_t
221 {
222 sysPowerState = 0x00,
223 devPowerState = 0x01,
224 };
225
226 // Defined in 20.6 of ipmi doc
227 enum class PowerState : uint8_t
228 {
229 s0G0D0 = 0x00,
230 s1D1 = 0x01,
231 s2D2 = 0x02,
232 s3D3 = 0x03,
233 s4 = 0x04,
234 s5G2 = 0x05,
235 s4S5 = 0x06,
236 g3 = 0x07,
237 sleep = 0x08,
238 g1Sleep = 0x09,
239 override = 0x0a,
240 legacyOn = 0x20,
241 legacyOff = 0x21,
242 unknown = 0x2a,
243 noChange = 0x7f,
244 };
245
246 static constexpr uint8_t stateChanged = 0x80;
247
248 std::map<ACPIPowerState::ACPI, PowerState> dbusToIPMI = {
249 {ACPIPowerState::ACPI::S0_G0_D0, PowerState::s0G0D0},
250 {ACPIPowerState::ACPI::S1_D1, PowerState::s1D1},
251 {ACPIPowerState::ACPI::S2_D2, PowerState::s2D2},
252 {ACPIPowerState::ACPI::S3_D3, PowerState::s3D3},
253 {ACPIPowerState::ACPI::S4, PowerState::s4},
254 {ACPIPowerState::ACPI::S5_G2, PowerState::s5G2},
255 {ACPIPowerState::ACPI::S4_S5, PowerState::s4S5},
256 {ACPIPowerState::ACPI::G3, PowerState::g3},
257 {ACPIPowerState::ACPI::SLEEP, PowerState::sleep},
258 {ACPIPowerState::ACPI::G1_SLEEP, PowerState::g1Sleep},
259 {ACPIPowerState::ACPI::OVERRIDE, PowerState::override},
260 {ACPIPowerState::ACPI::LEGACY_ON, PowerState::legacyOn},
261 {ACPIPowerState::ACPI::LEGACY_OFF, PowerState::legacyOff},
262 {ACPIPowerState::ACPI::Unknown, PowerState::unknown}};
263
isValidACPIState(acpi_state::PowerStateType type,uint8_t state)264 bool isValidACPIState(acpi_state::PowerStateType type, uint8_t state)
265 {
266 if (type == acpi_state::PowerStateType::sysPowerState)
267 {
268 if ((state <= static_cast<uint8_t>(acpi_state::PowerState::override)) ||
269 (state == static_cast<uint8_t>(acpi_state::PowerState::legacyOn)) ||
270 (state ==
271 static_cast<uint8_t>(acpi_state::PowerState::legacyOff)) ||
272 (state == static_cast<uint8_t>(acpi_state::PowerState::unknown)) ||
273 (state == static_cast<uint8_t>(acpi_state::PowerState::noChange)))
274 {
275 return true;
276 }
277 else
278 {
279 return false;
280 }
281 }
282 else if (type == acpi_state::PowerStateType::devPowerState)
283 {
284 if ((state <= static_cast<uint8_t>(acpi_state::PowerState::s3D3)) ||
285 (state == static_cast<uint8_t>(acpi_state::PowerState::unknown)) ||
286 (state == static_cast<uint8_t>(acpi_state::PowerState::noChange)))
287 {
288 return true;
289 }
290 else
291 {
292 return false;
293 }
294 }
295 else
296 {
297 return false;
298 }
299 return false;
300 }
301 } // namespace acpi_state
302
303 /** @brief implements Set ACPI Power State command
304 * @param sysAcpiState - ACPI system power state to set
305 * @param devAcpiState - ACPI device power state to set
306 *
307 * @return IPMI completion code on success
308 **/
ipmiSetAcpiPowerState(uint8_t sysAcpiState,uint8_t devAcpiState)309 ipmi::RspType<> ipmiSetAcpiPowerState(uint8_t sysAcpiState,
310 uint8_t devAcpiState)
311 {
312 auto s = static_cast<uint8_t>(acpi_state::PowerState::unknown);
313
314 sdbusplus::bus_t bus{ipmid_get_sd_bus_connection()};
315
316 auto value = acpi_state::ACPIPowerState::ACPI::Unknown;
317
318 if (sysAcpiState & acpi_state::stateChanged)
319 {
320 // set system power state
321 s = sysAcpiState & ~acpi_state::stateChanged;
322
323 if (!acpi_state::isValidACPIState(
324 acpi_state::PowerStateType::sysPowerState, s))
325 {
326 lg2::error("set_acpi_power sys invalid input, S: {S}", "S", s);
327 return ipmi::responseParmOutOfRange();
328 }
329
330 // valid input
331 if (s == static_cast<uint8_t>(acpi_state::PowerState::noChange))
332 {
333 lg2::debug("No change for system power state");
334 }
335 else
336 {
337 auto found = std::find_if(
338 acpi_state::dbusToIPMI.begin(), acpi_state::dbusToIPMI.end(),
339 [&s](const auto& iter) {
340 return (static_cast<uint8_t>(iter.second) == s);
341 });
342
343 value = found->first;
344
345 try
346 {
347 auto acpiObject =
348 ipmi::getDbusObject(bus, acpi_state::acpiInterface);
349 ipmi::setDbusProperty(bus, acpiObject.second, acpiObject.first,
350 acpi_state::acpiInterface,
351 acpi_state::sysACPIProp,
352 convertForMessage(value));
353 }
354 catch (const InternalFailure& e)
355 {
356 lg2::error("Failed in set ACPI system property: {ERROR}",
357 "ERROR", e);
358 return ipmi::responseUnspecifiedError();
359 }
360 }
361 }
362 else
363 {
364 lg2::debug("Do not change system power state");
365 }
366
367 if (devAcpiState & acpi_state::stateChanged)
368 {
369 // set device power state
370 s = devAcpiState & ~acpi_state::stateChanged;
371 if (!acpi_state::isValidACPIState(
372 acpi_state::PowerStateType::devPowerState, s))
373 {
374 lg2::error("set_acpi_power dev invalid input, S: {S}", "S", s);
375 return ipmi::responseParmOutOfRange();
376 }
377
378 // valid input
379 if (s == static_cast<uint8_t>(acpi_state::PowerState::noChange))
380 {
381 lg2::debug("No change for device power state");
382 }
383 else
384 {
385 auto found = std::find_if(
386 acpi_state::dbusToIPMI.begin(), acpi_state::dbusToIPMI.end(),
387 [&s](const auto& iter) {
388 return (static_cast<uint8_t>(iter.second) == s);
389 });
390
391 value = found->first;
392
393 try
394 {
395 auto acpiObject =
396 ipmi::getDbusObject(bus, acpi_state::acpiInterface);
397 ipmi::setDbusProperty(bus, acpiObject.second, acpiObject.first,
398 acpi_state::acpiInterface,
399 acpi_state::devACPIProp,
400 convertForMessage(value));
401 }
402 catch (const InternalFailure& e)
403 {
404 lg2::error("Failed in set ACPI device property: {ERROR}",
405 "ERROR", e);
406 return ipmi::responseUnspecifiedError();
407 }
408 }
409 }
410 else
411 {
412 lg2::debug("Do not change device power state");
413 }
414 return ipmi::responseSuccess();
415 }
416
417 /**
418 * @brief implements the get ACPI power state command
419 *
420 * @return IPMI completion code plus response data on success.
421 * - ACPI system power state
422 * - ACPI device power state
423 **/
424 ipmi::RspType<uint8_t, // acpiSystemPowerState
425 uint8_t // acpiDevicePowerState
426 >
ipmiGetAcpiPowerState()427 ipmiGetAcpiPowerState()
428 {
429 uint8_t sysAcpiState;
430 uint8_t devAcpiState;
431
432 sdbusplus::bus_t bus{ipmid_get_sd_bus_connection()};
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(
442 std::get<std::string>(sysACPIVal));
443 sysAcpiState = static_cast<uint8_t>(acpi_state::dbusToIPMI.at(sysACPI));
444
445 auto devACPIVal = ipmi::getDbusProperty(
446 bus, acpiObject.second, acpiObject.first, acpi_state::acpiInterface,
447 acpi_state::devACPIProp);
448 auto devACPI = acpi_state::ACPIPowerState::convertACPIFromString(
449 std::get<std::string>(devACPIVal));
450 devAcpiState = static_cast<uint8_t>(acpi_state::dbusToIPMI.at(devACPI));
451 }
452 catch (const InternalFailure& e)
453 {
454 return ipmi::responseUnspecifiedError();
455 }
456
457 return ipmi::responseSuccess(sysAcpiState, devAcpiState);
458 }
459
460 typedef struct
461 {
462 char major;
463 char minor;
464 uint8_t aux[4];
465 } Revision;
466
467 /* Use regular expression searching matched pattern X.Y, and convert it to */
468 /* Major (X) and Minor (Y) version. */
469 /* Example: */
470 /* version = 2.14.0-dev */
471 /* ^ ^ */
472 /* | |---------------- Minor */
473 /* |------------------ Major */
474 /* */
475 /* Default regex string only tries to match Major and Minor version. */
476 /* */
477 /* To match more firmware version info, platforms need to define it own */
478 /* regex string to match more strings, and assign correct mapping index in */
479 /* matches array. */
480 /* */
481 /* matches[0]: matched index for major ver */
482 /* matches[1]: matched index for minor ver */
483 /* matches[2]: matched index for aux[0] (set 0 to skip) */
484 /* matches[3]: matched index for aux[1] (set 0 to skip) */
485 /* matches[4]: matched index for aux[2] (set 0 to skip) */
486 /* matches[5]: matched index for aux[3] (set 0 to skip) */
487 /* Example: */
488 /* regex = "([\d]+).([\d]+).([\d]+)-dev-([\d]+)-g([0-9a-fA-F]{2}) */
489 /* ([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})" */
490 /* matches = {1,2,5,6,7,8} */
491 /* version = 2.14.0-dev-750-g37a7c5ad1-dirty */
492 /* ^ ^ ^ ^ ^ ^ ^ ^ */
493 /* | | | | | | | | */
494 /* | | | | | | | |-- Aux byte 3 (0xAD), index 8 */
495 /* | | | | | | |---- Aux byte 2 (0xC5), index 7 */
496 /* | | | | | |------ Aux byte 1 (0xA7), index 6 */
497 /* | | | | |-------- Aux byte 0 (0x37), index 5 */
498 /* | | | |------------- Not used, index 4 */
499 /* | | |------------------- Not used, index 3 */
500 /* | |---------------------- Minor (14), index 2 */
501 /* |------------------------ Major (2), index 1 */
convertVersion(std::string s,Revision & rev)502 int convertVersion(std::string s, Revision& rev)
503 {
504 static const std::vector<size_t> matches = {
505 MAJOR_MATCH_INDEX, MINOR_MATCH_INDEX, AUX_0_MATCH_INDEX,
506 AUX_1_MATCH_INDEX, AUX_2_MATCH_INDEX, AUX_3_MATCH_INDEX};
507 std::regex fw_regex(FW_VER_REGEX);
508 std::smatch m;
509 Revision r = {0};
510 size_t val;
511
512 if (std::regex_search(s, m, fw_regex))
513 {
514 if (m.size() < *std::max_element(matches.begin(), matches.end()))
515 { // max index higher than match count
516 return -1;
517 }
518
519 // convert major
520 {
521 std::string str = m[matches[0]].str();
522 const auto& [ptr, ec] =
523 std::from_chars(str.data(), str.data() + str.size(), val);
524 if (ec != std::errc() || ptr != str.data() + str.size())
525 { // failed to convert major string
526 return -1;
527 }
528
529 if (val >= 2000)
530 { // For the platforms use year as major version, it would expect to
531 // have major version between 0 - 99. If the major version is
532 // greater than or equal to 2000, it is treated as a year and
533 // converted to 0 - 99.
534 r.major = val % 100;
535 }
536 else
537 {
538 r.major = val & 0x7F;
539 }
540 }
541
542 // convert minor
543 {
544 std::string str = m[matches[1]].str();
545 const auto& [ptr, ec] =
546 std::from_chars(str.data(), str.data() + str.size(), val);
547 if (ec != std::errc() || ptr != str.data() + str.size())
548 { // failed to convert minor string
549 return -1;
550 }
551 r.minor = val & 0xFF;
552 }
553
554 // convert aux bytes
555 {
556 size_t i;
557 for (i = 0; i < 4; i++)
558 {
559 if (matches[i + 2] == 0)
560 {
561 continue;
562 }
563
564 std::string str = m[matches[i + 2]].str();
565 const char* cstr = str.c_str();
566 auto [ptr,
567 ec] = std::from_chars(cstr, cstr + str.size(), val, 16);
568 if (ec != std::errc() || ptr != cstr + str.size())
569 { // failed to convert aux byte string
570 break;
571 }
572
573 r.aux[i] = val & 0xFF;
574 }
575
576 if (i != 4)
577 { // something wrong durign converting aux bytes
578 return -1;
579 }
580 }
581
582 // all matched
583 rev = r;
584 return 0;
585 }
586
587 return -1;
588 }
589
590 /* @brief: Implement the Get Device ID IPMI command per the IPMI spec
591 * @param[in] ctx - shared_ptr to an IPMI context struct
592 *
593 * @returns IPMI completion code plus response data
594 * - Device ID (manufacturer defined)
595 * - Device revision[4 bits]; reserved[3 bits]; SDR support[1 bit]
596 * - FW revision major[7 bits] (binary encoded); available[1 bit]
597 * - FW Revision minor (BCD encoded)
598 * - IPMI version (0x02 for IPMI 2.0)
599 * - device support (bitfield of supported options)
600 * - MFG IANA ID (3 bytes)
601 * - product ID (2 bytes)
602 * - AUX info (4 bytes)
603 */
604 ipmi::RspType<uint8_t, // Device ID
605 uint8_t, // Device Revision
606 uint8_t, // Firmware Revision Major
607 uint8_t, // Firmware Revision minor
608 uint8_t, // IPMI version
609 uint8_t, // Additional device support
610 uint24_t, // MFG ID
611 uint16_t, // Product ID
612 uint32_t // AUX info
613 >
ipmiAppGetDeviceId(ipmi::Context::ptr ctx)614 ipmiAppGetDeviceId([[maybe_unused]] ipmi::Context::ptr ctx)
615 {
616 static struct
617 {
618 uint8_t id;
619 uint8_t revision;
620 uint8_t fw[2];
621 uint8_t ipmiVer;
622 uint8_t addnDevSupport;
623 uint24_t manufId;
624 uint16_t prodId;
625 uint32_t aux;
626 } devId;
627 static bool dev_id_initialized = false;
628 static bool defaultActivationSetting = true;
629 const char* filename = "/usr/share/ipmi-providers/dev_id.json";
630 constexpr auto ipmiDevIdStateShift = 7;
631 constexpr auto ipmiDevIdFw1Mask = ~(1 << ipmiDevIdStateShift);
632
633 #ifdef GET_DBUS_ACTIVE_SOFTWARE
634 static bool haveBMCVersion = false;
635 if (!haveBMCVersion || !dev_id_initialized)
636 {
637 int r = -1;
638 Revision rev = {0, 0, {0, 0, 0, 0}};
639 try
640 {
641 auto version = getActiveSoftwareVersionInfo(ctx);
642 r = convertVersion(version, rev);
643 }
644 catch (const std::exception& e)
645 {
646 lg2::error("error message: {ERROR}", "ERROR", e);
647 }
648
649 if (r >= 0)
650 {
651 // bit7 identifies if the device is available
652 // 0=normal operation
653 // 1=device firmware, SDR update,
654 // or self-initialization in progress.
655 // The availability may change in run time, so mask here
656 // and initialize later.
657 devId.fw[0] = rev.major & ipmiDevIdFw1Mask;
658
659 rev.minor = (rev.minor > 99 ? 99 : rev.minor);
660 devId.fw[1] = rev.minor % 10 + (rev.minor / 10) * 16;
661 std::memcpy(&devId.aux, rev.aux, sizeof(rev.aux));
662 haveBMCVersion = true;
663 }
664 }
665 #endif
666 if (!dev_id_initialized)
667 {
668 // IPMI Spec version 2.0
669 devId.ipmiVer = 2;
670
671 std::ifstream devIdFile(filename);
672 if (devIdFile.is_open())
673 {
674 auto data = nlohmann::json::parse(devIdFile, nullptr, false);
675 if (!data.is_discarded())
676 {
677 devId.id = data.value("id", 0);
678 devId.revision = data.value("revision", 0);
679 devId.addnDevSupport = data.value("addn_dev_support", 0);
680 devId.manufId = data.value("manuf_id", 0);
681 devId.prodId = data.value("prod_id", 0);
682 #ifdef GET_DBUS_ACTIVE_SOFTWARE
683 if (!(AUX_0_MATCH_INDEX || AUX_1_MATCH_INDEX ||
684 AUX_2_MATCH_INDEX || AUX_3_MATCH_INDEX))
685 #endif
686 {
687 devId.aux = data.value("aux", 0);
688 }
689
690 if (data.contains("firmware_revision"))
691 {
692 const auto& firmwareRevision = data.at("firmware_revision");
693 if (firmwareRevision.contains("major"))
694 {
695 firmwareRevision.at("major").get_to(devId.fw[0]);
696 }
697 if (firmwareRevision.contains("minor"))
698 {
699 firmwareRevision.at("minor").get_to(devId.fw[1]);
700 }
701 }
702
703 // Set the availablitity of the BMC.
704 defaultActivationSetting = data.value("availability", true);
705
706 // Don't read the file every time if successful
707 dev_id_initialized = true;
708 }
709 else
710 {
711 lg2::error("Device ID JSON parser failure");
712 return ipmi::responseUnspecifiedError();
713 }
714 }
715 else
716 {
717 lg2::error("Device ID file not found");
718 return ipmi::responseUnspecifiedError();
719 }
720 }
721
722 // Set availability to the actual current BMC state
723 devId.fw[0] &= ipmiDevIdFw1Mask;
724 if (!getCurrentBmcStateWithFallback(defaultActivationSetting))
725 {
726 devId.fw[0] |= (1 << ipmiDevIdStateShift);
727 }
728
729 return ipmi::responseSuccess(
730 devId.id, devId.revision, devId.fw[0], devId.fw[1], devId.ipmiVer,
731 devId.addnDevSupport, devId.manufId, devId.prodId, devId.aux);
732 }
733
ipmiAppGetSelfTestResults()734 auto ipmiAppGetSelfTestResults() -> ipmi::RspType<uint8_t, uint8_t>
735 {
736 // Byte 2:
737 // 55h - No error.
738 // 56h - Self Test function not implemented in this controller.
739 // 57h - Corrupted or inaccesssible data or devices.
740 // 58h - Fatal hardware error.
741 // FFh - reserved.
742 // all other: Device-specific 'internal failure'.
743 // Byte 3:
744 // For byte 2 = 55h, 56h, FFh: 00h
745 // For byte 2 = 58h, all other: Device-specific
746 // For byte 2 = 57h: self-test error bitfield.
747 // Note: returning 57h does not imply that all test were run.
748 // [7] 1b = Cannot access SEL device.
749 // [6] 1b = Cannot access SDR Repository.
750 // [5] 1b = Cannot access BMC FRU device.
751 // [4] 1b = IPMB signal lines do not respond.
752 // [3] 1b = SDR Repository empty.
753 // [2] 1b = Internal Use Area of BMC FRU corrupted.
754 // [1] 1b = controller update 'boot block' firmware corrupted.
755 // [0] 1b = controller operational firmware corrupted.
756 constexpr uint8_t notImplemented = 0x56;
757 constexpr uint8_t zero = 0;
758 return ipmi::responseSuccess(notImplemented, zero);
759 }
760
761 static constexpr size_t uuidBinaryLength = 16;
rfc4122ToIpmi(std::string rfc4122)762 static std::array<uint8_t, uuidBinaryLength> rfc4122ToIpmi(std::string rfc4122)
763 {
764 using Argument = xyz::openbmc_project::common::InvalidArgument;
765 // UUID is in RFC4122 format. Ex: 61a39523-78f2-11e5-9862-e6402cfc3223
766 // Per IPMI Spec 2.0 need to convert to 16 hex bytes and reverse the byte
767 // order
768 // Ex: 0x2332fc2c40e66298e511f2782395a361
769 constexpr size_t uuidHexLength = (2 * uuidBinaryLength);
770 constexpr size_t uuidRfc4122Length = (uuidHexLength + 4);
771 std::array<uint8_t, uuidBinaryLength> uuid;
772 if (rfc4122.size() == uuidRfc4122Length)
773 {
774 rfc4122.erase(std::remove(rfc4122.begin(), rfc4122.end(), '-'),
775 rfc4122.end());
776 }
777 if (rfc4122.size() != uuidHexLength)
778 {
779 elog<InvalidArgument>(Argument::ARGUMENT_NAME("rfc4122"),
780 Argument::ARGUMENT_VALUE(rfc4122.c_str()));
781 }
782 for (size_t ind = 0; ind < uuidHexLength; ind += 2)
783 {
784 char v[3];
785 v[0] = rfc4122[ind];
786 v[1] = rfc4122[ind + 1];
787 v[2] = 0;
788 size_t err;
789 long b;
790 try
791 {
792 b = std::stoul(v, &err, 16);
793 }
794 catch (const std::exception& e)
795 {
796 elog<InvalidArgument>(Argument::ARGUMENT_NAME("rfc4122"),
797 Argument::ARGUMENT_VALUE(rfc4122.c_str()));
798 }
799 // check that exactly two ascii bytes were converted
800 if (err != 2)
801 {
802 elog<InvalidArgument>(Argument::ARGUMENT_NAME("rfc4122"),
803 Argument::ARGUMENT_VALUE(rfc4122.c_str()));
804 }
805 uuid[uuidBinaryLength - (ind / 2) - 1] = static_cast<uint8_t>(b);
806 }
807 return uuid;
808 }
809
ipmiAppGetDeviceGuid()810 auto ipmiAppGetDeviceGuid()
811 -> ipmi::RspType<std::array<uint8_t, uuidBinaryLength>>
812 {
813 // return a fixed GUID based on /etc/machine-id
814 // This should match the /redfish/v1/Managers/bmc's UUID data
815
816 // machine specific application ID (for BMC ID)
817 // generated by systemd-id128 -p new as per man page
818 static constexpr sd_id128_t bmcUuidAppId = SD_ID128_MAKE(
819 e0, e1, 73, 76, 64, 61, 47, da, a5, 0c, d0, cc, 64, 12, 45, 78);
820
821 sd_id128_t bmcUuid;
822 // create the UUID from /etc/machine-id via the systemd API
823 sd_id128_get_machine_app_specific(bmcUuidAppId, &bmcUuid);
824
825 char bmcUuidCstr[SD_ID128_STRING_MAX];
826 std::string systemUuid = sd_id128_to_string(bmcUuid, bmcUuidCstr);
827
828 std::array<uint8_t, uuidBinaryLength> uuid = rfc4122ToIpmi(systemUuid);
829 return ipmi::responseSuccess(uuid);
830 }
831
ipmiAppGetBtCapabilities()832 auto ipmiAppGetBtCapabilities()
833 -> ipmi::RspType<uint8_t, uint8_t, uint8_t, uint8_t, uint8_t>
834 {
835 // Per IPMI 2.0 spec, the input and output buffer size must be the max
836 // buffer size minus one byte to allocate space for the length byte.
837 constexpr uint8_t nrOutstanding = 0x01;
838 constexpr uint8_t inputBufferSize = MAX_IPMI_BUFFER - 1;
839 constexpr uint8_t outputBufferSize = MAX_IPMI_BUFFER - 1;
840 constexpr uint8_t transactionTime = 0x0A;
841 constexpr uint8_t nrRetries = 0x01;
842
843 return ipmi::responseSuccess(nrOutstanding, inputBufferSize,
844 outputBufferSize, transactionTime, nrRetries);
845 }
846
ipmiAppGetSystemGuid(ipmi::Context::ptr & ctx)847 auto ipmiAppGetSystemGuid(ipmi::Context::ptr& ctx)
848 -> ipmi::RspType<std::array<uint8_t, 16>>
849 {
850 static constexpr auto uuidService = "xyz.openbmc_project.Settings";
851 static constexpr auto uuidObject = "/xyz/openbmc_project/Common/UUID";
852 static constexpr auto uuidInterface = "xyz.openbmc_project.Common.UUID";
853 static constexpr auto uuidProperty = "UUID";
854
855 // Read UUID property value from settings object
856 // UUID is in RFC4122 format Ex: 61a39523-78f2-11e5-9862-e6402cfc3223
857 std::string rfc4122Uuid{};
858 boost::system::error_code ec = ipmi::getDbusProperty(
859 ctx, uuidService, uuidObject, uuidInterface, uuidProperty, rfc4122Uuid);
860 if (ec.value())
861 {
862 lg2::error("Failed to read System UUID property, "
863 "interface: {INTERFACE}, property: {PROPERTY}, "
864 "error: {ERROR}",
865 "INTERFACE", uuidInterface, "PROPERTY", uuidProperty,
866 "ERROR", ec.message());
867 return ipmi::responseUnspecifiedError();
868 }
869 std::array<uint8_t, 16> uuid;
870 try
871 {
872 // convert to IPMI format
873 uuid = rfc4122ToIpmi(rfc4122Uuid);
874 }
875 catch (const InvalidArgument& e)
876 {
877 lg2::error("Failed in parsing BMC UUID property, "
878 "interface: {INTERFACE}, property: {PROPERTY}, "
879 "value: {VALUE}, error: {ERROR}",
880 "INTERFACE", uuidInterface, "PROPERTY", uuidProperty,
881 "VALUE", rfc4122Uuid, "ERROR", e);
882 return ipmi::responseUnspecifiedError();
883 }
884 return ipmi::responseSuccess(uuid);
885 }
886
887 /**
888 * @brief set the session state as teardown
889 *
890 * This function is to set the session state to tear down in progress if the
891 * state is active.
892 *
893 * @param[in] busp - Dbus obj
894 * @param[in] service - service name
895 * @param[in] obj - object path
896 *
897 * @return success completion code if it sets the session state to
898 * tearDownInProgress else return the corresponding error completion code.
899 **/
setSessionState(std::shared_ptr<sdbusplus::asio::connection> & busp,const std::string & service,const std::string & obj)900 uint8_t setSessionState(std::shared_ptr<sdbusplus::asio::connection>& busp,
901 const std::string& service, const std::string& obj)
902 {
903 try
904 {
905 uint8_t sessionState = std::get<uint8_t>(ipmi::getDbusProperty(
906 *busp, service, obj, session::sessionIntf, "State"));
907
908 if (sessionState == static_cast<uint8_t>(session::State::active))
909 {
910 ipmi::setDbusProperty(
911 *busp, service, obj, session::sessionIntf, "State",
912 static_cast<uint8_t>(session::State::tearDownInProgress));
913 return ipmi::ccSuccess;
914 }
915 }
916 catch (const std::exception& e)
917 {
918 lg2::error("Failed in getting session state property, "
919 "service: {SERVICE}, object path: {OBJECT_PATH}, "
920 "interface: {INTERFACE}, error: {ERROR}",
921 "SERVICE", service, "OBJECT_PATH", obj, "INTERFACE",
922 session::sessionIntf, "ERROR", e);
923 return ipmi::ccUnspecifiedError;
924 }
925
926 return ipmi::ccInvalidFieldRequest;
927 }
928
ipmiAppCloseSession(uint32_t reqSessionId,std::optional<uint8_t> requestSessionHandle)929 ipmi::RspType<> ipmiAppCloseSession(uint32_t reqSessionId,
930 std::optional<uint8_t> requestSessionHandle)
931 {
932 auto busp = getSdBus();
933 uint8_t reqSessionHandle =
934 requestSessionHandle.value_or(session::defaultSessionHandle);
935
936 if (reqSessionId == session::sessionZero &&
937 reqSessionHandle == session::defaultSessionHandle)
938 {
939 return ipmi::response(session::ccInvalidSessionId);
940 }
941
942 if (reqSessionId == session::sessionZero &&
943 reqSessionHandle == session::invalidSessionHandle)
944 {
945 return ipmi::response(session::ccInvalidSessionHandle);
946 }
947
948 if (reqSessionId != session::sessionZero &&
949 reqSessionHandle != session::defaultSessionHandle)
950 {
951 return ipmi::response(ipmi::ccInvalidFieldRequest);
952 }
953
954 try
955 {
956 ipmi::ObjectTree objectTree = ipmi::getAllDbusObjects(
957 *busp, session::sessionManagerRootPath, session::sessionIntf);
958
959 for (auto& objectTreeItr : objectTree)
960 {
961 const std::string obj = objectTreeItr.first;
962
963 if (isSessionObjectMatched(obj, reqSessionId, reqSessionHandle))
964 {
965 auto& serviceMap = objectTreeItr.second;
966
967 // Session id and session handle are unique for each session.
968 // Session id and handler are retrived from the object path and
969 // object path will be unique for each session. Checking if
970 // multiple objects exist with same object path under multiple
971 // services.
972 if (serviceMap.size() != 1)
973 {
974 return ipmi::responseUnspecifiedError();
975 }
976
977 auto itr = serviceMap.begin();
978 const std::string service = itr->first;
979 return ipmi::response(setSessionState(busp, service, obj));
980 }
981 }
982 }
983 catch (const sdbusplus::exception_t& e)
984 {
985 lg2::error("Failed to fetch object from dbus, "
986 "interface: {INTERFACE}, error: {ERROR}",
987 "INTERFACE", session::sessionIntf, "ERROR", e);
988 return ipmi::responseUnspecifiedError();
989 }
990
991 return ipmi::responseInvalidFieldRequest();
992 }
993
getTotalSessionCount()994 uint8_t getTotalSessionCount()
995 {
996 uint8_t count = 0, ch = 0;
997
998 while (ch < ipmi::maxIpmiChannels &&
999 count < session::maxNetworkInstanceSupported)
1000 {
1001 ipmi::ChannelInfo chInfo{};
1002 ipmi::getChannelInfo(ch, chInfo);
1003 if (static_cast<ipmi::EChannelMediumType>(chInfo.mediumType) ==
1004 ipmi::EChannelMediumType::lan8032)
1005 {
1006 count++;
1007 }
1008 ch++;
1009 }
1010 return count * session::maxSessionCountPerChannel;
1011 }
1012
1013 /**
1014 * @brief get session info request data.
1015 *
1016 * This function validates the request data and retrive request session id,
1017 * session handle.
1018 *
1019 * @param[in] ctx - context of current session.
1020 * @param[in] sessionIndex - request session index
1021 * @param[in] payload - input payload
1022 * @param[in] reqSessionId - unpacked session Id will be asigned
1023 * @param[in] reqSessionHandle - unpacked session handle will be asigned
1024 *
1025 * @return success completion code if request data is valid
1026 * else return the correcponding error completion code.
1027 **/
getSessionInfoRequestData(const ipmi::Context::ptr ctx,const uint8_t sessionIndex,ipmi::message::Payload & payload,uint32_t & reqSessionId,uint8_t & reqSessionHandle)1028 uint8_t getSessionInfoRequestData(
1029 const ipmi::Context::ptr ctx, const uint8_t sessionIndex,
1030 ipmi::message::Payload& payload, uint32_t& reqSessionId,
1031 uint8_t& reqSessionHandle)
1032 {
1033 if ((sessionIndex > session::maxSessionCountPerChannel) &&
1034 (sessionIndex < session::searchSessionByHandle))
1035 {
1036 return ipmi::ccInvalidFieldRequest;
1037 }
1038
1039 switch (sessionIndex)
1040 {
1041 case session::searchCurrentSession:
1042
1043 ipmi::ChannelInfo chInfo;
1044 ipmi::getChannelInfo(ctx->channel, chInfo);
1045
1046 if (static_cast<ipmi::EChannelMediumType>(chInfo.mediumType) !=
1047 ipmi::EChannelMediumType::lan8032)
1048 {
1049 return ipmi::ccInvalidFieldRequest;
1050 }
1051
1052 if (!payload.fullyUnpacked())
1053 {
1054 return ipmi::ccReqDataLenInvalid;
1055 }
1056 // Check if current sessionId is 0, sessionId 0 is reserved.
1057 if (ctx->sessionId == session::sessionZero)
1058 {
1059 return session::ccInvalidSessionId;
1060 }
1061 reqSessionId = ctx->sessionId;
1062 break;
1063
1064 case session::searchSessionByHandle:
1065
1066 if ((payload.unpack(reqSessionHandle)) ||
1067 (!payload.fullyUnpacked()))
1068 {
1069 return ipmi::ccReqDataLenInvalid;
1070 }
1071
1072 if ((reqSessionHandle == session::sessionZero) ||
1073 ((reqSessionHandle & session::multiIntfaceSessionHandleMask) >
1074 session::maxSessionCountPerChannel))
1075 {
1076 return session::ccInvalidSessionHandle;
1077 }
1078 break;
1079
1080 case session::searchSessionById:
1081
1082 if ((payload.unpack(reqSessionId)) || (!payload.fullyUnpacked()))
1083 {
1084 return ipmi::ccReqDataLenInvalid;
1085 }
1086
1087 if (reqSessionId == session::sessionZero)
1088 {
1089 return session::ccInvalidSessionId;
1090 }
1091 break;
1092
1093 default:
1094 if (!payload.fullyUnpacked())
1095 {
1096 return ipmi::ccReqDataLenInvalid;
1097 }
1098 break;
1099 }
1100 return ipmi::ccSuccess;
1101 }
1102
getSessionState(ipmi::Context::ptr ctx,const std::string & service,const std::string & objPath,uint8_t & sessionState)1103 uint8_t getSessionState(ipmi::Context::ptr ctx, const std::string& service,
1104 const std::string& objPath, uint8_t& sessionState)
1105 {
1106 boost::system::error_code ec = ipmi::getDbusProperty(
1107 ctx, service, objPath, session::sessionIntf, "State", sessionState);
1108 if (ec)
1109 {
1110 lg2::error("Failed to fetch state property, service: {SERVICE}, "
1111 "object path: {OBJECTPATH}, interface: {INTERFACE}, "
1112 "error: {ERROR}",
1113 "SERVICE", service, "OBJECTPATH", objPath, "INTERFACE",
1114 session::sessionIntf, "ERROR", ec.message());
1115 return ipmi::ccUnspecifiedError;
1116 }
1117 return ipmi::ccSuccess;
1118 }
1119
1120 static constexpr uint8_t macAddrLen = 6;
1121 /** Alias SessionDetails - contain the optional information about an
1122 * RMCP+ session.
1123 *
1124 * @param userID - uint6_t session user ID (0-63)
1125 * @param reserved - uint2_t reserved
1126 * @param privilege - uint4_t session privilege (0-5)
1127 * @param reserved - uint4_t reserved
1128 * @param channel - uint4_t session channel number
1129 * @param protocol - uint4_t session protocol
1130 * @param remoteIP - uint32_t remote IP address
1131 * @param macAddr - std::array<uint8_t, 6> mac address
1132 * @param port - uint16_t remote port
1133 */
1134 using SessionDetails =
1135 std::tuple<uint2_t, uint6_t, uint4_t, uint4_t, uint4_t, uint4_t, uint32_t,
1136 std::array<uint8_t, macAddrLen>, uint16_t>;
1137
1138 /** @brief get session details for a given session
1139 *
1140 * @param[in] ctx - ipmi::Context pointer for accessing D-Bus
1141 * @param[in] service - D-Bus service name to fetch details from
1142 * @param[in] objPath - D-Bus object path for session
1143 * @param[out] sessionHandle - return session handle for session
1144 * @param[out] sessionState - return session state for session
1145 * @param[out] details - return a SessionDetails tuple containing other
1146 * session info
1147 * @return - ipmi::Cc success or error code
1148 */
getSessionDetails(ipmi::Context::ptr ctx,const std::string & service,const std::string & objPath,uint8_t & sessionHandle,uint8_t & sessionState,SessionDetails & details)1149 ipmi::Cc getSessionDetails(ipmi::Context::ptr ctx, const std::string& service,
1150 const std::string& objPath, uint8_t& sessionHandle,
1151 uint8_t& sessionState, SessionDetails& details)
1152 {
1153 ipmi::PropertyMap sessionProps;
1154 boost::system::error_code ec = ipmi::getAllDbusProperties(
1155 ctx, service, objPath, session::sessionIntf, sessionProps);
1156
1157 if (ec)
1158 {
1159 lg2::error("Failed to fetch state property, service: {SERVICE}, "
1160 "object path: {OBJECTPATH}, interface: {INTERFACE}, "
1161 "error: {ERROR}",
1162 "SERVICE", service, "OBJECTPATH", objPath, "INTERFACE",
1163 session::sessionIntf, "ERROR", ec.message());
1164 return ipmi::ccUnspecifiedError;
1165 }
1166
1167 sessionState = ipmi::mappedVariant<uint8_t>(
1168 sessionProps, "State", static_cast<uint8_t>(session::State::inactive));
1169 if (sessionState == static_cast<uint8_t>(session::State::active))
1170 {
1171 sessionHandle =
1172 ipmi::mappedVariant<uint8_t>(sessionProps, "SessionHandle", 0);
1173 std::get<0>(details) =
1174 ipmi::mappedVariant<uint8_t>(sessionProps, "UserID", 0xff);
1175 // std::get<1>(details) = 0; // (default constructed to 0)
1176 std::get<2>(details) =
1177 ipmi::mappedVariant<uint8_t>(sessionProps, "CurrentPrivilege", 0);
1178 // std::get<3>(details) = 0; // (default constructed to 0)
1179 std::get<4>(details) =
1180 ipmi::mappedVariant<uint8_t>(sessionProps, "ChannelNum", 0xff);
1181 constexpr uint4_t rmcpPlusProtocol = 1;
1182 std::get<5>(details) = rmcpPlusProtocol;
1183 std::get<6>(details) =
1184 ipmi::mappedVariant<uint32_t>(sessionProps, "RemoteIPAddr", 0);
1185 // std::get<7>(details) = {{0}}; // default constructed to all 0
1186 std::get<8>(details) =
1187 ipmi::mappedVariant<uint16_t>(sessionProps, "RemotePort", 0);
1188 }
1189
1190 return ipmi::ccSuccess;
1191 }
1192
1193 ipmi::RspType<uint8_t, // session handle,
1194 uint8_t, // total session count
1195 uint8_t, // active session count
1196 std::optional<SessionDetails>>
ipmiAppGetSessionInfo(ipmi::Context::ptr ctx,uint8_t sessionIndex,ipmi::message::Payload & payload)1197 ipmiAppGetSessionInfo(ipmi::Context::ptr ctx, uint8_t sessionIndex,
1198 ipmi::message::Payload& payload)
1199 {
1200 uint32_t reqSessionId = 0;
1201 uint8_t reqSessionHandle = session::defaultSessionHandle;
1202 // initializing state to 0xff as 0 represents state as inactive.
1203 uint8_t state = 0xFF;
1204
1205 uint8_t completionCode = getSessionInfoRequestData(
1206 ctx, sessionIndex, payload, reqSessionId, reqSessionHandle);
1207
1208 if (completionCode)
1209 {
1210 return ipmi::response(completionCode);
1211 }
1212 ipmi::ObjectTree objectTree;
1213 boost::system::error_code ec = ipmi::getAllDbusObjects(
1214 ctx, session::sessionManagerRootPath, session::sessionIntf, objectTree);
1215 if (ec)
1216 {
1217 lg2::error("Failed to fetch object from dbus, "
1218 "interface: {INTERFACE}, error: {ERROR}",
1219 "INTERFACE", session::sessionIntf, "ERROR", ec.message());
1220 return ipmi::responseUnspecifiedError();
1221 }
1222
1223 uint8_t totalSessionCount = getTotalSessionCount();
1224 uint8_t activeSessionCount = 0;
1225 uint8_t sessionHandle = session::defaultSessionHandle;
1226 uint8_t activeSessionHandle = 0;
1227 std::optional<SessionDetails> maybeDetails;
1228 uint8_t index = 0;
1229 for (auto& objectTreeItr : objectTree)
1230 {
1231 uint32_t sessionId = 0;
1232 std::string objectPath = objectTreeItr.first;
1233
1234 if (!parseCloseSessionInputPayload(objectPath, sessionId,
1235 sessionHandle))
1236 {
1237 continue;
1238 }
1239 index++;
1240 auto& serviceMap = objectTreeItr.second;
1241 auto itr = serviceMap.begin();
1242
1243 if (serviceMap.size() != 1)
1244 {
1245 return ipmi::responseUnspecifiedError();
1246 }
1247
1248 std::string service = itr->first;
1249 uint8_t sessionState = 0;
1250 completionCode =
1251 getSessionState(ctx, service, objectPath, sessionState);
1252 if (completionCode)
1253 {
1254 return ipmi::response(completionCode);
1255 }
1256
1257 if (sessionState == static_cast<uint8_t>(session::State::active))
1258 {
1259 activeSessionCount++;
1260 }
1261
1262 if (index == sessionIndex || reqSessionId == sessionId ||
1263 reqSessionHandle == sessionHandle)
1264 {
1265 SessionDetails details{};
1266 completionCode = getSessionDetails(ctx, service, objectPath,
1267 sessionHandle, state, details);
1268
1269 if (completionCode)
1270 {
1271 return ipmi::response(completionCode);
1272 }
1273 activeSessionHandle = sessionHandle;
1274 maybeDetails = std::move(details);
1275 }
1276 }
1277
1278 if (state == static_cast<uint8_t>(session::State::active) ||
1279 state == static_cast<uint8_t>(session::State::tearDownInProgress))
1280 {
1281 return ipmi::responseSuccess(activeSessionHandle, totalSessionCount,
1282 activeSessionCount, maybeDetails);
1283 }
1284
1285 return ipmi::responseInvalidFieldRequest();
1286 }
1287
getSysFWVersion(ipmi::Context::ptr & ctx)1288 std::optional<std::string> getSysFWVersion(ipmi::Context::ptr& ctx)
1289 {
1290 /*
1291 * The System Firmware version is detected via following steps:
1292 * - Get all of object paths that include
1293 * "xyz.openbmc_project.Software.Version" interface.
1294 * - Get the Purpose property of above object paths.
1295 * - If the Purpose is Host then get the Version property.
1296 */
1297 ipmi::ObjectTree objectTree;
1298 boost::system::error_code ec =
1299 ipmi::getAllDbusObjects(ctx, softwareRoot, versionIntf, objectTree);
1300 if (ec.value())
1301 {
1302 return std::nullopt;
1303 }
1304
1305 for (const auto& [objPath, serviceMap] : objectTree)
1306 {
1307 for (const auto& [service, intfs] : serviceMap)
1308 {
1309 ipmi::PropertyMap props;
1310 ec = ipmi::getAllDbusProperties(ctx, service, objPath, versionIntf,
1311 props);
1312 if (ec.value())
1313 {
1314 continue;
1315 }
1316
1317 std::string purposeProp = std::string(
1318 ipmi::mappedVariant<std::string>(props, "Purpose", ""));
1319
1320 if (!purposeProp.ends_with(versionPurposeHostEnd))
1321 {
1322 continue;
1323 }
1324
1325 std::string sysFWVersion = std::string(
1326 ipmi::mappedVariant<std::string>(props, "Version", ""));
1327
1328 if (sysFWVersion.empty())
1329 {
1330 return std::nullopt;
1331 }
1332
1333 return sysFWVersion;
1334 }
1335 }
1336
1337 return std::nullopt;
1338 }
1339
1340 static std::unique_ptr<SysInfoParamStore> sysInfoParamStore;
1341
sysInfoReadSystemName()1342 static std::string sysInfoReadSystemName()
1343 {
1344 // Use the BMC hostname as the "System Name."
1345 char hostname[HOST_NAME_MAX + 1] = {};
1346 if (gethostname(hostname, HOST_NAME_MAX) != 0)
1347 {
1348 perror("System info parameter: system name");
1349 }
1350 return hostname;
1351 }
1352
1353 static constexpr uint8_t paramRevision = 0x11;
1354 static constexpr size_t configParameterLength = 16;
1355
1356 static constexpr size_t smallChunkSize = 14;
1357 static constexpr size_t fullChunkSize = 16;
1358 static constexpr uint8_t progressMask = 0x3;
1359 static constexpr uint8_t maxValidEncodingData = 0x02;
1360
1361 static constexpr uint8_t setComplete = 0x0;
1362 static constexpr uint8_t setInProgress = 0x1;
1363 static uint8_t transferStatus = setComplete;
1364
1365 static constexpr uint8_t configDataOverhead = 2;
1366
1367 namespace ipmi
1368 {
1369 constexpr Cc ccParmNotSupported = 0x80;
1370 constexpr Cc ccSetInProgressActive = 0x81;
1371
responseParmNotSupported()1372 static inline auto responseParmNotSupported()
1373 {
1374 return response(ccParmNotSupported);
1375 }
responseSetInProgressActive()1376 static inline auto responseSetInProgressActive()
1377 {
1378 return response(ccSetInProgressActive);
1379 }
1380 } // namespace ipmi
1381
1382 ipmi::RspType<uint8_t, // Parameter revision
1383 std::optional<uint8_t>, // data1 / setSelector / ProgressStatus
1384 std::optional<std::vector<uint8_t>>> // data2-17
ipmiAppGetSystemInfo(ipmi::Context::ptr ctx,uint7_t reserved,bool getRevision,uint8_t paramSelector,uint8_t setSelector,uint8_t BlockSelector)1385 ipmiAppGetSystemInfo(ipmi::Context::ptr ctx, uint7_t reserved,
1386 bool getRevision, uint8_t paramSelector,
1387 uint8_t setSelector, uint8_t BlockSelector)
1388 {
1389 if (reserved || (paramSelector >= invalidParamSelectorStart &&
1390 paramSelector <= invalidParamSelectorEnd))
1391 {
1392 return ipmi::responseInvalidFieldRequest();
1393 }
1394 if (paramSelector >= oemCmdStart)
1395 {
1396 return ipmi::responseParmNotSupported();
1397 }
1398 if (getRevision)
1399 {
1400 return ipmi::responseSuccess(paramRevision, std::nullopt, std::nullopt);
1401 }
1402
1403 if (paramSelector == 0)
1404 {
1405 return ipmi::responseSuccess(paramRevision, transferStatus,
1406 std::nullopt);
1407 }
1408
1409 if (BlockSelector != 0) // 00h if parameter does not require a block number
1410 {
1411 return ipmi::responseParmNotSupported();
1412 }
1413
1414 if (sysInfoParamStore == nullptr)
1415 {
1416 sysInfoParamStore = std::make_unique<SysInfoParamStore>();
1417 sysInfoParamStore->update(IPMI_SYSINFO_SYSTEM_NAME,
1418 sysInfoReadSystemName);
1419 }
1420
1421 if (paramSelector == IPMI_SYSINFO_SYSTEM_FW_VERSION)
1422 {
1423 // If the system firmware version is not cached, get it from the D-Bus.
1424 const auto& [found, paramString] =
1425 sysInfoParamStore->lookup(IPMI_SYSINFO_SYSTEM_FW_VERSION);
1426
1427 if (!found || paramString.empty())
1428 {
1429 auto fwVersion = getSysFWVersion(ctx);
1430 if (fwVersion == std::nullopt)
1431 {
1432 return ipmi::responseUnspecifiedError();
1433 }
1434 sysInfoParamStore->update(IPMI_SYSINFO_SYSTEM_FW_VERSION,
1435 *fwVersion);
1436 }
1437 }
1438
1439 // Parameters other than Set In Progress are assumed to be strings.
1440 std::tuple<bool, std::string> ret =
1441 sysInfoParamStore->lookup(paramSelector);
1442 bool found = std::get<0>(ret);
1443 if (!found)
1444 {
1445 return ipmi::responseSensorInvalid();
1446 }
1447 std::string& paramString = std::get<1>(ret);
1448 std::vector<uint8_t> configData;
1449 size_t count = 0;
1450 if (setSelector == 0)
1451 { // First chunk has only 14 bytes.
1452 configData.emplace_back(0); // encoding
1453 configData.emplace_back(paramString.length()); // string length
1454 count = std::min(paramString.length(), smallChunkSize);
1455 configData.resize(count + configDataOverhead);
1456 std::copy_n(paramString.begin(), count,
1457 configData.begin() + configDataOverhead); // 14 bytes chunk
1458
1459 // Append zero's to remaining bytes
1460 if (configData.size() < configParameterLength)
1461 {
1462 std::fill_n(std::back_inserter(configData),
1463 configParameterLength - configData.size(), 0x00);
1464 }
1465 }
1466 else
1467 {
1468 size_t offset = (setSelector * fullChunkSize) - configDataOverhead;
1469 if (offset >= paramString.length())
1470 {
1471 return ipmi::responseParmOutOfRange();
1472 }
1473 count = std::min(paramString.length() - offset, fullChunkSize);
1474 configData.resize(count);
1475 std::copy_n(paramString.begin() + offset, count,
1476 configData.begin()); // 16 bytes chunk
1477 }
1478 return ipmi::responseSuccess(paramRevision, setSelector, configData);
1479 }
1480
ipmiAppSetSystemInfo(uint8_t paramSelector,uint8_t data1,std::vector<uint8_t> configData)1481 ipmi::RspType<> ipmiAppSetSystemInfo(uint8_t paramSelector, uint8_t data1,
1482 std::vector<uint8_t> configData)
1483 {
1484 if (paramSelector >= invalidParamSelectorStart &&
1485 paramSelector <= invalidParamSelectorEnd)
1486 {
1487 return ipmi::responseInvalidFieldRequest();
1488 }
1489 if (paramSelector >= oemCmdStart)
1490 {
1491 return ipmi::responseParmNotSupported();
1492 }
1493
1494 if (paramSelector == 0)
1495 {
1496 // attempt to set the 'set in progress' value (in parameter #0)
1497 // when not in the set complete state.
1498 if ((transferStatus != setComplete) && (data1 == setInProgress))
1499 {
1500 return ipmi::responseSetInProgressActive();
1501 }
1502 // only following 2 states are supported
1503 if (data1 > setInProgress)
1504 {
1505 lg2::error("illegal SetInProgress status");
1506 return ipmi::responseInvalidFieldRequest();
1507 }
1508
1509 transferStatus = data1 & progressMask;
1510 return ipmi::responseSuccess();
1511 }
1512
1513 if (configData.size() > configParameterLength)
1514 {
1515 return ipmi::responseInvalidFieldRequest();
1516 }
1517
1518 // Append zero's to remaining bytes
1519 if (configData.size() < configParameterLength)
1520 {
1521 fill_n(back_inserter(configData),
1522 (configParameterLength - configData.size()), 0x00);
1523 }
1524
1525 if (!sysInfoParamStore)
1526 {
1527 sysInfoParamStore = std::make_unique<SysInfoParamStore>();
1528 sysInfoParamStore->update(IPMI_SYSINFO_SYSTEM_NAME,
1529 sysInfoReadSystemName);
1530 }
1531
1532 // lookup
1533 std::tuple<bool, std::string> ret =
1534 sysInfoParamStore->lookup(paramSelector);
1535 bool found = std::get<0>(ret);
1536 std::string& paramString = std::get<1>(ret);
1537 if (!found)
1538 {
1539 // parameter does not exist. Init new
1540 paramString = "";
1541 }
1542
1543 uint8_t setSelector = data1;
1544 size_t count = 0;
1545 if (setSelector == 0) // First chunk has only 14 bytes.
1546 {
1547 uint8_t encoding = configData.at(0);
1548 if (encoding > maxValidEncodingData)
1549 {
1550 return ipmi::responseInvalidFieldRequest();
1551 }
1552
1553 size_t stringLen = configData.at(1); // string length
1554 count = std::min(stringLen, smallChunkSize);
1555 count = std::min(count, configData.size());
1556 paramString.resize(stringLen); // reserve space
1557 std::copy_n(configData.begin() + configDataOverhead, count,
1558 paramString.begin());
1559 }
1560 else
1561 {
1562 size_t offset = (setSelector * fullChunkSize) - configDataOverhead;
1563 if (offset >= paramString.length())
1564 {
1565 return ipmi::responseParmOutOfRange();
1566 }
1567 count = std::min(paramString.length() - offset, configData.size());
1568 std::copy_n(configData.begin(), count, paramString.begin() + offset);
1569 }
1570 sysInfoParamStore->update(paramSelector, paramString);
1571 return ipmi::responseSuccess();
1572 }
1573
1574 #ifdef ENABLE_I2C_WHITELIST_CHECK
convertStringToData(const std::string & command)1575 inline std::vector<uint8_t> convertStringToData(const std::string& command)
1576 {
1577 std::istringstream iss(command);
1578 std::string token;
1579 std::vector<uint8_t> dataValue;
1580 while (std::getline(iss, token, ' '))
1581 {
1582 dataValue.emplace_back(
1583 static_cast<uint8_t>(std::stoul(token, nullptr, base_16)));
1584 }
1585 return dataValue;
1586 }
1587
populateI2CControllerWRAllowlist()1588 static bool populateI2CControllerWRAllowlist()
1589 {
1590 nlohmann::json data = nullptr;
1591 std::ifstream jsonFile(i2cControllerWRAllowlistFile);
1592
1593 if (!jsonFile.good())
1594 {
1595 lg2::warning("i2c allow list file not found! file name: {FILE_NAME}",
1596 "FILE_NAME", i2cControllerWRAllowlistFile);
1597 return false;
1598 }
1599
1600 try
1601 {
1602 data = nlohmann::json::parse(jsonFile, nullptr, false);
1603 }
1604 catch (const nlohmann::json::parse_error& e)
1605 {
1606 lg2::error("Corrupted i2c allow list config file, "
1607 "file name: {FILE_NAME}, error: {ERROR}",
1608 "FILE_NAME", i2cControllerWRAllowlistFile, "ERROR", e);
1609 return false;
1610 }
1611
1612 try
1613 {
1614 // Example JSON Structure format
1615 // "filters": [
1616 // {
1617 // "Description": "Allow full read - ignore first byte write value
1618 // for 0x40 to 0x4F",
1619 // "busId": "0x01",
1620 // "slaveAddr": "0x40",
1621 // "slaveAddrMask": "0x0F",
1622 // "command": "0x00",
1623 // "commandMask": "0xFF"
1624 // },
1625 // {
1626 // "Description": "Allow full read - first byte match 0x05 and
1627 // ignore second byte",
1628 // "busId": "0x01",
1629 // "slaveAddr": "0x57",
1630 // "slaveAddrMask": "0x00",
1631 // "command": "0x05 0x00",
1632 // "commandMask": "0x00 0xFF"
1633 // },]
1634
1635 nlohmann::json filters = data[filtersStr].get<nlohmann::json>();
1636 std::vector<i2cControllerWRAllowlist>& allowlist = getWRAllowlist();
1637 for (const auto& it : filters.items())
1638 {
1639 nlohmann::json filter = it.value();
1640 if (filter.is_null())
1641 {
1642 lg2::error(
1643 "Corrupted I2C controller write read allowlist config file, "
1644 "file name: {FILE_NAME}",
1645 "FILE_NAME", i2cControllerWRAllowlistFile);
1646 return false;
1647 }
1648 const std::vector<uint8_t>& writeData =
1649 convertStringToData(filter[cmdStr].get<std::string>());
1650 const std::vector<uint8_t>& writeDataMask =
1651 convertStringToData(filter[cmdMaskStr].get<std::string>());
1652 if (writeDataMask.size() != writeData.size())
1653 {
1654 lg2::error("I2C controller write read allowlist filter "
1655 "mismatch for command & mask size");
1656 return false;
1657 }
1658 allowlist.push_back(
1659 {static_cast<uint8_t>(std::stoul(
1660 filter[busIdStr].get<std::string>(), nullptr, base_16)),
1661 static_cast<uint8_t>(
1662 std::stoul(filter[targetAddrStr].get<std::string>(),
1663 nullptr, base_16)),
1664 static_cast<uint8_t>(
1665 std::stoul(filter[targetAddrMaskStr].get<std::string>(),
1666 nullptr, base_16)),
1667 writeData, writeDataMask});
1668 }
1669 if (allowlist.size() != filters.size())
1670 {
1671 lg2::error(
1672 "I2C controller write read allowlist filter size mismatch");
1673 return false;
1674 }
1675 }
1676 catch (const std::exception& e)
1677 {
1678 lg2::error("I2C controller write read allowlist "
1679 "unexpected exception: {ERROR}",
1680 "ERROR", e);
1681 return false;
1682 }
1683 return true;
1684 }
1685
isWriteDataAllowlisted(const std::vector<uint8_t> & data,const std::vector<uint8_t> & dataMask,const std::vector<uint8_t> & writeData)1686 static inline bool isWriteDataAllowlisted(const std::vector<uint8_t>& data,
1687 const std::vector<uint8_t>& dataMask,
1688 const std::vector<uint8_t>& writeData)
1689 {
1690 std::vector<uint8_t> processedDataBuf(data.size());
1691 std::vector<uint8_t> processedReqBuf(dataMask.size());
1692 std::transform(writeData.begin(), writeData.end(), dataMask.begin(),
1693 processedReqBuf.begin(), std::bit_or<uint8_t>());
1694 std::transform(data.begin(), data.end(), dataMask.begin(),
1695 processedDataBuf.begin(), std::bit_or<uint8_t>());
1696
1697 return (processedDataBuf == processedReqBuf);
1698 }
1699
isCmdAllowlisted(uint8_t busId,uint8_t targetAddr,std::vector<uint8_t> & writeData)1700 static bool isCmdAllowlisted(uint8_t busId, uint8_t targetAddr,
1701 std::vector<uint8_t>& writeData)
1702 {
1703 std::vector<i2cControllerWRAllowlist>& allowList = getWRAllowlist();
1704 for (const auto& wlEntry : allowList)
1705 {
1706 if ((busId == wlEntry.busId) &&
1707 ((targetAddr | wlEntry.targetAddrMask) ==
1708 (wlEntry.targetAddr | wlEntry.targetAddrMask)))
1709 {
1710 const std::vector<uint8_t>& dataMask = wlEntry.dataMask;
1711 // Skip as no-match, if requested write data is more than the
1712 // write data mask size
1713 if (writeData.size() > dataMask.size())
1714 {
1715 continue;
1716 }
1717 if (isWriteDataAllowlisted(wlEntry.data, dataMask, writeData))
1718 {
1719 return true;
1720 }
1721 }
1722 }
1723 return false;
1724 }
1725 #else
populateI2CControllerWRAllowlist()1726 static bool populateI2CControllerWRAllowlist()
1727 {
1728 lg2::info("I2C_WHITELIST_CHECK is disabled, do not populate allowlist");
1729 return true;
1730 }
1731 #endif // ENABLE_I2C_WHITELIST_CHECK
1732
1733 /** @brief implements controller write read IPMI command which can be used for
1734 * low-level I2C/SMBus write, read or write-read access
1735 * @param isPrivateBus -to indicate private bus usage
1736 * @param busId - bus id
1737 * @param channelNum - channel number
1738 * @param reserved - skip 1 bit
1739 * @param targetAddr - target address
1740 * @param read count - number of bytes to be read
1741 * @param writeData - data to be written
1742 *
1743 * @returns IPMI completion code plus response data
1744 * - readData - i2c response data
1745 */
ipmiControllerWriteRead(bool isPrivateBus,uint3_t busId,uint4_t channelNum,bool reserved,uint7_t targetAddr,uint8_t readCount,std::vector<uint8_t> writeData)1746 ipmi::RspType<std::vector<uint8_t>> ipmiControllerWriteRead(
1747 [[maybe_unused]] bool isPrivateBus, uint3_t busId,
1748 [[maybe_unused]] uint4_t channelNum, bool reserved, uint7_t targetAddr,
1749 uint8_t readCount, std::vector<uint8_t> writeData)
1750 {
1751 if (reserved)
1752 {
1753 return ipmi::responseInvalidFieldRequest();
1754 }
1755 const size_t writeCount = writeData.size();
1756 if (!readCount && !writeCount)
1757 {
1758 lg2::error("Controller write read command: Read & write count are 0");
1759 return ipmi::responseInvalidFieldRequest();
1760 }
1761 #ifdef ENABLE_I2C_WHITELIST_CHECK
1762 if (!isCmdAllowlisted(static_cast<uint8_t>(busId),
1763 static_cast<uint8_t>(targetAddr), writeData))
1764 {
1765 lg2::error("Controller write read request blocked!, "
1766 "bus: {BUS}, addr: {ADDR}",
1767 "BUS", static_cast<uint8_t>(busId), "ADDR", lg2::hex,
1768 static_cast<uint8_t>(targetAddr));
1769 }
1770 #endif // ENABLE_I2C_WHITELIST_CHECK
1771 std::vector<uint8_t> readBuf(readCount);
1772 std::string i2cBus =
1773 "/dev/i2c-" + std::to_string(static_cast<uint8_t>(busId));
1774
1775 ipmi::Cc ret = ipmi::i2cWriteRead(i2cBus, static_cast<uint8_t>(targetAddr),
1776 writeData, readBuf);
1777 if (ret != ipmi::ccSuccess)
1778 {
1779 return ipmi::response(ret);
1780 }
1781 return ipmi::responseSuccess(readBuf);
1782 }
1783
registerNetFnAppFunctions()1784 void registerNetFnAppFunctions()
1785 {
1786 // <Get Device ID>
1787 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp,
1788 ipmi::app::cmdGetDeviceId, ipmi::Privilege::User,
1789 ipmiAppGetDeviceId);
1790
1791 // <Get BT Interface Capabilities>
1792 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp,
1793 ipmi::app::cmdGetBtIfaceCapabilities,
1794 ipmi::Privilege::User, ipmiAppGetBtCapabilities);
1795
1796 // <Reset Watchdog Timer>
1797 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp,
1798 ipmi::app::cmdResetWatchdogTimer,
1799 ipmi::Privilege::Operator, ipmiAppResetWatchdogTimer);
1800
1801 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp,
1802 ipmi::app::cmdGetSessionInfo, ipmi::Privilege::User,
1803 ipmiAppGetSessionInfo);
1804
1805 // <Set Watchdog Timer>
1806 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp,
1807 ipmi::app::cmdSetWatchdogTimer,
1808 ipmi::Privilege::Operator, ipmiSetWatchdogTimer);
1809
1810 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp,
1811 ipmi::app::cmdCloseSession, ipmi::Privilege::Callback,
1812 ipmiAppCloseSession);
1813
1814 // <Get Watchdog Timer>
1815 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp,
1816 ipmi::app::cmdGetWatchdogTimer, ipmi::Privilege::User,
1817 ipmiGetWatchdogTimer);
1818
1819 // <Get Self Test Results>
1820 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp,
1821 ipmi::app::cmdGetSelfTestResults,
1822 ipmi::Privilege::User, ipmiAppGetSelfTestResults);
1823
1824 // <Get Device GUID>
1825 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp,
1826 ipmi::app::cmdGetDeviceGuid, ipmi::Privilege::User,
1827 ipmiAppGetDeviceGuid);
1828
1829 // <Set ACPI Power State>
1830 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp,
1831 ipmi::app::cmdSetAcpiPowerState,
1832 ipmi::Privilege::Admin, ipmiSetAcpiPowerState);
1833 // <Get ACPI Power State>
1834 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp,
1835 ipmi::app::cmdGetAcpiPowerState,
1836 ipmi::Privilege::User, ipmiGetAcpiPowerState);
1837
1838 // Note: For security reason, this command will be registered only when
1839 // there are proper I2C Controller write read allowlist
1840 if (populateI2CControllerWRAllowlist())
1841 {
1842 // Note: For security reasons, registering controller write read as
1843 // admin privilege command, even though IPMI 2.0 specification allows it
1844 // as operator privilege.
1845 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp,
1846 ipmi::app::cmdMasterWriteRead,
1847 ipmi::Privilege::Admin, ipmiControllerWriteRead);
1848 }
1849
1850 // <Get System GUID Command>
1851 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp,
1852 ipmi::app::cmdGetSystemGuid, ipmi::Privilege::User,
1853 ipmiAppGetSystemGuid);
1854
1855 // <Get Channel Cipher Suites Command>
1856 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp,
1857 ipmi::app::cmdGetChannelCipherSuites,
1858 ipmi::Privilege::None, getChannelCipherSuites);
1859
1860 // <Get System Info Command>
1861 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp,
1862 ipmi::app::cmdGetSystemInfoParameters,
1863 ipmi::Privilege::User, ipmiAppGetSystemInfo);
1864 // <Set System Info Command>
1865 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp,
1866 ipmi::app::cmdSetSystemInfoParameters,
1867 ipmi::Privilege::Admin, ipmiAppSetSystemInfo);
1868 return;
1869 }
1870