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