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