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