blob: 05661fcc2bb77fa2f258f0e1466488776cc1e653 [file] [log] [blame]
Jia, Chunhuia835eaa2018-09-05 09:00:41 +08001/*
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
Patrick Venturec2a07d42020-05-30 16:35:03 -070017#include "types.hpp"
Jason M. Bills64796042018-10-03 16:51:55 -070018#include "xyz/openbmc_project/Common/error.hpp"
Kuiying Wang45f04982018-12-26 09:23:08 +080019#include "xyz/openbmc_project/Led/Physical/server.hpp"
Jason M. Bills64796042018-10-03 16:51:55 -070020
Jayaprakash Mutyala94204162020-10-23 06:17:56 +000021#include <openssl/crypto.h>
Jia, Chunhuicc49b542019-03-20 15:41:07 +080022#include <systemd/sd-journal.h>
Jia, Chunhuia835eaa2018-09-05 09:00:41 +080023
Chen Yugang7a04f3a2019-10-08 11:12:35 +080024#include <appcommands.hpp>
James Feist91244a62019-02-19 15:04:54 -080025#include <boost/container/flat_map.hpp>
Yong Li23737fe2019-02-19 08:49:55 +080026#include <boost/process/child.hpp>
27#include <boost/process/io.hpp>
Yong Li0669d192019-05-06 14:01:46 +080028#include <com/intel/Control/OCOTShutdownPolicy/server.hpp>
Jason M. Bills64796042018-10-03 16:51:55 -070029#include <commandutils.hpp>
Zhikui Rence4e73f2019-12-06 13:59:47 -080030#include <gpiod.hpp>
Jia, Chunhuicc49b542019-03-20 15:41:07 +080031#include <ipmid/api.hpp>
Vernon Mauery5480ef62019-03-20 13:43:11 -070032#include <ipmid/utils.hpp>
James Feist63efafa2019-07-24 12:39:21 -070033#include <nlohmann/json.hpp>
Jia, Chunhuia835eaa2018-09-05 09:00:41 +080034#include <oemcommands.hpp>
Jia, Chunhuia835eaa2018-09-05 09:00:41 +080035#include <phosphor-logging/log.hpp>
36#include <sdbusplus/bus.hpp>
Suryakanth Sekard509eb92018-11-15 17:44:11 +053037#include <sdbusplus/message/types.hpp>
Chen Yugang97cf96e2019-11-01 08:55:11 +080038#include <xyz/openbmc_project/Chassis/Control/NMISource/server.hpp>
Chen,Yugang4f7e76b2019-08-20 09:28:06 +080039#include <xyz/openbmc_project/Control/Boot/Mode/server.hpp>
40#include <xyz/openbmc_project/Control/Boot/Source/server.hpp>
Cheng C Yang773703a2019-08-15 09:41:11 +080041#include <xyz/openbmc_project/Control/PowerSupplyRedundancy/server.hpp>
Richard Marian Thomaiyard801e462019-06-20 01:05:40 +053042#include <xyz/openbmc_project/Control/Security/RestrictionMode/server.hpp>
Richard Marian Thomaiyar8d4f8d72019-11-11 12:06:40 +053043#include <xyz/openbmc_project/Control/Security/SpecialMode/server.hpp>
Jia, Chunhuia835eaa2018-09-05 09:00:41 +080044
James Feistfcd2d3a2020-05-28 10:38:15 -070045#include <array>
46#include <filesystem>
Jason M. Bills493d7762022-05-04 11:13:19 -070047#include <fstream>
James Feistfcd2d3a2020-05-28 10:38:15 -070048#include <iostream>
49#include <regex>
Ankita Vilas Gawadea1650382022-01-08 10:30:40 +000050#include <set>
James Feistfcd2d3a2020-05-28 10:38:15 -070051#include <string>
52#include <variant>
53#include <vector>
54
Jia, Chunhuia835eaa2018-09-05 09:00:41 +080055namespace ipmi
56{
Jason M. Bills64796042018-10-03 16:51:55 -070057static void registerOEMFunctions() __attribute__((constructor));
Vernon Mauery4ac799d2019-05-20 15:50:37 -070058
Jason M. Bills64796042018-10-03 16:51:55 -070059static constexpr size_t maxFRUStringLength = 0x3F;
Jia, Chunhuia835eaa2018-09-05 09:00:41 +080060
Suryakanth Sekard509eb92018-11-15 17:44:11 +053061static constexpr auto ethernetIntf =
62 "xyz.openbmc_project.Network.EthernetInterface";
63static constexpr auto networkIPIntf = "xyz.openbmc_project.Network.IP";
64static constexpr auto networkService = "xyz.openbmc_project.Network";
65static constexpr auto networkRoot = "/xyz/openbmc_project/network";
66
Chen Yugang97cf96e2019-11-01 08:55:11 +080067static constexpr const char* oemNmiSourceIntf =
68 "xyz.openbmc_project.Chassis.Control.NMISource";
Chen Yugang39736d52019-07-12 16:24:33 +080069static constexpr const char* oemNmiSourceObjPath =
Chen Yugang97cf96e2019-11-01 08:55:11 +080070 "/xyz/openbmc_project/Chassis/Control/NMISource";
Chen Yugang39736d52019-07-12 16:24:33 +080071static constexpr const char* oemNmiBmcSourceObjPathProp = "BMCSource";
72static constexpr const char* oemNmiEnabledObjPathProp = "Enabled";
73
James Feist63efafa2019-07-24 12:39:21 -070074static constexpr const char* dimmOffsetFile = "/var/lib/ipmi/ipmi_dimms.json";
srikanta mondal2030d7c2020-05-03 17:25:25 +000075static constexpr const char* multiNodeObjPath =
76 "/xyz/openbmc_project/MultiNode/Status";
77static constexpr const char* multiNodeIntf =
78 "xyz.openbmc_project.Chassis.MultiNode";
James Feist63efafa2019-07-24 12:39:21 -070079
Chen Yugang39736d52019-07-12 16:24:33 +080080enum class NmiSource : uint8_t
81{
82 none = 0,
Chen Yugang97cf96e2019-11-01 08:55:11 +080083 frontPanelButton = 1,
84 watchdog = 2,
85 chassisCmd = 3,
86 memoryError = 4,
87 pciBusError = 5,
88 pch = 6,
89 chipset = 7,
Chen Yugang39736d52019-07-12 16:24:33 +080090};
91
Suryakanth Sekar822b0b42019-11-15 18:32:53 +053092enum class SpecialUserIndex : uint8_t
93{
94 rootUser = 0,
95 atScaleDebugUser = 1
96};
97
Richard Marian Thomaiyard801e462019-06-20 01:05:40 +053098static constexpr const char* restricionModeService =
99 "xyz.openbmc_project.RestrictionMode.Manager";
100static constexpr const char* restricionModeBasePath =
101 "/xyz/openbmc_project/control/security/restriction_mode";
102static constexpr const char* restricionModeIntf =
103 "xyz.openbmc_project.Control.Security.RestrictionMode";
104static constexpr const char* restricionModeProperty = "RestrictionMode";
105
106static constexpr const char* specialModeService =
107 "xyz.openbmc_project.SpecialMode";
108static constexpr const char* specialModeBasePath =
Richard Marian Thomaiyara7b74282019-09-22 21:53:14 +0530109 "/xyz/openbmc_project/security/special_mode";
Richard Marian Thomaiyard801e462019-06-20 01:05:40 +0530110static constexpr const char* specialModeIntf =
111 "xyz.openbmc_project.Security.SpecialMode";
112static constexpr const char* specialModeProperty = "SpecialMode";
113
114static constexpr const char* dBusPropertyIntf =
115 "org.freedesktop.DBus.Properties";
116static constexpr const char* dBusPropertyGetMethod = "Get";
117static constexpr const char* dBusPropertySetMethod = "Set";
118
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800119// return code: 0 successful
Patrick Williamsf944d2e2022-07-22 19:26:52 -0500120int8_t getChassisSerialNumber(sdbusplus::bus_t& bus, std::string& serial)
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800121{
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
Jason M. Bills64796042018-10-03 16:51:55 -0700134 for (const auto& item : valueTree)
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800135 {
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;
Vernon Mauery8166c8d2019-05-23 11:22:30 -0700151 std::string& result = std::get<std::string>(variant);
Jason M. Bills64796042018-10-03 16:51:55 -0700152 if (result.size() > maxFRUStringLength)
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800153 {
154 phosphor::logging::log<phosphor::logging::level::ERR>(
155 "FRU serial number exceed maximum length");
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800156 return -1;
157 }
Jason M. Bills64796042018-10-03 16:51:55 -0700158 serial = result;
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800159 return 0;
160 }
Patrick Williamsbd51e6a2021-10-06 13:09:44 -0500161 catch (const std::bad_variant_access& e)
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800162 {
Jason M. Bills64796042018-10-03 16:51:55 -0700163 phosphor::logging::log<phosphor::logging::level::ERR>(e.what());
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800164 return -1;
165 }
166 }
167 return -1;
168}
Jason M. Bills64796042018-10-03 16:51:55 -0700169
Ankita Vilas Gawadea1650382022-01-08 10:30:40 +0000170namespace mailbox
171{
172static uint8_t bus = 4;
173static std::string i2cBus = "/dev/i2c-" + std::to_string(bus);
Matt Simmering80d4d5f2023-02-15 15:18:51 -0800174static uint8_t targetAddr = 56;
Ankita Vilas Gawadea1650382022-01-08 10:30:40 +0000175static constexpr auto systemRoot = "/xyz/openbmc_project/inventory/system";
176static constexpr auto sessionIntf = "xyz.openbmc_project.Configuration.PFR";
177const std::string match = "Baseboard/PFR";
178static bool i2cConfigLoaded = false;
179// Command register for UFM provisioning/access commands; read/write allowed
180// from CPU/BMC.
181static const constexpr uint8_t provisioningCommand = 0x0b;
182// Trigger register for the command set in the previous offset.
183static const constexpr uint8_t triggerCommand = 0x0c;
184// Set 0x0c to 0x05 to execute command specified at “UFM/Provisioning Command”
185// register
186static const constexpr uint8_t flushRead = 0x05;
187// FIFO read registers
188std::set<uint8_t> readFifoReg = {0x08, 0x0C, 0x0D, 0x13};
189
190// UFM Read FIFO
191static const constexpr uint8_t readFifo = 0x0e;
192
193enum registerType : uint8_t
194{
195 singleByteRegister = 0,
196 fifoReadRegister,
197
198};
199
200void 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 {
Ankita Vilas Gawadea1650382022-01-08 10:30:40 +0000209 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 {
Ankita Vilas Gawadea1650382022-01-08 10:30:40 +0000240 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);
Matt Simmering80d4d5f2023-02-15 15:18:51 -0800259 targetAddr = static_cast<int>(*address);
Ankita Vilas Gawadea1650382022-01-08 10:30:40 +0000260
261 i2cConfigLoaded = true;
262 }
263}
264
265void 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);
Patrick Williamsb37abfb2023-05-10 07:50:33 -0500271 ipmi::Cc retI2C = ipmi::i2cWriteRead(i2cBus, targetAddr, writeData,
272 readBuf);
Ankita Vilas Gawadea1650382022-01-08 10:30:40 +0000273}
274
275} // namespace mailbox
276
Vernon Maueryaf652682023-08-04 13:37:21 -0700277ipmi::RspType<std::string> ipmiOEMGetBmcVersionString()
278{
279 static std::string version{};
280 if (version.empty())
281 {
282 std::regex expr{"^VERSION_ID=(.*)$"};
283 static constexpr auto osReleasePath{"/etc/os-release"};
284 std::ifstream ifs(osReleasePath);
285 if (!ifs.is_open())
286 {
287 version = "os-release not present";
288 }
289 std::string line{};
290 while (std::getline(ifs, line))
291 {
292 std::smatch sm;
293 if (regex_match(line, sm, expr))
294 {
295 if (sm.size() == 2)
296 {
297 std::string v = sm[1].str();
298 // remove the quotes
299 v.erase(std::remove(v.begin(), v.end(), '\"'), v.end());
300 version = v;
301 break;
302 }
303 }
304 }
305 ifs.close();
306 if (version.empty())
307 {
308 version = "VERSION_ID not present";
309 }
310 }
311 return ipmi::responseSuccess(version);
312}
313
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800314// Returns the Chassis Identifier (serial #)
315ipmi_ret_t ipmiOEMGetChassisIdentifier(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
316 ipmi_request_t request,
317 ipmi_response_t response,
Jason M. Bills64796042018-10-03 16:51:55 -0700318 ipmi_data_len_t dataLen,
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800319 ipmi_context_t context)
320{
321 std::string serial;
Jason M. Bills64796042018-10-03 16:51:55 -0700322 if (*dataLen != 0) // invalid request if there are extra parameters
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800323 {
Jason M. Bills64796042018-10-03 16:51:55 -0700324 *dataLen = 0;
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800325 return IPMI_CC_REQ_DATA_LEN_INVALID;
326 }
Vernon Mauery15419dd2019-05-24 09:40:30 -0700327 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
328 if (getChassisSerialNumber(*dbus, serial) == 0)
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800329 {
Jason M. Bills64796042018-10-03 16:51:55 -0700330 *dataLen = serial.size(); // length will never exceed response length
331 // as it is checked in getChassisSerialNumber
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800332 char* resp = static_cast<char*>(response);
Jason M. Bills64796042018-10-03 16:51:55 -0700333 serial.copy(resp, *dataLen);
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800334 return IPMI_CC_OK;
335 }
Jason M. Bills64796042018-10-03 16:51:55 -0700336 *dataLen = 0;
337 return IPMI_CC_RESPONSE_ERROR;
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800338}
339
340ipmi_ret_t ipmiOEMSetSystemGUID(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
341 ipmi_request_t request,
342 ipmi_response_t response,
Jason M. Bills64796042018-10-03 16:51:55 -0700343 ipmi_data_len_t dataLen, ipmi_context_t context)
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800344{
345 static constexpr size_t safeBufferLength = 50;
346 char buf[safeBufferLength] = {0};
347 GUIDData* Data = reinterpret_cast<GUIDData*>(request);
348
Jason M. Bills64796042018-10-03 16:51:55 -0700349 if (*dataLen != sizeof(GUIDData)) // 16bytes
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800350 {
Jason M. Bills64796042018-10-03 16:51:55 -0700351 *dataLen = 0;
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800352 return IPMI_CC_REQ_DATA_LEN_INVALID;
353 }
354
Jason M. Bills64796042018-10-03 16:51:55 -0700355 *dataLen = 0;
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800356
357 snprintf(
358 buf, safeBufferLength,
359 "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x",
360 Data->timeLow4, Data->timeLow3, Data->timeLow2, Data->timeLow1,
361 Data->timeMid2, Data->timeMid1, Data->timeHigh2, Data->timeHigh1,
362 Data->clock2, Data->clock1, Data->node6, Data->node5, Data->node4,
363 Data->node3, Data->node2, Data->node1);
364 // UUID is in RFC4122 format. Ex: 61a39523-78f2-11e5-9862-e6402cfc3223
365 std::string guid = buf;
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800366
367 std::string objpath = "/xyz/openbmc_project/control/host0/systemGUID";
368 std::string intf = "xyz.openbmc_project.Common.UUID";
Vernon Mauery15419dd2019-05-24 09:40:30 -0700369 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
370 std::string service = getService(*dbus, intf, objpath);
371 setDbusProperty(*dbus, service, objpath, intf, "UUID", guid);
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800372 return IPMI_CC_OK;
373}
374
Jason M. Billsb02bf092019-08-15 13:01:56 -0700375ipmi::RspType<> ipmiOEMDisableBMCSystemReset(bool disableResetOnSMI,
376 uint7_t reserved1)
377{
Jayaprakash Mutyala0a652fa2021-07-01 17:09:39 +0000378 if (reserved1)
379 {
380 return ipmi::responseInvalidFieldRequest();
381 }
382
Jason M. Billsb02bf092019-08-15 13:01:56 -0700383 std::shared_ptr<sdbusplus::asio::connection> busp = getSdBus();
384
385 try
386 {
Patrick Williamsb37abfb2023-05-10 07:50:33 -0500387 auto service = ipmi::getService(*busp, bmcResetDisablesIntf,
388 bmcResetDisablesPath);
Jason M. Billsb02bf092019-08-15 13:01:56 -0700389 ipmi::setDbusProperty(*busp, service, bmcResetDisablesPath,
390 bmcResetDisablesIntf, "ResetOnSMI",
391 !disableResetOnSMI);
392 }
Patrick Williamsbd51e6a2021-10-06 13:09:44 -0500393 catch (const std::exception& e)
Jason M. Billsb02bf092019-08-15 13:01:56 -0700394 {
395 phosphor::logging::log<phosphor::logging::level::ERR>(
396 "Failed to set BMC reset disables",
397 phosphor::logging::entry("EXCEPTION=%s", e.what()));
398 return ipmi::responseUnspecifiedError();
399 }
400
401 return ipmi::responseSuccess();
402}
403
404ipmi::RspType<bool, // disableResetOnSMI
405 uint7_t // reserved
406 >
407 ipmiOEMGetBMCResetDisables()
408{
409 bool disableResetOnSMI = true;
410
411 std::shared_ptr<sdbusplus::asio::connection> busp = getSdBus();
412 try
413 {
Patrick Williamsb37abfb2023-05-10 07:50:33 -0500414 auto service = ipmi::getService(*busp, bmcResetDisablesIntf,
415 bmcResetDisablesPath);
Jason M. Billsb02bf092019-08-15 13:01:56 -0700416 Value variant =
417 ipmi::getDbusProperty(*busp, service, bmcResetDisablesPath,
418 bmcResetDisablesIntf, "ResetOnSMI");
419 disableResetOnSMI = !std::get<bool>(variant);
420 }
Patrick Williamsbd51e6a2021-10-06 13:09:44 -0500421 catch (const std::exception& e)
Jason M. Billsb02bf092019-08-15 13:01:56 -0700422 {
423 phosphor::logging::log<phosphor::logging::level::ERR>(
424 "Failed to get BMC reset disables",
425 phosphor::logging::entry("EXCEPTION=%s", e.what()));
426 return ipmi::responseUnspecifiedError();
427 }
428
429 return ipmi::responseSuccess(disableResetOnSMI, 0);
430}
431
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800432ipmi_ret_t ipmiOEMSetBIOSID(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
433 ipmi_request_t request, ipmi_response_t response,
434 ipmi_data_len_t dataLen, ipmi_context_t context)
435{
436 DeviceInfo* data = reinterpret_cast<DeviceInfo*>(request);
437
Jason M. Bills64796042018-10-03 16:51:55 -0700438 if ((*dataLen < 2) || (*dataLen != (1 + data->biosIDLength)))
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800439 {
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800440 *dataLen = 0;
441 return IPMI_CC_REQ_DATA_LEN_INVALID;
442 }
Jason M. Bills64796042018-10-03 16:51:55 -0700443 std::string idString((char*)data->biosId, data->biosIDLength);
Chalapathi Venkataramashettyfb9f1aa2021-05-07 08:37:07 +0000444 for (auto idChar : idString)
445 {
446 if (!std::isprint(static_cast<unsigned char>(idChar)))
447 {
448 phosphor::logging::log<phosphor::logging::level::ERR>(
449 "BIOS ID contains non printable character");
450 return IPMI_CC_INVALID_FIELD_REQUEST;
451 }
452 }
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800453
Vernon Mauery15419dd2019-05-24 09:40:30 -0700454 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
Chalapathi899bfd12020-04-15 15:07:02 +0000455 std::string service = getService(*dbus, biosVersionIntf, biosActiveObjPath);
456 setDbusProperty(*dbus, service, biosActiveObjPath, biosVersionIntf,
Yong Li2742b852019-12-16 14:55:11 +0800457 biosVersionProp, idString);
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800458 uint8_t* bytesWritten = static_cast<uint8_t*>(response);
459 *bytesWritten =
Jason M. Bills64796042018-10-03 16:51:55 -0700460 data->biosIDLength; // how many bytes are written into storage
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800461 *dataLen = 1;
462 return IPMI_CC_OK;
463}
464
Jayaprakash Mutyala90da3d92021-11-18 22:01:22 +0000465bool getActiveHSCSoftwareVersionInfo(std::string& hscVersion, size_t hscNumber)
466{
467 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
468 try
469 {
Patrick Williamsb37abfb2023-05-10 07:50:33 -0500470 std::string hsbpObjPath = "/xyz/openbmc_project/software/HSBP_" +
471 std::to_string(hscNumber);
Jayaprakash Mutyala90da3d92021-11-18 22:01:22 +0000472 auto service = getService(*dbus, biosVersionIntf, hsbpObjPath);
473 Value hscVersionValue =
474 getDbusProperty(*dbus, "xyz.openbmc_project.HsbpManager",
475 hsbpObjPath, biosVersionIntf, "Version");
476 hscVersion = std::get<std::string>(hscVersionValue);
477 }
Patrick Williamsf944d2e2022-07-22 19:26:52 -0500478 catch (const sdbusplus::exception_t& e)
Jayaprakash Mutyala90da3d92021-11-18 22:01:22 +0000479 {
480 phosphor::logging::log<phosphor::logging::level::INFO>(
481 "Failed to retrieve HSBP version information",
482 phosphor::logging::entry("HSBP Number=%d", hscNumber));
483 return false;
484 }
485 return true;
486}
487
488bool getHscVerInfo(ipmi::Context::ptr ctx, uint8_t& hsc0Major,
489 uint8_t& hsc0Minor, uint8_t& hsc1Major, uint8_t& hsc1Minor,
490 uint8_t& hsc2Major, uint8_t& hsc2Minor)
491{
492 std::string hscVersion;
493 std::array<uint8_t, 6> hscVersions{0};
494
495 for (size_t hscNumber = 1; hscNumber <= 3; hscNumber++)
496 {
497 if (!getActiveHSCSoftwareVersionInfo(hscVersion, hscNumber))
498 {
499 continue;
500 }
501 std::regex pattern1("(\\d+?).(\\d+?).(\\d+?)");
502 constexpr size_t matchedPhosphor = 4;
503 std::smatch results;
504 // hscVersion = BOOT_VER.FPGA_VER.SECURITY_REVISION (Example: 00.02.01)
505 if (std::regex_match(hscVersion, results, pattern1))
506 {
507 // Major version is FPGA_VER and Minor version is SECURITY_REV
508 if (results.size() == matchedPhosphor)
509 {
510 int index = (hscNumber - 1) * 2;
511 hscVersions[index] =
512 static_cast<uint8_t>(std::stoi(results[2]));
513 hscVersions[index + 1] =
514 static_cast<uint8_t>(std::stoi(results[3]));
515 }
516 }
517 }
518 hsc0Major = hscVersions[0];
519 hsc0Minor = hscVersions[1];
520 hsc1Major = hscVersions[2];
521 hsc1Minor = hscVersions[3];
522 hsc2Major = hscVersions[4];
523 hsc2Minor = hscVersions[5];
524 return true;
525}
526
AppaRao Pulie99e7ed2020-01-17 12:27:10 +0530527bool getSwVerInfo(ipmi::Context::ptr ctx, uint8_t& bmcMajor, uint8_t& bmcMinor,
528 uint8_t& meMajor, uint8_t& meMinor)
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800529{
Chen Yugang7a04f3a2019-10-08 11:12:35 +0800530 // step 1 : get BMC Major and Minor numbers from its DBUS property
AppaRao Pulie99e7ed2020-01-17 12:27:10 +0530531 std::string bmcVersion;
532 if (getActiveSoftwareVersionInfo(ctx, versionPurposeBMC, bmcVersion))
Chen Yugang7a04f3a2019-10-08 11:12:35 +0800533 {
534 return false;
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800535 }
536
AppaRao Pulie99e7ed2020-01-17 12:27:10 +0530537 std::optional<MetaRevision> rev = convertIntelVersion(bmcVersion);
Chen Yugang7a04f3a2019-10-08 11:12:35 +0800538 if (rev.has_value())
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800539 {
Chen Yugang7a04f3a2019-10-08 11:12:35 +0800540 MetaRevision revision = rev.value();
541 bmcMajor = revision.major;
542
543 revision.minor = (revision.minor > 99 ? 99 : revision.minor);
544 bmcMinor = revision.minor % 10 + (revision.minor / 10) * 16;
545 }
546
547 // step 2 : get ME Major and Minor numbers from its DBUS property
AppaRao Puli32825a22020-01-17 15:52:41 +0530548 std::string meVersion;
549 if (getActiveSoftwareVersionInfo(ctx, versionPurposeME, meVersion))
Chen Yugang7a04f3a2019-10-08 11:12:35 +0800550 {
Chen Yugang7a04f3a2019-10-08 11:12:35 +0800551 return false;
552 }
AppaRao Puli32825a22020-01-17 15:52:41 +0530553 std::regex pattern1("(\\d+?).(\\d+?).(\\d+?).(\\d+?).(\\d+?)");
554 constexpr size_t matchedPhosphor = 6;
555 std::smatch results;
556 if (std::regex_match(meVersion, results, pattern1))
557 {
558 if (results.size() == matchedPhosphor)
559 {
560 meMajor = static_cast<uint8_t>(std::stoi(results[1]));
Jayaprakash Mutyalad0657022021-08-26 21:18:08 +0000561 meMinor = static_cast<uint8_t>(std::stoi(results[2]) << 4 |
562 std::stoi(results[3]));
AppaRao Puli32825a22020-01-17 15:52:41 +0530563 }
564 }
Chen Yugang7a04f3a2019-10-08 11:12:35 +0800565 return true;
566}
567
568ipmi::RspType<
569 std::variant<std::string,
570 std::tuple<uint8_t, std::array<uint8_t, 2>,
571 std::array<uint8_t, 2>, std::array<uint8_t, 2>,
572 std::array<uint8_t, 2>, std::array<uint8_t, 2>>,
573 std::tuple<uint8_t, std::array<uint8_t, 2>>>>
AppaRao Pulie99e7ed2020-01-17 12:27:10 +0530574 ipmiOEMGetDeviceInfo(ipmi::Context::ptr ctx, uint8_t entityType,
575 std::optional<uint8_t> countToRead,
AppaRao Pulid46cb422020-01-21 18:40:21 +0530576 std::optional<uint8_t> offset)
Chen Yugang7a04f3a2019-10-08 11:12:35 +0800577{
Chen Yugang7a04f3a2019-10-08 11:12:35 +0800578 if (entityType > static_cast<uint8_t>(OEMDevEntityType::sdrVer))
579 {
580 return ipmi::responseInvalidFieldRequest();
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800581 }
582
583 // handle OEM command items
Chen Yugang7a04f3a2019-10-08 11:12:35 +0800584 switch (OEMDevEntityType(entityType))
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800585 {
586 case OEMDevEntityType::biosId:
587 {
AppaRao Pulid46cb422020-01-21 18:40:21 +0530588 // Byte 2&3, Only used with selecting BIOS
589 if (!countToRead || !offset)
590 {
591 return ipmi::responseReqDataLenInvalid();
592 }
593
Vernon Mauery15419dd2019-05-24 09:40:30 -0700594 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
Patrick Williamsb37abfb2023-05-10 07:50:33 -0500595 std::string service = getService(*dbus, biosVersionIntf,
596 biosActiveObjPath);
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800597 try
598 {
Yong Li2742b852019-12-16 14:55:11 +0800599 Value variant =
Chalapathi899bfd12020-04-15 15:07:02 +0000600 getDbusProperty(*dbus, service, biosActiveObjPath,
Yong Li2742b852019-12-16 14:55:11 +0800601 biosVersionIntf, biosVersionProp);
Vernon Mauery8166c8d2019-05-23 11:22:30 -0700602 std::string& idString = std::get<std::string>(variant);
AppaRao Pulid46cb422020-01-21 18:40:21 +0530603 if (*offset >= idString.size())
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800604 {
Chen Yugang7a04f3a2019-10-08 11:12:35 +0800605 return ipmi::responseParmOutOfRange();
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800606 }
Jason M. Bills64796042018-10-03 16:51:55 -0700607 size_t length = 0;
AppaRao Pulid46cb422020-01-21 18:40:21 +0530608 if (*countToRead > (idString.size() - *offset))
Jason M. Bills64796042018-10-03 16:51:55 -0700609 {
AppaRao Pulid46cb422020-01-21 18:40:21 +0530610 length = idString.size() - *offset;
Jason M. Bills64796042018-10-03 16:51:55 -0700611 }
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800612 else
613 {
AppaRao Pulid46cb422020-01-21 18:40:21 +0530614 length = *countToRead;
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800615 }
Chen Yugang7a04f3a2019-10-08 11:12:35 +0800616
617 std::string readBuf = {0};
618 readBuf.resize(length);
AppaRao Pulid46cb422020-01-21 18:40:21 +0530619 std::copy_n(idString.begin() + *offset, length,
Chen Yugang7a04f3a2019-10-08 11:12:35 +0800620 (readBuf.begin()));
621 return ipmi::responseSuccess(readBuf);
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800622 }
Patrick Williamsbd51e6a2021-10-06 13:09:44 -0500623 catch (const std::bad_variant_access& e)
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800624 {
Chen Yugang7a04f3a2019-10-08 11:12:35 +0800625 return ipmi::responseUnspecifiedError();
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800626 }
627 }
628 break;
629
630 case OEMDevEntityType::devVer:
Chen Yugang7a04f3a2019-10-08 11:12:35 +0800631 {
AppaRao Pulid46cb422020-01-21 18:40:21 +0530632 // Byte 2&3, Only used with selecting BIOS
633 if (countToRead || offset)
634 {
635 return ipmi::responseReqDataLenInvalid();
636 }
637
Chen Yugang7a04f3a2019-10-08 11:12:35 +0800638 constexpr const size_t verLen = 2;
639 constexpr const size_t verTotalLen = 10;
640 std::array<uint8_t, verLen> bmcBuf = {0xff, 0xff};
641 std::array<uint8_t, verLen> hsc0Buf = {0xff, 0xff};
642 std::array<uint8_t, verLen> hsc1Buf = {0xff, 0xff};
643 std::array<uint8_t, verLen> meBuf = {0xff, 0xff};
644 std::array<uint8_t, verLen> hsc2Buf = {0xff, 0xff};
645 // data0/1: BMC version number; data6/7: ME version number
AppaRao Pulie99e7ed2020-01-17 12:27:10 +0530646 if (!getSwVerInfo(ctx, bmcBuf[0], bmcBuf[1], meBuf[0], meBuf[1]))
Chen Yugang7a04f3a2019-10-08 11:12:35 +0800647 {
648 return ipmi::responseUnspecifiedError();
649 }
Jayaprakash Mutyala90da3d92021-11-18 22:01:22 +0000650 if (!getHscVerInfo(ctx, hsc0Buf[0], hsc0Buf[1], hsc1Buf[0],
651 hsc1Buf[1], hsc2Buf[0], hsc2Buf[1]))
652 {
653 return ipmi::responseUnspecifiedError();
654 }
Chen Yugang7a04f3a2019-10-08 11:12:35 +0800655 return ipmi::responseSuccess(
656 std::tuple<
657 uint8_t, std::array<uint8_t, verLen>,
658 std::array<uint8_t, verLen>, std::array<uint8_t, verLen>,
659 std::array<uint8_t, verLen>, std::array<uint8_t, verLen>>{
660 verTotalLen, bmcBuf, hsc0Buf, hsc1Buf, meBuf, hsc2Buf});
661 }
662 break;
663
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800664 case OEMDevEntityType::sdrVer:
Chen Yugang7a04f3a2019-10-08 11:12:35 +0800665 {
AppaRao Pulid46cb422020-01-21 18:40:21 +0530666 // Byte 2&3, Only used with selecting BIOS
667 if (countToRead || offset)
668 {
669 return ipmi::responseReqDataLenInvalid();
670 }
671
Chen Yugang7a04f3a2019-10-08 11:12:35 +0800672 constexpr const size_t sdrLen = 2;
673 std::array<uint8_t, sdrLen> readBuf = {0x01, 0x0};
674 return ipmi::responseSuccess(
675 std::tuple<uint8_t, std::array<uint8_t, sdrLen>>{sdrLen,
676 readBuf});
677 }
678 break;
679
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800680 default:
Chen Yugang7a04f3a2019-10-08 11:12:35 +0800681 return ipmi::responseInvalidFieldRequest();
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800682 }
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800683}
684
685ipmi_ret_t ipmiOEMGetAICFRU(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
686 ipmi_request_t request, ipmi_response_t response,
687 ipmi_data_len_t dataLen, ipmi_context_t context)
688{
689 if (*dataLen != 0)
690 {
Jason M. Bills64796042018-10-03 16:51:55 -0700691 *dataLen = 0;
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800692 return IPMI_CC_REQ_DATA_LEN_INVALID;
693 }
694
695 *dataLen = 1;
696 uint8_t* res = reinterpret_cast<uint8_t*>(response);
697 // temporary fix. We don't support AIC FRU now. Just tell BIOS that no
698 // AIC is available so that BIOS will not timeout repeatly which leads to
699 // slow booting.
700 *res = 0; // Byte1=Count of SlotPosition/FruID records.
701 return IPMI_CC_OK;
702}
703
Jason M. Bills64796042018-10-03 16:51:55 -0700704ipmi_ret_t ipmiOEMGetPowerRestoreDelay(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
705 ipmi_request_t request,
706 ipmi_response_t response,
707 ipmi_data_len_t dataLen,
708 ipmi_context_t context)
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800709{
Jason M. Bills64796042018-10-03 16:51:55 -0700710 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
Vernon Mauery15419dd2019-05-24 09:40:30 -0700719 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
Patrick Williamsb37abfb2023-05-10 07:50:33 -0500720 std::string service = getService(*dbus, powerRestoreDelayIntf,
721 powerRestoreDelayObjPath);
722 Value variant = getDbusProperty(*dbus, service, powerRestoreDelayObjPath,
723 powerRestoreDelayIntf,
724 powerRestoreDelayProp);
Jason M. Bills64796042018-10-03 16:51:55 -0700725
Andrei Kartashevc42c7ed2022-01-10 12:17:34 +0300726 uint64_t val = std::get<uint64_t>(variant);
727 val /= 1000000UL;
728 uint16_t delay = val;
Jason M. Bills64796042018-10-03 16:51:55 -0700729 resp->byteLSB = delay;
730 resp->byteMSB = delay >> 8;
731
732 *dataLen = sizeof(GetPowerRestoreDelayRes);
733
734 return IPMI_CC_OK;
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800735}
736
Jia, Chunhuicc49b542019-03-20 15:41:07 +0800737static 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.
745ipmi::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;
Jason M. Billsdc249272019-04-03 09:58:40 -0700751 int instance = (target & targetInstanceMask) >> targetInstanceShift;
Jia, Chunhuicc49b542019-03-20 15:41:07 +0800752 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 }
Jason M. Billsdc249272019-04-03 09:58:40 -0700759 /* 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 }
Jia, Chunhuicc49b542019-03-20 15:41:07 +0800764 /*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";
Jason M. Billsdc249272019-04-03 09:58:40 -0700773 buildInfo = "major: " + std::to_string(majorRevision) + " minor: " +
Jia, Chunhuicc49b542019-03-20 15:41:07 +0800774 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 =
Jason M. Billsdc249272019-04-03 09:58:40 -0700781 "major: " +
Jia, Chunhuicc49b542019-03-20 15:41:07 +0800782 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 =
Jason M. Billsdc249272019-04-03 09:58:40 -0700794 "major: " + std::to_string(majorRevision) + " minor1: " +
Jia, Chunhuicc49b542019-03-20 15:41:07 +0800795 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";
Jason M. Billsdc249272019-04-03 09:58:40 -0700805 buildInfo = "major: " + std::to_string(majorRevision) + " minor: " +
Jia, Chunhuicc49b542019-03-20 15:41:07 +0800806 std::to_string(bcdToDec(minorRevision)) + // BCD encoded
807 " BuildID: " + std::to_string(auxInfo);
808 break;
809 }
810
Jason M. Billsdc249272019-04-03 09:58:40 -0700811 static const std::string openBMCMessageRegistryVersion("0.1");
812 std::string redfishMsgID = "OpenBMC." + openBMCMessageRegistryVersion;
813
Jia, Chunhuicc49b542019-03-20 15:41:07 +0800814 switch (status)
815 {
816 case 0x0:
817 action = "update started";
Jason M. Billsdc249272019-04-03 09:58:40 -0700818 redfishMsgID += ".FirmwareUpdateStarted";
Jia, Chunhuicc49b542019-03-20 15:41:07 +0800819 break;
820 case 0x1:
821 action = "update completed successfully";
Jason M. Billsdc249272019-04-03 09:58:40 -0700822 redfishMsgID += ".FirmwareUpdateCompleted";
Jia, Chunhuicc49b542019-03-20 15:41:07 +0800823 break;
824 case 0x2:
825 action = "update failure";
Jason M. Billsdc249272019-04-03 09:58:40 -0700826 redfishMsgID += ".FirmwareUpdateFailed";
Jia, Chunhuicc49b542019-03-20 15:41:07 +0800827 break;
828 default:
829 action = "unknown";
830 break;
831 }
832
Patrick Williamsb37abfb2023-05-10 07:50:33 -0500833 std::string firmwareInstanceStr = firmware +
834 " instance: " + std::to_string(instance);
Jason M. Billsdc249272019-04-03 09:58:40 -0700835 std::string message("[firmware update] " + firmwareInstanceStr +
836 " status: <" + action + "> " + buildInfo);
Jia, Chunhuicc49b542019-03-20 15:41:07 +0800837
838 sd_journal_send("MESSAGE=%s", message.c_str(), "PRIORITY=%i", LOG_INFO,
Jason M. Billsdc249272019-04-03 09:58:40 -0700839 "REDFISH_MESSAGE_ID=%s", redfishMsgID.c_str(),
840 "REDFISH_MESSAGE_ARGS=%s,%s", firmwareInstanceStr.c_str(),
841 buildInfo.c_str(), NULL);
Jia, Chunhuicc49b542019-03-20 15:41:07 +0800842 return ipmi::responseSuccess();
843}
844
Rajashekar Gade Reddy2b664d52020-03-23 22:09:00 +0530845ipmi::RspType<uint8_t, std::vector<uint8_t>>
846 ipmiOEMSlotIpmb(ipmi::Context::ptr ctx, uint6_t reserved1,
847 uint2_t slotNumber, uint3_t baseBoardSlotNum,
Matt Simmering80d4d5f2023-02-15 15:18:51 -0800848 uint3_t riserSlotNum, uint2_t reserved2, uint8_t targetAddr,
Rajashekar Gade Reddy2b664d52020-03-23 22:09:00 +0530849 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),
Matt Simmering80d4d5f2023-02-15 15:18:51 -0800864 static_cast<uint8_t>(baseBoardSlotNum), targetAddr, netFn, cmd,
Rajashekar Gade Reddy2b664d52020-03-23 22:09:00 +0530865 *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
Jason M. Bills64796042018-10-03 16:51:55 -0700888ipmi_ret_t ipmiOEMSetPowerRestoreDelay(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
889 ipmi_request_t request,
890 ipmi_response_t response,
891 ipmi_data_len_t dataLen,
892 ipmi_context_t context)
893{
894 SetPowerRestoreDelayReq* data =
895 reinterpret_cast<SetPowerRestoreDelayReq*>(request);
896 uint16_t delay = 0;
897
898 if (*dataLen != sizeof(SetPowerRestoreDelayReq))
899 {
900 *dataLen = 0;
901 return IPMI_CC_REQ_DATA_LEN_INVALID;
902 }
903 delay = data->byteMSB;
904 delay = (delay << 8) | data->byteLSB;
Andrei Kartashevc42c7ed2022-01-10 12:17:34 +0300905 uint64_t val = delay * 1000000;
Vernon Mauery15419dd2019-05-24 09:40:30 -0700906 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
Patrick Williamsb37abfb2023-05-10 07:50:33 -0500907 std::string service = getService(*dbus, powerRestoreDelayIntf,
908 powerRestoreDelayObjPath);
Vernon Mauery15419dd2019-05-24 09:40:30 -0700909 setDbusProperty(*dbus, service, powerRestoreDelayObjPath,
Andrei Kartashevc42c7ed2022-01-10 12:17:34 +0300910 powerRestoreDelayIntf, powerRestoreDelayProp, val);
Jason M. Bills64796042018-10-03 16:51:55 -0700911 *dataLen = 0;
912
913 return IPMI_CC_OK;
914}
915
Jason M. Bills42bd9c82019-06-28 16:39:34 -0700916static bool cpuPresent(const std::string& cpuName)
Jason M. Bills64796042018-10-03 16:51:55 -0700917{
Jason M. Bills42bd9c82019-06-28 16:39:34 -0700918 static constexpr const char* cpuPresencePathPrefix =
919 "/xyz/openbmc_project/inventory/system/chassis/motherboard/";
920 static constexpr const char* cpuPresenceIntf =
921 "xyz.openbmc_project.Inventory.Item";
922 std::string cpuPresencePath = cpuPresencePathPrefix + cpuName;
923 std::shared_ptr<sdbusplus::asio::connection> busp = getSdBus();
924 try
Jason M. Bills64796042018-10-03 16:51:55 -0700925 {
Patrick Williamsb37abfb2023-05-10 07:50:33 -0500926 auto service = ipmi::getService(*busp, cpuPresenceIntf,
927 cpuPresencePath);
Jason M. Bills42bd9c82019-06-28 16:39:34 -0700928
929 ipmi::Value result = ipmi::getDbusProperty(
930 *busp, service, cpuPresencePath, cpuPresenceIntf, "Present");
931 return std::get<bool>(result);
932 }
933 catch (const std::exception& e)
934 {
935 phosphor::logging::log<phosphor::logging::level::INFO>(
936 "Cannot find processor presence",
937 phosphor::logging::entry("NAME=%s", cpuName.c_str()));
938 return false;
939 }
940}
941
Jason M. Billsf284f852023-09-07 15:48:48 -0700942ipmi::RspType<bool, // IERR Reset Enabled
Jason M. Bills42bd9c82019-06-28 16:39:34 -0700943 bool, // ERR2 Reset Enabled
Jason M. Bills51cf3112023-09-07 15:50:23 -0700944 bool, // MCERR Reset Enabled
945 uint5_t, // reserved
Jason M. Bills42bd9c82019-06-28 16:39:34 -0700946 uint8_t, // reserved, returns 0x3F
Jason M. Billsf284f852023-09-07 15:48:48 -0700947 uint6_t, // CPU1 IERR Count
Jason M. Bills42bd9c82019-06-28 16:39:34 -0700948 uint2_t, // CPU1 Status
Jason M. Billsf284f852023-09-07 15:48:48 -0700949 uint6_t, // CPU2 IERR Count
Jason M. Bills42bd9c82019-06-28 16:39:34 -0700950 uint2_t, // CPU2 Status
Jason M. Billsf284f852023-09-07 15:48:48 -0700951 uint6_t, // CPU3 IERR Count
Jason M. Bills42bd9c82019-06-28 16:39:34 -0700952 uint2_t, // CPU3 Status
Jason M. Billsf284f852023-09-07 15:48:48 -0700953 uint6_t, // CPU4 IERR Count
Jason M. Bills42bd9c82019-06-28 16:39:34 -0700954 uint2_t, // CPU4 Status
955 uint8_t // Crashdump Count
956 >
957 ipmiOEMGetProcessorErrConfig()
958{
Jason M. Billsf284f852023-09-07 15:48:48 -0700959 bool resetOnIERR = false;
Jason M. Bills42bd9c82019-06-28 16:39:34 -0700960 bool resetOnERR2 = false;
Jason M. Bills51cf3112023-09-07 15:50:23 -0700961 bool allowResetOnMCERR = false;
Jason M. Billsf284f852023-09-07 15:48:48 -0700962 uint6_t cpu1IERRCount = 0;
963 uint6_t cpu2IERRCount = 0;
964 uint6_t cpu3IERRCount = 0;
965 uint6_t cpu4IERRCount = 0;
Jason M. Bills42bd9c82019-06-28 16:39:34 -0700966 uint8_t crashdumpCount = 0;
Jason M. Bills24df90f2021-06-15 12:46:13 -0700967 uint2_t cpu1Status = cpuPresent("CPU_1")
968 ? types::enum_cast<uint8_t>(CPUStatus::enabled)
969 : types::enum_cast<uint8_t>(CPUStatus::notPresent);
970 uint2_t cpu2Status = cpuPresent("CPU_2")
971 ? types::enum_cast<uint8_t>(CPUStatus::enabled)
972 : types::enum_cast<uint8_t>(CPUStatus::notPresent);
973 uint2_t cpu3Status = cpuPresent("CPU_3")
974 ? types::enum_cast<uint8_t>(CPUStatus::enabled)
975 : types::enum_cast<uint8_t>(CPUStatus::notPresent);
976 uint2_t cpu4Status = cpuPresent("CPU_4")
977 ? types::enum_cast<uint8_t>(CPUStatus::enabled)
978 : types::enum_cast<uint8_t>(CPUStatus::notPresent);
Jason M. Bills42bd9c82019-06-28 16:39:34 -0700979
980 std::shared_ptr<sdbusplus::asio::connection> busp = getSdBus();
981 try
982 {
983 auto service = ipmi::getService(*busp, processorErrConfigIntf,
984 processorErrConfigObjPath);
985
986 ipmi::PropertyMap result = ipmi::getAllDbusProperties(
987 *busp, service, processorErrConfigObjPath, processorErrConfigIntf);
Jason M. Billsf284f852023-09-07 15:48:48 -0700988 resetOnIERR = std::get<bool>(result.at("ResetOnIERR"));
Jason M. Bills42bd9c82019-06-28 16:39:34 -0700989 resetOnERR2 = std::get<bool>(result.at("ResetOnERR2"));
Jason M. Bills51cf3112023-09-07 15:50:23 -0700990 allowResetOnMCERR = std::get<bool>(result.at("AllowResetOnMCERR"));
Jason M. Billsf284f852023-09-07 15:48:48 -0700991 cpu1IERRCount = std::get<uint8_t>(result.at("ErrorCountCPU1"));
992 cpu2IERRCount = std::get<uint8_t>(result.at("ErrorCountCPU2"));
993 cpu3IERRCount = std::get<uint8_t>(result.at("ErrorCountCPU3"));
994 cpu4IERRCount = std::get<uint8_t>(result.at("ErrorCountCPU4"));
Jason M. Bills42bd9c82019-06-28 16:39:34 -0700995 crashdumpCount = std::get<uint8_t>(result.at("CrashdumpCount"));
996 }
997 catch (const std::exception& e)
998 {
999 phosphor::logging::log<phosphor::logging::level::ERR>(
1000 "Failed to fetch processor error config",
1001 phosphor::logging::entry("ERROR=%s", e.what()));
1002 return ipmi::responseUnspecifiedError();
Jason M. Bills64796042018-10-03 16:51:55 -07001003 }
1004
Jason M. Bills51cf3112023-09-07 15:50:23 -07001005 return ipmi::responseSuccess(resetOnIERR, resetOnERR2, allowResetOnMCERR, 0,
1006 0x3F, cpu1IERRCount, cpu1Status, cpu2IERRCount,
Jason M. Billsf284f852023-09-07 15:48:48 -07001007 cpu2Status, cpu3IERRCount, cpu3Status,
1008 cpu4IERRCount, cpu4Status, crashdumpCount);
Jason M. Bills42bd9c82019-06-28 16:39:34 -07001009}
Jason M. Bills64796042018-10-03 16:51:55 -07001010
Jason M. Bills42bd9c82019-06-28 16:39:34 -07001011ipmi::RspType<> ipmiOEMSetProcessorErrConfig(
Jason M. Bills51cf3112023-09-07 15:50:23 -07001012 bool resetOnIERR, bool resetOnERR2, bool allowResetOnMCERR,
1013 uint5_t reserved1, uint8_t reserved2,
Jason M. Bills42bd9c82019-06-28 16:39:34 -07001014 std::optional<bool> clearCPUErrorCount,
1015 std::optional<bool> clearCrashdumpCount, std::optional<uint6_t> reserved3)
1016{
Jayaprakash Mutyala0a652fa2021-07-01 17:09:39 +00001017 if (reserved1 || reserved2)
1018 {
1019 return ipmi::responseInvalidFieldRequest();
1020 }
1021
Jason M. Bills42bd9c82019-06-28 16:39:34 -07001022 std::shared_ptr<sdbusplus::asio::connection> busp = getSdBus();
Jason M. Bills64796042018-10-03 16:51:55 -07001023
1024 try
1025 {
Jayaprakash Mutyala0a652fa2021-07-01 17:09:39 +00001026 if (reserved3.value_or(0))
1027 {
1028 return ipmi::responseInvalidFieldRequest();
1029 }
Jason M. Bills42bd9c82019-06-28 16:39:34 -07001030 auto service = ipmi::getService(*busp, processorErrConfigIntf,
1031 processorErrConfigObjPath);
1032 ipmi::setDbusProperty(*busp, service, processorErrConfigObjPath,
Jason M. Billsf284f852023-09-07 15:48:48 -07001033 processorErrConfigIntf, "ResetOnIERR",
1034 resetOnIERR);
Jason M. Bills42bd9c82019-06-28 16:39:34 -07001035 ipmi::setDbusProperty(*busp, service, processorErrConfigObjPath,
1036 processorErrConfigIntf, "ResetOnERR2",
1037 resetOnERR2);
Jason M. Bills51cf3112023-09-07 15:50:23 -07001038 ipmi::setDbusProperty(*busp, service, processorErrConfigObjPath,
1039 processorErrConfigIntf, "AllowResetOnMCERR",
1040 allowResetOnMCERR);
Jason M. Bills42bd9c82019-06-28 16:39:34 -07001041 if (clearCPUErrorCount.value_or(false))
1042 {
1043 ipmi::setDbusProperty(*busp, service, processorErrConfigObjPath,
Jason M. Billsd3e19932019-08-15 12:39:03 -07001044 processorErrConfigIntf, "ErrorCountCPU1",
1045 static_cast<uint8_t>(0));
Jason M. Bills42bd9c82019-06-28 16:39:34 -07001046 ipmi::setDbusProperty(*busp, service, processorErrConfigObjPath,
Jason M. Billsd3e19932019-08-15 12:39:03 -07001047 processorErrConfigIntf, "ErrorCountCPU2",
1048 static_cast<uint8_t>(0));
1049 ipmi::setDbusProperty(*busp, service, processorErrConfigObjPath,
1050 processorErrConfigIntf, "ErrorCountCPU3",
1051 static_cast<uint8_t>(0));
1052 ipmi::setDbusProperty(*busp, service, processorErrConfigObjPath,
1053 processorErrConfigIntf, "ErrorCountCPU4",
1054 static_cast<uint8_t>(0));
Jason M. Bills42bd9c82019-06-28 16:39:34 -07001055 }
1056 if (clearCrashdumpCount.value_or(false))
1057 {
1058 ipmi::setDbusProperty(*busp, service, processorErrConfigObjPath,
Jason M. Billsd3e19932019-08-15 12:39:03 -07001059 processorErrConfigIntf, "CrashdumpCount",
1060 static_cast<uint8_t>(0));
Jason M. Bills42bd9c82019-06-28 16:39:34 -07001061 }
Jason M. Bills64796042018-10-03 16:51:55 -07001062 }
Patrick Williamsbd51e6a2021-10-06 13:09:44 -05001063 catch (const std::exception& e)
Jason M. Bills64796042018-10-03 16:51:55 -07001064 {
Kuiying Wangbc546672018-11-23 15:41:05 +08001065 phosphor::logging::log<phosphor::logging::level::ERR>(
Jason M. Bills42bd9c82019-06-28 16:39:34 -07001066 "Failed to set processor error config",
1067 phosphor::logging::entry("EXCEPTION=%s", e.what()));
1068 return ipmi::responseUnspecifiedError();
Jason M. Bills64796042018-10-03 16:51:55 -07001069 }
1070
Jason M. Bills42bd9c82019-06-28 16:39:34 -07001071 return ipmi::responseSuccess();
Jason M. Bills64796042018-10-03 16:51:55 -07001072}
1073
Yong Li703922d2018-11-06 13:25:31 +08001074ipmi_ret_t ipmiOEMGetShutdownPolicy(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
1075 ipmi_request_t request,
1076 ipmi_response_t response,
1077 ipmi_data_len_t dataLen,
1078 ipmi_context_t context)
1079{
1080 GetOEMShutdownPolicyRes* resp =
1081 reinterpret_cast<GetOEMShutdownPolicyRes*>(response);
1082
1083 if (*dataLen != 0)
1084 {
1085 phosphor::logging::log<phosphor::logging::level::ERR>(
Kuiying Wang45f04982018-12-26 09:23:08 +08001086 "oem_get_shutdown_policy: invalid input len!");
Yong Li703922d2018-11-06 13:25:31 +08001087 *dataLen = 0;
1088 return IPMI_CC_REQ_DATA_LEN_INVALID;
1089 }
1090
1091 *dataLen = 0;
1092
1093 try
1094 {
Vernon Mauery15419dd2019-05-24 09:40:30 -07001095 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
Patrick Williamsb37abfb2023-05-10 07:50:33 -05001096 std::string service = getService(*dbus, oemShutdownPolicyIntf,
1097 oemShutdownPolicyObjPath);
Vernon Mauery15419dd2019-05-24 09:40:30 -07001098 Value variant = getDbusProperty(
1099 *dbus, service, oemShutdownPolicyObjPath, oemShutdownPolicyIntf,
1100 oemShutdownPolicyObjPathProp);
Yong Li0669d192019-05-06 14:01:46 +08001101
1102 if (sdbusplus::com::intel::Control::server::OCOTShutdownPolicy::
1103 convertPolicyFromString(std::get<std::string>(variant)) ==
1104 sdbusplus::com::intel::Control::server::OCOTShutdownPolicy::Policy::
1105 NoShutdownOnOCOT)
1106 {
1107 resp->policy = 0;
1108 }
1109 else if (sdbusplus::com::intel::Control::server::OCOTShutdownPolicy::
1110 convertPolicyFromString(std::get<std::string>(variant)) ==
1111 sdbusplus::com::intel::Control::server::OCOTShutdownPolicy::
1112 Policy::ShutdownOnOCOT)
1113 {
1114 resp->policy = 1;
1115 }
1116 else
1117 {
1118 phosphor::logging::log<phosphor::logging::level::ERR>(
1119 "oem_set_shutdown_policy: invalid property!",
1120 phosphor::logging::entry(
1121 "PROP=%s", std::get<std::string>(variant).c_str()));
1122 return IPMI_CC_UNSPECIFIED_ERROR;
1123 }
Yong Li703922d2018-11-06 13:25:31 +08001124 // TODO needs to check if it is multi-node products,
1125 // policy is only supported on node 3/4
1126 resp->policySupport = shutdownPolicySupported;
1127 }
Patrick Williamsbd51e6a2021-10-06 13:09:44 -05001128 catch (const sdbusplus::exception_t& e)
Yong Li703922d2018-11-06 13:25:31 +08001129 {
1130 phosphor::logging::log<phosphor::logging::level::ERR>(e.description());
1131 return IPMI_CC_UNSPECIFIED_ERROR;
1132 }
1133
1134 *dataLen = sizeof(GetOEMShutdownPolicyRes);
1135 return IPMI_CC_OK;
1136}
1137
1138ipmi_ret_t ipmiOEMSetShutdownPolicy(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
1139 ipmi_request_t request,
1140 ipmi_response_t response,
1141 ipmi_data_len_t dataLen,
1142 ipmi_context_t context)
1143{
1144 uint8_t* req = reinterpret_cast<uint8_t*>(request);
Yong Li0669d192019-05-06 14:01:46 +08001145 sdbusplus::com::intel::Control::server::OCOTShutdownPolicy::Policy policy =
1146 sdbusplus::com::intel::Control::server::OCOTShutdownPolicy::Policy::
1147 NoShutdownOnOCOT;
Yong Li703922d2018-11-06 13:25:31 +08001148
1149 // TODO needs to check if it is multi-node products,
1150 // policy is only supported on node 3/4
1151 if (*dataLen != 1)
1152 {
1153 phosphor::logging::log<phosphor::logging::level::ERR>(
1154 "oem_set_shutdown_policy: invalid input len!");
1155 *dataLen = 0;
1156 return IPMI_CC_REQ_DATA_LEN_INVALID;
1157 }
1158
1159 *dataLen = 0;
1160 if ((*req != noShutdownOnOCOT) && (*req != shutdownOnOCOT))
1161 {
1162 phosphor::logging::log<phosphor::logging::level::ERR>(
1163 "oem_set_shutdown_policy: invalid input!");
1164 return IPMI_CC_INVALID_FIELD_REQUEST;
1165 }
1166
Yong Li0669d192019-05-06 14:01:46 +08001167 if (*req == noShutdownOnOCOT)
1168 {
1169 policy = sdbusplus::com::intel::Control::server::OCOTShutdownPolicy::
1170 Policy::NoShutdownOnOCOT;
1171 }
1172 else
1173 {
1174 policy = sdbusplus::com::intel::Control::server::OCOTShutdownPolicy::
1175 Policy::ShutdownOnOCOT;
1176 }
1177
Yong Li703922d2018-11-06 13:25:31 +08001178 try
1179 {
Vernon Mauery15419dd2019-05-24 09:40:30 -07001180 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
Patrick Williamsb37abfb2023-05-10 07:50:33 -05001181 std::string service = getService(*dbus, oemShutdownPolicyIntf,
1182 oemShutdownPolicyObjPath);
Yong Li0669d192019-05-06 14:01:46 +08001183 setDbusProperty(
Vernon Mauery15419dd2019-05-24 09:40:30 -07001184 *dbus, service, oemShutdownPolicyObjPath, oemShutdownPolicyIntf,
Yong Li0669d192019-05-06 14:01:46 +08001185 oemShutdownPolicyObjPathProp,
1186 sdbusplus::com::intel::Control::server::convertForMessage(policy));
Yong Li703922d2018-11-06 13:25:31 +08001187 }
Patrick Williamsbd51e6a2021-10-06 13:09:44 -05001188 catch (const sdbusplus::exception_t& e)
Yong Li703922d2018-11-06 13:25:31 +08001189 {
1190 phosphor::logging::log<phosphor::logging::level::ERR>(e.description());
1191 return IPMI_CC_UNSPECIFIED_ERROR;
1192 }
1193
1194 return IPMI_CC_OK;
1195}
1196
Suryakanth Sekard509eb92018-11-15 17:44:11 +05301197/** @brief implementation for check the DHCP or not in IPv4
1198 * @param[in] Channel - Channel number
1199 * @returns true or false.
1200 */
1201static bool isDHCPEnabled(uint8_t Channel)
1202{
1203 try
1204 {
1205 auto ethdevice = getChannelName(Channel);
1206 if (ethdevice.empty())
1207 {
1208 return false;
1209 }
1210 auto ethIP = ethdevice + "/ipv4";
Vernon Mauery15419dd2019-05-24 09:40:30 -07001211 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
Patrick Williamsb37abfb2023-05-10 07:50:33 -05001212 auto ethernetObj = getDbusObject(*dbus, networkIPIntf, networkRoot,
1213 ethIP);
Vernon Mauery15419dd2019-05-24 09:40:30 -07001214 auto value = getDbusProperty(*dbus, networkService, ethernetObj.first,
Suryakanth Sekard509eb92018-11-15 17:44:11 +05301215 networkIPIntf, "Origin");
Vernon Mauery8166c8d2019-05-23 11:22:30 -07001216 if (std::get<std::string>(value) ==
Suryakanth Sekard509eb92018-11-15 17:44:11 +05301217 "xyz.openbmc_project.Network.IP.AddressOrigin.DHCP")
1218 {
1219 return true;
1220 }
1221 else
1222 {
1223 return false;
1224 }
1225 }
Patrick Williamsbd51e6a2021-10-06 13:09:44 -05001226 catch (const sdbusplus::exception_t& e)
Suryakanth Sekard509eb92018-11-15 17:44:11 +05301227 {
1228 phosphor::logging::log<phosphor::logging::level::ERR>(e.description());
1229 return true;
1230 }
1231}
1232
1233/** @brief implementes for check the DHCP or not in IPv6
1234 * @param[in] Channel - Channel number
1235 * @returns true or false.
1236 */
1237static bool isDHCPIPv6Enabled(uint8_t Channel)
1238{
Suryakanth Sekard509eb92018-11-15 17:44:11 +05301239 try
1240 {
1241 auto ethdevice = getChannelName(Channel);
1242 if (ethdevice.empty())
1243 {
1244 return false;
1245 }
1246 auto ethIP = ethdevice + "/ipv6";
Vernon Mauery15419dd2019-05-24 09:40:30 -07001247 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
Patrick Williamsb37abfb2023-05-10 07:50:33 -05001248 auto objectInfo = getDbusObject(*dbus, networkIPIntf, networkRoot,
1249 ethIP);
Vernon Mauery15419dd2019-05-24 09:40:30 -07001250 auto properties = getAllDbusProperties(*dbus, objectInfo.second,
Suryakanth Sekard509eb92018-11-15 17:44:11 +05301251 objectInfo.first, networkIPIntf);
Vernon Mauery8166c8d2019-05-23 11:22:30 -07001252 if (std::get<std::string>(properties["Origin"]) ==
Suryakanth Sekard509eb92018-11-15 17:44:11 +05301253 "xyz.openbmc_project.Network.IP.AddressOrigin.DHCP")
1254 {
1255 return true;
1256 }
1257 else
1258 {
1259 return false;
1260 }
1261 }
Patrick Williamsbd51e6a2021-10-06 13:09:44 -05001262 catch (const sdbusplus::exception_t& e)
Suryakanth Sekard509eb92018-11-15 17:44:11 +05301263 {
1264 phosphor::logging::log<phosphor::logging::level::ERR>(e.description());
1265 return true;
1266 }
1267}
1268
1269/** @brief implementes the creating of default new user
1270 * @param[in] userName - new username in 16 bytes.
1271 * @param[in] userPassword - new password in 20 bytes
1272 * @returns ipmi completion code.
1273 */
1274ipmi::RspType<> ipmiOEMSetUser2Activation(
1275 std::array<uint8_t, ipmi::ipmiMaxUserName>& userName,
Vernon Mauery3b3d29b2021-08-05 15:03:35 -07001276 const SecureBuffer& userPassword)
Suryakanth Sekard509eb92018-11-15 17:44:11 +05301277{
Vernon Mauery3b3d29b2021-08-05 15:03:35 -07001278 if (userPassword.size() != ipmi::maxIpmi20PasswordSize)
1279 {
1280 return ipmi::responseReqDataLenInvalid();
1281 }
Suryakanth Sekard509eb92018-11-15 17:44:11 +05301282 bool userState = false;
1283 // Check for System Interface not exist and LAN should be static
1284 for (uint8_t channel = 0; channel < maxIpmiChannels; channel++)
1285 {
Manish Baing440f62b2021-07-15 22:00:37 +00001286 ChannelInfo chInfo{};
Suryakanth Sekard509eb92018-11-15 17:44:11 +05301287 try
1288 {
1289 getChannelInfo(channel, chInfo);
1290 }
Patrick Williamsbd51e6a2021-10-06 13:09:44 -05001291 catch (const sdbusplus::exception_t& e)
Suryakanth Sekard509eb92018-11-15 17:44:11 +05301292 {
1293 phosphor::logging::log<phosphor::logging::level::ERR>(
1294 "ipmiOEMSetUser2Activation: Failed to get Channel Info",
1295 phosphor::logging::entry("MSG: %s", e.description()));
1296 return ipmi::response(ipmi::ccUnspecifiedError);
1297 }
1298 if (chInfo.mediumType ==
1299 static_cast<uint8_t>(EChannelMediumType::systemInterface))
1300 {
1301 phosphor::logging::log<phosphor::logging::level::ERR>(
1302 "ipmiOEMSetUser2Activation: system interface exist .");
1303 return ipmi::response(ipmi::ccCommandNotAvailable);
1304 }
1305 else
1306 {
Suryakanth Sekard509eb92018-11-15 17:44:11 +05301307 if (chInfo.mediumType ==
1308 static_cast<uint8_t>(EChannelMediumType::lan8032))
1309 {
1310 if (isDHCPIPv6Enabled(channel) || isDHCPEnabled(channel))
1311 {
1312 phosphor::logging::log<phosphor::logging::level::ERR>(
1313 "ipmiOEMSetUser2Activation: DHCP enabled .");
1314 return ipmi::response(ipmi::ccCommandNotAvailable);
1315 }
1316 }
1317 }
1318 }
1319 uint8_t maxChUsers = 0, enabledUsers = 0, fixedUsers = 0;
1320 if (ipmi::ccSuccess ==
1321 ipmiUserGetAllCounts(maxChUsers, enabledUsers, fixedUsers))
1322 {
1323 if (enabledUsers > 1)
1324 {
1325 phosphor::logging::log<phosphor::logging::level::ERR>(
1326 "ipmiOEMSetUser2Activation: more than one user is enabled.");
1327 return ipmi::response(ipmi::ccCommandNotAvailable);
1328 }
1329 // Check the user 2 is enabled or not
1330 ipmiUserCheckEnabled(ipmiDefaultUserId, userState);
1331 if (userState == true)
1332 {
1333 phosphor::logging::log<phosphor::logging::level::ERR>(
1334 "ipmiOEMSetUser2Activation: user 2 already enabled .");
1335 return ipmi::response(ipmi::ccCommandNotAvailable);
1336 }
1337 }
1338 else
1339 {
1340 return ipmi::response(ipmi::ccUnspecifiedError);
1341 }
1342
1343#if BYTE_ORDER == LITTLE_ENDIAN
1344 PrivAccess privAccess = {PRIVILEGE_ADMIN, true, true, true, 0};
1345#endif
1346#if BYTE_ORDER == BIG_ENDIAN
1347 PrivAccess privAccess = {0, true, true, true, PRIVILEGE_ADMIN};
1348#endif
1349
Vernon Mauery037cabd2020-05-14 12:16:01 -07001350 // ipmiUserSetUserName correctly handles char*, possibly non-null
1351 // terminated strings using ipmiMaxUserName size
Jayaprakash Mutyala3fbe8d22020-10-29 14:42:59 +00001352 size_t nameLen = strnlen(reinterpret_cast<const char*>(userName.data()),
1353 sizeof(userName));
1354 const std::string userNameRaw(
1355 reinterpret_cast<const char*>(userName.data()), nameLen);
jayaprakash Mutyala1429d4f2020-03-04 18:20:16 +00001356
Vernon Mauery037cabd2020-05-14 12:16:01 -07001357 if (ipmi::ccSuccess == ipmiUserSetUserName(ipmiDefaultUserId, userNameRaw))
Suryakanth Sekard509eb92018-11-15 17:44:11 +05301358 {
1359 if (ipmi::ccSuccess ==
1360 ipmiUserSetUserPassword(
1361 ipmiDefaultUserId,
1362 reinterpret_cast<const char*>(userPassword.data())))
1363 {
1364 if (ipmi::ccSuccess ==
1365 ipmiUserSetPrivilegeAccess(
1366 ipmiDefaultUserId,
1367 static_cast<uint8_t>(ipmi::EChannelID::chanLan1),
1368 privAccess, true))
1369 {
1370 phosphor::logging::log<phosphor::logging::level::INFO>(
1371 "ipmiOEMSetUser2Activation: user created successfully ");
Jayaprakash Mutyala94204162020-10-23 06:17:56 +00001372
Suryakanth Sekard509eb92018-11-15 17:44:11 +05301373 return ipmi::responseSuccess();
1374 }
1375 }
1376 // we need to delete the default user id which added in this command as
1377 // password / priv setting is failed.
Jayaprakash Mutyala3fbe8d22020-10-29 14:42:59 +00001378 ipmiUserSetUserName(ipmiDefaultUserId, static_cast<std::string>(""));
Suryakanth Sekard509eb92018-11-15 17:44:11 +05301379 phosphor::logging::log<phosphor::logging::level::ERR>(
1380 "ipmiOEMSetUser2Activation: password / priv setting is failed.");
1381 }
1382 else
1383 {
1384 phosphor::logging::log<phosphor::logging::level::ERR>(
1385 "ipmiOEMSetUser2Activation: Setting username failed.");
1386 }
1387
1388 return ipmi::response(ipmi::ccCommandNotAvailable);
1389}
1390
Suryakanth Sekar822b0b42019-11-15 18:32:53 +05301391/** @brief implementes executing the linux command
1392 * @param[in] linux command
1393 * @returns status
1394 */
1395
1396static uint8_t executeCmd(const char* path)
1397{
1398 boost::process::child execProg(path);
1399 execProg.wait();
1400
1401 int retCode = execProg.exit_code();
1402 if (retCode)
1403 {
1404 return ipmi::ccUnspecifiedError;
1405 }
1406 return ipmi::ccSuccess;
1407}
1408
1409/** @brief implementes ASD Security event logging
1410 * @param[in] Event message string
1411 * @param[in] Event Severity
1412 * @returns status
1413 */
1414
1415static void atScaleDebugEventlog(std::string msg, int severity)
1416{
1417 std::string eventStr = "OpenBMC.0.1." + msg;
1418 sd_journal_send("MESSAGE=Security Event: %s", eventStr.c_str(),
1419 "PRIORITY=%i", severity, "REDFISH_MESSAGE_ID=%s",
1420 eventStr.c_str(), NULL);
1421}
1422
Richard Marian Thomaiyarfc5e9852019-04-14 15:06:27 +05301423/** @brief implementes setting password for special user
1424 * @param[in] specialUserIndex
1425 * @param[in] userPassword - new password in 20 bytes
1426 * @returns ipmi completion code.
1427 */
1428ipmi::RspType<> ipmiOEMSetSpecialUserPassword(ipmi::Context::ptr ctx,
1429 uint8_t specialUserIndex,
1430 std::vector<uint8_t> userPassword)
1431{
1432 ChannelInfo chInfo;
Suryakanth Sekar822b0b42019-11-15 18:32:53 +05301433 ipmi_ret_t status = ipmi::ccSuccess;
1434
Richard Marian Thomaiyarfc5e9852019-04-14 15:06:27 +05301435 try
1436 {
1437 getChannelInfo(ctx->channel, chInfo);
1438 }
Patrick Williamsbd51e6a2021-10-06 13:09:44 -05001439 catch (const sdbusplus::exception_t& e)
Richard Marian Thomaiyarfc5e9852019-04-14 15:06:27 +05301440 {
1441 phosphor::logging::log<phosphor::logging::level::ERR>(
1442 "ipmiOEMSetSpecialUserPassword: Failed to get Channel Info",
1443 phosphor::logging::entry("MSG: %s", e.description()));
1444 return ipmi::responseUnspecifiedError();
1445 }
1446 if (chInfo.mediumType !=
1447 static_cast<uint8_t>(EChannelMediumType::systemInterface))
1448 {
1449 phosphor::logging::log<phosphor::logging::level::ERR>(
1450 "ipmiOEMSetSpecialUserPassword: Error - supported only in KCS "
1451 "interface");
1452 return ipmi::responseCommandNotAvailable();
1453 }
Suryakanth Sekar822b0b42019-11-15 18:32:53 +05301454
1455 // 0 for root user and 1 for AtScaleDebug is allowed
1456 if (specialUserIndex >
1457 static_cast<uint8_t>(SpecialUserIndex::atScaleDebugUser))
Richard Marian Thomaiyarfc5e9852019-04-14 15:06:27 +05301458 {
1459 phosphor::logging::log<phosphor::logging::level::ERR>(
1460 "ipmiOEMSetSpecialUserPassword: Invalid user account");
1461 return ipmi::responseParmOutOfRange();
1462 }
Suryakanth Sekar822b0b42019-11-15 18:32:53 +05301463 if (userPassword.size() != 0)
Richard Marian Thomaiyarfc5e9852019-04-14 15:06:27 +05301464 {
Suryakanth Sekar822b0b42019-11-15 18:32:53 +05301465 constexpr uint8_t minPasswordSizeRequired = 6;
Patrick Williams23939852021-09-02 11:18:35 -05001466 SecureString passwd;
Suryakanth Sekar822b0b42019-11-15 18:32:53 +05301467 if (userPassword.size() < minPasswordSizeRequired ||
1468 userPassword.size() > ipmi::maxIpmi20PasswordSize)
1469 {
Jayaprakash Mutyala94204162020-10-23 06:17:56 +00001470 OPENSSL_cleanse(userPassword.data(), userPassword.size());
Suryakanth Sekar822b0b42019-11-15 18:32:53 +05301471 return ipmi::responseReqDataLenInvalid();
1472 }
1473 passwd.assign(reinterpret_cast<const char*>(userPassword.data()),
1474 userPassword.size());
Jayaprakash Mutyala94204162020-10-23 06:17:56 +00001475 // Clear sensitive data
1476 OPENSSL_cleanse(userPassword.data(), userPassword.size());
Suryakanth Sekar822b0b42019-11-15 18:32:53 +05301477 if (specialUserIndex ==
1478 static_cast<uint8_t>(SpecialUserIndex::atScaleDebugUser))
1479 {
1480 status = ipmiSetSpecialUserPassword("asdbg", passwd);
1481
1482 atScaleDebugEventlog("AtScaleDebugSpecialUserEnabled", LOG_CRIT);
1483 }
1484 else
1485 {
1486 status = ipmiSetSpecialUserPassword("root", passwd);
1487 }
1488 return ipmi::response(status);
Richard Marian Thomaiyarfc5e9852019-04-14 15:06:27 +05301489 }
Suryakanth Sekar822b0b42019-11-15 18:32:53 +05301490 else
1491 {
1492 if (specialUserIndex ==
1493 static_cast<uint8_t>(SpecialUserIndex::rootUser))
1494 {
1495 status = executeCmd("passwd -d root");
1496 }
1497 else
1498 {
Suryakanth Sekar822b0b42019-11-15 18:32:53 +05301499 status = executeCmd("passwd -d asdbg");
1500
1501 if (status == 0)
1502 {
1503 atScaleDebugEventlog("AtScaleDebugSpecialUserDisabled",
1504 LOG_INFO);
1505 }
1506 }
1507 return ipmi::response(status);
1508 }
Richard Marian Thomaiyarfc5e9852019-04-14 15:06:27 +05301509}
1510
Kuiying Wang45f04982018-12-26 09:23:08 +08001511namespace ledAction
1512{
1513using namespace sdbusplus::xyz::openbmc_project::Led::server;
1514std::map<Physical::Action, uint8_t> actionDbusToIpmi = {
jayaprakash Mutyala934ee9c2019-12-13 17:49:27 +00001515 {Physical::Action::Off, 0},
1516 {Physical::Action::On, 2},
1517 {Physical::Action::Blink, 1}};
Kuiying Wang45f04982018-12-26 09:23:08 +08001518
1519std::map<uint8_t, std::string> offsetObjPath = {
1520 {2, statusAmberObjPath}, {4, statusGreenObjPath}, {6, identifyLEDObjPath}};
1521
1522} // namespace ledAction
1523
Patrick Williamsf944d2e2022-07-22 19:26:52 -05001524int8_t getLEDState(sdbusplus::bus_t& bus, const std::string& intf,
Kuiying Wang45f04982018-12-26 09:23:08 +08001525 const std::string& objPath, uint8_t& state)
1526{
1527 try
1528 {
1529 std::string service = getService(bus, intf, objPath);
Patrick Williamsb37abfb2023-05-10 07:50:33 -05001530 Value stateValue = getDbusProperty(bus, service, objPath, intf,
1531 "State");
Vernon Mauery8166c8d2019-05-23 11:22:30 -07001532 std::string strState = std::get<std::string>(stateValue);
Kuiying Wang45f04982018-12-26 09:23:08 +08001533 state = ledAction::actionDbusToIpmi.at(
1534 sdbusplus::xyz::openbmc_project::Led::server::Physical::
1535 convertActionFromString(strState));
1536 }
Patrick Williamsf944d2e2022-07-22 19:26:52 -05001537 catch (const sdbusplus::exception_t& e)
Kuiying Wang45f04982018-12-26 09:23:08 +08001538 {
1539 phosphor::logging::log<phosphor::logging::level::ERR>(e.what());
1540 return -1;
1541 }
1542 return 0;
1543}
1544
NITIN SHARMAabd11ca2019-06-12 12:31:42 +00001545ipmi::RspType<uint8_t> ipmiOEMGetLEDStatus()
Kuiying Wang45f04982018-12-26 09:23:08 +08001546{
NITIN SHARMAabd11ca2019-06-12 12:31:42 +00001547 uint8_t ledstate = 0;
Kuiying Wang45f04982018-12-26 09:23:08 +08001548 phosphor::logging::log<phosphor::logging::level::DEBUG>("GET led status");
Vernon Mauery15419dd2019-05-24 09:40:30 -07001549 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
Kuiying Wang45f04982018-12-26 09:23:08 +08001550 for (auto it = ledAction::offsetObjPath.begin();
1551 it != ledAction::offsetObjPath.end(); ++it)
1552 {
1553 uint8_t state = 0;
NITIN SHARMAabd11ca2019-06-12 12:31:42 +00001554 if (getLEDState(*dbus, ledIntf, it->second, state) == -1)
Kuiying Wang45f04982018-12-26 09:23:08 +08001555 {
1556 phosphor::logging::log<phosphor::logging::level::ERR>(
1557 "oem_get_led_status: fail to get ID LED status!");
NITIN SHARMAabd11ca2019-06-12 12:31:42 +00001558 return ipmi::responseUnspecifiedError();
Kuiying Wang45f04982018-12-26 09:23:08 +08001559 }
NITIN SHARMAabd11ca2019-06-12 12:31:42 +00001560 ledstate |= state << it->first;
Kuiying Wang45f04982018-12-26 09:23:08 +08001561 }
NITIN SHARMAabd11ca2019-06-12 12:31:42 +00001562 return ipmi::responseSuccess(ledstate);
Kuiying Wang45f04982018-12-26 09:23:08 +08001563}
1564
Yong Li23737fe2019-02-19 08:49:55 +08001565ipmi_ret_t ipmiOEMCfgHostSerialPortSpeed(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
1566 ipmi_request_t request,
1567 ipmi_response_t response,
1568 ipmi_data_len_t dataLen,
1569 ipmi_context_t context)
1570{
1571 CfgHostSerialReq* req = reinterpret_cast<CfgHostSerialReq*>(request);
1572 uint8_t* resp = reinterpret_cast<uint8_t*>(response);
1573
1574 if (*dataLen == 0)
1575 {
1576 phosphor::logging::log<phosphor::logging::level::ERR>(
1577 "CfgHostSerial: invalid input len!",
1578 phosphor::logging::entry("LEN=%d", *dataLen));
1579 return IPMI_CC_REQ_DATA_LEN_INVALID;
1580 }
1581
1582 switch (req->command)
1583 {
1584 case getHostSerialCfgCmd:
1585 {
1586 if (*dataLen != 1)
1587 {
1588 phosphor::logging::log<phosphor::logging::level::ERR>(
1589 "CfgHostSerial: invalid input len!");
1590 *dataLen = 0;
1591 return IPMI_CC_REQ_DATA_LEN_INVALID;
1592 }
1593
1594 *dataLen = 0;
1595
1596 boost::process::ipstream is;
1597 std::vector<std::string> data;
1598 std::string line;
1599 boost::process::child c1(fwGetEnvCmd, "-n", fwHostSerailCfgEnvName,
1600 boost::process::std_out > is);
1601
1602 while (c1.running() && std::getline(is, line) && !line.empty())
1603 {
1604 data.push_back(line);
1605 }
1606
1607 c1.wait();
1608 if (c1.exit_code())
1609 {
1610 phosphor::logging::log<phosphor::logging::level::ERR>(
1611 "CfgHostSerial:: error on execute",
1612 phosphor::logging::entry("EXECUTE=%s", fwSetEnvCmd));
1613 // Using the default value
1614 *resp = 0;
1615 }
1616 else
1617 {
1618 if (data.size() != 1)
1619 {
1620 phosphor::logging::log<phosphor::logging::level::ERR>(
1621 "CfgHostSerial:: error on read env");
1622 return IPMI_CC_UNSPECIFIED_ERROR;
1623 }
1624 try
1625 {
1626 unsigned long tmp = std::stoul(data[0]);
1627 if (tmp > std::numeric_limits<uint8_t>::max())
1628 {
1629 throw std::out_of_range("Out of range");
1630 }
1631 *resp = static_cast<uint8_t>(tmp);
1632 }
1633 catch (const std::invalid_argument& e)
1634 {
1635 phosphor::logging::log<phosphor::logging::level::ERR>(
1636 "invalid config ",
1637 phosphor::logging::entry("ERR=%s", e.what()));
1638 return IPMI_CC_UNSPECIFIED_ERROR;
1639 }
1640 catch (const std::out_of_range& e)
1641 {
1642 phosphor::logging::log<phosphor::logging::level::ERR>(
1643 "out_of_range config ",
1644 phosphor::logging::entry("ERR=%s", e.what()));
1645 return IPMI_CC_UNSPECIFIED_ERROR;
1646 }
1647 }
1648
1649 *dataLen = 1;
1650 break;
1651 }
1652 case setHostSerialCfgCmd:
1653 {
1654 if (*dataLen != sizeof(CfgHostSerialReq))
1655 {
1656 phosphor::logging::log<phosphor::logging::level::ERR>(
1657 "CfgHostSerial: invalid input len!");
1658 *dataLen = 0;
1659 return IPMI_CC_REQ_DATA_LEN_INVALID;
1660 }
1661
1662 *dataLen = 0;
1663
1664 if (req->parameter > HostSerialCfgParamMax)
1665 {
1666 phosphor::logging::log<phosphor::logging::level::ERR>(
1667 "CfgHostSerial: invalid input!");
1668 return IPMI_CC_INVALID_FIELD_REQUEST;
1669 }
1670
1671 boost::process::child c1(fwSetEnvCmd, fwHostSerailCfgEnvName,
1672 std::to_string(req->parameter));
1673
1674 c1.wait();
1675 if (c1.exit_code())
1676 {
1677 phosphor::logging::log<phosphor::logging::level::ERR>(
1678 "CfgHostSerial:: error on execute",
1679 phosphor::logging::entry("EXECUTE=%s", fwGetEnvCmd));
1680 return IPMI_CC_UNSPECIFIED_ERROR;
1681 }
1682 break;
1683 }
1684 default:
1685 phosphor::logging::log<phosphor::logging::level::ERR>(
1686 "CfgHostSerial: invalid input!");
1687 *dataLen = 0;
1688 return IPMI_CC_INVALID_FIELD_REQUEST;
1689 }
1690
1691 return IPMI_CC_OK;
1692}
1693
James Feist91244a62019-02-19 15:04:54 -08001694constexpr const char* thermalModeInterface =
1695 "xyz.openbmc_project.Control.ThermalMode";
1696constexpr const char* thermalModePath =
1697 "/xyz/openbmc_project/control/thermal_mode";
1698
1699bool getFanProfileInterface(
Patrick Williamsf944d2e2022-07-22 19:26:52 -05001700 sdbusplus::bus_t& bus,
Jason M. Bills0748c692022-09-08 15:34:08 -07001701 boost::container::flat_map<std::string, ipmi::DbusVariant>& resp)
James Feist91244a62019-02-19 15:04:54 -08001702{
1703 auto call = bus.new_method_call(settingsBusName, thermalModePath, PROP_INTF,
1704 "GetAll");
1705 call.append(thermalModeInterface);
1706 try
1707 {
1708 auto data = bus.call(call);
1709 data.read(resp);
1710 }
Patrick Williamsbd51e6a2021-10-06 13:09:44 -05001711 catch (const sdbusplus::exception_t& e)
James Feist91244a62019-02-19 15:04:54 -08001712 {
1713 phosphor::logging::log<phosphor::logging::level::ERR>(
1714 "getFanProfileInterface: can't get thermal mode!",
1715 phosphor::logging::entry("ERR=%s", e.what()));
1716 return false;
1717 }
1718 return true;
1719}
1720
anil kumar appanaf945eee2019-09-25 23:29:11 +00001721/**@brief implements the OEM set fan config.
1722 * @param selectedFanProfile - fan profile to enable
1723 * @param reserved1
1724 * @param performanceMode - Performance/Acoustic mode
1725 * @param reserved2
1726 * @param setPerformanceMode - set Performance/Acoustic mode
1727 * @param setFanProfile - set fan profile
1728 *
1729 * @return IPMI completion code.
1730 **/
1731ipmi::RspType<> ipmiOEMSetFanConfig(uint8_t selectedFanProfile,
1732
1733 uint2_t reserved1, bool performanceMode,
1734 uint3_t reserved2, bool setPerformanceMode,
Joshi-Mansi619186d2020-01-27 19:16:03 +05301735 bool setFanProfile,
1736 std::optional<uint8_t> dimmGroupId,
1737 std::optional<uint32_t> dimmPresenceBitmap)
James Feist91244a62019-02-19 15:04:54 -08001738{
anil kumar appanaf945eee2019-09-25 23:29:11 +00001739 if (reserved1 || reserved2)
James Feist91244a62019-02-19 15:04:54 -08001740 {
anil kumar appanaf945eee2019-09-25 23:29:11 +00001741 return ipmi::responseInvalidFieldRequest();
James Feist91244a62019-02-19 15:04:54 -08001742 }
Joshi-Mansi619186d2020-01-27 19:16:03 +05301743
1744 if (dimmGroupId)
1745 {
1746 if (*dimmGroupId >= maxCPUNum)
1747 {
1748 return ipmi::responseInvalidFieldRequest();
1749 }
Snehalatha Venkatesh6224dec2023-01-24 11:28:53 +00001750 if (!cpuPresent("cpu" + std::to_string(*dimmGroupId)))
Joshi-Mansi619186d2020-01-27 19:16:03 +05301751 {
1752 return ipmi::responseInvalidFieldRequest();
1753 }
1754 }
1755
James Feist91244a62019-02-19 15:04:54 -08001756 // todo: tell bios to only send first 2 bytes
Jason M. Bills0748c692022-09-08 15:34:08 -07001757 boost::container::flat_map<std::string, ipmi::DbusVariant> profileData;
Vernon Mauery15419dd2019-05-24 09:40:30 -07001758 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
1759 if (!getFanProfileInterface(*dbus, profileData))
James Feist91244a62019-02-19 15:04:54 -08001760 {
anil kumar appanaf945eee2019-09-25 23:29:11 +00001761 return ipmi::responseUnspecifiedError();
James Feist91244a62019-02-19 15:04:54 -08001762 }
1763
1764 std::vector<std::string>* supported =
1765 std::get_if<std::vector<std::string>>(&profileData["Supported"]);
1766 if (supported == nullptr)
1767 {
anil kumar appanaf945eee2019-09-25 23:29:11 +00001768 return ipmi::responseInvalidFieldRequest();
James Feist91244a62019-02-19 15:04:54 -08001769 }
1770 std::string mode;
anil kumar appanaf945eee2019-09-25 23:29:11 +00001771 if (setPerformanceMode)
James Feist91244a62019-02-19 15:04:54 -08001772 {
James Feist91244a62019-02-19 15:04:54 -08001773 if (performanceMode)
1774 {
James Feist91244a62019-02-19 15:04:54 -08001775 if (std::find(supported->begin(), supported->end(),
1776 "Performance") != supported->end())
1777 {
1778 mode = "Performance";
1779 }
1780 }
1781 else
1782 {
James Feist91244a62019-02-19 15:04:54 -08001783 if (std::find(supported->begin(), supported->end(), "Acoustic") !=
1784 supported->end())
1785 {
1786 mode = "Acoustic";
1787 }
1788 }
1789 if (mode.empty())
1790 {
anil kumar appanaf945eee2019-09-25 23:29:11 +00001791 return ipmi::responseInvalidFieldRequest();
James Feist91244a62019-02-19 15:04:54 -08001792 }
anil kumar appanaf945eee2019-09-25 23:29:11 +00001793
1794 try
1795 {
1796 setDbusProperty(*dbus, settingsBusName, thermalModePath,
1797 thermalModeInterface, "Current", mode);
1798 }
Patrick Williamsbd51e6a2021-10-06 13:09:44 -05001799 catch (const sdbusplus::exception_t& e)
anil kumar appanaf945eee2019-09-25 23:29:11 +00001800 {
1801 phosphor::logging::log<phosphor::logging::level::ERR>(
1802 "ipmiOEMSetFanConfig: can't set thermal mode!",
1803 phosphor::logging::entry("EXCEPTION=%s", e.what()));
1804 return ipmi::responseResponseError();
1805 }
James Feist91244a62019-02-19 15:04:54 -08001806 }
1807
anil kumar appanaf945eee2019-09-25 23:29:11 +00001808 return ipmi::responseSuccess();
James Feist91244a62019-02-19 15:04:54 -08001809}
1810
James Feist5b693632019-07-09 09:06:09 -07001811ipmi::RspType<uint8_t, // profile support map
1812 uint8_t, // fan control profile enable
1813 uint8_t, // flags
1814 uint32_t // dimm presence bit map
1815 >
1816 ipmiOEMGetFanConfig(uint8_t dimmGroupId)
James Feist91244a62019-02-19 15:04:54 -08001817{
Joshi-Mansi36f05ce2020-01-14 14:29:34 +05301818 if (dimmGroupId >= maxCPUNum)
1819 {
1820 return ipmi::responseInvalidFieldRequest();
1821 }
1822
Snehalatha Venkatesh6224dec2023-01-24 11:28:53 +00001823 bool cpuStatus = cpuPresent("cpu" + std::to_string(dimmGroupId));
Joshi-Mansi36f05ce2020-01-14 14:29:34 +05301824
1825 if (!cpuStatus)
1826 {
1827 return ipmi::responseInvalidFieldRequest();
1828 }
1829
Jason M. Bills0748c692022-09-08 15:34:08 -07001830 boost::container::flat_map<std::string, ipmi::DbusVariant> profileData;
James Feist91244a62019-02-19 15:04:54 -08001831
Vernon Mauery15419dd2019-05-24 09:40:30 -07001832 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
1833 if (!getFanProfileInterface(*dbus, profileData))
James Feist91244a62019-02-19 15:04:54 -08001834 {
James Feist5b693632019-07-09 09:06:09 -07001835 return ipmi::responseResponseError();
James Feist91244a62019-02-19 15:04:54 -08001836 }
1837
1838 std::string* current = std::get_if<std::string>(&profileData["Current"]);
1839
1840 if (current == nullptr)
1841 {
1842 phosphor::logging::log<phosphor::logging::level::ERR>(
1843 "ipmiOEMGetFanConfig: can't get current mode!");
James Feist5b693632019-07-09 09:06:09 -07001844 return ipmi::responseResponseError();
James Feist91244a62019-02-19 15:04:54 -08001845 }
1846 bool performance = (*current == "Performance");
1847
James Feist5b693632019-07-09 09:06:09 -07001848 uint8_t flags = 0;
James Feist91244a62019-02-19 15:04:54 -08001849 if (performance)
1850 {
James Feist5b693632019-07-09 09:06:09 -07001851 flags |= 1 << 2;
James Feist91244a62019-02-19 15:04:54 -08001852 }
1853
jayaprakash Mutyala4b1552d2020-02-11 12:07:29 +00001854 constexpr uint8_t fanControlDefaultProfile = 0x80;
1855 constexpr uint8_t fanControlProfileState = 0x00;
1856 constexpr uint32_t dimmPresenceBitmap = 0x00;
1857
1858 return ipmi::responseSuccess(fanControlDefaultProfile,
1859 fanControlProfileState, flags,
1860 dimmPresenceBitmap);
James Feist91244a62019-02-19 15:04:54 -08001861}
James Feist5f957ca2019-03-14 15:33:55 -07001862constexpr const char* cfmLimitSettingPath =
1863 "/xyz/openbmc_project/control/cfm_limit";
1864constexpr const char* cfmLimitIface = "xyz.openbmc_project.Control.CFMLimit";
James Feistfaa4f222019-03-21 16:21:55 -07001865constexpr const size_t legacyExitAirSensorNumber = 0x2e;
James Feist09f6b602019-08-08 11:30:03 -07001866constexpr const size_t legacyPCHSensorNumber = 0x22;
1867constexpr const char* exitAirPathName = "Exit_Air";
1868constexpr const char* pchPathName = "SSB_Temp";
James Feistacc8a4e2019-04-02 14:23:57 -07001869constexpr const char* pidConfigurationIface =
1870 "xyz.openbmc_project.Configuration.Pid";
James Feistfaa4f222019-03-21 16:21:55 -07001871
James Feist09f6b602019-08-08 11:30:03 -07001872static std::string getConfigPath(const std::string& name)
James Feistfaa4f222019-03-21 16:21:55 -07001873{
Vernon Mauery15419dd2019-05-24 09:40:30 -07001874 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
Patrick Williamsb37abfb2023-05-10 07:50:33 -05001875 auto method = dbus->new_method_call("xyz.openbmc_project.ObjectMapper",
1876 "/xyz/openbmc_project/object_mapper",
1877 "xyz.openbmc_project.ObjectMapper",
1878 "GetSubTree");
James Feistfaa4f222019-03-21 16:21:55 -07001879
James Feistacc8a4e2019-04-02 14:23:57 -07001880 method.append("/", 0, std::array<const char*, 1>{pidConfigurationIface});
James Feistfaa4f222019-03-21 16:21:55 -07001881 std::string path;
1882 GetSubTreeType resp;
1883 try
1884 {
Vernon Mauery15419dd2019-05-24 09:40:30 -07001885 auto reply = dbus->call(method);
James Feistfaa4f222019-03-21 16:21:55 -07001886 reply.read(resp);
1887 }
Patrick Williamsbd51e6a2021-10-06 13:09:44 -05001888 catch (const sdbusplus::exception_t&)
James Feistfaa4f222019-03-21 16:21:55 -07001889 {
1890 phosphor::logging::log<phosphor::logging::level::ERR>(
1891 "ipmiOEMGetFscParameter: mapper error");
1892 };
Patrick Williamsb37abfb2023-05-10 07:50:33 -05001893 auto config = std::find_if(resp.begin(), resp.end(),
1894 [&name](const auto& pair) {
1895 return pair.first.find(name) != std::string::npos;
1896 });
James Feistfaa4f222019-03-21 16:21:55 -07001897 if (config != resp.end())
1898 {
1899 path = std::move(config->first);
1900 }
1901 return path;
1902}
James Feist5f957ca2019-03-14 15:33:55 -07001903
James Feistacc8a4e2019-04-02 14:23:57 -07001904// flat map to make alphabetical
1905static boost::container::flat_map<std::string, PropertyMap> getPidConfigs()
1906{
1907 boost::container::flat_map<std::string, PropertyMap> ret;
Vernon Mauery15419dd2019-05-24 09:40:30 -07001908 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
Patrick Williamsb37abfb2023-05-10 07:50:33 -05001909 auto method = dbus->new_method_call("xyz.openbmc_project.ObjectMapper",
1910 "/xyz/openbmc_project/object_mapper",
1911 "xyz.openbmc_project.ObjectMapper",
1912 "GetSubTree");
James Feistacc8a4e2019-04-02 14:23:57 -07001913
1914 method.append("/", 0, std::array<const char*, 1>{pidConfigurationIface});
1915 GetSubTreeType resp;
1916
1917 try
1918 {
Vernon Mauery15419dd2019-05-24 09:40:30 -07001919 auto reply = dbus->call(method);
James Feistacc8a4e2019-04-02 14:23:57 -07001920 reply.read(resp);
1921 }
Patrick Williamsbd51e6a2021-10-06 13:09:44 -05001922 catch (const sdbusplus::exception_t&)
James Feistacc8a4e2019-04-02 14:23:57 -07001923 {
1924 phosphor::logging::log<phosphor::logging::level::ERR>(
1925 "getFanConfigPaths: mapper error");
1926 };
1927 for (const auto& [path, objects] : resp)
1928 {
1929 if (objects.empty())
1930 {
1931 continue; // should be impossible
1932 }
Zhu, Yungebe560b02019-04-21 21:19:21 -04001933
1934 try
1935 {
Vernon Mauery15419dd2019-05-24 09:40:30 -07001936 ret.emplace(path,
1937 getAllDbusProperties(*dbus, objects[0].first, path,
1938 pidConfigurationIface));
Zhu, Yungebe560b02019-04-21 21:19:21 -04001939 }
Patrick Williamsbd51e6a2021-10-06 13:09:44 -05001940 catch (const sdbusplus::exception_t& e)
Zhu, Yungebe560b02019-04-21 21:19:21 -04001941 {
1942 phosphor::logging::log<phosphor::logging::level::ERR>(
1943 "getPidConfigs: can't get DbusProperties!",
1944 phosphor::logging::entry("ERR=%s", e.what()));
1945 }
James Feistacc8a4e2019-04-02 14:23:57 -07001946 }
1947 return ret;
1948}
1949
1950ipmi::RspType<uint8_t> ipmiOEMGetFanSpeedOffset(void)
1951{
1952 boost::container::flat_map<std::string, PropertyMap> data = getPidConfigs();
1953 if (data.empty())
1954 {
1955 return ipmi::responseResponseError();
1956 }
1957 uint8_t minOffset = std::numeric_limits<uint8_t>::max();
1958 for (const auto& [_, pid] : data)
1959 {
1960 auto findClass = pid.find("Class");
1961 if (findClass == pid.end())
1962 {
1963 phosphor::logging::log<phosphor::logging::level::ERR>(
1964 "ipmiOEMGetFscParameter: found illegal pid "
1965 "configurations");
1966 return ipmi::responseResponseError();
1967 }
1968 std::string type = std::get<std::string>(findClass->second);
1969 if (type == "fan")
1970 {
1971 auto findOutLimit = pid.find("OutLimitMin");
1972 if (findOutLimit == pid.end())
1973 {
1974 phosphor::logging::log<phosphor::logging::level::ERR>(
1975 "ipmiOEMGetFscParameter: found illegal pid "
1976 "configurations");
1977 return ipmi::responseResponseError();
1978 }
1979 // get the min out of all the offsets
1980 minOffset = std::min(
1981 minOffset,
1982 static_cast<uint8_t>(std::get<double>(findOutLimit->second)));
1983 }
1984 }
1985 if (minOffset == std::numeric_limits<uint8_t>::max())
1986 {
1987 phosphor::logging::log<phosphor::logging::level::ERR>(
1988 "ipmiOEMGetFscParameter: found no fan configurations!");
1989 return ipmi::responseResponseError();
1990 }
1991
1992 return ipmi::responseSuccess(minOffset);
1993}
1994
1995ipmi::RspType<> ipmiOEMSetFanSpeedOffset(uint8_t offset)
1996{
Manish Baingbaa579f2021-10-08 22:30:32 +00001997 constexpr uint8_t maxFanSpeedOffset = 100;
1998 if (offset > maxFanSpeedOffset)
1999 {
2000 phosphor::logging::log<phosphor::logging::level::ERR>(
2001 "ipmiOEMSetFanSpeedOffset: fan offset greater than limit");
2002 return ipmi::responseInvalidFieldRequest();
2003 }
James Feistacc8a4e2019-04-02 14:23:57 -07002004 boost::container::flat_map<std::string, PropertyMap> data = getPidConfigs();
2005 if (data.empty())
2006 {
James Feistacc8a4e2019-04-02 14:23:57 -07002007 phosphor::logging::log<phosphor::logging::level::ERR>(
2008 "ipmiOEMSetFanSpeedOffset: found no pid configurations!");
2009 return ipmi::responseResponseError();
2010 }
2011
Vernon Mauery15419dd2019-05-24 09:40:30 -07002012 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
James Feistacc8a4e2019-04-02 14:23:57 -07002013 bool found = false;
2014 for (const auto& [path, pid] : data)
2015 {
2016 auto findClass = pid.find("Class");
2017 if (findClass == pid.end())
2018 {
James Feistacc8a4e2019-04-02 14:23:57 -07002019 phosphor::logging::log<phosphor::logging::level::ERR>(
2020 "ipmiOEMSetFanSpeedOffset: found illegal pid "
2021 "configurations");
2022 return ipmi::responseResponseError();
2023 }
2024 std::string type = std::get<std::string>(findClass->second);
2025 if (type == "fan")
2026 {
2027 auto findOutLimit = pid.find("OutLimitMin");
2028 if (findOutLimit == pid.end())
2029 {
James Feistacc8a4e2019-04-02 14:23:57 -07002030 phosphor::logging::log<phosphor::logging::level::ERR>(
2031 "ipmiOEMSetFanSpeedOffset: found illegal pid "
2032 "configurations");
2033 return ipmi::responseResponseError();
2034 }
Vernon Mauery15419dd2019-05-24 09:40:30 -07002035 ipmi::setDbusProperty(*dbus, "xyz.openbmc_project.EntityManager",
James Feistacc8a4e2019-04-02 14:23:57 -07002036 path, pidConfigurationIface, "OutLimitMin",
2037 static_cast<double>(offset));
2038 found = true;
2039 }
2040 }
2041 if (!found)
2042 {
2043 phosphor::logging::log<phosphor::logging::level::ERR>(
2044 "ipmiOEMSetFanSpeedOffset: set no fan offsets");
2045 return ipmi::responseResponseError();
2046 }
2047
2048 return ipmi::responseSuccess();
2049}
2050
2051ipmi::RspType<> ipmiOEMSetFscParameter(uint8_t command, uint8_t param1,
2052 uint8_t param2)
James Feist5f957ca2019-03-14 15:33:55 -07002053{
2054 constexpr const size_t disableLimiting = 0x0;
2055
Vernon Mauery15419dd2019-05-24 09:40:30 -07002056 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
James Feistacc8a4e2019-04-02 14:23:57 -07002057 if (command == static_cast<uint8_t>(setFscParamFlags::tcontrol))
James Feist5f957ca2019-03-14 15:33:55 -07002058 {
James Feist09f6b602019-08-08 11:30:03 -07002059 std::string pathName;
James Feistacc8a4e2019-04-02 14:23:57 -07002060 if (param1 == legacyExitAirSensorNumber)
James Feistfaa4f222019-03-21 16:21:55 -07002061 {
James Feist09f6b602019-08-08 11:30:03 -07002062 pathName = exitAirPathName;
2063 }
2064 else if (param1 == legacyPCHSensorNumber)
2065 {
2066 pathName = pchPathName;
James Feistfaa4f222019-03-21 16:21:55 -07002067 }
2068 else
2069 {
James Feistacc8a4e2019-04-02 14:23:57 -07002070 return ipmi::responseParmOutOfRange();
James Feistfaa4f222019-03-21 16:21:55 -07002071 }
James Feist09f6b602019-08-08 11:30:03 -07002072 std::string path = getConfigPath(pathName);
2073 ipmi::setDbusProperty(*dbus, "xyz.openbmc_project.EntityManager", path,
2074 pidConfigurationIface, "SetPoint",
2075 static_cast<double>(param2));
2076 return ipmi::responseSuccess();
James Feistfaa4f222019-03-21 16:21:55 -07002077 }
James Feistacc8a4e2019-04-02 14:23:57 -07002078 else if (command == static_cast<uint8_t>(setFscParamFlags::cfm))
James Feist5f957ca2019-03-14 15:33:55 -07002079 {
James Feistacc8a4e2019-04-02 14:23:57 -07002080 uint16_t cfm = param1 | (static_cast<uint16_t>(param2) << 8);
James Feist5f957ca2019-03-14 15:33:55 -07002081
2082 // must be greater than 50 based on eps
2083 if (cfm < 50 && cfm != disableLimiting)
2084 {
James Feistacc8a4e2019-04-02 14:23:57 -07002085 return ipmi::responseParmOutOfRange();
James Feist5f957ca2019-03-14 15:33:55 -07002086 }
2087
2088 try
2089 {
Vernon Mauery15419dd2019-05-24 09:40:30 -07002090 ipmi::setDbusProperty(*dbus, settingsBusName, cfmLimitSettingPath,
James Feist5f957ca2019-03-14 15:33:55 -07002091 cfmLimitIface, "Limit",
2092 static_cast<double>(cfm));
2093 }
Patrick Williamsbd51e6a2021-10-06 13:09:44 -05002094 catch (const sdbusplus::exception_t& e)
James Feist5f957ca2019-03-14 15:33:55 -07002095 {
2096 phosphor::logging::log<phosphor::logging::level::ERR>(
2097 "ipmiOEMSetFscParameter: can't set cfm setting!",
2098 phosphor::logging::entry("ERR=%s", e.what()));
James Feistacc8a4e2019-04-02 14:23:57 -07002099 return ipmi::responseResponseError();
James Feist5f957ca2019-03-14 15:33:55 -07002100 }
James Feistacc8a4e2019-04-02 14:23:57 -07002101 return ipmi::responseSuccess();
2102 }
2103 else if (command == static_cast<uint8_t>(setFscParamFlags::maxPwm))
2104 {
2105 constexpr const size_t maxDomainCount = 8;
2106 uint8_t requestedDomainMask = param1;
2107 boost::container::flat_map data = getPidConfigs();
2108 if (data.empty())
2109 {
James Feistacc8a4e2019-04-02 14:23:57 -07002110 phosphor::logging::log<phosphor::logging::level::ERR>(
2111 "ipmiOEMSetFscParameter: found no pid configurations!");
2112 return ipmi::responseResponseError();
2113 }
2114 size_t count = 0;
2115 for (const auto& [path, pid] : data)
2116 {
2117 auto findClass = pid.find("Class");
2118 if (findClass == pid.end())
2119 {
James Feistacc8a4e2019-04-02 14:23:57 -07002120 phosphor::logging::log<phosphor::logging::level::ERR>(
2121 "ipmiOEMSetFscParameter: found illegal pid "
2122 "configurations");
2123 return ipmi::responseResponseError();
2124 }
2125 std::string type = std::get<std::string>(findClass->second);
2126 if (type == "fan")
2127 {
2128 if (requestedDomainMask & (1 << count))
2129 {
2130 ipmi::setDbusProperty(
Vernon Mauery15419dd2019-05-24 09:40:30 -07002131 *dbus, "xyz.openbmc_project.EntityManager", path,
James Feistacc8a4e2019-04-02 14:23:57 -07002132 pidConfigurationIface, "OutLimitMax",
2133 static_cast<double>(param2));
2134 }
2135 count++;
2136 }
2137 }
2138 return ipmi::responseSuccess();
James Feist5f957ca2019-03-14 15:33:55 -07002139 }
2140 else
2141 {
2142 // todo other command parts possibly
2143 // tcontrol is handled in peci now
2144 // fan speed offset not implemented yet
2145 // domain pwm limit not implemented
James Feistacc8a4e2019-04-02 14:23:57 -07002146 return ipmi::responseParmOutOfRange();
James Feist5f957ca2019-03-14 15:33:55 -07002147 }
2148}
2149
James Feistacc8a4e2019-04-02 14:23:57 -07002150ipmi::RspType<
2151 std::variant<uint8_t, std::array<uint8_t, 2>, std::array<uint16_t, 2>>>
2152 ipmiOEMGetFscParameter(uint8_t command, std::optional<uint8_t> param)
James Feist5f957ca2019-03-14 15:33:55 -07002153{
James Feist09f6b602019-08-08 11:30:03 -07002154 constexpr uint8_t legacyDefaultSetpoint = -128;
James Feist5f957ca2019-03-14 15:33:55 -07002155
Vernon Mauery15419dd2019-05-24 09:40:30 -07002156 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
James Feistacc8a4e2019-04-02 14:23:57 -07002157 if (command == static_cast<uint8_t>(setFscParamFlags::tcontrol))
James Feist5f957ca2019-03-14 15:33:55 -07002158 {
James Feistacc8a4e2019-04-02 14:23:57 -07002159 if (!param)
James Feistfaa4f222019-03-21 16:21:55 -07002160 {
James Feistacc8a4e2019-04-02 14:23:57 -07002161 return ipmi::responseReqDataLenInvalid();
James Feistfaa4f222019-03-21 16:21:55 -07002162 }
2163
James Feist09f6b602019-08-08 11:30:03 -07002164 std::string pathName;
2165
2166 if (*param == legacyExitAirSensorNumber)
2167 {
2168 pathName = exitAirPathName;
2169 }
2170 else if (*param == legacyPCHSensorNumber)
2171 {
2172 pathName = pchPathName;
2173 }
2174 else
James Feistfaa4f222019-03-21 16:21:55 -07002175 {
James Feistacc8a4e2019-04-02 14:23:57 -07002176 return ipmi::responseParmOutOfRange();
James Feistfaa4f222019-03-21 16:21:55 -07002177 }
James Feist09f6b602019-08-08 11:30:03 -07002178
2179 uint8_t setpoint = legacyDefaultSetpoint;
2180 std::string path = getConfigPath(pathName);
James Feistfaa4f222019-03-21 16:21:55 -07002181 if (path.size())
2182 {
Vernon Mauery15419dd2019-05-24 09:40:30 -07002183 Value val = ipmi::getDbusProperty(
2184 *dbus, "xyz.openbmc_project.EntityManager", path,
2185 pidConfigurationIface, "SetPoint");
James Feistfaa4f222019-03-21 16:21:55 -07002186 setpoint = std::floor(std::get<double>(val) + 0.5);
2187 }
2188
2189 // old implementation used to return the "default" and current, we
2190 // don't make the default readily available so just make both the
2191 // same
James Feistfaa4f222019-03-21 16:21:55 -07002192
James Feistacc8a4e2019-04-02 14:23:57 -07002193 return ipmi::responseSuccess(
2194 std::array<uint8_t, 2>{setpoint, setpoint});
James Feistfaa4f222019-03-21 16:21:55 -07002195 }
James Feistacc8a4e2019-04-02 14:23:57 -07002196 else if (command == static_cast<uint8_t>(setFscParamFlags::maxPwm))
2197 {
2198 constexpr const size_t maxDomainCount = 8;
2199
2200 if (!param)
2201 {
2202 return ipmi::responseReqDataLenInvalid();
2203 }
2204 uint8_t requestedDomain = *param;
2205 if (requestedDomain >= maxDomainCount)
2206 {
2207 return ipmi::responseInvalidFieldRequest();
2208 }
2209
2210 boost::container::flat_map data = getPidConfigs();
2211 if (data.empty())
2212 {
2213 phosphor::logging::log<phosphor::logging::level::ERR>(
2214 "ipmiOEMGetFscParameter: found no pid configurations!");
2215 return ipmi::responseResponseError();
2216 }
2217 size_t count = 0;
2218 for (const auto& [_, pid] : data)
2219 {
2220 auto findClass = pid.find("Class");
2221 if (findClass == pid.end())
2222 {
2223 phosphor::logging::log<phosphor::logging::level::ERR>(
2224 "ipmiOEMGetFscParameter: found illegal pid "
2225 "configurations");
2226 return ipmi::responseResponseError();
2227 }
2228 std::string type = std::get<std::string>(findClass->second);
2229 if (type == "fan")
2230 {
2231 if (requestedDomain == count)
2232 {
2233 auto findOutLimit = pid.find("OutLimitMax");
2234 if (findOutLimit == pid.end())
2235 {
2236 phosphor::logging::log<phosphor::logging::level::ERR>(
2237 "ipmiOEMGetFscParameter: found illegal pid "
2238 "configurations");
2239 return ipmi::responseResponseError();
2240 }
2241
2242 return ipmi::responseSuccess(
2243 static_cast<uint8_t>(std::floor(
2244 std::get<double>(findOutLimit->second) + 0.5)));
2245 }
2246 else
2247 {
2248 count++;
2249 }
2250 }
2251 }
2252
2253 return ipmi::responseInvalidFieldRequest();
2254 }
2255 else if (command == static_cast<uint8_t>(setFscParamFlags::cfm))
James Feist5f957ca2019-03-14 15:33:55 -07002256 {
James Feist5f957ca2019-03-14 15:33:55 -07002257 /*
2258 DataLen should be 1, but host is sending us an extra bit. As the
James Feistacc8a4e2019-04-02 14:23:57 -07002259 previous behavior didn't seem to prevent this, ignore the check for
2260 now.
James Feist5f957ca2019-03-14 15:33:55 -07002261
James Feistacc8a4e2019-04-02 14:23:57 -07002262 if (param)
James Feist5f957ca2019-03-14 15:33:55 -07002263 {
2264 phosphor::logging::log<phosphor::logging::level::ERR>(
2265 "ipmiOEMGetFscParameter: invalid input len!");
James Feist5f957ca2019-03-14 15:33:55 -07002266 return IPMI_CC_REQ_DATA_LEN_INVALID;
2267 }
2268 */
2269 Value cfmLimit;
2270 Value cfmMaximum;
2271 try
2272 {
Vernon Mauery15419dd2019-05-24 09:40:30 -07002273 cfmLimit = ipmi::getDbusProperty(*dbus, settingsBusName,
James Feist5f957ca2019-03-14 15:33:55 -07002274 cfmLimitSettingPath, cfmLimitIface,
2275 "Limit");
2276 cfmMaximum = ipmi::getDbusProperty(
Vernon Mauery15419dd2019-05-24 09:40:30 -07002277 *dbus, "xyz.openbmc_project.ExitAirTempSensor",
James Feist5f957ca2019-03-14 15:33:55 -07002278 "/xyz/openbmc_project/control/MaxCFM", cfmLimitIface, "Limit");
2279 }
Patrick Williamsbd51e6a2021-10-06 13:09:44 -05002280 catch (const sdbusplus::exception_t& e)
James Feist5f957ca2019-03-14 15:33:55 -07002281 {
2282 phosphor::logging::log<phosphor::logging::level::ERR>(
James Feistacc8a4e2019-04-02 14:23:57 -07002283 "ipmiOEMGetFscParameter: can't get cfm setting!",
James Feist5f957ca2019-03-14 15:33:55 -07002284 phosphor::logging::entry("ERR=%s", e.what()));
James Feistacc8a4e2019-04-02 14:23:57 -07002285 return ipmi::responseResponseError();
James Feist5f957ca2019-03-14 15:33:55 -07002286 }
2287
James Feistacc8a4e2019-04-02 14:23:57 -07002288 double cfmMax = std::get<double>(cfmMaximum);
2289 double cfmLim = std::get<double>(cfmLimit);
James Feist5f957ca2019-03-14 15:33:55 -07002290
James Feistacc8a4e2019-04-02 14:23:57 -07002291 cfmLim = std::floor(cfmLim + 0.5);
2292 cfmMax = std::floor(cfmMax + 0.5);
2293 uint16_t cfmLimResp = static_cast<uint16_t>(cfmLim);
2294 uint16_t cfmMaxResp = static_cast<uint16_t>(cfmMax);
James Feist5f957ca2019-03-14 15:33:55 -07002295
James Feistacc8a4e2019-04-02 14:23:57 -07002296 return ipmi::responseSuccess(
2297 std::array<uint16_t, 2>{cfmLimResp, cfmMaxResp});
James Feist5f957ca2019-03-14 15:33:55 -07002298 }
James Feistacc8a4e2019-04-02 14:23:57 -07002299
James Feist5f957ca2019-03-14 15:33:55 -07002300 else
2301 {
2302 // todo other command parts possibly
James Feist5f957ca2019-03-14 15:33:55 -07002303 // domain pwm limit not implemented
James Feistacc8a4e2019-04-02 14:23:57 -07002304 return ipmi::responseParmOutOfRange();
James Feist5f957ca2019-03-14 15:33:55 -07002305 }
2306}
2307
Jason M. Bills0748c692022-09-08 15:34:08 -07002308using crConfigVariant = ipmi::DbusVariant;
Cheng C Yang773703a2019-08-15 09:41:11 +08002309
2310int setCRConfig(ipmi::Context::ptr ctx, const std::string& property,
2311 const crConfigVariant& value,
2312 std::chrono::microseconds timeout = ipmi::IPMI_DBUS_TIMEOUT)
2313{
2314 boost::system::error_code ec;
2315 ctx->bus->yield_method_call<void>(
Kuiying Wange45333a2020-07-22 22:06:37 +08002316 ctx->yield, ec, "xyz.openbmc_project.PSURedundancy",
Cheng C Yang773703a2019-08-15 09:41:11 +08002317 "/xyz/openbmc_project/control/power_supply_redundancy",
2318 "org.freedesktop.DBus.Properties", "Set",
2319 "xyz.openbmc_project.Control.PowerSupplyRedundancy", property, value);
2320 if (ec)
2321 {
2322 phosphor::logging::log<phosphor::logging::level::ERR>(
2323 "Failed to set dbus property to cold redundancy");
2324 return -1;
2325 }
2326
2327 return 0;
2328}
2329
Kuiying Wange45333a2020-07-22 22:06:37 +08002330int getCRConfig(
2331 ipmi::Context::ptr ctx, const std::string& property, crConfigVariant& value,
2332 const std::string& service = "xyz.openbmc_project.PSURedundancy",
2333 std::chrono::microseconds timeout = ipmi::IPMI_DBUS_TIMEOUT)
Cheng C Yang773703a2019-08-15 09:41:11 +08002334{
2335 boost::system::error_code ec;
2336 value = ctx->bus->yield_method_call<crConfigVariant>(
Yong Li19445ab2019-12-20 18:25:29 +08002337 ctx->yield, ec, service,
Cheng C Yang773703a2019-08-15 09:41:11 +08002338 "/xyz/openbmc_project/control/power_supply_redundancy",
2339 "org.freedesktop.DBus.Properties", "Get",
2340 "xyz.openbmc_project.Control.PowerSupplyRedundancy", property);
2341 if (ec)
2342 {
2343 phosphor::logging::log<phosphor::logging::level::ERR>(
2344 "Failed to get dbus property to cold redundancy");
2345 return -1;
2346 }
2347 return 0;
2348}
2349
2350uint8_t getPSUCount(void)
2351{
2352 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
2353 ipmi::Value num;
2354 try
2355 {
2356 num = ipmi::getDbusProperty(
2357 *dbus, "xyz.openbmc_project.PSURedundancy",
2358 "/xyz/openbmc_project/control/power_supply_redundancy",
2359 "xyz.openbmc_project.Control.PowerSupplyRedundancy", "PSUNumber");
2360 }
Patrick Williamsbd51e6a2021-10-06 13:09:44 -05002361 catch (const sdbusplus::exception_t& e)
Cheng C Yang773703a2019-08-15 09:41:11 +08002362 {
2363 phosphor::logging::log<phosphor::logging::level::ERR>(
2364 "Failed to get PSUNumber property from dbus interface");
2365 return 0;
2366 }
2367 uint8_t* pNum = std::get_if<uint8_t>(&num);
2368 if (!pNum)
2369 {
2370 phosphor::logging::log<phosphor::logging::level::ERR>(
2371 "Error to get PSU Number");
2372 return 0;
2373 }
2374 return *pNum;
2375}
2376
2377bool validateCRAlgo(std::vector<uint8_t>& conf, uint8_t num)
2378{
2379 if (conf.size() < num)
2380 {
2381 phosphor::logging::log<phosphor::logging::level::ERR>(
2382 "Invalid PSU Ranking");
2383 return false;
2384 }
2385 std::set<uint8_t> confSet;
2386 for (uint8_t i = 0; i < num; i++)
2387 {
2388 if (conf[i] > num)
2389 {
2390 phosphor::logging::log<phosphor::logging::level::ERR>(
2391 "PSU Ranking is larger than current PSU number");
2392 return false;
2393 }
2394 confSet.emplace(conf[i]);
2395 }
2396
2397 if (confSet.size() != num)
2398 {
2399 phosphor::logging::log<phosphor::logging::level::ERR>(
2400 "duplicate PSU Ranking");
2401 return false;
2402 }
2403 return true;
2404}
2405
2406enum class crParameter
2407{
2408 crStatus = 0,
2409 crFeature = 1,
2410 rotationFeature = 2,
2411 rotationAlgo = 3,
2412 rotationPeriod = 4,
Yong Li19445ab2019-12-20 18:25:29 +08002413 numOfPSU = 5,
2414 rotationRankOrderEffective = 6
Cheng C Yang773703a2019-08-15 09:41:11 +08002415};
2416
2417constexpr ipmi::Cc ccParameterNotSupported = 0x80;
2418static const constexpr uint32_t oneDay = 0x15180;
2419static const constexpr uint32_t oneMonth = 0xf53700;
2420static const constexpr uint8_t userSpecific = 0x01;
2421static const constexpr uint8_t crSetCompleted = 0;
2422ipmi::RspType<uint8_t> ipmiOEMSetCRConfig(ipmi::Context::ptr ctx,
2423 uint8_t parameter,
2424 ipmi::message::Payload& payload)
2425{
2426 switch (static_cast<crParameter>(parameter))
2427 {
Cheng C Yang773703a2019-08-15 09:41:11 +08002428 case crParameter::rotationFeature:
2429 {
2430 uint8_t param1;
2431 if (payload.unpack(param1) || !payload.fullyUnpacked())
2432 {
2433 return ipmi::responseReqDataLenInvalid();
2434 }
2435 // Rotation Enable can only be true or false
2436 if (param1 > 1)
2437 {
2438 return ipmi::responseInvalidFieldRequest();
2439 }
2440 if (setCRConfig(ctx, "RotationEnabled", static_cast<bool>(param1)))
2441 {
2442 return ipmi::responseResponseError();
2443 }
2444 break;
2445 }
2446 case crParameter::rotationAlgo:
2447 {
2448 // Rotation Algorithm can only be 0-BMC Specific or 1-User Specific
2449 std::string algoName;
2450 uint8_t param1;
2451 if (payload.unpack(param1))
2452 {
2453 return ipmi::responseReqDataLenInvalid();
2454 }
2455 switch (param1)
2456 {
2457 case 0:
2458 algoName = "xyz.openbmc_project.Control."
2459 "PowerSupplyRedundancy.Algo.bmcSpecific";
2460 break;
2461 case 1:
2462 algoName = "xyz.openbmc_project.Control."
2463 "PowerSupplyRedundancy.Algo.userSpecific";
2464 break;
2465 default:
2466 return ipmi::responseInvalidFieldRequest();
2467 }
2468 if (setCRConfig(ctx, "RotationAlgorithm", algoName))
2469 {
2470 return ipmi::responseResponseError();
2471 }
2472
2473 uint8_t numberOfPSU = getPSUCount();
2474 if (!numberOfPSU)
2475 {
2476 return ipmi::responseResponseError();
2477 }
2478 std::vector<uint8_t> rankOrder;
2479
2480 if (param1 == userSpecific)
2481 {
2482 if (payload.unpack(rankOrder) || !payload.fullyUnpacked())
2483 {
2484 ipmi::responseReqDataLenInvalid();
2485 }
Yong Li83315132019-10-23 17:42:24 +08002486 if (rankOrder.size() != numberOfPSU)
Cheng C Yang773703a2019-08-15 09:41:11 +08002487 {
2488 return ipmi::responseReqDataLenInvalid();
2489 }
2490
2491 if (!validateCRAlgo(rankOrder, numberOfPSU))
2492 {
2493 return ipmi::responseInvalidFieldRequest();
2494 }
2495 }
2496 else
2497 {
2498 if (rankOrder.size() > 0)
2499 {
2500 return ipmi::responseReqDataLenInvalid();
2501 }
2502 for (uint8_t i = 1; i <= numberOfPSU; i++)
2503 {
2504 rankOrder.emplace_back(i);
2505 }
2506 }
2507 if (setCRConfig(ctx, "RotationRankOrder", rankOrder))
2508 {
2509 return ipmi::responseResponseError();
2510 }
2511 break;
2512 }
2513 case crParameter::rotationPeriod:
2514 {
2515 // Minimum Rotation period is One day (86400 seconds) and Max
2516 // Rotation Period is 6 month (0xf53700 seconds)
2517 uint32_t period;
2518 if (payload.unpack(period) || !payload.fullyUnpacked())
2519 {
2520 return ipmi::responseReqDataLenInvalid();
2521 }
2522 if ((period < oneDay) || (period > oneMonth))
2523 {
2524 return ipmi::responseInvalidFieldRequest();
2525 }
2526 if (setCRConfig(ctx, "PeriodOfRotation", period))
2527 {
2528 return ipmi::responseResponseError();
2529 }
2530 break;
2531 }
2532 default:
2533 {
2534 return ipmi::response(ccParameterNotSupported);
2535 }
2536 }
2537
Cheng C Yang773703a2019-08-15 09:41:11 +08002538 return ipmi::responseSuccess(crSetCompleted);
2539}
2540
Yong Li83315132019-10-23 17:42:24 +08002541ipmi::RspType<uint8_t, std::variant<uint8_t, uint32_t, std::vector<uint8_t>>>
Cheng C Yang773703a2019-08-15 09:41:11 +08002542 ipmiOEMGetCRConfig(ipmi::Context::ptr ctx, uint8_t parameter)
2543{
2544 crConfigVariant value;
2545 switch (static_cast<crParameter>(parameter))
2546 {
2547 case crParameter::crStatus:
2548 {
2549 if (getCRConfig(ctx, "ColdRedundancyStatus", value))
2550 {
2551 return ipmi::responseResponseError();
2552 }
2553 std::string* pStatus = std::get_if<std::string>(&value);
2554 if (!pStatus)
2555 {
2556 phosphor::logging::log<phosphor::logging::level::ERR>(
2557 "Error to get ColdRedundancyStatus property");
2558 return ipmi::responseResponseError();
2559 }
2560 namespace server = sdbusplus::xyz::openbmc_project::Control::server;
2561 auto status =
2562 server::PowerSupplyRedundancy::convertStatusFromString(
2563 *pStatus);
2564 switch (status)
2565 {
2566 case server::PowerSupplyRedundancy::Status::inProgress:
Cheng C Yangf41e3342019-09-10 04:47:23 +08002567 return ipmi::responseSuccess(parameter,
Kuiying Wange45333a2020-07-22 22:06:37 +08002568 static_cast<uint8_t>(1));
Cheng C Yang773703a2019-08-15 09:41:11 +08002569
2570 case server::PowerSupplyRedundancy::Status::completed:
Cheng C Yangf41e3342019-09-10 04:47:23 +08002571 return ipmi::responseSuccess(parameter,
Kuiying Wange45333a2020-07-22 22:06:37 +08002572 static_cast<uint8_t>(0));
Cheng C Yang773703a2019-08-15 09:41:11 +08002573 default:
2574 phosphor::logging::log<phosphor::logging::level::ERR>(
2575 "Error to get valid status");
2576 return ipmi::responseResponseError();
2577 }
2578 }
2579 case crParameter::crFeature:
2580 {
Kuiying Wange45333a2020-07-22 22:06:37 +08002581 if (getCRConfig(ctx, "PowerSupplyRedundancyEnabled", value))
Cheng C Yang773703a2019-08-15 09:41:11 +08002582 {
2583 return ipmi::responseResponseError();
2584 }
2585 bool* pResponse = std::get_if<bool>(&value);
2586 if (!pResponse)
2587 {
2588 phosphor::logging::log<phosphor::logging::level::ERR>(
Kuiying Wange45333a2020-07-22 22:06:37 +08002589 "Error to get PowerSupplyRedundancyEnabled property");
Cheng C Yang773703a2019-08-15 09:41:11 +08002590 return ipmi::responseResponseError();
2591 }
2592
Cheng C Yangf41e3342019-09-10 04:47:23 +08002593 return ipmi::responseSuccess(parameter,
2594 static_cast<uint8_t>(*pResponse));
Cheng C Yang773703a2019-08-15 09:41:11 +08002595 }
2596 case crParameter::rotationFeature:
2597 {
2598 if (getCRConfig(ctx, "RotationEnabled", value))
2599 {
2600 return ipmi::responseResponseError();
2601 }
2602 bool* pResponse = std::get_if<bool>(&value);
2603 if (!pResponse)
2604 {
2605 phosphor::logging::log<phosphor::logging::level::ERR>(
2606 "Error to get RotationEnabled property");
2607 return ipmi::responseResponseError();
2608 }
Cheng C Yangf41e3342019-09-10 04:47:23 +08002609 return ipmi::responseSuccess(parameter,
2610 static_cast<uint8_t>(*pResponse));
Cheng C Yang773703a2019-08-15 09:41:11 +08002611 }
2612 case crParameter::rotationAlgo:
2613 {
2614 if (getCRConfig(ctx, "RotationAlgorithm", value))
2615 {
2616 return ipmi::responseResponseError();
2617 }
2618
2619 std::string* pAlgo = std::get_if<std::string>(&value);
2620 if (!pAlgo)
2621 {
2622 phosphor::logging::log<phosphor::logging::level::ERR>(
2623 "Error to get RotationAlgorithm property");
2624 return ipmi::responseResponseError();
2625 }
Yong Li83315132019-10-23 17:42:24 +08002626 std::vector<uint8_t> response;
Cheng C Yang773703a2019-08-15 09:41:11 +08002627 namespace server = sdbusplus::xyz::openbmc_project::Control::server;
2628 auto algo =
2629 server::PowerSupplyRedundancy::convertAlgoFromString(*pAlgo);
Yong Li83315132019-10-23 17:42:24 +08002630
Cheng C Yang773703a2019-08-15 09:41:11 +08002631 switch (algo)
2632 {
2633 case server::PowerSupplyRedundancy::Algo::bmcSpecific:
Yong Li83315132019-10-23 17:42:24 +08002634 response.push_back(0);
Cheng C Yang773703a2019-08-15 09:41:11 +08002635 break;
2636 case server::PowerSupplyRedundancy::Algo::userSpecific:
Yong Li83315132019-10-23 17:42:24 +08002637 response.push_back(1);
Cheng C Yang773703a2019-08-15 09:41:11 +08002638 break;
2639 default:
2640 phosphor::logging::log<phosphor::logging::level::ERR>(
2641 "Error to get valid algo");
2642 return ipmi::responseResponseError();
2643 }
2644
2645 if (getCRConfig(ctx, "RotationRankOrder", value))
2646 {
2647 return ipmi::responseResponseError();
2648 }
2649 std::vector<uint8_t>* pResponse =
2650 std::get_if<std::vector<uint8_t>>(&value);
2651 if (!pResponse)
2652 {
2653 phosphor::logging::log<phosphor::logging::level::ERR>(
2654 "Error to get RotationRankOrder property");
2655 return ipmi::responseResponseError();
2656 }
Yong Li83315132019-10-23 17:42:24 +08002657
Cheng C Yang773703a2019-08-15 09:41:11 +08002658 std::copy(pResponse->begin(), pResponse->end(),
Yong Li83315132019-10-23 17:42:24 +08002659 std::back_inserter(response));
2660
Cheng C Yangf41e3342019-09-10 04:47:23 +08002661 return ipmi::responseSuccess(parameter, response);
Cheng C Yang773703a2019-08-15 09:41:11 +08002662 }
2663 case crParameter::rotationPeriod:
2664 {
2665 if (getCRConfig(ctx, "PeriodOfRotation", value))
2666 {
2667 return ipmi::responseResponseError();
2668 }
2669 uint32_t* pResponse = std::get_if<uint32_t>(&value);
2670 if (!pResponse)
2671 {
2672 phosphor::logging::log<phosphor::logging::level::ERR>(
2673 "Error to get RotationAlgorithm property");
2674 return ipmi::responseResponseError();
2675 }
Cheng C Yangf41e3342019-09-10 04:47:23 +08002676 return ipmi::responseSuccess(parameter, *pResponse);
Cheng C Yang773703a2019-08-15 09:41:11 +08002677 }
2678 case crParameter::numOfPSU:
2679 {
2680 uint8_t numberOfPSU = getPSUCount();
2681 if (!numberOfPSU)
2682 {
2683 return ipmi::responseResponseError();
2684 }
Cheng C Yangf41e3342019-09-10 04:47:23 +08002685 return ipmi::responseSuccess(parameter, numberOfPSU);
Cheng C Yang773703a2019-08-15 09:41:11 +08002686 }
Yong Li19445ab2019-12-20 18:25:29 +08002687 case crParameter::rotationRankOrderEffective:
2688 {
2689 if (getCRConfig(ctx, "RotationRankOrder", value,
2690 "xyz.openbmc_project.PSURedundancy"))
2691 {
2692 return ipmi::responseResponseError();
2693 }
2694 std::vector<uint8_t>* pResponse =
2695 std::get_if<std::vector<uint8_t>>(&value);
2696 if (!pResponse)
2697 {
2698 phosphor::logging::log<phosphor::logging::level::ERR>(
2699 "Error to get effective RotationRankOrder property");
2700 return ipmi::responseResponseError();
2701 }
2702 return ipmi::responseSuccess(parameter, *pResponse);
2703 }
Cheng C Yang773703a2019-08-15 09:41:11 +08002704 default:
2705 {
2706 return ipmi::response(ccParameterNotSupported);
2707 }
2708 }
2709}
2710
Zhu, Yungebe560b02019-04-21 21:19:21 -04002711ipmi::RspType<> ipmiOEMSetFaultIndication(uint8_t sourceId, uint8_t faultType,
2712 uint8_t faultState,
2713 uint8_t faultGroup,
2714 std::array<uint8_t, 8>& ledStateData)
2715{
Zhu, Yungebe560b02019-04-21 21:19:21 -04002716 constexpr auto maxFaultType = static_cast<size_t>(RemoteFaultType::max);
2717 static const std::array<std::string, maxFaultType> faultNames = {
2718 "faultFan", "faultTemp", "faultPower",
2719 "faultDriveSlot", "faultSoftware", "faultMemory"};
Zhu, Yungebe560b02019-04-21 21:19:21 -04002720
2721 constexpr uint8_t maxFaultSource = 0x4;
2722 constexpr uint8_t skipLEDs = 0xFF;
2723 constexpr uint8_t pinSize = 64;
2724 constexpr uint8_t groupSize = 16;
Zhikui Rence4e73f2019-12-06 13:59:47 -08002725 constexpr uint8_t groupNum = 5; // 4 for fault memory, 1 for faultFan
Zhu, Yungebe560b02019-04-21 21:19:21 -04002726
Zhikui Rence4e73f2019-12-06 13:59:47 -08002727 // same pin names need to be defined in dts file
2728 static const std::array<std::array<std::string, groupSize>, groupNum>
2729 faultLedPinNames = {{
2730 "LED_CPU1_CH1_DIMM1_FAULT",
2731 "LED_CPU1_CH1_DIMM2_FAULT",
2732 "LED_CPU1_CH2_DIMM1_FAULT",
2733 "LED_CPU1_CH2_DIMM2_FAULT",
2734 "LED_CPU1_CH3_DIMM1_FAULT",
2735 "LED_CPU1_CH3_DIMM2_FAULT",
2736 "LED_CPU1_CH4_DIMM1_FAULT",
2737 "LED_CPU1_CH4_DIMM2_FAULT",
2738 "LED_CPU1_CH5_DIMM1_FAULT",
2739 "LED_CPU1_CH5_DIMM2_FAULT",
2740 "LED_CPU1_CH6_DIMM1_FAULT",
2741 "LED_CPU1_CH6_DIMM2_FAULT",
2742 "",
2743 "",
2744 "",
2745 "", // end of group1
2746 "LED_CPU2_CH1_DIMM1_FAULT",
2747 "LED_CPU2_CH1_DIMM2_FAULT",
2748 "LED_CPU2_CH2_DIMM1_FAULT",
2749 "LED_CPU2_CH2_DIMM2_FAULT",
2750 "LED_CPU2_CH3_DIMM1_FAULT",
2751 "LED_CPU2_CH3_DIMM2_FAULT",
2752 "LED_CPU2_CH4_DIMM1_FAULT",
2753 "LED_CPU2_CH4_DIMM2_FAULT",
2754 "LED_CPU2_CH5_DIMM1_FAULT",
2755 "LED_CPU2_CH5_DIMM2_FAULT",
2756 "LED_CPU2_CH6_DIMM1_FAULT",
2757 "LED_CPU2_CH6_DIMM2_FAULT",
2758 "",
2759 "",
2760 "",
2761 "", // endof group2
2762 "LED_CPU3_CH1_DIMM1_FAULT",
2763 "LED_CPU3_CH1_DIMM2_FAULT",
2764 "LED_CPU3_CH2_DIMM1_FAULT",
2765 "LED_CPU3_CH2_DIMM2_FAULT",
2766 "LED_CPU3_CH3_DIMM1_FAULT",
2767 "LED_CPU3_CH3_DIMM2_FAULT",
2768 "LED_CPU3_CH4_DIMM1_FAULT",
2769 "LED_CPU3_CH4_DIMM2_FAULT",
2770 "LED_CPU3_CH5_DIMM1_FAULT",
2771 "LED_CPU3_CH5_DIMM2_FAULT",
2772 "LED_CPU3_CH6_DIMM1_FAULT",
2773 "LED_CPU3_CH6_DIMM2_FAULT",
2774 "",
2775 "",
2776 "",
2777 "", // end of group3
2778 "LED_CPU4_CH1_DIMM1_FAULT",
2779 "LED_CPU4_CH1_DIMM2_FAULT",
2780 "LED_CPU4_CH2_DIMM1_FAULT",
2781 "LED_CPU4_CH2_DIMM2_FAULT",
2782 "LED_CPU4_CH3_DIMM1_FAULT",
2783 "LED_CPU4_CH3_DIMM2_FAULT",
2784 "LED_CPU4_CH4_DIMM1_FAULT",
2785 "LED_CPU4_CH4_DIMM2_FAULT",
2786 "LED_CPU4_CH5_DIMM1_FAULT",
2787 "LED_CPU4_CH5_DIMM2_FAULT",
2788 "LED_CPU4_CH6_DIMM1_FAULT",
2789 "LED_CPU4_CH6_DIMM2_FAULT",
2790 "",
2791 "",
2792 "",
2793 "", // end of group4
2794 "LED_FAN1_FAULT",
2795 "LED_FAN2_FAULT",
2796 "LED_FAN3_FAULT",
2797 "LED_FAN4_FAULT",
2798 "LED_FAN5_FAULT",
2799 "LED_FAN6_FAULT",
2800 "LED_FAN7_FAULT",
2801 "LED_FAN8_FAULT",
2802 "",
2803 "",
2804 "",
2805 "",
2806 "",
2807 "",
2808 "",
2809 "" // end of group5
2810 }};
Zhu, Yungebe560b02019-04-21 21:19:21 -04002811
Zhikui Rence4e73f2019-12-06 13:59:47 -08002812 // Validate the source, fault type --
2813 // (Byte 1) sourceId: Unspecified, Hot-Swap Controller 0, Hot-Swap
2814 // Controller 1, BIOS (Byte 2) fault type: fan, temperature, power,
2815 // driveslot, software, memory (Byte 3) FaultState: OK, Degraded,
2816 // Non-Critical, Critical, Non-Recoverable, (Byte 4) is faultGroup,
2817 // definition differs based on fault type (Byte 2)
2818 // Type Fan=> Group: 0=FanGroupID, FF-not used
2819 // Byte 5-11 00h, not used
2820 // Byte12 FanLedState [7:0]-Fans 7:0
2821 // Type Memory=> Group: 0 = DIMM GroupID, FF-not used
2822 // Byte 5:12 - DIMM LED state (64bit field, LS Byte first)
2823 // [63:48] = CPU4 channels 7:0, 2 bits per channel
2824 // [47:32] = CPU3 channels 7:0, 2 bits per channel
2825 // [31:16] = CPU2 channels 7:0, 2 bits per channel
2826 // [15:0] = CPU1 channels 7:0, 2 bits per channel
2827 // Type Other=> Component Fault LED Group ID, not used set to 0xFF
2828 // Byte[5:12]: reserved 0x00h
Zhu, Yungebe560b02019-04-21 21:19:21 -04002829 if ((sourceId >= maxFaultSource) ||
2830 (faultType >= static_cast<int8_t>(RemoteFaultType::max)) ||
2831 (faultState >= static_cast<int8_t>(RemoteFaultState::maxFaultState)) ||
2832 (faultGroup >= static_cast<int8_t>(DimmFaultType::maxFaultGroup)))
2833 {
2834 return ipmi::responseParmOutOfRange();
2835 }
2836
Zhikui Rence4e73f2019-12-06 13:59:47 -08002837 size_t pinGroupOffset = 0;
2838 size_t pinGroupMax = pinSize / groupSize;
2839 if (RemoteFaultType::fan == RemoteFaultType(faultType))
Zhu, Yungebe560b02019-04-21 21:19:21 -04002840 {
Zhikui Rence4e73f2019-12-06 13:59:47 -08002841 pinGroupOffset = 4;
2842 pinGroupMax = groupNum - pinSize / groupSize;
Zhu, Yungebe560b02019-04-21 21:19:21 -04002843 }
2844
2845 switch (RemoteFaultType(faultType))
2846 {
2847 case (RemoteFaultType::fan):
2848 case (RemoteFaultType::memory):
2849 {
2850 if (faultGroup == skipLEDs)
2851 {
2852 return ipmi::responseSuccess();
2853 }
Zhu, Yungebe560b02019-04-21 21:19:21 -04002854 // calculate led state bit filed count, each byte has 8bits
2855 // the maximum bits will be 8 * 8 bits
2856 constexpr uint8_t size = sizeof(ledStateData) * 8;
Zhikui Rence4e73f2019-12-06 13:59:47 -08002857
2858 // assemble ledState
2859 uint64_t ledState = 0;
2860 bool hasError = false;
Zhu, Yungebe560b02019-04-21 21:19:21 -04002861 for (int i = 0; i < sizeof(ledStateData); i++)
2862 {
2863 ledState = (uint64_t)(ledState << 8);
2864 ledState = (uint64_t)(ledState | (uint64_t)ledStateData[i]);
2865 }
Zhu, Yungebe560b02019-04-21 21:19:21 -04002866 std::bitset<size> ledStateBits(ledState);
Zhu, Yungebe560b02019-04-21 21:19:21 -04002867
Zhikui Rence4e73f2019-12-06 13:59:47 -08002868 for (int group = 0; group < pinGroupMax; group++)
2869 {
2870 for (int i = 0; i < groupSize; i++)
2871 { // skip non-existing pins
2872 if (0 == faultLedPinNames[group + pinGroupOffset][i].size())
2873 {
2874 continue;
2875 }
Zhu, Yungebe560b02019-04-21 21:19:21 -04002876
Zhikui Rence4e73f2019-12-06 13:59:47 -08002877 gpiod::line line = gpiod::find_line(
2878 faultLedPinNames[group + pinGroupOffset][i]);
2879 if (!line)
2880 {
2881 phosphor::logging::log<phosphor::logging::level::ERR>(
2882 "Not Find Led Gpio Device!",
2883 phosphor::logging::entry(
2884 "DEVICE=%s",
2885 faultLedPinNames[group + pinGroupOffset][i]
2886 .c_str()));
2887 hasError = true;
2888 continue;
2889 }
Zhu, Yungebe560b02019-04-21 21:19:21 -04002890
Zhikui Rence4e73f2019-12-06 13:59:47 -08002891 bool activeHigh =
2892 (line.active_state() == gpiod::line::ACTIVE_HIGH);
2893 try
2894 {
2895 line.request(
2896 {"faultLed", gpiod::line_request::DIRECTION_OUTPUT,
2897 activeHigh
2898 ? 0
2899 : gpiod::line_request::FLAG_ACTIVE_LOW});
2900 line.set_value(ledStateBits[i + group * groupSize]);
2901 }
Patrick Williamsbd51e6a2021-10-06 13:09:44 -05002902 catch (const std::system_error&)
Zhikui Rence4e73f2019-12-06 13:59:47 -08002903 {
2904 phosphor::logging::log<phosphor::logging::level::ERR>(
2905 "Error write Led Gpio Device!",
2906 phosphor::logging::entry(
2907 "DEVICE=%s",
2908 faultLedPinNames[group + pinGroupOffset][i]
2909 .c_str()));
2910 hasError = true;
2911 continue;
2912 }
2913 } // for int i
2914 }
2915 if (hasError)
2916 {
2917 return ipmi::responseResponseError();
Zhu, Yungebe560b02019-04-21 21:19:21 -04002918 }
2919 break;
2920 }
2921 default:
2922 {
2923 // now only support two fault types
2924 return ipmi::responseParmOutOfRange();
2925 }
Zhikui Rence4e73f2019-12-06 13:59:47 -08002926 } // switch
Zhu, Yungebe560b02019-04-21 21:19:21 -04002927 return ipmi::responseSuccess();
2928}
2929
Richard Marian Thomaiyarea537d52019-04-24 21:33:48 +05302930ipmi::RspType<uint8_t> ipmiOEMReadBoardProductId()
2931{
2932 uint8_t prodId = 0;
2933 try
2934 {
Vernon Mauery15419dd2019-05-24 09:40:30 -07002935 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
Richard Marian Thomaiyarea537d52019-04-24 21:33:48 +05302936 const DbusObjectInfo& object = getDbusObject(
Vernon Mauery15419dd2019-05-24 09:40:30 -07002937 *dbus, "xyz.openbmc_project.Inventory.Item.Board",
Richard Marian Thomaiyarea537d52019-04-24 21:33:48 +05302938 "/xyz/openbmc_project/inventory/system/board/", "Baseboard");
2939 const Value& propValue = getDbusProperty(
Vernon Mauery15419dd2019-05-24 09:40:30 -07002940 *dbus, object.second, object.first,
Suryakanth Sekar6c57e5c2020-01-10 17:11:58 +05302941 "xyz.openbmc_project.Inventory.Item.Board.Motherboard",
2942 "ProductId");
Richard Marian Thomaiyarea537d52019-04-24 21:33:48 +05302943 prodId = static_cast<uint8_t>(std::get<uint64_t>(propValue));
2944 }
Patrick Williamsbd51e6a2021-10-06 13:09:44 -05002945 catch (const std::exception& e)
Richard Marian Thomaiyarea537d52019-04-24 21:33:48 +05302946 {
2947 phosphor::logging::log<phosphor::logging::level::ERR>(
2948 "ipmiOEMReadBoardProductId: Product ID read failed!",
2949 phosphor::logging::entry("ERR=%s", e.what()));
2950 }
2951 return ipmi::responseSuccess(prodId);
2952}
2953
Richard Marian Thomaiyard801e462019-06-20 01:05:40 +05302954/** @brief implements the get security mode command
2955 * @param ctx - ctx pointer
2956 *
2957 * @returns IPMI completion code with following data
2958 * - restriction mode value - As specified in
2959 * xyz.openbmc_project.Control.Security.RestrictionMode.interface.yaml
2960 * - special mode value - As specified in
2961 * xyz.openbmc_project.Control.Security.SpecialMode.interface.yaml
2962 */
2963ipmi::RspType<uint8_t, uint8_t> ipmiGetSecurityMode(ipmi::Context::ptr ctx)
2964{
2965 namespace securityNameSpace =
2966 sdbusplus::xyz::openbmc_project::Control::Security::server;
2967 uint8_t restrictionModeValue = 0;
2968 uint8_t specialModeValue = 0;
2969
2970 boost::system::error_code ec;
Jason M. Bills0748c692022-09-08 15:34:08 -07002971 auto varRestrMode = ctx->bus->yield_method_call<ipmi::DbusVariant>(
James Feist28c72902019-09-16 10:34:07 -07002972 ctx->yield, ec, restricionModeService, restricionModeBasePath,
Richard Marian Thomaiyard801e462019-06-20 01:05:40 +05302973 dBusPropertyIntf, dBusPropertyGetMethod, restricionModeIntf,
2974 restricionModeProperty);
2975 if (ec)
2976 {
2977 phosphor::logging::log<phosphor::logging::level::ERR>(
2978 "ipmiGetSecurityMode: failed to get RestrictionMode property",
2979 phosphor::logging::entry("ERROR=%s", ec.message().c_str()));
2980 return ipmi::responseUnspecifiedError();
2981 }
2982 restrictionModeValue = static_cast<uint8_t>(
2983 securityNameSpace::RestrictionMode::convertModesFromString(
2984 std::get<std::string>(varRestrMode)));
Jason M. Bills0748c692022-09-08 15:34:08 -07002985 auto varSpecialMode = ctx->bus->yield_method_call<ipmi::DbusVariant>(
2986 ctx->yield, ec, specialModeService, specialModeBasePath,
2987 dBusPropertyIntf, dBusPropertyGetMethod, specialModeIntf,
2988 specialModeProperty);
Richard Marian Thomaiyard801e462019-06-20 01:05:40 +05302989 if (ec)
2990 {
2991 phosphor::logging::log<phosphor::logging::level::ERR>(
2992 "ipmiGetSecurityMode: failed to get SpecialMode property",
2993 phosphor::logging::entry("ERROR=%s", ec.message().c_str()));
2994 // fall through, let us not worry about SpecialMode property, which is
2995 // not required in user scenario
2996 }
2997 else
2998 {
Richard Marian Thomaiyar8d4f8d72019-11-11 12:06:40 +05302999 specialModeValue = static_cast<uint8_t>(
3000 securityNameSpace::SpecialMode::convertModesFromString(
3001 std::get<std::string>(varSpecialMode)));
Richard Marian Thomaiyard801e462019-06-20 01:05:40 +05303002 }
3003 return ipmi::responseSuccess(restrictionModeValue, specialModeValue);
3004}
3005
3006/** @brief implements the set security mode command
3007 * Command allows to upgrade the restriction mode and won't allow
3008 * to downgrade from system interface
3009 * @param ctx - ctx pointer
3010 * @param restrictionMode - restriction mode value to be set.
3011 *
3012 * @returns IPMI completion code
3013 */
3014ipmi::RspType<> ipmiSetSecurityMode(ipmi::Context::ptr ctx,
Richard Marian Thomaiyar10791062019-11-11 12:19:53 +05303015 uint8_t restrictionMode,
3016 std::optional<uint8_t> specialMode)
Richard Marian Thomaiyard801e462019-06-20 01:05:40 +05303017{
Richard Marian Thomaiyar10791062019-11-11 12:19:53 +05303018#ifndef BMC_VALIDATION_UNSECURE_FEATURE
3019 if (specialMode)
3020 {
3021 return ipmi::responseReqDataLenInvalid();
3022 }
3023#endif
Richard Marian Thomaiyard801e462019-06-20 01:05:40 +05303024 namespace securityNameSpace =
3025 sdbusplus::xyz::openbmc_project::Control::Security::server;
3026
3027 ChannelInfo chInfo;
3028 if (getChannelInfo(ctx->channel, chInfo) != ccSuccess)
3029 {
3030 phosphor::logging::log<phosphor::logging::level::ERR>(
3031 "ipmiSetSecurityMode: Failed to get Channel Info",
3032 phosphor::logging::entry("CHANNEL=%d", ctx->channel));
3033 return ipmi::responseUnspecifiedError();
3034 }
3035 auto reqMode =
3036 static_cast<securityNameSpace::RestrictionMode::Modes>(restrictionMode);
3037
3038 if ((reqMode < securityNameSpace::RestrictionMode::Modes::Provisioning) ||
3039 (reqMode >
3040 securityNameSpace::RestrictionMode::Modes::ProvisionedHostDisabled))
3041 {
3042 return ipmi::responseInvalidFieldRequest();
3043 }
3044
3045 boost::system::error_code ec;
Jason M. Bills0748c692022-09-08 15:34:08 -07003046 auto varRestrMode = ctx->bus->yield_method_call<ipmi::DbusVariant>(
James Feist28c72902019-09-16 10:34:07 -07003047 ctx->yield, ec, restricionModeService, restricionModeBasePath,
Richard Marian Thomaiyard801e462019-06-20 01:05:40 +05303048 dBusPropertyIntf, dBusPropertyGetMethod, restricionModeIntf,
3049 restricionModeProperty);
3050 if (ec)
3051 {
3052 phosphor::logging::log<phosphor::logging::level::ERR>(
3053 "ipmiSetSecurityMode: failed to get RestrictionMode property",
3054 phosphor::logging::entry("ERROR=%s", ec.message().c_str()));
3055 return ipmi::responseUnspecifiedError();
3056 }
3057 auto currentRestrictionMode =
3058 securityNameSpace::RestrictionMode::convertModesFromString(
3059 std::get<std::string>(varRestrMode));
3060
3061 if (chInfo.mediumType !=
3062 static_cast<uint8_t>(EChannelMediumType::lan8032) &&
3063 currentRestrictionMode > reqMode)
3064 {
3065 phosphor::logging::log<phosphor::logging::level::ERR>(
3066 "ipmiSetSecurityMode - Downgrading security mode not supported "
3067 "through system interface",
3068 phosphor::logging::entry(
3069 "CUR_MODE=%d", static_cast<uint8_t>(currentRestrictionMode)),
3070 phosphor::logging::entry("REQ_MODE=%d", restrictionMode));
3071 return ipmi::responseCommandNotAvailable();
3072 }
3073
3074 ec.clear();
3075 ctx->bus->yield_method_call<>(
James Feist28c72902019-09-16 10:34:07 -07003076 ctx->yield, ec, restricionModeService, restricionModeBasePath,
Richard Marian Thomaiyard801e462019-06-20 01:05:40 +05303077 dBusPropertyIntf, dBusPropertySetMethod, restricionModeIntf,
3078 restricionModeProperty,
Jason M. Bills0748c692022-09-08 15:34:08 -07003079 static_cast<ipmi::DbusVariant>(
Richard Marian Thomaiyard801e462019-06-20 01:05:40 +05303080 securityNameSpace::convertForMessage(reqMode)));
3081
3082 if (ec)
3083 {
3084 phosphor::logging::log<phosphor::logging::level::ERR>(
3085 "ipmiSetSecurityMode: failed to set RestrictionMode property",
3086 phosphor::logging::entry("ERROR=%s", ec.message().c_str()));
3087 return ipmi::responseUnspecifiedError();
3088 }
Richard Marian Thomaiyar10791062019-11-11 12:19:53 +05303089
3090#ifdef BMC_VALIDATION_UNSECURE_FEATURE
3091 if (specialMode)
3092 {
Jayaprakash Mutyalad77489f2020-09-05 01:00:04 +00003093 constexpr uint8_t mfgMode = 0x01;
3094 // Manufacturing mode is reserved. So can't enable this mode.
3095 if (specialMode.value() == mfgMode)
3096 {
3097 phosphor::logging::log<phosphor::logging::level::INFO>(
3098 "ipmiSetSecurityMode: Can't enable Manufacturing mode");
3099 return ipmi::responseInvalidFieldRequest();
3100 }
3101
Richard Marian Thomaiyar10791062019-11-11 12:19:53 +05303102 ec.clear();
3103 ctx->bus->yield_method_call<>(
3104 ctx->yield, ec, specialModeService, specialModeBasePath,
3105 dBusPropertyIntf, dBusPropertySetMethod, specialModeIntf,
3106 specialModeProperty,
Jason M. Bills0748c692022-09-08 15:34:08 -07003107 static_cast<ipmi::DbusVariant>(securityNameSpace::convertForMessage(
3108 static_cast<securityNameSpace::SpecialMode::Modes>(
3109 specialMode.value()))));
Richard Marian Thomaiyar10791062019-11-11 12:19:53 +05303110
3111 if (ec)
3112 {
3113 phosphor::logging::log<phosphor::logging::level::ERR>(
3114 "ipmiSetSecurityMode: failed to set SpecialMode property",
3115 phosphor::logging::entry("ERROR=%s", ec.message().c_str()));
3116 return ipmi::responseUnspecifiedError();
3117 }
3118 }
3119#endif
Richard Marian Thomaiyard801e462019-06-20 01:05:40 +05303120 return ipmi::responseSuccess();
3121}
3122
Vernon Mauery4ac799d2019-05-20 15:50:37 -07003123ipmi::RspType<uint8_t /* restore status */>
3124 ipmiRestoreConfiguration(const std::array<uint8_t, 3>& clr, uint8_t cmd)
3125{
3126 static constexpr std::array<uint8_t, 3> expClr = {'C', 'L', 'R'};
3127
3128 if (clr != expClr)
3129 {
3130 return ipmi::responseInvalidFieldRequest();
3131 }
3132 constexpr uint8_t cmdStatus = 0;
3133 constexpr uint8_t cmdDefaultRestore = 0xaa;
3134 constexpr uint8_t cmdFullRestore = 0xbb;
3135 constexpr uint8_t cmdFormat = 0xcc;
3136
3137 constexpr const char* restoreOpFname = "/tmp/.rwfs/.restore_op";
3138
3139 switch (cmd)
3140 {
3141 case cmdStatus:
3142 break;
3143 case cmdDefaultRestore:
3144 case cmdFullRestore:
3145 case cmdFormat:
3146 {
3147 // write file to rwfs root
3148 int value = (cmd - 1) & 0x03; // map aa, bb, cc => 1, 2, 3
3149 std::ofstream restoreFile(restoreOpFname);
3150 if (!restoreFile)
3151 {
3152 return ipmi::responseUnspecifiedError();
3153 }
3154 restoreFile << value << "\n";
Arun P. Mohananba1fbc82021-04-26 11:26:53 +05303155
3156 phosphor::logging::log<phosphor::logging::level::WARNING>(
3157 "Restore to default will be performed on next BMC boot",
3158 phosphor::logging::entry("ACTION=0x%0X", cmd));
3159
Vernon Mauery4ac799d2019-05-20 15:50:37 -07003160 break;
3161 }
3162 default:
3163 return ipmi::responseInvalidFieldRequest();
3164 }
3165
3166 constexpr uint8_t restorePending = 0;
3167 constexpr uint8_t restoreComplete = 1;
3168
3169 uint8_t restoreStatus = std::filesystem::exists(restoreOpFname)
3170 ? restorePending
3171 : restoreComplete;
3172 return ipmi::responseSuccess(restoreStatus);
3173}
3174
Chen Yugang39736d52019-07-12 16:24:33 +08003175ipmi::RspType<uint8_t> ipmiOEMGetNmiSource(void)
3176{
3177 uint8_t bmcSource;
Chen Yugang97cf96e2019-11-01 08:55:11 +08003178 namespace nmi = sdbusplus::xyz::openbmc_project::Chassis::Control::server;
Chen Yugang39736d52019-07-12 16:24:33 +08003179
3180 try
3181 {
3182 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
Patrick Williamsb37abfb2023-05-10 07:50:33 -05003183 std::string service = getService(*dbus, oemNmiSourceIntf,
3184 oemNmiSourceObjPath);
3185 Value variant = getDbusProperty(*dbus, service, oemNmiSourceObjPath,
3186 oemNmiSourceIntf,
3187 oemNmiBmcSourceObjPathProp);
Chen Yugang39736d52019-07-12 16:24:33 +08003188
3189 switch (nmi::NMISource::convertBMCSourceSignalFromString(
3190 std::get<std::string>(variant)))
3191 {
3192 case nmi::NMISource::BMCSourceSignal::None:
3193 bmcSource = static_cast<uint8_t>(NmiSource::none);
3194 break;
Chen Yugang97cf96e2019-11-01 08:55:11 +08003195 case nmi::NMISource::BMCSourceSignal::FrontPanelButton:
3196 bmcSource = static_cast<uint8_t>(NmiSource::frontPanelButton);
Chen Yugang39736d52019-07-12 16:24:33 +08003197 break;
Chen Yugang97cf96e2019-11-01 08:55:11 +08003198 case nmi::NMISource::BMCSourceSignal::Watchdog:
3199 bmcSource = static_cast<uint8_t>(NmiSource::watchdog);
Chen Yugang39736d52019-07-12 16:24:33 +08003200 break;
3201 case nmi::NMISource::BMCSourceSignal::ChassisCmd:
3202 bmcSource = static_cast<uint8_t>(NmiSource::chassisCmd);
3203 break;
3204 case nmi::NMISource::BMCSourceSignal::MemoryError:
3205 bmcSource = static_cast<uint8_t>(NmiSource::memoryError);
3206 break;
Chen Yugang97cf96e2019-11-01 08:55:11 +08003207 case nmi::NMISource::BMCSourceSignal::PciBusError:
3208 bmcSource = static_cast<uint8_t>(NmiSource::pciBusError);
Chen Yugang39736d52019-07-12 16:24:33 +08003209 break;
Chen Yugang97cf96e2019-11-01 08:55:11 +08003210 case nmi::NMISource::BMCSourceSignal::PCH:
3211 bmcSource = static_cast<uint8_t>(NmiSource::pch);
Chen Yugang39736d52019-07-12 16:24:33 +08003212 break;
Chen Yugang97cf96e2019-11-01 08:55:11 +08003213 case nmi::NMISource::BMCSourceSignal::Chipset:
3214 bmcSource = static_cast<uint8_t>(NmiSource::chipset);
Chen Yugang39736d52019-07-12 16:24:33 +08003215 break;
3216 default:
3217 phosphor::logging::log<phosphor::logging::level::ERR>(
3218 "NMI source: invalid property!",
3219 phosphor::logging::entry(
3220 "PROP=%s", std::get<std::string>(variant).c_str()));
3221 return ipmi::responseResponseError();
3222 }
3223 }
Patrick Williamsf944d2e2022-07-22 19:26:52 -05003224 catch (const sdbusplus::exception_t& e)
Chen Yugang39736d52019-07-12 16:24:33 +08003225 {
3226 phosphor::logging::log<phosphor::logging::level::ERR>(e.what());
3227 return ipmi::responseResponseError();
3228 }
3229
3230 return ipmi::responseSuccess(bmcSource);
3231}
3232
3233ipmi::RspType<> ipmiOEMSetNmiSource(uint8_t sourceId)
3234{
Chen Yugang97cf96e2019-11-01 08:55:11 +08003235 namespace nmi = sdbusplus::xyz::openbmc_project::Chassis::Control::server;
Chen Yugang39736d52019-07-12 16:24:33 +08003236
3237 nmi::NMISource::BMCSourceSignal bmcSourceSignal =
3238 nmi::NMISource::BMCSourceSignal::None;
3239
3240 switch (NmiSource(sourceId))
3241 {
3242 case NmiSource::none:
3243 bmcSourceSignal = nmi::NMISource::BMCSourceSignal::None;
3244 break;
Chen Yugang97cf96e2019-11-01 08:55:11 +08003245 case NmiSource::frontPanelButton:
3246 bmcSourceSignal = nmi::NMISource::BMCSourceSignal::FrontPanelButton;
Chen Yugang39736d52019-07-12 16:24:33 +08003247 break;
Chen Yugang97cf96e2019-11-01 08:55:11 +08003248 case NmiSource::watchdog:
3249 bmcSourceSignal = nmi::NMISource::BMCSourceSignal::Watchdog;
Chen Yugang39736d52019-07-12 16:24:33 +08003250 break;
3251 case NmiSource::chassisCmd:
3252 bmcSourceSignal = nmi::NMISource::BMCSourceSignal::ChassisCmd;
3253 break;
3254 case NmiSource::memoryError:
3255 bmcSourceSignal = nmi::NMISource::BMCSourceSignal::MemoryError;
3256 break;
Chen Yugang97cf96e2019-11-01 08:55:11 +08003257 case NmiSource::pciBusError:
3258 bmcSourceSignal = nmi::NMISource::BMCSourceSignal::PciBusError;
Chen Yugang39736d52019-07-12 16:24:33 +08003259 break;
Chen Yugang97cf96e2019-11-01 08:55:11 +08003260 case NmiSource::pch:
3261 bmcSourceSignal = nmi::NMISource::BMCSourceSignal::PCH;
Chen Yugang39736d52019-07-12 16:24:33 +08003262 break;
Chen Yugang97cf96e2019-11-01 08:55:11 +08003263 case NmiSource::chipset:
3264 bmcSourceSignal = nmi::NMISource::BMCSourceSignal::Chipset;
Chen Yugang39736d52019-07-12 16:24:33 +08003265 break;
3266 default:
3267 phosphor::logging::log<phosphor::logging::level::ERR>(
3268 "NMI source: invalid property!");
3269 return ipmi::responseResponseError();
3270 }
3271
3272 try
3273 {
3274 // keep NMI signal source
3275 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
Patrick Williamsb37abfb2023-05-10 07:50:33 -05003276 std::string service = getService(*dbus, oemNmiSourceIntf,
3277 oemNmiSourceObjPath);
Chen Yugang97cf96e2019-11-01 08:55:11 +08003278 setDbusProperty(*dbus, service, oemNmiSourceObjPath, oemNmiSourceIntf,
3279 oemNmiBmcSourceObjPathProp,
3280 nmi::convertForMessage(bmcSourceSignal));
Chen Yugang99be6332019-08-09 16:20:48 +08003281 // set Enabled property to inform NMI source handling
3282 // to trigger a NMI_OUT BSOD.
3283 // if it's triggered by NMI source property changed,
3284 // NMI_OUT BSOD could be missed if the same source occurs twice in a row
3285 if (bmcSourceSignal != nmi::NMISource::BMCSourceSignal::None)
3286 {
3287 setDbusProperty(*dbus, service, oemNmiSourceObjPath,
3288 oemNmiSourceIntf, oemNmiEnabledObjPathProp,
3289 static_cast<bool>(true));
3290 }
Chen Yugang39736d52019-07-12 16:24:33 +08003291 }
Patrick Williamsbd51e6a2021-10-06 13:09:44 -05003292 catch (const sdbusplus::exception_t& e)
Chen Yugang39736d52019-07-12 16:24:33 +08003293 {
3294 phosphor::logging::log<phosphor::logging::level::ERR>(e.what());
3295 return ipmi::responseResponseError();
3296 }
3297
3298 return ipmi::responseSuccess();
3299}
3300
James Feist63efafa2019-07-24 12:39:21 -07003301namespace dimmOffset
3302{
3303constexpr const char* dimmPower = "DimmPower";
3304constexpr const char* staticCltt = "StaticCltt";
3305constexpr const char* offsetPath = "/xyz/openbmc_project/Inventory/Item/Dimm";
3306constexpr const char* offsetInterface =
3307 "xyz.openbmc_project.Inventory.Item.Dimm.Offset";
3308constexpr const char* property = "DimmOffset";
3309
3310}; // namespace dimmOffset
3311
3312ipmi::RspType<>
3313 ipmiOEMSetDimmOffset(uint8_t type,
3314 const std::vector<std::tuple<uint8_t, uint8_t>>& data)
3315{
3316 if (type != static_cast<uint8_t>(dimmOffsetTypes::dimmPower) &&
3317 type != static_cast<uint8_t>(dimmOffsetTypes::staticCltt))
3318 {
3319 return ipmi::responseInvalidFieldRequest();
3320 }
3321
3322 if (data.empty())
3323 {
3324 return ipmi::responseInvalidFieldRequest();
3325 }
3326 nlohmann::json json;
3327
3328 std::ifstream jsonStream(dimmOffsetFile);
3329 if (jsonStream.good())
3330 {
3331 json = nlohmann::json::parse(jsonStream, nullptr, false);
3332 if (json.is_discarded())
3333 {
3334 json = nlohmann::json();
3335 }
3336 jsonStream.close();
3337 }
3338
3339 std::string typeName;
3340 if (type == static_cast<uint8_t>(dimmOffsetTypes::dimmPower))
3341 {
3342 typeName = dimmOffset::dimmPower;
3343 }
3344 else
3345 {
3346 typeName = dimmOffset::staticCltt;
3347 }
3348
3349 nlohmann::json& field = json[typeName];
3350
3351 for (const auto& [index, value] : data)
3352 {
3353 field[index] = value;
3354 }
3355
3356 for (nlohmann::json& val : field)
3357 {
3358 if (val == nullptr)
3359 {
3360 val = static_cast<uint8_t>(0);
3361 }
3362 }
3363
3364 std::ofstream output(dimmOffsetFile);
3365 if (!output.good())
3366 {
3367 std::cerr << "Error writing json file\n";
3368 return ipmi::responseResponseError();
3369 }
3370
3371 output << json.dump(4);
3372
3373 if (type == static_cast<uint8_t>(dimmOffsetTypes::staticCltt))
3374 {
3375 std::shared_ptr<sdbusplus::asio::connection> bus = getSdBus();
3376
Jason M. Bills0748c692022-09-08 15:34:08 -07003377 ipmi::DbusVariant offsets = field.get<std::vector<uint8_t>>();
James Feist63efafa2019-07-24 12:39:21 -07003378 auto call = bus->new_method_call(
3379 settingsBusName, dimmOffset::offsetPath, PROP_INTF, "Set");
3380 call.append(dimmOffset::offsetInterface, dimmOffset::property, offsets);
3381 try
3382 {
3383 bus->call(call);
3384 }
Patrick Williamsbd51e6a2021-10-06 13:09:44 -05003385 catch (const sdbusplus::exception_t& e)
James Feist63efafa2019-07-24 12:39:21 -07003386 {
3387 phosphor::logging::log<phosphor::logging::level::ERR>(
3388 "ipmiOEMSetDimmOffset: can't set dimm offsets!",
3389 phosphor::logging::entry("ERR=%s", e.what()));
3390 return ipmi::responseResponseError();
3391 }
3392 }
3393
3394 return ipmi::responseSuccess();
3395}
3396
3397ipmi::RspType<uint8_t> ipmiOEMGetDimmOffset(uint8_t type, uint8_t index)
3398{
James Feist63efafa2019-07-24 12:39:21 -07003399 if (type != static_cast<uint8_t>(dimmOffsetTypes::dimmPower) &&
3400 type != static_cast<uint8_t>(dimmOffsetTypes::staticCltt))
3401 {
3402 return ipmi::responseInvalidFieldRequest();
3403 }
3404
3405 std::ifstream jsonStream(dimmOffsetFile);
3406
3407 auto json = nlohmann::json::parse(jsonStream, nullptr, false);
3408 if (json.is_discarded())
3409 {
3410 std::cerr << "File error in " << dimmOffsetFile << "\n";
3411 return ipmi::responseResponseError();
3412 }
3413
3414 std::string typeName;
3415 if (type == static_cast<uint8_t>(dimmOffsetTypes::dimmPower))
3416 {
3417 typeName = dimmOffset::dimmPower;
3418 }
3419 else
3420 {
3421 typeName = dimmOffset::staticCltt;
3422 }
3423
3424 auto it = json.find(typeName);
3425 if (it == json.end())
3426 {
3427 return ipmi::responseInvalidFieldRequest();
3428 }
3429
3430 if (it->size() <= index)
3431 {
3432 return ipmi::responseInvalidFieldRequest();
3433 }
3434
3435 uint8_t resp = it->at(index).get<uint8_t>();
3436 return ipmi::responseSuccess(resp);
3437}
3438
Chen,Yugang4f7e76b2019-08-20 09:28:06 +08003439namespace boot_options
3440{
3441
3442using namespace sdbusplus::xyz::openbmc_project::Control::Boot::server;
3443using IpmiValue = uint8_t;
3444constexpr auto ipmiDefault = 0;
3445
3446std::map<IpmiValue, Source::Sources> sourceIpmiToDbus = {
3447 {0x01, Source::Sources::Network},
3448 {0x02, Source::Sources::Disk},
3449 {0x05, Source::Sources::ExternalMedia},
3450 {0x0f, Source::Sources::RemovableMedia},
3451 {ipmiDefault, Source::Sources::Default}};
3452
3453std::map<IpmiValue, Mode::Modes> modeIpmiToDbus = {
Chen Yugangca12a7b2019-09-03 18:11:44 +08003454 {0x06, Mode::Modes::Setup}, {ipmiDefault, Mode::Modes::Regular}};
Chen,Yugang4f7e76b2019-08-20 09:28:06 +08003455
3456std::map<Source::Sources, IpmiValue> sourceDbusToIpmi = {
3457 {Source::Sources::Network, 0x01},
3458 {Source::Sources::Disk, 0x02},
3459 {Source::Sources::ExternalMedia, 0x05},
3460 {Source::Sources::RemovableMedia, 0x0f},
3461 {Source::Sources::Default, ipmiDefault}};
3462
3463std::map<Mode::Modes, IpmiValue> modeDbusToIpmi = {
Chen Yugangca12a7b2019-09-03 18:11:44 +08003464 {Mode::Modes::Setup, 0x06}, {Mode::Modes::Regular, ipmiDefault}};
Chen,Yugang4f7e76b2019-08-20 09:28:06 +08003465
3466static constexpr auto bootModeIntf = "xyz.openbmc_project.Control.Boot.Mode";
3467static constexpr auto bootSourceIntf =
3468 "xyz.openbmc_project.Control.Boot.Source";
3469static constexpr auto enabledIntf = "xyz.openbmc_project.Object.Enable";
3470static constexpr auto persistentObjPath =
3471 "/xyz/openbmc_project/control/host0/boot";
3472static constexpr auto oneTimePath =
3473 "/xyz/openbmc_project/control/host0/boot/one_time";
3474static constexpr auto bootSourceProp = "BootSource";
3475static constexpr auto bootModeProp = "BootMode";
3476static constexpr auto oneTimeBootEnableProp = "Enabled";
3477static constexpr auto httpBootMode =
3478 "xyz.openbmc_project.Control.Boot.Source.Sources.Http";
3479
3480enum class BootOptionParameter : size_t
3481{
3482 setInProgress = 0x0,
3483 bootFlags = 0x5,
3484};
3485static constexpr uint8_t setComplete = 0x0;
3486static constexpr uint8_t setInProgress = 0x1;
3487static uint8_t transferStatus = setComplete;
3488static constexpr uint8_t setParmVersion = 0x01;
3489static constexpr uint8_t setParmBootFlagsPermanent = 0x40;
3490static constexpr uint8_t setParmBootFlagsValidOneTime = 0x80;
3491static constexpr uint8_t setParmBootFlagsValidPermanent = 0xC0;
3492static constexpr uint8_t httpBoot = 0xd;
3493static constexpr uint8_t bootSourceMask = 0x3c;
3494
3495} // namespace boot_options
3496
3497ipmi::RspType<uint8_t, // version
3498 uint8_t, // param
3499 uint8_t, // data0, dependent on parameter
3500 std::optional<uint8_t> // data1, dependent on parameter
3501 >
3502 ipmiOemGetEfiBootOptions(uint8_t parameter, uint8_t set, uint8_t block)
3503{
3504 using namespace boot_options;
3505 uint8_t bootOption = 0;
3506
3507 if (parameter == static_cast<uint8_t>(BootOptionParameter::setInProgress))
3508 {
3509 return ipmi::responseSuccess(setParmVersion, parameter, transferStatus,
3510 std::nullopt);
3511 }
3512
3513 if (parameter != static_cast<uint8_t>(BootOptionParameter::bootFlags))
3514 {
3515 phosphor::logging::log<phosphor::logging::level::ERR>(
3516 "Unsupported parameter");
Jayaprakash Mutyala3694d072021-07-22 10:34:37 +00003517 return ipmi::response(ccParameterNotSupported);
Chen,Yugang4f7e76b2019-08-20 09:28:06 +08003518 }
3519
3520 try
3521 {
3522 auto oneTimeEnabled = false;
3523 // read one time Enabled property
3524 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
3525 std::string service = getService(*dbus, enabledIntf, oneTimePath);
3526 Value variant = getDbusProperty(*dbus, service, oneTimePath,
3527 enabledIntf, oneTimeBootEnableProp);
3528 oneTimeEnabled = std::get<bool>(variant);
3529
3530 // get BootSource and BootMode properties
3531 // according to oneTimeEnable
3532 auto bootObjPath = oneTimePath;
3533 if (oneTimeEnabled == false)
3534 {
3535 bootObjPath = persistentObjPath;
3536 }
3537
3538 service = getService(*dbus, bootModeIntf, bootObjPath);
3539 variant = getDbusProperty(*dbus, service, bootObjPath, bootModeIntf,
3540 bootModeProp);
3541
3542 auto bootMode =
3543 Mode::convertModesFromString(std::get<std::string>(variant));
3544
3545 service = getService(*dbus, bootSourceIntf, bootObjPath);
3546 variant = getDbusProperty(*dbus, service, bootObjPath, bootSourceIntf,
3547 bootSourceProp);
3548
3549 if (std::get<std::string>(variant) == httpBootMode)
3550 {
3551 bootOption = httpBoot;
3552 }
3553 else
3554 {
3555 auto bootSource = Source::convertSourcesFromString(
3556 std::get<std::string>(variant));
3557 bootOption = sourceDbusToIpmi.at(bootSource);
3558 if (Source::Sources::Default == bootSource)
3559 {
3560 bootOption = modeDbusToIpmi.at(bootMode);
3561 }
3562 }
3563
3564 uint8_t oneTime = oneTimeEnabled ? setParmBootFlagsValidOneTime
3565 : setParmBootFlagsValidPermanent;
3566 bootOption <<= 2; // shift for responseconstexpr
3567 return ipmi::responseSuccess(setParmVersion, parameter, oneTime,
3568 bootOption);
3569 }
Patrick Williamsbd51e6a2021-10-06 13:09:44 -05003570 catch (const sdbusplus::exception_t& e)
Chen,Yugang4f7e76b2019-08-20 09:28:06 +08003571 {
3572 phosphor::logging::log<phosphor::logging::level::ERR>(e.what());
3573 return ipmi::responseResponseError();
3574 }
3575}
3576
3577ipmi::RspType<> ipmiOemSetEfiBootOptions(uint8_t bootFlag, uint8_t bootParam,
3578 std::optional<uint8_t> bootOption)
3579{
3580 using namespace boot_options;
3581 auto oneTimeEnabled = false;
3582
Mike Jonesbc01d212022-06-16 12:41:33 -07003583 if (bootFlag == 0 && bootParam == 0)
3584 {
3585 phosphor::logging::log<phosphor::logging::level::ERR>(
3586 "Unsupported parameter");
3587 return ipmi::response(ccParameterNotSupported);
3588 }
Chen,Yugang4f7e76b2019-08-20 09:28:06 +08003589 if (bootFlag == static_cast<uint8_t>(BootOptionParameter::setInProgress))
3590 {
3591 if (bootOption)
3592 {
3593 return ipmi::responseReqDataLenInvalid();
3594 }
3595
3596 if (transferStatus == setInProgress)
3597 {
3598 phosphor::logging::log<phosphor::logging::level::ERR>(
3599 "boot option set in progress!");
3600 return ipmi::responseResponseError();
3601 }
3602
3603 transferStatus = bootParam;
3604 return ipmi::responseSuccess();
3605 }
3606
3607 if (bootFlag != (uint8_t)BootOptionParameter::bootFlags)
3608 {
3609 phosphor::logging::log<phosphor::logging::level::ERR>(
3610 "Unsupported parameter");
Jayaprakash Mutyala3694d072021-07-22 10:34:37 +00003611 return ipmi::response(ccParameterNotSupported);
Chen,Yugang4f7e76b2019-08-20 09:28:06 +08003612 }
3613
3614 if (!bootOption)
3615 {
3616 return ipmi::responseReqDataLenInvalid();
3617 }
3618
3619 if (((bootOption.value() & bootSourceMask) >> 2) !=
3620 httpBoot) // not http boot, exit
3621 {
3622 phosphor::logging::log<phosphor::logging::level::ERR>(
3623 "wrong boot option parameter!");
3624 return ipmi::responseParmOutOfRange();
3625 }
3626
3627 try
3628 {
3629 bool permanent = (bootParam & setParmBootFlagsPermanent) ==
3630 setParmBootFlagsPermanent;
3631
3632 // read one time Enabled property
3633 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
3634 std::string service = getService(*dbus, enabledIntf, oneTimePath);
3635 Value variant = getDbusProperty(*dbus, service, oneTimePath,
3636 enabledIntf, oneTimeBootEnableProp);
3637 oneTimeEnabled = std::get<bool>(variant);
3638
3639 /*
3640 * Check if the current boot setting is onetime or permanent, if the
3641 * request in the command is otherwise, then set the "Enabled"
3642 * property in one_time object path to 'True' to indicate onetime
3643 * and 'False' to indicate permanent.
3644 *
3645 * Once the onetime/permanent setting is applied, then the bootMode
3646 * and bootSource is updated for the corresponding object.
3647 */
3648 if (permanent == oneTimeEnabled)
3649 {
3650 setDbusProperty(*dbus, service, oneTimePath, enabledIntf,
3651 oneTimeBootEnableProp, !permanent);
3652 }
3653
3654 // set BootSource and BootMode properties
3655 // according to oneTimeEnable or persistent
3656 auto bootObjPath = oneTimePath;
3657 if (oneTimeEnabled == false)
3658 {
3659 bootObjPath = persistentObjPath;
3660 }
3661 std::string bootMode =
3662 "xyz.openbmc_project.Control.Boot.Mode.Modes.Regular";
3663 std::string bootSource = httpBootMode;
3664
3665 service = getService(*dbus, bootModeIntf, bootObjPath);
3666 setDbusProperty(*dbus, service, bootObjPath, bootModeIntf, bootModeProp,
3667 bootMode);
3668
3669 service = getService(*dbus, bootSourceIntf, bootObjPath);
3670 setDbusProperty(*dbus, service, bootObjPath, bootSourceIntf,
3671 bootSourceProp, bootSource);
3672 }
Patrick Williamsbd51e6a2021-10-06 13:09:44 -05003673 catch (const sdbusplus::exception_t& e)
Chen,Yugang4f7e76b2019-08-20 09:28:06 +08003674 {
3675 phosphor::logging::log<phosphor::logging::level::ERR>(e.what());
3676 return ipmi::responseResponseError();
3677 }
3678
3679 return ipmi::responseSuccess();
3680}
3681
Jason M. Bills0748c692022-09-08 15:34:08 -07003682using BasicVariantType = ipmi::DbusVariant;
Cheng C Yang4e6ee152019-09-25 10:27:44 +08003683using PropertyMapType =
3684 boost::container::flat_map<std::string, BasicVariantType>;
3685static constexpr const std::array<const char*, 1> psuPresenceTypes = {
3686 "xyz.openbmc_project.Configuration.PSUPresence"};
3687int getPSUAddress(ipmi::Context::ptr ctx, uint8_t& bus,
3688 std::vector<uint64_t>& addrTable)
3689{
3690 boost::system::error_code ec;
3691 GetSubTreeType subtree = ctx->bus->yield_method_call<GetSubTreeType>(
3692 ctx->yield, ec, "xyz.openbmc_project.ObjectMapper",
3693 "/xyz/openbmc_project/object_mapper",
3694 "xyz.openbmc_project.ObjectMapper", "GetSubTree",
3695 "/xyz/openbmc_project/inventory/system", 3, psuPresenceTypes);
3696 if (ec)
3697 {
3698 phosphor::logging::log<phosphor::logging::level::ERR>(
3699 "Failed to set dbus property to cold redundancy");
3700 return -1;
3701 }
3702 for (const auto& object : subtree)
3703 {
3704 std::string pathName = object.first;
3705 for (const auto& serviceIface : object.second)
3706 {
3707 std::string serviceName = serviceIface.first;
3708
3709 ec.clear();
3710 PropertyMapType propMap =
3711 ctx->bus->yield_method_call<PropertyMapType>(
3712 ctx->yield, ec, serviceName, pathName,
3713 "org.freedesktop.DBus.Properties", "GetAll",
3714 "xyz.openbmc_project.Configuration.PSUPresence");
3715 if (ec)
3716 {
3717 phosphor::logging::log<phosphor::logging::level::ERR>(
3718 "Failed to set dbus property to cold redundancy");
3719 return -1;
3720 }
3721 auto psuBus = std::get_if<uint64_t>(&propMap["Bus"]);
3722 auto psuAddress =
3723 std::get_if<std::vector<uint64_t>>(&propMap["Address"]);
3724
3725 if (psuBus == nullptr || psuAddress == nullptr)
3726 {
3727 std::cerr << "error finding necessary "
3728 "entry in configuration\n";
3729 return -1;
3730 }
3731 bus = static_cast<uint8_t>(*psuBus);
3732 addrTable = *psuAddress;
3733 return 0;
3734 }
3735 }
3736 return -1;
3737}
3738
3739static const constexpr uint8_t addrOffset = 8;
3740static const constexpr uint8_t psuRevision = 0xd9;
3741static const constexpr uint8_t defaultPSUBus = 7;
3742// Second Minor, Primary Minor, Major
3743static const constexpr size_t verLen = 3;
3744ipmi::RspType<std::vector<uint8_t>> ipmiOEMGetPSUVersion(ipmi::Context::ptr ctx)
3745{
3746 uint8_t bus = defaultPSUBus;
3747 std::vector<uint64_t> addrTable;
3748 std::vector<uint8_t> result;
3749 if (getPSUAddress(ctx, bus, addrTable))
3750 {
3751 std::cerr << "Failed to get PSU bus and address\n";
3752 return ipmi::responseResponseError();
3753 }
3754
Matt Simmering80d4d5f2023-02-15 15:18:51 -08003755 for (const auto& targetAddr : addrTable)
Cheng C Yang4e6ee152019-09-25 10:27:44 +08003756 {
3757 std::vector<uint8_t> writeData = {psuRevision};
3758 std::vector<uint8_t> readBuf(verLen);
Matt Simmering80d4d5f2023-02-15 15:18:51 -08003759 uint8_t addr = static_cast<uint8_t>(targetAddr) + addrOffset;
Cheng C Yang4e6ee152019-09-25 10:27:44 +08003760 std::string i2cBus = "/dev/i2c-" + std::to_string(bus);
3761
3762 auto retI2C = ipmi::i2cWriteRead(i2cBus, addr, writeData, readBuf);
3763 if (retI2C != ipmi::ccSuccess)
3764 {
3765 for (size_t idx = 0; idx < verLen; idx++)
3766 {
3767 result.emplace_back(0x00);
3768 }
3769 }
3770 else
3771 {
3772 for (const uint8_t& data : readBuf)
3773 {
3774 result.emplace_back(data);
3775 }
3776 }
3777 }
3778
3779 return ipmi::responseSuccess(result);
3780}
3781
srikanta mondal2030d7c2020-05-03 17:25:25 +00003782std::optional<uint8_t> getMultiNodeInfoPresence(ipmi::Context::ptr ctx,
3783 const std::string& name)
3784{
3785 Value dbusValue = 0;
3786 std::string serviceName;
3787
3788 boost::system::error_code ec =
3789 ipmi::getService(ctx, multiNodeIntf, multiNodeObjPath, serviceName);
3790
3791 if (ec)
3792 {
3793 phosphor::logging::log<phosphor::logging::level::ERR>(
3794 "Failed to perform Multinode getService.");
3795 return std::nullopt;
3796 }
3797
3798 ec = ipmi::getDbusProperty(ctx, serviceName, multiNodeObjPath,
3799 multiNodeIntf, name, dbusValue);
3800 if (ec)
3801 {
3802 phosphor::logging::log<phosphor::logging::level::ERR>(
3803 "Failed to perform Multinode get property");
3804 return std::nullopt;
3805 }
3806
3807 auto multiNodeVal = std::get_if<uint8_t>(&dbusValue);
3808 if (!multiNodeVal)
3809 {
3810 phosphor::logging::log<phosphor::logging::level::ERR>(
3811 "getMultiNodeInfoPresence: error to get multinode");
3812 return std::nullopt;
3813 }
3814 return *multiNodeVal;
3815}
3816
3817/** @brief implements OEM get reading command
3818 * @param domain ID
3819 * @param reading Type
3820 * - 00h = platform Power Consumption
3821 * - 01h = inlet Air Temp
3822 * - 02h = icc_TDC from PECI
3823 * @param reserved, write as 0000h
3824 *
3825 * @returns IPMI completion code plus response data
3826 * - response
3827 * - domain ID
3828 * - reading Type
3829 * - 00h = platform Power Consumption
3830 * - 01h = inlet Air Temp
3831 * - 02h = icc_TDC from PECI
3832 * - reading
3833 */
3834ipmi::RspType<uint4_t, // domain ID
3835 uint4_t, // reading Type
3836 uint16_t // reading Value
3837 >
3838 ipmiOEMGetReading(ipmi::Context::ptr ctx, uint4_t domainId,
3839 uint4_t readingType, uint16_t reserved)
3840{
3841 constexpr uint8_t platformPower = 0;
3842 constexpr uint8_t inletAirTemp = 1;
3843 constexpr uint8_t iccTdc = 2;
3844
3845 if ((static_cast<uint8_t>(readingType) > iccTdc) || domainId || reserved)
3846 {
3847 return ipmi::responseInvalidFieldRequest();
3848 }
3849
3850 // This command should run only from multi-node product.
3851 // For all other platforms this command will return invalid.
3852
Patrick Williamsb37abfb2023-05-10 07:50:33 -05003853 std::optional<uint8_t> nodeInfo = getMultiNodeInfoPresence(ctx,
3854 "NodePresence");
srikanta mondal2030d7c2020-05-03 17:25:25 +00003855 if (!nodeInfo || !*nodeInfo)
3856 {
3857 return ipmi::responseInvalidCommand();
3858 }
3859
3860 uint16_t oemReadingValue = 0;
3861 if (static_cast<uint8_t>(readingType) == inletAirTemp)
3862 {
3863 double value = 0;
3864 boost::system::error_code ec = ipmi::getDbusProperty(
3865 ctx, "xyz.openbmc_project.HwmonTempSensor",
3866 "/xyz/openbmc_project/sensors/temperature/Inlet_BRD_Temp",
3867 "xyz.openbmc_project.Sensor.Value", "Value", value);
3868 if (ec)
3869 {
3870 phosphor::logging::log<phosphor::logging::level::ERR>(
3871 "Failed to get BMC Get OEM temperature",
3872 phosphor::logging::entry("EXCEPTION=%s", ec.message().c_str()));
3873 return ipmi::responseUnspecifiedError();
3874 }
3875 // Take the Inlet temperature
3876 oemReadingValue = static_cast<uint16_t>(value);
3877 }
3878 else
3879 {
3880 phosphor::logging::log<phosphor::logging::level::ERR>(
3881 "Currently Get OEM Reading support only for Inlet Air Temp");
3882 return ipmi::responseParmOutOfRange();
3883 }
3884 return ipmi::responseSuccess(domainId, readingType, oemReadingValue);
3885}
3886
AppaRao Puli28972062019-11-11 02:04:45 +05303887/** @brief implements the maximum size of
3888 * bridgeable messages used between KCS and
3889 * IPMB interfacesget security mode command.
3890 *
3891 * @returns IPMI completion code with following data
3892 * - KCS Buffer Size (In multiples of four bytes)
3893 * - IPMB Buffer Size (In multiples of four bytes)
3894 **/
3895ipmi::RspType<uint8_t, uint8_t> ipmiOEMGetBufferSize()
3896{
3897 // for now this is hard coded; really this number is dependent on
3898 // the BMC kcs driver as well as the host kcs driver....
3899 // we can't know the latter.
3900 uint8_t kcsMaxBufferSize = 63 / 4;
3901 uint8_t ipmbMaxBufferSize = 128 / 4;
3902
3903 return ipmi::responseSuccess(kcsMaxBufferSize, ipmbMaxBufferSize);
3904}
3905
Ankita Vilas Gawadea1650382022-01-08 10:30:40 +00003906ipmi::RspType<std::vector<uint8_t>>
3907 ipmiOEMReadPFRMailbox(ipmi::Context::ptr& ctx, const uint8_t readRegister,
3908 const uint8_t numOfBytes, uint8_t registerIdentifier)
3909{
3910 if (!ipmi::mailbox::i2cConfigLoaded)
3911 {
Ankita Vilas Gawadea1650382022-01-08 10:30:40 +00003912 phosphor::logging::log<phosphor::logging::level::ERR>(
Matt Simmering80d4d5f2023-02-15 15:18:51 -08003913 "Calling PFR Load Configuration Function to Get I2C Bus and Target "
Ankita Vilas Gawadea1650382022-01-08 10:30:40 +00003914 "Address ");
3915
3916 ipmi::mailbox::loadPfrConfig(ctx, ipmi::mailbox::i2cConfigLoaded);
3917 }
3918
3919 if (!numOfBytes && !readRegister)
3920 {
3921 phosphor::logging::log<phosphor::logging::level::ERR>(
3922 "OEM IPMI command: Read & write count are 0 which is invalid ");
3923 return ipmi::responseInvalidFieldRequest();
3924 }
3925
3926 switch (registerIdentifier)
3927 {
3928 case ipmi::mailbox::registerType::fifoReadRegister:
3929 {
3930 // Check if readRegister is an FIFO read register
3931 if (registerIdentifier == 1)
3932 {
3933 if (ipmi::mailbox::readFifoReg.find(readRegister) ==
3934 ipmi::mailbox::readFifoReg.end())
3935 {
3936 phosphor::logging::log<phosphor::logging::level::ERR>(
3937 "OEM IPMI command: Register is not a Read FIFO ");
3938 return ipmi::responseInvalidFieldRequest();
3939 }
3940
3941 phosphor::logging::log<phosphor::logging::level::ERR>(
3942 "OEM IPMI command: Register is a Read FIFO ");
3943
3944 ipmi::mailbox::writefifo(ipmi::mailbox::provisioningCommand,
3945 readRegister);
3946 ipmi::mailbox::writefifo(ipmi::mailbox::triggerCommand,
3947 ipmi::mailbox::flushRead);
3948
3949 std::vector<uint8_t> writeData = {ipmi::mailbox::readFifo};
3950 std::vector<uint8_t> readBuf(1);
3951 std::vector<uint8_t> result;
3952
3953 for (int i = 0; i < numOfBytes; i++)
3954 {
Ankita Vilas Gawadea1650382022-01-08 10:30:40 +00003955 ipmi::Cc ret = ipmi::i2cWriteRead(ipmi::mailbox::i2cBus,
Matt Simmering80d4d5f2023-02-15 15:18:51 -08003956 ipmi::mailbox::targetAddr,
Ankita Vilas Gawadea1650382022-01-08 10:30:40 +00003957 writeData, readBuf);
3958 if (ret != ipmi::ccSuccess)
3959 {
3960 return ipmi::response(ret);
3961 }
3962
3963 else
3964 {
3965 for (const uint8_t& data : readBuf)
3966 {
3967 result.emplace_back(data);
3968 }
3969 }
3970 }
3971
3972 return ipmi::responseSuccess(result);
3973 }
3974 }
3975
3976 case ipmi::mailbox::registerType::singleByteRegister:
3977 {
3978 phosphor::logging::log<phosphor::logging::level::ERR>(
3979 "OEM IPMI command: Register is a Single Byte Register ");
3980
3981 std::vector<uint8_t> writeData = {readRegister};
3982 std::vector<uint8_t> readBuf(numOfBytes);
3983
3984 ipmi::Cc ret = ipmi::i2cWriteRead(ipmi::mailbox::i2cBus,
Matt Simmering80d4d5f2023-02-15 15:18:51 -08003985 ipmi::mailbox::targetAddr,
Ankita Vilas Gawadea1650382022-01-08 10:30:40 +00003986 writeData, readBuf);
3987 if (ret != ipmi::ccSuccess)
3988 {
3989 return ipmi::response(ret);
3990 }
3991 return ipmi::responseSuccess(readBuf);
3992 }
3993
3994 default:
3995 {
Ankita Vilas Gawadea1650382022-01-08 10:30:40 +00003996 phosphor::logging::log<phosphor::logging::level::ERR>(
3997 "OEM IPMI command: Register identifier is not valid.It should "
3998 "be 0 "
3999 "for Single Byte Register and 1 for FIFO Read Register");
4000
4001 return ipmi::responseInvalidFieldRequest();
4002 }
4003 }
4004}
4005
Jason M. Bills64796042018-10-03 16:51:55 -07004006static void registerOEMFunctions(void)
Jia, Chunhuia835eaa2018-09-05 09:00:41 +08004007{
4008 phosphor::logging::log<phosphor::logging::level::INFO>(
4009 "Registering OEM commands");
Vernon Maueryaf652682023-08-04 13:37:21 -07004010 registerHandler(prioOemBase, intel::netFnGeneral,
4011 intel::general::cmdGetBmcVersionString, Privilege::User,
4012 ipmiOEMGetBmcVersionString);
4013
Vernon Mauery98bbf692019-09-16 11:14:59 -07004014 ipmiPrintAndRegister(intel::netFnGeneral,
4015 intel::general::cmdGetChassisIdentifier, NULL,
4016 ipmiOEMGetChassisIdentifier,
4017 PRIVILEGE_USER); // get chassis identifier
4018
4019 ipmiPrintAndRegister(intel::netFnGeneral, intel::general::cmdSetSystemGUID,
4020 NULL, ipmiOEMSetSystemGUID,
4021 PRIVILEGE_ADMIN); // set system guid
Jason M. Billsb02bf092019-08-15 13:01:56 -07004022
4023 // <Disable BMC System Reset Action>
Vernon Mauery98bbf692019-09-16 11:14:59 -07004024 registerHandler(prioOemBase, intel::netFnGeneral,
4025 intel::general::cmdDisableBMCSystemReset, Privilege::Admin,
4026 ipmiOEMDisableBMCSystemReset);
4027
Jason M. Billsb02bf092019-08-15 13:01:56 -07004028 // <Get BMC Reset Disables>
Vernon Mauery98bbf692019-09-16 11:14:59 -07004029 registerHandler(prioOemBase, intel::netFnGeneral,
4030 intel::general::cmdGetBMCResetDisables, Privilege::Admin,
4031 ipmiOEMGetBMCResetDisables);
Jason M. Billsb02bf092019-08-15 13:01:56 -07004032
Vernon Mauery98bbf692019-09-16 11:14:59 -07004033 ipmiPrintAndRegister(intel::netFnGeneral, intel::general::cmdSetBIOSID,
4034 NULL, ipmiOEMSetBIOSID, PRIVILEGE_ADMIN);
Jia, Chunhuicc49b542019-03-20 15:41:07 +08004035
Chen Yugang7a04f3a2019-10-08 11:12:35 +08004036 registerHandler(prioOemBase, intel::netFnGeneral,
4037 intel::general::cmdGetOEMDeviceInfo, Privilege::User,
4038 ipmiOEMGetDeviceInfo);
Jia, Chunhuicc49b542019-03-20 15:41:07 +08004039
Vernon Mauery98bbf692019-09-16 11:14:59 -07004040 ipmiPrintAndRegister(intel::netFnGeneral,
4041 intel::general::cmdGetAICSlotFRUIDSlotPosRecords, NULL,
4042 ipmiOEMGetAICFRU, PRIVILEGE_USER);
Suryakanth Sekard509eb92018-11-15 17:44:11 +05304043
Vernon Mauery98bbf692019-09-16 11:14:59 -07004044 registerHandler(prioOpenBmcBase, intel::netFnGeneral,
4045 intel::general::cmdSendEmbeddedFWUpdStatus,
4046 Privilege::Operator, ipmiOEMSendEmbeddedFwUpdStatus);
Suryakanth Sekard509eb92018-11-15 17:44:11 +05304047
Rajashekar Gade Reddy2b664d52020-03-23 22:09:00 +05304048 registerHandler(prioOpenBmcBase, intel::netFnApp, intel::app::cmdSlotIpmb,
4049 Privilege::Admin, ipmiOEMSlotIpmb);
4050
Vernon Mauery98bbf692019-09-16 11:14:59 -07004051 ipmiPrintAndRegister(intel::netFnGeneral,
4052 intel::general::cmdSetPowerRestoreDelay, NULL,
4053 ipmiOEMSetPowerRestoreDelay, PRIVILEGE_OPERATOR);
4054
4055 ipmiPrintAndRegister(intel::netFnGeneral,
4056 intel::general::cmdGetPowerRestoreDelay, NULL,
4057 ipmiOEMGetPowerRestoreDelay, PRIVILEGE_USER);
4058
4059 registerHandler(prioOpenBmcBase, intel::netFnGeneral,
4060 intel::general::cmdSetOEMUser2Activation,
4061 Privilege::Callback, ipmiOEMSetUser2Activation);
4062
4063 registerHandler(prioOpenBmcBase, intel::netFnGeneral,
4064 intel::general::cmdSetSpecialUserPassword,
4065 Privilege::Callback, ipmiOEMSetSpecialUserPassword);
Richard Marian Thomaiyarfc5e9852019-04-14 15:06:27 +05304066
Jason M. Bills42bd9c82019-06-28 16:39:34 -07004067 // <Get Processor Error Config>
Vernon Mauery98bbf692019-09-16 11:14:59 -07004068 registerHandler(prioOemBase, intel::netFnGeneral,
4069 intel::general::cmdGetProcessorErrConfig, Privilege::User,
4070 ipmiOEMGetProcessorErrConfig);
4071
Jason M. Bills42bd9c82019-06-28 16:39:34 -07004072 // <Set Processor Error Config>
Vernon Mauery98bbf692019-09-16 11:14:59 -07004073 registerHandler(prioOemBase, intel::netFnGeneral,
4074 intel::general::cmdSetProcessorErrConfig, Privilege::Admin,
4075 ipmiOEMSetProcessorErrConfig);
Jason M. Bills42bd9c82019-06-28 16:39:34 -07004076
Vernon Mauery98bbf692019-09-16 11:14:59 -07004077 ipmiPrintAndRegister(intel::netFnGeneral,
4078 intel::general::cmdSetShutdownPolicy, NULL,
4079 ipmiOEMSetShutdownPolicy, PRIVILEGE_ADMIN);
James Feist91244a62019-02-19 15:04:54 -08004080
Vernon Mauery98bbf692019-09-16 11:14:59 -07004081 ipmiPrintAndRegister(intel::netFnGeneral,
4082 intel::general::cmdGetShutdownPolicy, NULL,
4083 ipmiOEMGetShutdownPolicy, PRIVILEGE_ADMIN);
James Feist91244a62019-02-19 15:04:54 -08004084
anil kumar appanaf945eee2019-09-25 23:29:11 +00004085 registerHandler(prioOemBase, intel::netFnGeneral,
4086 intel::general::cmdSetFanConfig, Privilege::User,
4087 ipmiOEMSetFanConfig);
James Feist91244a62019-02-19 15:04:54 -08004088
Vernon Mauery98bbf692019-09-16 11:14:59 -07004089 registerHandler(prioOemBase, intel::netFnGeneral,
4090 intel::general::cmdGetFanConfig, Privilege::User,
4091 ipmiOEMGetFanConfig);
James Feist5f957ca2019-03-14 15:33:55 -07004092
Vernon Mauery98bbf692019-09-16 11:14:59 -07004093 registerHandler(prioOemBase, intel::netFnGeneral,
4094 intel::general::cmdGetFanSpeedOffset, Privilege::User,
4095 ipmiOEMGetFanSpeedOffset);
James Feistacc8a4e2019-04-02 14:23:57 -07004096
Vernon Mauery98bbf692019-09-16 11:14:59 -07004097 registerHandler(prioOemBase, intel::netFnGeneral,
4098 intel::general::cmdSetFanSpeedOffset, Privilege::User,
4099 ipmiOEMSetFanSpeedOffset);
James Feistacc8a4e2019-04-02 14:23:57 -07004100
Vernon Mauery98bbf692019-09-16 11:14:59 -07004101 registerHandler(prioOemBase, intel::netFnGeneral,
4102 intel::general::cmdSetFscParameter, Privilege::User,
4103 ipmiOEMSetFscParameter);
James Feist5f957ca2019-03-14 15:33:55 -07004104
Vernon Mauery98bbf692019-09-16 11:14:59 -07004105 registerHandler(prioOemBase, intel::netFnGeneral,
4106 intel::general::cmdGetFscParameter, Privilege::User,
4107 ipmiOEMGetFscParameter);
Richard Marian Thomaiyarea537d52019-04-24 21:33:48 +05304108
Vernon Mauery98bbf692019-09-16 11:14:59 -07004109 registerHandler(prioOpenBmcBase, intel::netFnGeneral,
4110 intel::general::cmdReadBaseBoardProductId, Privilege::Admin,
4111 ipmiOEMReadBoardProductId);
Chen Yugang39736d52019-07-12 16:24:33 +08004112
Vernon Mauery98bbf692019-09-16 11:14:59 -07004113 registerHandler(prioOemBase, intel::netFnGeneral,
4114 intel::general::cmdGetNmiStatus, Privilege::User,
4115 ipmiOEMGetNmiSource);
Chen Yugang39736d52019-07-12 16:24:33 +08004116
Vernon Mauery98bbf692019-09-16 11:14:59 -07004117 registerHandler(prioOemBase, intel::netFnGeneral,
4118 intel::general::cmdSetNmiStatus, Privilege::Operator,
4119 ipmiOEMSetNmiSource);
Chen,Yugang4f7e76b2019-08-20 09:28:06 +08004120
Vernon Mauery98bbf692019-09-16 11:14:59 -07004121 registerHandler(prioOemBase, intel::netFnGeneral,
4122 intel::general::cmdGetEfiBootOptions, Privilege::User,
4123 ipmiOemGetEfiBootOptions);
Chen,Yugang4f7e76b2019-08-20 09:28:06 +08004124
Vernon Mauery98bbf692019-09-16 11:14:59 -07004125 registerHandler(prioOemBase, intel::netFnGeneral,
4126 intel::general::cmdSetEfiBootOptions, Privilege::Operator,
4127 ipmiOemSetEfiBootOptions);
Richard Marian Thomaiyard801e462019-06-20 01:05:40 +05304128
Vernon Mauery98bbf692019-09-16 11:14:59 -07004129 registerHandler(prioOemBase, intel::netFnGeneral,
4130 intel::general::cmdGetSecurityMode, Privilege::User,
4131 ipmiGetSecurityMode);
Richard Marian Thomaiyard801e462019-06-20 01:05:40 +05304132
Vernon Mauery98bbf692019-09-16 11:14:59 -07004133 registerHandler(prioOemBase, intel::netFnGeneral,
4134 intel::general::cmdSetSecurityMode, Privilege::Admin,
4135 ipmiSetSecurityMode);
Vernon Mauery4ac799d2019-05-20 15:50:37 -07004136
NITIN SHARMAabd11ca2019-06-12 12:31:42 +00004137 registerHandler(prioOemBase, intel::netFnGeneral,
4138 intel::general::cmdGetLEDStatus, Privilege::Admin,
4139 ipmiOEMGetLEDStatus);
Cheng C Yang773703a2019-08-15 09:41:11 +08004140
Vernon Mauery98bbf692019-09-16 11:14:59 -07004141 ipmiPrintAndRegister(ipmi::intel::netFnPlatform,
4142 ipmi::intel::platform::cmdCfgHostSerialPortSpeed, NULL,
4143 ipmiOEMCfgHostSerialPortSpeed, PRIVILEGE_ADMIN);
4144
4145 registerHandler(prioOemBase, intel::netFnGeneral,
4146 intel::general::cmdSetFaultIndication, Privilege::Operator,
4147 ipmiOEMSetFaultIndication);
4148
4149 registerHandler(prioOemBase, intel::netFnGeneral,
4150 intel::general::cmdSetColdRedundancyConfig, Privilege::User,
4151 ipmiOEMSetCRConfig);
4152
4153 registerHandler(prioOemBase, intel::netFnGeneral,
4154 intel::general::cmdGetColdRedundancyConfig, Privilege::User,
4155 ipmiOEMGetCRConfig);
4156
4157 registerHandler(prioOemBase, intel::netFnGeneral,
4158 intel::general::cmdRestoreConfiguration, Privilege::Admin,
Vernon Mauery4ac799d2019-05-20 15:50:37 -07004159 ipmiRestoreConfiguration);
James Feist63efafa2019-07-24 12:39:21 -07004160
Vernon Mauery98bbf692019-09-16 11:14:59 -07004161 registerHandler(prioOemBase, intel::netFnGeneral,
4162 intel::general::cmdSetDimmOffset, Privilege::Operator,
4163 ipmiOEMSetDimmOffset);
James Feist63efafa2019-07-24 12:39:21 -07004164
Vernon Mauery98bbf692019-09-16 11:14:59 -07004165 registerHandler(prioOemBase, intel::netFnGeneral,
4166 intel::general::cmdGetDimmOffset, Privilege::Operator,
4167 ipmiOEMGetDimmOffset);
Chen Yugangca12a7b2019-09-03 18:11:44 +08004168
Cheng C Yang4e6ee152019-09-25 10:27:44 +08004169 registerHandler(prioOemBase, intel::netFnGeneral,
4170 intel::general::cmdGetPSUVersion, Privilege::User,
4171 ipmiOEMGetPSUVersion);
AppaRao Puli28972062019-11-11 02:04:45 +05304172
4173 registerHandler(prioOemBase, intel::netFnGeneral,
4174 intel::general::cmdGetBufferSize, Privilege::User,
4175 ipmiOEMGetBufferSize);
srikanta mondal2030d7c2020-05-03 17:25:25 +00004176
4177 registerHandler(prioOemBase, intel::netFnGeneral,
4178 intel::general::cmdOEMGetReading, Privilege::User,
4179 ipmiOEMGetReading);
Ankita Vilas Gawadea1650382022-01-08 10:30:40 +00004180
4181 registerHandler(prioOemBase, intel::netFnApp, intel::app::cmdPFRMailboxRead,
4182 Privilege::Admin, ipmiOEMReadPFRMailbox);
Jia, Chunhuia835eaa2018-09-05 09:00:41 +08004183}
4184
4185} // namespace ipmi