1 /*
2 // Copyright (c) 2018 Intel Corporation
3 //
4 // Licensed under the Apache License, Version 2.0 (the "License");
5 // you may not use this file except in compliance with the License.
6 // You may obtain a copy of the License at
7 //
8 //      http://www.apache.org/licenses/LICENSE-2.0
9 //
10 // Unless required by applicable law or agreed to in writing, software
11 // distributed under the License is distributed on an "AS IS" BASIS,
12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 // See the License for the specific language governing permissions and
14 // limitations under the License.
15 */
16 
17 #include "xyz/openbmc_project/Common/error.hpp"
18 #include "xyz/openbmc_project/Led/Physical/server.hpp"
19 
20 #include <systemd/sd-journal.h>
21 
22 #include <array>
23 #include <boost/container/flat_map.hpp>
24 #include <boost/process/child.hpp>
25 #include <boost/process/io.hpp>
26 #include <com/intel/Control/NMISource/server.hpp>
27 #include <com/intel/Control/OCOTShutdownPolicy/server.hpp>
28 #include <commandutils.hpp>
29 #include <filesystem>
30 #include <iostream>
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 <string>
39 #include <variant>
40 #include <vector>
41 
42 namespace ipmi
43 {
44 static void registerOEMFunctions() __attribute__((constructor));
45 
46 namespace netfn::intel
47 {
48 constexpr NetFn oemGeneral = netFnOemOne;
49 constexpr Cmd cmdRestoreConfiguration = 0x02;
50 } // namespace netfn::intel
51 
52 static constexpr size_t maxFRUStringLength = 0x3F;
53 
54 static constexpr auto ethernetIntf =
55     "xyz.openbmc_project.Network.EthernetInterface";
56 static constexpr auto networkIPIntf = "xyz.openbmc_project.Network.IP";
57 static constexpr auto networkService = "xyz.openbmc_project.Network";
58 static constexpr auto networkRoot = "/xyz/openbmc_project/network";
59 
60 static constexpr const char* oemNmiSourceIntf = "com.intel.Control.NMISource";
61 static constexpr const char* oemNmiSourceObjPath =
62     "/com/intel/control/NMISource";
63 static constexpr const char* oemNmiBmcSourceObjPathProp = "BMCSource";
64 static constexpr const char* oemNmiEnabledObjPathProp = "Enabled";
65 
66 static constexpr const char* dimmOffsetFile = "/var/lib/ipmi/ipmi_dimms.json";
67 
68 enum class NmiSource : uint8_t
69 {
70     none = 0,
71     fpBtn = 1,
72     wdPreTimeout = 2,
73     pefMatch = 3,
74     chassisCmd = 4,
75     memoryError = 5,
76     pciSerrPerr = 6,
77     southbridgeNmi = 7,
78     chipsetNmi = 8,
79 };
80 
81 // return code: 0 successful
82 int8_t getChassisSerialNumber(sdbusplus::bus::bus& bus, std::string& serial)
83 {
84     std::string objpath = "/xyz/openbmc_project/FruDevice";
85     std::string intf = "xyz.openbmc_project.FruDeviceManager";
86     std::string service = getService(bus, intf, objpath);
87     ObjectValueTree valueTree = getManagedObjects(bus, service, "/");
88     if (valueTree.empty())
89     {
90         phosphor::logging::log<phosphor::logging::level::ERR>(
91             "No object implements interface",
92             phosphor::logging::entry("INTF=%s", intf.c_str()));
93         return -1;
94     }
95 
96     for (const auto& item : valueTree)
97     {
98         auto interface = item.second.find("xyz.openbmc_project.FruDevice");
99         if (interface == item.second.end())
100         {
101             continue;
102         }
103 
104         auto property = interface->second.find("CHASSIS_SERIAL_NUMBER");
105         if (property == interface->second.end())
106         {
107             continue;
108         }
109 
110         try
111         {
112             Value variant = property->second;
113             std::string& result = std::get<std::string>(variant);
114             if (result.size() > maxFRUStringLength)
115             {
116                 phosphor::logging::log<phosphor::logging::level::ERR>(
117                     "FRU serial number exceed maximum length");
118                 return -1;
119             }
120             serial = result;
121             return 0;
122         }
123         catch (std::bad_variant_access& e)
124         {
125             phosphor::logging::log<phosphor::logging::level::ERR>(e.what());
126             return -1;
127         }
128     }
129     return -1;
130 }
131 
132 ipmi_ret_t ipmiOEMWildcard(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
133                            ipmi_request_t request, ipmi_response_t response,
134                            ipmi_data_len_t dataLen, ipmi_context_t context)
135 {
136     printCommand(+netfn, +cmd);
137     // Status code.
138     ipmi_ret_t rc = IPMI_CC_INVALID;
139     *dataLen = 0;
140     return rc;
141 }
142 
143 // Returns the Chassis Identifier (serial #)
144 ipmi_ret_t ipmiOEMGetChassisIdentifier(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
145                                        ipmi_request_t request,
146                                        ipmi_response_t response,
147                                        ipmi_data_len_t dataLen,
148                                        ipmi_context_t context)
149 {
150     std::string serial;
151     if (*dataLen != 0) // invalid request if there are extra parameters
152     {
153         *dataLen = 0;
154         return IPMI_CC_REQ_DATA_LEN_INVALID;
155     }
156     std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
157     if (getChassisSerialNumber(*dbus, serial) == 0)
158     {
159         *dataLen = serial.size(); // length will never exceed response length
160                                   // as it is checked in getChassisSerialNumber
161         char* resp = static_cast<char*>(response);
162         serial.copy(resp, *dataLen);
163         return IPMI_CC_OK;
164     }
165     *dataLen = 0;
166     return IPMI_CC_RESPONSE_ERROR;
167 }
168 
169 ipmi_ret_t ipmiOEMSetSystemGUID(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
170                                 ipmi_request_t request,
171                                 ipmi_response_t response,
172                                 ipmi_data_len_t dataLen, ipmi_context_t context)
173 {
174     static constexpr size_t safeBufferLength = 50;
175     char buf[safeBufferLength] = {0};
176     GUIDData* Data = reinterpret_cast<GUIDData*>(request);
177 
178     if (*dataLen != sizeof(GUIDData)) // 16bytes
179     {
180         *dataLen = 0;
181         return IPMI_CC_REQ_DATA_LEN_INVALID;
182     }
183 
184     *dataLen = 0;
185 
186     snprintf(
187         buf, safeBufferLength,
188         "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x",
189         Data->timeLow4, Data->timeLow3, Data->timeLow2, Data->timeLow1,
190         Data->timeMid2, Data->timeMid1, Data->timeHigh2, Data->timeHigh1,
191         Data->clock2, Data->clock1, Data->node6, Data->node5, Data->node4,
192         Data->node3, Data->node2, Data->node1);
193     // UUID is in RFC4122 format. Ex: 61a39523-78f2-11e5-9862-e6402cfc3223
194     std::string guid = buf;
195 
196     std::string objpath = "/xyz/openbmc_project/control/host0/systemGUID";
197     std::string intf = "xyz.openbmc_project.Common.UUID";
198     std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
199     std::string service = getService(*dbus, intf, objpath);
200     setDbusProperty(*dbus, service, objpath, intf, "UUID", guid);
201     return IPMI_CC_OK;
202 }
203 
204 ipmi_ret_t ipmiOEMSetBIOSID(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
205                             ipmi_request_t request, ipmi_response_t response,
206                             ipmi_data_len_t dataLen, ipmi_context_t context)
207 {
208     DeviceInfo* data = reinterpret_cast<DeviceInfo*>(request);
209 
210     if ((*dataLen < 2) || (*dataLen != (1 + data->biosIDLength)))
211     {
212         *dataLen = 0;
213         return IPMI_CC_REQ_DATA_LEN_INVALID;
214     }
215     std::string idString((char*)data->biosId, data->biosIDLength);
216 
217     std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
218     std::string service = getService(*dbus, biosIntf, biosObjPath);
219     setDbusProperty(*dbus, service, biosObjPath, biosIntf, biosProp, idString);
220     uint8_t* bytesWritten = static_cast<uint8_t*>(response);
221     *bytesWritten =
222         data->biosIDLength; // how many bytes are written into storage
223     *dataLen = 1;
224     return IPMI_CC_OK;
225 }
226 
227 ipmi_ret_t ipmiOEMGetDeviceInfo(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
228                                 ipmi_request_t request,
229                                 ipmi_response_t response,
230                                 ipmi_data_len_t dataLen, ipmi_context_t context)
231 {
232     GetOemDeviceInfoReq* req = reinterpret_cast<GetOemDeviceInfoReq*>(request);
233     GetOemDeviceInfoRes* res = reinterpret_cast<GetOemDeviceInfoRes*>(response);
234 
235     if (*dataLen == 0)
236     {
237         *dataLen = 0;
238         return IPMI_CC_REQ_DATA_LEN_INVALID;
239     }
240 
241     size_t reqDataLen = *dataLen;
242     *dataLen = 0;
243     if (req->entityType > static_cast<uint8_t>(OEMDevEntityType::sdrVer))
244     {
245         return IPMI_CC_INVALID_FIELD_REQUEST;
246     }
247 
248     // handle OEM command items
249     switch (OEMDevEntityType(req->entityType))
250     {
251         case OEMDevEntityType::biosId:
252         {
253             if (sizeof(GetOemDeviceInfoReq) != reqDataLen)
254             {
255                 return IPMI_CC_REQ_DATA_LEN_INVALID;
256             }
257 
258             std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
259             std::string service = getService(*dbus, biosIntf, biosObjPath);
260             try
261             {
262                 Value variant = getDbusProperty(*dbus, service, biosObjPath,
263                                                 biosIntf, biosProp);
264                 std::string& idString = std::get<std::string>(variant);
265                 if (req->offset >= idString.size())
266                 {
267                     return IPMI_CC_PARM_OUT_OF_RANGE;
268                 }
269                 size_t length = 0;
270                 if (req->countToRead > (idString.size() - req->offset))
271                 {
272                     length = idString.size() - req->offset;
273                 }
274                 else
275                 {
276                     length = req->countToRead;
277                 }
278                 std::copy(idString.begin() + req->offset, idString.end(),
279                           res->data);
280                 res->resDatalen = length;
281                 *dataLen = res->resDatalen + 1;
282             }
283             catch (std::bad_variant_access& e)
284             {
285                 phosphor::logging::log<phosphor::logging::level::ERR>(e.what());
286                 return IPMI_CC_UNSPECIFIED_ERROR;
287             }
288         }
289         break;
290 
291         case OEMDevEntityType::devVer:
292         case OEMDevEntityType::sdrVer:
293             // TODO:
294             return IPMI_CC_ILLEGAL_COMMAND;
295         default:
296             return IPMI_CC_INVALID_FIELD_REQUEST;
297     }
298     return IPMI_CC_OK;
299 }
300 
301 ipmi_ret_t ipmiOEMGetAICFRU(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
302                             ipmi_request_t request, ipmi_response_t response,
303                             ipmi_data_len_t dataLen, ipmi_context_t context)
304 {
305     if (*dataLen != 0)
306     {
307         *dataLen = 0;
308         return IPMI_CC_REQ_DATA_LEN_INVALID;
309     }
310 
311     *dataLen = 1;
312     uint8_t* res = reinterpret_cast<uint8_t*>(response);
313     // temporary fix. We don't support AIC FRU now. Just tell BIOS that no
314     // AIC is available so that BIOS will not timeout repeatly which leads to
315     // slow booting.
316     *res = 0; // Byte1=Count of SlotPosition/FruID records.
317     return IPMI_CC_OK;
318 }
319 
320 ipmi_ret_t ipmiOEMGetPowerRestoreDelay(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
321                                        ipmi_request_t request,
322                                        ipmi_response_t response,
323                                        ipmi_data_len_t dataLen,
324                                        ipmi_context_t context)
325 {
326     GetPowerRestoreDelayRes* resp =
327         reinterpret_cast<GetPowerRestoreDelayRes*>(response);
328 
329     if (*dataLen != 0)
330     {
331         *dataLen = 0;
332         return IPMI_CC_REQ_DATA_LEN_INVALID;
333     }
334 
335     std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
336     std::string service =
337         getService(*dbus, powerRestoreDelayIntf, powerRestoreDelayObjPath);
338     Value variant =
339         getDbusProperty(*dbus, service, powerRestoreDelayObjPath,
340                         powerRestoreDelayIntf, powerRestoreDelayProp);
341 
342     uint16_t delay = std::get<uint16_t>(variant);
343     resp->byteLSB = delay;
344     resp->byteMSB = delay >> 8;
345 
346     *dataLen = sizeof(GetPowerRestoreDelayRes);
347 
348     return IPMI_CC_OK;
349 }
350 
351 static uint8_t bcdToDec(uint8_t val)
352 {
353     return ((val / 16 * 10) + (val % 16));
354 }
355 
356 // Allows an update utility or system BIOS to send the status of an embedded
357 // firmware update attempt to the BMC. After received, BMC will create a logging
358 // record.
359 ipmi::RspType<> ipmiOEMSendEmbeddedFwUpdStatus(uint8_t status, uint8_t target,
360                                                uint8_t majorRevision,
361                                                uint8_t minorRevision,
362                                                uint32_t auxInfo)
363 {
364     std::string firmware;
365     int instance = (target & targetInstanceMask) >> targetInstanceShift;
366     target = (target & selEvtTargetMask) >> selEvtTargetShift;
367 
368     /* make sure the status is 0, 1, or 2 as per the spec */
369     if (status > 2)
370     {
371         return ipmi::response(ipmi::ccInvalidFieldRequest);
372     }
373     /* make sure the target is 0, 1, 2, or 4 as per the spec */
374     if (target > 4 || target == 3)
375     {
376         return ipmi::response(ipmi::ccInvalidFieldRequest);
377     }
378     /*orignal OEM command is to record OEM SEL.
379     But openbmc does not support OEM SEL, so we redirect it to redfish event
380     logging. */
381     std::string buildInfo;
382     std::string action;
383     switch (FWUpdateTarget(target))
384     {
385         case FWUpdateTarget::targetBMC:
386             firmware = "BMC";
387             buildInfo = "major: " + std::to_string(majorRevision) + " minor: " +
388                         std::to_string(bcdToDec(minorRevision)) + // BCD encoded
389                         " BuildID: " + std::to_string(auxInfo);
390             buildInfo += std::to_string(auxInfo);
391             break;
392         case FWUpdateTarget::targetBIOS:
393             firmware = "BIOS";
394             buildInfo =
395                 "major: " +
396                 std::to_string(bcdToDec(majorRevision)) + // BCD encoded
397                 " minor: " +
398                 std::to_string(bcdToDec(minorRevision)) + // BCD encoded
399                 " ReleaseNumber: " +                      // ASCII encoded
400                 std::to_string(static_cast<uint8_t>(auxInfo >> 0) - '0') +
401                 std::to_string(static_cast<uint8_t>(auxInfo >> 8) - '0') +
402                 std::to_string(static_cast<uint8_t>(auxInfo >> 16) - '0') +
403                 std::to_string(static_cast<uint8_t>(auxInfo >> 24) - '0');
404             break;
405         case FWUpdateTarget::targetME:
406             firmware = "ME";
407             buildInfo =
408                 "major: " + std::to_string(majorRevision) + " minor1: " +
409                 std::to_string(bcdToDec(minorRevision)) + // BCD encoded
410                 " minor2: " +
411                 std::to_string(bcdToDec(static_cast<uint8_t>(auxInfo >> 0))) +
412                 " build1: " +
413                 std::to_string(bcdToDec(static_cast<uint8_t>(auxInfo >> 8))) +
414                 " build2: " +
415                 std::to_string(bcdToDec(static_cast<uint8_t>(auxInfo >> 16)));
416             break;
417         case FWUpdateTarget::targetOEMEWS:
418             firmware = "EWS";
419             buildInfo = "major: " + std::to_string(majorRevision) + " minor: " +
420                         std::to_string(bcdToDec(minorRevision)) + // BCD encoded
421                         " BuildID: " + std::to_string(auxInfo);
422             break;
423     }
424 
425     static const std::string openBMCMessageRegistryVersion("0.1");
426     std::string redfishMsgID = "OpenBMC." + openBMCMessageRegistryVersion;
427 
428     switch (status)
429     {
430         case 0x0:
431             action = "update started";
432             redfishMsgID += ".FirmwareUpdateStarted";
433             break;
434         case 0x1:
435             action = "update completed successfully";
436             redfishMsgID += ".FirmwareUpdateCompleted";
437             break;
438         case 0x2:
439             action = "update failure";
440             redfishMsgID += ".FirmwareUpdateFailed";
441             break;
442         default:
443             action = "unknown";
444             break;
445     }
446 
447     std::string firmwareInstanceStr =
448         firmware + " instance: " + std::to_string(instance);
449     std::string message("[firmware update] " + firmwareInstanceStr +
450                         " status: <" + action + "> " + buildInfo);
451 
452     sd_journal_send("MESSAGE=%s", message.c_str(), "PRIORITY=%i", LOG_INFO,
453                     "REDFISH_MESSAGE_ID=%s", redfishMsgID.c_str(),
454                     "REDFISH_MESSAGE_ARGS=%s,%s", firmwareInstanceStr.c_str(),
455                     buildInfo.c_str(), NULL);
456     return ipmi::responseSuccess();
457 }
458 
459 ipmi_ret_t ipmiOEMSetPowerRestoreDelay(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
460                                        ipmi_request_t request,
461                                        ipmi_response_t response,
462                                        ipmi_data_len_t dataLen,
463                                        ipmi_context_t context)
464 {
465     SetPowerRestoreDelayReq* data =
466         reinterpret_cast<SetPowerRestoreDelayReq*>(request);
467     uint16_t delay = 0;
468 
469     if (*dataLen != sizeof(SetPowerRestoreDelayReq))
470     {
471         *dataLen = 0;
472         return IPMI_CC_REQ_DATA_LEN_INVALID;
473     }
474     delay = data->byteMSB;
475     delay = (delay << 8) | data->byteLSB;
476     std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
477     std::string service =
478         getService(*dbus, powerRestoreDelayIntf, powerRestoreDelayObjPath);
479     setDbusProperty(*dbus, service, powerRestoreDelayObjPath,
480                     powerRestoreDelayIntf, powerRestoreDelayProp, delay);
481     *dataLen = 0;
482 
483     return IPMI_CC_OK;
484 }
485 
486 static bool cpuPresent(const std::string& cpuName)
487 {
488     static constexpr const char* cpuPresencePathPrefix =
489         "/xyz/openbmc_project/inventory/system/chassis/motherboard/";
490     static constexpr const char* cpuPresenceIntf =
491         "xyz.openbmc_project.Inventory.Item";
492     std::string cpuPresencePath = cpuPresencePathPrefix + cpuName;
493     std::shared_ptr<sdbusplus::asio::connection> busp = getSdBus();
494     try
495     {
496         auto service =
497             ipmi::getService(*busp, cpuPresenceIntf, cpuPresencePath);
498 
499         ipmi::Value result = ipmi::getDbusProperty(
500             *busp, service, cpuPresencePath, cpuPresenceIntf, "Present");
501         return std::get<bool>(result);
502     }
503     catch (const std::exception& e)
504     {
505         phosphor::logging::log<phosphor::logging::level::INFO>(
506             "Cannot find processor presence",
507             phosphor::logging::entry("NAME=%s", cpuName.c_str()));
508         return false;
509     }
510 }
511 
512 ipmi::RspType<bool,    // CATERR Reset Enabled
513               bool,    // ERR2 Reset Enabled
514               uint6_t, // reserved
515               uint8_t, // reserved, returns 0x3F
516               uint6_t, // CPU1 CATERR Count
517               uint2_t, // CPU1 Status
518               uint6_t, // CPU2 CATERR Count
519               uint2_t, // CPU2 Status
520               uint6_t, // CPU3 CATERR Count
521               uint2_t, // CPU3 Status
522               uint6_t, // CPU4 CATERR Count
523               uint2_t, // CPU4 Status
524               uint8_t  // Crashdump Count
525               >
526     ipmiOEMGetProcessorErrConfig()
527 {
528     bool resetOnCATERR = false;
529     bool resetOnERR2 = false;
530     uint6_t cpu1CATERRCount = 0;
531     uint6_t cpu2CATERRCount = 0;
532     uint6_t cpu3CATERRCount = 0;
533     uint6_t cpu4CATERRCount = 0;
534     uint8_t crashdumpCount = 0;
535     uint2_t cpu1Status =
536         cpuPresent("CPU_1") ? CPUStatus::enabled : CPUStatus::notPresent;
537     uint2_t cpu2Status =
538         cpuPresent("CPU_2") ? CPUStatus::enabled : CPUStatus::notPresent;
539     uint2_t cpu3Status =
540         cpuPresent("CPU_3") ? CPUStatus::enabled : CPUStatus::notPresent;
541     uint2_t cpu4Status =
542         cpuPresent("CPU_4") ? CPUStatus::enabled : CPUStatus::notPresent;
543 
544     std::shared_ptr<sdbusplus::asio::connection> busp = getSdBus();
545     try
546     {
547         auto service = ipmi::getService(*busp, processorErrConfigIntf,
548                                         processorErrConfigObjPath);
549 
550         ipmi::PropertyMap result = ipmi::getAllDbusProperties(
551             *busp, service, processorErrConfigObjPath, processorErrConfigIntf);
552         resetOnCATERR = std::get<bool>(result.at("ResetOnCATERR"));
553         resetOnERR2 = std::get<bool>(result.at("ResetOnERR2"));
554         cpu1CATERRCount = std::get<uint8_t>(result.at("ErrorCountCPU1"));
555         cpu2CATERRCount = std::get<uint8_t>(result.at("ErrorCountCPU2"));
556         cpu3CATERRCount = std::get<uint8_t>(result.at("ErrorCountCPU3"));
557         cpu4CATERRCount = std::get<uint8_t>(result.at("ErrorCountCPU4"));
558         crashdumpCount = std::get<uint8_t>(result.at("CrashdumpCount"));
559     }
560     catch (const std::exception& e)
561     {
562         phosphor::logging::log<phosphor::logging::level::ERR>(
563             "Failed to fetch processor error config",
564             phosphor::logging::entry("ERROR=%s", e.what()));
565         return ipmi::responseUnspecifiedError();
566     }
567 
568     return ipmi::responseSuccess(resetOnCATERR, resetOnERR2, 0, 0x3F,
569                                  cpu1CATERRCount, cpu1Status, cpu2CATERRCount,
570                                  cpu2Status, cpu3CATERRCount, cpu3Status,
571                                  cpu4CATERRCount, cpu4Status, crashdumpCount);
572 }
573 
574 ipmi::RspType<> ipmiOEMSetProcessorErrConfig(
575     bool resetOnCATERR, bool resetOnERR2, uint6_t reserved1, uint8_t reserved2,
576     std::optional<bool> clearCPUErrorCount,
577     std::optional<bool> clearCrashdumpCount, std::optional<uint6_t> reserved3)
578 {
579     std::shared_ptr<sdbusplus::asio::connection> busp = getSdBus();
580 
581     try
582     {
583         auto service = ipmi::getService(*busp, processorErrConfigIntf,
584                                         processorErrConfigObjPath);
585         ipmi::setDbusProperty(*busp, service, processorErrConfigObjPath,
586                               processorErrConfigIntf, "ResetOnCATERR",
587                               resetOnCATERR);
588         ipmi::setDbusProperty(*busp, service, processorErrConfigObjPath,
589                               processorErrConfigIntf, "ResetOnERR2",
590                               resetOnERR2);
591         if (clearCPUErrorCount.value_or(false))
592         {
593             ipmi::setDbusProperty(*busp, service, processorErrConfigObjPath,
594                                   processorErrConfigIntf, "ErrorCountCPU1", 0);
595             ipmi::setDbusProperty(*busp, service, processorErrConfigObjPath,
596                                   processorErrConfigIntf, "ErrorCountCPU2", 0);
597         }
598         if (clearCrashdumpCount.value_or(false))
599         {
600             ipmi::setDbusProperty(*busp, service, processorErrConfigObjPath,
601                                   processorErrConfigIntf, "CrashdumpCount", 0);
602         }
603     }
604     catch (std::exception& e)
605     {
606         phosphor::logging::log<phosphor::logging::level::ERR>(
607             "Failed to set processor error config",
608             phosphor::logging::entry("EXCEPTION=%s", e.what()));
609         return ipmi::responseUnspecifiedError();
610     }
611 
612     return ipmi::responseSuccess();
613 }
614 
615 ipmi_ret_t ipmiOEMGetShutdownPolicy(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
616                                     ipmi_request_t request,
617                                     ipmi_response_t response,
618                                     ipmi_data_len_t dataLen,
619                                     ipmi_context_t context)
620 {
621     GetOEMShutdownPolicyRes* resp =
622         reinterpret_cast<GetOEMShutdownPolicyRes*>(response);
623 
624     if (*dataLen != 0)
625     {
626         phosphor::logging::log<phosphor::logging::level::ERR>(
627             "oem_get_shutdown_policy: invalid input len!");
628         *dataLen = 0;
629         return IPMI_CC_REQ_DATA_LEN_INVALID;
630     }
631 
632     *dataLen = 0;
633 
634     try
635     {
636         std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
637         std::string service =
638             getService(*dbus, oemShutdownPolicyIntf, oemShutdownPolicyObjPath);
639         Value variant = getDbusProperty(
640             *dbus, service, oemShutdownPolicyObjPath, oemShutdownPolicyIntf,
641             oemShutdownPolicyObjPathProp);
642 
643         if (sdbusplus::com::intel::Control::server::OCOTShutdownPolicy::
644                 convertPolicyFromString(std::get<std::string>(variant)) ==
645             sdbusplus::com::intel::Control::server::OCOTShutdownPolicy::Policy::
646                 NoShutdownOnOCOT)
647         {
648             resp->policy = 0;
649         }
650         else if (sdbusplus::com::intel::Control::server::OCOTShutdownPolicy::
651                      convertPolicyFromString(std::get<std::string>(variant)) ==
652                  sdbusplus::com::intel::Control::server::OCOTShutdownPolicy::
653                      Policy::ShutdownOnOCOT)
654         {
655             resp->policy = 1;
656         }
657         else
658         {
659             phosphor::logging::log<phosphor::logging::level::ERR>(
660                 "oem_set_shutdown_policy: invalid property!",
661                 phosphor::logging::entry(
662                     "PROP=%s", std::get<std::string>(variant).c_str()));
663             return IPMI_CC_UNSPECIFIED_ERROR;
664         }
665         // TODO needs to check if it is multi-node products,
666         // policy is only supported on node 3/4
667         resp->policySupport = shutdownPolicySupported;
668     }
669     catch (sdbusplus::exception_t& e)
670     {
671         phosphor::logging::log<phosphor::logging::level::ERR>(e.description());
672         return IPMI_CC_UNSPECIFIED_ERROR;
673     }
674 
675     *dataLen = sizeof(GetOEMShutdownPolicyRes);
676     return IPMI_CC_OK;
677 }
678 
679 ipmi_ret_t ipmiOEMSetShutdownPolicy(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
680                                     ipmi_request_t request,
681                                     ipmi_response_t response,
682                                     ipmi_data_len_t dataLen,
683                                     ipmi_context_t context)
684 {
685     uint8_t* req = reinterpret_cast<uint8_t*>(request);
686     sdbusplus::com::intel::Control::server::OCOTShutdownPolicy::Policy policy =
687         sdbusplus::com::intel::Control::server::OCOTShutdownPolicy::Policy::
688             NoShutdownOnOCOT;
689 
690     // TODO needs to check if it is multi-node products,
691     // policy is only supported on node 3/4
692     if (*dataLen != 1)
693     {
694         phosphor::logging::log<phosphor::logging::level::ERR>(
695             "oem_set_shutdown_policy: invalid input len!");
696         *dataLen = 0;
697         return IPMI_CC_REQ_DATA_LEN_INVALID;
698     }
699 
700     *dataLen = 0;
701     if ((*req != noShutdownOnOCOT) && (*req != shutdownOnOCOT))
702     {
703         phosphor::logging::log<phosphor::logging::level::ERR>(
704             "oem_set_shutdown_policy: invalid input!");
705         return IPMI_CC_INVALID_FIELD_REQUEST;
706     }
707 
708     if (*req == noShutdownOnOCOT)
709     {
710         policy = sdbusplus::com::intel::Control::server::OCOTShutdownPolicy::
711             Policy::NoShutdownOnOCOT;
712     }
713     else
714     {
715         policy = sdbusplus::com::intel::Control::server::OCOTShutdownPolicy::
716             Policy::ShutdownOnOCOT;
717     }
718 
719     try
720     {
721         std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
722         std::string service =
723             getService(*dbus, oemShutdownPolicyIntf, oemShutdownPolicyObjPath);
724         setDbusProperty(
725             *dbus, service, oemShutdownPolicyObjPath, oemShutdownPolicyIntf,
726             oemShutdownPolicyObjPathProp,
727             sdbusplus::com::intel::Control::server::convertForMessage(policy));
728     }
729     catch (sdbusplus::exception_t& e)
730     {
731         phosphor::logging::log<phosphor::logging::level::ERR>(e.description());
732         return IPMI_CC_UNSPECIFIED_ERROR;
733     }
734 
735     return IPMI_CC_OK;
736 }
737 
738 /** @brief implementation for check the DHCP or not in IPv4
739  *  @param[in] Channel - Channel number
740  *  @returns true or false.
741  */
742 static bool isDHCPEnabled(uint8_t Channel)
743 {
744     try
745     {
746         auto ethdevice = getChannelName(Channel);
747         if (ethdevice.empty())
748         {
749             return false;
750         }
751         auto ethIP = ethdevice + "/ipv4";
752         std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
753         auto ethernetObj =
754             getDbusObject(*dbus, networkIPIntf, networkRoot, ethIP);
755         auto value = getDbusProperty(*dbus, networkService, ethernetObj.first,
756                                      networkIPIntf, "Origin");
757         if (std::get<std::string>(value) ==
758             "xyz.openbmc_project.Network.IP.AddressOrigin.DHCP")
759         {
760             return true;
761         }
762         else
763         {
764             return false;
765         }
766     }
767     catch (sdbusplus::exception_t& e)
768     {
769         phosphor::logging::log<phosphor::logging::level::ERR>(e.description());
770         return true;
771     }
772 }
773 
774 /** @brief implementes for check the DHCP or not in IPv6
775  *  @param[in] Channel - Channel number
776  *  @returns true or false.
777  */
778 static bool isDHCPIPv6Enabled(uint8_t Channel)
779 {
780 
781     try
782     {
783         auto ethdevice = getChannelName(Channel);
784         if (ethdevice.empty())
785         {
786             return false;
787         }
788         auto ethIP = ethdevice + "/ipv6";
789         std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
790         auto objectInfo =
791             getDbusObject(*dbus, networkIPIntf, networkRoot, ethIP);
792         auto properties = getAllDbusProperties(*dbus, objectInfo.second,
793                                                objectInfo.first, networkIPIntf);
794         if (std::get<std::string>(properties["Origin"]) ==
795             "xyz.openbmc_project.Network.IP.AddressOrigin.DHCP")
796         {
797             return true;
798         }
799         else
800         {
801             return false;
802         }
803     }
804     catch (sdbusplus::exception_t& e)
805     {
806         phosphor::logging::log<phosphor::logging::level::ERR>(e.description());
807         return true;
808     }
809 }
810 
811 /** @brief implementes the creating of default new user
812  *  @param[in] userName - new username in 16 bytes.
813  *  @param[in] userPassword - new password in 20 bytes
814  *  @returns ipmi completion code.
815  */
816 ipmi::RspType<> ipmiOEMSetUser2Activation(
817     std::array<uint8_t, ipmi::ipmiMaxUserName>& userName,
818     std::array<uint8_t, ipmi::maxIpmi20PasswordSize>& userPassword)
819 {
820     bool userState = false;
821     // Check for System Interface not exist and LAN should be static
822     for (uint8_t channel = 0; channel < maxIpmiChannels; channel++)
823     {
824         ChannelInfo chInfo;
825         try
826         {
827             getChannelInfo(channel, chInfo);
828         }
829         catch (sdbusplus::exception_t& e)
830         {
831             phosphor::logging::log<phosphor::logging::level::ERR>(
832                 "ipmiOEMSetUser2Activation: Failed to get Channel Info",
833                 phosphor::logging::entry("MSG: %s", e.description()));
834             return ipmi::response(ipmi::ccUnspecifiedError);
835         }
836         if (chInfo.mediumType ==
837             static_cast<uint8_t>(EChannelMediumType::systemInterface))
838         {
839             phosphor::logging::log<phosphor::logging::level::ERR>(
840                 "ipmiOEMSetUser2Activation: system interface  exist .");
841             return ipmi::response(ipmi::ccCommandNotAvailable);
842         }
843         else
844         {
845 
846             if (chInfo.mediumType ==
847                 static_cast<uint8_t>(EChannelMediumType::lan8032))
848             {
849                 if (isDHCPIPv6Enabled(channel) || isDHCPEnabled(channel))
850                 {
851                     phosphor::logging::log<phosphor::logging::level::ERR>(
852                         "ipmiOEMSetUser2Activation: DHCP enabled .");
853                     return ipmi::response(ipmi::ccCommandNotAvailable);
854                 }
855             }
856         }
857     }
858     uint8_t maxChUsers = 0, enabledUsers = 0, fixedUsers = 0;
859     if (ipmi::ccSuccess ==
860         ipmiUserGetAllCounts(maxChUsers, enabledUsers, fixedUsers))
861     {
862         if (enabledUsers > 1)
863         {
864             phosphor::logging::log<phosphor::logging::level::ERR>(
865                 "ipmiOEMSetUser2Activation: more than one user is enabled.");
866             return ipmi::response(ipmi::ccCommandNotAvailable);
867         }
868         // Check the user 2 is enabled or not
869         ipmiUserCheckEnabled(ipmiDefaultUserId, userState);
870         if (userState == true)
871         {
872             phosphor::logging::log<phosphor::logging::level::ERR>(
873                 "ipmiOEMSetUser2Activation: user 2 already enabled .");
874             return ipmi::response(ipmi::ccCommandNotAvailable);
875         }
876     }
877     else
878     {
879         return ipmi::response(ipmi::ccUnspecifiedError);
880     }
881 
882 #if BYTE_ORDER == LITTLE_ENDIAN
883     PrivAccess privAccess = {PRIVILEGE_ADMIN, true, true, true, 0};
884 #endif
885 #if BYTE_ORDER == BIG_ENDIAN
886     PrivAccess privAccess = {0, true, true, true, PRIVILEGE_ADMIN};
887 #endif
888 
889     if (ipmi::ccSuccess ==
890         ipmiUserSetUserName(ipmiDefaultUserId,
891                             reinterpret_cast<const char*>(userName.data())))
892     {
893         if (ipmi::ccSuccess ==
894             ipmiUserSetUserPassword(
895                 ipmiDefaultUserId,
896                 reinterpret_cast<const char*>(userPassword.data())))
897         {
898             if (ipmi::ccSuccess ==
899                 ipmiUserSetPrivilegeAccess(
900                     ipmiDefaultUserId,
901                     static_cast<uint8_t>(ipmi::EChannelID::chanLan1),
902                     privAccess, true))
903             {
904                 phosphor::logging::log<phosphor::logging::level::INFO>(
905                     "ipmiOEMSetUser2Activation: user created successfully ");
906                 return ipmi::responseSuccess();
907             }
908         }
909         // we need to delete  the default user id which added in this command as
910         // password / priv setting is failed.
911         ipmiUserSetUserName(ipmiDefaultUserId, "");
912         phosphor::logging::log<phosphor::logging::level::ERR>(
913             "ipmiOEMSetUser2Activation: password / priv setting is failed.");
914     }
915     else
916     {
917         phosphor::logging::log<phosphor::logging::level::ERR>(
918             "ipmiOEMSetUser2Activation: Setting username failed.");
919     }
920 
921     return ipmi::response(ipmi::ccCommandNotAvailable);
922 }
923 
924 /** @brief implementes setting password for special user
925  *  @param[in] specialUserIndex
926  *  @param[in] userPassword - new password in 20 bytes
927  *  @returns ipmi completion code.
928  */
929 ipmi::RspType<> ipmiOEMSetSpecialUserPassword(ipmi::Context::ptr ctx,
930                                               uint8_t specialUserIndex,
931                                               std::vector<uint8_t> userPassword)
932 {
933     ChannelInfo chInfo;
934     try
935     {
936         getChannelInfo(ctx->channel, chInfo);
937     }
938     catch (sdbusplus::exception_t& e)
939     {
940         phosphor::logging::log<phosphor::logging::level::ERR>(
941             "ipmiOEMSetSpecialUserPassword: Failed to get Channel Info",
942             phosphor::logging::entry("MSG: %s", e.description()));
943         return ipmi::responseUnspecifiedError();
944     }
945     if (chInfo.mediumType !=
946         static_cast<uint8_t>(EChannelMediumType::systemInterface))
947     {
948         phosphor::logging::log<phosphor::logging::level::ERR>(
949             "ipmiOEMSetSpecialUserPassword: Error - supported only in KCS "
950             "interface");
951         return ipmi::responseCommandNotAvailable();
952     }
953     if (specialUserIndex != 0)
954     {
955         phosphor::logging::log<phosphor::logging::level::ERR>(
956             "ipmiOEMSetSpecialUserPassword: Invalid user account");
957         return ipmi::responseParmOutOfRange();
958     }
959     constexpr uint8_t minPasswordSizeRequired = 6;
960     if (userPassword.size() < minPasswordSizeRequired ||
961         userPassword.size() > ipmi::maxIpmi20PasswordSize)
962     {
963         return ipmi::responseReqDataLenInvalid();
964     }
965     std::string passwd;
966     passwd.assign(reinterpret_cast<const char*>(userPassword.data()),
967                   userPassword.size());
968     return ipmi::response(ipmiSetSpecialUserPassword("root", passwd));
969 }
970 
971 namespace ledAction
972 {
973 using namespace sdbusplus::xyz::openbmc_project::Led::server;
974 std::map<Physical::Action, uint8_t> actionDbusToIpmi = {
975     {Physical::Action::Off, 0x00},
976     {Physical::Action::On, 0x10},
977     {Physical::Action::Blink, 0x01}};
978 
979 std::map<uint8_t, std::string> offsetObjPath = {
980     {2, statusAmberObjPath}, {4, statusGreenObjPath}, {6, identifyLEDObjPath}};
981 
982 } // namespace ledAction
983 
984 int8_t getLEDState(sdbusplus::bus::bus& bus, const std::string& intf,
985                    const std::string& objPath, uint8_t& state)
986 {
987     try
988     {
989         std::string service = getService(bus, intf, objPath);
990         Value stateValue =
991             getDbusProperty(bus, service, objPath, intf, "State");
992         std::string strState = std::get<std::string>(stateValue);
993         state = ledAction::actionDbusToIpmi.at(
994             sdbusplus::xyz::openbmc_project::Led::server::Physical::
995                 convertActionFromString(strState));
996     }
997     catch (sdbusplus::exception::SdBusError& e)
998     {
999         phosphor::logging::log<phosphor::logging::level::ERR>(e.what());
1000         return -1;
1001     }
1002     return 0;
1003 }
1004 
1005 ipmi_ret_t ipmiOEMGetLEDStatus(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
1006                                ipmi_request_t request, ipmi_response_t response,
1007                                ipmi_data_len_t dataLen, ipmi_context_t context)
1008 {
1009     uint8_t* resp = reinterpret_cast<uint8_t*>(response);
1010     // LED Status
1011     //[1:0] = Reserved
1012     //[3:2] = Status(Amber)
1013     //[5:4] = Status(Green)
1014     //[7:6] = System Identify
1015     // Status definitions:
1016     // 00b = Off
1017     // 01b = Blink
1018     // 10b = On
1019     // 11b = invalid
1020     if (*dataLen != 0)
1021     {
1022         phosphor::logging::log<phosphor::logging::level::ERR>(
1023             "oem_get_led_status: invalid input len!");
1024         *dataLen = 0;
1025         return IPMI_CC_REQ_DATA_LEN_INVALID;
1026     }
1027 
1028     phosphor::logging::log<phosphor::logging::level::DEBUG>("GET led status");
1029     *resp = 0;
1030     *dataLen = 0;
1031     std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
1032     for (auto it = ledAction::offsetObjPath.begin();
1033          it != ledAction::offsetObjPath.end(); ++it)
1034     {
1035         uint8_t state = 0;
1036         if (-1 == getLEDState(*dbus, ledIntf, it->second, state))
1037         {
1038             phosphor::logging::log<phosphor::logging::level::ERR>(
1039                 "oem_get_led_status: fail to get ID LED status!");
1040             return IPMI_CC_UNSPECIFIED_ERROR;
1041         }
1042         *resp |= state << it->first;
1043     }
1044 
1045     *dataLen = sizeof(*resp);
1046     return IPMI_CC_OK;
1047 }
1048 
1049 ipmi_ret_t ipmiOEMCfgHostSerialPortSpeed(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
1050                                          ipmi_request_t request,
1051                                          ipmi_response_t response,
1052                                          ipmi_data_len_t dataLen,
1053                                          ipmi_context_t context)
1054 {
1055     CfgHostSerialReq* req = reinterpret_cast<CfgHostSerialReq*>(request);
1056     uint8_t* resp = reinterpret_cast<uint8_t*>(response);
1057 
1058     if (*dataLen == 0)
1059     {
1060         phosphor::logging::log<phosphor::logging::level::ERR>(
1061             "CfgHostSerial: invalid input len!",
1062             phosphor::logging::entry("LEN=%d", *dataLen));
1063         return IPMI_CC_REQ_DATA_LEN_INVALID;
1064     }
1065 
1066     switch (req->command)
1067     {
1068         case getHostSerialCfgCmd:
1069         {
1070             if (*dataLen != 1)
1071             {
1072                 phosphor::logging::log<phosphor::logging::level::ERR>(
1073                     "CfgHostSerial: invalid input len!");
1074                 *dataLen = 0;
1075                 return IPMI_CC_REQ_DATA_LEN_INVALID;
1076             }
1077 
1078             *dataLen = 0;
1079 
1080             boost::process::ipstream is;
1081             std::vector<std::string> data;
1082             std::string line;
1083             boost::process::child c1(fwGetEnvCmd, "-n", fwHostSerailCfgEnvName,
1084                                      boost::process::std_out > is);
1085 
1086             while (c1.running() && std::getline(is, line) && !line.empty())
1087             {
1088                 data.push_back(line);
1089             }
1090 
1091             c1.wait();
1092             if (c1.exit_code())
1093             {
1094                 phosphor::logging::log<phosphor::logging::level::ERR>(
1095                     "CfgHostSerial:: error on execute",
1096                     phosphor::logging::entry("EXECUTE=%s", fwSetEnvCmd));
1097                 // Using the default value
1098                 *resp = 0;
1099             }
1100             else
1101             {
1102                 if (data.size() != 1)
1103                 {
1104                     phosphor::logging::log<phosphor::logging::level::ERR>(
1105                         "CfgHostSerial:: error on read env");
1106                     return IPMI_CC_UNSPECIFIED_ERROR;
1107                 }
1108                 try
1109                 {
1110                     unsigned long tmp = std::stoul(data[0]);
1111                     if (tmp > std::numeric_limits<uint8_t>::max())
1112                     {
1113                         throw std::out_of_range("Out of range");
1114                     }
1115                     *resp = static_cast<uint8_t>(tmp);
1116                 }
1117                 catch (const std::invalid_argument& e)
1118                 {
1119                     phosphor::logging::log<phosphor::logging::level::ERR>(
1120                         "invalid config ",
1121                         phosphor::logging::entry("ERR=%s", e.what()));
1122                     return IPMI_CC_UNSPECIFIED_ERROR;
1123                 }
1124                 catch (const std::out_of_range& e)
1125                 {
1126                     phosphor::logging::log<phosphor::logging::level::ERR>(
1127                         "out_of_range config ",
1128                         phosphor::logging::entry("ERR=%s", e.what()));
1129                     return IPMI_CC_UNSPECIFIED_ERROR;
1130                 }
1131             }
1132 
1133             *dataLen = 1;
1134             break;
1135         }
1136         case setHostSerialCfgCmd:
1137         {
1138             if (*dataLen != sizeof(CfgHostSerialReq))
1139             {
1140                 phosphor::logging::log<phosphor::logging::level::ERR>(
1141                     "CfgHostSerial: invalid input len!");
1142                 *dataLen = 0;
1143                 return IPMI_CC_REQ_DATA_LEN_INVALID;
1144             }
1145 
1146             *dataLen = 0;
1147 
1148             if (req->parameter > HostSerialCfgParamMax)
1149             {
1150                 phosphor::logging::log<phosphor::logging::level::ERR>(
1151                     "CfgHostSerial: invalid input!");
1152                 return IPMI_CC_INVALID_FIELD_REQUEST;
1153             }
1154 
1155             boost::process::child c1(fwSetEnvCmd, fwHostSerailCfgEnvName,
1156                                      std::to_string(req->parameter));
1157 
1158             c1.wait();
1159             if (c1.exit_code())
1160             {
1161                 phosphor::logging::log<phosphor::logging::level::ERR>(
1162                     "CfgHostSerial:: error on execute",
1163                     phosphor::logging::entry("EXECUTE=%s", fwGetEnvCmd));
1164                 return IPMI_CC_UNSPECIFIED_ERROR;
1165             }
1166             break;
1167         }
1168         default:
1169             phosphor::logging::log<phosphor::logging::level::ERR>(
1170                 "CfgHostSerial: invalid input!");
1171             *dataLen = 0;
1172             return IPMI_CC_INVALID_FIELD_REQUEST;
1173     }
1174 
1175     return IPMI_CC_OK;
1176 }
1177 
1178 constexpr const char* thermalModeInterface =
1179     "xyz.openbmc_project.Control.ThermalMode";
1180 constexpr const char* thermalModePath =
1181     "/xyz/openbmc_project/control/thermal_mode";
1182 
1183 bool getFanProfileInterface(
1184     sdbusplus::bus::bus& bus,
1185     boost::container::flat_map<
1186         std::string, std::variant<std::vector<std::string>, std::string>>& resp)
1187 {
1188     auto call = bus.new_method_call(settingsBusName, thermalModePath, PROP_INTF,
1189                                     "GetAll");
1190     call.append(thermalModeInterface);
1191     try
1192     {
1193         auto data = bus.call(call);
1194         data.read(resp);
1195     }
1196     catch (sdbusplus::exception_t& e)
1197     {
1198         phosphor::logging::log<phosphor::logging::level::ERR>(
1199             "getFanProfileInterface: can't get thermal mode!",
1200             phosphor::logging::entry("ERR=%s", e.what()));
1201         return false;
1202     }
1203     return true;
1204 }
1205 
1206 ipmi_ret_t ipmiOEMSetFanConfig(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
1207                                ipmi_request_t request, ipmi_response_t response,
1208                                ipmi_data_len_t dataLen, ipmi_context_t context)
1209 {
1210 
1211     if (*dataLen < 2 || *dataLen > 7)
1212     {
1213         phosphor::logging::log<phosphor::logging::level::ERR>(
1214             "ipmiOEMSetFanConfig: invalid input len!");
1215         *dataLen = 0;
1216         return IPMI_CC_REQ_DATA_LEN_INVALID;
1217     }
1218 
1219     // todo: tell bios to only send first 2 bytes
1220 
1221     SetFanConfigReq* req = reinterpret_cast<SetFanConfigReq*>(request);
1222     boost::container::flat_map<
1223         std::string, std::variant<std::vector<std::string>, std::string>>
1224         profileData;
1225     std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
1226     if (!getFanProfileInterface(*dbus, profileData))
1227     {
1228         return IPMI_CC_UNSPECIFIED_ERROR;
1229     }
1230 
1231     std::vector<std::string>* supported =
1232         std::get_if<std::vector<std::string>>(&profileData["Supported"]);
1233     if (supported == nullptr)
1234     {
1235         return IPMI_CC_INVALID_FIELD_REQUEST;
1236     }
1237     std::string mode;
1238     if (req->flags &
1239         (1 << static_cast<uint8_t>(setFanProfileFlags::setPerfAcousMode)))
1240     {
1241         bool performanceMode =
1242             (req->flags & (1 << static_cast<uint8_t>(
1243                                setFanProfileFlags::performAcousSelect))) > 0;
1244 
1245         if (performanceMode)
1246         {
1247 
1248             if (std::find(supported->begin(), supported->end(),
1249                           "Performance") != supported->end())
1250             {
1251                 mode = "Performance";
1252             }
1253         }
1254         else
1255         {
1256 
1257             if (std::find(supported->begin(), supported->end(), "Acoustic") !=
1258                 supported->end())
1259             {
1260                 mode = "Acoustic";
1261             }
1262         }
1263         if (mode.empty())
1264         {
1265             return IPMI_CC_INVALID_FIELD_REQUEST;
1266         }
1267         setDbusProperty(*dbus, settingsBusName, thermalModePath,
1268                         thermalModeInterface, "Current", mode);
1269     }
1270 
1271     return IPMI_CC_OK;
1272 }
1273 
1274 ipmi::RspType<uint8_t, // profile support map
1275               uint8_t, // fan control profile enable
1276               uint8_t, // flags
1277               uint32_t // dimm presence bit map
1278               >
1279     ipmiOEMGetFanConfig(uint8_t dimmGroupId)
1280 {
1281     boost::container::flat_map<
1282         std::string, std::variant<std::vector<std::string>, std::string>>
1283         profileData;
1284 
1285     std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
1286     if (!getFanProfileInterface(*dbus, profileData))
1287     {
1288         return ipmi::responseResponseError();
1289     }
1290 
1291     std::string* current = std::get_if<std::string>(&profileData["Current"]);
1292 
1293     if (current == nullptr)
1294     {
1295         phosphor::logging::log<phosphor::logging::level::ERR>(
1296             "ipmiOEMGetFanConfig: can't get current mode!");
1297         return ipmi::responseResponseError();
1298     }
1299     bool performance = (*current == "Performance");
1300 
1301     uint8_t flags = 0;
1302     if (performance)
1303     {
1304         flags |= 1 << 2;
1305     }
1306 
1307     return ipmi::responseSuccess(0, 0, flags, 0);
1308 }
1309 constexpr const char* cfmLimitSettingPath =
1310     "/xyz/openbmc_project/control/cfm_limit";
1311 constexpr const char* cfmLimitIface = "xyz.openbmc_project.Control.CFMLimit";
1312 constexpr const size_t legacyExitAirSensorNumber = 0x2e;
1313 constexpr const size_t legacyPCHSensorNumber = 0x22;
1314 constexpr const char* exitAirPathName = "Exit_Air";
1315 constexpr const char* pchPathName = "SSB_Temp";
1316 constexpr const char* pidConfigurationIface =
1317     "xyz.openbmc_project.Configuration.Pid";
1318 
1319 static std::string getConfigPath(const std::string& name)
1320 {
1321     std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
1322     auto method =
1323         dbus->new_method_call("xyz.openbmc_project.ObjectMapper",
1324                               "/xyz/openbmc_project/object_mapper",
1325                               "xyz.openbmc_project.ObjectMapper", "GetSubTree");
1326 
1327     method.append("/", 0, std::array<const char*, 1>{pidConfigurationIface});
1328     std::string path;
1329     GetSubTreeType resp;
1330     try
1331     {
1332         auto reply = dbus->call(method);
1333         reply.read(resp);
1334     }
1335     catch (sdbusplus::exception_t&)
1336     {
1337         phosphor::logging::log<phosphor::logging::level::ERR>(
1338             "ipmiOEMGetFscParameter: mapper error");
1339     };
1340     auto config =
1341         std::find_if(resp.begin(), resp.end(), [&name](const auto& pair) {
1342             return pair.first.find(name) != std::string::npos;
1343         });
1344     if (config != resp.end())
1345     {
1346         path = std::move(config->first);
1347     }
1348     return path;
1349 }
1350 
1351 // flat map to make alphabetical
1352 static boost::container::flat_map<std::string, PropertyMap> getPidConfigs()
1353 {
1354     boost::container::flat_map<std::string, PropertyMap> ret;
1355     std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
1356     auto method =
1357         dbus->new_method_call("xyz.openbmc_project.ObjectMapper",
1358                               "/xyz/openbmc_project/object_mapper",
1359                               "xyz.openbmc_project.ObjectMapper", "GetSubTree");
1360 
1361     method.append("/", 0, std::array<const char*, 1>{pidConfigurationIface});
1362     GetSubTreeType resp;
1363 
1364     try
1365     {
1366         auto reply = dbus->call(method);
1367         reply.read(resp);
1368     }
1369     catch (sdbusplus::exception_t&)
1370     {
1371         phosphor::logging::log<phosphor::logging::level::ERR>(
1372             "getFanConfigPaths: mapper error");
1373     };
1374     for (const auto& [path, objects] : resp)
1375     {
1376         if (objects.empty())
1377         {
1378             continue; // should be impossible
1379         }
1380 
1381         try
1382         {
1383             ret.emplace(path,
1384                         getAllDbusProperties(*dbus, objects[0].first, path,
1385                                              pidConfigurationIface));
1386         }
1387         catch (sdbusplus::exception_t& e)
1388         {
1389             phosphor::logging::log<phosphor::logging::level::ERR>(
1390                 "getPidConfigs: can't get DbusProperties!",
1391                 phosphor::logging::entry("ERR=%s", e.what()));
1392         }
1393     }
1394     return ret;
1395 }
1396 
1397 ipmi::RspType<uint8_t> ipmiOEMGetFanSpeedOffset(void)
1398 {
1399     boost::container::flat_map<std::string, PropertyMap> data = getPidConfigs();
1400     if (data.empty())
1401     {
1402         return ipmi::responseResponseError();
1403     }
1404     uint8_t minOffset = std::numeric_limits<uint8_t>::max();
1405     for (const auto& [_, pid] : data)
1406     {
1407         auto findClass = pid.find("Class");
1408         if (findClass == pid.end())
1409         {
1410             phosphor::logging::log<phosphor::logging::level::ERR>(
1411                 "ipmiOEMGetFscParameter: found illegal pid "
1412                 "configurations");
1413             return ipmi::responseResponseError();
1414         }
1415         std::string type = std::get<std::string>(findClass->second);
1416         if (type == "fan")
1417         {
1418             auto findOutLimit = pid.find("OutLimitMin");
1419             if (findOutLimit == pid.end())
1420             {
1421                 phosphor::logging::log<phosphor::logging::level::ERR>(
1422                     "ipmiOEMGetFscParameter: found illegal pid "
1423                     "configurations");
1424                 return ipmi::responseResponseError();
1425             }
1426             // get the min out of all the offsets
1427             minOffset = std::min(
1428                 minOffset,
1429                 static_cast<uint8_t>(std::get<double>(findOutLimit->second)));
1430         }
1431     }
1432     if (minOffset == std::numeric_limits<uint8_t>::max())
1433     {
1434         phosphor::logging::log<phosphor::logging::level::ERR>(
1435             "ipmiOEMGetFscParameter: found no fan configurations!");
1436         return ipmi::responseResponseError();
1437     }
1438 
1439     return ipmi::responseSuccess(minOffset);
1440 }
1441 
1442 ipmi::RspType<> ipmiOEMSetFanSpeedOffset(uint8_t offset)
1443 {
1444     boost::container::flat_map<std::string, PropertyMap> data = getPidConfigs();
1445     if (data.empty())
1446     {
1447 
1448         phosphor::logging::log<phosphor::logging::level::ERR>(
1449             "ipmiOEMSetFanSpeedOffset: found no pid configurations!");
1450         return ipmi::responseResponseError();
1451     }
1452 
1453     std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
1454     bool found = false;
1455     for (const auto& [path, pid] : data)
1456     {
1457         auto findClass = pid.find("Class");
1458         if (findClass == pid.end())
1459         {
1460 
1461             phosphor::logging::log<phosphor::logging::level::ERR>(
1462                 "ipmiOEMSetFanSpeedOffset: found illegal pid "
1463                 "configurations");
1464             return ipmi::responseResponseError();
1465         }
1466         std::string type = std::get<std::string>(findClass->second);
1467         if (type == "fan")
1468         {
1469             auto findOutLimit = pid.find("OutLimitMin");
1470             if (findOutLimit == pid.end())
1471             {
1472 
1473                 phosphor::logging::log<phosphor::logging::level::ERR>(
1474                     "ipmiOEMSetFanSpeedOffset: found illegal pid "
1475                     "configurations");
1476                 return ipmi::responseResponseError();
1477             }
1478             ipmi::setDbusProperty(*dbus, "xyz.openbmc_project.EntityManager",
1479                                   path, pidConfigurationIface, "OutLimitMin",
1480                                   static_cast<double>(offset));
1481             found = true;
1482         }
1483     }
1484     if (!found)
1485     {
1486         phosphor::logging::log<phosphor::logging::level::ERR>(
1487             "ipmiOEMSetFanSpeedOffset: set no fan offsets");
1488         return ipmi::responseResponseError();
1489     }
1490 
1491     return ipmi::responseSuccess();
1492 }
1493 
1494 ipmi::RspType<> ipmiOEMSetFscParameter(uint8_t command, uint8_t param1,
1495                                        uint8_t param2)
1496 {
1497     constexpr const size_t disableLimiting = 0x0;
1498 
1499     std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
1500     if (command == static_cast<uint8_t>(setFscParamFlags::tcontrol))
1501     {
1502         std::string pathName;
1503         if (param1 == legacyExitAirSensorNumber)
1504         {
1505             pathName = exitAirPathName;
1506         }
1507         else if (param1 == legacyPCHSensorNumber)
1508         {
1509             pathName = pchPathName;
1510         }
1511         else
1512         {
1513             return ipmi::responseParmOutOfRange();
1514         }
1515         std::string path = getConfigPath(pathName);
1516         ipmi::setDbusProperty(*dbus, "xyz.openbmc_project.EntityManager", path,
1517                               pidConfigurationIface, "SetPoint",
1518                               static_cast<double>(param2));
1519         return ipmi::responseSuccess();
1520     }
1521     else if (command == static_cast<uint8_t>(setFscParamFlags::cfm))
1522     {
1523         uint16_t cfm = param1 | (static_cast<uint16_t>(param2) << 8);
1524 
1525         // must be greater than 50 based on eps
1526         if (cfm < 50 && cfm != disableLimiting)
1527         {
1528             return ipmi::responseParmOutOfRange();
1529         }
1530 
1531         try
1532         {
1533             ipmi::setDbusProperty(*dbus, settingsBusName, cfmLimitSettingPath,
1534                                   cfmLimitIface, "Limit",
1535                                   static_cast<double>(cfm));
1536         }
1537         catch (sdbusplus::exception_t& e)
1538         {
1539             phosphor::logging::log<phosphor::logging::level::ERR>(
1540                 "ipmiOEMSetFscParameter: can't set cfm setting!",
1541                 phosphor::logging::entry("ERR=%s", e.what()));
1542             return ipmi::responseResponseError();
1543         }
1544         return ipmi::responseSuccess();
1545     }
1546     else if (command == static_cast<uint8_t>(setFscParamFlags::maxPwm))
1547     {
1548         constexpr const size_t maxDomainCount = 8;
1549         uint8_t requestedDomainMask = param1;
1550         boost::container::flat_map data = getPidConfigs();
1551         if (data.empty())
1552         {
1553 
1554             phosphor::logging::log<phosphor::logging::level::ERR>(
1555                 "ipmiOEMSetFscParameter: found no pid configurations!");
1556             return ipmi::responseResponseError();
1557         }
1558         size_t count = 0;
1559         for (const auto& [path, pid] : data)
1560         {
1561             auto findClass = pid.find("Class");
1562             if (findClass == pid.end())
1563             {
1564 
1565                 phosphor::logging::log<phosphor::logging::level::ERR>(
1566                     "ipmiOEMSetFscParameter: found illegal pid "
1567                     "configurations");
1568                 return ipmi::responseResponseError();
1569             }
1570             std::string type = std::get<std::string>(findClass->second);
1571             if (type == "fan")
1572             {
1573                 if (requestedDomainMask & (1 << count))
1574                 {
1575                     ipmi::setDbusProperty(
1576                         *dbus, "xyz.openbmc_project.EntityManager", path,
1577                         pidConfigurationIface, "OutLimitMax",
1578                         static_cast<double>(param2));
1579                 }
1580                 count++;
1581             }
1582         }
1583         return ipmi::responseSuccess();
1584     }
1585     else
1586     {
1587         // todo other command parts possibly
1588         // tcontrol is handled in peci now
1589         // fan speed offset not implemented yet
1590         // domain pwm limit not implemented
1591         return ipmi::responseParmOutOfRange();
1592     }
1593 }
1594 
1595 ipmi::RspType<
1596     std::variant<uint8_t, std::array<uint8_t, 2>, std::array<uint16_t, 2>>>
1597     ipmiOEMGetFscParameter(uint8_t command, std::optional<uint8_t> param)
1598 {
1599     constexpr uint8_t legacyDefaultSetpoint = -128;
1600 
1601     std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
1602     if (command == static_cast<uint8_t>(setFscParamFlags::tcontrol))
1603     {
1604         if (!param)
1605         {
1606             return ipmi::responseReqDataLenInvalid();
1607         }
1608 
1609         std::string pathName;
1610 
1611         if (*param == legacyExitAirSensorNumber)
1612         {
1613             pathName = exitAirPathName;
1614         }
1615         else if (*param == legacyPCHSensorNumber)
1616         {
1617             pathName = pchPathName;
1618         }
1619         else
1620         {
1621             return ipmi::responseParmOutOfRange();
1622         }
1623 
1624         uint8_t setpoint = legacyDefaultSetpoint;
1625         std::string path = getConfigPath(pathName);
1626         if (path.size())
1627         {
1628             Value val = ipmi::getDbusProperty(
1629                 *dbus, "xyz.openbmc_project.EntityManager", path,
1630                 pidConfigurationIface, "SetPoint");
1631             setpoint = std::floor(std::get<double>(val) + 0.5);
1632         }
1633 
1634         // old implementation used to return the "default" and current, we
1635         // don't make the default readily available so just make both the
1636         // same
1637 
1638         return ipmi::responseSuccess(
1639             std::array<uint8_t, 2>{setpoint, setpoint});
1640     }
1641     else if (command == static_cast<uint8_t>(setFscParamFlags::maxPwm))
1642     {
1643         constexpr const size_t maxDomainCount = 8;
1644 
1645         if (!param)
1646         {
1647             return ipmi::responseReqDataLenInvalid();
1648         }
1649         uint8_t requestedDomain = *param;
1650         if (requestedDomain >= maxDomainCount)
1651         {
1652             return ipmi::responseInvalidFieldRequest();
1653         }
1654 
1655         boost::container::flat_map data = getPidConfigs();
1656         if (data.empty())
1657         {
1658             phosphor::logging::log<phosphor::logging::level::ERR>(
1659                 "ipmiOEMGetFscParameter: found no pid configurations!");
1660             return ipmi::responseResponseError();
1661         }
1662         size_t count = 0;
1663         for (const auto& [_, pid] : data)
1664         {
1665             auto findClass = pid.find("Class");
1666             if (findClass == pid.end())
1667             {
1668                 phosphor::logging::log<phosphor::logging::level::ERR>(
1669                     "ipmiOEMGetFscParameter: found illegal pid "
1670                     "configurations");
1671                 return ipmi::responseResponseError();
1672             }
1673             std::string type = std::get<std::string>(findClass->second);
1674             if (type == "fan")
1675             {
1676                 if (requestedDomain == count)
1677                 {
1678                     auto findOutLimit = pid.find("OutLimitMax");
1679                     if (findOutLimit == pid.end())
1680                     {
1681                         phosphor::logging::log<phosphor::logging::level::ERR>(
1682                             "ipmiOEMGetFscParameter: found illegal pid "
1683                             "configurations");
1684                         return ipmi::responseResponseError();
1685                     }
1686 
1687                     return ipmi::responseSuccess(
1688                         static_cast<uint8_t>(std::floor(
1689                             std::get<double>(findOutLimit->second) + 0.5)));
1690                 }
1691                 else
1692                 {
1693                     count++;
1694                 }
1695             }
1696         }
1697 
1698         return ipmi::responseInvalidFieldRequest();
1699     }
1700     else if (command == static_cast<uint8_t>(setFscParamFlags::cfm))
1701     {
1702 
1703         /*
1704         DataLen should be 1, but host is sending us an extra bit. As the
1705         previous behavior didn't seem to prevent this, ignore the check for
1706         now.
1707 
1708         if (param)
1709         {
1710             phosphor::logging::log<phosphor::logging::level::ERR>(
1711                 "ipmiOEMGetFscParameter: invalid input len!");
1712             return IPMI_CC_REQ_DATA_LEN_INVALID;
1713         }
1714         */
1715         Value cfmLimit;
1716         Value cfmMaximum;
1717         try
1718         {
1719             cfmLimit = ipmi::getDbusProperty(*dbus, settingsBusName,
1720                                              cfmLimitSettingPath, cfmLimitIface,
1721                                              "Limit");
1722             cfmMaximum = ipmi::getDbusProperty(
1723                 *dbus, "xyz.openbmc_project.ExitAirTempSensor",
1724                 "/xyz/openbmc_project/control/MaxCFM", cfmLimitIface, "Limit");
1725         }
1726         catch (sdbusplus::exception_t& e)
1727         {
1728             phosphor::logging::log<phosphor::logging::level::ERR>(
1729                 "ipmiOEMGetFscParameter: can't get cfm setting!",
1730                 phosphor::logging::entry("ERR=%s", e.what()));
1731             return ipmi::responseResponseError();
1732         }
1733 
1734         double cfmMax = std::get<double>(cfmMaximum);
1735         double cfmLim = std::get<double>(cfmLimit);
1736 
1737         cfmLim = std::floor(cfmLim + 0.5);
1738         cfmMax = std::floor(cfmMax + 0.5);
1739         uint16_t cfmLimResp = static_cast<uint16_t>(cfmLim);
1740         uint16_t cfmMaxResp = static_cast<uint16_t>(cfmMax);
1741 
1742         return ipmi::responseSuccess(
1743             std::array<uint16_t, 2>{cfmLimResp, cfmMaxResp});
1744     }
1745 
1746     else
1747     {
1748         // todo other command parts possibly
1749         // domain pwm limit not implemented
1750         return ipmi::responseParmOutOfRange();
1751     }
1752 }
1753 
1754 ipmi::RspType<> ipmiOEMSetFaultIndication(uint8_t sourceId, uint8_t faultType,
1755                                           uint8_t faultState,
1756                                           uint8_t faultGroup,
1757                                           std::array<uint8_t, 8>& ledStateData)
1758 {
1759     static constexpr const char* objpath = "/xyz/openbmc_project/EntityManager";
1760     static constexpr const char* intf = "xyz.openbmc_project.EntityManager";
1761     constexpr auto maxFaultType = static_cast<size_t>(RemoteFaultType::max);
1762     static const std::array<std::string, maxFaultType> faultNames = {
1763         "faultFan",       "faultTemp",     "faultPower",
1764         "faultDriveSlot", "faultSoftware", "faultMemory"};
1765     static constexpr const char* sysGpioPath = "/sys/class/gpio/gpio";
1766     static constexpr const char* postfixValue = "/value";
1767 
1768     constexpr uint8_t maxFaultSource = 0x4;
1769     constexpr uint8_t skipLEDs = 0xFF;
1770     constexpr uint8_t pinSize = 64;
1771     constexpr uint8_t groupSize = 16;
1772 
1773     std::vector<uint16_t> ledFaultPins(pinSize, 0xFFFF);
1774     uint64_t resFIndex = 0;
1775     std::string resFType;
1776     std::string service;
1777     ObjectValueTree valueTree;
1778 
1779     // Validate the source, fault type
1780     if ((sourceId >= maxFaultSource) ||
1781         (faultType >= static_cast<int8_t>(RemoteFaultType::max)) ||
1782         (faultState >= static_cast<int8_t>(RemoteFaultState::maxFaultState)) ||
1783         (faultGroup >= static_cast<int8_t>(DimmFaultType::maxFaultGroup)))
1784     {
1785         return ipmi::responseParmOutOfRange();
1786     }
1787 
1788     std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
1789     try
1790     {
1791         service = getService(*dbus, intf, objpath);
1792         valueTree = getManagedObjects(*dbus, service, "/");
1793     }
1794     catch (const std::exception& e)
1795     {
1796         phosphor::logging::log<phosphor::logging::level::ERR>(
1797             "No object implements interface",
1798             phosphor::logging::entry("SERVICE=%s", service.c_str()),
1799             phosphor::logging::entry("INTF=%s", intf));
1800         return ipmi::responseResponseError();
1801     }
1802 
1803     if (valueTree.empty())
1804     {
1805         phosphor::logging::log<phosphor::logging::level::ERR>(
1806             "No object implements interface",
1807             phosphor::logging::entry("INTF=%s", intf));
1808         return ipmi::responseResponseError();
1809     }
1810 
1811     for (const auto& item : valueTree)
1812     {
1813         // find LedFault configuration
1814         auto interface =
1815             item.second.find("xyz.openbmc_project.Configuration.LedFault");
1816         if (interface == item.second.end())
1817         {
1818             continue;
1819         }
1820 
1821         // find matched fault type: faultMemmory / faultFan
1822         // find LedGpioPins/FaultIndex configuration
1823         auto propertyFaultType = interface->second.find("FaultType");
1824         auto propertyFIndex = interface->second.find("FaultIndex");
1825         auto ledIndex = interface->second.find("LedGpioPins");
1826 
1827         if (propertyFaultType == interface->second.end() ||
1828             propertyFIndex == interface->second.end() ||
1829             ledIndex == interface->second.end())
1830         {
1831             continue;
1832         }
1833 
1834         try
1835         {
1836             Value valIndex = propertyFIndex->second;
1837             resFIndex = std::get<uint64_t>(valIndex);
1838 
1839             Value valFType = propertyFaultType->second;
1840             resFType = std::get<std::string>(valFType);
1841         }
1842         catch (const std::bad_variant_access& e)
1843         {
1844             phosphor::logging::log<phosphor::logging::level::ERR>(e.what());
1845             return ipmi::responseResponseError();
1846         }
1847         // find the matched requested fault type: faultMemmory or faultFan
1848         if (resFType != faultNames[faultType])
1849         {
1850             continue;
1851         }
1852 
1853         // read LedGpioPins data
1854         std::vector<uint64_t> ledgpios;
1855         std::variant<std::vector<uint64_t>> message;
1856 
1857         auto method = dbus->new_method_call(
1858             service.c_str(), (std::string(item.first)).c_str(),
1859             "org.freedesktop.DBus.Properties", "Get");
1860 
1861         method.append("xyz.openbmc_project.Configuration.LedFault",
1862                       "LedGpioPins");
1863 
1864         try
1865         {
1866             auto reply = dbus->call(method);
1867             reply.read(message);
1868             ledgpios = std::get<std::vector<uint64_t>>(message);
1869         }
1870         catch (std::exception& e)
1871         {
1872             phosphor::logging::log<phosphor::logging::level::ERR>(e.what());
1873             return ipmi::responseResponseError();
1874         }
1875 
1876         // Check the size to be sure it will never overflow on groupSize
1877         if (ledgpios.size() > groupSize)
1878         {
1879             phosphor::logging::log<phosphor::logging::level::ERR>(
1880                 "Fault gpio Pins out of range!");
1881             return ipmi::responseParmOutOfRange();
1882         }
1883         // Store data, according to command data bit index order
1884         for (int i = 0; i < ledgpios.size(); i++)
1885         {
1886             ledFaultPins[i + groupSize * resFIndex] = ledgpios[i];
1887         }
1888     }
1889 
1890     switch (RemoteFaultType(faultType))
1891     {
1892         case (RemoteFaultType::fan):
1893         case (RemoteFaultType::memory):
1894         {
1895             if (faultGroup == skipLEDs)
1896             {
1897                 return ipmi::responseSuccess();
1898             }
1899 
1900             uint64_t ledState = 0;
1901             // calculate led state bit filed count, each byte has 8bits
1902             // the maximum bits will be 8 * 8 bits
1903             constexpr uint8_t size = sizeof(ledStateData) * 8;
1904             for (int i = 0; i < sizeof(ledStateData); i++)
1905             {
1906                 ledState = (uint64_t)(ledState << 8);
1907                 ledState = (uint64_t)(ledState | (uint64_t)ledStateData[i]);
1908             }
1909 
1910             std::bitset<size> ledStateBits(ledState);
1911             std::string gpioValue;
1912             for (int i = 0; i < size; i++)
1913             { // skip invalid value
1914                 if (ledFaultPins[i] == 0xFFFF)
1915                 {
1916                     continue;
1917                 }
1918 
1919                 std::string device = sysGpioPath +
1920                                      std::to_string(ledFaultPins[i]) +
1921                                      postfixValue;
1922                 std::fstream gpioFile;
1923 
1924                 gpioFile.open(device, std::ios::out);
1925 
1926                 if (!gpioFile.good())
1927                 {
1928                     phosphor::logging::log<phosphor::logging::level::ERR>(
1929                         "Not Find Led Gpio Device!",
1930                         phosphor::logging::entry("DEVICE=%s", device.c_str()));
1931                     return ipmi::responseResponseError();
1932                 }
1933                 gpioFile << std::to_string(
1934                     static_cast<uint8_t>(ledStateBits[i]));
1935                 gpioFile.close();
1936             }
1937             break;
1938         }
1939         default:
1940         {
1941             // now only support two fault types
1942             return ipmi::responseParmOutOfRange();
1943         }
1944     }
1945 
1946     return ipmi::responseSuccess();
1947 }
1948 
1949 ipmi::RspType<uint8_t> ipmiOEMReadBoardProductId()
1950 {
1951     uint8_t prodId = 0;
1952     try
1953     {
1954         std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
1955         const DbusObjectInfo& object = getDbusObject(
1956             *dbus, "xyz.openbmc_project.Inventory.Item.Board",
1957             "/xyz/openbmc_project/inventory/system/board/", "Baseboard");
1958         const Value& propValue = getDbusProperty(
1959             *dbus, object.second, object.first,
1960             "xyz.openbmc_project.Inventory.Item.Board", "ProductId");
1961         prodId = static_cast<uint8_t>(std::get<uint64_t>(propValue));
1962     }
1963     catch (std::exception& e)
1964     {
1965         phosphor::logging::log<phosphor::logging::level::ERR>(
1966             "ipmiOEMReadBoardProductId: Product ID read failed!",
1967             phosphor::logging::entry("ERR=%s", e.what()));
1968     }
1969     return ipmi::responseSuccess(prodId);
1970 }
1971 
1972 ipmi::RspType<uint8_t /* restore status */>
1973     ipmiRestoreConfiguration(const std::array<uint8_t, 3>& clr, uint8_t cmd)
1974 {
1975     static constexpr std::array<uint8_t, 3> expClr = {'C', 'L', 'R'};
1976 
1977     if (clr != expClr)
1978     {
1979         return ipmi::responseInvalidFieldRequest();
1980     }
1981     constexpr uint8_t cmdStatus = 0;
1982     constexpr uint8_t cmdDefaultRestore = 0xaa;
1983     constexpr uint8_t cmdFullRestore = 0xbb;
1984     constexpr uint8_t cmdFormat = 0xcc;
1985 
1986     constexpr const char* restoreOpFname = "/tmp/.rwfs/.restore_op";
1987 
1988     switch (cmd)
1989     {
1990         case cmdStatus:
1991             break;
1992         case cmdDefaultRestore:
1993         case cmdFullRestore:
1994         case cmdFormat:
1995         {
1996             // write file to rwfs root
1997             int value = (cmd - 1) & 0x03; // map aa, bb, cc => 1, 2, 3
1998             std::ofstream restoreFile(restoreOpFname);
1999             if (!restoreFile)
2000             {
2001                 return ipmi::responseUnspecifiedError();
2002             }
2003             restoreFile << value << "\n";
2004             break;
2005         }
2006         default:
2007             return ipmi::responseInvalidFieldRequest();
2008     }
2009 
2010     constexpr uint8_t restorePending = 0;
2011     constexpr uint8_t restoreComplete = 1;
2012 
2013     uint8_t restoreStatus = std::filesystem::exists(restoreOpFname)
2014                                 ? restorePending
2015                                 : restoreComplete;
2016     return ipmi::responseSuccess(restoreStatus);
2017 }
2018 
2019 ipmi::RspType<uint8_t> ipmiOEMGetNmiSource(void)
2020 {
2021     uint8_t bmcSource;
2022     namespace nmi = sdbusplus::com::intel::Control::server;
2023 
2024     try
2025     {
2026         std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
2027         std::string service =
2028             getService(*dbus, oemNmiSourceIntf, oemNmiSourceObjPath);
2029         Value variant =
2030             getDbusProperty(*dbus, service, oemNmiSourceObjPath,
2031                             oemNmiSourceIntf, oemNmiBmcSourceObjPathProp);
2032 
2033         switch (nmi::NMISource::convertBMCSourceSignalFromString(
2034             std::get<std::string>(variant)))
2035         {
2036             case nmi::NMISource::BMCSourceSignal::None:
2037                 bmcSource = static_cast<uint8_t>(NmiSource::none);
2038                 break;
2039             case nmi::NMISource::BMCSourceSignal::FpBtn:
2040                 bmcSource = static_cast<uint8_t>(NmiSource::fpBtn);
2041                 break;
2042             case nmi::NMISource::BMCSourceSignal::WdPreTimeout:
2043                 bmcSource = static_cast<uint8_t>(NmiSource::wdPreTimeout);
2044                 break;
2045             case nmi::NMISource::BMCSourceSignal::PefMatch:
2046                 bmcSource = static_cast<uint8_t>(NmiSource::pefMatch);
2047                 break;
2048             case nmi::NMISource::BMCSourceSignal::ChassisCmd:
2049                 bmcSource = static_cast<uint8_t>(NmiSource::chassisCmd);
2050                 break;
2051             case nmi::NMISource::BMCSourceSignal::MemoryError:
2052                 bmcSource = static_cast<uint8_t>(NmiSource::memoryError);
2053                 break;
2054             case nmi::NMISource::BMCSourceSignal::PciSerrPerr:
2055                 bmcSource = static_cast<uint8_t>(NmiSource::pciSerrPerr);
2056                 break;
2057             case nmi::NMISource::BMCSourceSignal::SouthbridgeNmi:
2058                 bmcSource = static_cast<uint8_t>(NmiSource::southbridgeNmi);
2059                 break;
2060             case nmi::NMISource::BMCSourceSignal::ChipsetNmi:
2061                 bmcSource = static_cast<uint8_t>(NmiSource::chipsetNmi);
2062                 break;
2063             default:
2064                 phosphor::logging::log<phosphor::logging::level::ERR>(
2065                     "NMI source: invalid property!",
2066                     phosphor::logging::entry(
2067                         "PROP=%s", std::get<std::string>(variant).c_str()));
2068                 return ipmi::responseResponseError();
2069         }
2070     }
2071     catch (sdbusplus::exception::SdBusError& e)
2072     {
2073         phosphor::logging::log<phosphor::logging::level::ERR>(e.what());
2074         return ipmi::responseResponseError();
2075     }
2076 
2077     return ipmi::responseSuccess(bmcSource);
2078 }
2079 
2080 ipmi::RspType<> ipmiOEMSetNmiSource(uint8_t sourceId)
2081 {
2082     namespace nmi = sdbusplus::com::intel::Control::server;
2083 
2084     nmi::NMISource::BMCSourceSignal bmcSourceSignal =
2085         nmi::NMISource::BMCSourceSignal::None;
2086 
2087     switch (NmiSource(sourceId))
2088     {
2089         case NmiSource::none:
2090             bmcSourceSignal = nmi::NMISource::BMCSourceSignal::None;
2091             break;
2092         case NmiSource::fpBtn:
2093             bmcSourceSignal = nmi::NMISource::BMCSourceSignal::FpBtn;
2094             break;
2095         case NmiSource::wdPreTimeout:
2096             bmcSourceSignal = nmi::NMISource::BMCSourceSignal::WdPreTimeout;
2097             break;
2098         case NmiSource::pefMatch:
2099             bmcSourceSignal = nmi::NMISource::BMCSourceSignal::PefMatch;
2100             break;
2101         case NmiSource::chassisCmd:
2102             bmcSourceSignal = nmi::NMISource::BMCSourceSignal::ChassisCmd;
2103             break;
2104         case NmiSource::memoryError:
2105             bmcSourceSignal = nmi::NMISource::BMCSourceSignal::MemoryError;
2106             break;
2107         case NmiSource::pciSerrPerr:
2108             bmcSourceSignal = nmi::NMISource::BMCSourceSignal::PciSerrPerr;
2109             break;
2110         case NmiSource::southbridgeNmi:
2111             bmcSourceSignal = nmi::NMISource::BMCSourceSignal::SouthbridgeNmi;
2112             break;
2113         case NmiSource::chipsetNmi:
2114             bmcSourceSignal = nmi::NMISource::BMCSourceSignal::ChipsetNmi;
2115             break;
2116         default:
2117             phosphor::logging::log<phosphor::logging::level::ERR>(
2118                 "NMI source: invalid property!");
2119             return ipmi::responseResponseError();
2120     }
2121 
2122     try
2123     {
2124         // keep NMI signal source
2125         std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
2126         std::string service =
2127             getService(*dbus, oemNmiSourceIntf, oemNmiSourceObjPath);
2128         setDbusProperty(
2129             *dbus, service, oemNmiSourceObjPath, oemNmiSourceIntf,
2130             oemNmiBmcSourceObjPathProp,
2131             sdbusplus::com::intel::Control::server::convertForMessage(
2132                 bmcSourceSignal));
2133     }
2134     catch (sdbusplus::exception_t& e)
2135     {
2136         phosphor::logging::log<phosphor::logging::level::ERR>(e.what());
2137         return ipmi::responseResponseError();
2138     }
2139 
2140     return ipmi::responseSuccess();
2141 }
2142 
2143 namespace dimmOffset
2144 {
2145 constexpr const char* dimmPower = "DimmPower";
2146 constexpr const char* staticCltt = "StaticCltt";
2147 constexpr const char* offsetPath = "/xyz/openbmc_project/Inventory/Item/Dimm";
2148 constexpr const char* offsetInterface =
2149     "xyz.openbmc_project.Inventory.Item.Dimm.Offset";
2150 constexpr const char* property = "DimmOffset";
2151 
2152 }; // namespace dimmOffset
2153 
2154 ipmi::RspType<>
2155     ipmiOEMSetDimmOffset(uint8_t type,
2156                          const std::vector<std::tuple<uint8_t, uint8_t>>& data)
2157 {
2158     if (type != static_cast<uint8_t>(dimmOffsetTypes::dimmPower) &&
2159         type != static_cast<uint8_t>(dimmOffsetTypes::staticCltt))
2160     {
2161         return ipmi::responseInvalidFieldRequest();
2162     }
2163 
2164     if (data.empty())
2165     {
2166         return ipmi::responseInvalidFieldRequest();
2167     }
2168     nlohmann::json json;
2169 
2170     std::ifstream jsonStream(dimmOffsetFile);
2171     if (jsonStream.good())
2172     {
2173         json = nlohmann::json::parse(jsonStream, nullptr, false);
2174         if (json.is_discarded())
2175         {
2176             json = nlohmann::json();
2177         }
2178         jsonStream.close();
2179     }
2180 
2181     std::string typeName;
2182     if (type == static_cast<uint8_t>(dimmOffsetTypes::dimmPower))
2183     {
2184         typeName = dimmOffset::dimmPower;
2185     }
2186     else
2187     {
2188         typeName = dimmOffset::staticCltt;
2189     }
2190 
2191     nlohmann::json& field = json[typeName];
2192 
2193     for (const auto& [index, value] : data)
2194     {
2195         field[index] = value;
2196     }
2197 
2198     for (nlohmann::json& val : field)
2199     {
2200         if (val == nullptr)
2201         {
2202             val = static_cast<uint8_t>(0);
2203         }
2204     }
2205 
2206     std::ofstream output(dimmOffsetFile);
2207     if (!output.good())
2208     {
2209         std::cerr << "Error writing json file\n";
2210         return ipmi::responseResponseError();
2211     }
2212 
2213     output << json.dump(4);
2214 
2215     if (type == static_cast<uint8_t>(dimmOffsetTypes::staticCltt))
2216     {
2217         std::shared_ptr<sdbusplus::asio::connection> bus = getSdBus();
2218 
2219         std::variant<std::vector<uint8_t>> offsets =
2220             field.get<std::vector<uint8_t>>();
2221         auto call = bus->new_method_call(
2222             settingsBusName, dimmOffset::offsetPath, PROP_INTF, "Set");
2223         call.append(dimmOffset::offsetInterface, dimmOffset::property, offsets);
2224         try
2225         {
2226             bus->call(call);
2227         }
2228         catch (sdbusplus::exception_t& e)
2229         {
2230             phosphor::logging::log<phosphor::logging::level::ERR>(
2231                 "ipmiOEMSetDimmOffset: can't set dimm offsets!",
2232                 phosphor::logging::entry("ERR=%s", e.what()));
2233             return ipmi::responseResponseError();
2234         }
2235     }
2236 
2237     return ipmi::responseSuccess();
2238 }
2239 
2240 ipmi::RspType<uint8_t> ipmiOEMGetDimmOffset(uint8_t type, uint8_t index)
2241 {
2242 
2243     if (type != static_cast<uint8_t>(dimmOffsetTypes::dimmPower) &&
2244         type != static_cast<uint8_t>(dimmOffsetTypes::staticCltt))
2245     {
2246         return ipmi::responseInvalidFieldRequest();
2247     }
2248 
2249     std::ifstream jsonStream(dimmOffsetFile);
2250 
2251     auto json = nlohmann::json::parse(jsonStream, nullptr, false);
2252     if (json.is_discarded())
2253     {
2254         std::cerr << "File error in " << dimmOffsetFile << "\n";
2255         return ipmi::responseResponseError();
2256     }
2257 
2258     std::string typeName;
2259     if (type == static_cast<uint8_t>(dimmOffsetTypes::dimmPower))
2260     {
2261         typeName = dimmOffset::dimmPower;
2262     }
2263     else
2264     {
2265         typeName = dimmOffset::staticCltt;
2266     }
2267 
2268     auto it = json.find(typeName);
2269     if (it == json.end())
2270     {
2271         return ipmi::responseInvalidFieldRequest();
2272     }
2273 
2274     if (it->size() <= index)
2275     {
2276         return ipmi::responseInvalidFieldRequest();
2277     }
2278 
2279     uint8_t resp = it->at(index).get<uint8_t>();
2280     return ipmi::responseSuccess(resp);
2281 }
2282 
2283 static void registerOEMFunctions(void)
2284 {
2285     phosphor::logging::log<phosphor::logging::level::INFO>(
2286         "Registering OEM commands");
2287     ipmiPrintAndRegister(netfnIntcOEMGeneral, IPMI_CMD_WILDCARD, NULL,
2288                          ipmiOEMWildcard,
2289                          PRIVILEGE_USER); // wildcard default handler
2290     ipmiPrintAndRegister(netfunIntelAppOEM, IPMI_CMD_WILDCARD, NULL,
2291                          ipmiOEMWildcard,
2292                          PRIVILEGE_USER); // wildcard default handler
2293     ipmiPrintAndRegister(
2294         netfnIntcOEMGeneral,
2295         static_cast<ipmi_cmd_t>(
2296             IPMINetfnIntelOEMGeneralCmd::cmdGetChassisIdentifier),
2297         NULL, ipmiOEMGetChassisIdentifier,
2298         PRIVILEGE_USER); // get chassis identifier
2299     ipmiPrintAndRegister(
2300         netfnIntcOEMGeneral,
2301         static_cast<ipmi_cmd_t>(IPMINetfnIntelOEMGeneralCmd::cmdSetSystemGUID),
2302         NULL, ipmiOEMSetSystemGUID,
2303         PRIVILEGE_ADMIN); // set system guid
2304     ipmiPrintAndRegister(
2305         netfnIntcOEMGeneral,
2306         static_cast<ipmi_cmd_t>(IPMINetfnIntelOEMGeneralCmd::cmdSetBIOSID),
2307         NULL, ipmiOEMSetBIOSID, PRIVILEGE_ADMIN);
2308     ipmiPrintAndRegister(netfnIntcOEMGeneral,
2309                          static_cast<ipmi_cmd_t>(
2310                              IPMINetfnIntelOEMGeneralCmd::cmdGetOEMDeviceInfo),
2311                          NULL, ipmiOEMGetDeviceInfo, PRIVILEGE_USER);
2312     ipmiPrintAndRegister(
2313         netfnIntcOEMGeneral,
2314         static_cast<ipmi_cmd_t>(
2315             IPMINetfnIntelOEMGeneralCmd::cmdGetAICSlotFRUIDSlotPosRecords),
2316         NULL, ipmiOEMGetAICFRU, PRIVILEGE_USER);
2317 
2318     ipmi::registerHandler(
2319         ipmi::prioOpenBmcBase, ipmi::netFnOemOne,
2320         static_cast<ipmi::Cmd>(
2321             IPMINetfnIntelOEMGeneralCmd::cmdSendEmbeddedFWUpdStatus),
2322         ipmi::Privilege::Operator, ipmiOEMSendEmbeddedFwUpdStatus);
2323 
2324     ipmiPrintAndRegister(
2325         netfnIntcOEMGeneral,
2326         static_cast<ipmi_cmd_t>(
2327             IPMINetfnIntelOEMGeneralCmd::cmdSetPowerRestoreDelay),
2328         NULL, ipmiOEMSetPowerRestoreDelay, PRIVILEGE_OPERATOR);
2329     ipmiPrintAndRegister(
2330         netfnIntcOEMGeneral,
2331         static_cast<ipmi_cmd_t>(
2332             IPMINetfnIntelOEMGeneralCmd::cmdGetPowerRestoreDelay),
2333         NULL, ipmiOEMGetPowerRestoreDelay, PRIVILEGE_USER);
2334 
2335     ipmi::registerHandler(
2336         ipmi::prioOpenBmcBase, ipmi::netFnOemOne,
2337         static_cast<ipmi::Cmd>(
2338             IPMINetfnIntelOEMGeneralCmd::cmdSetOEMUser2Activation),
2339         ipmi::Privilege::Callback, ipmiOEMSetUser2Activation);
2340 
2341     ipmi::registerHandler(
2342         ipmi::prioOpenBmcBase, ipmi::netFnOemOne,
2343         static_cast<ipmi::Cmd>(
2344             IPMINetfnIntelOEMGeneralCmd::cmdSetSpecialUserPassword),
2345         ipmi::Privilege::Callback, ipmiOEMSetSpecialUserPassword);
2346 
2347     // <Get Processor Error Config>
2348     ipmi::registerHandler(
2349         ipmi::prioOemBase, netfnIntcOEMGeneral,
2350         static_cast<ipmi::Cmd>(
2351             IPMINetfnIntelOEMGeneralCmd::cmdGetProcessorErrConfig),
2352         ipmi::Privilege::User, ipmiOEMGetProcessorErrConfig);
2353     // <Set Processor Error Config>
2354     ipmi::registerHandler(
2355         ipmi::prioOemBase, netfnIntcOEMGeneral,
2356         static_cast<ipmi::Cmd>(
2357             IPMINetfnIntelOEMGeneralCmd::cmdSetProcessorErrConfig),
2358         ipmi::Privilege::Admin, ipmiOEMSetProcessorErrConfig);
2359 
2360     ipmiPrintAndRegister(netfnIntcOEMGeneral,
2361                          static_cast<ipmi_cmd_t>(
2362                              IPMINetfnIntelOEMGeneralCmd::cmdSetShutdownPolicy),
2363                          NULL, ipmiOEMSetShutdownPolicy, PRIVILEGE_ADMIN);
2364     ipmiPrintAndRegister(netfnIntcOEMGeneral,
2365                          static_cast<ipmi_cmd_t>(
2366                              IPMINetfnIntelOEMGeneralCmd::cmdGetShutdownPolicy),
2367                          NULL, ipmiOEMGetShutdownPolicy, PRIVILEGE_ADMIN);
2368 
2369     ipmiPrintAndRegister(
2370         netfnIntcOEMGeneral,
2371         static_cast<ipmi_cmd_t>(IPMINetfnIntelOEMGeneralCmd::cmdSetFanConfig),
2372         NULL, ipmiOEMSetFanConfig, PRIVILEGE_USER);
2373 
2374     ipmi::registerHandler(
2375         ipmi::prioOemBase, netfnIntcOEMGeneral,
2376         static_cast<ipmi::Cmd>(IPMINetfnIntelOEMGeneralCmd::cmdGetFanConfig),
2377         ipmi::Privilege::User, ipmiOEMGetFanConfig);
2378 
2379     ipmi::registerHandler(
2380         ipmi::prioOemBase, netfnIntcOEMGeneral,
2381         static_cast<ipmi::Cmd>(
2382             IPMINetfnIntelOEMGeneralCmd::cmdGetFanSpeedOffset),
2383         ipmi::Privilege::User, ipmiOEMGetFanSpeedOffset);
2384 
2385     ipmi::registerHandler(
2386         ipmi::prioOemBase, netfnIntcOEMGeneral,
2387         static_cast<ipmi::Cmd>(
2388             IPMINetfnIntelOEMGeneralCmd::cmdSetFanSpeedOffset),
2389         ipmi::Privilege::User, ipmiOEMSetFanSpeedOffset);
2390 
2391     ipmi::registerHandler(
2392         ipmi::prioOemBase, netfnIntcOEMGeneral,
2393         static_cast<ipmi::Cmd>(IPMINetfnIntelOEMGeneralCmd::cmdSetFscParameter),
2394         ipmi::Privilege::User, ipmiOEMSetFscParameter);
2395 
2396     ipmi::registerHandler(
2397         ipmi::prioOemBase, netfnIntcOEMGeneral,
2398         static_cast<ipmi::Cmd>(IPMINetfnIntelOEMGeneralCmd::cmdGetFscParameter),
2399         ipmi::Privilege::User, ipmiOEMGetFscParameter);
2400 
2401     ipmi::registerHandler(
2402         ipmi::prioOpenBmcBase, netfnIntcOEMGeneral,
2403         static_cast<ipmi::Cmd>(
2404             IPMINetfnIntelOEMGeneralCmd::cmdReadBaseBoardProductId),
2405         ipmi::Privilege::Admin, ipmiOEMReadBoardProductId);
2406 
2407     ipmi::registerHandler(
2408         ipmi::prioOemBase, netfnIntcOEMGeneral,
2409         static_cast<ipmi::Cmd>(IPMINetfnIntelOEMGeneralCmd::cmdGetNmiStatus),
2410         ipmi::Privilege::User, ipmiOEMGetNmiSource);
2411 
2412     ipmi::registerHandler(
2413         ipmi::prioOemBase, netfnIntcOEMGeneral,
2414         static_cast<ipmi::Cmd>(IPMINetfnIntelOEMGeneralCmd::cmdSetNmiStatus),
2415         ipmi::Privilege::Operator, ipmiOEMSetNmiSource);
2416 
2417     ipmiPrintAndRegister(
2418         netfnIntcOEMGeneral,
2419         static_cast<ipmi_cmd_t>(IPMINetfnIntelOEMGeneralCmd::cmdGetLEDStatus),
2420         NULL, ipmiOEMGetLEDStatus, PRIVILEGE_ADMIN);
2421     ipmiPrintAndRegister(
2422         netfnIntcOEMPlatform,
2423         static_cast<ipmi_cmd_t>(
2424             IPMINetfnIntelOEMPlatformCmd::cmdCfgHostSerialPortSpeed),
2425         NULL, ipmiOEMCfgHostSerialPortSpeed, PRIVILEGE_ADMIN);
2426     ipmi::registerHandler(
2427         ipmi::prioOemBase, netfnIntcOEMGeneral,
2428         static_cast<ipmi::Cmd>(
2429             IPMINetfnIntelOEMGeneralCmd::cmdSetFaultIndication),
2430         ipmi::Privilege::Operator, ipmiOEMSetFaultIndication);
2431 
2432     registerHandler(prioOemBase, netfn::intel::oemGeneral,
2433                     netfn::intel::cmdRestoreConfiguration, Privilege::Admin,
2434                     ipmiRestoreConfiguration);
2435 
2436     ipmi::registerHandler(
2437         ipmi::prioOemBase, netfnIntcOEMGeneral,
2438         static_cast<ipmi::Cmd>(IPMINetfnIntelOEMGeneralCmd::cmdSetDimmOffset),
2439         ipmi::Privilege::Operator, ipmiOEMSetDimmOffset);
2440 
2441     ipmi::registerHandler(
2442         ipmi::prioOemBase, netfnIntcOEMGeneral,
2443         static_cast<ipmi::Cmd>(IPMINetfnIntelOEMGeneralCmd::cmdGetDimmOffset),
2444         ipmi::Privilege::Operator, ipmiOEMGetDimmOffset);
2445 }
2446 
2447 } // namespace ipmi
2448