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