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