xref: /openbmc/intel-ipmi-oem/src/oemcommands.cpp (revision c4b3d304d5eac20d85b56baf5de60f4ef314ff57)
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/v1/child.hpp>
27 #include <boost/process/v1/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{};
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::ccReqDataLenInvalid;
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::ccSuccess;
338     }
339     *dataLen = 0;
340     return ipmi::ccResponseError;
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::ccReqDataLenInvalid;
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::ccSuccess;
375 }
376 
ipmiOEMDisableBMCSystemReset(bool disableResetOnSMI,uint7_t reserved1)377 ipmi::RspType<> ipmiOEMDisableBMCSystemReset(bool disableResetOnSMI,
378                                              uint7_t reserved1)
379 {
380     if (reserved1)
381     {
382         return ipmi::responseInvalidFieldRequest();
383     }
384 
385     std::shared_ptr<sdbusplus::asio::connection> busp = getSdBus();
386 
387     try
388     {
389         auto service =
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::ccReqDataLenInvalid;
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::ccInvalidFieldRequest;
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::ccSuccess;
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::ccReqDataLenInvalid;
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::ccSuccess;
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::ccReqDataLenInvalid;
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::ccSuccess;
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{};
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::ccReqDataLenInvalid;
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::ccSuccess;
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::ccReqDataLenInvalid;
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::ccUnspecifiedError;
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::ccUnspecifiedError;
1125     }
1126 
1127     *dataLen = sizeof(GetOEMShutdownPolicyRes);
1128     return ipmi::ccSuccess;
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::ccReqDataLenInvalid;
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::ccInvalidFieldRequest;
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::ccUnspecifiedError;
1183     }
1184 
1185     return ipmi::ccSuccess;
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::v1::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::ccReqDataLenInvalid;
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::ccReqDataLenInvalid;
1581             }
1582 
1583             *dataLen = 0;
1584 
1585             boost::process::v1::ipstream is;
1586             std::vector<std::string> data;
1587             std::string line;
1588             boost::process::v1::child c1(fwGetEnvCmd, "-n",
1589                                          fwHostSerailCfgEnvName,
1590                                          boost::process::v1::std_out > is);
1591 
1592             while (c1.running() && std::getline(is, line) && !line.empty())
1593             {
1594                 data.push_back(line);
1595             }
1596 
1597             c1.wait();
1598             if (c1.exit_code())
1599             {
1600                 phosphor::logging::log<phosphor::logging::level::ERR>(
1601                     "CfgHostSerial:: error on execute",
1602                     phosphor::logging::entry("EXECUTE=%s", fwSetEnvCmd));
1603                 // Using the default value
1604                 *resp = 0;
1605             }
1606             else
1607             {
1608                 if (data.size() != 1)
1609                 {
1610                     phosphor::logging::log<phosphor::logging::level::ERR>(
1611                         "CfgHostSerial:: error on read env");
1612                     return ipmi::ccUnspecifiedError;
1613                 }
1614                 try
1615                 {
1616                     unsigned long tmp = std::stoul(data[0]);
1617                     if (tmp > std::numeric_limits<uint8_t>::max())
1618                     {
1619                         throw std::out_of_range("Out of range");
1620                     }
1621                     *resp = static_cast<uint8_t>(tmp);
1622                 }
1623                 catch (const std::invalid_argument& e)
1624                 {
1625                     phosphor::logging::log<phosphor::logging::level::ERR>(
1626                         "invalid config ",
1627                         phosphor::logging::entry("ERR=%s", e.what()));
1628                     return ipmi::ccUnspecifiedError;
1629                 }
1630                 catch (const std::out_of_range& e)
1631                 {
1632                     phosphor::logging::log<phosphor::logging::level::ERR>(
1633                         "out_of_range config ",
1634                         phosphor::logging::entry("ERR=%s", e.what()));
1635                     return ipmi::ccUnspecifiedError;
1636                 }
1637             }
1638 
1639             *dataLen = 1;
1640             break;
1641         }
1642         case setHostSerialCfgCmd:
1643         {
1644             if (*dataLen != sizeof(CfgHostSerialReq))
1645             {
1646                 phosphor::logging::log<phosphor::logging::level::ERR>(
1647                     "CfgHostSerial: invalid input len!");
1648                 *dataLen = 0;
1649                 return ipmi::ccReqDataLenInvalid;
1650             }
1651 
1652             *dataLen = 0;
1653 
1654             if (req->parameter > HostSerialCfgParamMax)
1655             {
1656                 phosphor::logging::log<phosphor::logging::level::ERR>(
1657                     "CfgHostSerial: invalid input!");
1658                 return ipmi::ccInvalidFieldRequest;
1659             }
1660 
1661             boost::process::v1::child c1(fwSetEnvCmd, fwHostSerailCfgEnvName,
1662                                          std::to_string(req->parameter));
1663 
1664             c1.wait();
1665             if (c1.exit_code())
1666             {
1667                 phosphor::logging::log<phosphor::logging::level::ERR>(
1668                     "CfgHostSerial:: error on execute",
1669                     phosphor::logging::entry("EXECUTE=%s", fwGetEnvCmd));
1670                 return ipmi::ccUnspecifiedError;
1671             }
1672             break;
1673         }
1674         default:
1675             phosphor::logging::log<phosphor::logging::level::ERR>(
1676                 "CfgHostSerial: invalid input!");
1677             *dataLen = 0;
1678             return ipmi::ccInvalidFieldRequest;
1679     }
1680 
1681     return ipmi::ccSuccess;
1682 }
1683 
1684 constexpr const char* thermalModeInterface =
1685     "xyz.openbmc_project.Control.ThermalMode";
1686 constexpr const char* thermalModePath =
1687     "/xyz/openbmc_project/control/thermal_mode";
1688 
getFanProfileInterface(sdbusplus::bus_t & bus,boost::container::flat_map<std::string,ipmi::DbusVariant> & resp)1689 bool getFanProfileInterface(
1690     sdbusplus::bus_t& bus,
1691     boost::container::flat_map<std::string, ipmi::DbusVariant>& resp)
1692 {
1693     auto call = bus.new_method_call(settingsBusName, thermalModePath, PROP_INTF,
1694                                     "GetAll");
1695     call.append(thermalModeInterface);
1696     try
1697     {
1698         auto data = bus.call(call);
1699         data.read(resp);
1700     }
1701     catch (const sdbusplus::exception_t& e)
1702     {
1703         phosphor::logging::log<phosphor::logging::level::ERR>(
1704             "getFanProfileInterface: can't get thermal mode!",
1705             phosphor::logging::entry("ERR=%s", e.what()));
1706         return false;
1707     }
1708     return true;
1709 }
1710 
1711 /**@brief implements the OEM set fan config.
1712  * @param selectedFanProfile - fan profile to enable
1713  * @param reserved1
1714  * @param performanceMode - Performance/Acoustic mode
1715  * @param reserved2
1716  * @param setPerformanceMode - set Performance/Acoustic mode
1717  * @param setFanProfile - set fan profile
1718  *
1719  * @return IPMI completion code.
1720  **/
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)1721 ipmi::RspType<> ipmiOEMSetFanConfig(
1722     [[maybe_unused]] uint8_t selectedFanProfile, uint2_t reserved1,
1723     bool performanceMode, uint3_t reserved2, bool setPerformanceMode,
1724     [[maybe_unused]] bool setFanProfile, std::optional<uint8_t> dimmGroupId,
1725     [[maybe_unused]] std::optional<uint32_t> dimmPresenceBitmap)
1726 {
1727     if (reserved1 || reserved2)
1728     {
1729         return ipmi::responseInvalidFieldRequest();
1730     }
1731 
1732     if (dimmGroupId)
1733     {
1734         if (*dimmGroupId >= maxCPUNum)
1735         {
1736             return ipmi::responseInvalidFieldRequest();
1737         }
1738         if (!cpuPresent("cpu" + std::to_string(*dimmGroupId)))
1739         {
1740             return ipmi::responseInvalidFieldRequest();
1741         }
1742     }
1743 
1744     // todo: tell bios to only send first 2 bytes
1745     boost::container::flat_map<std::string, ipmi::DbusVariant> profileData;
1746     std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
1747     if (!getFanProfileInterface(*dbus, profileData))
1748     {
1749         return ipmi::responseUnspecifiedError();
1750     }
1751 
1752     std::vector<std::string>* supported =
1753         std::get_if<std::vector<std::string>>(&profileData["Supported"]);
1754     if (supported == nullptr)
1755     {
1756         return ipmi::responseInvalidFieldRequest();
1757     }
1758     std::string mode;
1759     if (setPerformanceMode)
1760     {
1761         if (performanceMode)
1762         {
1763             if (std::find(supported->begin(), supported->end(),
1764                           "Performance") != supported->end())
1765             {
1766                 mode = "Performance";
1767             }
1768         }
1769         else
1770         {
1771             if (std::find(supported->begin(), supported->end(), "Acoustic") !=
1772                 supported->end())
1773             {
1774                 mode = "Acoustic";
1775             }
1776         }
1777         if (mode.empty())
1778         {
1779             return ipmi::responseInvalidFieldRequest();
1780         }
1781 
1782         try
1783         {
1784             setDbusProperty(*dbus, settingsBusName, thermalModePath,
1785                             thermalModeInterface, "Current", mode);
1786         }
1787         catch (const sdbusplus::exception_t& e)
1788         {
1789             phosphor::logging::log<phosphor::logging::level::ERR>(
1790                 "ipmiOEMSetFanConfig: can't set thermal mode!",
1791                 phosphor::logging::entry("EXCEPTION=%s", e.what()));
1792             return ipmi::responseResponseError();
1793         }
1794     }
1795 
1796     return ipmi::responseSuccess();
1797 }
1798 
1799 ipmi::RspType<uint8_t, // profile support map
1800               uint8_t, // fan control profile enable
1801               uint8_t, // flags
1802               uint32_t // dimm presence bit map
1803               >
ipmiOEMGetFanConfig(uint8_t dimmGroupId)1804     ipmiOEMGetFanConfig(uint8_t dimmGroupId)
1805 {
1806     if (dimmGroupId >= maxCPUNum)
1807     {
1808         return ipmi::responseInvalidFieldRequest();
1809     }
1810 
1811     bool cpuStatus = cpuPresent("cpu" + std::to_string(dimmGroupId));
1812 
1813     if (!cpuStatus)
1814     {
1815         return ipmi::responseInvalidFieldRequest();
1816     }
1817 
1818     boost::container::flat_map<std::string, ipmi::DbusVariant> profileData;
1819 
1820     std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
1821     if (!getFanProfileInterface(*dbus, profileData))
1822     {
1823         return ipmi::responseResponseError();
1824     }
1825 
1826     std::string* current = std::get_if<std::string>(&profileData["Current"]);
1827 
1828     if (current == nullptr)
1829     {
1830         phosphor::logging::log<phosphor::logging::level::ERR>(
1831             "ipmiOEMGetFanConfig: can't get current mode!");
1832         return ipmi::responseResponseError();
1833     }
1834     bool performance = (*current == "Performance");
1835 
1836     uint8_t flags = 0;
1837     if (performance)
1838     {
1839         flags |= 1 << 2;
1840     }
1841 
1842     constexpr uint8_t fanControlDefaultProfile = 0x80;
1843     constexpr uint8_t fanControlProfileState = 0x00;
1844     constexpr uint32_t dimmPresenceBitmap = 0x00;
1845 
1846     return ipmi::responseSuccess(fanControlDefaultProfile,
1847                                  fanControlProfileState, flags,
1848                                  dimmPresenceBitmap);
1849 }
1850 constexpr const char* cfmLimitSettingPath =
1851     "/xyz/openbmc_project/control/cfm_limit";
1852 constexpr const char* cfmLimitIface = "xyz.openbmc_project.Control.CFMLimit";
1853 constexpr const size_t legacyExitAirSensorNumber = 0x2e;
1854 constexpr const size_t legacyPCHSensorNumber = 0x22;
1855 constexpr const char* exitAirPathName = "Exit_Air";
1856 constexpr const char* pchPathName = "SSB_Temp";
1857 constexpr const char* pidConfigurationIface =
1858     "xyz.openbmc_project.Configuration.Pid";
1859 
getConfigPath(const std::string & name)1860 static std::string getConfigPath(const std::string& name)
1861 {
1862     std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
1863     auto method =
1864         dbus->new_method_call("xyz.openbmc_project.ObjectMapper",
1865                               "/xyz/openbmc_project/object_mapper",
1866                               "xyz.openbmc_project.ObjectMapper", "GetSubTree");
1867 
1868     method.append("/", 0, std::array<const char*, 1>{pidConfigurationIface});
1869     std::string path;
1870     GetSubTreeType resp;
1871     try
1872     {
1873         auto reply = dbus->call(method);
1874         reply.read(resp);
1875     }
1876     catch (const sdbusplus::exception_t&)
1877     {
1878         phosphor::logging::log<phosphor::logging::level::ERR>(
1879             "ipmiOEMGetFscParameter: mapper error");
1880     };
1881     auto config =
1882         std::find_if(resp.begin(), resp.end(), [&name](const auto& pair) {
1883             return pair.first.find(name) != std::string::npos;
1884         });
1885     if (config != resp.end())
1886     {
1887         path = std::move(config->first);
1888     }
1889     return path;
1890 }
1891 
1892 // flat map to make alphabetical
getPidConfigs()1893 static boost::container::flat_map<std::string, PropertyMap> getPidConfigs()
1894 {
1895     boost::container::flat_map<std::string, PropertyMap> ret;
1896     std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
1897     auto method =
1898         dbus->new_method_call("xyz.openbmc_project.ObjectMapper",
1899                               "/xyz/openbmc_project/object_mapper",
1900                               "xyz.openbmc_project.ObjectMapper", "GetSubTree");
1901 
1902     method.append("/", 0, std::array<const char*, 1>{pidConfigurationIface});
1903     GetSubTreeType resp;
1904 
1905     try
1906     {
1907         auto reply = dbus->call(method);
1908         reply.read(resp);
1909     }
1910     catch (const sdbusplus::exception_t&)
1911     {
1912         phosphor::logging::log<phosphor::logging::level::ERR>(
1913             "getFanConfigPaths: mapper error");
1914     };
1915     for (const auto& [path, objects] : resp)
1916     {
1917         if (objects.empty())
1918         {
1919             continue; // should be impossible
1920         }
1921 
1922         try
1923         {
1924             ret.emplace(path,
1925                         getAllDbusProperties(*dbus, objects[0].first, path,
1926                                              pidConfigurationIface));
1927         }
1928         catch (const sdbusplus::exception_t& e)
1929         {
1930             phosphor::logging::log<phosphor::logging::level::ERR>(
1931                 "getPidConfigs: can't get DbusProperties!",
1932                 phosphor::logging::entry("ERR=%s", e.what()));
1933         }
1934     }
1935     return ret;
1936 }
1937 
ipmiOEMGetFanSpeedOffset(void)1938 ipmi::RspType<uint8_t> ipmiOEMGetFanSpeedOffset(void)
1939 {
1940     boost::container::flat_map<std::string, PropertyMap> data = getPidConfigs();
1941     if (data.empty())
1942     {
1943         return ipmi::responseResponseError();
1944     }
1945     uint8_t minOffset = std::numeric_limits<uint8_t>::max();
1946     for (const auto& [_, pid] : data)
1947     {
1948         auto findClass = pid.find("Class");
1949         if (findClass == pid.end())
1950         {
1951             phosphor::logging::log<phosphor::logging::level::ERR>(
1952                 "ipmiOEMGetFscParameter: found illegal pid "
1953                 "configurations");
1954             return ipmi::responseResponseError();
1955         }
1956         std::string type = std::get<std::string>(findClass->second);
1957         if (type == "fan")
1958         {
1959             auto findOutLimit = pid.find("OutLimitMin");
1960             if (findOutLimit == pid.end())
1961             {
1962                 phosphor::logging::log<phosphor::logging::level::ERR>(
1963                     "ipmiOEMGetFscParameter: found illegal pid "
1964                     "configurations");
1965                 return ipmi::responseResponseError();
1966             }
1967             // get the min out of all the offsets
1968             minOffset = std::min(
1969                 minOffset,
1970                 static_cast<uint8_t>(std::get<double>(findOutLimit->second)));
1971         }
1972     }
1973     if (minOffset == std::numeric_limits<uint8_t>::max())
1974     {
1975         phosphor::logging::log<phosphor::logging::level::ERR>(
1976             "ipmiOEMGetFscParameter: found no fan configurations!");
1977         return ipmi::responseResponseError();
1978     }
1979 
1980     return ipmi::responseSuccess(minOffset);
1981 }
1982 
ipmiOEMSetFanSpeedOffset(uint8_t offset)1983 ipmi::RspType<> ipmiOEMSetFanSpeedOffset(uint8_t offset)
1984 {
1985     constexpr uint8_t maxFanSpeedOffset = 100;
1986     if (offset > maxFanSpeedOffset)
1987     {
1988         phosphor::logging::log<phosphor::logging::level::ERR>(
1989             "ipmiOEMSetFanSpeedOffset: fan offset greater than limit");
1990         return ipmi::responseInvalidFieldRequest();
1991     }
1992     boost::container::flat_map<std::string, PropertyMap> data = getPidConfigs();
1993     if (data.empty())
1994     {
1995         phosphor::logging::log<phosphor::logging::level::ERR>(
1996             "ipmiOEMSetFanSpeedOffset: found no pid configurations!");
1997         return ipmi::responseResponseError();
1998     }
1999 
2000     std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
2001     bool found = false;
2002     for (const auto& [path, pid] : data)
2003     {
2004         auto findClass = pid.find("Class");
2005         if (findClass == pid.end())
2006         {
2007             phosphor::logging::log<phosphor::logging::level::ERR>(
2008                 "ipmiOEMSetFanSpeedOffset: found illegal pid "
2009                 "configurations");
2010             return ipmi::responseResponseError();
2011         }
2012         std::string type = std::get<std::string>(findClass->second);
2013         if (type == "fan")
2014         {
2015             auto findOutLimit = pid.find("OutLimitMin");
2016             if (findOutLimit == pid.end())
2017             {
2018                 phosphor::logging::log<phosphor::logging::level::ERR>(
2019                     "ipmiOEMSetFanSpeedOffset: found illegal pid "
2020                     "configurations");
2021                 return ipmi::responseResponseError();
2022             }
2023             ipmi::setDbusProperty(*dbus, "xyz.openbmc_project.EntityManager",
2024                                   path, pidConfigurationIface, "OutLimitMin",
2025                                   static_cast<double>(offset));
2026             found = true;
2027         }
2028     }
2029     if (!found)
2030     {
2031         phosphor::logging::log<phosphor::logging::level::ERR>(
2032             "ipmiOEMSetFanSpeedOffset: set no fan offsets");
2033         return ipmi::responseResponseError();
2034     }
2035 
2036     return ipmi::responseSuccess();
2037 }
2038 
ipmiOEMSetFscParameter(uint8_t command,uint8_t param1,uint8_t param2)2039 ipmi::RspType<> ipmiOEMSetFscParameter(uint8_t command, uint8_t param1,
2040                                        uint8_t param2)
2041 {
2042     constexpr const size_t disableLimiting = 0x0;
2043 
2044     std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
2045     if (command == static_cast<uint8_t>(setFscParamFlags::tcontrol))
2046     {
2047         std::string pathName;
2048         if (param1 == legacyExitAirSensorNumber)
2049         {
2050             pathName = exitAirPathName;
2051         }
2052         else if (param1 == legacyPCHSensorNumber)
2053         {
2054             pathName = pchPathName;
2055         }
2056         else
2057         {
2058             return ipmi::responseParmOutOfRange();
2059         }
2060         std::string path = getConfigPath(pathName);
2061         ipmi::setDbusProperty(*dbus, "xyz.openbmc_project.EntityManager", path,
2062                               pidConfigurationIface, "SetPoint",
2063                               static_cast<double>(param2));
2064         return ipmi::responseSuccess();
2065     }
2066     else if (command == static_cast<uint8_t>(setFscParamFlags::cfm))
2067     {
2068         uint16_t cfm = param1 | (static_cast<uint16_t>(param2) << 8);
2069 
2070         // must be greater than 50 based on eps
2071         if (cfm < 50 && cfm != disableLimiting)
2072         {
2073             return ipmi::responseParmOutOfRange();
2074         }
2075 
2076         try
2077         {
2078             ipmi::setDbusProperty(*dbus, settingsBusName, cfmLimitSettingPath,
2079                                   cfmLimitIface, "Limit",
2080                                   static_cast<double>(cfm));
2081         }
2082         catch (const sdbusplus::exception_t& e)
2083         {
2084             phosphor::logging::log<phosphor::logging::level::ERR>(
2085                 "ipmiOEMSetFscParameter: can't set cfm setting!",
2086                 phosphor::logging::entry("ERR=%s", e.what()));
2087             return ipmi::responseResponseError();
2088         }
2089         return ipmi::responseSuccess();
2090     }
2091     else if (command == static_cast<uint8_t>(setFscParamFlags::maxPwm))
2092     {
2093         uint8_t requestedDomainMask = param1;
2094         boost::container::flat_map data = getPidConfigs();
2095         if (data.empty())
2096         {
2097             phosphor::logging::log<phosphor::logging::level::ERR>(
2098                 "ipmiOEMSetFscParameter: found no pid configurations!");
2099             return ipmi::responseResponseError();
2100         }
2101         size_t count = 0;
2102         for (const auto& [path, pid] : data)
2103         {
2104             auto findClass = pid.find("Class");
2105             if (findClass == pid.end())
2106             {
2107                 phosphor::logging::log<phosphor::logging::level::ERR>(
2108                     "ipmiOEMSetFscParameter: found illegal pid "
2109                     "configurations");
2110                 return ipmi::responseResponseError();
2111             }
2112             std::string type = std::get<std::string>(findClass->second);
2113             if (type == "fan")
2114             {
2115                 if (requestedDomainMask & (1 << count))
2116                 {
2117                     ipmi::setDbusProperty(
2118                         *dbus, "xyz.openbmc_project.EntityManager", path,
2119                         pidConfigurationIface, "OutLimitMax",
2120                         static_cast<double>(param2));
2121                 }
2122                 count++;
2123             }
2124         }
2125         return ipmi::responseSuccess();
2126     }
2127     else
2128     {
2129         // todo other command parts possibly
2130         // tcontrol is handled in peci now
2131         // fan speed offset not implemented yet
2132         // domain pwm limit not implemented
2133         return ipmi::responseParmOutOfRange();
2134     }
2135 }
2136 
2137 ipmi::RspType<
2138     std::variant<uint8_t, std::array<uint8_t, 2>, std::array<uint16_t, 2>>>
ipmiOEMGetFscParameter(uint8_t command,std::optional<uint8_t> param)2139     ipmiOEMGetFscParameter(uint8_t command, std::optional<uint8_t> param)
2140 {
2141     constexpr uint8_t legacyDefaultSetpoint = -128;
2142 
2143     std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
2144     if (command == static_cast<uint8_t>(setFscParamFlags::tcontrol))
2145     {
2146         if (!param)
2147         {
2148             return ipmi::responseReqDataLenInvalid();
2149         }
2150 
2151         std::string pathName;
2152 
2153         if (*param == legacyExitAirSensorNumber)
2154         {
2155             pathName = exitAirPathName;
2156         }
2157         else if (*param == legacyPCHSensorNumber)
2158         {
2159             pathName = pchPathName;
2160         }
2161         else
2162         {
2163             return ipmi::responseParmOutOfRange();
2164         }
2165 
2166         uint8_t setpoint = legacyDefaultSetpoint;
2167         std::string path = getConfigPath(pathName);
2168         if (path.size())
2169         {
2170             Value val = ipmi::getDbusProperty(
2171                 *dbus, "xyz.openbmc_project.EntityManager", path,
2172                 pidConfigurationIface, "SetPoint");
2173             setpoint = std::floor(std::get<double>(val) + 0.5);
2174         }
2175 
2176         // old implementation used to return the "default" and current, we
2177         // don't make the default readily available so just make both the
2178         // same
2179 
2180         return ipmi::responseSuccess(
2181             std::array<uint8_t, 2>{setpoint, setpoint});
2182     }
2183     else if (command == static_cast<uint8_t>(setFscParamFlags::maxPwm))
2184     {
2185         constexpr const size_t maxDomainCount = 8;
2186 
2187         if (!param)
2188         {
2189             return ipmi::responseReqDataLenInvalid();
2190         }
2191         uint8_t requestedDomain = *param;
2192         if (requestedDomain >= maxDomainCount)
2193         {
2194             return ipmi::responseInvalidFieldRequest();
2195         }
2196 
2197         boost::container::flat_map data = getPidConfigs();
2198         if (data.empty())
2199         {
2200             phosphor::logging::log<phosphor::logging::level::ERR>(
2201                 "ipmiOEMGetFscParameter: found no pid configurations!");
2202             return ipmi::responseResponseError();
2203         }
2204         size_t count = 0;
2205         for (const auto& [_, pid] : data)
2206         {
2207             auto findClass = pid.find("Class");
2208             if (findClass == pid.end())
2209             {
2210                 phosphor::logging::log<phosphor::logging::level::ERR>(
2211                     "ipmiOEMGetFscParameter: found illegal pid "
2212                     "configurations");
2213                 return ipmi::responseResponseError();
2214             }
2215             std::string type = std::get<std::string>(findClass->second);
2216             if (type == "fan")
2217             {
2218                 if (requestedDomain == count)
2219                 {
2220                     auto findOutLimit = pid.find("OutLimitMax");
2221                     if (findOutLimit == pid.end())
2222                     {
2223                         phosphor::logging::log<phosphor::logging::level::ERR>(
2224                             "ipmiOEMGetFscParameter: found illegal pid "
2225                             "configurations");
2226                         return ipmi::responseResponseError();
2227                     }
2228 
2229                     return ipmi::responseSuccess(
2230                         static_cast<uint8_t>(std::floor(
2231                             std::get<double>(findOutLimit->second) + 0.5)));
2232                 }
2233                 else
2234                 {
2235                     count++;
2236                 }
2237             }
2238         }
2239 
2240         return ipmi::responseInvalidFieldRequest();
2241     }
2242     else if (command == static_cast<uint8_t>(setFscParamFlags::cfm))
2243     {
2244         /*
2245         DataLen should be 1, but host is sending us an extra bit. As the
2246         previous behavior didn't seem to prevent this, ignore the check for
2247         now.
2248 
2249         if (param)
2250         {
2251             phosphor::logging::log<phosphor::logging::level::ERR>(
2252                 "ipmiOEMGetFscParameter: invalid input len!");
2253             return ipmi::ccReqDataLenInvalid;
2254         }
2255         */
2256         Value cfmLimit;
2257         Value cfmMaximum;
2258         try
2259         {
2260             cfmLimit = ipmi::getDbusProperty(*dbus, settingsBusName,
2261                                              cfmLimitSettingPath, cfmLimitIface,
2262                                              "Limit");
2263             cfmMaximum = ipmi::getDbusProperty(
2264                 *dbus, "xyz.openbmc_project.ExitAirTempSensor",
2265                 "/xyz/openbmc_project/control/MaxCFM", cfmLimitIface, "Limit");
2266         }
2267         catch (const sdbusplus::exception_t& e)
2268         {
2269             phosphor::logging::log<phosphor::logging::level::ERR>(
2270                 "ipmiOEMGetFscParameter: can't get cfm setting!",
2271                 phosphor::logging::entry("ERR=%s", e.what()));
2272             return ipmi::responseResponseError();
2273         }
2274 
2275         double cfmMax = std::get<double>(cfmMaximum);
2276         double cfmLim = std::get<double>(cfmLimit);
2277 
2278         cfmLim = std::floor(cfmLim + 0.5);
2279         cfmMax = std::floor(cfmMax + 0.5);
2280         uint16_t cfmLimResp = static_cast<uint16_t>(cfmLim);
2281         uint16_t cfmMaxResp = static_cast<uint16_t>(cfmMax);
2282 
2283         return ipmi::responseSuccess(
2284             std::array<uint16_t, 2>{cfmLimResp, cfmMaxResp});
2285     }
2286 
2287     else
2288     {
2289         // todo other command parts possibly
2290         // domain pwm limit not implemented
2291         return ipmi::responseParmOutOfRange();
2292     }
2293 }
2294 
2295 using crConfigVariant = ipmi::DbusVariant;
2296 
setCRConfig(ipmi::Context::ptr & ctx,const std::string & property,const crConfigVariant & value,std::chrono::microseconds timeout=ipmi::IPMI_DBUS_TIMEOUT)2297 int setCRConfig(ipmi::Context::ptr& ctx, const std::string& property,
2298                 const crConfigVariant& value,
2299                 [[maybe_unused]] std::chrono::microseconds timeout =
2300                     ipmi::IPMI_DBUS_TIMEOUT)
2301 {
2302     boost::system::error_code ec;
2303     ctx->bus->yield_method_call<void>(
2304         ctx->yield, ec, "xyz.openbmc_project.PSURedundancy",
2305         "/xyz/openbmc_project/control/power_supply_redundancy",
2306         "org.freedesktop.DBus.Properties", "Set",
2307         "xyz.openbmc_project.Control.PowerSupplyRedundancy", property, value);
2308     if (ec)
2309     {
2310         phosphor::logging::log<phosphor::logging::level::ERR>(
2311             "Failed to set dbus property to cold redundancy");
2312         return -1;
2313     }
2314 
2315     return 0;
2316 }
2317 
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)2318 int getCRConfig(
2319     ipmi::Context::ptr& ctx, const std::string& property,
2320     crConfigVariant& value,
2321     const std::string& service = "xyz.openbmc_project.PSURedundancy",
2322     [[maybe_unused]] std::chrono::microseconds timeout =
2323         ipmi::IPMI_DBUS_TIMEOUT)
2324 {
2325     boost::system::error_code ec;
2326     value = ctx->bus->yield_method_call<crConfigVariant>(
2327         ctx->yield, ec, service,
2328         "/xyz/openbmc_project/control/power_supply_redundancy",
2329         "org.freedesktop.DBus.Properties", "Get",
2330         "xyz.openbmc_project.Control.PowerSupplyRedundancy", property);
2331     if (ec)
2332     {
2333         phosphor::logging::log<phosphor::logging::level::ERR>(
2334             "Failed to get dbus property to cold redundancy");
2335         return -1;
2336     }
2337     return 0;
2338 }
2339 
getPSUCount(void)2340 uint8_t getPSUCount(void)
2341 {
2342     std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
2343     ipmi::Value num;
2344     try
2345     {
2346         num = ipmi::getDbusProperty(
2347             *dbus, "xyz.openbmc_project.PSURedundancy",
2348             "/xyz/openbmc_project/control/power_supply_redundancy",
2349             "xyz.openbmc_project.Control.PowerSupplyRedundancy", "PSUNumber");
2350     }
2351     catch (const sdbusplus::exception_t& e)
2352     {
2353         phosphor::logging::log<phosphor::logging::level::ERR>(
2354             "Failed to get PSUNumber property from dbus interface");
2355         return 0;
2356     }
2357     uint8_t* pNum = std::get_if<uint8_t>(&num);
2358     if (!pNum)
2359     {
2360         phosphor::logging::log<phosphor::logging::level::ERR>(
2361             "Error to get PSU Number");
2362         return 0;
2363     }
2364     return *pNum;
2365 }
2366 
validateCRAlgo(std::vector<uint8_t> & conf,uint8_t num)2367 bool validateCRAlgo(std::vector<uint8_t>& conf, uint8_t num)
2368 {
2369     if (conf.size() < num)
2370     {
2371         phosphor::logging::log<phosphor::logging::level::ERR>(
2372             "Invalid PSU Ranking");
2373         return false;
2374     }
2375     std::set<uint8_t> confSet;
2376     for (uint8_t i = 0; i < num; i++)
2377     {
2378         if (conf[i] > num)
2379         {
2380             phosphor::logging::log<phosphor::logging::level::ERR>(
2381                 "PSU Ranking is larger than current PSU number");
2382             return false;
2383         }
2384         confSet.emplace(conf[i]);
2385     }
2386 
2387     if (confSet.size() != num)
2388     {
2389         phosphor::logging::log<phosphor::logging::level::ERR>(
2390             "duplicate PSU Ranking");
2391         return false;
2392     }
2393     return true;
2394 }
2395 
2396 enum class crParameter
2397 {
2398     crStatus = 0,
2399     crFeature = 1,
2400     rotationFeature = 2,
2401     rotationAlgo = 3,
2402     rotationPeriod = 4,
2403     numOfPSU = 5,
2404     rotationRankOrderEffective = 6
2405 };
2406 
2407 constexpr ipmi::Cc ccParameterNotSupported = 0x80;
2408 static const constexpr uint32_t oneDay = 0x15180;
2409 static const constexpr uint32_t oneMonth = 0xf53700;
2410 static const constexpr uint8_t userSpecific = 0x01;
2411 static const constexpr uint8_t crSetCompleted = 0;
ipmiOEMSetCRConfig(ipmi::Context::ptr & ctx,uint8_t parameter,ipmi::message::Payload & payload)2412 ipmi::RspType<uint8_t> ipmiOEMSetCRConfig(
2413     ipmi::Context::ptr& ctx, uint8_t parameter, ipmi::message::Payload& payload)
2414 {
2415     switch (static_cast<crParameter>(parameter))
2416     {
2417         case crParameter::rotationFeature:
2418         {
2419             uint8_t param1;
2420             if (payload.unpack(param1) || !payload.fullyUnpacked())
2421             {
2422                 return ipmi::responseReqDataLenInvalid();
2423             }
2424             // Rotation Enable can only be true or false
2425             if (param1 > 1)
2426             {
2427                 return ipmi::responseInvalidFieldRequest();
2428             }
2429             if (setCRConfig(ctx, "RotationEnabled", static_cast<bool>(param1)))
2430             {
2431                 return ipmi::responseResponseError();
2432             }
2433             break;
2434         }
2435         case crParameter::rotationAlgo:
2436         {
2437             // Rotation Algorithm can only be 0-BMC Specific or 1-User Specific
2438             std::string algoName;
2439             uint8_t param1;
2440             if (payload.unpack(param1))
2441             {
2442                 return ipmi::responseReqDataLenInvalid();
2443             }
2444             switch (param1)
2445             {
2446                 case 0:
2447                     algoName = "xyz.openbmc_project.Control."
2448                                "PowerSupplyRedundancy.Algo.bmcSpecific";
2449                     break;
2450                 case 1:
2451                     algoName = "xyz.openbmc_project.Control."
2452                                "PowerSupplyRedundancy.Algo.userSpecific";
2453                     break;
2454                 default:
2455                     return ipmi::responseInvalidFieldRequest();
2456             }
2457             if (setCRConfig(ctx, "RotationAlgorithm", algoName))
2458             {
2459                 return ipmi::responseResponseError();
2460             }
2461 
2462             uint8_t numberOfPSU = getPSUCount();
2463             if (!numberOfPSU)
2464             {
2465                 return ipmi::responseResponseError();
2466             }
2467             std::vector<uint8_t> rankOrder;
2468 
2469             if (param1 == userSpecific)
2470             {
2471                 if (payload.unpack(rankOrder) || !payload.fullyUnpacked())
2472                 {
2473                     ipmi::responseReqDataLenInvalid();
2474                 }
2475                 if (rankOrder.size() != numberOfPSU)
2476                 {
2477                     return ipmi::responseReqDataLenInvalid();
2478                 }
2479 
2480                 if (!validateCRAlgo(rankOrder, numberOfPSU))
2481                 {
2482                     return ipmi::responseInvalidFieldRequest();
2483                 }
2484             }
2485             else
2486             {
2487                 if (rankOrder.size() > 0)
2488                 {
2489                     return ipmi::responseReqDataLenInvalid();
2490                 }
2491                 for (uint8_t i = 1; i <= numberOfPSU; i++)
2492                 {
2493                     rankOrder.emplace_back(i);
2494                 }
2495             }
2496             if (setCRConfig(ctx, "RotationRankOrder", rankOrder))
2497             {
2498                 return ipmi::responseResponseError();
2499             }
2500             break;
2501         }
2502         case crParameter::rotationPeriod:
2503         {
2504             // Minimum Rotation period is  One day (86400 seconds) and Max
2505             // Rotation Period is 6 month (0xf53700 seconds)
2506             uint32_t period;
2507             if (payload.unpack(period) || !payload.fullyUnpacked())
2508             {
2509                 return ipmi::responseReqDataLenInvalid();
2510             }
2511             if ((period < oneDay) || (period > oneMonth))
2512             {
2513                 return ipmi::responseInvalidFieldRequest();
2514             }
2515             if (setCRConfig(ctx, "PeriodOfRotation", period))
2516             {
2517                 return ipmi::responseResponseError();
2518             }
2519             break;
2520         }
2521         default:
2522         {
2523             return ipmi::response(ccParameterNotSupported);
2524         }
2525     }
2526 
2527     return ipmi::responseSuccess(crSetCompleted);
2528 }
2529 
2530 ipmi::RspType<uint8_t, std::variant<uint8_t, uint32_t, std::vector<uint8_t>>>
ipmiOEMGetCRConfig(ipmi::Context::ptr & ctx,uint8_t parameter)2531     ipmiOEMGetCRConfig(ipmi::Context::ptr& ctx, uint8_t parameter)
2532 {
2533     crConfigVariant value;
2534     switch (static_cast<crParameter>(parameter))
2535     {
2536         case crParameter::crStatus:
2537         {
2538             if (getCRConfig(ctx, "ColdRedundancyStatus", value))
2539             {
2540                 return ipmi::responseResponseError();
2541             }
2542             std::string* pStatus = std::get_if<std::string>(&value);
2543             if (!pStatus)
2544             {
2545                 phosphor::logging::log<phosphor::logging::level::ERR>(
2546                     "Error to get ColdRedundancyStatus property");
2547                 return ipmi::responseResponseError();
2548             }
2549             namespace server = sdbusplus::xyz::openbmc_project::Control::server;
2550             auto status =
2551                 server::PowerSupplyRedundancy::convertStatusFromString(
2552                     *pStatus);
2553             switch (status)
2554             {
2555                 case server::PowerSupplyRedundancy::Status::inProgress:
2556                     return ipmi::responseSuccess(parameter,
2557                                                  static_cast<uint8_t>(1));
2558 
2559                 case server::PowerSupplyRedundancy::Status::completed:
2560                     return ipmi::responseSuccess(parameter,
2561                                                  static_cast<uint8_t>(0));
2562                 default:
2563                     phosphor::logging::log<phosphor::logging::level::ERR>(
2564                         "Error to get valid status");
2565                     return ipmi::responseResponseError();
2566             }
2567         }
2568         case crParameter::crFeature:
2569         {
2570             if (getCRConfig(ctx, "PowerSupplyRedundancyEnabled", value))
2571             {
2572                 return ipmi::responseResponseError();
2573             }
2574             bool* pResponse = std::get_if<bool>(&value);
2575             if (!pResponse)
2576             {
2577                 phosphor::logging::log<phosphor::logging::level::ERR>(
2578                     "Error to get PowerSupplyRedundancyEnabled property");
2579                 return ipmi::responseResponseError();
2580             }
2581 
2582             return ipmi::responseSuccess(parameter,
2583                                          static_cast<uint8_t>(*pResponse));
2584         }
2585         case crParameter::rotationFeature:
2586         {
2587             if (getCRConfig(ctx, "RotationEnabled", value))
2588             {
2589                 return ipmi::responseResponseError();
2590             }
2591             bool* pResponse = std::get_if<bool>(&value);
2592             if (!pResponse)
2593             {
2594                 phosphor::logging::log<phosphor::logging::level::ERR>(
2595                     "Error to get RotationEnabled property");
2596                 return ipmi::responseResponseError();
2597             }
2598             return ipmi::responseSuccess(parameter,
2599                                          static_cast<uint8_t>(*pResponse));
2600         }
2601         case crParameter::rotationAlgo:
2602         {
2603             if (getCRConfig(ctx, "RotationAlgorithm", value))
2604             {
2605                 return ipmi::responseResponseError();
2606             }
2607 
2608             std::string* pAlgo = std::get_if<std::string>(&value);
2609             if (!pAlgo)
2610             {
2611                 phosphor::logging::log<phosphor::logging::level::ERR>(
2612                     "Error to get RotationAlgorithm property");
2613                 return ipmi::responseResponseError();
2614             }
2615             std::vector<uint8_t> response;
2616             namespace server = sdbusplus::xyz::openbmc_project::Control::server;
2617             auto algo =
2618                 server::PowerSupplyRedundancy::convertAlgoFromString(*pAlgo);
2619 
2620             switch (algo)
2621             {
2622                 case server::PowerSupplyRedundancy::Algo::bmcSpecific:
2623                     response.push_back(0);
2624                     break;
2625                 case server::PowerSupplyRedundancy::Algo::userSpecific:
2626                     response.push_back(1);
2627                     break;
2628                 default:
2629                     phosphor::logging::log<phosphor::logging::level::ERR>(
2630                         "Error to get valid algo");
2631                     return ipmi::responseResponseError();
2632             }
2633 
2634             if (getCRConfig(ctx, "RotationRankOrder", value))
2635             {
2636                 return ipmi::responseResponseError();
2637             }
2638             std::vector<uint8_t>* pResponse =
2639                 std::get_if<std::vector<uint8_t>>(&value);
2640             if (!pResponse)
2641             {
2642                 phosphor::logging::log<phosphor::logging::level::ERR>(
2643                     "Error to get RotationRankOrder property");
2644                 return ipmi::responseResponseError();
2645             }
2646 
2647             std::copy(pResponse->begin(), pResponse->end(),
2648                       std::back_inserter(response));
2649 
2650             return ipmi::responseSuccess(parameter, response);
2651         }
2652         case crParameter::rotationPeriod:
2653         {
2654             if (getCRConfig(ctx, "PeriodOfRotation", value))
2655             {
2656                 return ipmi::responseResponseError();
2657             }
2658             uint32_t* pResponse = std::get_if<uint32_t>(&value);
2659             if (!pResponse)
2660             {
2661                 phosphor::logging::log<phosphor::logging::level::ERR>(
2662                     "Error to get RotationAlgorithm property");
2663                 return ipmi::responseResponseError();
2664             }
2665             return ipmi::responseSuccess(parameter, *pResponse);
2666         }
2667         case crParameter::numOfPSU:
2668         {
2669             uint8_t numberOfPSU = getPSUCount();
2670             if (!numberOfPSU)
2671             {
2672                 return ipmi::responseResponseError();
2673             }
2674             return ipmi::responseSuccess(parameter, numberOfPSU);
2675         }
2676         case crParameter::rotationRankOrderEffective:
2677         {
2678             if (getCRConfig(ctx, "RotationRankOrder", value,
2679                             "xyz.openbmc_project.PSURedundancy"))
2680             {
2681                 return ipmi::responseResponseError();
2682             }
2683             std::vector<uint8_t>* pResponse =
2684                 std::get_if<std::vector<uint8_t>>(&value);
2685             if (!pResponse)
2686             {
2687                 phosphor::logging::log<phosphor::logging::level::ERR>(
2688                     "Error to get effective RotationRankOrder property");
2689                 return ipmi::responseResponseError();
2690             }
2691             return ipmi::responseSuccess(parameter, *pResponse);
2692         }
2693         default:
2694         {
2695             return ipmi::response(ccParameterNotSupported);
2696         }
2697     }
2698 }
2699 
ipmiOEMSetFaultIndication(uint8_t sourceId,uint8_t faultType,uint8_t faultState,uint8_t faultGroup,std::array<uint8_t,8> & ledStateData)2700 ipmi::RspType<> ipmiOEMSetFaultIndication(
2701     uint8_t sourceId, uint8_t faultType, uint8_t faultState, uint8_t faultGroup,
2702     std::array<uint8_t, 8>& ledStateData)
2703 {
2704     constexpr auto maxFaultType = static_cast<size_t>(RemoteFaultType::max);
2705     static const std::array<std::string, maxFaultType> faultNames = {
2706         "faultFan",       "faultTemp",     "faultPower",
2707         "faultDriveSlot", "faultSoftware", "faultMemory"};
2708 
2709     constexpr uint8_t maxFaultSource = 0x4;
2710     constexpr uint8_t skipLEDs = 0xFF;
2711     constexpr uint8_t pinSize = 64;
2712     constexpr uint8_t groupSize = 16;
2713     constexpr uint8_t groupNum = 5; // 4 for fault memory, 1 for faultFan
2714 
2715     // same pin names need to be defined in dts file
2716     static const std::array<std::array<std::string, groupSize>, groupNum>
2717         faultLedPinNames = {{
2718             "LED_CPU1_CH1_DIMM1_FAULT",
2719             "LED_CPU1_CH1_DIMM2_FAULT",
2720             "LED_CPU1_CH2_DIMM1_FAULT",
2721             "LED_CPU1_CH2_DIMM2_FAULT",
2722             "LED_CPU1_CH3_DIMM1_FAULT",
2723             "LED_CPU1_CH3_DIMM2_FAULT",
2724             "LED_CPU1_CH4_DIMM1_FAULT",
2725             "LED_CPU1_CH4_DIMM2_FAULT",
2726             "LED_CPU1_CH5_DIMM1_FAULT",
2727             "LED_CPU1_CH5_DIMM2_FAULT",
2728             "LED_CPU1_CH6_DIMM1_FAULT",
2729             "LED_CPU1_CH6_DIMM2_FAULT",
2730             "",
2731             "",
2732             "",
2733             "", // end of group1
2734             "LED_CPU2_CH1_DIMM1_FAULT",
2735             "LED_CPU2_CH1_DIMM2_FAULT",
2736             "LED_CPU2_CH2_DIMM1_FAULT",
2737             "LED_CPU2_CH2_DIMM2_FAULT",
2738             "LED_CPU2_CH3_DIMM1_FAULT",
2739             "LED_CPU2_CH3_DIMM2_FAULT",
2740             "LED_CPU2_CH4_DIMM1_FAULT",
2741             "LED_CPU2_CH4_DIMM2_FAULT",
2742             "LED_CPU2_CH5_DIMM1_FAULT",
2743             "LED_CPU2_CH5_DIMM2_FAULT",
2744             "LED_CPU2_CH6_DIMM1_FAULT",
2745             "LED_CPU2_CH6_DIMM2_FAULT",
2746             "",
2747             "",
2748             "",
2749             "", // endof group2
2750             "LED_CPU3_CH1_DIMM1_FAULT",
2751             "LED_CPU3_CH1_DIMM2_FAULT",
2752             "LED_CPU3_CH2_DIMM1_FAULT",
2753             "LED_CPU3_CH2_DIMM2_FAULT",
2754             "LED_CPU3_CH3_DIMM1_FAULT",
2755             "LED_CPU3_CH3_DIMM2_FAULT",
2756             "LED_CPU3_CH4_DIMM1_FAULT",
2757             "LED_CPU3_CH4_DIMM2_FAULT",
2758             "LED_CPU3_CH5_DIMM1_FAULT",
2759             "LED_CPU3_CH5_DIMM2_FAULT",
2760             "LED_CPU3_CH6_DIMM1_FAULT",
2761             "LED_CPU3_CH6_DIMM2_FAULT",
2762             "",
2763             "",
2764             "",
2765             "", // end of group3
2766             "LED_CPU4_CH1_DIMM1_FAULT",
2767             "LED_CPU4_CH1_DIMM2_FAULT",
2768             "LED_CPU4_CH2_DIMM1_FAULT",
2769             "LED_CPU4_CH2_DIMM2_FAULT",
2770             "LED_CPU4_CH3_DIMM1_FAULT",
2771             "LED_CPU4_CH3_DIMM2_FAULT",
2772             "LED_CPU4_CH4_DIMM1_FAULT",
2773             "LED_CPU4_CH4_DIMM2_FAULT",
2774             "LED_CPU4_CH5_DIMM1_FAULT",
2775             "LED_CPU4_CH5_DIMM2_FAULT",
2776             "LED_CPU4_CH6_DIMM1_FAULT",
2777             "LED_CPU4_CH6_DIMM2_FAULT",
2778             "",
2779             "",
2780             "",
2781             "", // end of group4
2782             "LED_FAN1_FAULT",
2783             "LED_FAN2_FAULT",
2784             "LED_FAN3_FAULT",
2785             "LED_FAN4_FAULT",
2786             "LED_FAN5_FAULT",
2787             "LED_FAN6_FAULT",
2788             "LED_FAN7_FAULT",
2789             "LED_FAN8_FAULT",
2790             "",
2791             "",
2792             "",
2793             "",
2794             "",
2795             "",
2796             "",
2797             "" // end of group5
2798         }};
2799 
2800     // Validate the source, fault type --
2801     // (Byte 1) sourceId: Unspecified, Hot-Swap Controller 0, Hot-Swap
2802     // Controller 1, BIOS (Byte 2) fault type: fan, temperature, power,
2803     // driveslot, software, memory (Byte 3) FaultState: OK, Degraded,
2804     // Non-Critical, Critical, Non-Recoverable, (Byte 4) is faultGroup,
2805     // definition differs based on fault type (Byte 2)
2806     //          Type Fan=> Group: 0=FanGroupID, FF-not used
2807     //                  Byte 5-11 00h, not used
2808     //                  Byte12 FanLedState [7:0]-Fans 7:0
2809     //          Type Memory=> Group: 0 = DIMM GroupID, FF-not used
2810     //                  Byte 5:12 - DIMM LED state (64bit field, LS Byte first)
2811     //                  [63:48] = CPU4 channels 7:0, 2 bits per channel
2812     //                  [47:32] = CPU3 channels 7:0, 2 bits per channel
2813     //                  [31:16] = CPU2 channels 7:0, 2 bits per channel
2814     //                  [15:0] =  CPU1 channels 7:0, 2 bits per channel
2815     //          Type Other=> Component Fault LED Group ID, not used set to 0xFF
2816     //                  Byte[5:12]: reserved 0x00h
2817     if ((sourceId >= maxFaultSource) ||
2818         (faultType >= static_cast<int8_t>(RemoteFaultType::max)) ||
2819         (faultState >= static_cast<int8_t>(RemoteFaultState::maxFaultState)) ||
2820         (faultGroup >= static_cast<int8_t>(DimmFaultType::maxFaultGroup)))
2821     {
2822         return ipmi::responseParmOutOfRange();
2823     }
2824 
2825     size_t pinGroupOffset = 0;
2826     size_t pinGroupMax = pinSize / groupSize;
2827     if (RemoteFaultType::fan == RemoteFaultType(faultType))
2828     {
2829         pinGroupOffset = 4;
2830         pinGroupMax = groupNum - pinSize / groupSize;
2831     }
2832 
2833     switch (RemoteFaultType(faultType))
2834     {
2835         case (RemoteFaultType::fan):
2836         case (RemoteFaultType::memory):
2837         {
2838             if (faultGroup == skipLEDs)
2839             {
2840                 return ipmi::responseSuccess();
2841             }
2842             // calculate led state bit filed count, each byte has 8bits
2843             // the maximum bits will be 8 * 8 bits
2844             constexpr uint8_t size = sizeof(ledStateData) * 8;
2845 
2846             // assemble ledState
2847             uint64_t ledState = 0;
2848             bool hasError = false;
2849             for (size_t i = 0; i < sizeof(ledStateData); i++)
2850             {
2851                 ledState = (uint64_t)(ledState << 8);
2852                 ledState = (uint64_t)(ledState | (uint64_t)ledStateData[i]);
2853             }
2854             std::bitset<size> ledStateBits(ledState);
2855 
2856             for (size_t group = 0; group < pinGroupMax; group++)
2857             {
2858                 for (int i = 0; i < groupSize; i++)
2859                 { // skip non-existing pins
2860                     if (0 == faultLedPinNames[group + pinGroupOffset][i].size())
2861                     {
2862                         continue;
2863                     }
2864 
2865                     gpiod::line line = gpiod::find_line(
2866                         faultLedPinNames[group + pinGroupOffset][i]);
2867                     if (!line)
2868                     {
2869                         phosphor::logging::log<phosphor::logging::level::ERR>(
2870                             "Not Find Led Gpio Device!",
2871                             phosphor::logging::entry(
2872                                 "DEVICE=%s",
2873                                 faultLedPinNames[group + pinGroupOffset][i]
2874                                     .c_str()));
2875                         hasError = true;
2876                         continue;
2877                     }
2878 
2879                     bool activeHigh =
2880                         (line.active_state() == gpiod::line::ACTIVE_HIGH);
2881                     try
2882                     {
2883                         line.request(
2884                             {"faultLed", gpiod::line_request::DIRECTION_OUTPUT,
2885                              activeHigh
2886                                  ? 0
2887                                  : gpiod::line_request::FLAG_ACTIVE_LOW});
2888                         line.set_value(ledStateBits[i + group * groupSize]);
2889                     }
2890                     catch (const std::system_error&)
2891                     {
2892                         phosphor::logging::log<phosphor::logging::level::ERR>(
2893                             "Error write Led Gpio Device!",
2894                             phosphor::logging::entry(
2895                                 "DEVICE=%s",
2896                                 faultLedPinNames[group + pinGroupOffset][i]
2897                                     .c_str()));
2898                         hasError = true;
2899                         continue;
2900                     }
2901                 } // for int i
2902             }
2903             if (hasError)
2904             {
2905                 return ipmi::responseResponseError();
2906             }
2907             break;
2908         }
2909         default:
2910         {
2911             // now only support two fault types
2912             return ipmi::responseParmOutOfRange();
2913         }
2914     } // switch
2915     return ipmi::responseSuccess();
2916 }
2917 
ipmiOEMReadBoardProductId()2918 ipmi::RspType<uint8_t> ipmiOEMReadBoardProductId()
2919 {
2920     uint8_t prodId = 0;
2921     try
2922     {
2923         std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
2924         const DbusObjectInfo& object = getDbusObject(
2925             *dbus, "xyz.openbmc_project.Inventory.Item.Board",
2926             "/xyz/openbmc_project/inventory/system/board/", "Baseboard");
2927         const Value& propValue = getDbusProperty(
2928             *dbus, object.second, object.first,
2929             "xyz.openbmc_project.Inventory.Item.Board.Motherboard",
2930             "ProductId");
2931         prodId = static_cast<uint8_t>(std::get<uint64_t>(propValue));
2932     }
2933     catch (const std::exception& e)
2934     {
2935         phosphor::logging::log<phosphor::logging::level::ERR>(
2936             "ipmiOEMReadBoardProductId: Product ID read failed!",
2937             phosphor::logging::entry("ERR=%s", e.what()));
2938     }
2939     return ipmi::responseSuccess(prodId);
2940 }
2941 
2942 /** @brief implements the get security mode command
2943  *  @param ctx - ctx pointer
2944  *
2945  *  @returns IPMI completion code with following data
2946  *   - restriction mode value - As specified in
2947  * xyz.openbmc_project.Control.Security.RestrictionMode.interface.yaml
2948  *   - special mode value - As specified in
2949  * xyz.openbmc_project.Control.Security.SpecialMode.interface.yaml
2950  */
ipmiGetSecurityMode(ipmi::Context::ptr & ctx)2951 ipmi::RspType<uint8_t, uint8_t> ipmiGetSecurityMode(ipmi::Context::ptr& ctx)
2952 {
2953     namespace securityNameSpace =
2954         sdbusplus::xyz::openbmc_project::Control::Security::server;
2955     uint8_t restrictionModeValue = 0;
2956     uint8_t specialModeValue = 0;
2957 
2958     boost::system::error_code ec;
2959     auto varRestrMode = ctx->bus->yield_method_call<ipmi::DbusVariant>(
2960         ctx->yield, ec, restricionModeService, restricionModeBasePath,
2961         dBusPropertyIntf, dBusPropertyGetMethod, restricionModeIntf,
2962         restricionModeProperty);
2963     if (ec)
2964     {
2965         phosphor::logging::log<phosphor::logging::level::ERR>(
2966             "ipmiGetSecurityMode: failed to get RestrictionMode property",
2967             phosphor::logging::entry("ERROR=%s", ec.message().c_str()));
2968         return ipmi::responseUnspecifiedError();
2969     }
2970     restrictionModeValue = static_cast<uint8_t>(
2971         securityNameSpace::RestrictionMode::convertModesFromString(
2972             std::get<std::string>(varRestrMode)));
2973     auto varSpecialMode = ctx->bus->yield_method_call<ipmi::DbusVariant>(
2974         ctx->yield, ec, specialModeService, specialModeBasePath,
2975         dBusPropertyIntf, dBusPropertyGetMethod, specialModeIntf,
2976         specialModeProperty);
2977     if (ec)
2978     {
2979         phosphor::logging::log<phosphor::logging::level::ERR>(
2980             "ipmiGetSecurityMode: failed to get SpecialMode property",
2981             phosphor::logging::entry("ERROR=%s", ec.message().c_str()));
2982         // fall through, let us not worry about SpecialMode property, which is
2983         // not required in user scenario
2984     }
2985     else
2986     {
2987         specialModeValue = static_cast<uint8_t>(
2988             securityNameSpace::SpecialMode::convertModesFromString(
2989                 std::get<std::string>(varSpecialMode)));
2990     }
2991     return ipmi::responseSuccess(restrictionModeValue, specialModeValue);
2992 }
2993 
2994 /** @brief implements the set security mode command
2995  *  Command allows to upgrade the restriction mode and won't allow
2996  *  to downgrade from system interface
2997  *  @param ctx - ctx pointer
2998  *  @param restrictionMode - restriction mode value to be set.
2999  *
3000  *  @returns IPMI completion code
3001  */
ipmiSetSecurityMode(ipmi::Context::ptr & ctx,uint8_t restrictionMode,std::optional<uint8_t> specialMode)3002 ipmi::RspType<> ipmiSetSecurityMode(ipmi::Context::ptr& ctx,
3003                                     uint8_t restrictionMode,
3004                                     std::optional<uint8_t> specialMode)
3005 {
3006 #ifndef BMC_VALIDATION_UNSECURE_FEATURE
3007     if (specialMode)
3008     {
3009         return ipmi::responseReqDataLenInvalid();
3010     }
3011 #endif
3012     namespace securityNameSpace =
3013         sdbusplus::xyz::openbmc_project::Control::Security::server;
3014 
3015     ChannelInfo chInfo;
3016     if (getChannelInfo(ctx->channel, chInfo) != ccSuccess)
3017     {
3018         phosphor::logging::log<phosphor::logging::level::ERR>(
3019             "ipmiSetSecurityMode: Failed to get Channel Info",
3020             phosphor::logging::entry("CHANNEL=%d", ctx->channel));
3021         return ipmi::responseUnspecifiedError();
3022     }
3023     auto reqMode =
3024         static_cast<securityNameSpace::RestrictionMode::Modes>(restrictionMode);
3025 
3026     if ((reqMode < securityNameSpace::RestrictionMode::Modes::Provisioning) ||
3027         (reqMode >
3028          securityNameSpace::RestrictionMode::Modes::ProvisionedHostDisabled))
3029     {
3030         return ipmi::responseInvalidFieldRequest();
3031     }
3032 
3033     boost::system::error_code ec;
3034     auto varRestrMode = ctx->bus->yield_method_call<ipmi::DbusVariant>(
3035         ctx->yield, ec, restricionModeService, restricionModeBasePath,
3036         dBusPropertyIntf, dBusPropertyGetMethod, restricionModeIntf,
3037         restricionModeProperty);
3038     if (ec)
3039     {
3040         phosphor::logging::log<phosphor::logging::level::ERR>(
3041             "ipmiSetSecurityMode: failed to get RestrictionMode property",
3042             phosphor::logging::entry("ERROR=%s", ec.message().c_str()));
3043         return ipmi::responseUnspecifiedError();
3044     }
3045     auto currentRestrictionMode =
3046         securityNameSpace::RestrictionMode::convertModesFromString(
3047             std::get<std::string>(varRestrMode));
3048 
3049     if (chInfo.mediumType !=
3050             static_cast<uint8_t>(EChannelMediumType::lan8032) &&
3051         currentRestrictionMode > reqMode)
3052     {
3053         phosphor::logging::log<phosphor::logging::level::ERR>(
3054             "ipmiSetSecurityMode - Downgrading security mode not supported "
3055             "through system interface",
3056             phosphor::logging::entry(
3057                 "CUR_MODE=%d", static_cast<uint8_t>(currentRestrictionMode)),
3058             phosphor::logging::entry("REQ_MODE=%d", restrictionMode));
3059         return ipmi::responseCommandNotAvailable();
3060     }
3061 
3062     ec.clear();
3063     ctx->bus->yield_method_call<>(
3064         ctx->yield, ec, restricionModeService, restricionModeBasePath,
3065         dBusPropertyIntf, dBusPropertySetMethod, restricionModeIntf,
3066         restricionModeProperty,
3067         static_cast<ipmi::DbusVariant>(
3068             securityNameSpace::convertForMessage(reqMode)));
3069 
3070     if (ec)
3071     {
3072         phosphor::logging::log<phosphor::logging::level::ERR>(
3073             "ipmiSetSecurityMode: failed to set RestrictionMode property",
3074             phosphor::logging::entry("ERROR=%s", ec.message().c_str()));
3075         return ipmi::responseUnspecifiedError();
3076     }
3077 
3078 #ifdef BMC_VALIDATION_UNSECURE_FEATURE
3079     if (specialMode)
3080     {
3081         constexpr uint8_t mfgMode = 0x01;
3082         // Manufacturing mode is reserved. So can't enable this mode.
3083         if (specialMode.value() == mfgMode)
3084         {
3085             phosphor::logging::log<phosphor::logging::level::INFO>(
3086                 "ipmiSetSecurityMode: Can't enable Manufacturing mode");
3087             return ipmi::responseInvalidFieldRequest();
3088         }
3089 
3090         ec.clear();
3091         ctx->bus->yield_method_call<>(
3092             ctx->yield, ec, specialModeService, specialModeBasePath,
3093             dBusPropertyIntf, dBusPropertySetMethod, specialModeIntf,
3094             specialModeProperty,
3095             static_cast<ipmi::DbusVariant>(securityNameSpace::convertForMessage(
3096                 static_cast<securityNameSpace::SpecialMode::Modes>(
3097                     specialMode.value()))));
3098 
3099         if (ec)
3100         {
3101             phosphor::logging::log<phosphor::logging::level::ERR>(
3102                 "ipmiSetSecurityMode: failed to set SpecialMode property",
3103                 phosphor::logging::entry("ERROR=%s", ec.message().c_str()));
3104             return ipmi::responseUnspecifiedError();
3105         }
3106     }
3107 #endif
3108     return ipmi::responseSuccess();
3109 }
3110 
ipmiRestoreConfiguration(const std::array<uint8_t,3> & clr,uint8_t cmd)3111 ipmi::RspType<uint8_t /* restore status */> ipmiRestoreConfiguration(
3112     const std::array<uint8_t, 3>& clr, uint8_t cmd)
3113 {
3114     static constexpr std::array<uint8_t, 3> expClr = {'C', 'L', 'R'};
3115 
3116     if (clr != expClr)
3117     {
3118         return ipmi::responseInvalidFieldRequest();
3119     }
3120     constexpr uint8_t cmdStatus = 0;
3121     constexpr uint8_t cmdDefaultRestore = 0xaa;
3122     constexpr uint8_t cmdFullRestore = 0xbb;
3123     constexpr uint8_t cmdFormat = 0xcc;
3124 
3125     constexpr const char* restoreOpFname = "/tmp/.rwfs/.restore_op";
3126 
3127     switch (cmd)
3128     {
3129         case cmdStatus:
3130             break;
3131         case cmdDefaultRestore:
3132         case cmdFullRestore:
3133         case cmdFormat:
3134         {
3135             // write file to rwfs root
3136             int value = (cmd - 1) & 0x03; // map aa, bb, cc => 1, 2, 3
3137             std::ofstream restoreFile(restoreOpFname);
3138             if (!restoreFile)
3139             {
3140                 return ipmi::responseUnspecifiedError();
3141             }
3142             restoreFile << value << "\n";
3143 
3144             phosphor::logging::log<phosphor::logging::level::WARNING>(
3145                 "Restore to default will be performed on next BMC boot",
3146                 phosphor::logging::entry("ACTION=0x%0X", cmd));
3147 
3148             break;
3149         }
3150         default:
3151             return ipmi::responseInvalidFieldRequest();
3152     }
3153 
3154     constexpr uint8_t restorePending = 0;
3155     constexpr uint8_t restoreComplete = 1;
3156 
3157     uint8_t restoreStatus = std::filesystem::exists(restoreOpFname)
3158                                 ? restorePending
3159                                 : restoreComplete;
3160     return ipmi::responseSuccess(restoreStatus);
3161 }
3162 
ipmiOEMGetNmiSource(void)3163 ipmi::RspType<uint8_t> ipmiOEMGetNmiSource(void)
3164 {
3165     uint8_t bmcSource;
3166     namespace nmi = sdbusplus::xyz::openbmc_project::Chassis::Control::server;
3167 
3168     try
3169     {
3170         std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
3171         std::string service =
3172             getService(*dbus, oemNmiSourceIntf, oemNmiSourceObjPath);
3173         Value variant =
3174             getDbusProperty(*dbus, service, oemNmiSourceObjPath,
3175                             oemNmiSourceIntf, oemNmiBmcSourceObjPathProp);
3176 
3177         switch (nmi::NMISource::convertBMCSourceSignalFromString(
3178             std::get<std::string>(variant)))
3179         {
3180             case nmi::NMISource::BMCSourceSignal::None:
3181                 bmcSource = static_cast<uint8_t>(NmiSource::none);
3182                 break;
3183             case nmi::NMISource::BMCSourceSignal::FrontPanelButton:
3184                 bmcSource = static_cast<uint8_t>(NmiSource::frontPanelButton);
3185                 break;
3186             case nmi::NMISource::BMCSourceSignal::Watchdog:
3187                 bmcSource = static_cast<uint8_t>(NmiSource::watchdog);
3188                 break;
3189             case nmi::NMISource::BMCSourceSignal::ChassisCmd:
3190                 bmcSource = static_cast<uint8_t>(NmiSource::chassisCmd);
3191                 break;
3192             case nmi::NMISource::BMCSourceSignal::MemoryError:
3193                 bmcSource = static_cast<uint8_t>(NmiSource::memoryError);
3194                 break;
3195             case nmi::NMISource::BMCSourceSignal::PciBusError:
3196                 bmcSource = static_cast<uint8_t>(NmiSource::pciBusError);
3197                 break;
3198             case nmi::NMISource::BMCSourceSignal::PCH:
3199                 bmcSource = static_cast<uint8_t>(NmiSource::pch);
3200                 break;
3201             case nmi::NMISource::BMCSourceSignal::Chipset:
3202                 bmcSource = static_cast<uint8_t>(NmiSource::chipset);
3203                 break;
3204             default:
3205                 phosphor::logging::log<phosphor::logging::level::ERR>(
3206                     "NMI source: invalid property!",
3207                     phosphor::logging::entry(
3208                         "PROP=%s", std::get<std::string>(variant).c_str()));
3209                 return ipmi::responseResponseError();
3210         }
3211     }
3212     catch (const sdbusplus::exception_t& e)
3213     {
3214         phosphor::logging::log<phosphor::logging::level::ERR>(e.what());
3215         return ipmi::responseResponseError();
3216     }
3217 
3218     return ipmi::responseSuccess(bmcSource);
3219 }
3220 
ipmiOEMSetNmiSource(uint8_t sourceId)3221 ipmi::RspType<> ipmiOEMSetNmiSource(uint8_t sourceId)
3222 {
3223     namespace nmi = sdbusplus::xyz::openbmc_project::Chassis::Control::server;
3224 
3225     nmi::NMISource::BMCSourceSignal bmcSourceSignal =
3226         nmi::NMISource::BMCSourceSignal::None;
3227 
3228     switch (NmiSource(sourceId))
3229     {
3230         case NmiSource::none:
3231             bmcSourceSignal = nmi::NMISource::BMCSourceSignal::None;
3232             break;
3233         case NmiSource::frontPanelButton:
3234             bmcSourceSignal = nmi::NMISource::BMCSourceSignal::FrontPanelButton;
3235             break;
3236         case NmiSource::watchdog:
3237             bmcSourceSignal = nmi::NMISource::BMCSourceSignal::Watchdog;
3238             break;
3239         case NmiSource::chassisCmd:
3240             bmcSourceSignal = nmi::NMISource::BMCSourceSignal::ChassisCmd;
3241             break;
3242         case NmiSource::memoryError:
3243             bmcSourceSignal = nmi::NMISource::BMCSourceSignal::MemoryError;
3244             break;
3245         case NmiSource::pciBusError:
3246             bmcSourceSignal = nmi::NMISource::BMCSourceSignal::PciBusError;
3247             break;
3248         case NmiSource::pch:
3249             bmcSourceSignal = nmi::NMISource::BMCSourceSignal::PCH;
3250             break;
3251         case NmiSource::chipset:
3252             bmcSourceSignal = nmi::NMISource::BMCSourceSignal::Chipset;
3253             break;
3254         default:
3255             phosphor::logging::log<phosphor::logging::level::ERR>(
3256                 "NMI source: invalid property!");
3257             return ipmi::responseResponseError();
3258     }
3259 
3260     try
3261     {
3262         // keep NMI signal source
3263         std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
3264         std::string service =
3265             getService(*dbus, oemNmiSourceIntf, oemNmiSourceObjPath);
3266         setDbusProperty(*dbus, service, oemNmiSourceObjPath, oemNmiSourceIntf,
3267                         oemNmiBmcSourceObjPathProp,
3268                         nmi::convertForMessage(bmcSourceSignal));
3269         // set Enabled property to inform NMI source handling
3270         // to trigger a NMI_OUT BSOD.
3271         // if it's triggered by NMI source property changed,
3272         // NMI_OUT BSOD could be missed if the same source occurs twice in a row
3273         if (bmcSourceSignal != nmi::NMISource::BMCSourceSignal::None)
3274         {
3275             setDbusProperty(*dbus, service, oemNmiSourceObjPath,
3276                             oemNmiSourceIntf, oemNmiEnabledObjPathProp,
3277                             static_cast<bool>(true));
3278         }
3279     }
3280     catch (const sdbusplus::exception_t& e)
3281     {
3282         phosphor::logging::log<phosphor::logging::level::ERR>(e.what());
3283         return ipmi::responseResponseError();
3284     }
3285 
3286     return ipmi::responseSuccess();
3287 }
3288 
3289 namespace dimmOffset
3290 {
3291 constexpr const char* dimmPower = "DimmPower";
3292 constexpr const char* staticCltt = "StaticCltt";
3293 constexpr const char* offsetPath = "/xyz/openbmc_project/Inventory/Item/Dimm";
3294 constexpr const char* offsetInterface =
3295     "xyz.openbmc_project.Inventory.Item.Dimm.Offset";
3296 constexpr const char* property = "DimmOffset";
3297 
3298 }; // namespace dimmOffset
3299 
ipmiOEMSetDimmOffset(uint8_t type,const std::vector<std::tuple<uint8_t,uint8_t>> & data)3300 ipmi::RspType<> ipmiOEMSetDimmOffset(
3301     uint8_t type, const std::vector<std::tuple<uint8_t, uint8_t>>& data)
3302 {
3303     if (type != static_cast<uint8_t>(dimmOffsetTypes::dimmPower) &&
3304         type != static_cast<uint8_t>(dimmOffsetTypes::staticCltt))
3305     {
3306         return ipmi::responseInvalidFieldRequest();
3307     }
3308 
3309     if (data.empty())
3310     {
3311         return ipmi::responseInvalidFieldRequest();
3312     }
3313     nlohmann::json json;
3314 
3315     std::ifstream jsonStream(dimmOffsetFile);
3316     if (jsonStream.good())
3317     {
3318         json = nlohmann::json::parse(jsonStream, nullptr, false);
3319         if (json.is_discarded())
3320         {
3321             json = nlohmann::json();
3322         }
3323         jsonStream.close();
3324     }
3325 
3326     std::string typeName;
3327     if (type == static_cast<uint8_t>(dimmOffsetTypes::dimmPower))
3328     {
3329         typeName = dimmOffset::dimmPower;
3330     }
3331     else
3332     {
3333         typeName = dimmOffset::staticCltt;
3334     }
3335 
3336     nlohmann::json& field = json[typeName];
3337 
3338     for (const auto& [index, value] : data)
3339     {
3340         field[index] = value;
3341     }
3342 
3343     for (nlohmann::json& val : field)
3344     {
3345         if (val == nullptr)
3346         {
3347             val = static_cast<uint8_t>(0);
3348         }
3349     }
3350 
3351     std::ofstream output(dimmOffsetFile);
3352     if (!output.good())
3353     {
3354         std::cerr << "Error writing json file\n";
3355         return ipmi::responseResponseError();
3356     }
3357 
3358     output << json.dump(4);
3359 
3360     if (type == static_cast<uint8_t>(dimmOffsetTypes::staticCltt))
3361     {
3362         std::shared_ptr<sdbusplus::asio::connection> bus = getSdBus();
3363 
3364         ipmi::DbusVariant offsets = field.get<std::vector<uint8_t>>();
3365         auto call = bus->new_method_call(
3366             settingsBusName, dimmOffset::offsetPath, PROP_INTF, "Set");
3367         call.append(dimmOffset::offsetInterface, dimmOffset::property, offsets);
3368         try
3369         {
3370             bus->call(call);
3371         }
3372         catch (const sdbusplus::exception_t& e)
3373         {
3374             phosphor::logging::log<phosphor::logging::level::ERR>(
3375                 "ipmiOEMSetDimmOffset: can't set dimm offsets!",
3376                 phosphor::logging::entry("ERR=%s", e.what()));
3377             return ipmi::responseResponseError();
3378         }
3379     }
3380 
3381     return ipmi::responseSuccess();
3382 }
3383 
ipmiOEMGetDimmOffset(uint8_t type,uint8_t index)3384 ipmi::RspType<uint8_t> ipmiOEMGetDimmOffset(uint8_t type, uint8_t index)
3385 {
3386     if (type != static_cast<uint8_t>(dimmOffsetTypes::dimmPower) &&
3387         type != static_cast<uint8_t>(dimmOffsetTypes::staticCltt))
3388     {
3389         return ipmi::responseInvalidFieldRequest();
3390     }
3391 
3392     std::ifstream jsonStream(dimmOffsetFile);
3393 
3394     auto json = nlohmann::json::parse(jsonStream, nullptr, false);
3395     if (json.is_discarded())
3396     {
3397         std::cerr << "File error in " << dimmOffsetFile << "\n";
3398         return ipmi::responseResponseError();
3399     }
3400 
3401     std::string typeName;
3402     if (type == static_cast<uint8_t>(dimmOffsetTypes::dimmPower))
3403     {
3404         typeName = dimmOffset::dimmPower;
3405     }
3406     else
3407     {
3408         typeName = dimmOffset::staticCltt;
3409     }
3410 
3411     auto it = json.find(typeName);
3412     if (it == json.end())
3413     {
3414         return ipmi::responseInvalidFieldRequest();
3415     }
3416 
3417     if (it->size() <= index)
3418     {
3419         return ipmi::responseInvalidFieldRequest();
3420     }
3421 
3422     uint8_t resp = it->at(index).get<uint8_t>();
3423     return ipmi::responseSuccess(resp);
3424 }
3425 
3426 namespace boot_options
3427 {
3428 
3429 using namespace sdbusplus::xyz::openbmc_project::Control::Boot::server;
3430 using IpmiValue = uint8_t;
3431 constexpr auto ipmiDefault = 0;
3432 
3433 std::map<IpmiValue, Source::Sources> sourceIpmiToDbus = {
3434     {0x01, Source::Sources::Network},
3435     {0x02, Source::Sources::Disk},
3436     {0x05, Source::Sources::ExternalMedia},
3437     {0x0f, Source::Sources::RemovableMedia},
3438     {ipmiDefault, Source::Sources::Default}};
3439 
3440 std::map<IpmiValue, Mode::Modes> modeIpmiToDbus = {
3441     {0x06, Mode::Modes::Setup}, {ipmiDefault, Mode::Modes::Regular}};
3442 
3443 std::map<Source::Sources, IpmiValue> sourceDbusToIpmi = {
3444     {Source::Sources::Network, 0x01},
3445     {Source::Sources::Disk, 0x02},
3446     {Source::Sources::ExternalMedia, 0x05},
3447     {Source::Sources::RemovableMedia, 0x0f},
3448     {Source::Sources::Default, ipmiDefault}};
3449 
3450 std::map<Mode::Modes, IpmiValue> modeDbusToIpmi = {
3451     {Mode::Modes::Setup, 0x06}, {Mode::Modes::Regular, ipmiDefault}};
3452 
3453 static constexpr auto bootModeIntf = "xyz.openbmc_project.Control.Boot.Mode";
3454 static constexpr auto bootSourceIntf =
3455     "xyz.openbmc_project.Control.Boot.Source";
3456 static constexpr auto enabledIntf = "xyz.openbmc_project.Object.Enable";
3457 static constexpr auto persistentObjPath =
3458     "/xyz/openbmc_project/control/host0/boot";
3459 static constexpr auto oneTimePath =
3460     "/xyz/openbmc_project/control/host0/boot/one_time";
3461 static constexpr auto bootSourceProp = "BootSource";
3462 static constexpr auto bootModeProp = "BootMode";
3463 static constexpr auto oneTimeBootEnableProp = "Enabled";
3464 static constexpr auto httpBootMode =
3465     "xyz.openbmc_project.Control.Boot.Source.Sources.Http";
3466 
3467 enum class BootOptionParameter : size_t
3468 {
3469     setInProgress = 0x0,
3470     bootFlags = 0x5,
3471 };
3472 static constexpr uint8_t setComplete = 0x0;
3473 static constexpr uint8_t setInProgress = 0x1;
3474 static uint8_t transferStatus = setComplete;
3475 static constexpr uint8_t setParmVersion = 0x01;
3476 static constexpr uint8_t setParmBootFlagsPermanent = 0x40;
3477 static constexpr uint8_t setParmBootFlagsValidOneTime = 0x80;
3478 static constexpr uint8_t setParmBootFlagsValidPermanent = 0xC0;
3479 static constexpr uint8_t httpBoot = 0xd;
3480 static constexpr uint8_t bootSourceMask = 0x3c;
3481 
3482 } // namespace boot_options
3483 
3484 ipmi::RspType<uint8_t,               // version
3485               uint8_t,               // param
3486               uint8_t,               // data0, dependent on parameter
3487               std::optional<uint8_t> // data1, dependent on parameter
3488               >
ipmiOemGetEfiBootOptions(uint8_t parameter,uint8_t set,uint8_t block)3489     ipmiOemGetEfiBootOptions(uint8_t parameter, [[maybe_unused]] uint8_t set,
3490                              [[maybe_unused]] uint8_t block)
3491 {
3492     using namespace boot_options;
3493     uint8_t bootOption = 0;
3494 
3495     if (parameter == static_cast<uint8_t>(BootOptionParameter::setInProgress))
3496     {
3497         return ipmi::responseSuccess(setParmVersion, parameter, transferStatus,
3498                                      std::nullopt);
3499     }
3500 
3501     if (parameter != static_cast<uint8_t>(BootOptionParameter::bootFlags))
3502     {
3503         phosphor::logging::log<phosphor::logging::level::ERR>(
3504             "Unsupported parameter");
3505         return ipmi::response(ccParameterNotSupported);
3506     }
3507 
3508     try
3509     {
3510         auto oneTimeEnabled = false;
3511         // read one time Enabled property
3512         std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
3513         std::string service = getService(*dbus, enabledIntf, oneTimePath);
3514         Value variant = getDbusProperty(*dbus, service, oneTimePath,
3515                                         enabledIntf, oneTimeBootEnableProp);
3516         oneTimeEnabled = std::get<bool>(variant);
3517 
3518         // get BootSource and BootMode properties
3519         // according to oneTimeEnable
3520         auto bootObjPath = oneTimePath;
3521         if (oneTimeEnabled == false)
3522         {
3523             bootObjPath = persistentObjPath;
3524         }
3525 
3526         service = getService(*dbus, bootModeIntf, bootObjPath);
3527         variant = getDbusProperty(*dbus, service, bootObjPath, bootModeIntf,
3528                                   bootModeProp);
3529 
3530         auto bootMode =
3531             Mode::convertModesFromString(std::get<std::string>(variant));
3532 
3533         service = getService(*dbus, bootSourceIntf, bootObjPath);
3534         variant = getDbusProperty(*dbus, service, bootObjPath, bootSourceIntf,
3535                                   bootSourceProp);
3536 
3537         if (std::get<std::string>(variant) == httpBootMode)
3538         {
3539             bootOption = httpBoot;
3540         }
3541         else
3542         {
3543             auto bootSource = Source::convertSourcesFromString(
3544                 std::get<std::string>(variant));
3545             bootOption = sourceDbusToIpmi.at(bootSource);
3546             if (Source::Sources::Default == bootSource)
3547             {
3548                 bootOption = modeDbusToIpmi.at(bootMode);
3549             }
3550         }
3551 
3552         uint8_t oneTime = oneTimeEnabled ? setParmBootFlagsValidOneTime
3553                                          : setParmBootFlagsValidPermanent;
3554         bootOption <<= 2; // shift for responseconstexpr
3555         return ipmi::responseSuccess(setParmVersion, parameter, oneTime,
3556                                      bootOption);
3557     }
3558     catch (const sdbusplus::exception_t& e)
3559     {
3560         phosphor::logging::log<phosphor::logging::level::ERR>(e.what());
3561         return ipmi::responseResponseError();
3562     }
3563 }
3564 
ipmiOemSetEfiBootOptions(uint8_t bootFlag,uint8_t bootParam,std::optional<uint8_t> bootOption)3565 ipmi::RspType<> ipmiOemSetEfiBootOptions(uint8_t bootFlag, uint8_t bootParam,
3566                                          std::optional<uint8_t> bootOption)
3567 {
3568     using namespace boot_options;
3569     auto oneTimeEnabled = false;
3570 
3571     if (bootFlag == 0 && bootParam == 0)
3572     {
3573         phosphor::logging::log<phosphor::logging::level::ERR>(
3574             "Unsupported parameter");
3575         return ipmi::response(ccParameterNotSupported);
3576     }
3577     if (bootFlag == static_cast<uint8_t>(BootOptionParameter::setInProgress))
3578     {
3579         if (bootOption)
3580         {
3581             return ipmi::responseReqDataLenInvalid();
3582         }
3583 
3584         if (transferStatus == setInProgress)
3585         {
3586             phosphor::logging::log<phosphor::logging::level::ERR>(
3587                 "boot option set in progress!");
3588             return ipmi::responseResponseError();
3589         }
3590 
3591         transferStatus = bootParam;
3592         return ipmi::responseSuccess();
3593     }
3594 
3595     if (bootFlag != (uint8_t)BootOptionParameter::bootFlags)
3596     {
3597         phosphor::logging::log<phosphor::logging::level::ERR>(
3598             "Unsupported parameter");
3599         return ipmi::response(ccParameterNotSupported);
3600     }
3601 
3602     if (!bootOption)
3603     {
3604         return ipmi::responseReqDataLenInvalid();
3605     }
3606 
3607     if (((bootOption.value() & bootSourceMask) >> 2) !=
3608         httpBoot) // not http boot, exit
3609     {
3610         phosphor::logging::log<phosphor::logging::level::ERR>(
3611             "wrong boot option parameter!");
3612         return ipmi::responseParmOutOfRange();
3613     }
3614 
3615     try
3616     {
3617         bool permanent = (bootParam & setParmBootFlagsPermanent) ==
3618                          setParmBootFlagsPermanent;
3619 
3620         // read one time Enabled property
3621         std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
3622         std::string service = getService(*dbus, enabledIntf, oneTimePath);
3623         Value variant = getDbusProperty(*dbus, service, oneTimePath,
3624                                         enabledIntf, oneTimeBootEnableProp);
3625         oneTimeEnabled = std::get<bool>(variant);
3626 
3627         /*
3628          * Check if the current boot setting is onetime or permanent, if the
3629          * request in the command is otherwise, then set the "Enabled"
3630          * property in one_time object path to 'True' to indicate onetime
3631          * and 'False' to indicate permanent.
3632          *
3633          * Once the onetime/permanent setting is applied, then the bootMode
3634          * and bootSource is updated for the corresponding object.
3635          */
3636         if (permanent == oneTimeEnabled)
3637         {
3638             setDbusProperty(*dbus, service, oneTimePath, enabledIntf,
3639                             oneTimeBootEnableProp, !permanent);
3640         }
3641 
3642         // set BootSource and BootMode properties
3643         // according to oneTimeEnable or persistent
3644         auto bootObjPath = oneTimePath;
3645         if (oneTimeEnabled == false)
3646         {
3647             bootObjPath = persistentObjPath;
3648         }
3649         std::string bootMode =
3650             "xyz.openbmc_project.Control.Boot.Mode.Modes.Regular";
3651         std::string bootSource = httpBootMode;
3652 
3653         service = getService(*dbus, bootModeIntf, bootObjPath);
3654         setDbusProperty(*dbus, service, bootObjPath, bootModeIntf, bootModeProp,
3655                         bootMode);
3656 
3657         service = getService(*dbus, bootSourceIntf, bootObjPath);
3658         setDbusProperty(*dbus, service, bootObjPath, bootSourceIntf,
3659                         bootSourceProp, bootSource);
3660     }
3661     catch (const sdbusplus::exception_t& e)
3662     {
3663         phosphor::logging::log<phosphor::logging::level::ERR>(e.what());
3664         return ipmi::responseResponseError();
3665     }
3666 
3667     return ipmi::responseSuccess();
3668 }
3669 
3670 using BasicVariantType = ipmi::DbusVariant;
3671 using PropertyMapType =
3672     boost::container::flat_map<std::string, BasicVariantType>;
3673 static constexpr const std::array<const char*, 1> psuPresenceTypes = {
3674     "xyz.openbmc_project.Configuration.PSUPresence"};
getPSUAddress(ipmi::Context::ptr & ctx,uint8_t & bus,std::vector<uint64_t> & addrTable)3675 int getPSUAddress(ipmi::Context::ptr& ctx, uint8_t& bus,
3676                   std::vector<uint64_t>& addrTable)
3677 {
3678     boost::system::error_code ec;
3679     GetSubTreeType subtree = ctx->bus->yield_method_call<GetSubTreeType>(
3680         ctx->yield, ec, "xyz.openbmc_project.ObjectMapper",
3681         "/xyz/openbmc_project/object_mapper",
3682         "xyz.openbmc_project.ObjectMapper", "GetSubTree",
3683         "/xyz/openbmc_project/inventory/system", 3, psuPresenceTypes);
3684     if (ec)
3685     {
3686         phosphor::logging::log<phosphor::logging::level::ERR>(
3687             "Failed to set dbus property to cold redundancy");
3688         return -1;
3689     }
3690     for (const auto& object : subtree)
3691     {
3692         std::string pathName = object.first;
3693         for (const auto& serviceIface : object.second)
3694         {
3695             std::string serviceName = serviceIface.first;
3696 
3697             ec.clear();
3698             PropertyMapType propMap =
3699                 ctx->bus->yield_method_call<PropertyMapType>(
3700                     ctx->yield, ec, serviceName, pathName,
3701                     "org.freedesktop.DBus.Properties", "GetAll",
3702                     "xyz.openbmc_project.Configuration.PSUPresence");
3703             if (ec)
3704             {
3705                 phosphor::logging::log<phosphor::logging::level::ERR>(
3706                     "Failed to set dbus property to cold redundancy");
3707                 return -1;
3708             }
3709             auto psuBus = std::get_if<uint64_t>(&propMap["Bus"]);
3710             auto psuAddress =
3711                 std::get_if<std::vector<uint64_t>>(&propMap["Address"]);
3712 
3713             if (psuBus == nullptr || psuAddress == nullptr)
3714             {
3715                 std::cerr << "error finding necessary "
3716                              "entry in configuration\n";
3717                 return -1;
3718             }
3719             bus = static_cast<uint8_t>(*psuBus);
3720             addrTable = *psuAddress;
3721             return 0;
3722         }
3723     }
3724     return -1;
3725 }
3726 
3727 static const constexpr uint8_t addrOffset = 8;
3728 static const constexpr uint8_t psuRevision = 0xd9;
3729 static const constexpr uint8_t defaultPSUBus = 7;
3730 // Second Minor, Primary Minor, Major
3731 static const constexpr size_t verLen = 3;
ipmiOEMGetPSUVersion(ipmi::Context::ptr & ctx)3732 ipmi::RspType<std::vector<uint8_t>> ipmiOEMGetPSUVersion(
3733     ipmi::Context::ptr& ctx)
3734 {
3735     uint8_t bus = defaultPSUBus;
3736     std::vector<uint64_t> addrTable;
3737     std::vector<uint8_t> result;
3738     if (getPSUAddress(ctx, bus, addrTable))
3739     {
3740         std::cerr << "Failed to get PSU bus and address\n";
3741         return ipmi::responseResponseError();
3742     }
3743 
3744     for (const auto& targetAddr : addrTable)
3745     {
3746         std::vector<uint8_t> writeData = {psuRevision};
3747         std::vector<uint8_t> readBuf(verLen);
3748         uint8_t addr = static_cast<uint8_t>(targetAddr) + addrOffset;
3749         std::string i2cBus = "/dev/i2c-" + std::to_string(bus);
3750 
3751         auto retI2C = ipmi::i2cWriteRead(i2cBus, addr, writeData, readBuf);
3752         if (retI2C != ipmi::ccSuccess)
3753         {
3754             for (size_t idx = 0; idx < verLen; idx++)
3755             {
3756                 result.emplace_back(0x00);
3757             }
3758         }
3759         else
3760         {
3761             for (const uint8_t& data : readBuf)
3762             {
3763                 result.emplace_back(data);
3764             }
3765         }
3766     }
3767 
3768     return ipmi::responseSuccess(result);
3769 }
3770 
getMultiNodeInfoPresence(ipmi::Context::ptr & ctx,const std::string & name)3771 std::optional<uint8_t> getMultiNodeInfoPresence(ipmi::Context::ptr& ctx,
3772                                                 const std::string& name)
3773 {
3774     Value dbusValue = 0;
3775     std::string serviceName;
3776 
3777     boost::system::error_code ec =
3778         ipmi::getService(ctx, multiNodeIntf, multiNodeObjPath, serviceName);
3779 
3780     if (ec)
3781     {
3782         phosphor::logging::log<phosphor::logging::level::ERR>(
3783             "Failed to perform Multinode getService.");
3784         return std::nullopt;
3785     }
3786 
3787     ec = ipmi::getDbusProperty(ctx, serviceName, multiNodeObjPath,
3788                                multiNodeIntf, name, dbusValue);
3789     if (ec)
3790     {
3791         phosphor::logging::log<phosphor::logging::level::ERR>(
3792             "Failed to perform Multinode get property");
3793         return std::nullopt;
3794     }
3795 
3796     auto multiNodeVal = std::get_if<uint8_t>(&dbusValue);
3797     if (!multiNodeVal)
3798     {
3799         phosphor::logging::log<phosphor::logging::level::ERR>(
3800             "getMultiNodeInfoPresence: error to get multinode");
3801         return std::nullopt;
3802     }
3803     return *multiNodeVal;
3804 }
3805 
3806 /** @brief implements OEM get reading command
3807  *  @param domain ID
3808  *  @param reading Type
3809  *    - 00h = platform Power Consumption
3810  *    - 01h = inlet Air Temp
3811  *    - 02h = icc_TDC from PECI
3812  *  @param reserved, write as 0000h
3813  *
3814  *  @returns IPMI completion code plus response data
3815  *  - response
3816  *     - domain ID
3817  *     - reading Type
3818  *       - 00h = platform Power Consumption
3819  *       - 01h = inlet Air Temp
3820  *       - 02h = icc_TDC from PECI
3821  *     - reading
3822  */
3823 ipmi::RspType<uint4_t, // domain ID
3824               uint4_t, // reading Type
3825               uint16_t // reading Value
3826               >
ipmiOEMGetReading(ipmi::Context::ptr & ctx,uint4_t domainId,uint4_t readingType,uint16_t reserved)3827     ipmiOEMGetReading(ipmi::Context::ptr& ctx, uint4_t domainId,
3828                       uint4_t readingType, uint16_t reserved)
3829 {
3830     [[maybe_unused]] constexpr uint8_t platformPower = 0;
3831     constexpr uint8_t inletAirTemp = 1;
3832     constexpr uint8_t iccTdc = 2;
3833 
3834     if ((static_cast<uint8_t>(readingType) > iccTdc) || domainId || reserved)
3835     {
3836         return ipmi::responseInvalidFieldRequest();
3837     }
3838 
3839     // This command should run only from multi-node product.
3840     // For all other platforms this command will return invalid.
3841 
3842     std::optional<uint8_t> nodeInfo =
3843         getMultiNodeInfoPresence(ctx, "NodePresence");
3844     if (!nodeInfo || !*nodeInfo)
3845     {
3846         return ipmi::responseInvalidCommand();
3847     }
3848 
3849     uint16_t oemReadingValue = 0;
3850     if (static_cast<uint8_t>(readingType) == inletAirTemp)
3851     {
3852         double value = 0;
3853         boost::system::error_code ec = ipmi::getDbusProperty(
3854             ctx, "xyz.openbmc_project.HwmonTempSensor",
3855             "/xyz/openbmc_project/sensors/temperature/Inlet_BRD_Temp",
3856             "xyz.openbmc_project.Sensor.Value", "Value", value);
3857         if (ec)
3858         {
3859             phosphor::logging::log<phosphor::logging::level::ERR>(
3860                 "Failed to get BMC Get OEM temperature",
3861                 phosphor::logging::entry("EXCEPTION=%s", ec.message().c_str()));
3862             return ipmi::responseUnspecifiedError();
3863         }
3864         // Take the Inlet temperature
3865         oemReadingValue = static_cast<uint16_t>(value);
3866     }
3867     else
3868     {
3869         phosphor::logging::log<phosphor::logging::level::ERR>(
3870             "Currently Get OEM Reading support only for Inlet Air Temp");
3871         return ipmi::responseParmOutOfRange();
3872     }
3873     return ipmi::responseSuccess(domainId, readingType, oemReadingValue);
3874 }
3875 
3876 /** @brief implements the maximum size of
3877  *  bridgeable messages used between KCS and
3878  *  IPMB interfacesget security mode command.
3879  *
3880  *  @returns IPMI completion code with following data
3881  *   - KCS Buffer Size (In multiples of four bytes)
3882  *   - IPMB Buffer Size (In multiples of four bytes)
3883  **/
ipmiOEMGetBufferSize()3884 ipmi::RspType<uint8_t, uint8_t> ipmiOEMGetBufferSize()
3885 {
3886     // for now this is hard coded; really this number is dependent on
3887     // the BMC kcs driver as well as the host kcs driver....
3888     // we can't know the latter.
3889     uint8_t kcsMaxBufferSize = 63 / 4;
3890     uint8_t ipmbMaxBufferSize = 128 / 4;
3891 
3892     return ipmi::responseSuccess(kcsMaxBufferSize, ipmbMaxBufferSize);
3893 }
3894 
ipmiOEMReadPFRMailbox(ipmi::Context::ptr & ctx,const uint8_t readRegister,const uint8_t numOfBytes,uint8_t registerIdentifier)3895 ipmi::RspType<std::vector<uint8_t>> ipmiOEMReadPFRMailbox(
3896     ipmi::Context::ptr& ctx, const uint8_t readRegister,
3897     const uint8_t numOfBytes, uint8_t registerIdentifier)
3898 {
3899     if (!ipmi::mailbox::i2cConfigLoaded)
3900     {
3901         phosphor::logging::log<phosphor::logging::level::ERR>(
3902             "Calling PFR Load Configuration Function to Get I2C Bus and Target "
3903             "Address ");
3904 
3905         ipmi::mailbox::loadPfrConfig(ctx, ipmi::mailbox::i2cConfigLoaded);
3906     }
3907 
3908     if (!numOfBytes && !readRegister)
3909     {
3910         phosphor::logging::log<phosphor::logging::level::ERR>(
3911             "OEM IPMI command: Read & write count are 0 which is invalid ");
3912         return ipmi::responseInvalidFieldRequest();
3913     }
3914 
3915     switch (registerIdentifier)
3916     {
3917         case ipmi::mailbox::registerType::fifoReadRegister:
3918         {
3919             // Check if readRegister is an FIFO read register
3920             if (ipmi::mailbox::readFifoReg.find(readRegister) ==
3921                 ipmi::mailbox::readFifoReg.end())
3922             {
3923                 phosphor::logging::log<phosphor::logging::level::ERR>(
3924                     "OEM IPMI command: Register is not a Read FIFO  ");
3925                 return ipmi::responseInvalidFieldRequest();
3926             }
3927 
3928             phosphor::logging::log<phosphor::logging::level::ERR>(
3929                 "OEM IPMI command: Register is a Read FIFO  ");
3930 
3931             ipmi::mailbox::writefifo(ipmi::mailbox::provisioningCommand,
3932                                      readRegister);
3933             ipmi::mailbox::writefifo(ipmi::mailbox::triggerCommand,
3934                                      ipmi::mailbox::flushRead);
3935 
3936             std::vector<uint8_t> writeData = {ipmi::mailbox::readFifo};
3937             std::vector<uint8_t> readBuf(1);
3938             std::vector<uint8_t> result;
3939 
3940             for (int i = 0; i < numOfBytes; i++)
3941             {
3942                 ipmi::Cc ret = ipmi::i2cWriteRead(ipmi::mailbox::i2cBus,
3943                                                   ipmi::mailbox::targetAddr,
3944                                                   writeData, readBuf);
3945                 if (ret != ipmi::ccSuccess)
3946                 {
3947                     return ipmi::response(ret);
3948                 }
3949 
3950                 else
3951                 {
3952                     for (const uint8_t& data : readBuf)
3953                     {
3954                         result.emplace_back(data);
3955                     }
3956                 }
3957             }
3958 
3959             return ipmi::responseSuccess(result);
3960         }
3961 
3962         case ipmi::mailbox::registerType::singleByteRegister:
3963         {
3964             phosphor::logging::log<phosphor::logging::level::ERR>(
3965                 "OEM IPMI command: Register is a Single Byte Register ");
3966 
3967             std::vector<uint8_t> writeData = {readRegister};
3968             std::vector<uint8_t> readBuf(numOfBytes);
3969 
3970             ipmi::Cc ret = ipmi::i2cWriteRead(ipmi::mailbox::i2cBus,
3971                                               ipmi::mailbox::targetAddr,
3972                                               writeData, readBuf);
3973             if (ret != ipmi::ccSuccess)
3974             {
3975                 return ipmi::response(ret);
3976             }
3977             return ipmi::responseSuccess(readBuf);
3978         }
3979 
3980         default:
3981         {
3982             phosphor::logging::log<phosphor::logging::level::ERR>(
3983                 "OEM IPMI command: Register identifier is not valid.It should "
3984                 "be 0 "
3985                 "for Single Byte Register and 1 for FIFO Read Register");
3986 
3987             return ipmi::responseInvalidFieldRequest();
3988         }
3989     }
3990 }
3991 
registerOEMFunctions(void)3992 static void registerOEMFunctions(void)
3993 {
3994     phosphor::logging::log<phosphor::logging::level::INFO>(
3995         "Registering OEM commands");
3996     registerHandler(prioOemBase, intel::netFnGeneral,
3997                     intel::general::cmdGetBmcVersionString, Privilege::User,
3998                     ipmiOEMGetBmcVersionString);
3999 
4000     ipmiPrintAndRegister(intel::netFnGeneral,
4001                          intel::general::cmdGetChassisIdentifier, NULL,
4002                          ipmiOEMGetChassisIdentifier,
4003                          PRIVILEGE_USER); // get chassis identifier
4004 
4005     ipmiPrintAndRegister(intel::netFnGeneral, intel::general::cmdSetSystemGUID,
4006                          NULL, ipmiOEMSetSystemGUID,
4007                          PRIVILEGE_ADMIN); // set system guid
4008 
4009     // <Disable BMC System Reset Action>
4010     registerHandler(prioOemBase, intel::netFnGeneral,
4011                     intel::general::cmdDisableBMCSystemReset, Privilege::Admin,
4012                     ipmiOEMDisableBMCSystemReset);
4013 
4014     // <Get BMC Reset Disables>
4015     registerHandler(prioOemBase, intel::netFnGeneral,
4016                     intel::general::cmdGetBMCResetDisables, Privilege::Admin,
4017                     ipmiOEMGetBMCResetDisables);
4018 
4019     ipmiPrintAndRegister(intel::netFnGeneral, intel::general::cmdSetBIOSID,
4020                          NULL, ipmiOEMSetBIOSID, PRIVILEGE_ADMIN);
4021 
4022     registerHandler(prioOemBase, intel::netFnGeneral,
4023                     intel::general::cmdGetOEMDeviceInfo, Privilege::User,
4024                     ipmiOEMGetDeviceInfo);
4025 
4026     ipmiPrintAndRegister(intel::netFnGeneral,
4027                          intel::general::cmdGetAICSlotFRUIDSlotPosRecords, NULL,
4028                          ipmiOEMGetAICFRU, PRIVILEGE_USER);
4029 
4030     registerHandler(prioOpenBmcBase, intel::netFnGeneral,
4031                     intel::general::cmdSendEmbeddedFWUpdStatus,
4032                     Privilege::Operator, ipmiOEMSendEmbeddedFwUpdStatus);
4033 
4034     registerHandler(prioOpenBmcBase, intel::netFnApp, intel::app::cmdSlotIpmb,
4035                     Privilege::Admin, ipmiOEMSlotIpmb);
4036 
4037     ipmiPrintAndRegister(intel::netFnGeneral,
4038                          intel::general::cmdSetPowerRestoreDelay, NULL,
4039                          ipmiOEMSetPowerRestoreDelay, PRIVILEGE_OPERATOR);
4040 
4041     ipmiPrintAndRegister(intel::netFnGeneral,
4042                          intel::general::cmdGetPowerRestoreDelay, NULL,
4043                          ipmiOEMGetPowerRestoreDelay, PRIVILEGE_USER);
4044 
4045     registerHandler(prioOpenBmcBase, intel::netFnGeneral,
4046                     intel::general::cmdSetOEMUser2Activation,
4047                     Privilege::Callback, ipmiOEMSetUser2Activation);
4048 
4049     registerHandler(prioOpenBmcBase, intel::netFnGeneral,
4050                     intel::general::cmdSetSpecialUserPassword,
4051                     Privilege::Callback, ipmiOEMSetSpecialUserPassword);
4052 
4053     // <Get Processor Error Config>
4054     registerHandler(prioOemBase, intel::netFnGeneral,
4055                     intel::general::cmdGetProcessorErrConfig, Privilege::User,
4056                     ipmiOEMGetProcessorErrConfig);
4057 
4058     // <Set Processor Error Config>
4059     registerHandler(prioOemBase, intel::netFnGeneral,
4060                     intel::general::cmdSetProcessorErrConfig, Privilege::Admin,
4061                     ipmiOEMSetProcessorErrConfig);
4062 
4063     ipmiPrintAndRegister(intel::netFnGeneral,
4064                          intel::general::cmdSetShutdownPolicy, NULL,
4065                          ipmiOEMSetShutdownPolicy, PRIVILEGE_ADMIN);
4066 
4067     ipmiPrintAndRegister(intel::netFnGeneral,
4068                          intel::general::cmdGetShutdownPolicy, NULL,
4069                          ipmiOEMGetShutdownPolicy, PRIVILEGE_ADMIN);
4070 
4071     registerHandler(prioOemBase, intel::netFnGeneral,
4072                     intel::general::cmdSetFanConfig, Privilege::User,
4073                     ipmiOEMSetFanConfig);
4074 
4075     registerHandler(prioOemBase, intel::netFnGeneral,
4076                     intel::general::cmdGetFanConfig, Privilege::User,
4077                     ipmiOEMGetFanConfig);
4078 
4079     registerHandler(prioOemBase, intel::netFnGeneral,
4080                     intel::general::cmdGetFanSpeedOffset, Privilege::User,
4081                     ipmiOEMGetFanSpeedOffset);
4082 
4083     registerHandler(prioOemBase, intel::netFnGeneral,
4084                     intel::general::cmdSetFanSpeedOffset, Privilege::User,
4085                     ipmiOEMSetFanSpeedOffset);
4086 
4087     registerHandler(prioOemBase, intel::netFnGeneral,
4088                     intel::general::cmdSetFscParameter, Privilege::User,
4089                     ipmiOEMSetFscParameter);
4090 
4091     registerHandler(prioOemBase, intel::netFnGeneral,
4092                     intel::general::cmdGetFscParameter, Privilege::User,
4093                     ipmiOEMGetFscParameter);
4094 
4095     registerHandler(prioOpenBmcBase, intel::netFnGeneral,
4096                     intel::general::cmdReadBaseBoardProductId, Privilege::Admin,
4097                     ipmiOEMReadBoardProductId);
4098 
4099     registerHandler(prioOemBase, intel::netFnGeneral,
4100                     intel::general::cmdGetNmiStatus, Privilege::User,
4101                     ipmiOEMGetNmiSource);
4102 
4103     registerHandler(prioOemBase, intel::netFnGeneral,
4104                     intel::general::cmdSetNmiStatus, Privilege::Operator,
4105                     ipmiOEMSetNmiSource);
4106 
4107     registerHandler(prioOemBase, intel::netFnGeneral,
4108                     intel::general::cmdGetEfiBootOptions, Privilege::User,
4109                     ipmiOemGetEfiBootOptions);
4110 
4111     registerHandler(prioOemBase, intel::netFnGeneral,
4112                     intel::general::cmdSetEfiBootOptions, Privilege::Operator,
4113                     ipmiOemSetEfiBootOptions);
4114 
4115     registerHandler(prioOemBase, intel::netFnGeneral,
4116                     intel::general::cmdGetSecurityMode, Privilege::User,
4117                     ipmiGetSecurityMode);
4118 
4119     registerHandler(prioOemBase, intel::netFnGeneral,
4120                     intel::general::cmdSetSecurityMode, Privilege::Admin,
4121                     ipmiSetSecurityMode);
4122 
4123     registerHandler(prioOemBase, intel::netFnGeneral,
4124                     intel::general::cmdGetLEDStatus, Privilege::Admin,
4125                     ipmiOEMGetLEDStatus);
4126 
4127     ipmiPrintAndRegister(ipmi::intel::netFnPlatform,
4128                          ipmi::intel::platform::cmdCfgHostSerialPortSpeed, NULL,
4129                          ipmiOEMCfgHostSerialPortSpeed, PRIVILEGE_ADMIN);
4130 
4131     registerHandler(prioOemBase, intel::netFnGeneral,
4132                     intel::general::cmdSetFaultIndication, Privilege::Operator,
4133                     ipmiOEMSetFaultIndication);
4134 
4135     registerHandler(prioOemBase, intel::netFnGeneral,
4136                     intel::general::cmdSetColdRedundancyConfig, Privilege::User,
4137                     ipmiOEMSetCRConfig);
4138 
4139     registerHandler(prioOemBase, intel::netFnGeneral,
4140                     intel::general::cmdGetColdRedundancyConfig, Privilege::User,
4141                     ipmiOEMGetCRConfig);
4142 
4143     registerHandler(prioOemBase, intel::netFnGeneral,
4144                     intel::general::cmdRestoreConfiguration, Privilege::Admin,
4145                     ipmiRestoreConfiguration);
4146 
4147     registerHandler(prioOemBase, intel::netFnGeneral,
4148                     intel::general::cmdSetDimmOffset, Privilege::Operator,
4149                     ipmiOEMSetDimmOffset);
4150 
4151     registerHandler(prioOemBase, intel::netFnGeneral,
4152                     intel::general::cmdGetDimmOffset, Privilege::Operator,
4153                     ipmiOEMGetDimmOffset);
4154 
4155     registerHandler(prioOemBase, intel::netFnGeneral,
4156                     intel::general::cmdGetPSUVersion, Privilege::User,
4157                     ipmiOEMGetPSUVersion);
4158 
4159     registerHandler(prioOemBase, intel::netFnGeneral,
4160                     intel::general::cmdGetBufferSize, Privilege::User,
4161                     ipmiOEMGetBufferSize);
4162 
4163     registerHandler(prioOemBase, intel::netFnGeneral,
4164                     intel::general::cmdOEMGetReading, Privilege::User,
4165                     ipmiOEMGetReading);
4166 
4167     registerHandler(prioOemBase, intel::netFnApp, intel::app::cmdPFRMailboxRead,
4168                     Privilege::Admin, ipmiOEMReadPFRMailbox);
4169 }
4170 
4171 } // namespace ipmi
4172