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