xref: /openbmc/phosphor-host-ipmid/apphandler.cpp (revision b1f1f1163b2313c04ea5e96e7897d801ad3ff8f8)
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