blob: 7cd7895d0d456f6f4af1eb6c3340e9617be5e7b1 [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;
331 target = (target & selEvtTargetMask) >> selEvtTargetShift;
332
333 /* make sure the status is 0, 1, or 2 as per the spec */
334 if (status > 2)
335 {
336 return ipmi::response(ipmi::ccInvalidFieldRequest);
337 }
338 /*orignal OEM command is to record OEM SEL.
339 But openbmc does not support OEM SEL, so we redirect it to redfish event
340 logging. */
341 std::string buildInfo;
342 std::string action;
343 switch (FWUpdateTarget(target))
344 {
345 case FWUpdateTarget::targetBMC:
346 firmware = "BMC";
347 buildInfo = " major: " + std::to_string(majorRevision) +
348 " minor: " +
349 std::to_string(bcdToDec(minorRevision)) + // BCD encoded
350 " BuildID: " + std::to_string(auxInfo);
351 buildInfo += std::to_string(auxInfo);
352 break;
353 case FWUpdateTarget::targetBIOS:
354 firmware = "BIOS";
355 buildInfo =
356 " major: " +
357 std::to_string(bcdToDec(majorRevision)) + // BCD encoded
358 " minor: " +
359 std::to_string(bcdToDec(minorRevision)) + // BCD encoded
360 " ReleaseNumber: " + // ASCII encoded
361 std::to_string(static_cast<uint8_t>(auxInfo >> 0) - '0') +
362 std::to_string(static_cast<uint8_t>(auxInfo >> 8) - '0') +
363 std::to_string(static_cast<uint8_t>(auxInfo >> 16) - '0') +
364 std::to_string(static_cast<uint8_t>(auxInfo >> 24) - '0');
365 break;
366 case FWUpdateTarget::targetME:
367 firmware = "ME";
368 buildInfo =
369 " major: " + std::to_string(majorRevision) + " minor1: " +
370 std::to_string(bcdToDec(minorRevision)) + // BCD encoded
371 " minor2: " +
372 std::to_string(bcdToDec(static_cast<uint8_t>(auxInfo >> 0))) +
373 " build1: " +
374 std::to_string(bcdToDec(static_cast<uint8_t>(auxInfo >> 8))) +
375 " build2: " +
376 std::to_string(bcdToDec(static_cast<uint8_t>(auxInfo >> 16)));
377 break;
378 case FWUpdateTarget::targetOEMEWS:
379 firmware = "EWS";
380 buildInfo = " major: " + std::to_string(majorRevision) +
381 " minor: " +
382 std::to_string(bcdToDec(minorRevision)) + // BCD encoded
383 " BuildID: " + std::to_string(auxInfo);
384 break;
385 }
386
387 switch (status)
388 {
389 case 0x0:
390 action = "update started";
391 break;
392 case 0x1:
393 action = "update completed successfully";
394 break;
395 case 0x2:
396 action = "update failure";
397 break;
398 default:
399 action = "unknown";
400 break;
401 }
402
403 std::string message(
404 "[firmware update] " + firmware + " instance: " +
405 std::to_string((target & targetInstanceMask) >> targetInstanceShift) +
406 " status: <" + action + ">" + buildInfo);
407 static constexpr const char* redfishMsgId = "FirmwareUpdate";
408
409 sd_journal_send("MESSAGE=%s", message.c_str(), "PRIORITY=%i", LOG_INFO,
410 "REDFISH_MESSAGE_ID=%s", redfishMsgId, NULL);
411 return ipmi::responseSuccess();
412}
413
Jason M. Bills64796042018-10-03 16:51:55 -0700414ipmi_ret_t ipmiOEMSetPowerRestoreDelay(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
415 ipmi_request_t request,
416 ipmi_response_t response,
417 ipmi_data_len_t dataLen,
418 ipmi_context_t context)
419{
420 SetPowerRestoreDelayReq* data =
421 reinterpret_cast<SetPowerRestoreDelayReq*>(request);
422 uint16_t delay = 0;
423
424 if (*dataLen != sizeof(SetPowerRestoreDelayReq))
425 {
426 *dataLen = 0;
427 return IPMI_CC_REQ_DATA_LEN_INVALID;
428 }
429 delay = data->byteMSB;
430 delay = (delay << 8) | data->byteLSB;
431 std::string service =
432 getService(dbus, powerRestoreDelayIntf, powerRestoreDelayObjPath);
433 setDbusProperty(dbus, service, powerRestoreDelayObjPath,
434 powerRestoreDelayIntf, powerRestoreDelayProp, delay);
435 *dataLen = 0;
436
437 return IPMI_CC_OK;
438}
439
440ipmi_ret_t ipmiOEMGetProcessorErrConfig(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
441 ipmi_request_t request,
442 ipmi_response_t response,
443 ipmi_data_len_t dataLen,
444 ipmi_context_t context)
445{
446 GetProcessorErrConfigRes* resp =
447 reinterpret_cast<GetProcessorErrConfigRes*>(response);
448
449 if (*dataLen != 0)
450 {
451 *dataLen = 0;
452 return IPMI_CC_REQ_DATA_LEN_INVALID;
453 }
454
455 std::string service =
456 getService(dbus, processorErrConfigIntf, processorErrConfigObjPath);
457 Value variant = getDbusProperty(dbus, service, processorErrConfigObjPath,
458 processorErrConfigIntf, "ResetCfg");
459 resp->resetCfg = sdbusplus::message::variant_ns::get<uint8_t>(variant);
460
461 std::vector<uint8_t> caterrStatus;
Kuiying Wangbc546672018-11-23 15:41:05 +0800462 sdbusplus::message::variant<std::vector<uint8_t>> message;
Jason M. Bills64796042018-10-03 16:51:55 -0700463
464 auto method =
465 dbus.new_method_call(service.c_str(), processorErrConfigObjPath,
466 "org.freedesktop.DBus.Properties", "Get");
467
468 method.append(processorErrConfigIntf, "CATERRStatus");
Kuiying Wangbc546672018-11-23 15:41:05 +0800469 auto reply = dbus.call(method);
Jason M. Bills64796042018-10-03 16:51:55 -0700470
471 try
472 {
Kuiying Wangbc546672018-11-23 15:41:05 +0800473 reply.read(message);
474 caterrStatus =
475 sdbusplus::message::variant_ns::get<std::vector<uint8_t>>(message);
Jason M. Bills64796042018-10-03 16:51:55 -0700476 }
477 catch (sdbusplus::exception_t&)
478 {
Kuiying Wangbc546672018-11-23 15:41:05 +0800479 phosphor::logging::log<phosphor::logging::level::ERR>(
Jason M. Bills64796042018-10-03 16:51:55 -0700480 "ipmiOEMGetProcessorErrConfig: error on dbus",
481 phosphor::logging::entry("PRORPERTY=CATERRStatus"),
482 phosphor::logging::entry("PATH=%s", processorErrConfigObjPath),
483 phosphor::logging::entry("INTERFACE=%s", processorErrConfigIntf));
484 return IPMI_CC_UNSPECIFIED_ERROR;
485 }
486
487 size_t len =
488 maxCPUNum <= caterrStatus.size() ? maxCPUNum : caterrStatus.size();
489 caterrStatus.resize(len);
490 std::copy(caterrStatus.begin(), caterrStatus.end(), resp->caterrStatus);
491 *dataLen = sizeof(GetProcessorErrConfigRes);
492
493 return IPMI_CC_OK;
494}
495
496ipmi_ret_t ipmiOEMSetProcessorErrConfig(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
497 ipmi_request_t request,
498 ipmi_response_t response,
499 ipmi_data_len_t dataLen,
500 ipmi_context_t context)
501{
502 SetProcessorErrConfigReq* req =
503 reinterpret_cast<SetProcessorErrConfigReq*>(request);
504
505 if (*dataLen != sizeof(SetProcessorErrConfigReq))
506 {
507 *dataLen = 0;
508 return IPMI_CC_REQ_DATA_LEN_INVALID;
509 }
510 std::string service =
511 getService(dbus, processorErrConfigIntf, processorErrConfigObjPath);
512 setDbusProperty(dbus, service, processorErrConfigObjPath,
513 processorErrConfigIntf, "ResetCfg", req->resetCfg);
514
515 setDbusProperty(dbus, service, processorErrConfigObjPath,
516 processorErrConfigIntf, "ResetErrorOccurrenceCounts",
517 req->resetErrorOccurrenceCounts);
518 *dataLen = 0;
519
520 return IPMI_CC_OK;
521}
522
Yong Li703922d2018-11-06 13:25:31 +0800523ipmi_ret_t ipmiOEMGetShutdownPolicy(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
524 ipmi_request_t request,
525 ipmi_response_t response,
526 ipmi_data_len_t dataLen,
527 ipmi_context_t context)
528{
529 GetOEMShutdownPolicyRes* resp =
530 reinterpret_cast<GetOEMShutdownPolicyRes*>(response);
531
532 if (*dataLen != 0)
533 {
534 phosphor::logging::log<phosphor::logging::level::ERR>(
Kuiying Wang45f04982018-12-26 09:23:08 +0800535 "oem_get_shutdown_policy: invalid input len!");
Yong Li703922d2018-11-06 13:25:31 +0800536 *dataLen = 0;
537 return IPMI_CC_REQ_DATA_LEN_INVALID;
538 }
539
540 *dataLen = 0;
541
542 try
543 {
544 std::string service =
545 getService(dbus, oemShutdownPolicyIntf, oemShutdownPolicyObjPath);
546 Value variant = getDbusProperty(dbus, service, oemShutdownPolicyObjPath,
547 oemShutdownPolicyIntf,
548 oemShutdownPolicyObjPathProp);
549 resp->policy = sdbusplus::message::variant_ns::get<uint8_t>(variant);
550 // TODO needs to check if it is multi-node products,
551 // policy is only supported on node 3/4
552 resp->policySupport = shutdownPolicySupported;
553 }
554 catch (sdbusplus::exception_t& e)
555 {
556 phosphor::logging::log<phosphor::logging::level::ERR>(e.description());
557 return IPMI_CC_UNSPECIFIED_ERROR;
558 }
559
560 *dataLen = sizeof(GetOEMShutdownPolicyRes);
561 return IPMI_CC_OK;
562}
563
564ipmi_ret_t ipmiOEMSetShutdownPolicy(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
565 ipmi_request_t request,
566 ipmi_response_t response,
567 ipmi_data_len_t dataLen,
568 ipmi_context_t context)
569{
570 uint8_t* req = reinterpret_cast<uint8_t*>(request);
571
572 // TODO needs to check if it is multi-node products,
573 // policy is only supported on node 3/4
574 if (*dataLen != 1)
575 {
576 phosphor::logging::log<phosphor::logging::level::ERR>(
577 "oem_set_shutdown_policy: invalid input len!");
578 *dataLen = 0;
579 return IPMI_CC_REQ_DATA_LEN_INVALID;
580 }
581
582 *dataLen = 0;
583 if ((*req != noShutdownOnOCOT) && (*req != shutdownOnOCOT))
584 {
585 phosphor::logging::log<phosphor::logging::level::ERR>(
586 "oem_set_shutdown_policy: invalid input!");
587 return IPMI_CC_INVALID_FIELD_REQUEST;
588 }
589
590 try
591 {
592 std::string service =
593 getService(dbus, oemShutdownPolicyIntf, oemShutdownPolicyObjPath);
594 setDbusProperty(dbus, service, oemShutdownPolicyObjPath,
595 oemShutdownPolicyIntf, oemShutdownPolicyObjPathProp,
596 *req);
597 }
598 catch (sdbusplus::exception_t& e)
599 {
600 phosphor::logging::log<phosphor::logging::level::ERR>(e.description());
601 return IPMI_CC_UNSPECIFIED_ERROR;
602 }
603
604 return IPMI_CC_OK;
605}
606
Suryakanth Sekard509eb92018-11-15 17:44:11 +0530607/** @brief implementation for check the DHCP or not in IPv4
608 * @param[in] Channel - Channel number
609 * @returns true or false.
610 */
611static bool isDHCPEnabled(uint8_t Channel)
612{
613 try
614 {
615 auto ethdevice = getChannelName(Channel);
616 if (ethdevice.empty())
617 {
618 return false;
619 }
620 auto ethIP = ethdevice + "/ipv4";
621 auto ethernetObj =
622 getDbusObject(dbus, networkIPIntf, networkRoot, ethIP);
623 auto value = getDbusProperty(dbus, networkService, ethernetObj.first,
624 networkIPIntf, "Origin");
625 if (sdbusplus::message::variant_ns::get<std::string>(value) ==
626 "xyz.openbmc_project.Network.IP.AddressOrigin.DHCP")
627 {
628 return true;
629 }
630 else
631 {
632 return false;
633 }
634 }
635 catch (sdbusplus::exception_t& e)
636 {
637 phosphor::logging::log<phosphor::logging::level::ERR>(e.description());
638 return true;
639 }
640}
641
642/** @brief implementes for check the DHCP or not in IPv6
643 * @param[in] Channel - Channel number
644 * @returns true or false.
645 */
646static bool isDHCPIPv6Enabled(uint8_t Channel)
647{
648
649 try
650 {
651 auto ethdevice = getChannelName(Channel);
652 if (ethdevice.empty())
653 {
654 return false;
655 }
656 auto ethIP = ethdevice + "/ipv6";
657 auto objectInfo =
658 getDbusObject(dbus, networkIPIntf, networkRoot, ethIP);
659 auto properties = getAllDbusProperties(dbus, objectInfo.second,
660 objectInfo.first, networkIPIntf);
661 if (sdbusplus::message::variant_ns::get<std::string>(
662 properties["Origin"]) ==
663 "xyz.openbmc_project.Network.IP.AddressOrigin.DHCP")
664 {
665 return true;
666 }
667 else
668 {
669 return false;
670 }
671 }
672 catch (sdbusplus::exception_t& e)
673 {
674 phosphor::logging::log<phosphor::logging::level::ERR>(e.description());
675 return true;
676 }
677}
678
679/** @brief implementes the creating of default new user
680 * @param[in] userName - new username in 16 bytes.
681 * @param[in] userPassword - new password in 20 bytes
682 * @returns ipmi completion code.
683 */
684ipmi::RspType<> ipmiOEMSetUser2Activation(
685 std::array<uint8_t, ipmi::ipmiMaxUserName>& userName,
686 std::array<uint8_t, ipmi::maxIpmi20PasswordSize>& userPassword)
687{
688 bool userState = false;
689 // Check for System Interface not exist and LAN should be static
690 for (uint8_t channel = 0; channel < maxIpmiChannels; channel++)
691 {
692 ChannelInfo chInfo;
693 try
694 {
695 getChannelInfo(channel, chInfo);
696 }
697 catch (sdbusplus::exception_t& e)
698 {
699 phosphor::logging::log<phosphor::logging::level::ERR>(
700 "ipmiOEMSetUser2Activation: Failed to get Channel Info",
701 phosphor::logging::entry("MSG: %s", e.description()));
702 return ipmi::response(ipmi::ccUnspecifiedError);
703 }
704 if (chInfo.mediumType ==
705 static_cast<uint8_t>(EChannelMediumType::systemInterface))
706 {
707 phosphor::logging::log<phosphor::logging::level::ERR>(
708 "ipmiOEMSetUser2Activation: system interface exist .");
709 return ipmi::response(ipmi::ccCommandNotAvailable);
710 }
711 else
712 {
713
714 if (chInfo.mediumType ==
715 static_cast<uint8_t>(EChannelMediumType::lan8032))
716 {
717 if (isDHCPIPv6Enabled(channel) || isDHCPEnabled(channel))
718 {
719 phosphor::logging::log<phosphor::logging::level::ERR>(
720 "ipmiOEMSetUser2Activation: DHCP enabled .");
721 return ipmi::response(ipmi::ccCommandNotAvailable);
722 }
723 }
724 }
725 }
726 uint8_t maxChUsers = 0, enabledUsers = 0, fixedUsers = 0;
727 if (ipmi::ccSuccess ==
728 ipmiUserGetAllCounts(maxChUsers, enabledUsers, fixedUsers))
729 {
730 if (enabledUsers > 1)
731 {
732 phosphor::logging::log<phosphor::logging::level::ERR>(
733 "ipmiOEMSetUser2Activation: more than one user is enabled.");
734 return ipmi::response(ipmi::ccCommandNotAvailable);
735 }
736 // Check the user 2 is enabled or not
737 ipmiUserCheckEnabled(ipmiDefaultUserId, userState);
738 if (userState == true)
739 {
740 phosphor::logging::log<phosphor::logging::level::ERR>(
741 "ipmiOEMSetUser2Activation: user 2 already enabled .");
742 return ipmi::response(ipmi::ccCommandNotAvailable);
743 }
744 }
745 else
746 {
747 return ipmi::response(ipmi::ccUnspecifiedError);
748 }
749
750#if BYTE_ORDER == LITTLE_ENDIAN
751 PrivAccess privAccess = {PRIVILEGE_ADMIN, true, true, true, 0};
752#endif
753#if BYTE_ORDER == BIG_ENDIAN
754 PrivAccess privAccess = {0, true, true, true, PRIVILEGE_ADMIN};
755#endif
756
757 if (ipmi::ccSuccess ==
758 ipmiUserSetUserName(ipmiDefaultUserId,
759 reinterpret_cast<const char*>(userName.data())))
760 {
761 if (ipmi::ccSuccess ==
762 ipmiUserSetUserPassword(
763 ipmiDefaultUserId,
764 reinterpret_cast<const char*>(userPassword.data())))
765 {
766 if (ipmi::ccSuccess ==
767 ipmiUserSetPrivilegeAccess(
768 ipmiDefaultUserId,
769 static_cast<uint8_t>(ipmi::EChannelID::chanLan1),
770 privAccess, true))
771 {
772 phosphor::logging::log<phosphor::logging::level::INFO>(
773 "ipmiOEMSetUser2Activation: user created successfully ");
774 return ipmi::responseSuccess();
775 }
776 }
777 // we need to delete the default user id which added in this command as
778 // password / priv setting is failed.
779 ipmiUserSetUserName(ipmiDefaultUserId, "");
780 phosphor::logging::log<phosphor::logging::level::ERR>(
781 "ipmiOEMSetUser2Activation: password / priv setting is failed.");
782 }
783 else
784 {
785 phosphor::logging::log<phosphor::logging::level::ERR>(
786 "ipmiOEMSetUser2Activation: Setting username failed.");
787 }
788
789 return ipmi::response(ipmi::ccCommandNotAvailable);
790}
791
Richard Marian Thomaiyarfc5e9852019-04-14 15:06:27 +0530792/** @brief implementes setting password for special user
793 * @param[in] specialUserIndex
794 * @param[in] userPassword - new password in 20 bytes
795 * @returns ipmi completion code.
796 */
797ipmi::RspType<> ipmiOEMSetSpecialUserPassword(ipmi::Context::ptr ctx,
798 uint8_t specialUserIndex,
799 std::vector<uint8_t> userPassword)
800{
801 ChannelInfo chInfo;
802 try
803 {
804 getChannelInfo(ctx->channel, chInfo);
805 }
806 catch (sdbusplus::exception_t& e)
807 {
808 phosphor::logging::log<phosphor::logging::level::ERR>(
809 "ipmiOEMSetSpecialUserPassword: Failed to get Channel Info",
810 phosphor::logging::entry("MSG: %s", e.description()));
811 return ipmi::responseUnspecifiedError();
812 }
813 if (chInfo.mediumType !=
814 static_cast<uint8_t>(EChannelMediumType::systemInterface))
815 {
816 phosphor::logging::log<phosphor::logging::level::ERR>(
817 "ipmiOEMSetSpecialUserPassword: Error - supported only in KCS "
818 "interface");
819 return ipmi::responseCommandNotAvailable();
820 }
821 if (specialUserIndex != 0)
822 {
823 phosphor::logging::log<phosphor::logging::level::ERR>(
824 "ipmiOEMSetSpecialUserPassword: Invalid user account");
825 return ipmi::responseParmOutOfRange();
826 }
827 constexpr uint8_t minPasswordSizeRequired = 6;
828 if (userPassword.size() < minPasswordSizeRequired ||
829 userPassword.size() > ipmi::maxIpmi20PasswordSize)
830 {
831 return ipmi::responseReqDataLenInvalid();
832 }
833 std::string passwd;
834 passwd.assign(reinterpret_cast<const char*>(userPassword.data()),
835 userPassword.size());
836 return ipmi::response(ipmiSetSpecialUserPassword("root", passwd));
837}
838
Kuiying Wang45f04982018-12-26 09:23:08 +0800839namespace ledAction
840{
841using namespace sdbusplus::xyz::openbmc_project::Led::server;
842std::map<Physical::Action, uint8_t> actionDbusToIpmi = {
843 {Physical::Action::Off, 0x00},
844 {Physical::Action::On, 0x10},
845 {Physical::Action::Blink, 0x01}};
846
847std::map<uint8_t, std::string> offsetObjPath = {
848 {2, statusAmberObjPath}, {4, statusGreenObjPath}, {6, identifyLEDObjPath}};
849
850} // namespace ledAction
851
852int8_t getLEDState(sdbusplus::bus::bus& bus, const std::string& intf,
853 const std::string& objPath, uint8_t& state)
854{
855 try
856 {
857 std::string service = getService(bus, intf, objPath);
858 Value stateValue =
859 getDbusProperty(bus, service, objPath, intf, "State");
860 std::string strState =
861 sdbusplus::message::variant_ns::get<std::string>(stateValue);
862 state = ledAction::actionDbusToIpmi.at(
863 sdbusplus::xyz::openbmc_project::Led::server::Physical::
864 convertActionFromString(strState));
865 }
866 catch (sdbusplus::exception::SdBusError& e)
867 {
868 phosphor::logging::log<phosphor::logging::level::ERR>(e.what());
869 return -1;
870 }
871 return 0;
872}
873
874ipmi_ret_t ipmiOEMGetLEDStatus(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
875 ipmi_request_t request, ipmi_response_t response,
876 ipmi_data_len_t dataLen, ipmi_context_t context)
877{
878 uint8_t* resp = reinterpret_cast<uint8_t*>(response);
879 // LED Status
880 //[1:0] = Reserved
881 //[3:2] = Status(Amber)
882 //[5:4] = Status(Green)
883 //[7:6] = System Identify
884 // Status definitions:
885 // 00b = Off
886 // 01b = Blink
887 // 10b = On
888 // 11b = invalid
889 if (*dataLen != 0)
890 {
891 phosphor::logging::log<phosphor::logging::level::ERR>(
892 "oem_get_led_status: invalid input len!");
893 *dataLen = 0;
894 return IPMI_CC_REQ_DATA_LEN_INVALID;
895 }
896
897 phosphor::logging::log<phosphor::logging::level::DEBUG>("GET led status");
898 *resp = 0;
899 *dataLen = 0;
900 for (auto it = ledAction::offsetObjPath.begin();
901 it != ledAction::offsetObjPath.end(); ++it)
902 {
903 uint8_t state = 0;
904 if (-1 == getLEDState(dbus, ledIntf, it->second, state))
905 {
906 phosphor::logging::log<phosphor::logging::level::ERR>(
907 "oem_get_led_status: fail to get ID LED status!");
908 return IPMI_CC_UNSPECIFIED_ERROR;
909 }
910 *resp |= state << it->first;
911 }
912
913 *dataLen = sizeof(*resp);
914 return IPMI_CC_OK;
915}
916
Yong Li23737fe2019-02-19 08:49:55 +0800917ipmi_ret_t ipmiOEMCfgHostSerialPortSpeed(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
918 ipmi_request_t request,
919 ipmi_response_t response,
920 ipmi_data_len_t dataLen,
921 ipmi_context_t context)
922{
923 CfgHostSerialReq* req = reinterpret_cast<CfgHostSerialReq*>(request);
924 uint8_t* resp = reinterpret_cast<uint8_t*>(response);
925
926 if (*dataLen == 0)
927 {
928 phosphor::logging::log<phosphor::logging::level::ERR>(
929 "CfgHostSerial: invalid input len!",
930 phosphor::logging::entry("LEN=%d", *dataLen));
931 return IPMI_CC_REQ_DATA_LEN_INVALID;
932 }
933
934 switch (req->command)
935 {
936 case getHostSerialCfgCmd:
937 {
938 if (*dataLen != 1)
939 {
940 phosphor::logging::log<phosphor::logging::level::ERR>(
941 "CfgHostSerial: invalid input len!");
942 *dataLen = 0;
943 return IPMI_CC_REQ_DATA_LEN_INVALID;
944 }
945
946 *dataLen = 0;
947
948 boost::process::ipstream is;
949 std::vector<std::string> data;
950 std::string line;
951 boost::process::child c1(fwGetEnvCmd, "-n", fwHostSerailCfgEnvName,
952 boost::process::std_out > is);
953
954 while (c1.running() && std::getline(is, line) && !line.empty())
955 {
956 data.push_back(line);
957 }
958
959 c1.wait();
960 if (c1.exit_code())
961 {
962 phosphor::logging::log<phosphor::logging::level::ERR>(
963 "CfgHostSerial:: error on execute",
964 phosphor::logging::entry("EXECUTE=%s", fwSetEnvCmd));
965 // Using the default value
966 *resp = 0;
967 }
968 else
969 {
970 if (data.size() != 1)
971 {
972 phosphor::logging::log<phosphor::logging::level::ERR>(
973 "CfgHostSerial:: error on read env");
974 return IPMI_CC_UNSPECIFIED_ERROR;
975 }
976 try
977 {
978 unsigned long tmp = std::stoul(data[0]);
979 if (tmp > std::numeric_limits<uint8_t>::max())
980 {
981 throw std::out_of_range("Out of range");
982 }
983 *resp = static_cast<uint8_t>(tmp);
984 }
985 catch (const std::invalid_argument& e)
986 {
987 phosphor::logging::log<phosphor::logging::level::ERR>(
988 "invalid config ",
989 phosphor::logging::entry("ERR=%s", e.what()));
990 return IPMI_CC_UNSPECIFIED_ERROR;
991 }
992 catch (const std::out_of_range& e)
993 {
994 phosphor::logging::log<phosphor::logging::level::ERR>(
995 "out_of_range config ",
996 phosphor::logging::entry("ERR=%s", e.what()));
997 return IPMI_CC_UNSPECIFIED_ERROR;
998 }
999 }
1000
1001 *dataLen = 1;
1002 break;
1003 }
1004 case setHostSerialCfgCmd:
1005 {
1006 if (*dataLen != sizeof(CfgHostSerialReq))
1007 {
1008 phosphor::logging::log<phosphor::logging::level::ERR>(
1009 "CfgHostSerial: invalid input len!");
1010 *dataLen = 0;
1011 return IPMI_CC_REQ_DATA_LEN_INVALID;
1012 }
1013
1014 *dataLen = 0;
1015
1016 if (req->parameter > HostSerialCfgParamMax)
1017 {
1018 phosphor::logging::log<phosphor::logging::level::ERR>(
1019 "CfgHostSerial: invalid input!");
1020 return IPMI_CC_INVALID_FIELD_REQUEST;
1021 }
1022
1023 boost::process::child c1(fwSetEnvCmd, fwHostSerailCfgEnvName,
1024 std::to_string(req->parameter));
1025
1026 c1.wait();
1027 if (c1.exit_code())
1028 {
1029 phosphor::logging::log<phosphor::logging::level::ERR>(
1030 "CfgHostSerial:: error on execute",
1031 phosphor::logging::entry("EXECUTE=%s", fwGetEnvCmd));
1032 return IPMI_CC_UNSPECIFIED_ERROR;
1033 }
1034 break;
1035 }
1036 default:
1037 phosphor::logging::log<phosphor::logging::level::ERR>(
1038 "CfgHostSerial: invalid input!");
1039 *dataLen = 0;
1040 return IPMI_CC_INVALID_FIELD_REQUEST;
1041 }
1042
1043 return IPMI_CC_OK;
1044}
1045
James Feist91244a62019-02-19 15:04:54 -08001046constexpr const char* thermalModeInterface =
1047 "xyz.openbmc_project.Control.ThermalMode";
1048constexpr const char* thermalModePath =
1049 "/xyz/openbmc_project/control/thermal_mode";
1050
1051bool getFanProfileInterface(
1052 sdbusplus::bus::bus& bus,
1053 boost::container::flat_map<
1054 std::string, std::variant<std::vector<std::string>, std::string>>& resp)
1055{
1056 auto call = bus.new_method_call(settingsBusName, thermalModePath, PROP_INTF,
1057 "GetAll");
1058 call.append(thermalModeInterface);
1059 try
1060 {
1061 auto data = bus.call(call);
1062 data.read(resp);
1063 }
1064 catch (sdbusplus::exception_t& e)
1065 {
1066 phosphor::logging::log<phosphor::logging::level::ERR>(
1067 "getFanProfileInterface: can't get thermal mode!",
1068 phosphor::logging::entry("ERR=%s", e.what()));
1069 return false;
1070 }
1071 return true;
1072}
1073
1074ipmi_ret_t ipmiOEMSetFanConfig(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
1075 ipmi_request_t request, ipmi_response_t response,
1076 ipmi_data_len_t dataLen, ipmi_context_t context)
1077{
1078
1079 if (*dataLen < 2 || *dataLen > 7)
1080 {
1081 phosphor::logging::log<phosphor::logging::level::ERR>(
1082 "ipmiOEMSetFanConfig: invalid input len!");
1083 *dataLen = 0;
1084 return IPMI_CC_REQ_DATA_LEN_INVALID;
1085 }
1086
1087 // todo: tell bios to only send first 2 bytes
1088
1089 SetFanConfigReq* req = reinterpret_cast<SetFanConfigReq*>(request);
1090 boost::container::flat_map<
1091 std::string, std::variant<std::vector<std::string>, std::string>>
1092 profileData;
1093 if (!getFanProfileInterface(dbus, profileData))
1094 {
1095 return IPMI_CC_UNSPECIFIED_ERROR;
1096 }
1097
1098 std::vector<std::string>* supported =
1099 std::get_if<std::vector<std::string>>(&profileData["Supported"]);
1100 if (supported == nullptr)
1101 {
1102 return IPMI_CC_INVALID_FIELD_REQUEST;
1103 }
1104 std::string mode;
1105 if (req->flags &
1106 (1 << static_cast<uint8_t>(setFanProfileFlags::setPerfAcousMode)))
1107 {
1108 bool performanceMode =
1109 (req->flags & (1 << static_cast<uint8_t>(
1110 setFanProfileFlags::performAcousSelect))) > 0;
1111
1112 if (performanceMode)
1113 {
1114
1115 if (std::find(supported->begin(), supported->end(),
1116 "Performance") != supported->end())
1117 {
1118 mode = "Performance";
1119 }
1120 }
1121 else
1122 {
1123
1124 if (std::find(supported->begin(), supported->end(), "Acoustic") !=
1125 supported->end())
1126 {
1127 mode = "Acoustic";
1128 }
1129 }
1130 if (mode.empty())
1131 {
1132 return IPMI_CC_INVALID_FIELD_REQUEST;
1133 }
1134 setDbusProperty(dbus, settingsBusName, thermalModePath,
1135 thermalModeInterface, "Current", mode);
1136 }
1137
1138 return IPMI_CC_OK;
1139}
1140
1141ipmi_ret_t ipmiOEMGetFanConfig(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
1142 ipmi_request_t request, ipmi_response_t response,
1143 ipmi_data_len_t dataLen, ipmi_context_t context)
1144{
1145
1146 if (*dataLen > 1)
1147 {
1148 phosphor::logging::log<phosphor::logging::level::ERR>(
1149 "ipmiOEMGetFanConfig: invalid input len!");
1150 *dataLen = 0;
1151 return IPMI_CC_REQ_DATA_LEN_INVALID;
1152 }
1153
1154 // todo: talk to bios about needing less information
1155
1156 GetFanConfigResp* resp = reinterpret_cast<GetFanConfigResp*>(response);
1157 *dataLen = sizeof(GetFanConfigResp);
1158
1159 boost::container::flat_map<
1160 std::string, std::variant<std::vector<std::string>, std::string>>
1161 profileData;
1162
1163 if (!getFanProfileInterface(dbus, profileData))
1164 {
1165 return IPMI_CC_UNSPECIFIED_ERROR;
1166 }
1167
1168 std::string* current = std::get_if<std::string>(&profileData["Current"]);
1169
1170 if (current == nullptr)
1171 {
1172 phosphor::logging::log<phosphor::logging::level::ERR>(
1173 "ipmiOEMGetFanConfig: can't get current mode!");
1174 return IPMI_CC_UNSPECIFIED_ERROR;
1175 }
1176 bool performance = (*current == "Performance");
1177
1178 if (performance)
1179 {
1180 resp->flags |= 1 << 2;
1181 }
1182
1183 return IPMI_CC_OK;
1184}
1185
James Feist5f957ca2019-03-14 15:33:55 -07001186constexpr const char* cfmLimitSettingPath =
1187 "/xyz/openbmc_project/control/cfm_limit";
1188constexpr const char* cfmLimitIface = "xyz.openbmc_project.Control.CFMLimit";
James Feistfaa4f222019-03-21 16:21:55 -07001189constexpr const size_t legacyExitAirSensorNumber = 0x2e;
James Feistacc8a4e2019-04-02 14:23:57 -07001190constexpr const char* pidConfigurationIface =
1191 "xyz.openbmc_project.Configuration.Pid";
James Feistfaa4f222019-03-21 16:21:55 -07001192
1193static std::string getExitAirConfigPath()
1194{
1195
1196 auto method =
1197 dbus.new_method_call("xyz.openbmc_project.ObjectMapper",
1198 "/xyz/openbmc_project/object_mapper",
1199 "xyz.openbmc_project.ObjectMapper", "GetSubTree");
1200
James Feistacc8a4e2019-04-02 14:23:57 -07001201 method.append("/", 0, std::array<const char*, 1>{pidConfigurationIface});
James Feistfaa4f222019-03-21 16:21:55 -07001202 std::string path;
1203 GetSubTreeType resp;
1204 try
1205 {
1206 auto reply = dbus.call(method);
1207 reply.read(resp);
1208 }
1209 catch (sdbusplus::exception_t&)
1210 {
1211 phosphor::logging::log<phosphor::logging::level::ERR>(
1212 "ipmiOEMGetFscParameter: mapper error");
1213 };
1214 auto config = std::find_if(resp.begin(), resp.end(), [](const auto& pair) {
1215 return pair.first.find("Exit_Air") != std::string::npos;
1216 });
1217 if (config != resp.end())
1218 {
1219 path = std::move(config->first);
1220 }
1221 return path;
1222}
James Feist5f957ca2019-03-14 15:33:55 -07001223
James Feistacc8a4e2019-04-02 14:23:57 -07001224// flat map to make alphabetical
1225static boost::container::flat_map<std::string, PropertyMap> getPidConfigs()
1226{
1227 boost::container::flat_map<std::string, PropertyMap> ret;
1228 auto method =
1229 dbus.new_method_call("xyz.openbmc_project.ObjectMapper",
1230 "/xyz/openbmc_project/object_mapper",
1231 "xyz.openbmc_project.ObjectMapper", "GetSubTree");
1232
1233 method.append("/", 0, std::array<const char*, 1>{pidConfigurationIface});
1234 GetSubTreeType resp;
1235
1236 try
1237 {
1238 auto reply = dbus.call(method);
1239 reply.read(resp);
1240 }
1241 catch (sdbusplus::exception_t&)
1242 {
1243 phosphor::logging::log<phosphor::logging::level::ERR>(
1244 "getFanConfigPaths: mapper error");
1245 };
1246 for (const auto& [path, objects] : resp)
1247 {
1248 if (objects.empty())
1249 {
1250 continue; // should be impossible
1251 }
Zhu, Yungebe560b02019-04-21 21:19:21 -04001252
1253 try
1254 {
1255 ret.emplace(path, getAllDbusProperties(dbus, objects[0].first, path,
1256 pidConfigurationIface));
1257 }
1258 catch (sdbusplus::exception_t& e)
1259 {
1260 phosphor::logging::log<phosphor::logging::level::ERR>(
1261 "getPidConfigs: can't get DbusProperties!",
1262 phosphor::logging::entry("ERR=%s", e.what()));
1263 }
James Feistacc8a4e2019-04-02 14:23:57 -07001264 }
1265 return ret;
1266}
1267
1268ipmi::RspType<uint8_t> ipmiOEMGetFanSpeedOffset(void)
1269{
1270 boost::container::flat_map<std::string, PropertyMap> data = getPidConfigs();
1271 if (data.empty())
1272 {
1273 return ipmi::responseResponseError();
1274 }
1275 uint8_t minOffset = std::numeric_limits<uint8_t>::max();
1276 for (const auto& [_, pid] : data)
1277 {
1278 auto findClass = pid.find("Class");
1279 if (findClass == pid.end())
1280 {
1281 phosphor::logging::log<phosphor::logging::level::ERR>(
1282 "ipmiOEMGetFscParameter: found illegal pid "
1283 "configurations");
1284 return ipmi::responseResponseError();
1285 }
1286 std::string type = std::get<std::string>(findClass->second);
1287 if (type == "fan")
1288 {
1289 auto findOutLimit = pid.find("OutLimitMin");
1290 if (findOutLimit == pid.end())
1291 {
1292 phosphor::logging::log<phosphor::logging::level::ERR>(
1293 "ipmiOEMGetFscParameter: found illegal pid "
1294 "configurations");
1295 return ipmi::responseResponseError();
1296 }
1297 // get the min out of all the offsets
1298 minOffset = std::min(
1299 minOffset,
1300 static_cast<uint8_t>(std::get<double>(findOutLimit->second)));
1301 }
1302 }
1303 if (minOffset == std::numeric_limits<uint8_t>::max())
1304 {
1305 phosphor::logging::log<phosphor::logging::level::ERR>(
1306 "ipmiOEMGetFscParameter: found no fan configurations!");
1307 return ipmi::responseResponseError();
1308 }
1309
1310 return ipmi::responseSuccess(minOffset);
1311}
1312
1313ipmi::RspType<> ipmiOEMSetFanSpeedOffset(uint8_t offset)
1314{
1315 boost::container::flat_map<std::string, PropertyMap> data = getPidConfigs();
1316 if (data.empty())
1317 {
1318
1319 phosphor::logging::log<phosphor::logging::level::ERR>(
1320 "ipmiOEMSetFanSpeedOffset: found no pid configurations!");
1321 return ipmi::responseResponseError();
1322 }
1323
1324 bool found = false;
1325 for (const auto& [path, pid] : data)
1326 {
1327 auto findClass = pid.find("Class");
1328 if (findClass == pid.end())
1329 {
1330
1331 phosphor::logging::log<phosphor::logging::level::ERR>(
1332 "ipmiOEMSetFanSpeedOffset: found illegal pid "
1333 "configurations");
1334 return ipmi::responseResponseError();
1335 }
1336 std::string type = std::get<std::string>(findClass->second);
1337 if (type == "fan")
1338 {
1339 auto findOutLimit = pid.find("OutLimitMin");
1340 if (findOutLimit == pid.end())
1341 {
1342
1343 phosphor::logging::log<phosphor::logging::level::ERR>(
1344 "ipmiOEMSetFanSpeedOffset: found illegal pid "
1345 "configurations");
1346 return ipmi::responseResponseError();
1347 }
1348 ipmi::setDbusProperty(dbus, "xyz.openbmc_project.EntityManager",
1349 path, pidConfigurationIface, "OutLimitMin",
1350 static_cast<double>(offset));
1351 found = true;
1352 }
1353 }
1354 if (!found)
1355 {
1356 phosphor::logging::log<phosphor::logging::level::ERR>(
1357 "ipmiOEMSetFanSpeedOffset: set no fan offsets");
1358 return ipmi::responseResponseError();
1359 }
1360
1361 return ipmi::responseSuccess();
1362}
1363
1364ipmi::RspType<> ipmiOEMSetFscParameter(uint8_t command, uint8_t param1,
1365 uint8_t param2)
James Feist5f957ca2019-03-14 15:33:55 -07001366{
1367 constexpr const size_t disableLimiting = 0x0;
1368
James Feistacc8a4e2019-04-02 14:23:57 -07001369 if (command == static_cast<uint8_t>(setFscParamFlags::tcontrol))
James Feist5f957ca2019-03-14 15:33:55 -07001370 {
James Feistacc8a4e2019-04-02 14:23:57 -07001371 if (param1 == legacyExitAirSensorNumber)
James Feistfaa4f222019-03-21 16:21:55 -07001372 {
James Feistfaa4f222019-03-21 16:21:55 -07001373 std::string path = getExitAirConfigPath();
1374 ipmi::setDbusProperty(dbus, "xyz.openbmc_project.EntityManager",
James Feistacc8a4e2019-04-02 14:23:57 -07001375 path, pidConfigurationIface, "SetPoint",
1376 static_cast<double>(param2));
1377 return ipmi::responseSuccess();
James Feistfaa4f222019-03-21 16:21:55 -07001378 }
1379 else
1380 {
James Feistacc8a4e2019-04-02 14:23:57 -07001381 return ipmi::responseParmOutOfRange();
James Feistfaa4f222019-03-21 16:21:55 -07001382 }
1383 }
James Feistacc8a4e2019-04-02 14:23:57 -07001384 else if (command == static_cast<uint8_t>(setFscParamFlags::cfm))
James Feist5f957ca2019-03-14 15:33:55 -07001385 {
James Feistacc8a4e2019-04-02 14:23:57 -07001386 uint16_t cfm = param1 | (static_cast<uint16_t>(param2) << 8);
James Feist5f957ca2019-03-14 15:33:55 -07001387
1388 // must be greater than 50 based on eps
1389 if (cfm < 50 && cfm != disableLimiting)
1390 {
James Feistacc8a4e2019-04-02 14:23:57 -07001391 return ipmi::responseParmOutOfRange();
James Feist5f957ca2019-03-14 15:33:55 -07001392 }
1393
1394 try
1395 {
1396 ipmi::setDbusProperty(dbus, settingsBusName, cfmLimitSettingPath,
1397 cfmLimitIface, "Limit",
1398 static_cast<double>(cfm));
1399 }
1400 catch (sdbusplus::exception_t& e)
1401 {
1402 phosphor::logging::log<phosphor::logging::level::ERR>(
1403 "ipmiOEMSetFscParameter: can't set cfm setting!",
1404 phosphor::logging::entry("ERR=%s", e.what()));
James Feistacc8a4e2019-04-02 14:23:57 -07001405 return ipmi::responseResponseError();
James Feist5f957ca2019-03-14 15:33:55 -07001406 }
James Feistacc8a4e2019-04-02 14:23:57 -07001407 return ipmi::responseSuccess();
1408 }
1409 else if (command == static_cast<uint8_t>(setFscParamFlags::maxPwm))
1410 {
1411 constexpr const size_t maxDomainCount = 8;
1412 uint8_t requestedDomainMask = param1;
1413 boost::container::flat_map data = getPidConfigs();
1414 if (data.empty())
1415 {
1416
1417 phosphor::logging::log<phosphor::logging::level::ERR>(
1418 "ipmiOEMSetFscParameter: found no pid configurations!");
1419 return ipmi::responseResponseError();
1420 }
1421 size_t count = 0;
1422 for (const auto& [path, pid] : data)
1423 {
1424 auto findClass = pid.find("Class");
1425 if (findClass == pid.end())
1426 {
1427
1428 phosphor::logging::log<phosphor::logging::level::ERR>(
1429 "ipmiOEMSetFscParameter: found illegal pid "
1430 "configurations");
1431 return ipmi::responseResponseError();
1432 }
1433 std::string type = std::get<std::string>(findClass->second);
1434 if (type == "fan")
1435 {
1436 if (requestedDomainMask & (1 << count))
1437 {
1438 ipmi::setDbusProperty(
1439 dbus, "xyz.openbmc_project.EntityManager", path,
1440 pidConfigurationIface, "OutLimitMax",
1441 static_cast<double>(param2));
1442 }
1443 count++;
1444 }
1445 }
1446 return ipmi::responseSuccess();
James Feist5f957ca2019-03-14 15:33:55 -07001447 }
1448 else
1449 {
1450 // todo other command parts possibly
1451 // tcontrol is handled in peci now
1452 // fan speed offset not implemented yet
1453 // domain pwm limit not implemented
James Feistacc8a4e2019-04-02 14:23:57 -07001454 return ipmi::responseParmOutOfRange();
James Feist5f957ca2019-03-14 15:33:55 -07001455 }
1456}
1457
James Feistacc8a4e2019-04-02 14:23:57 -07001458ipmi::RspType<
1459 std::variant<uint8_t, std::array<uint8_t, 2>, std::array<uint16_t, 2>>>
1460 ipmiOEMGetFscParameter(uint8_t command, std::optional<uint8_t> param)
James Feist5f957ca2019-03-14 15:33:55 -07001461{
James Feistfaa4f222019-03-21 16:21:55 -07001462 constexpr uint8_t legacyDefaultExitAirLimit = -128;
James Feist5f957ca2019-03-14 15:33:55 -07001463
James Feistacc8a4e2019-04-02 14:23:57 -07001464 if (command == static_cast<uint8_t>(setFscParamFlags::tcontrol))
James Feist5f957ca2019-03-14 15:33:55 -07001465 {
James Feistacc8a4e2019-04-02 14:23:57 -07001466 if (!param)
James Feistfaa4f222019-03-21 16:21:55 -07001467 {
James Feistacc8a4e2019-04-02 14:23:57 -07001468 return ipmi::responseReqDataLenInvalid();
James Feistfaa4f222019-03-21 16:21:55 -07001469 }
1470
James Feistacc8a4e2019-04-02 14:23:57 -07001471 if (*param != legacyExitAirSensorNumber)
James Feistfaa4f222019-03-21 16:21:55 -07001472 {
James Feistacc8a4e2019-04-02 14:23:57 -07001473 return ipmi::responseParmOutOfRange();
James Feistfaa4f222019-03-21 16:21:55 -07001474 }
1475 uint8_t setpoint = legacyDefaultExitAirLimit;
1476 std::string path = getExitAirConfigPath();
1477 if (path.size())
1478 {
James Feistacc8a4e2019-04-02 14:23:57 -07001479 Value val =
1480 ipmi::getDbusProperty(dbus, "xyz.openbmc_project.EntityManager",
1481 path, pidConfigurationIface, "SetPoint");
James Feistfaa4f222019-03-21 16:21:55 -07001482 setpoint = std::floor(std::get<double>(val) + 0.5);
1483 }
1484
1485 // old implementation used to return the "default" and current, we
1486 // don't make the default readily available so just make both the
1487 // same
James Feistfaa4f222019-03-21 16:21:55 -07001488
James Feistacc8a4e2019-04-02 14:23:57 -07001489 return ipmi::responseSuccess(
1490 std::array<uint8_t, 2>{setpoint, setpoint});
James Feistfaa4f222019-03-21 16:21:55 -07001491 }
James Feistacc8a4e2019-04-02 14:23:57 -07001492 else if (command == static_cast<uint8_t>(setFscParamFlags::maxPwm))
1493 {
1494 constexpr const size_t maxDomainCount = 8;
1495
1496 if (!param)
1497 {
1498 return ipmi::responseReqDataLenInvalid();
1499 }
1500 uint8_t requestedDomain = *param;
1501 if (requestedDomain >= maxDomainCount)
1502 {
1503 return ipmi::responseInvalidFieldRequest();
1504 }
1505
1506 boost::container::flat_map data = getPidConfigs();
1507 if (data.empty())
1508 {
1509 phosphor::logging::log<phosphor::logging::level::ERR>(
1510 "ipmiOEMGetFscParameter: found no pid configurations!");
1511 return ipmi::responseResponseError();
1512 }
1513 size_t count = 0;
1514 for (const auto& [_, pid] : data)
1515 {
1516 auto findClass = pid.find("Class");
1517 if (findClass == pid.end())
1518 {
1519 phosphor::logging::log<phosphor::logging::level::ERR>(
1520 "ipmiOEMGetFscParameter: found illegal pid "
1521 "configurations");
1522 return ipmi::responseResponseError();
1523 }
1524 std::string type = std::get<std::string>(findClass->second);
1525 if (type == "fan")
1526 {
1527 if (requestedDomain == count)
1528 {
1529 auto findOutLimit = pid.find("OutLimitMax");
1530 if (findOutLimit == pid.end())
1531 {
1532 phosphor::logging::log<phosphor::logging::level::ERR>(
1533 "ipmiOEMGetFscParameter: found illegal pid "
1534 "configurations");
1535 return ipmi::responseResponseError();
1536 }
1537
1538 return ipmi::responseSuccess(
1539 static_cast<uint8_t>(std::floor(
1540 std::get<double>(findOutLimit->second) + 0.5)));
1541 }
1542 else
1543 {
1544 count++;
1545 }
1546 }
1547 }
1548
1549 return ipmi::responseInvalidFieldRequest();
1550 }
1551 else if (command == static_cast<uint8_t>(setFscParamFlags::cfm))
James Feist5f957ca2019-03-14 15:33:55 -07001552 {
1553
1554 /*
1555 DataLen should be 1, but host is sending us an extra bit. As the
James Feistacc8a4e2019-04-02 14:23:57 -07001556 previous behavior didn't seem to prevent this, ignore the check for
1557 now.
James Feist5f957ca2019-03-14 15:33:55 -07001558
James Feistacc8a4e2019-04-02 14:23:57 -07001559 if (param)
James Feist5f957ca2019-03-14 15:33:55 -07001560 {
1561 phosphor::logging::log<phosphor::logging::level::ERR>(
1562 "ipmiOEMGetFscParameter: invalid input len!");
James Feist5f957ca2019-03-14 15:33:55 -07001563 return IPMI_CC_REQ_DATA_LEN_INVALID;
1564 }
1565 */
1566 Value cfmLimit;
1567 Value cfmMaximum;
1568 try
1569 {
1570 cfmLimit = ipmi::getDbusProperty(dbus, settingsBusName,
1571 cfmLimitSettingPath, cfmLimitIface,
1572 "Limit");
1573 cfmMaximum = ipmi::getDbusProperty(
1574 dbus, "xyz.openbmc_project.ExitAirTempSensor",
1575 "/xyz/openbmc_project/control/MaxCFM", cfmLimitIface, "Limit");
1576 }
1577 catch (sdbusplus::exception_t& e)
1578 {
1579 phosphor::logging::log<phosphor::logging::level::ERR>(
James Feistacc8a4e2019-04-02 14:23:57 -07001580 "ipmiOEMGetFscParameter: can't get cfm setting!",
James Feist5f957ca2019-03-14 15:33:55 -07001581 phosphor::logging::entry("ERR=%s", e.what()));
James Feistacc8a4e2019-04-02 14:23:57 -07001582 return ipmi::responseResponseError();
James Feist5f957ca2019-03-14 15:33:55 -07001583 }
1584
James Feistacc8a4e2019-04-02 14:23:57 -07001585 double cfmMax = std::get<double>(cfmMaximum);
1586 double cfmLim = std::get<double>(cfmLimit);
James Feist5f957ca2019-03-14 15:33:55 -07001587
James Feistacc8a4e2019-04-02 14:23:57 -07001588 cfmLim = std::floor(cfmLim + 0.5);
1589 cfmMax = std::floor(cfmMax + 0.5);
1590 uint16_t cfmLimResp = static_cast<uint16_t>(cfmLim);
1591 uint16_t cfmMaxResp = static_cast<uint16_t>(cfmMax);
James Feist5f957ca2019-03-14 15:33:55 -07001592
James Feistacc8a4e2019-04-02 14:23:57 -07001593 return ipmi::responseSuccess(
1594 std::array<uint16_t, 2>{cfmLimResp, cfmMaxResp});
James Feist5f957ca2019-03-14 15:33:55 -07001595 }
James Feistacc8a4e2019-04-02 14:23:57 -07001596
James Feist5f957ca2019-03-14 15:33:55 -07001597 else
1598 {
1599 // todo other command parts possibly
James Feist5f957ca2019-03-14 15:33:55 -07001600 // domain pwm limit not implemented
James Feistacc8a4e2019-04-02 14:23:57 -07001601 return ipmi::responseParmOutOfRange();
James Feist5f957ca2019-03-14 15:33:55 -07001602 }
1603}
1604
Zhu, Yungebe560b02019-04-21 21:19:21 -04001605ipmi::RspType<> ipmiOEMSetFaultIndication(uint8_t sourceId, uint8_t faultType,
1606 uint8_t faultState,
1607 uint8_t faultGroup,
1608 std::array<uint8_t, 8>& ledStateData)
1609{
1610 static constexpr const char* objpath = "/xyz/openbmc_project/EntityManager";
1611 static constexpr const char* intf = "xyz.openbmc_project.EntityManager";
1612 constexpr auto maxFaultType = static_cast<size_t>(RemoteFaultType::max);
1613 static const std::array<std::string, maxFaultType> faultNames = {
1614 "faultFan", "faultTemp", "faultPower",
1615 "faultDriveSlot", "faultSoftware", "faultMemory"};
1616 static constexpr const char* sysGpioPath = "/sys/class/gpio/gpio";
1617 static constexpr const char* postfixValue = "/value";
1618
1619 constexpr uint8_t maxFaultSource = 0x4;
1620 constexpr uint8_t skipLEDs = 0xFF;
1621 constexpr uint8_t pinSize = 64;
1622 constexpr uint8_t groupSize = 16;
1623
1624 std::vector<uint16_t> ledFaultPins(pinSize, 0xFFFF);
1625 uint64_t resFIndex = 0;
1626 std::string resFType;
1627 std::string service;
1628 ObjectValueTree valueTree;
1629
1630 // Validate the source, fault type
1631 if ((sourceId >= maxFaultSource) ||
1632 (faultType >= static_cast<int8_t>(RemoteFaultType::max)) ||
1633 (faultState >= static_cast<int8_t>(RemoteFaultState::maxFaultState)) ||
1634 (faultGroup >= static_cast<int8_t>(DimmFaultType::maxFaultGroup)))
1635 {
1636 return ipmi::responseParmOutOfRange();
1637 }
1638
1639 try
1640 {
1641 service = getService(dbus, intf, objpath);
1642 valueTree = getManagedObjects(dbus, service, "/");
1643 }
1644 catch (const std::exception& e)
1645 {
1646 phosphor::logging::log<phosphor::logging::level::ERR>(
1647 "No object implements interface",
1648 phosphor::logging::entry("SERVICE=%s", service.c_str()),
1649 phosphor::logging::entry("INTF=%s", intf));
1650 return ipmi::responseResponseError();
1651 }
1652
1653 if (valueTree.empty())
1654 {
1655 phosphor::logging::log<phosphor::logging::level::ERR>(
1656 "No object implements interface",
1657 phosphor::logging::entry("INTF=%s", intf));
1658 return ipmi::responseResponseError();
1659 }
1660
1661 for (const auto& item : valueTree)
1662 {
1663 // find LedFault configuration
1664 auto interface =
1665 item.second.find("xyz.openbmc_project.Configuration.LedFault");
1666 if (interface == item.second.end())
1667 {
1668 continue;
1669 }
1670
1671 // find matched fault type: faultMemmory / faultFan
1672 // find LedGpioPins/FaultIndex configuration
1673 auto propertyFaultType = interface->second.find("FaultType");
1674 auto propertyFIndex = interface->second.find("FaultIndex");
1675 auto ledIndex = interface->second.find("LedGpioPins");
1676
1677 if (propertyFaultType == interface->second.end() ||
1678 propertyFIndex == interface->second.end() ||
1679 ledIndex == interface->second.end())
1680 {
1681 continue;
1682 }
1683
1684 try
1685 {
1686 Value valIndex = propertyFIndex->second;
1687 resFIndex = std::get<uint64_t>(valIndex);
1688
1689 Value valFType = propertyFaultType->second;
1690 resFType = std::get<std::string>(valFType);
1691 }
1692 catch (const std::bad_variant_access& e)
1693 {
1694 phosphor::logging::log<phosphor::logging::level::ERR>(e.what());
1695 return ipmi::responseResponseError();
1696 }
1697 // find the matched requested fault type: faultMemmory or faultFan
1698 if (resFType != faultNames[faultType])
1699 {
1700 continue;
1701 }
1702
1703 // read LedGpioPins data
1704 std::vector<uint64_t> ledgpios;
1705 std::variant<std::vector<uint64_t>> message;
1706
1707 auto method = dbus.new_method_call(
1708 service.c_str(), (std::string(item.first)).c_str(),
1709 "org.freedesktop.DBus.Properties", "Get");
1710
1711 method.append("xyz.openbmc_project.Configuration.LedFault",
1712 "LedGpioPins");
1713
1714 try
1715 {
1716 auto reply = dbus.call(method);
1717 reply.read(message);
1718 ledgpios = std::get<std::vector<uint64_t>>(message);
1719 }
1720 catch (std::exception& e)
1721 {
1722 phosphor::logging::log<phosphor::logging::level::ERR>(e.what());
1723 return ipmi::responseResponseError();
1724 }
1725
1726 // Check the size to be sure it will never overflow on groupSize
1727 if (ledgpios.size() > groupSize)
1728 {
1729 phosphor::logging::log<phosphor::logging::level::ERR>(
1730 "Fault gpio Pins out of range!");
1731 return ipmi::responseParmOutOfRange();
1732 }
1733 // Store data, according to command data bit index order
1734 for (int i = 0; i < ledgpios.size(); i++)
1735 {
1736 ledFaultPins[i + groupSize * resFIndex] = ledgpios[i];
1737 }
1738 }
1739
1740 switch (RemoteFaultType(faultType))
1741 {
1742 case (RemoteFaultType::fan):
1743 case (RemoteFaultType::memory):
1744 {
1745 if (faultGroup == skipLEDs)
1746 {
1747 return ipmi::responseSuccess();
1748 }
1749
1750 uint64_t ledState = 0;
1751 // calculate led state bit filed count, each byte has 8bits
1752 // the maximum bits will be 8 * 8 bits
1753 constexpr uint8_t size = sizeof(ledStateData) * 8;
1754 for (int i = 0; i < sizeof(ledStateData); i++)
1755 {
1756 ledState = (uint64_t)(ledState << 8);
1757 ledState = (uint64_t)(ledState | (uint64_t)ledStateData[i]);
1758 }
1759
1760 std::bitset<size> ledStateBits(ledState);
1761 std::string gpioValue;
1762 for (int i = 0; i < size; i++)
1763 { // skip invalid value
1764 if (ledFaultPins[i] == 0xFFFF)
1765 {
1766 continue;
1767 }
1768
1769 std::string device = sysGpioPath +
1770 std::to_string(ledFaultPins[i]) +
1771 postfixValue;
1772 std::fstream gpioFile;
1773
1774 gpioFile.open(device, std::ios::out);
1775
1776 if (!gpioFile.good())
1777 {
1778 phosphor::logging::log<phosphor::logging::level::ERR>(
1779 "Not Find Led Gpio Device!",
1780 phosphor::logging::entry("DEVICE=%s", device.c_str()));
1781 return ipmi::responseResponseError();
1782 }
1783 gpioFile << std::to_string(
1784 static_cast<uint8_t>(ledStateBits[i]));
1785 gpioFile.close();
1786 }
1787 break;
1788 }
1789 default:
1790 {
1791 // now only support two fault types
1792 return ipmi::responseParmOutOfRange();
1793 }
1794 }
1795
1796 return ipmi::responseSuccess();
1797}
1798
Richard Marian Thomaiyarea537d52019-04-24 21:33:48 +05301799ipmi::RspType<uint8_t> ipmiOEMReadBoardProductId()
1800{
1801 uint8_t prodId = 0;
1802 try
1803 {
1804 const DbusObjectInfo& object = getDbusObject(
1805 dbus, "xyz.openbmc_project.Inventory.Item.Board",
1806 "/xyz/openbmc_project/inventory/system/board/", "Baseboard");
1807 const Value& propValue = getDbusProperty(
1808 dbus, object.second, object.first,
1809 "xyz.openbmc_project.Inventory.Item.Board", "ProductId");
1810 prodId = static_cast<uint8_t>(std::get<uint64_t>(propValue));
1811 }
1812 catch (std::exception& e)
1813 {
1814 phosphor::logging::log<phosphor::logging::level::ERR>(
1815 "ipmiOEMReadBoardProductId: Product ID read failed!",
1816 phosphor::logging::entry("ERR=%s", e.what()));
1817 }
1818 return ipmi::responseSuccess(prodId);
1819}
1820
Jason M. Bills64796042018-10-03 16:51:55 -07001821static void registerOEMFunctions(void)
Jia, Chunhuia835eaa2018-09-05 09:00:41 +08001822{
1823 phosphor::logging::log<phosphor::logging::level::INFO>(
1824 "Registering OEM commands");
Jason M. Bills64796042018-10-03 16:51:55 -07001825 ipmiPrintAndRegister(netfnIntcOEMGeneral, IPMI_CMD_WILDCARD, NULL,
1826 ipmiOEMWildcard,
1827 PRIVILEGE_USER); // wildcard default handler
1828 ipmiPrintAndRegister(netfunIntelAppOEM, IPMI_CMD_WILDCARD, NULL,
1829 ipmiOEMWildcard,
1830 PRIVILEGE_USER); // wildcard default handler
1831 ipmiPrintAndRegister(
1832 netfnIntcOEMGeneral,
1833 static_cast<ipmi_cmd_t>(
1834 IPMINetfnIntelOEMGeneralCmd::cmdGetChassisIdentifier),
1835 NULL, ipmiOEMGetChassisIdentifier,
1836 PRIVILEGE_USER); // get chassis identifier
1837 ipmiPrintAndRegister(
1838 netfnIntcOEMGeneral,
1839 static_cast<ipmi_cmd_t>(IPMINetfnIntelOEMGeneralCmd::cmdSetSystemGUID),
1840 NULL, ipmiOEMSetSystemGUID,
1841 PRIVILEGE_ADMIN); // set system guid
1842 ipmiPrintAndRegister(
1843 netfnIntcOEMGeneral,
1844 static_cast<ipmi_cmd_t>(IPMINetfnIntelOEMGeneralCmd::cmdSetBIOSID),
1845 NULL, ipmiOEMSetBIOSID, PRIVILEGE_ADMIN);
1846 ipmiPrintAndRegister(netfnIntcOEMGeneral,
1847 static_cast<ipmi_cmd_t>(
1848 IPMINetfnIntelOEMGeneralCmd::cmdGetOEMDeviceInfo),
1849 NULL, ipmiOEMGetDeviceInfo, PRIVILEGE_USER);
1850 ipmiPrintAndRegister(
1851 netfnIntcOEMGeneral,
1852 static_cast<ipmi_cmd_t>(
1853 IPMINetfnIntelOEMGeneralCmd::cmdGetAICSlotFRUIDSlotPosRecords),
1854 NULL, ipmiOEMGetAICFRU, PRIVILEGE_USER);
Jia, Chunhuicc49b542019-03-20 15:41:07 +08001855
1856 ipmi::registerHandler(
1857 ipmi::prioOpenBmcBase, ipmi::netFnOemOne,
1858 static_cast<ipmi::Cmd>(
1859 IPMINetfnIntelOEMGeneralCmd::cmdSendEmbeddedFWUpdStatus),
1860 ipmi::Privilege::Operator, ipmiOEMSendEmbeddedFwUpdStatus);
1861
Jason M. Bills64796042018-10-03 16:51:55 -07001862 ipmiPrintAndRegister(
1863 netfnIntcOEMGeneral,
1864 static_cast<ipmi_cmd_t>(
1865 IPMINetfnIntelOEMGeneralCmd::cmdSetPowerRestoreDelay),
1866 NULL, ipmiOEMSetPowerRestoreDelay, PRIVILEGE_OPERATOR);
1867 ipmiPrintAndRegister(
1868 netfnIntcOEMGeneral,
1869 static_cast<ipmi_cmd_t>(
1870 IPMINetfnIntelOEMGeneralCmd::cmdGetPowerRestoreDelay),
1871 NULL, ipmiOEMGetPowerRestoreDelay, PRIVILEGE_USER);
Suryakanth Sekard509eb92018-11-15 17:44:11 +05301872
1873 ipmi::registerHandler(
1874 ipmi::prioOpenBmcBase, ipmi::netFnOemOne,
1875 static_cast<ipmi::Cmd>(
1876 IPMINetfnIntelOEMGeneralCmd::cmdSetOEMUser2Activation),
1877 ipmi::Privilege::Callback, ipmiOEMSetUser2Activation);
1878
Richard Marian Thomaiyarfc5e9852019-04-14 15:06:27 +05301879 ipmi::registerHandler(
1880 ipmi::prioOpenBmcBase, ipmi::netFnOemOne,
1881 static_cast<ipmi::Cmd>(
1882 IPMINetfnIntelOEMGeneralCmd::cmdSetSpecialUserPassword),
1883 ipmi::Privilege::Callback, ipmiOEMSetSpecialUserPassword);
1884
Jason M. Bills64796042018-10-03 16:51:55 -07001885 ipmiPrintAndRegister(
1886 netfnIntcOEMGeneral,
1887 static_cast<ipmi_cmd_t>(
1888 IPMINetfnIntelOEMGeneralCmd::cmdGetProcessorErrConfig),
1889 NULL, ipmiOEMGetProcessorErrConfig, PRIVILEGE_USER);
1890 ipmiPrintAndRegister(
1891 netfnIntcOEMGeneral,
1892 static_cast<ipmi_cmd_t>(
1893 IPMINetfnIntelOEMGeneralCmd::cmdSetProcessorErrConfig),
1894 NULL, ipmiOEMSetProcessorErrConfig, PRIVILEGE_ADMIN);
Yong Li703922d2018-11-06 13:25:31 +08001895 ipmiPrintAndRegister(netfnIntcOEMGeneral,
1896 static_cast<ipmi_cmd_t>(
1897 IPMINetfnIntelOEMGeneralCmd::cmdSetShutdownPolicy),
1898 NULL, ipmiOEMSetShutdownPolicy, PRIVILEGE_ADMIN);
1899 ipmiPrintAndRegister(netfnIntcOEMGeneral,
1900 static_cast<ipmi_cmd_t>(
1901 IPMINetfnIntelOEMGeneralCmd::cmdGetShutdownPolicy),
1902 NULL, ipmiOEMGetShutdownPolicy, PRIVILEGE_ADMIN);
James Feist91244a62019-02-19 15:04:54 -08001903
1904 ipmiPrintAndRegister(
1905 netfnIntcOEMGeneral,
1906 static_cast<ipmi_cmd_t>(IPMINetfnIntelOEMGeneralCmd::cmdSetFanConfig),
1907 NULL, ipmiOEMSetFanConfig, PRIVILEGE_USER);
1908
1909 ipmiPrintAndRegister(
1910 netfnIntcOEMGeneral,
1911 static_cast<ipmi_cmd_t>(IPMINetfnIntelOEMGeneralCmd::cmdGetFanConfig),
1912 NULL, ipmiOEMGetFanConfig, PRIVILEGE_USER);
1913
James Feistacc8a4e2019-04-02 14:23:57 -07001914 ipmi::registerHandler(
1915 ipmi::prioOemBase, netfnIntcOEMGeneral,
1916 static_cast<ipmi::Cmd>(
1917 IPMINetfnIntelOEMGeneralCmd::cmdGetFanSpeedOffset),
1918 ipmi::Privilege::User, ipmiOEMGetFanSpeedOffset);
James Feist5f957ca2019-03-14 15:33:55 -07001919
James Feistacc8a4e2019-04-02 14:23:57 -07001920 ipmi::registerHandler(
1921 ipmi::prioOemBase, netfnIntcOEMGeneral,
1922 static_cast<ipmi::Cmd>(
1923 IPMINetfnIntelOEMGeneralCmd::cmdSetFanSpeedOffset),
1924 ipmi::Privilege::User, ipmiOEMSetFanSpeedOffset);
1925
1926 ipmi::registerHandler(
1927 ipmi::prioOemBase, netfnIntcOEMGeneral,
1928 static_cast<ipmi::Cmd>(IPMINetfnIntelOEMGeneralCmd::cmdSetFscParameter),
1929 ipmi::Privilege::User, ipmiOEMSetFscParameter);
1930
1931 ipmi::registerHandler(
1932 ipmi::prioOemBase, netfnIntcOEMGeneral,
1933 static_cast<ipmi::Cmd>(IPMINetfnIntelOEMGeneralCmd::cmdGetFscParameter),
1934 ipmi::Privilege::User, ipmiOEMGetFscParameter);
James Feist5f957ca2019-03-14 15:33:55 -07001935
Richard Marian Thomaiyarea537d52019-04-24 21:33:48 +05301936 ipmi::registerHandler(
1937 ipmi::prioOpenBmcBase, netfnIntcOEMGeneral,
1938 static_cast<ipmi::Cmd>(
1939 IPMINetfnIntelOEMGeneralCmd::cmdReadBaseBoardProductId),
1940 ipmi::Privilege::Admin, ipmiOEMReadBoardProductId);
1941
Kuiying Wang45f04982018-12-26 09:23:08 +08001942 ipmiPrintAndRegister(
1943 netfnIntcOEMGeneral,
1944 static_cast<ipmi_cmd_t>(IPMINetfnIntelOEMGeneralCmd::cmdGetLEDStatus),
1945 NULL, ipmiOEMGetLEDStatus, PRIVILEGE_ADMIN);
Yong Li23737fe2019-02-19 08:49:55 +08001946 ipmiPrintAndRegister(
1947 netfnIntcOEMPlatform,
1948 static_cast<ipmi_cmd_t>(
1949 IPMINetfnIntelOEMPlatformCmd::cmdCfgHostSerialPortSpeed),
1950 NULL, ipmiOEMCfgHostSerialPortSpeed, PRIVILEGE_ADMIN);
Zhu, Yungebe560b02019-04-21 21:19:21 -04001951 ipmi::registerHandler(
1952 ipmi::prioOemBase, netfnIntcOEMGeneral,
1953 static_cast<ipmi::Cmd>(
1954 IPMINetfnIntelOEMGeneralCmd::cmdSetFaultIndication),
1955 ipmi::Privilege::Operator, ipmiOEMSetFaultIndication);
Jia, Chunhuia835eaa2018-09-05 09:00:41 +08001956 return;
1957}
1958
1959} // namespace ipmi