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/sessiondef.hpp>
24 #include <ipmid/sessionhelper.hpp>
25 #include <ipmid/types.hpp>
26 #include <ipmid/utils.hpp>
27 #include <memory>
28 #include <nlohmann/json.hpp>
29 #include <phosphor-logging/elog-errors.hpp>
30 #include <phosphor-logging/log.hpp>
31 #include <sdbusplus/message/types.hpp>
32 #include <string>
33 #include <sys_info_param.hpp>
34 #include <tuple>
35 #include <vector>
36 #include <xyz/openbmc_project/Common/error.hpp>
37 #include <xyz/openbmc_project/Control/Power/ACPIPowerState/server.hpp>
38 #include <xyz/openbmc_project/Software/Activation/server.hpp>
39 #include <xyz/openbmc_project/Software/Version/server.hpp>
40 #include <xyz/openbmc_project/State/BMC/server.hpp>
41 
42 extern sd_bus* bus;
43 
44 constexpr auto bmc_state_interface = "xyz.openbmc_project.State.BMC";
45 constexpr auto bmc_state_property = "CurrentBMCState";
46 
47 static constexpr auto redundancyIntf =
48     "xyz.openbmc_project.Software.RedundancyPriority";
49 static constexpr auto versionIntf = "xyz.openbmc_project.Software.Version";
50 static constexpr auto activationIntf =
51     "xyz.openbmc_project.Software.Activation";
52 static constexpr auto softwareRoot = "/xyz/openbmc_project/software";
53 
54 void register_netfn_app_functions() __attribute__((constructor));
55 
56 using namespace phosphor::logging;
57 using namespace sdbusplus::xyz::openbmc_project::Common::Error;
58 using Version = sdbusplus::xyz::openbmc_project::Software::server::Version;
59 using Activation =
60     sdbusplus::xyz::openbmc_project::Software::server::Activation;
61 using BMC = sdbusplus::xyz::openbmc_project::State::server::BMC;
62 namespace fs = std::filesystem;
63 
64 #ifdef ENABLE_I2C_WHITELIST_CHECK
65 typedef struct
66 {
67     uint8_t busId;
68     uint8_t slaveAddr;
69     uint8_t slaveAddrMask;
70     std::vector<uint8_t> data;
71     std::vector<uint8_t> dataMask;
72 } i2cMasterWRWhitelist;
73 
74 static std::vector<i2cMasterWRWhitelist>& getWRWhitelist()
75 {
76     static std::vector<i2cMasterWRWhitelist> wrWhitelist;
77     return wrWhitelist;
78 }
79 
80 static constexpr const char* i2cMasterWRWhitelistFile =
81     "/usr/share/ipmi-providers/master_write_read_white_list.json";
82 
83 static constexpr const char* filtersStr = "filters";
84 static constexpr const char* busIdStr = "busId";
85 static constexpr const char* slaveAddrStr = "slaveAddr";
86 static constexpr const char* slaveAddrMaskStr = "slaveAddrMask";
87 static constexpr const char* cmdStr = "command";
88 static constexpr const char* cmdMaskStr = "commandMask";
89 static constexpr int base_16 = 16;
90 #endif // ENABLE_I2C_WHITELIST_CHECK
91 static constexpr uint8_t maxIPMIWriteReadSize = 255;
92 static constexpr uint8_t oemCmdStart = 192;
93 static constexpr uint8_t oemCmdEnd = 255;
94 static constexpr uint8_t invalidParamSelectorStart = 8;
95 static constexpr uint8_t invalidParamSelectorEnd = 191;
96 
97 /**
98  * @brief Returns the Version info from primary s/w object
99  *
100  * Get the Version info from the active s/w object which is having high
101  * "Priority" value(a smaller number is a higher priority) and "Purpose"
102  * is "BMC" from the list of all s/w objects those are implementing
103  * RedundancyPriority interface from the given softwareRoot path.
104  *
105  * @return On success returns the Version info from primary s/w object.
106  *
107  */
108 std::string getActiveSoftwareVersionInfo(ipmi::Context::ptr ctx)
109 {
110     std::string revision{};
111     ipmi::ObjectTree objectTree;
112     try
113     {
114         objectTree =
115             ipmi::getAllDbusObjects(*ctx->bus, softwareRoot, redundancyIntf);
116     }
117     catch (const sdbusplus::exception::exception& e)
118     {
119         log<level::ERR>("Failed to fetch redundancy object from dbus",
120                         entry("INTERFACE=%s", redundancyIntf),
121                         entry("ERRMSG=%s", e.what()));
122         elog<InternalFailure>();
123     }
124 
125     auto objectFound = false;
126     for (auto& softObject : objectTree)
127     {
128         auto service =
129             ipmi::getService(*ctx->bus, redundancyIntf, softObject.first);
130         auto objValueTree =
131             ipmi::getManagedObjects(*ctx->bus, service, softwareRoot);
132 
133         auto minPriority = 0xFF;
134         for (const auto& objIter : objValueTree)
135         {
136             try
137             {
138                 auto& intfMap = objIter.second;
139                 auto& redundancyPriorityProps = intfMap.at(redundancyIntf);
140                 auto& versionProps = intfMap.at(versionIntf);
141                 auto& activationProps = intfMap.at(activationIntf);
142                 auto priority =
143                     std::get<uint8_t>(redundancyPriorityProps.at("Priority"));
144                 auto purpose =
145                     std::get<std::string>(versionProps.at("Purpose"));
146                 auto activation =
147                     std::get<std::string>(activationProps.at("Activation"));
148                 auto version =
149                     std::get<std::string>(versionProps.at("Version"));
150                 if ((Version::convertVersionPurposeFromString(purpose) ==
151                      Version::VersionPurpose::BMC) &&
152                     (Activation::convertActivationsFromString(activation) ==
153                      Activation::Activations::Active))
154                 {
155                     if (priority < minPriority)
156                     {
157                         minPriority = priority;
158                         objectFound = true;
159                         revision = std::move(version);
160                     }
161                 }
162             }
163             catch (const std::exception& e)
164             {
165                 log<level::ERR>(e.what());
166             }
167         }
168     }
169 
170     if (!objectFound)
171     {
172         log<level::ERR>("Could not found an BMC software Object");
173         elog<InternalFailure>();
174     }
175 
176     return revision;
177 }
178 
179 bool getCurrentBmcState()
180 {
181     sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()};
182 
183     // Get the Inventory object implementing the BMC interface
184     ipmi::DbusObjectInfo bmcObject =
185         ipmi::getDbusObject(bus, bmc_state_interface);
186     auto variant =
187         ipmi::getDbusProperty(bus, bmcObject.second, bmcObject.first,
188                               bmc_state_interface, bmc_state_property);
189 
190     return std::holds_alternative<std::string>(variant) &&
191            BMC::convertBMCStateFromString(std::get<std::string>(variant)) ==
192                BMC::BMCState::Ready;
193 }
194 
195 bool getCurrentBmcStateWithFallback(const bool fallbackAvailability)
196 {
197     try
198     {
199         return getCurrentBmcState();
200     }
201     catch (...)
202     {
203         // Nothing provided the BMC interface, therefore return whatever was
204         // configured as the default.
205         return fallbackAvailability;
206     }
207 }
208 
209 namespace acpi_state
210 {
211 using namespace sdbusplus::xyz::openbmc_project::Control::Power::server;
212 
213 const static constexpr char* acpiObjPath =
214     "/xyz/openbmc_project/control/host0/acpi_power_state";
215 const static constexpr char* acpiInterface =
216     "xyz.openbmc_project.Control.Power.ACPIPowerState";
217 const static constexpr char* sysACPIProp = "SysACPIStatus";
218 const static constexpr char* devACPIProp = "DevACPIStatus";
219 
220 enum class PowerStateType : uint8_t
221 {
222     sysPowerState = 0x00,
223     devPowerState = 0x01,
224 };
225 
226 // Defined in 20.6 of ipmi doc
227 enum class PowerState : uint8_t
228 {
229     s0G0D0 = 0x00,
230     s1D1 = 0x01,
231     s2D2 = 0x02,
232     s3D3 = 0x03,
233     s4 = 0x04,
234     s5G2 = 0x05,
235     s4S5 = 0x06,
236     g3 = 0x07,
237     sleep = 0x08,
238     g1Sleep = 0x09,
239     override = 0x0a,
240     legacyOn = 0x20,
241     legacyOff = 0x21,
242     unknown = 0x2a,
243     noChange = 0x7f,
244 };
245 
246 static constexpr uint8_t stateChanged = 0x80;
247 
248 std::map<ACPIPowerState::ACPI, PowerState> dbusToIPMI = {
249     {ACPIPowerState::ACPI::S0_G0_D0, PowerState::s0G0D0},
250     {ACPIPowerState::ACPI::S1_D1, PowerState::s1D1},
251     {ACPIPowerState::ACPI::S2_D2, PowerState::s2D2},
252     {ACPIPowerState::ACPI::S3_D3, PowerState::s3D3},
253     {ACPIPowerState::ACPI::S4, PowerState::s4},
254     {ACPIPowerState::ACPI::S5_G2, PowerState::s5G2},
255     {ACPIPowerState::ACPI::S4_S5, PowerState::s4S5},
256     {ACPIPowerState::ACPI::G3, PowerState::g3},
257     {ACPIPowerState::ACPI::SLEEP, PowerState::sleep},
258     {ACPIPowerState::ACPI::G1_SLEEP, PowerState::g1Sleep},
259     {ACPIPowerState::ACPI::OVERRIDE, PowerState::override},
260     {ACPIPowerState::ACPI::LEGACY_ON, PowerState::legacyOn},
261     {ACPIPowerState::ACPI::LEGACY_OFF, PowerState::legacyOff},
262     {ACPIPowerState::ACPI::Unknown, PowerState::unknown}};
263 
264 bool isValidACPIState(acpi_state::PowerStateType type, uint8_t state)
265 {
266     if (type == acpi_state::PowerStateType::sysPowerState)
267     {
268         if ((state <= static_cast<uint8_t>(acpi_state::PowerState::override)) ||
269             (state == static_cast<uint8_t>(acpi_state::PowerState::legacyOn)) ||
270             (state ==
271              static_cast<uint8_t>(acpi_state::PowerState::legacyOff)) ||
272             (state == static_cast<uint8_t>(acpi_state::PowerState::unknown)) ||
273             (state == static_cast<uint8_t>(acpi_state::PowerState::noChange)))
274         {
275             return true;
276         }
277         else
278         {
279             return false;
280         }
281     }
282     else if (type == acpi_state::PowerStateType::devPowerState)
283     {
284         if ((state <= static_cast<uint8_t>(acpi_state::PowerState::s3D3)) ||
285             (state == static_cast<uint8_t>(acpi_state::PowerState::unknown)) ||
286             (state == static_cast<uint8_t>(acpi_state::PowerState::noChange)))
287         {
288             return true;
289         }
290         else
291         {
292             return false;
293         }
294     }
295     else
296     {
297         return false;
298     }
299     return false;
300 }
301 } // namespace acpi_state
302 
303 /** @brief implements Set ACPI Power State command
304  * @param sysAcpiState - ACPI system power state to set
305  * @param devAcpiState - ACPI device power state to set
306  *
307  * @return IPMI completion code on success
308  **/
309 ipmi::RspType<> ipmiSetAcpiPowerState(uint8_t sysAcpiState,
310                                       uint8_t devAcpiState)
311 {
312     auto s = static_cast<uint8_t>(acpi_state::PowerState::unknown);
313 
314     sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()};
315 
316     auto value = acpi_state::ACPIPowerState::ACPI::Unknown;
317 
318     if (sysAcpiState & acpi_state::stateChanged)
319     {
320         // set system power state
321         s = sysAcpiState & ~acpi_state::stateChanged;
322 
323         if (!acpi_state::isValidACPIState(
324                 acpi_state::PowerStateType::sysPowerState, s))
325         {
326             log<level::ERR>("set_acpi_power sys invalid input",
327                             entry("S=%x", s));
328             return ipmi::responseParmOutOfRange();
329         }
330 
331         // valid input
332         if (s == static_cast<uint8_t>(acpi_state::PowerState::noChange))
333         {
334             log<level::DEBUG>("No change for system power state");
335         }
336         else
337         {
338             auto found = std::find_if(
339                 acpi_state::dbusToIPMI.begin(), acpi_state::dbusToIPMI.end(),
340                 [&s](const auto& iter) {
341                     return (static_cast<uint8_t>(iter.second) == s);
342                 });
343 
344             value = found->first;
345 
346             try
347             {
348                 auto acpiObject =
349                     ipmi::getDbusObject(bus, acpi_state::acpiInterface);
350                 ipmi::setDbusProperty(bus, acpiObject.second, acpiObject.first,
351                                       acpi_state::acpiInterface,
352                                       acpi_state::sysACPIProp,
353                                       convertForMessage(value));
354             }
355             catch (const InternalFailure& e)
356             {
357                 log<level::ERR>("Failed in set ACPI system property",
358                                 entry("EXCEPTION=%s", e.what()));
359                 return ipmi::responseUnspecifiedError();
360             }
361         }
362     }
363     else
364     {
365         log<level::DEBUG>("Do not change system power state");
366     }
367 
368     if (devAcpiState & acpi_state::stateChanged)
369     {
370         // set device power state
371         s = devAcpiState & ~acpi_state::stateChanged;
372         if (!acpi_state::isValidACPIState(
373                 acpi_state::PowerStateType::devPowerState, s))
374         {
375             log<level::ERR>("set_acpi_power dev invalid input",
376                             entry("S=%x", s));
377             return ipmi::responseParmOutOfRange();
378         }
379 
380         // valid input
381         if (s == static_cast<uint8_t>(acpi_state::PowerState::noChange))
382         {
383             log<level::DEBUG>("No change for device power state");
384         }
385         else
386         {
387             auto found = std::find_if(
388                 acpi_state::dbusToIPMI.begin(), acpi_state::dbusToIPMI.end(),
389                 [&s](const auto& iter) {
390                     return (static_cast<uint8_t>(iter.second) == s);
391                 });
392 
393             value = found->first;
394 
395             try
396             {
397                 auto acpiObject =
398                     ipmi::getDbusObject(bus, acpi_state::acpiInterface);
399                 ipmi::setDbusProperty(bus, acpiObject.second, acpiObject.first,
400                                       acpi_state::acpiInterface,
401                                       acpi_state::devACPIProp,
402                                       convertForMessage(value));
403             }
404             catch (const InternalFailure& e)
405             {
406                 log<level::ERR>("Failed in set ACPI device property",
407                                 entry("EXCEPTION=%s", e.what()));
408                 return ipmi::responseUnspecifiedError();
409             }
410         }
411     }
412     else
413     {
414         log<level::DEBUG>("Do not change device power state");
415     }
416     return ipmi::responseSuccess();
417 }
418 
419 /**
420  *  @brief implements the get ACPI power state command
421  *
422  *  @return IPMI completion code plus response data on success.
423  *   -  ACPI system power state
424  *   -  ACPI device power state
425  **/
426 ipmi::RspType<uint8_t, // acpiSystemPowerState
427               uint8_t  // acpiDevicePowerState
428               >
429     ipmiGetAcpiPowerState()
430 {
431     uint8_t sysAcpiState;
432     uint8_t devAcpiState;
433 
434     sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()};
435 
436     try
437     {
438         auto acpiObject = ipmi::getDbusObject(bus, acpi_state::acpiInterface);
439 
440         auto sysACPIVal = ipmi::getDbusProperty(
441             bus, acpiObject.second, acpiObject.first, acpi_state::acpiInterface,
442             acpi_state::sysACPIProp);
443         auto sysACPI = acpi_state::ACPIPowerState::convertACPIFromString(
444             std::get<std::string>(sysACPIVal));
445         sysAcpiState = static_cast<uint8_t>(acpi_state::dbusToIPMI.at(sysACPI));
446 
447         auto devACPIVal = ipmi::getDbusProperty(
448             bus, acpiObject.second, acpiObject.first, acpi_state::acpiInterface,
449             acpi_state::devACPIProp);
450         auto devACPI = acpi_state::ACPIPowerState::convertACPIFromString(
451             std::get<std::string>(devACPIVal));
452         devAcpiState = static_cast<uint8_t>(acpi_state::dbusToIPMI.at(devACPI));
453     }
454     catch (const InternalFailure& e)
455     {
456         return ipmi::responseUnspecifiedError();
457     }
458 
459     return ipmi::responseSuccess(sysAcpiState, devAcpiState);
460 }
461 
462 typedef struct
463 {
464     char major;
465     char minor;
466     uint16_t d[2];
467 } Revision;
468 
469 /* Currently supports the vx.x-x-[-x] and v1.x.x-x-[-x] format. It will     */
470 /* return -1 if not in those formats, this routine knows how to parse       */
471 /* version = v0.6-19-gf363f61-dirty                                         */
472 /*            ^ ^ ^^          ^                                             */
473 /*            | |  |----------|-- additional details                        */
474 /*            | |---------------- Minor                                     */
475 /*            |------------------ Major                                     */
476 /* and version = v1.99.10-113-g65edf7d-r3-0-g9e4f715                        */
477 /*                ^ ^  ^^ ^                                                 */
478 /*                | |  |--|---------- additional details                    */
479 /*                | |---------------- Minor                                 */
480 /*                |------------------ Major                                 */
481 /* Additional details : If the option group exists it will force Auxiliary  */
482 /* Firmware Revision Information 4th byte to 1 indicating the build was     */
483 /* derived with additional edits                                            */
484 int convertVersion(std::string s, Revision& rev)
485 {
486     std::string token;
487     uint16_t commits;
488 
489     auto location = s.find_first_of('v');
490     if (location != std::string::npos)
491     {
492         s = s.substr(location + 1);
493     }
494 
495     if (!s.empty())
496     {
497         location = s.find_first_of(".");
498         if (location != std::string::npos)
499         {
500             rev.major =
501                 static_cast<char>(std::stoi(s.substr(0, location), 0, 10));
502             token = s.substr(location + 1);
503         }
504 
505         if (!token.empty())
506         {
507             location = token.find_first_of(".-");
508             if (location != std::string::npos)
509             {
510                 rev.minor = static_cast<char>(
511                     std::stoi(token.substr(0, location), 0, 10));
512                 token = token.substr(location + 1);
513             }
514         }
515 
516         // Capture the number of commits on top of the minor tag.
517         // I'm using BE format like the ipmi spec asked for
518         location = token.find_first_of(".-");
519         if (!token.empty())
520         {
521             commits = std::stoi(token.substr(0, location), 0, 16);
522             rev.d[0] = (commits >> 8) | (commits << 8);
523 
524             // commit number we skip
525             location = token.find_first_of(".-");
526             if (location != std::string::npos)
527             {
528                 token = token.substr(location + 1);
529             }
530         }
531         else
532         {
533             rev.d[0] = 0;
534         }
535 
536         if (location != std::string::npos)
537         {
538             token = token.substr(location + 1);
539         }
540 
541         // Any value of the optional parameter forces it to 1
542         location = token.find_first_of(".-");
543         if (location != std::string::npos)
544         {
545             token = token.substr(location + 1);
546         }
547         commits = (!token.empty()) ? 1 : 0;
548 
549         // We do this operation to get this displayed in least significant bytes
550         // of ipmitool device id command.
551         rev.d[1] = (commits >> 8) | (commits << 8);
552     }
553 
554     return 0;
555 }
556 
557 /* @brief: Implement the Get Device ID IPMI command per the IPMI spec
558  *  @param[in] ctx - shared_ptr to an IPMI context struct
559  *
560  *  @returns IPMI completion code plus response data
561  *   - Device ID (manufacturer defined)
562  *   - Device revision[4 bits]; reserved[3 bits]; SDR support[1 bit]
563  *   - FW revision major[7 bits] (binary encoded); available[1 bit]
564  *   - FW Revision minor (BCD encoded)
565  *   - IPMI version (0x02 for IPMI 2.0)
566  *   - device support (bitfield of supported options)
567  *   - MFG IANA ID (3 bytes)
568  *   - product ID (2 bytes)
569  *   - AUX info (4 bytes)
570  */
571 ipmi::RspType<uint8_t,  // Device ID
572               uint8_t,  // Device Revision
573               uint8_t,  // Firmware Revision Major
574               uint8_t,  // Firmware Revision minor
575               uint8_t,  // IPMI version
576               uint8_t,  // Additional device support
577               uint24_t, // MFG ID
578               uint16_t, // Product ID
579               uint32_t  // AUX info
580               >
581     ipmiAppGetDeviceId([[maybe_unused]] ipmi::Context::ptr ctx)
582 {
583     int r = -1;
584     Revision rev = {0, 0, 0, 0};
585     static struct
586     {
587         uint8_t id;
588         uint8_t revision;
589         uint8_t fw[2];
590         uint8_t ipmiVer;
591         uint8_t addnDevSupport;
592         uint24_t manufId;
593         uint16_t prodId;
594         uint32_t aux;
595     } devId;
596     static bool dev_id_initialized = false;
597     static bool haveBMCVersion = false;
598     static bool defaultActivationSetting = true;
599     const char* filename = "/usr/share/ipmi-providers/dev_id.json";
600     constexpr auto ipmiDevIdStateShift = 7;
601     constexpr auto ipmiDevIdFw1Mask = ~(1 << ipmiDevIdStateShift);
602     if (!haveBMCVersion || !dev_id_initialized)
603     {
604         try
605         {
606             auto version = getActiveSoftwareVersionInfo(ctx);
607             r = convertVersion(version, rev);
608         }
609         catch (const std::exception& e)
610         {
611             log<level::ERR>(e.what());
612         }
613 
614         if (r >= 0)
615         {
616             // bit7 identifies if the device is available
617             // 0=normal operation
618             // 1=device firmware, SDR update,
619             // or self-initialization in progress.
620             // The availability may change in run time, so mask here
621             // and initialize later.
622             devId.fw[0] = rev.major & ipmiDevIdFw1Mask;
623 
624             rev.minor = (rev.minor > 99 ? 99 : rev.minor);
625             devId.fw[1] = rev.minor % 10 + (rev.minor / 10) * 16;
626             std::memcpy(&devId.aux, rev.d, 4);
627             haveBMCVersion = true;
628         }
629     }
630     if (!dev_id_initialized)
631     {
632         // IPMI Spec version 2.0
633         devId.ipmiVer = 2;
634 
635         std::ifstream devIdFile(filename);
636         if (devIdFile.is_open())
637         {
638             auto data = nlohmann::json::parse(devIdFile, nullptr, false);
639             if (!data.is_discarded())
640             {
641                 devId.id = data.value("id", 0);
642                 devId.revision = data.value("revision", 0);
643                 devId.addnDevSupport = data.value("addn_dev_support", 0);
644                 devId.manufId = data.value("manuf_id", 0);
645                 devId.prodId = data.value("prod_id", 0);
646                 devId.aux = data.value("aux", 0);
647 
648                 if (data.contains("firmware_revision"))
649                 {
650                     const auto& firmwareRevision = data.at("firmware_revision");
651                     if (firmwareRevision.contains("major"))
652                     {
653                         firmwareRevision.at("major").get_to(devId.fw[0]);
654                     }
655                     if (firmwareRevision.contains("minor"))
656                     {
657                         firmwareRevision.at("minor").get_to(devId.fw[1]);
658                     }
659                 }
660 
661                 // Set the availablitity of the BMC.
662                 defaultActivationSetting = data.value("availability", true);
663 
664                 // Don't read the file every time if successful
665                 dev_id_initialized = true;
666             }
667             else
668             {
669                 log<level::ERR>("Device ID JSON parser failure");
670                 return ipmi::responseUnspecifiedError();
671             }
672         }
673         else
674         {
675             log<level::ERR>("Device ID file not found");
676             return ipmi::responseUnspecifiedError();
677         }
678     }
679 
680     // Set availability to the actual current BMC state
681     devId.fw[0] &= ipmiDevIdFw1Mask;
682     if (!getCurrentBmcStateWithFallback(defaultActivationSetting))
683     {
684         devId.fw[0] |= (1 << ipmiDevIdStateShift);
685     }
686 
687     return ipmi::responseSuccess(
688         devId.id, devId.revision, devId.fw[0], devId.fw[1], devId.ipmiVer,
689         devId.addnDevSupport, devId.manufId, devId.prodId, devId.aux);
690 }
691 
692 auto ipmiAppGetSelfTestResults() -> ipmi::RspType<uint8_t, uint8_t>
693 {
694     // Byte 2:
695     //  55h - No error.
696     //  56h - Self Test function not implemented in this controller.
697     //  57h - Corrupted or inaccesssible data or devices.
698     //  58h - Fatal hardware error.
699     //  FFh - reserved.
700     //  all other: Device-specific 'internal failure'.
701     //  Byte 3:
702     //      For byte 2 = 55h, 56h, FFh:     00h
703     //      For byte 2 = 58h, all other:    Device-specific
704     //      For byte 2 = 57h:   self-test error bitfield.
705     //      Note: returning 57h does not imply that all test were run.
706     //      [7] 1b = Cannot access SEL device.
707     //      [6] 1b = Cannot access SDR Repository.
708     //      [5] 1b = Cannot access BMC FRU device.
709     //      [4] 1b = IPMB signal lines do not respond.
710     //      [3] 1b = SDR Repository empty.
711     //      [2] 1b = Internal Use Area of BMC FRU corrupted.
712     //      [1] 1b = controller update 'boot block' firmware corrupted.
713     //      [0] 1b = controller operational firmware corrupted.
714     constexpr uint8_t notImplemented = 0x56;
715     constexpr uint8_t zero = 0;
716     return ipmi::responseSuccess(notImplemented, zero);
717 }
718 
719 static constexpr size_t uuidBinaryLength = 16;
720 static std::array<uint8_t, uuidBinaryLength> rfc4122ToIpmi(std::string rfc4122)
721 {
722     using Argument = xyz::openbmc_project::Common::InvalidArgument;
723     // UUID is in RFC4122 format. Ex: 61a39523-78f2-11e5-9862-e6402cfc3223
724     // Per IPMI Spec 2.0 need to convert to 16 hex bytes and reverse the byte
725     // order
726     // Ex: 0x2332fc2c40e66298e511f2782395a361
727     constexpr size_t uuidHexLength = (2 * uuidBinaryLength);
728     constexpr size_t uuidRfc4122Length = (uuidHexLength + 4);
729     std::array<uint8_t, uuidBinaryLength> uuid;
730     if (rfc4122.size() == uuidRfc4122Length)
731     {
732         rfc4122.erase(std::remove(rfc4122.begin(), rfc4122.end(), '-'),
733                       rfc4122.end());
734     }
735     if (rfc4122.size() != uuidHexLength)
736     {
737         elog<InvalidArgument>(Argument::ARGUMENT_NAME("rfc4122"),
738                               Argument::ARGUMENT_VALUE(rfc4122.c_str()));
739     }
740     for (size_t ind = 0; ind < uuidHexLength; ind += 2)
741     {
742         char v[3];
743         v[0] = rfc4122[ind];
744         v[1] = rfc4122[ind + 1];
745         v[2] = 0;
746         size_t err;
747         long b;
748         try
749         {
750             b = std::stoul(v, &err, 16);
751         }
752         catch (const std::exception& e)
753         {
754             elog<InvalidArgument>(Argument::ARGUMENT_NAME("rfc4122"),
755                                   Argument::ARGUMENT_VALUE(rfc4122.c_str()));
756         }
757         // check that exactly two ascii bytes were converted
758         if (err != 2)
759         {
760             elog<InvalidArgument>(Argument::ARGUMENT_NAME("rfc4122"),
761                                   Argument::ARGUMENT_VALUE(rfc4122.c_str()));
762         }
763         uuid[uuidBinaryLength - (ind / 2) - 1] = static_cast<uint8_t>(b);
764     }
765     return uuid;
766 }
767 
768 auto ipmiAppGetDeviceGuid()
769     -> ipmi::RspType<std::array<uint8_t, uuidBinaryLength>>
770 {
771     // return a fixed GUID based on /etc/machine-id
772     // This should match the /redfish/v1/Managers/bmc's UUID data
773 
774     // machine specific application ID (for BMC ID)
775     // generated by systemd-id128 -p new as per man page
776     static constexpr sd_id128_t bmcUuidAppId = SD_ID128_MAKE(
777         e0, e1, 73, 76, 64, 61, 47, da, a5, 0c, d0, cc, 64, 12, 45, 78);
778 
779     sd_id128_t bmcUuid;
780     // create the UUID from /etc/machine-id via the systemd API
781     sd_id128_get_machine_app_specific(bmcUuidAppId, &bmcUuid);
782 
783     char bmcUuidCstr[SD_ID128_STRING_MAX];
784     std::string systemUuid = sd_id128_to_string(bmcUuid, bmcUuidCstr);
785 
786     std::array<uint8_t, uuidBinaryLength> uuid = rfc4122ToIpmi(systemUuid);
787     return ipmi::responseSuccess(uuid);
788 }
789 
790 auto ipmiAppGetBtCapabilities()
791     -> ipmi::RspType<uint8_t, uint8_t, uint8_t, uint8_t, uint8_t>
792 {
793     // Per IPMI 2.0 spec, the input and output buffer size must be the max
794     // buffer size minus one byte to allocate space for the length byte.
795     constexpr uint8_t nrOutstanding = 0x01;
796     constexpr uint8_t inputBufferSize = MAX_IPMI_BUFFER - 1;
797     constexpr uint8_t outputBufferSize = MAX_IPMI_BUFFER - 1;
798     constexpr uint8_t transactionTime = 0x0A;
799     constexpr uint8_t nrRetries = 0x01;
800 
801     return ipmi::responseSuccess(nrOutstanding, inputBufferSize,
802                                  outputBufferSize, transactionTime, nrRetries);
803 }
804 
805 auto ipmiAppGetSystemGuid() -> ipmi::RspType<std::array<uint8_t, 16>>
806 {
807     static constexpr auto bmcInterface =
808         "xyz.openbmc_project.Inventory.Item.Bmc";
809     static constexpr auto uuidInterface = "xyz.openbmc_project.Common.UUID";
810     static constexpr auto uuidProperty = "UUID";
811 
812     ipmi::Value propValue;
813     try
814     {
815         // Get the Inventory object implementing BMC interface
816         auto busPtr = getSdBus();
817         auto objectInfo = ipmi::getDbusObject(*busPtr, bmcInterface);
818 
819         // Read UUID property value from bmcObject
820         // UUID is in RFC4122 format Ex: 61a39523-78f2-11e5-9862-e6402cfc3223
821         propValue =
822             ipmi::getDbusProperty(*busPtr, objectInfo.second, objectInfo.first,
823                                   uuidInterface, uuidProperty);
824     }
825     catch (const InternalFailure& e)
826     {
827         log<level::ERR>("Failed in reading BMC UUID property",
828                         entry("INTERFACE=%s", uuidInterface),
829                         entry("PROPERTY=%s", uuidProperty));
830         return ipmi::responseUnspecifiedError();
831     }
832     std::array<uint8_t, 16> uuid;
833     std::string rfc4122Uuid = std::get<std::string>(propValue);
834     try
835     {
836         // convert to IPMI format
837         uuid = rfc4122ToIpmi(rfc4122Uuid);
838     }
839     catch (const InvalidArgument& e)
840     {
841         log<level::ERR>("Failed in parsing BMC UUID property",
842                         entry("INTERFACE=%s", uuidInterface),
843                         entry("PROPERTY=%s", uuidProperty),
844                         entry("VALUE=%s", rfc4122Uuid.c_str()));
845         return ipmi::responseUnspecifiedError();
846     }
847     return ipmi::responseSuccess(uuid);
848 }
849 
850 /**
851  * @brief set the session state as teardown
852  *
853  * This function is to set the session state to tear down in progress if the
854  * state is active.
855  *
856  * @param[in] busp - Dbus obj
857  * @param[in] service - service name
858  * @param[in] obj - object path
859  *
860  * @return success completion code if it sets the session state to
861  * tearDownInProgress else return the corresponding error completion code.
862  **/
863 uint8_t setSessionState(std::shared_ptr<sdbusplus::asio::connection>& busp,
864                         const std::string& service, const std::string& obj)
865 {
866     try
867     {
868         uint8_t sessionState = std::get<uint8_t>(ipmi::getDbusProperty(
869             *busp, service, obj, session::sessionIntf, "State"));
870 
871         if (sessionState == static_cast<uint8_t>(session::State::active))
872         {
873             ipmi::setDbusProperty(
874                 *busp, service, obj, session::sessionIntf, "State",
875                 static_cast<uint8_t>(session::State::tearDownInProgress));
876             return ipmi::ccSuccess;
877         }
878     }
879     catch (const std::exception& e)
880     {
881         log<level::ERR>("Failed in getting session state property",
882                         entry("service=%s", service.c_str()),
883                         entry("object path=%s", obj.c_str()),
884                         entry("interface=%s", session::sessionIntf));
885         return ipmi::ccUnspecifiedError;
886     }
887 
888     return ipmi::ccInvalidFieldRequest;
889 }
890 
891 ipmi::RspType<> ipmiAppCloseSession(uint32_t reqSessionId,
892                                     std::optional<uint8_t> requestSessionHandle)
893 {
894     auto busp = getSdBus();
895     uint8_t reqSessionHandle =
896         requestSessionHandle.value_or(session::defaultSessionHandle);
897 
898     if (reqSessionId == session::sessionZero &&
899         reqSessionHandle == session::defaultSessionHandle)
900     {
901         return ipmi::response(session::ccInvalidSessionId);
902     }
903 
904     if (reqSessionId == session::sessionZero &&
905         reqSessionHandle == session::invalidSessionHandle)
906     {
907         return ipmi::response(session::ccInvalidSessionHandle);
908     }
909 
910     if (reqSessionId != session::sessionZero &&
911         reqSessionHandle != session::defaultSessionHandle)
912     {
913         return ipmi::response(ipmi::ccInvalidFieldRequest);
914     }
915 
916     try
917     {
918         ipmi::ObjectTree objectTree = ipmi::getAllDbusObjects(
919             *busp, session::sessionManagerRootPath, session::sessionIntf);
920 
921         for (auto& objectTreeItr : objectTree)
922         {
923             const std::string obj = objectTreeItr.first;
924 
925             if (isSessionObjectMatched(obj, reqSessionId, reqSessionHandle))
926             {
927                 auto& serviceMap = objectTreeItr.second;
928 
929                 // Session id and session handle are unique for each session.
930                 // Session id and handler are retrived from the object path and
931                 // object path will be unique for each session. Checking if
932                 // multiple objects exist with same object path under multiple
933                 // services.
934                 if (serviceMap.size() != 1)
935                 {
936                     return ipmi::responseUnspecifiedError();
937                 }
938 
939                 auto itr = serviceMap.begin();
940                 const std::string service = itr->first;
941                 return ipmi::response(setSessionState(busp, service, obj));
942             }
943         }
944     }
945     catch (const sdbusplus::exception::exception& e)
946     {
947         log<level::ERR>("Failed to fetch object from dbus",
948                         entry("INTERFACE=%s", session::sessionIntf),
949                         entry("ERRMSG=%s", e.what()));
950         return ipmi::responseUnspecifiedError();
951     }
952 
953     return ipmi::responseInvalidFieldRequest();
954 }
955 
956 uint8_t getTotalSessionCount()
957 {
958     uint8_t count = 0, ch = 0;
959 
960     while (ch < ipmi::maxIpmiChannels &&
961            count < session::maxNetworkInstanceSupported)
962     {
963         ipmi::ChannelInfo chInfo{};
964         ipmi::getChannelInfo(ch, chInfo);
965         if (static_cast<ipmi::EChannelMediumType>(chInfo.mediumType) ==
966             ipmi::EChannelMediumType::lan8032)
967         {
968             count++;
969         }
970         ch++;
971     }
972     return count * session::maxSessionCountPerChannel;
973 }
974 
975 /**
976  * @brief get session info request data.
977  *
978  * This function validates the request data and retrive request session id,
979  * session handle.
980  *
981  * @param[in] ctx - context of current session.
982  * @param[in] sessionIndex - request session index
983  * @param[in] payload - input payload
984  * @param[in] reqSessionId - unpacked session Id will be asigned
985  * @param[in] reqSessionHandle - unpacked session handle will be asigned
986  *
987  * @return success completion code if request data is valid
988  * else return the correcponding error completion code.
989  **/
990 uint8_t getSessionInfoRequestData(const ipmi::Context::ptr ctx,
991                                   const uint8_t sessionIndex,
992                                   ipmi::message::Payload& payload,
993                                   uint32_t& reqSessionId,
994                                   uint8_t& reqSessionHandle)
995 {
996     if ((sessionIndex > session::maxSessionCountPerChannel) &&
997         (sessionIndex < session::searchSessionByHandle))
998     {
999         return ipmi::ccInvalidFieldRequest;
1000     }
1001 
1002     switch (sessionIndex)
1003     {
1004         case session::searchCurrentSession:
1005 
1006             ipmi::ChannelInfo chInfo;
1007             ipmi::getChannelInfo(ctx->channel, chInfo);
1008 
1009             if (static_cast<ipmi::EChannelMediumType>(chInfo.mediumType) !=
1010                 ipmi::EChannelMediumType::lan8032)
1011             {
1012                 return ipmi::ccInvalidFieldRequest;
1013             }
1014 
1015             if (!payload.fullyUnpacked())
1016             {
1017                 return ipmi::ccReqDataLenInvalid;
1018             }
1019             // Check if current sessionId is 0, sessionId 0 is reserved.
1020             if (ctx->sessionId == session::sessionZero)
1021             {
1022                 return session::ccInvalidSessionId;
1023             }
1024             reqSessionId = ctx->sessionId;
1025             break;
1026 
1027         case session::searchSessionByHandle:
1028 
1029             if ((payload.unpack(reqSessionHandle)) ||
1030                 (!payload.fullyUnpacked()))
1031             {
1032                 return ipmi::ccReqDataLenInvalid;
1033             }
1034 
1035             if ((reqSessionHandle == session::sessionZero) ||
1036                 ((reqSessionHandle & session::multiIntfaceSessionHandleMask) >
1037                  session::maxSessionCountPerChannel))
1038             {
1039                 return session::ccInvalidSessionHandle;
1040             }
1041             break;
1042 
1043         case session::searchSessionById:
1044 
1045             if ((payload.unpack(reqSessionId)) || (!payload.fullyUnpacked()))
1046             {
1047                 return ipmi::ccReqDataLenInvalid;
1048             }
1049 
1050             if (reqSessionId == session::sessionZero)
1051             {
1052                 return session::ccInvalidSessionId;
1053             }
1054             break;
1055 
1056         default:
1057             if (!payload.fullyUnpacked())
1058             {
1059                 return ipmi::ccReqDataLenInvalid;
1060             }
1061             break;
1062     }
1063     return ipmi::ccSuccess;
1064 }
1065 
1066 uint8_t getSessionState(ipmi::Context::ptr ctx, const std::string& service,
1067                         const std::string& objPath, uint8_t& sessionState)
1068 {
1069     boost::system::error_code ec = ipmi::getDbusProperty(
1070         ctx, service, objPath, session::sessionIntf, "State", sessionState);
1071     if (ec)
1072     {
1073         log<level::ERR>("Failed to fetch state property ",
1074                         entry("SERVICE=%s", service.c_str()),
1075                         entry("OBJECTPATH=%s", objPath.c_str()),
1076                         entry("INTERFACE=%s", session::sessionIntf),
1077                         entry("ERRMSG=%s", ec.message().c_str()));
1078         return ipmi::ccUnspecifiedError;
1079     }
1080     return ipmi::ccSuccess;
1081 }
1082 
1083 static constexpr uint8_t macAddrLen = 6;
1084 /** Alias SessionDetails - contain the optional information about an
1085  *        RMCP+ session.
1086  *
1087  *  @param userID - uint6_t session user ID (0-63)
1088  *  @param reserved - uint2_t reserved
1089  *  @param privilege - uint4_t session privilege (0-5)
1090  *  @param reserved - uint4_t reserved
1091  *  @param channel - uint4_t session channel number
1092  *  @param protocol - uint4_t session protocol
1093  *  @param remoteIP - uint32_t remote IP address
1094  *  @param macAddr - std::array<uint8_t, 6> mac address
1095  *  @param port - uint16_t remote port
1096  */
1097 using SessionDetails =
1098     std::tuple<uint2_t, uint6_t, uint4_t, uint4_t, uint4_t, uint4_t, uint32_t,
1099                std::array<uint8_t, macAddrLen>, uint16_t>;
1100 
1101 /** @brief get session details for a given session
1102  *
1103  *  @param[in] ctx - ipmi::Context pointer for accessing D-Bus
1104  *  @param[in] service - D-Bus service name to fetch details from
1105  *  @param[in] objPath - D-Bus object path for session
1106  *  @param[out] sessionHandle - return session handle for session
1107  *  @param[out] sessionState - return session state for session
1108  *  @param[out] details - return a SessionDetails tuple containing other
1109  *                        session info
1110  *  @return - ipmi::Cc success or error code
1111  */
1112 ipmi::Cc getSessionDetails(ipmi::Context::ptr ctx, const std::string& service,
1113                            const std::string& objPath, uint8_t& sessionHandle,
1114                            uint8_t& sessionState, SessionDetails& details)
1115 {
1116     ipmi::PropertyMap sessionProps;
1117     boost::system::error_code ec = ipmi::getAllDbusProperties(
1118         ctx, service, objPath, session::sessionIntf, sessionProps);
1119 
1120     if (ec)
1121     {
1122         log<level::ERR>("Failed to fetch state property ",
1123                         entry("SERVICE=%s", service.c_str()),
1124                         entry("OBJECTPATH=%s", objPath.c_str()),
1125                         entry("INTERFACE=%s", session::sessionIntf),
1126                         entry("ERRMSG=%s", ec.message().c_str()));
1127         return ipmi::ccUnspecifiedError;
1128     }
1129 
1130     sessionState = ipmi::mappedVariant<uint8_t>(
1131         sessionProps, "State", static_cast<uint8_t>(session::State::inactive));
1132     if (sessionState == static_cast<uint8_t>(session::State::active))
1133     {
1134         sessionHandle =
1135             ipmi::mappedVariant<uint8_t>(sessionProps, "SessionHandle", 0);
1136         std::get<0>(details) =
1137             ipmi::mappedVariant<uint8_t>(sessionProps, "UserID", 0xff);
1138         // std::get<1>(details) = 0; // (default constructed to 0)
1139         std::get<2>(details) =
1140             ipmi::mappedVariant<uint8_t>(sessionProps, "CurrentPrivilege", 0);
1141         // std::get<3>(details) = 0; // (default constructed to 0)
1142         std::get<4>(details) =
1143             ipmi::mappedVariant<uint8_t>(sessionProps, "ChannelNum", 0xff);
1144         constexpr uint4_t rmcpPlusProtocol = 1;
1145         std::get<5>(details) = rmcpPlusProtocol;
1146         std::get<6>(details) =
1147             ipmi::mappedVariant<uint32_t>(sessionProps, "RemoteIPAddr", 0);
1148         // std::get<7>(details) = {{0}}; // default constructed to all 0
1149         std::get<8>(details) =
1150             ipmi::mappedVariant<uint16_t>(sessionProps, "RemotePort", 0);
1151     }
1152 
1153     return ipmi::ccSuccess;
1154 }
1155 
1156 ipmi::RspType<uint8_t, // session handle,
1157               uint8_t, // total session count
1158               uint8_t, // active session count
1159               std::optional<SessionDetails>>
1160     ipmiAppGetSessionInfo(ipmi::Context::ptr ctx, uint8_t sessionIndex,
1161                           ipmi::message::Payload& payload)
1162 {
1163     uint32_t reqSessionId = 0;
1164     uint8_t reqSessionHandle = session::defaultSessionHandle;
1165     // initializing state to 0xff as 0 represents state as inactive.
1166     uint8_t state = 0xFF;
1167 
1168     uint8_t completionCode = getSessionInfoRequestData(
1169         ctx, sessionIndex, payload, reqSessionId, reqSessionHandle);
1170 
1171     if (completionCode)
1172     {
1173         return ipmi::response(completionCode);
1174     }
1175     ipmi::ObjectTree objectTree;
1176     boost::system::error_code ec = ipmi::getAllDbusObjects(
1177         ctx, session::sessionManagerRootPath, session::sessionIntf, objectTree);
1178     if (ec)
1179     {
1180         log<level::ERR>("Failed to fetch object from dbus",
1181                         entry("INTERFACE=%s", session::sessionIntf),
1182                         entry("ERRMSG=%s", ec.message().c_str()));
1183         return ipmi::responseUnspecifiedError();
1184     }
1185 
1186     uint8_t totalSessionCount = getTotalSessionCount();
1187     uint8_t activeSessionCount = 0;
1188     uint8_t sessionHandle = session::defaultSessionHandle;
1189     std::optional<SessionDetails> maybeDetails;
1190     uint8_t index = 0;
1191     for (auto& objectTreeItr : objectTree)
1192     {
1193         uint32_t sessionId = 0;
1194         std::string objectPath = objectTreeItr.first;
1195 
1196         if (!parseCloseSessionInputPayload(objectPath, sessionId,
1197                                            sessionHandle))
1198         {
1199             continue;
1200         }
1201         index++;
1202         auto& serviceMap = objectTreeItr.second;
1203         auto itr = serviceMap.begin();
1204 
1205         if (serviceMap.size() != 1)
1206         {
1207             return ipmi::responseUnspecifiedError();
1208         }
1209 
1210         std::string service = itr->first;
1211         uint8_t sessionState = 0;
1212         completionCode =
1213             getSessionState(ctx, service, objectPath, sessionState);
1214         if (completionCode)
1215         {
1216             return ipmi::response(completionCode);
1217         }
1218 
1219         if (sessionState == static_cast<uint8_t>(session::State::active))
1220         {
1221             activeSessionCount++;
1222         }
1223 
1224         if (index != sessionIndex && reqSessionId != sessionId &&
1225             reqSessionHandle != sessionHandle)
1226         {
1227             continue;
1228         }
1229 
1230         SessionDetails details{};
1231         completionCode = getSessionDetails(ctx, service, objectPath,
1232                                            sessionHandle, state, details);
1233 
1234         if (completionCode)
1235         {
1236             return ipmi::response(completionCode);
1237         }
1238         maybeDetails = std::move(details);
1239     }
1240 
1241     if (state == static_cast<uint8_t>(session::State::active) ||
1242         state == static_cast<uint8_t>(session::State::tearDownInProgress))
1243     {
1244         return ipmi::responseSuccess(sessionHandle, totalSessionCount,
1245                                      activeSessionCount, maybeDetails);
1246     }
1247 
1248     return ipmi::responseInvalidFieldRequest();
1249 }
1250 
1251 static std::unique_ptr<SysInfoParamStore> sysInfoParamStore;
1252 
1253 static std::string sysInfoReadSystemName()
1254 {
1255     // Use the BMC hostname as the "System Name."
1256     char hostname[HOST_NAME_MAX + 1] = {};
1257     if (gethostname(hostname, HOST_NAME_MAX) != 0)
1258     {
1259         perror("System info parameter: system name");
1260     }
1261     return hostname;
1262 }
1263 
1264 static constexpr uint8_t paramRevision = 0x11;
1265 static constexpr size_t configParameterLength = 16;
1266 
1267 static constexpr size_t smallChunkSize = 14;
1268 static constexpr size_t fullChunkSize = 16;
1269 static constexpr uint8_t progressMask = 0x3;
1270 
1271 static constexpr uint8_t setComplete = 0x0;
1272 static constexpr uint8_t setInProgress = 0x1;
1273 static constexpr uint8_t commitWrite = 0x2;
1274 static uint8_t transferStatus = setComplete;
1275 
1276 static constexpr uint8_t configDataOverhead = 2;
1277 
1278 // For EFI based system, 256 bytes is recommended.
1279 static constexpr size_t maxBytesPerParameter = 256;
1280 
1281 namespace ipmi
1282 {
1283 constexpr Cc ccParmNotSupported = 0x80;
1284 constexpr Cc ccSetInProgressActive = 0x81;
1285 constexpr Cc ccSystemInfoParameterSetReadOnly = 0x82;
1286 
1287 static inline auto responseParmNotSupported()
1288 {
1289     return response(ccParmNotSupported);
1290 }
1291 static inline auto responseSetInProgressActive()
1292 {
1293     return response(ccSetInProgressActive);
1294 }
1295 static inline auto responseSystemInfoParameterSetReadOnly()
1296 {
1297     return response(ccSystemInfoParameterSetReadOnly);
1298 }
1299 } // namespace ipmi
1300 
1301 ipmi::RspType<uint8_t,                // Parameter revision
1302               std::optional<uint8_t>, // data1 / setSelector / ProgressStatus
1303               std::optional<std::vector<uint8_t>>> // data2-17
1304     ipmiAppGetSystemInfo(uint7_t reserved, bool getRevision,
1305                          uint8_t paramSelector, uint8_t setSelector,
1306                          uint8_t BlockSelector)
1307 {
1308     if (reserved || (paramSelector >= invalidParamSelectorStart &&
1309                      paramSelector <= invalidParamSelectorEnd))
1310     {
1311         return ipmi::responseInvalidFieldRequest();
1312     }
1313     if ((paramSelector >= oemCmdStart) && (paramSelector <= oemCmdEnd))
1314     {
1315         return ipmi::responseParmNotSupported();
1316     }
1317     if (getRevision)
1318     {
1319         return ipmi::responseSuccess(paramRevision, std::nullopt, std::nullopt);
1320     }
1321 
1322     if (paramSelector == 0)
1323     {
1324         return ipmi::responseSuccess(paramRevision, transferStatus,
1325                                      std::nullopt);
1326     }
1327 
1328     if (BlockSelector != 0) // 00h if parameter does not require a block number
1329     {
1330         return ipmi::responseParmNotSupported();
1331     }
1332 
1333     if (sysInfoParamStore == nullptr)
1334     {
1335         sysInfoParamStore = std::make_unique<SysInfoParamStore>();
1336         sysInfoParamStore->update(IPMI_SYSINFO_SYSTEM_NAME,
1337                                   sysInfoReadSystemName);
1338     }
1339 
1340     // Parameters other than Set In Progress are assumed to be strings.
1341     std::tuple<bool, std::string> ret =
1342         sysInfoParamStore->lookup(paramSelector);
1343     bool found = std::get<0>(ret);
1344     if (!found)
1345     {
1346         return ipmi::responseSensorInvalid();
1347     }
1348     std::string& paramString = std::get<1>(ret);
1349     std::vector<uint8_t> configData;
1350     size_t count = 0;
1351     if (setSelector == 0)
1352     {                               // First chunk has only 14 bytes.
1353         configData.emplace_back(0); // encoding
1354         configData.emplace_back(paramString.length()); // string length
1355         count = std::min(paramString.length(), smallChunkSize);
1356         configData.resize(count + configDataOverhead);
1357         std::copy_n(paramString.begin(), count,
1358                     configData.begin() + configDataOverhead); // 14 bytes chunk
1359 
1360         // Append zero's to remaining bytes
1361         if (configData.size() < configParameterLength)
1362         {
1363             std::fill_n(std::back_inserter(configData),
1364                         configParameterLength - configData.size(), 0x00);
1365         }
1366     }
1367     else
1368     {
1369         size_t offset = (setSelector * fullChunkSize) - configDataOverhead;
1370         if (offset >= paramString.length())
1371         {
1372             return ipmi::responseParmOutOfRange();
1373         }
1374         count = std::min(paramString.length() - offset, fullChunkSize);
1375         configData.resize(count);
1376         std::copy_n(paramString.begin() + offset, count,
1377                     configData.begin()); // 16 bytes chunk
1378     }
1379     return ipmi::responseSuccess(paramRevision, setSelector, configData);
1380 }
1381 
1382 ipmi::RspType<> ipmiAppSetSystemInfo(uint8_t paramSelector, uint8_t data1,
1383                                      std::vector<uint8_t> configData)
1384 {
1385     if (paramSelector >= invalidParamSelectorStart &&
1386         paramSelector <= invalidParamSelectorEnd)
1387     {
1388         return ipmi::responseInvalidFieldRequest();
1389     }
1390     if ((paramSelector >= oemCmdStart) && (paramSelector <= oemCmdEnd))
1391     {
1392         return ipmi::responseParmNotSupported();
1393     }
1394 
1395     if (paramSelector == 0)
1396     {
1397         // attempt to set the 'set in progress' value (in parameter #0)
1398         // when not in the set complete state.
1399         if ((transferStatus != setComplete) && (data1 == setInProgress))
1400         {
1401             return ipmi::responseSetInProgressActive();
1402         }
1403         // only following 2 states are supported
1404         if (data1 > setInProgress)
1405         {
1406             phosphor::logging::log<phosphor::logging::level::ERR>(
1407                 "illegal SetInProgress status");
1408             return ipmi::responseInvalidFieldRequest();
1409         }
1410 
1411         transferStatus = data1 & progressMask;
1412         return ipmi::responseSuccess();
1413     }
1414 
1415     if (configData.size() > configParameterLength)
1416     {
1417         return ipmi::responseInvalidFieldRequest();
1418     }
1419 
1420     // Append zero's to remaining bytes
1421     if (configData.size() < configParameterLength)
1422     {
1423         fill_n(back_inserter(configData),
1424                (configParameterLength - configData.size()), 0x00);
1425     }
1426 
1427     if (!sysInfoParamStore)
1428     {
1429         sysInfoParamStore = std::make_unique<SysInfoParamStore>();
1430         sysInfoParamStore->update(IPMI_SYSINFO_SYSTEM_NAME,
1431                                   sysInfoReadSystemName);
1432     }
1433 
1434     // lookup
1435     std::tuple<bool, std::string> ret =
1436         sysInfoParamStore->lookup(paramSelector);
1437     bool found = std::get<0>(ret);
1438     std::string& paramString = std::get<1>(ret);
1439     if (!found)
1440     {
1441         // parameter does not exist. Init new
1442         paramString = "";
1443     }
1444 
1445     uint8_t setSelector = data1;
1446     size_t count = 0;
1447     if (setSelector == 0) // First chunk has only 14 bytes.
1448     {
1449         size_t stringLen = configData.at(1); // string length
1450         // maxBytesPerParamter is 256. It will always be greater than stringLen
1451         // (unit8_t) if maxBytes changes in future, then following line is
1452         // needed.
1453         // stringLen = std::min(stringLen, maxBytesPerParameter);
1454         count = std::min(stringLen, smallChunkSize);
1455         count = std::min(count, configData.size());
1456         paramString.resize(stringLen); // reserve space
1457         std::copy_n(configData.begin() + configDataOverhead, count,
1458                     paramString.begin());
1459     }
1460     else
1461     {
1462         size_t offset = (setSelector * fullChunkSize) - configDataOverhead;
1463         if (offset >= paramString.length())
1464         {
1465             return ipmi::responseParmOutOfRange();
1466         }
1467         count = std::min(paramString.length() - offset, configData.size());
1468         std::copy_n(configData.begin(), count, paramString.begin() + offset);
1469     }
1470     sysInfoParamStore->update(paramSelector, paramString);
1471     return ipmi::responseSuccess();
1472 }
1473 
1474 #ifdef ENABLE_I2C_WHITELIST_CHECK
1475 inline std::vector<uint8_t> convertStringToData(const std::string& command)
1476 {
1477     std::istringstream iss(command);
1478     std::string token;
1479     std::vector<uint8_t> dataValue;
1480     while (std::getline(iss, token, ' '))
1481     {
1482         dataValue.emplace_back(
1483             static_cast<uint8_t>(std::stoul(token, nullptr, base_16)));
1484     }
1485     return dataValue;
1486 }
1487 
1488 static bool populateI2CMasterWRWhitelist()
1489 {
1490     nlohmann::json data = nullptr;
1491     std::ifstream jsonFile(i2cMasterWRWhitelistFile);
1492 
1493     if (!jsonFile.good())
1494     {
1495         log<level::WARNING>("i2c white list file not found!",
1496                             entry("FILE_NAME: %s", i2cMasterWRWhitelistFile));
1497         return false;
1498     }
1499 
1500     try
1501     {
1502         data = nlohmann::json::parse(jsonFile, nullptr, false);
1503     }
1504     catch (const nlohmann::json::parse_error& e)
1505     {
1506         log<level::ERR>("Corrupted i2c white list config file",
1507                         entry("FILE_NAME: %s", i2cMasterWRWhitelistFile),
1508                         entry("MSG: %s", e.what()));
1509         return false;
1510     }
1511 
1512     try
1513     {
1514         // Example JSON Structure format
1515         // "filters": [
1516         //    {
1517         //      "Description": "Allow full read - ignore first byte write value
1518         //      for 0x40 to 0x4F",
1519         //      "busId": "0x01",
1520         //      "slaveAddr": "0x40",
1521         //      "slaveAddrMask": "0x0F",
1522         //      "command": "0x00",
1523         //      "commandMask": "0xFF"
1524         //    },
1525         //    {
1526         //      "Description": "Allow full read - first byte match 0x05 and
1527         //      ignore second byte",
1528         //      "busId": "0x01",
1529         //      "slaveAddr": "0x57",
1530         //      "slaveAddrMask": "0x00",
1531         //      "command": "0x05 0x00",
1532         //      "commandMask": "0x00 0xFF"
1533         //    },]
1534 
1535         nlohmann::json filters = data[filtersStr].get<nlohmann::json>();
1536         std::vector<i2cMasterWRWhitelist>& whitelist = getWRWhitelist();
1537         for (const auto& it : filters.items())
1538         {
1539             nlohmann::json filter = it.value();
1540             if (filter.is_null())
1541             {
1542                 log<level::ERR>(
1543                     "Corrupted I2C master write read whitelist config file",
1544                     entry("FILE_NAME: %s", i2cMasterWRWhitelistFile));
1545                 return false;
1546             }
1547             const std::vector<uint8_t>& writeData =
1548                 convertStringToData(filter[cmdStr].get<std::string>());
1549             const std::vector<uint8_t>& writeDataMask =
1550                 convertStringToData(filter[cmdMaskStr].get<std::string>());
1551             if (writeDataMask.size() != writeData.size())
1552             {
1553                 log<level::ERR>("I2C master write read whitelist filter "
1554                                 "mismatch for command & mask size");
1555                 return false;
1556             }
1557             whitelist.push_back(
1558                 {static_cast<uint8_t>(std::stoul(
1559                      filter[busIdStr].get<std::string>(), nullptr, base_16)),
1560                  static_cast<uint8_t>(
1561                      std::stoul(filter[slaveAddrStr].get<std::string>(),
1562                                 nullptr, base_16)),
1563                  static_cast<uint8_t>(
1564                      std::stoul(filter[slaveAddrMaskStr].get<std::string>(),
1565                                 nullptr, base_16)),
1566                  writeData, writeDataMask});
1567         }
1568         if (whitelist.size() != filters.size())
1569         {
1570             log<level::ERR>(
1571                 "I2C master write read whitelist filter size mismatch");
1572             return false;
1573         }
1574     }
1575     catch (const std::exception& e)
1576     {
1577         log<level::ERR>("I2C master write read whitelist unexpected exception",
1578                         entry("ERROR=%s", e.what()));
1579         return false;
1580     }
1581     return true;
1582 }
1583 
1584 static inline bool isWriteDataWhitelisted(const std::vector<uint8_t>& data,
1585                                           const std::vector<uint8_t>& dataMask,
1586                                           const std::vector<uint8_t>& writeData)
1587 {
1588     std::vector<uint8_t> processedDataBuf(data.size());
1589     std::vector<uint8_t> processedReqBuf(dataMask.size());
1590     std::transform(writeData.begin(), writeData.end(), dataMask.begin(),
1591                    processedReqBuf.begin(), std::bit_or<uint8_t>());
1592     std::transform(data.begin(), data.end(), dataMask.begin(),
1593                    processedDataBuf.begin(), std::bit_or<uint8_t>());
1594 
1595     return (processedDataBuf == processedReqBuf);
1596 }
1597 
1598 static bool isCmdWhitelisted(uint8_t busId, uint8_t slaveAddr,
1599                              std::vector<uint8_t>& writeData)
1600 {
1601     std::vector<i2cMasterWRWhitelist>& whiteList = getWRWhitelist();
1602     for (const auto& wlEntry : whiteList)
1603     {
1604         if ((busId == wlEntry.busId) &&
1605             ((slaveAddr | wlEntry.slaveAddrMask) ==
1606              (wlEntry.slaveAddr | wlEntry.slaveAddrMask)))
1607         {
1608             const std::vector<uint8_t>& dataMask = wlEntry.dataMask;
1609             // Skip as no-match, if requested write data is more than the
1610             // write data mask size
1611             if (writeData.size() > dataMask.size())
1612             {
1613                 continue;
1614             }
1615             if (isWriteDataWhitelisted(wlEntry.data, dataMask, writeData))
1616             {
1617                 return true;
1618             }
1619         }
1620     }
1621     return false;
1622 }
1623 #else
1624 static bool populateI2CMasterWRWhitelist()
1625 {
1626     log<level::INFO>(
1627         "I2C_WHITELIST_CHECK is disabled, do not populate whitelist");
1628     return true;
1629 }
1630 #endif // ENABLE_I2C_WHITELIST_CHECK
1631 
1632 /** @brief implements master write read IPMI command which can be used for
1633  * low-level I2C/SMBus write, read or write-read access
1634  *  @param isPrivateBus -to indicate private bus usage
1635  *  @param busId - bus id
1636  *  @param channelNum - channel number
1637  *  @param reserved - skip 1 bit
1638  *  @param slaveAddr - slave address
1639  *  @param read count - number of bytes to be read
1640  *  @param writeData - data to be written
1641  *
1642  *  @returns IPMI completion code plus response data
1643  *   - readData - i2c response data
1644  */
1645 ipmi::RspType<std::vector<uint8_t>>
1646     ipmiMasterWriteRead([[maybe_unused]] bool isPrivateBus, uint3_t busId,
1647                         [[maybe_unused]] uint4_t channelNum, bool reserved,
1648                         uint7_t slaveAddr, uint8_t readCount,
1649                         std::vector<uint8_t> writeData)
1650 {
1651     if (reserved)
1652     {
1653         return ipmi::responseInvalidFieldRequest();
1654     }
1655     if (readCount > maxIPMIWriteReadSize)
1656     {
1657         log<level::ERR>("Master write read command: Read count exceeds limit");
1658         return ipmi::responseParmOutOfRange();
1659     }
1660     const size_t writeCount = writeData.size();
1661     if (!readCount && !writeCount)
1662     {
1663         log<level::ERR>("Master write read command: Read & write count are 0");
1664         return ipmi::responseInvalidFieldRequest();
1665     }
1666 #ifdef ENABLE_I2C_WHITELIST_CHECK
1667     if (!isCmdWhitelisted(static_cast<uint8_t>(busId),
1668                           static_cast<uint8_t>(slaveAddr), writeData))
1669     {
1670         log<level::ERR>("Master write read request blocked!",
1671                         entry("BUS=%d", static_cast<uint8_t>(busId)),
1672                         entry("ADDR=0x%x", static_cast<uint8_t>(slaveAddr)));
1673         return ipmi::responseInvalidFieldRequest();
1674     }
1675 #endif // ENABLE_I2C_WHITELIST_CHECK
1676     std::vector<uint8_t> readBuf(readCount);
1677     std::string i2cBus =
1678         "/dev/i2c-" + std::to_string(static_cast<uint8_t>(busId));
1679 
1680     ipmi::Cc ret = ipmi::i2cWriteRead(i2cBus, static_cast<uint8_t>(slaveAddr),
1681                                       writeData, readBuf);
1682     if (ret != ipmi::ccSuccess)
1683     {
1684         return ipmi::response(ret);
1685     }
1686     return ipmi::responseSuccess(readBuf);
1687 }
1688 
1689 void register_netfn_app_functions()
1690 {
1691     // <Get Device ID>
1692     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp,
1693                           ipmi::app::cmdGetDeviceId, ipmi::Privilege::User,
1694                           ipmiAppGetDeviceId);
1695 
1696     // <Get BT Interface Capabilities>
1697     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp,
1698                           ipmi::app::cmdGetBtIfaceCapabilities,
1699                           ipmi::Privilege::User, ipmiAppGetBtCapabilities);
1700 
1701     // <Reset Watchdog Timer>
1702     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp,
1703                           ipmi::app::cmdResetWatchdogTimer,
1704                           ipmi::Privilege::Operator, ipmiAppResetWatchdogTimer);
1705 
1706     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp,
1707                           ipmi::app::cmdGetSessionInfo, ipmi::Privilege::User,
1708                           ipmiAppGetSessionInfo);
1709 
1710     // <Set Watchdog Timer>
1711     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp,
1712                           ipmi::app::cmdSetWatchdogTimer,
1713                           ipmi::Privilege::Operator, ipmiSetWatchdogTimer);
1714 
1715     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp,
1716                           ipmi::app::cmdCloseSession, ipmi::Privilege::Callback,
1717                           ipmiAppCloseSession);
1718 
1719     // <Get Watchdog Timer>
1720     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp,
1721                           ipmi::app::cmdGetWatchdogTimer, ipmi::Privilege::User,
1722                           ipmiGetWatchdogTimer);
1723 
1724     // <Get Self Test Results>
1725     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp,
1726                           ipmi::app::cmdGetSelfTestResults,
1727                           ipmi::Privilege::User, ipmiAppGetSelfTestResults);
1728 
1729     // <Get Device GUID>
1730     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp,
1731                           ipmi::app::cmdGetDeviceGuid, ipmi::Privilege::User,
1732                           ipmiAppGetDeviceGuid);
1733 
1734     // <Set ACPI Power State>
1735     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp,
1736                           ipmi::app::cmdSetAcpiPowerState,
1737                           ipmi::Privilege::Admin, ipmiSetAcpiPowerState);
1738     // <Get ACPI Power State>
1739     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp,
1740                           ipmi::app::cmdGetAcpiPowerState,
1741                           ipmi::Privilege::User, ipmiGetAcpiPowerState);
1742 
1743     // Note: For security reason, this command will be registered only when
1744     // there are proper I2C Master write read whitelist
1745     if (populateI2CMasterWRWhitelist())
1746     {
1747         // Note: For security reasons, registering master write read as admin
1748         // privilege command, even though IPMI 2.0 specification allows it as
1749         // operator privilege.
1750         ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp,
1751                               ipmi::app::cmdMasterWriteRead,
1752                               ipmi::Privilege::Admin, ipmiMasterWriteRead);
1753     }
1754 
1755     // <Get System GUID Command>
1756     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp,
1757                           ipmi::app::cmdGetSystemGuid, ipmi::Privilege::User,
1758                           ipmiAppGetSystemGuid);
1759 
1760     // <Get Channel Cipher Suites Command>
1761     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp,
1762                           ipmi::app::cmdGetChannelCipherSuites,
1763                           ipmi::Privilege::None, getChannelCipherSuites);
1764 
1765     // <Get System Info Command>
1766     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp,
1767                           ipmi::app::cmdGetSystemInfoParameters,
1768                           ipmi::Privilege::User, ipmiAppGetSystemInfo);
1769     // <Set System Info Command>
1770     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp,
1771                           ipmi::app::cmdSetSystemInfoParameters,
1772                           ipmi::Privilege::Admin, ipmiAppSetSystemInfo);
1773     return;
1774 }
1775