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