blob: 028671ada2f4d72cc30d985c8cf826775a571f2a [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;
Jason M. Bills64796042018-10-03 16:51:55 -070091 std::string& result =
92 sdbusplus::message::variant_ns::get<std::string>(variant);
93 if (result.size() > maxFRUStringLength)
Jia, Chunhuia835eaa2018-09-05 09:00:41 +080094 {
95 phosphor::logging::log<phosphor::logging::level::ERR>(
96 "FRU serial number exceed maximum length");
Jia, Chunhuia835eaa2018-09-05 09:00:41 +080097 return -1;
98 }
Jason M. Bills64796042018-10-03 16:51:55 -070099 serial = result;
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800100 return 0;
101 }
Jason M. Bills64796042018-10-03 16:51:55 -0700102 catch (sdbusplus::message::variant_ns::bad_variant_access& e)
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800103 {
Jason M. Bills64796042018-10-03 16:51:55 -0700104 phosphor::logging::log<phosphor::logging::level::ERR>(e.what());
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800105 return -1;
106 }
107 }
108 return -1;
109}
Jason M. Bills64796042018-10-03 16:51:55 -0700110
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800111ipmi_ret_t ipmiOEMWildcard(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
112 ipmi_request_t request, ipmi_response_t response,
Jason M. Bills64796042018-10-03 16:51:55 -0700113 ipmi_data_len_t dataLen, ipmi_context_t context)
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800114{
Jason M. Bills64796042018-10-03 16:51:55 -0700115 printCommand(+netfn, +cmd);
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800116 // Status code.
117 ipmi_ret_t rc = IPMI_CC_INVALID;
Jason M. Bills64796042018-10-03 16:51:55 -0700118 *dataLen = 0;
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800119 return rc;
120}
121
122// Returns the Chassis Identifier (serial #)
123ipmi_ret_t ipmiOEMGetChassisIdentifier(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
124 ipmi_request_t request,
125 ipmi_response_t response,
Jason M. Bills64796042018-10-03 16:51:55 -0700126 ipmi_data_len_t dataLen,
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800127 ipmi_context_t context)
128{
129 std::string serial;
Jason M. Bills64796042018-10-03 16:51:55 -0700130 if (*dataLen != 0) // invalid request if there are extra parameters
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800131 {
Jason M. Bills64796042018-10-03 16:51:55 -0700132 *dataLen = 0;
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800133 return IPMI_CC_REQ_DATA_LEN_INVALID;
134 }
Jason M. Bills64796042018-10-03 16:51:55 -0700135 if (getChassisSerialNumber(dbus, serial) == 0)
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800136 {
Jason M. Bills64796042018-10-03 16:51:55 -0700137 *dataLen = serial.size(); // length will never exceed response length
138 // as it is checked in getChassisSerialNumber
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800139 char* resp = static_cast<char*>(response);
Jason M. Bills64796042018-10-03 16:51:55 -0700140 serial.copy(resp, *dataLen);
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800141 return IPMI_CC_OK;
142 }
Jason M. Bills64796042018-10-03 16:51:55 -0700143 *dataLen = 0;
144 return IPMI_CC_RESPONSE_ERROR;
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800145}
146
147ipmi_ret_t ipmiOEMSetSystemGUID(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
148 ipmi_request_t request,
149 ipmi_response_t response,
Jason M. Bills64796042018-10-03 16:51:55 -0700150 ipmi_data_len_t dataLen, ipmi_context_t context)
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800151{
152 static constexpr size_t safeBufferLength = 50;
153 char buf[safeBufferLength] = {0};
154 GUIDData* Data = reinterpret_cast<GUIDData*>(request);
155
Jason M. Bills64796042018-10-03 16:51:55 -0700156 if (*dataLen != sizeof(GUIDData)) // 16bytes
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800157 {
Jason M. Bills64796042018-10-03 16:51:55 -0700158 *dataLen = 0;
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800159 return IPMI_CC_REQ_DATA_LEN_INVALID;
160 }
161
Jason M. Bills64796042018-10-03 16:51:55 -0700162 *dataLen = 0;
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800163
164 snprintf(
165 buf, safeBufferLength,
166 "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x",
167 Data->timeLow4, Data->timeLow3, Data->timeLow2, Data->timeLow1,
168 Data->timeMid2, Data->timeMid1, Data->timeHigh2, Data->timeHigh1,
169 Data->clock2, Data->clock1, Data->node6, Data->node5, Data->node4,
170 Data->node3, Data->node2, Data->node1);
171 // UUID is in RFC4122 format. Ex: 61a39523-78f2-11e5-9862-e6402cfc3223
172 std::string guid = buf;
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800173
174 std::string objpath = "/xyz/openbmc_project/control/host0/systemGUID";
175 std::string intf = "xyz.openbmc_project.Common.UUID";
Jason M. Bills64796042018-10-03 16:51:55 -0700176 std::string service = getService(dbus, intf, objpath);
177 setDbusProperty(dbus, service, objpath, intf, "UUID", guid);
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800178 return IPMI_CC_OK;
179}
180
181ipmi_ret_t ipmiOEMSetBIOSID(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
182 ipmi_request_t request, ipmi_response_t response,
183 ipmi_data_len_t dataLen, ipmi_context_t context)
184{
185 DeviceInfo* data = reinterpret_cast<DeviceInfo*>(request);
186
Jason M. Bills64796042018-10-03 16:51:55 -0700187 if ((*dataLen < 2) || (*dataLen != (1 + data->biosIDLength)))
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800188 {
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800189 *dataLen = 0;
190 return IPMI_CC_REQ_DATA_LEN_INVALID;
191 }
Jason M. Bills64796042018-10-03 16:51:55 -0700192 std::string idString((char*)data->biosId, data->biosIDLength);
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800193
Jason M. Bills64796042018-10-03 16:51:55 -0700194 std::string service = getService(dbus, biosIntf, biosObjPath);
195 setDbusProperty(dbus, service, biosObjPath, biosIntf, biosProp, idString);
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800196 uint8_t* bytesWritten = static_cast<uint8_t*>(response);
197 *bytesWritten =
Jason M. Bills64796042018-10-03 16:51:55 -0700198 data->biosIDLength; // how many bytes are written into storage
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800199 *dataLen = 1;
200 return IPMI_CC_OK;
201}
202
203ipmi_ret_t ipmiOEMGetDeviceInfo(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
204 ipmi_request_t request,
205 ipmi_response_t response,
206 ipmi_data_len_t dataLen, ipmi_context_t context)
207{
208 GetOemDeviceInfoReq* req = reinterpret_cast<GetOemDeviceInfoReq*>(request);
209 GetOemDeviceInfoRes* res = reinterpret_cast<GetOemDeviceInfoRes*>(response);
210
211 if (*dataLen == 0)
212 {
Jason M. Bills64796042018-10-03 16:51:55 -0700213 *dataLen = 0;
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800214 return IPMI_CC_REQ_DATA_LEN_INVALID;
215 }
216
217 size_t reqDataLen = *dataLen;
218 *dataLen = 0;
Jason M. Bills64796042018-10-03 16:51:55 -0700219 if (req->entityType > static_cast<uint8_t>(OEMDevEntityType::sdrVer))
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800220 {
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800221 return IPMI_CC_INVALID_FIELD_REQUEST;
222 }
223
224 // handle OEM command items
Jason M. Bills64796042018-10-03 16:51:55 -0700225 switch (OEMDevEntityType(req->entityType))
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800226 {
227 case OEMDevEntityType::biosId:
228 {
229 if (sizeof(GetOemDeviceInfoReq) != reqDataLen)
230 {
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800231 return IPMI_CC_REQ_DATA_LEN_INVALID;
232 }
233
Jason M. Bills64796042018-10-03 16:51:55 -0700234 std::string service = getService(dbus, biosIntf, biosObjPath);
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800235 try
236 {
Jason M. Bills64796042018-10-03 16:51:55 -0700237 Value variant = getDbusProperty(dbus, service, biosObjPath,
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800238 biosIntf, biosProp);
Jason M. Bills64796042018-10-03 16:51:55 -0700239 std::string& idString =
240 sdbusplus::message::variant_ns::get<std::string>(variant);
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800241 if (req->offset >= idString.size())
242 {
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800243 return IPMI_CC_PARM_OUT_OF_RANGE;
244 }
Jason M. Bills64796042018-10-03 16:51:55 -0700245 size_t length = 0;
246 if (req->countToRead > (idString.size() - req->offset))
247 {
248 length = idString.size() - req->offset;
249 }
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800250 else
251 {
Jason M. Bills64796042018-10-03 16:51:55 -0700252 length = req->countToRead;
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800253 }
Jason M. Bills64796042018-10-03 16:51:55 -0700254 std::copy(idString.begin() + req->offset, idString.end(),
255 res->data);
256 res->resDatalen = length;
257 *dataLen = res->resDatalen + 1;
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800258 }
Jason M. Bills64796042018-10-03 16:51:55 -0700259 catch (sdbusplus::message::variant_ns::bad_variant_access& e)
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800260 {
Jason M. Bills64796042018-10-03 16:51:55 -0700261 phosphor::logging::log<phosphor::logging::level::ERR>(e.what());
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800262 return IPMI_CC_UNSPECIFIED_ERROR;
263 }
264 }
265 break;
266
267 case OEMDevEntityType::devVer:
268 case OEMDevEntityType::sdrVer:
269 // TODO:
270 return IPMI_CC_ILLEGAL_COMMAND;
271 default:
272 return IPMI_CC_INVALID_FIELD_REQUEST;
273 }
274 return IPMI_CC_OK;
275}
276
277ipmi_ret_t ipmiOEMGetAICFRU(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
278 ipmi_request_t request, ipmi_response_t response,
279 ipmi_data_len_t dataLen, ipmi_context_t context)
280{
281 if (*dataLen != 0)
282 {
Jason M. Bills64796042018-10-03 16:51:55 -0700283 *dataLen = 0;
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800284 return IPMI_CC_REQ_DATA_LEN_INVALID;
285 }
286
287 *dataLen = 1;
288 uint8_t* res = reinterpret_cast<uint8_t*>(response);
289 // temporary fix. We don't support AIC FRU now. Just tell BIOS that no
290 // AIC is available so that BIOS will not timeout repeatly which leads to
291 // slow booting.
292 *res = 0; // Byte1=Count of SlotPosition/FruID records.
293 return IPMI_CC_OK;
294}
295
Jason M. Bills64796042018-10-03 16:51:55 -0700296ipmi_ret_t ipmiOEMGetPowerRestoreDelay(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
297 ipmi_request_t request,
298 ipmi_response_t response,
299 ipmi_data_len_t dataLen,
300 ipmi_context_t context)
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800301{
Jason M. Bills64796042018-10-03 16:51:55 -0700302 GetPowerRestoreDelayRes* resp =
303 reinterpret_cast<GetPowerRestoreDelayRes*>(response);
304
305 if (*dataLen != 0)
306 {
307 *dataLen = 0;
308 return IPMI_CC_REQ_DATA_LEN_INVALID;
309 }
310
311 std::string service =
312 getService(dbus, powerRestoreDelayIntf, powerRestoreDelayObjPath);
313 Value variant =
314 getDbusProperty(dbus, service, powerRestoreDelayObjPath,
315 powerRestoreDelayIntf, powerRestoreDelayProp);
316
317 uint16_t delay = sdbusplus::message::variant_ns::get<uint16_t>(variant);
318 resp->byteLSB = delay;
319 resp->byteMSB = delay >> 8;
320
321 *dataLen = sizeof(GetPowerRestoreDelayRes);
322
323 return IPMI_CC_OK;
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800324}
325
Jia, Chunhuicc49b542019-03-20 15:41:07 +0800326static uint8_t bcdToDec(uint8_t val)
327{
328 return ((val / 16 * 10) + (val % 16));
329}
330
331// Allows an update utility or system BIOS to send the status of an embedded
332// firmware update attempt to the BMC. After received, BMC will create a logging
333// record.
334ipmi::RspType<> ipmiOEMSendEmbeddedFwUpdStatus(uint8_t status, uint8_t target,
335 uint8_t majorRevision,
336 uint8_t minorRevision,
337 uint32_t auxInfo)
338{
339 std::string firmware;
Jason M. Billsdc249272019-04-03 09:58:40 -0700340 int instance = (target & targetInstanceMask) >> targetInstanceShift;
Jia, Chunhuicc49b542019-03-20 15:41:07 +0800341 target = (target & selEvtTargetMask) >> selEvtTargetShift;
342
343 /* make sure the status is 0, 1, or 2 as per the spec */
344 if (status > 2)
345 {
346 return ipmi::response(ipmi::ccInvalidFieldRequest);
347 }
Jason M. Billsdc249272019-04-03 09:58:40 -0700348 /* make sure the target is 0, 1, 2, or 4 as per the spec */
349 if (target > 4 || target == 3)
350 {
351 return ipmi::response(ipmi::ccInvalidFieldRequest);
352 }
Jia, Chunhuicc49b542019-03-20 15:41:07 +0800353 /*orignal OEM command is to record OEM SEL.
354 But openbmc does not support OEM SEL, so we redirect it to redfish event
355 logging. */
356 std::string buildInfo;
357 std::string action;
358 switch (FWUpdateTarget(target))
359 {
360 case FWUpdateTarget::targetBMC:
361 firmware = "BMC";
Jason M. Billsdc249272019-04-03 09:58:40 -0700362 buildInfo = "major: " + std::to_string(majorRevision) + " minor: " +
Jia, Chunhuicc49b542019-03-20 15:41:07 +0800363 std::to_string(bcdToDec(minorRevision)) + // BCD encoded
364 " BuildID: " + std::to_string(auxInfo);
365 buildInfo += std::to_string(auxInfo);
366 break;
367 case FWUpdateTarget::targetBIOS:
368 firmware = "BIOS";
369 buildInfo =
Jason M. Billsdc249272019-04-03 09:58:40 -0700370 "major: " +
Jia, Chunhuicc49b542019-03-20 15:41:07 +0800371 std::to_string(bcdToDec(majorRevision)) + // BCD encoded
372 " minor: " +
373 std::to_string(bcdToDec(minorRevision)) + // BCD encoded
374 " ReleaseNumber: " + // ASCII encoded
375 std::to_string(static_cast<uint8_t>(auxInfo >> 0) - '0') +
376 std::to_string(static_cast<uint8_t>(auxInfo >> 8) - '0') +
377 std::to_string(static_cast<uint8_t>(auxInfo >> 16) - '0') +
378 std::to_string(static_cast<uint8_t>(auxInfo >> 24) - '0');
379 break;
380 case FWUpdateTarget::targetME:
381 firmware = "ME";
382 buildInfo =
Jason M. Billsdc249272019-04-03 09:58:40 -0700383 "major: " + std::to_string(majorRevision) + " minor1: " +
Jia, Chunhuicc49b542019-03-20 15:41:07 +0800384 std::to_string(bcdToDec(minorRevision)) + // BCD encoded
385 " minor2: " +
386 std::to_string(bcdToDec(static_cast<uint8_t>(auxInfo >> 0))) +
387 " build1: " +
388 std::to_string(bcdToDec(static_cast<uint8_t>(auxInfo >> 8))) +
389 " build2: " +
390 std::to_string(bcdToDec(static_cast<uint8_t>(auxInfo >> 16)));
391 break;
392 case FWUpdateTarget::targetOEMEWS:
393 firmware = "EWS";
Jason M. Billsdc249272019-04-03 09:58:40 -0700394 buildInfo = "major: " + std::to_string(majorRevision) + " minor: " +
Jia, Chunhuicc49b542019-03-20 15:41:07 +0800395 std::to_string(bcdToDec(minorRevision)) + // BCD encoded
396 " BuildID: " + std::to_string(auxInfo);
397 break;
398 }
399
Jason M. Billsdc249272019-04-03 09:58:40 -0700400 static const std::string openBMCMessageRegistryVersion("0.1");
401 std::string redfishMsgID = "OpenBMC." + openBMCMessageRegistryVersion;
402
Jia, Chunhuicc49b542019-03-20 15:41:07 +0800403 switch (status)
404 {
405 case 0x0:
406 action = "update started";
Jason M. Billsdc249272019-04-03 09:58:40 -0700407 redfishMsgID += ".FirmwareUpdateStarted";
Jia, Chunhuicc49b542019-03-20 15:41:07 +0800408 break;
409 case 0x1:
410 action = "update completed successfully";
Jason M. Billsdc249272019-04-03 09:58:40 -0700411 redfishMsgID += ".FirmwareUpdateCompleted";
Jia, Chunhuicc49b542019-03-20 15:41:07 +0800412 break;
413 case 0x2:
414 action = "update failure";
Jason M. Billsdc249272019-04-03 09:58:40 -0700415 redfishMsgID += ".FirmwareUpdateFailed";
Jia, Chunhuicc49b542019-03-20 15:41:07 +0800416 break;
417 default:
418 action = "unknown";
419 break;
420 }
421
Jason M. Billsdc249272019-04-03 09:58:40 -0700422 std::string firmwareInstanceStr =
423 firmware + " instance: " + std::to_string(instance);
424 std::string message("[firmware update] " + firmwareInstanceStr +
425 " status: <" + action + "> " + buildInfo);
Jia, Chunhuicc49b542019-03-20 15:41:07 +0800426
427 sd_journal_send("MESSAGE=%s", message.c_str(), "PRIORITY=%i", LOG_INFO,
Jason M. Billsdc249272019-04-03 09:58:40 -0700428 "REDFISH_MESSAGE_ID=%s", redfishMsgID.c_str(),
429 "REDFISH_MESSAGE_ARGS=%s,%s", firmwareInstanceStr.c_str(),
430 buildInfo.c_str(), NULL);
Jia, Chunhuicc49b542019-03-20 15:41:07 +0800431 return ipmi::responseSuccess();
432}
433
Jason M. Bills64796042018-10-03 16:51:55 -0700434ipmi_ret_t ipmiOEMSetPowerRestoreDelay(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
435 ipmi_request_t request,
436 ipmi_response_t response,
437 ipmi_data_len_t dataLen,
438 ipmi_context_t context)
439{
440 SetPowerRestoreDelayReq* data =
441 reinterpret_cast<SetPowerRestoreDelayReq*>(request);
442 uint16_t delay = 0;
443
444 if (*dataLen != sizeof(SetPowerRestoreDelayReq))
445 {
446 *dataLen = 0;
447 return IPMI_CC_REQ_DATA_LEN_INVALID;
448 }
449 delay = data->byteMSB;
450 delay = (delay << 8) | data->byteLSB;
451 std::string service =
452 getService(dbus, powerRestoreDelayIntf, powerRestoreDelayObjPath);
453 setDbusProperty(dbus, service, powerRestoreDelayObjPath,
454 powerRestoreDelayIntf, powerRestoreDelayProp, delay);
455 *dataLen = 0;
456
457 return IPMI_CC_OK;
458}
459
460ipmi_ret_t ipmiOEMGetProcessorErrConfig(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
461 ipmi_request_t request,
462 ipmi_response_t response,
463 ipmi_data_len_t dataLen,
464 ipmi_context_t context)
465{
466 GetProcessorErrConfigRes* resp =
467 reinterpret_cast<GetProcessorErrConfigRes*>(response);
468
469 if (*dataLen != 0)
470 {
471 *dataLen = 0;
472 return IPMI_CC_REQ_DATA_LEN_INVALID;
473 }
474
475 std::string service =
476 getService(dbus, processorErrConfigIntf, processorErrConfigObjPath);
477 Value variant = getDbusProperty(dbus, service, processorErrConfigObjPath,
478 processorErrConfigIntf, "ResetCfg");
479 resp->resetCfg = sdbusplus::message::variant_ns::get<uint8_t>(variant);
480
481 std::vector<uint8_t> caterrStatus;
Kuiying Wangbc546672018-11-23 15:41:05 +0800482 sdbusplus::message::variant<std::vector<uint8_t>> message;
Jason M. Bills64796042018-10-03 16:51:55 -0700483
484 auto method =
485 dbus.new_method_call(service.c_str(), processorErrConfigObjPath,
486 "org.freedesktop.DBus.Properties", "Get");
487
488 method.append(processorErrConfigIntf, "CATERRStatus");
Kuiying Wangbc546672018-11-23 15:41:05 +0800489 auto reply = dbus.call(method);
Jason M. Bills64796042018-10-03 16:51:55 -0700490
491 try
492 {
Kuiying Wangbc546672018-11-23 15:41:05 +0800493 reply.read(message);
494 caterrStatus =
495 sdbusplus::message::variant_ns::get<std::vector<uint8_t>>(message);
Jason M. Bills64796042018-10-03 16:51:55 -0700496 }
497 catch (sdbusplus::exception_t&)
498 {
Kuiying Wangbc546672018-11-23 15:41:05 +0800499 phosphor::logging::log<phosphor::logging::level::ERR>(
Jason M. Bills64796042018-10-03 16:51:55 -0700500 "ipmiOEMGetProcessorErrConfig: error on dbus",
501 phosphor::logging::entry("PRORPERTY=CATERRStatus"),
502 phosphor::logging::entry("PATH=%s", processorErrConfigObjPath),
503 phosphor::logging::entry("INTERFACE=%s", processorErrConfigIntf));
504 return IPMI_CC_UNSPECIFIED_ERROR;
505 }
506
507 size_t len =
508 maxCPUNum <= caterrStatus.size() ? maxCPUNum : caterrStatus.size();
509 caterrStatus.resize(len);
510 std::copy(caterrStatus.begin(), caterrStatus.end(), resp->caterrStatus);
511 *dataLen = sizeof(GetProcessorErrConfigRes);
512
513 return IPMI_CC_OK;
514}
515
516ipmi_ret_t ipmiOEMSetProcessorErrConfig(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
517 ipmi_request_t request,
518 ipmi_response_t response,
519 ipmi_data_len_t dataLen,
520 ipmi_context_t context)
521{
522 SetProcessorErrConfigReq* req =
523 reinterpret_cast<SetProcessorErrConfigReq*>(request);
524
525 if (*dataLen != sizeof(SetProcessorErrConfigReq))
526 {
527 *dataLen = 0;
528 return IPMI_CC_REQ_DATA_LEN_INVALID;
529 }
530 std::string service =
531 getService(dbus, processorErrConfigIntf, processorErrConfigObjPath);
532 setDbusProperty(dbus, service, processorErrConfigObjPath,
533 processorErrConfigIntf, "ResetCfg", req->resetCfg);
534
535 setDbusProperty(dbus, service, processorErrConfigObjPath,
536 processorErrConfigIntf, "ResetErrorOccurrenceCounts",
537 req->resetErrorOccurrenceCounts);
538 *dataLen = 0;
539
540 return IPMI_CC_OK;
541}
542
Yong Li703922d2018-11-06 13:25:31 +0800543ipmi_ret_t ipmiOEMGetShutdownPolicy(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
544 ipmi_request_t request,
545 ipmi_response_t response,
546 ipmi_data_len_t dataLen,
547 ipmi_context_t context)
548{
549 GetOEMShutdownPolicyRes* resp =
550 reinterpret_cast<GetOEMShutdownPolicyRes*>(response);
551
552 if (*dataLen != 0)
553 {
554 phosphor::logging::log<phosphor::logging::level::ERR>(
Kuiying Wang45f04982018-12-26 09:23:08 +0800555 "oem_get_shutdown_policy: invalid input len!");
Yong Li703922d2018-11-06 13:25:31 +0800556 *dataLen = 0;
557 return IPMI_CC_REQ_DATA_LEN_INVALID;
558 }
559
560 *dataLen = 0;
561
562 try
563 {
564 std::string service =
565 getService(dbus, oemShutdownPolicyIntf, oemShutdownPolicyObjPath);
566 Value variant = getDbusProperty(dbus, service, oemShutdownPolicyObjPath,
567 oemShutdownPolicyIntf,
568 oemShutdownPolicyObjPathProp);
Yong Li0669d192019-05-06 14:01:46 +0800569
570 if (sdbusplus::com::intel::Control::server::OCOTShutdownPolicy::
571 convertPolicyFromString(std::get<std::string>(variant)) ==
572 sdbusplus::com::intel::Control::server::OCOTShutdownPolicy::Policy::
573 NoShutdownOnOCOT)
574 {
575 resp->policy = 0;
576 }
577 else if (sdbusplus::com::intel::Control::server::OCOTShutdownPolicy::
578 convertPolicyFromString(std::get<std::string>(variant)) ==
579 sdbusplus::com::intel::Control::server::OCOTShutdownPolicy::
580 Policy::ShutdownOnOCOT)
581 {
582 resp->policy = 1;
583 }
584 else
585 {
586 phosphor::logging::log<phosphor::logging::level::ERR>(
587 "oem_set_shutdown_policy: invalid property!",
588 phosphor::logging::entry(
589 "PROP=%s", std::get<std::string>(variant).c_str()));
590 return IPMI_CC_UNSPECIFIED_ERROR;
591 }
Yong Li703922d2018-11-06 13:25:31 +0800592 // TODO needs to check if it is multi-node products,
593 // policy is only supported on node 3/4
594 resp->policySupport = shutdownPolicySupported;
595 }
596 catch (sdbusplus::exception_t& e)
597 {
598 phosphor::logging::log<phosphor::logging::level::ERR>(e.description());
599 return IPMI_CC_UNSPECIFIED_ERROR;
600 }
601
602 *dataLen = sizeof(GetOEMShutdownPolicyRes);
603 return IPMI_CC_OK;
604}
605
606ipmi_ret_t ipmiOEMSetShutdownPolicy(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
607 ipmi_request_t request,
608 ipmi_response_t response,
609 ipmi_data_len_t dataLen,
610 ipmi_context_t context)
611{
612 uint8_t* req = reinterpret_cast<uint8_t*>(request);
Yong Li0669d192019-05-06 14:01:46 +0800613 sdbusplus::com::intel::Control::server::OCOTShutdownPolicy::Policy policy =
614 sdbusplus::com::intel::Control::server::OCOTShutdownPolicy::Policy::
615 NoShutdownOnOCOT;
Yong Li703922d2018-11-06 13:25:31 +0800616
617 // TODO needs to check if it is multi-node products,
618 // policy is only supported on node 3/4
619 if (*dataLen != 1)
620 {
621 phosphor::logging::log<phosphor::logging::level::ERR>(
622 "oem_set_shutdown_policy: invalid input len!");
623 *dataLen = 0;
624 return IPMI_CC_REQ_DATA_LEN_INVALID;
625 }
626
627 *dataLen = 0;
628 if ((*req != noShutdownOnOCOT) && (*req != shutdownOnOCOT))
629 {
630 phosphor::logging::log<phosphor::logging::level::ERR>(
631 "oem_set_shutdown_policy: invalid input!");
632 return IPMI_CC_INVALID_FIELD_REQUEST;
633 }
634
Yong Li0669d192019-05-06 14:01:46 +0800635 if (*req == noShutdownOnOCOT)
636 {
637 policy = sdbusplus::com::intel::Control::server::OCOTShutdownPolicy::
638 Policy::NoShutdownOnOCOT;
639 }
640 else
641 {
642 policy = sdbusplus::com::intel::Control::server::OCOTShutdownPolicy::
643 Policy::ShutdownOnOCOT;
644 }
645
Yong Li703922d2018-11-06 13:25:31 +0800646 try
647 {
648 std::string service =
649 getService(dbus, oemShutdownPolicyIntf, oemShutdownPolicyObjPath);
Yong Li0669d192019-05-06 14:01:46 +0800650 setDbusProperty(
651 dbus, service, oemShutdownPolicyObjPath, oemShutdownPolicyIntf,
652 oemShutdownPolicyObjPathProp,
653 sdbusplus::com::intel::Control::server::convertForMessage(policy));
Yong Li703922d2018-11-06 13:25:31 +0800654 }
655 catch (sdbusplus::exception_t& e)
656 {
657 phosphor::logging::log<phosphor::logging::level::ERR>(e.description());
658 return IPMI_CC_UNSPECIFIED_ERROR;
659 }
660
661 return IPMI_CC_OK;
662}
663
Suryakanth Sekard509eb92018-11-15 17:44:11 +0530664/** @brief implementation for check the DHCP or not in IPv4
665 * @param[in] Channel - Channel number
666 * @returns true or false.
667 */
668static bool isDHCPEnabled(uint8_t Channel)
669{
670 try
671 {
672 auto ethdevice = getChannelName(Channel);
673 if (ethdevice.empty())
674 {
675 return false;
676 }
677 auto ethIP = ethdevice + "/ipv4";
678 auto ethernetObj =
679 getDbusObject(dbus, networkIPIntf, networkRoot, ethIP);
680 auto value = getDbusProperty(dbus, networkService, ethernetObj.first,
681 networkIPIntf, "Origin");
682 if (sdbusplus::message::variant_ns::get<std::string>(value) ==
683 "xyz.openbmc_project.Network.IP.AddressOrigin.DHCP")
684 {
685 return true;
686 }
687 else
688 {
689 return false;
690 }
691 }
692 catch (sdbusplus::exception_t& e)
693 {
694 phosphor::logging::log<phosphor::logging::level::ERR>(e.description());
695 return true;
696 }
697}
698
699/** @brief implementes for check the DHCP or not in IPv6
700 * @param[in] Channel - Channel number
701 * @returns true or false.
702 */
703static bool isDHCPIPv6Enabled(uint8_t Channel)
704{
705
706 try
707 {
708 auto ethdevice = getChannelName(Channel);
709 if (ethdevice.empty())
710 {
711 return false;
712 }
713 auto ethIP = ethdevice + "/ipv6";
714 auto objectInfo =
715 getDbusObject(dbus, networkIPIntf, networkRoot, ethIP);
716 auto properties = getAllDbusProperties(dbus, objectInfo.second,
717 objectInfo.first, networkIPIntf);
718 if (sdbusplus::message::variant_ns::get<std::string>(
719 properties["Origin"]) ==
720 "xyz.openbmc_project.Network.IP.AddressOrigin.DHCP")
721 {
722 return true;
723 }
724 else
725 {
726 return false;
727 }
728 }
729 catch (sdbusplus::exception_t& e)
730 {
731 phosphor::logging::log<phosphor::logging::level::ERR>(e.description());
732 return true;
733 }
734}
735
736/** @brief implementes the creating of default new user
737 * @param[in] userName - new username in 16 bytes.
738 * @param[in] userPassword - new password in 20 bytes
739 * @returns ipmi completion code.
740 */
741ipmi::RspType<> ipmiOEMSetUser2Activation(
742 std::array<uint8_t, ipmi::ipmiMaxUserName>& userName,
743 std::array<uint8_t, ipmi::maxIpmi20PasswordSize>& userPassword)
744{
745 bool userState = false;
746 // Check for System Interface not exist and LAN should be static
747 for (uint8_t channel = 0; channel < maxIpmiChannels; channel++)
748 {
749 ChannelInfo chInfo;
750 try
751 {
752 getChannelInfo(channel, chInfo);
753 }
754 catch (sdbusplus::exception_t& e)
755 {
756 phosphor::logging::log<phosphor::logging::level::ERR>(
757 "ipmiOEMSetUser2Activation: Failed to get Channel Info",
758 phosphor::logging::entry("MSG: %s", e.description()));
759 return ipmi::response(ipmi::ccUnspecifiedError);
760 }
761 if (chInfo.mediumType ==
762 static_cast<uint8_t>(EChannelMediumType::systemInterface))
763 {
764 phosphor::logging::log<phosphor::logging::level::ERR>(
765 "ipmiOEMSetUser2Activation: system interface exist .");
766 return ipmi::response(ipmi::ccCommandNotAvailable);
767 }
768 else
769 {
770
771 if (chInfo.mediumType ==
772 static_cast<uint8_t>(EChannelMediumType::lan8032))
773 {
774 if (isDHCPIPv6Enabled(channel) || isDHCPEnabled(channel))
775 {
776 phosphor::logging::log<phosphor::logging::level::ERR>(
777 "ipmiOEMSetUser2Activation: DHCP enabled .");
778 return ipmi::response(ipmi::ccCommandNotAvailable);
779 }
780 }
781 }
782 }
783 uint8_t maxChUsers = 0, enabledUsers = 0, fixedUsers = 0;
784 if (ipmi::ccSuccess ==
785 ipmiUserGetAllCounts(maxChUsers, enabledUsers, fixedUsers))
786 {
787 if (enabledUsers > 1)
788 {
789 phosphor::logging::log<phosphor::logging::level::ERR>(
790 "ipmiOEMSetUser2Activation: more than one user is enabled.");
791 return ipmi::response(ipmi::ccCommandNotAvailable);
792 }
793 // Check the user 2 is enabled or not
794 ipmiUserCheckEnabled(ipmiDefaultUserId, userState);
795 if (userState == true)
796 {
797 phosphor::logging::log<phosphor::logging::level::ERR>(
798 "ipmiOEMSetUser2Activation: user 2 already enabled .");
799 return ipmi::response(ipmi::ccCommandNotAvailable);
800 }
801 }
802 else
803 {
804 return ipmi::response(ipmi::ccUnspecifiedError);
805 }
806
807#if BYTE_ORDER == LITTLE_ENDIAN
808 PrivAccess privAccess = {PRIVILEGE_ADMIN, true, true, true, 0};
809#endif
810#if BYTE_ORDER == BIG_ENDIAN
811 PrivAccess privAccess = {0, true, true, true, PRIVILEGE_ADMIN};
812#endif
813
814 if (ipmi::ccSuccess ==
815 ipmiUserSetUserName(ipmiDefaultUserId,
816 reinterpret_cast<const char*>(userName.data())))
817 {
818 if (ipmi::ccSuccess ==
819 ipmiUserSetUserPassword(
820 ipmiDefaultUserId,
821 reinterpret_cast<const char*>(userPassword.data())))
822 {
823 if (ipmi::ccSuccess ==
824 ipmiUserSetPrivilegeAccess(
825 ipmiDefaultUserId,
826 static_cast<uint8_t>(ipmi::EChannelID::chanLan1),
827 privAccess, true))
828 {
829 phosphor::logging::log<phosphor::logging::level::INFO>(
830 "ipmiOEMSetUser2Activation: user created successfully ");
831 return ipmi::responseSuccess();
832 }
833 }
834 // we need to delete the default user id which added in this command as
835 // password / priv setting is failed.
836 ipmiUserSetUserName(ipmiDefaultUserId, "");
837 phosphor::logging::log<phosphor::logging::level::ERR>(
838 "ipmiOEMSetUser2Activation: password / priv setting is failed.");
839 }
840 else
841 {
842 phosphor::logging::log<phosphor::logging::level::ERR>(
843 "ipmiOEMSetUser2Activation: Setting username failed.");
844 }
845
846 return ipmi::response(ipmi::ccCommandNotAvailable);
847}
848
Richard Marian Thomaiyarfc5e9852019-04-14 15:06:27 +0530849/** @brief implementes setting password for special user
850 * @param[in] specialUserIndex
851 * @param[in] userPassword - new password in 20 bytes
852 * @returns ipmi completion code.
853 */
854ipmi::RspType<> ipmiOEMSetSpecialUserPassword(ipmi::Context::ptr ctx,
855 uint8_t specialUserIndex,
856 std::vector<uint8_t> userPassword)
857{
858 ChannelInfo chInfo;
859 try
860 {
861 getChannelInfo(ctx->channel, chInfo);
862 }
863 catch (sdbusplus::exception_t& e)
864 {
865 phosphor::logging::log<phosphor::logging::level::ERR>(
866 "ipmiOEMSetSpecialUserPassword: Failed to get Channel Info",
867 phosphor::logging::entry("MSG: %s", e.description()));
868 return ipmi::responseUnspecifiedError();
869 }
870 if (chInfo.mediumType !=
871 static_cast<uint8_t>(EChannelMediumType::systemInterface))
872 {
873 phosphor::logging::log<phosphor::logging::level::ERR>(
874 "ipmiOEMSetSpecialUserPassword: Error - supported only in KCS "
875 "interface");
876 return ipmi::responseCommandNotAvailable();
877 }
878 if (specialUserIndex != 0)
879 {
880 phosphor::logging::log<phosphor::logging::level::ERR>(
881 "ipmiOEMSetSpecialUserPassword: Invalid user account");
882 return ipmi::responseParmOutOfRange();
883 }
884 constexpr uint8_t minPasswordSizeRequired = 6;
885 if (userPassword.size() < minPasswordSizeRequired ||
886 userPassword.size() > ipmi::maxIpmi20PasswordSize)
887 {
888 return ipmi::responseReqDataLenInvalid();
889 }
890 std::string passwd;
891 passwd.assign(reinterpret_cast<const char*>(userPassword.data()),
892 userPassword.size());
893 return ipmi::response(ipmiSetSpecialUserPassword("root", passwd));
894}
895
Kuiying Wang45f04982018-12-26 09:23:08 +0800896namespace ledAction
897{
898using namespace sdbusplus::xyz::openbmc_project::Led::server;
899std::map<Physical::Action, uint8_t> actionDbusToIpmi = {
900 {Physical::Action::Off, 0x00},
901 {Physical::Action::On, 0x10},
902 {Physical::Action::Blink, 0x01}};
903
904std::map<uint8_t, std::string> offsetObjPath = {
905 {2, statusAmberObjPath}, {4, statusGreenObjPath}, {6, identifyLEDObjPath}};
906
907} // namespace ledAction
908
909int8_t getLEDState(sdbusplus::bus::bus& bus, const std::string& intf,
910 const std::string& objPath, uint8_t& state)
911{
912 try
913 {
914 std::string service = getService(bus, intf, objPath);
915 Value stateValue =
916 getDbusProperty(bus, service, objPath, intf, "State");
917 std::string strState =
918 sdbusplus::message::variant_ns::get<std::string>(stateValue);
919 state = ledAction::actionDbusToIpmi.at(
920 sdbusplus::xyz::openbmc_project::Led::server::Physical::
921 convertActionFromString(strState));
922 }
923 catch (sdbusplus::exception::SdBusError& e)
924 {
925 phosphor::logging::log<phosphor::logging::level::ERR>(e.what());
926 return -1;
927 }
928 return 0;
929}
930
931ipmi_ret_t ipmiOEMGetLEDStatus(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
932 ipmi_request_t request, ipmi_response_t response,
933 ipmi_data_len_t dataLen, ipmi_context_t context)
934{
935 uint8_t* resp = reinterpret_cast<uint8_t*>(response);
936 // LED Status
937 //[1:0] = Reserved
938 //[3:2] = Status(Amber)
939 //[5:4] = Status(Green)
940 //[7:6] = System Identify
941 // Status definitions:
942 // 00b = Off
943 // 01b = Blink
944 // 10b = On
945 // 11b = invalid
946 if (*dataLen != 0)
947 {
948 phosphor::logging::log<phosphor::logging::level::ERR>(
949 "oem_get_led_status: invalid input len!");
950 *dataLen = 0;
951 return IPMI_CC_REQ_DATA_LEN_INVALID;
952 }
953
954 phosphor::logging::log<phosphor::logging::level::DEBUG>("GET led status");
955 *resp = 0;
956 *dataLen = 0;
957 for (auto it = ledAction::offsetObjPath.begin();
958 it != ledAction::offsetObjPath.end(); ++it)
959 {
960 uint8_t state = 0;
961 if (-1 == getLEDState(dbus, ledIntf, it->second, state))
962 {
963 phosphor::logging::log<phosphor::logging::level::ERR>(
964 "oem_get_led_status: fail to get ID LED status!");
965 return IPMI_CC_UNSPECIFIED_ERROR;
966 }
967 *resp |= state << it->first;
968 }
969
970 *dataLen = sizeof(*resp);
971 return IPMI_CC_OK;
972}
973
Yong Li23737fe2019-02-19 08:49:55 +0800974ipmi_ret_t ipmiOEMCfgHostSerialPortSpeed(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
975 ipmi_request_t request,
976 ipmi_response_t response,
977 ipmi_data_len_t dataLen,
978 ipmi_context_t context)
979{
980 CfgHostSerialReq* req = reinterpret_cast<CfgHostSerialReq*>(request);
981 uint8_t* resp = reinterpret_cast<uint8_t*>(response);
982
983 if (*dataLen == 0)
984 {
985 phosphor::logging::log<phosphor::logging::level::ERR>(
986 "CfgHostSerial: invalid input len!",
987 phosphor::logging::entry("LEN=%d", *dataLen));
988 return IPMI_CC_REQ_DATA_LEN_INVALID;
989 }
990
991 switch (req->command)
992 {
993 case getHostSerialCfgCmd:
994 {
995 if (*dataLen != 1)
996 {
997 phosphor::logging::log<phosphor::logging::level::ERR>(
998 "CfgHostSerial: invalid input len!");
999 *dataLen = 0;
1000 return IPMI_CC_REQ_DATA_LEN_INVALID;
1001 }
1002
1003 *dataLen = 0;
1004
1005 boost::process::ipstream is;
1006 std::vector<std::string> data;
1007 std::string line;
1008 boost::process::child c1(fwGetEnvCmd, "-n", fwHostSerailCfgEnvName,
1009 boost::process::std_out > is);
1010
1011 while (c1.running() && std::getline(is, line) && !line.empty())
1012 {
1013 data.push_back(line);
1014 }
1015
1016 c1.wait();
1017 if (c1.exit_code())
1018 {
1019 phosphor::logging::log<phosphor::logging::level::ERR>(
1020 "CfgHostSerial:: error on execute",
1021 phosphor::logging::entry("EXECUTE=%s", fwSetEnvCmd));
1022 // Using the default value
1023 *resp = 0;
1024 }
1025 else
1026 {
1027 if (data.size() != 1)
1028 {
1029 phosphor::logging::log<phosphor::logging::level::ERR>(
1030 "CfgHostSerial:: error on read env");
1031 return IPMI_CC_UNSPECIFIED_ERROR;
1032 }
1033 try
1034 {
1035 unsigned long tmp = std::stoul(data[0]);
1036 if (tmp > std::numeric_limits<uint8_t>::max())
1037 {
1038 throw std::out_of_range("Out of range");
1039 }
1040 *resp = static_cast<uint8_t>(tmp);
1041 }
1042 catch (const std::invalid_argument& e)
1043 {
1044 phosphor::logging::log<phosphor::logging::level::ERR>(
1045 "invalid config ",
1046 phosphor::logging::entry("ERR=%s", e.what()));
1047 return IPMI_CC_UNSPECIFIED_ERROR;
1048 }
1049 catch (const std::out_of_range& e)
1050 {
1051 phosphor::logging::log<phosphor::logging::level::ERR>(
1052 "out_of_range config ",
1053 phosphor::logging::entry("ERR=%s", e.what()));
1054 return IPMI_CC_UNSPECIFIED_ERROR;
1055 }
1056 }
1057
1058 *dataLen = 1;
1059 break;
1060 }
1061 case setHostSerialCfgCmd:
1062 {
1063 if (*dataLen != sizeof(CfgHostSerialReq))
1064 {
1065 phosphor::logging::log<phosphor::logging::level::ERR>(
1066 "CfgHostSerial: invalid input len!");
1067 *dataLen = 0;
1068 return IPMI_CC_REQ_DATA_LEN_INVALID;
1069 }
1070
1071 *dataLen = 0;
1072
1073 if (req->parameter > HostSerialCfgParamMax)
1074 {
1075 phosphor::logging::log<phosphor::logging::level::ERR>(
1076 "CfgHostSerial: invalid input!");
1077 return IPMI_CC_INVALID_FIELD_REQUEST;
1078 }
1079
1080 boost::process::child c1(fwSetEnvCmd, fwHostSerailCfgEnvName,
1081 std::to_string(req->parameter));
1082
1083 c1.wait();
1084 if (c1.exit_code())
1085 {
1086 phosphor::logging::log<phosphor::logging::level::ERR>(
1087 "CfgHostSerial:: error on execute",
1088 phosphor::logging::entry("EXECUTE=%s", fwGetEnvCmd));
1089 return IPMI_CC_UNSPECIFIED_ERROR;
1090 }
1091 break;
1092 }
1093 default:
1094 phosphor::logging::log<phosphor::logging::level::ERR>(
1095 "CfgHostSerial: invalid input!");
1096 *dataLen = 0;
1097 return IPMI_CC_INVALID_FIELD_REQUEST;
1098 }
1099
1100 return IPMI_CC_OK;
1101}
1102
James Feist91244a62019-02-19 15:04:54 -08001103constexpr const char* thermalModeInterface =
1104 "xyz.openbmc_project.Control.ThermalMode";
1105constexpr const char* thermalModePath =
1106 "/xyz/openbmc_project/control/thermal_mode";
1107
1108bool getFanProfileInterface(
1109 sdbusplus::bus::bus& bus,
1110 boost::container::flat_map<
1111 std::string, std::variant<std::vector<std::string>, std::string>>& resp)
1112{
1113 auto call = bus.new_method_call(settingsBusName, thermalModePath, PROP_INTF,
1114 "GetAll");
1115 call.append(thermalModeInterface);
1116 try
1117 {
1118 auto data = bus.call(call);
1119 data.read(resp);
1120 }
1121 catch (sdbusplus::exception_t& e)
1122 {
1123 phosphor::logging::log<phosphor::logging::level::ERR>(
1124 "getFanProfileInterface: can't get thermal mode!",
1125 phosphor::logging::entry("ERR=%s", e.what()));
1126 return false;
1127 }
1128 return true;
1129}
1130
1131ipmi_ret_t ipmiOEMSetFanConfig(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
1132 ipmi_request_t request, ipmi_response_t response,
1133 ipmi_data_len_t dataLen, ipmi_context_t context)
1134{
1135
1136 if (*dataLen < 2 || *dataLen > 7)
1137 {
1138 phosphor::logging::log<phosphor::logging::level::ERR>(
1139 "ipmiOEMSetFanConfig: invalid input len!");
1140 *dataLen = 0;
1141 return IPMI_CC_REQ_DATA_LEN_INVALID;
1142 }
1143
1144 // todo: tell bios to only send first 2 bytes
1145
1146 SetFanConfigReq* req = reinterpret_cast<SetFanConfigReq*>(request);
1147 boost::container::flat_map<
1148 std::string, std::variant<std::vector<std::string>, std::string>>
1149 profileData;
1150 if (!getFanProfileInterface(dbus, profileData))
1151 {
1152 return IPMI_CC_UNSPECIFIED_ERROR;
1153 }
1154
1155 std::vector<std::string>* supported =
1156 std::get_if<std::vector<std::string>>(&profileData["Supported"]);
1157 if (supported == nullptr)
1158 {
1159 return IPMI_CC_INVALID_FIELD_REQUEST;
1160 }
1161 std::string mode;
1162 if (req->flags &
1163 (1 << static_cast<uint8_t>(setFanProfileFlags::setPerfAcousMode)))
1164 {
1165 bool performanceMode =
1166 (req->flags & (1 << static_cast<uint8_t>(
1167 setFanProfileFlags::performAcousSelect))) > 0;
1168
1169 if (performanceMode)
1170 {
1171
1172 if (std::find(supported->begin(), supported->end(),
1173 "Performance") != supported->end())
1174 {
1175 mode = "Performance";
1176 }
1177 }
1178 else
1179 {
1180
1181 if (std::find(supported->begin(), supported->end(), "Acoustic") !=
1182 supported->end())
1183 {
1184 mode = "Acoustic";
1185 }
1186 }
1187 if (mode.empty())
1188 {
1189 return IPMI_CC_INVALID_FIELD_REQUEST;
1190 }
1191 setDbusProperty(dbus, settingsBusName, thermalModePath,
1192 thermalModeInterface, "Current", mode);
1193 }
1194
1195 return IPMI_CC_OK;
1196}
1197
1198ipmi_ret_t ipmiOEMGetFanConfig(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
1199 ipmi_request_t request, ipmi_response_t response,
1200 ipmi_data_len_t dataLen, ipmi_context_t context)
1201{
1202
1203 if (*dataLen > 1)
1204 {
1205 phosphor::logging::log<phosphor::logging::level::ERR>(
1206 "ipmiOEMGetFanConfig: invalid input len!");
1207 *dataLen = 0;
1208 return IPMI_CC_REQ_DATA_LEN_INVALID;
1209 }
1210
1211 // todo: talk to bios about needing less information
1212
1213 GetFanConfigResp* resp = reinterpret_cast<GetFanConfigResp*>(response);
1214 *dataLen = sizeof(GetFanConfigResp);
1215
1216 boost::container::flat_map<
1217 std::string, std::variant<std::vector<std::string>, std::string>>
1218 profileData;
1219
1220 if (!getFanProfileInterface(dbus, profileData))
1221 {
1222 return IPMI_CC_UNSPECIFIED_ERROR;
1223 }
1224
1225 std::string* current = std::get_if<std::string>(&profileData["Current"]);
1226
1227 if (current == nullptr)
1228 {
1229 phosphor::logging::log<phosphor::logging::level::ERR>(
1230 "ipmiOEMGetFanConfig: can't get current mode!");
1231 return IPMI_CC_UNSPECIFIED_ERROR;
1232 }
1233 bool performance = (*current == "Performance");
1234
1235 if (performance)
1236 {
1237 resp->flags |= 1 << 2;
1238 }
1239
1240 return IPMI_CC_OK;
1241}
1242
James Feist5f957ca2019-03-14 15:33:55 -07001243constexpr const char* cfmLimitSettingPath =
1244 "/xyz/openbmc_project/control/cfm_limit";
1245constexpr const char* cfmLimitIface = "xyz.openbmc_project.Control.CFMLimit";
James Feistfaa4f222019-03-21 16:21:55 -07001246constexpr const size_t legacyExitAirSensorNumber = 0x2e;
James Feistacc8a4e2019-04-02 14:23:57 -07001247constexpr const char* pidConfigurationIface =
1248 "xyz.openbmc_project.Configuration.Pid";
James Feistfaa4f222019-03-21 16:21:55 -07001249
1250static std::string getExitAirConfigPath()
1251{
1252
1253 auto method =
1254 dbus.new_method_call("xyz.openbmc_project.ObjectMapper",
1255 "/xyz/openbmc_project/object_mapper",
1256 "xyz.openbmc_project.ObjectMapper", "GetSubTree");
1257
James Feistacc8a4e2019-04-02 14:23:57 -07001258 method.append("/", 0, std::array<const char*, 1>{pidConfigurationIface});
James Feistfaa4f222019-03-21 16:21:55 -07001259 std::string path;
1260 GetSubTreeType resp;
1261 try
1262 {
1263 auto reply = dbus.call(method);
1264 reply.read(resp);
1265 }
1266 catch (sdbusplus::exception_t&)
1267 {
1268 phosphor::logging::log<phosphor::logging::level::ERR>(
1269 "ipmiOEMGetFscParameter: mapper error");
1270 };
1271 auto config = std::find_if(resp.begin(), resp.end(), [](const auto& pair) {
1272 return pair.first.find("Exit_Air") != std::string::npos;
1273 });
1274 if (config != resp.end())
1275 {
1276 path = std::move(config->first);
1277 }
1278 return path;
1279}
James Feist5f957ca2019-03-14 15:33:55 -07001280
James Feistacc8a4e2019-04-02 14:23:57 -07001281// flat map to make alphabetical
1282static boost::container::flat_map<std::string, PropertyMap> getPidConfigs()
1283{
1284 boost::container::flat_map<std::string, PropertyMap> ret;
1285 auto method =
1286 dbus.new_method_call("xyz.openbmc_project.ObjectMapper",
1287 "/xyz/openbmc_project/object_mapper",
1288 "xyz.openbmc_project.ObjectMapper", "GetSubTree");
1289
1290 method.append("/", 0, std::array<const char*, 1>{pidConfigurationIface});
1291 GetSubTreeType resp;
1292
1293 try
1294 {
1295 auto reply = dbus.call(method);
1296 reply.read(resp);
1297 }
1298 catch (sdbusplus::exception_t&)
1299 {
1300 phosphor::logging::log<phosphor::logging::level::ERR>(
1301 "getFanConfigPaths: mapper error");
1302 };
1303 for (const auto& [path, objects] : resp)
1304 {
1305 if (objects.empty())
1306 {
1307 continue; // should be impossible
1308 }
Zhu, Yungebe560b02019-04-21 21:19:21 -04001309
1310 try
1311 {
1312 ret.emplace(path, getAllDbusProperties(dbus, objects[0].first, path,
1313 pidConfigurationIface));
1314 }
1315 catch (sdbusplus::exception_t& e)
1316 {
1317 phosphor::logging::log<phosphor::logging::level::ERR>(
1318 "getPidConfigs: can't get DbusProperties!",
1319 phosphor::logging::entry("ERR=%s", e.what()));
1320 }
James Feistacc8a4e2019-04-02 14:23:57 -07001321 }
1322 return ret;
1323}
1324
1325ipmi::RspType<uint8_t> ipmiOEMGetFanSpeedOffset(void)
1326{
1327 boost::container::flat_map<std::string, PropertyMap> data = getPidConfigs();
1328 if (data.empty())
1329 {
1330 return ipmi::responseResponseError();
1331 }
1332 uint8_t minOffset = std::numeric_limits<uint8_t>::max();
1333 for (const auto& [_, pid] : data)
1334 {
1335 auto findClass = pid.find("Class");
1336 if (findClass == pid.end())
1337 {
1338 phosphor::logging::log<phosphor::logging::level::ERR>(
1339 "ipmiOEMGetFscParameter: found illegal pid "
1340 "configurations");
1341 return ipmi::responseResponseError();
1342 }
1343 std::string type = std::get<std::string>(findClass->second);
1344 if (type == "fan")
1345 {
1346 auto findOutLimit = pid.find("OutLimitMin");
1347 if (findOutLimit == pid.end())
1348 {
1349 phosphor::logging::log<phosphor::logging::level::ERR>(
1350 "ipmiOEMGetFscParameter: found illegal pid "
1351 "configurations");
1352 return ipmi::responseResponseError();
1353 }
1354 // get the min out of all the offsets
1355 minOffset = std::min(
1356 minOffset,
1357 static_cast<uint8_t>(std::get<double>(findOutLimit->second)));
1358 }
1359 }
1360 if (minOffset == std::numeric_limits<uint8_t>::max())
1361 {
1362 phosphor::logging::log<phosphor::logging::level::ERR>(
1363 "ipmiOEMGetFscParameter: found no fan configurations!");
1364 return ipmi::responseResponseError();
1365 }
1366
1367 return ipmi::responseSuccess(minOffset);
1368}
1369
1370ipmi::RspType<> ipmiOEMSetFanSpeedOffset(uint8_t offset)
1371{
1372 boost::container::flat_map<std::string, PropertyMap> data = getPidConfigs();
1373 if (data.empty())
1374 {
1375
1376 phosphor::logging::log<phosphor::logging::level::ERR>(
1377 "ipmiOEMSetFanSpeedOffset: found no pid configurations!");
1378 return ipmi::responseResponseError();
1379 }
1380
1381 bool found = false;
1382 for (const auto& [path, pid] : data)
1383 {
1384 auto findClass = pid.find("Class");
1385 if (findClass == pid.end())
1386 {
1387
1388 phosphor::logging::log<phosphor::logging::level::ERR>(
1389 "ipmiOEMSetFanSpeedOffset: found illegal pid "
1390 "configurations");
1391 return ipmi::responseResponseError();
1392 }
1393 std::string type = std::get<std::string>(findClass->second);
1394 if (type == "fan")
1395 {
1396 auto findOutLimit = pid.find("OutLimitMin");
1397 if (findOutLimit == pid.end())
1398 {
1399
1400 phosphor::logging::log<phosphor::logging::level::ERR>(
1401 "ipmiOEMSetFanSpeedOffset: found illegal pid "
1402 "configurations");
1403 return ipmi::responseResponseError();
1404 }
1405 ipmi::setDbusProperty(dbus, "xyz.openbmc_project.EntityManager",
1406 path, pidConfigurationIface, "OutLimitMin",
1407 static_cast<double>(offset));
1408 found = true;
1409 }
1410 }
1411 if (!found)
1412 {
1413 phosphor::logging::log<phosphor::logging::level::ERR>(
1414 "ipmiOEMSetFanSpeedOffset: set no fan offsets");
1415 return ipmi::responseResponseError();
1416 }
1417
1418 return ipmi::responseSuccess();
1419}
1420
1421ipmi::RspType<> ipmiOEMSetFscParameter(uint8_t command, uint8_t param1,
1422 uint8_t param2)
James Feist5f957ca2019-03-14 15:33:55 -07001423{
1424 constexpr const size_t disableLimiting = 0x0;
1425
James Feistacc8a4e2019-04-02 14:23:57 -07001426 if (command == static_cast<uint8_t>(setFscParamFlags::tcontrol))
James Feist5f957ca2019-03-14 15:33:55 -07001427 {
James Feistacc8a4e2019-04-02 14:23:57 -07001428 if (param1 == legacyExitAirSensorNumber)
James Feistfaa4f222019-03-21 16:21:55 -07001429 {
James Feistfaa4f222019-03-21 16:21:55 -07001430 std::string path = getExitAirConfigPath();
1431 ipmi::setDbusProperty(dbus, "xyz.openbmc_project.EntityManager",
James Feistacc8a4e2019-04-02 14:23:57 -07001432 path, pidConfigurationIface, "SetPoint",
1433 static_cast<double>(param2));
1434 return ipmi::responseSuccess();
James Feistfaa4f222019-03-21 16:21:55 -07001435 }
1436 else
1437 {
James Feistacc8a4e2019-04-02 14:23:57 -07001438 return ipmi::responseParmOutOfRange();
James Feistfaa4f222019-03-21 16:21:55 -07001439 }
1440 }
James Feistacc8a4e2019-04-02 14:23:57 -07001441 else if (command == static_cast<uint8_t>(setFscParamFlags::cfm))
James Feist5f957ca2019-03-14 15:33:55 -07001442 {
James Feistacc8a4e2019-04-02 14:23:57 -07001443 uint16_t cfm = param1 | (static_cast<uint16_t>(param2) << 8);
James Feist5f957ca2019-03-14 15:33:55 -07001444
1445 // must be greater than 50 based on eps
1446 if (cfm < 50 && cfm != disableLimiting)
1447 {
James Feistacc8a4e2019-04-02 14:23:57 -07001448 return ipmi::responseParmOutOfRange();
James Feist5f957ca2019-03-14 15:33:55 -07001449 }
1450
1451 try
1452 {
1453 ipmi::setDbusProperty(dbus, settingsBusName, cfmLimitSettingPath,
1454 cfmLimitIface, "Limit",
1455 static_cast<double>(cfm));
1456 }
1457 catch (sdbusplus::exception_t& e)
1458 {
1459 phosphor::logging::log<phosphor::logging::level::ERR>(
1460 "ipmiOEMSetFscParameter: can't set cfm setting!",
1461 phosphor::logging::entry("ERR=%s", e.what()));
James Feistacc8a4e2019-04-02 14:23:57 -07001462 return ipmi::responseResponseError();
James Feist5f957ca2019-03-14 15:33:55 -07001463 }
James Feistacc8a4e2019-04-02 14:23:57 -07001464 return ipmi::responseSuccess();
1465 }
1466 else if (command == static_cast<uint8_t>(setFscParamFlags::maxPwm))
1467 {
1468 constexpr const size_t maxDomainCount = 8;
1469 uint8_t requestedDomainMask = param1;
1470 boost::container::flat_map data = getPidConfigs();
1471 if (data.empty())
1472 {
1473
1474 phosphor::logging::log<phosphor::logging::level::ERR>(
1475 "ipmiOEMSetFscParameter: found no pid configurations!");
1476 return ipmi::responseResponseError();
1477 }
1478 size_t count = 0;
1479 for (const auto& [path, pid] : data)
1480 {
1481 auto findClass = pid.find("Class");
1482 if (findClass == pid.end())
1483 {
1484
1485 phosphor::logging::log<phosphor::logging::level::ERR>(
1486 "ipmiOEMSetFscParameter: found illegal pid "
1487 "configurations");
1488 return ipmi::responseResponseError();
1489 }
1490 std::string type = std::get<std::string>(findClass->second);
1491 if (type == "fan")
1492 {
1493 if (requestedDomainMask & (1 << count))
1494 {
1495 ipmi::setDbusProperty(
1496 dbus, "xyz.openbmc_project.EntityManager", path,
1497 pidConfigurationIface, "OutLimitMax",
1498 static_cast<double>(param2));
1499 }
1500 count++;
1501 }
1502 }
1503 return ipmi::responseSuccess();
James Feist5f957ca2019-03-14 15:33:55 -07001504 }
1505 else
1506 {
1507 // todo other command parts possibly
1508 // tcontrol is handled in peci now
1509 // fan speed offset not implemented yet
1510 // domain pwm limit not implemented
James Feistacc8a4e2019-04-02 14:23:57 -07001511 return ipmi::responseParmOutOfRange();
James Feist5f957ca2019-03-14 15:33:55 -07001512 }
1513}
1514
James Feistacc8a4e2019-04-02 14:23:57 -07001515ipmi::RspType<
1516 std::variant<uint8_t, std::array<uint8_t, 2>, std::array<uint16_t, 2>>>
1517 ipmiOEMGetFscParameter(uint8_t command, std::optional<uint8_t> param)
James Feist5f957ca2019-03-14 15:33:55 -07001518{
James Feistfaa4f222019-03-21 16:21:55 -07001519 constexpr uint8_t legacyDefaultExitAirLimit = -128;
James Feist5f957ca2019-03-14 15:33:55 -07001520
James Feistacc8a4e2019-04-02 14:23:57 -07001521 if (command == static_cast<uint8_t>(setFscParamFlags::tcontrol))
James Feist5f957ca2019-03-14 15:33:55 -07001522 {
James Feistacc8a4e2019-04-02 14:23:57 -07001523 if (!param)
James Feistfaa4f222019-03-21 16:21:55 -07001524 {
James Feistacc8a4e2019-04-02 14:23:57 -07001525 return ipmi::responseReqDataLenInvalid();
James Feistfaa4f222019-03-21 16:21:55 -07001526 }
1527
James Feistacc8a4e2019-04-02 14:23:57 -07001528 if (*param != legacyExitAirSensorNumber)
James Feistfaa4f222019-03-21 16:21:55 -07001529 {
James Feistacc8a4e2019-04-02 14:23:57 -07001530 return ipmi::responseParmOutOfRange();
James Feistfaa4f222019-03-21 16:21:55 -07001531 }
1532 uint8_t setpoint = legacyDefaultExitAirLimit;
1533 std::string path = getExitAirConfigPath();
1534 if (path.size())
1535 {
James Feistacc8a4e2019-04-02 14:23:57 -07001536 Value val =
1537 ipmi::getDbusProperty(dbus, "xyz.openbmc_project.EntityManager",
1538 path, pidConfigurationIface, "SetPoint");
James Feistfaa4f222019-03-21 16:21:55 -07001539 setpoint = std::floor(std::get<double>(val) + 0.5);
1540 }
1541
1542 // old implementation used to return the "default" and current, we
1543 // don't make the default readily available so just make both the
1544 // same
James Feistfaa4f222019-03-21 16:21:55 -07001545
James Feistacc8a4e2019-04-02 14:23:57 -07001546 return ipmi::responseSuccess(
1547 std::array<uint8_t, 2>{setpoint, setpoint});
James Feistfaa4f222019-03-21 16:21:55 -07001548 }
James Feistacc8a4e2019-04-02 14:23:57 -07001549 else if (command == static_cast<uint8_t>(setFscParamFlags::maxPwm))
1550 {
1551 constexpr const size_t maxDomainCount = 8;
1552
1553 if (!param)
1554 {
1555 return ipmi::responseReqDataLenInvalid();
1556 }
1557 uint8_t requestedDomain = *param;
1558 if (requestedDomain >= maxDomainCount)
1559 {
1560 return ipmi::responseInvalidFieldRequest();
1561 }
1562
1563 boost::container::flat_map data = getPidConfigs();
1564 if (data.empty())
1565 {
1566 phosphor::logging::log<phosphor::logging::level::ERR>(
1567 "ipmiOEMGetFscParameter: found no pid configurations!");
1568 return ipmi::responseResponseError();
1569 }
1570 size_t count = 0;
1571 for (const auto& [_, pid] : data)
1572 {
1573 auto findClass = pid.find("Class");
1574 if (findClass == pid.end())
1575 {
1576 phosphor::logging::log<phosphor::logging::level::ERR>(
1577 "ipmiOEMGetFscParameter: found illegal pid "
1578 "configurations");
1579 return ipmi::responseResponseError();
1580 }
1581 std::string type = std::get<std::string>(findClass->second);
1582 if (type == "fan")
1583 {
1584 if (requestedDomain == count)
1585 {
1586 auto findOutLimit = pid.find("OutLimitMax");
1587 if (findOutLimit == pid.end())
1588 {
1589 phosphor::logging::log<phosphor::logging::level::ERR>(
1590 "ipmiOEMGetFscParameter: found illegal pid "
1591 "configurations");
1592 return ipmi::responseResponseError();
1593 }
1594
1595 return ipmi::responseSuccess(
1596 static_cast<uint8_t>(std::floor(
1597 std::get<double>(findOutLimit->second) + 0.5)));
1598 }
1599 else
1600 {
1601 count++;
1602 }
1603 }
1604 }
1605
1606 return ipmi::responseInvalidFieldRequest();
1607 }
1608 else if (command == static_cast<uint8_t>(setFscParamFlags::cfm))
James Feist5f957ca2019-03-14 15:33:55 -07001609 {
1610
1611 /*
1612 DataLen should be 1, but host is sending us an extra bit. As the
James Feistacc8a4e2019-04-02 14:23:57 -07001613 previous behavior didn't seem to prevent this, ignore the check for
1614 now.
James Feist5f957ca2019-03-14 15:33:55 -07001615
James Feistacc8a4e2019-04-02 14:23:57 -07001616 if (param)
James Feist5f957ca2019-03-14 15:33:55 -07001617 {
1618 phosphor::logging::log<phosphor::logging::level::ERR>(
1619 "ipmiOEMGetFscParameter: invalid input len!");
James Feist5f957ca2019-03-14 15:33:55 -07001620 return IPMI_CC_REQ_DATA_LEN_INVALID;
1621 }
1622 */
1623 Value cfmLimit;
1624 Value cfmMaximum;
1625 try
1626 {
1627 cfmLimit = ipmi::getDbusProperty(dbus, settingsBusName,
1628 cfmLimitSettingPath, cfmLimitIface,
1629 "Limit");
1630 cfmMaximum = ipmi::getDbusProperty(
1631 dbus, "xyz.openbmc_project.ExitAirTempSensor",
1632 "/xyz/openbmc_project/control/MaxCFM", cfmLimitIface, "Limit");
1633 }
1634 catch (sdbusplus::exception_t& e)
1635 {
1636 phosphor::logging::log<phosphor::logging::level::ERR>(
James Feistacc8a4e2019-04-02 14:23:57 -07001637 "ipmiOEMGetFscParameter: can't get cfm setting!",
James Feist5f957ca2019-03-14 15:33:55 -07001638 phosphor::logging::entry("ERR=%s", e.what()));
James Feistacc8a4e2019-04-02 14:23:57 -07001639 return ipmi::responseResponseError();
James Feist5f957ca2019-03-14 15:33:55 -07001640 }
1641
James Feistacc8a4e2019-04-02 14:23:57 -07001642 double cfmMax = std::get<double>(cfmMaximum);
1643 double cfmLim = std::get<double>(cfmLimit);
James Feist5f957ca2019-03-14 15:33:55 -07001644
James Feistacc8a4e2019-04-02 14:23:57 -07001645 cfmLim = std::floor(cfmLim + 0.5);
1646 cfmMax = std::floor(cfmMax + 0.5);
1647 uint16_t cfmLimResp = static_cast<uint16_t>(cfmLim);
1648 uint16_t cfmMaxResp = static_cast<uint16_t>(cfmMax);
James Feist5f957ca2019-03-14 15:33:55 -07001649
James Feistacc8a4e2019-04-02 14:23:57 -07001650 return ipmi::responseSuccess(
1651 std::array<uint16_t, 2>{cfmLimResp, cfmMaxResp});
James Feist5f957ca2019-03-14 15:33:55 -07001652 }
James Feistacc8a4e2019-04-02 14:23:57 -07001653
James Feist5f957ca2019-03-14 15:33:55 -07001654 else
1655 {
1656 // todo other command parts possibly
James Feist5f957ca2019-03-14 15:33:55 -07001657 // domain pwm limit not implemented
James Feistacc8a4e2019-04-02 14:23:57 -07001658 return ipmi::responseParmOutOfRange();
James Feist5f957ca2019-03-14 15:33:55 -07001659 }
1660}
1661
Zhu, Yungebe560b02019-04-21 21:19:21 -04001662ipmi::RspType<> ipmiOEMSetFaultIndication(uint8_t sourceId, uint8_t faultType,
1663 uint8_t faultState,
1664 uint8_t faultGroup,
1665 std::array<uint8_t, 8>& ledStateData)
1666{
1667 static constexpr const char* objpath = "/xyz/openbmc_project/EntityManager";
1668 static constexpr const char* intf = "xyz.openbmc_project.EntityManager";
1669 constexpr auto maxFaultType = static_cast<size_t>(RemoteFaultType::max);
1670 static const std::array<std::string, maxFaultType> faultNames = {
1671 "faultFan", "faultTemp", "faultPower",
1672 "faultDriveSlot", "faultSoftware", "faultMemory"};
1673 static constexpr const char* sysGpioPath = "/sys/class/gpio/gpio";
1674 static constexpr const char* postfixValue = "/value";
1675
1676 constexpr uint8_t maxFaultSource = 0x4;
1677 constexpr uint8_t skipLEDs = 0xFF;
1678 constexpr uint8_t pinSize = 64;
1679 constexpr uint8_t groupSize = 16;
1680
1681 std::vector<uint16_t> ledFaultPins(pinSize, 0xFFFF);
1682 uint64_t resFIndex = 0;
1683 std::string resFType;
1684 std::string service;
1685 ObjectValueTree valueTree;
1686
1687 // Validate the source, fault type
1688 if ((sourceId >= maxFaultSource) ||
1689 (faultType >= static_cast<int8_t>(RemoteFaultType::max)) ||
1690 (faultState >= static_cast<int8_t>(RemoteFaultState::maxFaultState)) ||
1691 (faultGroup >= static_cast<int8_t>(DimmFaultType::maxFaultGroup)))
1692 {
1693 return ipmi::responseParmOutOfRange();
1694 }
1695
1696 try
1697 {
1698 service = getService(dbus, intf, objpath);
1699 valueTree = getManagedObjects(dbus, service, "/");
1700 }
1701 catch (const std::exception& e)
1702 {
1703 phosphor::logging::log<phosphor::logging::level::ERR>(
1704 "No object implements interface",
1705 phosphor::logging::entry("SERVICE=%s", service.c_str()),
1706 phosphor::logging::entry("INTF=%s", intf));
1707 return ipmi::responseResponseError();
1708 }
1709
1710 if (valueTree.empty())
1711 {
1712 phosphor::logging::log<phosphor::logging::level::ERR>(
1713 "No object implements interface",
1714 phosphor::logging::entry("INTF=%s", intf));
1715 return ipmi::responseResponseError();
1716 }
1717
1718 for (const auto& item : valueTree)
1719 {
1720 // find LedFault configuration
1721 auto interface =
1722 item.second.find("xyz.openbmc_project.Configuration.LedFault");
1723 if (interface == item.second.end())
1724 {
1725 continue;
1726 }
1727
1728 // find matched fault type: faultMemmory / faultFan
1729 // find LedGpioPins/FaultIndex configuration
1730 auto propertyFaultType = interface->second.find("FaultType");
1731 auto propertyFIndex = interface->second.find("FaultIndex");
1732 auto ledIndex = interface->second.find("LedGpioPins");
1733
1734 if (propertyFaultType == interface->second.end() ||
1735 propertyFIndex == interface->second.end() ||
1736 ledIndex == interface->second.end())
1737 {
1738 continue;
1739 }
1740
1741 try
1742 {
1743 Value valIndex = propertyFIndex->second;
1744 resFIndex = std::get<uint64_t>(valIndex);
1745
1746 Value valFType = propertyFaultType->second;
1747 resFType = std::get<std::string>(valFType);
1748 }
1749 catch (const std::bad_variant_access& e)
1750 {
1751 phosphor::logging::log<phosphor::logging::level::ERR>(e.what());
1752 return ipmi::responseResponseError();
1753 }
1754 // find the matched requested fault type: faultMemmory or faultFan
1755 if (resFType != faultNames[faultType])
1756 {
1757 continue;
1758 }
1759
1760 // read LedGpioPins data
1761 std::vector<uint64_t> ledgpios;
1762 std::variant<std::vector<uint64_t>> message;
1763
1764 auto method = dbus.new_method_call(
1765 service.c_str(), (std::string(item.first)).c_str(),
1766 "org.freedesktop.DBus.Properties", "Get");
1767
1768 method.append("xyz.openbmc_project.Configuration.LedFault",
1769 "LedGpioPins");
1770
1771 try
1772 {
1773 auto reply = dbus.call(method);
1774 reply.read(message);
1775 ledgpios = std::get<std::vector<uint64_t>>(message);
1776 }
1777 catch (std::exception& e)
1778 {
1779 phosphor::logging::log<phosphor::logging::level::ERR>(e.what());
1780 return ipmi::responseResponseError();
1781 }
1782
1783 // Check the size to be sure it will never overflow on groupSize
1784 if (ledgpios.size() > groupSize)
1785 {
1786 phosphor::logging::log<phosphor::logging::level::ERR>(
1787 "Fault gpio Pins out of range!");
1788 return ipmi::responseParmOutOfRange();
1789 }
1790 // Store data, according to command data bit index order
1791 for (int i = 0; i < ledgpios.size(); i++)
1792 {
1793 ledFaultPins[i + groupSize * resFIndex] = ledgpios[i];
1794 }
1795 }
1796
1797 switch (RemoteFaultType(faultType))
1798 {
1799 case (RemoteFaultType::fan):
1800 case (RemoteFaultType::memory):
1801 {
1802 if (faultGroup == skipLEDs)
1803 {
1804 return ipmi::responseSuccess();
1805 }
1806
1807 uint64_t ledState = 0;
1808 // calculate led state bit filed count, each byte has 8bits
1809 // the maximum bits will be 8 * 8 bits
1810 constexpr uint8_t size = sizeof(ledStateData) * 8;
1811 for (int i = 0; i < sizeof(ledStateData); i++)
1812 {
1813 ledState = (uint64_t)(ledState << 8);
1814 ledState = (uint64_t)(ledState | (uint64_t)ledStateData[i]);
1815 }
1816
1817 std::bitset<size> ledStateBits(ledState);
1818 std::string gpioValue;
1819 for (int i = 0; i < size; i++)
1820 { // skip invalid value
1821 if (ledFaultPins[i] == 0xFFFF)
1822 {
1823 continue;
1824 }
1825
1826 std::string device = sysGpioPath +
1827 std::to_string(ledFaultPins[i]) +
1828 postfixValue;
1829 std::fstream gpioFile;
1830
1831 gpioFile.open(device, std::ios::out);
1832
1833 if (!gpioFile.good())
1834 {
1835 phosphor::logging::log<phosphor::logging::level::ERR>(
1836 "Not Find Led Gpio Device!",
1837 phosphor::logging::entry("DEVICE=%s", device.c_str()));
1838 return ipmi::responseResponseError();
1839 }
1840 gpioFile << std::to_string(
1841 static_cast<uint8_t>(ledStateBits[i]));
1842 gpioFile.close();
1843 }
1844 break;
1845 }
1846 default:
1847 {
1848 // now only support two fault types
1849 return ipmi::responseParmOutOfRange();
1850 }
1851 }
1852
1853 return ipmi::responseSuccess();
1854}
1855
Richard Marian Thomaiyarea537d52019-04-24 21:33:48 +05301856ipmi::RspType<uint8_t> ipmiOEMReadBoardProductId()
1857{
1858 uint8_t prodId = 0;
1859 try
1860 {
1861 const DbusObjectInfo& object = getDbusObject(
1862 dbus, "xyz.openbmc_project.Inventory.Item.Board",
1863 "/xyz/openbmc_project/inventory/system/board/", "Baseboard");
1864 const Value& propValue = getDbusProperty(
1865 dbus, object.second, object.first,
1866 "xyz.openbmc_project.Inventory.Item.Board", "ProductId");
1867 prodId = static_cast<uint8_t>(std::get<uint64_t>(propValue));
1868 }
1869 catch (std::exception& e)
1870 {
1871 phosphor::logging::log<phosphor::logging::level::ERR>(
1872 "ipmiOEMReadBoardProductId: Product ID read failed!",
1873 phosphor::logging::entry("ERR=%s", e.what()));
1874 }
1875 return ipmi::responseSuccess(prodId);
1876}
1877
Vernon Mauery4ac799d2019-05-20 15:50:37 -07001878ipmi::RspType<uint8_t /* restore status */>
1879 ipmiRestoreConfiguration(const std::array<uint8_t, 3>& clr, uint8_t cmd)
1880{
1881 static constexpr std::array<uint8_t, 3> expClr = {'C', 'L', 'R'};
1882
1883 if (clr != expClr)
1884 {
1885 return ipmi::responseInvalidFieldRequest();
1886 }
1887 constexpr uint8_t cmdStatus = 0;
1888 constexpr uint8_t cmdDefaultRestore = 0xaa;
1889 constexpr uint8_t cmdFullRestore = 0xbb;
1890 constexpr uint8_t cmdFormat = 0xcc;
1891
1892 constexpr const char* restoreOpFname = "/tmp/.rwfs/.restore_op";
1893
1894 switch (cmd)
1895 {
1896 case cmdStatus:
1897 break;
1898 case cmdDefaultRestore:
1899 case cmdFullRestore:
1900 case cmdFormat:
1901 {
1902 // write file to rwfs root
1903 int value = (cmd - 1) & 0x03; // map aa, bb, cc => 1, 2, 3
1904 std::ofstream restoreFile(restoreOpFname);
1905 if (!restoreFile)
1906 {
1907 return ipmi::responseUnspecifiedError();
1908 }
1909 restoreFile << value << "\n";
1910 break;
1911 }
1912 default:
1913 return ipmi::responseInvalidFieldRequest();
1914 }
1915
1916 constexpr uint8_t restorePending = 0;
1917 constexpr uint8_t restoreComplete = 1;
1918
1919 uint8_t restoreStatus = std::filesystem::exists(restoreOpFname)
1920 ? restorePending
1921 : restoreComplete;
1922 return ipmi::responseSuccess(restoreStatus);
1923}
1924
Jason M. Bills64796042018-10-03 16:51:55 -07001925static void registerOEMFunctions(void)
Jia, Chunhuia835eaa2018-09-05 09:00:41 +08001926{
1927 phosphor::logging::log<phosphor::logging::level::INFO>(
1928 "Registering OEM commands");
Jason M. Bills64796042018-10-03 16:51:55 -07001929 ipmiPrintAndRegister(netfnIntcOEMGeneral, IPMI_CMD_WILDCARD, NULL,
1930 ipmiOEMWildcard,
1931 PRIVILEGE_USER); // wildcard default handler
1932 ipmiPrintAndRegister(netfunIntelAppOEM, IPMI_CMD_WILDCARD, NULL,
1933 ipmiOEMWildcard,
1934 PRIVILEGE_USER); // wildcard default handler
1935 ipmiPrintAndRegister(
1936 netfnIntcOEMGeneral,
1937 static_cast<ipmi_cmd_t>(
1938 IPMINetfnIntelOEMGeneralCmd::cmdGetChassisIdentifier),
1939 NULL, ipmiOEMGetChassisIdentifier,
1940 PRIVILEGE_USER); // get chassis identifier
1941 ipmiPrintAndRegister(
1942 netfnIntcOEMGeneral,
1943 static_cast<ipmi_cmd_t>(IPMINetfnIntelOEMGeneralCmd::cmdSetSystemGUID),
1944 NULL, ipmiOEMSetSystemGUID,
1945 PRIVILEGE_ADMIN); // set system guid
1946 ipmiPrintAndRegister(
1947 netfnIntcOEMGeneral,
1948 static_cast<ipmi_cmd_t>(IPMINetfnIntelOEMGeneralCmd::cmdSetBIOSID),
1949 NULL, ipmiOEMSetBIOSID, PRIVILEGE_ADMIN);
1950 ipmiPrintAndRegister(netfnIntcOEMGeneral,
1951 static_cast<ipmi_cmd_t>(
1952 IPMINetfnIntelOEMGeneralCmd::cmdGetOEMDeviceInfo),
1953 NULL, ipmiOEMGetDeviceInfo, PRIVILEGE_USER);
1954 ipmiPrintAndRegister(
1955 netfnIntcOEMGeneral,
1956 static_cast<ipmi_cmd_t>(
1957 IPMINetfnIntelOEMGeneralCmd::cmdGetAICSlotFRUIDSlotPosRecords),
1958 NULL, ipmiOEMGetAICFRU, PRIVILEGE_USER);
Jia, Chunhuicc49b542019-03-20 15:41:07 +08001959
1960 ipmi::registerHandler(
1961 ipmi::prioOpenBmcBase, ipmi::netFnOemOne,
1962 static_cast<ipmi::Cmd>(
1963 IPMINetfnIntelOEMGeneralCmd::cmdSendEmbeddedFWUpdStatus),
1964 ipmi::Privilege::Operator, ipmiOEMSendEmbeddedFwUpdStatus);
1965
Jason M. Bills64796042018-10-03 16:51:55 -07001966 ipmiPrintAndRegister(
1967 netfnIntcOEMGeneral,
1968 static_cast<ipmi_cmd_t>(
1969 IPMINetfnIntelOEMGeneralCmd::cmdSetPowerRestoreDelay),
1970 NULL, ipmiOEMSetPowerRestoreDelay, PRIVILEGE_OPERATOR);
1971 ipmiPrintAndRegister(
1972 netfnIntcOEMGeneral,
1973 static_cast<ipmi_cmd_t>(
1974 IPMINetfnIntelOEMGeneralCmd::cmdGetPowerRestoreDelay),
1975 NULL, ipmiOEMGetPowerRestoreDelay, PRIVILEGE_USER);
Suryakanth Sekard509eb92018-11-15 17:44:11 +05301976
1977 ipmi::registerHandler(
1978 ipmi::prioOpenBmcBase, ipmi::netFnOemOne,
1979 static_cast<ipmi::Cmd>(
1980 IPMINetfnIntelOEMGeneralCmd::cmdSetOEMUser2Activation),
1981 ipmi::Privilege::Callback, ipmiOEMSetUser2Activation);
1982
Richard Marian Thomaiyarfc5e9852019-04-14 15:06:27 +05301983 ipmi::registerHandler(
1984 ipmi::prioOpenBmcBase, ipmi::netFnOemOne,
1985 static_cast<ipmi::Cmd>(
1986 IPMINetfnIntelOEMGeneralCmd::cmdSetSpecialUserPassword),
1987 ipmi::Privilege::Callback, ipmiOEMSetSpecialUserPassword);
1988
Jason M. Bills64796042018-10-03 16:51:55 -07001989 ipmiPrintAndRegister(
1990 netfnIntcOEMGeneral,
1991 static_cast<ipmi_cmd_t>(
1992 IPMINetfnIntelOEMGeneralCmd::cmdGetProcessorErrConfig),
1993 NULL, ipmiOEMGetProcessorErrConfig, PRIVILEGE_USER);
1994 ipmiPrintAndRegister(
1995 netfnIntcOEMGeneral,
1996 static_cast<ipmi_cmd_t>(
1997 IPMINetfnIntelOEMGeneralCmd::cmdSetProcessorErrConfig),
1998 NULL, ipmiOEMSetProcessorErrConfig, PRIVILEGE_ADMIN);
Yong Li703922d2018-11-06 13:25:31 +08001999 ipmiPrintAndRegister(netfnIntcOEMGeneral,
2000 static_cast<ipmi_cmd_t>(
2001 IPMINetfnIntelOEMGeneralCmd::cmdSetShutdownPolicy),
2002 NULL, ipmiOEMSetShutdownPolicy, PRIVILEGE_ADMIN);
2003 ipmiPrintAndRegister(netfnIntcOEMGeneral,
2004 static_cast<ipmi_cmd_t>(
2005 IPMINetfnIntelOEMGeneralCmd::cmdGetShutdownPolicy),
2006 NULL, ipmiOEMGetShutdownPolicy, PRIVILEGE_ADMIN);
James Feist91244a62019-02-19 15:04:54 -08002007
2008 ipmiPrintAndRegister(
2009 netfnIntcOEMGeneral,
2010 static_cast<ipmi_cmd_t>(IPMINetfnIntelOEMGeneralCmd::cmdSetFanConfig),
2011 NULL, ipmiOEMSetFanConfig, PRIVILEGE_USER);
2012
2013 ipmiPrintAndRegister(
2014 netfnIntcOEMGeneral,
2015 static_cast<ipmi_cmd_t>(IPMINetfnIntelOEMGeneralCmd::cmdGetFanConfig),
2016 NULL, ipmiOEMGetFanConfig, PRIVILEGE_USER);
2017
James Feistacc8a4e2019-04-02 14:23:57 -07002018 ipmi::registerHandler(
2019 ipmi::prioOemBase, netfnIntcOEMGeneral,
2020 static_cast<ipmi::Cmd>(
2021 IPMINetfnIntelOEMGeneralCmd::cmdGetFanSpeedOffset),
2022 ipmi::Privilege::User, ipmiOEMGetFanSpeedOffset);
James Feist5f957ca2019-03-14 15:33:55 -07002023
James Feistacc8a4e2019-04-02 14:23:57 -07002024 ipmi::registerHandler(
2025 ipmi::prioOemBase, netfnIntcOEMGeneral,
2026 static_cast<ipmi::Cmd>(
2027 IPMINetfnIntelOEMGeneralCmd::cmdSetFanSpeedOffset),
2028 ipmi::Privilege::User, ipmiOEMSetFanSpeedOffset);
2029
2030 ipmi::registerHandler(
2031 ipmi::prioOemBase, netfnIntcOEMGeneral,
2032 static_cast<ipmi::Cmd>(IPMINetfnIntelOEMGeneralCmd::cmdSetFscParameter),
2033 ipmi::Privilege::User, ipmiOEMSetFscParameter);
2034
2035 ipmi::registerHandler(
2036 ipmi::prioOemBase, netfnIntcOEMGeneral,
2037 static_cast<ipmi::Cmd>(IPMINetfnIntelOEMGeneralCmd::cmdGetFscParameter),
2038 ipmi::Privilege::User, ipmiOEMGetFscParameter);
James Feist5f957ca2019-03-14 15:33:55 -07002039
Richard Marian Thomaiyarea537d52019-04-24 21:33:48 +05302040 ipmi::registerHandler(
2041 ipmi::prioOpenBmcBase, netfnIntcOEMGeneral,
2042 static_cast<ipmi::Cmd>(
2043 IPMINetfnIntelOEMGeneralCmd::cmdReadBaseBoardProductId),
2044 ipmi::Privilege::Admin, ipmiOEMReadBoardProductId);
2045
Kuiying Wang45f04982018-12-26 09:23:08 +08002046 ipmiPrintAndRegister(
2047 netfnIntcOEMGeneral,
2048 static_cast<ipmi_cmd_t>(IPMINetfnIntelOEMGeneralCmd::cmdGetLEDStatus),
2049 NULL, ipmiOEMGetLEDStatus, PRIVILEGE_ADMIN);
Yong Li23737fe2019-02-19 08:49:55 +08002050 ipmiPrintAndRegister(
2051 netfnIntcOEMPlatform,
2052 static_cast<ipmi_cmd_t>(
2053 IPMINetfnIntelOEMPlatformCmd::cmdCfgHostSerialPortSpeed),
2054 NULL, ipmiOEMCfgHostSerialPortSpeed, PRIVILEGE_ADMIN);
Zhu, Yungebe560b02019-04-21 21:19:21 -04002055 ipmi::registerHandler(
2056 ipmi::prioOemBase, netfnIntcOEMGeneral,
2057 static_cast<ipmi::Cmd>(
2058 IPMINetfnIntelOEMGeneralCmd::cmdSetFaultIndication),
2059 ipmi::Privilege::Operator, ipmiOEMSetFaultIndication);
Vernon Mauery4ac799d2019-05-20 15:50:37 -07002060
2061 registerHandler(prioOemBase, netfn::intel::oemGeneral,
2062 netfn::intel::cmdRestoreConfiguration, Privilege::Admin,
2063 ipmiRestoreConfiguration);
Jia, Chunhuia835eaa2018-09-05 09:00:41 +08002064}
2065
2066} // namespace ipmi