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