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