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