1 #include "apphandler.hpp"
2 
3 #include "app/channel.hpp"
4 #include "app/watchdog.hpp"
5 #include "ipmid.hpp"
6 #include "sys_info_param.hpp"
7 #include "transporthandler.hpp"
8 
9 #include <arpa/inet.h>
10 #include <ipmid/api.h>
11 #include <limits.h>
12 #include <mapper.h>
13 #include <systemd/sd-bus.h>
14 #include <unistd.h>
15 
16 #include <algorithm>
17 #include <array>
18 #include <cstddef>
19 #include <fstream>
20 #include <ipmid/types.hpp>
21 #include <ipmid/utils.hpp>
22 #include <memory>
23 #include <nlohmann/json.hpp>
24 #include <phosphor-logging/elog-errors.hpp>
25 #include <phosphor-logging/log.hpp>
26 #include <sdbusplus/message/types.hpp>
27 #include <string>
28 #include <tuple>
29 #include <vector>
30 #include <xyz/openbmc_project/Common/error.hpp>
31 #include <xyz/openbmc_project/Control/Power/ACPIPowerState/server.hpp>
32 #include <xyz/openbmc_project/Software/Activation/server.hpp>
33 #include <xyz/openbmc_project/Software/Version/server.hpp>
34 #include <xyz/openbmc_project/State/BMC/server.hpp>
35 
36 #if __has_include(<filesystem>)
37 #include <filesystem>
38 #elif __has_include(<experimental/filesystem>)
39 #include <experimental/filesystem>
40 namespace std
41 {
42 // splice experimental::filesystem into std
43 namespace filesystem = std::experimental::filesystem;
44 } // namespace std
45 #else
46 #error filesystem not available
47 #endif
48 
49 extern sd_bus* bus;
50 
51 constexpr auto bmc_state_interface = "xyz.openbmc_project.State.BMC";
52 constexpr auto bmc_state_property = "CurrentBMCState";
53 constexpr auto bmc_interface = "xyz.openbmc_project.Inventory.Item.Bmc";
54 constexpr auto bmc_guid_interface = "xyz.openbmc_project.Common.UUID";
55 constexpr auto bmc_guid_property = "UUID";
56 constexpr auto bmc_guid_len = 16;
57 
58 static constexpr auto redundancyIntf =
59     "xyz.openbmc_project.Software.RedundancyPriority";
60 static constexpr auto versionIntf = "xyz.openbmc_project.Software.Version";
61 static constexpr auto activationIntf =
62     "xyz.openbmc_project.Software.Activation";
63 static constexpr auto softwareRoot = "/xyz/openbmc_project/software";
64 
65 void register_netfn_app_functions() __attribute__((constructor));
66 
67 using namespace phosphor::logging;
68 using namespace sdbusplus::xyz::openbmc_project::Common::Error;
69 using Version = sdbusplus::xyz::openbmc_project::Software::server::Version;
70 using Activation =
71     sdbusplus::xyz::openbmc_project::Software::server::Activation;
72 using BMC = sdbusplus::xyz::openbmc_project::State::server::BMC;
73 namespace fs = std::filesystem;
74 namespace variant_ns = sdbusplus::message::variant_ns;
75 
76 // Offset in get device id command.
77 typedef struct
78 {
79     uint8_t id;
80     uint8_t revision;
81     uint8_t fw[2];
82     uint8_t ipmi_ver;
83     uint8_t addn_dev_support;
84     uint8_t manuf_id[3];
85     uint8_t prod_id[2];
86     uint8_t aux[4];
87 } __attribute__((packed)) ipmi_device_id_t;
88 
89 /**
90  * @brief Returns the Version info from primary s/w object
91  *
92  * Get the Version info from the active s/w object which is having high
93  * "Priority" value(a smaller number is a higher priority) and "Purpose"
94  * is "BMC" from the list of all s/w objects those are implementing
95  * RedundancyPriority interface from the given softwareRoot path.
96  *
97  * @return On success returns the Version info from primary s/w object.
98  *
99  */
100 std::string getActiveSoftwareVersionInfo()
101 {
102     sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()};
103 
104     std::string revision{};
105     auto objectTree =
106         ipmi::getAllDbusObjects(bus, softwareRoot, redundancyIntf, "");
107     if (objectTree.empty())
108     {
109         log<level::ERR>("No Obj has implemented the s/w redundancy interface",
110                         entry("INTERFACE=%s", redundancyIntf));
111         elog<InternalFailure>();
112     }
113 
114     auto objectFound = false;
115     for (auto& softObject : objectTree)
116     {
117         auto service = ipmi::getService(bus, redundancyIntf, softObject.first);
118         auto objValueTree = ipmi::getManagedObjects(bus, service, softwareRoot);
119 
120         auto minPriority = 0xFF;
121         for (const auto& objIter : objValueTree)
122         {
123             try
124             {
125                 auto& intfMap = objIter.second;
126                 auto& redundancyPriorityProps = intfMap.at(redundancyIntf);
127                 auto& versionProps = intfMap.at(versionIntf);
128                 auto& activationProps = intfMap.at(activationIntf);
129                 auto priority = variant_ns::get<uint8_t>(
130                     redundancyPriorityProps.at("Priority"));
131                 auto purpose =
132                     variant_ns::get<std::string>(versionProps.at("Purpose"));
133                 auto activation = variant_ns::get<std::string>(
134                     activationProps.at("Activation"));
135                 auto version =
136                     variant_ns::get<std::string>(versionProps.at("Version"));
137                 if ((Version::convertVersionPurposeFromString(purpose) ==
138                      Version::VersionPurpose::BMC) &&
139                     (Activation::convertActivationsFromString(activation) ==
140                      Activation::Activations::Active))
141                 {
142                     if (priority < minPriority)
143                     {
144                         minPriority = priority;
145                         objectFound = true;
146                         revision = std::move(version);
147                     }
148                 }
149             }
150             catch (const std::exception& e)
151             {
152                 log<level::ERR>(e.what());
153             }
154         }
155     }
156 
157     if (!objectFound)
158     {
159         log<level::ERR>("Could not found an BMC software Object");
160         elog<InternalFailure>();
161     }
162 
163     return revision;
164 }
165 
166 bool getCurrentBmcState()
167 {
168     sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()};
169 
170     // Get the Inventory object implementing the BMC interface
171     ipmi::DbusObjectInfo bmcObject =
172         ipmi::getDbusObject(bus, bmc_state_interface);
173     auto variant =
174         ipmi::getDbusProperty(bus, bmcObject.second, bmcObject.first,
175                               bmc_state_interface, bmc_state_property);
176 
177     return variant_ns::holds_alternative<std::string>(variant) &&
178            BMC::convertBMCStateFromString(
179                variant_ns::get<std::string>(variant)) == BMC::BMCState::Ready;
180 }
181 
182 namespace acpi_state
183 {
184 using namespace sdbusplus::xyz::openbmc_project::Control::Power::server;
185 
186 const static constexpr char* acpiObjPath =
187     "/xyz/openbmc_project/control/host0/acpi_power_state";
188 const static constexpr char* acpiInterface =
189     "xyz.openbmc_project.Control.Power.ACPIPowerState";
190 const static constexpr char* sysACPIProp = "SysACPIStatus";
191 const static constexpr char* devACPIProp = "DevACPIStatus";
192 
193 enum class PowerStateType : uint8_t
194 {
195     sysPowerState = 0x00,
196     devPowerState = 0x01,
197 };
198 
199 // Defined in 20.6 of ipmi doc
200 enum class PowerState : uint8_t
201 {
202     s0G0D0 = 0x00,
203     s1D1 = 0x01,
204     s2D2 = 0x02,
205     s3D3 = 0x03,
206     s4 = 0x04,
207     s5G2 = 0x05,
208     s4S5 = 0x06,
209     g3 = 0x07,
210     sleep = 0x08,
211     g1Sleep = 0x09,
212     override = 0x0a,
213     legacyOn = 0x20,
214     legacyOff = 0x21,
215     unknown = 0x2a,
216     noChange = 0x7f,
217 };
218 
219 static constexpr uint8_t stateChanged = 0x80;
220 
221 struct ACPIState
222 {
223     uint8_t sysACPIState;
224     uint8_t devACPIState;
225 } __attribute__((packed));
226 
227 std::map<ACPIPowerState::ACPI, PowerState> dbusToIPMI = {
228     {ACPIPowerState::ACPI::S0_G0_D0, PowerState::s0G0D0},
229     {ACPIPowerState::ACPI::S1_D1, PowerState::s1D1},
230     {ACPIPowerState::ACPI::S2_D2, PowerState::s2D2},
231     {ACPIPowerState::ACPI::S3_D3, PowerState::s3D3},
232     {ACPIPowerState::ACPI::S4, PowerState::s4},
233     {ACPIPowerState::ACPI::S5_G2, PowerState::s5G2},
234     {ACPIPowerState::ACPI::S4_S5, PowerState::s4S5},
235     {ACPIPowerState::ACPI::G3, PowerState::g3},
236     {ACPIPowerState::ACPI::SLEEP, PowerState::sleep},
237     {ACPIPowerState::ACPI::G1_SLEEP, PowerState::g1Sleep},
238     {ACPIPowerState::ACPI::OVERRIDE, PowerState::override},
239     {ACPIPowerState::ACPI::LEGACY_ON, PowerState::legacyOn},
240     {ACPIPowerState::ACPI::LEGACY_OFF, PowerState::legacyOff},
241     {ACPIPowerState::ACPI::Unknown, PowerState::unknown}};
242 
243 bool isValidACPIState(acpi_state::PowerStateType type, uint8_t state)
244 {
245     if (type == acpi_state::PowerStateType::sysPowerState)
246     {
247         if ((state <= static_cast<uint8_t>(acpi_state::PowerState::override)) ||
248             (state == static_cast<uint8_t>(acpi_state::PowerState::legacyOn)) ||
249             (state ==
250              static_cast<uint8_t>(acpi_state::PowerState::legacyOff)) ||
251             (state == static_cast<uint8_t>(acpi_state::PowerState::unknown)) ||
252             (state == static_cast<uint8_t>(acpi_state::PowerState::noChange)))
253         {
254             return true;
255         }
256         else
257         {
258             return false;
259         }
260     }
261     else if (type == acpi_state::PowerStateType::devPowerState)
262     {
263         if ((state <= static_cast<uint8_t>(acpi_state::PowerState::s3D3)) ||
264             (state == static_cast<uint8_t>(acpi_state::PowerState::unknown)) ||
265             (state == static_cast<uint8_t>(acpi_state::PowerState::noChange)))
266         {
267             return true;
268         }
269         else
270         {
271             return false;
272         }
273     }
274     else
275     {
276         return false;
277     }
278     return false;
279 }
280 } // namespace acpi_state
281 
282 ipmi_ret_t ipmi_app_set_acpi_power_state(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
283                                          ipmi_request_t request,
284                                          ipmi_response_t response,
285                                          ipmi_data_len_t data_len,
286                                          ipmi_context_t context)
287 {
288     auto s = static_cast<uint8_t>(acpi_state::PowerState::unknown);
289     ipmi_ret_t rc = IPMI_CC_OK;
290 
291     sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()};
292 
293     auto value = acpi_state::ACPIPowerState::ACPI::Unknown;
294 
295     auto* req = reinterpret_cast<acpi_state::ACPIState*>(request);
296 
297     if (*data_len != sizeof(acpi_state::ACPIState))
298     {
299         log<level::ERR>("set_acpi invalid len");
300         *data_len = 0;
301         return IPMI_CC_REQ_DATA_LEN_INVALID;
302     }
303 
304     *data_len = 0;
305 
306     if (req->sysACPIState & acpi_state::stateChanged)
307     {
308         // set system power state
309         s = req->sysACPIState & ~acpi_state::stateChanged;
310 
311         if (!acpi_state::isValidACPIState(
312                 acpi_state::PowerStateType::sysPowerState, s))
313         {
314             log<level::ERR>("set_acpi_power sys invalid input",
315                             entry("S=%x", s));
316             return IPMI_CC_PARM_OUT_OF_RANGE;
317         }
318 
319         // valid input
320         if (s == static_cast<uint8_t>(acpi_state::PowerState::noChange))
321         {
322             log<level::DEBUG>("No change for system power state");
323         }
324         else
325         {
326             auto found = std::find_if(
327                 acpi_state::dbusToIPMI.begin(), acpi_state::dbusToIPMI.end(),
328                 [&s](const auto& iter) {
329                     return (static_cast<uint8_t>(iter.second) == s);
330                 });
331 
332             value = found->first;
333 
334             try
335             {
336                 auto acpiObject =
337                     ipmi::getDbusObject(bus, acpi_state::acpiInterface);
338                 ipmi::setDbusProperty(bus, acpiObject.second, acpiObject.first,
339                                       acpi_state::acpiInterface,
340                                       acpi_state::sysACPIProp,
341                                       convertForMessage(value));
342             }
343             catch (const InternalFailure& e)
344             {
345                 log<level::ERR>("Failed in set ACPI system property",
346                                 entry("EXCEPTION=%s", e.what()));
347                 return IPMI_CC_UNSPECIFIED_ERROR;
348             }
349         }
350     }
351     else
352     {
353         log<level::DEBUG>("Do not change system power state");
354     }
355 
356     if (req->devACPIState & acpi_state::stateChanged)
357     {
358         // set device power state
359         s = req->devACPIState & ~acpi_state::stateChanged;
360         if (!acpi_state::isValidACPIState(
361                 acpi_state::PowerStateType::devPowerState, s))
362         {
363             log<level::ERR>("set_acpi_power dev invalid input",
364                             entry("S=%x", s));
365             return IPMI_CC_PARM_OUT_OF_RANGE;
366         }
367 
368         // valid input
369         if (s == static_cast<uint8_t>(acpi_state::PowerState::noChange))
370         {
371             log<level::DEBUG>("No change for device power state");
372         }
373         else
374         {
375             auto found = std::find_if(
376                 acpi_state::dbusToIPMI.begin(), acpi_state::dbusToIPMI.end(),
377                 [&s](const auto& iter) {
378                     return (static_cast<uint8_t>(iter.second) == s);
379                 });
380 
381             value = found->first;
382 
383             try
384             {
385                 auto acpiObject =
386                     ipmi::getDbusObject(bus, acpi_state::acpiInterface);
387                 ipmi::setDbusProperty(bus, acpiObject.second, acpiObject.first,
388                                       acpi_state::acpiInterface,
389                                       acpi_state::devACPIProp,
390                                       convertForMessage(value));
391             }
392             catch (const InternalFailure& e)
393             {
394                 log<level::ERR>("Failed in set ACPI device property",
395                                 entry("EXCEPTION=%s", e.what()));
396                 return IPMI_CC_UNSPECIFIED_ERROR;
397             }
398         }
399     }
400     else
401     {
402         log<level::DEBUG>("Do not change device power state");
403     }
404 
405     return rc;
406 }
407 
408 ipmi_ret_t ipmi_app_get_acpi_power_state(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
409                                          ipmi_request_t request,
410                                          ipmi_response_t response,
411                                          ipmi_data_len_t data_len,
412                                          ipmi_context_t context)
413 {
414     ipmi_ret_t rc = IPMI_CC_OK;
415 
416     auto* res = reinterpret_cast<acpi_state::ACPIState*>(response);
417 
418     sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()};
419 
420     *data_len = 0;
421 
422     try
423     {
424         auto acpiObject = ipmi::getDbusObject(bus, acpi_state::acpiInterface);
425 
426         auto sysACPIVal = ipmi::getDbusProperty(
427             bus, acpiObject.second, acpiObject.first, acpi_state::acpiInterface,
428             acpi_state::sysACPIProp);
429         auto sysACPI = acpi_state::ACPIPowerState::convertACPIFromString(
430             variant_ns::get<std::string>(sysACPIVal));
431         res->sysACPIState =
432             static_cast<uint8_t>(acpi_state::dbusToIPMI.at(sysACPI));
433 
434         auto devACPIVal = ipmi::getDbusProperty(
435             bus, acpiObject.second, acpiObject.first, acpi_state::acpiInterface,
436             acpi_state::devACPIProp);
437         auto devACPI = acpi_state::ACPIPowerState::convertACPIFromString(
438             variant_ns::get<std::string>(devACPIVal));
439         res->devACPIState =
440             static_cast<uint8_t>(acpi_state::dbusToIPMI.at(devACPI));
441 
442         *data_len = sizeof(acpi_state::ACPIState);
443     }
444     catch (const InternalFailure& e)
445     {
446         log<level::ERR>("Failed in get ACPI property");
447         return IPMI_CC_UNSPECIFIED_ERROR;
448     }
449     return rc;
450 }
451 
452 typedef struct
453 {
454     char major;
455     char minor;
456     uint16_t d[2];
457 } rev_t;
458 
459 /* Currently supports the vx.x-x-[-x] and v1.x.x-x-[-x] format. It will     */
460 /* return -1 if not in those formats, this routine knows how to parse       */
461 /* version = v0.6-19-gf363f61-dirty                                         */
462 /*            ^ ^ ^^          ^                                             */
463 /*            | |  |----------|-- additional details                        */
464 /*            | |---------------- Minor                                     */
465 /*            |------------------ Major                                     */
466 /* and version = v1.99.10-113-g65edf7d-r3-0-g9e4f715                        */
467 /*                ^ ^  ^^ ^                                                 */
468 /*                | |  |--|---------- additional details                    */
469 /*                | |---------------- Minor                                 */
470 /*                |------------------ Major                                 */
471 /* Additional details : If the option group exists it will force Auxiliary  */
472 /* Firmware Revision Information 4th byte to 1 indicating the build was     */
473 /* derived with additional edits                                            */
474 int convert_version(const char* p, rev_t* rev)
475 {
476     std::string s(p);
477     std::string token;
478     uint16_t commits;
479 
480     auto location = s.find_first_of('v');
481     if (location != std::string::npos)
482     {
483         s = s.substr(location + 1);
484     }
485 
486     if (!s.empty())
487     {
488         location = s.find_first_of(".");
489         if (location != std::string::npos)
490         {
491             rev->major =
492                 static_cast<char>(std::stoi(s.substr(0, location), 0, 16));
493             token = s.substr(location + 1);
494         }
495 
496         if (!token.empty())
497         {
498             location = token.find_first_of(".-");
499             if (location != std::string::npos)
500             {
501                 rev->minor = static_cast<char>(
502                     std::stoi(token.substr(0, location), 0, 16));
503                 token = token.substr(location + 1);
504             }
505         }
506 
507         // Capture the number of commits on top of the minor tag.
508         // I'm using BE format like the ipmi spec asked for
509         location = token.find_first_of(".-");
510         if (!token.empty())
511         {
512             commits = std::stoi(token.substr(0, location), 0, 16);
513             rev->d[0] = (commits >> 8) | (commits << 8);
514 
515             // commit number we skip
516             location = token.find_first_of(".-");
517             if (location != std::string::npos)
518             {
519                 token = token.substr(location + 1);
520             }
521         }
522         else
523         {
524             rev->d[0] = 0;
525         }
526 
527         if (location != std::string::npos)
528         {
529             token = token.substr(location + 1);
530         }
531 
532         // Any value of the optional parameter forces it to 1
533         location = token.find_first_of(".-");
534         if (location != std::string::npos)
535         {
536             token = token.substr(location + 1);
537         }
538         commits = (!token.empty()) ? 1 : 0;
539 
540         // We do this operation to get this displayed in least significant bytes
541         // of ipmitool device id command.
542         rev->d[1] = (commits >> 8) | (commits << 8);
543     }
544 
545     return 0;
546 }
547 
548 ipmi_ret_t ipmi_app_get_device_id(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
549                                   ipmi_request_t request,
550                                   ipmi_response_t response,
551                                   ipmi_data_len_t data_len,
552                                   ipmi_context_t context)
553 {
554     ipmi_ret_t rc = IPMI_CC_OK;
555     int r = -1;
556     rev_t rev = {0};
557     static ipmi_device_id_t dev_id{};
558     static bool dev_id_initialized = false;
559     const char* filename = "/usr/share/ipmi-providers/dev_id.json";
560     constexpr auto ipmiDevIdStateShift = 7;
561     constexpr auto ipmiDevIdFw1Mask = ~(1 << ipmiDevIdStateShift);
562 
563     // Data length
564     *data_len = sizeof(dev_id);
565 
566     if (!dev_id_initialized)
567     {
568         try
569         {
570             auto version = getActiveSoftwareVersionInfo();
571             r = convert_version(version.c_str(), &rev);
572         }
573         catch (const std::exception& e)
574         {
575             log<level::ERR>(e.what());
576         }
577 
578         if (r >= 0)
579         {
580             // bit7 identifies if the device is available
581             // 0=normal operation
582             // 1=device firmware, SDR update,
583             // or self-initialization in progress.
584             // The availability may change in run time, so mask here
585             // and initialize later.
586             dev_id.fw[0] = rev.major & ipmiDevIdFw1Mask;
587 
588             rev.minor = (rev.minor > 99 ? 99 : rev.minor);
589             dev_id.fw[1] = rev.minor % 10 + (rev.minor / 10) * 16;
590             std::memcpy(&dev_id.aux, rev.d, 4);
591         }
592 
593         // IPMI Spec version 2.0
594         dev_id.ipmi_ver = 2;
595 
596         std::ifstream dev_id_file(filename);
597         if (dev_id_file.is_open())
598         {
599             auto data = nlohmann::json::parse(dev_id_file, nullptr, false);
600             if (!data.is_discarded())
601             {
602                 dev_id.id = data.value("id", 0);
603                 dev_id.revision = data.value("revision", 0);
604                 dev_id.addn_dev_support = data.value("addn_dev_support", 0);
605                 dev_id.manuf_id[2] = data.value("manuf_id", 0) >> 16;
606                 dev_id.manuf_id[1] = data.value("manuf_id", 0) >> 8;
607                 dev_id.manuf_id[0] = data.value("manuf_id", 0);
608                 dev_id.prod_id[1] = data.value("prod_id", 0) >> 8;
609                 dev_id.prod_id[0] = data.value("prod_id", 0);
610                 dev_id.aux[3] = data.value("aux", 0);
611                 dev_id.aux[2] = data.value("aux", 0) >> 8;
612                 dev_id.aux[1] = data.value("aux", 0) >> 16;
613                 dev_id.aux[0] = data.value("aux", 0) >> 24;
614 
615                 // Don't read the file every time if successful
616                 dev_id_initialized = true;
617             }
618             else
619             {
620                 log<level::ERR>("Device ID JSON parser failure");
621                 rc = IPMI_CC_UNSPECIFIED_ERROR;
622             }
623         }
624         else
625         {
626             log<level::ERR>("Device ID file not found");
627             rc = IPMI_CC_UNSPECIFIED_ERROR;
628         }
629     }
630 
631     // Set availability to the actual current BMC state
632     dev_id.fw[0] &= ipmiDevIdFw1Mask;
633     if (!getCurrentBmcState())
634     {
635         dev_id.fw[0] |= (1 << ipmiDevIdStateShift);
636     }
637 
638     // Pack the actual response
639     std::memcpy(response, &dev_id, *data_len);
640 
641     return rc;
642 }
643 
644 ipmi_ret_t ipmi_app_get_self_test_results(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
645                                           ipmi_request_t request,
646                                           ipmi_response_t response,
647                                           ipmi_data_len_t data_len,
648                                           ipmi_context_t context)
649 {
650     ipmi_ret_t rc = IPMI_CC_OK;
651 
652     // Byte 2:
653     //  55h - No error.
654     //  56h - Self Test function not implemented in this controller.
655     //  57h - Corrupted or inaccesssible data or devices.
656     //  58h - Fatal hardware error.
657     //  FFh - reserved.
658     //  all other: Device-specific 'internal failure'.
659     //  Byte 3:
660     //      For byte 2 = 55h, 56h, FFh:     00h
661     //      For byte 2 = 58h, all other:    Device-specific
662     //      For byte 2 = 57h:   self-test error bitfield.
663     //      Note: returning 57h does not imply that all test were run.
664     //      [7] 1b = Cannot access SEL device.
665     //      [6] 1b = Cannot access SDR Repository.
666     //      [5] 1b = Cannot access BMC FRU device.
667     //      [4] 1b = IPMB signal lines do not respond.
668     //      [3] 1b = SDR Repository empty.
669     //      [2] 1b = Internal Use Area of BMC FRU corrupted.
670     //      [1] 1b = controller update 'boot block' firmware corrupted.
671     //      [0] 1b = controller operational firmware corrupted.
672 
673     char selftestresults[2] = {0};
674 
675     *data_len = 2;
676 
677     selftestresults[0] = 0x56;
678     selftestresults[1] = 0;
679 
680     std::memcpy(response, selftestresults, *data_len);
681 
682     return rc;
683 }
684 
685 ipmi_ret_t ipmi_app_get_device_guid(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
686                                     ipmi_request_t request,
687                                     ipmi_response_t response,
688                                     ipmi_data_len_t data_len,
689                                     ipmi_context_t context)
690 {
691     const char* objname = "/org/openbmc/control/chassis0";
692     const char* iface = "org.freedesktop.DBus.Properties";
693     const char* chassis_iface = "org.openbmc.control.Chassis";
694     sd_bus_message* reply = NULL;
695     sd_bus_error error = SD_BUS_ERROR_NULL;
696     int r = 0;
697     char* uuid = NULL;
698     char* busname = NULL;
699 
700     // UUID is in RFC4122 format. Ex: 61a39523-78f2-11e5-9862-e6402cfc3223
701     // Per IPMI Spec 2.0 need to convert to 16 hex bytes and reverse the byte
702     // order
703     // Ex: 0x2332fc2c40e66298e511f2782395a361
704 
705     const int resp_size = 16;     // Response is 16 hex bytes per IPMI Spec
706     uint8_t resp_uuid[resp_size]; // Array to hold the formatted response
707     // Point resp end of array to save in reverse order
708     int resp_loc = resp_size - 1;
709     int i = 0;
710     char* tokptr = NULL;
711     char* id_octet = NULL;
712     size_t total_uuid_size = 0;
713     // 1 byte of resp is built from 2 chars of uuid.
714     constexpr size_t max_uuid_size = 2 * resp_size;
715 
716     // Status code.
717     ipmi_ret_t rc = IPMI_CC_OK;
718     *data_len = 0;
719 
720     // Call Get properties method with the interface and property name
721     r = mapper_get_service(bus, objname, &busname);
722     if (r < 0)
723     {
724         log<level::ERR>("Failed to get bus name", entry("BUS=%s", objname),
725                         entry("ERRNO=0x%X", -r));
726         goto finish;
727     }
728     r = sd_bus_call_method(bus, busname, objname, iface, "Get", &error, &reply,
729                            "ss", chassis_iface, "uuid");
730     if (r < 0)
731     {
732         log<level::ERR>("Failed to call Get Method", entry("ERRNO=0x%X", -r));
733         rc = IPMI_CC_UNSPECIFIED_ERROR;
734         goto finish;
735     }
736 
737     r = sd_bus_message_read(reply, "v", "s", &uuid);
738     if (r < 0 || uuid == NULL)
739     {
740         log<level::ERR>("Failed to get a response", entry("ERRNO=0x%X", -r));
741         rc = IPMI_CC_RESPONSE_ERROR;
742         goto finish;
743     }
744 
745     // Traverse the UUID
746     // Get the UUID octects separated by dash
747     id_octet = strtok_r(uuid, "-", &tokptr);
748 
749     if (id_octet == NULL)
750     {
751         // Error
752         log<level::ERR>("Unexpected UUID format", entry("UUID=%s", uuid));
753         rc = IPMI_CC_RESPONSE_ERROR;
754         goto finish;
755     }
756 
757     while (id_octet != NULL)
758     {
759         // Calculate the octet string size since it varies
760         // Divide it by 2 for the array size since 1 byte is built from 2 chars
761         int tmp_size = strlen(id_octet) / 2;
762 
763         // Check if total UUID size has been exceeded
764         if ((total_uuid_size += strlen(id_octet)) > max_uuid_size)
765         {
766             // Error - UUID too long to store
767             log<level::ERR>("UUID too long", entry("UUID=%s", uuid));
768             rc = IPMI_CC_RESPONSE_ERROR;
769             goto finish;
770         }
771 
772         for (i = 0; i < tmp_size; i++)
773         {
774             // Holder of the 2 chars that will become a byte
775             char tmp_array[3] = {0};
776             strncpy(tmp_array, id_octet, 2); // 2 chars at a time
777 
778             int resp_byte = strtoul(tmp_array, NULL, 16); // Convert to hex byte
779             // Copy end to first
780             std::memcpy((void*)&resp_uuid[resp_loc], &resp_byte, 1);
781             resp_loc--;
782             id_octet += 2; // Finished with the 2 chars, advance
783         }
784         id_octet = strtok_r(NULL, "-", &tokptr); // Get next octet
785     }
786 
787     // Data length
788     *data_len = resp_size;
789 
790     // Pack the actual response
791     std::memcpy(response, &resp_uuid, *data_len);
792 
793 finish:
794     sd_bus_error_free(&error);
795     reply = sd_bus_message_unref(reply);
796     free(busname);
797 
798     return rc;
799 }
800 
801 ipmi_ret_t ipmi_app_get_bt_capabilities(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
802                                         ipmi_request_t request,
803                                         ipmi_response_t response,
804                                         ipmi_data_len_t data_len,
805                                         ipmi_context_t context)
806 {
807 
808     // Status code.
809     ipmi_ret_t rc = IPMI_CC_OK;
810 
811     // Per IPMI 2.0 spec, the input and output buffer size must be the max
812     // buffer size minus one byte to allocate space for the length byte.
813     uint8_t str[] = {0x01, MAX_IPMI_BUFFER - 1, MAX_IPMI_BUFFER - 1, 0x0A,
814                      0x01};
815 
816     // Data length
817     *data_len = sizeof(str);
818 
819     // Pack the actual response
820     std::memcpy(response, &str, *data_len);
821 
822     return rc;
823 }
824 
825 ipmi_ret_t ipmi_app_wildcard_handler(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
826                                      ipmi_request_t request,
827                                      ipmi_response_t response,
828                                      ipmi_data_len_t data_len,
829                                      ipmi_context_t context)
830 {
831     // Status code.
832     ipmi_ret_t rc = IPMI_CC_INVALID;
833 
834     *data_len = strlen("THIS IS WILDCARD");
835 
836     // Now pack actual response
837     std::memcpy(response, "THIS IS WILDCARD", *data_len);
838 
839     return rc;
840 }
841 
842 ipmi_ret_t ipmi_app_get_sys_guid(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
843                                  ipmi_request_t request,
844                                  ipmi_response_t response,
845                                  ipmi_data_len_t data_len,
846                                  ipmi_context_t context)
847 
848 {
849     ipmi_ret_t rc = IPMI_CC_OK;
850     sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()};
851 
852     try
853     {
854         // Get the Inventory object implementing BMC interface
855         ipmi::DbusObjectInfo bmcObject =
856             ipmi::getDbusObject(bus, bmc_interface);
857 
858         // Read UUID property value from bmcObject
859         // UUID is in RFC4122 format Ex: 61a39523-78f2-11e5-9862-e6402cfc3223
860         auto variant =
861             ipmi::getDbusProperty(bus, bmcObject.second, bmcObject.first,
862                                   bmc_guid_interface, bmc_guid_property);
863         std::string guidProp = variant_ns::get<std::string>(variant);
864 
865         // Erase "-" characters from the property value
866         guidProp.erase(std::remove(guidProp.begin(), guidProp.end(), '-'),
867                        guidProp.end());
868 
869         auto guidPropLen = guidProp.length();
870         // Validate UUID data
871         // Divide by 2 as 1 byte is built from 2 chars
872         if ((guidPropLen <= 0) || ((guidPropLen / 2) != bmc_guid_len))
873 
874         {
875             log<level::ERR>("Invalid UUID property value",
876                             entry("UUID_LENGTH=%d", guidPropLen));
877             return IPMI_CC_RESPONSE_ERROR;
878         }
879 
880         // Convert data in RFC4122(MSB) format to LSB format
881         // Get 2 characters at a time as 1 byte is built from 2 chars and
882         // convert to hex byte
883         // TODO: Data printed for GUID command is not as per the
884         // GUID format defined in IPMI specification 2.0 section 20.8
885         // Ticket raised: https://sourceforge.net/p/ipmitool/bugs/501/
886         uint8_t respGuid[bmc_guid_len];
887         for (size_t i = 0, respLoc = (bmc_guid_len - 1);
888              i < guidPropLen && respLoc >= 0; i += 2, respLoc--)
889         {
890             auto value = static_cast<uint8_t>(
891                 std::stoi(guidProp.substr(i, 2).c_str(), NULL, 16));
892             respGuid[respLoc] = value;
893         }
894 
895         *data_len = bmc_guid_len;
896         std::memcpy(response, &respGuid, bmc_guid_len);
897     }
898     catch (const InternalFailure& e)
899     {
900         log<level::ERR>("Failed in reading BMC UUID property",
901                         entry("INTERFACE=%s", bmc_interface),
902                         entry("PROPERTY_INTERFACE=%s", bmc_guid_interface),
903                         entry("PROPERTY=%s", bmc_guid_property));
904         return IPMI_CC_UNSPECIFIED_ERROR;
905     }
906     return rc;
907 }
908 
909 static std::unique_ptr<SysInfoParamStore> sysInfoParamStore;
910 
911 static std::string sysInfoReadSystemName()
912 {
913     // Use the BMC hostname as the "System Name."
914     char hostname[HOST_NAME_MAX + 1] = {};
915     if (gethostname(hostname, HOST_NAME_MAX) != 0)
916     {
917         perror("System info parameter: system name");
918     }
919     return hostname;
920 }
921 
922 struct IpmiSysInfoResp
923 {
924     uint8_t paramRevision;
925     uint8_t setSelector;
926     union
927     {
928         struct
929         {
930             uint8_t encoding;
931             uint8_t stringLen;
932             uint8_t stringData0[14];
933         } __attribute__((packed));
934         uint8_t stringDataN[16];
935         uint8_t byteData;
936     };
937 } __attribute__((packed));
938 
939 /**
940  * Split a string into (up to) 16-byte chunks as expected in response for get
941  * system info parameter.
942  *
943  * @param[in] fullString: Input string to be split
944  * @param[in] chunkIndex: Index of the chunk to be written out
945  * @param[in,out] chunk: Output data buffer; must have 14 byte capacity if
946  *          chunk_index = 0 and 16-byte capacity otherwise
947  * @return the number of bytes written into the output buffer, or -EINVAL for
948  * invalid arguments.
949  */
950 static int splitStringParam(const std::string& fullString, int chunkIndex,
951                             uint8_t* chunk)
952 {
953     constexpr int maxChunk = 255;
954     constexpr int smallChunk = 14;
955     constexpr int chunkSize = 16;
956     if (chunkIndex > maxChunk || chunk == nullptr)
957     {
958         return -EINVAL;
959     }
960     try
961     {
962         std::string output;
963         if (chunkIndex == 0)
964         {
965             // Output must have 14 byte capacity.
966             output = fullString.substr(0, smallChunk);
967         }
968         else
969         {
970             // Output must have 16 byte capacity.
971             output = fullString.substr((chunkIndex * chunkSize) - 2, chunkSize);
972         }
973 
974         std::memcpy(chunk, output.c_str(), output.length());
975         return output.length();
976     }
977     catch (const std::out_of_range& e)
978     {
979         // The position was beyond the end.
980         return -EINVAL;
981     }
982 }
983 
984 /**
985  * Packs the Get Sys Info Request Item into the response.
986  *
987  * @param[in] paramString - the parameter.
988  * @param[in] setSelector - the selector
989  * @param[in,out] resp - the System info response.
990  * @return The number of bytes packed or failure from splitStringParam().
991  */
992 static int packGetSysInfoResp(const std::string& paramString,
993                               uint8_t setSelector, IpmiSysInfoResp* resp)
994 {
995     uint8_t* dataBuffer = resp->stringDataN;
996     resp->setSelector = setSelector;
997     if (resp->setSelector == 0) // First chunk has only 14 bytes.
998     {
999         resp->encoding = 0;
1000         resp->stringLen = paramString.length();
1001         dataBuffer = resp->stringData0;
1002     }
1003     return splitStringParam(paramString, resp->setSelector, dataBuffer);
1004 }
1005 
1006 ipmi_ret_t ipmi_app_get_system_info(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
1007                                     ipmi_request_t request,
1008                                     ipmi_response_t response,
1009                                     ipmi_data_len_t dataLen,
1010                                     ipmi_context_t context)
1011 {
1012     IpmiSysInfoResp resp = {};
1013     size_t respLen = 0;
1014     uint8_t* const reqData = static_cast<uint8_t*>(request);
1015     std::string paramString;
1016     bool found;
1017     std::tuple<bool, std::string> ret;
1018     constexpr int minRequestSize = 4;
1019     constexpr int paramSelector = 1;
1020     constexpr uint8_t revisionOnly = 0x80;
1021     const uint8_t paramRequested = reqData[paramSelector];
1022     int rc;
1023 
1024     if (*dataLen < minRequestSize)
1025     {
1026         return IPMI_CC_REQ_DATA_LEN_INVALID;
1027     }
1028 
1029     *dataLen = 0; // default to 0.
1030 
1031     // Parameters revision as of IPMI spec v2.0 rev. 1.1 (Feb 11, 2014 E6)
1032     resp.paramRevision = 0x11;
1033     if (reqData[0] & revisionOnly) // Get parameter revision only
1034     {
1035         respLen = 1;
1036         goto writeResponse;
1037     }
1038 
1039     // The "Set In Progress" parameter can be used for rollback of parameter
1040     // data and is not implemented.
1041     if (paramRequested == 0)
1042     {
1043         resp.byteData = 0;
1044         respLen = 2;
1045         goto writeResponse;
1046     }
1047 
1048     if (sysInfoParamStore == nullptr)
1049     {
1050         sysInfoParamStore = std::make_unique<SysInfoParamStore>();
1051         sysInfoParamStore->update(IPMI_SYSINFO_SYSTEM_NAME,
1052                                   sysInfoReadSystemName);
1053     }
1054 
1055     // Parameters other than Set In Progress are assumed to be strings.
1056     ret = sysInfoParamStore->lookup(paramRequested);
1057     found = std::get<0>(ret);
1058     paramString = std::get<1>(ret);
1059     if (!found)
1060     {
1061         return IPMI_CC_SYSTEM_INFO_PARAMETER_NOT_SUPPORTED;
1062     }
1063     // TODO: Cache each parameter across multiple calls, until the whole string
1064     // has been read out. Otherwise, it's possible for a parameter to change
1065     // between requests for its chunks, returning chunks incoherent with each
1066     // other. For now, the parameter store is simply required to have only
1067     // idempotent callbacks.
1068     rc = packGetSysInfoResp(paramString, reqData[2], &resp);
1069     if (rc == -EINVAL)
1070     {
1071         return IPMI_CC_RESPONSE_ERROR;
1072     }
1073 
1074     respLen = sizeof(resp); // Write entire string data chunk in response.
1075 
1076 writeResponse:
1077     std::memcpy(response, &resp, sizeof(resp));
1078     *dataLen = respLen;
1079     return IPMI_CC_OK;
1080 }
1081 
1082 void register_netfn_app_functions()
1083 {
1084     // <Get BT Interface Capabilities>
1085     ipmi_register_callback(NETFUN_APP, IPMI_CMD_GET_CAP_BIT, NULL,
1086                            ipmi_app_get_bt_capabilities, PRIVILEGE_USER);
1087 
1088     // <Wildcard Command>
1089     ipmi_register_callback(NETFUN_APP, IPMI_CMD_WILDCARD, NULL,
1090                            ipmi_app_wildcard_handler, PRIVILEGE_USER);
1091 
1092     // <Reset Watchdog Timer>
1093     ipmi_register_callback(NETFUN_APP, IPMI_CMD_RESET_WD, NULL,
1094                            ipmi_app_watchdog_reset, PRIVILEGE_OPERATOR);
1095 
1096     // <Set Watchdog Timer>
1097     ipmi_register_callback(NETFUN_APP, IPMI_CMD_SET_WD, NULL,
1098                            ipmi_app_watchdog_set, PRIVILEGE_OPERATOR);
1099 
1100     // <Get Watchdog Timer>
1101     ipmi_register_callback(NETFUN_APP, IPMI_CMD_GET_WD, NULL,
1102                            ipmi_app_watchdog_get, PRIVILEGE_OPERATOR);
1103 
1104     // <Get Device ID>
1105     ipmi_register_callback(NETFUN_APP, IPMI_CMD_GET_DEVICE_ID, NULL,
1106                            ipmi_app_get_device_id, PRIVILEGE_USER);
1107 
1108     // <Get Self Test Results>
1109     ipmi_register_callback(NETFUN_APP, IPMI_CMD_GET_SELF_TEST_RESULTS, NULL,
1110                            ipmi_app_get_self_test_results, PRIVILEGE_USER);
1111 
1112     // <Get Device GUID>
1113     ipmi_register_callback(NETFUN_APP, IPMI_CMD_GET_DEVICE_GUID, NULL,
1114                            ipmi_app_get_device_guid, PRIVILEGE_USER);
1115 
1116     // <Set ACPI Power State>
1117     ipmi_register_callback(NETFUN_APP, IPMI_CMD_SET_ACPI, NULL,
1118                            ipmi_app_set_acpi_power_state, PRIVILEGE_ADMIN);
1119 
1120     // <Get ACPI Power State>
1121     ipmi_register_callback(NETFUN_APP, IPMI_CMD_GET_ACPI, NULL,
1122                            ipmi_app_get_acpi_power_state, PRIVILEGE_ADMIN);
1123 
1124 // TODO: Below code and associated api's need to be removed later.
1125 // Its commented for now to avoid merge conflicts with upstream
1126 // changes and smooth upstream upgrades.
1127 #if 0
1128 >>>>>>> IPMI Channel commands implementation
1129     // <Get Channel Access>
1130     ipmi_register_callback(NETFUN_APP, IPMI_CMD_GET_CHANNEL_ACCESS, NULL,
1131                            ipmi_get_channel_access, PRIVILEGE_USER);
1132 
1133     // <Get Channel Info Command>
1134     ipmi_register_callback(NETFUN_APP, IPMI_CMD_GET_CHAN_INFO, NULL,
1135                            ipmi_app_channel_info, PRIVILEGE_USER);
1136 #endif
1137 
1138     // <Get System GUID Command>
1139     ipmi_register_callback(NETFUN_APP, IPMI_CMD_GET_SYS_GUID, NULL,
1140                            ipmi_app_get_sys_guid, PRIVILEGE_USER);
1141 
1142     // <Get Channel Cipher Suites Command>
1143     ipmi_register_callback(NETFUN_APP, IPMI_CMD_GET_CHAN_CIPHER_SUITES, NULL,
1144                            getChannelCipherSuites, PRIVILEGE_CALLBACK);
1145 #if 0
1146     // <Set Channel Access Command>
1147     ipmi_register_callback(NETFUN_APP, IPMI_CMD_SET_CHAN_ACCESS, NULL,
1148                            ipmi_set_channel_access, PRIVILEGE_ADMIN);
1149 #endif
1150     // <Get System Info Command>
1151     ipmi_register_callback(NETFUN_APP, IPMI_CMD_GET_SYSTEM_INFO, NULL,
1152                            ipmi_app_get_system_info, PRIVILEGE_USER);
1153     return;
1154 }
1155