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