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 ipmi_ret_t ipmi_app_get_device_guid(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
704                                     ipmi_request_t request,
705                                     ipmi_response_t response,
706                                     ipmi_data_len_t data_len,
707                                     ipmi_context_t context)
708 {
709     const char* objname = "/org/openbmc/control/chassis0";
710     const char* iface = "org.freedesktop.DBus.Properties";
711     const char* chassis_iface = "org.openbmc.control.Chassis";
712     sd_bus_message* reply = NULL;
713     sd_bus_error error = SD_BUS_ERROR_NULL;
714     int r = 0;
715     char* uuid = NULL;
716     char* busname = NULL;
717 
718     // UUID is in RFC4122 format. Ex: 61a39523-78f2-11e5-9862-e6402cfc3223
719     // Per IPMI Spec 2.0 need to convert to 16 hex bytes and reverse the byte
720     // order
721     // Ex: 0x2332fc2c40e66298e511f2782395a361
722 
723     const int resp_size = 16;     // Response is 16 hex bytes per IPMI Spec
724     uint8_t resp_uuid[resp_size]; // Array to hold the formatted response
725     // Point resp end of array to save in reverse order
726     int resp_loc = resp_size - 1;
727     int i = 0;
728     char* tokptr = NULL;
729     char* id_octet = NULL;
730     size_t total_uuid_size = 0;
731     // 1 byte of resp is built from 2 chars of uuid.
732     constexpr size_t max_uuid_size = 2 * resp_size;
733 
734     // Status code.
735     ipmi_ret_t rc = IPMI_CC_OK;
736     *data_len = 0;
737 
738     // Call Get properties method with the interface and property name
739     r = mapper_get_service(bus, objname, &busname);
740     if (r < 0)
741     {
742         log<level::ERR>("Failed to get bus name", entry("BUS=%s", objname),
743                         entry("ERRNO=0x%X", -r));
744         goto finish;
745     }
746     r = sd_bus_call_method(bus, busname, objname, iface, "Get", &error, &reply,
747                            "ss", chassis_iface, "uuid");
748     if (r < 0)
749     {
750         log<level::ERR>("Failed to call Get Method", entry("ERRNO=0x%X", -r));
751         rc = IPMI_CC_UNSPECIFIED_ERROR;
752         goto finish;
753     }
754 
755     r = sd_bus_message_read(reply, "v", "s", &uuid);
756     if (r < 0 || uuid == NULL)
757     {
758         log<level::ERR>("Failed to get a response", entry("ERRNO=0x%X", -r));
759         rc = IPMI_CC_RESPONSE_ERROR;
760         goto finish;
761     }
762 
763     // Traverse the UUID
764     // Get the UUID octects separated by dash
765     id_octet = strtok_r(uuid, "-", &tokptr);
766 
767     if (id_octet == NULL)
768     {
769         // Error
770         log<level::ERR>("Unexpected UUID format", entry("UUID=%s", uuid));
771         rc = IPMI_CC_RESPONSE_ERROR;
772         goto finish;
773     }
774 
775     while (id_octet != NULL)
776     {
777         // Calculate the octet string size since it varies
778         // Divide it by 2 for the array size since 1 byte is built from 2 chars
779         int tmp_size = strlen(id_octet) / 2;
780 
781         // Check if total UUID size has been exceeded
782         if ((total_uuid_size += strlen(id_octet)) > max_uuid_size)
783         {
784             // Error - UUID too long to store
785             log<level::ERR>("UUID too long", entry("UUID=%s", uuid));
786             rc = IPMI_CC_RESPONSE_ERROR;
787             goto finish;
788         }
789 
790         for (i = 0; i < tmp_size; i++)
791         {
792             // Holder of the 2 chars that will become a byte
793             char tmp_array[3] = {0};
794             strncpy(tmp_array, id_octet, 2); // 2 chars at a time
795 
796             int resp_byte = strtoul(tmp_array, NULL, 16); // Convert to hex byte
797             // Copy end to first
798             std::memcpy((void*)&resp_uuid[resp_loc], &resp_byte, 1);
799             resp_loc--;
800             id_octet += 2; // Finished with the 2 chars, advance
801         }
802         id_octet = strtok_r(NULL, "-", &tokptr); // Get next octet
803     }
804 
805     // Data length
806     *data_len = resp_size;
807 
808     // Pack the actual response
809     std::memcpy(response, &resp_uuid, *data_len);
810 
811 finish:
812     sd_bus_error_free(&error);
813     reply = sd_bus_message_unref(reply);
814     free(busname);
815 
816     return rc;
817 }
818 
819 auto ipmiAppGetBtCapabilities()
820     -> ipmi::RspType<uint8_t, uint8_t, uint8_t, uint8_t, uint8_t>
821 {
822     // Per IPMI 2.0 spec, the input and output buffer size must be the max
823     // buffer size minus one byte to allocate space for the length byte.
824     constexpr uint8_t nrOutstanding = 0x01;
825     constexpr uint8_t inputBufferSize = MAX_IPMI_BUFFER - 1;
826     constexpr uint8_t outputBufferSize = MAX_IPMI_BUFFER - 1;
827     constexpr uint8_t transactionTime = 0x0A;
828     constexpr uint8_t nrRetries = 0x01;
829 
830     return ipmi::responseSuccess(nrOutstanding, inputBufferSize,
831                                  outputBufferSize, transactionTime, nrRetries);
832 }
833 
834 ipmi_ret_t ipmi_app_get_sys_guid(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
835                                  ipmi_request_t request,
836                                  ipmi_response_t response,
837                                  ipmi_data_len_t data_len,
838                                  ipmi_context_t context)
839 
840 {
841     ipmi_ret_t rc = IPMI_CC_OK;
842     sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()};
843 
844     try
845     {
846         // Get the Inventory object implementing BMC interface
847         ipmi::DbusObjectInfo bmcObject =
848             ipmi::getDbusObject(bus, bmc_interface);
849 
850         // Read UUID property value from bmcObject
851         // UUID is in RFC4122 format Ex: 61a39523-78f2-11e5-9862-e6402cfc3223
852         auto variant =
853             ipmi::getDbusProperty(bus, bmcObject.second, bmcObject.first,
854                                   bmc_guid_interface, bmc_guid_property);
855         std::string guidProp = std::get<std::string>(variant);
856 
857         // Erase "-" characters from the property value
858         guidProp.erase(std::remove(guidProp.begin(), guidProp.end(), '-'),
859                        guidProp.end());
860 
861         auto guidPropLen = guidProp.length();
862         // Validate UUID data
863         // Divide by 2 as 1 byte is built from 2 chars
864         if ((guidPropLen <= 0) || ((guidPropLen / 2) != bmc_guid_len))
865 
866         {
867             log<level::ERR>("Invalid UUID property value",
868                             entry("UUID_LENGTH=%d", guidPropLen));
869             return IPMI_CC_RESPONSE_ERROR;
870         }
871 
872         // Convert data in RFC4122(MSB) format to LSB format
873         // Get 2 characters at a time as 1 byte is built from 2 chars and
874         // convert to hex byte
875         // TODO: Data printed for GUID command is not as per the
876         // GUID format defined in IPMI specification 2.0 section 20.8
877         // Ticket raised: https://sourceforge.net/p/ipmitool/bugs/501/
878         uint8_t respGuid[bmc_guid_len];
879         for (size_t i = 0, respLoc = (bmc_guid_len - 1);
880              i < guidPropLen && respLoc >= 0; i += 2, respLoc--)
881         {
882             auto value = static_cast<uint8_t>(
883                 std::stoi(guidProp.substr(i, 2).c_str(), NULL, 16));
884             respGuid[respLoc] = value;
885         }
886 
887         *data_len = bmc_guid_len;
888         std::memcpy(response, &respGuid, bmc_guid_len);
889     }
890     catch (const InternalFailure& e)
891     {
892         log<level::ERR>("Failed in reading BMC UUID property",
893                         entry("INTERFACE=%s", bmc_interface),
894                         entry("PROPERTY_INTERFACE=%s", bmc_guid_interface),
895                         entry("PROPERTY=%s", bmc_guid_property));
896         return IPMI_CC_UNSPECIFIED_ERROR;
897     }
898     return rc;
899 }
900 
901 static std::unique_ptr<SysInfoParamStore> sysInfoParamStore;
902 
903 static std::string sysInfoReadSystemName()
904 {
905     // Use the BMC hostname as the "System Name."
906     char hostname[HOST_NAME_MAX + 1] = {};
907     if (gethostname(hostname, HOST_NAME_MAX) != 0)
908     {
909         perror("System info parameter: system name");
910     }
911     return hostname;
912 }
913 
914 struct IpmiSysInfoResp
915 {
916     uint8_t paramRevision;
917     uint8_t setSelector;
918     union
919     {
920         struct
921         {
922             uint8_t encoding;
923             uint8_t stringLen;
924             uint8_t stringData0[14];
925         } __attribute__((packed));
926         uint8_t stringDataN[16];
927         uint8_t byteData;
928     };
929 } __attribute__((packed));
930 
931 /**
932  * Split a string into (up to) 16-byte chunks as expected in response for get
933  * system info parameter.
934  *
935  * @param[in] fullString: Input string to be split
936  * @param[in] chunkIndex: Index of the chunk to be written out
937  * @param[in,out] chunk: Output data buffer; must have 14 byte capacity if
938  *          chunk_index = 0 and 16-byte capacity otherwise
939  * @return the number of bytes written into the output buffer, or -EINVAL for
940  * invalid arguments.
941  */
942 static int splitStringParam(const std::string& fullString, int chunkIndex,
943                             uint8_t* chunk)
944 {
945     constexpr int maxChunk = 255;
946     constexpr int smallChunk = 14;
947     constexpr int chunkSize = 16;
948     if (chunkIndex > maxChunk || chunk == nullptr)
949     {
950         return -EINVAL;
951     }
952     try
953     {
954         std::string output;
955         if (chunkIndex == 0)
956         {
957             // Output must have 14 byte capacity.
958             output = fullString.substr(0, smallChunk);
959         }
960         else
961         {
962             // Output must have 16 byte capacity.
963             output = fullString.substr((chunkIndex * chunkSize) - 2, chunkSize);
964         }
965 
966         std::memcpy(chunk, output.c_str(), output.length());
967         return output.length();
968     }
969     catch (const std::out_of_range& e)
970     {
971         // The position was beyond the end.
972         return -EINVAL;
973     }
974 }
975 
976 /**
977  * Packs the Get Sys Info Request Item into the response.
978  *
979  * @param[in] paramString - the parameter.
980  * @param[in] setSelector - the selector
981  * @param[in,out] resp - the System info response.
982  * @return The number of bytes packed or failure from splitStringParam().
983  */
984 static int packGetSysInfoResp(const std::string& paramString,
985                               uint8_t setSelector, IpmiSysInfoResp* resp)
986 {
987     uint8_t* dataBuffer = resp->stringDataN;
988     resp->setSelector = setSelector;
989     if (resp->setSelector == 0) // First chunk has only 14 bytes.
990     {
991         resp->encoding = 0;
992         resp->stringLen = paramString.length();
993         dataBuffer = resp->stringData0;
994     }
995     return splitStringParam(paramString, resp->setSelector, dataBuffer);
996 }
997 
998 ipmi_ret_t ipmi_app_get_system_info(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
999                                     ipmi_request_t request,
1000                                     ipmi_response_t response,
1001                                     ipmi_data_len_t dataLen,
1002                                     ipmi_context_t context)
1003 {
1004     IpmiSysInfoResp resp = {};
1005     size_t respLen = 0;
1006     uint8_t* const reqData = static_cast<uint8_t*>(request);
1007     std::string paramString;
1008     bool found;
1009     std::tuple<bool, std::string> ret;
1010     constexpr int minRequestSize = 4;
1011     constexpr int paramSelector = 1;
1012     constexpr uint8_t revisionOnly = 0x80;
1013     const uint8_t paramRequested = reqData[paramSelector];
1014     int rc;
1015 
1016     if (*dataLen < minRequestSize)
1017     {
1018         return IPMI_CC_REQ_DATA_LEN_INVALID;
1019     }
1020 
1021     *dataLen = 0; // default to 0.
1022 
1023     // Parameters revision as of IPMI spec v2.0 rev. 1.1 (Feb 11, 2014 E6)
1024     resp.paramRevision = 0x11;
1025     if (reqData[0] & revisionOnly) // Get parameter revision only
1026     {
1027         respLen = 1;
1028         goto writeResponse;
1029     }
1030 
1031     // The "Set In Progress" parameter can be used for rollback of parameter
1032     // data and is not implemented.
1033     if (paramRequested == 0)
1034     {
1035         resp.byteData = 0;
1036         respLen = 2;
1037         goto writeResponse;
1038     }
1039 
1040     if (sysInfoParamStore == nullptr)
1041     {
1042         sysInfoParamStore = std::make_unique<SysInfoParamStore>();
1043         sysInfoParamStore->update(IPMI_SYSINFO_SYSTEM_NAME,
1044                                   sysInfoReadSystemName);
1045     }
1046 
1047     // Parameters other than Set In Progress are assumed to be strings.
1048     ret = sysInfoParamStore->lookup(paramRequested);
1049     found = std::get<0>(ret);
1050     paramString = std::get<1>(ret);
1051     if (!found)
1052     {
1053         return IPMI_CC_SYSTEM_INFO_PARAMETER_NOT_SUPPORTED;
1054     }
1055     // TODO: Cache each parameter across multiple calls, until the whole string
1056     // has been read out. Otherwise, it's possible for a parameter to change
1057     // between requests for its chunks, returning chunks incoherent with each
1058     // other. For now, the parameter store is simply required to have only
1059     // idempotent callbacks.
1060     rc = packGetSysInfoResp(paramString, reqData[2], &resp);
1061     if (rc == -EINVAL)
1062     {
1063         return IPMI_CC_RESPONSE_ERROR;
1064     }
1065 
1066     respLen = sizeof(resp); // Write entire string data chunk in response.
1067 
1068 writeResponse:
1069     std::memcpy(response, &resp, sizeof(resp));
1070     *dataLen = respLen;
1071     return IPMI_CC_OK;
1072 }
1073 
1074 inline std::vector<uint8_t> convertStringToData(const std::string& command)
1075 {
1076     std::istringstream iss(command);
1077     std::string token;
1078     std::vector<uint8_t> dataValue;
1079     while (std::getline(iss, token, ' '))
1080     {
1081         dataValue.emplace_back(
1082             static_cast<uint8_t>(std::stoul(token, nullptr, base_16)));
1083     }
1084     return dataValue;
1085 }
1086 
1087 static bool populateI2CMasterWRWhitelist()
1088 {
1089     nlohmann::json data = nullptr;
1090     std::ifstream jsonFile(i2cMasterWRWhitelistFile);
1091 
1092     if (!jsonFile.good())
1093     {
1094         log<level::WARNING>("i2c white list file not found!",
1095                             entry("FILE_NAME: %s", i2cMasterWRWhitelistFile));
1096         return false;
1097     }
1098 
1099     try
1100     {
1101         data = nlohmann::json::parse(jsonFile, nullptr, false);
1102     }
1103     catch (nlohmann::json::parse_error& e)
1104     {
1105         log<level::ERR>("Corrupted i2c white list config file",
1106                         entry("FILE_NAME: %s", i2cMasterWRWhitelistFile),
1107                         entry("MSG: %s", e.what()));
1108         return false;
1109     }
1110 
1111     try
1112     {
1113         // Example JSON Structure format
1114         // "filters": [
1115         //    {
1116         //      "Description": "Allow full read - ignore first byte write value
1117         //      for 0x40 to 0x4F",
1118         //      "busId": "0x01",
1119         //      "slaveAddr": "0x40",
1120         //      "slaveAddrMask": "0x0F",
1121         //      "command": "0x00",
1122         //      "commandMask": "0xFF"
1123         //    },
1124         //    {
1125         //      "Description": "Allow full read - first byte match 0x05 and
1126         //      ignore second byte",
1127         //      "busId": "0x01",
1128         //      "slaveAddr": "0x57",
1129         //      "slaveAddrMask": "0x00",
1130         //      "command": "0x05 0x00",
1131         //      "commandMask": "0x00 0xFF"
1132         //    },]
1133 
1134         nlohmann::json filters = data[filtersStr].get<nlohmann::json>();
1135         std::vector<i2cMasterWRWhitelist>& whitelist = getWRWhitelist();
1136         for (const auto& it : filters.items())
1137         {
1138             nlohmann::json filter = it.value();
1139             if (filter.is_null())
1140             {
1141                 log<level::ERR>(
1142                     "Corrupted I2C master write read whitelist config file",
1143                     entry("FILE_NAME: %s", i2cMasterWRWhitelistFile));
1144                 return false;
1145             }
1146             const std::vector<uint8_t>& writeData =
1147                 convertStringToData(filter[cmdStr].get<std::string>());
1148             const std::vector<uint8_t>& writeDataMask =
1149                 convertStringToData(filter[cmdMaskStr].get<std::string>());
1150             if (writeDataMask.size() != writeData.size())
1151             {
1152                 log<level::ERR>("I2C master write read whitelist filter "
1153                                 "mismatch for command & mask size");
1154                 return false;
1155             }
1156             whitelist.push_back(
1157                 {static_cast<uint8_t>(std::stoul(
1158                      filter[busIdStr].get<std::string>(), nullptr, base_16)),
1159                  static_cast<uint8_t>(
1160                      std::stoul(filter[slaveAddrStr].get<std::string>(),
1161                                 nullptr, base_16)),
1162                  static_cast<uint8_t>(
1163                      std::stoul(filter[slaveAddrMaskStr].get<std::string>(),
1164                                 nullptr, base_16)),
1165                  writeData, writeDataMask});
1166         }
1167         if (whitelist.size() != filters.size())
1168         {
1169             log<level::ERR>(
1170                 "I2C master write read whitelist filter size mismatch");
1171             return false;
1172         }
1173     }
1174     catch (std::exception& e)
1175     {
1176         log<level::ERR>("I2C master write read whitelist unexpected exception",
1177                         entry("ERROR=%s", e.what()));
1178         return false;
1179     }
1180     return true;
1181 }
1182 
1183 static inline bool isWriteDataWhitelisted(const std::vector<uint8_t>& data,
1184                                           const std::vector<uint8_t>& dataMask,
1185                                           const std::vector<uint8_t>& writeData)
1186 {
1187     std::vector<uint8_t> processedDataBuf(data.size());
1188     std::vector<uint8_t> processedReqBuf(dataMask.size());
1189     std::transform(writeData.begin(), writeData.end(), dataMask.begin(),
1190                    processedReqBuf.begin(), std::bit_or<uint8_t>());
1191     std::transform(data.begin(), data.end(), dataMask.begin(),
1192                    processedDataBuf.begin(), std::bit_or<uint8_t>());
1193 
1194     return (processedDataBuf == processedReqBuf);
1195 }
1196 
1197 static bool isCmdWhitelisted(uint8_t busId, uint8_t slaveAddr,
1198                              std::vector<uint8_t>& writeData)
1199 {
1200     std::vector<i2cMasterWRWhitelist>& whiteList = getWRWhitelist();
1201     for (const auto& wlEntry : whiteList)
1202     {
1203         if ((busId == wlEntry.busId) &&
1204             ((slaveAddr | wlEntry.slaveAddrMask) ==
1205              (wlEntry.slaveAddr | wlEntry.slaveAddrMask)))
1206         {
1207             const std::vector<uint8_t>& dataMask = wlEntry.dataMask;
1208             // Skip as no-match, if requested write data is more than the
1209             // write data mask size
1210             if (writeData.size() > dataMask.size())
1211             {
1212                 continue;
1213             }
1214             if (isWriteDataWhitelisted(wlEntry.data, dataMask, writeData))
1215             {
1216                 return true;
1217             }
1218         }
1219     }
1220     return false;
1221 }
1222 
1223 /** @brief implements master write read IPMI command which can be used for
1224  * low-level I2C/SMBus write, read or write-read access
1225  *  @param isPrivateBus -to indicate private bus usage
1226  *  @param busId - bus id
1227  *  @param channelNum - channel number
1228  *  @param reserved - skip 1 bit
1229  *  @param slaveAddr - slave address
1230  *  @param read count - number of bytes to be read
1231  *  @param writeData - data to be written
1232  *
1233  *  @returns IPMI completion code plus response data
1234  *   - readData - i2c response data
1235  */
1236 ipmi::RspType<std::vector<uint8_t>>
1237     ipmiMasterWriteRead(bool isPrivateBus, uint3_t busId, uint4_t channelNum,
1238                         bool reserved, uint7_t slaveAddr, uint8_t readCount,
1239                         std::vector<uint8_t> writeData)
1240 {
1241     i2c_rdwr_ioctl_data msgReadWrite = {0};
1242     i2c_msg i2cmsg[2] = {0};
1243 
1244     if (readCount > maxIPMIWriteReadSize)
1245     {
1246         log<level::ERR>("Master write read command: Read count exceeds limit");
1247         return ipmi::responseParmOutOfRange();
1248     }
1249     const size_t writeCount = writeData.size();
1250     if (!readCount && !writeCount)
1251     {
1252         log<level::ERR>("Master write read command: Read & write count are 0");
1253         return ipmi::responseInvalidFieldRequest();
1254     }
1255     if (!isCmdWhitelisted(static_cast<uint8_t>(busId),
1256                           static_cast<uint8_t>(slaveAddr), writeData))
1257     {
1258         log<level::ERR>("Master write read request blocked!",
1259                         entry("BUS=%d", static_cast<uint8_t>(busId)),
1260                         entry("ADDR=0x%x", static_cast<uint8_t>(slaveAddr)));
1261         return ipmi::responseInvalidFieldRequest();
1262     }
1263     std::vector<uint8_t> readBuf(readCount);
1264     std::string i2cBus =
1265         "/dev/i2c-" + std::to_string(static_cast<uint8_t>(busId));
1266 
1267     int i2cDev = ::open(i2cBus.c_str(), O_RDWR | O_CLOEXEC);
1268     if (i2cDev < 0)
1269     {
1270         log<level::ERR>("Failed to open i2c bus",
1271                         entry("BUS=%s", i2cBus.c_str()));
1272         return ipmi::responseInvalidFieldRequest();
1273     }
1274 
1275     int msgCount = 0;
1276     if (writeCount)
1277     {
1278         i2cmsg[msgCount].addr = static_cast<uint8_t>(slaveAddr);
1279         i2cmsg[msgCount].flags = 0x00;
1280         i2cmsg[msgCount].len = writeCount;
1281         i2cmsg[msgCount].buf = writeData.data();
1282         msgCount++;
1283     }
1284     if (readCount)
1285     {
1286         i2cmsg[msgCount].addr = static_cast<uint8_t>(slaveAddr);
1287         i2cmsg[msgCount].flags = I2C_M_RD;
1288         i2cmsg[msgCount].len = readCount;
1289         i2cmsg[msgCount].buf = readBuf.data();
1290         msgCount++;
1291     }
1292 
1293     msgReadWrite.msgs = i2cmsg;
1294     msgReadWrite.nmsgs = msgCount;
1295 
1296     int ret = ::ioctl(i2cDev, I2C_RDWR, &msgReadWrite);
1297     ::close(i2cDev);
1298 
1299     if (ret < 0)
1300     {
1301         log<level::ERR>("Master write read: Failed", entry("RET=%d", ret));
1302         return ipmi::responseUnspecifiedError();
1303     }
1304     if (readCount)
1305     {
1306         readBuf.resize(msgReadWrite.msgs[msgCount - 1].len);
1307     }
1308     return ipmi::responseSuccess(readBuf);
1309 }
1310 
1311 void register_netfn_app_functions()
1312 {
1313     // <Get Device ID>
1314     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp,
1315                           ipmi::app::cmdGetDeviceId, ipmi::Privilege::User,
1316                           ipmiAppGetDeviceId);
1317 
1318     // <Get BT Interface Capabilities>
1319     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp,
1320                           ipmi::app::cmdGetBtIfaceCapabilities,
1321                           ipmi::Privilege::User, ipmiAppGetBtCapabilities);
1322 
1323     // <Reset Watchdog Timer>
1324     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp,
1325                           ipmi::app::cmdResetWatchdogTimer,
1326                           ipmi::Privilege::Operator, ipmiAppResetWatchdogTimer);
1327 
1328     // <Set Watchdog Timer>
1329     ipmi_register_callback(NETFUN_APP, IPMI_CMD_SET_WD, NULL,
1330                            ipmi_app_watchdog_set, PRIVILEGE_OPERATOR);
1331 
1332     // <Get Watchdog Timer>
1333     ipmi_register_callback(NETFUN_APP, IPMI_CMD_GET_WD, NULL,
1334                            ipmi_app_watchdog_get, PRIVILEGE_OPERATOR);
1335 
1336     // <Get Self Test Results>
1337     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp,
1338                           ipmi::app::cmdGetSelfTestResults,
1339                           ipmi::Privilege::User, ipmiAppGetSelfTestResults);
1340 
1341     // <Get Device GUID>
1342     ipmi_register_callback(NETFUN_APP, IPMI_CMD_GET_DEVICE_GUID, NULL,
1343                            ipmi_app_get_device_guid, PRIVILEGE_USER);
1344 
1345     // <Set ACPI Power State>
1346     ipmi_register_callback(NETFUN_APP, IPMI_CMD_SET_ACPI, NULL,
1347                            ipmi_app_set_acpi_power_state, PRIVILEGE_ADMIN);
1348 
1349     // <Get ACPI Power State>
1350     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp,
1351                           ipmi::app::cmdGetAcpiPowerState,
1352                           ipmi::Privilege::Admin, ipmiGetAcpiPowerState);
1353 
1354     // Note: For security reason, this command will be registered only when
1355     // there are proper I2C Master write read whitelist
1356     if (populateI2CMasterWRWhitelist())
1357     {
1358         // Note: For security reasons, registering master write read as admin
1359         // privilege command, even though IPMI 2.0 specification allows it as
1360         // operator privilege.
1361         ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp,
1362                               ipmi::app::cmdMasterWriteRead,
1363                               ipmi::Privilege::Admin, ipmiMasterWriteRead);
1364     }
1365 
1366     // <Get System GUID Command>
1367     ipmi_register_callback(NETFUN_APP, IPMI_CMD_GET_SYS_GUID, NULL,
1368                            ipmi_app_get_sys_guid, PRIVILEGE_USER);
1369 
1370     // <Get Channel Cipher Suites Command>
1371     ipmi_register_callback(NETFUN_APP, IPMI_CMD_GET_CHAN_CIPHER_SUITES, NULL,
1372                            getChannelCipherSuites, PRIVILEGE_CALLBACK);
1373 
1374     // <Get System Info Command>
1375     ipmi_register_callback(NETFUN_APP, IPMI_CMD_GET_SYSTEM_INFO, NULL,
1376                            ipmi_app_get_system_info, PRIVILEGE_USER);
1377     return;
1378 }
1379