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