xref: /openbmc/phosphor-host-ipmid/oem/example/apphandler.cpp (revision d0b99b1137ee811ca1c4a2d6f24f7caab8f2f4c0)
1*d0b99b11SVernon Mauery #include "config.h"
2*d0b99b11SVernon Mauery 
3*d0b99b11SVernon Mauery #include <ipmid/api.hpp>
4*d0b99b11SVernon Mauery #include <ipmid/types.hpp>
5*d0b99b11SVernon Mauery #include <ipmid/utils.hpp>
6*d0b99b11SVernon Mauery #include <nlohmann/json.hpp>
7*d0b99b11SVernon Mauery #include <phosphor-logging/elog-errors.hpp>
8*d0b99b11SVernon Mauery #include <phosphor-logging/lg2.hpp>
9*d0b99b11SVernon Mauery #include <sdbusplus/message/types.hpp>
10*d0b99b11SVernon Mauery #include <xyz/openbmc_project/Common/error.hpp>
11*d0b99b11SVernon Mauery #include <xyz/openbmc_project/Software/Activation/server.hpp>
12*d0b99b11SVernon Mauery #include <xyz/openbmc_project/Software/Version/server.hpp>
13*d0b99b11SVernon Mauery #include <xyz/openbmc_project/State/BMC/server.hpp>
14*d0b99b11SVernon Mauery 
15*d0b99b11SVernon Mauery #include <algorithm>
16*d0b99b11SVernon Mauery #include <array>
17*d0b99b11SVernon Mauery #include <charconv>
18*d0b99b11SVernon Mauery #include <cstddef>
19*d0b99b11SVernon Mauery #include <cstdint>
20*d0b99b11SVernon Mauery #include <filesystem>
21*d0b99b11SVernon Mauery #include <fstream>
22*d0b99b11SVernon Mauery #include <memory>
23*d0b99b11SVernon Mauery #include <regex>
24*d0b99b11SVernon Mauery #include <string>
25*d0b99b11SVernon Mauery #include <string_view>
26*d0b99b11SVernon Mauery #include <tuple>
27*d0b99b11SVernon Mauery #include <vector>
28*d0b99b11SVernon Mauery 
29*d0b99b11SVernon Mauery constexpr auto bmcStateInterface = "xyz.openbmc_project.State.BMC";
30*d0b99b11SVernon Mauery constexpr auto bmcStateProperty = "CurrentBMCState";
31*d0b99b11SVernon Mauery 
32*d0b99b11SVernon Mauery static constexpr auto redundancyIntf =
33*d0b99b11SVernon Mauery     "xyz.openbmc_project.Software.RedundancyPriority";
34*d0b99b11SVernon Mauery static constexpr auto versionIntf = "xyz.openbmc_project.Software.Version";
35*d0b99b11SVernon Mauery static constexpr auto activationIntf =
36*d0b99b11SVernon Mauery     "xyz.openbmc_project.Software.Activation";
37*d0b99b11SVernon Mauery static constexpr auto softwareRoot = "/xyz/openbmc_project/software";
38*d0b99b11SVernon Mauery 
39*d0b99b11SVernon Mauery void registerNetFnAppFunctions() __attribute__((constructor));
40*d0b99b11SVernon Mauery 
41*d0b99b11SVernon Mauery using namespace phosphor::logging;
42*d0b99b11SVernon Mauery using namespace sdbusplus::error::xyz::openbmc_project::common;
43*d0b99b11SVernon Mauery using Version = sdbusplus::server::xyz::openbmc_project::software::Version;
44*d0b99b11SVernon Mauery using Activation =
45*d0b99b11SVernon Mauery     sdbusplus::server::xyz::openbmc_project::software::Activation;
46*d0b99b11SVernon Mauery using BMC = sdbusplus::server::xyz::openbmc_project::state::BMC;
47*d0b99b11SVernon Mauery namespace fs = std::filesystem;
48*d0b99b11SVernon Mauery 
49*d0b99b11SVernon Mauery /**
50*d0b99b11SVernon Mauery  * @brief Returns the Version info from primary s/w object
51*d0b99b11SVernon Mauery  *
52*d0b99b11SVernon Mauery  * Get the Version info from the active s/w object which is having high
53*d0b99b11SVernon Mauery  * "Priority" value(a smaller number is a higher priority) and "Purpose"
54*d0b99b11SVernon Mauery  * is "BMC" from the list of all s/w objects those are implementing
55*d0b99b11SVernon Mauery  * RedundancyPriority interface from the given softwareRoot path.
56*d0b99b11SVernon Mauery  *
57*d0b99b11SVernon Mauery  * @return On success returns the Version info from primary s/w object.
58*d0b99b11SVernon Mauery  *
59*d0b99b11SVernon Mauery  */
getActiveSoftwareVersionInfo(ipmi::Context::ptr ctx)60*d0b99b11SVernon Mauery std::string getActiveSoftwareVersionInfo(ipmi::Context::ptr ctx)
61*d0b99b11SVernon Mauery {
62*d0b99b11SVernon Mauery     std::string revision{};
63*d0b99b11SVernon Mauery     ipmi::ObjectTree objectTree;
64*d0b99b11SVernon Mauery     try
65*d0b99b11SVernon Mauery     {
66*d0b99b11SVernon Mauery         objectTree =
67*d0b99b11SVernon Mauery             ipmi::getAllDbusObjects(*ctx->bus, softwareRoot, redundancyIntf);
68*d0b99b11SVernon Mauery     }
69*d0b99b11SVernon Mauery     catch (const sdbusplus::exception_t& e)
70*d0b99b11SVernon Mauery     {
71*d0b99b11SVernon Mauery         lg2::error("Failed to fetch redundancy object from dbus, "
72*d0b99b11SVernon Mauery                    "interface: {INTERFACE},  error: {ERROR}",
73*d0b99b11SVernon Mauery                    "INTERFACE", redundancyIntf, "ERROR", e);
74*d0b99b11SVernon Mauery         elog<InternalFailure>();
75*d0b99b11SVernon Mauery     }
76*d0b99b11SVernon Mauery 
77*d0b99b11SVernon Mauery     auto objectFound = false;
78*d0b99b11SVernon Mauery     for (auto& softObject : objectTree)
79*d0b99b11SVernon Mauery     {
80*d0b99b11SVernon Mauery         auto service =
81*d0b99b11SVernon Mauery             ipmi::getService(*ctx->bus, redundancyIntf, softObject.first);
82*d0b99b11SVernon Mauery         auto objValueTree =
83*d0b99b11SVernon Mauery             ipmi::getManagedObjects(*ctx->bus, service, softwareRoot);
84*d0b99b11SVernon Mauery 
85*d0b99b11SVernon Mauery         auto minPriority = 0xFF;
86*d0b99b11SVernon Mauery         for (const auto& objIter : objValueTree)
87*d0b99b11SVernon Mauery         {
88*d0b99b11SVernon Mauery             try
89*d0b99b11SVernon Mauery             {
90*d0b99b11SVernon Mauery                 auto& intfMap = objIter.second;
91*d0b99b11SVernon Mauery                 auto& redundancyPriorityProps = intfMap.at(redundancyIntf);
92*d0b99b11SVernon Mauery                 auto& versionProps = intfMap.at(versionIntf);
93*d0b99b11SVernon Mauery                 auto& activationProps = intfMap.at(activationIntf);
94*d0b99b11SVernon Mauery                 auto priority =
95*d0b99b11SVernon Mauery                     std::get<uint8_t>(redundancyPriorityProps.at("Priority"));
96*d0b99b11SVernon Mauery                 auto purpose =
97*d0b99b11SVernon Mauery                     std::get<std::string>(versionProps.at("Purpose"));
98*d0b99b11SVernon Mauery                 auto activation =
99*d0b99b11SVernon Mauery                     std::get<std::string>(activationProps.at("Activation"));
100*d0b99b11SVernon Mauery                 auto version =
101*d0b99b11SVernon Mauery                     std::get<std::string>(versionProps.at("Version"));
102*d0b99b11SVernon Mauery                 if ((Version::convertVersionPurposeFromString(purpose) ==
103*d0b99b11SVernon Mauery                      Version::VersionPurpose::BMC) &&
104*d0b99b11SVernon Mauery                     (Activation::convertActivationsFromString(activation) ==
105*d0b99b11SVernon Mauery                      Activation::Activations::Active))
106*d0b99b11SVernon Mauery                 {
107*d0b99b11SVernon Mauery                     if (priority < minPriority)
108*d0b99b11SVernon Mauery                     {
109*d0b99b11SVernon Mauery                         minPriority = priority;
110*d0b99b11SVernon Mauery                         objectFound = true;
111*d0b99b11SVernon Mauery                         revision = std::move(version);
112*d0b99b11SVernon Mauery                     }
113*d0b99b11SVernon Mauery                 }
114*d0b99b11SVernon Mauery             }
115*d0b99b11SVernon Mauery             catch (const std::exception& e)
116*d0b99b11SVernon Mauery             {
117*d0b99b11SVernon Mauery                 lg2::error("error message: {ERROR}", "ERROR", e);
118*d0b99b11SVernon Mauery             }
119*d0b99b11SVernon Mauery         }
120*d0b99b11SVernon Mauery     }
121*d0b99b11SVernon Mauery 
122*d0b99b11SVernon Mauery     if (!objectFound)
123*d0b99b11SVernon Mauery     {
124*d0b99b11SVernon Mauery         lg2::error("Could not found an BMC software Object");
125*d0b99b11SVernon Mauery         elog<InternalFailure>();
126*d0b99b11SVernon Mauery     }
127*d0b99b11SVernon Mauery 
128*d0b99b11SVernon Mauery     return revision;
129*d0b99b11SVernon Mauery }
130*d0b99b11SVernon Mauery 
getCurrentBmcStateWithFallback(ipmi::Context::ptr & ctx,const bool fallbackAvailability)131*d0b99b11SVernon Mauery bool getCurrentBmcStateWithFallback(ipmi::Context::ptr& ctx,
132*d0b99b11SVernon Mauery                                     const bool fallbackAvailability)
133*d0b99b11SVernon Mauery {
134*d0b99b11SVernon Mauery     // Get the Inventory object implementing the BMC interface
135*d0b99b11SVernon Mauery     ipmi::DbusObjectInfo bmcObject{};
136*d0b99b11SVernon Mauery     boost::system::error_code ec =
137*d0b99b11SVernon Mauery         ipmi::getDbusObject(ctx, bmcStateInterface, bmcObject);
138*d0b99b11SVernon Mauery     std::string bmcState{};
139*d0b99b11SVernon Mauery     if (ec.value())
140*d0b99b11SVernon Mauery     {
141*d0b99b11SVernon Mauery         return fallbackAvailability;
142*d0b99b11SVernon Mauery     }
143*d0b99b11SVernon Mauery     ec = ipmi::getDbusProperty(ctx, bmcObject.second, bmcObject.first,
144*d0b99b11SVernon Mauery                                bmcStateInterface, bmcStateProperty, bmcState);
145*d0b99b11SVernon Mauery     if (!ec.value())
146*d0b99b11SVernon Mauery     {
147*d0b99b11SVernon Mauery         return fallbackAvailability;
148*d0b99b11SVernon Mauery     }
149*d0b99b11SVernon Mauery     return BMC::convertBMCStateFromString(bmcState) == BMC::BMCState::Ready;
150*d0b99b11SVernon Mauery }
151*d0b99b11SVernon Mauery 
152*d0b99b11SVernon Mauery typedef struct
153*d0b99b11SVernon Mauery {
154*d0b99b11SVernon Mauery     char major;
155*d0b99b11SVernon Mauery     char minor;
156*d0b99b11SVernon Mauery     uint8_t aux[4];
157*d0b99b11SVernon Mauery } Revision;
158*d0b99b11SVernon Mauery 
159*d0b99b11SVernon Mauery /* Use regular expression searching matched pattern X.Y, and convert it to  */
160*d0b99b11SVernon Mauery /* Major (X) and Minor (Y) version.                                         */
161*d0b99b11SVernon Mauery /* Example:                                                                 */
162*d0b99b11SVernon Mauery /* version = 2.14.0-dev                                                     */
163*d0b99b11SVernon Mauery /*           ^ ^                                                            */
164*d0b99b11SVernon Mauery /*           | |---------------- Minor                                      */
165*d0b99b11SVernon Mauery /*           |------------------ Major                                      */
166*d0b99b11SVernon Mauery /*                                                                          */
167*d0b99b11SVernon Mauery /* Default regex string only tries to match Major and Minor version.        */
168*d0b99b11SVernon Mauery /*                                                                          */
169*d0b99b11SVernon Mauery /* To match more firmware version info, platforms need to define it own     */
170*d0b99b11SVernon Mauery /* regex string to match more strings, and assign correct mapping index in  */
171*d0b99b11SVernon Mauery /* matches array.                                                           */
172*d0b99b11SVernon Mauery /*                                                                          */
173*d0b99b11SVernon Mauery /* matches[0]: matched index for major ver                                  */
174*d0b99b11SVernon Mauery /* matches[1]: matched index for minor ver                                  */
175*d0b99b11SVernon Mauery /* matches[2]: matched index for aux[0] (set 0 to skip)                     */
176*d0b99b11SVernon Mauery /* matches[3]: matched index for aux[1] (set 0 to skip)                     */
177*d0b99b11SVernon Mauery /* matches[4]: matched index for aux[2] (set 0 to skip)                     */
178*d0b99b11SVernon Mauery /* matches[5]: matched index for aux[3] (set 0 to skip)                     */
179*d0b99b11SVernon Mauery /* Example:                                                                 */
180*d0b99b11SVernon Mauery /* regex = "([\d]+).([\d]+).([\d]+)-dev-([\d]+)-g([0-9a-fA-F]{2})           */
181*d0b99b11SVernon Mauery /*          ([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})"               */
182*d0b99b11SVernon Mauery /* matches = {1,2,5,6,7,8}                                                  */
183*d0b99b11SVernon Mauery /* version = 2.14.0-dev-750-g37a7c5ad1-dirty                                */
184*d0b99b11SVernon Mauery /*           ^ ^  ^     ^    ^ ^ ^ ^                                        */
185*d0b99b11SVernon Mauery /*           | |  |     |    | | | |                                        */
186*d0b99b11SVernon Mauery /*           | |  |     |    | | | |-- Aux byte 3 (0xAD), index 8           */
187*d0b99b11SVernon Mauery /*           | |  |     |    | | |---- Aux byte 2 (0xC5), index 7           */
188*d0b99b11SVernon Mauery /*           | |  |     |    | |------ Aux byte 1 (0xA7), index 6           */
189*d0b99b11SVernon Mauery /*           | |  |     |    |-------- Aux byte 0 (0x37), index 5           */
190*d0b99b11SVernon Mauery /*           | |  |     |------------- Not used, index 4                    */
191*d0b99b11SVernon Mauery /*           | |  |------------------- Not used, index 3                    */
192*d0b99b11SVernon Mauery /*           | |---------------------- Minor (14), index 2                  */
193*d0b99b11SVernon Mauery /*           |------------------------ Major (2), index 1                   */
convertVersion(std::string s,Revision & rev)194*d0b99b11SVernon Mauery int convertVersion(std::string s, Revision& rev)
195*d0b99b11SVernon Mauery {
196*d0b99b11SVernon Mauery     static const std::vector<size_t> matches = {
197*d0b99b11SVernon Mauery         MAJOR_MATCH_INDEX, MINOR_MATCH_INDEX, AUX_0_MATCH_INDEX,
198*d0b99b11SVernon Mauery         AUX_1_MATCH_INDEX, AUX_2_MATCH_INDEX, AUX_3_MATCH_INDEX};
199*d0b99b11SVernon Mauery     std::regex fw_regex(FW_VER_REGEX);
200*d0b99b11SVernon Mauery     std::smatch m;
201*d0b99b11SVernon Mauery     Revision r = {0};
202*d0b99b11SVernon Mauery     size_t val;
203*d0b99b11SVernon Mauery 
204*d0b99b11SVernon Mauery     if (std::regex_search(s, m, fw_regex))
205*d0b99b11SVernon Mauery     {
206*d0b99b11SVernon Mauery         if (m.size() < *std::max_element(matches.begin(), matches.end()))
207*d0b99b11SVernon Mauery         { // max index higher than match count
208*d0b99b11SVernon Mauery             return -1;
209*d0b99b11SVernon Mauery         }
210*d0b99b11SVernon Mauery 
211*d0b99b11SVernon Mauery         // convert major
212*d0b99b11SVernon Mauery         {
213*d0b99b11SVernon Mauery             std::string str = m[matches[0]].str();
214*d0b99b11SVernon Mauery             const auto& [ptr, ec] =
215*d0b99b11SVernon Mauery                 std::from_chars(str.data(), str.data() + str.size(), val);
216*d0b99b11SVernon Mauery             if (ec != std::errc() || ptr != str.data() + str.size())
217*d0b99b11SVernon Mauery             { // failed to convert major string
218*d0b99b11SVernon Mauery                 return -1;
219*d0b99b11SVernon Mauery             }
220*d0b99b11SVernon Mauery 
221*d0b99b11SVernon Mauery             if (val >= 2000)
222*d0b99b11SVernon Mauery             { // For the platforms use year as major version, it would expect to
223*d0b99b11SVernon Mauery               // have major version between 0 - 99. If the major version is
224*d0b99b11SVernon Mauery               // greater than or equal to 2000, it is treated as a year and
225*d0b99b11SVernon Mauery               // converted to 0 - 99.
226*d0b99b11SVernon Mauery                 r.major = val % 100;
227*d0b99b11SVernon Mauery             }
228*d0b99b11SVernon Mauery             else
229*d0b99b11SVernon Mauery             {
230*d0b99b11SVernon Mauery                 r.major = val & 0x7F;
231*d0b99b11SVernon Mauery             }
232*d0b99b11SVernon Mauery         }
233*d0b99b11SVernon Mauery 
234*d0b99b11SVernon Mauery         // convert minor
235*d0b99b11SVernon Mauery         {
236*d0b99b11SVernon Mauery             std::string str = m[matches[1]].str();
237*d0b99b11SVernon Mauery             const auto& [ptr, ec] =
238*d0b99b11SVernon Mauery                 std::from_chars(str.data(), str.data() + str.size(), val);
239*d0b99b11SVernon Mauery             if (ec != std::errc() || ptr != str.data() + str.size())
240*d0b99b11SVernon Mauery             { // failed to convert minor string
241*d0b99b11SVernon Mauery                 return -1;
242*d0b99b11SVernon Mauery             }
243*d0b99b11SVernon Mauery             r.minor = val & 0xFF;
244*d0b99b11SVernon Mauery         }
245*d0b99b11SVernon Mauery 
246*d0b99b11SVernon Mauery         // convert aux bytes
247*d0b99b11SVernon Mauery         {
248*d0b99b11SVernon Mauery             size_t i;
249*d0b99b11SVernon Mauery             for (i = 0; i < 4; i++)
250*d0b99b11SVernon Mauery             {
251*d0b99b11SVernon Mauery                 if (matches[i + 2] == 0)
252*d0b99b11SVernon Mauery                 {
253*d0b99b11SVernon Mauery                     continue;
254*d0b99b11SVernon Mauery                 }
255*d0b99b11SVernon Mauery 
256*d0b99b11SVernon Mauery                 std::string str = m[matches[i + 2]].str();
257*d0b99b11SVernon Mauery                 const char* cstr = str.c_str();
258*d0b99b11SVernon Mauery                 auto [ptr,
259*d0b99b11SVernon Mauery                       ec] = std::from_chars(cstr, cstr + str.size(), val, 16);
260*d0b99b11SVernon Mauery                 if (ec != std::errc() || ptr != cstr + str.size())
261*d0b99b11SVernon Mauery                 { // failed to convert aux byte string
262*d0b99b11SVernon Mauery                     break;
263*d0b99b11SVernon Mauery                 }
264*d0b99b11SVernon Mauery 
265*d0b99b11SVernon Mauery                 r.aux[i] = val & 0xFF;
266*d0b99b11SVernon Mauery             }
267*d0b99b11SVernon Mauery 
268*d0b99b11SVernon Mauery             if (i != 4)
269*d0b99b11SVernon Mauery             { // something wrong durign converting aux bytes
270*d0b99b11SVernon Mauery                 return -1;
271*d0b99b11SVernon Mauery             }
272*d0b99b11SVernon Mauery         }
273*d0b99b11SVernon Mauery 
274*d0b99b11SVernon Mauery         // all matched
275*d0b99b11SVernon Mauery         rev = r;
276*d0b99b11SVernon Mauery         return 0;
277*d0b99b11SVernon Mauery     }
278*d0b99b11SVernon Mauery 
279*d0b99b11SVernon Mauery     return -1;
280*d0b99b11SVernon Mauery }
281*d0b99b11SVernon Mauery 
282*d0b99b11SVernon Mauery /* @brief: Implement the Get Device ID IPMI command per the IPMI spec
283*d0b99b11SVernon Mauery  *  @param[in] ctx - shared_ptr to an IPMI context struct
284*d0b99b11SVernon Mauery  *
285*d0b99b11SVernon Mauery  *  @returns IPMI completion code plus response data
286*d0b99b11SVernon Mauery  *   - Device ID (manufacturer defined)
287*d0b99b11SVernon Mauery  *   - Device revision[4 bits]; reserved[3 bits]; SDR support[1 bit]
288*d0b99b11SVernon Mauery  *   - FW revision major[7 bits] (binary encoded); available[1 bit]
289*d0b99b11SVernon Mauery  *   - FW Revision minor (BCD encoded)
290*d0b99b11SVernon Mauery  *   - IPMI version (0x02 for IPMI 2.0)
291*d0b99b11SVernon Mauery  *   - device support (bitfield of supported options)
292*d0b99b11SVernon Mauery  *   - MFG IANA ID (3 bytes)
293*d0b99b11SVernon Mauery  *   - product ID (2 bytes)
294*d0b99b11SVernon Mauery  *   - AUX info (4 bytes)
295*d0b99b11SVernon Mauery  */
296*d0b99b11SVernon Mauery ipmi::RspType<uint8_t,  // Device ID
297*d0b99b11SVernon Mauery               uint8_t,  // Device Revision
298*d0b99b11SVernon Mauery               uint8_t,  // Firmware Revision Major
299*d0b99b11SVernon Mauery               uint8_t,  // Firmware Revision minor
300*d0b99b11SVernon Mauery               uint8_t,  // IPMI version
301*d0b99b11SVernon Mauery               uint8_t,  // Additional device support
302*d0b99b11SVernon Mauery               uint24_t, // MFG ID
303*d0b99b11SVernon Mauery               uint16_t, // Product ID
304*d0b99b11SVernon Mauery               uint32_t  // AUX info
305*d0b99b11SVernon Mauery               >
ipmiAppGetDeviceId(ipmi::Context::ptr ctx)306*d0b99b11SVernon Mauery     ipmiAppGetDeviceId([[maybe_unused]] ipmi::Context::ptr ctx)
307*d0b99b11SVernon Mauery {
308*d0b99b11SVernon Mauery     static struct
309*d0b99b11SVernon Mauery     {
310*d0b99b11SVernon Mauery         uint8_t id;
311*d0b99b11SVernon Mauery         uint8_t revision;
312*d0b99b11SVernon Mauery         uint8_t fw[2];
313*d0b99b11SVernon Mauery         uint8_t ipmiVer;
314*d0b99b11SVernon Mauery         uint8_t addnDevSupport;
315*d0b99b11SVernon Mauery         uint24_t manufId;
316*d0b99b11SVernon Mauery         uint16_t prodId;
317*d0b99b11SVernon Mauery         uint32_t aux;
318*d0b99b11SVernon Mauery     } devId;
319*d0b99b11SVernon Mauery     static bool dev_id_initialized = false;
320*d0b99b11SVernon Mauery     static bool defaultActivationSetting = true;
321*d0b99b11SVernon Mauery     const char* filename = "/usr/share/ipmi-providers/dev_id.json";
322*d0b99b11SVernon Mauery     constexpr auto ipmiDevIdStateShift = 7;
323*d0b99b11SVernon Mauery     constexpr auto ipmiDevIdFw1Mask = ~(1 << ipmiDevIdStateShift);
324*d0b99b11SVernon Mauery 
325*d0b99b11SVernon Mauery     static bool haveBMCVersion = false;
326*d0b99b11SVernon Mauery     if (!haveBMCVersion || !dev_id_initialized)
327*d0b99b11SVernon Mauery     {
328*d0b99b11SVernon Mauery         int r = -1;
329*d0b99b11SVernon Mauery         Revision rev = {0, 0, {0, 0, 0, 0}};
330*d0b99b11SVernon Mauery         try
331*d0b99b11SVernon Mauery         {
332*d0b99b11SVernon Mauery             auto version = getActiveSoftwareVersionInfo(ctx);
333*d0b99b11SVernon Mauery             r = convertVersion(version, rev);
334*d0b99b11SVernon Mauery         }
335*d0b99b11SVernon Mauery         catch (const std::exception& e)
336*d0b99b11SVernon Mauery         {
337*d0b99b11SVernon Mauery             lg2::error("error message: {ERROR}", "ERROR", e);
338*d0b99b11SVernon Mauery         }
339*d0b99b11SVernon Mauery 
340*d0b99b11SVernon Mauery         if (r >= 0)
341*d0b99b11SVernon Mauery         {
342*d0b99b11SVernon Mauery             // bit7 identifies if the device is available
343*d0b99b11SVernon Mauery             // 0=normal operation
344*d0b99b11SVernon Mauery             // 1=device firmware, SDR update,
345*d0b99b11SVernon Mauery             // or self-initialization in progress.
346*d0b99b11SVernon Mauery             // The availability may change in run time, so mask here
347*d0b99b11SVernon Mauery             // and initialize later.
348*d0b99b11SVernon Mauery             devId.fw[0] = rev.major & ipmiDevIdFw1Mask;
349*d0b99b11SVernon Mauery 
350*d0b99b11SVernon Mauery             rev.minor = (rev.minor > 99 ? 99 : rev.minor);
351*d0b99b11SVernon Mauery             devId.fw[1] = rev.minor % 10 + (rev.minor / 10) * 16;
352*d0b99b11SVernon Mauery             std::memcpy(&devId.aux, rev.aux, sizeof(rev.aux));
353*d0b99b11SVernon Mauery             haveBMCVersion = true;
354*d0b99b11SVernon Mauery         }
355*d0b99b11SVernon Mauery     }
356*d0b99b11SVernon Mauery     if (!dev_id_initialized)
357*d0b99b11SVernon Mauery     {
358*d0b99b11SVernon Mauery         // IPMI Spec version 2.0
359*d0b99b11SVernon Mauery         devId.ipmiVer = 2;
360*d0b99b11SVernon Mauery 
361*d0b99b11SVernon Mauery         std::ifstream devIdFile(filename);
362*d0b99b11SVernon Mauery         if (devIdFile.is_open())
363*d0b99b11SVernon Mauery         {
364*d0b99b11SVernon Mauery             auto data = nlohmann::json::parse(devIdFile, nullptr, false);
365*d0b99b11SVernon Mauery             if (!data.is_discarded())
366*d0b99b11SVernon Mauery             {
367*d0b99b11SVernon Mauery                 devId.id = data.value("id", 0);
368*d0b99b11SVernon Mauery                 devId.revision = data.value("revision", 0);
369*d0b99b11SVernon Mauery                 devId.addnDevSupport = data.value("addn_dev_support", 0);
370*d0b99b11SVernon Mauery                 devId.manufId = data.value("manuf_id", 0);
371*d0b99b11SVernon Mauery                 devId.prodId = data.value("prod_id", 0);
372*d0b99b11SVernon Mauery                 if (!(AUX_0_MATCH_INDEX || AUX_1_MATCH_INDEX ||
373*d0b99b11SVernon Mauery                       AUX_2_MATCH_INDEX || AUX_3_MATCH_INDEX))
374*d0b99b11SVernon Mauery                 {
375*d0b99b11SVernon Mauery                     devId.aux = data.value("aux", 0);
376*d0b99b11SVernon Mauery                 }
377*d0b99b11SVernon Mauery 
378*d0b99b11SVernon Mauery                 if (data.contains("firmware_revision"))
379*d0b99b11SVernon Mauery                 {
380*d0b99b11SVernon Mauery                     const auto& firmwareRevision = data.at("firmware_revision");
381*d0b99b11SVernon Mauery                     if (firmwareRevision.contains("major"))
382*d0b99b11SVernon Mauery                     {
383*d0b99b11SVernon Mauery                         firmwareRevision.at("major").get_to(devId.fw[0]);
384*d0b99b11SVernon Mauery                     }
385*d0b99b11SVernon Mauery                     if (firmwareRevision.contains("minor"))
386*d0b99b11SVernon Mauery                     {
387*d0b99b11SVernon Mauery                         firmwareRevision.at("minor").get_to(devId.fw[1]);
388*d0b99b11SVernon Mauery                     }
389*d0b99b11SVernon Mauery                 }
390*d0b99b11SVernon Mauery 
391*d0b99b11SVernon Mauery                 // Set the availablitity of the BMC.
392*d0b99b11SVernon Mauery                 defaultActivationSetting = data.value("availability", true);
393*d0b99b11SVernon Mauery 
394*d0b99b11SVernon Mauery                 // Don't read the file every time if successful
395*d0b99b11SVernon Mauery                 dev_id_initialized = true;
396*d0b99b11SVernon Mauery             }
397*d0b99b11SVernon Mauery             else
398*d0b99b11SVernon Mauery             {
399*d0b99b11SVernon Mauery                 lg2::error("Device ID JSON parser failure");
400*d0b99b11SVernon Mauery                 return ipmi::responseUnspecifiedError();
401*d0b99b11SVernon Mauery             }
402*d0b99b11SVernon Mauery         }
403*d0b99b11SVernon Mauery         else
404*d0b99b11SVernon Mauery         {
405*d0b99b11SVernon Mauery             lg2::error("Device ID file not found");
406*d0b99b11SVernon Mauery             return ipmi::responseUnspecifiedError();
407*d0b99b11SVernon Mauery         }
408*d0b99b11SVernon Mauery     }
409*d0b99b11SVernon Mauery 
410*d0b99b11SVernon Mauery     // Set availability to the actual current BMC state
411*d0b99b11SVernon Mauery     devId.fw[0] &= ipmiDevIdFw1Mask;
412*d0b99b11SVernon Mauery     if (!getCurrentBmcStateWithFallback(ctx, defaultActivationSetting))
413*d0b99b11SVernon Mauery     {
414*d0b99b11SVernon Mauery         devId.fw[0] |= (1 << ipmiDevIdStateShift);
415*d0b99b11SVernon Mauery     }
416*d0b99b11SVernon Mauery 
417*d0b99b11SVernon Mauery     return ipmi::responseSuccess(
418*d0b99b11SVernon Mauery         devId.id, devId.revision, devId.fw[0], devId.fw[1], devId.ipmiVer,
419*d0b99b11SVernon Mauery         devId.addnDevSupport, devId.manufId, devId.prodId, devId.aux);
420*d0b99b11SVernon Mauery }
421*d0b99b11SVernon Mauery 
registerNetFnAppFunctions()422*d0b99b11SVernon Mauery void registerNetFnAppFunctions()
423*d0b99b11SVernon Mauery {
424*d0b99b11SVernon Mauery     // OEM libraries should use ipmi::prioOemBase to override default
425*d0b99b11SVernon Mauery     // implementation of IPMI commands that use ipmi::prioOpenBmcBase
426*d0b99b11SVernon Mauery 
427*d0b99b11SVernon Mauery     // <Get Device ID>
428*d0b99b11SVernon Mauery     ipmi::registerHandler(ipmi::prioOemBase, ipmi::netFnApp,
429*d0b99b11SVernon Mauery                           ipmi::app::cmdGetDeviceId, ipmi::Privilege::User,
430*d0b99b11SVernon Mauery                           ipmiAppGetDeviceId);
431*d0b99b11SVernon Mauery }
432