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 static constexpr uint8_t revisionOnly = 0x80;
859 static constexpr uint8_t paramRevision = 0x11;
860 static constexpr size_t configParameterLength = 16;
861 
862 static constexpr size_t smallChunkSize = 14;
863 static constexpr size_t fullChunkSize = 16;
864 
865 static constexpr uint8_t setComplete = 0x0;
866 static constexpr uint8_t setInProgress = 0x1;
867 static constexpr uint8_t commitWrite = 0x2;
868 static uint8_t transferStatus = setComplete;
869 
870 namespace ipmi
871 {
872 constexpr Cc ccParmNotSupported = 0x80;
873 
874 static inline auto responseParmNotSupported()
875 {
876     return response(ccParmNotSupported);
877 }
878 } // namespace ipmi
879 
880 ipmi::RspType<
881     uint8_t,                // Parameter revision
882     std::optional<uint8_t>, // data1 / setSelector / ProgressStatus
883     std::optional<std::array<uint8_t, configParameterLength>>> // data2-17
884     ipmiAppGetSystemInfo(uint8_t getRevision, uint8_t paramSelector,
885                          uint8_t setSelector, uint8_t BlockSelector)
886 {
887     if (getRevision & revisionOnly)
888     {
889         return ipmi::responseSuccess(paramRevision, std::nullopt, std::nullopt);
890     }
891 
892     if (paramSelector == 0)
893     {
894         return ipmi::responseSuccess(paramRevision, transferStatus,
895                                      std::nullopt);
896     }
897 
898     if (BlockSelector != 0) // 00h if parameter does not require a block number
899     {
900         return ipmi::responseParmNotSupported();
901     }
902 
903     if (sysInfoParamStore == nullptr)
904     {
905         sysInfoParamStore = std::make_unique<SysInfoParamStore>();
906         sysInfoParamStore->update(IPMI_SYSINFO_SYSTEM_NAME,
907                                   sysInfoReadSystemName);
908     }
909 
910     // Parameters other than Set In Progress are assumed to be strings.
911     std::tuple<bool, std::string> ret =
912         sysInfoParamStore->lookup(paramSelector);
913     bool found = std::get<0>(ret);
914     if (!found)
915     {
916         return ipmi::responseParmNotSupported();
917     }
918     std::string& paramString = std::get<1>(ret);
919     std::array<uint8_t, configParameterLength> configData;
920     size_t count = 0;
921     if (setSelector == 0)
922     {                         // First chunk has only 14 bytes.
923         configData.at(0) = 0; // encoding
924         configData.at(1) = paramString.length(); // string length
925         count = (paramString.length() > smallChunkSize) ? smallChunkSize
926                                                         : paramString.length();
927         std::copy_n(paramString.begin(), count,
928                     configData.begin() + 2); // 14 bytes thunk
929     }
930     else
931     {
932         size_t offset = (setSelector * fullChunkSize) - 2;
933         if (offset >= paramString.length())
934         {
935             return ipmi::responseParmOutOfRange();
936         }
937         count = ((paramString.length() - offset) > fullChunkSize)
938                     ? fullChunkSize
939                     : (paramString.length() - offset);
940         std::copy_n(paramString.begin() + offset, count,
941                     configData.begin()); // 16 bytes chunk
942     }
943     return ipmi::responseSuccess(paramRevision, setSelector, configData);
944 }
945 
946 #ifdef ENABLE_I2C_WHITELIST_CHECK
947 inline std::vector<uint8_t> convertStringToData(const std::string& command)
948 {
949     std::istringstream iss(command);
950     std::string token;
951     std::vector<uint8_t> dataValue;
952     while (std::getline(iss, token, ' '))
953     {
954         dataValue.emplace_back(
955             static_cast<uint8_t>(std::stoul(token, nullptr, base_16)));
956     }
957     return dataValue;
958 }
959 
960 static bool populateI2CMasterWRWhitelist()
961 {
962     nlohmann::json data = nullptr;
963     std::ifstream jsonFile(i2cMasterWRWhitelistFile);
964 
965     if (!jsonFile.good())
966     {
967         log<level::WARNING>("i2c white list file not found!",
968                             entry("FILE_NAME: %s", i2cMasterWRWhitelistFile));
969         return false;
970     }
971 
972     try
973     {
974         data = nlohmann::json::parse(jsonFile, nullptr, false);
975     }
976     catch (nlohmann::json::parse_error& e)
977     {
978         log<level::ERR>("Corrupted i2c white list config file",
979                         entry("FILE_NAME: %s", i2cMasterWRWhitelistFile),
980                         entry("MSG: %s", e.what()));
981         return false;
982     }
983 
984     try
985     {
986         // Example JSON Structure format
987         // "filters": [
988         //    {
989         //      "Description": "Allow full read - ignore first byte write value
990         //      for 0x40 to 0x4F",
991         //      "busId": "0x01",
992         //      "slaveAddr": "0x40",
993         //      "slaveAddrMask": "0x0F",
994         //      "command": "0x00",
995         //      "commandMask": "0xFF"
996         //    },
997         //    {
998         //      "Description": "Allow full read - first byte match 0x05 and
999         //      ignore second byte",
1000         //      "busId": "0x01",
1001         //      "slaveAddr": "0x57",
1002         //      "slaveAddrMask": "0x00",
1003         //      "command": "0x05 0x00",
1004         //      "commandMask": "0x00 0xFF"
1005         //    },]
1006 
1007         nlohmann::json filters = data[filtersStr].get<nlohmann::json>();
1008         std::vector<i2cMasterWRWhitelist>& whitelist = getWRWhitelist();
1009         for (const auto& it : filters.items())
1010         {
1011             nlohmann::json filter = it.value();
1012             if (filter.is_null())
1013             {
1014                 log<level::ERR>(
1015                     "Corrupted I2C master write read whitelist config file",
1016                     entry("FILE_NAME: %s", i2cMasterWRWhitelistFile));
1017                 return false;
1018             }
1019             const std::vector<uint8_t>& writeData =
1020                 convertStringToData(filter[cmdStr].get<std::string>());
1021             const std::vector<uint8_t>& writeDataMask =
1022                 convertStringToData(filter[cmdMaskStr].get<std::string>());
1023             if (writeDataMask.size() != writeData.size())
1024             {
1025                 log<level::ERR>("I2C master write read whitelist filter "
1026                                 "mismatch for command & mask size");
1027                 return false;
1028             }
1029             whitelist.push_back(
1030                 {static_cast<uint8_t>(std::stoul(
1031                      filter[busIdStr].get<std::string>(), nullptr, base_16)),
1032                  static_cast<uint8_t>(
1033                      std::stoul(filter[slaveAddrStr].get<std::string>(),
1034                                 nullptr, base_16)),
1035                  static_cast<uint8_t>(
1036                      std::stoul(filter[slaveAddrMaskStr].get<std::string>(),
1037                                 nullptr, base_16)),
1038                  writeData, writeDataMask});
1039         }
1040         if (whitelist.size() != filters.size())
1041         {
1042             log<level::ERR>(
1043                 "I2C master write read whitelist filter size mismatch");
1044             return false;
1045         }
1046     }
1047     catch (std::exception& e)
1048     {
1049         log<level::ERR>("I2C master write read whitelist unexpected exception",
1050                         entry("ERROR=%s", e.what()));
1051         return false;
1052     }
1053     return true;
1054 }
1055 
1056 static inline bool isWriteDataWhitelisted(const std::vector<uint8_t>& data,
1057                                           const std::vector<uint8_t>& dataMask,
1058                                           const std::vector<uint8_t>& writeData)
1059 {
1060     std::vector<uint8_t> processedDataBuf(data.size());
1061     std::vector<uint8_t> processedReqBuf(dataMask.size());
1062     std::transform(writeData.begin(), writeData.end(), dataMask.begin(),
1063                    processedReqBuf.begin(), std::bit_or<uint8_t>());
1064     std::transform(data.begin(), data.end(), dataMask.begin(),
1065                    processedDataBuf.begin(), std::bit_or<uint8_t>());
1066 
1067     return (processedDataBuf == processedReqBuf);
1068 }
1069 
1070 static bool isCmdWhitelisted(uint8_t busId, uint8_t slaveAddr,
1071                              std::vector<uint8_t>& writeData)
1072 {
1073     std::vector<i2cMasterWRWhitelist>& whiteList = getWRWhitelist();
1074     for (const auto& wlEntry : whiteList)
1075     {
1076         if ((busId == wlEntry.busId) &&
1077             ((slaveAddr | wlEntry.slaveAddrMask) ==
1078              (wlEntry.slaveAddr | wlEntry.slaveAddrMask)))
1079         {
1080             const std::vector<uint8_t>& dataMask = wlEntry.dataMask;
1081             // Skip as no-match, if requested write data is more than the
1082             // write data mask size
1083             if (writeData.size() > dataMask.size())
1084             {
1085                 continue;
1086             }
1087             if (isWriteDataWhitelisted(wlEntry.data, dataMask, writeData))
1088             {
1089                 return true;
1090             }
1091         }
1092     }
1093     return false;
1094 }
1095 #else
1096 static bool populateI2CMasterWRWhitelist()
1097 {
1098     log<level::INFO>(
1099         "I2C_WHITELIST_CHECK is disabled, do not populate whitelist");
1100     return true;
1101 }
1102 #endif // ENABLE_I2C_WHITELIST_CHECK
1103 
1104 /** @brief implements master write read IPMI command which can be used for
1105  * low-level I2C/SMBus write, read or write-read access
1106  *  @param isPrivateBus -to indicate private bus usage
1107  *  @param busId - bus id
1108  *  @param channelNum - channel number
1109  *  @param reserved - skip 1 bit
1110  *  @param slaveAddr - slave address
1111  *  @param read count - number of bytes to be read
1112  *  @param writeData - data to be written
1113  *
1114  *  @returns IPMI completion code plus response data
1115  *   - readData - i2c response data
1116  */
1117 ipmi::RspType<std::vector<uint8_t>>
1118     ipmiMasterWriteRead(bool isPrivateBus, uint3_t busId, uint4_t channelNum,
1119                         bool reserved, uint7_t slaveAddr, uint8_t readCount,
1120                         std::vector<uint8_t> writeData)
1121 {
1122     if (readCount > maxIPMIWriteReadSize)
1123     {
1124         log<level::ERR>("Master write read command: Read count exceeds limit");
1125         return ipmi::responseParmOutOfRange();
1126     }
1127     const size_t writeCount = writeData.size();
1128     if (!readCount && !writeCount)
1129     {
1130         log<level::ERR>("Master write read command: Read & write count are 0");
1131         return ipmi::responseInvalidFieldRequest();
1132     }
1133 #ifdef ENABLE_I2C_WHITELIST_CHECK
1134     if (!isCmdWhitelisted(static_cast<uint8_t>(busId),
1135                           static_cast<uint8_t>(slaveAddr), writeData))
1136     {
1137         log<level::ERR>("Master write read request blocked!",
1138                         entry("BUS=%d", static_cast<uint8_t>(busId)),
1139                         entry("ADDR=0x%x", static_cast<uint8_t>(slaveAddr)));
1140         return ipmi::responseInvalidFieldRequest();
1141     }
1142 #endif // ENABLE_I2C_WHITELIST_CHECK
1143     std::vector<uint8_t> readBuf(readCount);
1144     std::string i2cBus =
1145         "/dev/i2c-" + std::to_string(static_cast<uint8_t>(busId));
1146 
1147     ipmi::Cc ret = ipmi::i2cWriteRead(i2cBus, static_cast<uint8_t>(slaveAddr),
1148                                       writeData, readBuf);
1149     if (ret != ipmi::ccSuccess)
1150     {
1151         return ipmi::response(ret);
1152     }
1153     return ipmi::responseSuccess(readBuf);
1154 }
1155 
1156 void register_netfn_app_functions()
1157 {
1158     // <Get Device ID>
1159     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp,
1160                           ipmi::app::cmdGetDeviceId, ipmi::Privilege::User,
1161                           ipmiAppGetDeviceId);
1162 
1163     // <Get BT Interface Capabilities>
1164     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp,
1165                           ipmi::app::cmdGetBtIfaceCapabilities,
1166                           ipmi::Privilege::User, ipmiAppGetBtCapabilities);
1167 
1168     // <Reset Watchdog Timer>
1169     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp,
1170                           ipmi::app::cmdResetWatchdogTimer,
1171                           ipmi::Privilege::Operator, ipmiAppResetWatchdogTimer);
1172 
1173     // <Set Watchdog Timer>
1174     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp,
1175                           ipmi::app::cmdSetWatchdogTimer,
1176                           ipmi::Privilege::Operator, ipmiSetWatchdogTimer);
1177 
1178     // <Get Watchdog Timer>
1179     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp,
1180                           ipmi::app::cmdGetWatchdogTimer,
1181                           ipmi::Privilege::Operator, ipmiGetWatchdogTimer);
1182 
1183     // <Get Self Test Results>
1184     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp,
1185                           ipmi::app::cmdGetSelfTestResults,
1186                           ipmi::Privilege::User, ipmiAppGetSelfTestResults);
1187 
1188     // <Get Device GUID>
1189     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp,
1190                           ipmi::app::cmdGetDeviceGuid, ipmi::Privilege::User,
1191                           ipmiAppGetDeviceGuid);
1192 
1193     // <Set ACPI Power State>
1194     ipmi_register_callback(NETFUN_APP, IPMI_CMD_SET_ACPI, NULL,
1195                            ipmi_app_set_acpi_power_state, PRIVILEGE_ADMIN);
1196 
1197     // <Get ACPI Power State>
1198     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp,
1199                           ipmi::app::cmdGetAcpiPowerState,
1200                           ipmi::Privilege::Admin, ipmiGetAcpiPowerState);
1201 
1202     // Note: For security reason, this command will be registered only when
1203     // there are proper I2C Master write read whitelist
1204     if (populateI2CMasterWRWhitelist())
1205     {
1206         // Note: For security reasons, registering master write read as admin
1207         // privilege command, even though IPMI 2.0 specification allows it as
1208         // operator privilege.
1209         ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp,
1210                               ipmi::app::cmdMasterWriteRead,
1211                               ipmi::Privilege::Admin, ipmiMasterWriteRead);
1212     }
1213 
1214     // <Get System GUID Command>
1215     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp,
1216                           ipmi::app::cmdGetSystemGuid, ipmi::Privilege::User,
1217                           ipmiAppGetSystemGuid);
1218 
1219     // <Get Channel Cipher Suites Command>
1220     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp,
1221                           ipmi::app::cmdGetChannelCipherSuites,
1222                           ipmi::Privilege::Callback, getChannelCipherSuites);
1223 
1224     // <Get System Info Command>
1225     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp,
1226                           ipmi::app::cmdGetSystemInfoParameters,
1227                           ipmi::Privilege::User, ipmiAppGetSystemInfo);
1228     return;
1229 }
1230