blob: 07f6f2261e7389a884c3ac5a26d3a32d2cd5b778 [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
22#include <array>
James Feist91244a62019-02-19 15:04:54 -080023#include <boost/container/flat_map.hpp>
Yong Li23737fe2019-02-19 08:49:55 +080024#include <boost/process/child.hpp>
25#include <boost/process/io.hpp>
Chen Yugang39736d52019-07-12 16:24:33 +080026#include <com/intel/Control/NMISource/server.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>
36#include <sdbusplus/bus.hpp>
Suryakanth Sekard509eb92018-11-15 17:44:11 +053037#include <sdbusplus/message/types.hpp>
Jia, Chunhuia835eaa2018-09-05 09:00:41 +080038#include <string>
James Feist91244a62019-02-19 15:04:54 -080039#include <variant>
Jia, Chunhuia835eaa2018-09-05 09:00:41 +080040#include <vector>
Chen,Yugang4f7e76b2019-08-20 09:28:06 +080041#include <xyz/openbmc_project/Control/Boot/Mode/server.hpp>
42#include <xyz/openbmc_project/Control/Boot/Source/server.hpp>
Cheng C Yang773703a2019-08-15 09:41:11 +080043#include <xyz/openbmc_project/Control/PowerSupplyRedundancy/server.hpp>
Richard Marian Thomaiyard801e462019-06-20 01:05:40 +053044#include <xyz/openbmc_project/Control/Security/RestrictionMode/server.hpp>
Jia, Chunhuia835eaa2018-09-05 09:00:41 +080045
46namespace ipmi
47{
Jason M. Bills64796042018-10-03 16:51:55 -070048static void registerOEMFunctions() __attribute__((constructor));
Vernon Mauery4ac799d2019-05-20 15:50:37 -070049
Jason M. Bills64796042018-10-03 16:51:55 -070050static constexpr size_t maxFRUStringLength = 0x3F;
Jia, Chunhuia835eaa2018-09-05 09:00:41 +080051
Suryakanth Sekard509eb92018-11-15 17:44:11 +053052static constexpr auto ethernetIntf =
53 "xyz.openbmc_project.Network.EthernetInterface";
54static constexpr auto networkIPIntf = "xyz.openbmc_project.Network.IP";
55static constexpr auto networkService = "xyz.openbmc_project.Network";
56static constexpr auto networkRoot = "/xyz/openbmc_project/network";
57
Chen Yugang39736d52019-07-12 16:24:33 +080058static constexpr const char* oemNmiSourceIntf = "com.intel.Control.NMISource";
59static constexpr const char* oemNmiSourceObjPath =
60 "/com/intel/control/NMISource";
61static constexpr const char* oemNmiBmcSourceObjPathProp = "BMCSource";
62static constexpr const char* oemNmiEnabledObjPathProp = "Enabled";
63
James Feist63efafa2019-07-24 12:39:21 -070064static constexpr const char* dimmOffsetFile = "/var/lib/ipmi/ipmi_dimms.json";
65
Chen Yugang39736d52019-07-12 16:24:33 +080066enum class NmiSource : uint8_t
67{
68 none = 0,
69 fpBtn = 1,
70 wdPreTimeout = 2,
71 pefMatch = 3,
72 chassisCmd = 4,
73 memoryError = 5,
74 pciSerrPerr = 6,
75 southbridgeNmi = 7,
76 chipsetNmi = 8,
77};
78
Richard Marian Thomaiyard801e462019-06-20 01:05:40 +053079static constexpr const char* restricionModeService =
80 "xyz.openbmc_project.RestrictionMode.Manager";
81static constexpr const char* restricionModeBasePath =
82 "/xyz/openbmc_project/control/security/restriction_mode";
83static constexpr const char* restricionModeIntf =
84 "xyz.openbmc_project.Control.Security.RestrictionMode";
85static constexpr const char* restricionModeProperty = "RestrictionMode";
86
87static constexpr const char* specialModeService =
88 "xyz.openbmc_project.SpecialMode";
89static constexpr const char* specialModeBasePath =
Richard Marian Thomaiyara7b74282019-09-22 21:53:14 +053090 "/xyz/openbmc_project/security/special_mode";
Richard Marian Thomaiyard801e462019-06-20 01:05:40 +053091static constexpr const char* specialModeIntf =
92 "xyz.openbmc_project.Security.SpecialMode";
93static constexpr const char* specialModeProperty = "SpecialMode";
94
95static constexpr const char* dBusPropertyIntf =
96 "org.freedesktop.DBus.Properties";
97static constexpr const char* dBusPropertyGetMethod = "Get";
98static constexpr const char* dBusPropertySetMethod = "Set";
99
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800100// return code: 0 successful
101int8_t getChassisSerialNumber(sdbusplus::bus::bus& bus, std::string& serial)
102{
103 std::string objpath = "/xyz/openbmc_project/FruDevice";
104 std::string intf = "xyz.openbmc_project.FruDeviceManager";
105 std::string service = getService(bus, intf, objpath);
106 ObjectValueTree valueTree = getManagedObjects(bus, service, "/");
107 if (valueTree.empty())
108 {
109 phosphor::logging::log<phosphor::logging::level::ERR>(
110 "No object implements interface",
111 phosphor::logging::entry("INTF=%s", intf.c_str()));
112 return -1;
113 }
114
Jason M. Bills64796042018-10-03 16:51:55 -0700115 for (const auto& item : valueTree)
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800116 {
117 auto interface = item.second.find("xyz.openbmc_project.FruDevice");
118 if (interface == item.second.end())
119 {
120 continue;
121 }
122
123 auto property = interface->second.find("CHASSIS_SERIAL_NUMBER");
124 if (property == interface->second.end())
125 {
126 continue;
127 }
128
129 try
130 {
131 Value variant = property->second;
Vernon Mauery8166c8d2019-05-23 11:22:30 -0700132 std::string& result = std::get<std::string>(variant);
Jason M. Bills64796042018-10-03 16:51:55 -0700133 if (result.size() > maxFRUStringLength)
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800134 {
135 phosphor::logging::log<phosphor::logging::level::ERR>(
136 "FRU serial number exceed maximum length");
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800137 return -1;
138 }
Jason M. Bills64796042018-10-03 16:51:55 -0700139 serial = result;
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800140 return 0;
141 }
Vernon Mauery8166c8d2019-05-23 11:22:30 -0700142 catch (std::bad_variant_access& e)
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800143 {
Jason M. Bills64796042018-10-03 16:51:55 -0700144 phosphor::logging::log<phosphor::logging::level::ERR>(e.what());
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800145 return -1;
146 }
147 }
148 return -1;
149}
Jason M. Bills64796042018-10-03 16:51:55 -0700150
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800151ipmi_ret_t ipmiOEMWildcard(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
152 ipmi_request_t request, ipmi_response_t response,
Jason M. Bills64796042018-10-03 16:51:55 -0700153 ipmi_data_len_t dataLen, ipmi_context_t context)
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800154{
Jason M. Bills64796042018-10-03 16:51:55 -0700155 printCommand(+netfn, +cmd);
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800156 // Status code.
157 ipmi_ret_t rc = IPMI_CC_INVALID;
Jason M. Bills64796042018-10-03 16:51:55 -0700158 *dataLen = 0;
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800159 return rc;
160}
161
162// Returns the Chassis Identifier (serial #)
163ipmi_ret_t ipmiOEMGetChassisIdentifier(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
164 ipmi_request_t request,
165 ipmi_response_t response,
Jason M. Bills64796042018-10-03 16:51:55 -0700166 ipmi_data_len_t dataLen,
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800167 ipmi_context_t context)
168{
169 std::string serial;
Jason M. Bills64796042018-10-03 16:51:55 -0700170 if (*dataLen != 0) // invalid request if there are extra parameters
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800171 {
Jason M. Bills64796042018-10-03 16:51:55 -0700172 *dataLen = 0;
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800173 return IPMI_CC_REQ_DATA_LEN_INVALID;
174 }
Vernon Mauery15419dd2019-05-24 09:40:30 -0700175 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
176 if (getChassisSerialNumber(*dbus, serial) == 0)
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800177 {
Jason M. Bills64796042018-10-03 16:51:55 -0700178 *dataLen = serial.size(); // length will never exceed response length
179 // as it is checked in getChassisSerialNumber
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800180 char* resp = static_cast<char*>(response);
Jason M. Bills64796042018-10-03 16:51:55 -0700181 serial.copy(resp, *dataLen);
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800182 return IPMI_CC_OK;
183 }
Jason M. Bills64796042018-10-03 16:51:55 -0700184 *dataLen = 0;
185 return IPMI_CC_RESPONSE_ERROR;
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800186}
187
188ipmi_ret_t ipmiOEMSetSystemGUID(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
189 ipmi_request_t request,
190 ipmi_response_t response,
Jason M. Bills64796042018-10-03 16:51:55 -0700191 ipmi_data_len_t dataLen, ipmi_context_t context)
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800192{
193 static constexpr size_t safeBufferLength = 50;
194 char buf[safeBufferLength] = {0};
195 GUIDData* Data = reinterpret_cast<GUIDData*>(request);
196
Jason M. Bills64796042018-10-03 16:51:55 -0700197 if (*dataLen != sizeof(GUIDData)) // 16bytes
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800198 {
Jason M. Bills64796042018-10-03 16:51:55 -0700199 *dataLen = 0;
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800200 return IPMI_CC_REQ_DATA_LEN_INVALID;
201 }
202
Jason M. Bills64796042018-10-03 16:51:55 -0700203 *dataLen = 0;
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800204
205 snprintf(
206 buf, safeBufferLength,
207 "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x",
208 Data->timeLow4, Data->timeLow3, Data->timeLow2, Data->timeLow1,
209 Data->timeMid2, Data->timeMid1, Data->timeHigh2, Data->timeHigh1,
210 Data->clock2, Data->clock1, Data->node6, Data->node5, Data->node4,
211 Data->node3, Data->node2, Data->node1);
212 // UUID is in RFC4122 format. Ex: 61a39523-78f2-11e5-9862-e6402cfc3223
213 std::string guid = buf;
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800214
215 std::string objpath = "/xyz/openbmc_project/control/host0/systemGUID";
216 std::string intf = "xyz.openbmc_project.Common.UUID";
Vernon Mauery15419dd2019-05-24 09:40:30 -0700217 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
218 std::string service = getService(*dbus, intf, objpath);
219 setDbusProperty(*dbus, service, objpath, intf, "UUID", guid);
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800220 return IPMI_CC_OK;
221}
222
Jason M. Billsb02bf092019-08-15 13:01:56 -0700223ipmi::RspType<> ipmiOEMDisableBMCSystemReset(bool disableResetOnSMI,
224 uint7_t reserved1)
225{
226 std::shared_ptr<sdbusplus::asio::connection> busp = getSdBus();
227
228 try
229 {
230 auto service =
231 ipmi::getService(*busp, bmcResetDisablesIntf, bmcResetDisablesPath);
232 ipmi::setDbusProperty(*busp, service, bmcResetDisablesPath,
233 bmcResetDisablesIntf, "ResetOnSMI",
234 !disableResetOnSMI);
235 }
236 catch (std::exception& e)
237 {
238 phosphor::logging::log<phosphor::logging::level::ERR>(
239 "Failed to set BMC reset disables",
240 phosphor::logging::entry("EXCEPTION=%s", e.what()));
241 return ipmi::responseUnspecifiedError();
242 }
243
244 return ipmi::responseSuccess();
245}
246
247ipmi::RspType<bool, // disableResetOnSMI
248 uint7_t // reserved
249 >
250 ipmiOEMGetBMCResetDisables()
251{
252 bool disableResetOnSMI = true;
253
254 std::shared_ptr<sdbusplus::asio::connection> busp = getSdBus();
255 try
256 {
257 auto service =
258 ipmi::getService(*busp, bmcResetDisablesIntf, bmcResetDisablesPath);
259 Value variant =
260 ipmi::getDbusProperty(*busp, service, bmcResetDisablesPath,
261 bmcResetDisablesIntf, "ResetOnSMI");
262 disableResetOnSMI = !std::get<bool>(variant);
263 }
264 catch (std::exception& e)
265 {
266 phosphor::logging::log<phosphor::logging::level::ERR>(
267 "Failed to get BMC reset disables",
268 phosphor::logging::entry("EXCEPTION=%s", e.what()));
269 return ipmi::responseUnspecifiedError();
270 }
271
272 return ipmi::responseSuccess(disableResetOnSMI, 0);
273}
274
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800275ipmi_ret_t ipmiOEMSetBIOSID(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
276 ipmi_request_t request, ipmi_response_t response,
277 ipmi_data_len_t dataLen, ipmi_context_t context)
278{
279 DeviceInfo* data = reinterpret_cast<DeviceInfo*>(request);
280
Jason M. Bills64796042018-10-03 16:51:55 -0700281 if ((*dataLen < 2) || (*dataLen != (1 + data->biosIDLength)))
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800282 {
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800283 *dataLen = 0;
284 return IPMI_CC_REQ_DATA_LEN_INVALID;
285 }
Jason M. Bills64796042018-10-03 16:51:55 -0700286 std::string idString((char*)data->biosId, data->biosIDLength);
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800287
Vernon Mauery15419dd2019-05-24 09:40:30 -0700288 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
289 std::string service = getService(*dbus, biosIntf, biosObjPath);
290 setDbusProperty(*dbus, service, biosObjPath, biosIntf, biosProp, idString);
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800291 uint8_t* bytesWritten = static_cast<uint8_t*>(response);
292 *bytesWritten =
Jason M. Bills64796042018-10-03 16:51:55 -0700293 data->biosIDLength; // how many bytes are written into storage
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800294 *dataLen = 1;
295 return IPMI_CC_OK;
296}
297
298ipmi_ret_t ipmiOEMGetDeviceInfo(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
299 ipmi_request_t request,
300 ipmi_response_t response,
301 ipmi_data_len_t dataLen, ipmi_context_t context)
302{
303 GetOemDeviceInfoReq* req = reinterpret_cast<GetOemDeviceInfoReq*>(request);
304 GetOemDeviceInfoRes* res = reinterpret_cast<GetOemDeviceInfoRes*>(response);
305
306 if (*dataLen == 0)
307 {
Jason M. Bills64796042018-10-03 16:51:55 -0700308 *dataLen = 0;
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800309 return IPMI_CC_REQ_DATA_LEN_INVALID;
310 }
311
312 size_t reqDataLen = *dataLen;
313 *dataLen = 0;
Jason M. Bills64796042018-10-03 16:51:55 -0700314 if (req->entityType > static_cast<uint8_t>(OEMDevEntityType::sdrVer))
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800315 {
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800316 return IPMI_CC_INVALID_FIELD_REQUEST;
317 }
318
319 // handle OEM command items
Jason M. Bills64796042018-10-03 16:51:55 -0700320 switch (OEMDevEntityType(req->entityType))
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800321 {
322 case OEMDevEntityType::biosId:
323 {
324 if (sizeof(GetOemDeviceInfoReq) != reqDataLen)
325 {
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800326 return IPMI_CC_REQ_DATA_LEN_INVALID;
327 }
328
Vernon Mauery15419dd2019-05-24 09:40:30 -0700329 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
330 std::string service = getService(*dbus, biosIntf, biosObjPath);
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800331 try
332 {
Vernon Mauery15419dd2019-05-24 09:40:30 -0700333 Value variant = getDbusProperty(*dbus, service, biosObjPath,
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800334 biosIntf, biosProp);
Vernon Mauery8166c8d2019-05-23 11:22:30 -0700335 std::string& idString = std::get<std::string>(variant);
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800336 if (req->offset >= idString.size())
337 {
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800338 return IPMI_CC_PARM_OUT_OF_RANGE;
339 }
Jason M. Bills64796042018-10-03 16:51:55 -0700340 size_t length = 0;
341 if (req->countToRead > (idString.size() - req->offset))
342 {
343 length = idString.size() - req->offset;
344 }
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800345 else
346 {
Jason M. Bills64796042018-10-03 16:51:55 -0700347 length = req->countToRead;
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800348 }
Jason M. Bills64796042018-10-03 16:51:55 -0700349 std::copy(idString.begin() + req->offset, idString.end(),
350 res->data);
351 res->resDatalen = length;
352 *dataLen = res->resDatalen + 1;
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800353 }
Vernon Mauery8166c8d2019-05-23 11:22:30 -0700354 catch (std::bad_variant_access& e)
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800355 {
Jason M. Bills64796042018-10-03 16:51:55 -0700356 phosphor::logging::log<phosphor::logging::level::ERR>(e.what());
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800357 return IPMI_CC_UNSPECIFIED_ERROR;
358 }
359 }
360 break;
361
362 case OEMDevEntityType::devVer:
363 case OEMDevEntityType::sdrVer:
364 // TODO:
365 return IPMI_CC_ILLEGAL_COMMAND;
366 default:
367 return IPMI_CC_INVALID_FIELD_REQUEST;
368 }
369 return IPMI_CC_OK;
370}
371
372ipmi_ret_t ipmiOEMGetAICFRU(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
373 ipmi_request_t request, ipmi_response_t response,
374 ipmi_data_len_t dataLen, ipmi_context_t context)
375{
376 if (*dataLen != 0)
377 {
Jason M. Bills64796042018-10-03 16:51:55 -0700378 *dataLen = 0;
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800379 return IPMI_CC_REQ_DATA_LEN_INVALID;
380 }
381
382 *dataLen = 1;
383 uint8_t* res = reinterpret_cast<uint8_t*>(response);
384 // temporary fix. We don't support AIC FRU now. Just tell BIOS that no
385 // AIC is available so that BIOS will not timeout repeatly which leads to
386 // slow booting.
387 *res = 0; // Byte1=Count of SlotPosition/FruID records.
388 return IPMI_CC_OK;
389}
390
Jason M. Bills64796042018-10-03 16:51:55 -0700391ipmi_ret_t ipmiOEMGetPowerRestoreDelay(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
392 ipmi_request_t request,
393 ipmi_response_t response,
394 ipmi_data_len_t dataLen,
395 ipmi_context_t context)
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800396{
Jason M. Bills64796042018-10-03 16:51:55 -0700397 GetPowerRestoreDelayRes* resp =
398 reinterpret_cast<GetPowerRestoreDelayRes*>(response);
399
400 if (*dataLen != 0)
401 {
402 *dataLen = 0;
403 return IPMI_CC_REQ_DATA_LEN_INVALID;
404 }
405
Vernon Mauery15419dd2019-05-24 09:40:30 -0700406 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
Jason M. Bills64796042018-10-03 16:51:55 -0700407 std::string service =
Vernon Mauery15419dd2019-05-24 09:40:30 -0700408 getService(*dbus, powerRestoreDelayIntf, powerRestoreDelayObjPath);
Jason M. Bills64796042018-10-03 16:51:55 -0700409 Value variant =
Vernon Mauery15419dd2019-05-24 09:40:30 -0700410 getDbusProperty(*dbus, service, powerRestoreDelayObjPath,
Jason M. Bills64796042018-10-03 16:51:55 -0700411 powerRestoreDelayIntf, powerRestoreDelayProp);
412
Vernon Mauery8166c8d2019-05-23 11:22:30 -0700413 uint16_t delay = std::get<uint16_t>(variant);
Jason M. Bills64796042018-10-03 16:51:55 -0700414 resp->byteLSB = delay;
415 resp->byteMSB = delay >> 8;
416
417 *dataLen = sizeof(GetPowerRestoreDelayRes);
418
419 return IPMI_CC_OK;
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800420}
421
Jia, Chunhuicc49b542019-03-20 15:41:07 +0800422static uint8_t bcdToDec(uint8_t val)
423{
424 return ((val / 16 * 10) + (val % 16));
425}
426
427// Allows an update utility or system BIOS to send the status of an embedded
428// firmware update attempt to the BMC. After received, BMC will create a logging
429// record.
430ipmi::RspType<> ipmiOEMSendEmbeddedFwUpdStatus(uint8_t status, uint8_t target,
431 uint8_t majorRevision,
432 uint8_t minorRevision,
433 uint32_t auxInfo)
434{
435 std::string firmware;
Jason M. Billsdc249272019-04-03 09:58:40 -0700436 int instance = (target & targetInstanceMask) >> targetInstanceShift;
Jia, Chunhuicc49b542019-03-20 15:41:07 +0800437 target = (target & selEvtTargetMask) >> selEvtTargetShift;
438
439 /* make sure the status is 0, 1, or 2 as per the spec */
440 if (status > 2)
441 {
442 return ipmi::response(ipmi::ccInvalidFieldRequest);
443 }
Jason M. Billsdc249272019-04-03 09:58:40 -0700444 /* make sure the target is 0, 1, 2, or 4 as per the spec */
445 if (target > 4 || target == 3)
446 {
447 return ipmi::response(ipmi::ccInvalidFieldRequest);
448 }
Jia, Chunhuicc49b542019-03-20 15:41:07 +0800449 /*orignal OEM command is to record OEM SEL.
450 But openbmc does not support OEM SEL, so we redirect it to redfish event
451 logging. */
452 std::string buildInfo;
453 std::string action;
454 switch (FWUpdateTarget(target))
455 {
456 case FWUpdateTarget::targetBMC:
457 firmware = "BMC";
Jason M. Billsdc249272019-04-03 09:58:40 -0700458 buildInfo = "major: " + std::to_string(majorRevision) + " minor: " +
Jia, Chunhuicc49b542019-03-20 15:41:07 +0800459 std::to_string(bcdToDec(minorRevision)) + // BCD encoded
460 " BuildID: " + std::to_string(auxInfo);
461 buildInfo += std::to_string(auxInfo);
462 break;
463 case FWUpdateTarget::targetBIOS:
464 firmware = "BIOS";
465 buildInfo =
Jason M. Billsdc249272019-04-03 09:58:40 -0700466 "major: " +
Jia, Chunhuicc49b542019-03-20 15:41:07 +0800467 std::to_string(bcdToDec(majorRevision)) + // BCD encoded
468 " minor: " +
469 std::to_string(bcdToDec(minorRevision)) + // BCD encoded
470 " ReleaseNumber: " + // ASCII encoded
471 std::to_string(static_cast<uint8_t>(auxInfo >> 0) - '0') +
472 std::to_string(static_cast<uint8_t>(auxInfo >> 8) - '0') +
473 std::to_string(static_cast<uint8_t>(auxInfo >> 16) - '0') +
474 std::to_string(static_cast<uint8_t>(auxInfo >> 24) - '0');
475 break;
476 case FWUpdateTarget::targetME:
477 firmware = "ME";
478 buildInfo =
Jason M. Billsdc249272019-04-03 09:58:40 -0700479 "major: " + std::to_string(majorRevision) + " minor1: " +
Jia, Chunhuicc49b542019-03-20 15:41:07 +0800480 std::to_string(bcdToDec(minorRevision)) + // BCD encoded
481 " minor2: " +
482 std::to_string(bcdToDec(static_cast<uint8_t>(auxInfo >> 0))) +
483 " build1: " +
484 std::to_string(bcdToDec(static_cast<uint8_t>(auxInfo >> 8))) +
485 " build2: " +
486 std::to_string(bcdToDec(static_cast<uint8_t>(auxInfo >> 16)));
487 break;
488 case FWUpdateTarget::targetOEMEWS:
489 firmware = "EWS";
Jason M. Billsdc249272019-04-03 09:58:40 -0700490 buildInfo = "major: " + std::to_string(majorRevision) + " minor: " +
Jia, Chunhuicc49b542019-03-20 15:41:07 +0800491 std::to_string(bcdToDec(minorRevision)) + // BCD encoded
492 " BuildID: " + std::to_string(auxInfo);
493 break;
494 }
495
Jason M. Billsdc249272019-04-03 09:58:40 -0700496 static const std::string openBMCMessageRegistryVersion("0.1");
497 std::string redfishMsgID = "OpenBMC." + openBMCMessageRegistryVersion;
498
Jia, Chunhuicc49b542019-03-20 15:41:07 +0800499 switch (status)
500 {
501 case 0x0:
502 action = "update started";
Jason M. Billsdc249272019-04-03 09:58:40 -0700503 redfishMsgID += ".FirmwareUpdateStarted";
Jia, Chunhuicc49b542019-03-20 15:41:07 +0800504 break;
505 case 0x1:
506 action = "update completed successfully";
Jason M. Billsdc249272019-04-03 09:58:40 -0700507 redfishMsgID += ".FirmwareUpdateCompleted";
Jia, Chunhuicc49b542019-03-20 15:41:07 +0800508 break;
509 case 0x2:
510 action = "update failure";
Jason M. Billsdc249272019-04-03 09:58:40 -0700511 redfishMsgID += ".FirmwareUpdateFailed";
Jia, Chunhuicc49b542019-03-20 15:41:07 +0800512 break;
513 default:
514 action = "unknown";
515 break;
516 }
517
Jason M. Billsdc249272019-04-03 09:58:40 -0700518 std::string firmwareInstanceStr =
519 firmware + " instance: " + std::to_string(instance);
520 std::string message("[firmware update] " + firmwareInstanceStr +
521 " status: <" + action + "> " + buildInfo);
Jia, Chunhuicc49b542019-03-20 15:41:07 +0800522
523 sd_journal_send("MESSAGE=%s", message.c_str(), "PRIORITY=%i", LOG_INFO,
Jason M. Billsdc249272019-04-03 09:58:40 -0700524 "REDFISH_MESSAGE_ID=%s", redfishMsgID.c_str(),
525 "REDFISH_MESSAGE_ARGS=%s,%s", firmwareInstanceStr.c_str(),
526 buildInfo.c_str(), NULL);
Jia, Chunhuicc49b542019-03-20 15:41:07 +0800527 return ipmi::responseSuccess();
528}
529
Jason M. Bills64796042018-10-03 16:51:55 -0700530ipmi_ret_t ipmiOEMSetPowerRestoreDelay(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
531 ipmi_request_t request,
532 ipmi_response_t response,
533 ipmi_data_len_t dataLen,
534 ipmi_context_t context)
535{
536 SetPowerRestoreDelayReq* data =
537 reinterpret_cast<SetPowerRestoreDelayReq*>(request);
538 uint16_t delay = 0;
539
540 if (*dataLen != sizeof(SetPowerRestoreDelayReq))
541 {
542 *dataLen = 0;
543 return IPMI_CC_REQ_DATA_LEN_INVALID;
544 }
545 delay = data->byteMSB;
546 delay = (delay << 8) | data->byteLSB;
Vernon Mauery15419dd2019-05-24 09:40:30 -0700547 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
Jason M. Bills64796042018-10-03 16:51:55 -0700548 std::string service =
Vernon Mauery15419dd2019-05-24 09:40:30 -0700549 getService(*dbus, powerRestoreDelayIntf, powerRestoreDelayObjPath);
550 setDbusProperty(*dbus, service, powerRestoreDelayObjPath,
Jason M. Bills64796042018-10-03 16:51:55 -0700551 powerRestoreDelayIntf, powerRestoreDelayProp, delay);
552 *dataLen = 0;
553
554 return IPMI_CC_OK;
555}
556
Jason M. Bills42bd9c82019-06-28 16:39:34 -0700557static bool cpuPresent(const std::string& cpuName)
Jason M. Bills64796042018-10-03 16:51:55 -0700558{
Jason M. Bills42bd9c82019-06-28 16:39:34 -0700559 static constexpr const char* cpuPresencePathPrefix =
560 "/xyz/openbmc_project/inventory/system/chassis/motherboard/";
561 static constexpr const char* cpuPresenceIntf =
562 "xyz.openbmc_project.Inventory.Item";
563 std::string cpuPresencePath = cpuPresencePathPrefix + cpuName;
564 std::shared_ptr<sdbusplus::asio::connection> busp = getSdBus();
565 try
Jason M. Bills64796042018-10-03 16:51:55 -0700566 {
Jason M. Bills42bd9c82019-06-28 16:39:34 -0700567 auto service =
568 ipmi::getService(*busp, cpuPresenceIntf, cpuPresencePath);
569
570 ipmi::Value result = ipmi::getDbusProperty(
571 *busp, service, cpuPresencePath, cpuPresenceIntf, "Present");
572 return std::get<bool>(result);
573 }
574 catch (const std::exception& e)
575 {
576 phosphor::logging::log<phosphor::logging::level::INFO>(
577 "Cannot find processor presence",
578 phosphor::logging::entry("NAME=%s", cpuName.c_str()));
579 return false;
580 }
581}
582
583ipmi::RspType<bool, // CATERR Reset Enabled
584 bool, // ERR2 Reset Enabled
585 uint6_t, // reserved
586 uint8_t, // reserved, returns 0x3F
587 uint6_t, // CPU1 CATERR Count
588 uint2_t, // CPU1 Status
589 uint6_t, // CPU2 CATERR Count
590 uint2_t, // CPU2 Status
591 uint6_t, // CPU3 CATERR Count
592 uint2_t, // CPU3 Status
593 uint6_t, // CPU4 CATERR Count
594 uint2_t, // CPU4 Status
595 uint8_t // Crashdump Count
596 >
597 ipmiOEMGetProcessorErrConfig()
598{
599 bool resetOnCATERR = false;
600 bool resetOnERR2 = false;
601 uint6_t cpu1CATERRCount = 0;
602 uint6_t cpu2CATERRCount = 0;
603 uint6_t cpu3CATERRCount = 0;
604 uint6_t cpu4CATERRCount = 0;
605 uint8_t crashdumpCount = 0;
606 uint2_t cpu1Status =
607 cpuPresent("CPU_1") ? CPUStatus::enabled : CPUStatus::notPresent;
608 uint2_t cpu2Status =
609 cpuPresent("CPU_2") ? CPUStatus::enabled : CPUStatus::notPresent;
610 uint2_t cpu3Status =
611 cpuPresent("CPU_3") ? CPUStatus::enabled : CPUStatus::notPresent;
612 uint2_t cpu4Status =
613 cpuPresent("CPU_4") ? CPUStatus::enabled : CPUStatus::notPresent;
614
615 std::shared_ptr<sdbusplus::asio::connection> busp = getSdBus();
616 try
617 {
618 auto service = ipmi::getService(*busp, processorErrConfigIntf,
619 processorErrConfigObjPath);
620
621 ipmi::PropertyMap result = ipmi::getAllDbusProperties(
622 *busp, service, processorErrConfigObjPath, processorErrConfigIntf);
623 resetOnCATERR = std::get<bool>(result.at("ResetOnCATERR"));
624 resetOnERR2 = std::get<bool>(result.at("ResetOnERR2"));
625 cpu1CATERRCount = std::get<uint8_t>(result.at("ErrorCountCPU1"));
626 cpu2CATERRCount = std::get<uint8_t>(result.at("ErrorCountCPU2"));
627 cpu3CATERRCount = std::get<uint8_t>(result.at("ErrorCountCPU3"));
628 cpu4CATERRCount = std::get<uint8_t>(result.at("ErrorCountCPU4"));
629 crashdumpCount = std::get<uint8_t>(result.at("CrashdumpCount"));
630 }
631 catch (const std::exception& e)
632 {
633 phosphor::logging::log<phosphor::logging::level::ERR>(
634 "Failed to fetch processor error config",
635 phosphor::logging::entry("ERROR=%s", e.what()));
636 return ipmi::responseUnspecifiedError();
Jason M. Bills64796042018-10-03 16:51:55 -0700637 }
638
Jason M. Bills42bd9c82019-06-28 16:39:34 -0700639 return ipmi::responseSuccess(resetOnCATERR, resetOnERR2, 0, 0x3F,
640 cpu1CATERRCount, cpu1Status, cpu2CATERRCount,
641 cpu2Status, cpu3CATERRCount, cpu3Status,
642 cpu4CATERRCount, cpu4Status, crashdumpCount);
643}
Jason M. Bills64796042018-10-03 16:51:55 -0700644
Jason M. Bills42bd9c82019-06-28 16:39:34 -0700645ipmi::RspType<> ipmiOEMSetProcessorErrConfig(
646 bool resetOnCATERR, bool resetOnERR2, uint6_t reserved1, uint8_t reserved2,
647 std::optional<bool> clearCPUErrorCount,
648 std::optional<bool> clearCrashdumpCount, std::optional<uint6_t> reserved3)
649{
650 std::shared_ptr<sdbusplus::asio::connection> busp = getSdBus();
Jason M. Bills64796042018-10-03 16:51:55 -0700651
652 try
653 {
Jason M. Bills42bd9c82019-06-28 16:39:34 -0700654 auto service = ipmi::getService(*busp, processorErrConfigIntf,
655 processorErrConfigObjPath);
656 ipmi::setDbusProperty(*busp, service, processorErrConfigObjPath,
657 processorErrConfigIntf, "ResetOnCATERR",
658 resetOnCATERR);
659 ipmi::setDbusProperty(*busp, service, processorErrConfigObjPath,
660 processorErrConfigIntf, "ResetOnERR2",
661 resetOnERR2);
662 if (clearCPUErrorCount.value_or(false))
663 {
664 ipmi::setDbusProperty(*busp, service, processorErrConfigObjPath,
Jason M. Billsd3e19932019-08-15 12:39:03 -0700665 processorErrConfigIntf, "ErrorCountCPU1",
666 static_cast<uint8_t>(0));
Jason M. Bills42bd9c82019-06-28 16:39:34 -0700667 ipmi::setDbusProperty(*busp, service, processorErrConfigObjPath,
Jason M. Billsd3e19932019-08-15 12:39:03 -0700668 processorErrConfigIntf, "ErrorCountCPU2",
669 static_cast<uint8_t>(0));
670 ipmi::setDbusProperty(*busp, service, processorErrConfigObjPath,
671 processorErrConfigIntf, "ErrorCountCPU3",
672 static_cast<uint8_t>(0));
673 ipmi::setDbusProperty(*busp, service, processorErrConfigObjPath,
674 processorErrConfigIntf, "ErrorCountCPU4",
675 static_cast<uint8_t>(0));
Jason M. Bills42bd9c82019-06-28 16:39:34 -0700676 }
677 if (clearCrashdumpCount.value_or(false))
678 {
679 ipmi::setDbusProperty(*busp, service, processorErrConfigObjPath,
Jason M. Billsd3e19932019-08-15 12:39:03 -0700680 processorErrConfigIntf, "CrashdumpCount",
681 static_cast<uint8_t>(0));
Jason M. Bills42bd9c82019-06-28 16:39:34 -0700682 }
Jason M. Bills64796042018-10-03 16:51:55 -0700683 }
Jason M. Bills42bd9c82019-06-28 16:39:34 -0700684 catch (std::exception& e)
Jason M. Bills64796042018-10-03 16:51:55 -0700685 {
Kuiying Wangbc546672018-11-23 15:41:05 +0800686 phosphor::logging::log<phosphor::logging::level::ERR>(
Jason M. Bills42bd9c82019-06-28 16:39:34 -0700687 "Failed to set processor error config",
688 phosphor::logging::entry("EXCEPTION=%s", e.what()));
689 return ipmi::responseUnspecifiedError();
Jason M. Bills64796042018-10-03 16:51:55 -0700690 }
691
Jason M. Bills42bd9c82019-06-28 16:39:34 -0700692 return ipmi::responseSuccess();
Jason M. Bills64796042018-10-03 16:51:55 -0700693}
694
Yong Li703922d2018-11-06 13:25:31 +0800695ipmi_ret_t ipmiOEMGetShutdownPolicy(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
696 ipmi_request_t request,
697 ipmi_response_t response,
698 ipmi_data_len_t dataLen,
699 ipmi_context_t context)
700{
701 GetOEMShutdownPolicyRes* resp =
702 reinterpret_cast<GetOEMShutdownPolicyRes*>(response);
703
704 if (*dataLen != 0)
705 {
706 phosphor::logging::log<phosphor::logging::level::ERR>(
Kuiying Wang45f04982018-12-26 09:23:08 +0800707 "oem_get_shutdown_policy: invalid input len!");
Yong Li703922d2018-11-06 13:25:31 +0800708 *dataLen = 0;
709 return IPMI_CC_REQ_DATA_LEN_INVALID;
710 }
711
712 *dataLen = 0;
713
714 try
715 {
Vernon Mauery15419dd2019-05-24 09:40:30 -0700716 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
Yong Li703922d2018-11-06 13:25:31 +0800717 std::string service =
Vernon Mauery15419dd2019-05-24 09:40:30 -0700718 getService(*dbus, oemShutdownPolicyIntf, oemShutdownPolicyObjPath);
719 Value variant = getDbusProperty(
720 *dbus, service, oemShutdownPolicyObjPath, oemShutdownPolicyIntf,
721 oemShutdownPolicyObjPathProp);
Yong Li0669d192019-05-06 14:01:46 +0800722
723 if (sdbusplus::com::intel::Control::server::OCOTShutdownPolicy::
724 convertPolicyFromString(std::get<std::string>(variant)) ==
725 sdbusplus::com::intel::Control::server::OCOTShutdownPolicy::Policy::
726 NoShutdownOnOCOT)
727 {
728 resp->policy = 0;
729 }
730 else if (sdbusplus::com::intel::Control::server::OCOTShutdownPolicy::
731 convertPolicyFromString(std::get<std::string>(variant)) ==
732 sdbusplus::com::intel::Control::server::OCOTShutdownPolicy::
733 Policy::ShutdownOnOCOT)
734 {
735 resp->policy = 1;
736 }
737 else
738 {
739 phosphor::logging::log<phosphor::logging::level::ERR>(
740 "oem_set_shutdown_policy: invalid property!",
741 phosphor::logging::entry(
742 "PROP=%s", std::get<std::string>(variant).c_str()));
743 return IPMI_CC_UNSPECIFIED_ERROR;
744 }
Yong Li703922d2018-11-06 13:25:31 +0800745 // TODO needs to check if it is multi-node products,
746 // policy is only supported on node 3/4
747 resp->policySupport = shutdownPolicySupported;
748 }
749 catch (sdbusplus::exception_t& e)
750 {
751 phosphor::logging::log<phosphor::logging::level::ERR>(e.description());
752 return IPMI_CC_UNSPECIFIED_ERROR;
753 }
754
755 *dataLen = sizeof(GetOEMShutdownPolicyRes);
756 return IPMI_CC_OK;
757}
758
759ipmi_ret_t ipmiOEMSetShutdownPolicy(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
760 ipmi_request_t request,
761 ipmi_response_t response,
762 ipmi_data_len_t dataLen,
763 ipmi_context_t context)
764{
765 uint8_t* req = reinterpret_cast<uint8_t*>(request);
Yong Li0669d192019-05-06 14:01:46 +0800766 sdbusplus::com::intel::Control::server::OCOTShutdownPolicy::Policy policy =
767 sdbusplus::com::intel::Control::server::OCOTShutdownPolicy::Policy::
768 NoShutdownOnOCOT;
Yong Li703922d2018-11-06 13:25:31 +0800769
770 // TODO needs to check if it is multi-node products,
771 // policy is only supported on node 3/4
772 if (*dataLen != 1)
773 {
774 phosphor::logging::log<phosphor::logging::level::ERR>(
775 "oem_set_shutdown_policy: invalid input len!");
776 *dataLen = 0;
777 return IPMI_CC_REQ_DATA_LEN_INVALID;
778 }
779
780 *dataLen = 0;
781 if ((*req != noShutdownOnOCOT) && (*req != shutdownOnOCOT))
782 {
783 phosphor::logging::log<phosphor::logging::level::ERR>(
784 "oem_set_shutdown_policy: invalid input!");
785 return IPMI_CC_INVALID_FIELD_REQUEST;
786 }
787
Yong Li0669d192019-05-06 14:01:46 +0800788 if (*req == noShutdownOnOCOT)
789 {
790 policy = sdbusplus::com::intel::Control::server::OCOTShutdownPolicy::
791 Policy::NoShutdownOnOCOT;
792 }
793 else
794 {
795 policy = sdbusplus::com::intel::Control::server::OCOTShutdownPolicy::
796 Policy::ShutdownOnOCOT;
797 }
798
Yong Li703922d2018-11-06 13:25:31 +0800799 try
800 {
Vernon Mauery15419dd2019-05-24 09:40:30 -0700801 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
Yong Li703922d2018-11-06 13:25:31 +0800802 std::string service =
Vernon Mauery15419dd2019-05-24 09:40:30 -0700803 getService(*dbus, oemShutdownPolicyIntf, oemShutdownPolicyObjPath);
Yong Li0669d192019-05-06 14:01:46 +0800804 setDbusProperty(
Vernon Mauery15419dd2019-05-24 09:40:30 -0700805 *dbus, service, oemShutdownPolicyObjPath, oemShutdownPolicyIntf,
Yong Li0669d192019-05-06 14:01:46 +0800806 oemShutdownPolicyObjPathProp,
807 sdbusplus::com::intel::Control::server::convertForMessage(policy));
Yong Li703922d2018-11-06 13:25:31 +0800808 }
809 catch (sdbusplus::exception_t& e)
810 {
811 phosphor::logging::log<phosphor::logging::level::ERR>(e.description());
812 return IPMI_CC_UNSPECIFIED_ERROR;
813 }
814
815 return IPMI_CC_OK;
816}
817
Suryakanth Sekard509eb92018-11-15 17:44:11 +0530818/** @brief implementation for check the DHCP or not in IPv4
819 * @param[in] Channel - Channel number
820 * @returns true or false.
821 */
822static bool isDHCPEnabled(uint8_t Channel)
823{
824 try
825 {
826 auto ethdevice = getChannelName(Channel);
827 if (ethdevice.empty())
828 {
829 return false;
830 }
831 auto ethIP = ethdevice + "/ipv4";
Vernon Mauery15419dd2019-05-24 09:40:30 -0700832 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
Suryakanth Sekard509eb92018-11-15 17:44:11 +0530833 auto ethernetObj =
Vernon Mauery15419dd2019-05-24 09:40:30 -0700834 getDbusObject(*dbus, networkIPIntf, networkRoot, ethIP);
835 auto value = getDbusProperty(*dbus, networkService, ethernetObj.first,
Suryakanth Sekard509eb92018-11-15 17:44:11 +0530836 networkIPIntf, "Origin");
Vernon Mauery8166c8d2019-05-23 11:22:30 -0700837 if (std::get<std::string>(value) ==
Suryakanth Sekard509eb92018-11-15 17:44:11 +0530838 "xyz.openbmc_project.Network.IP.AddressOrigin.DHCP")
839 {
840 return true;
841 }
842 else
843 {
844 return false;
845 }
846 }
847 catch (sdbusplus::exception_t& e)
848 {
849 phosphor::logging::log<phosphor::logging::level::ERR>(e.description());
850 return true;
851 }
852}
853
854/** @brief implementes for check the DHCP or not in IPv6
855 * @param[in] Channel - Channel number
856 * @returns true or false.
857 */
858static bool isDHCPIPv6Enabled(uint8_t Channel)
859{
860
861 try
862 {
863 auto ethdevice = getChannelName(Channel);
864 if (ethdevice.empty())
865 {
866 return false;
867 }
868 auto ethIP = ethdevice + "/ipv6";
Vernon Mauery15419dd2019-05-24 09:40:30 -0700869 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
Suryakanth Sekard509eb92018-11-15 17:44:11 +0530870 auto objectInfo =
Vernon Mauery15419dd2019-05-24 09:40:30 -0700871 getDbusObject(*dbus, networkIPIntf, networkRoot, ethIP);
872 auto properties = getAllDbusProperties(*dbus, objectInfo.second,
Suryakanth Sekard509eb92018-11-15 17:44:11 +0530873 objectInfo.first, networkIPIntf);
Vernon Mauery8166c8d2019-05-23 11:22:30 -0700874 if (std::get<std::string>(properties["Origin"]) ==
Suryakanth Sekard509eb92018-11-15 17:44:11 +0530875 "xyz.openbmc_project.Network.IP.AddressOrigin.DHCP")
876 {
877 return true;
878 }
879 else
880 {
881 return false;
882 }
883 }
884 catch (sdbusplus::exception_t& e)
885 {
886 phosphor::logging::log<phosphor::logging::level::ERR>(e.description());
887 return true;
888 }
889}
890
891/** @brief implementes the creating of default new user
892 * @param[in] userName - new username in 16 bytes.
893 * @param[in] userPassword - new password in 20 bytes
894 * @returns ipmi completion code.
895 */
896ipmi::RspType<> ipmiOEMSetUser2Activation(
897 std::array<uint8_t, ipmi::ipmiMaxUserName>& userName,
898 std::array<uint8_t, ipmi::maxIpmi20PasswordSize>& userPassword)
899{
900 bool userState = false;
901 // Check for System Interface not exist and LAN should be static
902 for (uint8_t channel = 0; channel < maxIpmiChannels; channel++)
903 {
904 ChannelInfo chInfo;
905 try
906 {
907 getChannelInfo(channel, chInfo);
908 }
909 catch (sdbusplus::exception_t& e)
910 {
911 phosphor::logging::log<phosphor::logging::level::ERR>(
912 "ipmiOEMSetUser2Activation: Failed to get Channel Info",
913 phosphor::logging::entry("MSG: %s", e.description()));
914 return ipmi::response(ipmi::ccUnspecifiedError);
915 }
916 if (chInfo.mediumType ==
917 static_cast<uint8_t>(EChannelMediumType::systemInterface))
918 {
919 phosphor::logging::log<phosphor::logging::level::ERR>(
920 "ipmiOEMSetUser2Activation: system interface exist .");
921 return ipmi::response(ipmi::ccCommandNotAvailable);
922 }
923 else
924 {
925
926 if (chInfo.mediumType ==
927 static_cast<uint8_t>(EChannelMediumType::lan8032))
928 {
929 if (isDHCPIPv6Enabled(channel) || isDHCPEnabled(channel))
930 {
931 phosphor::logging::log<phosphor::logging::level::ERR>(
932 "ipmiOEMSetUser2Activation: DHCP enabled .");
933 return ipmi::response(ipmi::ccCommandNotAvailable);
934 }
935 }
936 }
937 }
938 uint8_t maxChUsers = 0, enabledUsers = 0, fixedUsers = 0;
939 if (ipmi::ccSuccess ==
940 ipmiUserGetAllCounts(maxChUsers, enabledUsers, fixedUsers))
941 {
942 if (enabledUsers > 1)
943 {
944 phosphor::logging::log<phosphor::logging::level::ERR>(
945 "ipmiOEMSetUser2Activation: more than one user is enabled.");
946 return ipmi::response(ipmi::ccCommandNotAvailable);
947 }
948 // Check the user 2 is enabled or not
949 ipmiUserCheckEnabled(ipmiDefaultUserId, userState);
950 if (userState == true)
951 {
952 phosphor::logging::log<phosphor::logging::level::ERR>(
953 "ipmiOEMSetUser2Activation: user 2 already enabled .");
954 return ipmi::response(ipmi::ccCommandNotAvailable);
955 }
956 }
957 else
958 {
959 return ipmi::response(ipmi::ccUnspecifiedError);
960 }
961
962#if BYTE_ORDER == LITTLE_ENDIAN
963 PrivAccess privAccess = {PRIVILEGE_ADMIN, true, true, true, 0};
964#endif
965#if BYTE_ORDER == BIG_ENDIAN
966 PrivAccess privAccess = {0, true, true, true, PRIVILEGE_ADMIN};
967#endif
968
969 if (ipmi::ccSuccess ==
970 ipmiUserSetUserName(ipmiDefaultUserId,
971 reinterpret_cast<const char*>(userName.data())))
972 {
973 if (ipmi::ccSuccess ==
974 ipmiUserSetUserPassword(
975 ipmiDefaultUserId,
976 reinterpret_cast<const char*>(userPassword.data())))
977 {
978 if (ipmi::ccSuccess ==
979 ipmiUserSetPrivilegeAccess(
980 ipmiDefaultUserId,
981 static_cast<uint8_t>(ipmi::EChannelID::chanLan1),
982 privAccess, true))
983 {
984 phosphor::logging::log<phosphor::logging::level::INFO>(
985 "ipmiOEMSetUser2Activation: user created successfully ");
986 return ipmi::responseSuccess();
987 }
988 }
989 // we need to delete the default user id which added in this command as
990 // password / priv setting is failed.
991 ipmiUserSetUserName(ipmiDefaultUserId, "");
992 phosphor::logging::log<phosphor::logging::level::ERR>(
993 "ipmiOEMSetUser2Activation: password / priv setting is failed.");
994 }
995 else
996 {
997 phosphor::logging::log<phosphor::logging::level::ERR>(
998 "ipmiOEMSetUser2Activation: Setting username failed.");
999 }
1000
1001 return ipmi::response(ipmi::ccCommandNotAvailable);
1002}
1003
Richard Marian Thomaiyarfc5e9852019-04-14 15:06:27 +05301004/** @brief implementes setting password for special user
1005 * @param[in] specialUserIndex
1006 * @param[in] userPassword - new password in 20 bytes
1007 * @returns ipmi completion code.
1008 */
1009ipmi::RspType<> ipmiOEMSetSpecialUserPassword(ipmi::Context::ptr ctx,
1010 uint8_t specialUserIndex,
1011 std::vector<uint8_t> userPassword)
1012{
1013 ChannelInfo chInfo;
1014 try
1015 {
1016 getChannelInfo(ctx->channel, chInfo);
1017 }
1018 catch (sdbusplus::exception_t& e)
1019 {
1020 phosphor::logging::log<phosphor::logging::level::ERR>(
1021 "ipmiOEMSetSpecialUserPassword: Failed to get Channel Info",
1022 phosphor::logging::entry("MSG: %s", e.description()));
1023 return ipmi::responseUnspecifiedError();
1024 }
1025 if (chInfo.mediumType !=
1026 static_cast<uint8_t>(EChannelMediumType::systemInterface))
1027 {
1028 phosphor::logging::log<phosphor::logging::level::ERR>(
1029 "ipmiOEMSetSpecialUserPassword: Error - supported only in KCS "
1030 "interface");
1031 return ipmi::responseCommandNotAvailable();
1032 }
1033 if (specialUserIndex != 0)
1034 {
1035 phosphor::logging::log<phosphor::logging::level::ERR>(
1036 "ipmiOEMSetSpecialUserPassword: Invalid user account");
1037 return ipmi::responseParmOutOfRange();
1038 }
1039 constexpr uint8_t minPasswordSizeRequired = 6;
1040 if (userPassword.size() < minPasswordSizeRequired ||
1041 userPassword.size() > ipmi::maxIpmi20PasswordSize)
1042 {
1043 return ipmi::responseReqDataLenInvalid();
1044 }
1045 std::string passwd;
1046 passwd.assign(reinterpret_cast<const char*>(userPassword.data()),
1047 userPassword.size());
1048 return ipmi::response(ipmiSetSpecialUserPassword("root", passwd));
1049}
1050
Kuiying Wang45f04982018-12-26 09:23:08 +08001051namespace ledAction
1052{
1053using namespace sdbusplus::xyz::openbmc_project::Led::server;
1054std::map<Physical::Action, uint8_t> actionDbusToIpmi = {
1055 {Physical::Action::Off, 0x00},
1056 {Physical::Action::On, 0x10},
1057 {Physical::Action::Blink, 0x01}};
1058
1059std::map<uint8_t, std::string> offsetObjPath = {
1060 {2, statusAmberObjPath}, {4, statusGreenObjPath}, {6, identifyLEDObjPath}};
1061
1062} // namespace ledAction
1063
1064int8_t getLEDState(sdbusplus::bus::bus& bus, const std::string& intf,
1065 const std::string& objPath, uint8_t& state)
1066{
1067 try
1068 {
1069 std::string service = getService(bus, intf, objPath);
1070 Value stateValue =
1071 getDbusProperty(bus, service, objPath, intf, "State");
Vernon Mauery8166c8d2019-05-23 11:22:30 -07001072 std::string strState = std::get<std::string>(stateValue);
Kuiying Wang45f04982018-12-26 09:23:08 +08001073 state = ledAction::actionDbusToIpmi.at(
1074 sdbusplus::xyz::openbmc_project::Led::server::Physical::
1075 convertActionFromString(strState));
1076 }
1077 catch (sdbusplus::exception::SdBusError& e)
1078 {
1079 phosphor::logging::log<phosphor::logging::level::ERR>(e.what());
1080 return -1;
1081 }
1082 return 0;
1083}
1084
1085ipmi_ret_t ipmiOEMGetLEDStatus(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
1086 ipmi_request_t request, ipmi_response_t response,
1087 ipmi_data_len_t dataLen, ipmi_context_t context)
1088{
1089 uint8_t* resp = reinterpret_cast<uint8_t*>(response);
1090 // LED Status
1091 //[1:0] = Reserved
1092 //[3:2] = Status(Amber)
1093 //[5:4] = Status(Green)
1094 //[7:6] = System Identify
1095 // Status definitions:
1096 // 00b = Off
1097 // 01b = Blink
1098 // 10b = On
1099 // 11b = invalid
1100 if (*dataLen != 0)
1101 {
1102 phosphor::logging::log<phosphor::logging::level::ERR>(
1103 "oem_get_led_status: invalid input len!");
1104 *dataLen = 0;
1105 return IPMI_CC_REQ_DATA_LEN_INVALID;
1106 }
1107
1108 phosphor::logging::log<phosphor::logging::level::DEBUG>("GET led status");
1109 *resp = 0;
1110 *dataLen = 0;
Vernon Mauery15419dd2019-05-24 09:40:30 -07001111 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
Kuiying Wang45f04982018-12-26 09:23:08 +08001112 for (auto it = ledAction::offsetObjPath.begin();
1113 it != ledAction::offsetObjPath.end(); ++it)
1114 {
1115 uint8_t state = 0;
Vernon Mauery15419dd2019-05-24 09:40:30 -07001116 if (-1 == getLEDState(*dbus, ledIntf, it->second, state))
Kuiying Wang45f04982018-12-26 09:23:08 +08001117 {
1118 phosphor::logging::log<phosphor::logging::level::ERR>(
1119 "oem_get_led_status: fail to get ID LED status!");
1120 return IPMI_CC_UNSPECIFIED_ERROR;
1121 }
1122 *resp |= state << it->first;
1123 }
1124
1125 *dataLen = sizeof(*resp);
1126 return IPMI_CC_OK;
1127}
1128
Yong Li23737fe2019-02-19 08:49:55 +08001129ipmi_ret_t ipmiOEMCfgHostSerialPortSpeed(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
1130 ipmi_request_t request,
1131 ipmi_response_t response,
1132 ipmi_data_len_t dataLen,
1133 ipmi_context_t context)
1134{
1135 CfgHostSerialReq* req = reinterpret_cast<CfgHostSerialReq*>(request);
1136 uint8_t* resp = reinterpret_cast<uint8_t*>(response);
1137
1138 if (*dataLen == 0)
1139 {
1140 phosphor::logging::log<phosphor::logging::level::ERR>(
1141 "CfgHostSerial: invalid input len!",
1142 phosphor::logging::entry("LEN=%d", *dataLen));
1143 return IPMI_CC_REQ_DATA_LEN_INVALID;
1144 }
1145
1146 switch (req->command)
1147 {
1148 case getHostSerialCfgCmd:
1149 {
1150 if (*dataLen != 1)
1151 {
1152 phosphor::logging::log<phosphor::logging::level::ERR>(
1153 "CfgHostSerial: invalid input len!");
1154 *dataLen = 0;
1155 return IPMI_CC_REQ_DATA_LEN_INVALID;
1156 }
1157
1158 *dataLen = 0;
1159
1160 boost::process::ipstream is;
1161 std::vector<std::string> data;
1162 std::string line;
1163 boost::process::child c1(fwGetEnvCmd, "-n", fwHostSerailCfgEnvName,
1164 boost::process::std_out > is);
1165
1166 while (c1.running() && std::getline(is, line) && !line.empty())
1167 {
1168 data.push_back(line);
1169 }
1170
1171 c1.wait();
1172 if (c1.exit_code())
1173 {
1174 phosphor::logging::log<phosphor::logging::level::ERR>(
1175 "CfgHostSerial:: error on execute",
1176 phosphor::logging::entry("EXECUTE=%s", fwSetEnvCmd));
1177 // Using the default value
1178 *resp = 0;
1179 }
1180 else
1181 {
1182 if (data.size() != 1)
1183 {
1184 phosphor::logging::log<phosphor::logging::level::ERR>(
1185 "CfgHostSerial:: error on read env");
1186 return IPMI_CC_UNSPECIFIED_ERROR;
1187 }
1188 try
1189 {
1190 unsigned long tmp = std::stoul(data[0]);
1191 if (tmp > std::numeric_limits<uint8_t>::max())
1192 {
1193 throw std::out_of_range("Out of range");
1194 }
1195 *resp = static_cast<uint8_t>(tmp);
1196 }
1197 catch (const std::invalid_argument& e)
1198 {
1199 phosphor::logging::log<phosphor::logging::level::ERR>(
1200 "invalid config ",
1201 phosphor::logging::entry("ERR=%s", e.what()));
1202 return IPMI_CC_UNSPECIFIED_ERROR;
1203 }
1204 catch (const std::out_of_range& e)
1205 {
1206 phosphor::logging::log<phosphor::logging::level::ERR>(
1207 "out_of_range config ",
1208 phosphor::logging::entry("ERR=%s", e.what()));
1209 return IPMI_CC_UNSPECIFIED_ERROR;
1210 }
1211 }
1212
1213 *dataLen = 1;
1214 break;
1215 }
1216 case setHostSerialCfgCmd:
1217 {
1218 if (*dataLen != sizeof(CfgHostSerialReq))
1219 {
1220 phosphor::logging::log<phosphor::logging::level::ERR>(
1221 "CfgHostSerial: invalid input len!");
1222 *dataLen = 0;
1223 return IPMI_CC_REQ_DATA_LEN_INVALID;
1224 }
1225
1226 *dataLen = 0;
1227
1228 if (req->parameter > HostSerialCfgParamMax)
1229 {
1230 phosphor::logging::log<phosphor::logging::level::ERR>(
1231 "CfgHostSerial: invalid input!");
1232 return IPMI_CC_INVALID_FIELD_REQUEST;
1233 }
1234
1235 boost::process::child c1(fwSetEnvCmd, fwHostSerailCfgEnvName,
1236 std::to_string(req->parameter));
1237
1238 c1.wait();
1239 if (c1.exit_code())
1240 {
1241 phosphor::logging::log<phosphor::logging::level::ERR>(
1242 "CfgHostSerial:: error on execute",
1243 phosphor::logging::entry("EXECUTE=%s", fwGetEnvCmd));
1244 return IPMI_CC_UNSPECIFIED_ERROR;
1245 }
1246 break;
1247 }
1248 default:
1249 phosphor::logging::log<phosphor::logging::level::ERR>(
1250 "CfgHostSerial: invalid input!");
1251 *dataLen = 0;
1252 return IPMI_CC_INVALID_FIELD_REQUEST;
1253 }
1254
1255 return IPMI_CC_OK;
1256}
1257
James Feist91244a62019-02-19 15:04:54 -08001258constexpr const char* thermalModeInterface =
1259 "xyz.openbmc_project.Control.ThermalMode";
1260constexpr const char* thermalModePath =
1261 "/xyz/openbmc_project/control/thermal_mode";
1262
1263bool getFanProfileInterface(
1264 sdbusplus::bus::bus& bus,
1265 boost::container::flat_map<
1266 std::string, std::variant<std::vector<std::string>, std::string>>& resp)
1267{
1268 auto call = bus.new_method_call(settingsBusName, thermalModePath, PROP_INTF,
1269 "GetAll");
1270 call.append(thermalModeInterface);
1271 try
1272 {
1273 auto data = bus.call(call);
1274 data.read(resp);
1275 }
1276 catch (sdbusplus::exception_t& e)
1277 {
1278 phosphor::logging::log<phosphor::logging::level::ERR>(
1279 "getFanProfileInterface: can't get thermal mode!",
1280 phosphor::logging::entry("ERR=%s", e.what()));
1281 return false;
1282 }
1283 return true;
1284}
1285
1286ipmi_ret_t ipmiOEMSetFanConfig(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
1287 ipmi_request_t request, ipmi_response_t response,
1288 ipmi_data_len_t dataLen, ipmi_context_t context)
1289{
1290
1291 if (*dataLen < 2 || *dataLen > 7)
1292 {
1293 phosphor::logging::log<phosphor::logging::level::ERR>(
1294 "ipmiOEMSetFanConfig: invalid input len!");
1295 *dataLen = 0;
1296 return IPMI_CC_REQ_DATA_LEN_INVALID;
1297 }
1298
1299 // todo: tell bios to only send first 2 bytes
1300
1301 SetFanConfigReq* req = reinterpret_cast<SetFanConfigReq*>(request);
1302 boost::container::flat_map<
1303 std::string, std::variant<std::vector<std::string>, std::string>>
1304 profileData;
Vernon Mauery15419dd2019-05-24 09:40:30 -07001305 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
1306 if (!getFanProfileInterface(*dbus, profileData))
James Feist91244a62019-02-19 15:04:54 -08001307 {
1308 return IPMI_CC_UNSPECIFIED_ERROR;
1309 }
1310
1311 std::vector<std::string>* supported =
1312 std::get_if<std::vector<std::string>>(&profileData["Supported"]);
1313 if (supported == nullptr)
1314 {
1315 return IPMI_CC_INVALID_FIELD_REQUEST;
1316 }
1317 std::string mode;
1318 if (req->flags &
1319 (1 << static_cast<uint8_t>(setFanProfileFlags::setPerfAcousMode)))
1320 {
1321 bool performanceMode =
1322 (req->flags & (1 << static_cast<uint8_t>(
1323 setFanProfileFlags::performAcousSelect))) > 0;
1324
1325 if (performanceMode)
1326 {
1327
1328 if (std::find(supported->begin(), supported->end(),
1329 "Performance") != supported->end())
1330 {
1331 mode = "Performance";
1332 }
1333 }
1334 else
1335 {
1336
1337 if (std::find(supported->begin(), supported->end(), "Acoustic") !=
1338 supported->end())
1339 {
1340 mode = "Acoustic";
1341 }
1342 }
1343 if (mode.empty())
1344 {
1345 return IPMI_CC_INVALID_FIELD_REQUEST;
1346 }
Vernon Mauery15419dd2019-05-24 09:40:30 -07001347 setDbusProperty(*dbus, settingsBusName, thermalModePath,
James Feist91244a62019-02-19 15:04:54 -08001348 thermalModeInterface, "Current", mode);
1349 }
1350
1351 return IPMI_CC_OK;
1352}
1353
James Feist5b693632019-07-09 09:06:09 -07001354ipmi::RspType<uint8_t, // profile support map
1355 uint8_t, // fan control profile enable
1356 uint8_t, // flags
1357 uint32_t // dimm presence bit map
1358 >
1359 ipmiOEMGetFanConfig(uint8_t dimmGroupId)
James Feist91244a62019-02-19 15:04:54 -08001360{
James Feist91244a62019-02-19 15:04:54 -08001361 boost::container::flat_map<
1362 std::string, std::variant<std::vector<std::string>, std::string>>
1363 profileData;
1364
Vernon Mauery15419dd2019-05-24 09:40:30 -07001365 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
1366 if (!getFanProfileInterface(*dbus, profileData))
James Feist91244a62019-02-19 15:04:54 -08001367 {
James Feist5b693632019-07-09 09:06:09 -07001368 return ipmi::responseResponseError();
James Feist91244a62019-02-19 15:04:54 -08001369 }
1370
1371 std::string* current = std::get_if<std::string>(&profileData["Current"]);
1372
1373 if (current == nullptr)
1374 {
1375 phosphor::logging::log<phosphor::logging::level::ERR>(
1376 "ipmiOEMGetFanConfig: can't get current mode!");
James Feist5b693632019-07-09 09:06:09 -07001377 return ipmi::responseResponseError();
James Feist91244a62019-02-19 15:04:54 -08001378 }
1379 bool performance = (*current == "Performance");
1380
James Feist5b693632019-07-09 09:06:09 -07001381 uint8_t flags = 0;
James Feist91244a62019-02-19 15:04:54 -08001382 if (performance)
1383 {
James Feist5b693632019-07-09 09:06:09 -07001384 flags |= 1 << 2;
James Feist91244a62019-02-19 15:04:54 -08001385 }
1386
James Feist5b693632019-07-09 09:06:09 -07001387 return ipmi::responseSuccess(0, 0, flags, 0);
James Feist91244a62019-02-19 15:04:54 -08001388}
James Feist5f957ca2019-03-14 15:33:55 -07001389constexpr const char* cfmLimitSettingPath =
1390 "/xyz/openbmc_project/control/cfm_limit";
1391constexpr const char* cfmLimitIface = "xyz.openbmc_project.Control.CFMLimit";
James Feistfaa4f222019-03-21 16:21:55 -07001392constexpr const size_t legacyExitAirSensorNumber = 0x2e;
James Feist09f6b602019-08-08 11:30:03 -07001393constexpr const size_t legacyPCHSensorNumber = 0x22;
1394constexpr const char* exitAirPathName = "Exit_Air";
1395constexpr const char* pchPathName = "SSB_Temp";
James Feistacc8a4e2019-04-02 14:23:57 -07001396constexpr const char* pidConfigurationIface =
1397 "xyz.openbmc_project.Configuration.Pid";
James Feistfaa4f222019-03-21 16:21:55 -07001398
James Feist09f6b602019-08-08 11:30:03 -07001399static std::string getConfigPath(const std::string& name)
James Feistfaa4f222019-03-21 16:21:55 -07001400{
Vernon Mauery15419dd2019-05-24 09:40:30 -07001401 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
James Feistfaa4f222019-03-21 16:21:55 -07001402 auto method =
Vernon Mauery15419dd2019-05-24 09:40:30 -07001403 dbus->new_method_call("xyz.openbmc_project.ObjectMapper",
1404 "/xyz/openbmc_project/object_mapper",
1405 "xyz.openbmc_project.ObjectMapper", "GetSubTree");
James Feistfaa4f222019-03-21 16:21:55 -07001406
James Feistacc8a4e2019-04-02 14:23:57 -07001407 method.append("/", 0, std::array<const char*, 1>{pidConfigurationIface});
James Feistfaa4f222019-03-21 16:21:55 -07001408 std::string path;
1409 GetSubTreeType resp;
1410 try
1411 {
Vernon Mauery15419dd2019-05-24 09:40:30 -07001412 auto reply = dbus->call(method);
James Feistfaa4f222019-03-21 16:21:55 -07001413 reply.read(resp);
1414 }
1415 catch (sdbusplus::exception_t&)
1416 {
1417 phosphor::logging::log<phosphor::logging::level::ERR>(
1418 "ipmiOEMGetFscParameter: mapper error");
1419 };
James Feist09f6b602019-08-08 11:30:03 -07001420 auto config =
1421 std::find_if(resp.begin(), resp.end(), [&name](const auto& pair) {
1422 return pair.first.find(name) != std::string::npos;
1423 });
James Feistfaa4f222019-03-21 16:21:55 -07001424 if (config != resp.end())
1425 {
1426 path = std::move(config->first);
1427 }
1428 return path;
1429}
James Feist5f957ca2019-03-14 15:33:55 -07001430
James Feistacc8a4e2019-04-02 14:23:57 -07001431// flat map to make alphabetical
1432static boost::container::flat_map<std::string, PropertyMap> getPidConfigs()
1433{
1434 boost::container::flat_map<std::string, PropertyMap> ret;
Vernon Mauery15419dd2019-05-24 09:40:30 -07001435 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
James Feistacc8a4e2019-04-02 14:23:57 -07001436 auto method =
Vernon Mauery15419dd2019-05-24 09:40:30 -07001437 dbus->new_method_call("xyz.openbmc_project.ObjectMapper",
1438 "/xyz/openbmc_project/object_mapper",
1439 "xyz.openbmc_project.ObjectMapper", "GetSubTree");
James Feistacc8a4e2019-04-02 14:23:57 -07001440
1441 method.append("/", 0, std::array<const char*, 1>{pidConfigurationIface});
1442 GetSubTreeType resp;
1443
1444 try
1445 {
Vernon Mauery15419dd2019-05-24 09:40:30 -07001446 auto reply = dbus->call(method);
James Feistacc8a4e2019-04-02 14:23:57 -07001447 reply.read(resp);
1448 }
1449 catch (sdbusplus::exception_t&)
1450 {
1451 phosphor::logging::log<phosphor::logging::level::ERR>(
1452 "getFanConfigPaths: mapper error");
1453 };
1454 for (const auto& [path, objects] : resp)
1455 {
1456 if (objects.empty())
1457 {
1458 continue; // should be impossible
1459 }
Zhu, Yungebe560b02019-04-21 21:19:21 -04001460
1461 try
1462 {
Vernon Mauery15419dd2019-05-24 09:40:30 -07001463 ret.emplace(path,
1464 getAllDbusProperties(*dbus, objects[0].first, path,
1465 pidConfigurationIface));
Zhu, Yungebe560b02019-04-21 21:19:21 -04001466 }
1467 catch (sdbusplus::exception_t& e)
1468 {
1469 phosphor::logging::log<phosphor::logging::level::ERR>(
1470 "getPidConfigs: can't get DbusProperties!",
1471 phosphor::logging::entry("ERR=%s", e.what()));
1472 }
James Feistacc8a4e2019-04-02 14:23:57 -07001473 }
1474 return ret;
1475}
1476
1477ipmi::RspType<uint8_t> ipmiOEMGetFanSpeedOffset(void)
1478{
1479 boost::container::flat_map<std::string, PropertyMap> data = getPidConfigs();
1480 if (data.empty())
1481 {
1482 return ipmi::responseResponseError();
1483 }
1484 uint8_t minOffset = std::numeric_limits<uint8_t>::max();
1485 for (const auto& [_, pid] : data)
1486 {
1487 auto findClass = pid.find("Class");
1488 if (findClass == pid.end())
1489 {
1490 phosphor::logging::log<phosphor::logging::level::ERR>(
1491 "ipmiOEMGetFscParameter: found illegal pid "
1492 "configurations");
1493 return ipmi::responseResponseError();
1494 }
1495 std::string type = std::get<std::string>(findClass->second);
1496 if (type == "fan")
1497 {
1498 auto findOutLimit = pid.find("OutLimitMin");
1499 if (findOutLimit == pid.end())
1500 {
1501 phosphor::logging::log<phosphor::logging::level::ERR>(
1502 "ipmiOEMGetFscParameter: found illegal pid "
1503 "configurations");
1504 return ipmi::responseResponseError();
1505 }
1506 // get the min out of all the offsets
1507 minOffset = std::min(
1508 minOffset,
1509 static_cast<uint8_t>(std::get<double>(findOutLimit->second)));
1510 }
1511 }
1512 if (minOffset == std::numeric_limits<uint8_t>::max())
1513 {
1514 phosphor::logging::log<phosphor::logging::level::ERR>(
1515 "ipmiOEMGetFscParameter: found no fan configurations!");
1516 return ipmi::responseResponseError();
1517 }
1518
1519 return ipmi::responseSuccess(minOffset);
1520}
1521
1522ipmi::RspType<> ipmiOEMSetFanSpeedOffset(uint8_t offset)
1523{
1524 boost::container::flat_map<std::string, PropertyMap> data = getPidConfigs();
1525 if (data.empty())
1526 {
1527
1528 phosphor::logging::log<phosphor::logging::level::ERR>(
1529 "ipmiOEMSetFanSpeedOffset: found no pid configurations!");
1530 return ipmi::responseResponseError();
1531 }
1532
Vernon Mauery15419dd2019-05-24 09:40:30 -07001533 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
James Feistacc8a4e2019-04-02 14:23:57 -07001534 bool found = false;
1535 for (const auto& [path, pid] : data)
1536 {
1537 auto findClass = pid.find("Class");
1538 if (findClass == pid.end())
1539 {
1540
1541 phosphor::logging::log<phosphor::logging::level::ERR>(
1542 "ipmiOEMSetFanSpeedOffset: found illegal pid "
1543 "configurations");
1544 return ipmi::responseResponseError();
1545 }
1546 std::string type = std::get<std::string>(findClass->second);
1547 if (type == "fan")
1548 {
1549 auto findOutLimit = pid.find("OutLimitMin");
1550 if (findOutLimit == pid.end())
1551 {
1552
1553 phosphor::logging::log<phosphor::logging::level::ERR>(
1554 "ipmiOEMSetFanSpeedOffset: found illegal pid "
1555 "configurations");
1556 return ipmi::responseResponseError();
1557 }
Vernon Mauery15419dd2019-05-24 09:40:30 -07001558 ipmi::setDbusProperty(*dbus, "xyz.openbmc_project.EntityManager",
James Feistacc8a4e2019-04-02 14:23:57 -07001559 path, pidConfigurationIface, "OutLimitMin",
1560 static_cast<double>(offset));
1561 found = true;
1562 }
1563 }
1564 if (!found)
1565 {
1566 phosphor::logging::log<phosphor::logging::level::ERR>(
1567 "ipmiOEMSetFanSpeedOffset: set no fan offsets");
1568 return ipmi::responseResponseError();
1569 }
1570
1571 return ipmi::responseSuccess();
1572}
1573
1574ipmi::RspType<> ipmiOEMSetFscParameter(uint8_t command, uint8_t param1,
1575 uint8_t param2)
James Feist5f957ca2019-03-14 15:33:55 -07001576{
1577 constexpr const size_t disableLimiting = 0x0;
1578
Vernon Mauery15419dd2019-05-24 09:40:30 -07001579 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
James Feistacc8a4e2019-04-02 14:23:57 -07001580 if (command == static_cast<uint8_t>(setFscParamFlags::tcontrol))
James Feist5f957ca2019-03-14 15:33:55 -07001581 {
James Feist09f6b602019-08-08 11:30:03 -07001582 std::string pathName;
James Feistacc8a4e2019-04-02 14:23:57 -07001583 if (param1 == legacyExitAirSensorNumber)
James Feistfaa4f222019-03-21 16:21:55 -07001584 {
James Feist09f6b602019-08-08 11:30:03 -07001585 pathName = exitAirPathName;
1586 }
1587 else if (param1 == legacyPCHSensorNumber)
1588 {
1589 pathName = pchPathName;
James Feistfaa4f222019-03-21 16:21:55 -07001590 }
1591 else
1592 {
James Feistacc8a4e2019-04-02 14:23:57 -07001593 return ipmi::responseParmOutOfRange();
James Feistfaa4f222019-03-21 16:21:55 -07001594 }
James Feist09f6b602019-08-08 11:30:03 -07001595 std::string path = getConfigPath(pathName);
1596 ipmi::setDbusProperty(*dbus, "xyz.openbmc_project.EntityManager", path,
1597 pidConfigurationIface, "SetPoint",
1598 static_cast<double>(param2));
1599 return ipmi::responseSuccess();
James Feistfaa4f222019-03-21 16:21:55 -07001600 }
James Feistacc8a4e2019-04-02 14:23:57 -07001601 else if (command == static_cast<uint8_t>(setFscParamFlags::cfm))
James Feist5f957ca2019-03-14 15:33:55 -07001602 {
James Feistacc8a4e2019-04-02 14:23:57 -07001603 uint16_t cfm = param1 | (static_cast<uint16_t>(param2) << 8);
James Feist5f957ca2019-03-14 15:33:55 -07001604
1605 // must be greater than 50 based on eps
1606 if (cfm < 50 && cfm != disableLimiting)
1607 {
James Feistacc8a4e2019-04-02 14:23:57 -07001608 return ipmi::responseParmOutOfRange();
James Feist5f957ca2019-03-14 15:33:55 -07001609 }
1610
1611 try
1612 {
Vernon Mauery15419dd2019-05-24 09:40:30 -07001613 ipmi::setDbusProperty(*dbus, settingsBusName, cfmLimitSettingPath,
James Feist5f957ca2019-03-14 15:33:55 -07001614 cfmLimitIface, "Limit",
1615 static_cast<double>(cfm));
1616 }
1617 catch (sdbusplus::exception_t& e)
1618 {
1619 phosphor::logging::log<phosphor::logging::level::ERR>(
1620 "ipmiOEMSetFscParameter: can't set cfm setting!",
1621 phosphor::logging::entry("ERR=%s", e.what()));
James Feistacc8a4e2019-04-02 14:23:57 -07001622 return ipmi::responseResponseError();
James Feist5f957ca2019-03-14 15:33:55 -07001623 }
James Feistacc8a4e2019-04-02 14:23:57 -07001624 return ipmi::responseSuccess();
1625 }
1626 else if (command == static_cast<uint8_t>(setFscParamFlags::maxPwm))
1627 {
1628 constexpr const size_t maxDomainCount = 8;
1629 uint8_t requestedDomainMask = param1;
1630 boost::container::flat_map data = getPidConfigs();
1631 if (data.empty())
1632 {
1633
1634 phosphor::logging::log<phosphor::logging::level::ERR>(
1635 "ipmiOEMSetFscParameter: found no pid configurations!");
1636 return ipmi::responseResponseError();
1637 }
1638 size_t count = 0;
1639 for (const auto& [path, pid] : data)
1640 {
1641 auto findClass = pid.find("Class");
1642 if (findClass == pid.end())
1643 {
1644
1645 phosphor::logging::log<phosphor::logging::level::ERR>(
1646 "ipmiOEMSetFscParameter: found illegal pid "
1647 "configurations");
1648 return ipmi::responseResponseError();
1649 }
1650 std::string type = std::get<std::string>(findClass->second);
1651 if (type == "fan")
1652 {
1653 if (requestedDomainMask & (1 << count))
1654 {
1655 ipmi::setDbusProperty(
Vernon Mauery15419dd2019-05-24 09:40:30 -07001656 *dbus, "xyz.openbmc_project.EntityManager", path,
James Feistacc8a4e2019-04-02 14:23:57 -07001657 pidConfigurationIface, "OutLimitMax",
1658 static_cast<double>(param2));
1659 }
1660 count++;
1661 }
1662 }
1663 return ipmi::responseSuccess();
James Feist5f957ca2019-03-14 15:33:55 -07001664 }
1665 else
1666 {
1667 // todo other command parts possibly
1668 // tcontrol is handled in peci now
1669 // fan speed offset not implemented yet
1670 // domain pwm limit not implemented
James Feistacc8a4e2019-04-02 14:23:57 -07001671 return ipmi::responseParmOutOfRange();
James Feist5f957ca2019-03-14 15:33:55 -07001672 }
1673}
1674
James Feistacc8a4e2019-04-02 14:23:57 -07001675ipmi::RspType<
1676 std::variant<uint8_t, std::array<uint8_t, 2>, std::array<uint16_t, 2>>>
1677 ipmiOEMGetFscParameter(uint8_t command, std::optional<uint8_t> param)
James Feist5f957ca2019-03-14 15:33:55 -07001678{
James Feist09f6b602019-08-08 11:30:03 -07001679 constexpr uint8_t legacyDefaultSetpoint = -128;
James Feist5f957ca2019-03-14 15:33:55 -07001680
Vernon Mauery15419dd2019-05-24 09:40:30 -07001681 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
James Feistacc8a4e2019-04-02 14:23:57 -07001682 if (command == static_cast<uint8_t>(setFscParamFlags::tcontrol))
James Feist5f957ca2019-03-14 15:33:55 -07001683 {
James Feistacc8a4e2019-04-02 14:23:57 -07001684 if (!param)
James Feistfaa4f222019-03-21 16:21:55 -07001685 {
James Feistacc8a4e2019-04-02 14:23:57 -07001686 return ipmi::responseReqDataLenInvalid();
James Feistfaa4f222019-03-21 16:21:55 -07001687 }
1688
James Feist09f6b602019-08-08 11:30:03 -07001689 std::string pathName;
1690
1691 if (*param == legacyExitAirSensorNumber)
1692 {
1693 pathName = exitAirPathName;
1694 }
1695 else if (*param == legacyPCHSensorNumber)
1696 {
1697 pathName = pchPathName;
1698 }
1699 else
James Feistfaa4f222019-03-21 16:21:55 -07001700 {
James Feistacc8a4e2019-04-02 14:23:57 -07001701 return ipmi::responseParmOutOfRange();
James Feistfaa4f222019-03-21 16:21:55 -07001702 }
James Feist09f6b602019-08-08 11:30:03 -07001703
1704 uint8_t setpoint = legacyDefaultSetpoint;
1705 std::string path = getConfigPath(pathName);
James Feistfaa4f222019-03-21 16:21:55 -07001706 if (path.size())
1707 {
Vernon Mauery15419dd2019-05-24 09:40:30 -07001708 Value val = ipmi::getDbusProperty(
1709 *dbus, "xyz.openbmc_project.EntityManager", path,
1710 pidConfigurationIface, "SetPoint");
James Feistfaa4f222019-03-21 16:21:55 -07001711 setpoint = std::floor(std::get<double>(val) + 0.5);
1712 }
1713
1714 // old implementation used to return the "default" and current, we
1715 // don't make the default readily available so just make both the
1716 // same
James Feistfaa4f222019-03-21 16:21:55 -07001717
James Feistacc8a4e2019-04-02 14:23:57 -07001718 return ipmi::responseSuccess(
1719 std::array<uint8_t, 2>{setpoint, setpoint});
James Feistfaa4f222019-03-21 16:21:55 -07001720 }
James Feistacc8a4e2019-04-02 14:23:57 -07001721 else if (command == static_cast<uint8_t>(setFscParamFlags::maxPwm))
1722 {
1723 constexpr const size_t maxDomainCount = 8;
1724
1725 if (!param)
1726 {
1727 return ipmi::responseReqDataLenInvalid();
1728 }
1729 uint8_t requestedDomain = *param;
1730 if (requestedDomain >= maxDomainCount)
1731 {
1732 return ipmi::responseInvalidFieldRequest();
1733 }
1734
1735 boost::container::flat_map data = getPidConfigs();
1736 if (data.empty())
1737 {
1738 phosphor::logging::log<phosphor::logging::level::ERR>(
1739 "ipmiOEMGetFscParameter: found no pid configurations!");
1740 return ipmi::responseResponseError();
1741 }
1742 size_t count = 0;
1743 for (const auto& [_, pid] : data)
1744 {
1745 auto findClass = pid.find("Class");
1746 if (findClass == pid.end())
1747 {
1748 phosphor::logging::log<phosphor::logging::level::ERR>(
1749 "ipmiOEMGetFscParameter: found illegal pid "
1750 "configurations");
1751 return ipmi::responseResponseError();
1752 }
1753 std::string type = std::get<std::string>(findClass->second);
1754 if (type == "fan")
1755 {
1756 if (requestedDomain == count)
1757 {
1758 auto findOutLimit = pid.find("OutLimitMax");
1759 if (findOutLimit == pid.end())
1760 {
1761 phosphor::logging::log<phosphor::logging::level::ERR>(
1762 "ipmiOEMGetFscParameter: found illegal pid "
1763 "configurations");
1764 return ipmi::responseResponseError();
1765 }
1766
1767 return ipmi::responseSuccess(
1768 static_cast<uint8_t>(std::floor(
1769 std::get<double>(findOutLimit->second) + 0.5)));
1770 }
1771 else
1772 {
1773 count++;
1774 }
1775 }
1776 }
1777
1778 return ipmi::responseInvalidFieldRequest();
1779 }
1780 else if (command == static_cast<uint8_t>(setFscParamFlags::cfm))
James Feist5f957ca2019-03-14 15:33:55 -07001781 {
1782
1783 /*
1784 DataLen should be 1, but host is sending us an extra bit. As the
James Feistacc8a4e2019-04-02 14:23:57 -07001785 previous behavior didn't seem to prevent this, ignore the check for
1786 now.
James Feist5f957ca2019-03-14 15:33:55 -07001787
James Feistacc8a4e2019-04-02 14:23:57 -07001788 if (param)
James Feist5f957ca2019-03-14 15:33:55 -07001789 {
1790 phosphor::logging::log<phosphor::logging::level::ERR>(
1791 "ipmiOEMGetFscParameter: invalid input len!");
James Feist5f957ca2019-03-14 15:33:55 -07001792 return IPMI_CC_REQ_DATA_LEN_INVALID;
1793 }
1794 */
1795 Value cfmLimit;
1796 Value cfmMaximum;
1797 try
1798 {
Vernon Mauery15419dd2019-05-24 09:40:30 -07001799 cfmLimit = ipmi::getDbusProperty(*dbus, settingsBusName,
James Feist5f957ca2019-03-14 15:33:55 -07001800 cfmLimitSettingPath, cfmLimitIface,
1801 "Limit");
1802 cfmMaximum = ipmi::getDbusProperty(
Vernon Mauery15419dd2019-05-24 09:40:30 -07001803 *dbus, "xyz.openbmc_project.ExitAirTempSensor",
James Feist5f957ca2019-03-14 15:33:55 -07001804 "/xyz/openbmc_project/control/MaxCFM", cfmLimitIface, "Limit");
1805 }
1806 catch (sdbusplus::exception_t& e)
1807 {
1808 phosphor::logging::log<phosphor::logging::level::ERR>(
James Feistacc8a4e2019-04-02 14:23:57 -07001809 "ipmiOEMGetFscParameter: can't get cfm setting!",
James Feist5f957ca2019-03-14 15:33:55 -07001810 phosphor::logging::entry("ERR=%s", e.what()));
James Feistacc8a4e2019-04-02 14:23:57 -07001811 return ipmi::responseResponseError();
James Feist5f957ca2019-03-14 15:33:55 -07001812 }
1813
James Feistacc8a4e2019-04-02 14:23:57 -07001814 double cfmMax = std::get<double>(cfmMaximum);
1815 double cfmLim = std::get<double>(cfmLimit);
James Feist5f957ca2019-03-14 15:33:55 -07001816
James Feistacc8a4e2019-04-02 14:23:57 -07001817 cfmLim = std::floor(cfmLim + 0.5);
1818 cfmMax = std::floor(cfmMax + 0.5);
1819 uint16_t cfmLimResp = static_cast<uint16_t>(cfmLim);
1820 uint16_t cfmMaxResp = static_cast<uint16_t>(cfmMax);
James Feist5f957ca2019-03-14 15:33:55 -07001821
James Feistacc8a4e2019-04-02 14:23:57 -07001822 return ipmi::responseSuccess(
1823 std::array<uint16_t, 2>{cfmLimResp, cfmMaxResp});
James Feist5f957ca2019-03-14 15:33:55 -07001824 }
James Feistacc8a4e2019-04-02 14:23:57 -07001825
James Feist5f957ca2019-03-14 15:33:55 -07001826 else
1827 {
1828 // todo other command parts possibly
James Feist5f957ca2019-03-14 15:33:55 -07001829 // domain pwm limit not implemented
James Feistacc8a4e2019-04-02 14:23:57 -07001830 return ipmi::responseParmOutOfRange();
James Feist5f957ca2019-03-14 15:33:55 -07001831 }
1832}
1833
Cheng C Yang773703a2019-08-15 09:41:11 +08001834using crConfigVariant =
1835 std::variant<bool, uint8_t, uint32_t, std::vector<uint8_t>, std::string>;
1836
1837int setCRConfig(ipmi::Context::ptr ctx, const std::string& property,
1838 const crConfigVariant& value,
1839 std::chrono::microseconds timeout = ipmi::IPMI_DBUS_TIMEOUT)
1840{
1841 boost::system::error_code ec;
1842 ctx->bus->yield_method_call<void>(
James Feist28c72902019-09-16 10:34:07 -07001843 ctx->yield, ec, "xyz.openbmc_project.Settings",
Cheng C Yang773703a2019-08-15 09:41:11 +08001844 "/xyz/openbmc_project/control/power_supply_redundancy",
1845 "org.freedesktop.DBus.Properties", "Set",
1846 "xyz.openbmc_project.Control.PowerSupplyRedundancy", property, value);
1847 if (ec)
1848 {
1849 phosphor::logging::log<phosphor::logging::level::ERR>(
1850 "Failed to set dbus property to cold redundancy");
1851 return -1;
1852 }
1853
1854 return 0;
1855}
1856
1857int getCRConfig(ipmi::Context::ptr ctx, const std::string& property,
1858 crConfigVariant& value,
1859 std::chrono::microseconds timeout = ipmi::IPMI_DBUS_TIMEOUT)
1860{
1861 boost::system::error_code ec;
1862 value = ctx->bus->yield_method_call<crConfigVariant>(
James Feist28c72902019-09-16 10:34:07 -07001863 ctx->yield, ec, "xyz.openbmc_project.Settings",
Cheng C Yang773703a2019-08-15 09:41:11 +08001864 "/xyz/openbmc_project/control/power_supply_redundancy",
1865 "org.freedesktop.DBus.Properties", "Get",
1866 "xyz.openbmc_project.Control.PowerSupplyRedundancy", property);
1867 if (ec)
1868 {
1869 phosphor::logging::log<phosphor::logging::level::ERR>(
1870 "Failed to get dbus property to cold redundancy");
1871 return -1;
1872 }
1873 return 0;
1874}
1875
1876uint8_t getPSUCount(void)
1877{
1878 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
1879 ipmi::Value num;
1880 try
1881 {
1882 num = ipmi::getDbusProperty(
1883 *dbus, "xyz.openbmc_project.PSURedundancy",
1884 "/xyz/openbmc_project/control/power_supply_redundancy",
1885 "xyz.openbmc_project.Control.PowerSupplyRedundancy", "PSUNumber");
1886 }
1887 catch (sdbusplus::exception_t& e)
1888 {
1889 phosphor::logging::log<phosphor::logging::level::ERR>(
1890 "Failed to get PSUNumber property from dbus interface");
1891 return 0;
1892 }
1893 uint8_t* pNum = std::get_if<uint8_t>(&num);
1894 if (!pNum)
1895 {
1896 phosphor::logging::log<phosphor::logging::level::ERR>(
1897 "Error to get PSU Number");
1898 return 0;
1899 }
1900 return *pNum;
1901}
1902
1903bool validateCRAlgo(std::vector<uint8_t>& conf, uint8_t num)
1904{
1905 if (conf.size() < num)
1906 {
1907 phosphor::logging::log<phosphor::logging::level::ERR>(
1908 "Invalid PSU Ranking");
1909 return false;
1910 }
1911 std::set<uint8_t> confSet;
1912 for (uint8_t i = 0; i < num; i++)
1913 {
1914 if (conf[i] > num)
1915 {
1916 phosphor::logging::log<phosphor::logging::level::ERR>(
1917 "PSU Ranking is larger than current PSU number");
1918 return false;
1919 }
1920 confSet.emplace(conf[i]);
1921 }
1922
1923 if (confSet.size() != num)
1924 {
1925 phosphor::logging::log<phosphor::logging::level::ERR>(
1926 "duplicate PSU Ranking");
1927 return false;
1928 }
1929 return true;
1930}
1931
1932enum class crParameter
1933{
1934 crStatus = 0,
1935 crFeature = 1,
1936 rotationFeature = 2,
1937 rotationAlgo = 3,
1938 rotationPeriod = 4,
1939 numOfPSU = 5
1940};
1941
1942constexpr ipmi::Cc ccParameterNotSupported = 0x80;
1943static const constexpr uint32_t oneDay = 0x15180;
1944static const constexpr uint32_t oneMonth = 0xf53700;
1945static const constexpr uint8_t userSpecific = 0x01;
1946static const constexpr uint8_t crSetCompleted = 0;
1947ipmi::RspType<uint8_t> ipmiOEMSetCRConfig(ipmi::Context::ptr ctx,
1948 uint8_t parameter,
1949 ipmi::message::Payload& payload)
1950{
1951 switch (static_cast<crParameter>(parameter))
1952 {
1953 case crParameter::crFeature:
1954 {
1955 uint8_t param1;
1956 if (payload.unpack(param1) || !payload.fullyUnpacked())
1957 {
1958 return ipmi::responseReqDataLenInvalid();
1959 }
1960 // ColdRedundancy Enable can only be true or flase
1961 if (param1 > 1)
1962 {
1963 return ipmi::responseInvalidFieldRequest();
1964 }
1965 if (setCRConfig(ctx, "ColdRedundancyEnabled",
1966 static_cast<bool>(param1)))
1967 {
1968 return ipmi::responseResponseError();
1969 }
1970 break;
1971 }
1972 case crParameter::rotationFeature:
1973 {
1974 uint8_t param1;
1975 if (payload.unpack(param1) || !payload.fullyUnpacked())
1976 {
1977 return ipmi::responseReqDataLenInvalid();
1978 }
1979 // Rotation Enable can only be true or false
1980 if (param1 > 1)
1981 {
1982 return ipmi::responseInvalidFieldRequest();
1983 }
1984 if (setCRConfig(ctx, "RotationEnabled", static_cast<bool>(param1)))
1985 {
1986 return ipmi::responseResponseError();
1987 }
1988 break;
1989 }
1990 case crParameter::rotationAlgo:
1991 {
1992 // Rotation Algorithm can only be 0-BMC Specific or 1-User Specific
1993 std::string algoName;
1994 uint8_t param1;
1995 if (payload.unpack(param1))
1996 {
1997 return ipmi::responseReqDataLenInvalid();
1998 }
1999 switch (param1)
2000 {
2001 case 0:
2002 algoName = "xyz.openbmc_project.Control."
2003 "PowerSupplyRedundancy.Algo.bmcSpecific";
2004 break;
2005 case 1:
2006 algoName = "xyz.openbmc_project.Control."
2007 "PowerSupplyRedundancy.Algo.userSpecific";
2008 break;
2009 default:
2010 return ipmi::responseInvalidFieldRequest();
2011 }
2012 if (setCRConfig(ctx, "RotationAlgorithm", algoName))
2013 {
2014 return ipmi::responseResponseError();
2015 }
2016
2017 uint8_t numberOfPSU = getPSUCount();
2018 if (!numberOfPSU)
2019 {
2020 return ipmi::responseResponseError();
2021 }
2022 std::vector<uint8_t> rankOrder;
2023
2024 if (param1 == userSpecific)
2025 {
2026 if (payload.unpack(rankOrder) || !payload.fullyUnpacked())
2027 {
2028 ipmi::responseReqDataLenInvalid();
2029 }
2030 if (rankOrder.size() < numberOfPSU)
2031 {
2032 return ipmi::responseReqDataLenInvalid();
2033 }
2034
2035 if (!validateCRAlgo(rankOrder, numberOfPSU))
2036 {
2037 return ipmi::responseInvalidFieldRequest();
2038 }
2039 }
2040 else
2041 {
2042 if (rankOrder.size() > 0)
2043 {
2044 return ipmi::responseReqDataLenInvalid();
2045 }
2046 for (uint8_t i = 1; i <= numberOfPSU; i++)
2047 {
2048 rankOrder.emplace_back(i);
2049 }
2050 }
2051 if (setCRConfig(ctx, "RotationRankOrder", rankOrder))
2052 {
2053 return ipmi::responseResponseError();
2054 }
2055 break;
2056 }
2057 case crParameter::rotationPeriod:
2058 {
2059 // Minimum Rotation period is One day (86400 seconds) and Max
2060 // Rotation Period is 6 month (0xf53700 seconds)
2061 uint32_t period;
2062 if (payload.unpack(period) || !payload.fullyUnpacked())
2063 {
2064 return ipmi::responseReqDataLenInvalid();
2065 }
2066 if ((period < oneDay) || (period > oneMonth))
2067 {
2068 return ipmi::responseInvalidFieldRequest();
2069 }
2070 if (setCRConfig(ctx, "PeriodOfRotation", period))
2071 {
2072 return ipmi::responseResponseError();
2073 }
2074 break;
2075 }
2076 default:
2077 {
2078 return ipmi::response(ccParameterNotSupported);
2079 }
2080 }
2081
2082 // TODO Halfwidth needs to set SetInProgress
2083 if (setCRConfig(ctx, "ColdRedundancyStatus",
Cheng C Yange8cecdf2019-08-26 23:48:08 +08002084 std::string("xyz.openbmc_project.Control."
2085 "PowerSupplyRedundancy.Status.completed")))
Cheng C Yang773703a2019-08-15 09:41:11 +08002086 {
2087 return ipmi::responseResponseError();
2088 }
2089 return ipmi::responseSuccess(crSetCompleted);
2090}
2091
Cheng C Yangf41e3342019-09-10 04:47:23 +08002092ipmi::RspType<uint8_t, std::variant<uint8_t, uint32_t, std::array<uint8_t, 5>>>
Cheng C Yang773703a2019-08-15 09:41:11 +08002093 ipmiOEMGetCRConfig(ipmi::Context::ptr ctx, uint8_t parameter)
2094{
2095 crConfigVariant value;
2096 switch (static_cast<crParameter>(parameter))
2097 {
2098 case crParameter::crStatus:
2099 {
2100 if (getCRConfig(ctx, "ColdRedundancyStatus", value))
2101 {
2102 return ipmi::responseResponseError();
2103 }
2104 std::string* pStatus = std::get_if<std::string>(&value);
2105 if (!pStatus)
2106 {
2107 phosphor::logging::log<phosphor::logging::level::ERR>(
2108 "Error to get ColdRedundancyStatus property");
2109 return ipmi::responseResponseError();
2110 }
2111 namespace server = sdbusplus::xyz::openbmc_project::Control::server;
2112 auto status =
2113 server::PowerSupplyRedundancy::convertStatusFromString(
2114 *pStatus);
2115 switch (status)
2116 {
2117 case server::PowerSupplyRedundancy::Status::inProgress:
Cheng C Yangf41e3342019-09-10 04:47:23 +08002118 return ipmi::responseSuccess(parameter,
2119 static_cast<uint8_t>(0));
Cheng C Yang773703a2019-08-15 09:41:11 +08002120
2121 case server::PowerSupplyRedundancy::Status::completed:
Cheng C Yangf41e3342019-09-10 04:47:23 +08002122 return ipmi::responseSuccess(parameter,
2123 static_cast<uint8_t>(1));
Cheng C Yang773703a2019-08-15 09:41:11 +08002124 default:
2125 phosphor::logging::log<phosphor::logging::level::ERR>(
2126 "Error to get valid status");
2127 return ipmi::responseResponseError();
2128 }
2129 }
2130 case crParameter::crFeature:
2131 {
2132 if (getCRConfig(ctx, "ColdRedundancyEnabled", value))
2133 {
2134 return ipmi::responseResponseError();
2135 }
2136 bool* pResponse = std::get_if<bool>(&value);
2137 if (!pResponse)
2138 {
2139 phosphor::logging::log<phosphor::logging::level::ERR>(
2140 "Error to get ColdRedundancyEnable property");
2141 return ipmi::responseResponseError();
2142 }
2143
Cheng C Yangf41e3342019-09-10 04:47:23 +08002144 return ipmi::responseSuccess(parameter,
2145 static_cast<uint8_t>(*pResponse));
Cheng C Yang773703a2019-08-15 09:41:11 +08002146 }
2147 case crParameter::rotationFeature:
2148 {
2149 if (getCRConfig(ctx, "RotationEnabled", value))
2150 {
2151 return ipmi::responseResponseError();
2152 }
2153 bool* pResponse = std::get_if<bool>(&value);
2154 if (!pResponse)
2155 {
2156 phosphor::logging::log<phosphor::logging::level::ERR>(
2157 "Error to get RotationEnabled property");
2158 return ipmi::responseResponseError();
2159 }
Cheng C Yangf41e3342019-09-10 04:47:23 +08002160 return ipmi::responseSuccess(parameter,
2161 static_cast<uint8_t>(*pResponse));
Cheng C Yang773703a2019-08-15 09:41:11 +08002162 }
2163 case crParameter::rotationAlgo:
2164 {
2165 if (getCRConfig(ctx, "RotationAlgorithm", value))
2166 {
2167 return ipmi::responseResponseError();
2168 }
2169
2170 std::string* pAlgo = std::get_if<std::string>(&value);
2171 if (!pAlgo)
2172 {
2173 phosphor::logging::log<phosphor::logging::level::ERR>(
2174 "Error to get RotationAlgorithm property");
2175 return ipmi::responseResponseError();
2176 }
2177 std::array<uint8_t, 5> response = {0, 0, 0, 0, 0};
2178 namespace server = sdbusplus::xyz::openbmc_project::Control::server;
2179 auto algo =
2180 server::PowerSupplyRedundancy::convertAlgoFromString(*pAlgo);
2181 switch (algo)
2182 {
2183 case server::PowerSupplyRedundancy::Algo::bmcSpecific:
2184 response[0] = 0;
2185 break;
2186 case server::PowerSupplyRedundancy::Algo::userSpecific:
2187 response[0] = 1;
2188 break;
2189 default:
2190 phosphor::logging::log<phosphor::logging::level::ERR>(
2191 "Error to get valid algo");
2192 return ipmi::responseResponseError();
2193 }
2194
2195 if (getCRConfig(ctx, "RotationRankOrder", value))
2196 {
2197 return ipmi::responseResponseError();
2198 }
2199 std::vector<uint8_t>* pResponse =
2200 std::get_if<std::vector<uint8_t>>(&value);
2201 if (!pResponse)
2202 {
2203 phosphor::logging::log<phosphor::logging::level::ERR>(
2204 "Error to get RotationRankOrder property");
2205 return ipmi::responseResponseError();
2206 }
2207 if (pResponse->size() + 1 > response.size())
2208 {
2209 phosphor::logging::log<phosphor::logging::level::ERR>(
2210 "Incorrect size of RotationAlgorithm property");
2211 return ipmi::responseResponseError();
2212 }
2213 std::copy(pResponse->begin(), pResponse->end(),
2214 response.begin() + 1);
Cheng C Yangf41e3342019-09-10 04:47:23 +08002215 return ipmi::responseSuccess(parameter, response);
Cheng C Yang773703a2019-08-15 09:41:11 +08002216 }
2217 case crParameter::rotationPeriod:
2218 {
2219 if (getCRConfig(ctx, "PeriodOfRotation", value))
2220 {
2221 return ipmi::responseResponseError();
2222 }
2223 uint32_t* pResponse = std::get_if<uint32_t>(&value);
2224 if (!pResponse)
2225 {
2226 phosphor::logging::log<phosphor::logging::level::ERR>(
2227 "Error to get RotationAlgorithm property");
2228 return ipmi::responseResponseError();
2229 }
Cheng C Yangf41e3342019-09-10 04:47:23 +08002230 return ipmi::responseSuccess(parameter, *pResponse);
Cheng C Yang773703a2019-08-15 09:41:11 +08002231 }
2232 case crParameter::numOfPSU:
2233 {
2234 uint8_t numberOfPSU = getPSUCount();
2235 if (!numberOfPSU)
2236 {
2237 return ipmi::responseResponseError();
2238 }
Cheng C Yangf41e3342019-09-10 04:47:23 +08002239 return ipmi::responseSuccess(parameter, numberOfPSU);
Cheng C Yang773703a2019-08-15 09:41:11 +08002240 }
2241 default:
2242 {
2243 return ipmi::response(ccParameterNotSupported);
2244 }
2245 }
2246}
2247
Zhu, Yungebe560b02019-04-21 21:19:21 -04002248ipmi::RspType<> ipmiOEMSetFaultIndication(uint8_t sourceId, uint8_t faultType,
2249 uint8_t faultState,
2250 uint8_t faultGroup,
2251 std::array<uint8_t, 8>& ledStateData)
2252{
2253 static constexpr const char* objpath = "/xyz/openbmc_project/EntityManager";
2254 static constexpr const char* intf = "xyz.openbmc_project.EntityManager";
2255 constexpr auto maxFaultType = static_cast<size_t>(RemoteFaultType::max);
2256 static const std::array<std::string, maxFaultType> faultNames = {
2257 "faultFan", "faultTemp", "faultPower",
2258 "faultDriveSlot", "faultSoftware", "faultMemory"};
2259 static constexpr const char* sysGpioPath = "/sys/class/gpio/gpio";
2260 static constexpr const char* postfixValue = "/value";
2261
2262 constexpr uint8_t maxFaultSource = 0x4;
2263 constexpr uint8_t skipLEDs = 0xFF;
2264 constexpr uint8_t pinSize = 64;
2265 constexpr uint8_t groupSize = 16;
2266
2267 std::vector<uint16_t> ledFaultPins(pinSize, 0xFFFF);
2268 uint64_t resFIndex = 0;
2269 std::string resFType;
2270 std::string service;
2271 ObjectValueTree valueTree;
2272
2273 // Validate the source, fault type
2274 if ((sourceId >= maxFaultSource) ||
2275 (faultType >= static_cast<int8_t>(RemoteFaultType::max)) ||
2276 (faultState >= static_cast<int8_t>(RemoteFaultState::maxFaultState)) ||
2277 (faultGroup >= static_cast<int8_t>(DimmFaultType::maxFaultGroup)))
2278 {
2279 return ipmi::responseParmOutOfRange();
2280 }
2281
Vernon Mauery15419dd2019-05-24 09:40:30 -07002282 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
Zhu, Yungebe560b02019-04-21 21:19:21 -04002283 try
2284 {
Vernon Mauery15419dd2019-05-24 09:40:30 -07002285 service = getService(*dbus, intf, objpath);
2286 valueTree = getManagedObjects(*dbus, service, "/");
Zhu, Yungebe560b02019-04-21 21:19:21 -04002287 }
2288 catch (const std::exception& e)
2289 {
2290 phosphor::logging::log<phosphor::logging::level::ERR>(
2291 "No object implements interface",
2292 phosphor::logging::entry("SERVICE=%s", service.c_str()),
2293 phosphor::logging::entry("INTF=%s", intf));
2294 return ipmi::responseResponseError();
2295 }
2296
2297 if (valueTree.empty())
2298 {
2299 phosphor::logging::log<phosphor::logging::level::ERR>(
2300 "No object implements interface",
2301 phosphor::logging::entry("INTF=%s", intf));
2302 return ipmi::responseResponseError();
2303 }
2304
2305 for (const auto& item : valueTree)
2306 {
2307 // find LedFault configuration
2308 auto interface =
2309 item.second.find("xyz.openbmc_project.Configuration.LedFault");
2310 if (interface == item.second.end())
2311 {
2312 continue;
2313 }
2314
2315 // find matched fault type: faultMemmory / faultFan
2316 // find LedGpioPins/FaultIndex configuration
2317 auto propertyFaultType = interface->second.find("FaultType");
2318 auto propertyFIndex = interface->second.find("FaultIndex");
2319 auto ledIndex = interface->second.find("LedGpioPins");
2320
2321 if (propertyFaultType == interface->second.end() ||
2322 propertyFIndex == interface->second.end() ||
2323 ledIndex == interface->second.end())
2324 {
2325 continue;
2326 }
2327
2328 try
2329 {
2330 Value valIndex = propertyFIndex->second;
2331 resFIndex = std::get<uint64_t>(valIndex);
2332
2333 Value valFType = propertyFaultType->second;
2334 resFType = std::get<std::string>(valFType);
2335 }
2336 catch (const std::bad_variant_access& e)
2337 {
2338 phosphor::logging::log<phosphor::logging::level::ERR>(e.what());
2339 return ipmi::responseResponseError();
2340 }
2341 // find the matched requested fault type: faultMemmory or faultFan
2342 if (resFType != faultNames[faultType])
2343 {
2344 continue;
2345 }
2346
2347 // read LedGpioPins data
2348 std::vector<uint64_t> ledgpios;
2349 std::variant<std::vector<uint64_t>> message;
2350
Vernon Mauery15419dd2019-05-24 09:40:30 -07002351 auto method = dbus->new_method_call(
Zhu, Yungebe560b02019-04-21 21:19:21 -04002352 service.c_str(), (std::string(item.first)).c_str(),
2353 "org.freedesktop.DBus.Properties", "Get");
2354
2355 method.append("xyz.openbmc_project.Configuration.LedFault",
2356 "LedGpioPins");
2357
2358 try
2359 {
Vernon Mauery15419dd2019-05-24 09:40:30 -07002360 auto reply = dbus->call(method);
Zhu, Yungebe560b02019-04-21 21:19:21 -04002361 reply.read(message);
2362 ledgpios = std::get<std::vector<uint64_t>>(message);
2363 }
2364 catch (std::exception& e)
2365 {
2366 phosphor::logging::log<phosphor::logging::level::ERR>(e.what());
2367 return ipmi::responseResponseError();
2368 }
2369
2370 // Check the size to be sure it will never overflow on groupSize
2371 if (ledgpios.size() > groupSize)
2372 {
2373 phosphor::logging::log<phosphor::logging::level::ERR>(
2374 "Fault gpio Pins out of range!");
2375 return ipmi::responseParmOutOfRange();
2376 }
2377 // Store data, according to command data bit index order
2378 for (int i = 0; i < ledgpios.size(); i++)
2379 {
2380 ledFaultPins[i + groupSize * resFIndex] = ledgpios[i];
2381 }
2382 }
2383
2384 switch (RemoteFaultType(faultType))
2385 {
2386 case (RemoteFaultType::fan):
2387 case (RemoteFaultType::memory):
2388 {
2389 if (faultGroup == skipLEDs)
2390 {
2391 return ipmi::responseSuccess();
2392 }
2393
2394 uint64_t ledState = 0;
2395 // calculate led state bit filed count, each byte has 8bits
2396 // the maximum bits will be 8 * 8 bits
2397 constexpr uint8_t size = sizeof(ledStateData) * 8;
2398 for (int i = 0; i < sizeof(ledStateData); i++)
2399 {
2400 ledState = (uint64_t)(ledState << 8);
2401 ledState = (uint64_t)(ledState | (uint64_t)ledStateData[i]);
2402 }
2403
2404 std::bitset<size> ledStateBits(ledState);
2405 std::string gpioValue;
2406 for (int i = 0; i < size; i++)
2407 { // skip invalid value
2408 if (ledFaultPins[i] == 0xFFFF)
2409 {
2410 continue;
2411 }
2412
2413 std::string device = sysGpioPath +
2414 std::to_string(ledFaultPins[i]) +
2415 postfixValue;
2416 std::fstream gpioFile;
2417
2418 gpioFile.open(device, std::ios::out);
2419
2420 if (!gpioFile.good())
2421 {
2422 phosphor::logging::log<phosphor::logging::level::ERR>(
2423 "Not Find Led Gpio Device!",
2424 phosphor::logging::entry("DEVICE=%s", device.c_str()));
2425 return ipmi::responseResponseError();
2426 }
2427 gpioFile << std::to_string(
2428 static_cast<uint8_t>(ledStateBits[i]));
2429 gpioFile.close();
2430 }
2431 break;
2432 }
2433 default:
2434 {
2435 // now only support two fault types
2436 return ipmi::responseParmOutOfRange();
2437 }
2438 }
2439
2440 return ipmi::responseSuccess();
2441}
2442
Richard Marian Thomaiyarea537d52019-04-24 21:33:48 +05302443ipmi::RspType<uint8_t> ipmiOEMReadBoardProductId()
2444{
2445 uint8_t prodId = 0;
2446 try
2447 {
Vernon Mauery15419dd2019-05-24 09:40:30 -07002448 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
Richard Marian Thomaiyarea537d52019-04-24 21:33:48 +05302449 const DbusObjectInfo& object = getDbusObject(
Vernon Mauery15419dd2019-05-24 09:40:30 -07002450 *dbus, "xyz.openbmc_project.Inventory.Item.Board",
Richard Marian Thomaiyarea537d52019-04-24 21:33:48 +05302451 "/xyz/openbmc_project/inventory/system/board/", "Baseboard");
2452 const Value& propValue = getDbusProperty(
Vernon Mauery15419dd2019-05-24 09:40:30 -07002453 *dbus, object.second, object.first,
Richard Marian Thomaiyarea537d52019-04-24 21:33:48 +05302454 "xyz.openbmc_project.Inventory.Item.Board", "ProductId");
2455 prodId = static_cast<uint8_t>(std::get<uint64_t>(propValue));
2456 }
2457 catch (std::exception& e)
2458 {
2459 phosphor::logging::log<phosphor::logging::level::ERR>(
2460 "ipmiOEMReadBoardProductId: Product ID read failed!",
2461 phosphor::logging::entry("ERR=%s", e.what()));
2462 }
2463 return ipmi::responseSuccess(prodId);
2464}
2465
Richard Marian Thomaiyard801e462019-06-20 01:05:40 +05302466/** @brief implements the get security mode command
2467 * @param ctx - ctx pointer
2468 *
2469 * @returns IPMI completion code with following data
2470 * - restriction mode value - As specified in
2471 * xyz.openbmc_project.Control.Security.RestrictionMode.interface.yaml
2472 * - special mode value - As specified in
2473 * xyz.openbmc_project.Control.Security.SpecialMode.interface.yaml
2474 */
2475ipmi::RspType<uint8_t, uint8_t> ipmiGetSecurityMode(ipmi::Context::ptr ctx)
2476{
2477 namespace securityNameSpace =
2478 sdbusplus::xyz::openbmc_project::Control::Security::server;
2479 uint8_t restrictionModeValue = 0;
2480 uint8_t specialModeValue = 0;
2481
2482 boost::system::error_code ec;
2483 auto varRestrMode = ctx->bus->yield_method_call<std::variant<std::string>>(
James Feist28c72902019-09-16 10:34:07 -07002484 ctx->yield, ec, restricionModeService, restricionModeBasePath,
Richard Marian Thomaiyard801e462019-06-20 01:05:40 +05302485 dBusPropertyIntf, dBusPropertyGetMethod, restricionModeIntf,
2486 restricionModeProperty);
2487 if (ec)
2488 {
2489 phosphor::logging::log<phosphor::logging::level::ERR>(
2490 "ipmiGetSecurityMode: failed to get RestrictionMode property",
2491 phosphor::logging::entry("ERROR=%s", ec.message().c_str()));
2492 return ipmi::responseUnspecifiedError();
2493 }
2494 restrictionModeValue = static_cast<uint8_t>(
2495 securityNameSpace::RestrictionMode::convertModesFromString(
2496 std::get<std::string>(varRestrMode)));
2497 auto varSpecialMode = ctx->bus->yield_method_call<std::variant<uint8_t>>(
James Feist28c72902019-09-16 10:34:07 -07002498 ctx->yield, ec, specialModeService, specialModeBasePath,
Richard Marian Thomaiyard801e462019-06-20 01:05:40 +05302499 dBusPropertyIntf, dBusPropertyGetMethod, specialModeIntf,
2500 specialModeProperty);
2501 if (ec)
2502 {
2503 phosphor::logging::log<phosphor::logging::level::ERR>(
2504 "ipmiGetSecurityMode: failed to get SpecialMode property",
2505 phosphor::logging::entry("ERROR=%s", ec.message().c_str()));
2506 // fall through, let us not worry about SpecialMode property, which is
2507 // not required in user scenario
2508 }
2509 else
2510 {
2511 specialModeValue = std::get<uint8_t>(varSpecialMode);
2512 }
2513 return ipmi::responseSuccess(restrictionModeValue, specialModeValue);
2514}
2515
2516/** @brief implements the set security mode command
2517 * Command allows to upgrade the restriction mode and won't allow
2518 * to downgrade from system interface
2519 * @param ctx - ctx pointer
2520 * @param restrictionMode - restriction mode value to be set.
2521 *
2522 * @returns IPMI completion code
2523 */
2524ipmi::RspType<> ipmiSetSecurityMode(ipmi::Context::ptr ctx,
2525 uint8_t restrictionMode)
2526{
2527 namespace securityNameSpace =
2528 sdbusplus::xyz::openbmc_project::Control::Security::server;
2529
2530 ChannelInfo chInfo;
2531 if (getChannelInfo(ctx->channel, chInfo) != ccSuccess)
2532 {
2533 phosphor::logging::log<phosphor::logging::level::ERR>(
2534 "ipmiSetSecurityMode: Failed to get Channel Info",
2535 phosphor::logging::entry("CHANNEL=%d", ctx->channel));
2536 return ipmi::responseUnspecifiedError();
2537 }
2538 auto reqMode =
2539 static_cast<securityNameSpace::RestrictionMode::Modes>(restrictionMode);
2540
2541 if ((reqMode < securityNameSpace::RestrictionMode::Modes::Provisioning) ||
2542 (reqMode >
2543 securityNameSpace::RestrictionMode::Modes::ProvisionedHostDisabled))
2544 {
2545 return ipmi::responseInvalidFieldRequest();
2546 }
2547
2548 boost::system::error_code ec;
2549 auto varRestrMode = ctx->bus->yield_method_call<std::variant<std::string>>(
James Feist28c72902019-09-16 10:34:07 -07002550 ctx->yield, ec, restricionModeService, restricionModeBasePath,
Richard Marian Thomaiyard801e462019-06-20 01:05:40 +05302551 dBusPropertyIntf, dBusPropertyGetMethod, restricionModeIntf,
2552 restricionModeProperty);
2553 if (ec)
2554 {
2555 phosphor::logging::log<phosphor::logging::level::ERR>(
2556 "ipmiSetSecurityMode: failed to get RestrictionMode property",
2557 phosphor::logging::entry("ERROR=%s", ec.message().c_str()));
2558 return ipmi::responseUnspecifiedError();
2559 }
2560 auto currentRestrictionMode =
2561 securityNameSpace::RestrictionMode::convertModesFromString(
2562 std::get<std::string>(varRestrMode));
2563
2564 if (chInfo.mediumType !=
2565 static_cast<uint8_t>(EChannelMediumType::lan8032) &&
2566 currentRestrictionMode > reqMode)
2567 {
2568 phosphor::logging::log<phosphor::logging::level::ERR>(
2569 "ipmiSetSecurityMode - Downgrading security mode not supported "
2570 "through system interface",
2571 phosphor::logging::entry(
2572 "CUR_MODE=%d", static_cast<uint8_t>(currentRestrictionMode)),
2573 phosphor::logging::entry("REQ_MODE=%d", restrictionMode));
2574 return ipmi::responseCommandNotAvailable();
2575 }
2576
2577 ec.clear();
2578 ctx->bus->yield_method_call<>(
James Feist28c72902019-09-16 10:34:07 -07002579 ctx->yield, ec, restricionModeService, restricionModeBasePath,
Richard Marian Thomaiyard801e462019-06-20 01:05:40 +05302580 dBusPropertyIntf, dBusPropertySetMethod, restricionModeIntf,
2581 restricionModeProperty,
2582 static_cast<std::variant<std::string>>(
2583 securityNameSpace::convertForMessage(reqMode)));
2584
2585 if (ec)
2586 {
2587 phosphor::logging::log<phosphor::logging::level::ERR>(
2588 "ipmiSetSecurityMode: failed to set RestrictionMode property",
2589 phosphor::logging::entry("ERROR=%s", ec.message().c_str()));
2590 return ipmi::responseUnspecifiedError();
2591 }
2592 return ipmi::responseSuccess();
2593}
2594
Vernon Mauery4ac799d2019-05-20 15:50:37 -07002595ipmi::RspType<uint8_t /* restore status */>
2596 ipmiRestoreConfiguration(const std::array<uint8_t, 3>& clr, uint8_t cmd)
2597{
2598 static constexpr std::array<uint8_t, 3> expClr = {'C', 'L', 'R'};
2599
2600 if (clr != expClr)
2601 {
2602 return ipmi::responseInvalidFieldRequest();
2603 }
2604 constexpr uint8_t cmdStatus = 0;
2605 constexpr uint8_t cmdDefaultRestore = 0xaa;
2606 constexpr uint8_t cmdFullRestore = 0xbb;
2607 constexpr uint8_t cmdFormat = 0xcc;
2608
2609 constexpr const char* restoreOpFname = "/tmp/.rwfs/.restore_op";
2610
2611 switch (cmd)
2612 {
2613 case cmdStatus:
2614 break;
2615 case cmdDefaultRestore:
2616 case cmdFullRestore:
2617 case cmdFormat:
2618 {
2619 // write file to rwfs root
2620 int value = (cmd - 1) & 0x03; // map aa, bb, cc => 1, 2, 3
2621 std::ofstream restoreFile(restoreOpFname);
2622 if (!restoreFile)
2623 {
2624 return ipmi::responseUnspecifiedError();
2625 }
2626 restoreFile << value << "\n";
2627 break;
2628 }
2629 default:
2630 return ipmi::responseInvalidFieldRequest();
2631 }
2632
2633 constexpr uint8_t restorePending = 0;
2634 constexpr uint8_t restoreComplete = 1;
2635
2636 uint8_t restoreStatus = std::filesystem::exists(restoreOpFname)
2637 ? restorePending
2638 : restoreComplete;
2639 return ipmi::responseSuccess(restoreStatus);
2640}
2641
Chen Yugang39736d52019-07-12 16:24:33 +08002642ipmi::RspType<uint8_t> ipmiOEMGetNmiSource(void)
2643{
2644 uint8_t bmcSource;
2645 namespace nmi = sdbusplus::com::intel::Control::server;
2646
2647 try
2648 {
2649 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
2650 std::string service =
2651 getService(*dbus, oemNmiSourceIntf, oemNmiSourceObjPath);
2652 Value variant =
2653 getDbusProperty(*dbus, service, oemNmiSourceObjPath,
2654 oemNmiSourceIntf, oemNmiBmcSourceObjPathProp);
2655
2656 switch (nmi::NMISource::convertBMCSourceSignalFromString(
2657 std::get<std::string>(variant)))
2658 {
2659 case nmi::NMISource::BMCSourceSignal::None:
2660 bmcSource = static_cast<uint8_t>(NmiSource::none);
2661 break;
2662 case nmi::NMISource::BMCSourceSignal::FpBtn:
2663 bmcSource = static_cast<uint8_t>(NmiSource::fpBtn);
2664 break;
2665 case nmi::NMISource::BMCSourceSignal::WdPreTimeout:
2666 bmcSource = static_cast<uint8_t>(NmiSource::wdPreTimeout);
2667 break;
2668 case nmi::NMISource::BMCSourceSignal::PefMatch:
2669 bmcSource = static_cast<uint8_t>(NmiSource::pefMatch);
2670 break;
2671 case nmi::NMISource::BMCSourceSignal::ChassisCmd:
2672 bmcSource = static_cast<uint8_t>(NmiSource::chassisCmd);
2673 break;
2674 case nmi::NMISource::BMCSourceSignal::MemoryError:
2675 bmcSource = static_cast<uint8_t>(NmiSource::memoryError);
2676 break;
2677 case nmi::NMISource::BMCSourceSignal::PciSerrPerr:
2678 bmcSource = static_cast<uint8_t>(NmiSource::pciSerrPerr);
2679 break;
2680 case nmi::NMISource::BMCSourceSignal::SouthbridgeNmi:
2681 bmcSource = static_cast<uint8_t>(NmiSource::southbridgeNmi);
2682 break;
2683 case nmi::NMISource::BMCSourceSignal::ChipsetNmi:
2684 bmcSource = static_cast<uint8_t>(NmiSource::chipsetNmi);
2685 break;
2686 default:
2687 phosphor::logging::log<phosphor::logging::level::ERR>(
2688 "NMI source: invalid property!",
2689 phosphor::logging::entry(
2690 "PROP=%s", std::get<std::string>(variant).c_str()));
2691 return ipmi::responseResponseError();
2692 }
2693 }
2694 catch (sdbusplus::exception::SdBusError& e)
2695 {
2696 phosphor::logging::log<phosphor::logging::level::ERR>(e.what());
2697 return ipmi::responseResponseError();
2698 }
2699
2700 return ipmi::responseSuccess(bmcSource);
2701}
2702
2703ipmi::RspType<> ipmiOEMSetNmiSource(uint8_t sourceId)
2704{
2705 namespace nmi = sdbusplus::com::intel::Control::server;
2706
2707 nmi::NMISource::BMCSourceSignal bmcSourceSignal =
2708 nmi::NMISource::BMCSourceSignal::None;
2709
2710 switch (NmiSource(sourceId))
2711 {
2712 case NmiSource::none:
2713 bmcSourceSignal = nmi::NMISource::BMCSourceSignal::None;
2714 break;
2715 case NmiSource::fpBtn:
2716 bmcSourceSignal = nmi::NMISource::BMCSourceSignal::FpBtn;
2717 break;
2718 case NmiSource::wdPreTimeout:
2719 bmcSourceSignal = nmi::NMISource::BMCSourceSignal::WdPreTimeout;
2720 break;
2721 case NmiSource::pefMatch:
2722 bmcSourceSignal = nmi::NMISource::BMCSourceSignal::PefMatch;
2723 break;
2724 case NmiSource::chassisCmd:
2725 bmcSourceSignal = nmi::NMISource::BMCSourceSignal::ChassisCmd;
2726 break;
2727 case NmiSource::memoryError:
2728 bmcSourceSignal = nmi::NMISource::BMCSourceSignal::MemoryError;
2729 break;
2730 case NmiSource::pciSerrPerr:
2731 bmcSourceSignal = nmi::NMISource::BMCSourceSignal::PciSerrPerr;
2732 break;
2733 case NmiSource::southbridgeNmi:
2734 bmcSourceSignal = nmi::NMISource::BMCSourceSignal::SouthbridgeNmi;
2735 break;
2736 case NmiSource::chipsetNmi:
2737 bmcSourceSignal = nmi::NMISource::BMCSourceSignal::ChipsetNmi;
2738 break;
2739 default:
2740 phosphor::logging::log<phosphor::logging::level::ERR>(
2741 "NMI source: invalid property!");
2742 return ipmi::responseResponseError();
2743 }
2744
2745 try
2746 {
2747 // keep NMI signal source
2748 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
2749 std::string service =
2750 getService(*dbus, oemNmiSourceIntf, oemNmiSourceObjPath);
2751 setDbusProperty(
2752 *dbus, service, oemNmiSourceObjPath, oemNmiSourceIntf,
2753 oemNmiBmcSourceObjPathProp,
2754 sdbusplus::com::intel::Control::server::convertForMessage(
2755 bmcSourceSignal));
Chen Yugang99be6332019-08-09 16:20:48 +08002756 // set Enabled property to inform NMI source handling
2757 // to trigger a NMI_OUT BSOD.
2758 // if it's triggered by NMI source property changed,
2759 // NMI_OUT BSOD could be missed if the same source occurs twice in a row
2760 if (bmcSourceSignal != nmi::NMISource::BMCSourceSignal::None)
2761 {
2762 setDbusProperty(*dbus, service, oemNmiSourceObjPath,
2763 oemNmiSourceIntf, oemNmiEnabledObjPathProp,
2764 static_cast<bool>(true));
2765 }
Chen Yugang39736d52019-07-12 16:24:33 +08002766 }
2767 catch (sdbusplus::exception_t& e)
2768 {
2769 phosphor::logging::log<phosphor::logging::level::ERR>(e.what());
2770 return ipmi::responseResponseError();
2771 }
2772
2773 return ipmi::responseSuccess();
2774}
2775
James Feist63efafa2019-07-24 12:39:21 -07002776namespace dimmOffset
2777{
2778constexpr const char* dimmPower = "DimmPower";
2779constexpr const char* staticCltt = "StaticCltt";
2780constexpr const char* offsetPath = "/xyz/openbmc_project/Inventory/Item/Dimm";
2781constexpr const char* offsetInterface =
2782 "xyz.openbmc_project.Inventory.Item.Dimm.Offset";
2783constexpr const char* property = "DimmOffset";
2784
2785}; // namespace dimmOffset
2786
2787ipmi::RspType<>
2788 ipmiOEMSetDimmOffset(uint8_t type,
2789 const std::vector<std::tuple<uint8_t, uint8_t>>& data)
2790{
2791 if (type != static_cast<uint8_t>(dimmOffsetTypes::dimmPower) &&
2792 type != static_cast<uint8_t>(dimmOffsetTypes::staticCltt))
2793 {
2794 return ipmi::responseInvalidFieldRequest();
2795 }
2796
2797 if (data.empty())
2798 {
2799 return ipmi::responseInvalidFieldRequest();
2800 }
2801 nlohmann::json json;
2802
2803 std::ifstream jsonStream(dimmOffsetFile);
2804 if (jsonStream.good())
2805 {
2806 json = nlohmann::json::parse(jsonStream, nullptr, false);
2807 if (json.is_discarded())
2808 {
2809 json = nlohmann::json();
2810 }
2811 jsonStream.close();
2812 }
2813
2814 std::string typeName;
2815 if (type == static_cast<uint8_t>(dimmOffsetTypes::dimmPower))
2816 {
2817 typeName = dimmOffset::dimmPower;
2818 }
2819 else
2820 {
2821 typeName = dimmOffset::staticCltt;
2822 }
2823
2824 nlohmann::json& field = json[typeName];
2825
2826 for (const auto& [index, value] : data)
2827 {
2828 field[index] = value;
2829 }
2830
2831 for (nlohmann::json& val : field)
2832 {
2833 if (val == nullptr)
2834 {
2835 val = static_cast<uint8_t>(0);
2836 }
2837 }
2838
2839 std::ofstream output(dimmOffsetFile);
2840 if (!output.good())
2841 {
2842 std::cerr << "Error writing json file\n";
2843 return ipmi::responseResponseError();
2844 }
2845
2846 output << json.dump(4);
2847
2848 if (type == static_cast<uint8_t>(dimmOffsetTypes::staticCltt))
2849 {
2850 std::shared_ptr<sdbusplus::asio::connection> bus = getSdBus();
2851
2852 std::variant<std::vector<uint8_t>> offsets =
2853 field.get<std::vector<uint8_t>>();
2854 auto call = bus->new_method_call(
2855 settingsBusName, dimmOffset::offsetPath, PROP_INTF, "Set");
2856 call.append(dimmOffset::offsetInterface, dimmOffset::property, offsets);
2857 try
2858 {
2859 bus->call(call);
2860 }
2861 catch (sdbusplus::exception_t& e)
2862 {
2863 phosphor::logging::log<phosphor::logging::level::ERR>(
2864 "ipmiOEMSetDimmOffset: can't set dimm offsets!",
2865 phosphor::logging::entry("ERR=%s", e.what()));
2866 return ipmi::responseResponseError();
2867 }
2868 }
2869
2870 return ipmi::responseSuccess();
2871}
2872
2873ipmi::RspType<uint8_t> ipmiOEMGetDimmOffset(uint8_t type, uint8_t index)
2874{
2875
2876 if (type != static_cast<uint8_t>(dimmOffsetTypes::dimmPower) &&
2877 type != static_cast<uint8_t>(dimmOffsetTypes::staticCltt))
2878 {
2879 return ipmi::responseInvalidFieldRequest();
2880 }
2881
2882 std::ifstream jsonStream(dimmOffsetFile);
2883
2884 auto json = nlohmann::json::parse(jsonStream, nullptr, false);
2885 if (json.is_discarded())
2886 {
2887 std::cerr << "File error in " << dimmOffsetFile << "\n";
2888 return ipmi::responseResponseError();
2889 }
2890
2891 std::string typeName;
2892 if (type == static_cast<uint8_t>(dimmOffsetTypes::dimmPower))
2893 {
2894 typeName = dimmOffset::dimmPower;
2895 }
2896 else
2897 {
2898 typeName = dimmOffset::staticCltt;
2899 }
2900
2901 auto it = json.find(typeName);
2902 if (it == json.end())
2903 {
2904 return ipmi::responseInvalidFieldRequest();
2905 }
2906
2907 if (it->size() <= index)
2908 {
2909 return ipmi::responseInvalidFieldRequest();
2910 }
2911
2912 uint8_t resp = it->at(index).get<uint8_t>();
2913 return ipmi::responseSuccess(resp);
2914}
2915
Chen,Yugang4f7e76b2019-08-20 09:28:06 +08002916namespace boot_options
2917{
2918
2919using namespace sdbusplus::xyz::openbmc_project::Control::Boot::server;
2920using IpmiValue = uint8_t;
2921constexpr auto ipmiDefault = 0;
2922
2923std::map<IpmiValue, Source::Sources> sourceIpmiToDbus = {
2924 {0x01, Source::Sources::Network},
2925 {0x02, Source::Sources::Disk},
2926 {0x05, Source::Sources::ExternalMedia},
2927 {0x0f, Source::Sources::RemovableMedia},
2928 {ipmiDefault, Source::Sources::Default}};
2929
2930std::map<IpmiValue, Mode::Modes> modeIpmiToDbus = {
Chen Yugangca12a7b2019-09-03 18:11:44 +08002931 {0x06, Mode::Modes::Setup}, {ipmiDefault, Mode::Modes::Regular}};
Chen,Yugang4f7e76b2019-08-20 09:28:06 +08002932
2933std::map<Source::Sources, IpmiValue> sourceDbusToIpmi = {
2934 {Source::Sources::Network, 0x01},
2935 {Source::Sources::Disk, 0x02},
2936 {Source::Sources::ExternalMedia, 0x05},
2937 {Source::Sources::RemovableMedia, 0x0f},
2938 {Source::Sources::Default, ipmiDefault}};
2939
2940std::map<Mode::Modes, IpmiValue> modeDbusToIpmi = {
Chen Yugangca12a7b2019-09-03 18:11:44 +08002941 {Mode::Modes::Setup, 0x06}, {Mode::Modes::Regular, ipmiDefault}};
Chen,Yugang4f7e76b2019-08-20 09:28:06 +08002942
2943static constexpr auto bootModeIntf = "xyz.openbmc_project.Control.Boot.Mode";
2944static constexpr auto bootSourceIntf =
2945 "xyz.openbmc_project.Control.Boot.Source";
2946static constexpr auto enabledIntf = "xyz.openbmc_project.Object.Enable";
2947static constexpr auto persistentObjPath =
2948 "/xyz/openbmc_project/control/host0/boot";
2949static constexpr auto oneTimePath =
2950 "/xyz/openbmc_project/control/host0/boot/one_time";
2951static constexpr auto bootSourceProp = "BootSource";
2952static constexpr auto bootModeProp = "BootMode";
2953static constexpr auto oneTimeBootEnableProp = "Enabled";
2954static constexpr auto httpBootMode =
2955 "xyz.openbmc_project.Control.Boot.Source.Sources.Http";
2956
2957enum class BootOptionParameter : size_t
2958{
2959 setInProgress = 0x0,
2960 bootFlags = 0x5,
2961};
2962static constexpr uint8_t setComplete = 0x0;
2963static constexpr uint8_t setInProgress = 0x1;
2964static uint8_t transferStatus = setComplete;
2965static constexpr uint8_t setParmVersion = 0x01;
2966static constexpr uint8_t setParmBootFlagsPermanent = 0x40;
2967static constexpr uint8_t setParmBootFlagsValidOneTime = 0x80;
2968static constexpr uint8_t setParmBootFlagsValidPermanent = 0xC0;
2969static constexpr uint8_t httpBoot = 0xd;
2970static constexpr uint8_t bootSourceMask = 0x3c;
2971
2972} // namespace boot_options
2973
2974ipmi::RspType<uint8_t, // version
2975 uint8_t, // param
2976 uint8_t, // data0, dependent on parameter
2977 std::optional<uint8_t> // data1, dependent on parameter
2978 >
2979 ipmiOemGetEfiBootOptions(uint8_t parameter, uint8_t set, uint8_t block)
2980{
2981 using namespace boot_options;
2982 uint8_t bootOption = 0;
2983
2984 if (parameter == static_cast<uint8_t>(BootOptionParameter::setInProgress))
2985 {
2986 return ipmi::responseSuccess(setParmVersion, parameter, transferStatus,
2987 std::nullopt);
2988 }
2989
2990 if (parameter != static_cast<uint8_t>(BootOptionParameter::bootFlags))
2991 {
2992 phosphor::logging::log<phosphor::logging::level::ERR>(
2993 "Unsupported parameter");
2994 return ipmi::responseResponseError();
2995 }
2996
2997 try
2998 {
2999 auto oneTimeEnabled = false;
3000 // read one time Enabled property
3001 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
3002 std::string service = getService(*dbus, enabledIntf, oneTimePath);
3003 Value variant = getDbusProperty(*dbus, service, oneTimePath,
3004 enabledIntf, oneTimeBootEnableProp);
3005 oneTimeEnabled = std::get<bool>(variant);
3006
3007 // get BootSource and BootMode properties
3008 // according to oneTimeEnable
3009 auto bootObjPath = oneTimePath;
3010 if (oneTimeEnabled == false)
3011 {
3012 bootObjPath = persistentObjPath;
3013 }
3014
3015 service = getService(*dbus, bootModeIntf, bootObjPath);
3016 variant = getDbusProperty(*dbus, service, bootObjPath, bootModeIntf,
3017 bootModeProp);
3018
3019 auto bootMode =
3020 Mode::convertModesFromString(std::get<std::string>(variant));
3021
3022 service = getService(*dbus, bootSourceIntf, bootObjPath);
3023 variant = getDbusProperty(*dbus, service, bootObjPath, bootSourceIntf,
3024 bootSourceProp);
3025
3026 if (std::get<std::string>(variant) == httpBootMode)
3027 {
3028 bootOption = httpBoot;
3029 }
3030 else
3031 {
3032 auto bootSource = Source::convertSourcesFromString(
3033 std::get<std::string>(variant));
3034 bootOption = sourceDbusToIpmi.at(bootSource);
3035 if (Source::Sources::Default == bootSource)
3036 {
3037 bootOption = modeDbusToIpmi.at(bootMode);
3038 }
3039 }
3040
3041 uint8_t oneTime = oneTimeEnabled ? setParmBootFlagsValidOneTime
3042 : setParmBootFlagsValidPermanent;
3043 bootOption <<= 2; // shift for responseconstexpr
3044 return ipmi::responseSuccess(setParmVersion, parameter, oneTime,
3045 bootOption);
3046 }
3047 catch (sdbusplus::exception_t& e)
3048 {
3049 phosphor::logging::log<phosphor::logging::level::ERR>(e.what());
3050 return ipmi::responseResponseError();
3051 }
3052}
3053
3054ipmi::RspType<> ipmiOemSetEfiBootOptions(uint8_t bootFlag, uint8_t bootParam,
3055 std::optional<uint8_t> bootOption)
3056{
3057 using namespace boot_options;
3058 auto oneTimeEnabled = false;
3059
3060 if (bootFlag == static_cast<uint8_t>(BootOptionParameter::setInProgress))
3061 {
3062 if (bootOption)
3063 {
3064 return ipmi::responseReqDataLenInvalid();
3065 }
3066
3067 if (transferStatus == setInProgress)
3068 {
3069 phosphor::logging::log<phosphor::logging::level::ERR>(
3070 "boot option set in progress!");
3071 return ipmi::responseResponseError();
3072 }
3073
3074 transferStatus = bootParam;
3075 return ipmi::responseSuccess();
3076 }
3077
3078 if (bootFlag != (uint8_t)BootOptionParameter::bootFlags)
3079 {
3080 phosphor::logging::log<phosphor::logging::level::ERR>(
3081 "Unsupported parameter");
3082 return ipmi::responseResponseError();
3083 }
3084
3085 if (!bootOption)
3086 {
3087 return ipmi::responseReqDataLenInvalid();
3088 }
3089
3090 if (((bootOption.value() & bootSourceMask) >> 2) !=
3091 httpBoot) // not http boot, exit
3092 {
3093 phosphor::logging::log<phosphor::logging::level::ERR>(
3094 "wrong boot option parameter!");
3095 return ipmi::responseParmOutOfRange();
3096 }
3097
3098 try
3099 {
3100 bool permanent = (bootParam & setParmBootFlagsPermanent) ==
3101 setParmBootFlagsPermanent;
3102
3103 // read one time Enabled property
3104 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
3105 std::string service = getService(*dbus, enabledIntf, oneTimePath);
3106 Value variant = getDbusProperty(*dbus, service, oneTimePath,
3107 enabledIntf, oneTimeBootEnableProp);
3108 oneTimeEnabled = std::get<bool>(variant);
3109
3110 /*
3111 * Check if the current boot setting is onetime or permanent, if the
3112 * request in the command is otherwise, then set the "Enabled"
3113 * property in one_time object path to 'True' to indicate onetime
3114 * and 'False' to indicate permanent.
3115 *
3116 * Once the onetime/permanent setting is applied, then the bootMode
3117 * and bootSource is updated for the corresponding object.
3118 */
3119 if (permanent == oneTimeEnabled)
3120 {
3121 setDbusProperty(*dbus, service, oneTimePath, enabledIntf,
3122 oneTimeBootEnableProp, !permanent);
3123 }
3124
3125 // set BootSource and BootMode properties
3126 // according to oneTimeEnable or persistent
3127 auto bootObjPath = oneTimePath;
3128 if (oneTimeEnabled == false)
3129 {
3130 bootObjPath = persistentObjPath;
3131 }
3132 std::string bootMode =
3133 "xyz.openbmc_project.Control.Boot.Mode.Modes.Regular";
3134 std::string bootSource = httpBootMode;
3135
3136 service = getService(*dbus, bootModeIntf, bootObjPath);
3137 setDbusProperty(*dbus, service, bootObjPath, bootModeIntf, bootModeProp,
3138 bootMode);
3139
3140 service = getService(*dbus, bootSourceIntf, bootObjPath);
3141 setDbusProperty(*dbus, service, bootObjPath, bootSourceIntf,
3142 bootSourceProp, bootSource);
3143 }
3144 catch (sdbusplus::exception_t& e)
3145 {
3146 phosphor::logging::log<phosphor::logging::level::ERR>(e.what());
3147 return ipmi::responseResponseError();
3148 }
3149
3150 return ipmi::responseSuccess();
3151}
3152
Chen Yugangdd6122b2019-09-24 07:44:50 +08003153ipmi::RspType<>
3154 ipmiOemSetBootOptions(uint8_t bootFlag, uint8_t bootParam,
3155 std::optional<uint8_t> bootOption,
3156 std::optional<std::array<uint8_t, 3>> bootResv)
Chen Yugangca12a7b2019-09-03 18:11:44 +08003157{
3158 bool oneTimeEnabled = false;
3159 uint8_t bootOptionValue = 0;
3160 static constexpr const uint8_t shiftBits = 2;
3161
3162 if (bootFlag ==
3163 static_cast<uint8_t>(boot_options::BootOptionParameter::setInProgress))
3164 {
3165 if (bootOption)
3166 {
3167 return ipmi::responseReqDataLenInvalid();
3168 }
3169
Chen Yugangca12a7b2019-09-03 18:11:44 +08003170 boot_options::transferStatus = bootParam;
3171 return ipmi::responseSuccess();
3172 }
3173
3174 if (bootFlag !=
3175 static_cast<uint8_t>(boot_options::BootOptionParameter::bootFlags))
3176 {
3177 phosphor::logging::log<phosphor::logging::level::ERR>(
3178 "Unsupported parameter");
3179 return ipmi::responseResponseError();
3180 }
3181
3182 if (!bootOption)
3183 {
3184 return ipmi::responseReqDataLenInvalid();
3185 }
3186 bootOptionValue =
3187 (bootOption.value() & boot_options::bootSourceMask) >> shiftBits;
3188
3189 try
3190 {
3191 bool permanent =
3192 (bootParam & boot_options::setParmBootFlagsPermanent) ==
3193 boot_options::setParmBootFlagsPermanent;
3194 auto bootMode = boot_options::Mode::Modes::Regular;
3195 auto bootSource = boot_options::Source::Sources::Default;
3196
3197 // read one time Enabled property
3198 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
3199 std::string service = getService(*dbus, boot_options::enabledIntf,
3200 boot_options::oneTimePath);
3201 Value variant = getDbusProperty(
3202 *dbus, service, boot_options::oneTimePath,
3203 boot_options::enabledIntf, boot_options::oneTimeBootEnableProp);
3204 oneTimeEnabled = std::get_if<bool>(&variant);
3205
3206 /*
3207 * Check if the current boot setting is onetime or permanent, if the
3208 * request in the command is otherwise, then set the "Enabled"
3209 * property in one_time object path to 'True' to indicate onetime
3210 * and 'False' to indicate permanent.
3211 *
3212 * Once the onetime/permanent setting is applied, then the bootMode
3213 * and bootSource is updated for the corresponding object.
3214 */
3215 if (permanent == oneTimeEnabled)
3216 {
3217 setDbusProperty(*dbus, service, boot_options::oneTimePath,
3218 boot_options::enabledIntf,
3219 boot_options::oneTimeBootEnableProp, !permanent);
3220 }
3221
3222 // set BootSource and BootMode properties
3223 // according to oneTimeEnable or persistent
3224 auto bootObjPath = boot_options::oneTimePath;
3225 if (oneTimeEnabled == false)
3226 {
3227 bootObjPath = boot_options::persistentObjPath;
3228 }
3229
3230 auto modeItr = boot_options::modeIpmiToDbus.find(bootOptionValue);
3231 auto sourceItr = boot_options::sourceIpmiToDbus.find(bootOptionValue);
3232
3233 if (boot_options::sourceIpmiToDbus.end() != sourceItr)
3234 {
3235 bootSource = sourceItr->second;
3236 }
3237
3238 if (boot_options::modeIpmiToDbus.end() != modeItr)
3239 {
3240 bootMode = modeItr->second;
3241 }
3242
3243 if ((boot_options::modeIpmiToDbus.end() == modeItr) &&
3244 (boot_options::sourceIpmiToDbus.end() == sourceItr))
3245 {
3246 // return error if boot option is not supported
3247 return ipmi::responseInvalidFieldRequest();
3248 }
3249 service = getService(*dbus, boot_options::bootModeIntf, bootObjPath);
3250 setDbusProperty(*dbus, service, bootObjPath, boot_options::bootModeIntf,
3251 boot_options::bootModeProp,
3252 convertForMessage(bootMode));
3253
3254 service = getService(*dbus, boot_options::bootSourceIntf, bootObjPath);
3255 setDbusProperty(
3256 *dbus, service, bootObjPath, boot_options::bootSourceIntf,
3257 boot_options::bootSourceProp, convertForMessage(bootSource));
3258 }
3259 catch (sdbusplus::exception_t& e)
3260 {
3261 phosphor::logging::log<phosphor::logging::level::ERR>(e.what());
3262 return ipmi::responseResponseError();
3263 }
3264
3265 return ipmi::responseSuccess();
3266}
3267
Cheng C Yang4e6ee152019-09-25 10:27:44 +08003268using BasicVariantType =
3269 std::variant<std::vector<std::string>, std::vector<uint64_t>, std::string,
3270 int64_t, uint64_t, double, int32_t, uint32_t, int16_t,
3271 uint16_t, uint8_t, bool>;
3272using PropertyMapType =
3273 boost::container::flat_map<std::string, BasicVariantType>;
3274static constexpr const std::array<const char*, 1> psuPresenceTypes = {
3275 "xyz.openbmc_project.Configuration.PSUPresence"};
3276int getPSUAddress(ipmi::Context::ptr ctx, uint8_t& bus,
3277 std::vector<uint64_t>& addrTable)
3278{
3279 boost::system::error_code ec;
3280 GetSubTreeType subtree = ctx->bus->yield_method_call<GetSubTreeType>(
3281 ctx->yield, ec, "xyz.openbmc_project.ObjectMapper",
3282 "/xyz/openbmc_project/object_mapper",
3283 "xyz.openbmc_project.ObjectMapper", "GetSubTree",
3284 "/xyz/openbmc_project/inventory/system", 3, psuPresenceTypes);
3285 if (ec)
3286 {
3287 phosphor::logging::log<phosphor::logging::level::ERR>(
3288 "Failed to set dbus property to cold redundancy");
3289 return -1;
3290 }
3291 for (const auto& object : subtree)
3292 {
3293 std::string pathName = object.first;
3294 for (const auto& serviceIface : object.second)
3295 {
3296 std::string serviceName = serviceIface.first;
3297
3298 ec.clear();
3299 PropertyMapType propMap =
3300 ctx->bus->yield_method_call<PropertyMapType>(
3301 ctx->yield, ec, serviceName, pathName,
3302 "org.freedesktop.DBus.Properties", "GetAll",
3303 "xyz.openbmc_project.Configuration.PSUPresence");
3304 if (ec)
3305 {
3306 phosphor::logging::log<phosphor::logging::level::ERR>(
3307 "Failed to set dbus property to cold redundancy");
3308 return -1;
3309 }
3310 auto psuBus = std::get_if<uint64_t>(&propMap["Bus"]);
3311 auto psuAddress =
3312 std::get_if<std::vector<uint64_t>>(&propMap["Address"]);
3313
3314 if (psuBus == nullptr || psuAddress == nullptr)
3315 {
3316 std::cerr << "error finding necessary "
3317 "entry in configuration\n";
3318 return -1;
3319 }
3320 bus = static_cast<uint8_t>(*psuBus);
3321 addrTable = *psuAddress;
3322 return 0;
3323 }
3324 }
3325 return -1;
3326}
3327
3328static const constexpr uint8_t addrOffset = 8;
3329static const constexpr uint8_t psuRevision = 0xd9;
3330static const constexpr uint8_t defaultPSUBus = 7;
3331// Second Minor, Primary Minor, Major
3332static const constexpr size_t verLen = 3;
3333ipmi::RspType<std::vector<uint8_t>> ipmiOEMGetPSUVersion(ipmi::Context::ptr ctx)
3334{
3335 uint8_t bus = defaultPSUBus;
3336 std::vector<uint64_t> addrTable;
3337 std::vector<uint8_t> result;
3338 if (getPSUAddress(ctx, bus, addrTable))
3339 {
3340 std::cerr << "Failed to get PSU bus and address\n";
3341 return ipmi::responseResponseError();
3342 }
3343
3344 for (const auto& slaveAddr : addrTable)
3345 {
3346 std::vector<uint8_t> writeData = {psuRevision};
3347 std::vector<uint8_t> readBuf(verLen);
3348 uint8_t addr = static_cast<uint8_t>(slaveAddr) + addrOffset;
3349 std::string i2cBus = "/dev/i2c-" + std::to_string(bus);
3350
3351 auto retI2C = ipmi::i2cWriteRead(i2cBus, addr, writeData, readBuf);
3352 if (retI2C != ipmi::ccSuccess)
3353 {
3354 for (size_t idx = 0; idx < verLen; idx++)
3355 {
3356 result.emplace_back(0x00);
3357 }
3358 }
3359 else
3360 {
3361 for (const uint8_t& data : readBuf)
3362 {
3363 result.emplace_back(data);
3364 }
3365 }
3366 }
3367
3368 return ipmi::responseSuccess(result);
3369}
3370
Jason M. Bills64796042018-10-03 16:51:55 -07003371static void registerOEMFunctions(void)
Jia, Chunhuia835eaa2018-09-05 09:00:41 +08003372{
3373 phosphor::logging::log<phosphor::logging::level::INFO>(
3374 "Registering OEM commands");
Vernon Mauery98bbf692019-09-16 11:14:59 -07003375 ipmiPrintAndRegister(intel::netFnGeneral, IPMI_CMD_WILDCARD, NULL,
Jason M. Bills64796042018-10-03 16:51:55 -07003376 ipmiOEMWildcard,
3377 PRIVILEGE_USER); // wildcard default handler
Vernon Mauery98bbf692019-09-16 11:14:59 -07003378
3379 ipmiPrintAndRegister(intel::netFnApp, IPMI_CMD_WILDCARD, NULL,
Jason M. Bills64796042018-10-03 16:51:55 -07003380 ipmiOEMWildcard,
3381 PRIVILEGE_USER); // wildcard default handler
Vernon Mauery98bbf692019-09-16 11:14:59 -07003382
3383 ipmiPrintAndRegister(intel::netFnGeneral,
3384 intel::general::cmdGetChassisIdentifier, NULL,
3385 ipmiOEMGetChassisIdentifier,
3386 PRIVILEGE_USER); // get chassis identifier
3387
3388 ipmiPrintAndRegister(intel::netFnGeneral, intel::general::cmdSetSystemGUID,
3389 NULL, ipmiOEMSetSystemGUID,
3390 PRIVILEGE_ADMIN); // set system guid
Jason M. Billsb02bf092019-08-15 13:01:56 -07003391
3392 // <Disable BMC System Reset Action>
Vernon Mauery98bbf692019-09-16 11:14:59 -07003393 registerHandler(prioOemBase, intel::netFnGeneral,
3394 intel::general::cmdDisableBMCSystemReset, Privilege::Admin,
3395 ipmiOEMDisableBMCSystemReset);
3396
Jason M. Billsb02bf092019-08-15 13:01:56 -07003397 // <Get BMC Reset Disables>
Vernon Mauery98bbf692019-09-16 11:14:59 -07003398 registerHandler(prioOemBase, intel::netFnGeneral,
3399 intel::general::cmdGetBMCResetDisables, Privilege::Admin,
3400 ipmiOEMGetBMCResetDisables);
Jason M. Billsb02bf092019-08-15 13:01:56 -07003401
Vernon Mauery98bbf692019-09-16 11:14:59 -07003402 ipmiPrintAndRegister(intel::netFnGeneral, intel::general::cmdSetBIOSID,
3403 NULL, ipmiOEMSetBIOSID, PRIVILEGE_ADMIN);
Jia, Chunhuicc49b542019-03-20 15:41:07 +08003404
Vernon Mauery98bbf692019-09-16 11:14:59 -07003405 ipmiPrintAndRegister(intel::netFnGeneral,
3406 intel::general::cmdGetOEMDeviceInfo, NULL,
3407 ipmiOEMGetDeviceInfo, PRIVILEGE_USER);
Jia, Chunhuicc49b542019-03-20 15:41:07 +08003408
Vernon Mauery98bbf692019-09-16 11:14:59 -07003409 ipmiPrintAndRegister(intel::netFnGeneral,
3410 intel::general::cmdGetAICSlotFRUIDSlotPosRecords, NULL,
3411 ipmiOEMGetAICFRU, PRIVILEGE_USER);
Suryakanth Sekard509eb92018-11-15 17:44:11 +05303412
Vernon Mauery98bbf692019-09-16 11:14:59 -07003413 registerHandler(prioOpenBmcBase, intel::netFnGeneral,
3414 intel::general::cmdSendEmbeddedFWUpdStatus,
3415 Privilege::Operator, ipmiOEMSendEmbeddedFwUpdStatus);
Suryakanth Sekard509eb92018-11-15 17:44:11 +05303416
Vernon Mauery98bbf692019-09-16 11:14:59 -07003417 ipmiPrintAndRegister(intel::netFnGeneral,
3418 intel::general::cmdSetPowerRestoreDelay, NULL,
3419 ipmiOEMSetPowerRestoreDelay, PRIVILEGE_OPERATOR);
3420
3421 ipmiPrintAndRegister(intel::netFnGeneral,
3422 intel::general::cmdGetPowerRestoreDelay, NULL,
3423 ipmiOEMGetPowerRestoreDelay, PRIVILEGE_USER);
3424
3425 registerHandler(prioOpenBmcBase, intel::netFnGeneral,
3426 intel::general::cmdSetOEMUser2Activation,
3427 Privilege::Callback, ipmiOEMSetUser2Activation);
3428
3429 registerHandler(prioOpenBmcBase, intel::netFnGeneral,
3430 intel::general::cmdSetSpecialUserPassword,
3431 Privilege::Callback, ipmiOEMSetSpecialUserPassword);
Richard Marian Thomaiyarfc5e9852019-04-14 15:06:27 +05303432
Jason M. Bills42bd9c82019-06-28 16:39:34 -07003433 // <Get Processor Error Config>
Vernon Mauery98bbf692019-09-16 11:14:59 -07003434 registerHandler(prioOemBase, intel::netFnGeneral,
3435 intel::general::cmdGetProcessorErrConfig, Privilege::User,
3436 ipmiOEMGetProcessorErrConfig);
3437
Jason M. Bills42bd9c82019-06-28 16:39:34 -07003438 // <Set Processor Error Config>
Vernon Mauery98bbf692019-09-16 11:14:59 -07003439 registerHandler(prioOemBase, intel::netFnGeneral,
3440 intel::general::cmdSetProcessorErrConfig, Privilege::Admin,
3441 ipmiOEMSetProcessorErrConfig);
Jason M. Bills42bd9c82019-06-28 16:39:34 -07003442
Vernon Mauery98bbf692019-09-16 11:14:59 -07003443 ipmiPrintAndRegister(intel::netFnGeneral,
3444 intel::general::cmdSetShutdownPolicy, NULL,
3445 ipmiOEMSetShutdownPolicy, PRIVILEGE_ADMIN);
James Feist91244a62019-02-19 15:04:54 -08003446
Vernon Mauery98bbf692019-09-16 11:14:59 -07003447 ipmiPrintAndRegister(intel::netFnGeneral,
3448 intel::general::cmdGetShutdownPolicy, NULL,
3449 ipmiOEMGetShutdownPolicy, PRIVILEGE_ADMIN);
James Feist91244a62019-02-19 15:04:54 -08003450
Vernon Mauery98bbf692019-09-16 11:14:59 -07003451 ipmiPrintAndRegister(intel::netFnGeneral, intel::general::cmdSetFanConfig,
3452 NULL, ipmiOEMSetFanConfig, PRIVILEGE_USER);
James Feist91244a62019-02-19 15:04:54 -08003453
Vernon Mauery98bbf692019-09-16 11:14:59 -07003454 registerHandler(prioOemBase, intel::netFnGeneral,
3455 intel::general::cmdGetFanConfig, Privilege::User,
3456 ipmiOEMGetFanConfig);
James Feist5f957ca2019-03-14 15:33:55 -07003457
Vernon Mauery98bbf692019-09-16 11:14:59 -07003458 registerHandler(prioOemBase, intel::netFnGeneral,
3459 intel::general::cmdGetFanSpeedOffset, Privilege::User,
3460 ipmiOEMGetFanSpeedOffset);
James Feistacc8a4e2019-04-02 14:23:57 -07003461
Vernon Mauery98bbf692019-09-16 11:14:59 -07003462 registerHandler(prioOemBase, intel::netFnGeneral,
3463 intel::general::cmdSetFanSpeedOffset, Privilege::User,
3464 ipmiOEMSetFanSpeedOffset);
James Feistacc8a4e2019-04-02 14:23:57 -07003465
Vernon Mauery98bbf692019-09-16 11:14:59 -07003466 registerHandler(prioOemBase, intel::netFnGeneral,
3467 intel::general::cmdSetFscParameter, Privilege::User,
3468 ipmiOEMSetFscParameter);
James Feist5f957ca2019-03-14 15:33:55 -07003469
Vernon Mauery98bbf692019-09-16 11:14:59 -07003470 registerHandler(prioOemBase, intel::netFnGeneral,
3471 intel::general::cmdGetFscParameter, Privilege::User,
3472 ipmiOEMGetFscParameter);
Richard Marian Thomaiyarea537d52019-04-24 21:33:48 +05303473
Vernon Mauery98bbf692019-09-16 11:14:59 -07003474 registerHandler(prioOpenBmcBase, intel::netFnGeneral,
3475 intel::general::cmdReadBaseBoardProductId, Privilege::Admin,
3476 ipmiOEMReadBoardProductId);
Chen Yugang39736d52019-07-12 16:24:33 +08003477
Vernon Mauery98bbf692019-09-16 11:14:59 -07003478 registerHandler(prioOemBase, intel::netFnGeneral,
3479 intel::general::cmdGetNmiStatus, Privilege::User,
3480 ipmiOEMGetNmiSource);
Chen Yugang39736d52019-07-12 16:24:33 +08003481
Vernon Mauery98bbf692019-09-16 11:14:59 -07003482 registerHandler(prioOemBase, intel::netFnGeneral,
3483 intel::general::cmdSetNmiStatus, Privilege::Operator,
3484 ipmiOEMSetNmiSource);
Chen,Yugang4f7e76b2019-08-20 09:28:06 +08003485
Vernon Mauery98bbf692019-09-16 11:14:59 -07003486 registerHandler(prioOemBase, intel::netFnGeneral,
3487 intel::general::cmdGetEfiBootOptions, Privilege::User,
3488 ipmiOemGetEfiBootOptions);
Chen,Yugang4f7e76b2019-08-20 09:28:06 +08003489
Vernon Mauery98bbf692019-09-16 11:14:59 -07003490 registerHandler(prioOemBase, intel::netFnGeneral,
3491 intel::general::cmdSetEfiBootOptions, Privilege::Operator,
3492 ipmiOemSetEfiBootOptions);
Richard Marian Thomaiyard801e462019-06-20 01:05:40 +05303493
Vernon Mauery98bbf692019-09-16 11:14:59 -07003494 registerHandler(prioOemBase, intel::netFnGeneral,
3495 intel::general::cmdGetSecurityMode, Privilege::User,
3496 ipmiGetSecurityMode);
Richard Marian Thomaiyard801e462019-06-20 01:05:40 +05303497
Vernon Mauery98bbf692019-09-16 11:14:59 -07003498 registerHandler(prioOemBase, intel::netFnGeneral,
3499 intel::general::cmdSetSecurityMode, Privilege::Admin,
3500 ipmiSetSecurityMode);
Vernon Mauery4ac799d2019-05-20 15:50:37 -07003501
Vernon Mauery98bbf692019-09-16 11:14:59 -07003502 ipmiPrintAndRegister(intel::netFnGeneral, intel::general::cmdGetLEDStatus,
3503 NULL, ipmiOEMGetLEDStatus, PRIVILEGE_ADMIN);
Cheng C Yang773703a2019-08-15 09:41:11 +08003504
Vernon Mauery98bbf692019-09-16 11:14:59 -07003505 ipmiPrintAndRegister(ipmi::intel::netFnPlatform,
3506 ipmi::intel::platform::cmdCfgHostSerialPortSpeed, NULL,
3507 ipmiOEMCfgHostSerialPortSpeed, PRIVILEGE_ADMIN);
3508
3509 registerHandler(prioOemBase, intel::netFnGeneral,
3510 intel::general::cmdSetFaultIndication, Privilege::Operator,
3511 ipmiOEMSetFaultIndication);
3512
3513 registerHandler(prioOemBase, intel::netFnGeneral,
3514 intel::general::cmdSetColdRedundancyConfig, Privilege::User,
3515 ipmiOEMSetCRConfig);
3516
3517 registerHandler(prioOemBase, intel::netFnGeneral,
3518 intel::general::cmdGetColdRedundancyConfig, Privilege::User,
3519 ipmiOEMGetCRConfig);
3520
3521 registerHandler(prioOemBase, intel::netFnGeneral,
3522 intel::general::cmdRestoreConfiguration, Privilege::Admin,
Vernon Mauery4ac799d2019-05-20 15:50:37 -07003523 ipmiRestoreConfiguration);
James Feist63efafa2019-07-24 12:39:21 -07003524
Vernon Mauery98bbf692019-09-16 11:14:59 -07003525 registerHandler(prioOemBase, intel::netFnGeneral,
3526 intel::general::cmdSetDimmOffset, Privilege::Operator,
3527 ipmiOEMSetDimmOffset);
James Feist63efafa2019-07-24 12:39:21 -07003528
Vernon Mauery98bbf692019-09-16 11:14:59 -07003529 registerHandler(prioOemBase, intel::netFnGeneral,
3530 intel::general::cmdGetDimmOffset, Privilege::Operator,
3531 ipmiOEMGetDimmOffset);
Chen Yugangca12a7b2019-09-03 18:11:44 +08003532
Vernon Mauery98bbf692019-09-16 11:14:59 -07003533 registerHandler(prioOemBase, netFnChassis, chassis::cmdSetSystemBootOptions,
3534 Privilege::Operator, ipmiOemSetBootOptions);
Cheng C Yang4e6ee152019-09-25 10:27:44 +08003535
3536 registerHandler(prioOemBase, intel::netFnGeneral,
3537 intel::general::cmdGetPSUVersion, Privilege::User,
3538 ipmiOEMGetPSUVersion);
Jia, Chunhuia835eaa2018-09-05 09:00:41 +08003539}
3540
3541} // namespace ipmi