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