blob: 0711183f7801848839876b5346a442719d4065b2 [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>
Yong Li0669d192019-05-06 14:01:46 +080026#include <com/intel/Control/OCOTShutdownPolicy/server.hpp>
Jason M. Bills64796042018-10-03 16:51:55 -070027#include <commandutils.hpp>
Jia, Chunhuia835eaa2018-09-05 09:00:41 +080028#include <iostream>
Jia, Chunhuicc49b542019-03-20 15:41:07 +080029#include <ipmid/api.hpp>
Vernon Mauery5480ef62019-03-20 13:43:11 -070030#include <ipmid/utils.hpp>
Jia, Chunhuia835eaa2018-09-05 09:00:41 +080031#include <oemcommands.hpp>
Jia, Chunhuia835eaa2018-09-05 09:00:41 +080032#include <phosphor-logging/log.hpp>
33#include <sdbusplus/bus.hpp>
Suryakanth Sekard509eb92018-11-15 17:44:11 +053034#include <sdbusplus/message/types.hpp>
Jia, Chunhuia835eaa2018-09-05 09:00:41 +080035#include <string>
James Feist91244a62019-02-19 15:04:54 -080036#include <variant>
Jia, Chunhuia835eaa2018-09-05 09:00:41 +080037#include <vector>
Jia, Chunhuia835eaa2018-09-05 09:00:41 +080038
39namespace ipmi
40{
Jason M. Bills64796042018-10-03 16:51:55 -070041static void registerOEMFunctions() __attribute__((constructor));
Jason M. Bills6d9c83f2019-02-08 14:02:19 -080042sdbusplus::bus::bus dbus(ipmid_get_sd_bus_connection()); // from ipmid/api.h
Jason M. Bills64796042018-10-03 16:51:55 -070043static constexpr size_t maxFRUStringLength = 0x3F;
Jia, Chunhuia835eaa2018-09-05 09:00:41 +080044
Suryakanth Sekard509eb92018-11-15 17:44:11 +053045static constexpr auto ethernetIntf =
46 "xyz.openbmc_project.Network.EthernetInterface";
47static constexpr auto networkIPIntf = "xyz.openbmc_project.Network.IP";
48static constexpr auto networkService = "xyz.openbmc_project.Network";
49static constexpr auto networkRoot = "/xyz/openbmc_project/network";
50
Jia, Chunhuia835eaa2018-09-05 09:00:41 +080051// return code: 0 successful
52int8_t getChassisSerialNumber(sdbusplus::bus::bus& bus, std::string& serial)
53{
54 std::string objpath = "/xyz/openbmc_project/FruDevice";
55 std::string intf = "xyz.openbmc_project.FruDeviceManager";
56 std::string service = getService(bus, intf, objpath);
57 ObjectValueTree valueTree = getManagedObjects(bus, service, "/");
58 if (valueTree.empty())
59 {
60 phosphor::logging::log<phosphor::logging::level::ERR>(
61 "No object implements interface",
62 phosphor::logging::entry("INTF=%s", intf.c_str()));
63 return -1;
64 }
65
Jason M. Bills64796042018-10-03 16:51:55 -070066 for (const auto& item : valueTree)
Jia, Chunhuia835eaa2018-09-05 09:00:41 +080067 {
68 auto interface = item.second.find("xyz.openbmc_project.FruDevice");
69 if (interface == item.second.end())
70 {
71 continue;
72 }
73
74 auto property = interface->second.find("CHASSIS_SERIAL_NUMBER");
75 if (property == interface->second.end())
76 {
77 continue;
78 }
79
80 try
81 {
82 Value variant = property->second;
Jason M. Bills64796042018-10-03 16:51:55 -070083 std::string& result =
84 sdbusplus::message::variant_ns::get<std::string>(variant);
85 if (result.size() > maxFRUStringLength)
Jia, Chunhuia835eaa2018-09-05 09:00:41 +080086 {
87 phosphor::logging::log<phosphor::logging::level::ERR>(
88 "FRU serial number exceed maximum length");
Jia, Chunhuia835eaa2018-09-05 09:00:41 +080089 return -1;
90 }
Jason M. Bills64796042018-10-03 16:51:55 -070091 serial = result;
Jia, Chunhuia835eaa2018-09-05 09:00:41 +080092 return 0;
93 }
Jason M. Bills64796042018-10-03 16:51:55 -070094 catch (sdbusplus::message::variant_ns::bad_variant_access& e)
Jia, Chunhuia835eaa2018-09-05 09:00:41 +080095 {
Jason M. Bills64796042018-10-03 16:51:55 -070096 phosphor::logging::log<phosphor::logging::level::ERR>(e.what());
Jia, Chunhuia835eaa2018-09-05 09:00:41 +080097 return -1;
98 }
99 }
100 return -1;
101}
Jason M. Bills64796042018-10-03 16:51:55 -0700102
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800103ipmi_ret_t ipmiOEMWildcard(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
104 ipmi_request_t request, ipmi_response_t response,
Jason M. Bills64796042018-10-03 16:51:55 -0700105 ipmi_data_len_t dataLen, ipmi_context_t context)
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800106{
Jason M. Bills64796042018-10-03 16:51:55 -0700107 printCommand(+netfn, +cmd);
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800108 // Status code.
109 ipmi_ret_t rc = IPMI_CC_INVALID;
Jason M. Bills64796042018-10-03 16:51:55 -0700110 *dataLen = 0;
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800111 return rc;
112}
113
114// Returns the Chassis Identifier (serial #)
115ipmi_ret_t ipmiOEMGetChassisIdentifier(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
116 ipmi_request_t request,
117 ipmi_response_t response,
Jason M. Bills64796042018-10-03 16:51:55 -0700118 ipmi_data_len_t dataLen,
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800119 ipmi_context_t context)
120{
121 std::string serial;
Jason M. Bills64796042018-10-03 16:51:55 -0700122 if (*dataLen != 0) // invalid request if there are extra parameters
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800123 {
Jason M. Bills64796042018-10-03 16:51:55 -0700124 *dataLen = 0;
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800125 return IPMI_CC_REQ_DATA_LEN_INVALID;
126 }
Jason M. Bills64796042018-10-03 16:51:55 -0700127 if (getChassisSerialNumber(dbus, serial) == 0)
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800128 {
Jason M. Bills64796042018-10-03 16:51:55 -0700129 *dataLen = serial.size(); // length will never exceed response length
130 // as it is checked in getChassisSerialNumber
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800131 char* resp = static_cast<char*>(response);
Jason M. Bills64796042018-10-03 16:51:55 -0700132 serial.copy(resp, *dataLen);
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800133 return IPMI_CC_OK;
134 }
Jason M. Bills64796042018-10-03 16:51:55 -0700135 *dataLen = 0;
136 return IPMI_CC_RESPONSE_ERROR;
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800137}
138
139ipmi_ret_t ipmiOEMSetSystemGUID(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
140 ipmi_request_t request,
141 ipmi_response_t response,
Jason M. Bills64796042018-10-03 16:51:55 -0700142 ipmi_data_len_t dataLen, ipmi_context_t context)
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800143{
144 static constexpr size_t safeBufferLength = 50;
145 char buf[safeBufferLength] = {0};
146 GUIDData* Data = reinterpret_cast<GUIDData*>(request);
147
Jason M. Bills64796042018-10-03 16:51:55 -0700148 if (*dataLen != sizeof(GUIDData)) // 16bytes
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800149 {
Jason M. Bills64796042018-10-03 16:51:55 -0700150 *dataLen = 0;
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800151 return IPMI_CC_REQ_DATA_LEN_INVALID;
152 }
153
Jason M. Bills64796042018-10-03 16:51:55 -0700154 *dataLen = 0;
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800155
156 snprintf(
157 buf, safeBufferLength,
158 "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x",
159 Data->timeLow4, Data->timeLow3, Data->timeLow2, Data->timeLow1,
160 Data->timeMid2, Data->timeMid1, Data->timeHigh2, Data->timeHigh1,
161 Data->clock2, Data->clock1, Data->node6, Data->node5, Data->node4,
162 Data->node3, Data->node2, Data->node1);
163 // UUID is in RFC4122 format. Ex: 61a39523-78f2-11e5-9862-e6402cfc3223
164 std::string guid = buf;
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800165
166 std::string objpath = "/xyz/openbmc_project/control/host0/systemGUID";
167 std::string intf = "xyz.openbmc_project.Common.UUID";
Jason M. Bills64796042018-10-03 16:51:55 -0700168 std::string service = getService(dbus, intf, objpath);
169 setDbusProperty(dbus, service, objpath, intf, "UUID", guid);
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800170 return IPMI_CC_OK;
171}
172
173ipmi_ret_t ipmiOEMSetBIOSID(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
174 ipmi_request_t request, ipmi_response_t response,
175 ipmi_data_len_t dataLen, ipmi_context_t context)
176{
177 DeviceInfo* data = reinterpret_cast<DeviceInfo*>(request);
178
Jason M. Bills64796042018-10-03 16:51:55 -0700179 if ((*dataLen < 2) || (*dataLen != (1 + data->biosIDLength)))
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800180 {
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800181 *dataLen = 0;
182 return IPMI_CC_REQ_DATA_LEN_INVALID;
183 }
Jason M. Bills64796042018-10-03 16:51:55 -0700184 std::string idString((char*)data->biosId, data->biosIDLength);
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800185
Jason M. Bills64796042018-10-03 16:51:55 -0700186 std::string service = getService(dbus, biosIntf, biosObjPath);
187 setDbusProperty(dbus, service, biosObjPath, biosIntf, biosProp, idString);
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800188 uint8_t* bytesWritten = static_cast<uint8_t*>(response);
189 *bytesWritten =
Jason M. Bills64796042018-10-03 16:51:55 -0700190 data->biosIDLength; // how many bytes are written into storage
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800191 *dataLen = 1;
192 return IPMI_CC_OK;
193}
194
195ipmi_ret_t ipmiOEMGetDeviceInfo(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
196 ipmi_request_t request,
197 ipmi_response_t response,
198 ipmi_data_len_t dataLen, ipmi_context_t context)
199{
200 GetOemDeviceInfoReq* req = reinterpret_cast<GetOemDeviceInfoReq*>(request);
201 GetOemDeviceInfoRes* res = reinterpret_cast<GetOemDeviceInfoRes*>(response);
202
203 if (*dataLen == 0)
204 {
Jason M. Bills64796042018-10-03 16:51:55 -0700205 *dataLen = 0;
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800206 return IPMI_CC_REQ_DATA_LEN_INVALID;
207 }
208
209 size_t reqDataLen = *dataLen;
210 *dataLen = 0;
Jason M. Bills64796042018-10-03 16:51:55 -0700211 if (req->entityType > static_cast<uint8_t>(OEMDevEntityType::sdrVer))
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800212 {
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800213 return IPMI_CC_INVALID_FIELD_REQUEST;
214 }
215
216 // handle OEM command items
Jason M. Bills64796042018-10-03 16:51:55 -0700217 switch (OEMDevEntityType(req->entityType))
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800218 {
219 case OEMDevEntityType::biosId:
220 {
221 if (sizeof(GetOemDeviceInfoReq) != reqDataLen)
222 {
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800223 return IPMI_CC_REQ_DATA_LEN_INVALID;
224 }
225
Jason M. Bills64796042018-10-03 16:51:55 -0700226 std::string service = getService(dbus, biosIntf, biosObjPath);
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800227 try
228 {
Jason M. Bills64796042018-10-03 16:51:55 -0700229 Value variant = getDbusProperty(dbus, service, biosObjPath,
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800230 biosIntf, biosProp);
Jason M. Bills64796042018-10-03 16:51:55 -0700231 std::string& idString =
232 sdbusplus::message::variant_ns::get<std::string>(variant);
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800233 if (req->offset >= idString.size())
234 {
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800235 return IPMI_CC_PARM_OUT_OF_RANGE;
236 }
Jason M. Bills64796042018-10-03 16:51:55 -0700237 size_t length = 0;
238 if (req->countToRead > (idString.size() - req->offset))
239 {
240 length = idString.size() - req->offset;
241 }
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800242 else
243 {
Jason M. Bills64796042018-10-03 16:51:55 -0700244 length = req->countToRead;
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800245 }
Jason M. Bills64796042018-10-03 16:51:55 -0700246 std::copy(idString.begin() + req->offset, idString.end(),
247 res->data);
248 res->resDatalen = length;
249 *dataLen = res->resDatalen + 1;
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800250 }
Jason M. Bills64796042018-10-03 16:51:55 -0700251 catch (sdbusplus::message::variant_ns::bad_variant_access& e)
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800252 {
Jason M. Bills64796042018-10-03 16:51:55 -0700253 phosphor::logging::log<phosphor::logging::level::ERR>(e.what());
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800254 return IPMI_CC_UNSPECIFIED_ERROR;
255 }
256 }
257 break;
258
259 case OEMDevEntityType::devVer:
260 case OEMDevEntityType::sdrVer:
261 // TODO:
262 return IPMI_CC_ILLEGAL_COMMAND;
263 default:
264 return IPMI_CC_INVALID_FIELD_REQUEST;
265 }
266 return IPMI_CC_OK;
267}
268
269ipmi_ret_t ipmiOEMGetAICFRU(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
270 ipmi_request_t request, ipmi_response_t response,
271 ipmi_data_len_t dataLen, ipmi_context_t context)
272{
273 if (*dataLen != 0)
274 {
Jason M. Bills64796042018-10-03 16:51:55 -0700275 *dataLen = 0;
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800276 return IPMI_CC_REQ_DATA_LEN_INVALID;
277 }
278
279 *dataLen = 1;
280 uint8_t* res = reinterpret_cast<uint8_t*>(response);
281 // temporary fix. We don't support AIC FRU now. Just tell BIOS that no
282 // AIC is available so that BIOS will not timeout repeatly which leads to
283 // slow booting.
284 *res = 0; // Byte1=Count of SlotPosition/FruID records.
285 return IPMI_CC_OK;
286}
287
Jason M. Bills64796042018-10-03 16:51:55 -0700288ipmi_ret_t ipmiOEMGetPowerRestoreDelay(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
289 ipmi_request_t request,
290 ipmi_response_t response,
291 ipmi_data_len_t dataLen,
292 ipmi_context_t context)
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800293{
Jason M. Bills64796042018-10-03 16:51:55 -0700294 GetPowerRestoreDelayRes* resp =
295 reinterpret_cast<GetPowerRestoreDelayRes*>(response);
296
297 if (*dataLen != 0)
298 {
299 *dataLen = 0;
300 return IPMI_CC_REQ_DATA_LEN_INVALID;
301 }
302
303 std::string service =
304 getService(dbus, powerRestoreDelayIntf, powerRestoreDelayObjPath);
305 Value variant =
306 getDbusProperty(dbus, service, powerRestoreDelayObjPath,
307 powerRestoreDelayIntf, powerRestoreDelayProp);
308
309 uint16_t delay = sdbusplus::message::variant_ns::get<uint16_t>(variant);
310 resp->byteLSB = delay;
311 resp->byteMSB = delay >> 8;
312
313 *dataLen = sizeof(GetPowerRestoreDelayRes);
314
315 return IPMI_CC_OK;
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800316}
317
Jia, Chunhuicc49b542019-03-20 15:41:07 +0800318static uint8_t bcdToDec(uint8_t val)
319{
320 return ((val / 16 * 10) + (val % 16));
321}
322
323// Allows an update utility or system BIOS to send the status of an embedded
324// firmware update attempt to the BMC. After received, BMC will create a logging
325// record.
326ipmi::RspType<> ipmiOEMSendEmbeddedFwUpdStatus(uint8_t status, uint8_t target,
327 uint8_t majorRevision,
328 uint8_t minorRevision,
329 uint32_t auxInfo)
330{
331 std::string firmware;
Jason M. Billsdc249272019-04-03 09:58:40 -0700332 int instance = (target & targetInstanceMask) >> targetInstanceShift;
Jia, Chunhuicc49b542019-03-20 15:41:07 +0800333 target = (target & selEvtTargetMask) >> selEvtTargetShift;
334
335 /* make sure the status is 0, 1, or 2 as per the spec */
336 if (status > 2)
337 {
338 return ipmi::response(ipmi::ccInvalidFieldRequest);
339 }
Jason M. Billsdc249272019-04-03 09:58:40 -0700340 /* make sure the target is 0, 1, 2, or 4 as per the spec */
341 if (target > 4 || target == 3)
342 {
343 return ipmi::response(ipmi::ccInvalidFieldRequest);
344 }
Jia, Chunhuicc49b542019-03-20 15:41:07 +0800345 /*orignal OEM command is to record OEM SEL.
346 But openbmc does not support OEM SEL, so we redirect it to redfish event
347 logging. */
348 std::string buildInfo;
349 std::string action;
350 switch (FWUpdateTarget(target))
351 {
352 case FWUpdateTarget::targetBMC:
353 firmware = "BMC";
Jason M. Billsdc249272019-04-03 09:58:40 -0700354 buildInfo = "major: " + std::to_string(majorRevision) + " minor: " +
Jia, Chunhuicc49b542019-03-20 15:41:07 +0800355 std::to_string(bcdToDec(minorRevision)) + // BCD encoded
356 " BuildID: " + std::to_string(auxInfo);
357 buildInfo += std::to_string(auxInfo);
358 break;
359 case FWUpdateTarget::targetBIOS:
360 firmware = "BIOS";
361 buildInfo =
Jason M. Billsdc249272019-04-03 09:58:40 -0700362 "major: " +
Jia, Chunhuicc49b542019-03-20 15:41:07 +0800363 std::to_string(bcdToDec(majorRevision)) + // BCD encoded
364 " minor: " +
365 std::to_string(bcdToDec(minorRevision)) + // BCD encoded
366 " ReleaseNumber: " + // ASCII encoded
367 std::to_string(static_cast<uint8_t>(auxInfo >> 0) - '0') +
368 std::to_string(static_cast<uint8_t>(auxInfo >> 8) - '0') +
369 std::to_string(static_cast<uint8_t>(auxInfo >> 16) - '0') +
370 std::to_string(static_cast<uint8_t>(auxInfo >> 24) - '0');
371 break;
372 case FWUpdateTarget::targetME:
373 firmware = "ME";
374 buildInfo =
Jason M. Billsdc249272019-04-03 09:58:40 -0700375 "major: " + std::to_string(majorRevision) + " minor1: " +
Jia, Chunhuicc49b542019-03-20 15:41:07 +0800376 std::to_string(bcdToDec(minorRevision)) + // BCD encoded
377 " minor2: " +
378 std::to_string(bcdToDec(static_cast<uint8_t>(auxInfo >> 0))) +
379 " build1: " +
380 std::to_string(bcdToDec(static_cast<uint8_t>(auxInfo >> 8))) +
381 " build2: " +
382 std::to_string(bcdToDec(static_cast<uint8_t>(auxInfo >> 16)));
383 break;
384 case FWUpdateTarget::targetOEMEWS:
385 firmware = "EWS";
Jason M. Billsdc249272019-04-03 09:58:40 -0700386 buildInfo = "major: " + std::to_string(majorRevision) + " minor: " +
Jia, Chunhuicc49b542019-03-20 15:41:07 +0800387 std::to_string(bcdToDec(minorRevision)) + // BCD encoded
388 " BuildID: " + std::to_string(auxInfo);
389 break;
390 }
391
Jason M. Billsdc249272019-04-03 09:58:40 -0700392 static const std::string openBMCMessageRegistryVersion("0.1");
393 std::string redfishMsgID = "OpenBMC." + openBMCMessageRegistryVersion;
394
Jia, Chunhuicc49b542019-03-20 15:41:07 +0800395 switch (status)
396 {
397 case 0x0:
398 action = "update started";
Jason M. Billsdc249272019-04-03 09:58:40 -0700399 redfishMsgID += ".FirmwareUpdateStarted";
Jia, Chunhuicc49b542019-03-20 15:41:07 +0800400 break;
401 case 0x1:
402 action = "update completed successfully";
Jason M. Billsdc249272019-04-03 09:58:40 -0700403 redfishMsgID += ".FirmwareUpdateCompleted";
Jia, Chunhuicc49b542019-03-20 15:41:07 +0800404 break;
405 case 0x2:
406 action = "update failure";
Jason M. Billsdc249272019-04-03 09:58:40 -0700407 redfishMsgID += ".FirmwareUpdateFailed";
Jia, Chunhuicc49b542019-03-20 15:41:07 +0800408 break;
409 default:
410 action = "unknown";
411 break;
412 }
413
Jason M. Billsdc249272019-04-03 09:58:40 -0700414 std::string firmwareInstanceStr =
415 firmware + " instance: " + std::to_string(instance);
416 std::string message("[firmware update] " + firmwareInstanceStr +
417 " status: <" + action + "> " + buildInfo);
Jia, Chunhuicc49b542019-03-20 15:41:07 +0800418
419 sd_journal_send("MESSAGE=%s", message.c_str(), "PRIORITY=%i", LOG_INFO,
Jason M. Billsdc249272019-04-03 09:58:40 -0700420 "REDFISH_MESSAGE_ID=%s", redfishMsgID.c_str(),
421 "REDFISH_MESSAGE_ARGS=%s,%s", firmwareInstanceStr.c_str(),
422 buildInfo.c_str(), NULL);
Jia, Chunhuicc49b542019-03-20 15:41:07 +0800423 return ipmi::responseSuccess();
424}
425
Jason M. Bills64796042018-10-03 16:51:55 -0700426ipmi_ret_t ipmiOEMSetPowerRestoreDelay(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
427 ipmi_request_t request,
428 ipmi_response_t response,
429 ipmi_data_len_t dataLen,
430 ipmi_context_t context)
431{
432 SetPowerRestoreDelayReq* data =
433 reinterpret_cast<SetPowerRestoreDelayReq*>(request);
434 uint16_t delay = 0;
435
436 if (*dataLen != sizeof(SetPowerRestoreDelayReq))
437 {
438 *dataLen = 0;
439 return IPMI_CC_REQ_DATA_LEN_INVALID;
440 }
441 delay = data->byteMSB;
442 delay = (delay << 8) | data->byteLSB;
443 std::string service =
444 getService(dbus, powerRestoreDelayIntf, powerRestoreDelayObjPath);
445 setDbusProperty(dbus, service, powerRestoreDelayObjPath,
446 powerRestoreDelayIntf, powerRestoreDelayProp, delay);
447 *dataLen = 0;
448
449 return IPMI_CC_OK;
450}
451
452ipmi_ret_t ipmiOEMGetProcessorErrConfig(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
453 ipmi_request_t request,
454 ipmi_response_t response,
455 ipmi_data_len_t dataLen,
456 ipmi_context_t context)
457{
458 GetProcessorErrConfigRes* resp =
459 reinterpret_cast<GetProcessorErrConfigRes*>(response);
460
461 if (*dataLen != 0)
462 {
463 *dataLen = 0;
464 return IPMI_CC_REQ_DATA_LEN_INVALID;
465 }
466
467 std::string service =
468 getService(dbus, processorErrConfigIntf, processorErrConfigObjPath);
469 Value variant = getDbusProperty(dbus, service, processorErrConfigObjPath,
470 processorErrConfigIntf, "ResetCfg");
471 resp->resetCfg = sdbusplus::message::variant_ns::get<uint8_t>(variant);
472
473 std::vector<uint8_t> caterrStatus;
Kuiying Wangbc546672018-11-23 15:41:05 +0800474 sdbusplus::message::variant<std::vector<uint8_t>> message;
Jason M. Bills64796042018-10-03 16:51:55 -0700475
476 auto method =
477 dbus.new_method_call(service.c_str(), processorErrConfigObjPath,
478 "org.freedesktop.DBus.Properties", "Get");
479
480 method.append(processorErrConfigIntf, "CATERRStatus");
Kuiying Wangbc546672018-11-23 15:41:05 +0800481 auto reply = dbus.call(method);
Jason M. Bills64796042018-10-03 16:51:55 -0700482
483 try
484 {
Kuiying Wangbc546672018-11-23 15:41:05 +0800485 reply.read(message);
486 caterrStatus =
487 sdbusplus::message::variant_ns::get<std::vector<uint8_t>>(message);
Jason M. Bills64796042018-10-03 16:51:55 -0700488 }
489 catch (sdbusplus::exception_t&)
490 {
Kuiying Wangbc546672018-11-23 15:41:05 +0800491 phosphor::logging::log<phosphor::logging::level::ERR>(
Jason M. Bills64796042018-10-03 16:51:55 -0700492 "ipmiOEMGetProcessorErrConfig: error on dbus",
493 phosphor::logging::entry("PRORPERTY=CATERRStatus"),
494 phosphor::logging::entry("PATH=%s", processorErrConfigObjPath),
495 phosphor::logging::entry("INTERFACE=%s", processorErrConfigIntf));
496 return IPMI_CC_UNSPECIFIED_ERROR;
497 }
498
499 size_t len =
500 maxCPUNum <= caterrStatus.size() ? maxCPUNum : caterrStatus.size();
501 caterrStatus.resize(len);
502 std::copy(caterrStatus.begin(), caterrStatus.end(), resp->caterrStatus);
503 *dataLen = sizeof(GetProcessorErrConfigRes);
504
505 return IPMI_CC_OK;
506}
507
508ipmi_ret_t ipmiOEMSetProcessorErrConfig(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
509 ipmi_request_t request,
510 ipmi_response_t response,
511 ipmi_data_len_t dataLen,
512 ipmi_context_t context)
513{
514 SetProcessorErrConfigReq* req =
515 reinterpret_cast<SetProcessorErrConfigReq*>(request);
516
517 if (*dataLen != sizeof(SetProcessorErrConfigReq))
518 {
519 *dataLen = 0;
520 return IPMI_CC_REQ_DATA_LEN_INVALID;
521 }
522 std::string service =
523 getService(dbus, processorErrConfigIntf, processorErrConfigObjPath);
524 setDbusProperty(dbus, service, processorErrConfigObjPath,
525 processorErrConfigIntf, "ResetCfg", req->resetCfg);
526
527 setDbusProperty(dbus, service, processorErrConfigObjPath,
528 processorErrConfigIntf, "ResetErrorOccurrenceCounts",
529 req->resetErrorOccurrenceCounts);
530 *dataLen = 0;
531
532 return IPMI_CC_OK;
533}
534
Yong Li703922d2018-11-06 13:25:31 +0800535ipmi_ret_t ipmiOEMGetShutdownPolicy(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
536 ipmi_request_t request,
537 ipmi_response_t response,
538 ipmi_data_len_t dataLen,
539 ipmi_context_t context)
540{
541 GetOEMShutdownPolicyRes* resp =
542 reinterpret_cast<GetOEMShutdownPolicyRes*>(response);
543
544 if (*dataLen != 0)
545 {
546 phosphor::logging::log<phosphor::logging::level::ERR>(
Kuiying Wang45f04982018-12-26 09:23:08 +0800547 "oem_get_shutdown_policy: invalid input len!");
Yong Li703922d2018-11-06 13:25:31 +0800548 *dataLen = 0;
549 return IPMI_CC_REQ_DATA_LEN_INVALID;
550 }
551
552 *dataLen = 0;
553
554 try
555 {
556 std::string service =
557 getService(dbus, oemShutdownPolicyIntf, oemShutdownPolicyObjPath);
558 Value variant = getDbusProperty(dbus, service, oemShutdownPolicyObjPath,
559 oemShutdownPolicyIntf,
560 oemShutdownPolicyObjPathProp);
Yong Li0669d192019-05-06 14:01:46 +0800561
562 if (sdbusplus::com::intel::Control::server::OCOTShutdownPolicy::
563 convertPolicyFromString(std::get<std::string>(variant)) ==
564 sdbusplus::com::intel::Control::server::OCOTShutdownPolicy::Policy::
565 NoShutdownOnOCOT)
566 {
567 resp->policy = 0;
568 }
569 else if (sdbusplus::com::intel::Control::server::OCOTShutdownPolicy::
570 convertPolicyFromString(std::get<std::string>(variant)) ==
571 sdbusplus::com::intel::Control::server::OCOTShutdownPolicy::
572 Policy::ShutdownOnOCOT)
573 {
574 resp->policy = 1;
575 }
576 else
577 {
578 phosphor::logging::log<phosphor::logging::level::ERR>(
579 "oem_set_shutdown_policy: invalid property!",
580 phosphor::logging::entry(
581 "PROP=%s", std::get<std::string>(variant).c_str()));
582 return IPMI_CC_UNSPECIFIED_ERROR;
583 }
Yong Li703922d2018-11-06 13:25:31 +0800584 // TODO needs to check if it is multi-node products,
585 // policy is only supported on node 3/4
586 resp->policySupport = shutdownPolicySupported;
587 }
588 catch (sdbusplus::exception_t& e)
589 {
590 phosphor::logging::log<phosphor::logging::level::ERR>(e.description());
591 return IPMI_CC_UNSPECIFIED_ERROR;
592 }
593
594 *dataLen = sizeof(GetOEMShutdownPolicyRes);
595 return IPMI_CC_OK;
596}
597
598ipmi_ret_t ipmiOEMSetShutdownPolicy(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
599 ipmi_request_t request,
600 ipmi_response_t response,
601 ipmi_data_len_t dataLen,
602 ipmi_context_t context)
603{
604 uint8_t* req = reinterpret_cast<uint8_t*>(request);
Yong Li0669d192019-05-06 14:01:46 +0800605 sdbusplus::com::intel::Control::server::OCOTShutdownPolicy::Policy policy =
606 sdbusplus::com::intel::Control::server::OCOTShutdownPolicy::Policy::
607 NoShutdownOnOCOT;
Yong Li703922d2018-11-06 13:25:31 +0800608
609 // TODO needs to check if it is multi-node products,
610 // policy is only supported on node 3/4
611 if (*dataLen != 1)
612 {
613 phosphor::logging::log<phosphor::logging::level::ERR>(
614 "oem_set_shutdown_policy: invalid input len!");
615 *dataLen = 0;
616 return IPMI_CC_REQ_DATA_LEN_INVALID;
617 }
618
619 *dataLen = 0;
620 if ((*req != noShutdownOnOCOT) && (*req != shutdownOnOCOT))
621 {
622 phosphor::logging::log<phosphor::logging::level::ERR>(
623 "oem_set_shutdown_policy: invalid input!");
624 return IPMI_CC_INVALID_FIELD_REQUEST;
625 }
626
Yong Li0669d192019-05-06 14:01:46 +0800627 if (*req == noShutdownOnOCOT)
628 {
629 policy = sdbusplus::com::intel::Control::server::OCOTShutdownPolicy::
630 Policy::NoShutdownOnOCOT;
631 }
632 else
633 {
634 policy = sdbusplus::com::intel::Control::server::OCOTShutdownPolicy::
635 Policy::ShutdownOnOCOT;
636 }
637
Yong Li703922d2018-11-06 13:25:31 +0800638 try
639 {
640 std::string service =
641 getService(dbus, oemShutdownPolicyIntf, oemShutdownPolicyObjPath);
Yong Li0669d192019-05-06 14:01:46 +0800642 setDbusProperty(
643 dbus, service, oemShutdownPolicyObjPath, oemShutdownPolicyIntf,
644 oemShutdownPolicyObjPathProp,
645 sdbusplus::com::intel::Control::server::convertForMessage(policy));
Yong Li703922d2018-11-06 13:25:31 +0800646 }
647 catch (sdbusplus::exception_t& e)
648 {
649 phosphor::logging::log<phosphor::logging::level::ERR>(e.description());
650 return IPMI_CC_UNSPECIFIED_ERROR;
651 }
652
653 return IPMI_CC_OK;
654}
655
Suryakanth Sekard509eb92018-11-15 17:44:11 +0530656/** @brief implementation for check the DHCP or not in IPv4
657 * @param[in] Channel - Channel number
658 * @returns true or false.
659 */
660static bool isDHCPEnabled(uint8_t Channel)
661{
662 try
663 {
664 auto ethdevice = getChannelName(Channel);
665 if (ethdevice.empty())
666 {
667 return false;
668 }
669 auto ethIP = ethdevice + "/ipv4";
670 auto ethernetObj =
671 getDbusObject(dbus, networkIPIntf, networkRoot, ethIP);
672 auto value = getDbusProperty(dbus, networkService, ethernetObj.first,
673 networkIPIntf, "Origin");
674 if (sdbusplus::message::variant_ns::get<std::string>(value) ==
675 "xyz.openbmc_project.Network.IP.AddressOrigin.DHCP")
676 {
677 return true;
678 }
679 else
680 {
681 return false;
682 }
683 }
684 catch (sdbusplus::exception_t& e)
685 {
686 phosphor::logging::log<phosphor::logging::level::ERR>(e.description());
687 return true;
688 }
689}
690
691/** @brief implementes for check the DHCP or not in IPv6
692 * @param[in] Channel - Channel number
693 * @returns true or false.
694 */
695static bool isDHCPIPv6Enabled(uint8_t Channel)
696{
697
698 try
699 {
700 auto ethdevice = getChannelName(Channel);
701 if (ethdevice.empty())
702 {
703 return false;
704 }
705 auto ethIP = ethdevice + "/ipv6";
706 auto objectInfo =
707 getDbusObject(dbus, networkIPIntf, networkRoot, ethIP);
708 auto properties = getAllDbusProperties(dbus, objectInfo.second,
709 objectInfo.first, networkIPIntf);
710 if (sdbusplus::message::variant_ns::get<std::string>(
711 properties["Origin"]) ==
712 "xyz.openbmc_project.Network.IP.AddressOrigin.DHCP")
713 {
714 return true;
715 }
716 else
717 {
718 return false;
719 }
720 }
721 catch (sdbusplus::exception_t& e)
722 {
723 phosphor::logging::log<phosphor::logging::level::ERR>(e.description());
724 return true;
725 }
726}
727
728/** @brief implementes the creating of default new user
729 * @param[in] userName - new username in 16 bytes.
730 * @param[in] userPassword - new password in 20 bytes
731 * @returns ipmi completion code.
732 */
733ipmi::RspType<> ipmiOEMSetUser2Activation(
734 std::array<uint8_t, ipmi::ipmiMaxUserName>& userName,
735 std::array<uint8_t, ipmi::maxIpmi20PasswordSize>& userPassword)
736{
737 bool userState = false;
738 // Check for System Interface not exist and LAN should be static
739 for (uint8_t channel = 0; channel < maxIpmiChannels; channel++)
740 {
741 ChannelInfo chInfo;
742 try
743 {
744 getChannelInfo(channel, chInfo);
745 }
746 catch (sdbusplus::exception_t& e)
747 {
748 phosphor::logging::log<phosphor::logging::level::ERR>(
749 "ipmiOEMSetUser2Activation: Failed to get Channel Info",
750 phosphor::logging::entry("MSG: %s", e.description()));
751 return ipmi::response(ipmi::ccUnspecifiedError);
752 }
753 if (chInfo.mediumType ==
754 static_cast<uint8_t>(EChannelMediumType::systemInterface))
755 {
756 phosphor::logging::log<phosphor::logging::level::ERR>(
757 "ipmiOEMSetUser2Activation: system interface exist .");
758 return ipmi::response(ipmi::ccCommandNotAvailable);
759 }
760 else
761 {
762
763 if (chInfo.mediumType ==
764 static_cast<uint8_t>(EChannelMediumType::lan8032))
765 {
766 if (isDHCPIPv6Enabled(channel) || isDHCPEnabled(channel))
767 {
768 phosphor::logging::log<phosphor::logging::level::ERR>(
769 "ipmiOEMSetUser2Activation: DHCP enabled .");
770 return ipmi::response(ipmi::ccCommandNotAvailable);
771 }
772 }
773 }
774 }
775 uint8_t maxChUsers = 0, enabledUsers = 0, fixedUsers = 0;
776 if (ipmi::ccSuccess ==
777 ipmiUserGetAllCounts(maxChUsers, enabledUsers, fixedUsers))
778 {
779 if (enabledUsers > 1)
780 {
781 phosphor::logging::log<phosphor::logging::level::ERR>(
782 "ipmiOEMSetUser2Activation: more than one user is enabled.");
783 return ipmi::response(ipmi::ccCommandNotAvailable);
784 }
785 // Check the user 2 is enabled or not
786 ipmiUserCheckEnabled(ipmiDefaultUserId, userState);
787 if (userState == true)
788 {
789 phosphor::logging::log<phosphor::logging::level::ERR>(
790 "ipmiOEMSetUser2Activation: user 2 already enabled .");
791 return ipmi::response(ipmi::ccCommandNotAvailable);
792 }
793 }
794 else
795 {
796 return ipmi::response(ipmi::ccUnspecifiedError);
797 }
798
799#if BYTE_ORDER == LITTLE_ENDIAN
800 PrivAccess privAccess = {PRIVILEGE_ADMIN, true, true, true, 0};
801#endif
802#if BYTE_ORDER == BIG_ENDIAN
803 PrivAccess privAccess = {0, true, true, true, PRIVILEGE_ADMIN};
804#endif
805
806 if (ipmi::ccSuccess ==
807 ipmiUserSetUserName(ipmiDefaultUserId,
808 reinterpret_cast<const char*>(userName.data())))
809 {
810 if (ipmi::ccSuccess ==
811 ipmiUserSetUserPassword(
812 ipmiDefaultUserId,
813 reinterpret_cast<const char*>(userPassword.data())))
814 {
815 if (ipmi::ccSuccess ==
816 ipmiUserSetPrivilegeAccess(
817 ipmiDefaultUserId,
818 static_cast<uint8_t>(ipmi::EChannelID::chanLan1),
819 privAccess, true))
820 {
821 phosphor::logging::log<phosphor::logging::level::INFO>(
822 "ipmiOEMSetUser2Activation: user created successfully ");
823 return ipmi::responseSuccess();
824 }
825 }
826 // we need to delete the default user id which added in this command as
827 // password / priv setting is failed.
828 ipmiUserSetUserName(ipmiDefaultUserId, "");
829 phosphor::logging::log<phosphor::logging::level::ERR>(
830 "ipmiOEMSetUser2Activation: password / priv setting is failed.");
831 }
832 else
833 {
834 phosphor::logging::log<phosphor::logging::level::ERR>(
835 "ipmiOEMSetUser2Activation: Setting username failed.");
836 }
837
838 return ipmi::response(ipmi::ccCommandNotAvailable);
839}
840
Richard Marian Thomaiyarfc5e9852019-04-14 15:06:27 +0530841/** @brief implementes setting password for special user
842 * @param[in] specialUserIndex
843 * @param[in] userPassword - new password in 20 bytes
844 * @returns ipmi completion code.
845 */
846ipmi::RspType<> ipmiOEMSetSpecialUserPassword(ipmi::Context::ptr ctx,
847 uint8_t specialUserIndex,
848 std::vector<uint8_t> userPassword)
849{
850 ChannelInfo chInfo;
851 try
852 {
853 getChannelInfo(ctx->channel, chInfo);
854 }
855 catch (sdbusplus::exception_t& e)
856 {
857 phosphor::logging::log<phosphor::logging::level::ERR>(
858 "ipmiOEMSetSpecialUserPassword: Failed to get Channel Info",
859 phosphor::logging::entry("MSG: %s", e.description()));
860 return ipmi::responseUnspecifiedError();
861 }
862 if (chInfo.mediumType !=
863 static_cast<uint8_t>(EChannelMediumType::systemInterface))
864 {
865 phosphor::logging::log<phosphor::logging::level::ERR>(
866 "ipmiOEMSetSpecialUserPassword: Error - supported only in KCS "
867 "interface");
868 return ipmi::responseCommandNotAvailable();
869 }
870 if (specialUserIndex != 0)
871 {
872 phosphor::logging::log<phosphor::logging::level::ERR>(
873 "ipmiOEMSetSpecialUserPassword: Invalid user account");
874 return ipmi::responseParmOutOfRange();
875 }
876 constexpr uint8_t minPasswordSizeRequired = 6;
877 if (userPassword.size() < minPasswordSizeRequired ||
878 userPassword.size() > ipmi::maxIpmi20PasswordSize)
879 {
880 return ipmi::responseReqDataLenInvalid();
881 }
882 std::string passwd;
883 passwd.assign(reinterpret_cast<const char*>(userPassword.data()),
884 userPassword.size());
885 return ipmi::response(ipmiSetSpecialUserPassword("root", passwd));
886}
887
Kuiying Wang45f04982018-12-26 09:23:08 +0800888namespace ledAction
889{
890using namespace sdbusplus::xyz::openbmc_project::Led::server;
891std::map<Physical::Action, uint8_t> actionDbusToIpmi = {
892 {Physical::Action::Off, 0x00},
893 {Physical::Action::On, 0x10},
894 {Physical::Action::Blink, 0x01}};
895
896std::map<uint8_t, std::string> offsetObjPath = {
897 {2, statusAmberObjPath}, {4, statusGreenObjPath}, {6, identifyLEDObjPath}};
898
899} // namespace ledAction
900
901int8_t getLEDState(sdbusplus::bus::bus& bus, const std::string& intf,
902 const std::string& objPath, uint8_t& state)
903{
904 try
905 {
906 std::string service = getService(bus, intf, objPath);
907 Value stateValue =
908 getDbusProperty(bus, service, objPath, intf, "State");
909 std::string strState =
910 sdbusplus::message::variant_ns::get<std::string>(stateValue);
911 state = ledAction::actionDbusToIpmi.at(
912 sdbusplus::xyz::openbmc_project::Led::server::Physical::
913 convertActionFromString(strState));
914 }
915 catch (sdbusplus::exception::SdBusError& e)
916 {
917 phosphor::logging::log<phosphor::logging::level::ERR>(e.what());
918 return -1;
919 }
920 return 0;
921}
922
923ipmi_ret_t ipmiOEMGetLEDStatus(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
924 ipmi_request_t request, ipmi_response_t response,
925 ipmi_data_len_t dataLen, ipmi_context_t context)
926{
927 uint8_t* resp = reinterpret_cast<uint8_t*>(response);
928 // LED Status
929 //[1:0] = Reserved
930 //[3:2] = Status(Amber)
931 //[5:4] = Status(Green)
932 //[7:6] = System Identify
933 // Status definitions:
934 // 00b = Off
935 // 01b = Blink
936 // 10b = On
937 // 11b = invalid
938 if (*dataLen != 0)
939 {
940 phosphor::logging::log<phosphor::logging::level::ERR>(
941 "oem_get_led_status: invalid input len!");
942 *dataLen = 0;
943 return IPMI_CC_REQ_DATA_LEN_INVALID;
944 }
945
946 phosphor::logging::log<phosphor::logging::level::DEBUG>("GET led status");
947 *resp = 0;
948 *dataLen = 0;
949 for (auto it = ledAction::offsetObjPath.begin();
950 it != ledAction::offsetObjPath.end(); ++it)
951 {
952 uint8_t state = 0;
953 if (-1 == getLEDState(dbus, ledIntf, it->second, state))
954 {
955 phosphor::logging::log<phosphor::logging::level::ERR>(
956 "oem_get_led_status: fail to get ID LED status!");
957 return IPMI_CC_UNSPECIFIED_ERROR;
958 }
959 *resp |= state << it->first;
960 }
961
962 *dataLen = sizeof(*resp);
963 return IPMI_CC_OK;
964}
965
Yong Li23737fe2019-02-19 08:49:55 +0800966ipmi_ret_t ipmiOEMCfgHostSerialPortSpeed(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
967 ipmi_request_t request,
968 ipmi_response_t response,
969 ipmi_data_len_t dataLen,
970 ipmi_context_t context)
971{
972 CfgHostSerialReq* req = reinterpret_cast<CfgHostSerialReq*>(request);
973 uint8_t* resp = reinterpret_cast<uint8_t*>(response);
974
975 if (*dataLen == 0)
976 {
977 phosphor::logging::log<phosphor::logging::level::ERR>(
978 "CfgHostSerial: invalid input len!",
979 phosphor::logging::entry("LEN=%d", *dataLen));
980 return IPMI_CC_REQ_DATA_LEN_INVALID;
981 }
982
983 switch (req->command)
984 {
985 case getHostSerialCfgCmd:
986 {
987 if (*dataLen != 1)
988 {
989 phosphor::logging::log<phosphor::logging::level::ERR>(
990 "CfgHostSerial: invalid input len!");
991 *dataLen = 0;
992 return IPMI_CC_REQ_DATA_LEN_INVALID;
993 }
994
995 *dataLen = 0;
996
997 boost::process::ipstream is;
998 std::vector<std::string> data;
999 std::string line;
1000 boost::process::child c1(fwGetEnvCmd, "-n", fwHostSerailCfgEnvName,
1001 boost::process::std_out > is);
1002
1003 while (c1.running() && std::getline(is, line) && !line.empty())
1004 {
1005 data.push_back(line);
1006 }
1007
1008 c1.wait();
1009 if (c1.exit_code())
1010 {
1011 phosphor::logging::log<phosphor::logging::level::ERR>(
1012 "CfgHostSerial:: error on execute",
1013 phosphor::logging::entry("EXECUTE=%s", fwSetEnvCmd));
1014 // Using the default value
1015 *resp = 0;
1016 }
1017 else
1018 {
1019 if (data.size() != 1)
1020 {
1021 phosphor::logging::log<phosphor::logging::level::ERR>(
1022 "CfgHostSerial:: error on read env");
1023 return IPMI_CC_UNSPECIFIED_ERROR;
1024 }
1025 try
1026 {
1027 unsigned long tmp = std::stoul(data[0]);
1028 if (tmp > std::numeric_limits<uint8_t>::max())
1029 {
1030 throw std::out_of_range("Out of range");
1031 }
1032 *resp = static_cast<uint8_t>(tmp);
1033 }
1034 catch (const std::invalid_argument& e)
1035 {
1036 phosphor::logging::log<phosphor::logging::level::ERR>(
1037 "invalid config ",
1038 phosphor::logging::entry("ERR=%s", e.what()));
1039 return IPMI_CC_UNSPECIFIED_ERROR;
1040 }
1041 catch (const std::out_of_range& e)
1042 {
1043 phosphor::logging::log<phosphor::logging::level::ERR>(
1044 "out_of_range config ",
1045 phosphor::logging::entry("ERR=%s", e.what()));
1046 return IPMI_CC_UNSPECIFIED_ERROR;
1047 }
1048 }
1049
1050 *dataLen = 1;
1051 break;
1052 }
1053 case setHostSerialCfgCmd:
1054 {
1055 if (*dataLen != sizeof(CfgHostSerialReq))
1056 {
1057 phosphor::logging::log<phosphor::logging::level::ERR>(
1058 "CfgHostSerial: invalid input len!");
1059 *dataLen = 0;
1060 return IPMI_CC_REQ_DATA_LEN_INVALID;
1061 }
1062
1063 *dataLen = 0;
1064
1065 if (req->parameter > HostSerialCfgParamMax)
1066 {
1067 phosphor::logging::log<phosphor::logging::level::ERR>(
1068 "CfgHostSerial: invalid input!");
1069 return IPMI_CC_INVALID_FIELD_REQUEST;
1070 }
1071
1072 boost::process::child c1(fwSetEnvCmd, fwHostSerailCfgEnvName,
1073 std::to_string(req->parameter));
1074
1075 c1.wait();
1076 if (c1.exit_code())
1077 {
1078 phosphor::logging::log<phosphor::logging::level::ERR>(
1079 "CfgHostSerial:: error on execute",
1080 phosphor::logging::entry("EXECUTE=%s", fwGetEnvCmd));
1081 return IPMI_CC_UNSPECIFIED_ERROR;
1082 }
1083 break;
1084 }
1085 default:
1086 phosphor::logging::log<phosphor::logging::level::ERR>(
1087 "CfgHostSerial: invalid input!");
1088 *dataLen = 0;
1089 return IPMI_CC_INVALID_FIELD_REQUEST;
1090 }
1091
1092 return IPMI_CC_OK;
1093}
1094
James Feist91244a62019-02-19 15:04:54 -08001095constexpr const char* thermalModeInterface =
1096 "xyz.openbmc_project.Control.ThermalMode";
1097constexpr const char* thermalModePath =
1098 "/xyz/openbmc_project/control/thermal_mode";
1099
1100bool getFanProfileInterface(
1101 sdbusplus::bus::bus& bus,
1102 boost::container::flat_map<
1103 std::string, std::variant<std::vector<std::string>, std::string>>& resp)
1104{
1105 auto call = bus.new_method_call(settingsBusName, thermalModePath, PROP_INTF,
1106 "GetAll");
1107 call.append(thermalModeInterface);
1108 try
1109 {
1110 auto data = bus.call(call);
1111 data.read(resp);
1112 }
1113 catch (sdbusplus::exception_t& e)
1114 {
1115 phosphor::logging::log<phosphor::logging::level::ERR>(
1116 "getFanProfileInterface: can't get thermal mode!",
1117 phosphor::logging::entry("ERR=%s", e.what()));
1118 return false;
1119 }
1120 return true;
1121}
1122
1123ipmi_ret_t ipmiOEMSetFanConfig(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
1124 ipmi_request_t request, ipmi_response_t response,
1125 ipmi_data_len_t dataLen, ipmi_context_t context)
1126{
1127
1128 if (*dataLen < 2 || *dataLen > 7)
1129 {
1130 phosphor::logging::log<phosphor::logging::level::ERR>(
1131 "ipmiOEMSetFanConfig: invalid input len!");
1132 *dataLen = 0;
1133 return IPMI_CC_REQ_DATA_LEN_INVALID;
1134 }
1135
1136 // todo: tell bios to only send first 2 bytes
1137
1138 SetFanConfigReq* req = reinterpret_cast<SetFanConfigReq*>(request);
1139 boost::container::flat_map<
1140 std::string, std::variant<std::vector<std::string>, std::string>>
1141 profileData;
1142 if (!getFanProfileInterface(dbus, profileData))
1143 {
1144 return IPMI_CC_UNSPECIFIED_ERROR;
1145 }
1146
1147 std::vector<std::string>* supported =
1148 std::get_if<std::vector<std::string>>(&profileData["Supported"]);
1149 if (supported == nullptr)
1150 {
1151 return IPMI_CC_INVALID_FIELD_REQUEST;
1152 }
1153 std::string mode;
1154 if (req->flags &
1155 (1 << static_cast<uint8_t>(setFanProfileFlags::setPerfAcousMode)))
1156 {
1157 bool performanceMode =
1158 (req->flags & (1 << static_cast<uint8_t>(
1159 setFanProfileFlags::performAcousSelect))) > 0;
1160
1161 if (performanceMode)
1162 {
1163
1164 if (std::find(supported->begin(), supported->end(),
1165 "Performance") != supported->end())
1166 {
1167 mode = "Performance";
1168 }
1169 }
1170 else
1171 {
1172
1173 if (std::find(supported->begin(), supported->end(), "Acoustic") !=
1174 supported->end())
1175 {
1176 mode = "Acoustic";
1177 }
1178 }
1179 if (mode.empty())
1180 {
1181 return IPMI_CC_INVALID_FIELD_REQUEST;
1182 }
1183 setDbusProperty(dbus, settingsBusName, thermalModePath,
1184 thermalModeInterface, "Current", mode);
1185 }
1186
1187 return IPMI_CC_OK;
1188}
1189
1190ipmi_ret_t ipmiOEMGetFanConfig(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
1191 ipmi_request_t request, ipmi_response_t response,
1192 ipmi_data_len_t dataLen, ipmi_context_t context)
1193{
1194
1195 if (*dataLen > 1)
1196 {
1197 phosphor::logging::log<phosphor::logging::level::ERR>(
1198 "ipmiOEMGetFanConfig: invalid input len!");
1199 *dataLen = 0;
1200 return IPMI_CC_REQ_DATA_LEN_INVALID;
1201 }
1202
1203 // todo: talk to bios about needing less information
1204
1205 GetFanConfigResp* resp = reinterpret_cast<GetFanConfigResp*>(response);
1206 *dataLen = sizeof(GetFanConfigResp);
1207
1208 boost::container::flat_map<
1209 std::string, std::variant<std::vector<std::string>, std::string>>
1210 profileData;
1211
1212 if (!getFanProfileInterface(dbus, profileData))
1213 {
1214 return IPMI_CC_UNSPECIFIED_ERROR;
1215 }
1216
1217 std::string* current = std::get_if<std::string>(&profileData["Current"]);
1218
1219 if (current == nullptr)
1220 {
1221 phosphor::logging::log<phosphor::logging::level::ERR>(
1222 "ipmiOEMGetFanConfig: can't get current mode!");
1223 return IPMI_CC_UNSPECIFIED_ERROR;
1224 }
1225 bool performance = (*current == "Performance");
1226
1227 if (performance)
1228 {
1229 resp->flags |= 1 << 2;
1230 }
1231
1232 return IPMI_CC_OK;
1233}
1234
James Feist5f957ca2019-03-14 15:33:55 -07001235constexpr const char* cfmLimitSettingPath =
1236 "/xyz/openbmc_project/control/cfm_limit";
1237constexpr const char* cfmLimitIface = "xyz.openbmc_project.Control.CFMLimit";
James Feistfaa4f222019-03-21 16:21:55 -07001238constexpr const size_t legacyExitAirSensorNumber = 0x2e;
James Feistacc8a4e2019-04-02 14:23:57 -07001239constexpr const char* pidConfigurationIface =
1240 "xyz.openbmc_project.Configuration.Pid";
James Feistfaa4f222019-03-21 16:21:55 -07001241
1242static std::string getExitAirConfigPath()
1243{
1244
1245 auto method =
1246 dbus.new_method_call("xyz.openbmc_project.ObjectMapper",
1247 "/xyz/openbmc_project/object_mapper",
1248 "xyz.openbmc_project.ObjectMapper", "GetSubTree");
1249
James Feistacc8a4e2019-04-02 14:23:57 -07001250 method.append("/", 0, std::array<const char*, 1>{pidConfigurationIface});
James Feistfaa4f222019-03-21 16:21:55 -07001251 std::string path;
1252 GetSubTreeType resp;
1253 try
1254 {
1255 auto reply = dbus.call(method);
1256 reply.read(resp);
1257 }
1258 catch (sdbusplus::exception_t&)
1259 {
1260 phosphor::logging::log<phosphor::logging::level::ERR>(
1261 "ipmiOEMGetFscParameter: mapper error");
1262 };
1263 auto config = std::find_if(resp.begin(), resp.end(), [](const auto& pair) {
1264 return pair.first.find("Exit_Air") != std::string::npos;
1265 });
1266 if (config != resp.end())
1267 {
1268 path = std::move(config->first);
1269 }
1270 return path;
1271}
James Feist5f957ca2019-03-14 15:33:55 -07001272
James Feistacc8a4e2019-04-02 14:23:57 -07001273// flat map to make alphabetical
1274static boost::container::flat_map<std::string, PropertyMap> getPidConfigs()
1275{
1276 boost::container::flat_map<std::string, PropertyMap> ret;
1277 auto method =
1278 dbus.new_method_call("xyz.openbmc_project.ObjectMapper",
1279 "/xyz/openbmc_project/object_mapper",
1280 "xyz.openbmc_project.ObjectMapper", "GetSubTree");
1281
1282 method.append("/", 0, std::array<const char*, 1>{pidConfigurationIface});
1283 GetSubTreeType resp;
1284
1285 try
1286 {
1287 auto reply = dbus.call(method);
1288 reply.read(resp);
1289 }
1290 catch (sdbusplus::exception_t&)
1291 {
1292 phosphor::logging::log<phosphor::logging::level::ERR>(
1293 "getFanConfigPaths: mapper error");
1294 };
1295 for (const auto& [path, objects] : resp)
1296 {
1297 if (objects.empty())
1298 {
1299 continue; // should be impossible
1300 }
Zhu, Yungebe560b02019-04-21 21:19:21 -04001301
1302 try
1303 {
1304 ret.emplace(path, getAllDbusProperties(dbus, objects[0].first, path,
1305 pidConfigurationIface));
1306 }
1307 catch (sdbusplus::exception_t& e)
1308 {
1309 phosphor::logging::log<phosphor::logging::level::ERR>(
1310 "getPidConfigs: can't get DbusProperties!",
1311 phosphor::logging::entry("ERR=%s", e.what()));
1312 }
James Feistacc8a4e2019-04-02 14:23:57 -07001313 }
1314 return ret;
1315}
1316
1317ipmi::RspType<uint8_t> ipmiOEMGetFanSpeedOffset(void)
1318{
1319 boost::container::flat_map<std::string, PropertyMap> data = getPidConfigs();
1320 if (data.empty())
1321 {
1322 return ipmi::responseResponseError();
1323 }
1324 uint8_t minOffset = std::numeric_limits<uint8_t>::max();
1325 for (const auto& [_, pid] : data)
1326 {
1327 auto findClass = pid.find("Class");
1328 if (findClass == pid.end())
1329 {
1330 phosphor::logging::log<phosphor::logging::level::ERR>(
1331 "ipmiOEMGetFscParameter: found illegal pid "
1332 "configurations");
1333 return ipmi::responseResponseError();
1334 }
1335 std::string type = std::get<std::string>(findClass->second);
1336 if (type == "fan")
1337 {
1338 auto findOutLimit = pid.find("OutLimitMin");
1339 if (findOutLimit == pid.end())
1340 {
1341 phosphor::logging::log<phosphor::logging::level::ERR>(
1342 "ipmiOEMGetFscParameter: found illegal pid "
1343 "configurations");
1344 return ipmi::responseResponseError();
1345 }
1346 // get the min out of all the offsets
1347 minOffset = std::min(
1348 minOffset,
1349 static_cast<uint8_t>(std::get<double>(findOutLimit->second)));
1350 }
1351 }
1352 if (minOffset == std::numeric_limits<uint8_t>::max())
1353 {
1354 phosphor::logging::log<phosphor::logging::level::ERR>(
1355 "ipmiOEMGetFscParameter: found no fan configurations!");
1356 return ipmi::responseResponseError();
1357 }
1358
1359 return ipmi::responseSuccess(minOffset);
1360}
1361
1362ipmi::RspType<> ipmiOEMSetFanSpeedOffset(uint8_t offset)
1363{
1364 boost::container::flat_map<std::string, PropertyMap> data = getPidConfigs();
1365 if (data.empty())
1366 {
1367
1368 phosphor::logging::log<phosphor::logging::level::ERR>(
1369 "ipmiOEMSetFanSpeedOffset: found no pid configurations!");
1370 return ipmi::responseResponseError();
1371 }
1372
1373 bool found = false;
1374 for (const auto& [path, pid] : data)
1375 {
1376 auto findClass = pid.find("Class");
1377 if (findClass == pid.end())
1378 {
1379
1380 phosphor::logging::log<phosphor::logging::level::ERR>(
1381 "ipmiOEMSetFanSpeedOffset: found illegal pid "
1382 "configurations");
1383 return ipmi::responseResponseError();
1384 }
1385 std::string type = std::get<std::string>(findClass->second);
1386 if (type == "fan")
1387 {
1388 auto findOutLimit = pid.find("OutLimitMin");
1389 if (findOutLimit == pid.end())
1390 {
1391
1392 phosphor::logging::log<phosphor::logging::level::ERR>(
1393 "ipmiOEMSetFanSpeedOffset: found illegal pid "
1394 "configurations");
1395 return ipmi::responseResponseError();
1396 }
1397 ipmi::setDbusProperty(dbus, "xyz.openbmc_project.EntityManager",
1398 path, pidConfigurationIface, "OutLimitMin",
1399 static_cast<double>(offset));
1400 found = true;
1401 }
1402 }
1403 if (!found)
1404 {
1405 phosphor::logging::log<phosphor::logging::level::ERR>(
1406 "ipmiOEMSetFanSpeedOffset: set no fan offsets");
1407 return ipmi::responseResponseError();
1408 }
1409
1410 return ipmi::responseSuccess();
1411}
1412
1413ipmi::RspType<> ipmiOEMSetFscParameter(uint8_t command, uint8_t param1,
1414 uint8_t param2)
James Feist5f957ca2019-03-14 15:33:55 -07001415{
1416 constexpr const size_t disableLimiting = 0x0;
1417
James Feistacc8a4e2019-04-02 14:23:57 -07001418 if (command == static_cast<uint8_t>(setFscParamFlags::tcontrol))
James Feist5f957ca2019-03-14 15:33:55 -07001419 {
James Feistacc8a4e2019-04-02 14:23:57 -07001420 if (param1 == legacyExitAirSensorNumber)
James Feistfaa4f222019-03-21 16:21:55 -07001421 {
James Feistfaa4f222019-03-21 16:21:55 -07001422 std::string path = getExitAirConfigPath();
1423 ipmi::setDbusProperty(dbus, "xyz.openbmc_project.EntityManager",
James Feistacc8a4e2019-04-02 14:23:57 -07001424 path, pidConfigurationIface, "SetPoint",
1425 static_cast<double>(param2));
1426 return ipmi::responseSuccess();
James Feistfaa4f222019-03-21 16:21:55 -07001427 }
1428 else
1429 {
James Feistacc8a4e2019-04-02 14:23:57 -07001430 return ipmi::responseParmOutOfRange();
James Feistfaa4f222019-03-21 16:21:55 -07001431 }
1432 }
James Feistacc8a4e2019-04-02 14:23:57 -07001433 else if (command == static_cast<uint8_t>(setFscParamFlags::cfm))
James Feist5f957ca2019-03-14 15:33:55 -07001434 {
James Feistacc8a4e2019-04-02 14:23:57 -07001435 uint16_t cfm = param1 | (static_cast<uint16_t>(param2) << 8);
James Feist5f957ca2019-03-14 15:33:55 -07001436
1437 // must be greater than 50 based on eps
1438 if (cfm < 50 && cfm != disableLimiting)
1439 {
James Feistacc8a4e2019-04-02 14:23:57 -07001440 return ipmi::responseParmOutOfRange();
James Feist5f957ca2019-03-14 15:33:55 -07001441 }
1442
1443 try
1444 {
1445 ipmi::setDbusProperty(dbus, settingsBusName, cfmLimitSettingPath,
1446 cfmLimitIface, "Limit",
1447 static_cast<double>(cfm));
1448 }
1449 catch (sdbusplus::exception_t& e)
1450 {
1451 phosphor::logging::log<phosphor::logging::level::ERR>(
1452 "ipmiOEMSetFscParameter: can't set cfm setting!",
1453 phosphor::logging::entry("ERR=%s", e.what()));
James Feistacc8a4e2019-04-02 14:23:57 -07001454 return ipmi::responseResponseError();
James Feist5f957ca2019-03-14 15:33:55 -07001455 }
James Feistacc8a4e2019-04-02 14:23:57 -07001456 return ipmi::responseSuccess();
1457 }
1458 else if (command == static_cast<uint8_t>(setFscParamFlags::maxPwm))
1459 {
1460 constexpr const size_t maxDomainCount = 8;
1461 uint8_t requestedDomainMask = param1;
1462 boost::container::flat_map data = getPidConfigs();
1463 if (data.empty())
1464 {
1465
1466 phosphor::logging::log<phosphor::logging::level::ERR>(
1467 "ipmiOEMSetFscParameter: found no pid configurations!");
1468 return ipmi::responseResponseError();
1469 }
1470 size_t count = 0;
1471 for (const auto& [path, pid] : data)
1472 {
1473 auto findClass = pid.find("Class");
1474 if (findClass == pid.end())
1475 {
1476
1477 phosphor::logging::log<phosphor::logging::level::ERR>(
1478 "ipmiOEMSetFscParameter: found illegal pid "
1479 "configurations");
1480 return ipmi::responseResponseError();
1481 }
1482 std::string type = std::get<std::string>(findClass->second);
1483 if (type == "fan")
1484 {
1485 if (requestedDomainMask & (1 << count))
1486 {
1487 ipmi::setDbusProperty(
1488 dbus, "xyz.openbmc_project.EntityManager", path,
1489 pidConfigurationIface, "OutLimitMax",
1490 static_cast<double>(param2));
1491 }
1492 count++;
1493 }
1494 }
1495 return ipmi::responseSuccess();
James Feist5f957ca2019-03-14 15:33:55 -07001496 }
1497 else
1498 {
1499 // todo other command parts possibly
1500 // tcontrol is handled in peci now
1501 // fan speed offset not implemented yet
1502 // domain pwm limit not implemented
James Feistacc8a4e2019-04-02 14:23:57 -07001503 return ipmi::responseParmOutOfRange();
James Feist5f957ca2019-03-14 15:33:55 -07001504 }
1505}
1506
James Feistacc8a4e2019-04-02 14:23:57 -07001507ipmi::RspType<
1508 std::variant<uint8_t, std::array<uint8_t, 2>, std::array<uint16_t, 2>>>
1509 ipmiOEMGetFscParameter(uint8_t command, std::optional<uint8_t> param)
James Feist5f957ca2019-03-14 15:33:55 -07001510{
James Feistfaa4f222019-03-21 16:21:55 -07001511 constexpr uint8_t legacyDefaultExitAirLimit = -128;
James Feist5f957ca2019-03-14 15:33:55 -07001512
James Feistacc8a4e2019-04-02 14:23:57 -07001513 if (command == static_cast<uint8_t>(setFscParamFlags::tcontrol))
James Feist5f957ca2019-03-14 15:33:55 -07001514 {
James Feistacc8a4e2019-04-02 14:23:57 -07001515 if (!param)
James Feistfaa4f222019-03-21 16:21:55 -07001516 {
James Feistacc8a4e2019-04-02 14:23:57 -07001517 return ipmi::responseReqDataLenInvalid();
James Feistfaa4f222019-03-21 16:21:55 -07001518 }
1519
James Feistacc8a4e2019-04-02 14:23:57 -07001520 if (*param != legacyExitAirSensorNumber)
James Feistfaa4f222019-03-21 16:21:55 -07001521 {
James Feistacc8a4e2019-04-02 14:23:57 -07001522 return ipmi::responseParmOutOfRange();
James Feistfaa4f222019-03-21 16:21:55 -07001523 }
1524 uint8_t setpoint = legacyDefaultExitAirLimit;
1525 std::string path = getExitAirConfigPath();
1526 if (path.size())
1527 {
James Feistacc8a4e2019-04-02 14:23:57 -07001528 Value val =
1529 ipmi::getDbusProperty(dbus, "xyz.openbmc_project.EntityManager",
1530 path, pidConfigurationIface, "SetPoint");
James Feistfaa4f222019-03-21 16:21:55 -07001531 setpoint = std::floor(std::get<double>(val) + 0.5);
1532 }
1533
1534 // old implementation used to return the "default" and current, we
1535 // don't make the default readily available so just make both the
1536 // same
James Feistfaa4f222019-03-21 16:21:55 -07001537
James Feistacc8a4e2019-04-02 14:23:57 -07001538 return ipmi::responseSuccess(
1539 std::array<uint8_t, 2>{setpoint, setpoint});
James Feistfaa4f222019-03-21 16:21:55 -07001540 }
James Feistacc8a4e2019-04-02 14:23:57 -07001541 else if (command == static_cast<uint8_t>(setFscParamFlags::maxPwm))
1542 {
1543 constexpr const size_t maxDomainCount = 8;
1544
1545 if (!param)
1546 {
1547 return ipmi::responseReqDataLenInvalid();
1548 }
1549 uint8_t requestedDomain = *param;
1550 if (requestedDomain >= maxDomainCount)
1551 {
1552 return ipmi::responseInvalidFieldRequest();
1553 }
1554
1555 boost::container::flat_map data = getPidConfigs();
1556 if (data.empty())
1557 {
1558 phosphor::logging::log<phosphor::logging::level::ERR>(
1559 "ipmiOEMGetFscParameter: found no pid configurations!");
1560 return ipmi::responseResponseError();
1561 }
1562 size_t count = 0;
1563 for (const auto& [_, pid] : data)
1564 {
1565 auto findClass = pid.find("Class");
1566 if (findClass == pid.end())
1567 {
1568 phosphor::logging::log<phosphor::logging::level::ERR>(
1569 "ipmiOEMGetFscParameter: found illegal pid "
1570 "configurations");
1571 return ipmi::responseResponseError();
1572 }
1573 std::string type = std::get<std::string>(findClass->second);
1574 if (type == "fan")
1575 {
1576 if (requestedDomain == count)
1577 {
1578 auto findOutLimit = pid.find("OutLimitMax");
1579 if (findOutLimit == pid.end())
1580 {
1581 phosphor::logging::log<phosphor::logging::level::ERR>(
1582 "ipmiOEMGetFscParameter: found illegal pid "
1583 "configurations");
1584 return ipmi::responseResponseError();
1585 }
1586
1587 return ipmi::responseSuccess(
1588 static_cast<uint8_t>(std::floor(
1589 std::get<double>(findOutLimit->second) + 0.5)));
1590 }
1591 else
1592 {
1593 count++;
1594 }
1595 }
1596 }
1597
1598 return ipmi::responseInvalidFieldRequest();
1599 }
1600 else if (command == static_cast<uint8_t>(setFscParamFlags::cfm))
James Feist5f957ca2019-03-14 15:33:55 -07001601 {
1602
1603 /*
1604 DataLen should be 1, but host is sending us an extra bit. As the
James Feistacc8a4e2019-04-02 14:23:57 -07001605 previous behavior didn't seem to prevent this, ignore the check for
1606 now.
James Feist5f957ca2019-03-14 15:33:55 -07001607
James Feistacc8a4e2019-04-02 14:23:57 -07001608 if (param)
James Feist5f957ca2019-03-14 15:33:55 -07001609 {
1610 phosphor::logging::log<phosphor::logging::level::ERR>(
1611 "ipmiOEMGetFscParameter: invalid input len!");
James Feist5f957ca2019-03-14 15:33:55 -07001612 return IPMI_CC_REQ_DATA_LEN_INVALID;
1613 }
1614 */
1615 Value cfmLimit;
1616 Value cfmMaximum;
1617 try
1618 {
1619 cfmLimit = ipmi::getDbusProperty(dbus, settingsBusName,
1620 cfmLimitSettingPath, cfmLimitIface,
1621 "Limit");
1622 cfmMaximum = ipmi::getDbusProperty(
1623 dbus, "xyz.openbmc_project.ExitAirTempSensor",
1624 "/xyz/openbmc_project/control/MaxCFM", cfmLimitIface, "Limit");
1625 }
1626 catch (sdbusplus::exception_t& e)
1627 {
1628 phosphor::logging::log<phosphor::logging::level::ERR>(
James Feistacc8a4e2019-04-02 14:23:57 -07001629 "ipmiOEMGetFscParameter: can't get cfm setting!",
James Feist5f957ca2019-03-14 15:33:55 -07001630 phosphor::logging::entry("ERR=%s", e.what()));
James Feistacc8a4e2019-04-02 14:23:57 -07001631 return ipmi::responseResponseError();
James Feist5f957ca2019-03-14 15:33:55 -07001632 }
1633
James Feistacc8a4e2019-04-02 14:23:57 -07001634 double cfmMax = std::get<double>(cfmMaximum);
1635 double cfmLim = std::get<double>(cfmLimit);
James Feist5f957ca2019-03-14 15:33:55 -07001636
James Feistacc8a4e2019-04-02 14:23:57 -07001637 cfmLim = std::floor(cfmLim + 0.5);
1638 cfmMax = std::floor(cfmMax + 0.5);
1639 uint16_t cfmLimResp = static_cast<uint16_t>(cfmLim);
1640 uint16_t cfmMaxResp = static_cast<uint16_t>(cfmMax);
James Feist5f957ca2019-03-14 15:33:55 -07001641
James Feistacc8a4e2019-04-02 14:23:57 -07001642 return ipmi::responseSuccess(
1643 std::array<uint16_t, 2>{cfmLimResp, cfmMaxResp});
James Feist5f957ca2019-03-14 15:33:55 -07001644 }
James Feistacc8a4e2019-04-02 14:23:57 -07001645
James Feist5f957ca2019-03-14 15:33:55 -07001646 else
1647 {
1648 // todo other command parts possibly
James Feist5f957ca2019-03-14 15:33:55 -07001649 // domain pwm limit not implemented
James Feistacc8a4e2019-04-02 14:23:57 -07001650 return ipmi::responseParmOutOfRange();
James Feist5f957ca2019-03-14 15:33:55 -07001651 }
1652}
1653
Zhu, Yungebe560b02019-04-21 21:19:21 -04001654ipmi::RspType<> ipmiOEMSetFaultIndication(uint8_t sourceId, uint8_t faultType,
1655 uint8_t faultState,
1656 uint8_t faultGroup,
1657 std::array<uint8_t, 8>& ledStateData)
1658{
1659 static constexpr const char* objpath = "/xyz/openbmc_project/EntityManager";
1660 static constexpr const char* intf = "xyz.openbmc_project.EntityManager";
1661 constexpr auto maxFaultType = static_cast<size_t>(RemoteFaultType::max);
1662 static const std::array<std::string, maxFaultType> faultNames = {
1663 "faultFan", "faultTemp", "faultPower",
1664 "faultDriveSlot", "faultSoftware", "faultMemory"};
1665 static constexpr const char* sysGpioPath = "/sys/class/gpio/gpio";
1666 static constexpr const char* postfixValue = "/value";
1667
1668 constexpr uint8_t maxFaultSource = 0x4;
1669 constexpr uint8_t skipLEDs = 0xFF;
1670 constexpr uint8_t pinSize = 64;
1671 constexpr uint8_t groupSize = 16;
1672
1673 std::vector<uint16_t> ledFaultPins(pinSize, 0xFFFF);
1674 uint64_t resFIndex = 0;
1675 std::string resFType;
1676 std::string service;
1677 ObjectValueTree valueTree;
1678
1679 // Validate the source, fault type
1680 if ((sourceId >= maxFaultSource) ||
1681 (faultType >= static_cast<int8_t>(RemoteFaultType::max)) ||
1682 (faultState >= static_cast<int8_t>(RemoteFaultState::maxFaultState)) ||
1683 (faultGroup >= static_cast<int8_t>(DimmFaultType::maxFaultGroup)))
1684 {
1685 return ipmi::responseParmOutOfRange();
1686 }
1687
1688 try
1689 {
1690 service = getService(dbus, intf, objpath);
1691 valueTree = getManagedObjects(dbus, service, "/");
1692 }
1693 catch (const std::exception& e)
1694 {
1695 phosphor::logging::log<phosphor::logging::level::ERR>(
1696 "No object implements interface",
1697 phosphor::logging::entry("SERVICE=%s", service.c_str()),
1698 phosphor::logging::entry("INTF=%s", intf));
1699 return ipmi::responseResponseError();
1700 }
1701
1702 if (valueTree.empty())
1703 {
1704 phosphor::logging::log<phosphor::logging::level::ERR>(
1705 "No object implements interface",
1706 phosphor::logging::entry("INTF=%s", intf));
1707 return ipmi::responseResponseError();
1708 }
1709
1710 for (const auto& item : valueTree)
1711 {
1712 // find LedFault configuration
1713 auto interface =
1714 item.second.find("xyz.openbmc_project.Configuration.LedFault");
1715 if (interface == item.second.end())
1716 {
1717 continue;
1718 }
1719
1720 // find matched fault type: faultMemmory / faultFan
1721 // find LedGpioPins/FaultIndex configuration
1722 auto propertyFaultType = interface->second.find("FaultType");
1723 auto propertyFIndex = interface->second.find("FaultIndex");
1724 auto ledIndex = interface->second.find("LedGpioPins");
1725
1726 if (propertyFaultType == interface->second.end() ||
1727 propertyFIndex == interface->second.end() ||
1728 ledIndex == interface->second.end())
1729 {
1730 continue;
1731 }
1732
1733 try
1734 {
1735 Value valIndex = propertyFIndex->second;
1736 resFIndex = std::get<uint64_t>(valIndex);
1737
1738 Value valFType = propertyFaultType->second;
1739 resFType = std::get<std::string>(valFType);
1740 }
1741 catch (const std::bad_variant_access& e)
1742 {
1743 phosphor::logging::log<phosphor::logging::level::ERR>(e.what());
1744 return ipmi::responseResponseError();
1745 }
1746 // find the matched requested fault type: faultMemmory or faultFan
1747 if (resFType != faultNames[faultType])
1748 {
1749 continue;
1750 }
1751
1752 // read LedGpioPins data
1753 std::vector<uint64_t> ledgpios;
1754 std::variant<std::vector<uint64_t>> message;
1755
1756 auto method = dbus.new_method_call(
1757 service.c_str(), (std::string(item.first)).c_str(),
1758 "org.freedesktop.DBus.Properties", "Get");
1759
1760 method.append("xyz.openbmc_project.Configuration.LedFault",
1761 "LedGpioPins");
1762
1763 try
1764 {
1765 auto reply = dbus.call(method);
1766 reply.read(message);
1767 ledgpios = std::get<std::vector<uint64_t>>(message);
1768 }
1769 catch (std::exception& e)
1770 {
1771 phosphor::logging::log<phosphor::logging::level::ERR>(e.what());
1772 return ipmi::responseResponseError();
1773 }
1774
1775 // Check the size to be sure it will never overflow on groupSize
1776 if (ledgpios.size() > groupSize)
1777 {
1778 phosphor::logging::log<phosphor::logging::level::ERR>(
1779 "Fault gpio Pins out of range!");
1780 return ipmi::responseParmOutOfRange();
1781 }
1782 // Store data, according to command data bit index order
1783 for (int i = 0; i < ledgpios.size(); i++)
1784 {
1785 ledFaultPins[i + groupSize * resFIndex] = ledgpios[i];
1786 }
1787 }
1788
1789 switch (RemoteFaultType(faultType))
1790 {
1791 case (RemoteFaultType::fan):
1792 case (RemoteFaultType::memory):
1793 {
1794 if (faultGroup == skipLEDs)
1795 {
1796 return ipmi::responseSuccess();
1797 }
1798
1799 uint64_t ledState = 0;
1800 // calculate led state bit filed count, each byte has 8bits
1801 // the maximum bits will be 8 * 8 bits
1802 constexpr uint8_t size = sizeof(ledStateData) * 8;
1803 for (int i = 0; i < sizeof(ledStateData); i++)
1804 {
1805 ledState = (uint64_t)(ledState << 8);
1806 ledState = (uint64_t)(ledState | (uint64_t)ledStateData[i]);
1807 }
1808
1809 std::bitset<size> ledStateBits(ledState);
1810 std::string gpioValue;
1811 for (int i = 0; i < size; i++)
1812 { // skip invalid value
1813 if (ledFaultPins[i] == 0xFFFF)
1814 {
1815 continue;
1816 }
1817
1818 std::string device = sysGpioPath +
1819 std::to_string(ledFaultPins[i]) +
1820 postfixValue;
1821 std::fstream gpioFile;
1822
1823 gpioFile.open(device, std::ios::out);
1824
1825 if (!gpioFile.good())
1826 {
1827 phosphor::logging::log<phosphor::logging::level::ERR>(
1828 "Not Find Led Gpio Device!",
1829 phosphor::logging::entry("DEVICE=%s", device.c_str()));
1830 return ipmi::responseResponseError();
1831 }
1832 gpioFile << std::to_string(
1833 static_cast<uint8_t>(ledStateBits[i]));
1834 gpioFile.close();
1835 }
1836 break;
1837 }
1838 default:
1839 {
1840 // now only support two fault types
1841 return ipmi::responseParmOutOfRange();
1842 }
1843 }
1844
1845 return ipmi::responseSuccess();
1846}
1847
Richard Marian Thomaiyarea537d52019-04-24 21:33:48 +05301848ipmi::RspType<uint8_t> ipmiOEMReadBoardProductId()
1849{
1850 uint8_t prodId = 0;
1851 try
1852 {
1853 const DbusObjectInfo& object = getDbusObject(
1854 dbus, "xyz.openbmc_project.Inventory.Item.Board",
1855 "/xyz/openbmc_project/inventory/system/board/", "Baseboard");
1856 const Value& propValue = getDbusProperty(
1857 dbus, object.second, object.first,
1858 "xyz.openbmc_project.Inventory.Item.Board", "ProductId");
1859 prodId = static_cast<uint8_t>(std::get<uint64_t>(propValue));
1860 }
1861 catch (std::exception& e)
1862 {
1863 phosphor::logging::log<phosphor::logging::level::ERR>(
1864 "ipmiOEMReadBoardProductId: Product ID read failed!",
1865 phosphor::logging::entry("ERR=%s", e.what()));
1866 }
1867 return ipmi::responseSuccess(prodId);
1868}
1869
Jason M. Bills64796042018-10-03 16:51:55 -07001870static void registerOEMFunctions(void)
Jia, Chunhuia835eaa2018-09-05 09:00:41 +08001871{
1872 phosphor::logging::log<phosphor::logging::level::INFO>(
1873 "Registering OEM commands");
Jason M. Bills64796042018-10-03 16:51:55 -07001874 ipmiPrintAndRegister(netfnIntcOEMGeneral, IPMI_CMD_WILDCARD, NULL,
1875 ipmiOEMWildcard,
1876 PRIVILEGE_USER); // wildcard default handler
1877 ipmiPrintAndRegister(netfunIntelAppOEM, IPMI_CMD_WILDCARD, NULL,
1878 ipmiOEMWildcard,
1879 PRIVILEGE_USER); // wildcard default handler
1880 ipmiPrintAndRegister(
1881 netfnIntcOEMGeneral,
1882 static_cast<ipmi_cmd_t>(
1883 IPMINetfnIntelOEMGeneralCmd::cmdGetChassisIdentifier),
1884 NULL, ipmiOEMGetChassisIdentifier,
1885 PRIVILEGE_USER); // get chassis identifier
1886 ipmiPrintAndRegister(
1887 netfnIntcOEMGeneral,
1888 static_cast<ipmi_cmd_t>(IPMINetfnIntelOEMGeneralCmd::cmdSetSystemGUID),
1889 NULL, ipmiOEMSetSystemGUID,
1890 PRIVILEGE_ADMIN); // set system guid
1891 ipmiPrintAndRegister(
1892 netfnIntcOEMGeneral,
1893 static_cast<ipmi_cmd_t>(IPMINetfnIntelOEMGeneralCmd::cmdSetBIOSID),
1894 NULL, ipmiOEMSetBIOSID, PRIVILEGE_ADMIN);
1895 ipmiPrintAndRegister(netfnIntcOEMGeneral,
1896 static_cast<ipmi_cmd_t>(
1897 IPMINetfnIntelOEMGeneralCmd::cmdGetOEMDeviceInfo),
1898 NULL, ipmiOEMGetDeviceInfo, PRIVILEGE_USER);
1899 ipmiPrintAndRegister(
1900 netfnIntcOEMGeneral,
1901 static_cast<ipmi_cmd_t>(
1902 IPMINetfnIntelOEMGeneralCmd::cmdGetAICSlotFRUIDSlotPosRecords),
1903 NULL, ipmiOEMGetAICFRU, PRIVILEGE_USER);
Jia, Chunhuicc49b542019-03-20 15:41:07 +08001904
1905 ipmi::registerHandler(
1906 ipmi::prioOpenBmcBase, ipmi::netFnOemOne,
1907 static_cast<ipmi::Cmd>(
1908 IPMINetfnIntelOEMGeneralCmd::cmdSendEmbeddedFWUpdStatus),
1909 ipmi::Privilege::Operator, ipmiOEMSendEmbeddedFwUpdStatus);
1910
Jason M. Bills64796042018-10-03 16:51:55 -07001911 ipmiPrintAndRegister(
1912 netfnIntcOEMGeneral,
1913 static_cast<ipmi_cmd_t>(
1914 IPMINetfnIntelOEMGeneralCmd::cmdSetPowerRestoreDelay),
1915 NULL, ipmiOEMSetPowerRestoreDelay, PRIVILEGE_OPERATOR);
1916 ipmiPrintAndRegister(
1917 netfnIntcOEMGeneral,
1918 static_cast<ipmi_cmd_t>(
1919 IPMINetfnIntelOEMGeneralCmd::cmdGetPowerRestoreDelay),
1920 NULL, ipmiOEMGetPowerRestoreDelay, PRIVILEGE_USER);
Suryakanth Sekard509eb92018-11-15 17:44:11 +05301921
1922 ipmi::registerHandler(
1923 ipmi::prioOpenBmcBase, ipmi::netFnOemOne,
1924 static_cast<ipmi::Cmd>(
1925 IPMINetfnIntelOEMGeneralCmd::cmdSetOEMUser2Activation),
1926 ipmi::Privilege::Callback, ipmiOEMSetUser2Activation);
1927
Richard Marian Thomaiyarfc5e9852019-04-14 15:06:27 +05301928 ipmi::registerHandler(
1929 ipmi::prioOpenBmcBase, ipmi::netFnOemOne,
1930 static_cast<ipmi::Cmd>(
1931 IPMINetfnIntelOEMGeneralCmd::cmdSetSpecialUserPassword),
1932 ipmi::Privilege::Callback, ipmiOEMSetSpecialUserPassword);
1933
Jason M. Bills64796042018-10-03 16:51:55 -07001934 ipmiPrintAndRegister(
1935 netfnIntcOEMGeneral,
1936 static_cast<ipmi_cmd_t>(
1937 IPMINetfnIntelOEMGeneralCmd::cmdGetProcessorErrConfig),
1938 NULL, ipmiOEMGetProcessorErrConfig, PRIVILEGE_USER);
1939 ipmiPrintAndRegister(
1940 netfnIntcOEMGeneral,
1941 static_cast<ipmi_cmd_t>(
1942 IPMINetfnIntelOEMGeneralCmd::cmdSetProcessorErrConfig),
1943 NULL, ipmiOEMSetProcessorErrConfig, PRIVILEGE_ADMIN);
Yong Li703922d2018-11-06 13:25:31 +08001944 ipmiPrintAndRegister(netfnIntcOEMGeneral,
1945 static_cast<ipmi_cmd_t>(
1946 IPMINetfnIntelOEMGeneralCmd::cmdSetShutdownPolicy),
1947 NULL, ipmiOEMSetShutdownPolicy, PRIVILEGE_ADMIN);
1948 ipmiPrintAndRegister(netfnIntcOEMGeneral,
1949 static_cast<ipmi_cmd_t>(
1950 IPMINetfnIntelOEMGeneralCmd::cmdGetShutdownPolicy),
1951 NULL, ipmiOEMGetShutdownPolicy, PRIVILEGE_ADMIN);
James Feist91244a62019-02-19 15:04:54 -08001952
1953 ipmiPrintAndRegister(
1954 netfnIntcOEMGeneral,
1955 static_cast<ipmi_cmd_t>(IPMINetfnIntelOEMGeneralCmd::cmdSetFanConfig),
1956 NULL, ipmiOEMSetFanConfig, PRIVILEGE_USER);
1957
1958 ipmiPrintAndRegister(
1959 netfnIntcOEMGeneral,
1960 static_cast<ipmi_cmd_t>(IPMINetfnIntelOEMGeneralCmd::cmdGetFanConfig),
1961 NULL, ipmiOEMGetFanConfig, PRIVILEGE_USER);
1962
James Feistacc8a4e2019-04-02 14:23:57 -07001963 ipmi::registerHandler(
1964 ipmi::prioOemBase, netfnIntcOEMGeneral,
1965 static_cast<ipmi::Cmd>(
1966 IPMINetfnIntelOEMGeneralCmd::cmdGetFanSpeedOffset),
1967 ipmi::Privilege::User, ipmiOEMGetFanSpeedOffset);
James Feist5f957ca2019-03-14 15:33:55 -07001968
James Feistacc8a4e2019-04-02 14:23:57 -07001969 ipmi::registerHandler(
1970 ipmi::prioOemBase, netfnIntcOEMGeneral,
1971 static_cast<ipmi::Cmd>(
1972 IPMINetfnIntelOEMGeneralCmd::cmdSetFanSpeedOffset),
1973 ipmi::Privilege::User, ipmiOEMSetFanSpeedOffset);
1974
1975 ipmi::registerHandler(
1976 ipmi::prioOemBase, netfnIntcOEMGeneral,
1977 static_cast<ipmi::Cmd>(IPMINetfnIntelOEMGeneralCmd::cmdSetFscParameter),
1978 ipmi::Privilege::User, ipmiOEMSetFscParameter);
1979
1980 ipmi::registerHandler(
1981 ipmi::prioOemBase, netfnIntcOEMGeneral,
1982 static_cast<ipmi::Cmd>(IPMINetfnIntelOEMGeneralCmd::cmdGetFscParameter),
1983 ipmi::Privilege::User, ipmiOEMGetFscParameter);
James Feist5f957ca2019-03-14 15:33:55 -07001984
Richard Marian Thomaiyarea537d52019-04-24 21:33:48 +05301985 ipmi::registerHandler(
1986 ipmi::prioOpenBmcBase, netfnIntcOEMGeneral,
1987 static_cast<ipmi::Cmd>(
1988 IPMINetfnIntelOEMGeneralCmd::cmdReadBaseBoardProductId),
1989 ipmi::Privilege::Admin, ipmiOEMReadBoardProductId);
1990
Kuiying Wang45f04982018-12-26 09:23:08 +08001991 ipmiPrintAndRegister(
1992 netfnIntcOEMGeneral,
1993 static_cast<ipmi_cmd_t>(IPMINetfnIntelOEMGeneralCmd::cmdGetLEDStatus),
1994 NULL, ipmiOEMGetLEDStatus, PRIVILEGE_ADMIN);
Yong Li23737fe2019-02-19 08:49:55 +08001995 ipmiPrintAndRegister(
1996 netfnIntcOEMPlatform,
1997 static_cast<ipmi_cmd_t>(
1998 IPMINetfnIntelOEMPlatformCmd::cmdCfgHostSerialPortSpeed),
1999 NULL, ipmiOEMCfgHostSerialPortSpeed, PRIVILEGE_ADMIN);
Zhu, Yungebe560b02019-04-21 21:19:21 -04002000 ipmi::registerHandler(
2001 ipmi::prioOemBase, netfnIntcOEMGeneral,
2002 static_cast<ipmi::Cmd>(
2003 IPMINetfnIntelOEMGeneralCmd::cmdSetFaultIndication),
2004 ipmi::Privilege::Operator, ipmiOEMSetFaultIndication);
Jia, Chunhuia835eaa2018-09-05 09:00:41 +08002005 return;
2006}
2007
2008} // namespace ipmi