blob: 1cb8791e6e5957d970ccc512db5d99f9dcf48d3c [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>
Jason M. Bills64796042018-10-03 16:51:55 -070026#include <commandutils.hpp>
Jia, Chunhuia835eaa2018-09-05 09:00:41 +080027#include <iostream>
Jia, Chunhuicc49b542019-03-20 15:41:07 +080028#include <ipmid/api.hpp>
Vernon Mauery5480ef62019-03-20 13:43:11 -070029#include <ipmid/utils.hpp>
Jia, Chunhuia835eaa2018-09-05 09:00:41 +080030#include <oemcommands.hpp>
Jia, Chunhuia835eaa2018-09-05 09:00:41 +080031#include <phosphor-logging/log.hpp>
32#include <sdbusplus/bus.hpp>
Suryakanth Sekard509eb92018-11-15 17:44:11 +053033#include <sdbusplus/message/types.hpp>
Jia, Chunhuia835eaa2018-09-05 09:00:41 +080034#include <string>
James Feist91244a62019-02-19 15:04:54 -080035#include <variant>
Jia, Chunhuia835eaa2018-09-05 09:00:41 +080036#include <vector>
Jia, Chunhuia835eaa2018-09-05 09:00:41 +080037
38namespace ipmi
39{
Jason M. Bills64796042018-10-03 16:51:55 -070040static void registerOEMFunctions() __attribute__((constructor));
Jason M. Bills6d9c83f2019-02-08 14:02:19 -080041sdbusplus::bus::bus dbus(ipmid_get_sd_bus_connection()); // from ipmid/api.h
Jason M. Bills64796042018-10-03 16:51:55 -070042static constexpr size_t maxFRUStringLength = 0x3F;
Jia, Chunhuia835eaa2018-09-05 09:00:41 +080043
Suryakanth Sekard509eb92018-11-15 17:44:11 +053044static constexpr auto ethernetIntf =
45 "xyz.openbmc_project.Network.EthernetInterface";
46static constexpr auto networkIPIntf = "xyz.openbmc_project.Network.IP";
47static constexpr auto networkService = "xyz.openbmc_project.Network";
48static constexpr auto networkRoot = "/xyz/openbmc_project/network";
49
Jia, Chunhuia835eaa2018-09-05 09:00:41 +080050// return code: 0 successful
51int8_t getChassisSerialNumber(sdbusplus::bus::bus& bus, std::string& serial)
52{
53 std::string objpath = "/xyz/openbmc_project/FruDevice";
54 std::string intf = "xyz.openbmc_project.FruDeviceManager";
55 std::string service = getService(bus, intf, objpath);
56 ObjectValueTree valueTree = getManagedObjects(bus, service, "/");
57 if (valueTree.empty())
58 {
59 phosphor::logging::log<phosphor::logging::level::ERR>(
60 "No object implements interface",
61 phosphor::logging::entry("INTF=%s", intf.c_str()));
62 return -1;
63 }
64
Jason M. Bills64796042018-10-03 16:51:55 -070065 for (const auto& item : valueTree)
Jia, Chunhuia835eaa2018-09-05 09:00:41 +080066 {
67 auto interface = item.second.find("xyz.openbmc_project.FruDevice");
68 if (interface == item.second.end())
69 {
70 continue;
71 }
72
73 auto property = interface->second.find("CHASSIS_SERIAL_NUMBER");
74 if (property == interface->second.end())
75 {
76 continue;
77 }
78
79 try
80 {
81 Value variant = property->second;
Jason M. Bills64796042018-10-03 16:51:55 -070082 std::string& result =
83 sdbusplus::message::variant_ns::get<std::string>(variant);
84 if (result.size() > maxFRUStringLength)
Jia, Chunhuia835eaa2018-09-05 09:00:41 +080085 {
86 phosphor::logging::log<phosphor::logging::level::ERR>(
87 "FRU serial number exceed maximum length");
Jia, Chunhuia835eaa2018-09-05 09:00:41 +080088 return -1;
89 }
Jason M. Bills64796042018-10-03 16:51:55 -070090 serial = result;
Jia, Chunhuia835eaa2018-09-05 09:00:41 +080091 return 0;
92 }
Jason M. Bills64796042018-10-03 16:51:55 -070093 catch (sdbusplus::message::variant_ns::bad_variant_access& e)
Jia, Chunhuia835eaa2018-09-05 09:00:41 +080094 {
Jason M. Bills64796042018-10-03 16:51:55 -070095 phosphor::logging::log<phosphor::logging::level::ERR>(e.what());
Jia, Chunhuia835eaa2018-09-05 09:00:41 +080096 return -1;
97 }
98 }
99 return -1;
100}
Jason M. Bills64796042018-10-03 16:51:55 -0700101
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800102ipmi_ret_t ipmiOEMWildcard(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
103 ipmi_request_t request, ipmi_response_t response,
Jason M. Bills64796042018-10-03 16:51:55 -0700104 ipmi_data_len_t dataLen, ipmi_context_t context)
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800105{
Jason M. Bills64796042018-10-03 16:51:55 -0700106 printCommand(+netfn, +cmd);
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800107 // Status code.
108 ipmi_ret_t rc = IPMI_CC_INVALID;
Jason M. Bills64796042018-10-03 16:51:55 -0700109 *dataLen = 0;
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800110 return rc;
111}
112
113// Returns the Chassis Identifier (serial #)
114ipmi_ret_t ipmiOEMGetChassisIdentifier(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
115 ipmi_request_t request,
116 ipmi_response_t response,
Jason M. Bills64796042018-10-03 16:51:55 -0700117 ipmi_data_len_t dataLen,
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800118 ipmi_context_t context)
119{
120 std::string serial;
Jason M. Bills64796042018-10-03 16:51:55 -0700121 if (*dataLen != 0) // invalid request if there are extra parameters
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800122 {
Jason M. Bills64796042018-10-03 16:51:55 -0700123 *dataLen = 0;
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800124 return IPMI_CC_REQ_DATA_LEN_INVALID;
125 }
Jason M. Bills64796042018-10-03 16:51:55 -0700126 if (getChassisSerialNumber(dbus, serial) == 0)
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800127 {
Jason M. Bills64796042018-10-03 16:51:55 -0700128 *dataLen = serial.size(); // length will never exceed response length
129 // as it is checked in getChassisSerialNumber
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800130 char* resp = static_cast<char*>(response);
Jason M. Bills64796042018-10-03 16:51:55 -0700131 serial.copy(resp, *dataLen);
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800132 return IPMI_CC_OK;
133 }
Jason M. Bills64796042018-10-03 16:51:55 -0700134 *dataLen = 0;
135 return IPMI_CC_RESPONSE_ERROR;
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800136}
137
138ipmi_ret_t ipmiOEMSetSystemGUID(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
139 ipmi_request_t request,
140 ipmi_response_t response,
Jason M. Bills64796042018-10-03 16:51:55 -0700141 ipmi_data_len_t dataLen, ipmi_context_t context)
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800142{
143 static constexpr size_t safeBufferLength = 50;
144 char buf[safeBufferLength] = {0};
145 GUIDData* Data = reinterpret_cast<GUIDData*>(request);
146
Jason M. Bills64796042018-10-03 16:51:55 -0700147 if (*dataLen != sizeof(GUIDData)) // 16bytes
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800148 {
Jason M. Bills64796042018-10-03 16:51:55 -0700149 *dataLen = 0;
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800150 return IPMI_CC_REQ_DATA_LEN_INVALID;
151 }
152
Jason M. Bills64796042018-10-03 16:51:55 -0700153 *dataLen = 0;
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800154
155 snprintf(
156 buf, safeBufferLength,
157 "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x",
158 Data->timeLow4, Data->timeLow3, Data->timeLow2, Data->timeLow1,
159 Data->timeMid2, Data->timeMid1, Data->timeHigh2, Data->timeHigh1,
160 Data->clock2, Data->clock1, Data->node6, Data->node5, Data->node4,
161 Data->node3, Data->node2, Data->node1);
162 // UUID is in RFC4122 format. Ex: 61a39523-78f2-11e5-9862-e6402cfc3223
163 std::string guid = buf;
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800164
165 std::string objpath = "/xyz/openbmc_project/control/host0/systemGUID";
166 std::string intf = "xyz.openbmc_project.Common.UUID";
Jason M. Bills64796042018-10-03 16:51:55 -0700167 std::string service = getService(dbus, intf, objpath);
168 setDbusProperty(dbus, service, objpath, intf, "UUID", guid);
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800169 return IPMI_CC_OK;
170}
171
172ipmi_ret_t ipmiOEMSetBIOSID(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
173 ipmi_request_t request, ipmi_response_t response,
174 ipmi_data_len_t dataLen, ipmi_context_t context)
175{
176 DeviceInfo* data = reinterpret_cast<DeviceInfo*>(request);
177
Jason M. Bills64796042018-10-03 16:51:55 -0700178 if ((*dataLen < 2) || (*dataLen != (1 + data->biosIDLength)))
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800179 {
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800180 *dataLen = 0;
181 return IPMI_CC_REQ_DATA_LEN_INVALID;
182 }
Jason M. Bills64796042018-10-03 16:51:55 -0700183 std::string idString((char*)data->biosId, data->biosIDLength);
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800184
Jason M. Bills64796042018-10-03 16:51:55 -0700185 std::string service = getService(dbus, biosIntf, biosObjPath);
186 setDbusProperty(dbus, service, biosObjPath, biosIntf, biosProp, idString);
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800187 uint8_t* bytesWritten = static_cast<uint8_t*>(response);
188 *bytesWritten =
Jason M. Bills64796042018-10-03 16:51:55 -0700189 data->biosIDLength; // how many bytes are written into storage
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800190 *dataLen = 1;
191 return IPMI_CC_OK;
192}
193
194ipmi_ret_t ipmiOEMGetDeviceInfo(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
195 ipmi_request_t request,
196 ipmi_response_t response,
197 ipmi_data_len_t dataLen, ipmi_context_t context)
198{
199 GetOemDeviceInfoReq* req = reinterpret_cast<GetOemDeviceInfoReq*>(request);
200 GetOemDeviceInfoRes* res = reinterpret_cast<GetOemDeviceInfoRes*>(response);
201
202 if (*dataLen == 0)
203 {
Jason M. Bills64796042018-10-03 16:51:55 -0700204 *dataLen = 0;
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800205 return IPMI_CC_REQ_DATA_LEN_INVALID;
206 }
207
208 size_t reqDataLen = *dataLen;
209 *dataLen = 0;
Jason M. Bills64796042018-10-03 16:51:55 -0700210 if (req->entityType > static_cast<uint8_t>(OEMDevEntityType::sdrVer))
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800211 {
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800212 return IPMI_CC_INVALID_FIELD_REQUEST;
213 }
214
215 // handle OEM command items
Jason M. Bills64796042018-10-03 16:51:55 -0700216 switch (OEMDevEntityType(req->entityType))
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800217 {
218 case OEMDevEntityType::biosId:
219 {
220 if (sizeof(GetOemDeviceInfoReq) != reqDataLen)
221 {
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800222 return IPMI_CC_REQ_DATA_LEN_INVALID;
223 }
224
Jason M. Bills64796042018-10-03 16:51:55 -0700225 std::string service = getService(dbus, biosIntf, biosObjPath);
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800226 try
227 {
Jason M. Bills64796042018-10-03 16:51:55 -0700228 Value variant = getDbusProperty(dbus, service, biosObjPath,
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800229 biosIntf, biosProp);
Jason M. Bills64796042018-10-03 16:51:55 -0700230 std::string& idString =
231 sdbusplus::message::variant_ns::get<std::string>(variant);
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800232 if (req->offset >= idString.size())
233 {
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800234 return IPMI_CC_PARM_OUT_OF_RANGE;
235 }
Jason M. Bills64796042018-10-03 16:51:55 -0700236 size_t length = 0;
237 if (req->countToRead > (idString.size() - req->offset))
238 {
239 length = idString.size() - req->offset;
240 }
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800241 else
242 {
Jason M. Bills64796042018-10-03 16:51:55 -0700243 length = req->countToRead;
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800244 }
Jason M. Bills64796042018-10-03 16:51:55 -0700245 std::copy(idString.begin() + req->offset, idString.end(),
246 res->data);
247 res->resDatalen = length;
248 *dataLen = res->resDatalen + 1;
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800249 }
Jason M. Bills64796042018-10-03 16:51:55 -0700250 catch (sdbusplus::message::variant_ns::bad_variant_access& e)
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800251 {
Jason M. Bills64796042018-10-03 16:51:55 -0700252 phosphor::logging::log<phosphor::logging::level::ERR>(e.what());
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800253 return IPMI_CC_UNSPECIFIED_ERROR;
254 }
255 }
256 break;
257
258 case OEMDevEntityType::devVer:
259 case OEMDevEntityType::sdrVer:
260 // TODO:
261 return IPMI_CC_ILLEGAL_COMMAND;
262 default:
263 return IPMI_CC_INVALID_FIELD_REQUEST;
264 }
265 return IPMI_CC_OK;
266}
267
268ipmi_ret_t ipmiOEMGetAICFRU(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
269 ipmi_request_t request, ipmi_response_t response,
270 ipmi_data_len_t dataLen, ipmi_context_t context)
271{
272 if (*dataLen != 0)
273 {
Jason M. Bills64796042018-10-03 16:51:55 -0700274 *dataLen = 0;
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800275 return IPMI_CC_REQ_DATA_LEN_INVALID;
276 }
277
278 *dataLen = 1;
279 uint8_t* res = reinterpret_cast<uint8_t*>(response);
280 // temporary fix. We don't support AIC FRU now. Just tell BIOS that no
281 // AIC is available so that BIOS will not timeout repeatly which leads to
282 // slow booting.
283 *res = 0; // Byte1=Count of SlotPosition/FruID records.
284 return IPMI_CC_OK;
285}
286
Jason M. Bills64796042018-10-03 16:51:55 -0700287ipmi_ret_t ipmiOEMGetPowerRestoreDelay(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
288 ipmi_request_t request,
289 ipmi_response_t response,
290 ipmi_data_len_t dataLen,
291 ipmi_context_t context)
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800292{
Jason M. Bills64796042018-10-03 16:51:55 -0700293 GetPowerRestoreDelayRes* resp =
294 reinterpret_cast<GetPowerRestoreDelayRes*>(response);
295
296 if (*dataLen != 0)
297 {
298 *dataLen = 0;
299 return IPMI_CC_REQ_DATA_LEN_INVALID;
300 }
301
302 std::string service =
303 getService(dbus, powerRestoreDelayIntf, powerRestoreDelayObjPath);
304 Value variant =
305 getDbusProperty(dbus, service, powerRestoreDelayObjPath,
306 powerRestoreDelayIntf, powerRestoreDelayProp);
307
308 uint16_t delay = sdbusplus::message::variant_ns::get<uint16_t>(variant);
309 resp->byteLSB = delay;
310 resp->byteMSB = delay >> 8;
311
312 *dataLen = sizeof(GetPowerRestoreDelayRes);
313
314 return IPMI_CC_OK;
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800315}
316
Jia, Chunhuicc49b542019-03-20 15:41:07 +0800317static uint8_t bcdToDec(uint8_t val)
318{
319 return ((val / 16 * 10) + (val % 16));
320}
321
322// Allows an update utility or system BIOS to send the status of an embedded
323// firmware update attempt to the BMC. After received, BMC will create a logging
324// record.
325ipmi::RspType<> ipmiOEMSendEmbeddedFwUpdStatus(uint8_t status, uint8_t target,
326 uint8_t majorRevision,
327 uint8_t minorRevision,
328 uint32_t auxInfo)
329{
330 std::string firmware;
Jason M. Billsdc249272019-04-03 09:58:40 -0700331 int instance = (target & targetInstanceMask) >> targetInstanceShift;
Jia, Chunhuicc49b542019-03-20 15:41:07 +0800332 target = (target & selEvtTargetMask) >> selEvtTargetShift;
333
334 /* make sure the status is 0, 1, or 2 as per the spec */
335 if (status > 2)
336 {
337 return ipmi::response(ipmi::ccInvalidFieldRequest);
338 }
Jason M. Billsdc249272019-04-03 09:58:40 -0700339 /* make sure the target is 0, 1, 2, or 4 as per the spec */
340 if (target > 4 || target == 3)
341 {
342 return ipmi::response(ipmi::ccInvalidFieldRequest);
343 }
Jia, Chunhuicc49b542019-03-20 15:41:07 +0800344 /*orignal OEM command is to record OEM SEL.
345 But openbmc does not support OEM SEL, so we redirect it to redfish event
346 logging. */
347 std::string buildInfo;
348 std::string action;
349 switch (FWUpdateTarget(target))
350 {
351 case FWUpdateTarget::targetBMC:
352 firmware = "BMC";
Jason M. Billsdc249272019-04-03 09:58:40 -0700353 buildInfo = "major: " + std::to_string(majorRevision) + " minor: " +
Jia, Chunhuicc49b542019-03-20 15:41:07 +0800354 std::to_string(bcdToDec(minorRevision)) + // BCD encoded
355 " BuildID: " + std::to_string(auxInfo);
356 buildInfo += std::to_string(auxInfo);
357 break;
358 case FWUpdateTarget::targetBIOS:
359 firmware = "BIOS";
360 buildInfo =
Jason M. Billsdc249272019-04-03 09:58:40 -0700361 "major: " +
Jia, Chunhuicc49b542019-03-20 15:41:07 +0800362 std::to_string(bcdToDec(majorRevision)) + // BCD encoded
363 " minor: " +
364 std::to_string(bcdToDec(minorRevision)) + // BCD encoded
365 " ReleaseNumber: " + // ASCII encoded
366 std::to_string(static_cast<uint8_t>(auxInfo >> 0) - '0') +
367 std::to_string(static_cast<uint8_t>(auxInfo >> 8) - '0') +
368 std::to_string(static_cast<uint8_t>(auxInfo >> 16) - '0') +
369 std::to_string(static_cast<uint8_t>(auxInfo >> 24) - '0');
370 break;
371 case FWUpdateTarget::targetME:
372 firmware = "ME";
373 buildInfo =
Jason M. Billsdc249272019-04-03 09:58:40 -0700374 "major: " + std::to_string(majorRevision) + " minor1: " +
Jia, Chunhuicc49b542019-03-20 15:41:07 +0800375 std::to_string(bcdToDec(minorRevision)) + // BCD encoded
376 " minor2: " +
377 std::to_string(bcdToDec(static_cast<uint8_t>(auxInfo >> 0))) +
378 " build1: " +
379 std::to_string(bcdToDec(static_cast<uint8_t>(auxInfo >> 8))) +
380 " build2: " +
381 std::to_string(bcdToDec(static_cast<uint8_t>(auxInfo >> 16)));
382 break;
383 case FWUpdateTarget::targetOEMEWS:
384 firmware = "EWS";
Jason M. Billsdc249272019-04-03 09:58:40 -0700385 buildInfo = "major: " + std::to_string(majorRevision) + " minor: " +
Jia, Chunhuicc49b542019-03-20 15:41:07 +0800386 std::to_string(bcdToDec(minorRevision)) + // BCD encoded
387 " BuildID: " + std::to_string(auxInfo);
388 break;
389 }
390
Jason M. Billsdc249272019-04-03 09:58:40 -0700391 static const std::string openBMCMessageRegistryVersion("0.1");
392 std::string redfishMsgID = "OpenBMC." + openBMCMessageRegistryVersion;
393
Jia, Chunhuicc49b542019-03-20 15:41:07 +0800394 switch (status)
395 {
396 case 0x0:
397 action = "update started";
Jason M. Billsdc249272019-04-03 09:58:40 -0700398 redfishMsgID += ".FirmwareUpdateStarted";
Jia, Chunhuicc49b542019-03-20 15:41:07 +0800399 break;
400 case 0x1:
401 action = "update completed successfully";
Jason M. Billsdc249272019-04-03 09:58:40 -0700402 redfishMsgID += ".FirmwareUpdateCompleted";
Jia, Chunhuicc49b542019-03-20 15:41:07 +0800403 break;
404 case 0x2:
405 action = "update failure";
Jason M. Billsdc249272019-04-03 09:58:40 -0700406 redfishMsgID += ".FirmwareUpdateFailed";
Jia, Chunhuicc49b542019-03-20 15:41:07 +0800407 break;
408 default:
409 action = "unknown";
410 break;
411 }
412
Jason M. Billsdc249272019-04-03 09:58:40 -0700413 std::string firmwareInstanceStr =
414 firmware + " instance: " + std::to_string(instance);
415 std::string message("[firmware update] " + firmwareInstanceStr +
416 " status: <" + action + "> " + buildInfo);
Jia, Chunhuicc49b542019-03-20 15:41:07 +0800417
418 sd_journal_send("MESSAGE=%s", message.c_str(), "PRIORITY=%i", LOG_INFO,
Jason M. Billsdc249272019-04-03 09:58:40 -0700419 "REDFISH_MESSAGE_ID=%s", redfishMsgID.c_str(),
420 "REDFISH_MESSAGE_ARGS=%s,%s", firmwareInstanceStr.c_str(),
421 buildInfo.c_str(), NULL);
Jia, Chunhuicc49b542019-03-20 15:41:07 +0800422 return ipmi::responseSuccess();
423}
424
Jason M. Bills64796042018-10-03 16:51:55 -0700425ipmi_ret_t ipmiOEMSetPowerRestoreDelay(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
426 ipmi_request_t request,
427 ipmi_response_t response,
428 ipmi_data_len_t dataLen,
429 ipmi_context_t context)
430{
431 SetPowerRestoreDelayReq* data =
432 reinterpret_cast<SetPowerRestoreDelayReq*>(request);
433 uint16_t delay = 0;
434
435 if (*dataLen != sizeof(SetPowerRestoreDelayReq))
436 {
437 *dataLen = 0;
438 return IPMI_CC_REQ_DATA_LEN_INVALID;
439 }
440 delay = data->byteMSB;
441 delay = (delay << 8) | data->byteLSB;
442 std::string service =
443 getService(dbus, powerRestoreDelayIntf, powerRestoreDelayObjPath);
444 setDbusProperty(dbus, service, powerRestoreDelayObjPath,
445 powerRestoreDelayIntf, powerRestoreDelayProp, delay);
446 *dataLen = 0;
447
448 return IPMI_CC_OK;
449}
450
451ipmi_ret_t ipmiOEMGetProcessorErrConfig(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
452 ipmi_request_t request,
453 ipmi_response_t response,
454 ipmi_data_len_t dataLen,
455 ipmi_context_t context)
456{
457 GetProcessorErrConfigRes* resp =
458 reinterpret_cast<GetProcessorErrConfigRes*>(response);
459
460 if (*dataLen != 0)
461 {
462 *dataLen = 0;
463 return IPMI_CC_REQ_DATA_LEN_INVALID;
464 }
465
466 std::string service =
467 getService(dbus, processorErrConfigIntf, processorErrConfigObjPath);
468 Value variant = getDbusProperty(dbus, service, processorErrConfigObjPath,
469 processorErrConfigIntf, "ResetCfg");
470 resp->resetCfg = sdbusplus::message::variant_ns::get<uint8_t>(variant);
471
472 std::vector<uint8_t> caterrStatus;
Kuiying Wangbc546672018-11-23 15:41:05 +0800473 sdbusplus::message::variant<std::vector<uint8_t>> message;
Jason M. Bills64796042018-10-03 16:51:55 -0700474
475 auto method =
476 dbus.new_method_call(service.c_str(), processorErrConfigObjPath,
477 "org.freedesktop.DBus.Properties", "Get");
478
479 method.append(processorErrConfigIntf, "CATERRStatus");
Kuiying Wangbc546672018-11-23 15:41:05 +0800480 auto reply = dbus.call(method);
Jason M. Bills64796042018-10-03 16:51:55 -0700481
482 try
483 {
Kuiying Wangbc546672018-11-23 15:41:05 +0800484 reply.read(message);
485 caterrStatus =
486 sdbusplus::message::variant_ns::get<std::vector<uint8_t>>(message);
Jason M. Bills64796042018-10-03 16:51:55 -0700487 }
488 catch (sdbusplus::exception_t&)
489 {
Kuiying Wangbc546672018-11-23 15:41:05 +0800490 phosphor::logging::log<phosphor::logging::level::ERR>(
Jason M. Bills64796042018-10-03 16:51:55 -0700491 "ipmiOEMGetProcessorErrConfig: error on dbus",
492 phosphor::logging::entry("PRORPERTY=CATERRStatus"),
493 phosphor::logging::entry("PATH=%s", processorErrConfigObjPath),
494 phosphor::logging::entry("INTERFACE=%s", processorErrConfigIntf));
495 return IPMI_CC_UNSPECIFIED_ERROR;
496 }
497
498 size_t len =
499 maxCPUNum <= caterrStatus.size() ? maxCPUNum : caterrStatus.size();
500 caterrStatus.resize(len);
501 std::copy(caterrStatus.begin(), caterrStatus.end(), resp->caterrStatus);
502 *dataLen = sizeof(GetProcessorErrConfigRes);
503
504 return IPMI_CC_OK;
505}
506
507ipmi_ret_t ipmiOEMSetProcessorErrConfig(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
508 ipmi_request_t request,
509 ipmi_response_t response,
510 ipmi_data_len_t dataLen,
511 ipmi_context_t context)
512{
513 SetProcessorErrConfigReq* req =
514 reinterpret_cast<SetProcessorErrConfigReq*>(request);
515
516 if (*dataLen != sizeof(SetProcessorErrConfigReq))
517 {
518 *dataLen = 0;
519 return IPMI_CC_REQ_DATA_LEN_INVALID;
520 }
521 std::string service =
522 getService(dbus, processorErrConfigIntf, processorErrConfigObjPath);
523 setDbusProperty(dbus, service, processorErrConfigObjPath,
524 processorErrConfigIntf, "ResetCfg", req->resetCfg);
525
526 setDbusProperty(dbus, service, processorErrConfigObjPath,
527 processorErrConfigIntf, "ResetErrorOccurrenceCounts",
528 req->resetErrorOccurrenceCounts);
529 *dataLen = 0;
530
531 return IPMI_CC_OK;
532}
533
Yong Li703922d2018-11-06 13:25:31 +0800534ipmi_ret_t ipmiOEMGetShutdownPolicy(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
535 ipmi_request_t request,
536 ipmi_response_t response,
537 ipmi_data_len_t dataLen,
538 ipmi_context_t context)
539{
540 GetOEMShutdownPolicyRes* resp =
541 reinterpret_cast<GetOEMShutdownPolicyRes*>(response);
542
543 if (*dataLen != 0)
544 {
545 phosphor::logging::log<phosphor::logging::level::ERR>(
Kuiying Wang45f04982018-12-26 09:23:08 +0800546 "oem_get_shutdown_policy: invalid input len!");
Yong Li703922d2018-11-06 13:25:31 +0800547 *dataLen = 0;
548 return IPMI_CC_REQ_DATA_LEN_INVALID;
549 }
550
551 *dataLen = 0;
552
553 try
554 {
555 std::string service =
556 getService(dbus, oemShutdownPolicyIntf, oemShutdownPolicyObjPath);
557 Value variant = getDbusProperty(dbus, service, oemShutdownPolicyObjPath,
558 oemShutdownPolicyIntf,
559 oemShutdownPolicyObjPathProp);
560 resp->policy = sdbusplus::message::variant_ns::get<uint8_t>(variant);
561 // TODO needs to check if it is multi-node products,
562 // policy is only supported on node 3/4
563 resp->policySupport = shutdownPolicySupported;
564 }
565 catch (sdbusplus::exception_t& e)
566 {
567 phosphor::logging::log<phosphor::logging::level::ERR>(e.description());
568 return IPMI_CC_UNSPECIFIED_ERROR;
569 }
570
571 *dataLen = sizeof(GetOEMShutdownPolicyRes);
572 return IPMI_CC_OK;
573}
574
575ipmi_ret_t ipmiOEMSetShutdownPolicy(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
576 ipmi_request_t request,
577 ipmi_response_t response,
578 ipmi_data_len_t dataLen,
579 ipmi_context_t context)
580{
581 uint8_t* req = reinterpret_cast<uint8_t*>(request);
582
583 // TODO needs to check if it is multi-node products,
584 // policy is only supported on node 3/4
585 if (*dataLen != 1)
586 {
587 phosphor::logging::log<phosphor::logging::level::ERR>(
588 "oem_set_shutdown_policy: invalid input len!");
589 *dataLen = 0;
590 return IPMI_CC_REQ_DATA_LEN_INVALID;
591 }
592
593 *dataLen = 0;
594 if ((*req != noShutdownOnOCOT) && (*req != shutdownOnOCOT))
595 {
596 phosphor::logging::log<phosphor::logging::level::ERR>(
597 "oem_set_shutdown_policy: invalid input!");
598 return IPMI_CC_INVALID_FIELD_REQUEST;
599 }
600
601 try
602 {
603 std::string service =
604 getService(dbus, oemShutdownPolicyIntf, oemShutdownPolicyObjPath);
605 setDbusProperty(dbus, service, oemShutdownPolicyObjPath,
606 oemShutdownPolicyIntf, oemShutdownPolicyObjPathProp,
607 *req);
608 }
609 catch (sdbusplus::exception_t& e)
610 {
611 phosphor::logging::log<phosphor::logging::level::ERR>(e.description());
612 return IPMI_CC_UNSPECIFIED_ERROR;
613 }
614
615 return IPMI_CC_OK;
616}
617
Suryakanth Sekard509eb92018-11-15 17:44:11 +0530618/** @brief implementation for check the DHCP or not in IPv4
619 * @param[in] Channel - Channel number
620 * @returns true or false.
621 */
622static bool isDHCPEnabled(uint8_t Channel)
623{
624 try
625 {
626 auto ethdevice = getChannelName(Channel);
627 if (ethdevice.empty())
628 {
629 return false;
630 }
631 auto ethIP = ethdevice + "/ipv4";
632 auto ethernetObj =
633 getDbusObject(dbus, networkIPIntf, networkRoot, ethIP);
634 auto value = getDbusProperty(dbus, networkService, ethernetObj.first,
635 networkIPIntf, "Origin");
636 if (sdbusplus::message::variant_ns::get<std::string>(value) ==
637 "xyz.openbmc_project.Network.IP.AddressOrigin.DHCP")
638 {
639 return true;
640 }
641 else
642 {
643 return false;
644 }
645 }
646 catch (sdbusplus::exception_t& e)
647 {
648 phosphor::logging::log<phosphor::logging::level::ERR>(e.description());
649 return true;
650 }
651}
652
653/** @brief implementes for check the DHCP or not in IPv6
654 * @param[in] Channel - Channel number
655 * @returns true or false.
656 */
657static bool isDHCPIPv6Enabled(uint8_t Channel)
658{
659
660 try
661 {
662 auto ethdevice = getChannelName(Channel);
663 if (ethdevice.empty())
664 {
665 return false;
666 }
667 auto ethIP = ethdevice + "/ipv6";
668 auto objectInfo =
669 getDbusObject(dbus, networkIPIntf, networkRoot, ethIP);
670 auto properties = getAllDbusProperties(dbus, objectInfo.second,
671 objectInfo.first, networkIPIntf);
672 if (sdbusplus::message::variant_ns::get<std::string>(
673 properties["Origin"]) ==
674 "xyz.openbmc_project.Network.IP.AddressOrigin.DHCP")
675 {
676 return true;
677 }
678 else
679 {
680 return false;
681 }
682 }
683 catch (sdbusplus::exception_t& e)
684 {
685 phosphor::logging::log<phosphor::logging::level::ERR>(e.description());
686 return true;
687 }
688}
689
690/** @brief implementes the creating of default new user
691 * @param[in] userName - new username in 16 bytes.
692 * @param[in] userPassword - new password in 20 bytes
693 * @returns ipmi completion code.
694 */
695ipmi::RspType<> ipmiOEMSetUser2Activation(
696 std::array<uint8_t, ipmi::ipmiMaxUserName>& userName,
697 std::array<uint8_t, ipmi::maxIpmi20PasswordSize>& userPassword)
698{
699 bool userState = false;
700 // Check for System Interface not exist and LAN should be static
701 for (uint8_t channel = 0; channel < maxIpmiChannels; channel++)
702 {
703 ChannelInfo chInfo;
704 try
705 {
706 getChannelInfo(channel, chInfo);
707 }
708 catch (sdbusplus::exception_t& e)
709 {
710 phosphor::logging::log<phosphor::logging::level::ERR>(
711 "ipmiOEMSetUser2Activation: Failed to get Channel Info",
712 phosphor::logging::entry("MSG: %s", e.description()));
713 return ipmi::response(ipmi::ccUnspecifiedError);
714 }
715 if (chInfo.mediumType ==
716 static_cast<uint8_t>(EChannelMediumType::systemInterface))
717 {
718 phosphor::logging::log<phosphor::logging::level::ERR>(
719 "ipmiOEMSetUser2Activation: system interface exist .");
720 return ipmi::response(ipmi::ccCommandNotAvailable);
721 }
722 else
723 {
724
725 if (chInfo.mediumType ==
726 static_cast<uint8_t>(EChannelMediumType::lan8032))
727 {
728 if (isDHCPIPv6Enabled(channel) || isDHCPEnabled(channel))
729 {
730 phosphor::logging::log<phosphor::logging::level::ERR>(
731 "ipmiOEMSetUser2Activation: DHCP enabled .");
732 return ipmi::response(ipmi::ccCommandNotAvailable);
733 }
734 }
735 }
736 }
737 uint8_t maxChUsers = 0, enabledUsers = 0, fixedUsers = 0;
738 if (ipmi::ccSuccess ==
739 ipmiUserGetAllCounts(maxChUsers, enabledUsers, fixedUsers))
740 {
741 if (enabledUsers > 1)
742 {
743 phosphor::logging::log<phosphor::logging::level::ERR>(
744 "ipmiOEMSetUser2Activation: more than one user is enabled.");
745 return ipmi::response(ipmi::ccCommandNotAvailable);
746 }
747 // Check the user 2 is enabled or not
748 ipmiUserCheckEnabled(ipmiDefaultUserId, userState);
749 if (userState == true)
750 {
751 phosphor::logging::log<phosphor::logging::level::ERR>(
752 "ipmiOEMSetUser2Activation: user 2 already enabled .");
753 return ipmi::response(ipmi::ccCommandNotAvailable);
754 }
755 }
756 else
757 {
758 return ipmi::response(ipmi::ccUnspecifiedError);
759 }
760
761#if BYTE_ORDER == LITTLE_ENDIAN
762 PrivAccess privAccess = {PRIVILEGE_ADMIN, true, true, true, 0};
763#endif
764#if BYTE_ORDER == BIG_ENDIAN
765 PrivAccess privAccess = {0, true, true, true, PRIVILEGE_ADMIN};
766#endif
767
768 if (ipmi::ccSuccess ==
769 ipmiUserSetUserName(ipmiDefaultUserId,
770 reinterpret_cast<const char*>(userName.data())))
771 {
772 if (ipmi::ccSuccess ==
773 ipmiUserSetUserPassword(
774 ipmiDefaultUserId,
775 reinterpret_cast<const char*>(userPassword.data())))
776 {
777 if (ipmi::ccSuccess ==
778 ipmiUserSetPrivilegeAccess(
779 ipmiDefaultUserId,
780 static_cast<uint8_t>(ipmi::EChannelID::chanLan1),
781 privAccess, true))
782 {
783 phosphor::logging::log<phosphor::logging::level::INFO>(
784 "ipmiOEMSetUser2Activation: user created successfully ");
785 return ipmi::responseSuccess();
786 }
787 }
788 // we need to delete the default user id which added in this command as
789 // password / priv setting is failed.
790 ipmiUserSetUserName(ipmiDefaultUserId, "");
791 phosphor::logging::log<phosphor::logging::level::ERR>(
792 "ipmiOEMSetUser2Activation: password / priv setting is failed.");
793 }
794 else
795 {
796 phosphor::logging::log<phosphor::logging::level::ERR>(
797 "ipmiOEMSetUser2Activation: Setting username failed.");
798 }
799
800 return ipmi::response(ipmi::ccCommandNotAvailable);
801}
802
Richard Marian Thomaiyarfc5e9852019-04-14 15:06:27 +0530803/** @brief implementes setting password for special user
804 * @param[in] specialUserIndex
805 * @param[in] userPassword - new password in 20 bytes
806 * @returns ipmi completion code.
807 */
808ipmi::RspType<> ipmiOEMSetSpecialUserPassword(ipmi::Context::ptr ctx,
809 uint8_t specialUserIndex,
810 std::vector<uint8_t> userPassword)
811{
812 ChannelInfo chInfo;
813 try
814 {
815 getChannelInfo(ctx->channel, chInfo);
816 }
817 catch (sdbusplus::exception_t& e)
818 {
819 phosphor::logging::log<phosphor::logging::level::ERR>(
820 "ipmiOEMSetSpecialUserPassword: Failed to get Channel Info",
821 phosphor::logging::entry("MSG: %s", e.description()));
822 return ipmi::responseUnspecifiedError();
823 }
824 if (chInfo.mediumType !=
825 static_cast<uint8_t>(EChannelMediumType::systemInterface))
826 {
827 phosphor::logging::log<phosphor::logging::level::ERR>(
828 "ipmiOEMSetSpecialUserPassword: Error - supported only in KCS "
829 "interface");
830 return ipmi::responseCommandNotAvailable();
831 }
832 if (specialUserIndex != 0)
833 {
834 phosphor::logging::log<phosphor::logging::level::ERR>(
835 "ipmiOEMSetSpecialUserPassword: Invalid user account");
836 return ipmi::responseParmOutOfRange();
837 }
838 constexpr uint8_t minPasswordSizeRequired = 6;
839 if (userPassword.size() < minPasswordSizeRequired ||
840 userPassword.size() > ipmi::maxIpmi20PasswordSize)
841 {
842 return ipmi::responseReqDataLenInvalid();
843 }
844 std::string passwd;
845 passwd.assign(reinterpret_cast<const char*>(userPassword.data()),
846 userPassword.size());
847 return ipmi::response(ipmiSetSpecialUserPassword("root", passwd));
848}
849
Kuiying Wang45f04982018-12-26 09:23:08 +0800850namespace ledAction
851{
852using namespace sdbusplus::xyz::openbmc_project::Led::server;
853std::map<Physical::Action, uint8_t> actionDbusToIpmi = {
854 {Physical::Action::Off, 0x00},
855 {Physical::Action::On, 0x10},
856 {Physical::Action::Blink, 0x01}};
857
858std::map<uint8_t, std::string> offsetObjPath = {
859 {2, statusAmberObjPath}, {4, statusGreenObjPath}, {6, identifyLEDObjPath}};
860
861} // namespace ledAction
862
863int8_t getLEDState(sdbusplus::bus::bus& bus, const std::string& intf,
864 const std::string& objPath, uint8_t& state)
865{
866 try
867 {
868 std::string service = getService(bus, intf, objPath);
869 Value stateValue =
870 getDbusProperty(bus, service, objPath, intf, "State");
871 std::string strState =
872 sdbusplus::message::variant_ns::get<std::string>(stateValue);
873 state = ledAction::actionDbusToIpmi.at(
874 sdbusplus::xyz::openbmc_project::Led::server::Physical::
875 convertActionFromString(strState));
876 }
877 catch (sdbusplus::exception::SdBusError& e)
878 {
879 phosphor::logging::log<phosphor::logging::level::ERR>(e.what());
880 return -1;
881 }
882 return 0;
883}
884
885ipmi_ret_t ipmiOEMGetLEDStatus(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
886 ipmi_request_t request, ipmi_response_t response,
887 ipmi_data_len_t dataLen, ipmi_context_t context)
888{
889 uint8_t* resp = reinterpret_cast<uint8_t*>(response);
890 // LED Status
891 //[1:0] = Reserved
892 //[3:2] = Status(Amber)
893 //[5:4] = Status(Green)
894 //[7:6] = System Identify
895 // Status definitions:
896 // 00b = Off
897 // 01b = Blink
898 // 10b = On
899 // 11b = invalid
900 if (*dataLen != 0)
901 {
902 phosphor::logging::log<phosphor::logging::level::ERR>(
903 "oem_get_led_status: invalid input len!");
904 *dataLen = 0;
905 return IPMI_CC_REQ_DATA_LEN_INVALID;
906 }
907
908 phosphor::logging::log<phosphor::logging::level::DEBUG>("GET led status");
909 *resp = 0;
910 *dataLen = 0;
911 for (auto it = ledAction::offsetObjPath.begin();
912 it != ledAction::offsetObjPath.end(); ++it)
913 {
914 uint8_t state = 0;
915 if (-1 == getLEDState(dbus, ledIntf, it->second, state))
916 {
917 phosphor::logging::log<phosphor::logging::level::ERR>(
918 "oem_get_led_status: fail to get ID LED status!");
919 return IPMI_CC_UNSPECIFIED_ERROR;
920 }
921 *resp |= state << it->first;
922 }
923
924 *dataLen = sizeof(*resp);
925 return IPMI_CC_OK;
926}
927
Yong Li23737fe2019-02-19 08:49:55 +0800928ipmi_ret_t ipmiOEMCfgHostSerialPortSpeed(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
929 ipmi_request_t request,
930 ipmi_response_t response,
931 ipmi_data_len_t dataLen,
932 ipmi_context_t context)
933{
934 CfgHostSerialReq* req = reinterpret_cast<CfgHostSerialReq*>(request);
935 uint8_t* resp = reinterpret_cast<uint8_t*>(response);
936
937 if (*dataLen == 0)
938 {
939 phosphor::logging::log<phosphor::logging::level::ERR>(
940 "CfgHostSerial: invalid input len!",
941 phosphor::logging::entry("LEN=%d", *dataLen));
942 return IPMI_CC_REQ_DATA_LEN_INVALID;
943 }
944
945 switch (req->command)
946 {
947 case getHostSerialCfgCmd:
948 {
949 if (*dataLen != 1)
950 {
951 phosphor::logging::log<phosphor::logging::level::ERR>(
952 "CfgHostSerial: invalid input len!");
953 *dataLen = 0;
954 return IPMI_CC_REQ_DATA_LEN_INVALID;
955 }
956
957 *dataLen = 0;
958
959 boost::process::ipstream is;
960 std::vector<std::string> data;
961 std::string line;
962 boost::process::child c1(fwGetEnvCmd, "-n", fwHostSerailCfgEnvName,
963 boost::process::std_out > is);
964
965 while (c1.running() && std::getline(is, line) && !line.empty())
966 {
967 data.push_back(line);
968 }
969
970 c1.wait();
971 if (c1.exit_code())
972 {
973 phosphor::logging::log<phosphor::logging::level::ERR>(
974 "CfgHostSerial:: error on execute",
975 phosphor::logging::entry("EXECUTE=%s", fwSetEnvCmd));
976 // Using the default value
977 *resp = 0;
978 }
979 else
980 {
981 if (data.size() != 1)
982 {
983 phosphor::logging::log<phosphor::logging::level::ERR>(
984 "CfgHostSerial:: error on read env");
985 return IPMI_CC_UNSPECIFIED_ERROR;
986 }
987 try
988 {
989 unsigned long tmp = std::stoul(data[0]);
990 if (tmp > std::numeric_limits<uint8_t>::max())
991 {
992 throw std::out_of_range("Out of range");
993 }
994 *resp = static_cast<uint8_t>(tmp);
995 }
996 catch (const std::invalid_argument& e)
997 {
998 phosphor::logging::log<phosphor::logging::level::ERR>(
999 "invalid config ",
1000 phosphor::logging::entry("ERR=%s", e.what()));
1001 return IPMI_CC_UNSPECIFIED_ERROR;
1002 }
1003 catch (const std::out_of_range& e)
1004 {
1005 phosphor::logging::log<phosphor::logging::level::ERR>(
1006 "out_of_range config ",
1007 phosphor::logging::entry("ERR=%s", e.what()));
1008 return IPMI_CC_UNSPECIFIED_ERROR;
1009 }
1010 }
1011
1012 *dataLen = 1;
1013 break;
1014 }
1015 case setHostSerialCfgCmd:
1016 {
1017 if (*dataLen != sizeof(CfgHostSerialReq))
1018 {
1019 phosphor::logging::log<phosphor::logging::level::ERR>(
1020 "CfgHostSerial: invalid input len!");
1021 *dataLen = 0;
1022 return IPMI_CC_REQ_DATA_LEN_INVALID;
1023 }
1024
1025 *dataLen = 0;
1026
1027 if (req->parameter > HostSerialCfgParamMax)
1028 {
1029 phosphor::logging::log<phosphor::logging::level::ERR>(
1030 "CfgHostSerial: invalid input!");
1031 return IPMI_CC_INVALID_FIELD_REQUEST;
1032 }
1033
1034 boost::process::child c1(fwSetEnvCmd, fwHostSerailCfgEnvName,
1035 std::to_string(req->parameter));
1036
1037 c1.wait();
1038 if (c1.exit_code())
1039 {
1040 phosphor::logging::log<phosphor::logging::level::ERR>(
1041 "CfgHostSerial:: error on execute",
1042 phosphor::logging::entry("EXECUTE=%s", fwGetEnvCmd));
1043 return IPMI_CC_UNSPECIFIED_ERROR;
1044 }
1045 break;
1046 }
1047 default:
1048 phosphor::logging::log<phosphor::logging::level::ERR>(
1049 "CfgHostSerial: invalid input!");
1050 *dataLen = 0;
1051 return IPMI_CC_INVALID_FIELD_REQUEST;
1052 }
1053
1054 return IPMI_CC_OK;
1055}
1056
James Feist91244a62019-02-19 15:04:54 -08001057constexpr const char* thermalModeInterface =
1058 "xyz.openbmc_project.Control.ThermalMode";
1059constexpr const char* thermalModePath =
1060 "/xyz/openbmc_project/control/thermal_mode";
1061
1062bool getFanProfileInterface(
1063 sdbusplus::bus::bus& bus,
1064 boost::container::flat_map<
1065 std::string, std::variant<std::vector<std::string>, std::string>>& resp)
1066{
1067 auto call = bus.new_method_call(settingsBusName, thermalModePath, PROP_INTF,
1068 "GetAll");
1069 call.append(thermalModeInterface);
1070 try
1071 {
1072 auto data = bus.call(call);
1073 data.read(resp);
1074 }
1075 catch (sdbusplus::exception_t& e)
1076 {
1077 phosphor::logging::log<phosphor::logging::level::ERR>(
1078 "getFanProfileInterface: can't get thermal mode!",
1079 phosphor::logging::entry("ERR=%s", e.what()));
1080 return false;
1081 }
1082 return true;
1083}
1084
1085ipmi_ret_t ipmiOEMSetFanConfig(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
1086 ipmi_request_t request, ipmi_response_t response,
1087 ipmi_data_len_t dataLen, ipmi_context_t context)
1088{
1089
1090 if (*dataLen < 2 || *dataLen > 7)
1091 {
1092 phosphor::logging::log<phosphor::logging::level::ERR>(
1093 "ipmiOEMSetFanConfig: invalid input len!");
1094 *dataLen = 0;
1095 return IPMI_CC_REQ_DATA_LEN_INVALID;
1096 }
1097
1098 // todo: tell bios to only send first 2 bytes
1099
1100 SetFanConfigReq* req = reinterpret_cast<SetFanConfigReq*>(request);
1101 boost::container::flat_map<
1102 std::string, std::variant<std::vector<std::string>, std::string>>
1103 profileData;
1104 if (!getFanProfileInterface(dbus, profileData))
1105 {
1106 return IPMI_CC_UNSPECIFIED_ERROR;
1107 }
1108
1109 std::vector<std::string>* supported =
1110 std::get_if<std::vector<std::string>>(&profileData["Supported"]);
1111 if (supported == nullptr)
1112 {
1113 return IPMI_CC_INVALID_FIELD_REQUEST;
1114 }
1115 std::string mode;
1116 if (req->flags &
1117 (1 << static_cast<uint8_t>(setFanProfileFlags::setPerfAcousMode)))
1118 {
1119 bool performanceMode =
1120 (req->flags & (1 << static_cast<uint8_t>(
1121 setFanProfileFlags::performAcousSelect))) > 0;
1122
1123 if (performanceMode)
1124 {
1125
1126 if (std::find(supported->begin(), supported->end(),
1127 "Performance") != supported->end())
1128 {
1129 mode = "Performance";
1130 }
1131 }
1132 else
1133 {
1134
1135 if (std::find(supported->begin(), supported->end(), "Acoustic") !=
1136 supported->end())
1137 {
1138 mode = "Acoustic";
1139 }
1140 }
1141 if (mode.empty())
1142 {
1143 return IPMI_CC_INVALID_FIELD_REQUEST;
1144 }
1145 setDbusProperty(dbus, settingsBusName, thermalModePath,
1146 thermalModeInterface, "Current", mode);
1147 }
1148
1149 return IPMI_CC_OK;
1150}
1151
1152ipmi_ret_t ipmiOEMGetFanConfig(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
1153 ipmi_request_t request, ipmi_response_t response,
1154 ipmi_data_len_t dataLen, ipmi_context_t context)
1155{
1156
1157 if (*dataLen > 1)
1158 {
1159 phosphor::logging::log<phosphor::logging::level::ERR>(
1160 "ipmiOEMGetFanConfig: invalid input len!");
1161 *dataLen = 0;
1162 return IPMI_CC_REQ_DATA_LEN_INVALID;
1163 }
1164
1165 // todo: talk to bios about needing less information
1166
1167 GetFanConfigResp* resp = reinterpret_cast<GetFanConfigResp*>(response);
1168 *dataLen = sizeof(GetFanConfigResp);
1169
1170 boost::container::flat_map<
1171 std::string, std::variant<std::vector<std::string>, std::string>>
1172 profileData;
1173
1174 if (!getFanProfileInterface(dbus, profileData))
1175 {
1176 return IPMI_CC_UNSPECIFIED_ERROR;
1177 }
1178
1179 std::string* current = std::get_if<std::string>(&profileData["Current"]);
1180
1181 if (current == nullptr)
1182 {
1183 phosphor::logging::log<phosphor::logging::level::ERR>(
1184 "ipmiOEMGetFanConfig: can't get current mode!");
1185 return IPMI_CC_UNSPECIFIED_ERROR;
1186 }
1187 bool performance = (*current == "Performance");
1188
1189 if (performance)
1190 {
1191 resp->flags |= 1 << 2;
1192 }
1193
1194 return IPMI_CC_OK;
1195}
1196
James Feist5f957ca2019-03-14 15:33:55 -07001197constexpr const char* cfmLimitSettingPath =
1198 "/xyz/openbmc_project/control/cfm_limit";
1199constexpr const char* cfmLimitIface = "xyz.openbmc_project.Control.CFMLimit";
James Feistfaa4f222019-03-21 16:21:55 -07001200constexpr const size_t legacyExitAirSensorNumber = 0x2e;
James Feistacc8a4e2019-04-02 14:23:57 -07001201constexpr const char* pidConfigurationIface =
1202 "xyz.openbmc_project.Configuration.Pid";
James Feistfaa4f222019-03-21 16:21:55 -07001203
1204static std::string getExitAirConfigPath()
1205{
1206
1207 auto method =
1208 dbus.new_method_call("xyz.openbmc_project.ObjectMapper",
1209 "/xyz/openbmc_project/object_mapper",
1210 "xyz.openbmc_project.ObjectMapper", "GetSubTree");
1211
James Feistacc8a4e2019-04-02 14:23:57 -07001212 method.append("/", 0, std::array<const char*, 1>{pidConfigurationIface});
James Feistfaa4f222019-03-21 16:21:55 -07001213 std::string path;
1214 GetSubTreeType resp;
1215 try
1216 {
1217 auto reply = dbus.call(method);
1218 reply.read(resp);
1219 }
1220 catch (sdbusplus::exception_t&)
1221 {
1222 phosphor::logging::log<phosphor::logging::level::ERR>(
1223 "ipmiOEMGetFscParameter: mapper error");
1224 };
1225 auto config = std::find_if(resp.begin(), resp.end(), [](const auto& pair) {
1226 return pair.first.find("Exit_Air") != std::string::npos;
1227 });
1228 if (config != resp.end())
1229 {
1230 path = std::move(config->first);
1231 }
1232 return path;
1233}
James Feist5f957ca2019-03-14 15:33:55 -07001234
James Feistacc8a4e2019-04-02 14:23:57 -07001235// flat map to make alphabetical
1236static boost::container::flat_map<std::string, PropertyMap> getPidConfigs()
1237{
1238 boost::container::flat_map<std::string, PropertyMap> ret;
1239 auto method =
1240 dbus.new_method_call("xyz.openbmc_project.ObjectMapper",
1241 "/xyz/openbmc_project/object_mapper",
1242 "xyz.openbmc_project.ObjectMapper", "GetSubTree");
1243
1244 method.append("/", 0, std::array<const char*, 1>{pidConfigurationIface});
1245 GetSubTreeType resp;
1246
1247 try
1248 {
1249 auto reply = dbus.call(method);
1250 reply.read(resp);
1251 }
1252 catch (sdbusplus::exception_t&)
1253 {
1254 phosphor::logging::log<phosphor::logging::level::ERR>(
1255 "getFanConfigPaths: mapper error");
1256 };
1257 for (const auto& [path, objects] : resp)
1258 {
1259 if (objects.empty())
1260 {
1261 continue; // should be impossible
1262 }
Zhu, Yungebe560b02019-04-21 21:19:21 -04001263
1264 try
1265 {
1266 ret.emplace(path, getAllDbusProperties(dbus, objects[0].first, path,
1267 pidConfigurationIface));
1268 }
1269 catch (sdbusplus::exception_t& e)
1270 {
1271 phosphor::logging::log<phosphor::logging::level::ERR>(
1272 "getPidConfigs: can't get DbusProperties!",
1273 phosphor::logging::entry("ERR=%s", e.what()));
1274 }
James Feistacc8a4e2019-04-02 14:23:57 -07001275 }
1276 return ret;
1277}
1278
1279ipmi::RspType<uint8_t> ipmiOEMGetFanSpeedOffset(void)
1280{
1281 boost::container::flat_map<std::string, PropertyMap> data = getPidConfigs();
1282 if (data.empty())
1283 {
1284 return ipmi::responseResponseError();
1285 }
1286 uint8_t minOffset = std::numeric_limits<uint8_t>::max();
1287 for (const auto& [_, pid] : data)
1288 {
1289 auto findClass = pid.find("Class");
1290 if (findClass == pid.end())
1291 {
1292 phosphor::logging::log<phosphor::logging::level::ERR>(
1293 "ipmiOEMGetFscParameter: found illegal pid "
1294 "configurations");
1295 return ipmi::responseResponseError();
1296 }
1297 std::string type = std::get<std::string>(findClass->second);
1298 if (type == "fan")
1299 {
1300 auto findOutLimit = pid.find("OutLimitMin");
1301 if (findOutLimit == pid.end())
1302 {
1303 phosphor::logging::log<phosphor::logging::level::ERR>(
1304 "ipmiOEMGetFscParameter: found illegal pid "
1305 "configurations");
1306 return ipmi::responseResponseError();
1307 }
1308 // get the min out of all the offsets
1309 minOffset = std::min(
1310 minOffset,
1311 static_cast<uint8_t>(std::get<double>(findOutLimit->second)));
1312 }
1313 }
1314 if (minOffset == std::numeric_limits<uint8_t>::max())
1315 {
1316 phosphor::logging::log<phosphor::logging::level::ERR>(
1317 "ipmiOEMGetFscParameter: found no fan configurations!");
1318 return ipmi::responseResponseError();
1319 }
1320
1321 return ipmi::responseSuccess(minOffset);
1322}
1323
1324ipmi::RspType<> ipmiOEMSetFanSpeedOffset(uint8_t offset)
1325{
1326 boost::container::flat_map<std::string, PropertyMap> data = getPidConfigs();
1327 if (data.empty())
1328 {
1329
1330 phosphor::logging::log<phosphor::logging::level::ERR>(
1331 "ipmiOEMSetFanSpeedOffset: found no pid configurations!");
1332 return ipmi::responseResponseError();
1333 }
1334
1335 bool found = false;
1336 for (const auto& [path, pid] : data)
1337 {
1338 auto findClass = pid.find("Class");
1339 if (findClass == pid.end())
1340 {
1341
1342 phosphor::logging::log<phosphor::logging::level::ERR>(
1343 "ipmiOEMSetFanSpeedOffset: found illegal pid "
1344 "configurations");
1345 return ipmi::responseResponseError();
1346 }
1347 std::string type = std::get<std::string>(findClass->second);
1348 if (type == "fan")
1349 {
1350 auto findOutLimit = pid.find("OutLimitMin");
1351 if (findOutLimit == pid.end())
1352 {
1353
1354 phosphor::logging::log<phosphor::logging::level::ERR>(
1355 "ipmiOEMSetFanSpeedOffset: found illegal pid "
1356 "configurations");
1357 return ipmi::responseResponseError();
1358 }
1359 ipmi::setDbusProperty(dbus, "xyz.openbmc_project.EntityManager",
1360 path, pidConfigurationIface, "OutLimitMin",
1361 static_cast<double>(offset));
1362 found = true;
1363 }
1364 }
1365 if (!found)
1366 {
1367 phosphor::logging::log<phosphor::logging::level::ERR>(
1368 "ipmiOEMSetFanSpeedOffset: set no fan offsets");
1369 return ipmi::responseResponseError();
1370 }
1371
1372 return ipmi::responseSuccess();
1373}
1374
1375ipmi::RspType<> ipmiOEMSetFscParameter(uint8_t command, uint8_t param1,
1376 uint8_t param2)
James Feist5f957ca2019-03-14 15:33:55 -07001377{
1378 constexpr const size_t disableLimiting = 0x0;
1379
James Feistacc8a4e2019-04-02 14:23:57 -07001380 if (command == static_cast<uint8_t>(setFscParamFlags::tcontrol))
James Feist5f957ca2019-03-14 15:33:55 -07001381 {
James Feistacc8a4e2019-04-02 14:23:57 -07001382 if (param1 == legacyExitAirSensorNumber)
James Feistfaa4f222019-03-21 16:21:55 -07001383 {
James Feistfaa4f222019-03-21 16:21:55 -07001384 std::string path = getExitAirConfigPath();
1385 ipmi::setDbusProperty(dbus, "xyz.openbmc_project.EntityManager",
James Feistacc8a4e2019-04-02 14:23:57 -07001386 path, pidConfigurationIface, "SetPoint",
1387 static_cast<double>(param2));
1388 return ipmi::responseSuccess();
James Feistfaa4f222019-03-21 16:21:55 -07001389 }
1390 else
1391 {
James Feistacc8a4e2019-04-02 14:23:57 -07001392 return ipmi::responseParmOutOfRange();
James Feistfaa4f222019-03-21 16:21:55 -07001393 }
1394 }
James Feistacc8a4e2019-04-02 14:23:57 -07001395 else if (command == static_cast<uint8_t>(setFscParamFlags::cfm))
James Feist5f957ca2019-03-14 15:33:55 -07001396 {
James Feistacc8a4e2019-04-02 14:23:57 -07001397 uint16_t cfm = param1 | (static_cast<uint16_t>(param2) << 8);
James Feist5f957ca2019-03-14 15:33:55 -07001398
1399 // must be greater than 50 based on eps
1400 if (cfm < 50 && cfm != disableLimiting)
1401 {
James Feistacc8a4e2019-04-02 14:23:57 -07001402 return ipmi::responseParmOutOfRange();
James Feist5f957ca2019-03-14 15:33:55 -07001403 }
1404
1405 try
1406 {
1407 ipmi::setDbusProperty(dbus, settingsBusName, cfmLimitSettingPath,
1408 cfmLimitIface, "Limit",
1409 static_cast<double>(cfm));
1410 }
1411 catch (sdbusplus::exception_t& e)
1412 {
1413 phosphor::logging::log<phosphor::logging::level::ERR>(
1414 "ipmiOEMSetFscParameter: can't set cfm setting!",
1415 phosphor::logging::entry("ERR=%s", e.what()));
James Feistacc8a4e2019-04-02 14:23:57 -07001416 return ipmi::responseResponseError();
James Feist5f957ca2019-03-14 15:33:55 -07001417 }
James Feistacc8a4e2019-04-02 14:23:57 -07001418 return ipmi::responseSuccess();
1419 }
1420 else if (command == static_cast<uint8_t>(setFscParamFlags::maxPwm))
1421 {
1422 constexpr const size_t maxDomainCount = 8;
1423 uint8_t requestedDomainMask = param1;
1424 boost::container::flat_map data = getPidConfigs();
1425 if (data.empty())
1426 {
1427
1428 phosphor::logging::log<phosphor::logging::level::ERR>(
1429 "ipmiOEMSetFscParameter: found no pid configurations!");
1430 return ipmi::responseResponseError();
1431 }
1432 size_t count = 0;
1433 for (const auto& [path, pid] : data)
1434 {
1435 auto findClass = pid.find("Class");
1436 if (findClass == pid.end())
1437 {
1438
1439 phosphor::logging::log<phosphor::logging::level::ERR>(
1440 "ipmiOEMSetFscParameter: found illegal pid "
1441 "configurations");
1442 return ipmi::responseResponseError();
1443 }
1444 std::string type = std::get<std::string>(findClass->second);
1445 if (type == "fan")
1446 {
1447 if (requestedDomainMask & (1 << count))
1448 {
1449 ipmi::setDbusProperty(
1450 dbus, "xyz.openbmc_project.EntityManager", path,
1451 pidConfigurationIface, "OutLimitMax",
1452 static_cast<double>(param2));
1453 }
1454 count++;
1455 }
1456 }
1457 return ipmi::responseSuccess();
James Feist5f957ca2019-03-14 15:33:55 -07001458 }
1459 else
1460 {
1461 // todo other command parts possibly
1462 // tcontrol is handled in peci now
1463 // fan speed offset not implemented yet
1464 // domain pwm limit not implemented
James Feistacc8a4e2019-04-02 14:23:57 -07001465 return ipmi::responseParmOutOfRange();
James Feist5f957ca2019-03-14 15:33:55 -07001466 }
1467}
1468
James Feistacc8a4e2019-04-02 14:23:57 -07001469ipmi::RspType<
1470 std::variant<uint8_t, std::array<uint8_t, 2>, std::array<uint16_t, 2>>>
1471 ipmiOEMGetFscParameter(uint8_t command, std::optional<uint8_t> param)
James Feist5f957ca2019-03-14 15:33:55 -07001472{
James Feistfaa4f222019-03-21 16:21:55 -07001473 constexpr uint8_t legacyDefaultExitAirLimit = -128;
James Feist5f957ca2019-03-14 15:33:55 -07001474
James Feistacc8a4e2019-04-02 14:23:57 -07001475 if (command == static_cast<uint8_t>(setFscParamFlags::tcontrol))
James Feist5f957ca2019-03-14 15:33:55 -07001476 {
James Feistacc8a4e2019-04-02 14:23:57 -07001477 if (!param)
James Feistfaa4f222019-03-21 16:21:55 -07001478 {
James Feistacc8a4e2019-04-02 14:23:57 -07001479 return ipmi::responseReqDataLenInvalid();
James Feistfaa4f222019-03-21 16:21:55 -07001480 }
1481
James Feistacc8a4e2019-04-02 14:23:57 -07001482 if (*param != legacyExitAirSensorNumber)
James Feistfaa4f222019-03-21 16:21:55 -07001483 {
James Feistacc8a4e2019-04-02 14:23:57 -07001484 return ipmi::responseParmOutOfRange();
James Feistfaa4f222019-03-21 16:21:55 -07001485 }
1486 uint8_t setpoint = legacyDefaultExitAirLimit;
1487 std::string path = getExitAirConfigPath();
1488 if (path.size())
1489 {
James Feistacc8a4e2019-04-02 14:23:57 -07001490 Value val =
1491 ipmi::getDbusProperty(dbus, "xyz.openbmc_project.EntityManager",
1492 path, pidConfigurationIface, "SetPoint");
James Feistfaa4f222019-03-21 16:21:55 -07001493 setpoint = std::floor(std::get<double>(val) + 0.5);
1494 }
1495
1496 // old implementation used to return the "default" and current, we
1497 // don't make the default readily available so just make both the
1498 // same
James Feistfaa4f222019-03-21 16:21:55 -07001499
James Feistacc8a4e2019-04-02 14:23:57 -07001500 return ipmi::responseSuccess(
1501 std::array<uint8_t, 2>{setpoint, setpoint});
James Feistfaa4f222019-03-21 16:21:55 -07001502 }
James Feistacc8a4e2019-04-02 14:23:57 -07001503 else if (command == static_cast<uint8_t>(setFscParamFlags::maxPwm))
1504 {
1505 constexpr const size_t maxDomainCount = 8;
1506
1507 if (!param)
1508 {
1509 return ipmi::responseReqDataLenInvalid();
1510 }
1511 uint8_t requestedDomain = *param;
1512 if (requestedDomain >= maxDomainCount)
1513 {
1514 return ipmi::responseInvalidFieldRequest();
1515 }
1516
1517 boost::container::flat_map data = getPidConfigs();
1518 if (data.empty())
1519 {
1520 phosphor::logging::log<phosphor::logging::level::ERR>(
1521 "ipmiOEMGetFscParameter: found no pid configurations!");
1522 return ipmi::responseResponseError();
1523 }
1524 size_t count = 0;
1525 for (const auto& [_, pid] : data)
1526 {
1527 auto findClass = pid.find("Class");
1528 if (findClass == pid.end())
1529 {
1530 phosphor::logging::log<phosphor::logging::level::ERR>(
1531 "ipmiOEMGetFscParameter: found illegal pid "
1532 "configurations");
1533 return ipmi::responseResponseError();
1534 }
1535 std::string type = std::get<std::string>(findClass->second);
1536 if (type == "fan")
1537 {
1538 if (requestedDomain == count)
1539 {
1540 auto findOutLimit = pid.find("OutLimitMax");
1541 if (findOutLimit == pid.end())
1542 {
1543 phosphor::logging::log<phosphor::logging::level::ERR>(
1544 "ipmiOEMGetFscParameter: found illegal pid "
1545 "configurations");
1546 return ipmi::responseResponseError();
1547 }
1548
1549 return ipmi::responseSuccess(
1550 static_cast<uint8_t>(std::floor(
1551 std::get<double>(findOutLimit->second) + 0.5)));
1552 }
1553 else
1554 {
1555 count++;
1556 }
1557 }
1558 }
1559
1560 return ipmi::responseInvalidFieldRequest();
1561 }
1562 else if (command == static_cast<uint8_t>(setFscParamFlags::cfm))
James Feist5f957ca2019-03-14 15:33:55 -07001563 {
1564
1565 /*
1566 DataLen should be 1, but host is sending us an extra bit. As the
James Feistacc8a4e2019-04-02 14:23:57 -07001567 previous behavior didn't seem to prevent this, ignore the check for
1568 now.
James Feist5f957ca2019-03-14 15:33:55 -07001569
James Feistacc8a4e2019-04-02 14:23:57 -07001570 if (param)
James Feist5f957ca2019-03-14 15:33:55 -07001571 {
1572 phosphor::logging::log<phosphor::logging::level::ERR>(
1573 "ipmiOEMGetFscParameter: invalid input len!");
James Feist5f957ca2019-03-14 15:33:55 -07001574 return IPMI_CC_REQ_DATA_LEN_INVALID;
1575 }
1576 */
1577 Value cfmLimit;
1578 Value cfmMaximum;
1579 try
1580 {
1581 cfmLimit = ipmi::getDbusProperty(dbus, settingsBusName,
1582 cfmLimitSettingPath, cfmLimitIface,
1583 "Limit");
1584 cfmMaximum = ipmi::getDbusProperty(
1585 dbus, "xyz.openbmc_project.ExitAirTempSensor",
1586 "/xyz/openbmc_project/control/MaxCFM", cfmLimitIface, "Limit");
1587 }
1588 catch (sdbusplus::exception_t& e)
1589 {
1590 phosphor::logging::log<phosphor::logging::level::ERR>(
James Feistacc8a4e2019-04-02 14:23:57 -07001591 "ipmiOEMGetFscParameter: can't get cfm setting!",
James Feist5f957ca2019-03-14 15:33:55 -07001592 phosphor::logging::entry("ERR=%s", e.what()));
James Feistacc8a4e2019-04-02 14:23:57 -07001593 return ipmi::responseResponseError();
James Feist5f957ca2019-03-14 15:33:55 -07001594 }
1595
James Feistacc8a4e2019-04-02 14:23:57 -07001596 double cfmMax = std::get<double>(cfmMaximum);
1597 double cfmLim = std::get<double>(cfmLimit);
James Feist5f957ca2019-03-14 15:33:55 -07001598
James Feistacc8a4e2019-04-02 14:23:57 -07001599 cfmLim = std::floor(cfmLim + 0.5);
1600 cfmMax = std::floor(cfmMax + 0.5);
1601 uint16_t cfmLimResp = static_cast<uint16_t>(cfmLim);
1602 uint16_t cfmMaxResp = static_cast<uint16_t>(cfmMax);
James Feist5f957ca2019-03-14 15:33:55 -07001603
James Feistacc8a4e2019-04-02 14:23:57 -07001604 return ipmi::responseSuccess(
1605 std::array<uint16_t, 2>{cfmLimResp, cfmMaxResp});
James Feist5f957ca2019-03-14 15:33:55 -07001606 }
James Feistacc8a4e2019-04-02 14:23:57 -07001607
James Feist5f957ca2019-03-14 15:33:55 -07001608 else
1609 {
1610 // todo other command parts possibly
James Feist5f957ca2019-03-14 15:33:55 -07001611 // domain pwm limit not implemented
James Feistacc8a4e2019-04-02 14:23:57 -07001612 return ipmi::responseParmOutOfRange();
James Feist5f957ca2019-03-14 15:33:55 -07001613 }
1614}
1615
Zhu, Yungebe560b02019-04-21 21:19:21 -04001616ipmi::RspType<> ipmiOEMSetFaultIndication(uint8_t sourceId, uint8_t faultType,
1617 uint8_t faultState,
1618 uint8_t faultGroup,
1619 std::array<uint8_t, 8>& ledStateData)
1620{
1621 static constexpr const char* objpath = "/xyz/openbmc_project/EntityManager";
1622 static constexpr const char* intf = "xyz.openbmc_project.EntityManager";
1623 constexpr auto maxFaultType = static_cast<size_t>(RemoteFaultType::max);
1624 static const std::array<std::string, maxFaultType> faultNames = {
1625 "faultFan", "faultTemp", "faultPower",
1626 "faultDriveSlot", "faultSoftware", "faultMemory"};
1627 static constexpr const char* sysGpioPath = "/sys/class/gpio/gpio";
1628 static constexpr const char* postfixValue = "/value";
1629
1630 constexpr uint8_t maxFaultSource = 0x4;
1631 constexpr uint8_t skipLEDs = 0xFF;
1632 constexpr uint8_t pinSize = 64;
1633 constexpr uint8_t groupSize = 16;
1634
1635 std::vector<uint16_t> ledFaultPins(pinSize, 0xFFFF);
1636 uint64_t resFIndex = 0;
1637 std::string resFType;
1638 std::string service;
1639 ObjectValueTree valueTree;
1640
1641 // Validate the source, fault type
1642 if ((sourceId >= maxFaultSource) ||
1643 (faultType >= static_cast<int8_t>(RemoteFaultType::max)) ||
1644 (faultState >= static_cast<int8_t>(RemoteFaultState::maxFaultState)) ||
1645 (faultGroup >= static_cast<int8_t>(DimmFaultType::maxFaultGroup)))
1646 {
1647 return ipmi::responseParmOutOfRange();
1648 }
1649
1650 try
1651 {
1652 service = getService(dbus, intf, objpath);
1653 valueTree = getManagedObjects(dbus, service, "/");
1654 }
1655 catch (const std::exception& e)
1656 {
1657 phosphor::logging::log<phosphor::logging::level::ERR>(
1658 "No object implements interface",
1659 phosphor::logging::entry("SERVICE=%s", service.c_str()),
1660 phosphor::logging::entry("INTF=%s", intf));
1661 return ipmi::responseResponseError();
1662 }
1663
1664 if (valueTree.empty())
1665 {
1666 phosphor::logging::log<phosphor::logging::level::ERR>(
1667 "No object implements interface",
1668 phosphor::logging::entry("INTF=%s", intf));
1669 return ipmi::responseResponseError();
1670 }
1671
1672 for (const auto& item : valueTree)
1673 {
1674 // find LedFault configuration
1675 auto interface =
1676 item.second.find("xyz.openbmc_project.Configuration.LedFault");
1677 if (interface == item.second.end())
1678 {
1679 continue;
1680 }
1681
1682 // find matched fault type: faultMemmory / faultFan
1683 // find LedGpioPins/FaultIndex configuration
1684 auto propertyFaultType = interface->second.find("FaultType");
1685 auto propertyFIndex = interface->second.find("FaultIndex");
1686 auto ledIndex = interface->second.find("LedGpioPins");
1687
1688 if (propertyFaultType == interface->second.end() ||
1689 propertyFIndex == interface->second.end() ||
1690 ledIndex == interface->second.end())
1691 {
1692 continue;
1693 }
1694
1695 try
1696 {
1697 Value valIndex = propertyFIndex->second;
1698 resFIndex = std::get<uint64_t>(valIndex);
1699
1700 Value valFType = propertyFaultType->second;
1701 resFType = std::get<std::string>(valFType);
1702 }
1703 catch (const std::bad_variant_access& e)
1704 {
1705 phosphor::logging::log<phosphor::logging::level::ERR>(e.what());
1706 return ipmi::responseResponseError();
1707 }
1708 // find the matched requested fault type: faultMemmory or faultFan
1709 if (resFType != faultNames[faultType])
1710 {
1711 continue;
1712 }
1713
1714 // read LedGpioPins data
1715 std::vector<uint64_t> ledgpios;
1716 std::variant<std::vector<uint64_t>> message;
1717
1718 auto method = dbus.new_method_call(
1719 service.c_str(), (std::string(item.first)).c_str(),
1720 "org.freedesktop.DBus.Properties", "Get");
1721
1722 method.append("xyz.openbmc_project.Configuration.LedFault",
1723 "LedGpioPins");
1724
1725 try
1726 {
1727 auto reply = dbus.call(method);
1728 reply.read(message);
1729 ledgpios = std::get<std::vector<uint64_t>>(message);
1730 }
1731 catch (std::exception& e)
1732 {
1733 phosphor::logging::log<phosphor::logging::level::ERR>(e.what());
1734 return ipmi::responseResponseError();
1735 }
1736
1737 // Check the size to be sure it will never overflow on groupSize
1738 if (ledgpios.size() > groupSize)
1739 {
1740 phosphor::logging::log<phosphor::logging::level::ERR>(
1741 "Fault gpio Pins out of range!");
1742 return ipmi::responseParmOutOfRange();
1743 }
1744 // Store data, according to command data bit index order
1745 for (int i = 0; i < ledgpios.size(); i++)
1746 {
1747 ledFaultPins[i + groupSize * resFIndex] = ledgpios[i];
1748 }
1749 }
1750
1751 switch (RemoteFaultType(faultType))
1752 {
1753 case (RemoteFaultType::fan):
1754 case (RemoteFaultType::memory):
1755 {
1756 if (faultGroup == skipLEDs)
1757 {
1758 return ipmi::responseSuccess();
1759 }
1760
1761 uint64_t ledState = 0;
1762 // calculate led state bit filed count, each byte has 8bits
1763 // the maximum bits will be 8 * 8 bits
1764 constexpr uint8_t size = sizeof(ledStateData) * 8;
1765 for (int i = 0; i < sizeof(ledStateData); i++)
1766 {
1767 ledState = (uint64_t)(ledState << 8);
1768 ledState = (uint64_t)(ledState | (uint64_t)ledStateData[i]);
1769 }
1770
1771 std::bitset<size> ledStateBits(ledState);
1772 std::string gpioValue;
1773 for (int i = 0; i < size; i++)
1774 { // skip invalid value
1775 if (ledFaultPins[i] == 0xFFFF)
1776 {
1777 continue;
1778 }
1779
1780 std::string device = sysGpioPath +
1781 std::to_string(ledFaultPins[i]) +
1782 postfixValue;
1783 std::fstream gpioFile;
1784
1785 gpioFile.open(device, std::ios::out);
1786
1787 if (!gpioFile.good())
1788 {
1789 phosphor::logging::log<phosphor::logging::level::ERR>(
1790 "Not Find Led Gpio Device!",
1791 phosphor::logging::entry("DEVICE=%s", device.c_str()));
1792 return ipmi::responseResponseError();
1793 }
1794 gpioFile << std::to_string(
1795 static_cast<uint8_t>(ledStateBits[i]));
1796 gpioFile.close();
1797 }
1798 break;
1799 }
1800 default:
1801 {
1802 // now only support two fault types
1803 return ipmi::responseParmOutOfRange();
1804 }
1805 }
1806
1807 return ipmi::responseSuccess();
1808}
1809
Richard Marian Thomaiyarea537d52019-04-24 21:33:48 +05301810ipmi::RspType<uint8_t> ipmiOEMReadBoardProductId()
1811{
1812 uint8_t prodId = 0;
1813 try
1814 {
1815 const DbusObjectInfo& object = getDbusObject(
1816 dbus, "xyz.openbmc_project.Inventory.Item.Board",
1817 "/xyz/openbmc_project/inventory/system/board/", "Baseboard");
1818 const Value& propValue = getDbusProperty(
1819 dbus, object.second, object.first,
1820 "xyz.openbmc_project.Inventory.Item.Board", "ProductId");
1821 prodId = static_cast<uint8_t>(std::get<uint64_t>(propValue));
1822 }
1823 catch (std::exception& e)
1824 {
1825 phosphor::logging::log<phosphor::logging::level::ERR>(
1826 "ipmiOEMReadBoardProductId: Product ID read failed!",
1827 phosphor::logging::entry("ERR=%s", e.what()));
1828 }
1829 return ipmi::responseSuccess(prodId);
1830}
1831
Jason M. Bills64796042018-10-03 16:51:55 -07001832static void registerOEMFunctions(void)
Jia, Chunhuia835eaa2018-09-05 09:00:41 +08001833{
1834 phosphor::logging::log<phosphor::logging::level::INFO>(
1835 "Registering OEM commands");
Jason M. Bills64796042018-10-03 16:51:55 -07001836 ipmiPrintAndRegister(netfnIntcOEMGeneral, IPMI_CMD_WILDCARD, NULL,
1837 ipmiOEMWildcard,
1838 PRIVILEGE_USER); // wildcard default handler
1839 ipmiPrintAndRegister(netfunIntelAppOEM, IPMI_CMD_WILDCARD, NULL,
1840 ipmiOEMWildcard,
1841 PRIVILEGE_USER); // wildcard default handler
1842 ipmiPrintAndRegister(
1843 netfnIntcOEMGeneral,
1844 static_cast<ipmi_cmd_t>(
1845 IPMINetfnIntelOEMGeneralCmd::cmdGetChassisIdentifier),
1846 NULL, ipmiOEMGetChassisIdentifier,
1847 PRIVILEGE_USER); // get chassis identifier
1848 ipmiPrintAndRegister(
1849 netfnIntcOEMGeneral,
1850 static_cast<ipmi_cmd_t>(IPMINetfnIntelOEMGeneralCmd::cmdSetSystemGUID),
1851 NULL, ipmiOEMSetSystemGUID,
1852 PRIVILEGE_ADMIN); // set system guid
1853 ipmiPrintAndRegister(
1854 netfnIntcOEMGeneral,
1855 static_cast<ipmi_cmd_t>(IPMINetfnIntelOEMGeneralCmd::cmdSetBIOSID),
1856 NULL, ipmiOEMSetBIOSID, PRIVILEGE_ADMIN);
1857 ipmiPrintAndRegister(netfnIntcOEMGeneral,
1858 static_cast<ipmi_cmd_t>(
1859 IPMINetfnIntelOEMGeneralCmd::cmdGetOEMDeviceInfo),
1860 NULL, ipmiOEMGetDeviceInfo, PRIVILEGE_USER);
1861 ipmiPrintAndRegister(
1862 netfnIntcOEMGeneral,
1863 static_cast<ipmi_cmd_t>(
1864 IPMINetfnIntelOEMGeneralCmd::cmdGetAICSlotFRUIDSlotPosRecords),
1865 NULL, ipmiOEMGetAICFRU, PRIVILEGE_USER);
Jia, Chunhuicc49b542019-03-20 15:41:07 +08001866
1867 ipmi::registerHandler(
1868 ipmi::prioOpenBmcBase, ipmi::netFnOemOne,
1869 static_cast<ipmi::Cmd>(
1870 IPMINetfnIntelOEMGeneralCmd::cmdSendEmbeddedFWUpdStatus),
1871 ipmi::Privilege::Operator, ipmiOEMSendEmbeddedFwUpdStatus);
1872
Jason M. Bills64796042018-10-03 16:51:55 -07001873 ipmiPrintAndRegister(
1874 netfnIntcOEMGeneral,
1875 static_cast<ipmi_cmd_t>(
1876 IPMINetfnIntelOEMGeneralCmd::cmdSetPowerRestoreDelay),
1877 NULL, ipmiOEMSetPowerRestoreDelay, PRIVILEGE_OPERATOR);
1878 ipmiPrintAndRegister(
1879 netfnIntcOEMGeneral,
1880 static_cast<ipmi_cmd_t>(
1881 IPMINetfnIntelOEMGeneralCmd::cmdGetPowerRestoreDelay),
1882 NULL, ipmiOEMGetPowerRestoreDelay, PRIVILEGE_USER);
Suryakanth Sekard509eb92018-11-15 17:44:11 +05301883
1884 ipmi::registerHandler(
1885 ipmi::prioOpenBmcBase, ipmi::netFnOemOne,
1886 static_cast<ipmi::Cmd>(
1887 IPMINetfnIntelOEMGeneralCmd::cmdSetOEMUser2Activation),
1888 ipmi::Privilege::Callback, ipmiOEMSetUser2Activation);
1889
Richard Marian Thomaiyarfc5e9852019-04-14 15:06:27 +05301890 ipmi::registerHandler(
1891 ipmi::prioOpenBmcBase, ipmi::netFnOemOne,
1892 static_cast<ipmi::Cmd>(
1893 IPMINetfnIntelOEMGeneralCmd::cmdSetSpecialUserPassword),
1894 ipmi::Privilege::Callback, ipmiOEMSetSpecialUserPassword);
1895
Jason M. Bills64796042018-10-03 16:51:55 -07001896 ipmiPrintAndRegister(
1897 netfnIntcOEMGeneral,
1898 static_cast<ipmi_cmd_t>(
1899 IPMINetfnIntelOEMGeneralCmd::cmdGetProcessorErrConfig),
1900 NULL, ipmiOEMGetProcessorErrConfig, PRIVILEGE_USER);
1901 ipmiPrintAndRegister(
1902 netfnIntcOEMGeneral,
1903 static_cast<ipmi_cmd_t>(
1904 IPMINetfnIntelOEMGeneralCmd::cmdSetProcessorErrConfig),
1905 NULL, ipmiOEMSetProcessorErrConfig, PRIVILEGE_ADMIN);
Yong Li703922d2018-11-06 13:25:31 +08001906 ipmiPrintAndRegister(netfnIntcOEMGeneral,
1907 static_cast<ipmi_cmd_t>(
1908 IPMINetfnIntelOEMGeneralCmd::cmdSetShutdownPolicy),
1909 NULL, ipmiOEMSetShutdownPolicy, PRIVILEGE_ADMIN);
1910 ipmiPrintAndRegister(netfnIntcOEMGeneral,
1911 static_cast<ipmi_cmd_t>(
1912 IPMINetfnIntelOEMGeneralCmd::cmdGetShutdownPolicy),
1913 NULL, ipmiOEMGetShutdownPolicy, PRIVILEGE_ADMIN);
James Feist91244a62019-02-19 15:04:54 -08001914
1915 ipmiPrintAndRegister(
1916 netfnIntcOEMGeneral,
1917 static_cast<ipmi_cmd_t>(IPMINetfnIntelOEMGeneralCmd::cmdSetFanConfig),
1918 NULL, ipmiOEMSetFanConfig, PRIVILEGE_USER);
1919
1920 ipmiPrintAndRegister(
1921 netfnIntcOEMGeneral,
1922 static_cast<ipmi_cmd_t>(IPMINetfnIntelOEMGeneralCmd::cmdGetFanConfig),
1923 NULL, ipmiOEMGetFanConfig, PRIVILEGE_USER);
1924
James Feistacc8a4e2019-04-02 14:23:57 -07001925 ipmi::registerHandler(
1926 ipmi::prioOemBase, netfnIntcOEMGeneral,
1927 static_cast<ipmi::Cmd>(
1928 IPMINetfnIntelOEMGeneralCmd::cmdGetFanSpeedOffset),
1929 ipmi::Privilege::User, ipmiOEMGetFanSpeedOffset);
James Feist5f957ca2019-03-14 15:33:55 -07001930
James Feistacc8a4e2019-04-02 14:23:57 -07001931 ipmi::registerHandler(
1932 ipmi::prioOemBase, netfnIntcOEMGeneral,
1933 static_cast<ipmi::Cmd>(
1934 IPMINetfnIntelOEMGeneralCmd::cmdSetFanSpeedOffset),
1935 ipmi::Privilege::User, ipmiOEMSetFanSpeedOffset);
1936
1937 ipmi::registerHandler(
1938 ipmi::prioOemBase, netfnIntcOEMGeneral,
1939 static_cast<ipmi::Cmd>(IPMINetfnIntelOEMGeneralCmd::cmdSetFscParameter),
1940 ipmi::Privilege::User, ipmiOEMSetFscParameter);
1941
1942 ipmi::registerHandler(
1943 ipmi::prioOemBase, netfnIntcOEMGeneral,
1944 static_cast<ipmi::Cmd>(IPMINetfnIntelOEMGeneralCmd::cmdGetFscParameter),
1945 ipmi::Privilege::User, ipmiOEMGetFscParameter);
James Feist5f957ca2019-03-14 15:33:55 -07001946
Richard Marian Thomaiyarea537d52019-04-24 21:33:48 +05301947 ipmi::registerHandler(
1948 ipmi::prioOpenBmcBase, netfnIntcOEMGeneral,
1949 static_cast<ipmi::Cmd>(
1950 IPMINetfnIntelOEMGeneralCmd::cmdReadBaseBoardProductId),
1951 ipmi::Privilege::Admin, ipmiOEMReadBoardProductId);
1952
Kuiying Wang45f04982018-12-26 09:23:08 +08001953 ipmiPrintAndRegister(
1954 netfnIntcOEMGeneral,
1955 static_cast<ipmi_cmd_t>(IPMINetfnIntelOEMGeneralCmd::cmdGetLEDStatus),
1956 NULL, ipmiOEMGetLEDStatus, PRIVILEGE_ADMIN);
Yong Li23737fe2019-02-19 08:49:55 +08001957 ipmiPrintAndRegister(
1958 netfnIntcOEMPlatform,
1959 static_cast<ipmi_cmd_t>(
1960 IPMINetfnIntelOEMPlatformCmd::cmdCfgHostSerialPortSpeed),
1961 NULL, ipmiOEMCfgHostSerialPortSpeed, PRIVILEGE_ADMIN);
Zhu, Yungebe560b02019-04-21 21:19:21 -04001962 ipmi::registerHandler(
1963 ipmi::prioOemBase, netfnIntcOEMGeneral,
1964 static_cast<ipmi::Cmd>(
1965 IPMINetfnIntelOEMGeneralCmd::cmdSetFaultIndication),
1966 ipmi::Privilege::Operator, ipmiOEMSetFaultIndication);
Jia, Chunhuia835eaa2018-09-05 09:00:41 +08001967 return;
1968}
1969
1970} // namespace ipmi