1 /*
2 // Copyright (c) 2018 Intel Corporation
3 //
4 // Licensed under the Apache License, Version 2.0 (the "License");
5 // you may not use this file except in compliance with the License.
6 // You may obtain a copy of the License at
7 //
8 //      http://www.apache.org/licenses/LICENSE-2.0
9 //
10 // Unless required by applicable law or agreed to in writing, software
11 // distributed under the License is distributed on an "AS IS" BASIS,
12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 // See the License for the specific language governing permissions and
14 // limitations under the License.
15 */
16 
17 #include "xyz/openbmc_project/Common/error.hpp"
18 #include "xyz/openbmc_project/Led/Physical/server.hpp"
19 
20 #include <systemd/sd-journal.h>
21 
22 #include <appcommands.hpp>
23 #include <array>
24 #include <boost/container/flat_map.hpp>
25 #include <boost/process/child.hpp>
26 #include <boost/process/io.hpp>
27 #include <com/intel/Control/OCOTShutdownPolicy/server.hpp>
28 #include <commandutils.hpp>
29 #include <filesystem>
30 #include <gpiod.hpp>
31 #include <iostream>
32 #include <ipmid/api.hpp>
33 #include <ipmid/utils.hpp>
34 #include <nlohmann/json.hpp>
35 #include <oemcommands.hpp>
36 #include <phosphor-logging/log.hpp>
37 #include <regex>
38 #include <sdbusplus/bus.hpp>
39 #include <sdbusplus/message/types.hpp>
40 #include <string>
41 #include <variant>
42 #include <vector>
43 #include <xyz/openbmc_project/Chassis/Control/NMISource/server.hpp>
44 #include <xyz/openbmc_project/Control/Boot/Mode/server.hpp>
45 #include <xyz/openbmc_project/Control/Boot/Source/server.hpp>
46 #include <xyz/openbmc_project/Control/PowerSupplyRedundancy/server.hpp>
47 #include <xyz/openbmc_project/Control/Security/RestrictionMode/server.hpp>
48 #include <xyz/openbmc_project/Control/Security/SpecialMode/server.hpp>
49 
50 namespace ipmi
51 {
52 static void registerOEMFunctions() __attribute__((constructor));
53 
54 static constexpr size_t maxFRUStringLength = 0x3F;
55 
56 static constexpr auto ethernetIntf =
57     "xyz.openbmc_project.Network.EthernetInterface";
58 static constexpr auto networkIPIntf = "xyz.openbmc_project.Network.IP";
59 static constexpr auto networkService = "xyz.openbmc_project.Network";
60 static constexpr auto networkRoot = "/xyz/openbmc_project/network";
61 
62 static constexpr const char* oemNmiSourceIntf =
63     "xyz.openbmc_project.Chassis.Control.NMISource";
64 static constexpr const char* oemNmiSourceObjPath =
65     "/xyz/openbmc_project/Chassis/Control/NMISource";
66 static constexpr const char* oemNmiBmcSourceObjPathProp = "BMCSource";
67 static constexpr const char* oemNmiEnabledObjPathProp = "Enabled";
68 
69 static constexpr const char* dimmOffsetFile = "/var/lib/ipmi/ipmi_dimms.json";
70 
71 enum class NmiSource : uint8_t
72 {
73     none = 0,
74     frontPanelButton = 1,
75     watchdog = 2,
76     chassisCmd = 3,
77     memoryError = 4,
78     pciBusError = 5,
79     pch = 6,
80     chipset = 7,
81 };
82 
83 enum class SpecialUserIndex : uint8_t
84 {
85     rootUser = 0,
86     atScaleDebugUser = 1
87 };
88 
89 static constexpr const char* restricionModeService =
90     "xyz.openbmc_project.RestrictionMode.Manager";
91 static constexpr const char* restricionModeBasePath =
92     "/xyz/openbmc_project/control/security/restriction_mode";
93 static constexpr const char* restricionModeIntf =
94     "xyz.openbmc_project.Control.Security.RestrictionMode";
95 static constexpr const char* restricionModeProperty = "RestrictionMode";
96 
97 static constexpr const char* specialModeService =
98     "xyz.openbmc_project.SpecialMode";
99 static constexpr const char* specialModeBasePath =
100     "/xyz/openbmc_project/security/special_mode";
101 static constexpr const char* specialModeIntf =
102     "xyz.openbmc_project.Security.SpecialMode";
103 static constexpr const char* specialModeProperty = "SpecialMode";
104 
105 static constexpr const char* dBusPropertyIntf =
106     "org.freedesktop.DBus.Properties";
107 static constexpr const char* dBusPropertyGetMethod = "Get";
108 static constexpr const char* dBusPropertySetMethod = "Set";
109 
110 // return code: 0 successful
111 int8_t getChassisSerialNumber(sdbusplus::bus::bus& bus, std::string& serial)
112 {
113     std::string objpath = "/xyz/openbmc_project/FruDevice";
114     std::string intf = "xyz.openbmc_project.FruDeviceManager";
115     std::string service = getService(bus, intf, objpath);
116     ObjectValueTree valueTree = getManagedObjects(bus, service, "/");
117     if (valueTree.empty())
118     {
119         phosphor::logging::log<phosphor::logging::level::ERR>(
120             "No object implements interface",
121             phosphor::logging::entry("INTF=%s", intf.c_str()));
122         return -1;
123     }
124 
125     for (const auto& item : valueTree)
126     {
127         auto interface = item.second.find("xyz.openbmc_project.FruDevice");
128         if (interface == item.second.end())
129         {
130             continue;
131         }
132 
133         auto property = interface->second.find("CHASSIS_SERIAL_NUMBER");
134         if (property == interface->second.end())
135         {
136             continue;
137         }
138 
139         try
140         {
141             Value variant = property->second;
142             std::string& result = std::get<std::string>(variant);
143             if (result.size() > maxFRUStringLength)
144             {
145                 phosphor::logging::log<phosphor::logging::level::ERR>(
146                     "FRU serial number exceed maximum length");
147                 return -1;
148             }
149             serial = result;
150             return 0;
151         }
152         catch (std::bad_variant_access& e)
153         {
154             phosphor::logging::log<phosphor::logging::level::ERR>(e.what());
155             return -1;
156         }
157     }
158     return -1;
159 }
160 
161 ipmi_ret_t ipmiOEMWildcard(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
162                            ipmi_request_t request, ipmi_response_t response,
163                            ipmi_data_len_t dataLen, ipmi_context_t context)
164 {
165     printCommand(+netfn, +cmd);
166     // Status code.
167     ipmi_ret_t rc = IPMI_CC_INVALID;
168     *dataLen = 0;
169     return rc;
170 }
171 
172 // Returns the Chassis Identifier (serial #)
173 ipmi_ret_t ipmiOEMGetChassisIdentifier(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
174                                        ipmi_request_t request,
175                                        ipmi_response_t response,
176                                        ipmi_data_len_t dataLen,
177                                        ipmi_context_t context)
178 {
179     std::string serial;
180     if (*dataLen != 0) // invalid request if there are extra parameters
181     {
182         *dataLen = 0;
183         return IPMI_CC_REQ_DATA_LEN_INVALID;
184     }
185     std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
186     if (getChassisSerialNumber(*dbus, serial) == 0)
187     {
188         *dataLen = serial.size(); // length will never exceed response length
189                                   // as it is checked in getChassisSerialNumber
190         char* resp = static_cast<char*>(response);
191         serial.copy(resp, *dataLen);
192         return IPMI_CC_OK;
193     }
194     *dataLen = 0;
195     return IPMI_CC_RESPONSE_ERROR;
196 }
197 
198 ipmi_ret_t ipmiOEMSetSystemGUID(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
199                                 ipmi_request_t request,
200                                 ipmi_response_t response,
201                                 ipmi_data_len_t dataLen, ipmi_context_t context)
202 {
203     static constexpr size_t safeBufferLength = 50;
204     char buf[safeBufferLength] = {0};
205     GUIDData* Data = reinterpret_cast<GUIDData*>(request);
206 
207     if (*dataLen != sizeof(GUIDData)) // 16bytes
208     {
209         *dataLen = 0;
210         return IPMI_CC_REQ_DATA_LEN_INVALID;
211     }
212 
213     *dataLen = 0;
214 
215     snprintf(
216         buf, safeBufferLength,
217         "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x",
218         Data->timeLow4, Data->timeLow3, Data->timeLow2, Data->timeLow1,
219         Data->timeMid2, Data->timeMid1, Data->timeHigh2, Data->timeHigh1,
220         Data->clock2, Data->clock1, Data->node6, Data->node5, Data->node4,
221         Data->node3, Data->node2, Data->node1);
222     // UUID is in RFC4122 format. Ex: 61a39523-78f2-11e5-9862-e6402cfc3223
223     std::string guid = buf;
224 
225     std::string objpath = "/xyz/openbmc_project/control/host0/systemGUID";
226     std::string intf = "xyz.openbmc_project.Common.UUID";
227     std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
228     std::string service = getService(*dbus, intf, objpath);
229     setDbusProperty(*dbus, service, objpath, intf, "UUID", guid);
230     return IPMI_CC_OK;
231 }
232 
233 ipmi::RspType<> ipmiOEMDisableBMCSystemReset(bool disableResetOnSMI,
234                                              uint7_t reserved1)
235 {
236     std::shared_ptr<sdbusplus::asio::connection> busp = getSdBus();
237 
238     try
239     {
240         auto service =
241             ipmi::getService(*busp, bmcResetDisablesIntf, bmcResetDisablesPath);
242         ipmi::setDbusProperty(*busp, service, bmcResetDisablesPath,
243                               bmcResetDisablesIntf, "ResetOnSMI",
244                               !disableResetOnSMI);
245     }
246     catch (std::exception& e)
247     {
248         phosphor::logging::log<phosphor::logging::level::ERR>(
249             "Failed to set BMC reset disables",
250             phosphor::logging::entry("EXCEPTION=%s", e.what()));
251         return ipmi::responseUnspecifiedError();
252     }
253 
254     return ipmi::responseSuccess();
255 }
256 
257 ipmi::RspType<bool,   // disableResetOnSMI
258               uint7_t // reserved
259               >
260     ipmiOEMGetBMCResetDisables()
261 {
262     bool disableResetOnSMI = true;
263 
264     std::shared_ptr<sdbusplus::asio::connection> busp = getSdBus();
265     try
266     {
267         auto service =
268             ipmi::getService(*busp, bmcResetDisablesIntf, bmcResetDisablesPath);
269         Value variant =
270             ipmi::getDbusProperty(*busp, service, bmcResetDisablesPath,
271                                   bmcResetDisablesIntf, "ResetOnSMI");
272         disableResetOnSMI = !std::get<bool>(variant);
273     }
274     catch (std::exception& e)
275     {
276         phosphor::logging::log<phosphor::logging::level::ERR>(
277             "Failed to get BMC reset disables",
278             phosphor::logging::entry("EXCEPTION=%s", e.what()));
279         return ipmi::responseUnspecifiedError();
280     }
281 
282     return ipmi::responseSuccess(disableResetOnSMI, 0);
283 }
284 
285 ipmi_ret_t ipmiOEMSetBIOSID(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
286                             ipmi_request_t request, ipmi_response_t response,
287                             ipmi_data_len_t dataLen, ipmi_context_t context)
288 {
289     DeviceInfo* data = reinterpret_cast<DeviceInfo*>(request);
290 
291     if ((*dataLen < 2) || (*dataLen != (1 + data->biosIDLength)))
292     {
293         *dataLen = 0;
294         return IPMI_CC_REQ_DATA_LEN_INVALID;
295     }
296     std::string idString((char*)data->biosId, data->biosIDLength);
297 
298     std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
299     std::string service = getService(*dbus, biosIntf, biosObjPath);
300     setDbusProperty(*dbus, service, biosObjPath, biosIntf, biosProp, idString);
301     uint8_t* bytesWritten = static_cast<uint8_t*>(response);
302     *bytesWritten =
303         data->biosIDLength; // how many bytes are written into storage
304     *dataLen = 1;
305     return IPMI_CC_OK;
306 }
307 
308 bool getSwVerInfo(uint8_t& bmcMajor, uint8_t& bmcMinor, uint8_t& meMajor,
309                   uint8_t& meMinor)
310 {
311     // step 1 : get BMC Major and Minor numbers from its DBUS property
312     std::optional<MetaRevision> rev{};
313     try
314     {
315         std::string version = getActiveSoftwareVersionInfo();
316         rev = convertIntelVersion(version);
317     }
318     catch (const std::exception& e)
319     {
320         return false;
321     }
322 
323     if (rev.has_value())
324     {
325         MetaRevision revision = rev.value();
326         bmcMajor = revision.major;
327 
328         revision.minor = (revision.minor > 99 ? 99 : revision.minor);
329         bmcMinor = revision.minor % 10 + (revision.minor / 10) * 16;
330     }
331 
332     // step 2 : get ME Major and Minor numbers from its DBUS property
333     try
334     {
335         std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
336         std::string service =
337             getService(*dbus, "xyz.openbmc_project.Software.Version",
338                        "/xyz/openbmc_project/me_version");
339         Value variant =
340             getDbusProperty(*dbus, service, "/xyz/openbmc_project/me_version",
341                             "xyz.openbmc_project.Software.Version", "Version");
342 
343         std::string& meString = std::get<std::string>(variant);
344 
345         // get ME major number
346         std::regex pattern1("(\\d+?).(\\d+?).(\\d+?).(\\d+?).(\\d+?)");
347         constexpr size_t matchedPhosphor = 6;
348         std::smatch results;
349         if (std::regex_match(meString, results, pattern1))
350         {
351             if (results.size() == matchedPhosphor)
352             {
353                 meMajor = static_cast<uint8_t>(std::stoi(results[1]));
354                 meMinor = static_cast<uint8_t>(std::stoi(results[2]));
355             }
356         }
357     }
358     catch (sdbusplus::exception::SdBusError& e)
359     {
360         return false;
361     }
362     return true;
363 }
364 
365 ipmi::RspType<
366     std::variant<std::string,
367                  std::tuple<uint8_t, std::array<uint8_t, 2>,
368                             std::array<uint8_t, 2>, std::array<uint8_t, 2>,
369                             std::array<uint8_t, 2>, std::array<uint8_t, 2>>,
370                  std::tuple<uint8_t, std::array<uint8_t, 2>>>>
371     ipmiOEMGetDeviceInfo(uint8_t entityType, uint8_t countToRead,
372                          uint8_t offset)
373 {
374     if (countToRead == 0)
375     {
376         return ipmi::responseReqDataLenInvalid();
377     }
378 
379     if (entityType > static_cast<uint8_t>(OEMDevEntityType::sdrVer))
380     {
381         return ipmi::responseInvalidFieldRequest();
382     }
383 
384     // handle OEM command items
385     switch (OEMDevEntityType(entityType))
386     {
387         case OEMDevEntityType::biosId:
388         {
389             std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
390             std::string service = getService(*dbus, biosIntf, biosObjPath);
391             try
392             {
393                 Value variant = getDbusProperty(*dbus, service, biosObjPath,
394                                                 biosIntf, biosProp);
395                 std::string& idString = std::get<std::string>(variant);
396                 if (offset >= idString.size())
397                 {
398                     return ipmi::responseParmOutOfRange();
399                 }
400                 size_t length = 0;
401                 if (countToRead > (idString.size() - offset))
402                 {
403                     length = idString.size() - offset;
404                 }
405                 else
406                 {
407                     length = countToRead;
408                 }
409 
410                 std::string readBuf = {0};
411                 readBuf.resize(length);
412                 std::copy_n(idString.begin() + offset, length,
413                             (readBuf.begin()));
414                 return ipmi::responseSuccess(readBuf);
415             }
416             catch (std::bad_variant_access& e)
417             {
418                 return ipmi::responseUnspecifiedError();
419             }
420         }
421         break;
422 
423         case OEMDevEntityType::devVer:
424         {
425             constexpr const size_t verLen = 2;
426             constexpr const size_t verTotalLen = 10;
427             std::array<uint8_t, verLen> bmcBuf = {0xff, 0xff};
428             std::array<uint8_t, verLen> hsc0Buf = {0xff, 0xff};
429             std::array<uint8_t, verLen> hsc1Buf = {0xff, 0xff};
430             std::array<uint8_t, verLen> meBuf = {0xff, 0xff};
431             std::array<uint8_t, verLen> hsc2Buf = {0xff, 0xff};
432             // data0/1: BMC version number; data6/7: ME version number
433             // the others: HSC0/1/2 version number, not avaible.
434             if (true != getSwVerInfo(bmcBuf[0], bmcBuf[1], meBuf[0], meBuf[1]))
435             {
436                 return ipmi::responseUnspecifiedError();
437             }
438             return ipmi::responseSuccess(
439                 std::tuple<
440                     uint8_t, std::array<uint8_t, verLen>,
441                     std::array<uint8_t, verLen>, std::array<uint8_t, verLen>,
442                     std::array<uint8_t, verLen>, std::array<uint8_t, verLen>>{
443                     verTotalLen, bmcBuf, hsc0Buf, hsc1Buf, meBuf, hsc2Buf});
444         }
445         break;
446 
447         case OEMDevEntityType::sdrVer:
448         {
449             constexpr const size_t sdrLen = 2;
450             std::array<uint8_t, sdrLen> readBuf = {0x01, 0x0};
451             return ipmi::responseSuccess(
452                 std::tuple<uint8_t, std::array<uint8_t, sdrLen>>{sdrLen,
453                                                                  readBuf});
454         }
455         break;
456 
457         default:
458             return ipmi::responseInvalidFieldRequest();
459     }
460 }
461 
462 ipmi_ret_t ipmiOEMGetAICFRU(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
463                             ipmi_request_t request, ipmi_response_t response,
464                             ipmi_data_len_t dataLen, ipmi_context_t context)
465 {
466     if (*dataLen != 0)
467     {
468         *dataLen = 0;
469         return IPMI_CC_REQ_DATA_LEN_INVALID;
470     }
471 
472     *dataLen = 1;
473     uint8_t* res = reinterpret_cast<uint8_t*>(response);
474     // temporary fix. We don't support AIC FRU now. Just tell BIOS that no
475     // AIC is available so that BIOS will not timeout repeatly which leads to
476     // slow booting.
477     *res = 0; // Byte1=Count of SlotPosition/FruID records.
478     return IPMI_CC_OK;
479 }
480 
481 ipmi_ret_t ipmiOEMGetPowerRestoreDelay(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
482                                        ipmi_request_t request,
483                                        ipmi_response_t response,
484                                        ipmi_data_len_t dataLen,
485                                        ipmi_context_t context)
486 {
487     GetPowerRestoreDelayRes* resp =
488         reinterpret_cast<GetPowerRestoreDelayRes*>(response);
489 
490     if (*dataLen != 0)
491     {
492         *dataLen = 0;
493         return IPMI_CC_REQ_DATA_LEN_INVALID;
494     }
495 
496     std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
497     std::string service =
498         getService(*dbus, powerRestoreDelayIntf, powerRestoreDelayObjPath);
499     Value variant =
500         getDbusProperty(*dbus, service, powerRestoreDelayObjPath,
501                         powerRestoreDelayIntf, powerRestoreDelayProp);
502 
503     uint16_t delay = std::get<uint16_t>(variant);
504     resp->byteLSB = delay;
505     resp->byteMSB = delay >> 8;
506 
507     *dataLen = sizeof(GetPowerRestoreDelayRes);
508 
509     return IPMI_CC_OK;
510 }
511 
512 static uint8_t bcdToDec(uint8_t val)
513 {
514     return ((val / 16 * 10) + (val % 16));
515 }
516 
517 // Allows an update utility or system BIOS to send the status of an embedded
518 // firmware update attempt to the BMC. After received, BMC will create a logging
519 // record.
520 ipmi::RspType<> ipmiOEMSendEmbeddedFwUpdStatus(uint8_t status, uint8_t target,
521                                                uint8_t majorRevision,
522                                                uint8_t minorRevision,
523                                                uint32_t auxInfo)
524 {
525     std::string firmware;
526     int instance = (target & targetInstanceMask) >> targetInstanceShift;
527     target = (target & selEvtTargetMask) >> selEvtTargetShift;
528 
529     /* make sure the status is 0, 1, or 2 as per the spec */
530     if (status > 2)
531     {
532         return ipmi::response(ipmi::ccInvalidFieldRequest);
533     }
534     /* make sure the target is 0, 1, 2, or 4 as per the spec */
535     if (target > 4 || target == 3)
536     {
537         return ipmi::response(ipmi::ccInvalidFieldRequest);
538     }
539     /*orignal OEM command is to record OEM SEL.
540     But openbmc does not support OEM SEL, so we redirect it to redfish event
541     logging. */
542     std::string buildInfo;
543     std::string action;
544     switch (FWUpdateTarget(target))
545     {
546         case FWUpdateTarget::targetBMC:
547             firmware = "BMC";
548             buildInfo = "major: " + std::to_string(majorRevision) + " minor: " +
549                         std::to_string(bcdToDec(minorRevision)) + // BCD encoded
550                         " BuildID: " + std::to_string(auxInfo);
551             buildInfo += std::to_string(auxInfo);
552             break;
553         case FWUpdateTarget::targetBIOS:
554             firmware = "BIOS";
555             buildInfo =
556                 "major: " +
557                 std::to_string(bcdToDec(majorRevision)) + // BCD encoded
558                 " minor: " +
559                 std::to_string(bcdToDec(minorRevision)) + // BCD encoded
560                 " ReleaseNumber: " +                      // ASCII encoded
561                 std::to_string(static_cast<uint8_t>(auxInfo >> 0) - '0') +
562                 std::to_string(static_cast<uint8_t>(auxInfo >> 8) - '0') +
563                 std::to_string(static_cast<uint8_t>(auxInfo >> 16) - '0') +
564                 std::to_string(static_cast<uint8_t>(auxInfo >> 24) - '0');
565             break;
566         case FWUpdateTarget::targetME:
567             firmware = "ME";
568             buildInfo =
569                 "major: " + std::to_string(majorRevision) + " minor1: " +
570                 std::to_string(bcdToDec(minorRevision)) + // BCD encoded
571                 " minor2: " +
572                 std::to_string(bcdToDec(static_cast<uint8_t>(auxInfo >> 0))) +
573                 " build1: " +
574                 std::to_string(bcdToDec(static_cast<uint8_t>(auxInfo >> 8))) +
575                 " build2: " +
576                 std::to_string(bcdToDec(static_cast<uint8_t>(auxInfo >> 16)));
577             break;
578         case FWUpdateTarget::targetOEMEWS:
579             firmware = "EWS";
580             buildInfo = "major: " + std::to_string(majorRevision) + " minor: " +
581                         std::to_string(bcdToDec(minorRevision)) + // BCD encoded
582                         " BuildID: " + std::to_string(auxInfo);
583             break;
584     }
585 
586     static const std::string openBMCMessageRegistryVersion("0.1");
587     std::string redfishMsgID = "OpenBMC." + openBMCMessageRegistryVersion;
588 
589     switch (status)
590     {
591         case 0x0:
592             action = "update started";
593             redfishMsgID += ".FirmwareUpdateStarted";
594             break;
595         case 0x1:
596             action = "update completed successfully";
597             redfishMsgID += ".FirmwareUpdateCompleted";
598             break;
599         case 0x2:
600             action = "update failure";
601             redfishMsgID += ".FirmwareUpdateFailed";
602             break;
603         default:
604             action = "unknown";
605             break;
606     }
607 
608     std::string firmwareInstanceStr =
609         firmware + " instance: " + std::to_string(instance);
610     std::string message("[firmware update] " + firmwareInstanceStr +
611                         " status: <" + action + "> " + buildInfo);
612 
613     sd_journal_send("MESSAGE=%s", message.c_str(), "PRIORITY=%i", LOG_INFO,
614                     "REDFISH_MESSAGE_ID=%s", redfishMsgID.c_str(),
615                     "REDFISH_MESSAGE_ARGS=%s,%s", firmwareInstanceStr.c_str(),
616                     buildInfo.c_str(), NULL);
617     return ipmi::responseSuccess();
618 }
619 
620 ipmi_ret_t ipmiOEMSetPowerRestoreDelay(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
621                                        ipmi_request_t request,
622                                        ipmi_response_t response,
623                                        ipmi_data_len_t dataLen,
624                                        ipmi_context_t context)
625 {
626     SetPowerRestoreDelayReq* data =
627         reinterpret_cast<SetPowerRestoreDelayReq*>(request);
628     uint16_t delay = 0;
629 
630     if (*dataLen != sizeof(SetPowerRestoreDelayReq))
631     {
632         *dataLen = 0;
633         return IPMI_CC_REQ_DATA_LEN_INVALID;
634     }
635     delay = data->byteMSB;
636     delay = (delay << 8) | data->byteLSB;
637     std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
638     std::string service =
639         getService(*dbus, powerRestoreDelayIntf, powerRestoreDelayObjPath);
640     setDbusProperty(*dbus, service, powerRestoreDelayObjPath,
641                     powerRestoreDelayIntf, powerRestoreDelayProp, delay);
642     *dataLen = 0;
643 
644     return IPMI_CC_OK;
645 }
646 
647 static bool cpuPresent(const std::string& cpuName)
648 {
649     static constexpr const char* cpuPresencePathPrefix =
650         "/xyz/openbmc_project/inventory/system/chassis/motherboard/";
651     static constexpr const char* cpuPresenceIntf =
652         "xyz.openbmc_project.Inventory.Item";
653     std::string cpuPresencePath = cpuPresencePathPrefix + cpuName;
654     std::shared_ptr<sdbusplus::asio::connection> busp = getSdBus();
655     try
656     {
657         auto service =
658             ipmi::getService(*busp, cpuPresenceIntf, cpuPresencePath);
659 
660         ipmi::Value result = ipmi::getDbusProperty(
661             *busp, service, cpuPresencePath, cpuPresenceIntf, "Present");
662         return std::get<bool>(result);
663     }
664     catch (const std::exception& e)
665     {
666         phosphor::logging::log<phosphor::logging::level::INFO>(
667             "Cannot find processor presence",
668             phosphor::logging::entry("NAME=%s", cpuName.c_str()));
669         return false;
670     }
671 }
672 
673 ipmi::RspType<bool,    // CATERR Reset Enabled
674               bool,    // ERR2 Reset Enabled
675               uint6_t, // reserved
676               uint8_t, // reserved, returns 0x3F
677               uint6_t, // CPU1 CATERR Count
678               uint2_t, // CPU1 Status
679               uint6_t, // CPU2 CATERR Count
680               uint2_t, // CPU2 Status
681               uint6_t, // CPU3 CATERR Count
682               uint2_t, // CPU3 Status
683               uint6_t, // CPU4 CATERR Count
684               uint2_t, // CPU4 Status
685               uint8_t  // Crashdump Count
686               >
687     ipmiOEMGetProcessorErrConfig()
688 {
689     bool resetOnCATERR = false;
690     bool resetOnERR2 = false;
691     uint6_t cpu1CATERRCount = 0;
692     uint6_t cpu2CATERRCount = 0;
693     uint6_t cpu3CATERRCount = 0;
694     uint6_t cpu4CATERRCount = 0;
695     uint8_t crashdumpCount = 0;
696     uint2_t cpu1Status =
697         cpuPresent("CPU_1") ? CPUStatus::enabled : CPUStatus::notPresent;
698     uint2_t cpu2Status =
699         cpuPresent("CPU_2") ? CPUStatus::enabled : CPUStatus::notPresent;
700     uint2_t cpu3Status =
701         cpuPresent("CPU_3") ? CPUStatus::enabled : CPUStatus::notPresent;
702     uint2_t cpu4Status =
703         cpuPresent("CPU_4") ? CPUStatus::enabled : CPUStatus::notPresent;
704 
705     std::shared_ptr<sdbusplus::asio::connection> busp = getSdBus();
706     try
707     {
708         auto service = ipmi::getService(*busp, processorErrConfigIntf,
709                                         processorErrConfigObjPath);
710 
711         ipmi::PropertyMap result = ipmi::getAllDbusProperties(
712             *busp, service, processorErrConfigObjPath, processorErrConfigIntf);
713         resetOnCATERR = std::get<bool>(result.at("ResetOnCATERR"));
714         resetOnERR2 = std::get<bool>(result.at("ResetOnERR2"));
715         cpu1CATERRCount = std::get<uint8_t>(result.at("ErrorCountCPU1"));
716         cpu2CATERRCount = std::get<uint8_t>(result.at("ErrorCountCPU2"));
717         cpu3CATERRCount = std::get<uint8_t>(result.at("ErrorCountCPU3"));
718         cpu4CATERRCount = std::get<uint8_t>(result.at("ErrorCountCPU4"));
719         crashdumpCount = std::get<uint8_t>(result.at("CrashdumpCount"));
720     }
721     catch (const std::exception& e)
722     {
723         phosphor::logging::log<phosphor::logging::level::ERR>(
724             "Failed to fetch processor error config",
725             phosphor::logging::entry("ERROR=%s", e.what()));
726         return ipmi::responseUnspecifiedError();
727     }
728 
729     return ipmi::responseSuccess(resetOnCATERR, resetOnERR2, 0, 0x3F,
730                                  cpu1CATERRCount, cpu1Status, cpu2CATERRCount,
731                                  cpu2Status, cpu3CATERRCount, cpu3Status,
732                                  cpu4CATERRCount, cpu4Status, crashdumpCount);
733 }
734 
735 ipmi::RspType<> ipmiOEMSetProcessorErrConfig(
736     bool resetOnCATERR, bool resetOnERR2, uint6_t reserved1, uint8_t reserved2,
737     std::optional<bool> clearCPUErrorCount,
738     std::optional<bool> clearCrashdumpCount, std::optional<uint6_t> reserved3)
739 {
740     std::shared_ptr<sdbusplus::asio::connection> busp = getSdBus();
741 
742     try
743     {
744         auto service = ipmi::getService(*busp, processorErrConfigIntf,
745                                         processorErrConfigObjPath);
746         ipmi::setDbusProperty(*busp, service, processorErrConfigObjPath,
747                               processorErrConfigIntf, "ResetOnCATERR",
748                               resetOnCATERR);
749         ipmi::setDbusProperty(*busp, service, processorErrConfigObjPath,
750                               processorErrConfigIntf, "ResetOnERR2",
751                               resetOnERR2);
752         if (clearCPUErrorCount.value_or(false))
753         {
754             ipmi::setDbusProperty(*busp, service, processorErrConfigObjPath,
755                                   processorErrConfigIntf, "ErrorCountCPU1",
756                                   static_cast<uint8_t>(0));
757             ipmi::setDbusProperty(*busp, service, processorErrConfigObjPath,
758                                   processorErrConfigIntf, "ErrorCountCPU2",
759                                   static_cast<uint8_t>(0));
760             ipmi::setDbusProperty(*busp, service, processorErrConfigObjPath,
761                                   processorErrConfigIntf, "ErrorCountCPU3",
762                                   static_cast<uint8_t>(0));
763             ipmi::setDbusProperty(*busp, service, processorErrConfigObjPath,
764                                   processorErrConfigIntf, "ErrorCountCPU4",
765                                   static_cast<uint8_t>(0));
766         }
767         if (clearCrashdumpCount.value_or(false))
768         {
769             ipmi::setDbusProperty(*busp, service, processorErrConfigObjPath,
770                                   processorErrConfigIntf, "CrashdumpCount",
771                                   static_cast<uint8_t>(0));
772         }
773     }
774     catch (std::exception& e)
775     {
776         phosphor::logging::log<phosphor::logging::level::ERR>(
777             "Failed to set processor error config",
778             phosphor::logging::entry("EXCEPTION=%s", e.what()));
779         return ipmi::responseUnspecifiedError();
780     }
781 
782     return ipmi::responseSuccess();
783 }
784 
785 ipmi_ret_t ipmiOEMGetShutdownPolicy(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
786                                     ipmi_request_t request,
787                                     ipmi_response_t response,
788                                     ipmi_data_len_t dataLen,
789                                     ipmi_context_t context)
790 {
791     GetOEMShutdownPolicyRes* resp =
792         reinterpret_cast<GetOEMShutdownPolicyRes*>(response);
793 
794     if (*dataLen != 0)
795     {
796         phosphor::logging::log<phosphor::logging::level::ERR>(
797             "oem_get_shutdown_policy: invalid input len!");
798         *dataLen = 0;
799         return IPMI_CC_REQ_DATA_LEN_INVALID;
800     }
801 
802     *dataLen = 0;
803 
804     try
805     {
806         std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
807         std::string service =
808             getService(*dbus, oemShutdownPolicyIntf, oemShutdownPolicyObjPath);
809         Value variant = getDbusProperty(
810             *dbus, service, oemShutdownPolicyObjPath, oemShutdownPolicyIntf,
811             oemShutdownPolicyObjPathProp);
812 
813         if (sdbusplus::com::intel::Control::server::OCOTShutdownPolicy::
814                 convertPolicyFromString(std::get<std::string>(variant)) ==
815             sdbusplus::com::intel::Control::server::OCOTShutdownPolicy::Policy::
816                 NoShutdownOnOCOT)
817         {
818             resp->policy = 0;
819         }
820         else if (sdbusplus::com::intel::Control::server::OCOTShutdownPolicy::
821                      convertPolicyFromString(std::get<std::string>(variant)) ==
822                  sdbusplus::com::intel::Control::server::OCOTShutdownPolicy::
823                      Policy::ShutdownOnOCOT)
824         {
825             resp->policy = 1;
826         }
827         else
828         {
829             phosphor::logging::log<phosphor::logging::level::ERR>(
830                 "oem_set_shutdown_policy: invalid property!",
831                 phosphor::logging::entry(
832                     "PROP=%s", std::get<std::string>(variant).c_str()));
833             return IPMI_CC_UNSPECIFIED_ERROR;
834         }
835         // TODO needs to check if it is multi-node products,
836         // policy is only supported on node 3/4
837         resp->policySupport = shutdownPolicySupported;
838     }
839     catch (sdbusplus::exception_t& e)
840     {
841         phosphor::logging::log<phosphor::logging::level::ERR>(e.description());
842         return IPMI_CC_UNSPECIFIED_ERROR;
843     }
844 
845     *dataLen = sizeof(GetOEMShutdownPolicyRes);
846     return IPMI_CC_OK;
847 }
848 
849 ipmi_ret_t ipmiOEMSetShutdownPolicy(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
850                                     ipmi_request_t request,
851                                     ipmi_response_t response,
852                                     ipmi_data_len_t dataLen,
853                                     ipmi_context_t context)
854 {
855     uint8_t* req = reinterpret_cast<uint8_t*>(request);
856     sdbusplus::com::intel::Control::server::OCOTShutdownPolicy::Policy policy =
857         sdbusplus::com::intel::Control::server::OCOTShutdownPolicy::Policy::
858             NoShutdownOnOCOT;
859 
860     // TODO needs to check if it is multi-node products,
861     // policy is only supported on node 3/4
862     if (*dataLen != 1)
863     {
864         phosphor::logging::log<phosphor::logging::level::ERR>(
865             "oem_set_shutdown_policy: invalid input len!");
866         *dataLen = 0;
867         return IPMI_CC_REQ_DATA_LEN_INVALID;
868     }
869 
870     *dataLen = 0;
871     if ((*req != noShutdownOnOCOT) && (*req != shutdownOnOCOT))
872     {
873         phosphor::logging::log<phosphor::logging::level::ERR>(
874             "oem_set_shutdown_policy: invalid input!");
875         return IPMI_CC_INVALID_FIELD_REQUEST;
876     }
877 
878     if (*req == noShutdownOnOCOT)
879     {
880         policy = sdbusplus::com::intel::Control::server::OCOTShutdownPolicy::
881             Policy::NoShutdownOnOCOT;
882     }
883     else
884     {
885         policy = sdbusplus::com::intel::Control::server::OCOTShutdownPolicy::
886             Policy::ShutdownOnOCOT;
887     }
888 
889     try
890     {
891         std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
892         std::string service =
893             getService(*dbus, oemShutdownPolicyIntf, oemShutdownPolicyObjPath);
894         setDbusProperty(
895             *dbus, service, oemShutdownPolicyObjPath, oemShutdownPolicyIntf,
896             oemShutdownPolicyObjPathProp,
897             sdbusplus::com::intel::Control::server::convertForMessage(policy));
898     }
899     catch (sdbusplus::exception_t& e)
900     {
901         phosphor::logging::log<phosphor::logging::level::ERR>(e.description());
902         return IPMI_CC_UNSPECIFIED_ERROR;
903     }
904 
905     return IPMI_CC_OK;
906 }
907 
908 /** @brief implementation for check the DHCP or not in IPv4
909  *  @param[in] Channel - Channel number
910  *  @returns true or false.
911  */
912 static bool isDHCPEnabled(uint8_t Channel)
913 {
914     try
915     {
916         auto ethdevice = getChannelName(Channel);
917         if (ethdevice.empty())
918         {
919             return false;
920         }
921         auto ethIP = ethdevice + "/ipv4";
922         std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
923         auto ethernetObj =
924             getDbusObject(*dbus, networkIPIntf, networkRoot, ethIP);
925         auto value = getDbusProperty(*dbus, networkService, ethernetObj.first,
926                                      networkIPIntf, "Origin");
927         if (std::get<std::string>(value) ==
928             "xyz.openbmc_project.Network.IP.AddressOrigin.DHCP")
929         {
930             return true;
931         }
932         else
933         {
934             return false;
935         }
936     }
937     catch (sdbusplus::exception_t& e)
938     {
939         phosphor::logging::log<phosphor::logging::level::ERR>(e.description());
940         return true;
941     }
942 }
943 
944 /** @brief implementes for check the DHCP or not in IPv6
945  *  @param[in] Channel - Channel number
946  *  @returns true or false.
947  */
948 static bool isDHCPIPv6Enabled(uint8_t Channel)
949 {
950 
951     try
952     {
953         auto ethdevice = getChannelName(Channel);
954         if (ethdevice.empty())
955         {
956             return false;
957         }
958         auto ethIP = ethdevice + "/ipv6";
959         std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
960         auto objectInfo =
961             getDbusObject(*dbus, networkIPIntf, networkRoot, ethIP);
962         auto properties = getAllDbusProperties(*dbus, objectInfo.second,
963                                                objectInfo.first, networkIPIntf);
964         if (std::get<std::string>(properties["Origin"]) ==
965             "xyz.openbmc_project.Network.IP.AddressOrigin.DHCP")
966         {
967             return true;
968         }
969         else
970         {
971             return false;
972         }
973     }
974     catch (sdbusplus::exception_t& e)
975     {
976         phosphor::logging::log<phosphor::logging::level::ERR>(e.description());
977         return true;
978     }
979 }
980 
981 /** @brief implementes the creating of default new user
982  *  @param[in] userName - new username in 16 bytes.
983  *  @param[in] userPassword - new password in 20 bytes
984  *  @returns ipmi completion code.
985  */
986 ipmi::RspType<> ipmiOEMSetUser2Activation(
987     std::array<uint8_t, ipmi::ipmiMaxUserName>& userName,
988     std::array<uint8_t, ipmi::maxIpmi20PasswordSize>& userPassword)
989 {
990     bool userState = false;
991     // Check for System Interface not exist and LAN should be static
992     for (uint8_t channel = 0; channel < maxIpmiChannels; channel++)
993     {
994         ChannelInfo chInfo;
995         try
996         {
997             getChannelInfo(channel, chInfo);
998         }
999         catch (sdbusplus::exception_t& e)
1000         {
1001             phosphor::logging::log<phosphor::logging::level::ERR>(
1002                 "ipmiOEMSetUser2Activation: Failed to get Channel Info",
1003                 phosphor::logging::entry("MSG: %s", e.description()));
1004             return ipmi::response(ipmi::ccUnspecifiedError);
1005         }
1006         if (chInfo.mediumType ==
1007             static_cast<uint8_t>(EChannelMediumType::systemInterface))
1008         {
1009             phosphor::logging::log<phosphor::logging::level::ERR>(
1010                 "ipmiOEMSetUser2Activation: system interface  exist .");
1011             return ipmi::response(ipmi::ccCommandNotAvailable);
1012         }
1013         else
1014         {
1015 
1016             if (chInfo.mediumType ==
1017                 static_cast<uint8_t>(EChannelMediumType::lan8032))
1018             {
1019                 if (isDHCPIPv6Enabled(channel) || isDHCPEnabled(channel))
1020                 {
1021                     phosphor::logging::log<phosphor::logging::level::ERR>(
1022                         "ipmiOEMSetUser2Activation: DHCP enabled .");
1023                     return ipmi::response(ipmi::ccCommandNotAvailable);
1024                 }
1025             }
1026         }
1027     }
1028     uint8_t maxChUsers = 0, enabledUsers = 0, fixedUsers = 0;
1029     if (ipmi::ccSuccess ==
1030         ipmiUserGetAllCounts(maxChUsers, enabledUsers, fixedUsers))
1031     {
1032         if (enabledUsers > 1)
1033         {
1034             phosphor::logging::log<phosphor::logging::level::ERR>(
1035                 "ipmiOEMSetUser2Activation: more than one user is enabled.");
1036             return ipmi::response(ipmi::ccCommandNotAvailable);
1037         }
1038         // Check the user 2 is enabled or not
1039         ipmiUserCheckEnabled(ipmiDefaultUserId, userState);
1040         if (userState == true)
1041         {
1042             phosphor::logging::log<phosphor::logging::level::ERR>(
1043                 "ipmiOEMSetUser2Activation: user 2 already enabled .");
1044             return ipmi::response(ipmi::ccCommandNotAvailable);
1045         }
1046     }
1047     else
1048     {
1049         return ipmi::response(ipmi::ccUnspecifiedError);
1050     }
1051 
1052 #if BYTE_ORDER == LITTLE_ENDIAN
1053     PrivAccess privAccess = {PRIVILEGE_ADMIN, true, true, true, 0};
1054 #endif
1055 #if BYTE_ORDER == BIG_ENDIAN
1056     PrivAccess privAccess = {0, true, true, true, PRIVILEGE_ADMIN};
1057 #endif
1058 
1059     if (ipmi::ccSuccess ==
1060         ipmiUserSetUserName(ipmiDefaultUserId,
1061                             reinterpret_cast<const char*>(userName.data())))
1062     {
1063         if (ipmi::ccSuccess ==
1064             ipmiUserSetUserPassword(
1065                 ipmiDefaultUserId,
1066                 reinterpret_cast<const char*>(userPassword.data())))
1067         {
1068             if (ipmi::ccSuccess ==
1069                 ipmiUserSetPrivilegeAccess(
1070                     ipmiDefaultUserId,
1071                     static_cast<uint8_t>(ipmi::EChannelID::chanLan1),
1072                     privAccess, true))
1073             {
1074                 phosphor::logging::log<phosphor::logging::level::INFO>(
1075                     "ipmiOEMSetUser2Activation: user created successfully ");
1076                 return ipmi::responseSuccess();
1077             }
1078         }
1079         // we need to delete  the default user id which added in this command as
1080         // password / priv setting is failed.
1081         ipmiUserSetUserName(ipmiDefaultUserId, "");
1082         phosphor::logging::log<phosphor::logging::level::ERR>(
1083             "ipmiOEMSetUser2Activation: password / priv setting is failed.");
1084     }
1085     else
1086     {
1087         phosphor::logging::log<phosphor::logging::level::ERR>(
1088             "ipmiOEMSetUser2Activation: Setting username failed.");
1089     }
1090 
1091     return ipmi::response(ipmi::ccCommandNotAvailable);
1092 }
1093 
1094 /** @brief implementes executing the linux command
1095  *  @param[in] linux command
1096  *  @returns status
1097  */
1098 
1099 static uint8_t executeCmd(const char* path)
1100 {
1101     boost::process::child execProg(path);
1102     execProg.wait();
1103 
1104     int retCode = execProg.exit_code();
1105     if (retCode)
1106     {
1107         return ipmi::ccUnspecifiedError;
1108     }
1109     return ipmi::ccSuccess;
1110 }
1111 
1112 /** @brief implementes ASD Security event logging
1113  *  @param[in] Event message string
1114  *  @param[in] Event Severity
1115  *  @returns status
1116  */
1117 
1118 static void atScaleDebugEventlog(std::string msg, int severity)
1119 {
1120     std::string eventStr = "OpenBMC.0.1." + msg;
1121     sd_journal_send("MESSAGE=Security Event: %s", eventStr.c_str(),
1122                     "PRIORITY=%i", severity, "REDFISH_MESSAGE_ID=%s",
1123                     eventStr.c_str(), NULL);
1124 }
1125 
1126 /** @brief implementes setting password for special user
1127  *  @param[in] specialUserIndex
1128  *  @param[in] userPassword - new password in 20 bytes
1129  *  @returns ipmi completion code.
1130  */
1131 ipmi::RspType<> ipmiOEMSetSpecialUserPassword(ipmi::Context::ptr ctx,
1132                                               uint8_t specialUserIndex,
1133                                               std::vector<uint8_t> userPassword)
1134 {
1135     ChannelInfo chInfo;
1136     ipmi_ret_t status = ipmi::ccSuccess;
1137 
1138     try
1139     {
1140         getChannelInfo(ctx->channel, chInfo);
1141     }
1142     catch (sdbusplus::exception_t& e)
1143     {
1144         phosphor::logging::log<phosphor::logging::level::ERR>(
1145             "ipmiOEMSetSpecialUserPassword: Failed to get Channel Info",
1146             phosphor::logging::entry("MSG: %s", e.description()));
1147         return ipmi::responseUnspecifiedError();
1148     }
1149     if (chInfo.mediumType !=
1150         static_cast<uint8_t>(EChannelMediumType::systemInterface))
1151     {
1152         phosphor::logging::log<phosphor::logging::level::ERR>(
1153             "ipmiOEMSetSpecialUserPassword: Error - supported only in KCS "
1154             "interface");
1155         return ipmi::responseCommandNotAvailable();
1156     }
1157 
1158     // 0 for root user  and 1 for AtScaleDebug is allowed
1159     if (specialUserIndex >
1160         static_cast<uint8_t>(SpecialUserIndex::atScaleDebugUser))
1161     {
1162         phosphor::logging::log<phosphor::logging::level::ERR>(
1163             "ipmiOEMSetSpecialUserPassword: Invalid user account");
1164         return ipmi::responseParmOutOfRange();
1165     }
1166     if (userPassword.size() != 0)
1167     {
1168         constexpr uint8_t minPasswordSizeRequired = 6;
1169         std::string passwd;
1170         if (userPassword.size() < minPasswordSizeRequired ||
1171             userPassword.size() > ipmi::maxIpmi20PasswordSize)
1172         {
1173             return ipmi::responseReqDataLenInvalid();
1174         }
1175         passwd.assign(reinterpret_cast<const char*>(userPassword.data()),
1176                       userPassword.size());
1177         if (specialUserIndex ==
1178             static_cast<uint8_t>(SpecialUserIndex::atScaleDebugUser))
1179         {
1180             status = ipmiSetSpecialUserPassword("asdbg", passwd);
1181 
1182             atScaleDebugEventlog("AtScaleDebugSpecialUserEnabled", LOG_CRIT);
1183         }
1184         else
1185         {
1186             status = ipmiSetSpecialUserPassword("root", passwd);
1187         }
1188         return ipmi::response(status);
1189     }
1190     else
1191     {
1192         if (specialUserIndex ==
1193             static_cast<uint8_t>(SpecialUserIndex::rootUser))
1194         {
1195             status = executeCmd("passwd -d root");
1196         }
1197         else
1198         {
1199 
1200             status = executeCmd("passwd -d asdbg");
1201 
1202             if (status == 0)
1203             {
1204                 atScaleDebugEventlog("AtScaleDebugSpecialUserDisabled",
1205                                      LOG_INFO);
1206             }
1207         }
1208         return ipmi::response(status);
1209     }
1210 }
1211 
1212 namespace ledAction
1213 {
1214 using namespace sdbusplus::xyz::openbmc_project::Led::server;
1215 std::map<Physical::Action, uint8_t> actionDbusToIpmi = {
1216     {Physical::Action::Off, 0},
1217     {Physical::Action::On, 2},
1218     {Physical::Action::Blink, 1}};
1219 
1220 std::map<uint8_t, std::string> offsetObjPath = {
1221     {2, statusAmberObjPath}, {4, statusGreenObjPath}, {6, identifyLEDObjPath}};
1222 
1223 } // namespace ledAction
1224 
1225 int8_t getLEDState(sdbusplus::bus::bus& bus, const std::string& intf,
1226                    const std::string& objPath, uint8_t& state)
1227 {
1228     try
1229     {
1230         std::string service = getService(bus, intf, objPath);
1231         Value stateValue =
1232             getDbusProperty(bus, service, objPath, intf, "State");
1233         std::string strState = std::get<std::string>(stateValue);
1234         state = ledAction::actionDbusToIpmi.at(
1235             sdbusplus::xyz::openbmc_project::Led::server::Physical::
1236                 convertActionFromString(strState));
1237     }
1238     catch (sdbusplus::exception::SdBusError& e)
1239     {
1240         phosphor::logging::log<phosphor::logging::level::ERR>(e.what());
1241         return -1;
1242     }
1243     return 0;
1244 }
1245 
1246 ipmi_ret_t ipmiOEMGetLEDStatus(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
1247                                ipmi_request_t request, ipmi_response_t response,
1248                                ipmi_data_len_t dataLen, ipmi_context_t context)
1249 {
1250     uint8_t* resp = reinterpret_cast<uint8_t*>(response);
1251     // LED Status
1252     //[1:0] = Reserved
1253     //[3:2] = Status(Amber)
1254     //[5:4] = Status(Green)
1255     //[7:6] = System Identify
1256     // Status definitions:
1257     // 00b = Off
1258     // 01b = Blink
1259     // 10b = On
1260     // 11b = invalid
1261     if (*dataLen != 0)
1262     {
1263         phosphor::logging::log<phosphor::logging::level::ERR>(
1264             "oem_get_led_status: invalid input len!");
1265         *dataLen = 0;
1266         return IPMI_CC_REQ_DATA_LEN_INVALID;
1267     }
1268 
1269     phosphor::logging::log<phosphor::logging::level::DEBUG>("GET led status");
1270     *resp = 0;
1271     *dataLen = 0;
1272     std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
1273     for (auto it = ledAction::offsetObjPath.begin();
1274          it != ledAction::offsetObjPath.end(); ++it)
1275     {
1276         uint8_t state = 0;
1277         if (-1 == getLEDState(*dbus, ledIntf, it->second, state))
1278         {
1279             phosphor::logging::log<phosphor::logging::level::ERR>(
1280                 "oem_get_led_status: fail to get ID LED status!");
1281             return IPMI_CC_UNSPECIFIED_ERROR;
1282         }
1283         *resp |= state << it->first;
1284     }
1285 
1286     *dataLen = sizeof(*resp);
1287     return IPMI_CC_OK;
1288 }
1289 
1290 ipmi_ret_t ipmiOEMCfgHostSerialPortSpeed(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
1291                                          ipmi_request_t request,
1292                                          ipmi_response_t response,
1293                                          ipmi_data_len_t dataLen,
1294                                          ipmi_context_t context)
1295 {
1296     CfgHostSerialReq* req = reinterpret_cast<CfgHostSerialReq*>(request);
1297     uint8_t* resp = reinterpret_cast<uint8_t*>(response);
1298 
1299     if (*dataLen == 0)
1300     {
1301         phosphor::logging::log<phosphor::logging::level::ERR>(
1302             "CfgHostSerial: invalid input len!",
1303             phosphor::logging::entry("LEN=%d", *dataLen));
1304         return IPMI_CC_REQ_DATA_LEN_INVALID;
1305     }
1306 
1307     switch (req->command)
1308     {
1309         case getHostSerialCfgCmd:
1310         {
1311             if (*dataLen != 1)
1312             {
1313                 phosphor::logging::log<phosphor::logging::level::ERR>(
1314                     "CfgHostSerial: invalid input len!");
1315                 *dataLen = 0;
1316                 return IPMI_CC_REQ_DATA_LEN_INVALID;
1317             }
1318 
1319             *dataLen = 0;
1320 
1321             boost::process::ipstream is;
1322             std::vector<std::string> data;
1323             std::string line;
1324             boost::process::child c1(fwGetEnvCmd, "-n", fwHostSerailCfgEnvName,
1325                                      boost::process::std_out > is);
1326 
1327             while (c1.running() && std::getline(is, line) && !line.empty())
1328             {
1329                 data.push_back(line);
1330             }
1331 
1332             c1.wait();
1333             if (c1.exit_code())
1334             {
1335                 phosphor::logging::log<phosphor::logging::level::ERR>(
1336                     "CfgHostSerial:: error on execute",
1337                     phosphor::logging::entry("EXECUTE=%s", fwSetEnvCmd));
1338                 // Using the default value
1339                 *resp = 0;
1340             }
1341             else
1342             {
1343                 if (data.size() != 1)
1344                 {
1345                     phosphor::logging::log<phosphor::logging::level::ERR>(
1346                         "CfgHostSerial:: error on read env");
1347                     return IPMI_CC_UNSPECIFIED_ERROR;
1348                 }
1349                 try
1350                 {
1351                     unsigned long tmp = std::stoul(data[0]);
1352                     if (tmp > std::numeric_limits<uint8_t>::max())
1353                     {
1354                         throw std::out_of_range("Out of range");
1355                     }
1356                     *resp = static_cast<uint8_t>(tmp);
1357                 }
1358                 catch (const std::invalid_argument& e)
1359                 {
1360                     phosphor::logging::log<phosphor::logging::level::ERR>(
1361                         "invalid config ",
1362                         phosphor::logging::entry("ERR=%s", e.what()));
1363                     return IPMI_CC_UNSPECIFIED_ERROR;
1364                 }
1365                 catch (const std::out_of_range& e)
1366                 {
1367                     phosphor::logging::log<phosphor::logging::level::ERR>(
1368                         "out_of_range config ",
1369                         phosphor::logging::entry("ERR=%s", e.what()));
1370                     return IPMI_CC_UNSPECIFIED_ERROR;
1371                 }
1372             }
1373 
1374             *dataLen = 1;
1375             break;
1376         }
1377         case setHostSerialCfgCmd:
1378         {
1379             if (*dataLen != sizeof(CfgHostSerialReq))
1380             {
1381                 phosphor::logging::log<phosphor::logging::level::ERR>(
1382                     "CfgHostSerial: invalid input len!");
1383                 *dataLen = 0;
1384                 return IPMI_CC_REQ_DATA_LEN_INVALID;
1385             }
1386 
1387             *dataLen = 0;
1388 
1389             if (req->parameter > HostSerialCfgParamMax)
1390             {
1391                 phosphor::logging::log<phosphor::logging::level::ERR>(
1392                     "CfgHostSerial: invalid input!");
1393                 return IPMI_CC_INVALID_FIELD_REQUEST;
1394             }
1395 
1396             boost::process::child c1(fwSetEnvCmd, fwHostSerailCfgEnvName,
1397                                      std::to_string(req->parameter));
1398 
1399             c1.wait();
1400             if (c1.exit_code())
1401             {
1402                 phosphor::logging::log<phosphor::logging::level::ERR>(
1403                     "CfgHostSerial:: error on execute",
1404                     phosphor::logging::entry("EXECUTE=%s", fwGetEnvCmd));
1405                 return IPMI_CC_UNSPECIFIED_ERROR;
1406             }
1407             break;
1408         }
1409         default:
1410             phosphor::logging::log<phosphor::logging::level::ERR>(
1411                 "CfgHostSerial: invalid input!");
1412             *dataLen = 0;
1413             return IPMI_CC_INVALID_FIELD_REQUEST;
1414     }
1415 
1416     return IPMI_CC_OK;
1417 }
1418 
1419 constexpr const char* thermalModeInterface =
1420     "xyz.openbmc_project.Control.ThermalMode";
1421 constexpr const char* thermalModePath =
1422     "/xyz/openbmc_project/control/thermal_mode";
1423 
1424 bool getFanProfileInterface(
1425     sdbusplus::bus::bus& bus,
1426     boost::container::flat_map<
1427         std::string, std::variant<std::vector<std::string>, std::string>>& resp)
1428 {
1429     auto call = bus.new_method_call(settingsBusName, thermalModePath, PROP_INTF,
1430                                     "GetAll");
1431     call.append(thermalModeInterface);
1432     try
1433     {
1434         auto data = bus.call(call);
1435         data.read(resp);
1436     }
1437     catch (sdbusplus::exception_t& e)
1438     {
1439         phosphor::logging::log<phosphor::logging::level::ERR>(
1440             "getFanProfileInterface: can't get thermal mode!",
1441             phosphor::logging::entry("ERR=%s", e.what()));
1442         return false;
1443     }
1444     return true;
1445 }
1446 
1447 /**@brief implements the OEM set fan config.
1448  * @param selectedFanProfile - fan profile to enable
1449  * @param reserved1
1450  * @param performanceMode - Performance/Acoustic mode
1451  * @param reserved2
1452  * @param setPerformanceMode - set Performance/Acoustic mode
1453  * @param setFanProfile - set fan profile
1454  *
1455  * @return IPMI completion code.
1456  **/
1457 ipmi::RspType<> ipmiOEMSetFanConfig(uint8_t selectedFanProfile,
1458 
1459                                     uint2_t reserved1, bool performanceMode,
1460                                     uint3_t reserved2, bool setPerformanceMode,
1461                                     bool setFanProfile)
1462 {
1463     if (reserved1 || reserved2)
1464     {
1465         return ipmi::responseInvalidFieldRequest();
1466     }
1467     // todo: tell bios to only send first 2 bytes
1468     boost::container::flat_map<
1469         std::string, std::variant<std::vector<std::string>, std::string>>
1470         profileData;
1471     std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
1472     if (!getFanProfileInterface(*dbus, profileData))
1473     {
1474         return ipmi::responseUnspecifiedError();
1475     }
1476 
1477     std::vector<std::string>* supported =
1478         std::get_if<std::vector<std::string>>(&profileData["Supported"]);
1479     if (supported == nullptr)
1480     {
1481         return ipmi::responseInvalidFieldRequest();
1482     }
1483     std::string mode;
1484     if (setPerformanceMode)
1485     {
1486         if (performanceMode)
1487         {
1488 
1489             if (std::find(supported->begin(), supported->end(),
1490                           "Performance") != supported->end())
1491             {
1492                 mode = "Performance";
1493             }
1494         }
1495         else
1496         {
1497             if (std::find(supported->begin(), supported->end(), "Acoustic") !=
1498                 supported->end())
1499             {
1500                 mode = "Acoustic";
1501             }
1502         }
1503         if (mode.empty())
1504         {
1505             return ipmi::responseInvalidFieldRequest();
1506         }
1507 
1508         try
1509         {
1510             setDbusProperty(*dbus, settingsBusName, thermalModePath,
1511                             thermalModeInterface, "Current", mode);
1512         }
1513         catch (sdbusplus::exception_t& e)
1514         {
1515             phosphor::logging::log<phosphor::logging::level::ERR>(
1516                 "ipmiOEMSetFanConfig: can't set thermal mode!",
1517                 phosphor::logging::entry("EXCEPTION=%s", e.what()));
1518             return ipmi::responseResponseError();
1519         }
1520     }
1521 
1522     return ipmi::responseSuccess();
1523 }
1524 
1525 ipmi::RspType<uint8_t, // profile support map
1526               uint8_t, // fan control profile enable
1527               uint8_t, // flags
1528               uint32_t // dimm presence bit map
1529               >
1530     ipmiOEMGetFanConfig(uint8_t dimmGroupId)
1531 {
1532     boost::container::flat_map<
1533         std::string, std::variant<std::vector<std::string>, std::string>>
1534         profileData;
1535 
1536     std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
1537     if (!getFanProfileInterface(*dbus, profileData))
1538     {
1539         return ipmi::responseResponseError();
1540     }
1541 
1542     std::string* current = std::get_if<std::string>(&profileData["Current"]);
1543 
1544     if (current == nullptr)
1545     {
1546         phosphor::logging::log<phosphor::logging::level::ERR>(
1547             "ipmiOEMGetFanConfig: can't get current mode!");
1548         return ipmi::responseResponseError();
1549     }
1550     bool performance = (*current == "Performance");
1551 
1552     uint8_t flags = 0;
1553     if (performance)
1554     {
1555         flags |= 1 << 2;
1556     }
1557 
1558     return ipmi::responseSuccess(0, 0, flags, 0);
1559 }
1560 constexpr const char* cfmLimitSettingPath =
1561     "/xyz/openbmc_project/control/cfm_limit";
1562 constexpr const char* cfmLimitIface = "xyz.openbmc_project.Control.CFMLimit";
1563 constexpr const size_t legacyExitAirSensorNumber = 0x2e;
1564 constexpr const size_t legacyPCHSensorNumber = 0x22;
1565 constexpr const char* exitAirPathName = "Exit_Air";
1566 constexpr const char* pchPathName = "SSB_Temp";
1567 constexpr const char* pidConfigurationIface =
1568     "xyz.openbmc_project.Configuration.Pid";
1569 
1570 static std::string getConfigPath(const std::string& name)
1571 {
1572     std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
1573     auto method =
1574         dbus->new_method_call("xyz.openbmc_project.ObjectMapper",
1575                               "/xyz/openbmc_project/object_mapper",
1576                               "xyz.openbmc_project.ObjectMapper", "GetSubTree");
1577 
1578     method.append("/", 0, std::array<const char*, 1>{pidConfigurationIface});
1579     std::string path;
1580     GetSubTreeType resp;
1581     try
1582     {
1583         auto reply = dbus->call(method);
1584         reply.read(resp);
1585     }
1586     catch (sdbusplus::exception_t&)
1587     {
1588         phosphor::logging::log<phosphor::logging::level::ERR>(
1589             "ipmiOEMGetFscParameter: mapper error");
1590     };
1591     auto config =
1592         std::find_if(resp.begin(), resp.end(), [&name](const auto& pair) {
1593             return pair.first.find(name) != std::string::npos;
1594         });
1595     if (config != resp.end())
1596     {
1597         path = std::move(config->first);
1598     }
1599     return path;
1600 }
1601 
1602 // flat map to make alphabetical
1603 static boost::container::flat_map<std::string, PropertyMap> getPidConfigs()
1604 {
1605     boost::container::flat_map<std::string, PropertyMap> ret;
1606     std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
1607     auto method =
1608         dbus->new_method_call("xyz.openbmc_project.ObjectMapper",
1609                               "/xyz/openbmc_project/object_mapper",
1610                               "xyz.openbmc_project.ObjectMapper", "GetSubTree");
1611 
1612     method.append("/", 0, std::array<const char*, 1>{pidConfigurationIface});
1613     GetSubTreeType resp;
1614 
1615     try
1616     {
1617         auto reply = dbus->call(method);
1618         reply.read(resp);
1619     }
1620     catch (sdbusplus::exception_t&)
1621     {
1622         phosphor::logging::log<phosphor::logging::level::ERR>(
1623             "getFanConfigPaths: mapper error");
1624     };
1625     for (const auto& [path, objects] : resp)
1626     {
1627         if (objects.empty())
1628         {
1629             continue; // should be impossible
1630         }
1631 
1632         try
1633         {
1634             ret.emplace(path,
1635                         getAllDbusProperties(*dbus, objects[0].first, path,
1636                                              pidConfigurationIface));
1637         }
1638         catch (sdbusplus::exception_t& e)
1639         {
1640             phosphor::logging::log<phosphor::logging::level::ERR>(
1641                 "getPidConfigs: can't get DbusProperties!",
1642                 phosphor::logging::entry("ERR=%s", e.what()));
1643         }
1644     }
1645     return ret;
1646 }
1647 
1648 ipmi::RspType<uint8_t> ipmiOEMGetFanSpeedOffset(void)
1649 {
1650     boost::container::flat_map<std::string, PropertyMap> data = getPidConfigs();
1651     if (data.empty())
1652     {
1653         return ipmi::responseResponseError();
1654     }
1655     uint8_t minOffset = std::numeric_limits<uint8_t>::max();
1656     for (const auto& [_, pid] : data)
1657     {
1658         auto findClass = pid.find("Class");
1659         if (findClass == pid.end())
1660         {
1661             phosphor::logging::log<phosphor::logging::level::ERR>(
1662                 "ipmiOEMGetFscParameter: found illegal pid "
1663                 "configurations");
1664             return ipmi::responseResponseError();
1665         }
1666         std::string type = std::get<std::string>(findClass->second);
1667         if (type == "fan")
1668         {
1669             auto findOutLimit = pid.find("OutLimitMin");
1670             if (findOutLimit == pid.end())
1671             {
1672                 phosphor::logging::log<phosphor::logging::level::ERR>(
1673                     "ipmiOEMGetFscParameter: found illegal pid "
1674                     "configurations");
1675                 return ipmi::responseResponseError();
1676             }
1677             // get the min out of all the offsets
1678             minOffset = std::min(
1679                 minOffset,
1680                 static_cast<uint8_t>(std::get<double>(findOutLimit->second)));
1681         }
1682     }
1683     if (minOffset == std::numeric_limits<uint8_t>::max())
1684     {
1685         phosphor::logging::log<phosphor::logging::level::ERR>(
1686             "ipmiOEMGetFscParameter: found no fan configurations!");
1687         return ipmi::responseResponseError();
1688     }
1689 
1690     return ipmi::responseSuccess(minOffset);
1691 }
1692 
1693 ipmi::RspType<> ipmiOEMSetFanSpeedOffset(uint8_t offset)
1694 {
1695     boost::container::flat_map<std::string, PropertyMap> data = getPidConfigs();
1696     if (data.empty())
1697     {
1698 
1699         phosphor::logging::log<phosphor::logging::level::ERR>(
1700             "ipmiOEMSetFanSpeedOffset: found no pid configurations!");
1701         return ipmi::responseResponseError();
1702     }
1703 
1704     std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
1705     bool found = false;
1706     for (const auto& [path, pid] : data)
1707     {
1708         auto findClass = pid.find("Class");
1709         if (findClass == pid.end())
1710         {
1711 
1712             phosphor::logging::log<phosphor::logging::level::ERR>(
1713                 "ipmiOEMSetFanSpeedOffset: found illegal pid "
1714                 "configurations");
1715             return ipmi::responseResponseError();
1716         }
1717         std::string type = std::get<std::string>(findClass->second);
1718         if (type == "fan")
1719         {
1720             auto findOutLimit = pid.find("OutLimitMin");
1721             if (findOutLimit == pid.end())
1722             {
1723 
1724                 phosphor::logging::log<phosphor::logging::level::ERR>(
1725                     "ipmiOEMSetFanSpeedOffset: found illegal pid "
1726                     "configurations");
1727                 return ipmi::responseResponseError();
1728             }
1729             ipmi::setDbusProperty(*dbus, "xyz.openbmc_project.EntityManager",
1730                                   path, pidConfigurationIface, "OutLimitMin",
1731                                   static_cast<double>(offset));
1732             found = true;
1733         }
1734     }
1735     if (!found)
1736     {
1737         phosphor::logging::log<phosphor::logging::level::ERR>(
1738             "ipmiOEMSetFanSpeedOffset: set no fan offsets");
1739         return ipmi::responseResponseError();
1740     }
1741 
1742     return ipmi::responseSuccess();
1743 }
1744 
1745 ipmi::RspType<> ipmiOEMSetFscParameter(uint8_t command, uint8_t param1,
1746                                        uint8_t param2)
1747 {
1748     constexpr const size_t disableLimiting = 0x0;
1749 
1750     std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
1751     if (command == static_cast<uint8_t>(setFscParamFlags::tcontrol))
1752     {
1753         std::string pathName;
1754         if (param1 == legacyExitAirSensorNumber)
1755         {
1756             pathName = exitAirPathName;
1757         }
1758         else if (param1 == legacyPCHSensorNumber)
1759         {
1760             pathName = pchPathName;
1761         }
1762         else
1763         {
1764             return ipmi::responseParmOutOfRange();
1765         }
1766         std::string path = getConfigPath(pathName);
1767         ipmi::setDbusProperty(*dbus, "xyz.openbmc_project.EntityManager", path,
1768                               pidConfigurationIface, "SetPoint",
1769                               static_cast<double>(param2));
1770         return ipmi::responseSuccess();
1771     }
1772     else if (command == static_cast<uint8_t>(setFscParamFlags::cfm))
1773     {
1774         uint16_t cfm = param1 | (static_cast<uint16_t>(param2) << 8);
1775 
1776         // must be greater than 50 based on eps
1777         if (cfm < 50 && cfm != disableLimiting)
1778         {
1779             return ipmi::responseParmOutOfRange();
1780         }
1781 
1782         try
1783         {
1784             ipmi::setDbusProperty(*dbus, settingsBusName, cfmLimitSettingPath,
1785                                   cfmLimitIface, "Limit",
1786                                   static_cast<double>(cfm));
1787         }
1788         catch (sdbusplus::exception_t& e)
1789         {
1790             phosphor::logging::log<phosphor::logging::level::ERR>(
1791                 "ipmiOEMSetFscParameter: can't set cfm setting!",
1792                 phosphor::logging::entry("ERR=%s", e.what()));
1793             return ipmi::responseResponseError();
1794         }
1795         return ipmi::responseSuccess();
1796     }
1797     else if (command == static_cast<uint8_t>(setFscParamFlags::maxPwm))
1798     {
1799         constexpr const size_t maxDomainCount = 8;
1800         uint8_t requestedDomainMask = param1;
1801         boost::container::flat_map data = getPidConfigs();
1802         if (data.empty())
1803         {
1804 
1805             phosphor::logging::log<phosphor::logging::level::ERR>(
1806                 "ipmiOEMSetFscParameter: found no pid configurations!");
1807             return ipmi::responseResponseError();
1808         }
1809         size_t count = 0;
1810         for (const auto& [path, pid] : data)
1811         {
1812             auto findClass = pid.find("Class");
1813             if (findClass == pid.end())
1814             {
1815 
1816                 phosphor::logging::log<phosphor::logging::level::ERR>(
1817                     "ipmiOEMSetFscParameter: found illegal pid "
1818                     "configurations");
1819                 return ipmi::responseResponseError();
1820             }
1821             std::string type = std::get<std::string>(findClass->second);
1822             if (type == "fan")
1823             {
1824                 if (requestedDomainMask & (1 << count))
1825                 {
1826                     ipmi::setDbusProperty(
1827                         *dbus, "xyz.openbmc_project.EntityManager", path,
1828                         pidConfigurationIface, "OutLimitMax",
1829                         static_cast<double>(param2));
1830                 }
1831                 count++;
1832             }
1833         }
1834         return ipmi::responseSuccess();
1835     }
1836     else
1837     {
1838         // todo other command parts possibly
1839         // tcontrol is handled in peci now
1840         // fan speed offset not implemented yet
1841         // domain pwm limit not implemented
1842         return ipmi::responseParmOutOfRange();
1843     }
1844 }
1845 
1846 ipmi::RspType<
1847     std::variant<uint8_t, std::array<uint8_t, 2>, std::array<uint16_t, 2>>>
1848     ipmiOEMGetFscParameter(uint8_t command, std::optional<uint8_t> param)
1849 {
1850     constexpr uint8_t legacyDefaultSetpoint = -128;
1851 
1852     std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
1853     if (command == static_cast<uint8_t>(setFscParamFlags::tcontrol))
1854     {
1855         if (!param)
1856         {
1857             return ipmi::responseReqDataLenInvalid();
1858         }
1859 
1860         std::string pathName;
1861 
1862         if (*param == legacyExitAirSensorNumber)
1863         {
1864             pathName = exitAirPathName;
1865         }
1866         else if (*param == legacyPCHSensorNumber)
1867         {
1868             pathName = pchPathName;
1869         }
1870         else
1871         {
1872             return ipmi::responseParmOutOfRange();
1873         }
1874 
1875         uint8_t setpoint = legacyDefaultSetpoint;
1876         std::string path = getConfigPath(pathName);
1877         if (path.size())
1878         {
1879             Value val = ipmi::getDbusProperty(
1880                 *dbus, "xyz.openbmc_project.EntityManager", path,
1881                 pidConfigurationIface, "SetPoint");
1882             setpoint = std::floor(std::get<double>(val) + 0.5);
1883         }
1884 
1885         // old implementation used to return the "default" and current, we
1886         // don't make the default readily available so just make both the
1887         // same
1888 
1889         return ipmi::responseSuccess(
1890             std::array<uint8_t, 2>{setpoint, setpoint});
1891     }
1892     else if (command == static_cast<uint8_t>(setFscParamFlags::maxPwm))
1893     {
1894         constexpr const size_t maxDomainCount = 8;
1895 
1896         if (!param)
1897         {
1898             return ipmi::responseReqDataLenInvalid();
1899         }
1900         uint8_t requestedDomain = *param;
1901         if (requestedDomain >= maxDomainCount)
1902         {
1903             return ipmi::responseInvalidFieldRequest();
1904         }
1905 
1906         boost::container::flat_map data = getPidConfigs();
1907         if (data.empty())
1908         {
1909             phosphor::logging::log<phosphor::logging::level::ERR>(
1910                 "ipmiOEMGetFscParameter: found no pid configurations!");
1911             return ipmi::responseResponseError();
1912         }
1913         size_t count = 0;
1914         for (const auto& [_, pid] : data)
1915         {
1916             auto findClass = pid.find("Class");
1917             if (findClass == pid.end())
1918             {
1919                 phosphor::logging::log<phosphor::logging::level::ERR>(
1920                     "ipmiOEMGetFscParameter: found illegal pid "
1921                     "configurations");
1922                 return ipmi::responseResponseError();
1923             }
1924             std::string type = std::get<std::string>(findClass->second);
1925             if (type == "fan")
1926             {
1927                 if (requestedDomain == count)
1928                 {
1929                     auto findOutLimit = pid.find("OutLimitMax");
1930                     if (findOutLimit == pid.end())
1931                     {
1932                         phosphor::logging::log<phosphor::logging::level::ERR>(
1933                             "ipmiOEMGetFscParameter: found illegal pid "
1934                             "configurations");
1935                         return ipmi::responseResponseError();
1936                     }
1937 
1938                     return ipmi::responseSuccess(
1939                         static_cast<uint8_t>(std::floor(
1940                             std::get<double>(findOutLimit->second) + 0.5)));
1941                 }
1942                 else
1943                 {
1944                     count++;
1945                 }
1946             }
1947         }
1948 
1949         return ipmi::responseInvalidFieldRequest();
1950     }
1951     else if (command == static_cast<uint8_t>(setFscParamFlags::cfm))
1952     {
1953 
1954         /*
1955         DataLen should be 1, but host is sending us an extra bit. As the
1956         previous behavior didn't seem to prevent this, ignore the check for
1957         now.
1958 
1959         if (param)
1960         {
1961             phosphor::logging::log<phosphor::logging::level::ERR>(
1962                 "ipmiOEMGetFscParameter: invalid input len!");
1963             return IPMI_CC_REQ_DATA_LEN_INVALID;
1964         }
1965         */
1966         Value cfmLimit;
1967         Value cfmMaximum;
1968         try
1969         {
1970             cfmLimit = ipmi::getDbusProperty(*dbus, settingsBusName,
1971                                              cfmLimitSettingPath, cfmLimitIface,
1972                                              "Limit");
1973             cfmMaximum = ipmi::getDbusProperty(
1974                 *dbus, "xyz.openbmc_project.ExitAirTempSensor",
1975                 "/xyz/openbmc_project/control/MaxCFM", cfmLimitIface, "Limit");
1976         }
1977         catch (sdbusplus::exception_t& e)
1978         {
1979             phosphor::logging::log<phosphor::logging::level::ERR>(
1980                 "ipmiOEMGetFscParameter: can't get cfm setting!",
1981                 phosphor::logging::entry("ERR=%s", e.what()));
1982             return ipmi::responseResponseError();
1983         }
1984 
1985         double cfmMax = std::get<double>(cfmMaximum);
1986         double cfmLim = std::get<double>(cfmLimit);
1987 
1988         cfmLim = std::floor(cfmLim + 0.5);
1989         cfmMax = std::floor(cfmMax + 0.5);
1990         uint16_t cfmLimResp = static_cast<uint16_t>(cfmLim);
1991         uint16_t cfmMaxResp = static_cast<uint16_t>(cfmMax);
1992 
1993         return ipmi::responseSuccess(
1994             std::array<uint16_t, 2>{cfmLimResp, cfmMaxResp});
1995     }
1996 
1997     else
1998     {
1999         // todo other command parts possibly
2000         // domain pwm limit not implemented
2001         return ipmi::responseParmOutOfRange();
2002     }
2003 }
2004 
2005 using crConfigVariant =
2006     std::variant<bool, uint8_t, uint32_t, std::vector<uint8_t>, std::string>;
2007 
2008 int setCRConfig(ipmi::Context::ptr ctx, const std::string& property,
2009                 const crConfigVariant& value,
2010                 std::chrono::microseconds timeout = ipmi::IPMI_DBUS_TIMEOUT)
2011 {
2012     boost::system::error_code ec;
2013     ctx->bus->yield_method_call<void>(
2014         ctx->yield, ec, "xyz.openbmc_project.Settings",
2015         "/xyz/openbmc_project/control/power_supply_redundancy",
2016         "org.freedesktop.DBus.Properties", "Set",
2017         "xyz.openbmc_project.Control.PowerSupplyRedundancy", property, value);
2018     if (ec)
2019     {
2020         phosphor::logging::log<phosphor::logging::level::ERR>(
2021             "Failed to set dbus property to cold redundancy");
2022         return -1;
2023     }
2024 
2025     return 0;
2026 }
2027 
2028 int getCRConfig(ipmi::Context::ptr ctx, const std::string& property,
2029                 crConfigVariant& value,
2030                 std::chrono::microseconds timeout = ipmi::IPMI_DBUS_TIMEOUT)
2031 {
2032     boost::system::error_code ec;
2033     value = ctx->bus->yield_method_call<crConfigVariant>(
2034         ctx->yield, ec, "xyz.openbmc_project.Settings",
2035         "/xyz/openbmc_project/control/power_supply_redundancy",
2036         "org.freedesktop.DBus.Properties", "Get",
2037         "xyz.openbmc_project.Control.PowerSupplyRedundancy", property);
2038     if (ec)
2039     {
2040         phosphor::logging::log<phosphor::logging::level::ERR>(
2041             "Failed to get dbus property to cold redundancy");
2042         return -1;
2043     }
2044     return 0;
2045 }
2046 
2047 uint8_t getPSUCount(void)
2048 {
2049     std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
2050     ipmi::Value num;
2051     try
2052     {
2053         num = ipmi::getDbusProperty(
2054             *dbus, "xyz.openbmc_project.PSURedundancy",
2055             "/xyz/openbmc_project/control/power_supply_redundancy",
2056             "xyz.openbmc_project.Control.PowerSupplyRedundancy", "PSUNumber");
2057     }
2058     catch (sdbusplus::exception_t& e)
2059     {
2060         phosphor::logging::log<phosphor::logging::level::ERR>(
2061             "Failed to get PSUNumber property from dbus interface");
2062         return 0;
2063     }
2064     uint8_t* pNum = std::get_if<uint8_t>(&num);
2065     if (!pNum)
2066     {
2067         phosphor::logging::log<phosphor::logging::level::ERR>(
2068             "Error to get PSU Number");
2069         return 0;
2070     }
2071     return *pNum;
2072 }
2073 
2074 bool validateCRAlgo(std::vector<uint8_t>& conf, uint8_t num)
2075 {
2076     if (conf.size() < num)
2077     {
2078         phosphor::logging::log<phosphor::logging::level::ERR>(
2079             "Invalid PSU Ranking");
2080         return false;
2081     }
2082     std::set<uint8_t> confSet;
2083     for (uint8_t i = 0; i < num; i++)
2084     {
2085         if (conf[i] > num)
2086         {
2087             phosphor::logging::log<phosphor::logging::level::ERR>(
2088                 "PSU Ranking is larger than current PSU number");
2089             return false;
2090         }
2091         confSet.emplace(conf[i]);
2092     }
2093 
2094     if (confSet.size() != num)
2095     {
2096         phosphor::logging::log<phosphor::logging::level::ERR>(
2097             "duplicate PSU Ranking");
2098         return false;
2099     }
2100     return true;
2101 }
2102 
2103 enum class crParameter
2104 {
2105     crStatus = 0,
2106     crFeature = 1,
2107     rotationFeature = 2,
2108     rotationAlgo = 3,
2109     rotationPeriod = 4,
2110     numOfPSU = 5
2111 };
2112 
2113 constexpr ipmi::Cc ccParameterNotSupported = 0x80;
2114 static const constexpr uint32_t oneDay = 0x15180;
2115 static const constexpr uint32_t oneMonth = 0xf53700;
2116 static const constexpr uint8_t userSpecific = 0x01;
2117 static const constexpr uint8_t crSetCompleted = 0;
2118 ipmi::RspType<uint8_t> ipmiOEMSetCRConfig(ipmi::Context::ptr ctx,
2119                                           uint8_t parameter,
2120                                           ipmi::message::Payload& payload)
2121 {
2122     switch (static_cast<crParameter>(parameter))
2123     {
2124         case crParameter::crFeature:
2125         {
2126             uint8_t param1;
2127             if (payload.unpack(param1) || !payload.fullyUnpacked())
2128             {
2129                 return ipmi::responseReqDataLenInvalid();
2130             }
2131             // ColdRedundancy Enable can only be true or flase
2132             if (param1 > 1)
2133             {
2134                 return ipmi::responseInvalidFieldRequest();
2135             }
2136             if (setCRConfig(ctx, "ColdRedundancyEnabled",
2137                             static_cast<bool>(param1)))
2138             {
2139                 return ipmi::responseResponseError();
2140             }
2141             break;
2142         }
2143         case crParameter::rotationFeature:
2144         {
2145             uint8_t param1;
2146             if (payload.unpack(param1) || !payload.fullyUnpacked())
2147             {
2148                 return ipmi::responseReqDataLenInvalid();
2149             }
2150             // Rotation Enable can only be true or false
2151             if (param1 > 1)
2152             {
2153                 return ipmi::responseInvalidFieldRequest();
2154             }
2155             if (setCRConfig(ctx, "RotationEnabled", static_cast<bool>(param1)))
2156             {
2157                 return ipmi::responseResponseError();
2158             }
2159             break;
2160         }
2161         case crParameter::rotationAlgo:
2162         {
2163             // Rotation Algorithm can only be 0-BMC Specific or 1-User Specific
2164             std::string algoName;
2165             uint8_t param1;
2166             if (payload.unpack(param1))
2167             {
2168                 return ipmi::responseReqDataLenInvalid();
2169             }
2170             switch (param1)
2171             {
2172                 case 0:
2173                     algoName = "xyz.openbmc_project.Control."
2174                                "PowerSupplyRedundancy.Algo.bmcSpecific";
2175                     break;
2176                 case 1:
2177                     algoName = "xyz.openbmc_project.Control."
2178                                "PowerSupplyRedundancy.Algo.userSpecific";
2179                     break;
2180                 default:
2181                     return ipmi::responseInvalidFieldRequest();
2182             }
2183             if (setCRConfig(ctx, "RotationAlgorithm", algoName))
2184             {
2185                 return ipmi::responseResponseError();
2186             }
2187 
2188             uint8_t numberOfPSU = getPSUCount();
2189             if (!numberOfPSU)
2190             {
2191                 return ipmi::responseResponseError();
2192             }
2193             std::vector<uint8_t> rankOrder;
2194 
2195             if (param1 == userSpecific)
2196             {
2197                 if (payload.unpack(rankOrder) || !payload.fullyUnpacked())
2198                 {
2199                     ipmi::responseReqDataLenInvalid();
2200                 }
2201                 if (rankOrder.size() != numberOfPSU)
2202                 {
2203                     return ipmi::responseReqDataLenInvalid();
2204                 }
2205 
2206                 if (!validateCRAlgo(rankOrder, numberOfPSU))
2207                 {
2208                     return ipmi::responseInvalidFieldRequest();
2209                 }
2210             }
2211             else
2212             {
2213                 if (rankOrder.size() > 0)
2214                 {
2215                     return ipmi::responseReqDataLenInvalid();
2216                 }
2217                 for (uint8_t i = 1; i <= numberOfPSU; i++)
2218                 {
2219                     rankOrder.emplace_back(i);
2220                 }
2221             }
2222             if (setCRConfig(ctx, "RotationRankOrder", rankOrder))
2223             {
2224                 return ipmi::responseResponseError();
2225             }
2226             break;
2227         }
2228         case crParameter::rotationPeriod:
2229         {
2230             // Minimum Rotation period is  One day (86400 seconds) and Max
2231             // Rotation Period is 6 month (0xf53700 seconds)
2232             uint32_t period;
2233             if (payload.unpack(period) || !payload.fullyUnpacked())
2234             {
2235                 return ipmi::responseReqDataLenInvalid();
2236             }
2237             if ((period < oneDay) || (period > oneMonth))
2238             {
2239                 return ipmi::responseInvalidFieldRequest();
2240             }
2241             if (setCRConfig(ctx, "PeriodOfRotation", period))
2242             {
2243                 return ipmi::responseResponseError();
2244             }
2245             break;
2246         }
2247         default:
2248         {
2249             return ipmi::response(ccParameterNotSupported);
2250         }
2251     }
2252 
2253     // TODO Halfwidth needs to set SetInProgress
2254     if (setCRConfig(ctx, "ColdRedundancyStatus",
2255                     std::string("xyz.openbmc_project.Control."
2256                                 "PowerSupplyRedundancy.Status.completed")))
2257     {
2258         return ipmi::responseResponseError();
2259     }
2260     return ipmi::responseSuccess(crSetCompleted);
2261 }
2262 
2263 ipmi::RspType<uint8_t, std::variant<uint8_t, uint32_t, std::vector<uint8_t>>>
2264     ipmiOEMGetCRConfig(ipmi::Context::ptr ctx, uint8_t parameter)
2265 {
2266     crConfigVariant value;
2267     switch (static_cast<crParameter>(parameter))
2268     {
2269         case crParameter::crStatus:
2270         {
2271             if (getCRConfig(ctx, "ColdRedundancyStatus", value))
2272             {
2273                 return ipmi::responseResponseError();
2274             }
2275             std::string* pStatus = std::get_if<std::string>(&value);
2276             if (!pStatus)
2277             {
2278                 phosphor::logging::log<phosphor::logging::level::ERR>(
2279                     "Error to get ColdRedundancyStatus property");
2280                 return ipmi::responseResponseError();
2281             }
2282             namespace server = sdbusplus::xyz::openbmc_project::Control::server;
2283             auto status =
2284                 server::PowerSupplyRedundancy::convertStatusFromString(
2285                     *pStatus);
2286             switch (status)
2287             {
2288                 case server::PowerSupplyRedundancy::Status::inProgress:
2289                     return ipmi::responseSuccess(parameter,
2290                                                  static_cast<uint8_t>(0));
2291 
2292                 case server::PowerSupplyRedundancy::Status::completed:
2293                     return ipmi::responseSuccess(parameter,
2294                                                  static_cast<uint8_t>(1));
2295                 default:
2296                     phosphor::logging::log<phosphor::logging::level::ERR>(
2297                         "Error to get valid status");
2298                     return ipmi::responseResponseError();
2299             }
2300         }
2301         case crParameter::crFeature:
2302         {
2303             if (getCRConfig(ctx, "ColdRedundancyEnabled", value))
2304             {
2305                 return ipmi::responseResponseError();
2306             }
2307             bool* pResponse = std::get_if<bool>(&value);
2308             if (!pResponse)
2309             {
2310                 phosphor::logging::log<phosphor::logging::level::ERR>(
2311                     "Error to get ColdRedundancyEnable property");
2312                 return ipmi::responseResponseError();
2313             }
2314 
2315             return ipmi::responseSuccess(parameter,
2316                                          static_cast<uint8_t>(*pResponse));
2317         }
2318         case crParameter::rotationFeature:
2319         {
2320             if (getCRConfig(ctx, "RotationEnabled", value))
2321             {
2322                 return ipmi::responseResponseError();
2323             }
2324             bool* pResponse = std::get_if<bool>(&value);
2325             if (!pResponse)
2326             {
2327                 phosphor::logging::log<phosphor::logging::level::ERR>(
2328                     "Error to get RotationEnabled property");
2329                 return ipmi::responseResponseError();
2330             }
2331             return ipmi::responseSuccess(parameter,
2332                                          static_cast<uint8_t>(*pResponse));
2333         }
2334         case crParameter::rotationAlgo:
2335         {
2336             if (getCRConfig(ctx, "RotationAlgorithm", value))
2337             {
2338                 return ipmi::responseResponseError();
2339             }
2340 
2341             std::string* pAlgo = std::get_if<std::string>(&value);
2342             if (!pAlgo)
2343             {
2344                 phosphor::logging::log<phosphor::logging::level::ERR>(
2345                     "Error to get RotationAlgorithm property");
2346                 return ipmi::responseResponseError();
2347             }
2348             std::vector<uint8_t> response;
2349             namespace server = sdbusplus::xyz::openbmc_project::Control::server;
2350             auto algo =
2351                 server::PowerSupplyRedundancy::convertAlgoFromString(*pAlgo);
2352 
2353             switch (algo)
2354             {
2355                 case server::PowerSupplyRedundancy::Algo::bmcSpecific:
2356                     response.push_back(0);
2357                     break;
2358                 case server::PowerSupplyRedundancy::Algo::userSpecific:
2359                     response.push_back(1);
2360                     break;
2361                 default:
2362                     phosphor::logging::log<phosphor::logging::level::ERR>(
2363                         "Error to get valid algo");
2364                     return ipmi::responseResponseError();
2365             }
2366 
2367             if (getCRConfig(ctx, "RotationRankOrder", value))
2368             {
2369                 return ipmi::responseResponseError();
2370             }
2371             std::vector<uint8_t>* pResponse =
2372                 std::get_if<std::vector<uint8_t>>(&value);
2373             if (!pResponse)
2374             {
2375                 phosphor::logging::log<phosphor::logging::level::ERR>(
2376                     "Error to get RotationRankOrder property");
2377                 return ipmi::responseResponseError();
2378             }
2379 
2380             std::copy(pResponse->begin(), pResponse->end(),
2381                       std::back_inserter(response));
2382 
2383             return ipmi::responseSuccess(parameter, response);
2384         }
2385         case crParameter::rotationPeriod:
2386         {
2387             if (getCRConfig(ctx, "PeriodOfRotation", value))
2388             {
2389                 return ipmi::responseResponseError();
2390             }
2391             uint32_t* pResponse = std::get_if<uint32_t>(&value);
2392             if (!pResponse)
2393             {
2394                 phosphor::logging::log<phosphor::logging::level::ERR>(
2395                     "Error to get RotationAlgorithm property");
2396                 return ipmi::responseResponseError();
2397             }
2398             return ipmi::responseSuccess(parameter, *pResponse);
2399         }
2400         case crParameter::numOfPSU:
2401         {
2402             uint8_t numberOfPSU = getPSUCount();
2403             if (!numberOfPSU)
2404             {
2405                 return ipmi::responseResponseError();
2406             }
2407             return ipmi::responseSuccess(parameter, numberOfPSU);
2408         }
2409         default:
2410         {
2411             return ipmi::response(ccParameterNotSupported);
2412         }
2413     }
2414 }
2415 
2416 ipmi::RspType<> ipmiOEMSetFaultIndication(uint8_t sourceId, uint8_t faultType,
2417                                           uint8_t faultState,
2418                                           uint8_t faultGroup,
2419                                           std::array<uint8_t, 8>& ledStateData)
2420 {
2421     constexpr auto maxFaultType = static_cast<size_t>(RemoteFaultType::max);
2422     static const std::array<std::string, maxFaultType> faultNames = {
2423         "faultFan",       "faultTemp",     "faultPower",
2424         "faultDriveSlot", "faultSoftware", "faultMemory"};
2425 
2426     constexpr uint8_t maxFaultSource = 0x4;
2427     constexpr uint8_t skipLEDs = 0xFF;
2428     constexpr uint8_t pinSize = 64;
2429     constexpr uint8_t groupSize = 16;
2430     constexpr uint8_t groupNum = 5; // 4 for fault memory, 1 for faultFan
2431 
2432     // same pin names need to be defined in dts file
2433     static const std::array<std::array<std::string, groupSize>, groupNum>
2434         faultLedPinNames = {{
2435             "LED_CPU1_CH1_DIMM1_FAULT",
2436             "LED_CPU1_CH1_DIMM2_FAULT",
2437             "LED_CPU1_CH2_DIMM1_FAULT",
2438             "LED_CPU1_CH2_DIMM2_FAULT",
2439             "LED_CPU1_CH3_DIMM1_FAULT",
2440             "LED_CPU1_CH3_DIMM2_FAULT",
2441             "LED_CPU1_CH4_DIMM1_FAULT",
2442             "LED_CPU1_CH4_DIMM2_FAULT",
2443             "LED_CPU1_CH5_DIMM1_FAULT",
2444             "LED_CPU1_CH5_DIMM2_FAULT",
2445             "LED_CPU1_CH6_DIMM1_FAULT",
2446             "LED_CPU1_CH6_DIMM2_FAULT",
2447             "",
2448             "",
2449             "",
2450             "", // end of group1
2451             "LED_CPU2_CH1_DIMM1_FAULT",
2452             "LED_CPU2_CH1_DIMM2_FAULT",
2453             "LED_CPU2_CH2_DIMM1_FAULT",
2454             "LED_CPU2_CH2_DIMM2_FAULT",
2455             "LED_CPU2_CH3_DIMM1_FAULT",
2456             "LED_CPU2_CH3_DIMM2_FAULT",
2457             "LED_CPU2_CH4_DIMM1_FAULT",
2458             "LED_CPU2_CH4_DIMM2_FAULT",
2459             "LED_CPU2_CH5_DIMM1_FAULT",
2460             "LED_CPU2_CH5_DIMM2_FAULT",
2461             "LED_CPU2_CH6_DIMM1_FAULT",
2462             "LED_CPU2_CH6_DIMM2_FAULT",
2463             "",
2464             "",
2465             "",
2466             "", // endof group2
2467             "LED_CPU3_CH1_DIMM1_FAULT",
2468             "LED_CPU3_CH1_DIMM2_FAULT",
2469             "LED_CPU3_CH2_DIMM1_FAULT",
2470             "LED_CPU3_CH2_DIMM2_FAULT",
2471             "LED_CPU3_CH3_DIMM1_FAULT",
2472             "LED_CPU3_CH3_DIMM2_FAULT",
2473             "LED_CPU3_CH4_DIMM1_FAULT",
2474             "LED_CPU3_CH4_DIMM2_FAULT",
2475             "LED_CPU3_CH5_DIMM1_FAULT",
2476             "LED_CPU3_CH5_DIMM2_FAULT",
2477             "LED_CPU3_CH6_DIMM1_FAULT",
2478             "LED_CPU3_CH6_DIMM2_FAULT",
2479             "",
2480             "",
2481             "",
2482             "", // end of group3
2483             "LED_CPU4_CH1_DIMM1_FAULT",
2484             "LED_CPU4_CH1_DIMM2_FAULT",
2485             "LED_CPU4_CH2_DIMM1_FAULT",
2486             "LED_CPU4_CH2_DIMM2_FAULT",
2487             "LED_CPU4_CH3_DIMM1_FAULT",
2488             "LED_CPU4_CH3_DIMM2_FAULT",
2489             "LED_CPU4_CH4_DIMM1_FAULT",
2490             "LED_CPU4_CH4_DIMM2_FAULT",
2491             "LED_CPU4_CH5_DIMM1_FAULT",
2492             "LED_CPU4_CH5_DIMM2_FAULT",
2493             "LED_CPU4_CH6_DIMM1_FAULT",
2494             "LED_CPU4_CH6_DIMM2_FAULT",
2495             "",
2496             "",
2497             "",
2498             "", // end of group4
2499             "LED_FAN1_FAULT",
2500             "LED_FAN2_FAULT",
2501             "LED_FAN3_FAULT",
2502             "LED_FAN4_FAULT",
2503             "LED_FAN5_FAULT",
2504             "LED_FAN6_FAULT",
2505             "LED_FAN7_FAULT",
2506             "LED_FAN8_FAULT",
2507             "",
2508             "",
2509             "",
2510             "",
2511             "",
2512             "",
2513             "",
2514             "" // end of group5
2515         }};
2516 
2517     // Validate the source, fault type --
2518     // (Byte 1) sourceId: Unspecified, Hot-Swap Controller 0, Hot-Swap
2519     // Controller 1, BIOS (Byte 2) fault type: fan, temperature, power,
2520     // driveslot, software, memory (Byte 3) FaultState: OK, Degraded,
2521     // Non-Critical, Critical, Non-Recoverable, (Byte 4) is faultGroup,
2522     // definition differs based on fault type (Byte 2)
2523     //          Type Fan=> Group: 0=FanGroupID, FF-not used
2524     //                  Byte 5-11 00h, not used
2525     //                  Byte12 FanLedState [7:0]-Fans 7:0
2526     //          Type Memory=> Group: 0 = DIMM GroupID, FF-not used
2527     //                  Byte 5:12 - DIMM LED state (64bit field, LS Byte first)
2528     //                  [63:48] = CPU4 channels 7:0, 2 bits per channel
2529     //                  [47:32] = CPU3 channels 7:0, 2 bits per channel
2530     //                  [31:16] = CPU2 channels 7:0, 2 bits per channel
2531     //                  [15:0] =  CPU1 channels 7:0, 2 bits per channel
2532     //          Type Other=> Component Fault LED Group ID, not used set to 0xFF
2533     //                  Byte[5:12]: reserved 0x00h
2534     if ((sourceId >= maxFaultSource) ||
2535         (faultType >= static_cast<int8_t>(RemoteFaultType::max)) ||
2536         (faultState >= static_cast<int8_t>(RemoteFaultState::maxFaultState)) ||
2537         (faultGroup >= static_cast<int8_t>(DimmFaultType::maxFaultGroup)))
2538     {
2539         return ipmi::responseParmOutOfRange();
2540     }
2541 
2542     size_t pinGroupOffset = 0;
2543     size_t pinGroupMax = pinSize / groupSize;
2544     if (RemoteFaultType::fan == RemoteFaultType(faultType))
2545     {
2546         pinGroupOffset = 4;
2547         pinGroupMax = groupNum - pinSize / groupSize;
2548     }
2549 
2550     switch (RemoteFaultType(faultType))
2551     {
2552         case (RemoteFaultType::fan):
2553         case (RemoteFaultType::memory):
2554         {
2555             if (faultGroup == skipLEDs)
2556             {
2557                 return ipmi::responseSuccess();
2558             }
2559             // calculate led state bit filed count, each byte has 8bits
2560             // the maximum bits will be 8 * 8 bits
2561             constexpr uint8_t size = sizeof(ledStateData) * 8;
2562 
2563             // assemble ledState
2564             uint64_t ledState = 0;
2565             bool hasError = false;
2566             for (int i = 0; i < sizeof(ledStateData); i++)
2567             {
2568                 ledState = (uint64_t)(ledState << 8);
2569                 ledState = (uint64_t)(ledState | (uint64_t)ledStateData[i]);
2570             }
2571             std::bitset<size> ledStateBits(ledState);
2572 
2573             for (int group = 0; group < pinGroupMax; group++)
2574             {
2575                 for (int i = 0; i < groupSize; i++)
2576                 { // skip non-existing pins
2577                     if (0 == faultLedPinNames[group + pinGroupOffset][i].size())
2578                     {
2579                         continue;
2580                     }
2581 
2582                     gpiod::line line = gpiod::find_line(
2583                         faultLedPinNames[group + pinGroupOffset][i]);
2584                     if (!line)
2585                     {
2586                         phosphor::logging::log<phosphor::logging::level::ERR>(
2587                             "Not Find Led Gpio Device!",
2588                             phosphor::logging::entry(
2589                                 "DEVICE=%s",
2590                                 faultLedPinNames[group + pinGroupOffset][i]
2591                                     .c_str()));
2592                         hasError = true;
2593                         continue;
2594                     }
2595 
2596                     bool activeHigh =
2597                         (line.active_state() == gpiod::line::ACTIVE_HIGH);
2598                     try
2599                     {
2600                         line.request(
2601                             {"faultLed", gpiod::line_request::DIRECTION_OUTPUT,
2602                              activeHigh
2603                                  ? 0
2604                                  : gpiod::line_request::FLAG_ACTIVE_LOW});
2605                         line.set_value(ledStateBits[i + group * groupSize]);
2606                     }
2607                     catch (std::system_error&)
2608                     {
2609                         phosphor::logging::log<phosphor::logging::level::ERR>(
2610                             "Error write Led Gpio Device!",
2611                             phosphor::logging::entry(
2612                                 "DEVICE=%s",
2613                                 faultLedPinNames[group + pinGroupOffset][i]
2614                                     .c_str()));
2615                         hasError = true;
2616                         continue;
2617                     }
2618                 } // for int i
2619             }
2620             if (hasError)
2621             {
2622                 return ipmi::responseResponseError();
2623             }
2624             break;
2625         }
2626         default:
2627         {
2628             // now only support two fault types
2629             return ipmi::responseParmOutOfRange();
2630         }
2631     } // switch
2632     return ipmi::responseSuccess();
2633 }
2634 
2635 ipmi::RspType<uint8_t> ipmiOEMReadBoardProductId()
2636 {
2637     uint8_t prodId = 0;
2638     try
2639     {
2640         std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
2641         const DbusObjectInfo& object = getDbusObject(
2642             *dbus, "xyz.openbmc_project.Inventory.Item.Board",
2643             "/xyz/openbmc_project/inventory/system/board/", "Baseboard");
2644         const Value& propValue = getDbusProperty(
2645             *dbus, object.second, object.first,
2646             "xyz.openbmc_project.Inventory.Item.Board", "ProductId");
2647         prodId = static_cast<uint8_t>(std::get<uint64_t>(propValue));
2648     }
2649     catch (std::exception& e)
2650     {
2651         phosphor::logging::log<phosphor::logging::level::ERR>(
2652             "ipmiOEMReadBoardProductId: Product ID read failed!",
2653             phosphor::logging::entry("ERR=%s", e.what()));
2654     }
2655     return ipmi::responseSuccess(prodId);
2656 }
2657 
2658 /** @brief implements the get security mode command
2659  *  @param ctx - ctx pointer
2660  *
2661  *  @returns IPMI completion code with following data
2662  *   - restriction mode value - As specified in
2663  * xyz.openbmc_project.Control.Security.RestrictionMode.interface.yaml
2664  *   - special mode value - As specified in
2665  * xyz.openbmc_project.Control.Security.SpecialMode.interface.yaml
2666  */
2667 ipmi::RspType<uint8_t, uint8_t> ipmiGetSecurityMode(ipmi::Context::ptr ctx)
2668 {
2669     namespace securityNameSpace =
2670         sdbusplus::xyz::openbmc_project::Control::Security::server;
2671     uint8_t restrictionModeValue = 0;
2672     uint8_t specialModeValue = 0;
2673 
2674     boost::system::error_code ec;
2675     auto varRestrMode = ctx->bus->yield_method_call<std::variant<std::string>>(
2676         ctx->yield, ec, restricionModeService, restricionModeBasePath,
2677         dBusPropertyIntf, dBusPropertyGetMethod, restricionModeIntf,
2678         restricionModeProperty);
2679     if (ec)
2680     {
2681         phosphor::logging::log<phosphor::logging::level::ERR>(
2682             "ipmiGetSecurityMode: failed to get RestrictionMode property",
2683             phosphor::logging::entry("ERROR=%s", ec.message().c_str()));
2684         return ipmi::responseUnspecifiedError();
2685     }
2686     restrictionModeValue = static_cast<uint8_t>(
2687         securityNameSpace::RestrictionMode::convertModesFromString(
2688             std::get<std::string>(varRestrMode)));
2689     auto varSpecialMode =
2690         ctx->bus->yield_method_call<std::variant<std::string>>(
2691             ctx->yield, ec, specialModeService, specialModeBasePath,
2692             dBusPropertyIntf, dBusPropertyGetMethod, specialModeIntf,
2693             specialModeProperty);
2694     if (ec)
2695     {
2696         phosphor::logging::log<phosphor::logging::level::ERR>(
2697             "ipmiGetSecurityMode: failed to get SpecialMode property",
2698             phosphor::logging::entry("ERROR=%s", ec.message().c_str()));
2699         // fall through, let us not worry about SpecialMode property, which is
2700         // not required in user scenario
2701     }
2702     else
2703     {
2704         specialModeValue = static_cast<uint8_t>(
2705             securityNameSpace::SpecialMode::convertModesFromString(
2706                 std::get<std::string>(varSpecialMode)));
2707     }
2708     return ipmi::responseSuccess(restrictionModeValue, specialModeValue);
2709 }
2710 
2711 /** @brief implements the set security mode command
2712  *  Command allows to upgrade the restriction mode and won't allow
2713  *  to downgrade from system interface
2714  *  @param ctx - ctx pointer
2715  *  @param restrictionMode - restriction mode value to be set.
2716  *
2717  *  @returns IPMI completion code
2718  */
2719 ipmi::RspType<> ipmiSetSecurityMode(ipmi::Context::ptr ctx,
2720                                     uint8_t restrictionMode,
2721                                     std::optional<uint8_t> specialMode)
2722 {
2723 #ifndef BMC_VALIDATION_UNSECURE_FEATURE
2724     if (specialMode)
2725     {
2726         return ipmi::responseReqDataLenInvalid();
2727     }
2728 #endif
2729     namespace securityNameSpace =
2730         sdbusplus::xyz::openbmc_project::Control::Security::server;
2731 
2732     ChannelInfo chInfo;
2733     if (getChannelInfo(ctx->channel, chInfo) != ccSuccess)
2734     {
2735         phosphor::logging::log<phosphor::logging::level::ERR>(
2736             "ipmiSetSecurityMode: Failed to get Channel Info",
2737             phosphor::logging::entry("CHANNEL=%d", ctx->channel));
2738         return ipmi::responseUnspecifiedError();
2739     }
2740     auto reqMode =
2741         static_cast<securityNameSpace::RestrictionMode::Modes>(restrictionMode);
2742 
2743     if ((reqMode < securityNameSpace::RestrictionMode::Modes::Provisioning) ||
2744         (reqMode >
2745          securityNameSpace::RestrictionMode::Modes::ProvisionedHostDisabled))
2746     {
2747         return ipmi::responseInvalidFieldRequest();
2748     }
2749 
2750     boost::system::error_code ec;
2751     auto varRestrMode = ctx->bus->yield_method_call<std::variant<std::string>>(
2752         ctx->yield, ec, restricionModeService, restricionModeBasePath,
2753         dBusPropertyIntf, dBusPropertyGetMethod, restricionModeIntf,
2754         restricionModeProperty);
2755     if (ec)
2756     {
2757         phosphor::logging::log<phosphor::logging::level::ERR>(
2758             "ipmiSetSecurityMode: failed to get RestrictionMode property",
2759             phosphor::logging::entry("ERROR=%s", ec.message().c_str()));
2760         return ipmi::responseUnspecifiedError();
2761     }
2762     auto currentRestrictionMode =
2763         securityNameSpace::RestrictionMode::convertModesFromString(
2764             std::get<std::string>(varRestrMode));
2765 
2766     if (chInfo.mediumType !=
2767             static_cast<uint8_t>(EChannelMediumType::lan8032) &&
2768         currentRestrictionMode > reqMode)
2769     {
2770         phosphor::logging::log<phosphor::logging::level::ERR>(
2771             "ipmiSetSecurityMode - Downgrading security mode not supported "
2772             "through system interface",
2773             phosphor::logging::entry(
2774                 "CUR_MODE=%d", static_cast<uint8_t>(currentRestrictionMode)),
2775             phosphor::logging::entry("REQ_MODE=%d", restrictionMode));
2776         return ipmi::responseCommandNotAvailable();
2777     }
2778 
2779     ec.clear();
2780     ctx->bus->yield_method_call<>(
2781         ctx->yield, ec, restricionModeService, restricionModeBasePath,
2782         dBusPropertyIntf, dBusPropertySetMethod, restricionModeIntf,
2783         restricionModeProperty,
2784         static_cast<std::variant<std::string>>(
2785             securityNameSpace::convertForMessage(reqMode)));
2786 
2787     if (ec)
2788     {
2789         phosphor::logging::log<phosphor::logging::level::ERR>(
2790             "ipmiSetSecurityMode: failed to set RestrictionMode property",
2791             phosphor::logging::entry("ERROR=%s", ec.message().c_str()));
2792         return ipmi::responseUnspecifiedError();
2793     }
2794 
2795 #ifdef BMC_VALIDATION_UNSECURE_FEATURE
2796     if (specialMode)
2797     {
2798         ec.clear();
2799         ctx->bus->yield_method_call<>(
2800             ctx->yield, ec, specialModeService, specialModeBasePath,
2801             dBusPropertyIntf, dBusPropertySetMethod, specialModeIntf,
2802             specialModeProperty,
2803             static_cast<std::variant<std::string>>(
2804                 securityNameSpace::convertForMessage(
2805                     static_cast<securityNameSpace::SpecialMode::Modes>(
2806                         specialMode.value()))));
2807 
2808         if (ec)
2809         {
2810             phosphor::logging::log<phosphor::logging::level::ERR>(
2811                 "ipmiSetSecurityMode: failed to set SpecialMode property",
2812                 phosphor::logging::entry("ERROR=%s", ec.message().c_str()));
2813             return ipmi::responseUnspecifiedError();
2814         }
2815     }
2816 #endif
2817     return ipmi::responseSuccess();
2818 }
2819 
2820 ipmi::RspType<uint8_t /* restore status */>
2821     ipmiRestoreConfiguration(const std::array<uint8_t, 3>& clr, uint8_t cmd)
2822 {
2823     static constexpr std::array<uint8_t, 3> expClr = {'C', 'L', 'R'};
2824 
2825     if (clr != expClr)
2826     {
2827         return ipmi::responseInvalidFieldRequest();
2828     }
2829     constexpr uint8_t cmdStatus = 0;
2830     constexpr uint8_t cmdDefaultRestore = 0xaa;
2831     constexpr uint8_t cmdFullRestore = 0xbb;
2832     constexpr uint8_t cmdFormat = 0xcc;
2833 
2834     constexpr const char* restoreOpFname = "/tmp/.rwfs/.restore_op";
2835 
2836     switch (cmd)
2837     {
2838         case cmdStatus:
2839             break;
2840         case cmdDefaultRestore:
2841         case cmdFullRestore:
2842         case cmdFormat:
2843         {
2844             // write file to rwfs root
2845             int value = (cmd - 1) & 0x03; // map aa, bb, cc => 1, 2, 3
2846             std::ofstream restoreFile(restoreOpFname);
2847             if (!restoreFile)
2848             {
2849                 return ipmi::responseUnspecifiedError();
2850             }
2851             restoreFile << value << "\n";
2852             break;
2853         }
2854         default:
2855             return ipmi::responseInvalidFieldRequest();
2856     }
2857 
2858     constexpr uint8_t restorePending = 0;
2859     constexpr uint8_t restoreComplete = 1;
2860 
2861     uint8_t restoreStatus = std::filesystem::exists(restoreOpFname)
2862                                 ? restorePending
2863                                 : restoreComplete;
2864     return ipmi::responseSuccess(restoreStatus);
2865 }
2866 
2867 ipmi::RspType<uint8_t> ipmiOEMGetNmiSource(void)
2868 {
2869     uint8_t bmcSource;
2870     namespace nmi = sdbusplus::xyz::openbmc_project::Chassis::Control::server;
2871 
2872     try
2873     {
2874         std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
2875         std::string service =
2876             getService(*dbus, oemNmiSourceIntf, oemNmiSourceObjPath);
2877         Value variant =
2878             getDbusProperty(*dbus, service, oemNmiSourceObjPath,
2879                             oemNmiSourceIntf, oemNmiBmcSourceObjPathProp);
2880 
2881         switch (nmi::NMISource::convertBMCSourceSignalFromString(
2882             std::get<std::string>(variant)))
2883         {
2884             case nmi::NMISource::BMCSourceSignal::None:
2885                 bmcSource = static_cast<uint8_t>(NmiSource::none);
2886                 break;
2887             case nmi::NMISource::BMCSourceSignal::FrontPanelButton:
2888                 bmcSource = static_cast<uint8_t>(NmiSource::frontPanelButton);
2889                 break;
2890             case nmi::NMISource::BMCSourceSignal::Watchdog:
2891                 bmcSource = static_cast<uint8_t>(NmiSource::watchdog);
2892                 break;
2893             case nmi::NMISource::BMCSourceSignal::ChassisCmd:
2894                 bmcSource = static_cast<uint8_t>(NmiSource::chassisCmd);
2895                 break;
2896             case nmi::NMISource::BMCSourceSignal::MemoryError:
2897                 bmcSource = static_cast<uint8_t>(NmiSource::memoryError);
2898                 break;
2899             case nmi::NMISource::BMCSourceSignal::PciBusError:
2900                 bmcSource = static_cast<uint8_t>(NmiSource::pciBusError);
2901                 break;
2902             case nmi::NMISource::BMCSourceSignal::PCH:
2903                 bmcSource = static_cast<uint8_t>(NmiSource::pch);
2904                 break;
2905             case nmi::NMISource::BMCSourceSignal::Chipset:
2906                 bmcSource = static_cast<uint8_t>(NmiSource::chipset);
2907                 break;
2908             default:
2909                 phosphor::logging::log<phosphor::logging::level::ERR>(
2910                     "NMI source: invalid property!",
2911                     phosphor::logging::entry(
2912                         "PROP=%s", std::get<std::string>(variant).c_str()));
2913                 return ipmi::responseResponseError();
2914         }
2915     }
2916     catch (sdbusplus::exception::SdBusError& e)
2917     {
2918         phosphor::logging::log<phosphor::logging::level::ERR>(e.what());
2919         return ipmi::responseResponseError();
2920     }
2921 
2922     return ipmi::responseSuccess(bmcSource);
2923 }
2924 
2925 ipmi::RspType<> ipmiOEMSetNmiSource(uint8_t sourceId)
2926 {
2927     namespace nmi = sdbusplus::xyz::openbmc_project::Chassis::Control::server;
2928 
2929     nmi::NMISource::BMCSourceSignal bmcSourceSignal =
2930         nmi::NMISource::BMCSourceSignal::None;
2931 
2932     switch (NmiSource(sourceId))
2933     {
2934         case NmiSource::none:
2935             bmcSourceSignal = nmi::NMISource::BMCSourceSignal::None;
2936             break;
2937         case NmiSource::frontPanelButton:
2938             bmcSourceSignal = nmi::NMISource::BMCSourceSignal::FrontPanelButton;
2939             break;
2940         case NmiSource::watchdog:
2941             bmcSourceSignal = nmi::NMISource::BMCSourceSignal::Watchdog;
2942             break;
2943         case NmiSource::chassisCmd:
2944             bmcSourceSignal = nmi::NMISource::BMCSourceSignal::ChassisCmd;
2945             break;
2946         case NmiSource::memoryError:
2947             bmcSourceSignal = nmi::NMISource::BMCSourceSignal::MemoryError;
2948             break;
2949         case NmiSource::pciBusError:
2950             bmcSourceSignal = nmi::NMISource::BMCSourceSignal::PciBusError;
2951             break;
2952         case NmiSource::pch:
2953             bmcSourceSignal = nmi::NMISource::BMCSourceSignal::PCH;
2954             break;
2955         case NmiSource::chipset:
2956             bmcSourceSignal = nmi::NMISource::BMCSourceSignal::Chipset;
2957             break;
2958         default:
2959             phosphor::logging::log<phosphor::logging::level::ERR>(
2960                 "NMI source: invalid property!");
2961             return ipmi::responseResponseError();
2962     }
2963 
2964     try
2965     {
2966         // keep NMI signal source
2967         std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
2968         std::string service =
2969             getService(*dbus, oemNmiSourceIntf, oemNmiSourceObjPath);
2970         setDbusProperty(*dbus, service, oemNmiSourceObjPath, oemNmiSourceIntf,
2971                         oemNmiBmcSourceObjPathProp,
2972                         nmi::convertForMessage(bmcSourceSignal));
2973         // set Enabled property to inform NMI source handling
2974         // to trigger a NMI_OUT BSOD.
2975         // if it's triggered by NMI source property changed,
2976         // NMI_OUT BSOD could be missed if the same source occurs twice in a row
2977         if (bmcSourceSignal != nmi::NMISource::BMCSourceSignal::None)
2978         {
2979             setDbusProperty(*dbus, service, oemNmiSourceObjPath,
2980                             oemNmiSourceIntf, oemNmiEnabledObjPathProp,
2981                             static_cast<bool>(true));
2982         }
2983     }
2984     catch (sdbusplus::exception_t& e)
2985     {
2986         phosphor::logging::log<phosphor::logging::level::ERR>(e.what());
2987         return ipmi::responseResponseError();
2988     }
2989 
2990     return ipmi::responseSuccess();
2991 }
2992 
2993 namespace dimmOffset
2994 {
2995 constexpr const char* dimmPower = "DimmPower";
2996 constexpr const char* staticCltt = "StaticCltt";
2997 constexpr const char* offsetPath = "/xyz/openbmc_project/Inventory/Item/Dimm";
2998 constexpr const char* offsetInterface =
2999     "xyz.openbmc_project.Inventory.Item.Dimm.Offset";
3000 constexpr const char* property = "DimmOffset";
3001 
3002 }; // namespace dimmOffset
3003 
3004 ipmi::RspType<>
3005     ipmiOEMSetDimmOffset(uint8_t type,
3006                          const std::vector<std::tuple<uint8_t, uint8_t>>& data)
3007 {
3008     if (type != static_cast<uint8_t>(dimmOffsetTypes::dimmPower) &&
3009         type != static_cast<uint8_t>(dimmOffsetTypes::staticCltt))
3010     {
3011         return ipmi::responseInvalidFieldRequest();
3012     }
3013 
3014     if (data.empty())
3015     {
3016         return ipmi::responseInvalidFieldRequest();
3017     }
3018     nlohmann::json json;
3019 
3020     std::ifstream jsonStream(dimmOffsetFile);
3021     if (jsonStream.good())
3022     {
3023         json = nlohmann::json::parse(jsonStream, nullptr, false);
3024         if (json.is_discarded())
3025         {
3026             json = nlohmann::json();
3027         }
3028         jsonStream.close();
3029     }
3030 
3031     std::string typeName;
3032     if (type == static_cast<uint8_t>(dimmOffsetTypes::dimmPower))
3033     {
3034         typeName = dimmOffset::dimmPower;
3035     }
3036     else
3037     {
3038         typeName = dimmOffset::staticCltt;
3039     }
3040 
3041     nlohmann::json& field = json[typeName];
3042 
3043     for (const auto& [index, value] : data)
3044     {
3045         field[index] = value;
3046     }
3047 
3048     for (nlohmann::json& val : field)
3049     {
3050         if (val == nullptr)
3051         {
3052             val = static_cast<uint8_t>(0);
3053         }
3054     }
3055 
3056     std::ofstream output(dimmOffsetFile);
3057     if (!output.good())
3058     {
3059         std::cerr << "Error writing json file\n";
3060         return ipmi::responseResponseError();
3061     }
3062 
3063     output << json.dump(4);
3064 
3065     if (type == static_cast<uint8_t>(dimmOffsetTypes::staticCltt))
3066     {
3067         std::shared_ptr<sdbusplus::asio::connection> bus = getSdBus();
3068 
3069         std::variant<std::vector<uint8_t>> offsets =
3070             field.get<std::vector<uint8_t>>();
3071         auto call = bus->new_method_call(
3072             settingsBusName, dimmOffset::offsetPath, PROP_INTF, "Set");
3073         call.append(dimmOffset::offsetInterface, dimmOffset::property, offsets);
3074         try
3075         {
3076             bus->call(call);
3077         }
3078         catch (sdbusplus::exception_t& e)
3079         {
3080             phosphor::logging::log<phosphor::logging::level::ERR>(
3081                 "ipmiOEMSetDimmOffset: can't set dimm offsets!",
3082                 phosphor::logging::entry("ERR=%s", e.what()));
3083             return ipmi::responseResponseError();
3084         }
3085     }
3086 
3087     return ipmi::responseSuccess();
3088 }
3089 
3090 ipmi::RspType<uint8_t> ipmiOEMGetDimmOffset(uint8_t type, uint8_t index)
3091 {
3092 
3093     if (type != static_cast<uint8_t>(dimmOffsetTypes::dimmPower) &&
3094         type != static_cast<uint8_t>(dimmOffsetTypes::staticCltt))
3095     {
3096         return ipmi::responseInvalidFieldRequest();
3097     }
3098 
3099     std::ifstream jsonStream(dimmOffsetFile);
3100 
3101     auto json = nlohmann::json::parse(jsonStream, nullptr, false);
3102     if (json.is_discarded())
3103     {
3104         std::cerr << "File error in " << dimmOffsetFile << "\n";
3105         return ipmi::responseResponseError();
3106     }
3107 
3108     std::string typeName;
3109     if (type == static_cast<uint8_t>(dimmOffsetTypes::dimmPower))
3110     {
3111         typeName = dimmOffset::dimmPower;
3112     }
3113     else
3114     {
3115         typeName = dimmOffset::staticCltt;
3116     }
3117 
3118     auto it = json.find(typeName);
3119     if (it == json.end())
3120     {
3121         return ipmi::responseInvalidFieldRequest();
3122     }
3123 
3124     if (it->size() <= index)
3125     {
3126         return ipmi::responseInvalidFieldRequest();
3127     }
3128 
3129     uint8_t resp = it->at(index).get<uint8_t>();
3130     return ipmi::responseSuccess(resp);
3131 }
3132 
3133 namespace boot_options
3134 {
3135 
3136 using namespace sdbusplus::xyz::openbmc_project::Control::Boot::server;
3137 using IpmiValue = uint8_t;
3138 constexpr auto ipmiDefault = 0;
3139 
3140 std::map<IpmiValue, Source::Sources> sourceIpmiToDbus = {
3141     {0x01, Source::Sources::Network},
3142     {0x02, Source::Sources::Disk},
3143     {0x05, Source::Sources::ExternalMedia},
3144     {0x0f, Source::Sources::RemovableMedia},
3145     {ipmiDefault, Source::Sources::Default}};
3146 
3147 std::map<IpmiValue, Mode::Modes> modeIpmiToDbus = {
3148     {0x06, Mode::Modes::Setup}, {ipmiDefault, Mode::Modes::Regular}};
3149 
3150 std::map<Source::Sources, IpmiValue> sourceDbusToIpmi = {
3151     {Source::Sources::Network, 0x01},
3152     {Source::Sources::Disk, 0x02},
3153     {Source::Sources::ExternalMedia, 0x05},
3154     {Source::Sources::RemovableMedia, 0x0f},
3155     {Source::Sources::Default, ipmiDefault}};
3156 
3157 std::map<Mode::Modes, IpmiValue> modeDbusToIpmi = {
3158     {Mode::Modes::Setup, 0x06}, {Mode::Modes::Regular, ipmiDefault}};
3159 
3160 static constexpr auto bootModeIntf = "xyz.openbmc_project.Control.Boot.Mode";
3161 static constexpr auto bootSourceIntf =
3162     "xyz.openbmc_project.Control.Boot.Source";
3163 static constexpr auto enabledIntf = "xyz.openbmc_project.Object.Enable";
3164 static constexpr auto persistentObjPath =
3165     "/xyz/openbmc_project/control/host0/boot";
3166 static constexpr auto oneTimePath =
3167     "/xyz/openbmc_project/control/host0/boot/one_time";
3168 static constexpr auto bootSourceProp = "BootSource";
3169 static constexpr auto bootModeProp = "BootMode";
3170 static constexpr auto oneTimeBootEnableProp = "Enabled";
3171 static constexpr auto httpBootMode =
3172     "xyz.openbmc_project.Control.Boot.Source.Sources.Http";
3173 
3174 enum class BootOptionParameter : size_t
3175 {
3176     setInProgress = 0x0,
3177     bootFlags = 0x5,
3178 };
3179 static constexpr uint8_t setComplete = 0x0;
3180 static constexpr uint8_t setInProgress = 0x1;
3181 static uint8_t transferStatus = setComplete;
3182 static constexpr uint8_t setParmVersion = 0x01;
3183 static constexpr uint8_t setParmBootFlagsPermanent = 0x40;
3184 static constexpr uint8_t setParmBootFlagsValidOneTime = 0x80;
3185 static constexpr uint8_t setParmBootFlagsValidPermanent = 0xC0;
3186 static constexpr uint8_t httpBoot = 0xd;
3187 static constexpr uint8_t bootSourceMask = 0x3c;
3188 
3189 } // namespace boot_options
3190 
3191 ipmi::RspType<uint8_t,               // version
3192               uint8_t,               // param
3193               uint8_t,               // data0, dependent on parameter
3194               std::optional<uint8_t> // data1, dependent on parameter
3195               >
3196     ipmiOemGetEfiBootOptions(uint8_t parameter, uint8_t set, uint8_t block)
3197 {
3198     using namespace boot_options;
3199     uint8_t bootOption = 0;
3200 
3201     if (parameter == static_cast<uint8_t>(BootOptionParameter::setInProgress))
3202     {
3203         return ipmi::responseSuccess(setParmVersion, parameter, transferStatus,
3204                                      std::nullopt);
3205     }
3206 
3207     if (parameter != static_cast<uint8_t>(BootOptionParameter::bootFlags))
3208     {
3209         phosphor::logging::log<phosphor::logging::level::ERR>(
3210             "Unsupported parameter");
3211         return ipmi::responseResponseError();
3212     }
3213 
3214     try
3215     {
3216         auto oneTimeEnabled = false;
3217         // read one time Enabled property
3218         std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
3219         std::string service = getService(*dbus, enabledIntf, oneTimePath);
3220         Value variant = getDbusProperty(*dbus, service, oneTimePath,
3221                                         enabledIntf, oneTimeBootEnableProp);
3222         oneTimeEnabled = std::get<bool>(variant);
3223 
3224         // get BootSource and BootMode properties
3225         // according to oneTimeEnable
3226         auto bootObjPath = oneTimePath;
3227         if (oneTimeEnabled == false)
3228         {
3229             bootObjPath = persistentObjPath;
3230         }
3231 
3232         service = getService(*dbus, bootModeIntf, bootObjPath);
3233         variant = getDbusProperty(*dbus, service, bootObjPath, bootModeIntf,
3234                                   bootModeProp);
3235 
3236         auto bootMode =
3237             Mode::convertModesFromString(std::get<std::string>(variant));
3238 
3239         service = getService(*dbus, bootSourceIntf, bootObjPath);
3240         variant = getDbusProperty(*dbus, service, bootObjPath, bootSourceIntf,
3241                                   bootSourceProp);
3242 
3243         if (std::get<std::string>(variant) == httpBootMode)
3244         {
3245             bootOption = httpBoot;
3246         }
3247         else
3248         {
3249             auto bootSource = Source::convertSourcesFromString(
3250                 std::get<std::string>(variant));
3251             bootOption = sourceDbusToIpmi.at(bootSource);
3252             if (Source::Sources::Default == bootSource)
3253             {
3254                 bootOption = modeDbusToIpmi.at(bootMode);
3255             }
3256         }
3257 
3258         uint8_t oneTime = oneTimeEnabled ? setParmBootFlagsValidOneTime
3259                                          : setParmBootFlagsValidPermanent;
3260         bootOption <<= 2; // shift for responseconstexpr
3261         return ipmi::responseSuccess(setParmVersion, parameter, oneTime,
3262                                      bootOption);
3263     }
3264     catch (sdbusplus::exception_t& e)
3265     {
3266         phosphor::logging::log<phosphor::logging::level::ERR>(e.what());
3267         return ipmi::responseResponseError();
3268     }
3269 }
3270 
3271 ipmi::RspType<> ipmiOemSetEfiBootOptions(uint8_t bootFlag, uint8_t bootParam,
3272                                          std::optional<uint8_t> bootOption)
3273 {
3274     using namespace boot_options;
3275     auto oneTimeEnabled = false;
3276 
3277     if (bootFlag == static_cast<uint8_t>(BootOptionParameter::setInProgress))
3278     {
3279         if (bootOption)
3280         {
3281             return ipmi::responseReqDataLenInvalid();
3282         }
3283 
3284         if (transferStatus == setInProgress)
3285         {
3286             phosphor::logging::log<phosphor::logging::level::ERR>(
3287                 "boot option set in progress!");
3288             return ipmi::responseResponseError();
3289         }
3290 
3291         transferStatus = bootParam;
3292         return ipmi::responseSuccess();
3293     }
3294 
3295     if (bootFlag != (uint8_t)BootOptionParameter::bootFlags)
3296     {
3297         phosphor::logging::log<phosphor::logging::level::ERR>(
3298             "Unsupported parameter");
3299         return ipmi::responseResponseError();
3300     }
3301 
3302     if (!bootOption)
3303     {
3304         return ipmi::responseReqDataLenInvalid();
3305     }
3306 
3307     if (((bootOption.value() & bootSourceMask) >> 2) !=
3308         httpBoot) // not http boot, exit
3309     {
3310         phosphor::logging::log<phosphor::logging::level::ERR>(
3311             "wrong boot option parameter!");
3312         return ipmi::responseParmOutOfRange();
3313     }
3314 
3315     try
3316     {
3317         bool permanent = (bootParam & setParmBootFlagsPermanent) ==
3318                          setParmBootFlagsPermanent;
3319 
3320         // read one time Enabled property
3321         std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
3322         std::string service = getService(*dbus, enabledIntf, oneTimePath);
3323         Value variant = getDbusProperty(*dbus, service, oneTimePath,
3324                                         enabledIntf, oneTimeBootEnableProp);
3325         oneTimeEnabled = std::get<bool>(variant);
3326 
3327         /*
3328          * Check if the current boot setting is onetime or permanent, if the
3329          * request in the command is otherwise, then set the "Enabled"
3330          * property in one_time object path to 'True' to indicate onetime
3331          * and 'False' to indicate permanent.
3332          *
3333          * Once the onetime/permanent setting is applied, then the bootMode
3334          * and bootSource is updated for the corresponding object.
3335          */
3336         if (permanent == oneTimeEnabled)
3337         {
3338             setDbusProperty(*dbus, service, oneTimePath, enabledIntf,
3339                             oneTimeBootEnableProp, !permanent);
3340         }
3341 
3342         // set BootSource and BootMode properties
3343         // according to oneTimeEnable or persistent
3344         auto bootObjPath = oneTimePath;
3345         if (oneTimeEnabled == false)
3346         {
3347             bootObjPath = persistentObjPath;
3348         }
3349         std::string bootMode =
3350             "xyz.openbmc_project.Control.Boot.Mode.Modes.Regular";
3351         std::string bootSource = httpBootMode;
3352 
3353         service = getService(*dbus, bootModeIntf, bootObjPath);
3354         setDbusProperty(*dbus, service, bootObjPath, bootModeIntf, bootModeProp,
3355                         bootMode);
3356 
3357         service = getService(*dbus, bootSourceIntf, bootObjPath);
3358         setDbusProperty(*dbus, service, bootObjPath, bootSourceIntf,
3359                         bootSourceProp, bootSource);
3360     }
3361     catch (sdbusplus::exception_t& e)
3362     {
3363         phosphor::logging::log<phosphor::logging::level::ERR>(e.what());
3364         return ipmi::responseResponseError();
3365     }
3366 
3367     return ipmi::responseSuccess();
3368 }
3369 
3370 using BasicVariantType =
3371     std::variant<std::vector<std::string>, std::vector<uint64_t>, std::string,
3372                  int64_t, uint64_t, double, int32_t, uint32_t, int16_t,
3373                  uint16_t, uint8_t, bool>;
3374 using PropertyMapType =
3375     boost::container::flat_map<std::string, BasicVariantType>;
3376 static constexpr const std::array<const char*, 1> psuPresenceTypes = {
3377     "xyz.openbmc_project.Configuration.PSUPresence"};
3378 int getPSUAddress(ipmi::Context::ptr ctx, uint8_t& bus,
3379                   std::vector<uint64_t>& addrTable)
3380 {
3381     boost::system::error_code ec;
3382     GetSubTreeType subtree = ctx->bus->yield_method_call<GetSubTreeType>(
3383         ctx->yield, ec, "xyz.openbmc_project.ObjectMapper",
3384         "/xyz/openbmc_project/object_mapper",
3385         "xyz.openbmc_project.ObjectMapper", "GetSubTree",
3386         "/xyz/openbmc_project/inventory/system", 3, psuPresenceTypes);
3387     if (ec)
3388     {
3389         phosphor::logging::log<phosphor::logging::level::ERR>(
3390             "Failed to set dbus property to cold redundancy");
3391         return -1;
3392     }
3393     for (const auto& object : subtree)
3394     {
3395         std::string pathName = object.first;
3396         for (const auto& serviceIface : object.second)
3397         {
3398             std::string serviceName = serviceIface.first;
3399 
3400             ec.clear();
3401             PropertyMapType propMap =
3402                 ctx->bus->yield_method_call<PropertyMapType>(
3403                     ctx->yield, ec, serviceName, pathName,
3404                     "org.freedesktop.DBus.Properties", "GetAll",
3405                     "xyz.openbmc_project.Configuration.PSUPresence");
3406             if (ec)
3407             {
3408                 phosphor::logging::log<phosphor::logging::level::ERR>(
3409                     "Failed to set dbus property to cold redundancy");
3410                 return -1;
3411             }
3412             auto psuBus = std::get_if<uint64_t>(&propMap["Bus"]);
3413             auto psuAddress =
3414                 std::get_if<std::vector<uint64_t>>(&propMap["Address"]);
3415 
3416             if (psuBus == nullptr || psuAddress == nullptr)
3417             {
3418                 std::cerr << "error finding necessary "
3419                              "entry in configuration\n";
3420                 return -1;
3421             }
3422             bus = static_cast<uint8_t>(*psuBus);
3423             addrTable = *psuAddress;
3424             return 0;
3425         }
3426     }
3427     return -1;
3428 }
3429 
3430 static const constexpr uint8_t addrOffset = 8;
3431 static const constexpr uint8_t psuRevision = 0xd9;
3432 static const constexpr uint8_t defaultPSUBus = 7;
3433 // Second Minor, Primary Minor, Major
3434 static const constexpr size_t verLen = 3;
3435 ipmi::RspType<std::vector<uint8_t>> ipmiOEMGetPSUVersion(ipmi::Context::ptr ctx)
3436 {
3437     uint8_t bus = defaultPSUBus;
3438     std::vector<uint64_t> addrTable;
3439     std::vector<uint8_t> result;
3440     if (getPSUAddress(ctx, bus, addrTable))
3441     {
3442         std::cerr << "Failed to get PSU bus and address\n";
3443         return ipmi::responseResponseError();
3444     }
3445 
3446     for (const auto& slaveAddr : addrTable)
3447     {
3448         std::vector<uint8_t> writeData = {psuRevision};
3449         std::vector<uint8_t> readBuf(verLen);
3450         uint8_t addr = static_cast<uint8_t>(slaveAddr) + addrOffset;
3451         std::string i2cBus = "/dev/i2c-" + std::to_string(bus);
3452 
3453         auto retI2C = ipmi::i2cWriteRead(i2cBus, addr, writeData, readBuf);
3454         if (retI2C != ipmi::ccSuccess)
3455         {
3456             for (size_t idx = 0; idx < verLen; idx++)
3457             {
3458                 result.emplace_back(0x00);
3459             }
3460         }
3461         else
3462         {
3463             for (const uint8_t& data : readBuf)
3464             {
3465                 result.emplace_back(data);
3466             }
3467         }
3468     }
3469 
3470     return ipmi::responseSuccess(result);
3471 }
3472 
3473 /** @brief implements the maximum size of
3474  *  bridgeable messages used between KCS and
3475  *  IPMB interfacesget security mode command.
3476  *
3477  *  @returns IPMI completion code with following data
3478  *   - KCS Buffer Size (In multiples of four bytes)
3479  *   - IPMB Buffer Size (In multiples of four bytes)
3480  **/
3481 ipmi::RspType<uint8_t, uint8_t> ipmiOEMGetBufferSize()
3482 {
3483     // for now this is hard coded; really this number is dependent on
3484     // the BMC kcs driver as well as the host kcs driver....
3485     // we can't know the latter.
3486     uint8_t kcsMaxBufferSize = 63 / 4;
3487     uint8_t ipmbMaxBufferSize = 128 / 4;
3488 
3489     return ipmi::responseSuccess(kcsMaxBufferSize, ipmbMaxBufferSize);
3490 }
3491 
3492 static void registerOEMFunctions(void)
3493 {
3494     phosphor::logging::log<phosphor::logging::level::INFO>(
3495         "Registering OEM commands");
3496     ipmiPrintAndRegister(intel::netFnGeneral, IPMI_CMD_WILDCARD, NULL,
3497                          ipmiOEMWildcard,
3498                          PRIVILEGE_USER); // wildcard default handler
3499 
3500     ipmiPrintAndRegister(intel::netFnApp, IPMI_CMD_WILDCARD, NULL,
3501                          ipmiOEMWildcard,
3502                          PRIVILEGE_USER); // wildcard default handler
3503 
3504     ipmiPrintAndRegister(intel::netFnGeneral,
3505                          intel::general::cmdGetChassisIdentifier, NULL,
3506                          ipmiOEMGetChassisIdentifier,
3507                          PRIVILEGE_USER); // get chassis identifier
3508 
3509     ipmiPrintAndRegister(intel::netFnGeneral, intel::general::cmdSetSystemGUID,
3510                          NULL, ipmiOEMSetSystemGUID,
3511                          PRIVILEGE_ADMIN); // set system guid
3512 
3513     // <Disable BMC System Reset Action>
3514     registerHandler(prioOemBase, intel::netFnGeneral,
3515                     intel::general::cmdDisableBMCSystemReset, Privilege::Admin,
3516                     ipmiOEMDisableBMCSystemReset);
3517 
3518     // <Get BMC Reset Disables>
3519     registerHandler(prioOemBase, intel::netFnGeneral,
3520                     intel::general::cmdGetBMCResetDisables, Privilege::Admin,
3521                     ipmiOEMGetBMCResetDisables);
3522 
3523     ipmiPrintAndRegister(intel::netFnGeneral, intel::general::cmdSetBIOSID,
3524                          NULL, ipmiOEMSetBIOSID, PRIVILEGE_ADMIN);
3525 
3526     registerHandler(prioOemBase, intel::netFnGeneral,
3527                     intel::general::cmdGetOEMDeviceInfo, Privilege::User,
3528                     ipmiOEMGetDeviceInfo);
3529 
3530     ipmiPrintAndRegister(intel::netFnGeneral,
3531                          intel::general::cmdGetAICSlotFRUIDSlotPosRecords, NULL,
3532                          ipmiOEMGetAICFRU, PRIVILEGE_USER);
3533 
3534     registerHandler(prioOpenBmcBase, intel::netFnGeneral,
3535                     intel::general::cmdSendEmbeddedFWUpdStatus,
3536                     Privilege::Operator, ipmiOEMSendEmbeddedFwUpdStatus);
3537 
3538     ipmiPrintAndRegister(intel::netFnGeneral,
3539                          intel::general::cmdSetPowerRestoreDelay, NULL,
3540                          ipmiOEMSetPowerRestoreDelay, PRIVILEGE_OPERATOR);
3541 
3542     ipmiPrintAndRegister(intel::netFnGeneral,
3543                          intel::general::cmdGetPowerRestoreDelay, NULL,
3544                          ipmiOEMGetPowerRestoreDelay, PRIVILEGE_USER);
3545 
3546     registerHandler(prioOpenBmcBase, intel::netFnGeneral,
3547                     intel::general::cmdSetOEMUser2Activation,
3548                     Privilege::Callback, ipmiOEMSetUser2Activation);
3549 
3550     registerHandler(prioOpenBmcBase, intel::netFnGeneral,
3551                     intel::general::cmdSetSpecialUserPassword,
3552                     Privilege::Callback, ipmiOEMSetSpecialUserPassword);
3553 
3554     // <Get Processor Error Config>
3555     registerHandler(prioOemBase, intel::netFnGeneral,
3556                     intel::general::cmdGetProcessorErrConfig, Privilege::User,
3557                     ipmiOEMGetProcessorErrConfig);
3558 
3559     // <Set Processor Error Config>
3560     registerHandler(prioOemBase, intel::netFnGeneral,
3561                     intel::general::cmdSetProcessorErrConfig, Privilege::Admin,
3562                     ipmiOEMSetProcessorErrConfig);
3563 
3564     ipmiPrintAndRegister(intel::netFnGeneral,
3565                          intel::general::cmdSetShutdownPolicy, NULL,
3566                          ipmiOEMSetShutdownPolicy, PRIVILEGE_ADMIN);
3567 
3568     ipmiPrintAndRegister(intel::netFnGeneral,
3569                          intel::general::cmdGetShutdownPolicy, NULL,
3570                          ipmiOEMGetShutdownPolicy, PRIVILEGE_ADMIN);
3571 
3572     registerHandler(prioOemBase, intel::netFnGeneral,
3573                     intel::general::cmdSetFanConfig, Privilege::User,
3574                     ipmiOEMSetFanConfig);
3575 
3576     registerHandler(prioOemBase, intel::netFnGeneral,
3577                     intel::general::cmdGetFanConfig, Privilege::User,
3578                     ipmiOEMGetFanConfig);
3579 
3580     registerHandler(prioOemBase, intel::netFnGeneral,
3581                     intel::general::cmdGetFanSpeedOffset, Privilege::User,
3582                     ipmiOEMGetFanSpeedOffset);
3583 
3584     registerHandler(prioOemBase, intel::netFnGeneral,
3585                     intel::general::cmdSetFanSpeedOffset, Privilege::User,
3586                     ipmiOEMSetFanSpeedOffset);
3587 
3588     registerHandler(prioOemBase, intel::netFnGeneral,
3589                     intel::general::cmdSetFscParameter, Privilege::User,
3590                     ipmiOEMSetFscParameter);
3591 
3592     registerHandler(prioOemBase, intel::netFnGeneral,
3593                     intel::general::cmdGetFscParameter, Privilege::User,
3594                     ipmiOEMGetFscParameter);
3595 
3596     registerHandler(prioOpenBmcBase, intel::netFnGeneral,
3597                     intel::general::cmdReadBaseBoardProductId, Privilege::Admin,
3598                     ipmiOEMReadBoardProductId);
3599 
3600     registerHandler(prioOemBase, intel::netFnGeneral,
3601                     intel::general::cmdGetNmiStatus, Privilege::User,
3602                     ipmiOEMGetNmiSource);
3603 
3604     registerHandler(prioOemBase, intel::netFnGeneral,
3605                     intel::general::cmdSetNmiStatus, Privilege::Operator,
3606                     ipmiOEMSetNmiSource);
3607 
3608     registerHandler(prioOemBase, intel::netFnGeneral,
3609                     intel::general::cmdGetEfiBootOptions, Privilege::User,
3610                     ipmiOemGetEfiBootOptions);
3611 
3612     registerHandler(prioOemBase, intel::netFnGeneral,
3613                     intel::general::cmdSetEfiBootOptions, Privilege::Operator,
3614                     ipmiOemSetEfiBootOptions);
3615 
3616     registerHandler(prioOemBase, intel::netFnGeneral,
3617                     intel::general::cmdGetSecurityMode, Privilege::User,
3618                     ipmiGetSecurityMode);
3619 
3620     registerHandler(prioOemBase, intel::netFnGeneral,
3621                     intel::general::cmdSetSecurityMode, Privilege::Admin,
3622                     ipmiSetSecurityMode);
3623 
3624     ipmiPrintAndRegister(intel::netFnGeneral, intel::general::cmdGetLEDStatus,
3625                          NULL, ipmiOEMGetLEDStatus, PRIVILEGE_ADMIN);
3626 
3627     ipmiPrintAndRegister(ipmi::intel::netFnPlatform,
3628                          ipmi::intel::platform::cmdCfgHostSerialPortSpeed, NULL,
3629                          ipmiOEMCfgHostSerialPortSpeed, PRIVILEGE_ADMIN);
3630 
3631     registerHandler(prioOemBase, intel::netFnGeneral,
3632                     intel::general::cmdSetFaultIndication, Privilege::Operator,
3633                     ipmiOEMSetFaultIndication);
3634 
3635     registerHandler(prioOemBase, intel::netFnGeneral,
3636                     intel::general::cmdSetColdRedundancyConfig, Privilege::User,
3637                     ipmiOEMSetCRConfig);
3638 
3639     registerHandler(prioOemBase, intel::netFnGeneral,
3640                     intel::general::cmdGetColdRedundancyConfig, Privilege::User,
3641                     ipmiOEMGetCRConfig);
3642 
3643     registerHandler(prioOemBase, intel::netFnGeneral,
3644                     intel::general::cmdRestoreConfiguration, Privilege::Admin,
3645                     ipmiRestoreConfiguration);
3646 
3647     registerHandler(prioOemBase, intel::netFnGeneral,
3648                     intel::general::cmdSetDimmOffset, Privilege::Operator,
3649                     ipmiOEMSetDimmOffset);
3650 
3651     registerHandler(prioOemBase, intel::netFnGeneral,
3652                     intel::general::cmdGetDimmOffset, Privilege::Operator,
3653                     ipmiOEMGetDimmOffset);
3654 
3655     registerHandler(prioOemBase, intel::netFnGeneral,
3656                     intel::general::cmdGetPSUVersion, Privilege::User,
3657                     ipmiOEMGetPSUVersion);
3658 
3659     registerHandler(prioOemBase, intel::netFnGeneral,
3660                     intel::general::cmdGetBufferSize, Privilege::User,
3661                     ipmiOEMGetBufferSize);
3662 }
3663 
3664 } // namespace ipmi
3665