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