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