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