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 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 (sdbusplus::exception::SdBusError& 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, 16));
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, 16));
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(ipmi::Context::ptr ctx)
582 {
583     int r = -1;
584     Revision rev = {0};
585     static struct
586     {
587         uint8_t id;
588         uint8_t revision;
589         uint8_t fw[2];
590         uint8_t ipmiVer;
591         uint8_t addnDevSupport;
592         uint24_t manufId;
593         uint16_t prodId;
594         uint32_t aux;
595     } devId;
596     static bool dev_id_initialized = false;
597     static bool defaultActivationSetting = true;
598     const char* filename = "/usr/share/ipmi-providers/dev_id.json";
599     constexpr auto ipmiDevIdStateShift = 7;
600     constexpr auto ipmiDevIdFw1Mask = ~(1 << ipmiDevIdStateShift);
601 
602     if (!dev_id_initialized)
603     {
604         try
605         {
606             auto version = getActiveSoftwareVersionInfo(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         }
628 
629         // IPMI Spec version 2.0
630         devId.ipmiVer = 2;
631 
632         std::ifstream devIdFile(filename);
633         if (devIdFile.is_open())
634         {
635             auto data = nlohmann::json::parse(devIdFile, nullptr, false);
636             if (!data.is_discarded())
637             {
638                 devId.id = data.value("id", 0);
639                 devId.revision = data.value("revision", 0);
640                 devId.addnDevSupport = data.value("addn_dev_support", 0);
641                 devId.manufId = data.value("manuf_id", 0);
642                 devId.prodId = data.value("prod_id", 0);
643                 devId.aux = data.value("aux", 0);
644 
645                 // Set the availablitity of the BMC.
646                 defaultActivationSetting = data.value("availability", true);
647 
648                 // Don't read the file every time if successful
649                 dev_id_initialized = true;
650             }
651             else
652             {
653                 log<level::ERR>("Device ID JSON parser failure");
654                 return ipmi::responseUnspecifiedError();
655             }
656         }
657         else
658         {
659             log<level::ERR>("Device ID file not found");
660             return ipmi::responseUnspecifiedError();
661         }
662     }
663 
664     // Set availability to the actual current BMC state
665     devId.fw[0] &= ipmiDevIdFw1Mask;
666     if (!getCurrentBmcStateWithFallback(defaultActivationSetting))
667     {
668         devId.fw[0] |= (1 << ipmiDevIdStateShift);
669     }
670 
671     return ipmi::responseSuccess(
672         devId.id, devId.revision, devId.fw[0], devId.fw[1], devId.ipmiVer,
673         devId.addnDevSupport, devId.manufId, devId.prodId, devId.aux);
674 }
675 
676 auto ipmiAppGetSelfTestResults() -> ipmi::RspType<uint8_t, uint8_t>
677 {
678     // Byte 2:
679     //  55h - No error.
680     //  56h - Self Test function not implemented in this controller.
681     //  57h - Corrupted or inaccesssible data or devices.
682     //  58h - Fatal hardware error.
683     //  FFh - reserved.
684     //  all other: Device-specific 'internal failure'.
685     //  Byte 3:
686     //      For byte 2 = 55h, 56h, FFh:     00h
687     //      For byte 2 = 58h, all other:    Device-specific
688     //      For byte 2 = 57h:   self-test error bitfield.
689     //      Note: returning 57h does not imply that all test were run.
690     //      [7] 1b = Cannot access SEL device.
691     //      [6] 1b = Cannot access SDR Repository.
692     //      [5] 1b = Cannot access BMC FRU device.
693     //      [4] 1b = IPMB signal lines do not respond.
694     //      [3] 1b = SDR Repository empty.
695     //      [2] 1b = Internal Use Area of BMC FRU corrupted.
696     //      [1] 1b = controller update 'boot block' firmware corrupted.
697     //      [0] 1b = controller operational firmware corrupted.
698     constexpr uint8_t notImplemented = 0x56;
699     constexpr uint8_t zero = 0;
700     return ipmi::responseSuccess(notImplemented, zero);
701 }
702 
703 static constexpr size_t uuidBinaryLength = 16;
704 static std::array<uint8_t, uuidBinaryLength> rfc4122ToIpmi(std::string rfc4122)
705 {
706     using Argument = xyz::openbmc_project::Common::InvalidArgument;
707     // UUID is in RFC4122 format. Ex: 61a39523-78f2-11e5-9862-e6402cfc3223
708     // Per IPMI Spec 2.0 need to convert to 16 hex bytes and reverse the byte
709     // order
710     // Ex: 0x2332fc2c40e66298e511f2782395a361
711     constexpr size_t uuidHexLength = (2 * uuidBinaryLength);
712     constexpr size_t uuidRfc4122Length = (uuidHexLength + 4);
713     std::array<uint8_t, uuidBinaryLength> uuid;
714     if (rfc4122.size() == uuidRfc4122Length)
715     {
716         rfc4122.erase(std::remove(rfc4122.begin(), rfc4122.end(), '-'),
717                       rfc4122.end());
718     }
719     if (rfc4122.size() != uuidHexLength)
720     {
721         elog<InvalidArgument>(Argument::ARGUMENT_NAME("rfc4122"),
722                               Argument::ARGUMENT_VALUE(rfc4122.c_str()));
723     }
724     for (size_t ind = 0; ind < uuidHexLength; ind += 2)
725     {
726         char v[3];
727         v[0] = rfc4122[ind];
728         v[1] = rfc4122[ind + 1];
729         v[2] = 0;
730         size_t err;
731         long b;
732         try
733         {
734             b = std::stoul(v, &err, 16);
735         }
736         catch (std::exception& e)
737         {
738             elog<InvalidArgument>(Argument::ARGUMENT_NAME("rfc4122"),
739                                   Argument::ARGUMENT_VALUE(rfc4122.c_str()));
740         }
741         // check that exactly two ascii bytes were converted
742         if (err != 2)
743         {
744             elog<InvalidArgument>(Argument::ARGUMENT_NAME("rfc4122"),
745                                   Argument::ARGUMENT_VALUE(rfc4122.c_str()));
746         }
747         uuid[uuidBinaryLength - (ind / 2) - 1] = static_cast<uint8_t>(b);
748     }
749     return uuid;
750 }
751 
752 auto ipmiAppGetDeviceGuid()
753     -> ipmi::RspType<std::array<uint8_t, uuidBinaryLength>>
754 {
755     // return a fixed GUID based on /etc/machine-id
756     // This should match the /redfish/v1/Managers/bmc's UUID data
757 
758     // machine specific application ID (for BMC ID)
759     // generated by systemd-id128 -p new as per man page
760     static constexpr sd_id128_t bmcUuidAppId = SD_ID128_MAKE(
761         e0, e1, 73, 76, 64, 61, 47, da, a5, 0c, d0, cc, 64, 12, 45, 78);
762 
763     sd_id128_t bmcUuid;
764     // create the UUID from /etc/machine-id via the systemd API
765     sd_id128_get_machine_app_specific(bmcUuidAppId, &bmcUuid);
766 
767     char bmcUuidCstr[SD_ID128_STRING_MAX];
768     std::string systemUuid = sd_id128_to_string(bmcUuid, bmcUuidCstr);
769 
770     std::array<uint8_t, uuidBinaryLength> uuid = rfc4122ToIpmi(systemUuid);
771     return ipmi::responseSuccess(uuid);
772 }
773 
774 auto ipmiAppGetBtCapabilities()
775     -> ipmi::RspType<uint8_t, uint8_t, uint8_t, uint8_t, uint8_t>
776 {
777     // Per IPMI 2.0 spec, the input and output buffer size must be the max
778     // buffer size minus one byte to allocate space for the length byte.
779     constexpr uint8_t nrOutstanding = 0x01;
780     constexpr uint8_t inputBufferSize = MAX_IPMI_BUFFER - 1;
781     constexpr uint8_t outputBufferSize = MAX_IPMI_BUFFER - 1;
782     constexpr uint8_t transactionTime = 0x0A;
783     constexpr uint8_t nrRetries = 0x01;
784 
785     return ipmi::responseSuccess(nrOutstanding, inputBufferSize,
786                                  outputBufferSize, transactionTime, nrRetries);
787 }
788 
789 auto ipmiAppGetSystemGuid() -> ipmi::RspType<std::array<uint8_t, 16>>
790 {
791     static constexpr auto bmcInterface =
792         "xyz.openbmc_project.Inventory.Item.Bmc";
793     static constexpr auto uuidInterface = "xyz.openbmc_project.Common.UUID";
794     static constexpr auto uuidProperty = "UUID";
795 
796     ipmi::Value propValue;
797     try
798     {
799         // Get the Inventory object implementing BMC interface
800         auto busPtr = getSdBus();
801         auto objectInfo = ipmi::getDbusObject(*busPtr, bmcInterface);
802 
803         // Read UUID property value from bmcObject
804         // UUID is in RFC4122 format Ex: 61a39523-78f2-11e5-9862-e6402cfc3223
805         propValue =
806             ipmi::getDbusProperty(*busPtr, objectInfo.second, objectInfo.first,
807                                   uuidInterface, uuidProperty);
808     }
809     catch (const InternalFailure& e)
810     {
811         log<level::ERR>("Failed in reading BMC UUID property",
812                         entry("INTERFACE=%s", uuidInterface),
813                         entry("PROPERTY=%s", uuidProperty));
814         return ipmi::responseUnspecifiedError();
815     }
816     std::array<uint8_t, 16> uuid;
817     std::string rfc4122Uuid = std::get<std::string>(propValue);
818     try
819     {
820         // convert to IPMI format
821         uuid = rfc4122ToIpmi(rfc4122Uuid);
822     }
823     catch (const InvalidArgument& e)
824     {
825         log<level::ERR>("Failed in parsing BMC UUID property",
826                         entry("INTERFACE=%s", uuidInterface),
827                         entry("PROPERTY=%s", uuidProperty),
828                         entry("VALUE=%s", rfc4122Uuid.c_str()));
829         return ipmi::responseUnspecifiedError();
830     }
831     return ipmi::responseSuccess(uuid);
832 }
833 
834 /**
835  * @brief set the session state as teardown
836  *
837  * This function is to set the session state to tear down in progress if the
838  * state is active.
839  *
840  * @param[in] busp - Dbus obj
841  * @param[in] service - service name
842  * @param[in] obj - object path
843  *
844  * @return success completion code if it sets the session state to
845  * tearDownInProgress else return the corresponding error completion code.
846  **/
847 uint8_t setSessionState(std::shared_ptr<sdbusplus::asio::connection>& busp,
848                         const std::string& service, const std::string& obj)
849 {
850     try
851     {
852         uint8_t sessionState = std::get<uint8_t>(ipmi::getDbusProperty(
853             *busp, service, obj, session::sessionIntf, "State"));
854 
855         if (sessionState == static_cast<uint8_t>(session::State::active))
856         {
857             ipmi::setDbusProperty(
858                 *busp, service, obj, session::sessionIntf, "State",
859                 static_cast<uint8_t>(session::State::tearDownInProgress));
860             return ipmi::ccSuccess;
861         }
862     }
863     catch (std::exception& e)
864     {
865         log<level::ERR>("Failed in getting session state property",
866                         entry("service=%s", service.c_str()),
867                         entry("object path=%s", obj.c_str()),
868                         entry("interface=%s", session::sessionIntf));
869         return ipmi::ccUnspecifiedError;
870     }
871 
872     return ipmi::ccInvalidFieldRequest;
873 }
874 
875 ipmi::RspType<> ipmiAppCloseSession(uint32_t reqSessionId,
876                                     std::optional<uint8_t> requestSessionHandle)
877 {
878     auto busp = getSdBus();
879     uint8_t reqSessionHandle =
880         requestSessionHandle.value_or(session::defaultSessionHandle);
881 
882     if (reqSessionId == session::sessionZero &&
883         reqSessionHandle == session::defaultSessionHandle)
884     {
885         return ipmi::response(session::ccInvalidSessionId);
886     }
887 
888     if (reqSessionId == session::sessionZero &&
889         reqSessionHandle == session::invalidSessionHandle)
890     {
891         return ipmi::response(session::ccInvalidSessionHandle);
892     }
893 
894     if (reqSessionId != session::sessionZero &&
895         reqSessionHandle != session::defaultSessionHandle)
896     {
897         return ipmi::response(ipmi::ccInvalidFieldRequest);
898     }
899 
900     try
901     {
902         ipmi::ObjectTree objectTree = ipmi::getAllDbusObjects(
903             *busp, session::sessionManagerRootPath, session::sessionIntf);
904 
905         for (auto& objectTreeItr : objectTree)
906         {
907             const std::string obj = objectTreeItr.first;
908 
909             if (isSessionObjectMatched(obj, reqSessionId, reqSessionHandle))
910             {
911                 auto& serviceMap = objectTreeItr.second;
912 
913                 // Session id and session handle are unique for each session.
914                 // Session id and handler are retrived from the object path and
915                 // object path will be unique for each session. Checking if
916                 // multiple objects exist with same object path under multiple
917                 // services.
918                 if (serviceMap.size() != 1)
919                 {
920                     return ipmi::responseUnspecifiedError();
921                 }
922 
923                 auto itr = serviceMap.begin();
924                 const std::string service = itr->first;
925                 return ipmi::response(setSessionState(busp, service, obj));
926             }
927         }
928     }
929     catch (sdbusplus::exception::SdBusError& e)
930     {
931         log<level::ERR>("Failed to fetch object from dbus",
932                         entry("INTERFACE=%s", session::sessionIntf),
933                         entry("ERRMSG=%s", e.what()));
934         return ipmi::responseUnspecifiedError();
935     }
936 
937     return ipmi::responseInvalidFieldRequest();
938 }
939 
940 uint8_t getTotalSessionCount()
941 {
942     uint8_t count = 0, ch = 1;
943 
944     while (ch < ipmi::maxIpmiChannels &&
945            count < session::maxNetworkInstanceSupported)
946     {
947         ipmi::ChannelInfo chInfo{};
948         ipmi::getChannelInfo(ch, chInfo);
949         if (static_cast<ipmi::EChannelMediumType>(chInfo.mediumType) ==
950             ipmi::EChannelMediumType::lan8032)
951         {
952             count++;
953         }
954         ch++;
955     }
956     return count * session::maxSessionCountPerChannel;
957 }
958 
959 /**
960  * @brief get session info request data.
961  *
962  * This function validates the request data and retrive request session id,
963  * session handle.
964  *
965  * @param[in] ctx - context of current session.
966  * @param[in] sessionIndex - request session index
967  * @param[in] payload - input payload
968  * @param[in] reqSessionId - unpacked session Id will be asigned
969  * @param[in] reqSessionHandle - unpacked session handle will be asigned
970  *
971  * @return success completion code if request data is valid
972  * else return the correcponding error completion code.
973  **/
974 uint8_t getSessionInfoRequestData(const ipmi::Context::ptr ctx,
975                                   const uint8_t sessionIndex,
976                                   ipmi::message::Payload& payload,
977                                   uint32_t& reqSessionId,
978                                   uint8_t& reqSessionHandle)
979 {
980     if ((sessionIndex > session::maxSessionCountPerChannel) &&
981         (sessionIndex < session::searchSessionByHandle))
982     {
983         return ipmi::ccInvalidFieldRequest;
984     }
985 
986     switch (sessionIndex)
987     {
988         case session::searchCurrentSession:
989 
990             ipmi::ChannelInfo chInfo;
991             ipmi::getChannelInfo(ctx->channel, chInfo);
992 
993             if (static_cast<ipmi::EChannelMediumType>(chInfo.mediumType) !=
994                 ipmi::EChannelMediumType::lan8032)
995             {
996                 return ipmi::ccInvalidFieldRequest;
997             }
998 
999             if (!payload.fullyUnpacked())
1000             {
1001                 return ipmi::ccReqDataLenInvalid;
1002             }
1003             // Check if current sessionId is 0, sessionId 0 is reserved.
1004             if (ctx->sessionId == session::sessionZero)
1005             {
1006                 return session::ccInvalidSessionId;
1007             }
1008             reqSessionId = ctx->sessionId;
1009             break;
1010 
1011         case session::searchSessionByHandle:
1012 
1013             if ((payload.unpack(reqSessionHandle)) ||
1014                 (!payload.fullyUnpacked()))
1015             {
1016                 return ipmi::ccReqDataLenInvalid;
1017             }
1018 
1019             if ((reqSessionHandle == session::sessionZero) ||
1020                 ((reqSessionHandle & session::multiIntfaceSessionHandleMask) >
1021                  session::maxSessionCountPerChannel))
1022             {
1023                 return session::ccInvalidSessionHandle;
1024             }
1025             break;
1026 
1027         case session::searchSessionById:
1028 
1029             if ((payload.unpack(reqSessionId)) || (!payload.fullyUnpacked()))
1030             {
1031                 return ipmi::ccReqDataLenInvalid;
1032             }
1033 
1034             if (reqSessionId == session::sessionZero)
1035             {
1036                 return session::ccInvalidSessionId;
1037             }
1038             break;
1039 
1040         default:
1041             if (!payload.fullyUnpacked())
1042             {
1043                 return ipmi::ccReqDataLenInvalid;
1044             }
1045             break;
1046     }
1047     return ipmi::ccSuccess;
1048 }
1049 
1050 uint8_t getSessionState(ipmi::Context::ptr ctx, const std::string& service,
1051                         const std::string& objPath, uint8_t& sessionState)
1052 {
1053     boost::system::error_code ec = ipmi::getDbusProperty(
1054         ctx, service, objPath, session::sessionIntf, "State", sessionState);
1055     if (ec)
1056     {
1057         log<level::ERR>("Failed to fetch state property ",
1058                         entry("SERVICE=%s", service.c_str()),
1059                         entry("OBJECTPATH=%s", objPath.c_str()),
1060                         entry("INTERFACE=%s", session::sessionIntf),
1061                         entry("ERRMSG=%s", ec.message().c_str()));
1062         return ipmi::ccUnspecifiedError;
1063     }
1064     return ipmi::ccSuccess;
1065 }
1066 
1067 static constexpr uint8_t macAddrLen = 6;
1068 /** Alias SessionDetails - contain the optional information about an
1069  *        RMCP+ session.
1070  *
1071  *  @param userID - uint6_t session user ID (0-63)
1072  *  @param reserved - uint2_t reserved
1073  *  @param privilege - uint4_t session privilege (0-5)
1074  *  @param reserved - uint4_t reserved
1075  *  @param channel - uint4_t session channel number
1076  *  @param protocol - uint4_t session protocol
1077  *  @param remoteIP - uint32_t remote IP address
1078  *  @param macAddr - std::array<uint8_t, 6> mac address
1079  *  @param port - uint16_t remote port
1080  */
1081 using SessionDetails =
1082     std::tuple<uint2_t, uint6_t, uint4_t, uint4_t, uint4_t, uint4_t, uint32_t,
1083                std::array<uint8_t, macAddrLen>, uint16_t>;
1084 
1085 /** @brief get session details for a given session
1086  *
1087  *  @param[in] ctx - ipmi::Context pointer for accessing D-Bus
1088  *  @param[in] service - D-Bus service name to fetch details from
1089  *  @param[in] objPath - D-Bus object path for session
1090  *  @param[out] sessionHandle - return session handle for session
1091  *  @param[out] sessionState - return session state for session
1092  *  @param[out] details - return a SessionDetails tuple containing other
1093  *                        session info
1094  *  @return - ipmi::Cc success or error code
1095  */
1096 ipmi::Cc getSessionDetails(ipmi::Context::ptr ctx, const std::string& service,
1097                            const std::string& objPath, uint8_t& sessionHandle,
1098                            uint8_t& sessionState, SessionDetails& details)
1099 {
1100     ipmi::PropertyMap sessionProps;
1101     boost::system::error_code ec = ipmi::getAllDbusProperties(
1102         ctx, service, objPath, session::sessionIntf, sessionProps);
1103 
1104     if (ec)
1105     {
1106         log<level::ERR>("Failed to fetch state property ",
1107                         entry("SERVICE=%s", service.c_str()),
1108                         entry("OBJECTPATH=%s", objPath.c_str()),
1109                         entry("INTERFACE=%s", session::sessionIntf),
1110                         entry("ERRMSG=%s", ec.message().c_str()));
1111         return ipmi::ccUnspecifiedError;
1112     }
1113 
1114     sessionState = ipmi::mappedVariant<uint8_t>(
1115         sessionProps, "State", static_cast<uint8_t>(session::State::inactive));
1116     if (sessionState == static_cast<uint8_t>(session::State::active))
1117     {
1118         sessionHandle =
1119             ipmi::mappedVariant<uint8_t>(sessionProps, "SessionHandle", 0);
1120         std::get<0>(details) =
1121             ipmi::mappedVariant<uint8_t>(sessionProps, "UserID", 0xff);
1122         // std::get<1>(details) = 0; // (default constructed to 0)
1123         std::get<2>(details) =
1124             ipmi::mappedVariant<uint8_t>(sessionProps, "CurrentPrivilege", 0);
1125         // std::get<3>(details) = 0; // (default constructed to 0)
1126         std::get<4>(details) =
1127             ipmi::mappedVariant<uint8_t>(sessionProps, "ChannelNum", 0xff);
1128         constexpr uint4_t rmcpPlusProtocol = 1;
1129         std::get<5>(details) = rmcpPlusProtocol;
1130         std::get<6>(details) =
1131             ipmi::mappedVariant<uint32_t>(sessionProps, "RemoteIPAddr", 0);
1132         // std::get<7>(details) = {{0}}; // default constructed to all 0
1133         std::get<8>(details) =
1134             ipmi::mappedVariant<uint16_t>(sessionProps, "RemotePort", 0);
1135     }
1136 
1137     return ipmi::ccSuccess;
1138 }
1139 
1140 ipmi::RspType<uint8_t, // session handle,
1141               uint8_t, // total session count
1142               uint8_t, // active session count
1143               std::optional<SessionDetails>>
1144     ipmiAppGetSessionInfo(ipmi::Context::ptr ctx, uint8_t sessionIndex,
1145                           ipmi::message::Payload& payload)
1146 {
1147     uint32_t reqSessionId = 0;
1148     uint8_t reqSessionHandle = session::defaultSessionHandle;
1149     // initializing state to 0xff as 0 represents state as inactive.
1150     uint8_t state = 0xFF;
1151 
1152     uint8_t completionCode = getSessionInfoRequestData(
1153         ctx, sessionIndex, payload, reqSessionId, reqSessionHandle);
1154 
1155     if (completionCode)
1156     {
1157         return ipmi::response(completionCode);
1158     }
1159     ipmi::ObjectTree objectTree;
1160     boost::system::error_code ec = ipmi::getAllDbusObjects(
1161         ctx, session::sessionManagerRootPath, session::sessionIntf, objectTree);
1162     if (ec)
1163     {
1164         log<level::ERR>("Failed to fetch object from dbus",
1165                         entry("INTERFACE=%s", session::sessionIntf),
1166                         entry("ERRMSG=%s", ec.message().c_str()));
1167         return ipmi::responseUnspecifiedError();
1168     }
1169 
1170     uint8_t totalSessionCount = getTotalSessionCount();
1171     uint8_t activeSessionCount = 0;
1172     uint8_t sessionHandle = session::defaultSessionHandle;
1173     std::optional<SessionDetails> maybeDetails;
1174     uint8_t index = 0;
1175     for (auto& objectTreeItr : objectTree)
1176     {
1177         uint32_t sessionId = 0;
1178         std::string objectPath = objectTreeItr.first;
1179 
1180         if (!parseCloseSessionInputPayload(objectPath, sessionId,
1181                                            sessionHandle))
1182         {
1183             continue;
1184         }
1185         index++;
1186         auto& serviceMap = objectTreeItr.second;
1187         auto itr = serviceMap.begin();
1188 
1189         if (serviceMap.size() != 1)
1190         {
1191             return ipmi::responseUnspecifiedError();
1192         }
1193 
1194         std::string service = itr->first;
1195         uint8_t sessionState = 0;
1196         completionCode =
1197             getSessionState(ctx, service, objectPath, sessionState);
1198         if (completionCode)
1199         {
1200             return ipmi::response(completionCode);
1201         }
1202 
1203         if (sessionState == static_cast<uint8_t>(session::State::active))
1204         {
1205             activeSessionCount++;
1206         }
1207 
1208         if (index != sessionIndex && reqSessionId != sessionId &&
1209             reqSessionHandle != sessionHandle)
1210         {
1211             continue;
1212         }
1213 
1214         SessionDetails details{};
1215         completionCode = getSessionDetails(ctx, service, objectPath,
1216                                            sessionHandle, state, details);
1217 
1218         if (completionCode)
1219         {
1220             return ipmi::response(completionCode);
1221         }
1222         maybeDetails = std::move(details);
1223     }
1224 
1225     if (state == static_cast<uint8_t>(session::State::active) ||
1226         state == static_cast<uint8_t>(session::State::tearDownInProgress))
1227     {
1228         return ipmi::responseSuccess(sessionHandle, totalSessionCount,
1229                                      activeSessionCount, maybeDetails);
1230     }
1231 
1232     return ipmi::responseInvalidFieldRequest();
1233 }
1234 
1235 static std::unique_ptr<SysInfoParamStore> sysInfoParamStore;
1236 
1237 static std::string sysInfoReadSystemName()
1238 {
1239     // Use the BMC hostname as the "System Name."
1240     char hostname[HOST_NAME_MAX + 1] = {};
1241     if (gethostname(hostname, HOST_NAME_MAX) != 0)
1242     {
1243         perror("System info parameter: system name");
1244     }
1245     return hostname;
1246 }
1247 
1248 static constexpr uint8_t paramRevision = 0x11;
1249 static constexpr size_t configParameterLength = 16;
1250 
1251 static constexpr size_t smallChunkSize = 14;
1252 static constexpr size_t fullChunkSize = 16;
1253 static constexpr uint8_t progressMask = 0x3;
1254 
1255 static constexpr uint8_t setComplete = 0x0;
1256 static constexpr uint8_t setInProgress = 0x1;
1257 static constexpr uint8_t commitWrite = 0x2;
1258 static uint8_t transferStatus = setComplete;
1259 
1260 static constexpr uint8_t configDataOverhead = 2;
1261 
1262 // For EFI based system, 256 bytes is recommended.
1263 static constexpr size_t maxBytesPerParameter = 256;
1264 
1265 namespace ipmi
1266 {
1267 constexpr Cc ccParmNotSupported = 0x80;
1268 constexpr Cc ccSetInProgressActive = 0x81;
1269 constexpr Cc ccSystemInfoParameterSetReadOnly = 0x82;
1270 
1271 static inline auto responseParmNotSupported()
1272 {
1273     return response(ccParmNotSupported);
1274 }
1275 static inline auto responseSetInProgressActive()
1276 {
1277     return response(ccSetInProgressActive);
1278 }
1279 static inline auto responseSystemInfoParameterSetReadOnly()
1280 {
1281     return response(ccSystemInfoParameterSetReadOnly);
1282 }
1283 } // namespace ipmi
1284 
1285 ipmi::RspType<uint8_t,                // Parameter revision
1286               std::optional<uint8_t>, // data1 / setSelector / ProgressStatus
1287               std::optional<std::vector<uint8_t>>> // data2-17
1288     ipmiAppGetSystemInfo(uint7_t reserved, bool getRevision,
1289                          uint8_t paramSelector, uint8_t setSelector,
1290                          uint8_t BlockSelector)
1291 {
1292     if (reserved || (paramSelector >= invalidParamSelectorStart &&
1293                      paramSelector <= invalidParamSelectorEnd))
1294     {
1295         return ipmi::responseInvalidFieldRequest();
1296     }
1297     if ((paramSelector >= oemCmdStart) && (paramSelector <= oemCmdEnd))
1298     {
1299         return ipmi::responseParmNotSupported();
1300     }
1301     if (getRevision)
1302     {
1303         return ipmi::responseSuccess(paramRevision, std::nullopt, std::nullopt);
1304     }
1305 
1306     if (paramSelector == 0)
1307     {
1308         return ipmi::responseSuccess(paramRevision, transferStatus,
1309                                      std::nullopt);
1310     }
1311 
1312     if (BlockSelector != 0) // 00h if parameter does not require a block number
1313     {
1314         return ipmi::responseParmNotSupported();
1315     }
1316 
1317     if (sysInfoParamStore == nullptr)
1318     {
1319         sysInfoParamStore = std::make_unique<SysInfoParamStore>();
1320         sysInfoParamStore->update(IPMI_SYSINFO_SYSTEM_NAME,
1321                                   sysInfoReadSystemName);
1322     }
1323 
1324     // Parameters other than Set In Progress are assumed to be strings.
1325     std::tuple<bool, std::string> ret =
1326         sysInfoParamStore->lookup(paramSelector);
1327     bool found = std::get<0>(ret);
1328     if (!found)
1329     {
1330         return ipmi::responseSensorInvalid();
1331     }
1332     std::string& paramString = std::get<1>(ret);
1333     std::vector<uint8_t> configData;
1334     size_t count = 0;
1335     if (setSelector == 0)
1336     {                               // First chunk has only 14 bytes.
1337         configData.emplace_back(0); // encoding
1338         configData.emplace_back(paramString.length()); // string length
1339         count = std::min(paramString.length(), smallChunkSize);
1340         configData.resize(count + configDataOverhead);
1341         std::copy_n(paramString.begin(), count,
1342                     configData.begin() + configDataOverhead); // 14 bytes chunk
1343 
1344         // Append zero's to remaining bytes
1345         if (configData.size() < configParameterLength)
1346         {
1347             std::fill_n(std::back_inserter(configData),
1348                         configParameterLength - configData.size(), 0x00);
1349         }
1350     }
1351     else
1352     {
1353         size_t offset = (setSelector * fullChunkSize) - configDataOverhead;
1354         if (offset >= paramString.length())
1355         {
1356             return ipmi::responseParmOutOfRange();
1357         }
1358         count = std::min(paramString.length() - offset, fullChunkSize);
1359         configData.resize(count);
1360         std::copy_n(paramString.begin() + offset, count,
1361                     configData.begin()); // 16 bytes chunk
1362     }
1363     return ipmi::responseSuccess(paramRevision, setSelector, configData);
1364 }
1365 
1366 ipmi::RspType<> ipmiAppSetSystemInfo(uint8_t paramSelector, uint8_t data1,
1367                                      std::vector<uint8_t> configData)
1368 {
1369     if (paramSelector >= invalidParamSelectorStart &&
1370         paramSelector <= invalidParamSelectorEnd)
1371     {
1372         return ipmi::responseInvalidFieldRequest();
1373     }
1374     if ((paramSelector >= oemCmdStart) && (paramSelector <= oemCmdEnd))
1375     {
1376         return ipmi::responseParmNotSupported();
1377     }
1378 
1379     if (paramSelector == 0)
1380     {
1381         // attempt to set the 'set in progress' value (in parameter #0)
1382         // when not in the set complete state.
1383         if ((transferStatus != setComplete) && (data1 == setInProgress))
1384         {
1385             return ipmi::responseSetInProgressActive();
1386         }
1387         // only following 2 states are supported
1388         if (data1 > setInProgress)
1389         {
1390             phosphor::logging::log<phosphor::logging::level::ERR>(
1391                 "illegal SetInProgress status");
1392             return ipmi::responseInvalidFieldRequest();
1393         }
1394 
1395         transferStatus = data1 & progressMask;
1396         return ipmi::responseSuccess();
1397     }
1398 
1399     if (configData.size() > configParameterLength)
1400     {
1401         return ipmi::responseInvalidFieldRequest();
1402     }
1403 
1404     // Append zero's to remaining bytes
1405     if (configData.size() < configParameterLength)
1406     {
1407         fill_n(back_inserter(configData),
1408                (configParameterLength - configData.size()), 0x00);
1409     }
1410 
1411     if (!sysInfoParamStore)
1412     {
1413         sysInfoParamStore = std::make_unique<SysInfoParamStore>();
1414         sysInfoParamStore->update(IPMI_SYSINFO_SYSTEM_NAME,
1415                                   sysInfoReadSystemName);
1416     }
1417 
1418     // lookup
1419     std::tuple<bool, std::string> ret =
1420         sysInfoParamStore->lookup(paramSelector);
1421     bool found = std::get<0>(ret);
1422     std::string& paramString = std::get<1>(ret);
1423     if (!found)
1424     {
1425         // parameter does not exist. Init new
1426         paramString = "";
1427     }
1428 
1429     uint8_t setSelector = data1;
1430     size_t count = 0;
1431     if (setSelector == 0) // First chunk has only 14 bytes.
1432     {
1433         size_t stringLen = configData.at(1); // string length
1434         // maxBytesPerParamter is 256. It will always be greater than stringLen
1435         // (unit8_t) if maxBytes changes in future, then following line is
1436         // needed.
1437         // stringLen = std::min(stringLen, maxBytesPerParameter);
1438         count = std::min(stringLen, smallChunkSize);
1439         count = std::min(count, configData.size());
1440         paramString.resize(stringLen); // reserve space
1441         std::copy_n(configData.begin() + configDataOverhead, count,
1442                     paramString.begin());
1443     }
1444     else
1445     {
1446         size_t offset = (setSelector * fullChunkSize) - configDataOverhead;
1447         if (offset >= paramString.length())
1448         {
1449             return ipmi::responseParmOutOfRange();
1450         }
1451         count = std::min(paramString.length() - offset, configData.size());
1452         std::copy_n(configData.begin(), count, paramString.begin() + offset);
1453     }
1454     sysInfoParamStore->update(paramSelector, paramString);
1455     return ipmi::responseSuccess();
1456 }
1457 
1458 #ifdef ENABLE_I2C_WHITELIST_CHECK
1459 inline std::vector<uint8_t> convertStringToData(const std::string& command)
1460 {
1461     std::istringstream iss(command);
1462     std::string token;
1463     std::vector<uint8_t> dataValue;
1464     while (std::getline(iss, token, ' '))
1465     {
1466         dataValue.emplace_back(
1467             static_cast<uint8_t>(std::stoul(token, nullptr, base_16)));
1468     }
1469     return dataValue;
1470 }
1471 
1472 static bool populateI2CMasterWRWhitelist()
1473 {
1474     nlohmann::json data = nullptr;
1475     std::ifstream jsonFile(i2cMasterWRWhitelistFile);
1476 
1477     if (!jsonFile.good())
1478     {
1479         log<level::WARNING>("i2c white list file not found!",
1480                             entry("FILE_NAME: %s", i2cMasterWRWhitelistFile));
1481         return false;
1482     }
1483 
1484     try
1485     {
1486         data = nlohmann::json::parse(jsonFile, nullptr, false);
1487     }
1488     catch (nlohmann::json::parse_error& e)
1489     {
1490         log<level::ERR>("Corrupted i2c white list config file",
1491                         entry("FILE_NAME: %s", i2cMasterWRWhitelistFile),
1492                         entry("MSG: %s", e.what()));
1493         return false;
1494     }
1495 
1496     try
1497     {
1498         // Example JSON Structure format
1499         // "filters": [
1500         //    {
1501         //      "Description": "Allow full read - ignore first byte write value
1502         //      for 0x40 to 0x4F",
1503         //      "busId": "0x01",
1504         //      "slaveAddr": "0x40",
1505         //      "slaveAddrMask": "0x0F",
1506         //      "command": "0x00",
1507         //      "commandMask": "0xFF"
1508         //    },
1509         //    {
1510         //      "Description": "Allow full read - first byte match 0x05 and
1511         //      ignore second byte",
1512         //      "busId": "0x01",
1513         //      "slaveAddr": "0x57",
1514         //      "slaveAddrMask": "0x00",
1515         //      "command": "0x05 0x00",
1516         //      "commandMask": "0x00 0xFF"
1517         //    },]
1518 
1519         nlohmann::json filters = data[filtersStr].get<nlohmann::json>();
1520         std::vector<i2cMasterWRWhitelist>& whitelist = getWRWhitelist();
1521         for (const auto& it : filters.items())
1522         {
1523             nlohmann::json filter = it.value();
1524             if (filter.is_null())
1525             {
1526                 log<level::ERR>(
1527                     "Corrupted I2C master write read whitelist config file",
1528                     entry("FILE_NAME: %s", i2cMasterWRWhitelistFile));
1529                 return false;
1530             }
1531             const std::vector<uint8_t>& writeData =
1532                 convertStringToData(filter[cmdStr].get<std::string>());
1533             const std::vector<uint8_t>& writeDataMask =
1534                 convertStringToData(filter[cmdMaskStr].get<std::string>());
1535             if (writeDataMask.size() != writeData.size())
1536             {
1537                 log<level::ERR>("I2C master write read whitelist filter "
1538                                 "mismatch for command & mask size");
1539                 return false;
1540             }
1541             whitelist.push_back(
1542                 {static_cast<uint8_t>(std::stoul(
1543                      filter[busIdStr].get<std::string>(), nullptr, base_16)),
1544                  static_cast<uint8_t>(
1545                      std::stoul(filter[slaveAddrStr].get<std::string>(),
1546                                 nullptr, base_16)),
1547                  static_cast<uint8_t>(
1548                      std::stoul(filter[slaveAddrMaskStr].get<std::string>(),
1549                                 nullptr, base_16)),
1550                  writeData, writeDataMask});
1551         }
1552         if (whitelist.size() != filters.size())
1553         {
1554             log<level::ERR>(
1555                 "I2C master write read whitelist filter size mismatch");
1556             return false;
1557         }
1558     }
1559     catch (std::exception& e)
1560     {
1561         log<level::ERR>("I2C master write read whitelist unexpected exception",
1562                         entry("ERROR=%s", e.what()));
1563         return false;
1564     }
1565     return true;
1566 }
1567 
1568 static inline bool isWriteDataWhitelisted(const std::vector<uint8_t>& data,
1569                                           const std::vector<uint8_t>& dataMask,
1570                                           const std::vector<uint8_t>& writeData)
1571 {
1572     std::vector<uint8_t> processedDataBuf(data.size());
1573     std::vector<uint8_t> processedReqBuf(dataMask.size());
1574     std::transform(writeData.begin(), writeData.end(), dataMask.begin(),
1575                    processedReqBuf.begin(), std::bit_or<uint8_t>());
1576     std::transform(data.begin(), data.end(), dataMask.begin(),
1577                    processedDataBuf.begin(), std::bit_or<uint8_t>());
1578 
1579     return (processedDataBuf == processedReqBuf);
1580 }
1581 
1582 static bool isCmdWhitelisted(uint8_t busId, uint8_t slaveAddr,
1583                              std::vector<uint8_t>& writeData)
1584 {
1585     std::vector<i2cMasterWRWhitelist>& whiteList = getWRWhitelist();
1586     for (const auto& wlEntry : whiteList)
1587     {
1588         if ((busId == wlEntry.busId) &&
1589             ((slaveAddr | wlEntry.slaveAddrMask) ==
1590              (wlEntry.slaveAddr | wlEntry.slaveAddrMask)))
1591         {
1592             const std::vector<uint8_t>& dataMask = wlEntry.dataMask;
1593             // Skip as no-match, if requested write data is more than the
1594             // write data mask size
1595             if (writeData.size() > dataMask.size())
1596             {
1597                 continue;
1598             }
1599             if (isWriteDataWhitelisted(wlEntry.data, dataMask, writeData))
1600             {
1601                 return true;
1602             }
1603         }
1604     }
1605     return false;
1606 }
1607 #else
1608 static bool populateI2CMasterWRWhitelist()
1609 {
1610     log<level::INFO>(
1611         "I2C_WHITELIST_CHECK is disabled, do not populate whitelist");
1612     return true;
1613 }
1614 #endif // ENABLE_I2C_WHITELIST_CHECK
1615 
1616 /** @brief implements master write read IPMI command which can be used for
1617  * low-level I2C/SMBus write, read or write-read access
1618  *  @param isPrivateBus -to indicate private bus usage
1619  *  @param busId - bus id
1620  *  @param channelNum - channel number
1621  *  @param reserved - skip 1 bit
1622  *  @param slaveAddr - slave address
1623  *  @param read count - number of bytes to be read
1624  *  @param writeData - data to be written
1625  *
1626  *  @returns IPMI completion code plus response data
1627  *   - readData - i2c response data
1628  */
1629 ipmi::RspType<std::vector<uint8_t>>
1630     ipmiMasterWriteRead(bool isPrivateBus, uint3_t busId, uint4_t channelNum,
1631                         bool reserved, uint7_t slaveAddr, uint8_t readCount,
1632                         std::vector<uint8_t> writeData)
1633 {
1634     if (readCount > maxIPMIWriteReadSize)
1635     {
1636         log<level::ERR>("Master write read command: Read count exceeds limit");
1637         return ipmi::responseParmOutOfRange();
1638     }
1639     const size_t writeCount = writeData.size();
1640     if (!readCount && !writeCount)
1641     {
1642         log<level::ERR>("Master write read command: Read & write count are 0");
1643         return ipmi::responseInvalidFieldRequest();
1644     }
1645 #ifdef ENABLE_I2C_WHITELIST_CHECK
1646     if (!isCmdWhitelisted(static_cast<uint8_t>(busId),
1647                           static_cast<uint8_t>(slaveAddr), writeData))
1648     {
1649         log<level::ERR>("Master write read request blocked!",
1650                         entry("BUS=%d", static_cast<uint8_t>(busId)),
1651                         entry("ADDR=0x%x", static_cast<uint8_t>(slaveAddr)));
1652         return ipmi::responseInvalidFieldRequest();
1653     }
1654 #endif // ENABLE_I2C_WHITELIST_CHECK
1655     std::vector<uint8_t> readBuf(readCount);
1656     std::string i2cBus =
1657         "/dev/i2c-" + std::to_string(static_cast<uint8_t>(busId));
1658 
1659     ipmi::Cc ret = ipmi::i2cWriteRead(i2cBus, static_cast<uint8_t>(slaveAddr),
1660                                       writeData, readBuf);
1661     if (ret != ipmi::ccSuccess)
1662     {
1663         return ipmi::response(ret);
1664     }
1665     return ipmi::responseSuccess(readBuf);
1666 }
1667 
1668 void register_netfn_app_functions()
1669 {
1670     // <Get Device ID>
1671     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp,
1672                           ipmi::app::cmdGetDeviceId, ipmi::Privilege::User,
1673                           ipmiAppGetDeviceId);
1674 
1675     // <Get BT Interface Capabilities>
1676     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp,
1677                           ipmi::app::cmdGetBtIfaceCapabilities,
1678                           ipmi::Privilege::User, ipmiAppGetBtCapabilities);
1679 
1680     // <Reset Watchdog Timer>
1681     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp,
1682                           ipmi::app::cmdResetWatchdogTimer,
1683                           ipmi::Privilege::Operator, ipmiAppResetWatchdogTimer);
1684 
1685     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp,
1686                           ipmi::app::cmdGetSessionInfo, ipmi::Privilege::User,
1687                           ipmiAppGetSessionInfo);
1688 
1689     // <Set Watchdog Timer>
1690     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp,
1691                           ipmi::app::cmdSetWatchdogTimer,
1692                           ipmi::Privilege::Operator, ipmiSetWatchdogTimer);
1693 
1694     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp,
1695                           ipmi::app::cmdCloseSession, ipmi::Privilege::Callback,
1696                           ipmiAppCloseSession);
1697 
1698     // <Get Watchdog Timer>
1699     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp,
1700                           ipmi::app::cmdGetWatchdogTimer, ipmi::Privilege::User,
1701                           ipmiGetWatchdogTimer);
1702 
1703     // <Get Self Test Results>
1704     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp,
1705                           ipmi::app::cmdGetSelfTestResults,
1706                           ipmi::Privilege::User, ipmiAppGetSelfTestResults);
1707 
1708     // <Get Device GUID>
1709     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp,
1710                           ipmi::app::cmdGetDeviceGuid, ipmi::Privilege::User,
1711                           ipmiAppGetDeviceGuid);
1712 
1713     // <Set ACPI Power State>
1714     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp,
1715                           ipmi::app::cmdSetAcpiPowerState,
1716                           ipmi::Privilege::Admin, ipmiSetAcpiPowerState);
1717     // <Get ACPI Power State>
1718     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp,
1719                           ipmi::app::cmdGetAcpiPowerState,
1720                           ipmi::Privilege::User, ipmiGetAcpiPowerState);
1721 
1722     // Note: For security reason, this command will be registered only when
1723     // there are proper I2C Master write read whitelist
1724     if (populateI2CMasterWRWhitelist())
1725     {
1726         // Note: For security reasons, registering master write read as admin
1727         // privilege command, even though IPMI 2.0 specification allows it as
1728         // operator privilege.
1729         ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp,
1730                               ipmi::app::cmdMasterWriteRead,
1731                               ipmi::Privilege::Admin, ipmiMasterWriteRead);
1732     }
1733 
1734     // <Get System GUID Command>
1735     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp,
1736                           ipmi::app::cmdGetSystemGuid, ipmi::Privilege::User,
1737                           ipmiAppGetSystemGuid);
1738 
1739     // <Get Channel Cipher Suites Command>
1740     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp,
1741                           ipmi::app::cmdGetChannelCipherSuites,
1742                           ipmi::Privilege::None, getChannelCipherSuites);
1743 
1744     // <Get System Info Command>
1745     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp,
1746                           ipmi::app::cmdGetSystemInfoParameters,
1747                           ipmi::Privilege::User, ipmiAppGetSystemInfo);
1748     // <Set System Info Command>
1749     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp,
1750                           ipmi::app::cmdSetSystemInfoParameters,
1751                           ipmi::Privilege::Admin, ipmiAppSetSystemInfo);
1752     return;
1753 }
1754