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