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