blob: 05bd0b5fad2999d5d66a481570308c1a7fe4d20d [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>
Jia, Chunhuia835eaa2018-09-05 09:00:41 +080041
42namespace ipmi
43{
Jason M. Bills64796042018-10-03 16:51:55 -070044static void registerOEMFunctions() __attribute__((constructor));
Vernon Mauery4ac799d2019-05-20 15:50:37 -070045
46namespace netfn::intel
47{
48constexpr NetFn oemGeneral = netFnOemOne;
49constexpr Cmd cmdRestoreConfiguration = 0x02;
50} // namespace netfn::intel
51
Jason M. Bills64796042018-10-03 16:51:55 -070052static constexpr size_t maxFRUStringLength = 0x3F;
Jia, Chunhuia835eaa2018-09-05 09:00:41 +080053
Suryakanth Sekard509eb92018-11-15 17:44:11 +053054static constexpr auto ethernetIntf =
55 "xyz.openbmc_project.Network.EthernetInterface";
56static constexpr auto networkIPIntf = "xyz.openbmc_project.Network.IP";
57static constexpr auto networkService = "xyz.openbmc_project.Network";
58static constexpr auto networkRoot = "/xyz/openbmc_project/network";
59
Chen Yugang39736d52019-07-12 16:24:33 +080060static constexpr const char* oemNmiSourceIntf = "com.intel.Control.NMISource";
61static constexpr const char* oemNmiSourceObjPath =
62 "/com/intel/control/NMISource";
63static constexpr const char* oemNmiBmcSourceObjPathProp = "BMCSource";
64static constexpr const char* oemNmiEnabledObjPathProp = "Enabled";
65
James Feist63efafa2019-07-24 12:39:21 -070066static constexpr const char* dimmOffsetFile = "/var/lib/ipmi/ipmi_dimms.json";
67
Chen Yugang39736d52019-07-12 16:24:33 +080068enum class NmiSource : uint8_t
69{
70 none = 0,
71 fpBtn = 1,
72 wdPreTimeout = 2,
73 pefMatch = 3,
74 chassisCmd = 4,
75 memoryError = 5,
76 pciSerrPerr = 6,
77 southbridgeNmi = 7,
78 chipsetNmi = 8,
79};
80
Jia, Chunhuia835eaa2018-09-05 09:00:41 +080081// return code: 0 successful
82int8_t getChassisSerialNumber(sdbusplus::bus::bus& bus, std::string& serial)
83{
84 std::string objpath = "/xyz/openbmc_project/FruDevice";
85 std::string intf = "xyz.openbmc_project.FruDeviceManager";
86 std::string service = getService(bus, intf, objpath);
87 ObjectValueTree valueTree = getManagedObjects(bus, service, "/");
88 if (valueTree.empty())
89 {
90 phosphor::logging::log<phosphor::logging::level::ERR>(
91 "No object implements interface",
92 phosphor::logging::entry("INTF=%s", intf.c_str()));
93 return -1;
94 }
95
Jason M. Bills64796042018-10-03 16:51:55 -070096 for (const auto& item : valueTree)
Jia, Chunhuia835eaa2018-09-05 09:00:41 +080097 {
98 auto interface = item.second.find("xyz.openbmc_project.FruDevice");
99 if (interface == item.second.end())
100 {
101 continue;
102 }
103
104 auto property = interface->second.find("CHASSIS_SERIAL_NUMBER");
105 if (property == interface->second.end())
106 {
107 continue;
108 }
109
110 try
111 {
112 Value variant = property->second;
Vernon Mauery8166c8d2019-05-23 11:22:30 -0700113 std::string& result = std::get<std::string>(variant);
Jason M. Bills64796042018-10-03 16:51:55 -0700114 if (result.size() > maxFRUStringLength)
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800115 {
116 phosphor::logging::log<phosphor::logging::level::ERR>(
117 "FRU serial number exceed maximum length");
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800118 return -1;
119 }
Jason M. Bills64796042018-10-03 16:51:55 -0700120 serial = result;
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800121 return 0;
122 }
Vernon Mauery8166c8d2019-05-23 11:22:30 -0700123 catch (std::bad_variant_access& e)
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800124 {
Jason M. Bills64796042018-10-03 16:51:55 -0700125 phosphor::logging::log<phosphor::logging::level::ERR>(e.what());
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800126 return -1;
127 }
128 }
129 return -1;
130}
Jason M. Bills64796042018-10-03 16:51:55 -0700131
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800132ipmi_ret_t ipmiOEMWildcard(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
133 ipmi_request_t request, ipmi_response_t response,
Jason M. Bills64796042018-10-03 16:51:55 -0700134 ipmi_data_len_t dataLen, ipmi_context_t context)
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800135{
Jason M. Bills64796042018-10-03 16:51:55 -0700136 printCommand(+netfn, +cmd);
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800137 // Status code.
138 ipmi_ret_t rc = IPMI_CC_INVALID;
Jason M. Bills64796042018-10-03 16:51:55 -0700139 *dataLen = 0;
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800140 return rc;
141}
142
143// Returns the Chassis Identifier (serial #)
144ipmi_ret_t ipmiOEMGetChassisIdentifier(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
145 ipmi_request_t request,
146 ipmi_response_t response,
Jason M. Bills64796042018-10-03 16:51:55 -0700147 ipmi_data_len_t dataLen,
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800148 ipmi_context_t context)
149{
150 std::string serial;
Jason M. Bills64796042018-10-03 16:51:55 -0700151 if (*dataLen != 0) // invalid request if there are extra parameters
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800152 {
Jason M. Bills64796042018-10-03 16:51:55 -0700153 *dataLen = 0;
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800154 return IPMI_CC_REQ_DATA_LEN_INVALID;
155 }
Vernon Mauery15419dd2019-05-24 09:40:30 -0700156 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
157 if (getChassisSerialNumber(*dbus, serial) == 0)
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800158 {
Jason M. Bills64796042018-10-03 16:51:55 -0700159 *dataLen = serial.size(); // length will never exceed response length
160 // as it is checked in getChassisSerialNumber
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800161 char* resp = static_cast<char*>(response);
Jason M. Bills64796042018-10-03 16:51:55 -0700162 serial.copy(resp, *dataLen);
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800163 return IPMI_CC_OK;
164 }
Jason M. Bills64796042018-10-03 16:51:55 -0700165 *dataLen = 0;
166 return IPMI_CC_RESPONSE_ERROR;
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800167}
168
169ipmi_ret_t ipmiOEMSetSystemGUID(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
170 ipmi_request_t request,
171 ipmi_response_t response,
Jason M. Bills64796042018-10-03 16:51:55 -0700172 ipmi_data_len_t dataLen, ipmi_context_t context)
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800173{
174 static constexpr size_t safeBufferLength = 50;
175 char buf[safeBufferLength] = {0};
176 GUIDData* Data = reinterpret_cast<GUIDData*>(request);
177
Jason M. Bills64796042018-10-03 16:51:55 -0700178 if (*dataLen != sizeof(GUIDData)) // 16bytes
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800179 {
Jason M. Bills64796042018-10-03 16:51:55 -0700180 *dataLen = 0;
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800181 return IPMI_CC_REQ_DATA_LEN_INVALID;
182 }
183
Jason M. Bills64796042018-10-03 16:51:55 -0700184 *dataLen = 0;
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800185
186 snprintf(
187 buf, safeBufferLength,
188 "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x",
189 Data->timeLow4, Data->timeLow3, Data->timeLow2, Data->timeLow1,
190 Data->timeMid2, Data->timeMid1, Data->timeHigh2, Data->timeHigh1,
191 Data->clock2, Data->clock1, Data->node6, Data->node5, Data->node4,
192 Data->node3, Data->node2, Data->node1);
193 // UUID is in RFC4122 format. Ex: 61a39523-78f2-11e5-9862-e6402cfc3223
194 std::string guid = buf;
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800195
196 std::string objpath = "/xyz/openbmc_project/control/host0/systemGUID";
197 std::string intf = "xyz.openbmc_project.Common.UUID";
Vernon Mauery15419dd2019-05-24 09:40:30 -0700198 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
199 std::string service = getService(*dbus, intf, objpath);
200 setDbusProperty(*dbus, service, objpath, intf, "UUID", guid);
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800201 return IPMI_CC_OK;
202}
203
204ipmi_ret_t ipmiOEMSetBIOSID(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
205 ipmi_request_t request, ipmi_response_t response,
206 ipmi_data_len_t dataLen, ipmi_context_t context)
207{
208 DeviceInfo* data = reinterpret_cast<DeviceInfo*>(request);
209
Jason M. Bills64796042018-10-03 16:51:55 -0700210 if ((*dataLen < 2) || (*dataLen != (1 + data->biosIDLength)))
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800211 {
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800212 *dataLen = 0;
213 return IPMI_CC_REQ_DATA_LEN_INVALID;
214 }
Jason M. Bills64796042018-10-03 16:51:55 -0700215 std::string idString((char*)data->biosId, data->biosIDLength);
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800216
Vernon Mauery15419dd2019-05-24 09:40:30 -0700217 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
218 std::string service = getService(*dbus, biosIntf, biosObjPath);
219 setDbusProperty(*dbus, service, biosObjPath, biosIntf, biosProp, idString);
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800220 uint8_t* bytesWritten = static_cast<uint8_t*>(response);
221 *bytesWritten =
Jason M. Bills64796042018-10-03 16:51:55 -0700222 data->biosIDLength; // how many bytes are written into storage
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800223 *dataLen = 1;
224 return IPMI_CC_OK;
225}
226
227ipmi_ret_t ipmiOEMGetDeviceInfo(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
228 ipmi_request_t request,
229 ipmi_response_t response,
230 ipmi_data_len_t dataLen, ipmi_context_t context)
231{
232 GetOemDeviceInfoReq* req = reinterpret_cast<GetOemDeviceInfoReq*>(request);
233 GetOemDeviceInfoRes* res = reinterpret_cast<GetOemDeviceInfoRes*>(response);
234
235 if (*dataLen == 0)
236 {
Jason M. Bills64796042018-10-03 16:51:55 -0700237 *dataLen = 0;
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800238 return IPMI_CC_REQ_DATA_LEN_INVALID;
239 }
240
241 size_t reqDataLen = *dataLen;
242 *dataLen = 0;
Jason M. Bills64796042018-10-03 16:51:55 -0700243 if (req->entityType > static_cast<uint8_t>(OEMDevEntityType::sdrVer))
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800244 {
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800245 return IPMI_CC_INVALID_FIELD_REQUEST;
246 }
247
248 // handle OEM command items
Jason M. Bills64796042018-10-03 16:51:55 -0700249 switch (OEMDevEntityType(req->entityType))
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800250 {
251 case OEMDevEntityType::biosId:
252 {
253 if (sizeof(GetOemDeviceInfoReq) != reqDataLen)
254 {
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800255 return IPMI_CC_REQ_DATA_LEN_INVALID;
256 }
257
Vernon Mauery15419dd2019-05-24 09:40:30 -0700258 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
259 std::string service = getService(*dbus, biosIntf, biosObjPath);
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800260 try
261 {
Vernon Mauery15419dd2019-05-24 09:40:30 -0700262 Value variant = getDbusProperty(*dbus, service, biosObjPath,
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800263 biosIntf, biosProp);
Vernon Mauery8166c8d2019-05-23 11:22:30 -0700264 std::string& idString = std::get<std::string>(variant);
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800265 if (req->offset >= idString.size())
266 {
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800267 return IPMI_CC_PARM_OUT_OF_RANGE;
268 }
Jason M. Bills64796042018-10-03 16:51:55 -0700269 size_t length = 0;
270 if (req->countToRead > (idString.size() - req->offset))
271 {
272 length = idString.size() - req->offset;
273 }
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800274 else
275 {
Jason M. Bills64796042018-10-03 16:51:55 -0700276 length = req->countToRead;
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800277 }
Jason M. Bills64796042018-10-03 16:51:55 -0700278 std::copy(idString.begin() + req->offset, idString.end(),
279 res->data);
280 res->resDatalen = length;
281 *dataLen = res->resDatalen + 1;
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800282 }
Vernon Mauery8166c8d2019-05-23 11:22:30 -0700283 catch (std::bad_variant_access& e)
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800284 {
Jason M. Bills64796042018-10-03 16:51:55 -0700285 phosphor::logging::log<phosphor::logging::level::ERR>(e.what());
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800286 return IPMI_CC_UNSPECIFIED_ERROR;
287 }
288 }
289 break;
290
291 case OEMDevEntityType::devVer:
292 case OEMDevEntityType::sdrVer:
293 // TODO:
294 return IPMI_CC_ILLEGAL_COMMAND;
295 default:
296 return IPMI_CC_INVALID_FIELD_REQUEST;
297 }
298 return IPMI_CC_OK;
299}
300
301ipmi_ret_t ipmiOEMGetAICFRU(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
302 ipmi_request_t request, ipmi_response_t response,
303 ipmi_data_len_t dataLen, ipmi_context_t context)
304{
305 if (*dataLen != 0)
306 {
Jason M. Bills64796042018-10-03 16:51:55 -0700307 *dataLen = 0;
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800308 return IPMI_CC_REQ_DATA_LEN_INVALID;
309 }
310
311 *dataLen = 1;
312 uint8_t* res = reinterpret_cast<uint8_t*>(response);
313 // temporary fix. We don't support AIC FRU now. Just tell BIOS that no
314 // AIC is available so that BIOS will not timeout repeatly which leads to
315 // slow booting.
316 *res = 0; // Byte1=Count of SlotPosition/FruID records.
317 return IPMI_CC_OK;
318}
319
Jason M. Bills64796042018-10-03 16:51:55 -0700320ipmi_ret_t ipmiOEMGetPowerRestoreDelay(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
321 ipmi_request_t request,
322 ipmi_response_t response,
323 ipmi_data_len_t dataLen,
324 ipmi_context_t context)
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800325{
Jason M. Bills64796042018-10-03 16:51:55 -0700326 GetPowerRestoreDelayRes* resp =
327 reinterpret_cast<GetPowerRestoreDelayRes*>(response);
328
329 if (*dataLen != 0)
330 {
331 *dataLen = 0;
332 return IPMI_CC_REQ_DATA_LEN_INVALID;
333 }
334
Vernon Mauery15419dd2019-05-24 09:40:30 -0700335 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
Jason M. Bills64796042018-10-03 16:51:55 -0700336 std::string service =
Vernon Mauery15419dd2019-05-24 09:40:30 -0700337 getService(*dbus, powerRestoreDelayIntf, powerRestoreDelayObjPath);
Jason M. Bills64796042018-10-03 16:51:55 -0700338 Value variant =
Vernon Mauery15419dd2019-05-24 09:40:30 -0700339 getDbusProperty(*dbus, service, powerRestoreDelayObjPath,
Jason M. Bills64796042018-10-03 16:51:55 -0700340 powerRestoreDelayIntf, powerRestoreDelayProp);
341
Vernon Mauery8166c8d2019-05-23 11:22:30 -0700342 uint16_t delay = std::get<uint16_t>(variant);
Jason M. Bills64796042018-10-03 16:51:55 -0700343 resp->byteLSB = delay;
344 resp->byteMSB = delay >> 8;
345
346 *dataLen = sizeof(GetPowerRestoreDelayRes);
347
348 return IPMI_CC_OK;
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800349}
350
Jia, Chunhuicc49b542019-03-20 15:41:07 +0800351static uint8_t bcdToDec(uint8_t val)
352{
353 return ((val / 16 * 10) + (val % 16));
354}
355
356// Allows an update utility or system BIOS to send the status of an embedded
357// firmware update attempt to the BMC. After received, BMC will create a logging
358// record.
359ipmi::RspType<> ipmiOEMSendEmbeddedFwUpdStatus(uint8_t status, uint8_t target,
360 uint8_t majorRevision,
361 uint8_t minorRevision,
362 uint32_t auxInfo)
363{
364 std::string firmware;
Jason M. Billsdc249272019-04-03 09:58:40 -0700365 int instance = (target & targetInstanceMask) >> targetInstanceShift;
Jia, Chunhuicc49b542019-03-20 15:41:07 +0800366 target = (target & selEvtTargetMask) >> selEvtTargetShift;
367
368 /* make sure the status is 0, 1, or 2 as per the spec */
369 if (status > 2)
370 {
371 return ipmi::response(ipmi::ccInvalidFieldRequest);
372 }
Jason M. Billsdc249272019-04-03 09:58:40 -0700373 /* make sure the target is 0, 1, 2, or 4 as per the spec */
374 if (target > 4 || target == 3)
375 {
376 return ipmi::response(ipmi::ccInvalidFieldRequest);
377 }
Jia, Chunhuicc49b542019-03-20 15:41:07 +0800378 /*orignal OEM command is to record OEM SEL.
379 But openbmc does not support OEM SEL, so we redirect it to redfish event
380 logging. */
381 std::string buildInfo;
382 std::string action;
383 switch (FWUpdateTarget(target))
384 {
385 case FWUpdateTarget::targetBMC:
386 firmware = "BMC";
Jason M. Billsdc249272019-04-03 09:58:40 -0700387 buildInfo = "major: " + std::to_string(majorRevision) + " minor: " +
Jia, Chunhuicc49b542019-03-20 15:41:07 +0800388 std::to_string(bcdToDec(minorRevision)) + // BCD encoded
389 " BuildID: " + std::to_string(auxInfo);
390 buildInfo += std::to_string(auxInfo);
391 break;
392 case FWUpdateTarget::targetBIOS:
393 firmware = "BIOS";
394 buildInfo =
Jason M. Billsdc249272019-04-03 09:58:40 -0700395 "major: " +
Jia, Chunhuicc49b542019-03-20 15:41:07 +0800396 std::to_string(bcdToDec(majorRevision)) + // BCD encoded
397 " minor: " +
398 std::to_string(bcdToDec(minorRevision)) + // BCD encoded
399 " ReleaseNumber: " + // ASCII encoded
400 std::to_string(static_cast<uint8_t>(auxInfo >> 0) - '0') +
401 std::to_string(static_cast<uint8_t>(auxInfo >> 8) - '0') +
402 std::to_string(static_cast<uint8_t>(auxInfo >> 16) - '0') +
403 std::to_string(static_cast<uint8_t>(auxInfo >> 24) - '0');
404 break;
405 case FWUpdateTarget::targetME:
406 firmware = "ME";
407 buildInfo =
Jason M. Billsdc249272019-04-03 09:58:40 -0700408 "major: " + std::to_string(majorRevision) + " minor1: " +
Jia, Chunhuicc49b542019-03-20 15:41:07 +0800409 std::to_string(bcdToDec(minorRevision)) + // BCD encoded
410 " minor2: " +
411 std::to_string(bcdToDec(static_cast<uint8_t>(auxInfo >> 0))) +
412 " build1: " +
413 std::to_string(bcdToDec(static_cast<uint8_t>(auxInfo >> 8))) +
414 " build2: " +
415 std::to_string(bcdToDec(static_cast<uint8_t>(auxInfo >> 16)));
416 break;
417 case FWUpdateTarget::targetOEMEWS:
418 firmware = "EWS";
Jason M. Billsdc249272019-04-03 09:58:40 -0700419 buildInfo = "major: " + std::to_string(majorRevision) + " minor: " +
Jia, Chunhuicc49b542019-03-20 15:41:07 +0800420 std::to_string(bcdToDec(minorRevision)) + // BCD encoded
421 " BuildID: " + std::to_string(auxInfo);
422 break;
423 }
424
Jason M. Billsdc249272019-04-03 09:58:40 -0700425 static const std::string openBMCMessageRegistryVersion("0.1");
426 std::string redfishMsgID = "OpenBMC." + openBMCMessageRegistryVersion;
427
Jia, Chunhuicc49b542019-03-20 15:41:07 +0800428 switch (status)
429 {
430 case 0x0:
431 action = "update started";
Jason M. Billsdc249272019-04-03 09:58:40 -0700432 redfishMsgID += ".FirmwareUpdateStarted";
Jia, Chunhuicc49b542019-03-20 15:41:07 +0800433 break;
434 case 0x1:
435 action = "update completed successfully";
Jason M. Billsdc249272019-04-03 09:58:40 -0700436 redfishMsgID += ".FirmwareUpdateCompleted";
Jia, Chunhuicc49b542019-03-20 15:41:07 +0800437 break;
438 case 0x2:
439 action = "update failure";
Jason M. Billsdc249272019-04-03 09:58:40 -0700440 redfishMsgID += ".FirmwareUpdateFailed";
Jia, Chunhuicc49b542019-03-20 15:41:07 +0800441 break;
442 default:
443 action = "unknown";
444 break;
445 }
446
Jason M. Billsdc249272019-04-03 09:58:40 -0700447 std::string firmwareInstanceStr =
448 firmware + " instance: " + std::to_string(instance);
449 std::string message("[firmware update] " + firmwareInstanceStr +
450 " status: <" + action + "> " + buildInfo);
Jia, Chunhuicc49b542019-03-20 15:41:07 +0800451
452 sd_journal_send("MESSAGE=%s", message.c_str(), "PRIORITY=%i", LOG_INFO,
Jason M. Billsdc249272019-04-03 09:58:40 -0700453 "REDFISH_MESSAGE_ID=%s", redfishMsgID.c_str(),
454 "REDFISH_MESSAGE_ARGS=%s,%s", firmwareInstanceStr.c_str(),
455 buildInfo.c_str(), NULL);
Jia, Chunhuicc49b542019-03-20 15:41:07 +0800456 return ipmi::responseSuccess();
457}
458
Jason M. Bills64796042018-10-03 16:51:55 -0700459ipmi_ret_t ipmiOEMSetPowerRestoreDelay(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
460 ipmi_request_t request,
461 ipmi_response_t response,
462 ipmi_data_len_t dataLen,
463 ipmi_context_t context)
464{
465 SetPowerRestoreDelayReq* data =
466 reinterpret_cast<SetPowerRestoreDelayReq*>(request);
467 uint16_t delay = 0;
468
469 if (*dataLen != sizeof(SetPowerRestoreDelayReq))
470 {
471 *dataLen = 0;
472 return IPMI_CC_REQ_DATA_LEN_INVALID;
473 }
474 delay = data->byteMSB;
475 delay = (delay << 8) | data->byteLSB;
Vernon Mauery15419dd2019-05-24 09:40:30 -0700476 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
Jason M. Bills64796042018-10-03 16:51:55 -0700477 std::string service =
Vernon Mauery15419dd2019-05-24 09:40:30 -0700478 getService(*dbus, powerRestoreDelayIntf, powerRestoreDelayObjPath);
479 setDbusProperty(*dbus, service, powerRestoreDelayObjPath,
Jason M. Bills64796042018-10-03 16:51:55 -0700480 powerRestoreDelayIntf, powerRestoreDelayProp, delay);
481 *dataLen = 0;
482
483 return IPMI_CC_OK;
484}
485
Jason M. Bills42bd9c82019-06-28 16:39:34 -0700486static bool cpuPresent(const std::string& cpuName)
Jason M. Bills64796042018-10-03 16:51:55 -0700487{
Jason M. Bills42bd9c82019-06-28 16:39:34 -0700488 static constexpr const char* cpuPresencePathPrefix =
489 "/xyz/openbmc_project/inventory/system/chassis/motherboard/";
490 static constexpr const char* cpuPresenceIntf =
491 "xyz.openbmc_project.Inventory.Item";
492 std::string cpuPresencePath = cpuPresencePathPrefix + cpuName;
493 std::shared_ptr<sdbusplus::asio::connection> busp = getSdBus();
494 try
Jason M. Bills64796042018-10-03 16:51:55 -0700495 {
Jason M. Bills42bd9c82019-06-28 16:39:34 -0700496 auto service =
497 ipmi::getService(*busp, cpuPresenceIntf, cpuPresencePath);
498
499 ipmi::Value result = ipmi::getDbusProperty(
500 *busp, service, cpuPresencePath, cpuPresenceIntf, "Present");
501 return std::get<bool>(result);
502 }
503 catch (const std::exception& e)
504 {
505 phosphor::logging::log<phosphor::logging::level::INFO>(
506 "Cannot find processor presence",
507 phosphor::logging::entry("NAME=%s", cpuName.c_str()));
508 return false;
509 }
510}
511
512ipmi::RspType<bool, // CATERR Reset Enabled
513 bool, // ERR2 Reset Enabled
514 uint6_t, // reserved
515 uint8_t, // reserved, returns 0x3F
516 uint6_t, // CPU1 CATERR Count
517 uint2_t, // CPU1 Status
518 uint6_t, // CPU2 CATERR Count
519 uint2_t, // CPU2 Status
520 uint6_t, // CPU3 CATERR Count
521 uint2_t, // CPU3 Status
522 uint6_t, // CPU4 CATERR Count
523 uint2_t, // CPU4 Status
524 uint8_t // Crashdump Count
525 >
526 ipmiOEMGetProcessorErrConfig()
527{
528 bool resetOnCATERR = false;
529 bool resetOnERR2 = false;
530 uint6_t cpu1CATERRCount = 0;
531 uint6_t cpu2CATERRCount = 0;
532 uint6_t cpu3CATERRCount = 0;
533 uint6_t cpu4CATERRCount = 0;
534 uint8_t crashdumpCount = 0;
535 uint2_t cpu1Status =
536 cpuPresent("CPU_1") ? CPUStatus::enabled : CPUStatus::notPresent;
537 uint2_t cpu2Status =
538 cpuPresent("CPU_2") ? CPUStatus::enabled : CPUStatus::notPresent;
539 uint2_t cpu3Status =
540 cpuPresent("CPU_3") ? CPUStatus::enabled : CPUStatus::notPresent;
541 uint2_t cpu4Status =
542 cpuPresent("CPU_4") ? CPUStatus::enabled : CPUStatus::notPresent;
543
544 std::shared_ptr<sdbusplus::asio::connection> busp = getSdBus();
545 try
546 {
547 auto service = ipmi::getService(*busp, processorErrConfigIntf,
548 processorErrConfigObjPath);
549
550 ipmi::PropertyMap result = ipmi::getAllDbusProperties(
551 *busp, service, processorErrConfigObjPath, processorErrConfigIntf);
552 resetOnCATERR = std::get<bool>(result.at("ResetOnCATERR"));
553 resetOnERR2 = std::get<bool>(result.at("ResetOnERR2"));
554 cpu1CATERRCount = std::get<uint8_t>(result.at("ErrorCountCPU1"));
555 cpu2CATERRCount = std::get<uint8_t>(result.at("ErrorCountCPU2"));
556 cpu3CATERRCount = std::get<uint8_t>(result.at("ErrorCountCPU3"));
557 cpu4CATERRCount = std::get<uint8_t>(result.at("ErrorCountCPU4"));
558 crashdumpCount = std::get<uint8_t>(result.at("CrashdumpCount"));
559 }
560 catch (const std::exception& e)
561 {
562 phosphor::logging::log<phosphor::logging::level::ERR>(
563 "Failed to fetch processor error config",
564 phosphor::logging::entry("ERROR=%s", e.what()));
565 return ipmi::responseUnspecifiedError();
Jason M. Bills64796042018-10-03 16:51:55 -0700566 }
567
Jason M. Bills42bd9c82019-06-28 16:39:34 -0700568 return ipmi::responseSuccess(resetOnCATERR, resetOnERR2, 0, 0x3F,
569 cpu1CATERRCount, cpu1Status, cpu2CATERRCount,
570 cpu2Status, cpu3CATERRCount, cpu3Status,
571 cpu4CATERRCount, cpu4Status, crashdumpCount);
572}
Jason M. Bills64796042018-10-03 16:51:55 -0700573
Jason M. Bills42bd9c82019-06-28 16:39:34 -0700574ipmi::RspType<> ipmiOEMSetProcessorErrConfig(
575 bool resetOnCATERR, bool resetOnERR2, uint6_t reserved1, uint8_t reserved2,
576 std::optional<bool> clearCPUErrorCount,
577 std::optional<bool> clearCrashdumpCount, std::optional<uint6_t> reserved3)
578{
579 std::shared_ptr<sdbusplus::asio::connection> busp = getSdBus();
Jason M. Bills64796042018-10-03 16:51:55 -0700580
581 try
582 {
Jason M. Bills42bd9c82019-06-28 16:39:34 -0700583 auto service = ipmi::getService(*busp, processorErrConfigIntf,
584 processorErrConfigObjPath);
585 ipmi::setDbusProperty(*busp, service, processorErrConfigObjPath,
586 processorErrConfigIntf, "ResetOnCATERR",
587 resetOnCATERR);
588 ipmi::setDbusProperty(*busp, service, processorErrConfigObjPath,
589 processorErrConfigIntf, "ResetOnERR2",
590 resetOnERR2);
591 if (clearCPUErrorCount.value_or(false))
592 {
593 ipmi::setDbusProperty(*busp, service, processorErrConfigObjPath,
594 processorErrConfigIntf, "ErrorCountCPU1", 0);
595 ipmi::setDbusProperty(*busp, service, processorErrConfigObjPath,
596 processorErrConfigIntf, "ErrorCountCPU2", 0);
597 }
598 if (clearCrashdumpCount.value_or(false))
599 {
600 ipmi::setDbusProperty(*busp, service, processorErrConfigObjPath,
601 processorErrConfigIntf, "CrashdumpCount", 0);
602 }
Jason M. Bills64796042018-10-03 16:51:55 -0700603 }
Jason M. Bills42bd9c82019-06-28 16:39:34 -0700604 catch (std::exception& e)
Jason M. Bills64796042018-10-03 16:51:55 -0700605 {
Kuiying Wangbc546672018-11-23 15:41:05 +0800606 phosphor::logging::log<phosphor::logging::level::ERR>(
Jason M. Bills42bd9c82019-06-28 16:39:34 -0700607 "Failed to set processor error config",
608 phosphor::logging::entry("EXCEPTION=%s", e.what()));
609 return ipmi::responseUnspecifiedError();
Jason M. Bills64796042018-10-03 16:51:55 -0700610 }
611
Jason M. Bills42bd9c82019-06-28 16:39:34 -0700612 return ipmi::responseSuccess();
Jason M. Bills64796042018-10-03 16:51:55 -0700613}
614
Yong Li703922d2018-11-06 13:25:31 +0800615ipmi_ret_t ipmiOEMGetShutdownPolicy(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
616 ipmi_request_t request,
617 ipmi_response_t response,
618 ipmi_data_len_t dataLen,
619 ipmi_context_t context)
620{
621 GetOEMShutdownPolicyRes* resp =
622 reinterpret_cast<GetOEMShutdownPolicyRes*>(response);
623
624 if (*dataLen != 0)
625 {
626 phosphor::logging::log<phosphor::logging::level::ERR>(
Kuiying Wang45f04982018-12-26 09:23:08 +0800627 "oem_get_shutdown_policy: invalid input len!");
Yong Li703922d2018-11-06 13:25:31 +0800628 *dataLen = 0;
629 return IPMI_CC_REQ_DATA_LEN_INVALID;
630 }
631
632 *dataLen = 0;
633
634 try
635 {
Vernon Mauery15419dd2019-05-24 09:40:30 -0700636 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
Yong Li703922d2018-11-06 13:25:31 +0800637 std::string service =
Vernon Mauery15419dd2019-05-24 09:40:30 -0700638 getService(*dbus, oemShutdownPolicyIntf, oemShutdownPolicyObjPath);
639 Value variant = getDbusProperty(
640 *dbus, service, oemShutdownPolicyObjPath, oemShutdownPolicyIntf,
641 oemShutdownPolicyObjPathProp);
Yong Li0669d192019-05-06 14:01:46 +0800642
643 if (sdbusplus::com::intel::Control::server::OCOTShutdownPolicy::
644 convertPolicyFromString(std::get<std::string>(variant)) ==
645 sdbusplus::com::intel::Control::server::OCOTShutdownPolicy::Policy::
646 NoShutdownOnOCOT)
647 {
648 resp->policy = 0;
649 }
650 else if (sdbusplus::com::intel::Control::server::OCOTShutdownPolicy::
651 convertPolicyFromString(std::get<std::string>(variant)) ==
652 sdbusplus::com::intel::Control::server::OCOTShutdownPolicy::
653 Policy::ShutdownOnOCOT)
654 {
655 resp->policy = 1;
656 }
657 else
658 {
659 phosphor::logging::log<phosphor::logging::level::ERR>(
660 "oem_set_shutdown_policy: invalid property!",
661 phosphor::logging::entry(
662 "PROP=%s", std::get<std::string>(variant).c_str()));
663 return IPMI_CC_UNSPECIFIED_ERROR;
664 }
Yong Li703922d2018-11-06 13:25:31 +0800665 // TODO needs to check if it is multi-node products,
666 // policy is only supported on node 3/4
667 resp->policySupport = shutdownPolicySupported;
668 }
669 catch (sdbusplus::exception_t& e)
670 {
671 phosphor::logging::log<phosphor::logging::level::ERR>(e.description());
672 return IPMI_CC_UNSPECIFIED_ERROR;
673 }
674
675 *dataLen = sizeof(GetOEMShutdownPolicyRes);
676 return IPMI_CC_OK;
677}
678
679ipmi_ret_t ipmiOEMSetShutdownPolicy(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
680 ipmi_request_t request,
681 ipmi_response_t response,
682 ipmi_data_len_t dataLen,
683 ipmi_context_t context)
684{
685 uint8_t* req = reinterpret_cast<uint8_t*>(request);
Yong Li0669d192019-05-06 14:01:46 +0800686 sdbusplus::com::intel::Control::server::OCOTShutdownPolicy::Policy policy =
687 sdbusplus::com::intel::Control::server::OCOTShutdownPolicy::Policy::
688 NoShutdownOnOCOT;
Yong Li703922d2018-11-06 13:25:31 +0800689
690 // TODO needs to check if it is multi-node products,
691 // policy is only supported on node 3/4
692 if (*dataLen != 1)
693 {
694 phosphor::logging::log<phosphor::logging::level::ERR>(
695 "oem_set_shutdown_policy: invalid input len!");
696 *dataLen = 0;
697 return IPMI_CC_REQ_DATA_LEN_INVALID;
698 }
699
700 *dataLen = 0;
701 if ((*req != noShutdownOnOCOT) && (*req != shutdownOnOCOT))
702 {
703 phosphor::logging::log<phosphor::logging::level::ERR>(
704 "oem_set_shutdown_policy: invalid input!");
705 return IPMI_CC_INVALID_FIELD_REQUEST;
706 }
707
Yong Li0669d192019-05-06 14:01:46 +0800708 if (*req == noShutdownOnOCOT)
709 {
710 policy = sdbusplus::com::intel::Control::server::OCOTShutdownPolicy::
711 Policy::NoShutdownOnOCOT;
712 }
713 else
714 {
715 policy = sdbusplus::com::intel::Control::server::OCOTShutdownPolicy::
716 Policy::ShutdownOnOCOT;
717 }
718
Yong Li703922d2018-11-06 13:25:31 +0800719 try
720 {
Vernon Mauery15419dd2019-05-24 09:40:30 -0700721 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
Yong Li703922d2018-11-06 13:25:31 +0800722 std::string service =
Vernon Mauery15419dd2019-05-24 09:40:30 -0700723 getService(*dbus, oemShutdownPolicyIntf, oemShutdownPolicyObjPath);
Yong Li0669d192019-05-06 14:01:46 +0800724 setDbusProperty(
Vernon Mauery15419dd2019-05-24 09:40:30 -0700725 *dbus, service, oemShutdownPolicyObjPath, oemShutdownPolicyIntf,
Yong Li0669d192019-05-06 14:01:46 +0800726 oemShutdownPolicyObjPathProp,
727 sdbusplus::com::intel::Control::server::convertForMessage(policy));
Yong Li703922d2018-11-06 13:25:31 +0800728 }
729 catch (sdbusplus::exception_t& e)
730 {
731 phosphor::logging::log<phosphor::logging::level::ERR>(e.description());
732 return IPMI_CC_UNSPECIFIED_ERROR;
733 }
734
735 return IPMI_CC_OK;
736}
737
Suryakanth Sekard509eb92018-11-15 17:44:11 +0530738/** @brief implementation for check the DHCP or not in IPv4
739 * @param[in] Channel - Channel number
740 * @returns true or false.
741 */
742static bool isDHCPEnabled(uint8_t Channel)
743{
744 try
745 {
746 auto ethdevice = getChannelName(Channel);
747 if (ethdevice.empty())
748 {
749 return false;
750 }
751 auto ethIP = ethdevice + "/ipv4";
Vernon Mauery15419dd2019-05-24 09:40:30 -0700752 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
Suryakanth Sekard509eb92018-11-15 17:44:11 +0530753 auto ethernetObj =
Vernon Mauery15419dd2019-05-24 09:40:30 -0700754 getDbusObject(*dbus, networkIPIntf, networkRoot, ethIP);
755 auto value = getDbusProperty(*dbus, networkService, ethernetObj.first,
Suryakanth Sekard509eb92018-11-15 17:44:11 +0530756 networkIPIntf, "Origin");
Vernon Mauery8166c8d2019-05-23 11:22:30 -0700757 if (std::get<std::string>(value) ==
Suryakanth Sekard509eb92018-11-15 17:44:11 +0530758 "xyz.openbmc_project.Network.IP.AddressOrigin.DHCP")
759 {
760 return true;
761 }
762 else
763 {
764 return false;
765 }
766 }
767 catch (sdbusplus::exception_t& e)
768 {
769 phosphor::logging::log<phosphor::logging::level::ERR>(e.description());
770 return true;
771 }
772}
773
774/** @brief implementes for check the DHCP or not in IPv6
775 * @param[in] Channel - Channel number
776 * @returns true or false.
777 */
778static bool isDHCPIPv6Enabled(uint8_t Channel)
779{
780
781 try
782 {
783 auto ethdevice = getChannelName(Channel);
784 if (ethdevice.empty())
785 {
786 return false;
787 }
788 auto ethIP = ethdevice + "/ipv6";
Vernon Mauery15419dd2019-05-24 09:40:30 -0700789 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
Suryakanth Sekard509eb92018-11-15 17:44:11 +0530790 auto objectInfo =
Vernon Mauery15419dd2019-05-24 09:40:30 -0700791 getDbusObject(*dbus, networkIPIntf, networkRoot, ethIP);
792 auto properties = getAllDbusProperties(*dbus, objectInfo.second,
Suryakanth Sekard509eb92018-11-15 17:44:11 +0530793 objectInfo.first, networkIPIntf);
Vernon Mauery8166c8d2019-05-23 11:22:30 -0700794 if (std::get<std::string>(properties["Origin"]) ==
Suryakanth Sekard509eb92018-11-15 17:44:11 +0530795 "xyz.openbmc_project.Network.IP.AddressOrigin.DHCP")
796 {
797 return true;
798 }
799 else
800 {
801 return false;
802 }
803 }
804 catch (sdbusplus::exception_t& e)
805 {
806 phosphor::logging::log<phosphor::logging::level::ERR>(e.description());
807 return true;
808 }
809}
810
811/** @brief implementes the creating of default new user
812 * @param[in] userName - new username in 16 bytes.
813 * @param[in] userPassword - new password in 20 bytes
814 * @returns ipmi completion code.
815 */
816ipmi::RspType<> ipmiOEMSetUser2Activation(
817 std::array<uint8_t, ipmi::ipmiMaxUserName>& userName,
818 std::array<uint8_t, ipmi::maxIpmi20PasswordSize>& userPassword)
819{
820 bool userState = false;
821 // Check for System Interface not exist and LAN should be static
822 for (uint8_t channel = 0; channel < maxIpmiChannels; channel++)
823 {
824 ChannelInfo chInfo;
825 try
826 {
827 getChannelInfo(channel, chInfo);
828 }
829 catch (sdbusplus::exception_t& e)
830 {
831 phosphor::logging::log<phosphor::logging::level::ERR>(
832 "ipmiOEMSetUser2Activation: Failed to get Channel Info",
833 phosphor::logging::entry("MSG: %s", e.description()));
834 return ipmi::response(ipmi::ccUnspecifiedError);
835 }
836 if (chInfo.mediumType ==
837 static_cast<uint8_t>(EChannelMediumType::systemInterface))
838 {
839 phosphor::logging::log<phosphor::logging::level::ERR>(
840 "ipmiOEMSetUser2Activation: system interface exist .");
841 return ipmi::response(ipmi::ccCommandNotAvailable);
842 }
843 else
844 {
845
846 if (chInfo.mediumType ==
847 static_cast<uint8_t>(EChannelMediumType::lan8032))
848 {
849 if (isDHCPIPv6Enabled(channel) || isDHCPEnabled(channel))
850 {
851 phosphor::logging::log<phosphor::logging::level::ERR>(
852 "ipmiOEMSetUser2Activation: DHCP enabled .");
853 return ipmi::response(ipmi::ccCommandNotAvailable);
854 }
855 }
856 }
857 }
858 uint8_t maxChUsers = 0, enabledUsers = 0, fixedUsers = 0;
859 if (ipmi::ccSuccess ==
860 ipmiUserGetAllCounts(maxChUsers, enabledUsers, fixedUsers))
861 {
862 if (enabledUsers > 1)
863 {
864 phosphor::logging::log<phosphor::logging::level::ERR>(
865 "ipmiOEMSetUser2Activation: more than one user is enabled.");
866 return ipmi::response(ipmi::ccCommandNotAvailable);
867 }
868 // Check the user 2 is enabled or not
869 ipmiUserCheckEnabled(ipmiDefaultUserId, userState);
870 if (userState == true)
871 {
872 phosphor::logging::log<phosphor::logging::level::ERR>(
873 "ipmiOEMSetUser2Activation: user 2 already enabled .");
874 return ipmi::response(ipmi::ccCommandNotAvailable);
875 }
876 }
877 else
878 {
879 return ipmi::response(ipmi::ccUnspecifiedError);
880 }
881
882#if BYTE_ORDER == LITTLE_ENDIAN
883 PrivAccess privAccess = {PRIVILEGE_ADMIN, true, true, true, 0};
884#endif
885#if BYTE_ORDER == BIG_ENDIAN
886 PrivAccess privAccess = {0, true, true, true, PRIVILEGE_ADMIN};
887#endif
888
889 if (ipmi::ccSuccess ==
890 ipmiUserSetUserName(ipmiDefaultUserId,
891 reinterpret_cast<const char*>(userName.data())))
892 {
893 if (ipmi::ccSuccess ==
894 ipmiUserSetUserPassword(
895 ipmiDefaultUserId,
896 reinterpret_cast<const char*>(userPassword.data())))
897 {
898 if (ipmi::ccSuccess ==
899 ipmiUserSetPrivilegeAccess(
900 ipmiDefaultUserId,
901 static_cast<uint8_t>(ipmi::EChannelID::chanLan1),
902 privAccess, true))
903 {
904 phosphor::logging::log<phosphor::logging::level::INFO>(
905 "ipmiOEMSetUser2Activation: user created successfully ");
906 return ipmi::responseSuccess();
907 }
908 }
909 // we need to delete the default user id which added in this command as
910 // password / priv setting is failed.
911 ipmiUserSetUserName(ipmiDefaultUserId, "");
912 phosphor::logging::log<phosphor::logging::level::ERR>(
913 "ipmiOEMSetUser2Activation: password / priv setting is failed.");
914 }
915 else
916 {
917 phosphor::logging::log<phosphor::logging::level::ERR>(
918 "ipmiOEMSetUser2Activation: Setting username failed.");
919 }
920
921 return ipmi::response(ipmi::ccCommandNotAvailable);
922}
923
Richard Marian Thomaiyarfc5e9852019-04-14 15:06:27 +0530924/** @brief implementes setting password for special user
925 * @param[in] specialUserIndex
926 * @param[in] userPassword - new password in 20 bytes
927 * @returns ipmi completion code.
928 */
929ipmi::RspType<> ipmiOEMSetSpecialUserPassword(ipmi::Context::ptr ctx,
930 uint8_t specialUserIndex,
931 std::vector<uint8_t> userPassword)
932{
933 ChannelInfo chInfo;
934 try
935 {
936 getChannelInfo(ctx->channel, chInfo);
937 }
938 catch (sdbusplus::exception_t& e)
939 {
940 phosphor::logging::log<phosphor::logging::level::ERR>(
941 "ipmiOEMSetSpecialUserPassword: Failed to get Channel Info",
942 phosphor::logging::entry("MSG: %s", e.description()));
943 return ipmi::responseUnspecifiedError();
944 }
945 if (chInfo.mediumType !=
946 static_cast<uint8_t>(EChannelMediumType::systemInterface))
947 {
948 phosphor::logging::log<phosphor::logging::level::ERR>(
949 "ipmiOEMSetSpecialUserPassword: Error - supported only in KCS "
950 "interface");
951 return ipmi::responseCommandNotAvailable();
952 }
953 if (specialUserIndex != 0)
954 {
955 phosphor::logging::log<phosphor::logging::level::ERR>(
956 "ipmiOEMSetSpecialUserPassword: Invalid user account");
957 return ipmi::responseParmOutOfRange();
958 }
959 constexpr uint8_t minPasswordSizeRequired = 6;
960 if (userPassword.size() < minPasswordSizeRequired ||
961 userPassword.size() > ipmi::maxIpmi20PasswordSize)
962 {
963 return ipmi::responseReqDataLenInvalid();
964 }
965 std::string passwd;
966 passwd.assign(reinterpret_cast<const char*>(userPassword.data()),
967 userPassword.size());
968 return ipmi::response(ipmiSetSpecialUserPassword("root", passwd));
969}
970
Kuiying Wang45f04982018-12-26 09:23:08 +0800971namespace ledAction
972{
973using namespace sdbusplus::xyz::openbmc_project::Led::server;
974std::map<Physical::Action, uint8_t> actionDbusToIpmi = {
975 {Physical::Action::Off, 0x00},
976 {Physical::Action::On, 0x10},
977 {Physical::Action::Blink, 0x01}};
978
979std::map<uint8_t, std::string> offsetObjPath = {
980 {2, statusAmberObjPath}, {4, statusGreenObjPath}, {6, identifyLEDObjPath}};
981
982} // namespace ledAction
983
984int8_t getLEDState(sdbusplus::bus::bus& bus, const std::string& intf,
985 const std::string& objPath, uint8_t& state)
986{
987 try
988 {
989 std::string service = getService(bus, intf, objPath);
990 Value stateValue =
991 getDbusProperty(bus, service, objPath, intf, "State");
Vernon Mauery8166c8d2019-05-23 11:22:30 -0700992 std::string strState = std::get<std::string>(stateValue);
Kuiying Wang45f04982018-12-26 09:23:08 +0800993 state = ledAction::actionDbusToIpmi.at(
994 sdbusplus::xyz::openbmc_project::Led::server::Physical::
995 convertActionFromString(strState));
996 }
997 catch (sdbusplus::exception::SdBusError& e)
998 {
999 phosphor::logging::log<phosphor::logging::level::ERR>(e.what());
1000 return -1;
1001 }
1002 return 0;
1003}
1004
1005ipmi_ret_t ipmiOEMGetLEDStatus(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
1006 ipmi_request_t request, ipmi_response_t response,
1007 ipmi_data_len_t dataLen, ipmi_context_t context)
1008{
1009 uint8_t* resp = reinterpret_cast<uint8_t*>(response);
1010 // LED Status
1011 //[1:0] = Reserved
1012 //[3:2] = Status(Amber)
1013 //[5:4] = Status(Green)
1014 //[7:6] = System Identify
1015 // Status definitions:
1016 // 00b = Off
1017 // 01b = Blink
1018 // 10b = On
1019 // 11b = invalid
1020 if (*dataLen != 0)
1021 {
1022 phosphor::logging::log<phosphor::logging::level::ERR>(
1023 "oem_get_led_status: invalid input len!");
1024 *dataLen = 0;
1025 return IPMI_CC_REQ_DATA_LEN_INVALID;
1026 }
1027
1028 phosphor::logging::log<phosphor::logging::level::DEBUG>("GET led status");
1029 *resp = 0;
1030 *dataLen = 0;
Vernon Mauery15419dd2019-05-24 09:40:30 -07001031 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
Kuiying Wang45f04982018-12-26 09:23:08 +08001032 for (auto it = ledAction::offsetObjPath.begin();
1033 it != ledAction::offsetObjPath.end(); ++it)
1034 {
1035 uint8_t state = 0;
Vernon Mauery15419dd2019-05-24 09:40:30 -07001036 if (-1 == getLEDState(*dbus, ledIntf, it->second, state))
Kuiying Wang45f04982018-12-26 09:23:08 +08001037 {
1038 phosphor::logging::log<phosphor::logging::level::ERR>(
1039 "oem_get_led_status: fail to get ID LED status!");
1040 return IPMI_CC_UNSPECIFIED_ERROR;
1041 }
1042 *resp |= state << it->first;
1043 }
1044
1045 *dataLen = sizeof(*resp);
1046 return IPMI_CC_OK;
1047}
1048
Yong Li23737fe2019-02-19 08:49:55 +08001049ipmi_ret_t ipmiOEMCfgHostSerialPortSpeed(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
1050 ipmi_request_t request,
1051 ipmi_response_t response,
1052 ipmi_data_len_t dataLen,
1053 ipmi_context_t context)
1054{
1055 CfgHostSerialReq* req = reinterpret_cast<CfgHostSerialReq*>(request);
1056 uint8_t* resp = reinterpret_cast<uint8_t*>(response);
1057
1058 if (*dataLen == 0)
1059 {
1060 phosphor::logging::log<phosphor::logging::level::ERR>(
1061 "CfgHostSerial: invalid input len!",
1062 phosphor::logging::entry("LEN=%d", *dataLen));
1063 return IPMI_CC_REQ_DATA_LEN_INVALID;
1064 }
1065
1066 switch (req->command)
1067 {
1068 case getHostSerialCfgCmd:
1069 {
1070 if (*dataLen != 1)
1071 {
1072 phosphor::logging::log<phosphor::logging::level::ERR>(
1073 "CfgHostSerial: invalid input len!");
1074 *dataLen = 0;
1075 return IPMI_CC_REQ_DATA_LEN_INVALID;
1076 }
1077
1078 *dataLen = 0;
1079
1080 boost::process::ipstream is;
1081 std::vector<std::string> data;
1082 std::string line;
1083 boost::process::child c1(fwGetEnvCmd, "-n", fwHostSerailCfgEnvName,
1084 boost::process::std_out > is);
1085
1086 while (c1.running() && std::getline(is, line) && !line.empty())
1087 {
1088 data.push_back(line);
1089 }
1090
1091 c1.wait();
1092 if (c1.exit_code())
1093 {
1094 phosphor::logging::log<phosphor::logging::level::ERR>(
1095 "CfgHostSerial:: error on execute",
1096 phosphor::logging::entry("EXECUTE=%s", fwSetEnvCmd));
1097 // Using the default value
1098 *resp = 0;
1099 }
1100 else
1101 {
1102 if (data.size() != 1)
1103 {
1104 phosphor::logging::log<phosphor::logging::level::ERR>(
1105 "CfgHostSerial:: error on read env");
1106 return IPMI_CC_UNSPECIFIED_ERROR;
1107 }
1108 try
1109 {
1110 unsigned long tmp = std::stoul(data[0]);
1111 if (tmp > std::numeric_limits<uint8_t>::max())
1112 {
1113 throw std::out_of_range("Out of range");
1114 }
1115 *resp = static_cast<uint8_t>(tmp);
1116 }
1117 catch (const std::invalid_argument& e)
1118 {
1119 phosphor::logging::log<phosphor::logging::level::ERR>(
1120 "invalid config ",
1121 phosphor::logging::entry("ERR=%s", e.what()));
1122 return IPMI_CC_UNSPECIFIED_ERROR;
1123 }
1124 catch (const std::out_of_range& e)
1125 {
1126 phosphor::logging::log<phosphor::logging::level::ERR>(
1127 "out_of_range config ",
1128 phosphor::logging::entry("ERR=%s", e.what()));
1129 return IPMI_CC_UNSPECIFIED_ERROR;
1130 }
1131 }
1132
1133 *dataLen = 1;
1134 break;
1135 }
1136 case setHostSerialCfgCmd:
1137 {
1138 if (*dataLen != sizeof(CfgHostSerialReq))
1139 {
1140 phosphor::logging::log<phosphor::logging::level::ERR>(
1141 "CfgHostSerial: invalid input len!");
1142 *dataLen = 0;
1143 return IPMI_CC_REQ_DATA_LEN_INVALID;
1144 }
1145
1146 *dataLen = 0;
1147
1148 if (req->parameter > HostSerialCfgParamMax)
1149 {
1150 phosphor::logging::log<phosphor::logging::level::ERR>(
1151 "CfgHostSerial: invalid input!");
1152 return IPMI_CC_INVALID_FIELD_REQUEST;
1153 }
1154
1155 boost::process::child c1(fwSetEnvCmd, fwHostSerailCfgEnvName,
1156 std::to_string(req->parameter));
1157
1158 c1.wait();
1159 if (c1.exit_code())
1160 {
1161 phosphor::logging::log<phosphor::logging::level::ERR>(
1162 "CfgHostSerial:: error on execute",
1163 phosphor::logging::entry("EXECUTE=%s", fwGetEnvCmd));
1164 return IPMI_CC_UNSPECIFIED_ERROR;
1165 }
1166 break;
1167 }
1168 default:
1169 phosphor::logging::log<phosphor::logging::level::ERR>(
1170 "CfgHostSerial: invalid input!");
1171 *dataLen = 0;
1172 return IPMI_CC_INVALID_FIELD_REQUEST;
1173 }
1174
1175 return IPMI_CC_OK;
1176}
1177
James Feist91244a62019-02-19 15:04:54 -08001178constexpr const char* thermalModeInterface =
1179 "xyz.openbmc_project.Control.ThermalMode";
1180constexpr const char* thermalModePath =
1181 "/xyz/openbmc_project/control/thermal_mode";
1182
1183bool getFanProfileInterface(
1184 sdbusplus::bus::bus& bus,
1185 boost::container::flat_map<
1186 std::string, std::variant<std::vector<std::string>, std::string>>& resp)
1187{
1188 auto call = bus.new_method_call(settingsBusName, thermalModePath, PROP_INTF,
1189 "GetAll");
1190 call.append(thermalModeInterface);
1191 try
1192 {
1193 auto data = bus.call(call);
1194 data.read(resp);
1195 }
1196 catch (sdbusplus::exception_t& e)
1197 {
1198 phosphor::logging::log<phosphor::logging::level::ERR>(
1199 "getFanProfileInterface: can't get thermal mode!",
1200 phosphor::logging::entry("ERR=%s", e.what()));
1201 return false;
1202 }
1203 return true;
1204}
1205
1206ipmi_ret_t ipmiOEMSetFanConfig(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
1207 ipmi_request_t request, ipmi_response_t response,
1208 ipmi_data_len_t dataLen, ipmi_context_t context)
1209{
1210
1211 if (*dataLen < 2 || *dataLen > 7)
1212 {
1213 phosphor::logging::log<phosphor::logging::level::ERR>(
1214 "ipmiOEMSetFanConfig: invalid input len!");
1215 *dataLen = 0;
1216 return IPMI_CC_REQ_DATA_LEN_INVALID;
1217 }
1218
1219 // todo: tell bios to only send first 2 bytes
1220
1221 SetFanConfigReq* req = reinterpret_cast<SetFanConfigReq*>(request);
1222 boost::container::flat_map<
1223 std::string, std::variant<std::vector<std::string>, std::string>>
1224 profileData;
Vernon Mauery15419dd2019-05-24 09:40:30 -07001225 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
1226 if (!getFanProfileInterface(*dbus, profileData))
James Feist91244a62019-02-19 15:04:54 -08001227 {
1228 return IPMI_CC_UNSPECIFIED_ERROR;
1229 }
1230
1231 std::vector<std::string>* supported =
1232 std::get_if<std::vector<std::string>>(&profileData["Supported"]);
1233 if (supported == nullptr)
1234 {
1235 return IPMI_CC_INVALID_FIELD_REQUEST;
1236 }
1237 std::string mode;
1238 if (req->flags &
1239 (1 << static_cast<uint8_t>(setFanProfileFlags::setPerfAcousMode)))
1240 {
1241 bool performanceMode =
1242 (req->flags & (1 << static_cast<uint8_t>(
1243 setFanProfileFlags::performAcousSelect))) > 0;
1244
1245 if (performanceMode)
1246 {
1247
1248 if (std::find(supported->begin(), supported->end(),
1249 "Performance") != supported->end())
1250 {
1251 mode = "Performance";
1252 }
1253 }
1254 else
1255 {
1256
1257 if (std::find(supported->begin(), supported->end(), "Acoustic") !=
1258 supported->end())
1259 {
1260 mode = "Acoustic";
1261 }
1262 }
1263 if (mode.empty())
1264 {
1265 return IPMI_CC_INVALID_FIELD_REQUEST;
1266 }
Vernon Mauery15419dd2019-05-24 09:40:30 -07001267 setDbusProperty(*dbus, settingsBusName, thermalModePath,
James Feist91244a62019-02-19 15:04:54 -08001268 thermalModeInterface, "Current", mode);
1269 }
1270
1271 return IPMI_CC_OK;
1272}
1273
James Feist5b693632019-07-09 09:06:09 -07001274ipmi::RspType<uint8_t, // profile support map
1275 uint8_t, // fan control profile enable
1276 uint8_t, // flags
1277 uint32_t // dimm presence bit map
1278 >
1279 ipmiOEMGetFanConfig(uint8_t dimmGroupId)
James Feist91244a62019-02-19 15:04:54 -08001280{
James Feist91244a62019-02-19 15:04:54 -08001281 boost::container::flat_map<
1282 std::string, std::variant<std::vector<std::string>, std::string>>
1283 profileData;
1284
Vernon Mauery15419dd2019-05-24 09:40:30 -07001285 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
1286 if (!getFanProfileInterface(*dbus, profileData))
James Feist91244a62019-02-19 15:04:54 -08001287 {
James Feist5b693632019-07-09 09:06:09 -07001288 return ipmi::responseResponseError();
James Feist91244a62019-02-19 15:04:54 -08001289 }
1290
1291 std::string* current = std::get_if<std::string>(&profileData["Current"]);
1292
1293 if (current == nullptr)
1294 {
1295 phosphor::logging::log<phosphor::logging::level::ERR>(
1296 "ipmiOEMGetFanConfig: can't get current mode!");
James Feist5b693632019-07-09 09:06:09 -07001297 return ipmi::responseResponseError();
James Feist91244a62019-02-19 15:04:54 -08001298 }
1299 bool performance = (*current == "Performance");
1300
James Feist5b693632019-07-09 09:06:09 -07001301 uint8_t flags = 0;
James Feist91244a62019-02-19 15:04:54 -08001302 if (performance)
1303 {
James Feist5b693632019-07-09 09:06:09 -07001304 flags |= 1 << 2;
James Feist91244a62019-02-19 15:04:54 -08001305 }
1306
James Feist5b693632019-07-09 09:06:09 -07001307 return ipmi::responseSuccess(0, 0, flags, 0);
James Feist91244a62019-02-19 15:04:54 -08001308}
James Feist5f957ca2019-03-14 15:33:55 -07001309constexpr const char* cfmLimitSettingPath =
1310 "/xyz/openbmc_project/control/cfm_limit";
1311constexpr const char* cfmLimitIface = "xyz.openbmc_project.Control.CFMLimit";
James Feistfaa4f222019-03-21 16:21:55 -07001312constexpr const size_t legacyExitAirSensorNumber = 0x2e;
James Feist09f6b602019-08-08 11:30:03 -07001313constexpr const size_t legacyPCHSensorNumber = 0x22;
1314constexpr const char* exitAirPathName = "Exit_Air";
1315constexpr const char* pchPathName = "SSB_Temp";
James Feistacc8a4e2019-04-02 14:23:57 -07001316constexpr const char* pidConfigurationIface =
1317 "xyz.openbmc_project.Configuration.Pid";
James Feistfaa4f222019-03-21 16:21:55 -07001318
James Feist09f6b602019-08-08 11:30:03 -07001319static std::string getConfigPath(const std::string& name)
James Feistfaa4f222019-03-21 16:21:55 -07001320{
Vernon Mauery15419dd2019-05-24 09:40:30 -07001321 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
James Feistfaa4f222019-03-21 16:21:55 -07001322 auto method =
Vernon Mauery15419dd2019-05-24 09:40:30 -07001323 dbus->new_method_call("xyz.openbmc_project.ObjectMapper",
1324 "/xyz/openbmc_project/object_mapper",
1325 "xyz.openbmc_project.ObjectMapper", "GetSubTree");
James Feistfaa4f222019-03-21 16:21:55 -07001326
James Feistacc8a4e2019-04-02 14:23:57 -07001327 method.append("/", 0, std::array<const char*, 1>{pidConfigurationIface});
James Feistfaa4f222019-03-21 16:21:55 -07001328 std::string path;
1329 GetSubTreeType resp;
1330 try
1331 {
Vernon Mauery15419dd2019-05-24 09:40:30 -07001332 auto reply = dbus->call(method);
James Feistfaa4f222019-03-21 16:21:55 -07001333 reply.read(resp);
1334 }
1335 catch (sdbusplus::exception_t&)
1336 {
1337 phosphor::logging::log<phosphor::logging::level::ERR>(
1338 "ipmiOEMGetFscParameter: mapper error");
1339 };
James Feist09f6b602019-08-08 11:30:03 -07001340 auto config =
1341 std::find_if(resp.begin(), resp.end(), [&name](const auto& pair) {
1342 return pair.first.find(name) != std::string::npos;
1343 });
James Feistfaa4f222019-03-21 16:21:55 -07001344 if (config != resp.end())
1345 {
1346 path = std::move(config->first);
1347 }
1348 return path;
1349}
James Feist5f957ca2019-03-14 15:33:55 -07001350
James Feistacc8a4e2019-04-02 14:23:57 -07001351// flat map to make alphabetical
1352static boost::container::flat_map<std::string, PropertyMap> getPidConfigs()
1353{
1354 boost::container::flat_map<std::string, PropertyMap> ret;
Vernon Mauery15419dd2019-05-24 09:40:30 -07001355 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
James Feistacc8a4e2019-04-02 14:23:57 -07001356 auto method =
Vernon Mauery15419dd2019-05-24 09:40:30 -07001357 dbus->new_method_call("xyz.openbmc_project.ObjectMapper",
1358 "/xyz/openbmc_project/object_mapper",
1359 "xyz.openbmc_project.ObjectMapper", "GetSubTree");
James Feistacc8a4e2019-04-02 14:23:57 -07001360
1361 method.append("/", 0, std::array<const char*, 1>{pidConfigurationIface});
1362 GetSubTreeType resp;
1363
1364 try
1365 {
Vernon Mauery15419dd2019-05-24 09:40:30 -07001366 auto reply = dbus->call(method);
James Feistacc8a4e2019-04-02 14:23:57 -07001367 reply.read(resp);
1368 }
1369 catch (sdbusplus::exception_t&)
1370 {
1371 phosphor::logging::log<phosphor::logging::level::ERR>(
1372 "getFanConfigPaths: mapper error");
1373 };
1374 for (const auto& [path, objects] : resp)
1375 {
1376 if (objects.empty())
1377 {
1378 continue; // should be impossible
1379 }
Zhu, Yungebe560b02019-04-21 21:19:21 -04001380
1381 try
1382 {
Vernon Mauery15419dd2019-05-24 09:40:30 -07001383 ret.emplace(path,
1384 getAllDbusProperties(*dbus, objects[0].first, path,
1385 pidConfigurationIface));
Zhu, Yungebe560b02019-04-21 21:19:21 -04001386 }
1387 catch (sdbusplus::exception_t& e)
1388 {
1389 phosphor::logging::log<phosphor::logging::level::ERR>(
1390 "getPidConfigs: can't get DbusProperties!",
1391 phosphor::logging::entry("ERR=%s", e.what()));
1392 }
James Feistacc8a4e2019-04-02 14:23:57 -07001393 }
1394 return ret;
1395}
1396
1397ipmi::RspType<uint8_t> ipmiOEMGetFanSpeedOffset(void)
1398{
1399 boost::container::flat_map<std::string, PropertyMap> data = getPidConfigs();
1400 if (data.empty())
1401 {
1402 return ipmi::responseResponseError();
1403 }
1404 uint8_t minOffset = std::numeric_limits<uint8_t>::max();
1405 for (const auto& [_, pid] : data)
1406 {
1407 auto findClass = pid.find("Class");
1408 if (findClass == pid.end())
1409 {
1410 phosphor::logging::log<phosphor::logging::level::ERR>(
1411 "ipmiOEMGetFscParameter: found illegal pid "
1412 "configurations");
1413 return ipmi::responseResponseError();
1414 }
1415 std::string type = std::get<std::string>(findClass->second);
1416 if (type == "fan")
1417 {
1418 auto findOutLimit = pid.find("OutLimitMin");
1419 if (findOutLimit == pid.end())
1420 {
1421 phosphor::logging::log<phosphor::logging::level::ERR>(
1422 "ipmiOEMGetFscParameter: found illegal pid "
1423 "configurations");
1424 return ipmi::responseResponseError();
1425 }
1426 // get the min out of all the offsets
1427 minOffset = std::min(
1428 minOffset,
1429 static_cast<uint8_t>(std::get<double>(findOutLimit->second)));
1430 }
1431 }
1432 if (minOffset == std::numeric_limits<uint8_t>::max())
1433 {
1434 phosphor::logging::log<phosphor::logging::level::ERR>(
1435 "ipmiOEMGetFscParameter: found no fan configurations!");
1436 return ipmi::responseResponseError();
1437 }
1438
1439 return ipmi::responseSuccess(minOffset);
1440}
1441
1442ipmi::RspType<> ipmiOEMSetFanSpeedOffset(uint8_t offset)
1443{
1444 boost::container::flat_map<std::string, PropertyMap> data = getPidConfigs();
1445 if (data.empty())
1446 {
1447
1448 phosphor::logging::log<phosphor::logging::level::ERR>(
1449 "ipmiOEMSetFanSpeedOffset: found no pid configurations!");
1450 return ipmi::responseResponseError();
1451 }
1452
Vernon Mauery15419dd2019-05-24 09:40:30 -07001453 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
James Feistacc8a4e2019-04-02 14:23:57 -07001454 bool found = false;
1455 for (const auto& [path, pid] : data)
1456 {
1457 auto findClass = pid.find("Class");
1458 if (findClass == pid.end())
1459 {
1460
1461 phosphor::logging::log<phosphor::logging::level::ERR>(
1462 "ipmiOEMSetFanSpeedOffset: found illegal pid "
1463 "configurations");
1464 return ipmi::responseResponseError();
1465 }
1466 std::string type = std::get<std::string>(findClass->second);
1467 if (type == "fan")
1468 {
1469 auto findOutLimit = pid.find("OutLimitMin");
1470 if (findOutLimit == pid.end())
1471 {
1472
1473 phosphor::logging::log<phosphor::logging::level::ERR>(
1474 "ipmiOEMSetFanSpeedOffset: found illegal pid "
1475 "configurations");
1476 return ipmi::responseResponseError();
1477 }
Vernon Mauery15419dd2019-05-24 09:40:30 -07001478 ipmi::setDbusProperty(*dbus, "xyz.openbmc_project.EntityManager",
James Feistacc8a4e2019-04-02 14:23:57 -07001479 path, pidConfigurationIface, "OutLimitMin",
1480 static_cast<double>(offset));
1481 found = true;
1482 }
1483 }
1484 if (!found)
1485 {
1486 phosphor::logging::log<phosphor::logging::level::ERR>(
1487 "ipmiOEMSetFanSpeedOffset: set no fan offsets");
1488 return ipmi::responseResponseError();
1489 }
1490
1491 return ipmi::responseSuccess();
1492}
1493
1494ipmi::RspType<> ipmiOEMSetFscParameter(uint8_t command, uint8_t param1,
1495 uint8_t param2)
James Feist5f957ca2019-03-14 15:33:55 -07001496{
1497 constexpr const size_t disableLimiting = 0x0;
1498
Vernon Mauery15419dd2019-05-24 09:40:30 -07001499 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
James Feistacc8a4e2019-04-02 14:23:57 -07001500 if (command == static_cast<uint8_t>(setFscParamFlags::tcontrol))
James Feist5f957ca2019-03-14 15:33:55 -07001501 {
James Feist09f6b602019-08-08 11:30:03 -07001502 std::string pathName;
James Feistacc8a4e2019-04-02 14:23:57 -07001503 if (param1 == legacyExitAirSensorNumber)
James Feistfaa4f222019-03-21 16:21:55 -07001504 {
James Feist09f6b602019-08-08 11:30:03 -07001505 pathName = exitAirPathName;
1506 }
1507 else if (param1 == legacyPCHSensorNumber)
1508 {
1509 pathName = pchPathName;
James Feistfaa4f222019-03-21 16:21:55 -07001510 }
1511 else
1512 {
James Feistacc8a4e2019-04-02 14:23:57 -07001513 return ipmi::responseParmOutOfRange();
James Feistfaa4f222019-03-21 16:21:55 -07001514 }
James Feist09f6b602019-08-08 11:30:03 -07001515 std::string path = getConfigPath(pathName);
1516 ipmi::setDbusProperty(*dbus, "xyz.openbmc_project.EntityManager", path,
1517 pidConfigurationIface, "SetPoint",
1518 static_cast<double>(param2));
1519 return ipmi::responseSuccess();
James Feistfaa4f222019-03-21 16:21:55 -07001520 }
James Feistacc8a4e2019-04-02 14:23:57 -07001521 else if (command == static_cast<uint8_t>(setFscParamFlags::cfm))
James Feist5f957ca2019-03-14 15:33:55 -07001522 {
James Feistacc8a4e2019-04-02 14:23:57 -07001523 uint16_t cfm = param1 | (static_cast<uint16_t>(param2) << 8);
James Feist5f957ca2019-03-14 15:33:55 -07001524
1525 // must be greater than 50 based on eps
1526 if (cfm < 50 && cfm != disableLimiting)
1527 {
James Feistacc8a4e2019-04-02 14:23:57 -07001528 return ipmi::responseParmOutOfRange();
James Feist5f957ca2019-03-14 15:33:55 -07001529 }
1530
1531 try
1532 {
Vernon Mauery15419dd2019-05-24 09:40:30 -07001533 ipmi::setDbusProperty(*dbus, settingsBusName, cfmLimitSettingPath,
James Feist5f957ca2019-03-14 15:33:55 -07001534 cfmLimitIface, "Limit",
1535 static_cast<double>(cfm));
1536 }
1537 catch (sdbusplus::exception_t& e)
1538 {
1539 phosphor::logging::log<phosphor::logging::level::ERR>(
1540 "ipmiOEMSetFscParameter: can't set cfm setting!",
1541 phosphor::logging::entry("ERR=%s", e.what()));
James Feistacc8a4e2019-04-02 14:23:57 -07001542 return ipmi::responseResponseError();
James Feist5f957ca2019-03-14 15:33:55 -07001543 }
James Feistacc8a4e2019-04-02 14:23:57 -07001544 return ipmi::responseSuccess();
1545 }
1546 else if (command == static_cast<uint8_t>(setFscParamFlags::maxPwm))
1547 {
1548 constexpr const size_t maxDomainCount = 8;
1549 uint8_t requestedDomainMask = param1;
1550 boost::container::flat_map data = getPidConfigs();
1551 if (data.empty())
1552 {
1553
1554 phosphor::logging::log<phosphor::logging::level::ERR>(
1555 "ipmiOEMSetFscParameter: found no pid configurations!");
1556 return ipmi::responseResponseError();
1557 }
1558 size_t count = 0;
1559 for (const auto& [path, pid] : data)
1560 {
1561 auto findClass = pid.find("Class");
1562 if (findClass == pid.end())
1563 {
1564
1565 phosphor::logging::log<phosphor::logging::level::ERR>(
1566 "ipmiOEMSetFscParameter: found illegal pid "
1567 "configurations");
1568 return ipmi::responseResponseError();
1569 }
1570 std::string type = std::get<std::string>(findClass->second);
1571 if (type == "fan")
1572 {
1573 if (requestedDomainMask & (1 << count))
1574 {
1575 ipmi::setDbusProperty(
Vernon Mauery15419dd2019-05-24 09:40:30 -07001576 *dbus, "xyz.openbmc_project.EntityManager", path,
James Feistacc8a4e2019-04-02 14:23:57 -07001577 pidConfigurationIface, "OutLimitMax",
1578 static_cast<double>(param2));
1579 }
1580 count++;
1581 }
1582 }
1583 return ipmi::responseSuccess();
James Feist5f957ca2019-03-14 15:33:55 -07001584 }
1585 else
1586 {
1587 // todo other command parts possibly
1588 // tcontrol is handled in peci now
1589 // fan speed offset not implemented yet
1590 // domain pwm limit not implemented
James Feistacc8a4e2019-04-02 14:23:57 -07001591 return ipmi::responseParmOutOfRange();
James Feist5f957ca2019-03-14 15:33:55 -07001592 }
1593}
1594
James Feistacc8a4e2019-04-02 14:23:57 -07001595ipmi::RspType<
1596 std::variant<uint8_t, std::array<uint8_t, 2>, std::array<uint16_t, 2>>>
1597 ipmiOEMGetFscParameter(uint8_t command, std::optional<uint8_t> param)
James Feist5f957ca2019-03-14 15:33:55 -07001598{
James Feist09f6b602019-08-08 11:30:03 -07001599 constexpr uint8_t legacyDefaultSetpoint = -128;
James Feist5f957ca2019-03-14 15:33:55 -07001600
Vernon Mauery15419dd2019-05-24 09:40:30 -07001601 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
James Feistacc8a4e2019-04-02 14:23:57 -07001602 if (command == static_cast<uint8_t>(setFscParamFlags::tcontrol))
James Feist5f957ca2019-03-14 15:33:55 -07001603 {
James Feistacc8a4e2019-04-02 14:23:57 -07001604 if (!param)
James Feistfaa4f222019-03-21 16:21:55 -07001605 {
James Feistacc8a4e2019-04-02 14:23:57 -07001606 return ipmi::responseReqDataLenInvalid();
James Feistfaa4f222019-03-21 16:21:55 -07001607 }
1608
James Feist09f6b602019-08-08 11:30:03 -07001609 std::string pathName;
1610
1611 if (*param == legacyExitAirSensorNumber)
1612 {
1613 pathName = exitAirPathName;
1614 }
1615 else if (*param == legacyPCHSensorNumber)
1616 {
1617 pathName = pchPathName;
1618 }
1619 else
James Feistfaa4f222019-03-21 16:21:55 -07001620 {
James Feistacc8a4e2019-04-02 14:23:57 -07001621 return ipmi::responseParmOutOfRange();
James Feistfaa4f222019-03-21 16:21:55 -07001622 }
James Feist09f6b602019-08-08 11:30:03 -07001623
1624 uint8_t setpoint = legacyDefaultSetpoint;
1625 std::string path = getConfigPath(pathName);
James Feistfaa4f222019-03-21 16:21:55 -07001626 if (path.size())
1627 {
Vernon Mauery15419dd2019-05-24 09:40:30 -07001628 Value val = ipmi::getDbusProperty(
1629 *dbus, "xyz.openbmc_project.EntityManager", path,
1630 pidConfigurationIface, "SetPoint");
James Feistfaa4f222019-03-21 16:21:55 -07001631 setpoint = std::floor(std::get<double>(val) + 0.5);
1632 }
1633
1634 // old implementation used to return the "default" and current, we
1635 // don't make the default readily available so just make both the
1636 // same
James Feistfaa4f222019-03-21 16:21:55 -07001637
James Feistacc8a4e2019-04-02 14:23:57 -07001638 return ipmi::responseSuccess(
1639 std::array<uint8_t, 2>{setpoint, setpoint});
James Feistfaa4f222019-03-21 16:21:55 -07001640 }
James Feistacc8a4e2019-04-02 14:23:57 -07001641 else if (command == static_cast<uint8_t>(setFscParamFlags::maxPwm))
1642 {
1643 constexpr const size_t maxDomainCount = 8;
1644
1645 if (!param)
1646 {
1647 return ipmi::responseReqDataLenInvalid();
1648 }
1649 uint8_t requestedDomain = *param;
1650 if (requestedDomain >= maxDomainCount)
1651 {
1652 return ipmi::responseInvalidFieldRequest();
1653 }
1654
1655 boost::container::flat_map data = getPidConfigs();
1656 if (data.empty())
1657 {
1658 phosphor::logging::log<phosphor::logging::level::ERR>(
1659 "ipmiOEMGetFscParameter: found no pid configurations!");
1660 return ipmi::responseResponseError();
1661 }
1662 size_t count = 0;
1663 for (const auto& [_, pid] : data)
1664 {
1665 auto findClass = pid.find("Class");
1666 if (findClass == pid.end())
1667 {
1668 phosphor::logging::log<phosphor::logging::level::ERR>(
1669 "ipmiOEMGetFscParameter: found illegal pid "
1670 "configurations");
1671 return ipmi::responseResponseError();
1672 }
1673 std::string type = std::get<std::string>(findClass->second);
1674 if (type == "fan")
1675 {
1676 if (requestedDomain == count)
1677 {
1678 auto findOutLimit = pid.find("OutLimitMax");
1679 if (findOutLimit == pid.end())
1680 {
1681 phosphor::logging::log<phosphor::logging::level::ERR>(
1682 "ipmiOEMGetFscParameter: found illegal pid "
1683 "configurations");
1684 return ipmi::responseResponseError();
1685 }
1686
1687 return ipmi::responseSuccess(
1688 static_cast<uint8_t>(std::floor(
1689 std::get<double>(findOutLimit->second) + 0.5)));
1690 }
1691 else
1692 {
1693 count++;
1694 }
1695 }
1696 }
1697
1698 return ipmi::responseInvalidFieldRequest();
1699 }
1700 else if (command == static_cast<uint8_t>(setFscParamFlags::cfm))
James Feist5f957ca2019-03-14 15:33:55 -07001701 {
1702
1703 /*
1704 DataLen should be 1, but host is sending us an extra bit. As the
James Feistacc8a4e2019-04-02 14:23:57 -07001705 previous behavior didn't seem to prevent this, ignore the check for
1706 now.
James Feist5f957ca2019-03-14 15:33:55 -07001707
James Feistacc8a4e2019-04-02 14:23:57 -07001708 if (param)
James Feist5f957ca2019-03-14 15:33:55 -07001709 {
1710 phosphor::logging::log<phosphor::logging::level::ERR>(
1711 "ipmiOEMGetFscParameter: invalid input len!");
James Feist5f957ca2019-03-14 15:33:55 -07001712 return IPMI_CC_REQ_DATA_LEN_INVALID;
1713 }
1714 */
1715 Value cfmLimit;
1716 Value cfmMaximum;
1717 try
1718 {
Vernon Mauery15419dd2019-05-24 09:40:30 -07001719 cfmLimit = ipmi::getDbusProperty(*dbus, settingsBusName,
James Feist5f957ca2019-03-14 15:33:55 -07001720 cfmLimitSettingPath, cfmLimitIface,
1721 "Limit");
1722 cfmMaximum = ipmi::getDbusProperty(
Vernon Mauery15419dd2019-05-24 09:40:30 -07001723 *dbus, "xyz.openbmc_project.ExitAirTempSensor",
James Feist5f957ca2019-03-14 15:33:55 -07001724 "/xyz/openbmc_project/control/MaxCFM", cfmLimitIface, "Limit");
1725 }
1726 catch (sdbusplus::exception_t& e)
1727 {
1728 phosphor::logging::log<phosphor::logging::level::ERR>(
James Feistacc8a4e2019-04-02 14:23:57 -07001729 "ipmiOEMGetFscParameter: can't get cfm setting!",
James Feist5f957ca2019-03-14 15:33:55 -07001730 phosphor::logging::entry("ERR=%s", e.what()));
James Feistacc8a4e2019-04-02 14:23:57 -07001731 return ipmi::responseResponseError();
James Feist5f957ca2019-03-14 15:33:55 -07001732 }
1733
James Feistacc8a4e2019-04-02 14:23:57 -07001734 double cfmMax = std::get<double>(cfmMaximum);
1735 double cfmLim = std::get<double>(cfmLimit);
James Feist5f957ca2019-03-14 15:33:55 -07001736
James Feistacc8a4e2019-04-02 14:23:57 -07001737 cfmLim = std::floor(cfmLim + 0.5);
1738 cfmMax = std::floor(cfmMax + 0.5);
1739 uint16_t cfmLimResp = static_cast<uint16_t>(cfmLim);
1740 uint16_t cfmMaxResp = static_cast<uint16_t>(cfmMax);
James Feist5f957ca2019-03-14 15:33:55 -07001741
James Feistacc8a4e2019-04-02 14:23:57 -07001742 return ipmi::responseSuccess(
1743 std::array<uint16_t, 2>{cfmLimResp, cfmMaxResp});
James Feist5f957ca2019-03-14 15:33:55 -07001744 }
James Feistacc8a4e2019-04-02 14:23:57 -07001745
James Feist5f957ca2019-03-14 15:33:55 -07001746 else
1747 {
1748 // todo other command parts possibly
James Feist5f957ca2019-03-14 15:33:55 -07001749 // domain pwm limit not implemented
James Feistacc8a4e2019-04-02 14:23:57 -07001750 return ipmi::responseParmOutOfRange();
James Feist5f957ca2019-03-14 15:33:55 -07001751 }
1752}
1753
Zhu, Yungebe560b02019-04-21 21:19:21 -04001754ipmi::RspType<> ipmiOEMSetFaultIndication(uint8_t sourceId, uint8_t faultType,
1755 uint8_t faultState,
1756 uint8_t faultGroup,
1757 std::array<uint8_t, 8>& ledStateData)
1758{
1759 static constexpr const char* objpath = "/xyz/openbmc_project/EntityManager";
1760 static constexpr const char* intf = "xyz.openbmc_project.EntityManager";
1761 constexpr auto maxFaultType = static_cast<size_t>(RemoteFaultType::max);
1762 static const std::array<std::string, maxFaultType> faultNames = {
1763 "faultFan", "faultTemp", "faultPower",
1764 "faultDriveSlot", "faultSoftware", "faultMemory"};
1765 static constexpr const char* sysGpioPath = "/sys/class/gpio/gpio";
1766 static constexpr const char* postfixValue = "/value";
1767
1768 constexpr uint8_t maxFaultSource = 0x4;
1769 constexpr uint8_t skipLEDs = 0xFF;
1770 constexpr uint8_t pinSize = 64;
1771 constexpr uint8_t groupSize = 16;
1772
1773 std::vector<uint16_t> ledFaultPins(pinSize, 0xFFFF);
1774 uint64_t resFIndex = 0;
1775 std::string resFType;
1776 std::string service;
1777 ObjectValueTree valueTree;
1778
1779 // Validate the source, fault type
1780 if ((sourceId >= maxFaultSource) ||
1781 (faultType >= static_cast<int8_t>(RemoteFaultType::max)) ||
1782 (faultState >= static_cast<int8_t>(RemoteFaultState::maxFaultState)) ||
1783 (faultGroup >= static_cast<int8_t>(DimmFaultType::maxFaultGroup)))
1784 {
1785 return ipmi::responseParmOutOfRange();
1786 }
1787
Vernon Mauery15419dd2019-05-24 09:40:30 -07001788 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
Zhu, Yungebe560b02019-04-21 21:19:21 -04001789 try
1790 {
Vernon Mauery15419dd2019-05-24 09:40:30 -07001791 service = getService(*dbus, intf, objpath);
1792 valueTree = getManagedObjects(*dbus, service, "/");
Zhu, Yungebe560b02019-04-21 21:19:21 -04001793 }
1794 catch (const std::exception& e)
1795 {
1796 phosphor::logging::log<phosphor::logging::level::ERR>(
1797 "No object implements interface",
1798 phosphor::logging::entry("SERVICE=%s", service.c_str()),
1799 phosphor::logging::entry("INTF=%s", intf));
1800 return ipmi::responseResponseError();
1801 }
1802
1803 if (valueTree.empty())
1804 {
1805 phosphor::logging::log<phosphor::logging::level::ERR>(
1806 "No object implements interface",
1807 phosphor::logging::entry("INTF=%s", intf));
1808 return ipmi::responseResponseError();
1809 }
1810
1811 for (const auto& item : valueTree)
1812 {
1813 // find LedFault configuration
1814 auto interface =
1815 item.second.find("xyz.openbmc_project.Configuration.LedFault");
1816 if (interface == item.second.end())
1817 {
1818 continue;
1819 }
1820
1821 // find matched fault type: faultMemmory / faultFan
1822 // find LedGpioPins/FaultIndex configuration
1823 auto propertyFaultType = interface->second.find("FaultType");
1824 auto propertyFIndex = interface->second.find("FaultIndex");
1825 auto ledIndex = interface->second.find("LedGpioPins");
1826
1827 if (propertyFaultType == interface->second.end() ||
1828 propertyFIndex == interface->second.end() ||
1829 ledIndex == interface->second.end())
1830 {
1831 continue;
1832 }
1833
1834 try
1835 {
1836 Value valIndex = propertyFIndex->second;
1837 resFIndex = std::get<uint64_t>(valIndex);
1838
1839 Value valFType = propertyFaultType->second;
1840 resFType = std::get<std::string>(valFType);
1841 }
1842 catch (const std::bad_variant_access& e)
1843 {
1844 phosphor::logging::log<phosphor::logging::level::ERR>(e.what());
1845 return ipmi::responseResponseError();
1846 }
1847 // find the matched requested fault type: faultMemmory or faultFan
1848 if (resFType != faultNames[faultType])
1849 {
1850 continue;
1851 }
1852
1853 // read LedGpioPins data
1854 std::vector<uint64_t> ledgpios;
1855 std::variant<std::vector<uint64_t>> message;
1856
Vernon Mauery15419dd2019-05-24 09:40:30 -07001857 auto method = dbus->new_method_call(
Zhu, Yungebe560b02019-04-21 21:19:21 -04001858 service.c_str(), (std::string(item.first)).c_str(),
1859 "org.freedesktop.DBus.Properties", "Get");
1860
1861 method.append("xyz.openbmc_project.Configuration.LedFault",
1862 "LedGpioPins");
1863
1864 try
1865 {
Vernon Mauery15419dd2019-05-24 09:40:30 -07001866 auto reply = dbus->call(method);
Zhu, Yungebe560b02019-04-21 21:19:21 -04001867 reply.read(message);
1868 ledgpios = std::get<std::vector<uint64_t>>(message);
1869 }
1870 catch (std::exception& e)
1871 {
1872 phosphor::logging::log<phosphor::logging::level::ERR>(e.what());
1873 return ipmi::responseResponseError();
1874 }
1875
1876 // Check the size to be sure it will never overflow on groupSize
1877 if (ledgpios.size() > groupSize)
1878 {
1879 phosphor::logging::log<phosphor::logging::level::ERR>(
1880 "Fault gpio Pins out of range!");
1881 return ipmi::responseParmOutOfRange();
1882 }
1883 // Store data, according to command data bit index order
1884 for (int i = 0; i < ledgpios.size(); i++)
1885 {
1886 ledFaultPins[i + groupSize * resFIndex] = ledgpios[i];
1887 }
1888 }
1889
1890 switch (RemoteFaultType(faultType))
1891 {
1892 case (RemoteFaultType::fan):
1893 case (RemoteFaultType::memory):
1894 {
1895 if (faultGroup == skipLEDs)
1896 {
1897 return ipmi::responseSuccess();
1898 }
1899
1900 uint64_t ledState = 0;
1901 // calculate led state bit filed count, each byte has 8bits
1902 // the maximum bits will be 8 * 8 bits
1903 constexpr uint8_t size = sizeof(ledStateData) * 8;
1904 for (int i = 0; i < sizeof(ledStateData); i++)
1905 {
1906 ledState = (uint64_t)(ledState << 8);
1907 ledState = (uint64_t)(ledState | (uint64_t)ledStateData[i]);
1908 }
1909
1910 std::bitset<size> ledStateBits(ledState);
1911 std::string gpioValue;
1912 for (int i = 0; i < size; i++)
1913 { // skip invalid value
1914 if (ledFaultPins[i] == 0xFFFF)
1915 {
1916 continue;
1917 }
1918
1919 std::string device = sysGpioPath +
1920 std::to_string(ledFaultPins[i]) +
1921 postfixValue;
1922 std::fstream gpioFile;
1923
1924 gpioFile.open(device, std::ios::out);
1925
1926 if (!gpioFile.good())
1927 {
1928 phosphor::logging::log<phosphor::logging::level::ERR>(
1929 "Not Find Led Gpio Device!",
1930 phosphor::logging::entry("DEVICE=%s", device.c_str()));
1931 return ipmi::responseResponseError();
1932 }
1933 gpioFile << std::to_string(
1934 static_cast<uint8_t>(ledStateBits[i]));
1935 gpioFile.close();
1936 }
1937 break;
1938 }
1939 default:
1940 {
1941 // now only support two fault types
1942 return ipmi::responseParmOutOfRange();
1943 }
1944 }
1945
1946 return ipmi::responseSuccess();
1947}
1948
Richard Marian Thomaiyarea537d52019-04-24 21:33:48 +05301949ipmi::RspType<uint8_t> ipmiOEMReadBoardProductId()
1950{
1951 uint8_t prodId = 0;
1952 try
1953 {
Vernon Mauery15419dd2019-05-24 09:40:30 -07001954 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
Richard Marian Thomaiyarea537d52019-04-24 21:33:48 +05301955 const DbusObjectInfo& object = getDbusObject(
Vernon Mauery15419dd2019-05-24 09:40:30 -07001956 *dbus, "xyz.openbmc_project.Inventory.Item.Board",
Richard Marian Thomaiyarea537d52019-04-24 21:33:48 +05301957 "/xyz/openbmc_project/inventory/system/board/", "Baseboard");
1958 const Value& propValue = getDbusProperty(
Vernon Mauery15419dd2019-05-24 09:40:30 -07001959 *dbus, object.second, object.first,
Richard Marian Thomaiyarea537d52019-04-24 21:33:48 +05301960 "xyz.openbmc_project.Inventory.Item.Board", "ProductId");
1961 prodId = static_cast<uint8_t>(std::get<uint64_t>(propValue));
1962 }
1963 catch (std::exception& e)
1964 {
1965 phosphor::logging::log<phosphor::logging::level::ERR>(
1966 "ipmiOEMReadBoardProductId: Product ID read failed!",
1967 phosphor::logging::entry("ERR=%s", e.what()));
1968 }
1969 return ipmi::responseSuccess(prodId);
1970}
1971
Vernon Mauery4ac799d2019-05-20 15:50:37 -07001972ipmi::RspType<uint8_t /* restore status */>
1973 ipmiRestoreConfiguration(const std::array<uint8_t, 3>& clr, uint8_t cmd)
1974{
1975 static constexpr std::array<uint8_t, 3> expClr = {'C', 'L', 'R'};
1976
1977 if (clr != expClr)
1978 {
1979 return ipmi::responseInvalidFieldRequest();
1980 }
1981 constexpr uint8_t cmdStatus = 0;
1982 constexpr uint8_t cmdDefaultRestore = 0xaa;
1983 constexpr uint8_t cmdFullRestore = 0xbb;
1984 constexpr uint8_t cmdFormat = 0xcc;
1985
1986 constexpr const char* restoreOpFname = "/tmp/.rwfs/.restore_op";
1987
1988 switch (cmd)
1989 {
1990 case cmdStatus:
1991 break;
1992 case cmdDefaultRestore:
1993 case cmdFullRestore:
1994 case cmdFormat:
1995 {
1996 // write file to rwfs root
1997 int value = (cmd - 1) & 0x03; // map aa, bb, cc => 1, 2, 3
1998 std::ofstream restoreFile(restoreOpFname);
1999 if (!restoreFile)
2000 {
2001 return ipmi::responseUnspecifiedError();
2002 }
2003 restoreFile << value << "\n";
2004 break;
2005 }
2006 default:
2007 return ipmi::responseInvalidFieldRequest();
2008 }
2009
2010 constexpr uint8_t restorePending = 0;
2011 constexpr uint8_t restoreComplete = 1;
2012
2013 uint8_t restoreStatus = std::filesystem::exists(restoreOpFname)
2014 ? restorePending
2015 : restoreComplete;
2016 return ipmi::responseSuccess(restoreStatus);
2017}
2018
Chen Yugang39736d52019-07-12 16:24:33 +08002019ipmi::RspType<uint8_t> ipmiOEMGetNmiSource(void)
2020{
2021 uint8_t bmcSource;
2022 namespace nmi = sdbusplus::com::intel::Control::server;
2023
2024 try
2025 {
2026 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
2027 std::string service =
2028 getService(*dbus, oemNmiSourceIntf, oemNmiSourceObjPath);
2029 Value variant =
2030 getDbusProperty(*dbus, service, oemNmiSourceObjPath,
2031 oemNmiSourceIntf, oemNmiBmcSourceObjPathProp);
2032
2033 switch (nmi::NMISource::convertBMCSourceSignalFromString(
2034 std::get<std::string>(variant)))
2035 {
2036 case nmi::NMISource::BMCSourceSignal::None:
2037 bmcSource = static_cast<uint8_t>(NmiSource::none);
2038 break;
2039 case nmi::NMISource::BMCSourceSignal::FpBtn:
2040 bmcSource = static_cast<uint8_t>(NmiSource::fpBtn);
2041 break;
2042 case nmi::NMISource::BMCSourceSignal::WdPreTimeout:
2043 bmcSource = static_cast<uint8_t>(NmiSource::wdPreTimeout);
2044 break;
2045 case nmi::NMISource::BMCSourceSignal::PefMatch:
2046 bmcSource = static_cast<uint8_t>(NmiSource::pefMatch);
2047 break;
2048 case nmi::NMISource::BMCSourceSignal::ChassisCmd:
2049 bmcSource = static_cast<uint8_t>(NmiSource::chassisCmd);
2050 break;
2051 case nmi::NMISource::BMCSourceSignal::MemoryError:
2052 bmcSource = static_cast<uint8_t>(NmiSource::memoryError);
2053 break;
2054 case nmi::NMISource::BMCSourceSignal::PciSerrPerr:
2055 bmcSource = static_cast<uint8_t>(NmiSource::pciSerrPerr);
2056 break;
2057 case nmi::NMISource::BMCSourceSignal::SouthbridgeNmi:
2058 bmcSource = static_cast<uint8_t>(NmiSource::southbridgeNmi);
2059 break;
2060 case nmi::NMISource::BMCSourceSignal::ChipsetNmi:
2061 bmcSource = static_cast<uint8_t>(NmiSource::chipsetNmi);
2062 break;
2063 default:
2064 phosphor::logging::log<phosphor::logging::level::ERR>(
2065 "NMI source: invalid property!",
2066 phosphor::logging::entry(
2067 "PROP=%s", std::get<std::string>(variant).c_str()));
2068 return ipmi::responseResponseError();
2069 }
2070 }
2071 catch (sdbusplus::exception::SdBusError& e)
2072 {
2073 phosphor::logging::log<phosphor::logging::level::ERR>(e.what());
2074 return ipmi::responseResponseError();
2075 }
2076
2077 return ipmi::responseSuccess(bmcSource);
2078}
2079
2080ipmi::RspType<> ipmiOEMSetNmiSource(uint8_t sourceId)
2081{
2082 namespace nmi = sdbusplus::com::intel::Control::server;
2083
2084 nmi::NMISource::BMCSourceSignal bmcSourceSignal =
2085 nmi::NMISource::BMCSourceSignal::None;
2086
2087 switch (NmiSource(sourceId))
2088 {
2089 case NmiSource::none:
2090 bmcSourceSignal = nmi::NMISource::BMCSourceSignal::None;
2091 break;
2092 case NmiSource::fpBtn:
2093 bmcSourceSignal = nmi::NMISource::BMCSourceSignal::FpBtn;
2094 break;
2095 case NmiSource::wdPreTimeout:
2096 bmcSourceSignal = nmi::NMISource::BMCSourceSignal::WdPreTimeout;
2097 break;
2098 case NmiSource::pefMatch:
2099 bmcSourceSignal = nmi::NMISource::BMCSourceSignal::PefMatch;
2100 break;
2101 case NmiSource::chassisCmd:
2102 bmcSourceSignal = nmi::NMISource::BMCSourceSignal::ChassisCmd;
2103 break;
2104 case NmiSource::memoryError:
2105 bmcSourceSignal = nmi::NMISource::BMCSourceSignal::MemoryError;
2106 break;
2107 case NmiSource::pciSerrPerr:
2108 bmcSourceSignal = nmi::NMISource::BMCSourceSignal::PciSerrPerr;
2109 break;
2110 case NmiSource::southbridgeNmi:
2111 bmcSourceSignal = nmi::NMISource::BMCSourceSignal::SouthbridgeNmi;
2112 break;
2113 case NmiSource::chipsetNmi:
2114 bmcSourceSignal = nmi::NMISource::BMCSourceSignal::ChipsetNmi;
2115 break;
2116 default:
2117 phosphor::logging::log<phosphor::logging::level::ERR>(
2118 "NMI source: invalid property!");
2119 return ipmi::responseResponseError();
2120 }
2121
2122 try
2123 {
2124 // keep NMI signal source
2125 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
2126 std::string service =
2127 getService(*dbus, oemNmiSourceIntf, oemNmiSourceObjPath);
2128 setDbusProperty(
2129 *dbus, service, oemNmiSourceObjPath, oemNmiSourceIntf,
2130 oemNmiBmcSourceObjPathProp,
2131 sdbusplus::com::intel::Control::server::convertForMessage(
2132 bmcSourceSignal));
2133 }
2134 catch (sdbusplus::exception_t& e)
2135 {
2136 phosphor::logging::log<phosphor::logging::level::ERR>(e.what());
2137 return ipmi::responseResponseError();
2138 }
2139
2140 return ipmi::responseSuccess();
2141}
2142
James Feist63efafa2019-07-24 12:39:21 -07002143namespace dimmOffset
2144{
2145constexpr const char* dimmPower = "DimmPower";
2146constexpr const char* staticCltt = "StaticCltt";
2147constexpr const char* offsetPath = "/xyz/openbmc_project/Inventory/Item/Dimm";
2148constexpr const char* offsetInterface =
2149 "xyz.openbmc_project.Inventory.Item.Dimm.Offset";
2150constexpr const char* property = "DimmOffset";
2151
2152}; // namespace dimmOffset
2153
2154ipmi::RspType<>
2155 ipmiOEMSetDimmOffset(uint8_t type,
2156 const std::vector<std::tuple<uint8_t, uint8_t>>& data)
2157{
2158 if (type != static_cast<uint8_t>(dimmOffsetTypes::dimmPower) &&
2159 type != static_cast<uint8_t>(dimmOffsetTypes::staticCltt))
2160 {
2161 return ipmi::responseInvalidFieldRequest();
2162 }
2163
2164 if (data.empty())
2165 {
2166 return ipmi::responseInvalidFieldRequest();
2167 }
2168 nlohmann::json json;
2169
2170 std::ifstream jsonStream(dimmOffsetFile);
2171 if (jsonStream.good())
2172 {
2173 json = nlohmann::json::parse(jsonStream, nullptr, false);
2174 if (json.is_discarded())
2175 {
2176 json = nlohmann::json();
2177 }
2178 jsonStream.close();
2179 }
2180
2181 std::string typeName;
2182 if (type == static_cast<uint8_t>(dimmOffsetTypes::dimmPower))
2183 {
2184 typeName = dimmOffset::dimmPower;
2185 }
2186 else
2187 {
2188 typeName = dimmOffset::staticCltt;
2189 }
2190
2191 nlohmann::json& field = json[typeName];
2192
2193 for (const auto& [index, value] : data)
2194 {
2195 field[index] = value;
2196 }
2197
2198 for (nlohmann::json& val : field)
2199 {
2200 if (val == nullptr)
2201 {
2202 val = static_cast<uint8_t>(0);
2203 }
2204 }
2205
2206 std::ofstream output(dimmOffsetFile);
2207 if (!output.good())
2208 {
2209 std::cerr << "Error writing json file\n";
2210 return ipmi::responseResponseError();
2211 }
2212
2213 output << json.dump(4);
2214
2215 if (type == static_cast<uint8_t>(dimmOffsetTypes::staticCltt))
2216 {
2217 std::shared_ptr<sdbusplus::asio::connection> bus = getSdBus();
2218
2219 std::variant<std::vector<uint8_t>> offsets =
2220 field.get<std::vector<uint8_t>>();
2221 auto call = bus->new_method_call(
2222 settingsBusName, dimmOffset::offsetPath, PROP_INTF, "Set");
2223 call.append(dimmOffset::offsetInterface, dimmOffset::property, offsets);
2224 try
2225 {
2226 bus->call(call);
2227 }
2228 catch (sdbusplus::exception_t& e)
2229 {
2230 phosphor::logging::log<phosphor::logging::level::ERR>(
2231 "ipmiOEMSetDimmOffset: can't set dimm offsets!",
2232 phosphor::logging::entry("ERR=%s", e.what()));
2233 return ipmi::responseResponseError();
2234 }
2235 }
2236
2237 return ipmi::responseSuccess();
2238}
2239
2240ipmi::RspType<uint8_t> ipmiOEMGetDimmOffset(uint8_t type, uint8_t index)
2241{
2242
2243 if (type != static_cast<uint8_t>(dimmOffsetTypes::dimmPower) &&
2244 type != static_cast<uint8_t>(dimmOffsetTypes::staticCltt))
2245 {
2246 return ipmi::responseInvalidFieldRequest();
2247 }
2248
2249 std::ifstream jsonStream(dimmOffsetFile);
2250
2251 auto json = nlohmann::json::parse(jsonStream, nullptr, false);
2252 if (json.is_discarded())
2253 {
2254 std::cerr << "File error in " << dimmOffsetFile << "\n";
2255 return ipmi::responseResponseError();
2256 }
2257
2258 std::string typeName;
2259 if (type == static_cast<uint8_t>(dimmOffsetTypes::dimmPower))
2260 {
2261 typeName = dimmOffset::dimmPower;
2262 }
2263 else
2264 {
2265 typeName = dimmOffset::staticCltt;
2266 }
2267
2268 auto it = json.find(typeName);
2269 if (it == json.end())
2270 {
2271 return ipmi::responseInvalidFieldRequest();
2272 }
2273
2274 if (it->size() <= index)
2275 {
2276 return ipmi::responseInvalidFieldRequest();
2277 }
2278
2279 uint8_t resp = it->at(index).get<uint8_t>();
2280 return ipmi::responseSuccess(resp);
2281}
2282
Jason M. Bills64796042018-10-03 16:51:55 -07002283static void registerOEMFunctions(void)
Jia, Chunhuia835eaa2018-09-05 09:00:41 +08002284{
2285 phosphor::logging::log<phosphor::logging::level::INFO>(
2286 "Registering OEM commands");
Jason M. Bills64796042018-10-03 16:51:55 -07002287 ipmiPrintAndRegister(netfnIntcOEMGeneral, IPMI_CMD_WILDCARD, NULL,
2288 ipmiOEMWildcard,
2289 PRIVILEGE_USER); // wildcard default handler
2290 ipmiPrintAndRegister(netfunIntelAppOEM, IPMI_CMD_WILDCARD, NULL,
2291 ipmiOEMWildcard,
2292 PRIVILEGE_USER); // wildcard default handler
2293 ipmiPrintAndRegister(
2294 netfnIntcOEMGeneral,
2295 static_cast<ipmi_cmd_t>(
2296 IPMINetfnIntelOEMGeneralCmd::cmdGetChassisIdentifier),
2297 NULL, ipmiOEMGetChassisIdentifier,
2298 PRIVILEGE_USER); // get chassis identifier
2299 ipmiPrintAndRegister(
2300 netfnIntcOEMGeneral,
2301 static_cast<ipmi_cmd_t>(IPMINetfnIntelOEMGeneralCmd::cmdSetSystemGUID),
2302 NULL, ipmiOEMSetSystemGUID,
2303 PRIVILEGE_ADMIN); // set system guid
2304 ipmiPrintAndRegister(
2305 netfnIntcOEMGeneral,
2306 static_cast<ipmi_cmd_t>(IPMINetfnIntelOEMGeneralCmd::cmdSetBIOSID),
2307 NULL, ipmiOEMSetBIOSID, PRIVILEGE_ADMIN);
2308 ipmiPrintAndRegister(netfnIntcOEMGeneral,
2309 static_cast<ipmi_cmd_t>(
2310 IPMINetfnIntelOEMGeneralCmd::cmdGetOEMDeviceInfo),
2311 NULL, ipmiOEMGetDeviceInfo, PRIVILEGE_USER);
2312 ipmiPrintAndRegister(
2313 netfnIntcOEMGeneral,
2314 static_cast<ipmi_cmd_t>(
2315 IPMINetfnIntelOEMGeneralCmd::cmdGetAICSlotFRUIDSlotPosRecords),
2316 NULL, ipmiOEMGetAICFRU, PRIVILEGE_USER);
Jia, Chunhuicc49b542019-03-20 15:41:07 +08002317
2318 ipmi::registerHandler(
2319 ipmi::prioOpenBmcBase, ipmi::netFnOemOne,
2320 static_cast<ipmi::Cmd>(
2321 IPMINetfnIntelOEMGeneralCmd::cmdSendEmbeddedFWUpdStatus),
2322 ipmi::Privilege::Operator, ipmiOEMSendEmbeddedFwUpdStatus);
2323
Jason M. Bills64796042018-10-03 16:51:55 -07002324 ipmiPrintAndRegister(
2325 netfnIntcOEMGeneral,
2326 static_cast<ipmi_cmd_t>(
2327 IPMINetfnIntelOEMGeneralCmd::cmdSetPowerRestoreDelay),
2328 NULL, ipmiOEMSetPowerRestoreDelay, PRIVILEGE_OPERATOR);
2329 ipmiPrintAndRegister(
2330 netfnIntcOEMGeneral,
2331 static_cast<ipmi_cmd_t>(
2332 IPMINetfnIntelOEMGeneralCmd::cmdGetPowerRestoreDelay),
2333 NULL, ipmiOEMGetPowerRestoreDelay, PRIVILEGE_USER);
Suryakanth Sekard509eb92018-11-15 17:44:11 +05302334
2335 ipmi::registerHandler(
2336 ipmi::prioOpenBmcBase, ipmi::netFnOemOne,
2337 static_cast<ipmi::Cmd>(
2338 IPMINetfnIntelOEMGeneralCmd::cmdSetOEMUser2Activation),
2339 ipmi::Privilege::Callback, ipmiOEMSetUser2Activation);
2340
Richard Marian Thomaiyarfc5e9852019-04-14 15:06:27 +05302341 ipmi::registerHandler(
2342 ipmi::prioOpenBmcBase, ipmi::netFnOemOne,
2343 static_cast<ipmi::Cmd>(
2344 IPMINetfnIntelOEMGeneralCmd::cmdSetSpecialUserPassword),
2345 ipmi::Privilege::Callback, ipmiOEMSetSpecialUserPassword);
2346
Jason M. Bills42bd9c82019-06-28 16:39:34 -07002347 // <Get Processor Error Config>
2348 ipmi::registerHandler(
2349 ipmi::prioOemBase, netfnIntcOEMGeneral,
2350 static_cast<ipmi::Cmd>(
Jason M. Bills64796042018-10-03 16:51:55 -07002351 IPMINetfnIntelOEMGeneralCmd::cmdGetProcessorErrConfig),
Jason M. Bills42bd9c82019-06-28 16:39:34 -07002352 ipmi::Privilege::User, ipmiOEMGetProcessorErrConfig);
2353 // <Set Processor Error Config>
2354 ipmi::registerHandler(
2355 ipmi::prioOemBase, netfnIntcOEMGeneral,
2356 static_cast<ipmi::Cmd>(
Jason M. Bills64796042018-10-03 16:51:55 -07002357 IPMINetfnIntelOEMGeneralCmd::cmdSetProcessorErrConfig),
Jason M. Bills42bd9c82019-06-28 16:39:34 -07002358 ipmi::Privilege::Admin, ipmiOEMSetProcessorErrConfig);
2359
Yong Li703922d2018-11-06 13:25:31 +08002360 ipmiPrintAndRegister(netfnIntcOEMGeneral,
2361 static_cast<ipmi_cmd_t>(
2362 IPMINetfnIntelOEMGeneralCmd::cmdSetShutdownPolicy),
2363 NULL, ipmiOEMSetShutdownPolicy, PRIVILEGE_ADMIN);
2364 ipmiPrintAndRegister(netfnIntcOEMGeneral,
2365 static_cast<ipmi_cmd_t>(
2366 IPMINetfnIntelOEMGeneralCmd::cmdGetShutdownPolicy),
2367 NULL, ipmiOEMGetShutdownPolicy, PRIVILEGE_ADMIN);
James Feist91244a62019-02-19 15:04:54 -08002368
2369 ipmiPrintAndRegister(
2370 netfnIntcOEMGeneral,
2371 static_cast<ipmi_cmd_t>(IPMINetfnIntelOEMGeneralCmd::cmdSetFanConfig),
2372 NULL, ipmiOEMSetFanConfig, PRIVILEGE_USER);
2373
James Feist5b693632019-07-09 09:06:09 -07002374 ipmi::registerHandler(
2375 ipmi::prioOemBase, netfnIntcOEMGeneral,
2376 static_cast<ipmi::Cmd>(IPMINetfnIntelOEMGeneralCmd::cmdGetFanConfig),
2377 ipmi::Privilege::User, ipmiOEMGetFanConfig);
James Feist91244a62019-02-19 15:04:54 -08002378
James Feistacc8a4e2019-04-02 14:23:57 -07002379 ipmi::registerHandler(
2380 ipmi::prioOemBase, netfnIntcOEMGeneral,
2381 static_cast<ipmi::Cmd>(
2382 IPMINetfnIntelOEMGeneralCmd::cmdGetFanSpeedOffset),
2383 ipmi::Privilege::User, ipmiOEMGetFanSpeedOffset);
James Feist5f957ca2019-03-14 15:33:55 -07002384
James Feistacc8a4e2019-04-02 14:23:57 -07002385 ipmi::registerHandler(
2386 ipmi::prioOemBase, netfnIntcOEMGeneral,
2387 static_cast<ipmi::Cmd>(
2388 IPMINetfnIntelOEMGeneralCmd::cmdSetFanSpeedOffset),
2389 ipmi::Privilege::User, ipmiOEMSetFanSpeedOffset);
2390
2391 ipmi::registerHandler(
2392 ipmi::prioOemBase, netfnIntcOEMGeneral,
2393 static_cast<ipmi::Cmd>(IPMINetfnIntelOEMGeneralCmd::cmdSetFscParameter),
2394 ipmi::Privilege::User, ipmiOEMSetFscParameter);
2395
2396 ipmi::registerHandler(
2397 ipmi::prioOemBase, netfnIntcOEMGeneral,
2398 static_cast<ipmi::Cmd>(IPMINetfnIntelOEMGeneralCmd::cmdGetFscParameter),
2399 ipmi::Privilege::User, ipmiOEMGetFscParameter);
James Feist5f957ca2019-03-14 15:33:55 -07002400
Richard Marian Thomaiyarea537d52019-04-24 21:33:48 +05302401 ipmi::registerHandler(
2402 ipmi::prioOpenBmcBase, netfnIntcOEMGeneral,
2403 static_cast<ipmi::Cmd>(
2404 IPMINetfnIntelOEMGeneralCmd::cmdReadBaseBoardProductId),
2405 ipmi::Privilege::Admin, ipmiOEMReadBoardProductId);
2406
Chen Yugang39736d52019-07-12 16:24:33 +08002407 ipmi::registerHandler(
2408 ipmi::prioOemBase, netfnIntcOEMGeneral,
2409 static_cast<ipmi::Cmd>(IPMINetfnIntelOEMGeneralCmd::cmdGetNmiStatus),
2410 ipmi::Privilege::User, ipmiOEMGetNmiSource);
2411
2412 ipmi::registerHandler(
2413 ipmi::prioOemBase, netfnIntcOEMGeneral,
2414 static_cast<ipmi::Cmd>(IPMINetfnIntelOEMGeneralCmd::cmdSetNmiStatus),
2415 ipmi::Privilege::Operator, ipmiOEMSetNmiSource);
2416
Kuiying Wang45f04982018-12-26 09:23:08 +08002417 ipmiPrintAndRegister(
2418 netfnIntcOEMGeneral,
2419 static_cast<ipmi_cmd_t>(IPMINetfnIntelOEMGeneralCmd::cmdGetLEDStatus),
2420 NULL, ipmiOEMGetLEDStatus, PRIVILEGE_ADMIN);
Yong Li23737fe2019-02-19 08:49:55 +08002421 ipmiPrintAndRegister(
2422 netfnIntcOEMPlatform,
2423 static_cast<ipmi_cmd_t>(
2424 IPMINetfnIntelOEMPlatformCmd::cmdCfgHostSerialPortSpeed),
2425 NULL, ipmiOEMCfgHostSerialPortSpeed, PRIVILEGE_ADMIN);
Zhu, Yungebe560b02019-04-21 21:19:21 -04002426 ipmi::registerHandler(
2427 ipmi::prioOemBase, netfnIntcOEMGeneral,
2428 static_cast<ipmi::Cmd>(
2429 IPMINetfnIntelOEMGeneralCmd::cmdSetFaultIndication),
2430 ipmi::Privilege::Operator, ipmiOEMSetFaultIndication);
Vernon Mauery4ac799d2019-05-20 15:50:37 -07002431
2432 registerHandler(prioOemBase, netfn::intel::oemGeneral,
2433 netfn::intel::cmdRestoreConfiguration, Privilege::Admin,
2434 ipmiRestoreConfiguration);
James Feist63efafa2019-07-24 12:39:21 -07002435
2436 ipmi::registerHandler(
2437 ipmi::prioOemBase, netfnIntcOEMGeneral,
2438 static_cast<ipmi::Cmd>(IPMINetfnIntelOEMGeneralCmd::cmdSetDimmOffset),
2439 ipmi::Privilege::Operator, ipmiOEMSetDimmOffset);
2440
2441 ipmi::registerHandler(
2442 ipmi::prioOemBase, netfnIntcOEMGeneral,
2443 static_cast<ipmi::Cmd>(IPMINetfnIntelOEMGeneralCmd::cmdGetDimmOffset),
2444 ipmi::Privilege::Operator, ipmiOEMGetDimmOffset);
Jia, Chunhuia835eaa2018-09-05 09:00:41 +08002445}
2446
2447} // namespace ipmi