blob: 9c1a70db4565db2887a91e1f8eda66c2484a21c8 [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
Jason M. Bills64796042018-10-03 16:51:55 -070017#include "xyz/openbmc_project/Common/error.hpp"
Kuiying Wang45f04982018-12-26 09:23:08 +080018#include "xyz/openbmc_project/Led/Physical/server.hpp"
Jason M. Bills64796042018-10-03 16:51:55 -070019
Jia, Chunhuicc49b542019-03-20 15:41:07 +080020#include <systemd/sd-journal.h>
Jia, Chunhuia835eaa2018-09-05 09:00:41 +080021
Chen Yugang7a04f3a2019-10-08 11:12:35 +080022#include <appcommands.hpp>
Jia, Chunhuia835eaa2018-09-05 09:00:41 +080023#include <array>
James Feist91244a62019-02-19 15:04:54 -080024#include <boost/container/flat_map.hpp>
Yong Li23737fe2019-02-19 08:49:55 +080025#include <boost/process/child.hpp>
26#include <boost/process/io.hpp>
Yong Li0669d192019-05-06 14:01:46 +080027#include <com/intel/Control/OCOTShutdownPolicy/server.hpp>
Jason M. Bills64796042018-10-03 16:51:55 -070028#include <commandutils.hpp>
Vernon Mauery4ac799d2019-05-20 15:50:37 -070029#include <filesystem>
Jia, Chunhuia835eaa2018-09-05 09:00:41 +080030#include <iostream>
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>
Chen Yugang7a04f3a2019-10-08 11:12:35 +080036#include <regex>
Jia, Chunhuia835eaa2018-09-05 09:00:41 +080037#include <sdbusplus/bus.hpp>
Suryakanth Sekard509eb92018-11-15 17:44:11 +053038#include <sdbusplus/message/types.hpp>
Jia, Chunhuia835eaa2018-09-05 09:00:41 +080039#include <string>
James Feist91244a62019-02-19 15:04:54 -080040#include <variant>
Jia, Chunhuia835eaa2018-09-05 09:00:41 +080041#include <vector>
Chen Yugang97cf96e2019-11-01 08:55:11 +080042#include <xyz/openbmc_project/Chassis/Control/NMISource/server.hpp>
Chen,Yugang4f7e76b2019-08-20 09:28:06 +080043#include <xyz/openbmc_project/Control/Boot/Mode/server.hpp>
44#include <xyz/openbmc_project/Control/Boot/Source/server.hpp>
Cheng C Yang773703a2019-08-15 09:41:11 +080045#include <xyz/openbmc_project/Control/PowerSupplyRedundancy/server.hpp>
Richard Marian Thomaiyard801e462019-06-20 01:05:40 +053046#include <xyz/openbmc_project/Control/Security/RestrictionMode/server.hpp>
Richard Marian Thomaiyar8d4f8d72019-11-11 12:06:40 +053047#include <xyz/openbmc_project/Control/Security/SpecialMode/server.hpp>
Jia, Chunhuia835eaa2018-09-05 09:00:41 +080048
49namespace ipmi
50{
Jason M. Bills64796042018-10-03 16:51:55 -070051static void registerOEMFunctions() __attribute__((constructor));
Vernon Mauery4ac799d2019-05-20 15:50:37 -070052
Jason M. Bills64796042018-10-03 16:51:55 -070053static constexpr size_t maxFRUStringLength = 0x3F;
Jia, Chunhuia835eaa2018-09-05 09:00:41 +080054
Suryakanth Sekard509eb92018-11-15 17:44:11 +053055static constexpr auto ethernetIntf =
56 "xyz.openbmc_project.Network.EthernetInterface";
57static constexpr auto networkIPIntf = "xyz.openbmc_project.Network.IP";
58static constexpr auto networkService = "xyz.openbmc_project.Network";
59static constexpr auto networkRoot = "/xyz/openbmc_project/network";
60
Chen Yugang97cf96e2019-11-01 08:55:11 +080061static constexpr const char* oemNmiSourceIntf =
62 "xyz.openbmc_project.Chassis.Control.NMISource";
Chen Yugang39736d52019-07-12 16:24:33 +080063static constexpr const char* oemNmiSourceObjPath =
Chen Yugang97cf96e2019-11-01 08:55:11 +080064 "/xyz/openbmc_project/Chassis/Control/NMISource";
Chen Yugang39736d52019-07-12 16:24:33 +080065static constexpr const char* oemNmiBmcSourceObjPathProp = "BMCSource";
66static constexpr const char* oemNmiEnabledObjPathProp = "Enabled";
67
James Feist63efafa2019-07-24 12:39:21 -070068static constexpr const char* dimmOffsetFile = "/var/lib/ipmi/ipmi_dimms.json";
69
Chen Yugang39736d52019-07-12 16:24:33 +080070enum class NmiSource : uint8_t
71{
72 none = 0,
Chen Yugang97cf96e2019-11-01 08:55:11 +080073 frontPanelButton = 1,
74 watchdog = 2,
75 chassisCmd = 3,
76 memoryError = 4,
77 pciBusError = 5,
78 pch = 6,
79 chipset = 7,
Chen Yugang39736d52019-07-12 16:24:33 +080080};
81
Richard Marian Thomaiyard801e462019-06-20 01:05:40 +053082static constexpr const char* restricionModeService =
83 "xyz.openbmc_project.RestrictionMode.Manager";
84static constexpr const char* restricionModeBasePath =
85 "/xyz/openbmc_project/control/security/restriction_mode";
86static constexpr const char* restricionModeIntf =
87 "xyz.openbmc_project.Control.Security.RestrictionMode";
88static constexpr const char* restricionModeProperty = "RestrictionMode";
89
90static constexpr const char* specialModeService =
91 "xyz.openbmc_project.SpecialMode";
92static constexpr const char* specialModeBasePath =
Richard Marian Thomaiyara7b74282019-09-22 21:53:14 +053093 "/xyz/openbmc_project/security/special_mode";
Richard Marian Thomaiyard801e462019-06-20 01:05:40 +053094static constexpr const char* specialModeIntf =
95 "xyz.openbmc_project.Security.SpecialMode";
96static constexpr const char* specialModeProperty = "SpecialMode";
97
98static constexpr const char* dBusPropertyIntf =
99 "org.freedesktop.DBus.Properties";
100static constexpr const char* dBusPropertyGetMethod = "Get";
101static constexpr const char* dBusPropertySetMethod = "Set";
102
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800103// return code: 0 successful
104int8_t getChassisSerialNumber(sdbusplus::bus::bus& bus, std::string& serial)
105{
106 std::string objpath = "/xyz/openbmc_project/FruDevice";
107 std::string intf = "xyz.openbmc_project.FruDeviceManager";
108 std::string service = getService(bus, intf, objpath);
109 ObjectValueTree valueTree = getManagedObjects(bus, service, "/");
110 if (valueTree.empty())
111 {
112 phosphor::logging::log<phosphor::logging::level::ERR>(
113 "No object implements interface",
114 phosphor::logging::entry("INTF=%s", intf.c_str()));
115 return -1;
116 }
117
Jason M. Bills64796042018-10-03 16:51:55 -0700118 for (const auto& item : valueTree)
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800119 {
120 auto interface = item.second.find("xyz.openbmc_project.FruDevice");
121 if (interface == item.second.end())
122 {
123 continue;
124 }
125
126 auto property = interface->second.find("CHASSIS_SERIAL_NUMBER");
127 if (property == interface->second.end())
128 {
129 continue;
130 }
131
132 try
133 {
134 Value variant = property->second;
Vernon Mauery8166c8d2019-05-23 11:22:30 -0700135 std::string& result = std::get<std::string>(variant);
Jason M. Bills64796042018-10-03 16:51:55 -0700136 if (result.size() > maxFRUStringLength)
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800137 {
138 phosphor::logging::log<phosphor::logging::level::ERR>(
139 "FRU serial number exceed maximum length");
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800140 return -1;
141 }
Jason M. Bills64796042018-10-03 16:51:55 -0700142 serial = result;
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800143 return 0;
144 }
Vernon Mauery8166c8d2019-05-23 11:22:30 -0700145 catch (std::bad_variant_access& e)
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800146 {
Jason M. Bills64796042018-10-03 16:51:55 -0700147 phosphor::logging::log<phosphor::logging::level::ERR>(e.what());
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800148 return -1;
149 }
150 }
151 return -1;
152}
Jason M. Bills64796042018-10-03 16:51:55 -0700153
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800154ipmi_ret_t ipmiOEMWildcard(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
155 ipmi_request_t request, ipmi_response_t response,
Jason M. Bills64796042018-10-03 16:51:55 -0700156 ipmi_data_len_t dataLen, ipmi_context_t context)
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800157{
Jason M. Bills64796042018-10-03 16:51:55 -0700158 printCommand(+netfn, +cmd);
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800159 // Status code.
160 ipmi_ret_t rc = IPMI_CC_INVALID;
Jason M. Bills64796042018-10-03 16:51:55 -0700161 *dataLen = 0;
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800162 return rc;
163}
164
165// Returns the Chassis Identifier (serial #)
166ipmi_ret_t ipmiOEMGetChassisIdentifier(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
167 ipmi_request_t request,
168 ipmi_response_t response,
Jason M. Bills64796042018-10-03 16:51:55 -0700169 ipmi_data_len_t dataLen,
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800170 ipmi_context_t context)
171{
172 std::string serial;
Jason M. Bills64796042018-10-03 16:51:55 -0700173 if (*dataLen != 0) // invalid request if there are extra parameters
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800174 {
Jason M. Bills64796042018-10-03 16:51:55 -0700175 *dataLen = 0;
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800176 return IPMI_CC_REQ_DATA_LEN_INVALID;
177 }
Vernon Mauery15419dd2019-05-24 09:40:30 -0700178 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
179 if (getChassisSerialNumber(*dbus, serial) == 0)
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800180 {
Jason M. Bills64796042018-10-03 16:51:55 -0700181 *dataLen = serial.size(); // length will never exceed response length
182 // as it is checked in getChassisSerialNumber
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800183 char* resp = static_cast<char*>(response);
Jason M. Bills64796042018-10-03 16:51:55 -0700184 serial.copy(resp, *dataLen);
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800185 return IPMI_CC_OK;
186 }
Jason M. Bills64796042018-10-03 16:51:55 -0700187 *dataLen = 0;
188 return IPMI_CC_RESPONSE_ERROR;
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800189}
190
191ipmi_ret_t ipmiOEMSetSystemGUID(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
192 ipmi_request_t request,
193 ipmi_response_t response,
Jason M. Bills64796042018-10-03 16:51:55 -0700194 ipmi_data_len_t dataLen, ipmi_context_t context)
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800195{
196 static constexpr size_t safeBufferLength = 50;
197 char buf[safeBufferLength] = {0};
198 GUIDData* Data = reinterpret_cast<GUIDData*>(request);
199
Jason M. Bills64796042018-10-03 16:51:55 -0700200 if (*dataLen != sizeof(GUIDData)) // 16bytes
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800201 {
Jason M. Bills64796042018-10-03 16:51:55 -0700202 *dataLen = 0;
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800203 return IPMI_CC_REQ_DATA_LEN_INVALID;
204 }
205
Jason M. Bills64796042018-10-03 16:51:55 -0700206 *dataLen = 0;
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800207
208 snprintf(
209 buf, safeBufferLength,
210 "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x",
211 Data->timeLow4, Data->timeLow3, Data->timeLow2, Data->timeLow1,
212 Data->timeMid2, Data->timeMid1, Data->timeHigh2, Data->timeHigh1,
213 Data->clock2, Data->clock1, Data->node6, Data->node5, Data->node4,
214 Data->node3, Data->node2, Data->node1);
215 // UUID is in RFC4122 format. Ex: 61a39523-78f2-11e5-9862-e6402cfc3223
216 std::string guid = buf;
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800217
218 std::string objpath = "/xyz/openbmc_project/control/host0/systemGUID";
219 std::string intf = "xyz.openbmc_project.Common.UUID";
Vernon Mauery15419dd2019-05-24 09:40:30 -0700220 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
221 std::string service = getService(*dbus, intf, objpath);
222 setDbusProperty(*dbus, service, objpath, intf, "UUID", guid);
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800223 return IPMI_CC_OK;
224}
225
Jason M. Billsb02bf092019-08-15 13:01:56 -0700226ipmi::RspType<> ipmiOEMDisableBMCSystemReset(bool disableResetOnSMI,
227 uint7_t reserved1)
228{
229 std::shared_ptr<sdbusplus::asio::connection> busp = getSdBus();
230
231 try
232 {
233 auto service =
234 ipmi::getService(*busp, bmcResetDisablesIntf, bmcResetDisablesPath);
235 ipmi::setDbusProperty(*busp, service, bmcResetDisablesPath,
236 bmcResetDisablesIntf, "ResetOnSMI",
237 !disableResetOnSMI);
238 }
239 catch (std::exception& e)
240 {
241 phosphor::logging::log<phosphor::logging::level::ERR>(
242 "Failed to set BMC reset disables",
243 phosphor::logging::entry("EXCEPTION=%s", e.what()));
244 return ipmi::responseUnspecifiedError();
245 }
246
247 return ipmi::responseSuccess();
248}
249
250ipmi::RspType<bool, // disableResetOnSMI
251 uint7_t // reserved
252 >
253 ipmiOEMGetBMCResetDisables()
254{
255 bool disableResetOnSMI = true;
256
257 std::shared_ptr<sdbusplus::asio::connection> busp = getSdBus();
258 try
259 {
260 auto service =
261 ipmi::getService(*busp, bmcResetDisablesIntf, bmcResetDisablesPath);
262 Value variant =
263 ipmi::getDbusProperty(*busp, service, bmcResetDisablesPath,
264 bmcResetDisablesIntf, "ResetOnSMI");
265 disableResetOnSMI = !std::get<bool>(variant);
266 }
267 catch (std::exception& e)
268 {
269 phosphor::logging::log<phosphor::logging::level::ERR>(
270 "Failed to get BMC reset disables",
271 phosphor::logging::entry("EXCEPTION=%s", e.what()));
272 return ipmi::responseUnspecifiedError();
273 }
274
275 return ipmi::responseSuccess(disableResetOnSMI, 0);
276}
277
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800278ipmi_ret_t ipmiOEMSetBIOSID(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
279 ipmi_request_t request, ipmi_response_t response,
280 ipmi_data_len_t dataLen, ipmi_context_t context)
281{
282 DeviceInfo* data = reinterpret_cast<DeviceInfo*>(request);
283
Jason M. Bills64796042018-10-03 16:51:55 -0700284 if ((*dataLen < 2) || (*dataLen != (1 + data->biosIDLength)))
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800285 {
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800286 *dataLen = 0;
287 return IPMI_CC_REQ_DATA_LEN_INVALID;
288 }
Jason M. Bills64796042018-10-03 16:51:55 -0700289 std::string idString((char*)data->biosId, data->biosIDLength);
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800290
Vernon Mauery15419dd2019-05-24 09:40:30 -0700291 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
292 std::string service = getService(*dbus, biosIntf, biosObjPath);
293 setDbusProperty(*dbus, service, biosObjPath, biosIntf, biosProp, idString);
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800294 uint8_t* bytesWritten = static_cast<uint8_t*>(response);
295 *bytesWritten =
Jason M. Bills64796042018-10-03 16:51:55 -0700296 data->biosIDLength; // how many bytes are written into storage
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800297 *dataLen = 1;
298 return IPMI_CC_OK;
299}
300
Chen Yugang7a04f3a2019-10-08 11:12:35 +0800301bool getSwVerInfo(uint8_t& bmcMajor, uint8_t& bmcMinor, uint8_t& meMajor,
302 uint8_t& meMinor)
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800303{
Chen Yugang7a04f3a2019-10-08 11:12:35 +0800304 // step 1 : get BMC Major and Minor numbers from its DBUS property
305 std::optional<MetaRevision> rev{};
306 try
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800307 {
Chen Yugang7a04f3a2019-10-08 11:12:35 +0800308 std::string version = getActiveSoftwareVersionInfo();
309 rev = convertIntelVersion(version);
310 }
311 catch (const std::exception& e)
312 {
313 return false;
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800314 }
315
Chen Yugang7a04f3a2019-10-08 11:12:35 +0800316 if (rev.has_value())
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800317 {
Chen Yugang7a04f3a2019-10-08 11:12:35 +0800318 MetaRevision revision = rev.value();
319 bmcMajor = revision.major;
320
321 revision.minor = (revision.minor > 99 ? 99 : revision.minor);
322 bmcMinor = revision.minor % 10 + (revision.minor / 10) * 16;
323 }
324
325 // step 2 : get ME Major and Minor numbers from its DBUS property
326 try
327 {
328 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
329 std::string service =
330 getService(*dbus, "xyz.openbmc_project.Software.Version",
331 "/xyz/openbmc_project/me_version");
332 Value variant =
333 getDbusProperty(*dbus, service, "/xyz/openbmc_project/me_version",
334 "xyz.openbmc_project.Software.Version", "Version");
335
336 std::string& meString = std::get<std::string>(variant);
337
338 // get ME major number
339 std::regex pattern1("(\\d+?).(\\d+?).(\\d+?).(\\d+?).(\\d+?)");
340 constexpr size_t matchedPhosphor = 6;
341 std::smatch results;
342 if (std::regex_match(meString, results, pattern1))
343 {
344 if (results.size() == matchedPhosphor)
345 {
346 meMajor = static_cast<uint8_t>(std::stoi(results[1]));
347 meMinor = static_cast<uint8_t>(std::stoi(results[2]));
348 }
349 }
350 }
351 catch (sdbusplus::exception::SdBusError& e)
352 {
353 return false;
354 }
355 return true;
356}
357
358ipmi::RspType<
359 std::variant<std::string,
360 std::tuple<uint8_t, std::array<uint8_t, 2>,
361 std::array<uint8_t, 2>, std::array<uint8_t, 2>,
362 std::array<uint8_t, 2>, std::array<uint8_t, 2>>,
363 std::tuple<uint8_t, std::array<uint8_t, 2>>>>
364 ipmiOEMGetDeviceInfo(uint8_t entityType, uint8_t countToRead,
365 uint8_t offset)
366{
367 if (countToRead == 0)
368 {
369 return ipmi::responseReqDataLenInvalid();
370 }
371
372 if (entityType > static_cast<uint8_t>(OEMDevEntityType::sdrVer))
373 {
374 return ipmi::responseInvalidFieldRequest();
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800375 }
376
377 // handle OEM command items
Chen Yugang7a04f3a2019-10-08 11:12:35 +0800378 switch (OEMDevEntityType(entityType))
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800379 {
380 case OEMDevEntityType::biosId:
381 {
Vernon Mauery15419dd2019-05-24 09:40:30 -0700382 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
383 std::string service = getService(*dbus, biosIntf, biosObjPath);
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800384 try
385 {
Vernon Mauery15419dd2019-05-24 09:40:30 -0700386 Value variant = getDbusProperty(*dbus, service, biosObjPath,
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800387 biosIntf, biosProp);
Vernon Mauery8166c8d2019-05-23 11:22:30 -0700388 std::string& idString = std::get<std::string>(variant);
Chen Yugang7a04f3a2019-10-08 11:12:35 +0800389 if (offset >= idString.size())
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800390 {
Chen Yugang7a04f3a2019-10-08 11:12:35 +0800391 return ipmi::responseParmOutOfRange();
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800392 }
Jason M. Bills64796042018-10-03 16:51:55 -0700393 size_t length = 0;
Chen Yugang7a04f3a2019-10-08 11:12:35 +0800394 if (countToRead > (idString.size() - offset))
Jason M. Bills64796042018-10-03 16:51:55 -0700395 {
Chen Yugang7a04f3a2019-10-08 11:12:35 +0800396 length = idString.size() - offset;
Jason M. Bills64796042018-10-03 16:51:55 -0700397 }
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800398 else
399 {
Chen Yugang7a04f3a2019-10-08 11:12:35 +0800400 length = countToRead;
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800401 }
Chen Yugang7a04f3a2019-10-08 11:12:35 +0800402
403 std::string readBuf = {0};
404 readBuf.resize(length);
405 std::copy_n(idString.begin() + offset, length,
406 (readBuf.begin()));
407 return ipmi::responseSuccess(readBuf);
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800408 }
Vernon Mauery8166c8d2019-05-23 11:22:30 -0700409 catch (std::bad_variant_access& e)
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800410 {
Chen Yugang7a04f3a2019-10-08 11:12:35 +0800411 return ipmi::responseUnspecifiedError();
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800412 }
413 }
414 break;
415
416 case OEMDevEntityType::devVer:
Chen Yugang7a04f3a2019-10-08 11:12:35 +0800417 {
418 constexpr const size_t verLen = 2;
419 constexpr const size_t verTotalLen = 10;
420 std::array<uint8_t, verLen> bmcBuf = {0xff, 0xff};
421 std::array<uint8_t, verLen> hsc0Buf = {0xff, 0xff};
422 std::array<uint8_t, verLen> hsc1Buf = {0xff, 0xff};
423 std::array<uint8_t, verLen> meBuf = {0xff, 0xff};
424 std::array<uint8_t, verLen> hsc2Buf = {0xff, 0xff};
425 // data0/1: BMC version number; data6/7: ME version number
426 // the others: HSC0/1/2 version number, not avaible.
427 if (true != getSwVerInfo(bmcBuf[0], bmcBuf[1], meBuf[0], meBuf[1]))
428 {
429 return ipmi::responseUnspecifiedError();
430 }
431 return ipmi::responseSuccess(
432 std::tuple<
433 uint8_t, std::array<uint8_t, verLen>,
434 std::array<uint8_t, verLen>, std::array<uint8_t, verLen>,
435 std::array<uint8_t, verLen>, std::array<uint8_t, verLen>>{
436 verTotalLen, bmcBuf, hsc0Buf, hsc1Buf, meBuf, hsc2Buf});
437 }
438 break;
439
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800440 case OEMDevEntityType::sdrVer:
Chen Yugang7a04f3a2019-10-08 11:12:35 +0800441 {
442 constexpr const size_t sdrLen = 2;
443 std::array<uint8_t, sdrLen> readBuf = {0x01, 0x0};
444 return ipmi::responseSuccess(
445 std::tuple<uint8_t, std::array<uint8_t, sdrLen>>{sdrLen,
446 readBuf});
447 }
448 break;
449
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800450 default:
Chen Yugang7a04f3a2019-10-08 11:12:35 +0800451 return ipmi::responseInvalidFieldRequest();
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800452 }
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800453}
454
455ipmi_ret_t ipmiOEMGetAICFRU(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
456 ipmi_request_t request, ipmi_response_t response,
457 ipmi_data_len_t dataLen, ipmi_context_t context)
458{
459 if (*dataLen != 0)
460 {
Jason M. Bills64796042018-10-03 16:51:55 -0700461 *dataLen = 0;
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800462 return IPMI_CC_REQ_DATA_LEN_INVALID;
463 }
464
465 *dataLen = 1;
466 uint8_t* res = reinterpret_cast<uint8_t*>(response);
467 // temporary fix. We don't support AIC FRU now. Just tell BIOS that no
468 // AIC is available so that BIOS will not timeout repeatly which leads to
469 // slow booting.
470 *res = 0; // Byte1=Count of SlotPosition/FruID records.
471 return IPMI_CC_OK;
472}
473
Jason M. Bills64796042018-10-03 16:51:55 -0700474ipmi_ret_t ipmiOEMGetPowerRestoreDelay(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
475 ipmi_request_t request,
476 ipmi_response_t response,
477 ipmi_data_len_t dataLen,
478 ipmi_context_t context)
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800479{
Jason M. Bills64796042018-10-03 16:51:55 -0700480 GetPowerRestoreDelayRes* resp =
481 reinterpret_cast<GetPowerRestoreDelayRes*>(response);
482
483 if (*dataLen != 0)
484 {
485 *dataLen = 0;
486 return IPMI_CC_REQ_DATA_LEN_INVALID;
487 }
488
Vernon Mauery15419dd2019-05-24 09:40:30 -0700489 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
Jason M. Bills64796042018-10-03 16:51:55 -0700490 std::string service =
Vernon Mauery15419dd2019-05-24 09:40:30 -0700491 getService(*dbus, powerRestoreDelayIntf, powerRestoreDelayObjPath);
Jason M. Bills64796042018-10-03 16:51:55 -0700492 Value variant =
Vernon Mauery15419dd2019-05-24 09:40:30 -0700493 getDbusProperty(*dbus, service, powerRestoreDelayObjPath,
Jason M. Bills64796042018-10-03 16:51:55 -0700494 powerRestoreDelayIntf, powerRestoreDelayProp);
495
Vernon Mauery8166c8d2019-05-23 11:22:30 -0700496 uint16_t delay = std::get<uint16_t>(variant);
Jason M. Bills64796042018-10-03 16:51:55 -0700497 resp->byteLSB = delay;
498 resp->byteMSB = delay >> 8;
499
500 *dataLen = sizeof(GetPowerRestoreDelayRes);
501
502 return IPMI_CC_OK;
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800503}
504
Jia, Chunhuicc49b542019-03-20 15:41:07 +0800505static uint8_t bcdToDec(uint8_t val)
506{
507 return ((val / 16 * 10) + (val % 16));
508}
509
510// Allows an update utility or system BIOS to send the status of an embedded
511// firmware update attempt to the BMC. After received, BMC will create a logging
512// record.
513ipmi::RspType<> ipmiOEMSendEmbeddedFwUpdStatus(uint8_t status, uint8_t target,
514 uint8_t majorRevision,
515 uint8_t minorRevision,
516 uint32_t auxInfo)
517{
518 std::string firmware;
Jason M. Billsdc249272019-04-03 09:58:40 -0700519 int instance = (target & targetInstanceMask) >> targetInstanceShift;
Jia, Chunhuicc49b542019-03-20 15:41:07 +0800520 target = (target & selEvtTargetMask) >> selEvtTargetShift;
521
522 /* make sure the status is 0, 1, or 2 as per the spec */
523 if (status > 2)
524 {
525 return ipmi::response(ipmi::ccInvalidFieldRequest);
526 }
Jason M. Billsdc249272019-04-03 09:58:40 -0700527 /* make sure the target is 0, 1, 2, or 4 as per the spec */
528 if (target > 4 || target == 3)
529 {
530 return ipmi::response(ipmi::ccInvalidFieldRequest);
531 }
Jia, Chunhuicc49b542019-03-20 15:41:07 +0800532 /*orignal OEM command is to record OEM SEL.
533 But openbmc does not support OEM SEL, so we redirect it to redfish event
534 logging. */
535 std::string buildInfo;
536 std::string action;
537 switch (FWUpdateTarget(target))
538 {
539 case FWUpdateTarget::targetBMC:
540 firmware = "BMC";
Jason M. Billsdc249272019-04-03 09:58:40 -0700541 buildInfo = "major: " + std::to_string(majorRevision) + " minor: " +
Jia, Chunhuicc49b542019-03-20 15:41:07 +0800542 std::to_string(bcdToDec(minorRevision)) + // BCD encoded
543 " BuildID: " + std::to_string(auxInfo);
544 buildInfo += std::to_string(auxInfo);
545 break;
546 case FWUpdateTarget::targetBIOS:
547 firmware = "BIOS";
548 buildInfo =
Jason M. Billsdc249272019-04-03 09:58:40 -0700549 "major: " +
Jia, Chunhuicc49b542019-03-20 15:41:07 +0800550 std::to_string(bcdToDec(majorRevision)) + // BCD encoded
551 " minor: " +
552 std::to_string(bcdToDec(minorRevision)) + // BCD encoded
553 " ReleaseNumber: " + // ASCII encoded
554 std::to_string(static_cast<uint8_t>(auxInfo >> 0) - '0') +
555 std::to_string(static_cast<uint8_t>(auxInfo >> 8) - '0') +
556 std::to_string(static_cast<uint8_t>(auxInfo >> 16) - '0') +
557 std::to_string(static_cast<uint8_t>(auxInfo >> 24) - '0');
558 break;
559 case FWUpdateTarget::targetME:
560 firmware = "ME";
561 buildInfo =
Jason M. Billsdc249272019-04-03 09:58:40 -0700562 "major: " + std::to_string(majorRevision) + " minor1: " +
Jia, Chunhuicc49b542019-03-20 15:41:07 +0800563 std::to_string(bcdToDec(minorRevision)) + // BCD encoded
564 " minor2: " +
565 std::to_string(bcdToDec(static_cast<uint8_t>(auxInfo >> 0))) +
566 " build1: " +
567 std::to_string(bcdToDec(static_cast<uint8_t>(auxInfo >> 8))) +
568 " build2: " +
569 std::to_string(bcdToDec(static_cast<uint8_t>(auxInfo >> 16)));
570 break;
571 case FWUpdateTarget::targetOEMEWS:
572 firmware = "EWS";
Jason M. Billsdc249272019-04-03 09:58:40 -0700573 buildInfo = "major: " + std::to_string(majorRevision) + " minor: " +
Jia, Chunhuicc49b542019-03-20 15:41:07 +0800574 std::to_string(bcdToDec(minorRevision)) + // BCD encoded
575 " BuildID: " + std::to_string(auxInfo);
576 break;
577 }
578
Jason M. Billsdc249272019-04-03 09:58:40 -0700579 static const std::string openBMCMessageRegistryVersion("0.1");
580 std::string redfishMsgID = "OpenBMC." + openBMCMessageRegistryVersion;
581
Jia, Chunhuicc49b542019-03-20 15:41:07 +0800582 switch (status)
583 {
584 case 0x0:
585 action = "update started";
Jason M. Billsdc249272019-04-03 09:58:40 -0700586 redfishMsgID += ".FirmwareUpdateStarted";
Jia, Chunhuicc49b542019-03-20 15:41:07 +0800587 break;
588 case 0x1:
589 action = "update completed successfully";
Jason M. Billsdc249272019-04-03 09:58:40 -0700590 redfishMsgID += ".FirmwareUpdateCompleted";
Jia, Chunhuicc49b542019-03-20 15:41:07 +0800591 break;
592 case 0x2:
593 action = "update failure";
Jason M. Billsdc249272019-04-03 09:58:40 -0700594 redfishMsgID += ".FirmwareUpdateFailed";
Jia, Chunhuicc49b542019-03-20 15:41:07 +0800595 break;
596 default:
597 action = "unknown";
598 break;
599 }
600
Jason M. Billsdc249272019-04-03 09:58:40 -0700601 std::string firmwareInstanceStr =
602 firmware + " instance: " + std::to_string(instance);
603 std::string message("[firmware update] " + firmwareInstanceStr +
604 " status: <" + action + "> " + buildInfo);
Jia, Chunhuicc49b542019-03-20 15:41:07 +0800605
606 sd_journal_send("MESSAGE=%s", message.c_str(), "PRIORITY=%i", LOG_INFO,
Jason M. Billsdc249272019-04-03 09:58:40 -0700607 "REDFISH_MESSAGE_ID=%s", redfishMsgID.c_str(),
608 "REDFISH_MESSAGE_ARGS=%s,%s", firmwareInstanceStr.c_str(),
609 buildInfo.c_str(), NULL);
Jia, Chunhuicc49b542019-03-20 15:41:07 +0800610 return ipmi::responseSuccess();
611}
612
Jason M. Bills64796042018-10-03 16:51:55 -0700613ipmi_ret_t ipmiOEMSetPowerRestoreDelay(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
614 ipmi_request_t request,
615 ipmi_response_t response,
616 ipmi_data_len_t dataLen,
617 ipmi_context_t context)
618{
619 SetPowerRestoreDelayReq* data =
620 reinterpret_cast<SetPowerRestoreDelayReq*>(request);
621 uint16_t delay = 0;
622
623 if (*dataLen != sizeof(SetPowerRestoreDelayReq))
624 {
625 *dataLen = 0;
626 return IPMI_CC_REQ_DATA_LEN_INVALID;
627 }
628 delay = data->byteMSB;
629 delay = (delay << 8) | data->byteLSB;
Vernon Mauery15419dd2019-05-24 09:40:30 -0700630 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
Jason M. Bills64796042018-10-03 16:51:55 -0700631 std::string service =
Vernon Mauery15419dd2019-05-24 09:40:30 -0700632 getService(*dbus, powerRestoreDelayIntf, powerRestoreDelayObjPath);
633 setDbusProperty(*dbus, service, powerRestoreDelayObjPath,
Jason M. Bills64796042018-10-03 16:51:55 -0700634 powerRestoreDelayIntf, powerRestoreDelayProp, delay);
635 *dataLen = 0;
636
637 return IPMI_CC_OK;
638}
639
Jason M. Bills42bd9c82019-06-28 16:39:34 -0700640static bool cpuPresent(const std::string& cpuName)
Jason M. Bills64796042018-10-03 16:51:55 -0700641{
Jason M. Bills42bd9c82019-06-28 16:39:34 -0700642 static constexpr const char* cpuPresencePathPrefix =
643 "/xyz/openbmc_project/inventory/system/chassis/motherboard/";
644 static constexpr const char* cpuPresenceIntf =
645 "xyz.openbmc_project.Inventory.Item";
646 std::string cpuPresencePath = cpuPresencePathPrefix + cpuName;
647 std::shared_ptr<sdbusplus::asio::connection> busp = getSdBus();
648 try
Jason M. Bills64796042018-10-03 16:51:55 -0700649 {
Jason M. Bills42bd9c82019-06-28 16:39:34 -0700650 auto service =
651 ipmi::getService(*busp, cpuPresenceIntf, cpuPresencePath);
652
653 ipmi::Value result = ipmi::getDbusProperty(
654 *busp, service, cpuPresencePath, cpuPresenceIntf, "Present");
655 return std::get<bool>(result);
656 }
657 catch (const std::exception& e)
658 {
659 phosphor::logging::log<phosphor::logging::level::INFO>(
660 "Cannot find processor presence",
661 phosphor::logging::entry("NAME=%s", cpuName.c_str()));
662 return false;
663 }
664}
665
666ipmi::RspType<bool, // CATERR Reset Enabled
667 bool, // ERR2 Reset Enabled
668 uint6_t, // reserved
669 uint8_t, // reserved, returns 0x3F
670 uint6_t, // CPU1 CATERR Count
671 uint2_t, // CPU1 Status
672 uint6_t, // CPU2 CATERR Count
673 uint2_t, // CPU2 Status
674 uint6_t, // CPU3 CATERR Count
675 uint2_t, // CPU3 Status
676 uint6_t, // CPU4 CATERR Count
677 uint2_t, // CPU4 Status
678 uint8_t // Crashdump Count
679 >
680 ipmiOEMGetProcessorErrConfig()
681{
682 bool resetOnCATERR = false;
683 bool resetOnERR2 = false;
684 uint6_t cpu1CATERRCount = 0;
685 uint6_t cpu2CATERRCount = 0;
686 uint6_t cpu3CATERRCount = 0;
687 uint6_t cpu4CATERRCount = 0;
688 uint8_t crashdumpCount = 0;
689 uint2_t cpu1Status =
690 cpuPresent("CPU_1") ? CPUStatus::enabled : CPUStatus::notPresent;
691 uint2_t cpu2Status =
692 cpuPresent("CPU_2") ? CPUStatus::enabled : CPUStatus::notPresent;
693 uint2_t cpu3Status =
694 cpuPresent("CPU_3") ? CPUStatus::enabled : CPUStatus::notPresent;
695 uint2_t cpu4Status =
696 cpuPresent("CPU_4") ? CPUStatus::enabled : CPUStatus::notPresent;
697
698 std::shared_ptr<sdbusplus::asio::connection> busp = getSdBus();
699 try
700 {
701 auto service = ipmi::getService(*busp, processorErrConfigIntf,
702 processorErrConfigObjPath);
703
704 ipmi::PropertyMap result = ipmi::getAllDbusProperties(
705 *busp, service, processorErrConfigObjPath, processorErrConfigIntf);
706 resetOnCATERR = std::get<bool>(result.at("ResetOnCATERR"));
707 resetOnERR2 = std::get<bool>(result.at("ResetOnERR2"));
708 cpu1CATERRCount = std::get<uint8_t>(result.at("ErrorCountCPU1"));
709 cpu2CATERRCount = std::get<uint8_t>(result.at("ErrorCountCPU2"));
710 cpu3CATERRCount = std::get<uint8_t>(result.at("ErrorCountCPU3"));
711 cpu4CATERRCount = std::get<uint8_t>(result.at("ErrorCountCPU4"));
712 crashdumpCount = std::get<uint8_t>(result.at("CrashdumpCount"));
713 }
714 catch (const std::exception& e)
715 {
716 phosphor::logging::log<phosphor::logging::level::ERR>(
717 "Failed to fetch processor error config",
718 phosphor::logging::entry("ERROR=%s", e.what()));
719 return ipmi::responseUnspecifiedError();
Jason M. Bills64796042018-10-03 16:51:55 -0700720 }
721
Jason M. Bills42bd9c82019-06-28 16:39:34 -0700722 return ipmi::responseSuccess(resetOnCATERR, resetOnERR2, 0, 0x3F,
723 cpu1CATERRCount, cpu1Status, cpu2CATERRCount,
724 cpu2Status, cpu3CATERRCount, cpu3Status,
725 cpu4CATERRCount, cpu4Status, crashdumpCount);
726}
Jason M. Bills64796042018-10-03 16:51:55 -0700727
Jason M. Bills42bd9c82019-06-28 16:39:34 -0700728ipmi::RspType<> ipmiOEMSetProcessorErrConfig(
729 bool resetOnCATERR, bool resetOnERR2, uint6_t reserved1, uint8_t reserved2,
730 std::optional<bool> clearCPUErrorCount,
731 std::optional<bool> clearCrashdumpCount, std::optional<uint6_t> reserved3)
732{
733 std::shared_ptr<sdbusplus::asio::connection> busp = getSdBus();
Jason M. Bills64796042018-10-03 16:51:55 -0700734
735 try
736 {
Jason M. Bills42bd9c82019-06-28 16:39:34 -0700737 auto service = ipmi::getService(*busp, processorErrConfigIntf,
738 processorErrConfigObjPath);
739 ipmi::setDbusProperty(*busp, service, processorErrConfigObjPath,
740 processorErrConfigIntf, "ResetOnCATERR",
741 resetOnCATERR);
742 ipmi::setDbusProperty(*busp, service, processorErrConfigObjPath,
743 processorErrConfigIntf, "ResetOnERR2",
744 resetOnERR2);
745 if (clearCPUErrorCount.value_or(false))
746 {
747 ipmi::setDbusProperty(*busp, service, processorErrConfigObjPath,
Jason M. Billsd3e19932019-08-15 12:39:03 -0700748 processorErrConfigIntf, "ErrorCountCPU1",
749 static_cast<uint8_t>(0));
Jason M. Bills42bd9c82019-06-28 16:39:34 -0700750 ipmi::setDbusProperty(*busp, service, processorErrConfigObjPath,
Jason M. Billsd3e19932019-08-15 12:39:03 -0700751 processorErrConfigIntf, "ErrorCountCPU2",
752 static_cast<uint8_t>(0));
753 ipmi::setDbusProperty(*busp, service, processorErrConfigObjPath,
754 processorErrConfigIntf, "ErrorCountCPU3",
755 static_cast<uint8_t>(0));
756 ipmi::setDbusProperty(*busp, service, processorErrConfigObjPath,
757 processorErrConfigIntf, "ErrorCountCPU4",
758 static_cast<uint8_t>(0));
Jason M. Bills42bd9c82019-06-28 16:39:34 -0700759 }
760 if (clearCrashdumpCount.value_or(false))
761 {
762 ipmi::setDbusProperty(*busp, service, processorErrConfigObjPath,
Jason M. Billsd3e19932019-08-15 12:39:03 -0700763 processorErrConfigIntf, "CrashdumpCount",
764 static_cast<uint8_t>(0));
Jason M. Bills42bd9c82019-06-28 16:39:34 -0700765 }
Jason M. Bills64796042018-10-03 16:51:55 -0700766 }
Jason M. Bills42bd9c82019-06-28 16:39:34 -0700767 catch (std::exception& e)
Jason M. Bills64796042018-10-03 16:51:55 -0700768 {
Kuiying Wangbc546672018-11-23 15:41:05 +0800769 phosphor::logging::log<phosphor::logging::level::ERR>(
Jason M. Bills42bd9c82019-06-28 16:39:34 -0700770 "Failed to set processor error config",
771 phosphor::logging::entry("EXCEPTION=%s", e.what()));
772 return ipmi::responseUnspecifiedError();
Jason M. Bills64796042018-10-03 16:51:55 -0700773 }
774
Jason M. Bills42bd9c82019-06-28 16:39:34 -0700775 return ipmi::responseSuccess();
Jason M. Bills64796042018-10-03 16:51:55 -0700776}
777
Yong Li703922d2018-11-06 13:25:31 +0800778ipmi_ret_t ipmiOEMGetShutdownPolicy(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
779 ipmi_request_t request,
780 ipmi_response_t response,
781 ipmi_data_len_t dataLen,
782 ipmi_context_t context)
783{
784 GetOEMShutdownPolicyRes* resp =
785 reinterpret_cast<GetOEMShutdownPolicyRes*>(response);
786
787 if (*dataLen != 0)
788 {
789 phosphor::logging::log<phosphor::logging::level::ERR>(
Kuiying Wang45f04982018-12-26 09:23:08 +0800790 "oem_get_shutdown_policy: invalid input len!");
Yong Li703922d2018-11-06 13:25:31 +0800791 *dataLen = 0;
792 return IPMI_CC_REQ_DATA_LEN_INVALID;
793 }
794
795 *dataLen = 0;
796
797 try
798 {
Vernon Mauery15419dd2019-05-24 09:40:30 -0700799 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
Yong Li703922d2018-11-06 13:25:31 +0800800 std::string service =
Vernon Mauery15419dd2019-05-24 09:40:30 -0700801 getService(*dbus, oemShutdownPolicyIntf, oemShutdownPolicyObjPath);
802 Value variant = getDbusProperty(
803 *dbus, service, oemShutdownPolicyObjPath, oemShutdownPolicyIntf,
804 oemShutdownPolicyObjPathProp);
Yong Li0669d192019-05-06 14:01:46 +0800805
806 if (sdbusplus::com::intel::Control::server::OCOTShutdownPolicy::
807 convertPolicyFromString(std::get<std::string>(variant)) ==
808 sdbusplus::com::intel::Control::server::OCOTShutdownPolicy::Policy::
809 NoShutdownOnOCOT)
810 {
811 resp->policy = 0;
812 }
813 else if (sdbusplus::com::intel::Control::server::OCOTShutdownPolicy::
814 convertPolicyFromString(std::get<std::string>(variant)) ==
815 sdbusplus::com::intel::Control::server::OCOTShutdownPolicy::
816 Policy::ShutdownOnOCOT)
817 {
818 resp->policy = 1;
819 }
820 else
821 {
822 phosphor::logging::log<phosphor::logging::level::ERR>(
823 "oem_set_shutdown_policy: invalid property!",
824 phosphor::logging::entry(
825 "PROP=%s", std::get<std::string>(variant).c_str()));
826 return IPMI_CC_UNSPECIFIED_ERROR;
827 }
Yong Li703922d2018-11-06 13:25:31 +0800828 // TODO needs to check if it is multi-node products,
829 // policy is only supported on node 3/4
830 resp->policySupport = shutdownPolicySupported;
831 }
832 catch (sdbusplus::exception_t& e)
833 {
834 phosphor::logging::log<phosphor::logging::level::ERR>(e.description());
835 return IPMI_CC_UNSPECIFIED_ERROR;
836 }
837
838 *dataLen = sizeof(GetOEMShutdownPolicyRes);
839 return IPMI_CC_OK;
840}
841
842ipmi_ret_t ipmiOEMSetShutdownPolicy(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
843 ipmi_request_t request,
844 ipmi_response_t response,
845 ipmi_data_len_t dataLen,
846 ipmi_context_t context)
847{
848 uint8_t* req = reinterpret_cast<uint8_t*>(request);
Yong Li0669d192019-05-06 14:01:46 +0800849 sdbusplus::com::intel::Control::server::OCOTShutdownPolicy::Policy policy =
850 sdbusplus::com::intel::Control::server::OCOTShutdownPolicy::Policy::
851 NoShutdownOnOCOT;
Yong Li703922d2018-11-06 13:25:31 +0800852
853 // TODO needs to check if it is multi-node products,
854 // policy is only supported on node 3/4
855 if (*dataLen != 1)
856 {
857 phosphor::logging::log<phosphor::logging::level::ERR>(
858 "oem_set_shutdown_policy: invalid input len!");
859 *dataLen = 0;
860 return IPMI_CC_REQ_DATA_LEN_INVALID;
861 }
862
863 *dataLen = 0;
864 if ((*req != noShutdownOnOCOT) && (*req != shutdownOnOCOT))
865 {
866 phosphor::logging::log<phosphor::logging::level::ERR>(
867 "oem_set_shutdown_policy: invalid input!");
868 return IPMI_CC_INVALID_FIELD_REQUEST;
869 }
870
Yong Li0669d192019-05-06 14:01:46 +0800871 if (*req == noShutdownOnOCOT)
872 {
873 policy = sdbusplus::com::intel::Control::server::OCOTShutdownPolicy::
874 Policy::NoShutdownOnOCOT;
875 }
876 else
877 {
878 policy = sdbusplus::com::intel::Control::server::OCOTShutdownPolicy::
879 Policy::ShutdownOnOCOT;
880 }
881
Yong Li703922d2018-11-06 13:25:31 +0800882 try
883 {
Vernon Mauery15419dd2019-05-24 09:40:30 -0700884 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
Yong Li703922d2018-11-06 13:25:31 +0800885 std::string service =
Vernon Mauery15419dd2019-05-24 09:40:30 -0700886 getService(*dbus, oemShutdownPolicyIntf, oemShutdownPolicyObjPath);
Yong Li0669d192019-05-06 14:01:46 +0800887 setDbusProperty(
Vernon Mauery15419dd2019-05-24 09:40:30 -0700888 *dbus, service, oemShutdownPolicyObjPath, oemShutdownPolicyIntf,
Yong Li0669d192019-05-06 14:01:46 +0800889 oemShutdownPolicyObjPathProp,
890 sdbusplus::com::intel::Control::server::convertForMessage(policy));
Yong Li703922d2018-11-06 13:25:31 +0800891 }
892 catch (sdbusplus::exception_t& e)
893 {
894 phosphor::logging::log<phosphor::logging::level::ERR>(e.description());
895 return IPMI_CC_UNSPECIFIED_ERROR;
896 }
897
898 return IPMI_CC_OK;
899}
900
Suryakanth Sekard509eb92018-11-15 17:44:11 +0530901/** @brief implementation for check the DHCP or not in IPv4
902 * @param[in] Channel - Channel number
903 * @returns true or false.
904 */
905static bool isDHCPEnabled(uint8_t Channel)
906{
907 try
908 {
909 auto ethdevice = getChannelName(Channel);
910 if (ethdevice.empty())
911 {
912 return false;
913 }
914 auto ethIP = ethdevice + "/ipv4";
Vernon Mauery15419dd2019-05-24 09:40:30 -0700915 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
Suryakanth Sekard509eb92018-11-15 17:44:11 +0530916 auto ethernetObj =
Vernon Mauery15419dd2019-05-24 09:40:30 -0700917 getDbusObject(*dbus, networkIPIntf, networkRoot, ethIP);
918 auto value = getDbusProperty(*dbus, networkService, ethernetObj.first,
Suryakanth Sekard509eb92018-11-15 17:44:11 +0530919 networkIPIntf, "Origin");
Vernon Mauery8166c8d2019-05-23 11:22:30 -0700920 if (std::get<std::string>(value) ==
Suryakanth Sekard509eb92018-11-15 17:44:11 +0530921 "xyz.openbmc_project.Network.IP.AddressOrigin.DHCP")
922 {
923 return true;
924 }
925 else
926 {
927 return false;
928 }
929 }
930 catch (sdbusplus::exception_t& e)
931 {
932 phosphor::logging::log<phosphor::logging::level::ERR>(e.description());
933 return true;
934 }
935}
936
937/** @brief implementes for check the DHCP or not in IPv6
938 * @param[in] Channel - Channel number
939 * @returns true or false.
940 */
941static bool isDHCPIPv6Enabled(uint8_t Channel)
942{
943
944 try
945 {
946 auto ethdevice = getChannelName(Channel);
947 if (ethdevice.empty())
948 {
949 return false;
950 }
951 auto ethIP = ethdevice + "/ipv6";
Vernon Mauery15419dd2019-05-24 09:40:30 -0700952 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
Suryakanth Sekard509eb92018-11-15 17:44:11 +0530953 auto objectInfo =
Vernon Mauery15419dd2019-05-24 09:40:30 -0700954 getDbusObject(*dbus, networkIPIntf, networkRoot, ethIP);
955 auto properties = getAllDbusProperties(*dbus, objectInfo.second,
Suryakanth Sekard509eb92018-11-15 17:44:11 +0530956 objectInfo.first, networkIPIntf);
Vernon Mauery8166c8d2019-05-23 11:22:30 -0700957 if (std::get<std::string>(properties["Origin"]) ==
Suryakanth Sekard509eb92018-11-15 17:44:11 +0530958 "xyz.openbmc_project.Network.IP.AddressOrigin.DHCP")
959 {
960 return true;
961 }
962 else
963 {
964 return false;
965 }
966 }
967 catch (sdbusplus::exception_t& e)
968 {
969 phosphor::logging::log<phosphor::logging::level::ERR>(e.description());
970 return true;
971 }
972}
973
974/** @brief implementes the creating of default new user
975 * @param[in] userName - new username in 16 bytes.
976 * @param[in] userPassword - new password in 20 bytes
977 * @returns ipmi completion code.
978 */
979ipmi::RspType<> ipmiOEMSetUser2Activation(
980 std::array<uint8_t, ipmi::ipmiMaxUserName>& userName,
981 std::array<uint8_t, ipmi::maxIpmi20PasswordSize>& userPassword)
982{
983 bool userState = false;
984 // Check for System Interface not exist and LAN should be static
985 for (uint8_t channel = 0; channel < maxIpmiChannels; channel++)
986 {
987 ChannelInfo chInfo;
988 try
989 {
990 getChannelInfo(channel, chInfo);
991 }
992 catch (sdbusplus::exception_t& e)
993 {
994 phosphor::logging::log<phosphor::logging::level::ERR>(
995 "ipmiOEMSetUser2Activation: Failed to get Channel Info",
996 phosphor::logging::entry("MSG: %s", e.description()));
997 return ipmi::response(ipmi::ccUnspecifiedError);
998 }
999 if (chInfo.mediumType ==
1000 static_cast<uint8_t>(EChannelMediumType::systemInterface))
1001 {
1002 phosphor::logging::log<phosphor::logging::level::ERR>(
1003 "ipmiOEMSetUser2Activation: system interface exist .");
1004 return ipmi::response(ipmi::ccCommandNotAvailable);
1005 }
1006 else
1007 {
1008
1009 if (chInfo.mediumType ==
1010 static_cast<uint8_t>(EChannelMediumType::lan8032))
1011 {
1012 if (isDHCPIPv6Enabled(channel) || isDHCPEnabled(channel))
1013 {
1014 phosphor::logging::log<phosphor::logging::level::ERR>(
1015 "ipmiOEMSetUser2Activation: DHCP enabled .");
1016 return ipmi::response(ipmi::ccCommandNotAvailable);
1017 }
1018 }
1019 }
1020 }
1021 uint8_t maxChUsers = 0, enabledUsers = 0, fixedUsers = 0;
1022 if (ipmi::ccSuccess ==
1023 ipmiUserGetAllCounts(maxChUsers, enabledUsers, fixedUsers))
1024 {
1025 if (enabledUsers > 1)
1026 {
1027 phosphor::logging::log<phosphor::logging::level::ERR>(
1028 "ipmiOEMSetUser2Activation: more than one user is enabled.");
1029 return ipmi::response(ipmi::ccCommandNotAvailable);
1030 }
1031 // Check the user 2 is enabled or not
1032 ipmiUserCheckEnabled(ipmiDefaultUserId, userState);
1033 if (userState == true)
1034 {
1035 phosphor::logging::log<phosphor::logging::level::ERR>(
1036 "ipmiOEMSetUser2Activation: user 2 already enabled .");
1037 return ipmi::response(ipmi::ccCommandNotAvailable);
1038 }
1039 }
1040 else
1041 {
1042 return ipmi::response(ipmi::ccUnspecifiedError);
1043 }
1044
1045#if BYTE_ORDER == LITTLE_ENDIAN
1046 PrivAccess privAccess = {PRIVILEGE_ADMIN, true, true, true, 0};
1047#endif
1048#if BYTE_ORDER == BIG_ENDIAN
1049 PrivAccess privAccess = {0, true, true, true, PRIVILEGE_ADMIN};
1050#endif
1051
1052 if (ipmi::ccSuccess ==
1053 ipmiUserSetUserName(ipmiDefaultUserId,
1054 reinterpret_cast<const char*>(userName.data())))
1055 {
1056 if (ipmi::ccSuccess ==
1057 ipmiUserSetUserPassword(
1058 ipmiDefaultUserId,
1059 reinterpret_cast<const char*>(userPassword.data())))
1060 {
1061 if (ipmi::ccSuccess ==
1062 ipmiUserSetPrivilegeAccess(
1063 ipmiDefaultUserId,
1064 static_cast<uint8_t>(ipmi::EChannelID::chanLan1),
1065 privAccess, true))
1066 {
1067 phosphor::logging::log<phosphor::logging::level::INFO>(
1068 "ipmiOEMSetUser2Activation: user created successfully ");
1069 return ipmi::responseSuccess();
1070 }
1071 }
1072 // we need to delete the default user id which added in this command as
1073 // password / priv setting is failed.
1074 ipmiUserSetUserName(ipmiDefaultUserId, "");
1075 phosphor::logging::log<phosphor::logging::level::ERR>(
1076 "ipmiOEMSetUser2Activation: password / priv setting is failed.");
1077 }
1078 else
1079 {
1080 phosphor::logging::log<phosphor::logging::level::ERR>(
1081 "ipmiOEMSetUser2Activation: Setting username failed.");
1082 }
1083
1084 return ipmi::response(ipmi::ccCommandNotAvailable);
1085}
1086
Richard Marian Thomaiyarfc5e9852019-04-14 15:06:27 +05301087/** @brief implementes setting password for special user
1088 * @param[in] specialUserIndex
1089 * @param[in] userPassword - new password in 20 bytes
1090 * @returns ipmi completion code.
1091 */
1092ipmi::RspType<> ipmiOEMSetSpecialUserPassword(ipmi::Context::ptr ctx,
1093 uint8_t specialUserIndex,
1094 std::vector<uint8_t> userPassword)
1095{
1096 ChannelInfo chInfo;
1097 try
1098 {
1099 getChannelInfo(ctx->channel, chInfo);
1100 }
1101 catch (sdbusplus::exception_t& e)
1102 {
1103 phosphor::logging::log<phosphor::logging::level::ERR>(
1104 "ipmiOEMSetSpecialUserPassword: Failed to get Channel Info",
1105 phosphor::logging::entry("MSG: %s", e.description()));
1106 return ipmi::responseUnspecifiedError();
1107 }
1108 if (chInfo.mediumType !=
1109 static_cast<uint8_t>(EChannelMediumType::systemInterface))
1110 {
1111 phosphor::logging::log<phosphor::logging::level::ERR>(
1112 "ipmiOEMSetSpecialUserPassword: Error - supported only in KCS "
1113 "interface");
1114 return ipmi::responseCommandNotAvailable();
1115 }
1116 if (specialUserIndex != 0)
1117 {
1118 phosphor::logging::log<phosphor::logging::level::ERR>(
1119 "ipmiOEMSetSpecialUserPassword: Invalid user account");
1120 return ipmi::responseParmOutOfRange();
1121 }
1122 constexpr uint8_t minPasswordSizeRequired = 6;
1123 if (userPassword.size() < minPasswordSizeRequired ||
1124 userPassword.size() > ipmi::maxIpmi20PasswordSize)
1125 {
1126 return ipmi::responseReqDataLenInvalid();
1127 }
1128 std::string passwd;
1129 passwd.assign(reinterpret_cast<const char*>(userPassword.data()),
1130 userPassword.size());
1131 return ipmi::response(ipmiSetSpecialUserPassword("root", passwd));
1132}
1133
Kuiying Wang45f04982018-12-26 09:23:08 +08001134namespace ledAction
1135{
1136using namespace sdbusplus::xyz::openbmc_project::Led::server;
1137std::map<Physical::Action, uint8_t> actionDbusToIpmi = {
1138 {Physical::Action::Off, 0x00},
1139 {Physical::Action::On, 0x10},
1140 {Physical::Action::Blink, 0x01}};
1141
1142std::map<uint8_t, std::string> offsetObjPath = {
1143 {2, statusAmberObjPath}, {4, statusGreenObjPath}, {6, identifyLEDObjPath}};
1144
1145} // namespace ledAction
1146
1147int8_t getLEDState(sdbusplus::bus::bus& bus, const std::string& intf,
1148 const std::string& objPath, uint8_t& state)
1149{
1150 try
1151 {
1152 std::string service = getService(bus, intf, objPath);
1153 Value stateValue =
1154 getDbusProperty(bus, service, objPath, intf, "State");
Vernon Mauery8166c8d2019-05-23 11:22:30 -07001155 std::string strState = std::get<std::string>(stateValue);
Kuiying Wang45f04982018-12-26 09:23:08 +08001156 state = ledAction::actionDbusToIpmi.at(
1157 sdbusplus::xyz::openbmc_project::Led::server::Physical::
1158 convertActionFromString(strState));
1159 }
1160 catch (sdbusplus::exception::SdBusError& e)
1161 {
1162 phosphor::logging::log<phosphor::logging::level::ERR>(e.what());
1163 return -1;
1164 }
1165 return 0;
1166}
1167
1168ipmi_ret_t ipmiOEMGetLEDStatus(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
1169 ipmi_request_t request, ipmi_response_t response,
1170 ipmi_data_len_t dataLen, ipmi_context_t context)
1171{
1172 uint8_t* resp = reinterpret_cast<uint8_t*>(response);
1173 // LED Status
1174 //[1:0] = Reserved
1175 //[3:2] = Status(Amber)
1176 //[5:4] = Status(Green)
1177 //[7:6] = System Identify
1178 // Status definitions:
1179 // 00b = Off
1180 // 01b = Blink
1181 // 10b = On
1182 // 11b = invalid
1183 if (*dataLen != 0)
1184 {
1185 phosphor::logging::log<phosphor::logging::level::ERR>(
1186 "oem_get_led_status: invalid input len!");
1187 *dataLen = 0;
1188 return IPMI_CC_REQ_DATA_LEN_INVALID;
1189 }
1190
1191 phosphor::logging::log<phosphor::logging::level::DEBUG>("GET led status");
1192 *resp = 0;
1193 *dataLen = 0;
Vernon Mauery15419dd2019-05-24 09:40:30 -07001194 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
Kuiying Wang45f04982018-12-26 09:23:08 +08001195 for (auto it = ledAction::offsetObjPath.begin();
1196 it != ledAction::offsetObjPath.end(); ++it)
1197 {
1198 uint8_t state = 0;
Vernon Mauery15419dd2019-05-24 09:40:30 -07001199 if (-1 == getLEDState(*dbus, ledIntf, it->second, state))
Kuiying Wang45f04982018-12-26 09:23:08 +08001200 {
1201 phosphor::logging::log<phosphor::logging::level::ERR>(
1202 "oem_get_led_status: fail to get ID LED status!");
1203 return IPMI_CC_UNSPECIFIED_ERROR;
1204 }
1205 *resp |= state << it->first;
1206 }
1207
1208 *dataLen = sizeof(*resp);
1209 return IPMI_CC_OK;
1210}
1211
Yong Li23737fe2019-02-19 08:49:55 +08001212ipmi_ret_t ipmiOEMCfgHostSerialPortSpeed(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
1213 ipmi_request_t request,
1214 ipmi_response_t response,
1215 ipmi_data_len_t dataLen,
1216 ipmi_context_t context)
1217{
1218 CfgHostSerialReq* req = reinterpret_cast<CfgHostSerialReq*>(request);
1219 uint8_t* resp = reinterpret_cast<uint8_t*>(response);
1220
1221 if (*dataLen == 0)
1222 {
1223 phosphor::logging::log<phosphor::logging::level::ERR>(
1224 "CfgHostSerial: invalid input len!",
1225 phosphor::logging::entry("LEN=%d", *dataLen));
1226 return IPMI_CC_REQ_DATA_LEN_INVALID;
1227 }
1228
1229 switch (req->command)
1230 {
1231 case getHostSerialCfgCmd:
1232 {
1233 if (*dataLen != 1)
1234 {
1235 phosphor::logging::log<phosphor::logging::level::ERR>(
1236 "CfgHostSerial: invalid input len!");
1237 *dataLen = 0;
1238 return IPMI_CC_REQ_DATA_LEN_INVALID;
1239 }
1240
1241 *dataLen = 0;
1242
1243 boost::process::ipstream is;
1244 std::vector<std::string> data;
1245 std::string line;
1246 boost::process::child c1(fwGetEnvCmd, "-n", fwHostSerailCfgEnvName,
1247 boost::process::std_out > is);
1248
1249 while (c1.running() && std::getline(is, line) && !line.empty())
1250 {
1251 data.push_back(line);
1252 }
1253
1254 c1.wait();
1255 if (c1.exit_code())
1256 {
1257 phosphor::logging::log<phosphor::logging::level::ERR>(
1258 "CfgHostSerial:: error on execute",
1259 phosphor::logging::entry("EXECUTE=%s", fwSetEnvCmd));
1260 // Using the default value
1261 *resp = 0;
1262 }
1263 else
1264 {
1265 if (data.size() != 1)
1266 {
1267 phosphor::logging::log<phosphor::logging::level::ERR>(
1268 "CfgHostSerial:: error on read env");
1269 return IPMI_CC_UNSPECIFIED_ERROR;
1270 }
1271 try
1272 {
1273 unsigned long tmp = std::stoul(data[0]);
1274 if (tmp > std::numeric_limits<uint8_t>::max())
1275 {
1276 throw std::out_of_range("Out of range");
1277 }
1278 *resp = static_cast<uint8_t>(tmp);
1279 }
1280 catch (const std::invalid_argument& e)
1281 {
1282 phosphor::logging::log<phosphor::logging::level::ERR>(
1283 "invalid config ",
1284 phosphor::logging::entry("ERR=%s", e.what()));
1285 return IPMI_CC_UNSPECIFIED_ERROR;
1286 }
1287 catch (const std::out_of_range& e)
1288 {
1289 phosphor::logging::log<phosphor::logging::level::ERR>(
1290 "out_of_range config ",
1291 phosphor::logging::entry("ERR=%s", e.what()));
1292 return IPMI_CC_UNSPECIFIED_ERROR;
1293 }
1294 }
1295
1296 *dataLen = 1;
1297 break;
1298 }
1299 case setHostSerialCfgCmd:
1300 {
1301 if (*dataLen != sizeof(CfgHostSerialReq))
1302 {
1303 phosphor::logging::log<phosphor::logging::level::ERR>(
1304 "CfgHostSerial: invalid input len!");
1305 *dataLen = 0;
1306 return IPMI_CC_REQ_DATA_LEN_INVALID;
1307 }
1308
1309 *dataLen = 0;
1310
1311 if (req->parameter > HostSerialCfgParamMax)
1312 {
1313 phosphor::logging::log<phosphor::logging::level::ERR>(
1314 "CfgHostSerial: invalid input!");
1315 return IPMI_CC_INVALID_FIELD_REQUEST;
1316 }
1317
1318 boost::process::child c1(fwSetEnvCmd, fwHostSerailCfgEnvName,
1319 std::to_string(req->parameter));
1320
1321 c1.wait();
1322 if (c1.exit_code())
1323 {
1324 phosphor::logging::log<phosphor::logging::level::ERR>(
1325 "CfgHostSerial:: error on execute",
1326 phosphor::logging::entry("EXECUTE=%s", fwGetEnvCmd));
1327 return IPMI_CC_UNSPECIFIED_ERROR;
1328 }
1329 break;
1330 }
1331 default:
1332 phosphor::logging::log<phosphor::logging::level::ERR>(
1333 "CfgHostSerial: invalid input!");
1334 *dataLen = 0;
1335 return IPMI_CC_INVALID_FIELD_REQUEST;
1336 }
1337
1338 return IPMI_CC_OK;
1339}
1340
James Feist91244a62019-02-19 15:04:54 -08001341constexpr const char* thermalModeInterface =
1342 "xyz.openbmc_project.Control.ThermalMode";
1343constexpr const char* thermalModePath =
1344 "/xyz/openbmc_project/control/thermal_mode";
1345
1346bool getFanProfileInterface(
1347 sdbusplus::bus::bus& bus,
1348 boost::container::flat_map<
1349 std::string, std::variant<std::vector<std::string>, std::string>>& resp)
1350{
1351 auto call = bus.new_method_call(settingsBusName, thermalModePath, PROP_INTF,
1352 "GetAll");
1353 call.append(thermalModeInterface);
1354 try
1355 {
1356 auto data = bus.call(call);
1357 data.read(resp);
1358 }
1359 catch (sdbusplus::exception_t& e)
1360 {
1361 phosphor::logging::log<phosphor::logging::level::ERR>(
1362 "getFanProfileInterface: can't get thermal mode!",
1363 phosphor::logging::entry("ERR=%s", e.what()));
1364 return false;
1365 }
1366 return true;
1367}
1368
1369ipmi_ret_t ipmiOEMSetFanConfig(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
1370 ipmi_request_t request, ipmi_response_t response,
1371 ipmi_data_len_t dataLen, ipmi_context_t context)
1372{
1373
1374 if (*dataLen < 2 || *dataLen > 7)
1375 {
1376 phosphor::logging::log<phosphor::logging::level::ERR>(
1377 "ipmiOEMSetFanConfig: invalid input len!");
1378 *dataLen = 0;
1379 return IPMI_CC_REQ_DATA_LEN_INVALID;
1380 }
1381
1382 // todo: tell bios to only send first 2 bytes
1383
1384 SetFanConfigReq* req = reinterpret_cast<SetFanConfigReq*>(request);
1385 boost::container::flat_map<
1386 std::string, std::variant<std::vector<std::string>, std::string>>
1387 profileData;
Vernon Mauery15419dd2019-05-24 09:40:30 -07001388 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
1389 if (!getFanProfileInterface(*dbus, profileData))
James Feist91244a62019-02-19 15:04:54 -08001390 {
1391 return IPMI_CC_UNSPECIFIED_ERROR;
1392 }
1393
1394 std::vector<std::string>* supported =
1395 std::get_if<std::vector<std::string>>(&profileData["Supported"]);
1396 if (supported == nullptr)
1397 {
1398 return IPMI_CC_INVALID_FIELD_REQUEST;
1399 }
1400 std::string mode;
1401 if (req->flags &
1402 (1 << static_cast<uint8_t>(setFanProfileFlags::setPerfAcousMode)))
1403 {
1404 bool performanceMode =
1405 (req->flags & (1 << static_cast<uint8_t>(
1406 setFanProfileFlags::performAcousSelect))) > 0;
1407
1408 if (performanceMode)
1409 {
1410
1411 if (std::find(supported->begin(), supported->end(),
1412 "Performance") != supported->end())
1413 {
1414 mode = "Performance";
1415 }
1416 }
1417 else
1418 {
1419
1420 if (std::find(supported->begin(), supported->end(), "Acoustic") !=
1421 supported->end())
1422 {
1423 mode = "Acoustic";
1424 }
1425 }
1426 if (mode.empty())
1427 {
1428 return IPMI_CC_INVALID_FIELD_REQUEST;
1429 }
Vernon Mauery15419dd2019-05-24 09:40:30 -07001430 setDbusProperty(*dbus, settingsBusName, thermalModePath,
James Feist91244a62019-02-19 15:04:54 -08001431 thermalModeInterface, "Current", mode);
1432 }
1433
1434 return IPMI_CC_OK;
1435}
1436
James Feist5b693632019-07-09 09:06:09 -07001437ipmi::RspType<uint8_t, // profile support map
1438 uint8_t, // fan control profile enable
1439 uint8_t, // flags
1440 uint32_t // dimm presence bit map
1441 >
1442 ipmiOEMGetFanConfig(uint8_t dimmGroupId)
James Feist91244a62019-02-19 15:04:54 -08001443{
James Feist91244a62019-02-19 15:04:54 -08001444 boost::container::flat_map<
1445 std::string, std::variant<std::vector<std::string>, std::string>>
1446 profileData;
1447
Vernon Mauery15419dd2019-05-24 09:40:30 -07001448 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
1449 if (!getFanProfileInterface(*dbus, profileData))
James Feist91244a62019-02-19 15:04:54 -08001450 {
James Feist5b693632019-07-09 09:06:09 -07001451 return ipmi::responseResponseError();
James Feist91244a62019-02-19 15:04:54 -08001452 }
1453
1454 std::string* current = std::get_if<std::string>(&profileData["Current"]);
1455
1456 if (current == nullptr)
1457 {
1458 phosphor::logging::log<phosphor::logging::level::ERR>(
1459 "ipmiOEMGetFanConfig: can't get current mode!");
James Feist5b693632019-07-09 09:06:09 -07001460 return ipmi::responseResponseError();
James Feist91244a62019-02-19 15:04:54 -08001461 }
1462 bool performance = (*current == "Performance");
1463
James Feist5b693632019-07-09 09:06:09 -07001464 uint8_t flags = 0;
James Feist91244a62019-02-19 15:04:54 -08001465 if (performance)
1466 {
James Feist5b693632019-07-09 09:06:09 -07001467 flags |= 1 << 2;
James Feist91244a62019-02-19 15:04:54 -08001468 }
1469
James Feist5b693632019-07-09 09:06:09 -07001470 return ipmi::responseSuccess(0, 0, flags, 0);
James Feist91244a62019-02-19 15:04:54 -08001471}
James Feist5f957ca2019-03-14 15:33:55 -07001472constexpr const char* cfmLimitSettingPath =
1473 "/xyz/openbmc_project/control/cfm_limit";
1474constexpr const char* cfmLimitIface = "xyz.openbmc_project.Control.CFMLimit";
James Feistfaa4f222019-03-21 16:21:55 -07001475constexpr const size_t legacyExitAirSensorNumber = 0x2e;
James Feist09f6b602019-08-08 11:30:03 -07001476constexpr const size_t legacyPCHSensorNumber = 0x22;
1477constexpr const char* exitAirPathName = "Exit_Air";
1478constexpr const char* pchPathName = "SSB_Temp";
James Feistacc8a4e2019-04-02 14:23:57 -07001479constexpr const char* pidConfigurationIface =
1480 "xyz.openbmc_project.Configuration.Pid";
James Feistfaa4f222019-03-21 16:21:55 -07001481
James Feist09f6b602019-08-08 11:30:03 -07001482static std::string getConfigPath(const std::string& name)
James Feistfaa4f222019-03-21 16:21:55 -07001483{
Vernon Mauery15419dd2019-05-24 09:40:30 -07001484 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
James Feistfaa4f222019-03-21 16:21:55 -07001485 auto method =
Vernon Mauery15419dd2019-05-24 09:40:30 -07001486 dbus->new_method_call("xyz.openbmc_project.ObjectMapper",
1487 "/xyz/openbmc_project/object_mapper",
1488 "xyz.openbmc_project.ObjectMapper", "GetSubTree");
James Feistfaa4f222019-03-21 16:21:55 -07001489
James Feistacc8a4e2019-04-02 14:23:57 -07001490 method.append("/", 0, std::array<const char*, 1>{pidConfigurationIface});
James Feistfaa4f222019-03-21 16:21:55 -07001491 std::string path;
1492 GetSubTreeType resp;
1493 try
1494 {
Vernon Mauery15419dd2019-05-24 09:40:30 -07001495 auto reply = dbus->call(method);
James Feistfaa4f222019-03-21 16:21:55 -07001496 reply.read(resp);
1497 }
1498 catch (sdbusplus::exception_t&)
1499 {
1500 phosphor::logging::log<phosphor::logging::level::ERR>(
1501 "ipmiOEMGetFscParameter: mapper error");
1502 };
James Feist09f6b602019-08-08 11:30:03 -07001503 auto config =
1504 std::find_if(resp.begin(), resp.end(), [&name](const auto& pair) {
1505 return pair.first.find(name) != std::string::npos;
1506 });
James Feistfaa4f222019-03-21 16:21:55 -07001507 if (config != resp.end())
1508 {
1509 path = std::move(config->first);
1510 }
1511 return path;
1512}
James Feist5f957ca2019-03-14 15:33:55 -07001513
James Feistacc8a4e2019-04-02 14:23:57 -07001514// flat map to make alphabetical
1515static boost::container::flat_map<std::string, PropertyMap> getPidConfigs()
1516{
1517 boost::container::flat_map<std::string, PropertyMap> ret;
Vernon Mauery15419dd2019-05-24 09:40:30 -07001518 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
James Feistacc8a4e2019-04-02 14:23:57 -07001519 auto method =
Vernon Mauery15419dd2019-05-24 09:40:30 -07001520 dbus->new_method_call("xyz.openbmc_project.ObjectMapper",
1521 "/xyz/openbmc_project/object_mapper",
1522 "xyz.openbmc_project.ObjectMapper", "GetSubTree");
James Feistacc8a4e2019-04-02 14:23:57 -07001523
1524 method.append("/", 0, std::array<const char*, 1>{pidConfigurationIface});
1525 GetSubTreeType resp;
1526
1527 try
1528 {
Vernon Mauery15419dd2019-05-24 09:40:30 -07001529 auto reply = dbus->call(method);
James Feistacc8a4e2019-04-02 14:23:57 -07001530 reply.read(resp);
1531 }
1532 catch (sdbusplus::exception_t&)
1533 {
1534 phosphor::logging::log<phosphor::logging::level::ERR>(
1535 "getFanConfigPaths: mapper error");
1536 };
1537 for (const auto& [path, objects] : resp)
1538 {
1539 if (objects.empty())
1540 {
1541 continue; // should be impossible
1542 }
Zhu, Yungebe560b02019-04-21 21:19:21 -04001543
1544 try
1545 {
Vernon Mauery15419dd2019-05-24 09:40:30 -07001546 ret.emplace(path,
1547 getAllDbusProperties(*dbus, objects[0].first, path,
1548 pidConfigurationIface));
Zhu, Yungebe560b02019-04-21 21:19:21 -04001549 }
1550 catch (sdbusplus::exception_t& e)
1551 {
1552 phosphor::logging::log<phosphor::logging::level::ERR>(
1553 "getPidConfigs: can't get DbusProperties!",
1554 phosphor::logging::entry("ERR=%s", e.what()));
1555 }
James Feistacc8a4e2019-04-02 14:23:57 -07001556 }
1557 return ret;
1558}
1559
1560ipmi::RspType<uint8_t> ipmiOEMGetFanSpeedOffset(void)
1561{
1562 boost::container::flat_map<std::string, PropertyMap> data = getPidConfigs();
1563 if (data.empty())
1564 {
1565 return ipmi::responseResponseError();
1566 }
1567 uint8_t minOffset = std::numeric_limits<uint8_t>::max();
1568 for (const auto& [_, pid] : data)
1569 {
1570 auto findClass = pid.find("Class");
1571 if (findClass == pid.end())
1572 {
1573 phosphor::logging::log<phosphor::logging::level::ERR>(
1574 "ipmiOEMGetFscParameter: found illegal pid "
1575 "configurations");
1576 return ipmi::responseResponseError();
1577 }
1578 std::string type = std::get<std::string>(findClass->second);
1579 if (type == "fan")
1580 {
1581 auto findOutLimit = pid.find("OutLimitMin");
1582 if (findOutLimit == pid.end())
1583 {
1584 phosphor::logging::log<phosphor::logging::level::ERR>(
1585 "ipmiOEMGetFscParameter: found illegal pid "
1586 "configurations");
1587 return ipmi::responseResponseError();
1588 }
1589 // get the min out of all the offsets
1590 minOffset = std::min(
1591 minOffset,
1592 static_cast<uint8_t>(std::get<double>(findOutLimit->second)));
1593 }
1594 }
1595 if (minOffset == std::numeric_limits<uint8_t>::max())
1596 {
1597 phosphor::logging::log<phosphor::logging::level::ERR>(
1598 "ipmiOEMGetFscParameter: found no fan configurations!");
1599 return ipmi::responseResponseError();
1600 }
1601
1602 return ipmi::responseSuccess(minOffset);
1603}
1604
1605ipmi::RspType<> ipmiOEMSetFanSpeedOffset(uint8_t offset)
1606{
1607 boost::container::flat_map<std::string, PropertyMap> data = getPidConfigs();
1608 if (data.empty())
1609 {
1610
1611 phosphor::logging::log<phosphor::logging::level::ERR>(
1612 "ipmiOEMSetFanSpeedOffset: found no pid configurations!");
1613 return ipmi::responseResponseError();
1614 }
1615
Vernon Mauery15419dd2019-05-24 09:40:30 -07001616 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
James Feistacc8a4e2019-04-02 14:23:57 -07001617 bool found = false;
1618 for (const auto& [path, pid] : data)
1619 {
1620 auto findClass = pid.find("Class");
1621 if (findClass == pid.end())
1622 {
1623
1624 phosphor::logging::log<phosphor::logging::level::ERR>(
1625 "ipmiOEMSetFanSpeedOffset: found illegal pid "
1626 "configurations");
1627 return ipmi::responseResponseError();
1628 }
1629 std::string type = std::get<std::string>(findClass->second);
1630 if (type == "fan")
1631 {
1632 auto findOutLimit = pid.find("OutLimitMin");
1633 if (findOutLimit == pid.end())
1634 {
1635
1636 phosphor::logging::log<phosphor::logging::level::ERR>(
1637 "ipmiOEMSetFanSpeedOffset: found illegal pid "
1638 "configurations");
1639 return ipmi::responseResponseError();
1640 }
Vernon Mauery15419dd2019-05-24 09:40:30 -07001641 ipmi::setDbusProperty(*dbus, "xyz.openbmc_project.EntityManager",
James Feistacc8a4e2019-04-02 14:23:57 -07001642 path, pidConfigurationIface, "OutLimitMin",
1643 static_cast<double>(offset));
1644 found = true;
1645 }
1646 }
1647 if (!found)
1648 {
1649 phosphor::logging::log<phosphor::logging::level::ERR>(
1650 "ipmiOEMSetFanSpeedOffset: set no fan offsets");
1651 return ipmi::responseResponseError();
1652 }
1653
1654 return ipmi::responseSuccess();
1655}
1656
1657ipmi::RspType<> ipmiOEMSetFscParameter(uint8_t command, uint8_t param1,
1658 uint8_t param2)
James Feist5f957ca2019-03-14 15:33:55 -07001659{
1660 constexpr const size_t disableLimiting = 0x0;
1661
Vernon Mauery15419dd2019-05-24 09:40:30 -07001662 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
James Feistacc8a4e2019-04-02 14:23:57 -07001663 if (command == static_cast<uint8_t>(setFscParamFlags::tcontrol))
James Feist5f957ca2019-03-14 15:33:55 -07001664 {
James Feist09f6b602019-08-08 11:30:03 -07001665 std::string pathName;
James Feistacc8a4e2019-04-02 14:23:57 -07001666 if (param1 == legacyExitAirSensorNumber)
James Feistfaa4f222019-03-21 16:21:55 -07001667 {
James Feist09f6b602019-08-08 11:30:03 -07001668 pathName = exitAirPathName;
1669 }
1670 else if (param1 == legacyPCHSensorNumber)
1671 {
1672 pathName = pchPathName;
James Feistfaa4f222019-03-21 16:21:55 -07001673 }
1674 else
1675 {
James Feistacc8a4e2019-04-02 14:23:57 -07001676 return ipmi::responseParmOutOfRange();
James Feistfaa4f222019-03-21 16:21:55 -07001677 }
James Feist09f6b602019-08-08 11:30:03 -07001678 std::string path = getConfigPath(pathName);
1679 ipmi::setDbusProperty(*dbus, "xyz.openbmc_project.EntityManager", path,
1680 pidConfigurationIface, "SetPoint",
1681 static_cast<double>(param2));
1682 return ipmi::responseSuccess();
James Feistfaa4f222019-03-21 16:21:55 -07001683 }
James Feistacc8a4e2019-04-02 14:23:57 -07001684 else if (command == static_cast<uint8_t>(setFscParamFlags::cfm))
James Feist5f957ca2019-03-14 15:33:55 -07001685 {
James Feistacc8a4e2019-04-02 14:23:57 -07001686 uint16_t cfm = param1 | (static_cast<uint16_t>(param2) << 8);
James Feist5f957ca2019-03-14 15:33:55 -07001687
1688 // must be greater than 50 based on eps
1689 if (cfm < 50 && cfm != disableLimiting)
1690 {
James Feistacc8a4e2019-04-02 14:23:57 -07001691 return ipmi::responseParmOutOfRange();
James Feist5f957ca2019-03-14 15:33:55 -07001692 }
1693
1694 try
1695 {
Vernon Mauery15419dd2019-05-24 09:40:30 -07001696 ipmi::setDbusProperty(*dbus, settingsBusName, cfmLimitSettingPath,
James Feist5f957ca2019-03-14 15:33:55 -07001697 cfmLimitIface, "Limit",
1698 static_cast<double>(cfm));
1699 }
1700 catch (sdbusplus::exception_t& e)
1701 {
1702 phosphor::logging::log<phosphor::logging::level::ERR>(
1703 "ipmiOEMSetFscParameter: can't set cfm setting!",
1704 phosphor::logging::entry("ERR=%s", e.what()));
James Feistacc8a4e2019-04-02 14:23:57 -07001705 return ipmi::responseResponseError();
James Feist5f957ca2019-03-14 15:33:55 -07001706 }
James Feistacc8a4e2019-04-02 14:23:57 -07001707 return ipmi::responseSuccess();
1708 }
1709 else if (command == static_cast<uint8_t>(setFscParamFlags::maxPwm))
1710 {
1711 constexpr const size_t maxDomainCount = 8;
1712 uint8_t requestedDomainMask = param1;
1713 boost::container::flat_map data = getPidConfigs();
1714 if (data.empty())
1715 {
1716
1717 phosphor::logging::log<phosphor::logging::level::ERR>(
1718 "ipmiOEMSetFscParameter: found no pid configurations!");
1719 return ipmi::responseResponseError();
1720 }
1721 size_t count = 0;
1722 for (const auto& [path, pid] : data)
1723 {
1724 auto findClass = pid.find("Class");
1725 if (findClass == pid.end())
1726 {
1727
1728 phosphor::logging::log<phosphor::logging::level::ERR>(
1729 "ipmiOEMSetFscParameter: found illegal pid "
1730 "configurations");
1731 return ipmi::responseResponseError();
1732 }
1733 std::string type = std::get<std::string>(findClass->second);
1734 if (type == "fan")
1735 {
1736 if (requestedDomainMask & (1 << count))
1737 {
1738 ipmi::setDbusProperty(
Vernon Mauery15419dd2019-05-24 09:40:30 -07001739 *dbus, "xyz.openbmc_project.EntityManager", path,
James Feistacc8a4e2019-04-02 14:23:57 -07001740 pidConfigurationIface, "OutLimitMax",
1741 static_cast<double>(param2));
1742 }
1743 count++;
1744 }
1745 }
1746 return ipmi::responseSuccess();
James Feist5f957ca2019-03-14 15:33:55 -07001747 }
1748 else
1749 {
1750 // todo other command parts possibly
1751 // tcontrol is handled in peci now
1752 // fan speed offset not implemented yet
1753 // domain pwm limit not implemented
James Feistacc8a4e2019-04-02 14:23:57 -07001754 return ipmi::responseParmOutOfRange();
James Feist5f957ca2019-03-14 15:33:55 -07001755 }
1756}
1757
James Feistacc8a4e2019-04-02 14:23:57 -07001758ipmi::RspType<
1759 std::variant<uint8_t, std::array<uint8_t, 2>, std::array<uint16_t, 2>>>
1760 ipmiOEMGetFscParameter(uint8_t command, std::optional<uint8_t> param)
James Feist5f957ca2019-03-14 15:33:55 -07001761{
James Feist09f6b602019-08-08 11:30:03 -07001762 constexpr uint8_t legacyDefaultSetpoint = -128;
James Feist5f957ca2019-03-14 15:33:55 -07001763
Vernon Mauery15419dd2019-05-24 09:40:30 -07001764 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
James Feistacc8a4e2019-04-02 14:23:57 -07001765 if (command == static_cast<uint8_t>(setFscParamFlags::tcontrol))
James Feist5f957ca2019-03-14 15:33:55 -07001766 {
James Feistacc8a4e2019-04-02 14:23:57 -07001767 if (!param)
James Feistfaa4f222019-03-21 16:21:55 -07001768 {
James Feistacc8a4e2019-04-02 14:23:57 -07001769 return ipmi::responseReqDataLenInvalid();
James Feistfaa4f222019-03-21 16:21:55 -07001770 }
1771
James Feist09f6b602019-08-08 11:30:03 -07001772 std::string pathName;
1773
1774 if (*param == legacyExitAirSensorNumber)
1775 {
1776 pathName = exitAirPathName;
1777 }
1778 else if (*param == legacyPCHSensorNumber)
1779 {
1780 pathName = pchPathName;
1781 }
1782 else
James Feistfaa4f222019-03-21 16:21:55 -07001783 {
James Feistacc8a4e2019-04-02 14:23:57 -07001784 return ipmi::responseParmOutOfRange();
James Feistfaa4f222019-03-21 16:21:55 -07001785 }
James Feist09f6b602019-08-08 11:30:03 -07001786
1787 uint8_t setpoint = legacyDefaultSetpoint;
1788 std::string path = getConfigPath(pathName);
James Feistfaa4f222019-03-21 16:21:55 -07001789 if (path.size())
1790 {
Vernon Mauery15419dd2019-05-24 09:40:30 -07001791 Value val = ipmi::getDbusProperty(
1792 *dbus, "xyz.openbmc_project.EntityManager", path,
1793 pidConfigurationIface, "SetPoint");
James Feistfaa4f222019-03-21 16:21:55 -07001794 setpoint = std::floor(std::get<double>(val) + 0.5);
1795 }
1796
1797 // old implementation used to return the "default" and current, we
1798 // don't make the default readily available so just make both the
1799 // same
James Feistfaa4f222019-03-21 16:21:55 -07001800
James Feistacc8a4e2019-04-02 14:23:57 -07001801 return ipmi::responseSuccess(
1802 std::array<uint8_t, 2>{setpoint, setpoint});
James Feistfaa4f222019-03-21 16:21:55 -07001803 }
James Feistacc8a4e2019-04-02 14:23:57 -07001804 else if (command == static_cast<uint8_t>(setFscParamFlags::maxPwm))
1805 {
1806 constexpr const size_t maxDomainCount = 8;
1807
1808 if (!param)
1809 {
1810 return ipmi::responseReqDataLenInvalid();
1811 }
1812 uint8_t requestedDomain = *param;
1813 if (requestedDomain >= maxDomainCount)
1814 {
1815 return ipmi::responseInvalidFieldRequest();
1816 }
1817
1818 boost::container::flat_map data = getPidConfigs();
1819 if (data.empty())
1820 {
1821 phosphor::logging::log<phosphor::logging::level::ERR>(
1822 "ipmiOEMGetFscParameter: found no pid configurations!");
1823 return ipmi::responseResponseError();
1824 }
1825 size_t count = 0;
1826 for (const auto& [_, pid] : data)
1827 {
1828 auto findClass = pid.find("Class");
1829 if (findClass == pid.end())
1830 {
1831 phosphor::logging::log<phosphor::logging::level::ERR>(
1832 "ipmiOEMGetFscParameter: found illegal pid "
1833 "configurations");
1834 return ipmi::responseResponseError();
1835 }
1836 std::string type = std::get<std::string>(findClass->second);
1837 if (type == "fan")
1838 {
1839 if (requestedDomain == count)
1840 {
1841 auto findOutLimit = pid.find("OutLimitMax");
1842 if (findOutLimit == pid.end())
1843 {
1844 phosphor::logging::log<phosphor::logging::level::ERR>(
1845 "ipmiOEMGetFscParameter: found illegal pid "
1846 "configurations");
1847 return ipmi::responseResponseError();
1848 }
1849
1850 return ipmi::responseSuccess(
1851 static_cast<uint8_t>(std::floor(
1852 std::get<double>(findOutLimit->second) + 0.5)));
1853 }
1854 else
1855 {
1856 count++;
1857 }
1858 }
1859 }
1860
1861 return ipmi::responseInvalidFieldRequest();
1862 }
1863 else if (command == static_cast<uint8_t>(setFscParamFlags::cfm))
James Feist5f957ca2019-03-14 15:33:55 -07001864 {
1865
1866 /*
1867 DataLen should be 1, but host is sending us an extra bit. As the
James Feistacc8a4e2019-04-02 14:23:57 -07001868 previous behavior didn't seem to prevent this, ignore the check for
1869 now.
James Feist5f957ca2019-03-14 15:33:55 -07001870
James Feistacc8a4e2019-04-02 14:23:57 -07001871 if (param)
James Feist5f957ca2019-03-14 15:33:55 -07001872 {
1873 phosphor::logging::log<phosphor::logging::level::ERR>(
1874 "ipmiOEMGetFscParameter: invalid input len!");
James Feist5f957ca2019-03-14 15:33:55 -07001875 return IPMI_CC_REQ_DATA_LEN_INVALID;
1876 }
1877 */
1878 Value cfmLimit;
1879 Value cfmMaximum;
1880 try
1881 {
Vernon Mauery15419dd2019-05-24 09:40:30 -07001882 cfmLimit = ipmi::getDbusProperty(*dbus, settingsBusName,
James Feist5f957ca2019-03-14 15:33:55 -07001883 cfmLimitSettingPath, cfmLimitIface,
1884 "Limit");
1885 cfmMaximum = ipmi::getDbusProperty(
Vernon Mauery15419dd2019-05-24 09:40:30 -07001886 *dbus, "xyz.openbmc_project.ExitAirTempSensor",
James Feist5f957ca2019-03-14 15:33:55 -07001887 "/xyz/openbmc_project/control/MaxCFM", cfmLimitIface, "Limit");
1888 }
1889 catch (sdbusplus::exception_t& e)
1890 {
1891 phosphor::logging::log<phosphor::logging::level::ERR>(
James Feistacc8a4e2019-04-02 14:23:57 -07001892 "ipmiOEMGetFscParameter: can't get cfm setting!",
James Feist5f957ca2019-03-14 15:33:55 -07001893 phosphor::logging::entry("ERR=%s", e.what()));
James Feistacc8a4e2019-04-02 14:23:57 -07001894 return ipmi::responseResponseError();
James Feist5f957ca2019-03-14 15:33:55 -07001895 }
1896
James Feistacc8a4e2019-04-02 14:23:57 -07001897 double cfmMax = std::get<double>(cfmMaximum);
1898 double cfmLim = std::get<double>(cfmLimit);
James Feist5f957ca2019-03-14 15:33:55 -07001899
James Feistacc8a4e2019-04-02 14:23:57 -07001900 cfmLim = std::floor(cfmLim + 0.5);
1901 cfmMax = std::floor(cfmMax + 0.5);
1902 uint16_t cfmLimResp = static_cast<uint16_t>(cfmLim);
1903 uint16_t cfmMaxResp = static_cast<uint16_t>(cfmMax);
James Feist5f957ca2019-03-14 15:33:55 -07001904
James Feistacc8a4e2019-04-02 14:23:57 -07001905 return ipmi::responseSuccess(
1906 std::array<uint16_t, 2>{cfmLimResp, cfmMaxResp});
James Feist5f957ca2019-03-14 15:33:55 -07001907 }
James Feistacc8a4e2019-04-02 14:23:57 -07001908
James Feist5f957ca2019-03-14 15:33:55 -07001909 else
1910 {
1911 // todo other command parts possibly
James Feist5f957ca2019-03-14 15:33:55 -07001912 // domain pwm limit not implemented
James Feistacc8a4e2019-04-02 14:23:57 -07001913 return ipmi::responseParmOutOfRange();
James Feist5f957ca2019-03-14 15:33:55 -07001914 }
1915}
1916
Cheng C Yang773703a2019-08-15 09:41:11 +08001917using crConfigVariant =
1918 std::variant<bool, uint8_t, uint32_t, std::vector<uint8_t>, std::string>;
1919
1920int setCRConfig(ipmi::Context::ptr ctx, const std::string& property,
1921 const crConfigVariant& value,
1922 std::chrono::microseconds timeout = ipmi::IPMI_DBUS_TIMEOUT)
1923{
1924 boost::system::error_code ec;
1925 ctx->bus->yield_method_call<void>(
James Feist28c72902019-09-16 10:34:07 -07001926 ctx->yield, ec, "xyz.openbmc_project.Settings",
Cheng C Yang773703a2019-08-15 09:41:11 +08001927 "/xyz/openbmc_project/control/power_supply_redundancy",
1928 "org.freedesktop.DBus.Properties", "Set",
1929 "xyz.openbmc_project.Control.PowerSupplyRedundancy", property, value);
1930 if (ec)
1931 {
1932 phosphor::logging::log<phosphor::logging::level::ERR>(
1933 "Failed to set dbus property to cold redundancy");
1934 return -1;
1935 }
1936
1937 return 0;
1938}
1939
1940int getCRConfig(ipmi::Context::ptr ctx, const std::string& property,
1941 crConfigVariant& value,
1942 std::chrono::microseconds timeout = ipmi::IPMI_DBUS_TIMEOUT)
1943{
1944 boost::system::error_code ec;
1945 value = ctx->bus->yield_method_call<crConfigVariant>(
James Feist28c72902019-09-16 10:34:07 -07001946 ctx->yield, ec, "xyz.openbmc_project.Settings",
Cheng C Yang773703a2019-08-15 09:41:11 +08001947 "/xyz/openbmc_project/control/power_supply_redundancy",
1948 "org.freedesktop.DBus.Properties", "Get",
1949 "xyz.openbmc_project.Control.PowerSupplyRedundancy", property);
1950 if (ec)
1951 {
1952 phosphor::logging::log<phosphor::logging::level::ERR>(
1953 "Failed to get dbus property to cold redundancy");
1954 return -1;
1955 }
1956 return 0;
1957}
1958
1959uint8_t getPSUCount(void)
1960{
1961 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
1962 ipmi::Value num;
1963 try
1964 {
1965 num = ipmi::getDbusProperty(
1966 *dbus, "xyz.openbmc_project.PSURedundancy",
1967 "/xyz/openbmc_project/control/power_supply_redundancy",
1968 "xyz.openbmc_project.Control.PowerSupplyRedundancy", "PSUNumber");
1969 }
1970 catch (sdbusplus::exception_t& e)
1971 {
1972 phosphor::logging::log<phosphor::logging::level::ERR>(
1973 "Failed to get PSUNumber property from dbus interface");
1974 return 0;
1975 }
1976 uint8_t* pNum = std::get_if<uint8_t>(&num);
1977 if (!pNum)
1978 {
1979 phosphor::logging::log<phosphor::logging::level::ERR>(
1980 "Error to get PSU Number");
1981 return 0;
1982 }
1983 return *pNum;
1984}
1985
1986bool validateCRAlgo(std::vector<uint8_t>& conf, uint8_t num)
1987{
1988 if (conf.size() < num)
1989 {
1990 phosphor::logging::log<phosphor::logging::level::ERR>(
1991 "Invalid PSU Ranking");
1992 return false;
1993 }
1994 std::set<uint8_t> confSet;
1995 for (uint8_t i = 0; i < num; i++)
1996 {
1997 if (conf[i] > num)
1998 {
1999 phosphor::logging::log<phosphor::logging::level::ERR>(
2000 "PSU Ranking is larger than current PSU number");
2001 return false;
2002 }
2003 confSet.emplace(conf[i]);
2004 }
2005
2006 if (confSet.size() != num)
2007 {
2008 phosphor::logging::log<phosphor::logging::level::ERR>(
2009 "duplicate PSU Ranking");
2010 return false;
2011 }
2012 return true;
2013}
2014
2015enum class crParameter
2016{
2017 crStatus = 0,
2018 crFeature = 1,
2019 rotationFeature = 2,
2020 rotationAlgo = 3,
2021 rotationPeriod = 4,
2022 numOfPSU = 5
2023};
2024
2025constexpr ipmi::Cc ccParameterNotSupported = 0x80;
2026static const constexpr uint32_t oneDay = 0x15180;
2027static const constexpr uint32_t oneMonth = 0xf53700;
2028static const constexpr uint8_t userSpecific = 0x01;
2029static const constexpr uint8_t crSetCompleted = 0;
2030ipmi::RspType<uint8_t> ipmiOEMSetCRConfig(ipmi::Context::ptr ctx,
2031 uint8_t parameter,
2032 ipmi::message::Payload& payload)
2033{
2034 switch (static_cast<crParameter>(parameter))
2035 {
2036 case crParameter::crFeature:
2037 {
2038 uint8_t param1;
2039 if (payload.unpack(param1) || !payload.fullyUnpacked())
2040 {
2041 return ipmi::responseReqDataLenInvalid();
2042 }
2043 // ColdRedundancy Enable can only be true or flase
2044 if (param1 > 1)
2045 {
2046 return ipmi::responseInvalidFieldRequest();
2047 }
2048 if (setCRConfig(ctx, "ColdRedundancyEnabled",
2049 static_cast<bool>(param1)))
2050 {
2051 return ipmi::responseResponseError();
2052 }
2053 break;
2054 }
2055 case crParameter::rotationFeature:
2056 {
2057 uint8_t param1;
2058 if (payload.unpack(param1) || !payload.fullyUnpacked())
2059 {
2060 return ipmi::responseReqDataLenInvalid();
2061 }
2062 // Rotation Enable can only be true or false
2063 if (param1 > 1)
2064 {
2065 return ipmi::responseInvalidFieldRequest();
2066 }
2067 if (setCRConfig(ctx, "RotationEnabled", static_cast<bool>(param1)))
2068 {
2069 return ipmi::responseResponseError();
2070 }
2071 break;
2072 }
2073 case crParameter::rotationAlgo:
2074 {
2075 // Rotation Algorithm can only be 0-BMC Specific or 1-User Specific
2076 std::string algoName;
2077 uint8_t param1;
2078 if (payload.unpack(param1))
2079 {
2080 return ipmi::responseReqDataLenInvalid();
2081 }
2082 switch (param1)
2083 {
2084 case 0:
2085 algoName = "xyz.openbmc_project.Control."
2086 "PowerSupplyRedundancy.Algo.bmcSpecific";
2087 break;
2088 case 1:
2089 algoName = "xyz.openbmc_project.Control."
2090 "PowerSupplyRedundancy.Algo.userSpecific";
2091 break;
2092 default:
2093 return ipmi::responseInvalidFieldRequest();
2094 }
2095 if (setCRConfig(ctx, "RotationAlgorithm", algoName))
2096 {
2097 return ipmi::responseResponseError();
2098 }
2099
2100 uint8_t numberOfPSU = getPSUCount();
2101 if (!numberOfPSU)
2102 {
2103 return ipmi::responseResponseError();
2104 }
2105 std::vector<uint8_t> rankOrder;
2106
2107 if (param1 == userSpecific)
2108 {
2109 if (payload.unpack(rankOrder) || !payload.fullyUnpacked())
2110 {
2111 ipmi::responseReqDataLenInvalid();
2112 }
Yong Li83315132019-10-23 17:42:24 +08002113 if (rankOrder.size() != numberOfPSU)
Cheng C Yang773703a2019-08-15 09:41:11 +08002114 {
2115 return ipmi::responseReqDataLenInvalid();
2116 }
2117
2118 if (!validateCRAlgo(rankOrder, numberOfPSU))
2119 {
2120 return ipmi::responseInvalidFieldRequest();
2121 }
2122 }
2123 else
2124 {
2125 if (rankOrder.size() > 0)
2126 {
2127 return ipmi::responseReqDataLenInvalid();
2128 }
2129 for (uint8_t i = 1; i <= numberOfPSU; i++)
2130 {
2131 rankOrder.emplace_back(i);
2132 }
2133 }
2134 if (setCRConfig(ctx, "RotationRankOrder", rankOrder))
2135 {
2136 return ipmi::responseResponseError();
2137 }
2138 break;
2139 }
2140 case crParameter::rotationPeriod:
2141 {
2142 // Minimum Rotation period is One day (86400 seconds) and Max
2143 // Rotation Period is 6 month (0xf53700 seconds)
2144 uint32_t period;
2145 if (payload.unpack(period) || !payload.fullyUnpacked())
2146 {
2147 return ipmi::responseReqDataLenInvalid();
2148 }
2149 if ((period < oneDay) || (period > oneMonth))
2150 {
2151 return ipmi::responseInvalidFieldRequest();
2152 }
2153 if (setCRConfig(ctx, "PeriodOfRotation", period))
2154 {
2155 return ipmi::responseResponseError();
2156 }
2157 break;
2158 }
2159 default:
2160 {
2161 return ipmi::response(ccParameterNotSupported);
2162 }
2163 }
2164
2165 // TODO Halfwidth needs to set SetInProgress
2166 if (setCRConfig(ctx, "ColdRedundancyStatus",
Cheng C Yange8cecdf2019-08-26 23:48:08 +08002167 std::string("xyz.openbmc_project.Control."
2168 "PowerSupplyRedundancy.Status.completed")))
Cheng C Yang773703a2019-08-15 09:41:11 +08002169 {
2170 return ipmi::responseResponseError();
2171 }
2172 return ipmi::responseSuccess(crSetCompleted);
2173}
2174
Yong Li83315132019-10-23 17:42:24 +08002175ipmi::RspType<uint8_t, std::variant<uint8_t, uint32_t, std::vector<uint8_t>>>
Cheng C Yang773703a2019-08-15 09:41:11 +08002176 ipmiOEMGetCRConfig(ipmi::Context::ptr ctx, uint8_t parameter)
2177{
2178 crConfigVariant value;
2179 switch (static_cast<crParameter>(parameter))
2180 {
2181 case crParameter::crStatus:
2182 {
2183 if (getCRConfig(ctx, "ColdRedundancyStatus", value))
2184 {
2185 return ipmi::responseResponseError();
2186 }
2187 std::string* pStatus = std::get_if<std::string>(&value);
2188 if (!pStatus)
2189 {
2190 phosphor::logging::log<phosphor::logging::level::ERR>(
2191 "Error to get ColdRedundancyStatus property");
2192 return ipmi::responseResponseError();
2193 }
2194 namespace server = sdbusplus::xyz::openbmc_project::Control::server;
2195 auto status =
2196 server::PowerSupplyRedundancy::convertStatusFromString(
2197 *pStatus);
2198 switch (status)
2199 {
2200 case server::PowerSupplyRedundancy::Status::inProgress:
Cheng C Yangf41e3342019-09-10 04:47:23 +08002201 return ipmi::responseSuccess(parameter,
2202 static_cast<uint8_t>(0));
Cheng C Yang773703a2019-08-15 09:41:11 +08002203
2204 case server::PowerSupplyRedundancy::Status::completed:
Cheng C Yangf41e3342019-09-10 04:47:23 +08002205 return ipmi::responseSuccess(parameter,
2206 static_cast<uint8_t>(1));
Cheng C Yang773703a2019-08-15 09:41:11 +08002207 default:
2208 phosphor::logging::log<phosphor::logging::level::ERR>(
2209 "Error to get valid status");
2210 return ipmi::responseResponseError();
2211 }
2212 }
2213 case crParameter::crFeature:
2214 {
2215 if (getCRConfig(ctx, "ColdRedundancyEnabled", value))
2216 {
2217 return ipmi::responseResponseError();
2218 }
2219 bool* pResponse = std::get_if<bool>(&value);
2220 if (!pResponse)
2221 {
2222 phosphor::logging::log<phosphor::logging::level::ERR>(
2223 "Error to get ColdRedundancyEnable property");
2224 return ipmi::responseResponseError();
2225 }
2226
Cheng C Yangf41e3342019-09-10 04:47:23 +08002227 return ipmi::responseSuccess(parameter,
2228 static_cast<uint8_t>(*pResponse));
Cheng C Yang773703a2019-08-15 09:41:11 +08002229 }
2230 case crParameter::rotationFeature:
2231 {
2232 if (getCRConfig(ctx, "RotationEnabled", value))
2233 {
2234 return ipmi::responseResponseError();
2235 }
2236 bool* pResponse = std::get_if<bool>(&value);
2237 if (!pResponse)
2238 {
2239 phosphor::logging::log<phosphor::logging::level::ERR>(
2240 "Error to get RotationEnabled property");
2241 return ipmi::responseResponseError();
2242 }
Cheng C Yangf41e3342019-09-10 04:47:23 +08002243 return ipmi::responseSuccess(parameter,
2244 static_cast<uint8_t>(*pResponse));
Cheng C Yang773703a2019-08-15 09:41:11 +08002245 }
2246 case crParameter::rotationAlgo:
2247 {
2248 if (getCRConfig(ctx, "RotationAlgorithm", value))
2249 {
2250 return ipmi::responseResponseError();
2251 }
2252
2253 std::string* pAlgo = std::get_if<std::string>(&value);
2254 if (!pAlgo)
2255 {
2256 phosphor::logging::log<phosphor::logging::level::ERR>(
2257 "Error to get RotationAlgorithm property");
2258 return ipmi::responseResponseError();
2259 }
Yong Li83315132019-10-23 17:42:24 +08002260 std::vector<uint8_t> response;
Cheng C Yang773703a2019-08-15 09:41:11 +08002261 namespace server = sdbusplus::xyz::openbmc_project::Control::server;
2262 auto algo =
2263 server::PowerSupplyRedundancy::convertAlgoFromString(*pAlgo);
Yong Li83315132019-10-23 17:42:24 +08002264
Cheng C Yang773703a2019-08-15 09:41:11 +08002265 switch (algo)
2266 {
2267 case server::PowerSupplyRedundancy::Algo::bmcSpecific:
Yong Li83315132019-10-23 17:42:24 +08002268 response.push_back(0);
Cheng C Yang773703a2019-08-15 09:41:11 +08002269 break;
2270 case server::PowerSupplyRedundancy::Algo::userSpecific:
Yong Li83315132019-10-23 17:42:24 +08002271 response.push_back(1);
Cheng C Yang773703a2019-08-15 09:41:11 +08002272 break;
2273 default:
2274 phosphor::logging::log<phosphor::logging::level::ERR>(
2275 "Error to get valid algo");
2276 return ipmi::responseResponseError();
2277 }
2278
2279 if (getCRConfig(ctx, "RotationRankOrder", value))
2280 {
2281 return ipmi::responseResponseError();
2282 }
2283 std::vector<uint8_t>* pResponse =
2284 std::get_if<std::vector<uint8_t>>(&value);
2285 if (!pResponse)
2286 {
2287 phosphor::logging::log<phosphor::logging::level::ERR>(
2288 "Error to get RotationRankOrder property");
2289 return ipmi::responseResponseError();
2290 }
Yong Li83315132019-10-23 17:42:24 +08002291
Cheng C Yang773703a2019-08-15 09:41:11 +08002292 std::copy(pResponse->begin(), pResponse->end(),
Yong Li83315132019-10-23 17:42:24 +08002293 std::back_inserter(response));
2294
Cheng C Yangf41e3342019-09-10 04:47:23 +08002295 return ipmi::responseSuccess(parameter, response);
Cheng C Yang773703a2019-08-15 09:41:11 +08002296 }
2297 case crParameter::rotationPeriod:
2298 {
2299 if (getCRConfig(ctx, "PeriodOfRotation", value))
2300 {
2301 return ipmi::responseResponseError();
2302 }
2303 uint32_t* pResponse = std::get_if<uint32_t>(&value);
2304 if (!pResponse)
2305 {
2306 phosphor::logging::log<phosphor::logging::level::ERR>(
2307 "Error to get RotationAlgorithm property");
2308 return ipmi::responseResponseError();
2309 }
Cheng C Yangf41e3342019-09-10 04:47:23 +08002310 return ipmi::responseSuccess(parameter, *pResponse);
Cheng C Yang773703a2019-08-15 09:41:11 +08002311 }
2312 case crParameter::numOfPSU:
2313 {
2314 uint8_t numberOfPSU = getPSUCount();
2315 if (!numberOfPSU)
2316 {
2317 return ipmi::responseResponseError();
2318 }
Cheng C Yangf41e3342019-09-10 04:47:23 +08002319 return ipmi::responseSuccess(parameter, numberOfPSU);
Cheng C Yang773703a2019-08-15 09:41:11 +08002320 }
2321 default:
2322 {
2323 return ipmi::response(ccParameterNotSupported);
2324 }
2325 }
2326}
2327
Zhu, Yungebe560b02019-04-21 21:19:21 -04002328ipmi::RspType<> ipmiOEMSetFaultIndication(uint8_t sourceId, uint8_t faultType,
2329 uint8_t faultState,
2330 uint8_t faultGroup,
2331 std::array<uint8_t, 8>& ledStateData)
2332{
2333 static constexpr const char* objpath = "/xyz/openbmc_project/EntityManager";
2334 static constexpr const char* intf = "xyz.openbmc_project.EntityManager";
2335 constexpr auto maxFaultType = static_cast<size_t>(RemoteFaultType::max);
2336 static const std::array<std::string, maxFaultType> faultNames = {
2337 "faultFan", "faultTemp", "faultPower",
2338 "faultDriveSlot", "faultSoftware", "faultMemory"};
2339 static constexpr const char* sysGpioPath = "/sys/class/gpio/gpio";
2340 static constexpr const char* postfixValue = "/value";
2341
2342 constexpr uint8_t maxFaultSource = 0x4;
2343 constexpr uint8_t skipLEDs = 0xFF;
2344 constexpr uint8_t pinSize = 64;
2345 constexpr uint8_t groupSize = 16;
2346
2347 std::vector<uint16_t> ledFaultPins(pinSize, 0xFFFF);
2348 uint64_t resFIndex = 0;
2349 std::string resFType;
2350 std::string service;
2351 ObjectValueTree valueTree;
2352
2353 // Validate the source, fault type
2354 if ((sourceId >= maxFaultSource) ||
2355 (faultType >= static_cast<int8_t>(RemoteFaultType::max)) ||
2356 (faultState >= static_cast<int8_t>(RemoteFaultState::maxFaultState)) ||
2357 (faultGroup >= static_cast<int8_t>(DimmFaultType::maxFaultGroup)))
2358 {
2359 return ipmi::responseParmOutOfRange();
2360 }
2361
Vernon Mauery15419dd2019-05-24 09:40:30 -07002362 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
Zhu, Yungebe560b02019-04-21 21:19:21 -04002363 try
2364 {
Vernon Mauery15419dd2019-05-24 09:40:30 -07002365 service = getService(*dbus, intf, objpath);
2366 valueTree = getManagedObjects(*dbus, service, "/");
Zhu, Yungebe560b02019-04-21 21:19:21 -04002367 }
2368 catch (const std::exception& e)
2369 {
2370 phosphor::logging::log<phosphor::logging::level::ERR>(
2371 "No object implements interface",
2372 phosphor::logging::entry("SERVICE=%s", service.c_str()),
2373 phosphor::logging::entry("INTF=%s", intf));
2374 return ipmi::responseResponseError();
2375 }
2376
2377 if (valueTree.empty())
2378 {
2379 phosphor::logging::log<phosphor::logging::level::ERR>(
2380 "No object implements interface",
2381 phosphor::logging::entry("INTF=%s", intf));
2382 return ipmi::responseResponseError();
2383 }
2384
2385 for (const auto& item : valueTree)
2386 {
2387 // find LedFault configuration
2388 auto interface =
2389 item.second.find("xyz.openbmc_project.Configuration.LedFault");
2390 if (interface == item.second.end())
2391 {
2392 continue;
2393 }
2394
2395 // find matched fault type: faultMemmory / faultFan
2396 // find LedGpioPins/FaultIndex configuration
2397 auto propertyFaultType = interface->second.find("FaultType");
2398 auto propertyFIndex = interface->second.find("FaultIndex");
2399 auto ledIndex = interface->second.find("LedGpioPins");
2400
2401 if (propertyFaultType == interface->second.end() ||
2402 propertyFIndex == interface->second.end() ||
2403 ledIndex == interface->second.end())
2404 {
2405 continue;
2406 }
2407
2408 try
2409 {
2410 Value valIndex = propertyFIndex->second;
2411 resFIndex = std::get<uint64_t>(valIndex);
2412
2413 Value valFType = propertyFaultType->second;
2414 resFType = std::get<std::string>(valFType);
2415 }
2416 catch (const std::bad_variant_access& e)
2417 {
2418 phosphor::logging::log<phosphor::logging::level::ERR>(e.what());
2419 return ipmi::responseResponseError();
2420 }
2421 // find the matched requested fault type: faultMemmory or faultFan
2422 if (resFType != faultNames[faultType])
2423 {
2424 continue;
2425 }
2426
2427 // read LedGpioPins data
2428 std::vector<uint64_t> ledgpios;
2429 std::variant<std::vector<uint64_t>> message;
2430
Vernon Mauery15419dd2019-05-24 09:40:30 -07002431 auto method = dbus->new_method_call(
Zhu, Yungebe560b02019-04-21 21:19:21 -04002432 service.c_str(), (std::string(item.first)).c_str(),
2433 "org.freedesktop.DBus.Properties", "Get");
2434
2435 method.append("xyz.openbmc_project.Configuration.LedFault",
2436 "LedGpioPins");
2437
2438 try
2439 {
Vernon Mauery15419dd2019-05-24 09:40:30 -07002440 auto reply = dbus->call(method);
Zhu, Yungebe560b02019-04-21 21:19:21 -04002441 reply.read(message);
2442 ledgpios = std::get<std::vector<uint64_t>>(message);
2443 }
2444 catch (std::exception& e)
2445 {
2446 phosphor::logging::log<phosphor::logging::level::ERR>(e.what());
2447 return ipmi::responseResponseError();
2448 }
2449
2450 // Check the size to be sure it will never overflow on groupSize
2451 if (ledgpios.size() > groupSize)
2452 {
2453 phosphor::logging::log<phosphor::logging::level::ERR>(
2454 "Fault gpio Pins out of range!");
2455 return ipmi::responseParmOutOfRange();
2456 }
2457 // Store data, according to command data bit index order
2458 for (int i = 0; i < ledgpios.size(); i++)
2459 {
2460 ledFaultPins[i + groupSize * resFIndex] = ledgpios[i];
2461 }
2462 }
2463
2464 switch (RemoteFaultType(faultType))
2465 {
2466 case (RemoteFaultType::fan):
2467 case (RemoteFaultType::memory):
2468 {
2469 if (faultGroup == skipLEDs)
2470 {
2471 return ipmi::responseSuccess();
2472 }
2473
2474 uint64_t ledState = 0;
2475 // calculate led state bit filed count, each byte has 8bits
2476 // the maximum bits will be 8 * 8 bits
2477 constexpr uint8_t size = sizeof(ledStateData) * 8;
2478 for (int i = 0; i < sizeof(ledStateData); i++)
2479 {
2480 ledState = (uint64_t)(ledState << 8);
2481 ledState = (uint64_t)(ledState | (uint64_t)ledStateData[i]);
2482 }
2483
2484 std::bitset<size> ledStateBits(ledState);
2485 std::string gpioValue;
2486 for (int i = 0; i < size; i++)
2487 { // skip invalid value
2488 if (ledFaultPins[i] == 0xFFFF)
2489 {
2490 continue;
2491 }
2492
2493 std::string device = sysGpioPath +
2494 std::to_string(ledFaultPins[i]) +
2495 postfixValue;
2496 std::fstream gpioFile;
2497
2498 gpioFile.open(device, std::ios::out);
2499
2500 if (!gpioFile.good())
2501 {
2502 phosphor::logging::log<phosphor::logging::level::ERR>(
2503 "Not Find Led Gpio Device!",
2504 phosphor::logging::entry("DEVICE=%s", device.c_str()));
2505 return ipmi::responseResponseError();
2506 }
2507 gpioFile << std::to_string(
2508 static_cast<uint8_t>(ledStateBits[i]));
2509 gpioFile.close();
2510 }
2511 break;
2512 }
2513 default:
2514 {
2515 // now only support two fault types
2516 return ipmi::responseParmOutOfRange();
2517 }
2518 }
2519
2520 return ipmi::responseSuccess();
2521}
2522
Richard Marian Thomaiyarea537d52019-04-24 21:33:48 +05302523ipmi::RspType<uint8_t> ipmiOEMReadBoardProductId()
2524{
2525 uint8_t prodId = 0;
2526 try
2527 {
Vernon Mauery15419dd2019-05-24 09:40:30 -07002528 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
Richard Marian Thomaiyarea537d52019-04-24 21:33:48 +05302529 const DbusObjectInfo& object = getDbusObject(
Vernon Mauery15419dd2019-05-24 09:40:30 -07002530 *dbus, "xyz.openbmc_project.Inventory.Item.Board",
Richard Marian Thomaiyarea537d52019-04-24 21:33:48 +05302531 "/xyz/openbmc_project/inventory/system/board/", "Baseboard");
2532 const Value& propValue = getDbusProperty(
Vernon Mauery15419dd2019-05-24 09:40:30 -07002533 *dbus, object.second, object.first,
Richard Marian Thomaiyarea537d52019-04-24 21:33:48 +05302534 "xyz.openbmc_project.Inventory.Item.Board", "ProductId");
2535 prodId = static_cast<uint8_t>(std::get<uint64_t>(propValue));
2536 }
2537 catch (std::exception& e)
2538 {
2539 phosphor::logging::log<phosphor::logging::level::ERR>(
2540 "ipmiOEMReadBoardProductId: Product ID read failed!",
2541 phosphor::logging::entry("ERR=%s", e.what()));
2542 }
2543 return ipmi::responseSuccess(prodId);
2544}
2545
Richard Marian Thomaiyard801e462019-06-20 01:05:40 +05302546/** @brief implements the get security mode command
2547 * @param ctx - ctx pointer
2548 *
2549 * @returns IPMI completion code with following data
2550 * - restriction mode value - As specified in
2551 * xyz.openbmc_project.Control.Security.RestrictionMode.interface.yaml
2552 * - special mode value - As specified in
2553 * xyz.openbmc_project.Control.Security.SpecialMode.interface.yaml
2554 */
2555ipmi::RspType<uint8_t, uint8_t> ipmiGetSecurityMode(ipmi::Context::ptr ctx)
2556{
2557 namespace securityNameSpace =
2558 sdbusplus::xyz::openbmc_project::Control::Security::server;
2559 uint8_t restrictionModeValue = 0;
2560 uint8_t specialModeValue = 0;
2561
2562 boost::system::error_code ec;
2563 auto varRestrMode = ctx->bus->yield_method_call<std::variant<std::string>>(
James Feist28c72902019-09-16 10:34:07 -07002564 ctx->yield, ec, restricionModeService, restricionModeBasePath,
Richard Marian Thomaiyard801e462019-06-20 01:05:40 +05302565 dBusPropertyIntf, dBusPropertyGetMethod, restricionModeIntf,
2566 restricionModeProperty);
2567 if (ec)
2568 {
2569 phosphor::logging::log<phosphor::logging::level::ERR>(
2570 "ipmiGetSecurityMode: failed to get RestrictionMode property",
2571 phosphor::logging::entry("ERROR=%s", ec.message().c_str()));
2572 return ipmi::responseUnspecifiedError();
2573 }
2574 restrictionModeValue = static_cast<uint8_t>(
2575 securityNameSpace::RestrictionMode::convertModesFromString(
2576 std::get<std::string>(varRestrMode)));
Richard Marian Thomaiyar8d4f8d72019-11-11 12:06:40 +05302577 auto varSpecialMode =
2578 ctx->bus->yield_method_call<std::variant<std::string>>(
2579 ctx->yield, ec, specialModeService, specialModeBasePath,
2580 dBusPropertyIntf, dBusPropertyGetMethod, specialModeIntf,
2581 specialModeProperty);
Richard Marian Thomaiyard801e462019-06-20 01:05:40 +05302582 if (ec)
2583 {
2584 phosphor::logging::log<phosphor::logging::level::ERR>(
2585 "ipmiGetSecurityMode: failed to get SpecialMode property",
2586 phosphor::logging::entry("ERROR=%s", ec.message().c_str()));
2587 // fall through, let us not worry about SpecialMode property, which is
2588 // not required in user scenario
2589 }
2590 else
2591 {
Richard Marian Thomaiyar8d4f8d72019-11-11 12:06:40 +05302592 specialModeValue = static_cast<uint8_t>(
2593 securityNameSpace::SpecialMode::convertModesFromString(
2594 std::get<std::string>(varSpecialMode)));
Richard Marian Thomaiyard801e462019-06-20 01:05:40 +05302595 }
2596 return ipmi::responseSuccess(restrictionModeValue, specialModeValue);
2597}
2598
2599/** @brief implements the set security mode command
2600 * Command allows to upgrade the restriction mode and won't allow
2601 * to downgrade from system interface
2602 * @param ctx - ctx pointer
2603 * @param restrictionMode - restriction mode value to be set.
2604 *
2605 * @returns IPMI completion code
2606 */
2607ipmi::RspType<> ipmiSetSecurityMode(ipmi::Context::ptr ctx,
2608 uint8_t restrictionMode)
2609{
2610 namespace securityNameSpace =
2611 sdbusplus::xyz::openbmc_project::Control::Security::server;
2612
2613 ChannelInfo chInfo;
2614 if (getChannelInfo(ctx->channel, chInfo) != ccSuccess)
2615 {
2616 phosphor::logging::log<phosphor::logging::level::ERR>(
2617 "ipmiSetSecurityMode: Failed to get Channel Info",
2618 phosphor::logging::entry("CHANNEL=%d", ctx->channel));
2619 return ipmi::responseUnspecifiedError();
2620 }
2621 auto reqMode =
2622 static_cast<securityNameSpace::RestrictionMode::Modes>(restrictionMode);
2623
2624 if ((reqMode < securityNameSpace::RestrictionMode::Modes::Provisioning) ||
2625 (reqMode >
2626 securityNameSpace::RestrictionMode::Modes::ProvisionedHostDisabled))
2627 {
2628 return ipmi::responseInvalidFieldRequest();
2629 }
2630
2631 boost::system::error_code ec;
2632 auto varRestrMode = ctx->bus->yield_method_call<std::variant<std::string>>(
James Feist28c72902019-09-16 10:34:07 -07002633 ctx->yield, ec, restricionModeService, restricionModeBasePath,
Richard Marian Thomaiyard801e462019-06-20 01:05:40 +05302634 dBusPropertyIntf, dBusPropertyGetMethod, restricionModeIntf,
2635 restricionModeProperty);
2636 if (ec)
2637 {
2638 phosphor::logging::log<phosphor::logging::level::ERR>(
2639 "ipmiSetSecurityMode: failed to get RestrictionMode property",
2640 phosphor::logging::entry("ERROR=%s", ec.message().c_str()));
2641 return ipmi::responseUnspecifiedError();
2642 }
2643 auto currentRestrictionMode =
2644 securityNameSpace::RestrictionMode::convertModesFromString(
2645 std::get<std::string>(varRestrMode));
2646
2647 if (chInfo.mediumType !=
2648 static_cast<uint8_t>(EChannelMediumType::lan8032) &&
2649 currentRestrictionMode > reqMode)
2650 {
2651 phosphor::logging::log<phosphor::logging::level::ERR>(
2652 "ipmiSetSecurityMode - Downgrading security mode not supported "
2653 "through system interface",
2654 phosphor::logging::entry(
2655 "CUR_MODE=%d", static_cast<uint8_t>(currentRestrictionMode)),
2656 phosphor::logging::entry("REQ_MODE=%d", restrictionMode));
2657 return ipmi::responseCommandNotAvailable();
2658 }
2659
2660 ec.clear();
2661 ctx->bus->yield_method_call<>(
James Feist28c72902019-09-16 10:34:07 -07002662 ctx->yield, ec, restricionModeService, restricionModeBasePath,
Richard Marian Thomaiyard801e462019-06-20 01:05:40 +05302663 dBusPropertyIntf, dBusPropertySetMethod, restricionModeIntf,
2664 restricionModeProperty,
2665 static_cast<std::variant<std::string>>(
2666 securityNameSpace::convertForMessage(reqMode)));
2667
2668 if (ec)
2669 {
2670 phosphor::logging::log<phosphor::logging::level::ERR>(
2671 "ipmiSetSecurityMode: failed to set RestrictionMode property",
2672 phosphor::logging::entry("ERROR=%s", ec.message().c_str()));
2673 return ipmi::responseUnspecifiedError();
2674 }
2675 return ipmi::responseSuccess();
2676}
2677
Vernon Mauery4ac799d2019-05-20 15:50:37 -07002678ipmi::RspType<uint8_t /* restore status */>
2679 ipmiRestoreConfiguration(const std::array<uint8_t, 3>& clr, uint8_t cmd)
2680{
2681 static constexpr std::array<uint8_t, 3> expClr = {'C', 'L', 'R'};
2682
2683 if (clr != expClr)
2684 {
2685 return ipmi::responseInvalidFieldRequest();
2686 }
2687 constexpr uint8_t cmdStatus = 0;
2688 constexpr uint8_t cmdDefaultRestore = 0xaa;
2689 constexpr uint8_t cmdFullRestore = 0xbb;
2690 constexpr uint8_t cmdFormat = 0xcc;
2691
2692 constexpr const char* restoreOpFname = "/tmp/.rwfs/.restore_op";
2693
2694 switch (cmd)
2695 {
2696 case cmdStatus:
2697 break;
2698 case cmdDefaultRestore:
2699 case cmdFullRestore:
2700 case cmdFormat:
2701 {
2702 // write file to rwfs root
2703 int value = (cmd - 1) & 0x03; // map aa, bb, cc => 1, 2, 3
2704 std::ofstream restoreFile(restoreOpFname);
2705 if (!restoreFile)
2706 {
2707 return ipmi::responseUnspecifiedError();
2708 }
2709 restoreFile << value << "\n";
2710 break;
2711 }
2712 default:
2713 return ipmi::responseInvalidFieldRequest();
2714 }
2715
2716 constexpr uint8_t restorePending = 0;
2717 constexpr uint8_t restoreComplete = 1;
2718
2719 uint8_t restoreStatus = std::filesystem::exists(restoreOpFname)
2720 ? restorePending
2721 : restoreComplete;
2722 return ipmi::responseSuccess(restoreStatus);
2723}
2724
Chen Yugang39736d52019-07-12 16:24:33 +08002725ipmi::RspType<uint8_t> ipmiOEMGetNmiSource(void)
2726{
2727 uint8_t bmcSource;
Chen Yugang97cf96e2019-11-01 08:55:11 +08002728 namespace nmi = sdbusplus::xyz::openbmc_project::Chassis::Control::server;
Chen Yugang39736d52019-07-12 16:24:33 +08002729
2730 try
2731 {
2732 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
2733 std::string service =
2734 getService(*dbus, oemNmiSourceIntf, oemNmiSourceObjPath);
2735 Value variant =
2736 getDbusProperty(*dbus, service, oemNmiSourceObjPath,
2737 oemNmiSourceIntf, oemNmiBmcSourceObjPathProp);
2738
2739 switch (nmi::NMISource::convertBMCSourceSignalFromString(
2740 std::get<std::string>(variant)))
2741 {
2742 case nmi::NMISource::BMCSourceSignal::None:
2743 bmcSource = static_cast<uint8_t>(NmiSource::none);
2744 break;
Chen Yugang97cf96e2019-11-01 08:55:11 +08002745 case nmi::NMISource::BMCSourceSignal::FrontPanelButton:
2746 bmcSource = static_cast<uint8_t>(NmiSource::frontPanelButton);
Chen Yugang39736d52019-07-12 16:24:33 +08002747 break;
Chen Yugang97cf96e2019-11-01 08:55:11 +08002748 case nmi::NMISource::BMCSourceSignal::Watchdog:
2749 bmcSource = static_cast<uint8_t>(NmiSource::watchdog);
Chen Yugang39736d52019-07-12 16:24:33 +08002750 break;
2751 case nmi::NMISource::BMCSourceSignal::ChassisCmd:
2752 bmcSource = static_cast<uint8_t>(NmiSource::chassisCmd);
2753 break;
2754 case nmi::NMISource::BMCSourceSignal::MemoryError:
2755 bmcSource = static_cast<uint8_t>(NmiSource::memoryError);
2756 break;
Chen Yugang97cf96e2019-11-01 08:55:11 +08002757 case nmi::NMISource::BMCSourceSignal::PciBusError:
2758 bmcSource = static_cast<uint8_t>(NmiSource::pciBusError);
Chen Yugang39736d52019-07-12 16:24:33 +08002759 break;
Chen Yugang97cf96e2019-11-01 08:55:11 +08002760 case nmi::NMISource::BMCSourceSignal::PCH:
2761 bmcSource = static_cast<uint8_t>(NmiSource::pch);
Chen Yugang39736d52019-07-12 16:24:33 +08002762 break;
Chen Yugang97cf96e2019-11-01 08:55:11 +08002763 case nmi::NMISource::BMCSourceSignal::Chipset:
2764 bmcSource = static_cast<uint8_t>(NmiSource::chipset);
Chen Yugang39736d52019-07-12 16:24:33 +08002765 break;
2766 default:
2767 phosphor::logging::log<phosphor::logging::level::ERR>(
2768 "NMI source: invalid property!",
2769 phosphor::logging::entry(
2770 "PROP=%s", std::get<std::string>(variant).c_str()));
2771 return ipmi::responseResponseError();
2772 }
2773 }
2774 catch (sdbusplus::exception::SdBusError& e)
2775 {
2776 phosphor::logging::log<phosphor::logging::level::ERR>(e.what());
2777 return ipmi::responseResponseError();
2778 }
2779
2780 return ipmi::responseSuccess(bmcSource);
2781}
2782
2783ipmi::RspType<> ipmiOEMSetNmiSource(uint8_t sourceId)
2784{
Chen Yugang97cf96e2019-11-01 08:55:11 +08002785 namespace nmi = sdbusplus::xyz::openbmc_project::Chassis::Control::server;
Chen Yugang39736d52019-07-12 16:24:33 +08002786
2787 nmi::NMISource::BMCSourceSignal bmcSourceSignal =
2788 nmi::NMISource::BMCSourceSignal::None;
2789
2790 switch (NmiSource(sourceId))
2791 {
2792 case NmiSource::none:
2793 bmcSourceSignal = nmi::NMISource::BMCSourceSignal::None;
2794 break;
Chen Yugang97cf96e2019-11-01 08:55:11 +08002795 case NmiSource::frontPanelButton:
2796 bmcSourceSignal = nmi::NMISource::BMCSourceSignal::FrontPanelButton;
Chen Yugang39736d52019-07-12 16:24:33 +08002797 break;
Chen Yugang97cf96e2019-11-01 08:55:11 +08002798 case NmiSource::watchdog:
2799 bmcSourceSignal = nmi::NMISource::BMCSourceSignal::Watchdog;
Chen Yugang39736d52019-07-12 16:24:33 +08002800 break;
2801 case NmiSource::chassisCmd:
2802 bmcSourceSignal = nmi::NMISource::BMCSourceSignal::ChassisCmd;
2803 break;
2804 case NmiSource::memoryError:
2805 bmcSourceSignal = nmi::NMISource::BMCSourceSignal::MemoryError;
2806 break;
Chen Yugang97cf96e2019-11-01 08:55:11 +08002807 case NmiSource::pciBusError:
2808 bmcSourceSignal = nmi::NMISource::BMCSourceSignal::PciBusError;
Chen Yugang39736d52019-07-12 16:24:33 +08002809 break;
Chen Yugang97cf96e2019-11-01 08:55:11 +08002810 case NmiSource::pch:
2811 bmcSourceSignal = nmi::NMISource::BMCSourceSignal::PCH;
Chen Yugang39736d52019-07-12 16:24:33 +08002812 break;
Chen Yugang97cf96e2019-11-01 08:55:11 +08002813 case NmiSource::chipset:
2814 bmcSourceSignal = nmi::NMISource::BMCSourceSignal::Chipset;
Chen Yugang39736d52019-07-12 16:24:33 +08002815 break;
2816 default:
2817 phosphor::logging::log<phosphor::logging::level::ERR>(
2818 "NMI source: invalid property!");
2819 return ipmi::responseResponseError();
2820 }
2821
2822 try
2823 {
2824 // keep NMI signal source
2825 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
2826 std::string service =
2827 getService(*dbus, oemNmiSourceIntf, oemNmiSourceObjPath);
Chen Yugang97cf96e2019-11-01 08:55:11 +08002828 setDbusProperty(*dbus, service, oemNmiSourceObjPath, oemNmiSourceIntf,
2829 oemNmiBmcSourceObjPathProp,
2830 nmi::convertForMessage(bmcSourceSignal));
Chen Yugang99be6332019-08-09 16:20:48 +08002831 // set Enabled property to inform NMI source handling
2832 // to trigger a NMI_OUT BSOD.
2833 // if it's triggered by NMI source property changed,
2834 // NMI_OUT BSOD could be missed if the same source occurs twice in a row
2835 if (bmcSourceSignal != nmi::NMISource::BMCSourceSignal::None)
2836 {
2837 setDbusProperty(*dbus, service, oemNmiSourceObjPath,
2838 oemNmiSourceIntf, oemNmiEnabledObjPathProp,
2839 static_cast<bool>(true));
2840 }
Chen Yugang39736d52019-07-12 16:24:33 +08002841 }
2842 catch (sdbusplus::exception_t& e)
2843 {
2844 phosphor::logging::log<phosphor::logging::level::ERR>(e.what());
2845 return ipmi::responseResponseError();
2846 }
2847
2848 return ipmi::responseSuccess();
2849}
2850
James Feist63efafa2019-07-24 12:39:21 -07002851namespace dimmOffset
2852{
2853constexpr const char* dimmPower = "DimmPower";
2854constexpr const char* staticCltt = "StaticCltt";
2855constexpr const char* offsetPath = "/xyz/openbmc_project/Inventory/Item/Dimm";
2856constexpr const char* offsetInterface =
2857 "xyz.openbmc_project.Inventory.Item.Dimm.Offset";
2858constexpr const char* property = "DimmOffset";
2859
2860}; // namespace dimmOffset
2861
2862ipmi::RspType<>
2863 ipmiOEMSetDimmOffset(uint8_t type,
2864 const std::vector<std::tuple<uint8_t, uint8_t>>& data)
2865{
2866 if (type != static_cast<uint8_t>(dimmOffsetTypes::dimmPower) &&
2867 type != static_cast<uint8_t>(dimmOffsetTypes::staticCltt))
2868 {
2869 return ipmi::responseInvalidFieldRequest();
2870 }
2871
2872 if (data.empty())
2873 {
2874 return ipmi::responseInvalidFieldRequest();
2875 }
2876 nlohmann::json json;
2877
2878 std::ifstream jsonStream(dimmOffsetFile);
2879 if (jsonStream.good())
2880 {
2881 json = nlohmann::json::parse(jsonStream, nullptr, false);
2882 if (json.is_discarded())
2883 {
2884 json = nlohmann::json();
2885 }
2886 jsonStream.close();
2887 }
2888
2889 std::string typeName;
2890 if (type == static_cast<uint8_t>(dimmOffsetTypes::dimmPower))
2891 {
2892 typeName = dimmOffset::dimmPower;
2893 }
2894 else
2895 {
2896 typeName = dimmOffset::staticCltt;
2897 }
2898
2899 nlohmann::json& field = json[typeName];
2900
2901 for (const auto& [index, value] : data)
2902 {
2903 field[index] = value;
2904 }
2905
2906 for (nlohmann::json& val : field)
2907 {
2908 if (val == nullptr)
2909 {
2910 val = static_cast<uint8_t>(0);
2911 }
2912 }
2913
2914 std::ofstream output(dimmOffsetFile);
2915 if (!output.good())
2916 {
2917 std::cerr << "Error writing json file\n";
2918 return ipmi::responseResponseError();
2919 }
2920
2921 output << json.dump(4);
2922
2923 if (type == static_cast<uint8_t>(dimmOffsetTypes::staticCltt))
2924 {
2925 std::shared_ptr<sdbusplus::asio::connection> bus = getSdBus();
2926
2927 std::variant<std::vector<uint8_t>> offsets =
2928 field.get<std::vector<uint8_t>>();
2929 auto call = bus->new_method_call(
2930 settingsBusName, dimmOffset::offsetPath, PROP_INTF, "Set");
2931 call.append(dimmOffset::offsetInterface, dimmOffset::property, offsets);
2932 try
2933 {
2934 bus->call(call);
2935 }
2936 catch (sdbusplus::exception_t& e)
2937 {
2938 phosphor::logging::log<phosphor::logging::level::ERR>(
2939 "ipmiOEMSetDimmOffset: can't set dimm offsets!",
2940 phosphor::logging::entry("ERR=%s", e.what()));
2941 return ipmi::responseResponseError();
2942 }
2943 }
2944
2945 return ipmi::responseSuccess();
2946}
2947
2948ipmi::RspType<uint8_t> ipmiOEMGetDimmOffset(uint8_t type, uint8_t index)
2949{
2950
2951 if (type != static_cast<uint8_t>(dimmOffsetTypes::dimmPower) &&
2952 type != static_cast<uint8_t>(dimmOffsetTypes::staticCltt))
2953 {
2954 return ipmi::responseInvalidFieldRequest();
2955 }
2956
2957 std::ifstream jsonStream(dimmOffsetFile);
2958
2959 auto json = nlohmann::json::parse(jsonStream, nullptr, false);
2960 if (json.is_discarded())
2961 {
2962 std::cerr << "File error in " << dimmOffsetFile << "\n";
2963 return ipmi::responseResponseError();
2964 }
2965
2966 std::string typeName;
2967 if (type == static_cast<uint8_t>(dimmOffsetTypes::dimmPower))
2968 {
2969 typeName = dimmOffset::dimmPower;
2970 }
2971 else
2972 {
2973 typeName = dimmOffset::staticCltt;
2974 }
2975
2976 auto it = json.find(typeName);
2977 if (it == json.end())
2978 {
2979 return ipmi::responseInvalidFieldRequest();
2980 }
2981
2982 if (it->size() <= index)
2983 {
2984 return ipmi::responseInvalidFieldRequest();
2985 }
2986
2987 uint8_t resp = it->at(index).get<uint8_t>();
2988 return ipmi::responseSuccess(resp);
2989}
2990
Chen,Yugang4f7e76b2019-08-20 09:28:06 +08002991namespace boot_options
2992{
2993
2994using namespace sdbusplus::xyz::openbmc_project::Control::Boot::server;
2995using IpmiValue = uint8_t;
2996constexpr auto ipmiDefault = 0;
2997
2998std::map<IpmiValue, Source::Sources> sourceIpmiToDbus = {
2999 {0x01, Source::Sources::Network},
3000 {0x02, Source::Sources::Disk},
3001 {0x05, Source::Sources::ExternalMedia},
3002 {0x0f, Source::Sources::RemovableMedia},
3003 {ipmiDefault, Source::Sources::Default}};
3004
3005std::map<IpmiValue, Mode::Modes> modeIpmiToDbus = {
Chen Yugangca12a7b2019-09-03 18:11:44 +08003006 {0x06, Mode::Modes::Setup}, {ipmiDefault, Mode::Modes::Regular}};
Chen,Yugang4f7e76b2019-08-20 09:28:06 +08003007
3008std::map<Source::Sources, IpmiValue> sourceDbusToIpmi = {
3009 {Source::Sources::Network, 0x01},
3010 {Source::Sources::Disk, 0x02},
3011 {Source::Sources::ExternalMedia, 0x05},
3012 {Source::Sources::RemovableMedia, 0x0f},
3013 {Source::Sources::Default, ipmiDefault}};
3014
3015std::map<Mode::Modes, IpmiValue> modeDbusToIpmi = {
Chen Yugangca12a7b2019-09-03 18:11:44 +08003016 {Mode::Modes::Setup, 0x06}, {Mode::Modes::Regular, ipmiDefault}};
Chen,Yugang4f7e76b2019-08-20 09:28:06 +08003017
3018static constexpr auto bootModeIntf = "xyz.openbmc_project.Control.Boot.Mode";
3019static constexpr auto bootSourceIntf =
3020 "xyz.openbmc_project.Control.Boot.Source";
3021static constexpr auto enabledIntf = "xyz.openbmc_project.Object.Enable";
3022static constexpr auto persistentObjPath =
3023 "/xyz/openbmc_project/control/host0/boot";
3024static constexpr auto oneTimePath =
3025 "/xyz/openbmc_project/control/host0/boot/one_time";
3026static constexpr auto bootSourceProp = "BootSource";
3027static constexpr auto bootModeProp = "BootMode";
3028static constexpr auto oneTimeBootEnableProp = "Enabled";
3029static constexpr auto httpBootMode =
3030 "xyz.openbmc_project.Control.Boot.Source.Sources.Http";
3031
3032enum class BootOptionParameter : size_t
3033{
3034 setInProgress = 0x0,
3035 bootFlags = 0x5,
3036};
3037static constexpr uint8_t setComplete = 0x0;
3038static constexpr uint8_t setInProgress = 0x1;
3039static uint8_t transferStatus = setComplete;
3040static constexpr uint8_t setParmVersion = 0x01;
3041static constexpr uint8_t setParmBootFlagsPermanent = 0x40;
3042static constexpr uint8_t setParmBootFlagsValidOneTime = 0x80;
3043static constexpr uint8_t setParmBootFlagsValidPermanent = 0xC0;
3044static constexpr uint8_t httpBoot = 0xd;
3045static constexpr uint8_t bootSourceMask = 0x3c;
3046
3047} // namespace boot_options
3048
3049ipmi::RspType<uint8_t, // version
3050 uint8_t, // param
3051 uint8_t, // data0, dependent on parameter
3052 std::optional<uint8_t> // data1, dependent on parameter
3053 >
3054 ipmiOemGetEfiBootOptions(uint8_t parameter, uint8_t set, uint8_t block)
3055{
3056 using namespace boot_options;
3057 uint8_t bootOption = 0;
3058
3059 if (parameter == static_cast<uint8_t>(BootOptionParameter::setInProgress))
3060 {
3061 return ipmi::responseSuccess(setParmVersion, parameter, transferStatus,
3062 std::nullopt);
3063 }
3064
3065 if (parameter != static_cast<uint8_t>(BootOptionParameter::bootFlags))
3066 {
3067 phosphor::logging::log<phosphor::logging::level::ERR>(
3068 "Unsupported parameter");
3069 return ipmi::responseResponseError();
3070 }
3071
3072 try
3073 {
3074 auto oneTimeEnabled = false;
3075 // read one time Enabled property
3076 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
3077 std::string service = getService(*dbus, enabledIntf, oneTimePath);
3078 Value variant = getDbusProperty(*dbus, service, oneTimePath,
3079 enabledIntf, oneTimeBootEnableProp);
3080 oneTimeEnabled = std::get<bool>(variant);
3081
3082 // get BootSource and BootMode properties
3083 // according to oneTimeEnable
3084 auto bootObjPath = oneTimePath;
3085 if (oneTimeEnabled == false)
3086 {
3087 bootObjPath = persistentObjPath;
3088 }
3089
3090 service = getService(*dbus, bootModeIntf, bootObjPath);
3091 variant = getDbusProperty(*dbus, service, bootObjPath, bootModeIntf,
3092 bootModeProp);
3093
3094 auto bootMode =
3095 Mode::convertModesFromString(std::get<std::string>(variant));
3096
3097 service = getService(*dbus, bootSourceIntf, bootObjPath);
3098 variant = getDbusProperty(*dbus, service, bootObjPath, bootSourceIntf,
3099 bootSourceProp);
3100
3101 if (std::get<std::string>(variant) == httpBootMode)
3102 {
3103 bootOption = httpBoot;
3104 }
3105 else
3106 {
3107 auto bootSource = Source::convertSourcesFromString(
3108 std::get<std::string>(variant));
3109 bootOption = sourceDbusToIpmi.at(bootSource);
3110 if (Source::Sources::Default == bootSource)
3111 {
3112 bootOption = modeDbusToIpmi.at(bootMode);
3113 }
3114 }
3115
3116 uint8_t oneTime = oneTimeEnabled ? setParmBootFlagsValidOneTime
3117 : setParmBootFlagsValidPermanent;
3118 bootOption <<= 2; // shift for responseconstexpr
3119 return ipmi::responseSuccess(setParmVersion, parameter, oneTime,
3120 bootOption);
3121 }
3122 catch (sdbusplus::exception_t& e)
3123 {
3124 phosphor::logging::log<phosphor::logging::level::ERR>(e.what());
3125 return ipmi::responseResponseError();
3126 }
3127}
3128
3129ipmi::RspType<> ipmiOemSetEfiBootOptions(uint8_t bootFlag, uint8_t bootParam,
3130 std::optional<uint8_t> bootOption)
3131{
3132 using namespace boot_options;
3133 auto oneTimeEnabled = false;
3134
3135 if (bootFlag == static_cast<uint8_t>(BootOptionParameter::setInProgress))
3136 {
3137 if (bootOption)
3138 {
3139 return ipmi::responseReqDataLenInvalid();
3140 }
3141
3142 if (transferStatus == setInProgress)
3143 {
3144 phosphor::logging::log<phosphor::logging::level::ERR>(
3145 "boot option set in progress!");
3146 return ipmi::responseResponseError();
3147 }
3148
3149 transferStatus = bootParam;
3150 return ipmi::responseSuccess();
3151 }
3152
3153 if (bootFlag != (uint8_t)BootOptionParameter::bootFlags)
3154 {
3155 phosphor::logging::log<phosphor::logging::level::ERR>(
3156 "Unsupported parameter");
3157 return ipmi::responseResponseError();
3158 }
3159
3160 if (!bootOption)
3161 {
3162 return ipmi::responseReqDataLenInvalid();
3163 }
3164
3165 if (((bootOption.value() & bootSourceMask) >> 2) !=
3166 httpBoot) // not http boot, exit
3167 {
3168 phosphor::logging::log<phosphor::logging::level::ERR>(
3169 "wrong boot option parameter!");
3170 return ipmi::responseParmOutOfRange();
3171 }
3172
3173 try
3174 {
3175 bool permanent = (bootParam & setParmBootFlagsPermanent) ==
3176 setParmBootFlagsPermanent;
3177
3178 // read one time Enabled property
3179 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
3180 std::string service = getService(*dbus, enabledIntf, oneTimePath);
3181 Value variant = getDbusProperty(*dbus, service, oneTimePath,
3182 enabledIntf, oneTimeBootEnableProp);
3183 oneTimeEnabled = std::get<bool>(variant);
3184
3185 /*
3186 * Check if the current boot setting is onetime or permanent, if the
3187 * request in the command is otherwise, then set the "Enabled"
3188 * property in one_time object path to 'True' to indicate onetime
3189 * and 'False' to indicate permanent.
3190 *
3191 * Once the onetime/permanent setting is applied, then the bootMode
3192 * and bootSource is updated for the corresponding object.
3193 */
3194 if (permanent == oneTimeEnabled)
3195 {
3196 setDbusProperty(*dbus, service, oneTimePath, enabledIntf,
3197 oneTimeBootEnableProp, !permanent);
3198 }
3199
3200 // set BootSource and BootMode properties
3201 // according to oneTimeEnable or persistent
3202 auto bootObjPath = oneTimePath;
3203 if (oneTimeEnabled == false)
3204 {
3205 bootObjPath = persistentObjPath;
3206 }
3207 std::string bootMode =
3208 "xyz.openbmc_project.Control.Boot.Mode.Modes.Regular";
3209 std::string bootSource = httpBootMode;
3210
3211 service = getService(*dbus, bootModeIntf, bootObjPath);
3212 setDbusProperty(*dbus, service, bootObjPath, bootModeIntf, bootModeProp,
3213 bootMode);
3214
3215 service = getService(*dbus, bootSourceIntf, bootObjPath);
3216 setDbusProperty(*dbus, service, bootObjPath, bootSourceIntf,
3217 bootSourceProp, bootSource);
3218 }
3219 catch (sdbusplus::exception_t& e)
3220 {
3221 phosphor::logging::log<phosphor::logging::level::ERR>(e.what());
3222 return ipmi::responseResponseError();
3223 }
3224
3225 return ipmi::responseSuccess();
3226}
3227
Cheng C Yang4e6ee152019-09-25 10:27:44 +08003228using BasicVariantType =
3229 std::variant<std::vector<std::string>, std::vector<uint64_t>, std::string,
3230 int64_t, uint64_t, double, int32_t, uint32_t, int16_t,
3231 uint16_t, uint8_t, bool>;
3232using PropertyMapType =
3233 boost::container::flat_map<std::string, BasicVariantType>;
3234static constexpr const std::array<const char*, 1> psuPresenceTypes = {
3235 "xyz.openbmc_project.Configuration.PSUPresence"};
3236int getPSUAddress(ipmi::Context::ptr ctx, uint8_t& bus,
3237 std::vector<uint64_t>& addrTable)
3238{
3239 boost::system::error_code ec;
3240 GetSubTreeType subtree = ctx->bus->yield_method_call<GetSubTreeType>(
3241 ctx->yield, ec, "xyz.openbmc_project.ObjectMapper",
3242 "/xyz/openbmc_project/object_mapper",
3243 "xyz.openbmc_project.ObjectMapper", "GetSubTree",
3244 "/xyz/openbmc_project/inventory/system", 3, psuPresenceTypes);
3245 if (ec)
3246 {
3247 phosphor::logging::log<phosphor::logging::level::ERR>(
3248 "Failed to set dbus property to cold redundancy");
3249 return -1;
3250 }
3251 for (const auto& object : subtree)
3252 {
3253 std::string pathName = object.first;
3254 for (const auto& serviceIface : object.second)
3255 {
3256 std::string serviceName = serviceIface.first;
3257
3258 ec.clear();
3259 PropertyMapType propMap =
3260 ctx->bus->yield_method_call<PropertyMapType>(
3261 ctx->yield, ec, serviceName, pathName,
3262 "org.freedesktop.DBus.Properties", "GetAll",
3263 "xyz.openbmc_project.Configuration.PSUPresence");
3264 if (ec)
3265 {
3266 phosphor::logging::log<phosphor::logging::level::ERR>(
3267 "Failed to set dbus property to cold redundancy");
3268 return -1;
3269 }
3270 auto psuBus = std::get_if<uint64_t>(&propMap["Bus"]);
3271 auto psuAddress =
3272 std::get_if<std::vector<uint64_t>>(&propMap["Address"]);
3273
3274 if (psuBus == nullptr || psuAddress == nullptr)
3275 {
3276 std::cerr << "error finding necessary "
3277 "entry in configuration\n";
3278 return -1;
3279 }
3280 bus = static_cast<uint8_t>(*psuBus);
3281 addrTable = *psuAddress;
3282 return 0;
3283 }
3284 }
3285 return -1;
3286}
3287
3288static const constexpr uint8_t addrOffset = 8;
3289static const constexpr uint8_t psuRevision = 0xd9;
3290static const constexpr uint8_t defaultPSUBus = 7;
3291// Second Minor, Primary Minor, Major
3292static const constexpr size_t verLen = 3;
3293ipmi::RspType<std::vector<uint8_t>> ipmiOEMGetPSUVersion(ipmi::Context::ptr ctx)
3294{
3295 uint8_t bus = defaultPSUBus;
3296 std::vector<uint64_t> addrTable;
3297 std::vector<uint8_t> result;
3298 if (getPSUAddress(ctx, bus, addrTable))
3299 {
3300 std::cerr << "Failed to get PSU bus and address\n";
3301 return ipmi::responseResponseError();
3302 }
3303
3304 for (const auto& slaveAddr : addrTable)
3305 {
3306 std::vector<uint8_t> writeData = {psuRevision};
3307 std::vector<uint8_t> readBuf(verLen);
3308 uint8_t addr = static_cast<uint8_t>(slaveAddr) + addrOffset;
3309 std::string i2cBus = "/dev/i2c-" + std::to_string(bus);
3310
3311 auto retI2C = ipmi::i2cWriteRead(i2cBus, addr, writeData, readBuf);
3312 if (retI2C != ipmi::ccSuccess)
3313 {
3314 for (size_t idx = 0; idx < verLen; idx++)
3315 {
3316 result.emplace_back(0x00);
3317 }
3318 }
3319 else
3320 {
3321 for (const uint8_t& data : readBuf)
3322 {
3323 result.emplace_back(data);
3324 }
3325 }
3326 }
3327
3328 return ipmi::responseSuccess(result);
3329}
3330
Jason M. Bills64796042018-10-03 16:51:55 -07003331static void registerOEMFunctions(void)
Jia, Chunhuia835eaa2018-09-05 09:00:41 +08003332{
3333 phosphor::logging::log<phosphor::logging::level::INFO>(
3334 "Registering OEM commands");
Vernon Mauery98bbf692019-09-16 11:14:59 -07003335 ipmiPrintAndRegister(intel::netFnGeneral, IPMI_CMD_WILDCARD, NULL,
Jason M. Bills64796042018-10-03 16:51:55 -07003336 ipmiOEMWildcard,
3337 PRIVILEGE_USER); // wildcard default handler
Vernon Mauery98bbf692019-09-16 11:14:59 -07003338
3339 ipmiPrintAndRegister(intel::netFnApp, IPMI_CMD_WILDCARD, NULL,
Jason M. Bills64796042018-10-03 16:51:55 -07003340 ipmiOEMWildcard,
3341 PRIVILEGE_USER); // wildcard default handler
Vernon Mauery98bbf692019-09-16 11:14:59 -07003342
3343 ipmiPrintAndRegister(intel::netFnGeneral,
3344 intel::general::cmdGetChassisIdentifier, NULL,
3345 ipmiOEMGetChassisIdentifier,
3346 PRIVILEGE_USER); // get chassis identifier
3347
3348 ipmiPrintAndRegister(intel::netFnGeneral, intel::general::cmdSetSystemGUID,
3349 NULL, ipmiOEMSetSystemGUID,
3350 PRIVILEGE_ADMIN); // set system guid
Jason M. Billsb02bf092019-08-15 13:01:56 -07003351
3352 // <Disable BMC System Reset Action>
Vernon Mauery98bbf692019-09-16 11:14:59 -07003353 registerHandler(prioOemBase, intel::netFnGeneral,
3354 intel::general::cmdDisableBMCSystemReset, Privilege::Admin,
3355 ipmiOEMDisableBMCSystemReset);
3356
Jason M. Billsb02bf092019-08-15 13:01:56 -07003357 // <Get BMC Reset Disables>
Vernon Mauery98bbf692019-09-16 11:14:59 -07003358 registerHandler(prioOemBase, intel::netFnGeneral,
3359 intel::general::cmdGetBMCResetDisables, Privilege::Admin,
3360 ipmiOEMGetBMCResetDisables);
Jason M. Billsb02bf092019-08-15 13:01:56 -07003361
Vernon Mauery98bbf692019-09-16 11:14:59 -07003362 ipmiPrintAndRegister(intel::netFnGeneral, intel::general::cmdSetBIOSID,
3363 NULL, ipmiOEMSetBIOSID, PRIVILEGE_ADMIN);
Jia, Chunhuicc49b542019-03-20 15:41:07 +08003364
Chen Yugang7a04f3a2019-10-08 11:12:35 +08003365 registerHandler(prioOemBase, intel::netFnGeneral,
3366 intel::general::cmdGetOEMDeviceInfo, Privilege::User,
3367 ipmiOEMGetDeviceInfo);
Jia, Chunhuicc49b542019-03-20 15:41:07 +08003368
Vernon Mauery98bbf692019-09-16 11:14:59 -07003369 ipmiPrintAndRegister(intel::netFnGeneral,
3370 intel::general::cmdGetAICSlotFRUIDSlotPosRecords, NULL,
3371 ipmiOEMGetAICFRU, PRIVILEGE_USER);
Suryakanth Sekard509eb92018-11-15 17:44:11 +05303372
Vernon Mauery98bbf692019-09-16 11:14:59 -07003373 registerHandler(prioOpenBmcBase, intel::netFnGeneral,
3374 intel::general::cmdSendEmbeddedFWUpdStatus,
3375 Privilege::Operator, ipmiOEMSendEmbeddedFwUpdStatus);
Suryakanth Sekard509eb92018-11-15 17:44:11 +05303376
Vernon Mauery98bbf692019-09-16 11:14:59 -07003377 ipmiPrintAndRegister(intel::netFnGeneral,
3378 intel::general::cmdSetPowerRestoreDelay, NULL,
3379 ipmiOEMSetPowerRestoreDelay, PRIVILEGE_OPERATOR);
3380
3381 ipmiPrintAndRegister(intel::netFnGeneral,
3382 intel::general::cmdGetPowerRestoreDelay, NULL,
3383 ipmiOEMGetPowerRestoreDelay, PRIVILEGE_USER);
3384
3385 registerHandler(prioOpenBmcBase, intel::netFnGeneral,
3386 intel::general::cmdSetOEMUser2Activation,
3387 Privilege::Callback, ipmiOEMSetUser2Activation);
3388
3389 registerHandler(prioOpenBmcBase, intel::netFnGeneral,
3390 intel::general::cmdSetSpecialUserPassword,
3391 Privilege::Callback, ipmiOEMSetSpecialUserPassword);
Richard Marian Thomaiyarfc5e9852019-04-14 15:06:27 +05303392
Jason M. Bills42bd9c82019-06-28 16:39:34 -07003393 // <Get Processor Error Config>
Vernon Mauery98bbf692019-09-16 11:14:59 -07003394 registerHandler(prioOemBase, intel::netFnGeneral,
3395 intel::general::cmdGetProcessorErrConfig, Privilege::User,
3396 ipmiOEMGetProcessorErrConfig);
3397
Jason M. Bills42bd9c82019-06-28 16:39:34 -07003398 // <Set Processor Error Config>
Vernon Mauery98bbf692019-09-16 11:14:59 -07003399 registerHandler(prioOemBase, intel::netFnGeneral,
3400 intel::general::cmdSetProcessorErrConfig, Privilege::Admin,
3401 ipmiOEMSetProcessorErrConfig);
Jason M. Bills42bd9c82019-06-28 16:39:34 -07003402
Vernon Mauery98bbf692019-09-16 11:14:59 -07003403 ipmiPrintAndRegister(intel::netFnGeneral,
3404 intel::general::cmdSetShutdownPolicy, NULL,
3405 ipmiOEMSetShutdownPolicy, PRIVILEGE_ADMIN);
James Feist91244a62019-02-19 15:04:54 -08003406
Vernon Mauery98bbf692019-09-16 11:14:59 -07003407 ipmiPrintAndRegister(intel::netFnGeneral,
3408 intel::general::cmdGetShutdownPolicy, NULL,
3409 ipmiOEMGetShutdownPolicy, PRIVILEGE_ADMIN);
James Feist91244a62019-02-19 15:04:54 -08003410
Vernon Mauery98bbf692019-09-16 11:14:59 -07003411 ipmiPrintAndRegister(intel::netFnGeneral, intel::general::cmdSetFanConfig,
3412 NULL, ipmiOEMSetFanConfig, PRIVILEGE_USER);
James Feist91244a62019-02-19 15:04:54 -08003413
Vernon Mauery98bbf692019-09-16 11:14:59 -07003414 registerHandler(prioOemBase, intel::netFnGeneral,
3415 intel::general::cmdGetFanConfig, Privilege::User,
3416 ipmiOEMGetFanConfig);
James Feist5f957ca2019-03-14 15:33:55 -07003417
Vernon Mauery98bbf692019-09-16 11:14:59 -07003418 registerHandler(prioOemBase, intel::netFnGeneral,
3419 intel::general::cmdGetFanSpeedOffset, Privilege::User,
3420 ipmiOEMGetFanSpeedOffset);
James Feistacc8a4e2019-04-02 14:23:57 -07003421
Vernon Mauery98bbf692019-09-16 11:14:59 -07003422 registerHandler(prioOemBase, intel::netFnGeneral,
3423 intel::general::cmdSetFanSpeedOffset, Privilege::User,
3424 ipmiOEMSetFanSpeedOffset);
James Feistacc8a4e2019-04-02 14:23:57 -07003425
Vernon Mauery98bbf692019-09-16 11:14:59 -07003426 registerHandler(prioOemBase, intel::netFnGeneral,
3427 intel::general::cmdSetFscParameter, Privilege::User,
3428 ipmiOEMSetFscParameter);
James Feist5f957ca2019-03-14 15:33:55 -07003429
Vernon Mauery98bbf692019-09-16 11:14:59 -07003430 registerHandler(prioOemBase, intel::netFnGeneral,
3431 intel::general::cmdGetFscParameter, Privilege::User,
3432 ipmiOEMGetFscParameter);
Richard Marian Thomaiyarea537d52019-04-24 21:33:48 +05303433
Vernon Mauery98bbf692019-09-16 11:14:59 -07003434 registerHandler(prioOpenBmcBase, intel::netFnGeneral,
3435 intel::general::cmdReadBaseBoardProductId, Privilege::Admin,
3436 ipmiOEMReadBoardProductId);
Chen Yugang39736d52019-07-12 16:24:33 +08003437
Vernon Mauery98bbf692019-09-16 11:14:59 -07003438 registerHandler(prioOemBase, intel::netFnGeneral,
3439 intel::general::cmdGetNmiStatus, Privilege::User,
3440 ipmiOEMGetNmiSource);
Chen Yugang39736d52019-07-12 16:24:33 +08003441
Vernon Mauery98bbf692019-09-16 11:14:59 -07003442 registerHandler(prioOemBase, intel::netFnGeneral,
3443 intel::general::cmdSetNmiStatus, Privilege::Operator,
3444 ipmiOEMSetNmiSource);
Chen,Yugang4f7e76b2019-08-20 09:28:06 +08003445
Vernon Mauery98bbf692019-09-16 11:14:59 -07003446 registerHandler(prioOemBase, intel::netFnGeneral,
3447 intel::general::cmdGetEfiBootOptions, Privilege::User,
3448 ipmiOemGetEfiBootOptions);
Chen,Yugang4f7e76b2019-08-20 09:28:06 +08003449
Vernon Mauery98bbf692019-09-16 11:14:59 -07003450 registerHandler(prioOemBase, intel::netFnGeneral,
3451 intel::general::cmdSetEfiBootOptions, Privilege::Operator,
3452 ipmiOemSetEfiBootOptions);
Richard Marian Thomaiyard801e462019-06-20 01:05:40 +05303453
Vernon Mauery98bbf692019-09-16 11:14:59 -07003454 registerHandler(prioOemBase, intel::netFnGeneral,
3455 intel::general::cmdGetSecurityMode, Privilege::User,
3456 ipmiGetSecurityMode);
Richard Marian Thomaiyard801e462019-06-20 01:05:40 +05303457
Vernon Mauery98bbf692019-09-16 11:14:59 -07003458 registerHandler(prioOemBase, intel::netFnGeneral,
3459 intel::general::cmdSetSecurityMode, Privilege::Admin,
3460 ipmiSetSecurityMode);
Vernon Mauery4ac799d2019-05-20 15:50:37 -07003461
Vernon Mauery98bbf692019-09-16 11:14:59 -07003462 ipmiPrintAndRegister(intel::netFnGeneral, intel::general::cmdGetLEDStatus,
3463 NULL, ipmiOEMGetLEDStatus, PRIVILEGE_ADMIN);
Cheng C Yang773703a2019-08-15 09:41:11 +08003464
Vernon Mauery98bbf692019-09-16 11:14:59 -07003465 ipmiPrintAndRegister(ipmi::intel::netFnPlatform,
3466 ipmi::intel::platform::cmdCfgHostSerialPortSpeed, NULL,
3467 ipmiOEMCfgHostSerialPortSpeed, PRIVILEGE_ADMIN);
3468
3469 registerHandler(prioOemBase, intel::netFnGeneral,
3470 intel::general::cmdSetFaultIndication, Privilege::Operator,
3471 ipmiOEMSetFaultIndication);
3472
3473 registerHandler(prioOemBase, intel::netFnGeneral,
3474 intel::general::cmdSetColdRedundancyConfig, Privilege::User,
3475 ipmiOEMSetCRConfig);
3476
3477 registerHandler(prioOemBase, intel::netFnGeneral,
3478 intel::general::cmdGetColdRedundancyConfig, Privilege::User,
3479 ipmiOEMGetCRConfig);
3480
3481 registerHandler(prioOemBase, intel::netFnGeneral,
3482 intel::general::cmdRestoreConfiguration, Privilege::Admin,
Vernon Mauery4ac799d2019-05-20 15:50:37 -07003483 ipmiRestoreConfiguration);
James Feist63efafa2019-07-24 12:39:21 -07003484
Vernon Mauery98bbf692019-09-16 11:14:59 -07003485 registerHandler(prioOemBase, intel::netFnGeneral,
3486 intel::general::cmdSetDimmOffset, Privilege::Operator,
3487 ipmiOEMSetDimmOffset);
James Feist63efafa2019-07-24 12:39:21 -07003488
Vernon Mauery98bbf692019-09-16 11:14:59 -07003489 registerHandler(prioOemBase, intel::netFnGeneral,
3490 intel::general::cmdGetDimmOffset, Privilege::Operator,
3491 ipmiOEMGetDimmOffset);
Chen Yugangca12a7b2019-09-03 18:11:44 +08003492
Cheng C Yang4e6ee152019-09-25 10:27:44 +08003493 registerHandler(prioOemBase, intel::netFnGeneral,
3494 intel::general::cmdGetPSUVersion, Privilege::User,
3495 ipmiOEMGetPSUVersion);
Jia, Chunhuia835eaa2018-09-05 09:00:41 +08003496}
3497
3498} // namespace ipmi