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