1 #include <arpa/inet.h>
2 #include <fcntl.h>
3 #include <limits.h>
4 #include <linux/i2c-dev.h>
5 #include <linux/i2c.h>
6 #include <mapper.h>
7 #include <sys/ioctl.h>
8 #include <sys/stat.h>
9 #include <sys/types.h>
10 #include <systemd/sd-bus.h>
11 #include <unistd.h>
12 
13 #include <algorithm>
14 #include <app/channel.hpp>
15 #include <app/watchdog.hpp>
16 #include <apphandler.hpp>
17 #include <array>
18 #include <cstddef>
19 #include <cstdint>
20 #include <filesystem>
21 #include <fstream>
22 #include <ipmid/api.hpp>
23 #include <ipmid/types.hpp>
24 #include <ipmid/utils.hpp>
25 #include <memory>
26 #include <nlohmann/json.hpp>
27 #include <phosphor-logging/elog-errors.hpp>
28 #include <phosphor-logging/log.hpp>
29 #include <sdbusplus/message/types.hpp>
30 #include <string>
31 #include <sys_info_param.hpp>
32 #include <transporthandler.hpp>
33 #include <tuple>
34 #include <vector>
35 #include <xyz/openbmc_project/Common/error.hpp>
36 #include <xyz/openbmc_project/Control/Power/ACPIPowerState/server.hpp>
37 #include <xyz/openbmc_project/Software/Activation/server.hpp>
38 #include <xyz/openbmc_project/Software/Version/server.hpp>
39 #include <xyz/openbmc_project/State/BMC/server.hpp>
40 
41 extern sd_bus* bus;
42 
43 constexpr auto bmc_state_interface = "xyz.openbmc_project.State.BMC";
44 constexpr auto bmc_state_property = "CurrentBMCState";
45 constexpr auto bmc_interface = "xyz.openbmc_project.Inventory.Item.Bmc";
46 constexpr auto bmc_guid_interface = "xyz.openbmc_project.Common.UUID";
47 constexpr auto bmc_guid_property = "UUID";
48 constexpr auto bmc_guid_len = 16;
49 
50 static constexpr auto redundancyIntf =
51     "xyz.openbmc_project.Software.RedundancyPriority";
52 static constexpr auto versionIntf = "xyz.openbmc_project.Software.Version";
53 static constexpr auto activationIntf =
54     "xyz.openbmc_project.Software.Activation";
55 static constexpr auto softwareRoot = "/xyz/openbmc_project/software";
56 
57 void register_netfn_app_functions() __attribute__((constructor));
58 
59 using namespace phosphor::logging;
60 using namespace sdbusplus::xyz::openbmc_project::Common::Error;
61 using Version = sdbusplus::xyz::openbmc_project::Software::server::Version;
62 using Activation =
63     sdbusplus::xyz::openbmc_project::Software::server::Activation;
64 using BMC = sdbusplus::xyz::openbmc_project::State::server::BMC;
65 namespace fs = std::filesystem;
66 
67 typedef struct
68 {
69     uint8_t busId;
70     uint8_t slaveAddr;
71     uint8_t slaveAddrMask;
72     std::vector<uint8_t> data;
73     std::vector<uint8_t> dataMask;
74 } i2cMasterWRWhitelist;
75 
76 static std::vector<i2cMasterWRWhitelist>& getWRWhitelist()
77 {
78     static std::vector<i2cMasterWRWhitelist> wrWhitelist;
79     return wrWhitelist;
80 }
81 
82 static constexpr const char* i2cMasterWRWhitelistFile =
83     "/usr/share/ipmi-providers/master_write_read_white_list.json";
84 
85 static constexpr uint8_t maxIPMIWriteReadSize = 144;
86 static constexpr const char* filtersStr = "filters";
87 static constexpr const char* busIdStr = "busId";
88 static constexpr const char* slaveAddrStr = "slaveAddr";
89 static constexpr const char* slaveAddrMaskStr = "slaveAddrMask";
90 static constexpr const char* cmdStr = "command";
91 static constexpr const char* cmdMaskStr = "commandMask";
92 static constexpr int base_16 = 16;
93 
94 /**
95  * @brief Returns the Version info from primary s/w object
96  *
97  * Get the Version info from the active s/w object which is having high
98  * "Priority" value(a smaller number is a higher priority) and "Purpose"
99  * is "BMC" from the list of all s/w objects those are implementing
100  * RedundancyPriority interface from the given softwareRoot path.
101  *
102  * @return On success returns the Version info from primary s/w object.
103  *
104  */
105 std::string getActiveSoftwareVersionInfo()
106 {
107     auto busp = getSdBus();
108 
109     std::string revision{};
110     ipmi::ObjectTree objectTree;
111     try
112     {
113         objectTree =
114             ipmi::getAllDbusObjects(*busp, softwareRoot, redundancyIntf);
115     }
116     catch (sdbusplus::exception::SdBusError& e)
117     {
118         log<level::ERR>("Failed to fetch redundancy object from dbus",
119                         entry("INTERFACE=%s", redundancyIntf),
120                         entry("ERRMSG=%s", e.what()));
121         elog<InternalFailure>();
122     }
123 
124     auto objectFound = false;
125     for (auto& softObject : objectTree)
126     {
127         auto service =
128             ipmi::getService(*busp, redundancyIntf, softObject.first);
129         auto objValueTree =
130             ipmi::getManagedObjects(*busp, service, softwareRoot);
131 
132         auto minPriority = 0xFF;
133         for (const auto& objIter : objValueTree)
134         {
135             try
136             {
137                 auto& intfMap = objIter.second;
138                 auto& redundancyPriorityProps = intfMap.at(redundancyIntf);
139                 auto& versionProps = intfMap.at(versionIntf);
140                 auto& activationProps = intfMap.at(activationIntf);
141                 auto priority =
142                     std::get<uint8_t>(redundancyPriorityProps.at("Priority"));
143                 auto purpose =
144                     std::get<std::string>(versionProps.at("Purpose"));
145                 auto activation =
146                     std::get<std::string>(activationProps.at("Activation"));
147                 auto version =
148                     std::get<std::string>(versionProps.at("Version"));
149                 if ((Version::convertVersionPurposeFromString(purpose) ==
150                      Version::VersionPurpose::BMC) &&
151                     (Activation::convertActivationsFromString(activation) ==
152                      Activation::Activations::Active))
153                 {
154                     if (priority < minPriority)
155                     {
156                         minPriority = priority;
157                         objectFound = true;
158                         revision = std::move(version);
159                     }
160                 }
161             }
162             catch (const std::exception& e)
163             {
164                 log<level::ERR>(e.what());
165             }
166         }
167     }
168 
169     if (!objectFound)
170     {
171         log<level::ERR>("Could not found an BMC software Object");
172         elog<InternalFailure>();
173     }
174 
175     return revision;
176 }
177 
178 bool getCurrentBmcState()
179 {
180     sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()};
181 
182     // Get the Inventory object implementing the BMC interface
183     ipmi::DbusObjectInfo bmcObject =
184         ipmi::getDbusObject(bus, bmc_state_interface);
185     auto variant =
186         ipmi::getDbusProperty(bus, bmcObject.second, bmcObject.first,
187                               bmc_state_interface, bmc_state_property);
188 
189     return std::holds_alternative<std::string>(variant) &&
190            BMC::convertBMCStateFromString(std::get<std::string>(variant)) ==
191                BMC::BMCState::Ready;
192 }
193 
194 bool getCurrentBmcStateWithFallback(const bool fallbackAvailability)
195 {
196     try
197     {
198         return getCurrentBmcState();
199     }
200     catch (...)
201     {
202         // Nothing provided the BMC interface, therefore return whatever was
203         // configured as the default.
204         return fallbackAvailability;
205     }
206 }
207 
208 namespace acpi_state
209 {
210 using namespace sdbusplus::xyz::openbmc_project::Control::Power::server;
211 
212 const static constexpr char* acpiObjPath =
213     "/xyz/openbmc_project/control/host0/acpi_power_state";
214 const static constexpr char* acpiInterface =
215     "xyz.openbmc_project.Control.Power.ACPIPowerState";
216 const static constexpr char* sysACPIProp = "SysACPIStatus";
217 const static constexpr char* devACPIProp = "DevACPIStatus";
218 
219 enum class PowerStateType : uint8_t
220 {
221     sysPowerState = 0x00,
222     devPowerState = 0x01,
223 };
224 
225 // Defined in 20.6 of ipmi doc
226 enum class PowerState : uint8_t
227 {
228     s0G0D0 = 0x00,
229     s1D1 = 0x01,
230     s2D2 = 0x02,
231     s3D3 = 0x03,
232     s4 = 0x04,
233     s5G2 = 0x05,
234     s4S5 = 0x06,
235     g3 = 0x07,
236     sleep = 0x08,
237     g1Sleep = 0x09,
238     override = 0x0a,
239     legacyOn = 0x20,
240     legacyOff = 0x21,
241     unknown = 0x2a,
242     noChange = 0x7f,
243 };
244 
245 static constexpr uint8_t stateChanged = 0x80;
246 
247 struct ACPIState
248 {
249     uint8_t sysACPIState;
250     uint8_t devACPIState;
251 } __attribute__((packed));
252 
253 std::map<ACPIPowerState::ACPI, PowerState> dbusToIPMI = {
254     {ACPIPowerState::ACPI::S0_G0_D0, PowerState::s0G0D0},
255     {ACPIPowerState::ACPI::S1_D1, PowerState::s1D1},
256     {ACPIPowerState::ACPI::S2_D2, PowerState::s2D2},
257     {ACPIPowerState::ACPI::S3_D3, PowerState::s3D3},
258     {ACPIPowerState::ACPI::S4, PowerState::s4},
259     {ACPIPowerState::ACPI::S5_G2, PowerState::s5G2},
260     {ACPIPowerState::ACPI::S4_S5, PowerState::s4S5},
261     {ACPIPowerState::ACPI::G3, PowerState::g3},
262     {ACPIPowerState::ACPI::SLEEP, PowerState::sleep},
263     {ACPIPowerState::ACPI::G1_SLEEP, PowerState::g1Sleep},
264     {ACPIPowerState::ACPI::OVERRIDE, PowerState::override},
265     {ACPIPowerState::ACPI::LEGACY_ON, PowerState::legacyOn},
266     {ACPIPowerState::ACPI::LEGACY_OFF, PowerState::legacyOff},
267     {ACPIPowerState::ACPI::Unknown, PowerState::unknown}};
268 
269 bool isValidACPIState(acpi_state::PowerStateType type, uint8_t state)
270 {
271     if (type == acpi_state::PowerStateType::sysPowerState)
272     {
273         if ((state <= static_cast<uint8_t>(acpi_state::PowerState::override)) ||
274             (state == static_cast<uint8_t>(acpi_state::PowerState::legacyOn)) ||
275             (state ==
276              static_cast<uint8_t>(acpi_state::PowerState::legacyOff)) ||
277             (state == static_cast<uint8_t>(acpi_state::PowerState::unknown)) ||
278             (state == static_cast<uint8_t>(acpi_state::PowerState::noChange)))
279         {
280             return true;
281         }
282         else
283         {
284             return false;
285         }
286     }
287     else if (type == acpi_state::PowerStateType::devPowerState)
288     {
289         if ((state <= static_cast<uint8_t>(acpi_state::PowerState::s3D3)) ||
290             (state == static_cast<uint8_t>(acpi_state::PowerState::unknown)) ||
291             (state == static_cast<uint8_t>(acpi_state::PowerState::noChange)))
292         {
293             return true;
294         }
295         else
296         {
297             return false;
298         }
299     }
300     else
301     {
302         return false;
303     }
304     return false;
305 }
306 } // namespace acpi_state
307 
308 ipmi_ret_t ipmi_app_set_acpi_power_state(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
309                                          ipmi_request_t request,
310                                          ipmi_response_t response,
311                                          ipmi_data_len_t data_len,
312                                          ipmi_context_t context)
313 {
314     auto s = static_cast<uint8_t>(acpi_state::PowerState::unknown);
315     ipmi_ret_t rc = IPMI_CC_OK;
316 
317     sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()};
318 
319     auto value = acpi_state::ACPIPowerState::ACPI::Unknown;
320 
321     auto* req = reinterpret_cast<acpi_state::ACPIState*>(request);
322 
323     if (*data_len != sizeof(acpi_state::ACPIState))
324     {
325         log<level::ERR>("set_acpi invalid len");
326         *data_len = 0;
327         return IPMI_CC_REQ_DATA_LEN_INVALID;
328     }
329 
330     *data_len = 0;
331 
332     if (req->sysACPIState & acpi_state::stateChanged)
333     {
334         // set system power state
335         s = req->sysACPIState & ~acpi_state::stateChanged;
336 
337         if (!acpi_state::isValidACPIState(
338                 acpi_state::PowerStateType::sysPowerState, s))
339         {
340             log<level::ERR>("set_acpi_power sys invalid input",
341                             entry("S=%x", s));
342             return IPMI_CC_PARM_OUT_OF_RANGE;
343         }
344 
345         // valid input
346         if (s == static_cast<uint8_t>(acpi_state::PowerState::noChange))
347         {
348             log<level::DEBUG>("No change for system power state");
349         }
350         else
351         {
352             auto found = std::find_if(
353                 acpi_state::dbusToIPMI.begin(), acpi_state::dbusToIPMI.end(),
354                 [&s](const auto& iter) {
355                     return (static_cast<uint8_t>(iter.second) == s);
356                 });
357 
358             value = found->first;
359 
360             try
361             {
362                 auto acpiObject =
363                     ipmi::getDbusObject(bus, acpi_state::acpiInterface);
364                 ipmi::setDbusProperty(bus, acpiObject.second, acpiObject.first,
365                                       acpi_state::acpiInterface,
366                                       acpi_state::sysACPIProp,
367                                       convertForMessage(value));
368             }
369             catch (const InternalFailure& e)
370             {
371                 log<level::ERR>("Failed in set ACPI system property",
372                                 entry("EXCEPTION=%s", e.what()));
373                 return IPMI_CC_UNSPECIFIED_ERROR;
374             }
375         }
376     }
377     else
378     {
379         log<level::DEBUG>("Do not change system power state");
380     }
381 
382     if (req->devACPIState & acpi_state::stateChanged)
383     {
384         // set device power state
385         s = req->devACPIState & ~acpi_state::stateChanged;
386         if (!acpi_state::isValidACPIState(
387                 acpi_state::PowerStateType::devPowerState, s))
388         {
389             log<level::ERR>("set_acpi_power dev invalid input",
390                             entry("S=%x", s));
391             return IPMI_CC_PARM_OUT_OF_RANGE;
392         }
393 
394         // valid input
395         if (s == static_cast<uint8_t>(acpi_state::PowerState::noChange))
396         {
397             log<level::DEBUG>("No change for device power state");
398         }
399         else
400         {
401             auto found = std::find_if(
402                 acpi_state::dbusToIPMI.begin(), acpi_state::dbusToIPMI.end(),
403                 [&s](const auto& iter) {
404                     return (static_cast<uint8_t>(iter.second) == s);
405                 });
406 
407             value = found->first;
408 
409             try
410             {
411                 auto acpiObject =
412                     ipmi::getDbusObject(bus, acpi_state::acpiInterface);
413                 ipmi::setDbusProperty(bus, acpiObject.second, acpiObject.first,
414                                       acpi_state::acpiInterface,
415                                       acpi_state::devACPIProp,
416                                       convertForMessage(value));
417             }
418             catch (const InternalFailure& e)
419             {
420                 log<level::ERR>("Failed in set ACPI device property",
421                                 entry("EXCEPTION=%s", e.what()));
422                 return IPMI_CC_UNSPECIFIED_ERROR;
423             }
424         }
425     }
426     else
427     {
428         log<level::DEBUG>("Do not change device power state");
429     }
430 
431     return rc;
432 }
433 
434 /**
435  *  @brief implements the get ACPI power state command
436  *
437  *  @return IPMI completion code plus response data on success.
438  *   -  ACPI system power state
439  *   -  ACPI device power state
440  **/
441 ipmi::RspType<uint8_t, // acpiSystemPowerState
442               uint8_t  // acpiDevicePowerState
443               >
444     ipmiGetAcpiPowerState()
445 {
446     uint8_t sysAcpiState;
447     uint8_t devAcpiState;
448 
449     sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()};
450 
451     try
452     {
453         auto acpiObject = ipmi::getDbusObject(bus, acpi_state::acpiInterface);
454 
455         auto sysACPIVal = ipmi::getDbusProperty(
456             bus, acpiObject.second, acpiObject.first, acpi_state::acpiInterface,
457             acpi_state::sysACPIProp);
458         auto sysACPI = acpi_state::ACPIPowerState::convertACPIFromString(
459             std::get<std::string>(sysACPIVal));
460         sysAcpiState = static_cast<uint8_t>(acpi_state::dbusToIPMI.at(sysACPI));
461 
462         auto devACPIVal = ipmi::getDbusProperty(
463             bus, acpiObject.second, acpiObject.first, acpi_state::acpiInterface,
464             acpi_state::devACPIProp);
465         auto devACPI = acpi_state::ACPIPowerState::convertACPIFromString(
466             std::get<std::string>(devACPIVal));
467         devAcpiState = static_cast<uint8_t>(acpi_state::dbusToIPMI.at(devACPI));
468     }
469     catch (const InternalFailure& e)
470     {
471         return ipmi::responseUnspecifiedError();
472     }
473 
474     return ipmi::responseSuccess(sysAcpiState, devAcpiState);
475 }
476 
477 typedef struct
478 {
479     char major;
480     char minor;
481     uint16_t d[2];
482 } Revision;
483 
484 /* Currently supports the vx.x-x-[-x] and v1.x.x-x-[-x] format. It will     */
485 /* return -1 if not in those formats, this routine knows how to parse       */
486 /* version = v0.6-19-gf363f61-dirty                                         */
487 /*            ^ ^ ^^          ^                                             */
488 /*            | |  |----------|-- additional details                        */
489 /*            | |---------------- Minor                                     */
490 /*            |------------------ Major                                     */
491 /* and version = v1.99.10-113-g65edf7d-r3-0-g9e4f715                        */
492 /*                ^ ^  ^^ ^                                                 */
493 /*                | |  |--|---------- additional details                    */
494 /*                | |---------------- Minor                                 */
495 /*                |------------------ Major                                 */
496 /* Additional details : If the option group exists it will force Auxiliary  */
497 /* Firmware Revision Information 4th byte to 1 indicating the build was     */
498 /* derived with additional edits                                            */
499 int convertVersion(std::string s, Revision& rev)
500 {
501     std::string token;
502     uint16_t commits;
503 
504     auto location = s.find_first_of('v');
505     if (location != std::string::npos)
506     {
507         s = s.substr(location + 1);
508     }
509 
510     if (!s.empty())
511     {
512         location = s.find_first_of(".");
513         if (location != std::string::npos)
514         {
515             rev.major =
516                 static_cast<char>(std::stoi(s.substr(0, location), 0, 16));
517             token = s.substr(location + 1);
518         }
519 
520         if (!token.empty())
521         {
522             location = token.find_first_of(".-");
523             if (location != std::string::npos)
524             {
525                 rev.minor = static_cast<char>(
526                     std::stoi(token.substr(0, location), 0, 16));
527                 token = token.substr(location + 1);
528             }
529         }
530 
531         // Capture the number of commits on top of the minor tag.
532         // I'm using BE format like the ipmi spec asked for
533         location = token.find_first_of(".-");
534         if (!token.empty())
535         {
536             commits = std::stoi(token.substr(0, location), 0, 16);
537             rev.d[0] = (commits >> 8) | (commits << 8);
538 
539             // commit number we skip
540             location = token.find_first_of(".-");
541             if (location != std::string::npos)
542             {
543                 token = token.substr(location + 1);
544             }
545         }
546         else
547         {
548             rev.d[0] = 0;
549         }
550 
551         if (location != std::string::npos)
552         {
553             token = token.substr(location + 1);
554         }
555 
556         // Any value of the optional parameter forces it to 1
557         location = token.find_first_of(".-");
558         if (location != std::string::npos)
559         {
560             token = token.substr(location + 1);
561         }
562         commits = (!token.empty()) ? 1 : 0;
563 
564         // We do this operation to get this displayed in least significant bytes
565         // of ipmitool device id command.
566         rev.d[1] = (commits >> 8) | (commits << 8);
567     }
568 
569     return 0;
570 }
571 
572 auto ipmiAppGetDeviceId() -> ipmi::RspType<uint8_t, // Device ID
573                                            uint8_t, // Device Revision
574                                            uint8_t, // Firmware Revision Major
575                                            uint8_t, // Firmware Revision minor
576                                            uint8_t, // IPMI version
577                                            uint8_t, // Additional device support
578                                            uint24_t, // MFG ID
579                                            uint16_t, // Product ID
580                                            uint32_t  // AUX info
581                                            >
582 {
583     int r = -1;
584     Revision rev = {0};
585     static struct
586     {
587         uint8_t id;
588         uint8_t revision;
589         uint8_t fw[2];
590         uint8_t ipmiVer;
591         uint8_t addnDevSupport;
592         uint24_t manufId;
593         uint16_t prodId;
594         uint32_t aux;
595     } devId;
596     static bool dev_id_initialized = false;
597     static bool defaultActivationSetting = true;
598     const char* filename = "/usr/share/ipmi-providers/dev_id.json";
599     constexpr auto ipmiDevIdStateShift = 7;
600     constexpr auto ipmiDevIdFw1Mask = ~(1 << ipmiDevIdStateShift);
601 
602     if (!dev_id_initialized)
603     {
604         try
605         {
606             auto version = getActiveSoftwareVersionInfo();
607             r = convertVersion(version, rev);
608         }
609         catch (const std::exception& e)
610         {
611             log<level::ERR>(e.what());
612         }
613 
614         if (r >= 0)
615         {
616             // bit7 identifies if the device is available
617             // 0=normal operation
618             // 1=device firmware, SDR update,
619             // or self-initialization in progress.
620             // The availability may change in run time, so mask here
621             // and initialize later.
622             devId.fw[0] = rev.major & ipmiDevIdFw1Mask;
623 
624             rev.minor = (rev.minor > 99 ? 99 : rev.minor);
625             devId.fw[1] = rev.minor % 10 + (rev.minor / 10) * 16;
626             std::memcpy(&devId.aux, rev.d, 4);
627         }
628 
629         // IPMI Spec version 2.0
630         devId.ipmiVer = 2;
631 
632         std::ifstream devIdFile(filename);
633         if (devIdFile.is_open())
634         {
635             auto data = nlohmann::json::parse(devIdFile, nullptr, false);
636             if (!data.is_discarded())
637             {
638                 devId.id = data.value("id", 0);
639                 devId.revision = data.value("revision", 0);
640                 devId.addnDevSupport = data.value("addn_dev_support", 0);
641                 devId.manufId = data.value("manuf_id", 0);
642                 devId.prodId = data.value("prod_id", 0);
643                 devId.aux = data.value("aux", 0);
644 
645                 // Set the availablitity of the BMC.
646                 defaultActivationSetting = data.value("availability", true);
647 
648                 // Don't read the file every time if successful
649                 dev_id_initialized = true;
650             }
651             else
652             {
653                 log<level::ERR>("Device ID JSON parser failure");
654                 return ipmi::responseUnspecifiedError();
655             }
656         }
657         else
658         {
659             log<level::ERR>("Device ID file not found");
660             return ipmi::responseUnspecifiedError();
661         }
662     }
663 
664     // Set availability to the actual current BMC state
665     devId.fw[0] &= ipmiDevIdFw1Mask;
666     if (!getCurrentBmcStateWithFallback(defaultActivationSetting))
667     {
668         devId.fw[0] |= (1 << ipmiDevIdStateShift);
669     }
670 
671     return ipmi::responseSuccess(
672         devId.id, devId.revision, devId.fw[0], devId.fw[1], devId.ipmiVer,
673         devId.addnDevSupport, devId.manufId, devId.prodId, devId.aux);
674 }
675 
676 auto ipmiAppGetSelfTestResults() -> ipmi::RspType<uint8_t, uint8_t>
677 {
678     // Byte 2:
679     //  55h - No error.
680     //  56h - Self Test function not implemented in this controller.
681     //  57h - Corrupted or inaccesssible data or devices.
682     //  58h - Fatal hardware error.
683     //  FFh - reserved.
684     //  all other: Device-specific 'internal failure'.
685     //  Byte 3:
686     //      For byte 2 = 55h, 56h, FFh:     00h
687     //      For byte 2 = 58h, all other:    Device-specific
688     //      For byte 2 = 57h:   self-test error bitfield.
689     //      Note: returning 57h does not imply that all test were run.
690     //      [7] 1b = Cannot access SEL device.
691     //      [6] 1b = Cannot access SDR Repository.
692     //      [5] 1b = Cannot access BMC FRU device.
693     //      [4] 1b = IPMB signal lines do not respond.
694     //      [3] 1b = SDR Repository empty.
695     //      [2] 1b = Internal Use Area of BMC FRU corrupted.
696     //      [1] 1b = controller update 'boot block' firmware corrupted.
697     //      [0] 1b = controller operational firmware corrupted.
698     constexpr uint8_t notImplemented = 0x56;
699     constexpr uint8_t zero = 0;
700     return ipmi::responseSuccess(notImplemented, zero);
701 }
702 
703 static constexpr size_t uuidBinaryLength = 16;
704 static std::array<uint8_t, uuidBinaryLength> rfc4122ToIpmi(std::string rfc4122)
705 {
706     using Argument = xyz::openbmc_project::Common::InvalidArgument;
707     // UUID is in RFC4122 format. Ex: 61a39523-78f2-11e5-9862-e6402cfc3223
708     // Per IPMI Spec 2.0 need to convert to 16 hex bytes and reverse the byte
709     // order
710     // Ex: 0x2332fc2c40e66298e511f2782395a361
711     constexpr size_t uuidHexLength = (2 * uuidBinaryLength);
712     constexpr size_t uuidRfc4122Length = (uuidHexLength + 4);
713     std::array<uint8_t, uuidBinaryLength> uuid;
714     if (rfc4122.size() == uuidRfc4122Length)
715     {
716         rfc4122.erase(std::remove(rfc4122.begin(), rfc4122.end(), '-'),
717                       rfc4122.end());
718     }
719     if (rfc4122.size() != uuidHexLength)
720     {
721         elog<InvalidArgument>(Argument::ARGUMENT_NAME("rfc4122"),
722                               Argument::ARGUMENT_VALUE(rfc4122.c_str()));
723     }
724     for (size_t ind = 0; ind < uuidHexLength; ind += 2)
725     {
726         char v[3];
727         v[0] = rfc4122[ind];
728         v[1] = rfc4122[ind + 1];
729         v[2] = 0;
730         size_t err;
731         long b;
732         try
733         {
734             b = std::stoul(v, &err, 16);
735         }
736         catch (std::exception& e)
737         {
738             elog<InvalidArgument>(Argument::ARGUMENT_NAME("rfc4122"),
739                                   Argument::ARGUMENT_VALUE(rfc4122.c_str()));
740         }
741         // check that exactly two ascii bytes were converted
742         if (err != 2)
743         {
744             elog<InvalidArgument>(Argument::ARGUMENT_NAME("rfc4122"),
745                                   Argument::ARGUMENT_VALUE(rfc4122.c_str()));
746         }
747         uuid[uuidBinaryLength - (ind / 2) - 1] = static_cast<uint8_t>(b);
748     }
749     return uuid;
750 }
751 
752 auto ipmiAppGetDeviceGuid()
753     -> ipmi::RspType<std::array<uint8_t, uuidBinaryLength>>
754 {
755     // return a fixed GUID based on /etc/machine-id
756     // This should match the /redfish/v1/Managers/bmc's UUID data
757 
758     // machine specific application ID (for BMC ID)
759     // generated by systemd-id128 -p new as per man page
760     static constexpr sd_id128_t bmcUuidAppId = SD_ID128_MAKE(
761         e0, e1, 73, 76, 64, 61, 47, da, a5, 0c, d0, cc, 64, 12, 45, 78);
762 
763     sd_id128_t bmcUuid;
764     // create the UUID from /etc/machine-id via the systemd API
765     sd_id128_get_machine_app_specific(bmcUuidAppId, &bmcUuid);
766 
767     char bmcUuidCstr[SD_ID128_STRING_MAX];
768     std::string systemUuid = sd_id128_to_string(bmcUuid, bmcUuidCstr);
769 
770     std::array<uint8_t, uuidBinaryLength> uuid = rfc4122ToIpmi(systemUuid);
771     return ipmi::responseSuccess(uuid);
772 }
773 
774 auto ipmiAppGetBtCapabilities()
775     -> ipmi::RspType<uint8_t, uint8_t, uint8_t, uint8_t, uint8_t>
776 {
777     // Per IPMI 2.0 spec, the input and output buffer size must be the max
778     // buffer size minus one byte to allocate space for the length byte.
779     constexpr uint8_t nrOutstanding = 0x01;
780     constexpr uint8_t inputBufferSize = MAX_IPMI_BUFFER - 1;
781     constexpr uint8_t outputBufferSize = MAX_IPMI_BUFFER - 1;
782     constexpr uint8_t transactionTime = 0x0A;
783     constexpr uint8_t nrRetries = 0x01;
784 
785     return ipmi::responseSuccess(nrOutstanding, inputBufferSize,
786                                  outputBufferSize, transactionTime, nrRetries);
787 }
788 
789 ipmi_ret_t ipmi_app_get_sys_guid(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
790                                  ipmi_request_t request,
791                                  ipmi_response_t response,
792                                  ipmi_data_len_t data_len,
793                                  ipmi_context_t context)
794 
795 {
796     ipmi_ret_t rc = IPMI_CC_OK;
797     sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()};
798 
799     try
800     {
801         // Get the Inventory object implementing BMC interface
802         ipmi::DbusObjectInfo bmcObject =
803             ipmi::getDbusObject(bus, bmc_interface);
804 
805         // Read UUID property value from bmcObject
806         // UUID is in RFC4122 format Ex: 61a39523-78f2-11e5-9862-e6402cfc3223
807         auto variant =
808             ipmi::getDbusProperty(bus, bmcObject.second, bmcObject.first,
809                                   bmc_guid_interface, bmc_guid_property);
810         std::string guidProp = std::get<std::string>(variant);
811 
812         // Erase "-" characters from the property value
813         guidProp.erase(std::remove(guidProp.begin(), guidProp.end(), '-'),
814                        guidProp.end());
815 
816         auto guidPropLen = guidProp.length();
817         // Validate UUID data
818         // Divide by 2 as 1 byte is built from 2 chars
819         if ((guidPropLen <= 0) || ((guidPropLen / 2) != bmc_guid_len))
820 
821         {
822             log<level::ERR>("Invalid UUID property value",
823                             entry("UUID_LENGTH=%d", guidPropLen));
824             return IPMI_CC_RESPONSE_ERROR;
825         }
826 
827         // Convert data in RFC4122(MSB) format to LSB format
828         // Get 2 characters at a time as 1 byte is built from 2 chars and
829         // convert to hex byte
830         // TODO: Data printed for GUID command is not as per the
831         // GUID format defined in IPMI specification 2.0 section 20.8
832         // Ticket raised: https://sourceforge.net/p/ipmitool/bugs/501/
833         uint8_t respGuid[bmc_guid_len];
834         for (size_t i = 0, respLoc = (bmc_guid_len - 1);
835              i < guidPropLen && respLoc >= 0; i += 2, respLoc--)
836         {
837             auto value = static_cast<uint8_t>(
838                 std::stoi(guidProp.substr(i, 2).c_str(), NULL, 16));
839             respGuid[respLoc] = value;
840         }
841 
842         *data_len = bmc_guid_len;
843         std::memcpy(response, &respGuid, bmc_guid_len);
844     }
845     catch (const InternalFailure& e)
846     {
847         log<level::ERR>("Failed in reading BMC UUID property",
848                         entry("INTERFACE=%s", bmc_interface),
849                         entry("PROPERTY_INTERFACE=%s", bmc_guid_interface),
850                         entry("PROPERTY=%s", bmc_guid_property));
851         return IPMI_CC_UNSPECIFIED_ERROR;
852     }
853     return rc;
854 }
855 
856 static std::unique_ptr<SysInfoParamStore> sysInfoParamStore;
857 
858 static std::string sysInfoReadSystemName()
859 {
860     // Use the BMC hostname as the "System Name."
861     char hostname[HOST_NAME_MAX + 1] = {};
862     if (gethostname(hostname, HOST_NAME_MAX) != 0)
863     {
864         perror("System info parameter: system name");
865     }
866     return hostname;
867 }
868 
869 struct IpmiSysInfoResp
870 {
871     uint8_t paramRevision;
872     uint8_t setSelector;
873     union
874     {
875         struct
876         {
877             uint8_t encoding;
878             uint8_t stringLen;
879             uint8_t stringData0[14];
880         } __attribute__((packed));
881         uint8_t stringDataN[16];
882         uint8_t byteData;
883     };
884 } __attribute__((packed));
885 
886 /**
887  * Split a string into (up to) 16-byte chunks as expected in response for get
888  * system info parameter.
889  *
890  * @param[in] fullString: Input string to be split
891  * @param[in] chunkIndex: Index of the chunk to be written out
892  * @param[in,out] chunk: Output data buffer; must have 14 byte capacity if
893  *          chunk_index = 0 and 16-byte capacity otherwise
894  * @return the number of bytes written into the output buffer, or -EINVAL for
895  * invalid arguments.
896  */
897 static int splitStringParam(const std::string& fullString, int chunkIndex,
898                             uint8_t* chunk)
899 {
900     constexpr int maxChunk = 255;
901     constexpr int smallChunk = 14;
902     constexpr int chunkSize = 16;
903     if (chunkIndex > maxChunk || chunk == nullptr)
904     {
905         return -EINVAL;
906     }
907     try
908     {
909         std::string output;
910         if (chunkIndex == 0)
911         {
912             // Output must have 14 byte capacity.
913             output = fullString.substr(0, smallChunk);
914         }
915         else
916         {
917             // Output must have 16 byte capacity.
918             output = fullString.substr((chunkIndex * chunkSize) - 2, chunkSize);
919         }
920 
921         std::memcpy(chunk, output.c_str(), output.length());
922         return output.length();
923     }
924     catch (const std::out_of_range& e)
925     {
926         // The position was beyond the end.
927         return -EINVAL;
928     }
929 }
930 
931 /**
932  * Packs the Get Sys Info Request Item into the response.
933  *
934  * @param[in] paramString - the parameter.
935  * @param[in] setSelector - the selector
936  * @param[in,out] resp - the System info response.
937  * @return The number of bytes packed or failure from splitStringParam().
938  */
939 static int packGetSysInfoResp(const std::string& paramString,
940                               uint8_t setSelector, IpmiSysInfoResp* resp)
941 {
942     uint8_t* dataBuffer = resp->stringDataN;
943     resp->setSelector = setSelector;
944     if (resp->setSelector == 0) // First chunk has only 14 bytes.
945     {
946         resp->encoding = 0;
947         resp->stringLen = paramString.length();
948         dataBuffer = resp->stringData0;
949     }
950     return splitStringParam(paramString, resp->setSelector, dataBuffer);
951 }
952 
953 ipmi_ret_t ipmi_app_get_system_info(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
954                                     ipmi_request_t request,
955                                     ipmi_response_t response,
956                                     ipmi_data_len_t dataLen,
957                                     ipmi_context_t context)
958 {
959     IpmiSysInfoResp resp = {};
960     size_t respLen = 0;
961     uint8_t* const reqData = static_cast<uint8_t*>(request);
962     std::string paramString;
963     bool found;
964     std::tuple<bool, std::string> ret;
965     constexpr int minRequestSize = 4;
966     constexpr int paramSelector = 1;
967     constexpr uint8_t revisionOnly = 0x80;
968     const uint8_t paramRequested = reqData[paramSelector];
969     int rc;
970 
971     if (*dataLen < minRequestSize)
972     {
973         return IPMI_CC_REQ_DATA_LEN_INVALID;
974     }
975 
976     *dataLen = 0; // default to 0.
977 
978     // Parameters revision as of IPMI spec v2.0 rev. 1.1 (Feb 11, 2014 E6)
979     resp.paramRevision = 0x11;
980     if (reqData[0] & revisionOnly) // Get parameter revision only
981     {
982         respLen = 1;
983         goto writeResponse;
984     }
985 
986     // The "Set In Progress" parameter can be used for rollback of parameter
987     // data and is not implemented.
988     if (paramRequested == 0)
989     {
990         resp.byteData = 0;
991         respLen = 2;
992         goto writeResponse;
993     }
994 
995     if (sysInfoParamStore == nullptr)
996     {
997         sysInfoParamStore = std::make_unique<SysInfoParamStore>();
998         sysInfoParamStore->update(IPMI_SYSINFO_SYSTEM_NAME,
999                                   sysInfoReadSystemName);
1000     }
1001 
1002     // Parameters other than Set In Progress are assumed to be strings.
1003     ret = sysInfoParamStore->lookup(paramRequested);
1004     found = std::get<0>(ret);
1005     paramString = std::get<1>(ret);
1006     if (!found)
1007     {
1008         return IPMI_CC_SYSTEM_INFO_PARAMETER_NOT_SUPPORTED;
1009     }
1010     // TODO: Cache each parameter across multiple calls, until the whole string
1011     // has been read out. Otherwise, it's possible for a parameter to change
1012     // between requests for its chunks, returning chunks incoherent with each
1013     // other. For now, the parameter store is simply required to have only
1014     // idempotent callbacks.
1015     rc = packGetSysInfoResp(paramString, reqData[2], &resp);
1016     if (rc == -EINVAL)
1017     {
1018         return IPMI_CC_RESPONSE_ERROR;
1019     }
1020 
1021     respLen = sizeof(resp); // Write entire string data chunk in response.
1022 
1023 writeResponse:
1024     std::memcpy(response, &resp, sizeof(resp));
1025     *dataLen = respLen;
1026     return IPMI_CC_OK;
1027 }
1028 
1029 inline std::vector<uint8_t> convertStringToData(const std::string& command)
1030 {
1031     std::istringstream iss(command);
1032     std::string token;
1033     std::vector<uint8_t> dataValue;
1034     while (std::getline(iss, token, ' '))
1035     {
1036         dataValue.emplace_back(
1037             static_cast<uint8_t>(std::stoul(token, nullptr, base_16)));
1038     }
1039     return dataValue;
1040 }
1041 
1042 static bool populateI2CMasterWRWhitelist()
1043 {
1044     nlohmann::json data = nullptr;
1045     std::ifstream jsonFile(i2cMasterWRWhitelistFile);
1046 
1047     if (!jsonFile.good())
1048     {
1049         log<level::WARNING>("i2c white list file not found!",
1050                             entry("FILE_NAME: %s", i2cMasterWRWhitelistFile));
1051         return false;
1052     }
1053 
1054     try
1055     {
1056         data = nlohmann::json::parse(jsonFile, nullptr, false);
1057     }
1058     catch (nlohmann::json::parse_error& e)
1059     {
1060         log<level::ERR>("Corrupted i2c white list config file",
1061                         entry("FILE_NAME: %s", i2cMasterWRWhitelistFile),
1062                         entry("MSG: %s", e.what()));
1063         return false;
1064     }
1065 
1066     try
1067     {
1068         // Example JSON Structure format
1069         // "filters": [
1070         //    {
1071         //      "Description": "Allow full read - ignore first byte write value
1072         //      for 0x40 to 0x4F",
1073         //      "busId": "0x01",
1074         //      "slaveAddr": "0x40",
1075         //      "slaveAddrMask": "0x0F",
1076         //      "command": "0x00",
1077         //      "commandMask": "0xFF"
1078         //    },
1079         //    {
1080         //      "Description": "Allow full read - first byte match 0x05 and
1081         //      ignore second byte",
1082         //      "busId": "0x01",
1083         //      "slaveAddr": "0x57",
1084         //      "slaveAddrMask": "0x00",
1085         //      "command": "0x05 0x00",
1086         //      "commandMask": "0x00 0xFF"
1087         //    },]
1088 
1089         nlohmann::json filters = data[filtersStr].get<nlohmann::json>();
1090         std::vector<i2cMasterWRWhitelist>& whitelist = getWRWhitelist();
1091         for (const auto& it : filters.items())
1092         {
1093             nlohmann::json filter = it.value();
1094             if (filter.is_null())
1095             {
1096                 log<level::ERR>(
1097                     "Corrupted I2C master write read whitelist config file",
1098                     entry("FILE_NAME: %s", i2cMasterWRWhitelistFile));
1099                 return false;
1100             }
1101             const std::vector<uint8_t>& writeData =
1102                 convertStringToData(filter[cmdStr].get<std::string>());
1103             const std::vector<uint8_t>& writeDataMask =
1104                 convertStringToData(filter[cmdMaskStr].get<std::string>());
1105             if (writeDataMask.size() != writeData.size())
1106             {
1107                 log<level::ERR>("I2C master write read whitelist filter "
1108                                 "mismatch for command & mask size");
1109                 return false;
1110             }
1111             whitelist.push_back(
1112                 {static_cast<uint8_t>(std::stoul(
1113                      filter[busIdStr].get<std::string>(), nullptr, base_16)),
1114                  static_cast<uint8_t>(
1115                      std::stoul(filter[slaveAddrStr].get<std::string>(),
1116                                 nullptr, base_16)),
1117                  static_cast<uint8_t>(
1118                      std::stoul(filter[slaveAddrMaskStr].get<std::string>(),
1119                                 nullptr, base_16)),
1120                  writeData, writeDataMask});
1121         }
1122         if (whitelist.size() != filters.size())
1123         {
1124             log<level::ERR>(
1125                 "I2C master write read whitelist filter size mismatch");
1126             return false;
1127         }
1128     }
1129     catch (std::exception& e)
1130     {
1131         log<level::ERR>("I2C master write read whitelist unexpected exception",
1132                         entry("ERROR=%s", e.what()));
1133         return false;
1134     }
1135     return true;
1136 }
1137 
1138 static inline bool isWriteDataWhitelisted(const std::vector<uint8_t>& data,
1139                                           const std::vector<uint8_t>& dataMask,
1140                                           const std::vector<uint8_t>& writeData)
1141 {
1142     std::vector<uint8_t> processedDataBuf(data.size());
1143     std::vector<uint8_t> processedReqBuf(dataMask.size());
1144     std::transform(writeData.begin(), writeData.end(), dataMask.begin(),
1145                    processedReqBuf.begin(), std::bit_or<uint8_t>());
1146     std::transform(data.begin(), data.end(), dataMask.begin(),
1147                    processedDataBuf.begin(), std::bit_or<uint8_t>());
1148 
1149     return (processedDataBuf == processedReqBuf);
1150 }
1151 
1152 static bool isCmdWhitelisted(uint8_t busId, uint8_t slaveAddr,
1153                              std::vector<uint8_t>& writeData)
1154 {
1155     std::vector<i2cMasterWRWhitelist>& whiteList = getWRWhitelist();
1156     for (const auto& wlEntry : whiteList)
1157     {
1158         if ((busId == wlEntry.busId) &&
1159             ((slaveAddr | wlEntry.slaveAddrMask) ==
1160              (wlEntry.slaveAddr | wlEntry.slaveAddrMask)))
1161         {
1162             const std::vector<uint8_t>& dataMask = wlEntry.dataMask;
1163             // Skip as no-match, if requested write data is more than the
1164             // write data mask size
1165             if (writeData.size() > dataMask.size())
1166             {
1167                 continue;
1168             }
1169             if (isWriteDataWhitelisted(wlEntry.data, dataMask, writeData))
1170             {
1171                 return true;
1172             }
1173         }
1174     }
1175     return false;
1176 }
1177 
1178 /** @brief implements master write read IPMI command which can be used for
1179  * low-level I2C/SMBus write, read or write-read access
1180  *  @param isPrivateBus -to indicate private bus usage
1181  *  @param busId - bus id
1182  *  @param channelNum - channel number
1183  *  @param reserved - skip 1 bit
1184  *  @param slaveAddr - slave address
1185  *  @param read count - number of bytes to be read
1186  *  @param writeData - data to be written
1187  *
1188  *  @returns IPMI completion code plus response data
1189  *   - readData - i2c response data
1190  */
1191 ipmi::RspType<std::vector<uint8_t>>
1192     ipmiMasterWriteRead(bool isPrivateBus, uint3_t busId, uint4_t channelNum,
1193                         bool reserved, uint7_t slaveAddr, uint8_t readCount,
1194                         std::vector<uint8_t> writeData)
1195 {
1196     i2c_rdwr_ioctl_data msgReadWrite = {0};
1197     i2c_msg i2cmsg[2] = {0};
1198 
1199     if (readCount > maxIPMIWriteReadSize)
1200     {
1201         log<level::ERR>("Master write read command: Read count exceeds limit");
1202         return ipmi::responseParmOutOfRange();
1203     }
1204     const size_t writeCount = writeData.size();
1205     if (!readCount && !writeCount)
1206     {
1207         log<level::ERR>("Master write read command: Read & write count are 0");
1208         return ipmi::responseInvalidFieldRequest();
1209     }
1210     if (!isCmdWhitelisted(static_cast<uint8_t>(busId),
1211                           static_cast<uint8_t>(slaveAddr), writeData))
1212     {
1213         log<level::ERR>("Master write read request blocked!",
1214                         entry("BUS=%d", static_cast<uint8_t>(busId)),
1215                         entry("ADDR=0x%x", static_cast<uint8_t>(slaveAddr)));
1216         return ipmi::responseInvalidFieldRequest();
1217     }
1218     std::vector<uint8_t> readBuf(readCount);
1219     std::string i2cBus =
1220         "/dev/i2c-" + std::to_string(static_cast<uint8_t>(busId));
1221 
1222     int i2cDev = ::open(i2cBus.c_str(), O_RDWR | O_CLOEXEC);
1223     if (i2cDev < 0)
1224     {
1225         log<level::ERR>("Failed to open i2c bus",
1226                         entry("BUS=%s", i2cBus.c_str()));
1227         return ipmi::responseInvalidFieldRequest();
1228     }
1229 
1230     int msgCount = 0;
1231     if (writeCount)
1232     {
1233         i2cmsg[msgCount].addr = static_cast<uint8_t>(slaveAddr);
1234         i2cmsg[msgCount].flags = 0x00;
1235         i2cmsg[msgCount].len = writeCount;
1236         i2cmsg[msgCount].buf = writeData.data();
1237         msgCount++;
1238     }
1239     if (readCount)
1240     {
1241         i2cmsg[msgCount].addr = static_cast<uint8_t>(slaveAddr);
1242         i2cmsg[msgCount].flags = I2C_M_RD;
1243         i2cmsg[msgCount].len = readCount;
1244         i2cmsg[msgCount].buf = readBuf.data();
1245         msgCount++;
1246     }
1247 
1248     msgReadWrite.msgs = i2cmsg;
1249     msgReadWrite.nmsgs = msgCount;
1250 
1251     int ret = ::ioctl(i2cDev, I2C_RDWR, &msgReadWrite);
1252     ::close(i2cDev);
1253 
1254     if (ret < 0)
1255     {
1256         log<level::ERR>("Master write read: Failed", entry("RET=%d", ret));
1257         return ipmi::responseUnspecifiedError();
1258     }
1259     if (readCount)
1260     {
1261         readBuf.resize(msgReadWrite.msgs[msgCount - 1].len);
1262     }
1263     return ipmi::responseSuccess(readBuf);
1264 }
1265 
1266 void register_netfn_app_functions()
1267 {
1268     // <Get Device ID>
1269     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp,
1270                           ipmi::app::cmdGetDeviceId, ipmi::Privilege::User,
1271                           ipmiAppGetDeviceId);
1272 
1273     // <Get BT Interface Capabilities>
1274     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp,
1275                           ipmi::app::cmdGetBtIfaceCapabilities,
1276                           ipmi::Privilege::User, ipmiAppGetBtCapabilities);
1277 
1278     // <Reset Watchdog Timer>
1279     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp,
1280                           ipmi::app::cmdResetWatchdogTimer,
1281                           ipmi::Privilege::Operator, ipmiAppResetWatchdogTimer);
1282 
1283     // <Set Watchdog Timer>
1284     ipmi_register_callback(NETFUN_APP, IPMI_CMD_SET_WD, NULL,
1285                            ipmi_app_watchdog_set, PRIVILEGE_OPERATOR);
1286 
1287     // <Get Watchdog Timer>
1288     ipmi_register_callback(NETFUN_APP, IPMI_CMD_GET_WD, NULL,
1289                            ipmi_app_watchdog_get, PRIVILEGE_OPERATOR);
1290 
1291     // <Get Self Test Results>
1292     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp,
1293                           ipmi::app::cmdGetSelfTestResults,
1294                           ipmi::Privilege::User, ipmiAppGetSelfTestResults);
1295 
1296     // <Get Device GUID>
1297     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp,
1298                           ipmi::app::cmdGetDeviceGuid, ipmi::Privilege::User,
1299                           ipmiAppGetDeviceGuid);
1300 
1301     // <Set ACPI Power State>
1302     ipmi_register_callback(NETFUN_APP, IPMI_CMD_SET_ACPI, NULL,
1303                            ipmi_app_set_acpi_power_state, PRIVILEGE_ADMIN);
1304 
1305     // <Get ACPI Power State>
1306     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp,
1307                           ipmi::app::cmdGetAcpiPowerState,
1308                           ipmi::Privilege::Admin, ipmiGetAcpiPowerState);
1309 
1310     // Note: For security reason, this command will be registered only when
1311     // there are proper I2C Master write read whitelist
1312     if (populateI2CMasterWRWhitelist())
1313     {
1314         // Note: For security reasons, registering master write read as admin
1315         // privilege command, even though IPMI 2.0 specification allows it as
1316         // operator privilege.
1317         ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp,
1318                               ipmi::app::cmdMasterWriteRead,
1319                               ipmi::Privilege::Admin, ipmiMasterWriteRead);
1320     }
1321 
1322     // <Get System GUID Command>
1323     ipmi_register_callback(NETFUN_APP, IPMI_CMD_GET_SYS_GUID, NULL,
1324                            ipmi_app_get_sys_guid, PRIVILEGE_USER);
1325 
1326     // <Get Channel Cipher Suites Command>
1327     ipmi_register_callback(NETFUN_APP, IPMI_CMD_GET_CHAN_CIPHER_SUITES, NULL,
1328                            getChannelCipherSuites, PRIVILEGE_CALLBACK);
1329 
1330     // <Get System Info Command>
1331     ipmi_register_callback(NETFUN_APP, IPMI_CMD_GET_SYSTEM_INFO, NULL,
1332                            ipmi_app_get_system_info, PRIVILEGE_USER);
1333     return;
1334 }
1335