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