blob: 36a0d14a7592fe36c7677d807c26c583ba917024 [file] [log] [blame]
Jia, Chunhuia835eaa2018-09-05 09:00:41 +08001/*
2// Copyright (c) 2018 Intel Corporation
3//
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at
7//
8// http://www.apache.org/licenses/LICENSE-2.0
9//
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15*/
16
Jason M. Bills64796042018-10-03 16:51:55 -070017#include "xyz/openbmc_project/Common/error.hpp"
Kuiying Wang45f04982018-12-26 09:23:08 +080018#include "xyz/openbmc_project/Led/Physical/server.hpp"
Jason M. Bills64796042018-10-03 16:51:55 -070019
Jia, Chunhuicc49b542019-03-20 15:41:07 +080020#include <systemd/sd-journal.h>
Jia, Chunhuia835eaa2018-09-05 09:00:41 +080021
22#include <array>
James Feist91244a62019-02-19 15:04:54 -080023#include <boost/container/flat_map.hpp>
Yong Li23737fe2019-02-19 08:49:55 +080024#include <boost/process/child.hpp>
25#include <boost/process/io.hpp>
Yong Li0669d192019-05-06 14:01:46 +080026#include <com/intel/Control/OCOTShutdownPolicy/server.hpp>
Jason M. Bills64796042018-10-03 16:51:55 -070027#include <commandutils.hpp>
Vernon Mauery4ac799d2019-05-20 15:50:37 -070028#include <filesystem>
Jia, Chunhuia835eaa2018-09-05 09:00:41 +080029#include <iostream>
Jia, Chunhuicc49b542019-03-20 15:41:07 +080030#include <ipmid/api.hpp>
Vernon Mauery5480ef62019-03-20 13:43:11 -070031#include <ipmid/utils.hpp>
Jia, Chunhuia835eaa2018-09-05 09:00:41 +080032#include <oemcommands.hpp>
Jia, Chunhuia835eaa2018-09-05 09:00:41 +080033#include <phosphor-logging/log.hpp>
34#include <sdbusplus/bus.hpp>
Suryakanth Sekard509eb92018-11-15 17:44:11 +053035#include <sdbusplus/message/types.hpp>
Jia, Chunhuia835eaa2018-09-05 09:00:41 +080036#include <string>
James Feist91244a62019-02-19 15:04:54 -080037#include <variant>
Jia, Chunhuia835eaa2018-09-05 09:00:41 +080038#include <vector>
Jia, Chunhuia835eaa2018-09-05 09:00:41 +080039
40namespace ipmi
41{
Jason M. Bills64796042018-10-03 16:51:55 -070042static void registerOEMFunctions() __attribute__((constructor));
Vernon Mauery4ac799d2019-05-20 15:50:37 -070043
44namespace netfn::intel
45{
46constexpr NetFn oemGeneral = netFnOemOne;
47constexpr Cmd cmdRestoreConfiguration = 0x02;
48} // namespace netfn::intel
49
Jason M. Bills6d9c83f2019-02-08 14:02:19 -080050sdbusplus::bus::bus dbus(ipmid_get_sd_bus_connection()); // from ipmid/api.h
Jason M. Bills64796042018-10-03 16:51:55 -070051static constexpr size_t maxFRUStringLength = 0x3F;
Jia, Chunhuia835eaa2018-09-05 09:00:41 +080052
Suryakanth Sekard509eb92018-11-15 17:44:11 +053053static constexpr auto ethernetIntf =
54 "xyz.openbmc_project.Network.EthernetInterface";
55static constexpr auto networkIPIntf = "xyz.openbmc_project.Network.IP";
56static constexpr auto networkService = "xyz.openbmc_project.Network";
57static constexpr auto networkRoot = "/xyz/openbmc_project/network";
58
Jia, Chunhuia835eaa2018-09-05 09:00:41 +080059// return code: 0 successful
60int8_t getChassisSerialNumber(sdbusplus::bus::bus& bus, std::string& serial)
61{
62 std::string objpath = "/xyz/openbmc_project/FruDevice";
63 std::string intf = "xyz.openbmc_project.FruDeviceManager";
64 std::string service = getService(bus, intf, objpath);
65 ObjectValueTree valueTree = getManagedObjects(bus, service, "/");
66 if (valueTree.empty())
67 {
68 phosphor::logging::log<phosphor::logging::level::ERR>(
69 "No object implements interface",
70 phosphor::logging::entry("INTF=%s", intf.c_str()));
71 return -1;
72 }
73
Jason M. Bills64796042018-10-03 16:51:55 -070074 for (const auto& item : valueTree)
Jia, Chunhuia835eaa2018-09-05 09:00:41 +080075 {
76 auto interface = item.second.find("xyz.openbmc_project.FruDevice");
77 if (interface == item.second.end())
78 {
79 continue;
80 }
81
82 auto property = interface->second.find("CHASSIS_SERIAL_NUMBER");
83 if (property == interface->second.end())
84 {
85 continue;
86 }
87
88 try
89 {
90 Value variant = property->second;
Vernon Mauery8166c8d2019-05-23 11:22:30 -070091 std::string& result = std::get<std::string>(variant);
Jason M. Bills64796042018-10-03 16:51:55 -070092 if (result.size() > maxFRUStringLength)
Jia, Chunhuia835eaa2018-09-05 09:00:41 +080093 {
94 phosphor::logging::log<phosphor::logging::level::ERR>(
95 "FRU serial number exceed maximum length");
Jia, Chunhuia835eaa2018-09-05 09:00:41 +080096 return -1;
97 }
Jason M. Bills64796042018-10-03 16:51:55 -070098 serial = result;
Jia, Chunhuia835eaa2018-09-05 09:00:41 +080099 return 0;
100 }
Vernon Mauery8166c8d2019-05-23 11:22:30 -0700101 catch (std::bad_variant_access& e)
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800102 {
Jason M. Bills64796042018-10-03 16:51:55 -0700103 phosphor::logging::log<phosphor::logging::level::ERR>(e.what());
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800104 return -1;
105 }
106 }
107 return -1;
108}
Jason M. Bills64796042018-10-03 16:51:55 -0700109
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800110ipmi_ret_t ipmiOEMWildcard(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
111 ipmi_request_t request, ipmi_response_t response,
Jason M. Bills64796042018-10-03 16:51:55 -0700112 ipmi_data_len_t dataLen, ipmi_context_t context)
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800113{
Jason M. Bills64796042018-10-03 16:51:55 -0700114 printCommand(+netfn, +cmd);
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800115 // Status code.
116 ipmi_ret_t rc = IPMI_CC_INVALID;
Jason M. Bills64796042018-10-03 16:51:55 -0700117 *dataLen = 0;
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800118 return rc;
119}
120
121// Returns the Chassis Identifier (serial #)
122ipmi_ret_t ipmiOEMGetChassisIdentifier(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
123 ipmi_request_t request,
124 ipmi_response_t response,
Jason M. Bills64796042018-10-03 16:51:55 -0700125 ipmi_data_len_t dataLen,
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800126 ipmi_context_t context)
127{
128 std::string serial;
Jason M. Bills64796042018-10-03 16:51:55 -0700129 if (*dataLen != 0) // invalid request if there are extra parameters
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800130 {
Jason M. Bills64796042018-10-03 16:51:55 -0700131 *dataLen = 0;
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800132 return IPMI_CC_REQ_DATA_LEN_INVALID;
133 }
Jason M. Bills64796042018-10-03 16:51:55 -0700134 if (getChassisSerialNumber(dbus, serial) == 0)
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800135 {
Jason M. Bills64796042018-10-03 16:51:55 -0700136 *dataLen = serial.size(); // length will never exceed response length
137 // as it is checked in getChassisSerialNumber
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800138 char* resp = static_cast<char*>(response);
Jason M. Bills64796042018-10-03 16:51:55 -0700139 serial.copy(resp, *dataLen);
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800140 return IPMI_CC_OK;
141 }
Jason M. Bills64796042018-10-03 16:51:55 -0700142 *dataLen = 0;
143 return IPMI_CC_RESPONSE_ERROR;
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800144}
145
146ipmi_ret_t ipmiOEMSetSystemGUID(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
147 ipmi_request_t request,
148 ipmi_response_t response,
Jason M. Bills64796042018-10-03 16:51:55 -0700149 ipmi_data_len_t dataLen, ipmi_context_t context)
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800150{
151 static constexpr size_t safeBufferLength = 50;
152 char buf[safeBufferLength] = {0};
153 GUIDData* Data = reinterpret_cast<GUIDData*>(request);
154
Jason M. Bills64796042018-10-03 16:51:55 -0700155 if (*dataLen != sizeof(GUIDData)) // 16bytes
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800156 {
Jason M. Bills64796042018-10-03 16:51:55 -0700157 *dataLen = 0;
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800158 return IPMI_CC_REQ_DATA_LEN_INVALID;
159 }
160
Jason M. Bills64796042018-10-03 16:51:55 -0700161 *dataLen = 0;
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800162
163 snprintf(
164 buf, safeBufferLength,
165 "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x",
166 Data->timeLow4, Data->timeLow3, Data->timeLow2, Data->timeLow1,
167 Data->timeMid2, Data->timeMid1, Data->timeHigh2, Data->timeHigh1,
168 Data->clock2, Data->clock1, Data->node6, Data->node5, Data->node4,
169 Data->node3, Data->node2, Data->node1);
170 // UUID is in RFC4122 format. Ex: 61a39523-78f2-11e5-9862-e6402cfc3223
171 std::string guid = buf;
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800172
173 std::string objpath = "/xyz/openbmc_project/control/host0/systemGUID";
174 std::string intf = "xyz.openbmc_project.Common.UUID";
Jason M. Bills64796042018-10-03 16:51:55 -0700175 std::string service = getService(dbus, intf, objpath);
176 setDbusProperty(dbus, service, objpath, intf, "UUID", guid);
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800177 return IPMI_CC_OK;
178}
179
180ipmi_ret_t ipmiOEMSetBIOSID(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
181 ipmi_request_t request, ipmi_response_t response,
182 ipmi_data_len_t dataLen, ipmi_context_t context)
183{
184 DeviceInfo* data = reinterpret_cast<DeviceInfo*>(request);
185
Jason M. Bills64796042018-10-03 16:51:55 -0700186 if ((*dataLen < 2) || (*dataLen != (1 + data->biosIDLength)))
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800187 {
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800188 *dataLen = 0;
189 return IPMI_CC_REQ_DATA_LEN_INVALID;
190 }
Jason M. Bills64796042018-10-03 16:51:55 -0700191 std::string idString((char*)data->biosId, data->biosIDLength);
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800192
Jason M. Bills64796042018-10-03 16:51:55 -0700193 std::string service = getService(dbus, biosIntf, biosObjPath);
194 setDbusProperty(dbus, service, biosObjPath, biosIntf, biosProp, idString);
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800195 uint8_t* bytesWritten = static_cast<uint8_t*>(response);
196 *bytesWritten =
Jason M. Bills64796042018-10-03 16:51:55 -0700197 data->biosIDLength; // how many bytes are written into storage
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800198 *dataLen = 1;
199 return IPMI_CC_OK;
200}
201
202ipmi_ret_t ipmiOEMGetDeviceInfo(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
203 ipmi_request_t request,
204 ipmi_response_t response,
205 ipmi_data_len_t dataLen, ipmi_context_t context)
206{
207 GetOemDeviceInfoReq* req = reinterpret_cast<GetOemDeviceInfoReq*>(request);
208 GetOemDeviceInfoRes* res = reinterpret_cast<GetOemDeviceInfoRes*>(response);
209
210 if (*dataLen == 0)
211 {
Jason M. Bills64796042018-10-03 16:51:55 -0700212 *dataLen = 0;
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800213 return IPMI_CC_REQ_DATA_LEN_INVALID;
214 }
215
216 size_t reqDataLen = *dataLen;
217 *dataLen = 0;
Jason M. Bills64796042018-10-03 16:51:55 -0700218 if (req->entityType > static_cast<uint8_t>(OEMDevEntityType::sdrVer))
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800219 {
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800220 return IPMI_CC_INVALID_FIELD_REQUEST;
221 }
222
223 // handle OEM command items
Jason M. Bills64796042018-10-03 16:51:55 -0700224 switch (OEMDevEntityType(req->entityType))
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800225 {
226 case OEMDevEntityType::biosId:
227 {
228 if (sizeof(GetOemDeviceInfoReq) != reqDataLen)
229 {
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800230 return IPMI_CC_REQ_DATA_LEN_INVALID;
231 }
232
Jason M. Bills64796042018-10-03 16:51:55 -0700233 std::string service = getService(dbus, biosIntf, biosObjPath);
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800234 try
235 {
Jason M. Bills64796042018-10-03 16:51:55 -0700236 Value variant = getDbusProperty(dbus, service, biosObjPath,
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800237 biosIntf, biosProp);
Vernon Mauery8166c8d2019-05-23 11:22:30 -0700238 std::string& idString = std::get<std::string>(variant);
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800239 if (req->offset >= idString.size())
240 {
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800241 return IPMI_CC_PARM_OUT_OF_RANGE;
242 }
Jason M. Bills64796042018-10-03 16:51:55 -0700243 size_t length = 0;
244 if (req->countToRead > (idString.size() - req->offset))
245 {
246 length = idString.size() - req->offset;
247 }
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800248 else
249 {
Jason M. Bills64796042018-10-03 16:51:55 -0700250 length = req->countToRead;
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800251 }
Jason M. Bills64796042018-10-03 16:51:55 -0700252 std::copy(idString.begin() + req->offset, idString.end(),
253 res->data);
254 res->resDatalen = length;
255 *dataLen = res->resDatalen + 1;
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800256 }
Vernon Mauery8166c8d2019-05-23 11:22:30 -0700257 catch (std::bad_variant_access& e)
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800258 {
Jason M. Bills64796042018-10-03 16:51:55 -0700259 phosphor::logging::log<phosphor::logging::level::ERR>(e.what());
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800260 return IPMI_CC_UNSPECIFIED_ERROR;
261 }
262 }
263 break;
264
265 case OEMDevEntityType::devVer:
266 case OEMDevEntityType::sdrVer:
267 // TODO:
268 return IPMI_CC_ILLEGAL_COMMAND;
269 default:
270 return IPMI_CC_INVALID_FIELD_REQUEST;
271 }
272 return IPMI_CC_OK;
273}
274
275ipmi_ret_t ipmiOEMGetAICFRU(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
276 ipmi_request_t request, ipmi_response_t response,
277 ipmi_data_len_t dataLen, ipmi_context_t context)
278{
279 if (*dataLen != 0)
280 {
Jason M. Bills64796042018-10-03 16:51:55 -0700281 *dataLen = 0;
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800282 return IPMI_CC_REQ_DATA_LEN_INVALID;
283 }
284
285 *dataLen = 1;
286 uint8_t* res = reinterpret_cast<uint8_t*>(response);
287 // temporary fix. We don't support AIC FRU now. Just tell BIOS that no
288 // AIC is available so that BIOS will not timeout repeatly which leads to
289 // slow booting.
290 *res = 0; // Byte1=Count of SlotPosition/FruID records.
291 return IPMI_CC_OK;
292}
293
Jason M. Bills64796042018-10-03 16:51:55 -0700294ipmi_ret_t ipmiOEMGetPowerRestoreDelay(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
295 ipmi_request_t request,
296 ipmi_response_t response,
297 ipmi_data_len_t dataLen,
298 ipmi_context_t context)
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800299{
Jason M. Bills64796042018-10-03 16:51:55 -0700300 GetPowerRestoreDelayRes* resp =
301 reinterpret_cast<GetPowerRestoreDelayRes*>(response);
302
303 if (*dataLen != 0)
304 {
305 *dataLen = 0;
306 return IPMI_CC_REQ_DATA_LEN_INVALID;
307 }
308
309 std::string service =
310 getService(dbus, powerRestoreDelayIntf, powerRestoreDelayObjPath);
311 Value variant =
312 getDbusProperty(dbus, service, powerRestoreDelayObjPath,
313 powerRestoreDelayIntf, powerRestoreDelayProp);
314
Vernon Mauery8166c8d2019-05-23 11:22:30 -0700315 uint16_t delay = std::get<uint16_t>(variant);
Jason M. Bills64796042018-10-03 16:51:55 -0700316 resp->byteLSB = delay;
317 resp->byteMSB = delay >> 8;
318
319 *dataLen = sizeof(GetPowerRestoreDelayRes);
320
321 return IPMI_CC_OK;
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800322}
323
Jia, Chunhuicc49b542019-03-20 15:41:07 +0800324static uint8_t bcdToDec(uint8_t val)
325{
326 return ((val / 16 * 10) + (val % 16));
327}
328
329// Allows an update utility or system BIOS to send the status of an embedded
330// firmware update attempt to the BMC. After received, BMC will create a logging
331// record.
332ipmi::RspType<> ipmiOEMSendEmbeddedFwUpdStatus(uint8_t status, uint8_t target,
333 uint8_t majorRevision,
334 uint8_t minorRevision,
335 uint32_t auxInfo)
336{
337 std::string firmware;
Jason M. Billsdc249272019-04-03 09:58:40 -0700338 int instance = (target & targetInstanceMask) >> targetInstanceShift;
Jia, Chunhuicc49b542019-03-20 15:41:07 +0800339 target = (target & selEvtTargetMask) >> selEvtTargetShift;
340
341 /* make sure the status is 0, 1, or 2 as per the spec */
342 if (status > 2)
343 {
344 return ipmi::response(ipmi::ccInvalidFieldRequest);
345 }
Jason M. Billsdc249272019-04-03 09:58:40 -0700346 /* make sure the target is 0, 1, 2, or 4 as per the spec */
347 if (target > 4 || target == 3)
348 {
349 return ipmi::response(ipmi::ccInvalidFieldRequest);
350 }
Jia, Chunhuicc49b542019-03-20 15:41:07 +0800351 /*orignal OEM command is to record OEM SEL.
352 But openbmc does not support OEM SEL, so we redirect it to redfish event
353 logging. */
354 std::string buildInfo;
355 std::string action;
356 switch (FWUpdateTarget(target))
357 {
358 case FWUpdateTarget::targetBMC:
359 firmware = "BMC";
Jason M. Billsdc249272019-04-03 09:58:40 -0700360 buildInfo = "major: " + std::to_string(majorRevision) + " minor: " +
Jia, Chunhuicc49b542019-03-20 15:41:07 +0800361 std::to_string(bcdToDec(minorRevision)) + // BCD encoded
362 " BuildID: " + std::to_string(auxInfo);
363 buildInfo += std::to_string(auxInfo);
364 break;
365 case FWUpdateTarget::targetBIOS:
366 firmware = "BIOS";
367 buildInfo =
Jason M. Billsdc249272019-04-03 09:58:40 -0700368 "major: " +
Jia, Chunhuicc49b542019-03-20 15:41:07 +0800369 std::to_string(bcdToDec(majorRevision)) + // BCD encoded
370 " minor: " +
371 std::to_string(bcdToDec(minorRevision)) + // BCD encoded
372 " ReleaseNumber: " + // ASCII encoded
373 std::to_string(static_cast<uint8_t>(auxInfo >> 0) - '0') +
374 std::to_string(static_cast<uint8_t>(auxInfo >> 8) - '0') +
375 std::to_string(static_cast<uint8_t>(auxInfo >> 16) - '0') +
376 std::to_string(static_cast<uint8_t>(auxInfo >> 24) - '0');
377 break;
378 case FWUpdateTarget::targetME:
379 firmware = "ME";
380 buildInfo =
Jason M. Billsdc249272019-04-03 09:58:40 -0700381 "major: " + std::to_string(majorRevision) + " minor1: " +
Jia, Chunhuicc49b542019-03-20 15:41:07 +0800382 std::to_string(bcdToDec(minorRevision)) + // BCD encoded
383 " minor2: " +
384 std::to_string(bcdToDec(static_cast<uint8_t>(auxInfo >> 0))) +
385 " build1: " +
386 std::to_string(bcdToDec(static_cast<uint8_t>(auxInfo >> 8))) +
387 " build2: " +
388 std::to_string(bcdToDec(static_cast<uint8_t>(auxInfo >> 16)));
389 break;
390 case FWUpdateTarget::targetOEMEWS:
391 firmware = "EWS";
Jason M. Billsdc249272019-04-03 09:58:40 -0700392 buildInfo = "major: " + std::to_string(majorRevision) + " minor: " +
Jia, Chunhuicc49b542019-03-20 15:41:07 +0800393 std::to_string(bcdToDec(minorRevision)) + // BCD encoded
394 " BuildID: " + std::to_string(auxInfo);
395 break;
396 }
397
Jason M. Billsdc249272019-04-03 09:58:40 -0700398 static const std::string openBMCMessageRegistryVersion("0.1");
399 std::string redfishMsgID = "OpenBMC." + openBMCMessageRegistryVersion;
400
Jia, Chunhuicc49b542019-03-20 15:41:07 +0800401 switch (status)
402 {
403 case 0x0:
404 action = "update started";
Jason M. Billsdc249272019-04-03 09:58:40 -0700405 redfishMsgID += ".FirmwareUpdateStarted";
Jia, Chunhuicc49b542019-03-20 15:41:07 +0800406 break;
407 case 0x1:
408 action = "update completed successfully";
Jason M. Billsdc249272019-04-03 09:58:40 -0700409 redfishMsgID += ".FirmwareUpdateCompleted";
Jia, Chunhuicc49b542019-03-20 15:41:07 +0800410 break;
411 case 0x2:
412 action = "update failure";
Jason M. Billsdc249272019-04-03 09:58:40 -0700413 redfishMsgID += ".FirmwareUpdateFailed";
Jia, Chunhuicc49b542019-03-20 15:41:07 +0800414 break;
415 default:
416 action = "unknown";
417 break;
418 }
419
Jason M. Billsdc249272019-04-03 09:58:40 -0700420 std::string firmwareInstanceStr =
421 firmware + " instance: " + std::to_string(instance);
422 std::string message("[firmware update] " + firmwareInstanceStr +
423 " status: <" + action + "> " + buildInfo);
Jia, Chunhuicc49b542019-03-20 15:41:07 +0800424
425 sd_journal_send("MESSAGE=%s", message.c_str(), "PRIORITY=%i", LOG_INFO,
Jason M. Billsdc249272019-04-03 09:58:40 -0700426 "REDFISH_MESSAGE_ID=%s", redfishMsgID.c_str(),
427 "REDFISH_MESSAGE_ARGS=%s,%s", firmwareInstanceStr.c_str(),
428 buildInfo.c_str(), NULL);
Jia, Chunhuicc49b542019-03-20 15:41:07 +0800429 return ipmi::responseSuccess();
430}
431
Jason M. Bills64796042018-10-03 16:51:55 -0700432ipmi_ret_t ipmiOEMSetPowerRestoreDelay(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
433 ipmi_request_t request,
434 ipmi_response_t response,
435 ipmi_data_len_t dataLen,
436 ipmi_context_t context)
437{
438 SetPowerRestoreDelayReq* data =
439 reinterpret_cast<SetPowerRestoreDelayReq*>(request);
440 uint16_t delay = 0;
441
442 if (*dataLen != sizeof(SetPowerRestoreDelayReq))
443 {
444 *dataLen = 0;
445 return IPMI_CC_REQ_DATA_LEN_INVALID;
446 }
447 delay = data->byteMSB;
448 delay = (delay << 8) | data->byteLSB;
449 std::string service =
450 getService(dbus, powerRestoreDelayIntf, powerRestoreDelayObjPath);
451 setDbusProperty(dbus, service, powerRestoreDelayObjPath,
452 powerRestoreDelayIntf, powerRestoreDelayProp, delay);
453 *dataLen = 0;
454
455 return IPMI_CC_OK;
456}
457
458ipmi_ret_t ipmiOEMGetProcessorErrConfig(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
459 ipmi_request_t request,
460 ipmi_response_t response,
461 ipmi_data_len_t dataLen,
462 ipmi_context_t context)
463{
464 GetProcessorErrConfigRes* resp =
465 reinterpret_cast<GetProcessorErrConfigRes*>(response);
466
467 if (*dataLen != 0)
468 {
469 *dataLen = 0;
470 return IPMI_CC_REQ_DATA_LEN_INVALID;
471 }
472
473 std::string service =
474 getService(dbus, processorErrConfigIntf, processorErrConfigObjPath);
475 Value variant = getDbusProperty(dbus, service, processorErrConfigObjPath,
476 processorErrConfigIntf, "ResetCfg");
Vernon Mauery8166c8d2019-05-23 11:22:30 -0700477 resp->resetCfg = std::get<uint8_t>(variant);
Jason M. Bills64796042018-10-03 16:51:55 -0700478
479 std::vector<uint8_t> caterrStatus;
Kuiying Wangbc546672018-11-23 15:41:05 +0800480 sdbusplus::message::variant<std::vector<uint8_t>> message;
Jason M. Bills64796042018-10-03 16:51:55 -0700481
482 auto method =
483 dbus.new_method_call(service.c_str(), processorErrConfigObjPath,
484 "org.freedesktop.DBus.Properties", "Get");
485
486 method.append(processorErrConfigIntf, "CATERRStatus");
Kuiying Wangbc546672018-11-23 15:41:05 +0800487 auto reply = dbus.call(method);
Jason M. Bills64796042018-10-03 16:51:55 -0700488
489 try
490 {
Kuiying Wangbc546672018-11-23 15:41:05 +0800491 reply.read(message);
Vernon Mauery8166c8d2019-05-23 11:22:30 -0700492 caterrStatus = std::get<std::vector<uint8_t>>(message);
Jason M. Bills64796042018-10-03 16:51:55 -0700493 }
494 catch (sdbusplus::exception_t&)
495 {
Kuiying Wangbc546672018-11-23 15:41:05 +0800496 phosphor::logging::log<phosphor::logging::level::ERR>(
Jason M. Bills64796042018-10-03 16:51:55 -0700497 "ipmiOEMGetProcessorErrConfig: error on dbus",
498 phosphor::logging::entry("PRORPERTY=CATERRStatus"),
499 phosphor::logging::entry("PATH=%s", processorErrConfigObjPath),
500 phosphor::logging::entry("INTERFACE=%s", processorErrConfigIntf));
501 return IPMI_CC_UNSPECIFIED_ERROR;
502 }
503
504 size_t len =
505 maxCPUNum <= caterrStatus.size() ? maxCPUNum : caterrStatus.size();
506 caterrStatus.resize(len);
507 std::copy(caterrStatus.begin(), caterrStatus.end(), resp->caterrStatus);
508 *dataLen = sizeof(GetProcessorErrConfigRes);
509
510 return IPMI_CC_OK;
511}
512
513ipmi_ret_t ipmiOEMSetProcessorErrConfig(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
514 ipmi_request_t request,
515 ipmi_response_t response,
516 ipmi_data_len_t dataLen,
517 ipmi_context_t context)
518{
519 SetProcessorErrConfigReq* req =
520 reinterpret_cast<SetProcessorErrConfigReq*>(request);
521
522 if (*dataLen != sizeof(SetProcessorErrConfigReq))
523 {
524 *dataLen = 0;
525 return IPMI_CC_REQ_DATA_LEN_INVALID;
526 }
527 std::string service =
528 getService(dbus, processorErrConfigIntf, processorErrConfigObjPath);
529 setDbusProperty(dbus, service, processorErrConfigObjPath,
530 processorErrConfigIntf, "ResetCfg", req->resetCfg);
531
532 setDbusProperty(dbus, service, processorErrConfigObjPath,
533 processorErrConfigIntf, "ResetErrorOccurrenceCounts",
534 req->resetErrorOccurrenceCounts);
535 *dataLen = 0;
536
537 return IPMI_CC_OK;
538}
539
Yong Li703922d2018-11-06 13:25:31 +0800540ipmi_ret_t ipmiOEMGetShutdownPolicy(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
541 ipmi_request_t request,
542 ipmi_response_t response,
543 ipmi_data_len_t dataLen,
544 ipmi_context_t context)
545{
546 GetOEMShutdownPolicyRes* resp =
547 reinterpret_cast<GetOEMShutdownPolicyRes*>(response);
548
549 if (*dataLen != 0)
550 {
551 phosphor::logging::log<phosphor::logging::level::ERR>(
Kuiying Wang45f04982018-12-26 09:23:08 +0800552 "oem_get_shutdown_policy: invalid input len!");
Yong Li703922d2018-11-06 13:25:31 +0800553 *dataLen = 0;
554 return IPMI_CC_REQ_DATA_LEN_INVALID;
555 }
556
557 *dataLen = 0;
558
559 try
560 {
561 std::string service =
562 getService(dbus, oemShutdownPolicyIntf, oemShutdownPolicyObjPath);
563 Value variant = getDbusProperty(dbus, service, oemShutdownPolicyObjPath,
564 oemShutdownPolicyIntf,
565 oemShutdownPolicyObjPathProp);
Yong Li0669d192019-05-06 14:01:46 +0800566
567 if (sdbusplus::com::intel::Control::server::OCOTShutdownPolicy::
568 convertPolicyFromString(std::get<std::string>(variant)) ==
569 sdbusplus::com::intel::Control::server::OCOTShutdownPolicy::Policy::
570 NoShutdownOnOCOT)
571 {
572 resp->policy = 0;
573 }
574 else if (sdbusplus::com::intel::Control::server::OCOTShutdownPolicy::
575 convertPolicyFromString(std::get<std::string>(variant)) ==
576 sdbusplus::com::intel::Control::server::OCOTShutdownPolicy::
577 Policy::ShutdownOnOCOT)
578 {
579 resp->policy = 1;
580 }
581 else
582 {
583 phosphor::logging::log<phosphor::logging::level::ERR>(
584 "oem_set_shutdown_policy: invalid property!",
585 phosphor::logging::entry(
586 "PROP=%s", std::get<std::string>(variant).c_str()));
587 return IPMI_CC_UNSPECIFIED_ERROR;
588 }
Yong Li703922d2018-11-06 13:25:31 +0800589 // TODO needs to check if it is multi-node products,
590 // policy is only supported on node 3/4
591 resp->policySupport = shutdownPolicySupported;
592 }
593 catch (sdbusplus::exception_t& e)
594 {
595 phosphor::logging::log<phosphor::logging::level::ERR>(e.description());
596 return IPMI_CC_UNSPECIFIED_ERROR;
597 }
598
599 *dataLen = sizeof(GetOEMShutdownPolicyRes);
600 return IPMI_CC_OK;
601}
602
603ipmi_ret_t ipmiOEMSetShutdownPolicy(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
604 ipmi_request_t request,
605 ipmi_response_t response,
606 ipmi_data_len_t dataLen,
607 ipmi_context_t context)
608{
609 uint8_t* req = reinterpret_cast<uint8_t*>(request);
Yong Li0669d192019-05-06 14:01:46 +0800610 sdbusplus::com::intel::Control::server::OCOTShutdownPolicy::Policy policy =
611 sdbusplus::com::intel::Control::server::OCOTShutdownPolicy::Policy::
612 NoShutdownOnOCOT;
Yong Li703922d2018-11-06 13:25:31 +0800613
614 // TODO needs to check if it is multi-node products,
615 // policy is only supported on node 3/4
616 if (*dataLen != 1)
617 {
618 phosphor::logging::log<phosphor::logging::level::ERR>(
619 "oem_set_shutdown_policy: invalid input len!");
620 *dataLen = 0;
621 return IPMI_CC_REQ_DATA_LEN_INVALID;
622 }
623
624 *dataLen = 0;
625 if ((*req != noShutdownOnOCOT) && (*req != shutdownOnOCOT))
626 {
627 phosphor::logging::log<phosphor::logging::level::ERR>(
628 "oem_set_shutdown_policy: invalid input!");
629 return IPMI_CC_INVALID_FIELD_REQUEST;
630 }
631
Yong Li0669d192019-05-06 14:01:46 +0800632 if (*req == noShutdownOnOCOT)
633 {
634 policy = sdbusplus::com::intel::Control::server::OCOTShutdownPolicy::
635 Policy::NoShutdownOnOCOT;
636 }
637 else
638 {
639 policy = sdbusplus::com::intel::Control::server::OCOTShutdownPolicy::
640 Policy::ShutdownOnOCOT;
641 }
642
Yong Li703922d2018-11-06 13:25:31 +0800643 try
644 {
645 std::string service =
646 getService(dbus, oemShutdownPolicyIntf, oemShutdownPolicyObjPath);
Yong Li0669d192019-05-06 14:01:46 +0800647 setDbusProperty(
648 dbus, service, oemShutdownPolicyObjPath, oemShutdownPolicyIntf,
649 oemShutdownPolicyObjPathProp,
650 sdbusplus::com::intel::Control::server::convertForMessage(policy));
Yong Li703922d2018-11-06 13:25:31 +0800651 }
652 catch (sdbusplus::exception_t& e)
653 {
654 phosphor::logging::log<phosphor::logging::level::ERR>(e.description());
655 return IPMI_CC_UNSPECIFIED_ERROR;
656 }
657
658 return IPMI_CC_OK;
659}
660
Suryakanth Sekard509eb92018-11-15 17:44:11 +0530661/** @brief implementation for check the DHCP or not in IPv4
662 * @param[in] Channel - Channel number
663 * @returns true or false.
664 */
665static bool isDHCPEnabled(uint8_t Channel)
666{
667 try
668 {
669 auto ethdevice = getChannelName(Channel);
670 if (ethdevice.empty())
671 {
672 return false;
673 }
674 auto ethIP = ethdevice + "/ipv4";
675 auto ethernetObj =
676 getDbusObject(dbus, networkIPIntf, networkRoot, ethIP);
677 auto value = getDbusProperty(dbus, networkService, ethernetObj.first,
678 networkIPIntf, "Origin");
Vernon Mauery8166c8d2019-05-23 11:22:30 -0700679 if (std::get<std::string>(value) ==
Suryakanth Sekard509eb92018-11-15 17:44:11 +0530680 "xyz.openbmc_project.Network.IP.AddressOrigin.DHCP")
681 {
682 return true;
683 }
684 else
685 {
686 return false;
687 }
688 }
689 catch (sdbusplus::exception_t& e)
690 {
691 phosphor::logging::log<phosphor::logging::level::ERR>(e.description());
692 return true;
693 }
694}
695
696/** @brief implementes for check the DHCP or not in IPv6
697 * @param[in] Channel - Channel number
698 * @returns true or false.
699 */
700static bool isDHCPIPv6Enabled(uint8_t Channel)
701{
702
703 try
704 {
705 auto ethdevice = getChannelName(Channel);
706 if (ethdevice.empty())
707 {
708 return false;
709 }
710 auto ethIP = ethdevice + "/ipv6";
711 auto objectInfo =
712 getDbusObject(dbus, networkIPIntf, networkRoot, ethIP);
713 auto properties = getAllDbusProperties(dbus, objectInfo.second,
714 objectInfo.first, networkIPIntf);
Vernon Mauery8166c8d2019-05-23 11:22:30 -0700715 if (std::get<std::string>(properties["Origin"]) ==
Suryakanth Sekard509eb92018-11-15 17:44:11 +0530716 "xyz.openbmc_project.Network.IP.AddressOrigin.DHCP")
717 {
718 return true;
719 }
720 else
721 {
722 return false;
723 }
724 }
725 catch (sdbusplus::exception_t& e)
726 {
727 phosphor::logging::log<phosphor::logging::level::ERR>(e.description());
728 return true;
729 }
730}
731
732/** @brief implementes the creating of default new user
733 * @param[in] userName - new username in 16 bytes.
734 * @param[in] userPassword - new password in 20 bytes
735 * @returns ipmi completion code.
736 */
737ipmi::RspType<> ipmiOEMSetUser2Activation(
738 std::array<uint8_t, ipmi::ipmiMaxUserName>& userName,
739 std::array<uint8_t, ipmi::maxIpmi20PasswordSize>& userPassword)
740{
741 bool userState = false;
742 // Check for System Interface not exist and LAN should be static
743 for (uint8_t channel = 0; channel < maxIpmiChannels; channel++)
744 {
745 ChannelInfo chInfo;
746 try
747 {
748 getChannelInfo(channel, chInfo);
749 }
750 catch (sdbusplus::exception_t& e)
751 {
752 phosphor::logging::log<phosphor::logging::level::ERR>(
753 "ipmiOEMSetUser2Activation: Failed to get Channel Info",
754 phosphor::logging::entry("MSG: %s", e.description()));
755 return ipmi::response(ipmi::ccUnspecifiedError);
756 }
757 if (chInfo.mediumType ==
758 static_cast<uint8_t>(EChannelMediumType::systemInterface))
759 {
760 phosphor::logging::log<phosphor::logging::level::ERR>(
761 "ipmiOEMSetUser2Activation: system interface exist .");
762 return ipmi::response(ipmi::ccCommandNotAvailable);
763 }
764 else
765 {
766
767 if (chInfo.mediumType ==
768 static_cast<uint8_t>(EChannelMediumType::lan8032))
769 {
770 if (isDHCPIPv6Enabled(channel) || isDHCPEnabled(channel))
771 {
772 phosphor::logging::log<phosphor::logging::level::ERR>(
773 "ipmiOEMSetUser2Activation: DHCP enabled .");
774 return ipmi::response(ipmi::ccCommandNotAvailable);
775 }
776 }
777 }
778 }
779 uint8_t maxChUsers = 0, enabledUsers = 0, fixedUsers = 0;
780 if (ipmi::ccSuccess ==
781 ipmiUserGetAllCounts(maxChUsers, enabledUsers, fixedUsers))
782 {
783 if (enabledUsers > 1)
784 {
785 phosphor::logging::log<phosphor::logging::level::ERR>(
786 "ipmiOEMSetUser2Activation: more than one user is enabled.");
787 return ipmi::response(ipmi::ccCommandNotAvailable);
788 }
789 // Check the user 2 is enabled or not
790 ipmiUserCheckEnabled(ipmiDefaultUserId, userState);
791 if (userState == true)
792 {
793 phosphor::logging::log<phosphor::logging::level::ERR>(
794 "ipmiOEMSetUser2Activation: user 2 already enabled .");
795 return ipmi::response(ipmi::ccCommandNotAvailable);
796 }
797 }
798 else
799 {
800 return ipmi::response(ipmi::ccUnspecifiedError);
801 }
802
803#if BYTE_ORDER == LITTLE_ENDIAN
804 PrivAccess privAccess = {PRIVILEGE_ADMIN, true, true, true, 0};
805#endif
806#if BYTE_ORDER == BIG_ENDIAN
807 PrivAccess privAccess = {0, true, true, true, PRIVILEGE_ADMIN};
808#endif
809
810 if (ipmi::ccSuccess ==
811 ipmiUserSetUserName(ipmiDefaultUserId,
812 reinterpret_cast<const char*>(userName.data())))
813 {
814 if (ipmi::ccSuccess ==
815 ipmiUserSetUserPassword(
816 ipmiDefaultUserId,
817 reinterpret_cast<const char*>(userPassword.data())))
818 {
819 if (ipmi::ccSuccess ==
820 ipmiUserSetPrivilegeAccess(
821 ipmiDefaultUserId,
822 static_cast<uint8_t>(ipmi::EChannelID::chanLan1),
823 privAccess, true))
824 {
825 phosphor::logging::log<phosphor::logging::level::INFO>(
826 "ipmiOEMSetUser2Activation: user created successfully ");
827 return ipmi::responseSuccess();
828 }
829 }
830 // we need to delete the default user id which added in this command as
831 // password / priv setting is failed.
832 ipmiUserSetUserName(ipmiDefaultUserId, "");
833 phosphor::logging::log<phosphor::logging::level::ERR>(
834 "ipmiOEMSetUser2Activation: password / priv setting is failed.");
835 }
836 else
837 {
838 phosphor::logging::log<phosphor::logging::level::ERR>(
839 "ipmiOEMSetUser2Activation: Setting username failed.");
840 }
841
842 return ipmi::response(ipmi::ccCommandNotAvailable);
843}
844
Richard Marian Thomaiyarfc5e9852019-04-14 15:06:27 +0530845/** @brief implementes setting password for special user
846 * @param[in] specialUserIndex
847 * @param[in] userPassword - new password in 20 bytes
848 * @returns ipmi completion code.
849 */
850ipmi::RspType<> ipmiOEMSetSpecialUserPassword(ipmi::Context::ptr ctx,
851 uint8_t specialUserIndex,
852 std::vector<uint8_t> userPassword)
853{
854 ChannelInfo chInfo;
855 try
856 {
857 getChannelInfo(ctx->channel, chInfo);
858 }
859 catch (sdbusplus::exception_t& e)
860 {
861 phosphor::logging::log<phosphor::logging::level::ERR>(
862 "ipmiOEMSetSpecialUserPassword: Failed to get Channel Info",
863 phosphor::logging::entry("MSG: %s", e.description()));
864 return ipmi::responseUnspecifiedError();
865 }
866 if (chInfo.mediumType !=
867 static_cast<uint8_t>(EChannelMediumType::systemInterface))
868 {
869 phosphor::logging::log<phosphor::logging::level::ERR>(
870 "ipmiOEMSetSpecialUserPassword: Error - supported only in KCS "
871 "interface");
872 return ipmi::responseCommandNotAvailable();
873 }
874 if (specialUserIndex != 0)
875 {
876 phosphor::logging::log<phosphor::logging::level::ERR>(
877 "ipmiOEMSetSpecialUserPassword: Invalid user account");
878 return ipmi::responseParmOutOfRange();
879 }
880 constexpr uint8_t minPasswordSizeRequired = 6;
881 if (userPassword.size() < minPasswordSizeRequired ||
882 userPassword.size() > ipmi::maxIpmi20PasswordSize)
883 {
884 return ipmi::responseReqDataLenInvalid();
885 }
886 std::string passwd;
887 passwd.assign(reinterpret_cast<const char*>(userPassword.data()),
888 userPassword.size());
889 return ipmi::response(ipmiSetSpecialUserPassword("root", passwd));
890}
891
Kuiying Wang45f04982018-12-26 09:23:08 +0800892namespace ledAction
893{
894using namespace sdbusplus::xyz::openbmc_project::Led::server;
895std::map<Physical::Action, uint8_t> actionDbusToIpmi = {
896 {Physical::Action::Off, 0x00},
897 {Physical::Action::On, 0x10},
898 {Physical::Action::Blink, 0x01}};
899
900std::map<uint8_t, std::string> offsetObjPath = {
901 {2, statusAmberObjPath}, {4, statusGreenObjPath}, {6, identifyLEDObjPath}};
902
903} // namespace ledAction
904
905int8_t getLEDState(sdbusplus::bus::bus& bus, const std::string& intf,
906 const std::string& objPath, uint8_t& state)
907{
908 try
909 {
910 std::string service = getService(bus, intf, objPath);
911 Value stateValue =
912 getDbusProperty(bus, service, objPath, intf, "State");
Vernon Mauery8166c8d2019-05-23 11:22:30 -0700913 std::string strState = std::get<std::string>(stateValue);
Kuiying Wang45f04982018-12-26 09:23:08 +0800914 state = ledAction::actionDbusToIpmi.at(
915 sdbusplus::xyz::openbmc_project::Led::server::Physical::
916 convertActionFromString(strState));
917 }
918 catch (sdbusplus::exception::SdBusError& e)
919 {
920 phosphor::logging::log<phosphor::logging::level::ERR>(e.what());
921 return -1;
922 }
923 return 0;
924}
925
926ipmi_ret_t ipmiOEMGetLEDStatus(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
927 ipmi_request_t request, ipmi_response_t response,
928 ipmi_data_len_t dataLen, ipmi_context_t context)
929{
930 uint8_t* resp = reinterpret_cast<uint8_t*>(response);
931 // LED Status
932 //[1:0] = Reserved
933 //[3:2] = Status(Amber)
934 //[5:4] = Status(Green)
935 //[7:6] = System Identify
936 // Status definitions:
937 // 00b = Off
938 // 01b = Blink
939 // 10b = On
940 // 11b = invalid
941 if (*dataLen != 0)
942 {
943 phosphor::logging::log<phosphor::logging::level::ERR>(
944 "oem_get_led_status: invalid input len!");
945 *dataLen = 0;
946 return IPMI_CC_REQ_DATA_LEN_INVALID;
947 }
948
949 phosphor::logging::log<phosphor::logging::level::DEBUG>("GET led status");
950 *resp = 0;
951 *dataLen = 0;
952 for (auto it = ledAction::offsetObjPath.begin();
953 it != ledAction::offsetObjPath.end(); ++it)
954 {
955 uint8_t state = 0;
956 if (-1 == getLEDState(dbus, ledIntf, it->second, state))
957 {
958 phosphor::logging::log<phosphor::logging::level::ERR>(
959 "oem_get_led_status: fail to get ID LED status!");
960 return IPMI_CC_UNSPECIFIED_ERROR;
961 }
962 *resp |= state << it->first;
963 }
964
965 *dataLen = sizeof(*resp);
966 return IPMI_CC_OK;
967}
968
Yong Li23737fe2019-02-19 08:49:55 +0800969ipmi_ret_t ipmiOEMCfgHostSerialPortSpeed(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
970 ipmi_request_t request,
971 ipmi_response_t response,
972 ipmi_data_len_t dataLen,
973 ipmi_context_t context)
974{
975 CfgHostSerialReq* req = reinterpret_cast<CfgHostSerialReq*>(request);
976 uint8_t* resp = reinterpret_cast<uint8_t*>(response);
977
978 if (*dataLen == 0)
979 {
980 phosphor::logging::log<phosphor::logging::level::ERR>(
981 "CfgHostSerial: invalid input len!",
982 phosphor::logging::entry("LEN=%d", *dataLen));
983 return IPMI_CC_REQ_DATA_LEN_INVALID;
984 }
985
986 switch (req->command)
987 {
988 case getHostSerialCfgCmd:
989 {
990 if (*dataLen != 1)
991 {
992 phosphor::logging::log<phosphor::logging::level::ERR>(
993 "CfgHostSerial: invalid input len!");
994 *dataLen = 0;
995 return IPMI_CC_REQ_DATA_LEN_INVALID;
996 }
997
998 *dataLen = 0;
999
1000 boost::process::ipstream is;
1001 std::vector<std::string> data;
1002 std::string line;
1003 boost::process::child c1(fwGetEnvCmd, "-n", fwHostSerailCfgEnvName,
1004 boost::process::std_out > is);
1005
1006 while (c1.running() && std::getline(is, line) && !line.empty())
1007 {
1008 data.push_back(line);
1009 }
1010
1011 c1.wait();
1012 if (c1.exit_code())
1013 {
1014 phosphor::logging::log<phosphor::logging::level::ERR>(
1015 "CfgHostSerial:: error on execute",
1016 phosphor::logging::entry("EXECUTE=%s", fwSetEnvCmd));
1017 // Using the default value
1018 *resp = 0;
1019 }
1020 else
1021 {
1022 if (data.size() != 1)
1023 {
1024 phosphor::logging::log<phosphor::logging::level::ERR>(
1025 "CfgHostSerial:: error on read env");
1026 return IPMI_CC_UNSPECIFIED_ERROR;
1027 }
1028 try
1029 {
1030 unsigned long tmp = std::stoul(data[0]);
1031 if (tmp > std::numeric_limits<uint8_t>::max())
1032 {
1033 throw std::out_of_range("Out of range");
1034 }
1035 *resp = static_cast<uint8_t>(tmp);
1036 }
1037 catch (const std::invalid_argument& e)
1038 {
1039 phosphor::logging::log<phosphor::logging::level::ERR>(
1040 "invalid config ",
1041 phosphor::logging::entry("ERR=%s", e.what()));
1042 return IPMI_CC_UNSPECIFIED_ERROR;
1043 }
1044 catch (const std::out_of_range& e)
1045 {
1046 phosphor::logging::log<phosphor::logging::level::ERR>(
1047 "out_of_range config ",
1048 phosphor::logging::entry("ERR=%s", e.what()));
1049 return IPMI_CC_UNSPECIFIED_ERROR;
1050 }
1051 }
1052
1053 *dataLen = 1;
1054 break;
1055 }
1056 case setHostSerialCfgCmd:
1057 {
1058 if (*dataLen != sizeof(CfgHostSerialReq))
1059 {
1060 phosphor::logging::log<phosphor::logging::level::ERR>(
1061 "CfgHostSerial: invalid input len!");
1062 *dataLen = 0;
1063 return IPMI_CC_REQ_DATA_LEN_INVALID;
1064 }
1065
1066 *dataLen = 0;
1067
1068 if (req->parameter > HostSerialCfgParamMax)
1069 {
1070 phosphor::logging::log<phosphor::logging::level::ERR>(
1071 "CfgHostSerial: invalid input!");
1072 return IPMI_CC_INVALID_FIELD_REQUEST;
1073 }
1074
1075 boost::process::child c1(fwSetEnvCmd, fwHostSerailCfgEnvName,
1076 std::to_string(req->parameter));
1077
1078 c1.wait();
1079 if (c1.exit_code())
1080 {
1081 phosphor::logging::log<phosphor::logging::level::ERR>(
1082 "CfgHostSerial:: error on execute",
1083 phosphor::logging::entry("EXECUTE=%s", fwGetEnvCmd));
1084 return IPMI_CC_UNSPECIFIED_ERROR;
1085 }
1086 break;
1087 }
1088 default:
1089 phosphor::logging::log<phosphor::logging::level::ERR>(
1090 "CfgHostSerial: invalid input!");
1091 *dataLen = 0;
1092 return IPMI_CC_INVALID_FIELD_REQUEST;
1093 }
1094
1095 return IPMI_CC_OK;
1096}
1097
James Feist91244a62019-02-19 15:04:54 -08001098constexpr const char* thermalModeInterface =
1099 "xyz.openbmc_project.Control.ThermalMode";
1100constexpr const char* thermalModePath =
1101 "/xyz/openbmc_project/control/thermal_mode";
1102
1103bool getFanProfileInterface(
1104 sdbusplus::bus::bus& bus,
1105 boost::container::flat_map<
1106 std::string, std::variant<std::vector<std::string>, std::string>>& resp)
1107{
1108 auto call = bus.new_method_call(settingsBusName, thermalModePath, PROP_INTF,
1109 "GetAll");
1110 call.append(thermalModeInterface);
1111 try
1112 {
1113 auto data = bus.call(call);
1114 data.read(resp);
1115 }
1116 catch (sdbusplus::exception_t& e)
1117 {
1118 phosphor::logging::log<phosphor::logging::level::ERR>(
1119 "getFanProfileInterface: can't get thermal mode!",
1120 phosphor::logging::entry("ERR=%s", e.what()));
1121 return false;
1122 }
1123 return true;
1124}
1125
1126ipmi_ret_t ipmiOEMSetFanConfig(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
1127 ipmi_request_t request, ipmi_response_t response,
1128 ipmi_data_len_t dataLen, ipmi_context_t context)
1129{
1130
1131 if (*dataLen < 2 || *dataLen > 7)
1132 {
1133 phosphor::logging::log<phosphor::logging::level::ERR>(
1134 "ipmiOEMSetFanConfig: invalid input len!");
1135 *dataLen = 0;
1136 return IPMI_CC_REQ_DATA_LEN_INVALID;
1137 }
1138
1139 // todo: tell bios to only send first 2 bytes
1140
1141 SetFanConfigReq* req = reinterpret_cast<SetFanConfigReq*>(request);
1142 boost::container::flat_map<
1143 std::string, std::variant<std::vector<std::string>, std::string>>
1144 profileData;
1145 if (!getFanProfileInterface(dbus, profileData))
1146 {
1147 return IPMI_CC_UNSPECIFIED_ERROR;
1148 }
1149
1150 std::vector<std::string>* supported =
1151 std::get_if<std::vector<std::string>>(&profileData["Supported"]);
1152 if (supported == nullptr)
1153 {
1154 return IPMI_CC_INVALID_FIELD_REQUEST;
1155 }
1156 std::string mode;
1157 if (req->flags &
1158 (1 << static_cast<uint8_t>(setFanProfileFlags::setPerfAcousMode)))
1159 {
1160 bool performanceMode =
1161 (req->flags & (1 << static_cast<uint8_t>(
1162 setFanProfileFlags::performAcousSelect))) > 0;
1163
1164 if (performanceMode)
1165 {
1166
1167 if (std::find(supported->begin(), supported->end(),
1168 "Performance") != supported->end())
1169 {
1170 mode = "Performance";
1171 }
1172 }
1173 else
1174 {
1175
1176 if (std::find(supported->begin(), supported->end(), "Acoustic") !=
1177 supported->end())
1178 {
1179 mode = "Acoustic";
1180 }
1181 }
1182 if (mode.empty())
1183 {
1184 return IPMI_CC_INVALID_FIELD_REQUEST;
1185 }
1186 setDbusProperty(dbus, settingsBusName, thermalModePath,
1187 thermalModeInterface, "Current", mode);
1188 }
1189
1190 return IPMI_CC_OK;
1191}
1192
1193ipmi_ret_t ipmiOEMGetFanConfig(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
1194 ipmi_request_t request, ipmi_response_t response,
1195 ipmi_data_len_t dataLen, ipmi_context_t context)
1196{
1197
1198 if (*dataLen > 1)
1199 {
1200 phosphor::logging::log<phosphor::logging::level::ERR>(
1201 "ipmiOEMGetFanConfig: invalid input len!");
1202 *dataLen = 0;
1203 return IPMI_CC_REQ_DATA_LEN_INVALID;
1204 }
1205
1206 // todo: talk to bios about needing less information
1207
1208 GetFanConfigResp* resp = reinterpret_cast<GetFanConfigResp*>(response);
1209 *dataLen = sizeof(GetFanConfigResp);
1210
1211 boost::container::flat_map<
1212 std::string, std::variant<std::vector<std::string>, std::string>>
1213 profileData;
1214
1215 if (!getFanProfileInterface(dbus, profileData))
1216 {
1217 return IPMI_CC_UNSPECIFIED_ERROR;
1218 }
1219
1220 std::string* current = std::get_if<std::string>(&profileData["Current"]);
1221
1222 if (current == nullptr)
1223 {
1224 phosphor::logging::log<phosphor::logging::level::ERR>(
1225 "ipmiOEMGetFanConfig: can't get current mode!");
1226 return IPMI_CC_UNSPECIFIED_ERROR;
1227 }
1228 bool performance = (*current == "Performance");
1229
1230 if (performance)
1231 {
1232 resp->flags |= 1 << 2;
1233 }
1234
1235 return IPMI_CC_OK;
1236}
1237
James Feist5f957ca2019-03-14 15:33:55 -07001238constexpr const char* cfmLimitSettingPath =
1239 "/xyz/openbmc_project/control/cfm_limit";
1240constexpr const char* cfmLimitIface = "xyz.openbmc_project.Control.CFMLimit";
James Feistfaa4f222019-03-21 16:21:55 -07001241constexpr const size_t legacyExitAirSensorNumber = 0x2e;
James Feistacc8a4e2019-04-02 14:23:57 -07001242constexpr const char* pidConfigurationIface =
1243 "xyz.openbmc_project.Configuration.Pid";
James Feistfaa4f222019-03-21 16:21:55 -07001244
1245static std::string getExitAirConfigPath()
1246{
1247
1248 auto method =
1249 dbus.new_method_call("xyz.openbmc_project.ObjectMapper",
1250 "/xyz/openbmc_project/object_mapper",
1251 "xyz.openbmc_project.ObjectMapper", "GetSubTree");
1252
James Feistacc8a4e2019-04-02 14:23:57 -07001253 method.append("/", 0, std::array<const char*, 1>{pidConfigurationIface});
James Feistfaa4f222019-03-21 16:21:55 -07001254 std::string path;
1255 GetSubTreeType resp;
1256 try
1257 {
1258 auto reply = dbus.call(method);
1259 reply.read(resp);
1260 }
1261 catch (sdbusplus::exception_t&)
1262 {
1263 phosphor::logging::log<phosphor::logging::level::ERR>(
1264 "ipmiOEMGetFscParameter: mapper error");
1265 };
1266 auto config = std::find_if(resp.begin(), resp.end(), [](const auto& pair) {
1267 return pair.first.find("Exit_Air") != std::string::npos;
1268 });
1269 if (config != resp.end())
1270 {
1271 path = std::move(config->first);
1272 }
1273 return path;
1274}
James Feist5f957ca2019-03-14 15:33:55 -07001275
James Feistacc8a4e2019-04-02 14:23:57 -07001276// flat map to make alphabetical
1277static boost::container::flat_map<std::string, PropertyMap> getPidConfigs()
1278{
1279 boost::container::flat_map<std::string, PropertyMap> ret;
1280 auto method =
1281 dbus.new_method_call("xyz.openbmc_project.ObjectMapper",
1282 "/xyz/openbmc_project/object_mapper",
1283 "xyz.openbmc_project.ObjectMapper", "GetSubTree");
1284
1285 method.append("/", 0, std::array<const char*, 1>{pidConfigurationIface});
1286 GetSubTreeType resp;
1287
1288 try
1289 {
1290 auto reply = dbus.call(method);
1291 reply.read(resp);
1292 }
1293 catch (sdbusplus::exception_t&)
1294 {
1295 phosphor::logging::log<phosphor::logging::level::ERR>(
1296 "getFanConfigPaths: mapper error");
1297 };
1298 for (const auto& [path, objects] : resp)
1299 {
1300 if (objects.empty())
1301 {
1302 continue; // should be impossible
1303 }
Zhu, Yungebe560b02019-04-21 21:19:21 -04001304
1305 try
1306 {
1307 ret.emplace(path, getAllDbusProperties(dbus, objects[0].first, path,
1308 pidConfigurationIface));
1309 }
1310 catch (sdbusplus::exception_t& e)
1311 {
1312 phosphor::logging::log<phosphor::logging::level::ERR>(
1313 "getPidConfigs: can't get DbusProperties!",
1314 phosphor::logging::entry("ERR=%s", e.what()));
1315 }
James Feistacc8a4e2019-04-02 14:23:57 -07001316 }
1317 return ret;
1318}
1319
1320ipmi::RspType<uint8_t> ipmiOEMGetFanSpeedOffset(void)
1321{
1322 boost::container::flat_map<std::string, PropertyMap> data = getPidConfigs();
1323 if (data.empty())
1324 {
1325 return ipmi::responseResponseError();
1326 }
1327 uint8_t minOffset = std::numeric_limits<uint8_t>::max();
1328 for (const auto& [_, pid] : data)
1329 {
1330 auto findClass = pid.find("Class");
1331 if (findClass == pid.end())
1332 {
1333 phosphor::logging::log<phosphor::logging::level::ERR>(
1334 "ipmiOEMGetFscParameter: found illegal pid "
1335 "configurations");
1336 return ipmi::responseResponseError();
1337 }
1338 std::string type = std::get<std::string>(findClass->second);
1339 if (type == "fan")
1340 {
1341 auto findOutLimit = pid.find("OutLimitMin");
1342 if (findOutLimit == pid.end())
1343 {
1344 phosphor::logging::log<phosphor::logging::level::ERR>(
1345 "ipmiOEMGetFscParameter: found illegal pid "
1346 "configurations");
1347 return ipmi::responseResponseError();
1348 }
1349 // get the min out of all the offsets
1350 minOffset = std::min(
1351 minOffset,
1352 static_cast<uint8_t>(std::get<double>(findOutLimit->second)));
1353 }
1354 }
1355 if (minOffset == std::numeric_limits<uint8_t>::max())
1356 {
1357 phosphor::logging::log<phosphor::logging::level::ERR>(
1358 "ipmiOEMGetFscParameter: found no fan configurations!");
1359 return ipmi::responseResponseError();
1360 }
1361
1362 return ipmi::responseSuccess(minOffset);
1363}
1364
1365ipmi::RspType<> ipmiOEMSetFanSpeedOffset(uint8_t offset)
1366{
1367 boost::container::flat_map<std::string, PropertyMap> data = getPidConfigs();
1368 if (data.empty())
1369 {
1370
1371 phosphor::logging::log<phosphor::logging::level::ERR>(
1372 "ipmiOEMSetFanSpeedOffset: found no pid configurations!");
1373 return ipmi::responseResponseError();
1374 }
1375
1376 bool found = false;
1377 for (const auto& [path, pid] : data)
1378 {
1379 auto findClass = pid.find("Class");
1380 if (findClass == pid.end())
1381 {
1382
1383 phosphor::logging::log<phosphor::logging::level::ERR>(
1384 "ipmiOEMSetFanSpeedOffset: found illegal pid "
1385 "configurations");
1386 return ipmi::responseResponseError();
1387 }
1388 std::string type = std::get<std::string>(findClass->second);
1389 if (type == "fan")
1390 {
1391 auto findOutLimit = pid.find("OutLimitMin");
1392 if (findOutLimit == pid.end())
1393 {
1394
1395 phosphor::logging::log<phosphor::logging::level::ERR>(
1396 "ipmiOEMSetFanSpeedOffset: found illegal pid "
1397 "configurations");
1398 return ipmi::responseResponseError();
1399 }
1400 ipmi::setDbusProperty(dbus, "xyz.openbmc_project.EntityManager",
1401 path, pidConfigurationIface, "OutLimitMin",
1402 static_cast<double>(offset));
1403 found = true;
1404 }
1405 }
1406 if (!found)
1407 {
1408 phosphor::logging::log<phosphor::logging::level::ERR>(
1409 "ipmiOEMSetFanSpeedOffset: set no fan offsets");
1410 return ipmi::responseResponseError();
1411 }
1412
1413 return ipmi::responseSuccess();
1414}
1415
1416ipmi::RspType<> ipmiOEMSetFscParameter(uint8_t command, uint8_t param1,
1417 uint8_t param2)
James Feist5f957ca2019-03-14 15:33:55 -07001418{
1419 constexpr const size_t disableLimiting = 0x0;
1420
James Feistacc8a4e2019-04-02 14:23:57 -07001421 if (command == static_cast<uint8_t>(setFscParamFlags::tcontrol))
James Feist5f957ca2019-03-14 15:33:55 -07001422 {
James Feistacc8a4e2019-04-02 14:23:57 -07001423 if (param1 == legacyExitAirSensorNumber)
James Feistfaa4f222019-03-21 16:21:55 -07001424 {
James Feistfaa4f222019-03-21 16:21:55 -07001425 std::string path = getExitAirConfigPath();
1426 ipmi::setDbusProperty(dbus, "xyz.openbmc_project.EntityManager",
James Feistacc8a4e2019-04-02 14:23:57 -07001427 path, pidConfigurationIface, "SetPoint",
1428 static_cast<double>(param2));
1429 return ipmi::responseSuccess();
James Feistfaa4f222019-03-21 16:21:55 -07001430 }
1431 else
1432 {
James Feistacc8a4e2019-04-02 14:23:57 -07001433 return ipmi::responseParmOutOfRange();
James Feistfaa4f222019-03-21 16:21:55 -07001434 }
1435 }
James Feistacc8a4e2019-04-02 14:23:57 -07001436 else if (command == static_cast<uint8_t>(setFscParamFlags::cfm))
James Feist5f957ca2019-03-14 15:33:55 -07001437 {
James Feistacc8a4e2019-04-02 14:23:57 -07001438 uint16_t cfm = param1 | (static_cast<uint16_t>(param2) << 8);
James Feist5f957ca2019-03-14 15:33:55 -07001439
1440 // must be greater than 50 based on eps
1441 if (cfm < 50 && cfm != disableLimiting)
1442 {
James Feistacc8a4e2019-04-02 14:23:57 -07001443 return ipmi::responseParmOutOfRange();
James Feist5f957ca2019-03-14 15:33:55 -07001444 }
1445
1446 try
1447 {
1448 ipmi::setDbusProperty(dbus, settingsBusName, cfmLimitSettingPath,
1449 cfmLimitIface, "Limit",
1450 static_cast<double>(cfm));
1451 }
1452 catch (sdbusplus::exception_t& e)
1453 {
1454 phosphor::logging::log<phosphor::logging::level::ERR>(
1455 "ipmiOEMSetFscParameter: can't set cfm setting!",
1456 phosphor::logging::entry("ERR=%s", e.what()));
James Feistacc8a4e2019-04-02 14:23:57 -07001457 return ipmi::responseResponseError();
James Feist5f957ca2019-03-14 15:33:55 -07001458 }
James Feistacc8a4e2019-04-02 14:23:57 -07001459 return ipmi::responseSuccess();
1460 }
1461 else if (command == static_cast<uint8_t>(setFscParamFlags::maxPwm))
1462 {
1463 constexpr const size_t maxDomainCount = 8;
1464 uint8_t requestedDomainMask = param1;
1465 boost::container::flat_map data = getPidConfigs();
1466 if (data.empty())
1467 {
1468
1469 phosphor::logging::log<phosphor::logging::level::ERR>(
1470 "ipmiOEMSetFscParameter: found no pid configurations!");
1471 return ipmi::responseResponseError();
1472 }
1473 size_t count = 0;
1474 for (const auto& [path, pid] : data)
1475 {
1476 auto findClass = pid.find("Class");
1477 if (findClass == pid.end())
1478 {
1479
1480 phosphor::logging::log<phosphor::logging::level::ERR>(
1481 "ipmiOEMSetFscParameter: found illegal pid "
1482 "configurations");
1483 return ipmi::responseResponseError();
1484 }
1485 std::string type = std::get<std::string>(findClass->second);
1486 if (type == "fan")
1487 {
1488 if (requestedDomainMask & (1 << count))
1489 {
1490 ipmi::setDbusProperty(
1491 dbus, "xyz.openbmc_project.EntityManager", path,
1492 pidConfigurationIface, "OutLimitMax",
1493 static_cast<double>(param2));
1494 }
1495 count++;
1496 }
1497 }
1498 return ipmi::responseSuccess();
James Feist5f957ca2019-03-14 15:33:55 -07001499 }
1500 else
1501 {
1502 // todo other command parts possibly
1503 // tcontrol is handled in peci now
1504 // fan speed offset not implemented yet
1505 // domain pwm limit not implemented
James Feistacc8a4e2019-04-02 14:23:57 -07001506 return ipmi::responseParmOutOfRange();
James Feist5f957ca2019-03-14 15:33:55 -07001507 }
1508}
1509
James Feistacc8a4e2019-04-02 14:23:57 -07001510ipmi::RspType<
1511 std::variant<uint8_t, std::array<uint8_t, 2>, std::array<uint16_t, 2>>>
1512 ipmiOEMGetFscParameter(uint8_t command, std::optional<uint8_t> param)
James Feist5f957ca2019-03-14 15:33:55 -07001513{
James Feistfaa4f222019-03-21 16:21:55 -07001514 constexpr uint8_t legacyDefaultExitAirLimit = -128;
James Feist5f957ca2019-03-14 15:33:55 -07001515
James Feistacc8a4e2019-04-02 14:23:57 -07001516 if (command == static_cast<uint8_t>(setFscParamFlags::tcontrol))
James Feist5f957ca2019-03-14 15:33:55 -07001517 {
James Feistacc8a4e2019-04-02 14:23:57 -07001518 if (!param)
James Feistfaa4f222019-03-21 16:21:55 -07001519 {
James Feistacc8a4e2019-04-02 14:23:57 -07001520 return ipmi::responseReqDataLenInvalid();
James Feistfaa4f222019-03-21 16:21:55 -07001521 }
1522
James Feistacc8a4e2019-04-02 14:23:57 -07001523 if (*param != legacyExitAirSensorNumber)
James Feistfaa4f222019-03-21 16:21:55 -07001524 {
James Feistacc8a4e2019-04-02 14:23:57 -07001525 return ipmi::responseParmOutOfRange();
James Feistfaa4f222019-03-21 16:21:55 -07001526 }
1527 uint8_t setpoint = legacyDefaultExitAirLimit;
1528 std::string path = getExitAirConfigPath();
1529 if (path.size())
1530 {
James Feistacc8a4e2019-04-02 14:23:57 -07001531 Value val =
1532 ipmi::getDbusProperty(dbus, "xyz.openbmc_project.EntityManager",
1533 path, pidConfigurationIface, "SetPoint");
James Feistfaa4f222019-03-21 16:21:55 -07001534 setpoint = std::floor(std::get<double>(val) + 0.5);
1535 }
1536
1537 // old implementation used to return the "default" and current, we
1538 // don't make the default readily available so just make both the
1539 // same
James Feistfaa4f222019-03-21 16:21:55 -07001540
James Feistacc8a4e2019-04-02 14:23:57 -07001541 return ipmi::responseSuccess(
1542 std::array<uint8_t, 2>{setpoint, setpoint});
James Feistfaa4f222019-03-21 16:21:55 -07001543 }
James Feistacc8a4e2019-04-02 14:23:57 -07001544 else if (command == static_cast<uint8_t>(setFscParamFlags::maxPwm))
1545 {
1546 constexpr const size_t maxDomainCount = 8;
1547
1548 if (!param)
1549 {
1550 return ipmi::responseReqDataLenInvalid();
1551 }
1552 uint8_t requestedDomain = *param;
1553 if (requestedDomain >= maxDomainCount)
1554 {
1555 return ipmi::responseInvalidFieldRequest();
1556 }
1557
1558 boost::container::flat_map data = getPidConfigs();
1559 if (data.empty())
1560 {
1561 phosphor::logging::log<phosphor::logging::level::ERR>(
1562 "ipmiOEMGetFscParameter: found no pid configurations!");
1563 return ipmi::responseResponseError();
1564 }
1565 size_t count = 0;
1566 for (const auto& [_, pid] : data)
1567 {
1568 auto findClass = pid.find("Class");
1569 if (findClass == pid.end())
1570 {
1571 phosphor::logging::log<phosphor::logging::level::ERR>(
1572 "ipmiOEMGetFscParameter: found illegal pid "
1573 "configurations");
1574 return ipmi::responseResponseError();
1575 }
1576 std::string type = std::get<std::string>(findClass->second);
1577 if (type == "fan")
1578 {
1579 if (requestedDomain == count)
1580 {
1581 auto findOutLimit = pid.find("OutLimitMax");
1582 if (findOutLimit == pid.end())
1583 {
1584 phosphor::logging::log<phosphor::logging::level::ERR>(
1585 "ipmiOEMGetFscParameter: found illegal pid "
1586 "configurations");
1587 return ipmi::responseResponseError();
1588 }
1589
1590 return ipmi::responseSuccess(
1591 static_cast<uint8_t>(std::floor(
1592 std::get<double>(findOutLimit->second) + 0.5)));
1593 }
1594 else
1595 {
1596 count++;
1597 }
1598 }
1599 }
1600
1601 return ipmi::responseInvalidFieldRequest();
1602 }
1603 else if (command == static_cast<uint8_t>(setFscParamFlags::cfm))
James Feist5f957ca2019-03-14 15:33:55 -07001604 {
1605
1606 /*
1607 DataLen should be 1, but host is sending us an extra bit. As the
James Feistacc8a4e2019-04-02 14:23:57 -07001608 previous behavior didn't seem to prevent this, ignore the check for
1609 now.
James Feist5f957ca2019-03-14 15:33:55 -07001610
James Feistacc8a4e2019-04-02 14:23:57 -07001611 if (param)
James Feist5f957ca2019-03-14 15:33:55 -07001612 {
1613 phosphor::logging::log<phosphor::logging::level::ERR>(
1614 "ipmiOEMGetFscParameter: invalid input len!");
James Feist5f957ca2019-03-14 15:33:55 -07001615 return IPMI_CC_REQ_DATA_LEN_INVALID;
1616 }
1617 */
1618 Value cfmLimit;
1619 Value cfmMaximum;
1620 try
1621 {
1622 cfmLimit = ipmi::getDbusProperty(dbus, settingsBusName,
1623 cfmLimitSettingPath, cfmLimitIface,
1624 "Limit");
1625 cfmMaximum = ipmi::getDbusProperty(
1626 dbus, "xyz.openbmc_project.ExitAirTempSensor",
1627 "/xyz/openbmc_project/control/MaxCFM", cfmLimitIface, "Limit");
1628 }
1629 catch (sdbusplus::exception_t& e)
1630 {
1631 phosphor::logging::log<phosphor::logging::level::ERR>(
James Feistacc8a4e2019-04-02 14:23:57 -07001632 "ipmiOEMGetFscParameter: can't get cfm setting!",
James Feist5f957ca2019-03-14 15:33:55 -07001633 phosphor::logging::entry("ERR=%s", e.what()));
James Feistacc8a4e2019-04-02 14:23:57 -07001634 return ipmi::responseResponseError();
James Feist5f957ca2019-03-14 15:33:55 -07001635 }
1636
James Feistacc8a4e2019-04-02 14:23:57 -07001637 double cfmMax = std::get<double>(cfmMaximum);
1638 double cfmLim = std::get<double>(cfmLimit);
James Feist5f957ca2019-03-14 15:33:55 -07001639
James Feistacc8a4e2019-04-02 14:23:57 -07001640 cfmLim = std::floor(cfmLim + 0.5);
1641 cfmMax = std::floor(cfmMax + 0.5);
1642 uint16_t cfmLimResp = static_cast<uint16_t>(cfmLim);
1643 uint16_t cfmMaxResp = static_cast<uint16_t>(cfmMax);
James Feist5f957ca2019-03-14 15:33:55 -07001644
James Feistacc8a4e2019-04-02 14:23:57 -07001645 return ipmi::responseSuccess(
1646 std::array<uint16_t, 2>{cfmLimResp, cfmMaxResp});
James Feist5f957ca2019-03-14 15:33:55 -07001647 }
James Feistacc8a4e2019-04-02 14:23:57 -07001648
James Feist5f957ca2019-03-14 15:33:55 -07001649 else
1650 {
1651 // todo other command parts possibly
James Feist5f957ca2019-03-14 15:33:55 -07001652 // domain pwm limit not implemented
James Feistacc8a4e2019-04-02 14:23:57 -07001653 return ipmi::responseParmOutOfRange();
James Feist5f957ca2019-03-14 15:33:55 -07001654 }
1655}
1656
Zhu, Yungebe560b02019-04-21 21:19:21 -04001657ipmi::RspType<> ipmiOEMSetFaultIndication(uint8_t sourceId, uint8_t faultType,
1658 uint8_t faultState,
1659 uint8_t faultGroup,
1660 std::array<uint8_t, 8>& ledStateData)
1661{
1662 static constexpr const char* objpath = "/xyz/openbmc_project/EntityManager";
1663 static constexpr const char* intf = "xyz.openbmc_project.EntityManager";
1664 constexpr auto maxFaultType = static_cast<size_t>(RemoteFaultType::max);
1665 static const std::array<std::string, maxFaultType> faultNames = {
1666 "faultFan", "faultTemp", "faultPower",
1667 "faultDriveSlot", "faultSoftware", "faultMemory"};
1668 static constexpr const char* sysGpioPath = "/sys/class/gpio/gpio";
1669 static constexpr const char* postfixValue = "/value";
1670
1671 constexpr uint8_t maxFaultSource = 0x4;
1672 constexpr uint8_t skipLEDs = 0xFF;
1673 constexpr uint8_t pinSize = 64;
1674 constexpr uint8_t groupSize = 16;
1675
1676 std::vector<uint16_t> ledFaultPins(pinSize, 0xFFFF);
1677 uint64_t resFIndex = 0;
1678 std::string resFType;
1679 std::string service;
1680 ObjectValueTree valueTree;
1681
1682 // Validate the source, fault type
1683 if ((sourceId >= maxFaultSource) ||
1684 (faultType >= static_cast<int8_t>(RemoteFaultType::max)) ||
1685 (faultState >= static_cast<int8_t>(RemoteFaultState::maxFaultState)) ||
1686 (faultGroup >= static_cast<int8_t>(DimmFaultType::maxFaultGroup)))
1687 {
1688 return ipmi::responseParmOutOfRange();
1689 }
1690
1691 try
1692 {
1693 service = getService(dbus, intf, objpath);
1694 valueTree = getManagedObjects(dbus, service, "/");
1695 }
1696 catch (const std::exception& e)
1697 {
1698 phosphor::logging::log<phosphor::logging::level::ERR>(
1699 "No object implements interface",
1700 phosphor::logging::entry("SERVICE=%s", service.c_str()),
1701 phosphor::logging::entry("INTF=%s", intf));
1702 return ipmi::responseResponseError();
1703 }
1704
1705 if (valueTree.empty())
1706 {
1707 phosphor::logging::log<phosphor::logging::level::ERR>(
1708 "No object implements interface",
1709 phosphor::logging::entry("INTF=%s", intf));
1710 return ipmi::responseResponseError();
1711 }
1712
1713 for (const auto& item : valueTree)
1714 {
1715 // find LedFault configuration
1716 auto interface =
1717 item.second.find("xyz.openbmc_project.Configuration.LedFault");
1718 if (interface == item.second.end())
1719 {
1720 continue;
1721 }
1722
1723 // find matched fault type: faultMemmory / faultFan
1724 // find LedGpioPins/FaultIndex configuration
1725 auto propertyFaultType = interface->second.find("FaultType");
1726 auto propertyFIndex = interface->second.find("FaultIndex");
1727 auto ledIndex = interface->second.find("LedGpioPins");
1728
1729 if (propertyFaultType == interface->second.end() ||
1730 propertyFIndex == interface->second.end() ||
1731 ledIndex == interface->second.end())
1732 {
1733 continue;
1734 }
1735
1736 try
1737 {
1738 Value valIndex = propertyFIndex->second;
1739 resFIndex = std::get<uint64_t>(valIndex);
1740
1741 Value valFType = propertyFaultType->second;
1742 resFType = std::get<std::string>(valFType);
1743 }
1744 catch (const std::bad_variant_access& e)
1745 {
1746 phosphor::logging::log<phosphor::logging::level::ERR>(e.what());
1747 return ipmi::responseResponseError();
1748 }
1749 // find the matched requested fault type: faultMemmory or faultFan
1750 if (resFType != faultNames[faultType])
1751 {
1752 continue;
1753 }
1754
1755 // read LedGpioPins data
1756 std::vector<uint64_t> ledgpios;
1757 std::variant<std::vector<uint64_t>> message;
1758
1759 auto method = dbus.new_method_call(
1760 service.c_str(), (std::string(item.first)).c_str(),
1761 "org.freedesktop.DBus.Properties", "Get");
1762
1763 method.append("xyz.openbmc_project.Configuration.LedFault",
1764 "LedGpioPins");
1765
1766 try
1767 {
1768 auto reply = dbus.call(method);
1769 reply.read(message);
1770 ledgpios = std::get<std::vector<uint64_t>>(message);
1771 }
1772 catch (std::exception& e)
1773 {
1774 phosphor::logging::log<phosphor::logging::level::ERR>(e.what());
1775 return ipmi::responseResponseError();
1776 }
1777
1778 // Check the size to be sure it will never overflow on groupSize
1779 if (ledgpios.size() > groupSize)
1780 {
1781 phosphor::logging::log<phosphor::logging::level::ERR>(
1782 "Fault gpio Pins out of range!");
1783 return ipmi::responseParmOutOfRange();
1784 }
1785 // Store data, according to command data bit index order
1786 for (int i = 0; i < ledgpios.size(); i++)
1787 {
1788 ledFaultPins[i + groupSize * resFIndex] = ledgpios[i];
1789 }
1790 }
1791
1792 switch (RemoteFaultType(faultType))
1793 {
1794 case (RemoteFaultType::fan):
1795 case (RemoteFaultType::memory):
1796 {
1797 if (faultGroup == skipLEDs)
1798 {
1799 return ipmi::responseSuccess();
1800 }
1801
1802 uint64_t ledState = 0;
1803 // calculate led state bit filed count, each byte has 8bits
1804 // the maximum bits will be 8 * 8 bits
1805 constexpr uint8_t size = sizeof(ledStateData) * 8;
1806 for (int i = 0; i < sizeof(ledStateData); i++)
1807 {
1808 ledState = (uint64_t)(ledState << 8);
1809 ledState = (uint64_t)(ledState | (uint64_t)ledStateData[i]);
1810 }
1811
1812 std::bitset<size> ledStateBits(ledState);
1813 std::string gpioValue;
1814 for (int i = 0; i < size; i++)
1815 { // skip invalid value
1816 if (ledFaultPins[i] == 0xFFFF)
1817 {
1818 continue;
1819 }
1820
1821 std::string device = sysGpioPath +
1822 std::to_string(ledFaultPins[i]) +
1823 postfixValue;
1824 std::fstream gpioFile;
1825
1826 gpioFile.open(device, std::ios::out);
1827
1828 if (!gpioFile.good())
1829 {
1830 phosphor::logging::log<phosphor::logging::level::ERR>(
1831 "Not Find Led Gpio Device!",
1832 phosphor::logging::entry("DEVICE=%s", device.c_str()));
1833 return ipmi::responseResponseError();
1834 }
1835 gpioFile << std::to_string(
1836 static_cast<uint8_t>(ledStateBits[i]));
1837 gpioFile.close();
1838 }
1839 break;
1840 }
1841 default:
1842 {
1843 // now only support two fault types
1844 return ipmi::responseParmOutOfRange();
1845 }
1846 }
1847
1848 return ipmi::responseSuccess();
1849}
1850
Richard Marian Thomaiyarea537d52019-04-24 21:33:48 +05301851ipmi::RspType<uint8_t> ipmiOEMReadBoardProductId()
1852{
1853 uint8_t prodId = 0;
1854 try
1855 {
1856 const DbusObjectInfo& object = getDbusObject(
1857 dbus, "xyz.openbmc_project.Inventory.Item.Board",
1858 "/xyz/openbmc_project/inventory/system/board/", "Baseboard");
1859 const Value& propValue = getDbusProperty(
1860 dbus, object.second, object.first,
1861 "xyz.openbmc_project.Inventory.Item.Board", "ProductId");
1862 prodId = static_cast<uint8_t>(std::get<uint64_t>(propValue));
1863 }
1864 catch (std::exception& e)
1865 {
1866 phosphor::logging::log<phosphor::logging::level::ERR>(
1867 "ipmiOEMReadBoardProductId: Product ID read failed!",
1868 phosphor::logging::entry("ERR=%s", e.what()));
1869 }
1870 return ipmi::responseSuccess(prodId);
1871}
1872
Vernon Mauery4ac799d2019-05-20 15:50:37 -07001873ipmi::RspType<uint8_t /* restore status */>
1874 ipmiRestoreConfiguration(const std::array<uint8_t, 3>& clr, uint8_t cmd)
1875{
1876 static constexpr std::array<uint8_t, 3> expClr = {'C', 'L', 'R'};
1877
1878 if (clr != expClr)
1879 {
1880 return ipmi::responseInvalidFieldRequest();
1881 }
1882 constexpr uint8_t cmdStatus = 0;
1883 constexpr uint8_t cmdDefaultRestore = 0xaa;
1884 constexpr uint8_t cmdFullRestore = 0xbb;
1885 constexpr uint8_t cmdFormat = 0xcc;
1886
1887 constexpr const char* restoreOpFname = "/tmp/.rwfs/.restore_op";
1888
1889 switch (cmd)
1890 {
1891 case cmdStatus:
1892 break;
1893 case cmdDefaultRestore:
1894 case cmdFullRestore:
1895 case cmdFormat:
1896 {
1897 // write file to rwfs root
1898 int value = (cmd - 1) & 0x03; // map aa, bb, cc => 1, 2, 3
1899 std::ofstream restoreFile(restoreOpFname);
1900 if (!restoreFile)
1901 {
1902 return ipmi::responseUnspecifiedError();
1903 }
1904 restoreFile << value << "\n";
1905 break;
1906 }
1907 default:
1908 return ipmi::responseInvalidFieldRequest();
1909 }
1910
1911 constexpr uint8_t restorePending = 0;
1912 constexpr uint8_t restoreComplete = 1;
1913
1914 uint8_t restoreStatus = std::filesystem::exists(restoreOpFname)
1915 ? restorePending
1916 : restoreComplete;
1917 return ipmi::responseSuccess(restoreStatus);
1918}
1919
Jason M. Bills64796042018-10-03 16:51:55 -07001920static void registerOEMFunctions(void)
Jia, Chunhuia835eaa2018-09-05 09:00:41 +08001921{
1922 phosphor::logging::log<phosphor::logging::level::INFO>(
1923 "Registering OEM commands");
Jason M. Bills64796042018-10-03 16:51:55 -07001924 ipmiPrintAndRegister(netfnIntcOEMGeneral, IPMI_CMD_WILDCARD, NULL,
1925 ipmiOEMWildcard,
1926 PRIVILEGE_USER); // wildcard default handler
1927 ipmiPrintAndRegister(netfunIntelAppOEM, IPMI_CMD_WILDCARD, NULL,
1928 ipmiOEMWildcard,
1929 PRIVILEGE_USER); // wildcard default handler
1930 ipmiPrintAndRegister(
1931 netfnIntcOEMGeneral,
1932 static_cast<ipmi_cmd_t>(
1933 IPMINetfnIntelOEMGeneralCmd::cmdGetChassisIdentifier),
1934 NULL, ipmiOEMGetChassisIdentifier,
1935 PRIVILEGE_USER); // get chassis identifier
1936 ipmiPrintAndRegister(
1937 netfnIntcOEMGeneral,
1938 static_cast<ipmi_cmd_t>(IPMINetfnIntelOEMGeneralCmd::cmdSetSystemGUID),
1939 NULL, ipmiOEMSetSystemGUID,
1940 PRIVILEGE_ADMIN); // set system guid
1941 ipmiPrintAndRegister(
1942 netfnIntcOEMGeneral,
1943 static_cast<ipmi_cmd_t>(IPMINetfnIntelOEMGeneralCmd::cmdSetBIOSID),
1944 NULL, ipmiOEMSetBIOSID, PRIVILEGE_ADMIN);
1945 ipmiPrintAndRegister(netfnIntcOEMGeneral,
1946 static_cast<ipmi_cmd_t>(
1947 IPMINetfnIntelOEMGeneralCmd::cmdGetOEMDeviceInfo),
1948 NULL, ipmiOEMGetDeviceInfo, PRIVILEGE_USER);
1949 ipmiPrintAndRegister(
1950 netfnIntcOEMGeneral,
1951 static_cast<ipmi_cmd_t>(
1952 IPMINetfnIntelOEMGeneralCmd::cmdGetAICSlotFRUIDSlotPosRecords),
1953 NULL, ipmiOEMGetAICFRU, PRIVILEGE_USER);
Jia, Chunhuicc49b542019-03-20 15:41:07 +08001954
1955 ipmi::registerHandler(
1956 ipmi::prioOpenBmcBase, ipmi::netFnOemOne,
1957 static_cast<ipmi::Cmd>(
1958 IPMINetfnIntelOEMGeneralCmd::cmdSendEmbeddedFWUpdStatus),
1959 ipmi::Privilege::Operator, ipmiOEMSendEmbeddedFwUpdStatus);
1960
Jason M. Bills64796042018-10-03 16:51:55 -07001961 ipmiPrintAndRegister(
1962 netfnIntcOEMGeneral,
1963 static_cast<ipmi_cmd_t>(
1964 IPMINetfnIntelOEMGeneralCmd::cmdSetPowerRestoreDelay),
1965 NULL, ipmiOEMSetPowerRestoreDelay, PRIVILEGE_OPERATOR);
1966 ipmiPrintAndRegister(
1967 netfnIntcOEMGeneral,
1968 static_cast<ipmi_cmd_t>(
1969 IPMINetfnIntelOEMGeneralCmd::cmdGetPowerRestoreDelay),
1970 NULL, ipmiOEMGetPowerRestoreDelay, PRIVILEGE_USER);
Suryakanth Sekard509eb92018-11-15 17:44:11 +05301971
1972 ipmi::registerHandler(
1973 ipmi::prioOpenBmcBase, ipmi::netFnOemOne,
1974 static_cast<ipmi::Cmd>(
1975 IPMINetfnIntelOEMGeneralCmd::cmdSetOEMUser2Activation),
1976 ipmi::Privilege::Callback, ipmiOEMSetUser2Activation);
1977
Richard Marian Thomaiyarfc5e9852019-04-14 15:06:27 +05301978 ipmi::registerHandler(
1979 ipmi::prioOpenBmcBase, ipmi::netFnOemOne,
1980 static_cast<ipmi::Cmd>(
1981 IPMINetfnIntelOEMGeneralCmd::cmdSetSpecialUserPassword),
1982 ipmi::Privilege::Callback, ipmiOEMSetSpecialUserPassword);
1983
Jason M. Bills64796042018-10-03 16:51:55 -07001984 ipmiPrintAndRegister(
1985 netfnIntcOEMGeneral,
1986 static_cast<ipmi_cmd_t>(
1987 IPMINetfnIntelOEMGeneralCmd::cmdGetProcessorErrConfig),
1988 NULL, ipmiOEMGetProcessorErrConfig, PRIVILEGE_USER);
1989 ipmiPrintAndRegister(
1990 netfnIntcOEMGeneral,
1991 static_cast<ipmi_cmd_t>(
1992 IPMINetfnIntelOEMGeneralCmd::cmdSetProcessorErrConfig),
1993 NULL, ipmiOEMSetProcessorErrConfig, PRIVILEGE_ADMIN);
Yong Li703922d2018-11-06 13:25:31 +08001994 ipmiPrintAndRegister(netfnIntcOEMGeneral,
1995 static_cast<ipmi_cmd_t>(
1996 IPMINetfnIntelOEMGeneralCmd::cmdSetShutdownPolicy),
1997 NULL, ipmiOEMSetShutdownPolicy, PRIVILEGE_ADMIN);
1998 ipmiPrintAndRegister(netfnIntcOEMGeneral,
1999 static_cast<ipmi_cmd_t>(
2000 IPMINetfnIntelOEMGeneralCmd::cmdGetShutdownPolicy),
2001 NULL, ipmiOEMGetShutdownPolicy, PRIVILEGE_ADMIN);
James Feist91244a62019-02-19 15:04:54 -08002002
2003 ipmiPrintAndRegister(
2004 netfnIntcOEMGeneral,
2005 static_cast<ipmi_cmd_t>(IPMINetfnIntelOEMGeneralCmd::cmdSetFanConfig),
2006 NULL, ipmiOEMSetFanConfig, PRIVILEGE_USER);
2007
2008 ipmiPrintAndRegister(
2009 netfnIntcOEMGeneral,
2010 static_cast<ipmi_cmd_t>(IPMINetfnIntelOEMGeneralCmd::cmdGetFanConfig),
2011 NULL, ipmiOEMGetFanConfig, PRIVILEGE_USER);
2012
James Feistacc8a4e2019-04-02 14:23:57 -07002013 ipmi::registerHandler(
2014 ipmi::prioOemBase, netfnIntcOEMGeneral,
2015 static_cast<ipmi::Cmd>(
2016 IPMINetfnIntelOEMGeneralCmd::cmdGetFanSpeedOffset),
2017 ipmi::Privilege::User, ipmiOEMGetFanSpeedOffset);
James Feist5f957ca2019-03-14 15:33:55 -07002018
James Feistacc8a4e2019-04-02 14:23:57 -07002019 ipmi::registerHandler(
2020 ipmi::prioOemBase, netfnIntcOEMGeneral,
2021 static_cast<ipmi::Cmd>(
2022 IPMINetfnIntelOEMGeneralCmd::cmdSetFanSpeedOffset),
2023 ipmi::Privilege::User, ipmiOEMSetFanSpeedOffset);
2024
2025 ipmi::registerHandler(
2026 ipmi::prioOemBase, netfnIntcOEMGeneral,
2027 static_cast<ipmi::Cmd>(IPMINetfnIntelOEMGeneralCmd::cmdSetFscParameter),
2028 ipmi::Privilege::User, ipmiOEMSetFscParameter);
2029
2030 ipmi::registerHandler(
2031 ipmi::prioOemBase, netfnIntcOEMGeneral,
2032 static_cast<ipmi::Cmd>(IPMINetfnIntelOEMGeneralCmd::cmdGetFscParameter),
2033 ipmi::Privilege::User, ipmiOEMGetFscParameter);
James Feist5f957ca2019-03-14 15:33:55 -07002034
Richard Marian Thomaiyarea537d52019-04-24 21:33:48 +05302035 ipmi::registerHandler(
2036 ipmi::prioOpenBmcBase, netfnIntcOEMGeneral,
2037 static_cast<ipmi::Cmd>(
2038 IPMINetfnIntelOEMGeneralCmd::cmdReadBaseBoardProductId),
2039 ipmi::Privilege::Admin, ipmiOEMReadBoardProductId);
2040
Kuiying Wang45f04982018-12-26 09:23:08 +08002041 ipmiPrintAndRegister(
2042 netfnIntcOEMGeneral,
2043 static_cast<ipmi_cmd_t>(IPMINetfnIntelOEMGeneralCmd::cmdGetLEDStatus),
2044 NULL, ipmiOEMGetLEDStatus, PRIVILEGE_ADMIN);
Yong Li23737fe2019-02-19 08:49:55 +08002045 ipmiPrintAndRegister(
2046 netfnIntcOEMPlatform,
2047 static_cast<ipmi_cmd_t>(
2048 IPMINetfnIntelOEMPlatformCmd::cmdCfgHostSerialPortSpeed),
2049 NULL, ipmiOEMCfgHostSerialPortSpeed, PRIVILEGE_ADMIN);
Zhu, Yungebe560b02019-04-21 21:19:21 -04002050 ipmi::registerHandler(
2051 ipmi::prioOemBase, netfnIntcOEMGeneral,
2052 static_cast<ipmi::Cmd>(
2053 IPMINetfnIntelOEMGeneralCmd::cmdSetFaultIndication),
2054 ipmi::Privilege::Operator, ipmiOEMSetFaultIndication);
Vernon Mauery4ac799d2019-05-20 15:50:37 -07002055
2056 registerHandler(prioOemBase, netfn::intel::oemGeneral,
2057 netfn::intel::cmdRestoreConfiguration, Privilege::Admin,
2058 ipmiRestoreConfiguration);
Jia, Chunhuia835eaa2018-09-05 09:00:41 +08002059}
2060
2061} // namespace ipmi