blob: 2f46f13e1fd30a55c936c4851fcd98698312c0e4 [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
Jason M. Bills6d9c83f2019-02-08 14:02:19 -080020#include <ipmid/api.h>
Jia, Chunhuicc49b542019-03-20 15:41:07 +080021#include <systemd/sd-journal.h>
Jia, Chunhuia835eaa2018-09-05 09:00:41 +080022
23#include <array>
James Feist91244a62019-02-19 15:04:54 -080024#include <boost/container/flat_map.hpp>
Yong Li23737fe2019-02-19 08:49:55 +080025#include <boost/process/child.hpp>
26#include <boost/process/io.hpp>
Jason M. Bills64796042018-10-03 16:51:55 -070027#include <commandutils.hpp>
Jia, Chunhuia835eaa2018-09-05 09:00:41 +080028#include <iostream>
Jia, Chunhuicc49b542019-03-20 15:41:07 +080029#include <ipmid/api.hpp>
30#include <ipmid/registration.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>
Jia, Chunhuia835eaa2018-09-05 09:00:41 +080035#include <string>
James Feist91244a62019-02-19 15:04:54 -080036#include <variant>
Jia, Chunhuia835eaa2018-09-05 09:00:41 +080037#include <vector>
Jia, Chunhuia835eaa2018-09-05 09:00:41 +080038
39namespace ipmi
40{
Jason M. Bills64796042018-10-03 16:51:55 -070041static void registerOEMFunctions() __attribute__((constructor));
Jason M. Bills6d9c83f2019-02-08 14:02:19 -080042sdbusplus::bus::bus dbus(ipmid_get_sd_bus_connection()); // from ipmid/api.h
Jason M. Bills64796042018-10-03 16:51:55 -070043static constexpr size_t maxFRUStringLength = 0x3F;
Jia, Chunhuia835eaa2018-09-05 09:00:41 +080044
45// return code: 0 successful
46int8_t getChassisSerialNumber(sdbusplus::bus::bus& bus, std::string& serial)
47{
48 std::string objpath = "/xyz/openbmc_project/FruDevice";
49 std::string intf = "xyz.openbmc_project.FruDeviceManager";
50 std::string service = getService(bus, intf, objpath);
51 ObjectValueTree valueTree = getManagedObjects(bus, service, "/");
52 if (valueTree.empty())
53 {
54 phosphor::logging::log<phosphor::logging::level::ERR>(
55 "No object implements interface",
56 phosphor::logging::entry("INTF=%s", intf.c_str()));
57 return -1;
58 }
59
Jason M. Bills64796042018-10-03 16:51:55 -070060 for (const auto& item : valueTree)
Jia, Chunhuia835eaa2018-09-05 09:00:41 +080061 {
62 auto interface = item.second.find("xyz.openbmc_project.FruDevice");
63 if (interface == item.second.end())
64 {
65 continue;
66 }
67
68 auto property = interface->second.find("CHASSIS_SERIAL_NUMBER");
69 if (property == interface->second.end())
70 {
71 continue;
72 }
73
74 try
75 {
76 Value variant = property->second;
Jason M. Bills64796042018-10-03 16:51:55 -070077 std::string& result =
78 sdbusplus::message::variant_ns::get<std::string>(variant);
79 if (result.size() > maxFRUStringLength)
Jia, Chunhuia835eaa2018-09-05 09:00:41 +080080 {
81 phosphor::logging::log<phosphor::logging::level::ERR>(
82 "FRU serial number exceed maximum length");
Jia, Chunhuia835eaa2018-09-05 09:00:41 +080083 return -1;
84 }
Jason M. Bills64796042018-10-03 16:51:55 -070085 serial = result;
Jia, Chunhuia835eaa2018-09-05 09:00:41 +080086 return 0;
87 }
Jason M. Bills64796042018-10-03 16:51:55 -070088 catch (sdbusplus::message::variant_ns::bad_variant_access& e)
Jia, Chunhuia835eaa2018-09-05 09:00:41 +080089 {
Jason M. Bills64796042018-10-03 16:51:55 -070090 phosphor::logging::log<phosphor::logging::level::ERR>(e.what());
Jia, Chunhuia835eaa2018-09-05 09:00:41 +080091 return -1;
92 }
93 }
94 return -1;
95}
Jason M. Bills64796042018-10-03 16:51:55 -070096
Jia, Chunhuia835eaa2018-09-05 09:00:41 +080097ipmi_ret_t ipmiOEMWildcard(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
98 ipmi_request_t request, ipmi_response_t response,
Jason M. Bills64796042018-10-03 16:51:55 -070099 ipmi_data_len_t dataLen, ipmi_context_t context)
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800100{
Jason M. Bills64796042018-10-03 16:51:55 -0700101 printCommand(+netfn, +cmd);
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800102 // Status code.
103 ipmi_ret_t rc = IPMI_CC_INVALID;
Jason M. Bills64796042018-10-03 16:51:55 -0700104 *dataLen = 0;
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800105 return rc;
106}
107
108// Returns the Chassis Identifier (serial #)
109ipmi_ret_t ipmiOEMGetChassisIdentifier(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
110 ipmi_request_t request,
111 ipmi_response_t response,
Jason M. Bills64796042018-10-03 16:51:55 -0700112 ipmi_data_len_t dataLen,
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800113 ipmi_context_t context)
114{
115 std::string serial;
Jason M. Bills64796042018-10-03 16:51:55 -0700116 if (*dataLen != 0) // invalid request if there are extra parameters
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800117 {
Jason M. Bills64796042018-10-03 16:51:55 -0700118 *dataLen = 0;
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800119 return IPMI_CC_REQ_DATA_LEN_INVALID;
120 }
Jason M. Bills64796042018-10-03 16:51:55 -0700121 if (getChassisSerialNumber(dbus, serial) == 0)
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800122 {
Jason M. Bills64796042018-10-03 16:51:55 -0700123 *dataLen = serial.size(); // length will never exceed response length
124 // as it is checked in getChassisSerialNumber
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800125 char* resp = static_cast<char*>(response);
Jason M. Bills64796042018-10-03 16:51:55 -0700126 serial.copy(resp, *dataLen);
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800127 return IPMI_CC_OK;
128 }
Jason M. Bills64796042018-10-03 16:51:55 -0700129 *dataLen = 0;
130 return IPMI_CC_RESPONSE_ERROR;
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800131}
132
133ipmi_ret_t ipmiOEMSetSystemGUID(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
134 ipmi_request_t request,
135 ipmi_response_t response,
Jason M. Bills64796042018-10-03 16:51:55 -0700136 ipmi_data_len_t dataLen, ipmi_context_t context)
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800137{
138 static constexpr size_t safeBufferLength = 50;
139 char buf[safeBufferLength] = {0};
140 GUIDData* Data = reinterpret_cast<GUIDData*>(request);
141
Jason M. Bills64796042018-10-03 16:51:55 -0700142 if (*dataLen != sizeof(GUIDData)) // 16bytes
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800143 {
Jason M. Bills64796042018-10-03 16:51:55 -0700144 *dataLen = 0;
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800145 return IPMI_CC_REQ_DATA_LEN_INVALID;
146 }
147
Jason M. Bills64796042018-10-03 16:51:55 -0700148 *dataLen = 0;
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800149
150 snprintf(
151 buf, safeBufferLength,
152 "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x",
153 Data->timeLow4, Data->timeLow3, Data->timeLow2, Data->timeLow1,
154 Data->timeMid2, Data->timeMid1, Data->timeHigh2, Data->timeHigh1,
155 Data->clock2, Data->clock1, Data->node6, Data->node5, Data->node4,
156 Data->node3, Data->node2, Data->node1);
157 // UUID is in RFC4122 format. Ex: 61a39523-78f2-11e5-9862-e6402cfc3223
158 std::string guid = buf;
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800159
160 std::string objpath = "/xyz/openbmc_project/control/host0/systemGUID";
161 std::string intf = "xyz.openbmc_project.Common.UUID";
Jason M. Bills64796042018-10-03 16:51:55 -0700162 std::string service = getService(dbus, intf, objpath);
163 setDbusProperty(dbus, service, objpath, intf, "UUID", guid);
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800164 return IPMI_CC_OK;
165}
166
167ipmi_ret_t ipmiOEMSetBIOSID(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
168 ipmi_request_t request, ipmi_response_t response,
169 ipmi_data_len_t dataLen, ipmi_context_t context)
170{
171 DeviceInfo* data = reinterpret_cast<DeviceInfo*>(request);
172
Jason M. Bills64796042018-10-03 16:51:55 -0700173 if ((*dataLen < 2) || (*dataLen != (1 + data->biosIDLength)))
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800174 {
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800175 *dataLen = 0;
176 return IPMI_CC_REQ_DATA_LEN_INVALID;
177 }
Jason M. Bills64796042018-10-03 16:51:55 -0700178 std::string idString((char*)data->biosId, data->biosIDLength);
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800179
Jason M. Bills64796042018-10-03 16:51:55 -0700180 std::string service = getService(dbus, biosIntf, biosObjPath);
181 setDbusProperty(dbus, service, biosObjPath, biosIntf, biosProp, idString);
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800182 uint8_t* bytesWritten = static_cast<uint8_t*>(response);
183 *bytesWritten =
Jason M. Bills64796042018-10-03 16:51:55 -0700184 data->biosIDLength; // how many bytes are written into storage
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800185 *dataLen = 1;
186 return IPMI_CC_OK;
187}
188
189ipmi_ret_t ipmiOEMGetDeviceInfo(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
190 ipmi_request_t request,
191 ipmi_response_t response,
192 ipmi_data_len_t dataLen, ipmi_context_t context)
193{
194 GetOemDeviceInfoReq* req = reinterpret_cast<GetOemDeviceInfoReq*>(request);
195 GetOemDeviceInfoRes* res = reinterpret_cast<GetOemDeviceInfoRes*>(response);
196
197 if (*dataLen == 0)
198 {
Jason M. Bills64796042018-10-03 16:51:55 -0700199 *dataLen = 0;
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800200 return IPMI_CC_REQ_DATA_LEN_INVALID;
201 }
202
203 size_t reqDataLen = *dataLen;
204 *dataLen = 0;
Jason M. Bills64796042018-10-03 16:51:55 -0700205 if (req->entityType > static_cast<uint8_t>(OEMDevEntityType::sdrVer))
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800206 {
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800207 return IPMI_CC_INVALID_FIELD_REQUEST;
208 }
209
210 // handle OEM command items
Jason M. Bills64796042018-10-03 16:51:55 -0700211 switch (OEMDevEntityType(req->entityType))
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800212 {
213 case OEMDevEntityType::biosId:
214 {
215 if (sizeof(GetOemDeviceInfoReq) != reqDataLen)
216 {
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800217 return IPMI_CC_REQ_DATA_LEN_INVALID;
218 }
219
Jason M. Bills64796042018-10-03 16:51:55 -0700220 std::string service = getService(dbus, biosIntf, biosObjPath);
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800221 try
222 {
Jason M. Bills64796042018-10-03 16:51:55 -0700223 Value variant = getDbusProperty(dbus, service, biosObjPath,
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800224 biosIntf, biosProp);
Jason M. Bills64796042018-10-03 16:51:55 -0700225 std::string& idString =
226 sdbusplus::message::variant_ns::get<std::string>(variant);
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800227 if (req->offset >= idString.size())
228 {
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800229 return IPMI_CC_PARM_OUT_OF_RANGE;
230 }
Jason M. Bills64796042018-10-03 16:51:55 -0700231 size_t length = 0;
232 if (req->countToRead > (idString.size() - req->offset))
233 {
234 length = idString.size() - req->offset;
235 }
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800236 else
237 {
Jason M. Bills64796042018-10-03 16:51:55 -0700238 length = req->countToRead;
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800239 }
Jason M. Bills64796042018-10-03 16:51:55 -0700240 std::copy(idString.begin() + req->offset, idString.end(),
241 res->data);
242 res->resDatalen = length;
243 *dataLen = res->resDatalen + 1;
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800244 }
Jason M. Bills64796042018-10-03 16:51:55 -0700245 catch (sdbusplus::message::variant_ns::bad_variant_access& e)
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800246 {
Jason M. Bills64796042018-10-03 16:51:55 -0700247 phosphor::logging::log<phosphor::logging::level::ERR>(e.what());
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800248 return IPMI_CC_UNSPECIFIED_ERROR;
249 }
250 }
251 break;
252
253 case OEMDevEntityType::devVer:
254 case OEMDevEntityType::sdrVer:
255 // TODO:
256 return IPMI_CC_ILLEGAL_COMMAND;
257 default:
258 return IPMI_CC_INVALID_FIELD_REQUEST;
259 }
260 return IPMI_CC_OK;
261}
262
263ipmi_ret_t ipmiOEMGetAICFRU(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
264 ipmi_request_t request, ipmi_response_t response,
265 ipmi_data_len_t dataLen, ipmi_context_t context)
266{
267 if (*dataLen != 0)
268 {
Jason M. Bills64796042018-10-03 16:51:55 -0700269 *dataLen = 0;
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800270 return IPMI_CC_REQ_DATA_LEN_INVALID;
271 }
272
273 *dataLen = 1;
274 uint8_t* res = reinterpret_cast<uint8_t*>(response);
275 // temporary fix. We don't support AIC FRU now. Just tell BIOS that no
276 // AIC is available so that BIOS will not timeout repeatly which leads to
277 // slow booting.
278 *res = 0; // Byte1=Count of SlotPosition/FruID records.
279 return IPMI_CC_OK;
280}
281
Jason M. Bills64796042018-10-03 16:51:55 -0700282ipmi_ret_t ipmiOEMGetPowerRestoreDelay(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
283 ipmi_request_t request,
284 ipmi_response_t response,
285 ipmi_data_len_t dataLen,
286 ipmi_context_t context)
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800287{
Jason M. Bills64796042018-10-03 16:51:55 -0700288 GetPowerRestoreDelayRes* resp =
289 reinterpret_cast<GetPowerRestoreDelayRes*>(response);
290
291 if (*dataLen != 0)
292 {
293 *dataLen = 0;
294 return IPMI_CC_REQ_DATA_LEN_INVALID;
295 }
296
297 std::string service =
298 getService(dbus, powerRestoreDelayIntf, powerRestoreDelayObjPath);
299 Value variant =
300 getDbusProperty(dbus, service, powerRestoreDelayObjPath,
301 powerRestoreDelayIntf, powerRestoreDelayProp);
302
303 uint16_t delay = sdbusplus::message::variant_ns::get<uint16_t>(variant);
304 resp->byteLSB = delay;
305 resp->byteMSB = delay >> 8;
306
307 *dataLen = sizeof(GetPowerRestoreDelayRes);
308
309 return IPMI_CC_OK;
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800310}
311
Jia, Chunhuicc49b542019-03-20 15:41:07 +0800312static uint8_t bcdToDec(uint8_t val)
313{
314 return ((val / 16 * 10) + (val % 16));
315}
316
317// Allows an update utility or system BIOS to send the status of an embedded
318// firmware update attempt to the BMC. After received, BMC will create a logging
319// record.
320ipmi::RspType<> ipmiOEMSendEmbeddedFwUpdStatus(uint8_t status, uint8_t target,
321 uint8_t majorRevision,
322 uint8_t minorRevision,
323 uint32_t auxInfo)
324{
325 std::string firmware;
326 target = (target & selEvtTargetMask) >> selEvtTargetShift;
327
328 /* make sure the status is 0, 1, or 2 as per the spec */
329 if (status > 2)
330 {
331 return ipmi::response(ipmi::ccInvalidFieldRequest);
332 }
333 /*orignal OEM command is to record OEM SEL.
334 But openbmc does not support OEM SEL, so we redirect it to redfish event
335 logging. */
336 std::string buildInfo;
337 std::string action;
338 switch (FWUpdateTarget(target))
339 {
340 case FWUpdateTarget::targetBMC:
341 firmware = "BMC";
342 buildInfo = " major: " + std::to_string(majorRevision) +
343 " minor: " +
344 std::to_string(bcdToDec(minorRevision)) + // BCD encoded
345 " BuildID: " + std::to_string(auxInfo);
346 buildInfo += std::to_string(auxInfo);
347 break;
348 case FWUpdateTarget::targetBIOS:
349 firmware = "BIOS";
350 buildInfo =
351 " major: " +
352 std::to_string(bcdToDec(majorRevision)) + // BCD encoded
353 " minor: " +
354 std::to_string(bcdToDec(minorRevision)) + // BCD encoded
355 " ReleaseNumber: " + // ASCII encoded
356 std::to_string(static_cast<uint8_t>(auxInfo >> 0) - '0') +
357 std::to_string(static_cast<uint8_t>(auxInfo >> 8) - '0') +
358 std::to_string(static_cast<uint8_t>(auxInfo >> 16) - '0') +
359 std::to_string(static_cast<uint8_t>(auxInfo >> 24) - '0');
360 break;
361 case FWUpdateTarget::targetME:
362 firmware = "ME";
363 buildInfo =
364 " major: " + std::to_string(majorRevision) + " minor1: " +
365 std::to_string(bcdToDec(minorRevision)) + // BCD encoded
366 " minor2: " +
367 std::to_string(bcdToDec(static_cast<uint8_t>(auxInfo >> 0))) +
368 " build1: " +
369 std::to_string(bcdToDec(static_cast<uint8_t>(auxInfo >> 8))) +
370 " build2: " +
371 std::to_string(bcdToDec(static_cast<uint8_t>(auxInfo >> 16)));
372 break;
373 case FWUpdateTarget::targetOEMEWS:
374 firmware = "EWS";
375 buildInfo = " major: " + std::to_string(majorRevision) +
376 " minor: " +
377 std::to_string(bcdToDec(minorRevision)) + // BCD encoded
378 " BuildID: " + std::to_string(auxInfo);
379 break;
380 }
381
382 switch (status)
383 {
384 case 0x0:
385 action = "update started";
386 break;
387 case 0x1:
388 action = "update completed successfully";
389 break;
390 case 0x2:
391 action = "update failure";
392 break;
393 default:
394 action = "unknown";
395 break;
396 }
397
398 std::string message(
399 "[firmware update] " + firmware + " instance: " +
400 std::to_string((target & targetInstanceMask) >> targetInstanceShift) +
401 " status: <" + action + ">" + buildInfo);
402 static constexpr const char* redfishMsgId = "FirmwareUpdate";
403
404 sd_journal_send("MESSAGE=%s", message.c_str(), "PRIORITY=%i", LOG_INFO,
405 "REDFISH_MESSAGE_ID=%s", redfishMsgId, NULL);
406 return ipmi::responseSuccess();
407}
408
Jason M. Bills64796042018-10-03 16:51:55 -0700409ipmi_ret_t ipmiOEMSetPowerRestoreDelay(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
410 ipmi_request_t request,
411 ipmi_response_t response,
412 ipmi_data_len_t dataLen,
413 ipmi_context_t context)
414{
415 SetPowerRestoreDelayReq* data =
416 reinterpret_cast<SetPowerRestoreDelayReq*>(request);
417 uint16_t delay = 0;
418
419 if (*dataLen != sizeof(SetPowerRestoreDelayReq))
420 {
421 *dataLen = 0;
422 return IPMI_CC_REQ_DATA_LEN_INVALID;
423 }
424 delay = data->byteMSB;
425 delay = (delay << 8) | data->byteLSB;
426 std::string service =
427 getService(dbus, powerRestoreDelayIntf, powerRestoreDelayObjPath);
428 setDbusProperty(dbus, service, powerRestoreDelayObjPath,
429 powerRestoreDelayIntf, powerRestoreDelayProp, delay);
430 *dataLen = 0;
431
432 return IPMI_CC_OK;
433}
434
435ipmi_ret_t ipmiOEMGetProcessorErrConfig(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
436 ipmi_request_t request,
437 ipmi_response_t response,
438 ipmi_data_len_t dataLen,
439 ipmi_context_t context)
440{
441 GetProcessorErrConfigRes* resp =
442 reinterpret_cast<GetProcessorErrConfigRes*>(response);
443
444 if (*dataLen != 0)
445 {
446 *dataLen = 0;
447 return IPMI_CC_REQ_DATA_LEN_INVALID;
448 }
449
450 std::string service =
451 getService(dbus, processorErrConfigIntf, processorErrConfigObjPath);
452 Value variant = getDbusProperty(dbus, service, processorErrConfigObjPath,
453 processorErrConfigIntf, "ResetCfg");
454 resp->resetCfg = sdbusplus::message::variant_ns::get<uint8_t>(variant);
455
456 std::vector<uint8_t> caterrStatus;
Kuiying Wangbc546672018-11-23 15:41:05 +0800457 sdbusplus::message::variant<std::vector<uint8_t>> message;
Jason M. Bills64796042018-10-03 16:51:55 -0700458
459 auto method =
460 dbus.new_method_call(service.c_str(), processorErrConfigObjPath,
461 "org.freedesktop.DBus.Properties", "Get");
462
463 method.append(processorErrConfigIntf, "CATERRStatus");
Kuiying Wangbc546672018-11-23 15:41:05 +0800464 auto reply = dbus.call(method);
Jason M. Bills64796042018-10-03 16:51:55 -0700465
466 try
467 {
Kuiying Wangbc546672018-11-23 15:41:05 +0800468 reply.read(message);
469 caterrStatus =
470 sdbusplus::message::variant_ns::get<std::vector<uint8_t>>(message);
Jason M. Bills64796042018-10-03 16:51:55 -0700471 }
472 catch (sdbusplus::exception_t&)
473 {
Kuiying Wangbc546672018-11-23 15:41:05 +0800474 phosphor::logging::log<phosphor::logging::level::ERR>(
Jason M. Bills64796042018-10-03 16:51:55 -0700475 "ipmiOEMGetProcessorErrConfig: error on dbus",
476 phosphor::logging::entry("PRORPERTY=CATERRStatus"),
477 phosphor::logging::entry("PATH=%s", processorErrConfigObjPath),
478 phosphor::logging::entry("INTERFACE=%s", processorErrConfigIntf));
479 return IPMI_CC_UNSPECIFIED_ERROR;
480 }
481
482 size_t len =
483 maxCPUNum <= caterrStatus.size() ? maxCPUNum : caterrStatus.size();
484 caterrStatus.resize(len);
485 std::copy(caterrStatus.begin(), caterrStatus.end(), resp->caterrStatus);
486 *dataLen = sizeof(GetProcessorErrConfigRes);
487
488 return IPMI_CC_OK;
489}
490
491ipmi_ret_t ipmiOEMSetProcessorErrConfig(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
492 ipmi_request_t request,
493 ipmi_response_t response,
494 ipmi_data_len_t dataLen,
495 ipmi_context_t context)
496{
497 SetProcessorErrConfigReq* req =
498 reinterpret_cast<SetProcessorErrConfigReq*>(request);
499
500 if (*dataLen != sizeof(SetProcessorErrConfigReq))
501 {
502 *dataLen = 0;
503 return IPMI_CC_REQ_DATA_LEN_INVALID;
504 }
505 std::string service =
506 getService(dbus, processorErrConfigIntf, processorErrConfigObjPath);
507 setDbusProperty(dbus, service, processorErrConfigObjPath,
508 processorErrConfigIntf, "ResetCfg", req->resetCfg);
509
510 setDbusProperty(dbus, service, processorErrConfigObjPath,
511 processorErrConfigIntf, "ResetErrorOccurrenceCounts",
512 req->resetErrorOccurrenceCounts);
513 *dataLen = 0;
514
515 return IPMI_CC_OK;
516}
517
Yong Li703922d2018-11-06 13:25:31 +0800518ipmi_ret_t ipmiOEMGetShutdownPolicy(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
519 ipmi_request_t request,
520 ipmi_response_t response,
521 ipmi_data_len_t dataLen,
522 ipmi_context_t context)
523{
524 GetOEMShutdownPolicyRes* resp =
525 reinterpret_cast<GetOEMShutdownPolicyRes*>(response);
526
527 if (*dataLen != 0)
528 {
529 phosphor::logging::log<phosphor::logging::level::ERR>(
Kuiying Wang45f04982018-12-26 09:23:08 +0800530 "oem_get_shutdown_policy: invalid input len!");
Yong Li703922d2018-11-06 13:25:31 +0800531 *dataLen = 0;
532 return IPMI_CC_REQ_DATA_LEN_INVALID;
533 }
534
535 *dataLen = 0;
536
537 try
538 {
539 std::string service =
540 getService(dbus, oemShutdownPolicyIntf, oemShutdownPolicyObjPath);
541 Value variant = getDbusProperty(dbus, service, oemShutdownPolicyObjPath,
542 oemShutdownPolicyIntf,
543 oemShutdownPolicyObjPathProp);
544 resp->policy = sdbusplus::message::variant_ns::get<uint8_t>(variant);
545 // TODO needs to check if it is multi-node products,
546 // policy is only supported on node 3/4
547 resp->policySupport = shutdownPolicySupported;
548 }
549 catch (sdbusplus::exception_t& e)
550 {
551 phosphor::logging::log<phosphor::logging::level::ERR>(e.description());
552 return IPMI_CC_UNSPECIFIED_ERROR;
553 }
554
555 *dataLen = sizeof(GetOEMShutdownPolicyRes);
556 return IPMI_CC_OK;
557}
558
559ipmi_ret_t ipmiOEMSetShutdownPolicy(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
560 ipmi_request_t request,
561 ipmi_response_t response,
562 ipmi_data_len_t dataLen,
563 ipmi_context_t context)
564{
565 uint8_t* req = reinterpret_cast<uint8_t*>(request);
566
567 // TODO needs to check if it is multi-node products,
568 // policy is only supported on node 3/4
569 if (*dataLen != 1)
570 {
571 phosphor::logging::log<phosphor::logging::level::ERR>(
572 "oem_set_shutdown_policy: invalid input len!");
573 *dataLen = 0;
574 return IPMI_CC_REQ_DATA_LEN_INVALID;
575 }
576
577 *dataLen = 0;
578 if ((*req != noShutdownOnOCOT) && (*req != shutdownOnOCOT))
579 {
580 phosphor::logging::log<phosphor::logging::level::ERR>(
581 "oem_set_shutdown_policy: invalid input!");
582 return IPMI_CC_INVALID_FIELD_REQUEST;
583 }
584
585 try
586 {
587 std::string service =
588 getService(dbus, oemShutdownPolicyIntf, oemShutdownPolicyObjPath);
589 setDbusProperty(dbus, service, oemShutdownPolicyObjPath,
590 oemShutdownPolicyIntf, oemShutdownPolicyObjPathProp,
591 *req);
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 return IPMI_CC_OK;
600}
601
Kuiying Wang45f04982018-12-26 09:23:08 +0800602namespace ledAction
603{
604using namespace sdbusplus::xyz::openbmc_project::Led::server;
605std::map<Physical::Action, uint8_t> actionDbusToIpmi = {
606 {Physical::Action::Off, 0x00},
607 {Physical::Action::On, 0x10},
608 {Physical::Action::Blink, 0x01}};
609
610std::map<uint8_t, std::string> offsetObjPath = {
611 {2, statusAmberObjPath}, {4, statusGreenObjPath}, {6, identifyLEDObjPath}};
612
613} // namespace ledAction
614
615int8_t getLEDState(sdbusplus::bus::bus& bus, const std::string& intf,
616 const std::string& objPath, uint8_t& state)
617{
618 try
619 {
620 std::string service = getService(bus, intf, objPath);
621 Value stateValue =
622 getDbusProperty(bus, service, objPath, intf, "State");
623 std::string strState =
624 sdbusplus::message::variant_ns::get<std::string>(stateValue);
625 state = ledAction::actionDbusToIpmi.at(
626 sdbusplus::xyz::openbmc_project::Led::server::Physical::
627 convertActionFromString(strState));
628 }
629 catch (sdbusplus::exception::SdBusError& e)
630 {
631 phosphor::logging::log<phosphor::logging::level::ERR>(e.what());
632 return -1;
633 }
634 return 0;
635}
636
637ipmi_ret_t ipmiOEMGetLEDStatus(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
638 ipmi_request_t request, ipmi_response_t response,
639 ipmi_data_len_t dataLen, ipmi_context_t context)
640{
641 uint8_t* resp = reinterpret_cast<uint8_t*>(response);
642 // LED Status
643 //[1:0] = Reserved
644 //[3:2] = Status(Amber)
645 //[5:4] = Status(Green)
646 //[7:6] = System Identify
647 // Status definitions:
648 // 00b = Off
649 // 01b = Blink
650 // 10b = On
651 // 11b = invalid
652 if (*dataLen != 0)
653 {
654 phosphor::logging::log<phosphor::logging::level::ERR>(
655 "oem_get_led_status: invalid input len!");
656 *dataLen = 0;
657 return IPMI_CC_REQ_DATA_LEN_INVALID;
658 }
659
660 phosphor::logging::log<phosphor::logging::level::DEBUG>("GET led status");
661 *resp = 0;
662 *dataLen = 0;
663 for (auto it = ledAction::offsetObjPath.begin();
664 it != ledAction::offsetObjPath.end(); ++it)
665 {
666 uint8_t state = 0;
667 if (-1 == getLEDState(dbus, ledIntf, it->second, state))
668 {
669 phosphor::logging::log<phosphor::logging::level::ERR>(
670 "oem_get_led_status: fail to get ID LED status!");
671 return IPMI_CC_UNSPECIFIED_ERROR;
672 }
673 *resp |= state << it->first;
674 }
675
676 *dataLen = sizeof(*resp);
677 return IPMI_CC_OK;
678}
679
Yong Li23737fe2019-02-19 08:49:55 +0800680ipmi_ret_t ipmiOEMCfgHostSerialPortSpeed(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
681 ipmi_request_t request,
682 ipmi_response_t response,
683 ipmi_data_len_t dataLen,
684 ipmi_context_t context)
685{
686 CfgHostSerialReq* req = reinterpret_cast<CfgHostSerialReq*>(request);
687 uint8_t* resp = reinterpret_cast<uint8_t*>(response);
688
689 if (*dataLen == 0)
690 {
691 phosphor::logging::log<phosphor::logging::level::ERR>(
692 "CfgHostSerial: invalid input len!",
693 phosphor::logging::entry("LEN=%d", *dataLen));
694 return IPMI_CC_REQ_DATA_LEN_INVALID;
695 }
696
697 switch (req->command)
698 {
699 case getHostSerialCfgCmd:
700 {
701 if (*dataLen != 1)
702 {
703 phosphor::logging::log<phosphor::logging::level::ERR>(
704 "CfgHostSerial: invalid input len!");
705 *dataLen = 0;
706 return IPMI_CC_REQ_DATA_LEN_INVALID;
707 }
708
709 *dataLen = 0;
710
711 boost::process::ipstream is;
712 std::vector<std::string> data;
713 std::string line;
714 boost::process::child c1(fwGetEnvCmd, "-n", fwHostSerailCfgEnvName,
715 boost::process::std_out > is);
716
717 while (c1.running() && std::getline(is, line) && !line.empty())
718 {
719 data.push_back(line);
720 }
721
722 c1.wait();
723 if (c1.exit_code())
724 {
725 phosphor::logging::log<phosphor::logging::level::ERR>(
726 "CfgHostSerial:: error on execute",
727 phosphor::logging::entry("EXECUTE=%s", fwSetEnvCmd));
728 // Using the default value
729 *resp = 0;
730 }
731 else
732 {
733 if (data.size() != 1)
734 {
735 phosphor::logging::log<phosphor::logging::level::ERR>(
736 "CfgHostSerial:: error on read env");
737 return IPMI_CC_UNSPECIFIED_ERROR;
738 }
739 try
740 {
741 unsigned long tmp = std::stoul(data[0]);
742 if (tmp > std::numeric_limits<uint8_t>::max())
743 {
744 throw std::out_of_range("Out of range");
745 }
746 *resp = static_cast<uint8_t>(tmp);
747 }
748 catch (const std::invalid_argument& e)
749 {
750 phosphor::logging::log<phosphor::logging::level::ERR>(
751 "invalid config ",
752 phosphor::logging::entry("ERR=%s", e.what()));
753 return IPMI_CC_UNSPECIFIED_ERROR;
754 }
755 catch (const std::out_of_range& e)
756 {
757 phosphor::logging::log<phosphor::logging::level::ERR>(
758 "out_of_range config ",
759 phosphor::logging::entry("ERR=%s", e.what()));
760 return IPMI_CC_UNSPECIFIED_ERROR;
761 }
762 }
763
764 *dataLen = 1;
765 break;
766 }
767 case setHostSerialCfgCmd:
768 {
769 if (*dataLen != sizeof(CfgHostSerialReq))
770 {
771 phosphor::logging::log<phosphor::logging::level::ERR>(
772 "CfgHostSerial: invalid input len!");
773 *dataLen = 0;
774 return IPMI_CC_REQ_DATA_LEN_INVALID;
775 }
776
777 *dataLen = 0;
778
779 if (req->parameter > HostSerialCfgParamMax)
780 {
781 phosphor::logging::log<phosphor::logging::level::ERR>(
782 "CfgHostSerial: invalid input!");
783 return IPMI_CC_INVALID_FIELD_REQUEST;
784 }
785
786 boost::process::child c1(fwSetEnvCmd, fwHostSerailCfgEnvName,
787 std::to_string(req->parameter));
788
789 c1.wait();
790 if (c1.exit_code())
791 {
792 phosphor::logging::log<phosphor::logging::level::ERR>(
793 "CfgHostSerial:: error on execute",
794 phosphor::logging::entry("EXECUTE=%s", fwGetEnvCmd));
795 return IPMI_CC_UNSPECIFIED_ERROR;
796 }
797 break;
798 }
799 default:
800 phosphor::logging::log<phosphor::logging::level::ERR>(
801 "CfgHostSerial: invalid input!");
802 *dataLen = 0;
803 return IPMI_CC_INVALID_FIELD_REQUEST;
804 }
805
806 return IPMI_CC_OK;
807}
808
James Feist91244a62019-02-19 15:04:54 -0800809constexpr const char* thermalModeInterface =
810 "xyz.openbmc_project.Control.ThermalMode";
811constexpr const char* thermalModePath =
812 "/xyz/openbmc_project/control/thermal_mode";
813
814bool getFanProfileInterface(
815 sdbusplus::bus::bus& bus,
816 boost::container::flat_map<
817 std::string, std::variant<std::vector<std::string>, std::string>>& resp)
818{
819 auto call = bus.new_method_call(settingsBusName, thermalModePath, PROP_INTF,
820 "GetAll");
821 call.append(thermalModeInterface);
822 try
823 {
824 auto data = bus.call(call);
825 data.read(resp);
826 }
827 catch (sdbusplus::exception_t& e)
828 {
829 phosphor::logging::log<phosphor::logging::level::ERR>(
830 "getFanProfileInterface: can't get thermal mode!",
831 phosphor::logging::entry("ERR=%s", e.what()));
832 return false;
833 }
834 return true;
835}
836
837ipmi_ret_t ipmiOEMSetFanConfig(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
838 ipmi_request_t request, ipmi_response_t response,
839 ipmi_data_len_t dataLen, ipmi_context_t context)
840{
841
842 if (*dataLen < 2 || *dataLen > 7)
843 {
844 phosphor::logging::log<phosphor::logging::level::ERR>(
845 "ipmiOEMSetFanConfig: invalid input len!");
846 *dataLen = 0;
847 return IPMI_CC_REQ_DATA_LEN_INVALID;
848 }
849
850 // todo: tell bios to only send first 2 bytes
851
852 SetFanConfigReq* req = reinterpret_cast<SetFanConfigReq*>(request);
853 boost::container::flat_map<
854 std::string, std::variant<std::vector<std::string>, std::string>>
855 profileData;
856 if (!getFanProfileInterface(dbus, profileData))
857 {
858 return IPMI_CC_UNSPECIFIED_ERROR;
859 }
860
861 std::vector<std::string>* supported =
862 std::get_if<std::vector<std::string>>(&profileData["Supported"]);
863 if (supported == nullptr)
864 {
865 return IPMI_CC_INVALID_FIELD_REQUEST;
866 }
867 std::string mode;
868 if (req->flags &
869 (1 << static_cast<uint8_t>(setFanProfileFlags::setPerfAcousMode)))
870 {
871 bool performanceMode =
872 (req->flags & (1 << static_cast<uint8_t>(
873 setFanProfileFlags::performAcousSelect))) > 0;
874
875 if (performanceMode)
876 {
877
878 if (std::find(supported->begin(), supported->end(),
879 "Performance") != supported->end())
880 {
881 mode = "Performance";
882 }
883 }
884 else
885 {
886
887 if (std::find(supported->begin(), supported->end(), "Acoustic") !=
888 supported->end())
889 {
890 mode = "Acoustic";
891 }
892 }
893 if (mode.empty())
894 {
895 return IPMI_CC_INVALID_FIELD_REQUEST;
896 }
897 setDbusProperty(dbus, settingsBusName, thermalModePath,
898 thermalModeInterface, "Current", mode);
899 }
900
901 return IPMI_CC_OK;
902}
903
904ipmi_ret_t ipmiOEMGetFanConfig(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
905 ipmi_request_t request, ipmi_response_t response,
906 ipmi_data_len_t dataLen, ipmi_context_t context)
907{
908
909 if (*dataLen > 1)
910 {
911 phosphor::logging::log<phosphor::logging::level::ERR>(
912 "ipmiOEMGetFanConfig: invalid input len!");
913 *dataLen = 0;
914 return IPMI_CC_REQ_DATA_LEN_INVALID;
915 }
916
917 // todo: talk to bios about needing less information
918
919 GetFanConfigResp* resp = reinterpret_cast<GetFanConfigResp*>(response);
920 *dataLen = sizeof(GetFanConfigResp);
921
922 boost::container::flat_map<
923 std::string, std::variant<std::vector<std::string>, std::string>>
924 profileData;
925
926 if (!getFanProfileInterface(dbus, profileData))
927 {
928 return IPMI_CC_UNSPECIFIED_ERROR;
929 }
930
931 std::string* current = std::get_if<std::string>(&profileData["Current"]);
932
933 if (current == nullptr)
934 {
935 phosphor::logging::log<phosphor::logging::level::ERR>(
936 "ipmiOEMGetFanConfig: can't get current mode!");
937 return IPMI_CC_UNSPECIFIED_ERROR;
938 }
939 bool performance = (*current == "Performance");
940
941 if (performance)
942 {
943 resp->flags |= 1 << 2;
944 }
945
946 return IPMI_CC_OK;
947}
948
James Feist5f957ca2019-03-14 15:33:55 -0700949constexpr const char* cfmLimitSettingPath =
950 "/xyz/openbmc_project/control/cfm_limit";
951constexpr const char* cfmLimitIface = "xyz.openbmc_project.Control.CFMLimit";
James Feistfaa4f222019-03-21 16:21:55 -0700952constexpr const size_t legacyExitAirSensorNumber = 0x2e;
953
954static std::string getExitAirConfigPath()
955{
956
957 auto method =
958 dbus.new_method_call("xyz.openbmc_project.ObjectMapper",
959 "/xyz/openbmc_project/object_mapper",
960 "xyz.openbmc_project.ObjectMapper", "GetSubTree");
961
962 method.append(
963 "/", 0,
964 std::array<const char*, 1>{"xyz.openbmc_project.Configuration.Pid"});
965 std::string path;
966 GetSubTreeType resp;
967 try
968 {
969 auto reply = dbus.call(method);
970 reply.read(resp);
971 }
972 catch (sdbusplus::exception_t&)
973 {
974 phosphor::logging::log<phosphor::logging::level::ERR>(
975 "ipmiOEMGetFscParameter: mapper error");
976 };
977 auto config = std::find_if(resp.begin(), resp.end(), [](const auto& pair) {
978 return pair.first.find("Exit_Air") != std::string::npos;
979 });
980 if (config != resp.end())
981 {
982 path = std::move(config->first);
983 }
984 return path;
985}
James Feist5f957ca2019-03-14 15:33:55 -0700986
987ipmi_ret_t ipmiOEMSetFscParameter(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
988 ipmi_request_t request,
989 ipmi_response_t response,
990 ipmi_data_len_t dataLen,
991 ipmi_context_t context)
992{
993 constexpr const size_t disableLimiting = 0x0;
994
995 if (*dataLen < 2)
996 {
997 phosphor::logging::log<phosphor::logging::level::ERR>(
998 "ipmiOEMSetFscParameter: invalid input len!");
999 *dataLen = 0;
1000 return IPMI_CC_REQ_DATA_LEN_INVALID;
1001 }
1002
1003 uint8_t* req = static_cast<uint8_t*>(request);
1004
James Feistfaa4f222019-03-21 16:21:55 -07001005 if (*req == static_cast<uint8_t>(setFscParamFlags::tcontrol))
1006 {
1007 if (*dataLen == 3 && req[1] == legacyExitAirSensorNumber)
1008 {
1009 *dataLen = 0;
1010 std::string path = getExitAirConfigPath();
1011 ipmi::setDbusProperty(dbus, "xyz.openbmc_project.EntityManager",
1012 path, "xyz.openbmc_project.Configuration.Pid",
1013 "SetPoint", static_cast<double>(req[2]));
1014 return IPMI_CC_OK;
1015 }
1016 else if (*dataLen == 3)
1017 {
1018 *dataLen = 0;
1019 return IPMI_CC_INVALID_FIELD_REQUEST;
1020 }
1021 else
1022 {
1023 *dataLen = 0;
1024 return IPMI_CC_REQ_DATA_LEN_INVALID;
1025 }
1026 }
1027 else if (*req == static_cast<uint8_t>(setFscParamFlags::cfm))
James Feist5f957ca2019-03-14 15:33:55 -07001028 {
1029 if (*dataLen != 3)
1030 {
1031 phosphor::logging::log<phosphor::logging::level::ERR>(
1032 "ipmiOEMSetFscParameter: invalid input len!");
1033 *dataLen = 0;
1034 return IPMI_CC_REQ_DATA_LEN_INVALID;
1035 }
1036 *dataLen = 0;
1037
1038 uint16_t cfm = req[1] | (static_cast<uint16_t>(req[2]) << 8);
1039
1040 // must be greater than 50 based on eps
1041 if (cfm < 50 && cfm != disableLimiting)
1042 {
1043 return IPMI_CC_PARM_OUT_OF_RANGE;
1044 }
1045
1046 try
1047 {
1048 ipmi::setDbusProperty(dbus, settingsBusName, cfmLimitSettingPath,
1049 cfmLimitIface, "Limit",
1050 static_cast<double>(cfm));
1051 }
1052 catch (sdbusplus::exception_t& e)
1053 {
1054 phosphor::logging::log<phosphor::logging::level::ERR>(
1055 "ipmiOEMSetFscParameter: can't set cfm setting!",
1056 phosphor::logging::entry("ERR=%s", e.what()));
1057 return IPMI_CC_UNSPECIFIED_ERROR;
1058 }
1059 return IPMI_CC_OK;
1060 }
1061 else
1062 {
1063 // todo other command parts possibly
1064 // tcontrol is handled in peci now
1065 // fan speed offset not implemented yet
1066 // domain pwm limit not implemented
1067 *dataLen = 0;
1068 return IPMI_CC_PARM_OUT_OF_RANGE;
1069 }
1070}
1071
1072ipmi_ret_t ipmiOEMGetFscParameter(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
1073 ipmi_request_t request,
1074 ipmi_response_t response,
1075 ipmi_data_len_t dataLen,
1076 ipmi_context_t context)
1077{
James Feistfaa4f222019-03-21 16:21:55 -07001078 constexpr uint8_t legacyDefaultExitAirLimit = -128;
James Feist5f957ca2019-03-14 15:33:55 -07001079
1080 if (*dataLen < 1)
1081 {
1082 phosphor::logging::log<phosphor::logging::level::ERR>(
1083 "ipmiOEMGetFscParameter: invalid input len!");
1084 *dataLen = 0;
1085 return IPMI_CC_REQ_DATA_LEN_INVALID;
1086 }
1087
1088 uint8_t* req = static_cast<uint8_t*>(request);
1089
James Feistfaa4f222019-03-21 16:21:55 -07001090 if (*req == static_cast<uint8_t>(setFscParamFlags::tcontrol))
1091 {
1092 if (*dataLen != 2)
1093 {
1094 phosphor::logging::log<phosphor::logging::level::ERR>(
1095 "ipmiOEMGetFscParameter: invalid input len!");
1096 *dataLen = 0;
1097 return IPMI_CC_REQ_DATA_LEN_INVALID;
1098 }
1099
1100 if (req[1] != legacyExitAirSensorNumber)
1101 {
1102 return IPMI_CC_PARM_OUT_OF_RANGE;
1103 }
1104 uint8_t setpoint = legacyDefaultExitAirLimit;
1105 std::string path = getExitAirConfigPath();
1106 if (path.size())
1107 {
1108 Value val = ipmi::getDbusProperty(
1109 dbus, "xyz.openbmc_project.EntityManager", path,
1110 "xyz.openbmc_project.Configuration.Pid", "SetPoint");
1111 setpoint = std::floor(std::get<double>(val) + 0.5);
1112 }
1113
1114 // old implementation used to return the "default" and current, we
1115 // don't make the default readily available so just make both the
1116 // same
1117 auto resp = static_cast<uint8_t*>(response);
1118 resp[0] = setpoint;
1119 resp[1] = setpoint;
1120
1121 *dataLen = 2;
1122 return IPMI_CC_OK;
1123 }
1124 else if (*req == static_cast<uint8_t>(setFscParamFlags::cfm))
James Feist5f957ca2019-03-14 15:33:55 -07001125 {
1126
1127 /*
1128 DataLen should be 1, but host is sending us an extra bit. As the
1129 previous behavior didn't seem to prevent this, ignore the check for now.
1130
1131 if (*dataLen != 1)
1132 {
1133 phosphor::logging::log<phosphor::logging::level::ERR>(
1134 "ipmiOEMGetFscParameter: invalid input len!");
1135 *dataLen = 0;
1136 return IPMI_CC_REQ_DATA_LEN_INVALID;
1137 }
1138 */
1139 Value cfmLimit;
1140 Value cfmMaximum;
1141 try
1142 {
1143 cfmLimit = ipmi::getDbusProperty(dbus, settingsBusName,
1144 cfmLimitSettingPath, cfmLimitIface,
1145 "Limit");
1146 cfmMaximum = ipmi::getDbusProperty(
1147 dbus, "xyz.openbmc_project.ExitAirTempSensor",
1148 "/xyz/openbmc_project/control/MaxCFM", cfmLimitIface, "Limit");
1149 }
1150 catch (sdbusplus::exception_t& e)
1151 {
1152 phosphor::logging::log<phosphor::logging::level::ERR>(
1153 "ipmiOEMSetFscParameter: can't get cfm setting!",
1154 phosphor::logging::entry("ERR=%s", e.what()));
1155 *dataLen = 0;
1156 return IPMI_CC_UNSPECIFIED_ERROR;
1157 }
1158
1159 auto cfmLim = std::get_if<double>(&cfmLimit);
1160 if (cfmLim == nullptr ||
1161 *cfmLim > std::numeric_limits<uint16_t>::max() || *cfmLim < 0)
1162 {
1163 phosphor::logging::log<phosphor::logging::level::ERR>(
1164 "ipmiOEMSetFscParameter: cfm limit out of range!");
1165 *dataLen = 0;
1166 return IPMI_CC_UNSPECIFIED_ERROR;
1167 }
1168
1169 auto cfmMax = std::get_if<double>(&cfmMaximum);
1170 if (cfmMax == nullptr ||
1171 *cfmMax > std::numeric_limits<uint16_t>::max() || *cfmMax < 0)
1172 {
1173 phosphor::logging::log<phosphor::logging::level::ERR>(
1174 "ipmiOEMSetFscParameter: cfm max out of range!");
1175 *dataLen = 0;
1176 return IPMI_CC_UNSPECIFIED_ERROR;
1177 }
1178 *cfmLim = std::floor(*cfmLim + 0.5);
1179 *cfmMax = std::floor(*cfmMax + 0.5);
1180 uint16_t resp = static_cast<uint16_t>(*cfmLim);
1181 uint16_t* ptr = static_cast<uint16_t*>(response);
1182 ptr[0] = resp;
1183 resp = static_cast<uint16_t>(*cfmMax);
1184 ptr[1] = resp;
1185
1186 *dataLen = 4;
1187
1188 return IPMI_CC_OK;
1189 }
1190 else
1191 {
1192 // todo other command parts possibly
James Feist5f957ca2019-03-14 15:33:55 -07001193 // fan speed offset not implemented yet
1194 // domain pwm limit not implemented
1195 *dataLen = 0;
James Feistfaa4f222019-03-21 16:21:55 -07001196
James Feist5f957ca2019-03-14 15:33:55 -07001197 return IPMI_CC_PARM_OUT_OF_RANGE;
1198 }
1199}
1200
Jason M. Bills64796042018-10-03 16:51:55 -07001201static void registerOEMFunctions(void)
Jia, Chunhuia835eaa2018-09-05 09:00:41 +08001202{
1203 phosphor::logging::log<phosphor::logging::level::INFO>(
1204 "Registering OEM commands");
Jason M. Bills64796042018-10-03 16:51:55 -07001205 ipmiPrintAndRegister(netfnIntcOEMGeneral, IPMI_CMD_WILDCARD, NULL,
1206 ipmiOEMWildcard,
1207 PRIVILEGE_USER); // wildcard default handler
1208 ipmiPrintAndRegister(netfunIntelAppOEM, IPMI_CMD_WILDCARD, NULL,
1209 ipmiOEMWildcard,
1210 PRIVILEGE_USER); // wildcard default handler
1211 ipmiPrintAndRegister(
1212 netfnIntcOEMGeneral,
1213 static_cast<ipmi_cmd_t>(
1214 IPMINetfnIntelOEMGeneralCmd::cmdGetChassisIdentifier),
1215 NULL, ipmiOEMGetChassisIdentifier,
1216 PRIVILEGE_USER); // get chassis identifier
1217 ipmiPrintAndRegister(
1218 netfnIntcOEMGeneral,
1219 static_cast<ipmi_cmd_t>(IPMINetfnIntelOEMGeneralCmd::cmdSetSystemGUID),
1220 NULL, ipmiOEMSetSystemGUID,
1221 PRIVILEGE_ADMIN); // set system guid
1222 ipmiPrintAndRegister(
1223 netfnIntcOEMGeneral,
1224 static_cast<ipmi_cmd_t>(IPMINetfnIntelOEMGeneralCmd::cmdSetBIOSID),
1225 NULL, ipmiOEMSetBIOSID, PRIVILEGE_ADMIN);
1226 ipmiPrintAndRegister(netfnIntcOEMGeneral,
1227 static_cast<ipmi_cmd_t>(
1228 IPMINetfnIntelOEMGeneralCmd::cmdGetOEMDeviceInfo),
1229 NULL, ipmiOEMGetDeviceInfo, PRIVILEGE_USER);
1230 ipmiPrintAndRegister(
1231 netfnIntcOEMGeneral,
1232 static_cast<ipmi_cmd_t>(
1233 IPMINetfnIntelOEMGeneralCmd::cmdGetAICSlotFRUIDSlotPosRecords),
1234 NULL, ipmiOEMGetAICFRU, PRIVILEGE_USER);
Jia, Chunhuicc49b542019-03-20 15:41:07 +08001235
1236 ipmi::registerHandler(
1237 ipmi::prioOpenBmcBase, ipmi::netFnOemOne,
1238 static_cast<ipmi::Cmd>(
1239 IPMINetfnIntelOEMGeneralCmd::cmdSendEmbeddedFWUpdStatus),
1240 ipmi::Privilege::Operator, ipmiOEMSendEmbeddedFwUpdStatus);
1241
Jason M. Bills64796042018-10-03 16:51:55 -07001242 ipmiPrintAndRegister(
1243 netfnIntcOEMGeneral,
1244 static_cast<ipmi_cmd_t>(
1245 IPMINetfnIntelOEMGeneralCmd::cmdSetPowerRestoreDelay),
1246 NULL, ipmiOEMSetPowerRestoreDelay, PRIVILEGE_OPERATOR);
1247 ipmiPrintAndRegister(
1248 netfnIntcOEMGeneral,
1249 static_cast<ipmi_cmd_t>(
1250 IPMINetfnIntelOEMGeneralCmd::cmdGetPowerRestoreDelay),
1251 NULL, ipmiOEMGetPowerRestoreDelay, PRIVILEGE_USER);
1252 ipmiPrintAndRegister(
1253 netfnIntcOEMGeneral,
1254 static_cast<ipmi_cmd_t>(
1255 IPMINetfnIntelOEMGeneralCmd::cmdGetProcessorErrConfig),
1256 NULL, ipmiOEMGetProcessorErrConfig, PRIVILEGE_USER);
1257 ipmiPrintAndRegister(
1258 netfnIntcOEMGeneral,
1259 static_cast<ipmi_cmd_t>(
1260 IPMINetfnIntelOEMGeneralCmd::cmdSetProcessorErrConfig),
1261 NULL, ipmiOEMSetProcessorErrConfig, PRIVILEGE_ADMIN);
Yong Li703922d2018-11-06 13:25:31 +08001262 ipmiPrintAndRegister(netfnIntcOEMGeneral,
1263 static_cast<ipmi_cmd_t>(
1264 IPMINetfnIntelOEMGeneralCmd::cmdSetShutdownPolicy),
1265 NULL, ipmiOEMSetShutdownPolicy, PRIVILEGE_ADMIN);
1266 ipmiPrintAndRegister(netfnIntcOEMGeneral,
1267 static_cast<ipmi_cmd_t>(
1268 IPMINetfnIntelOEMGeneralCmd::cmdGetShutdownPolicy),
1269 NULL, ipmiOEMGetShutdownPolicy, PRIVILEGE_ADMIN);
James Feist91244a62019-02-19 15:04:54 -08001270
1271 ipmiPrintAndRegister(
1272 netfnIntcOEMGeneral,
1273 static_cast<ipmi_cmd_t>(IPMINetfnIntelOEMGeneralCmd::cmdSetFanConfig),
1274 NULL, ipmiOEMSetFanConfig, PRIVILEGE_USER);
1275
1276 ipmiPrintAndRegister(
1277 netfnIntcOEMGeneral,
1278 static_cast<ipmi_cmd_t>(IPMINetfnIntelOEMGeneralCmd::cmdGetFanConfig),
1279 NULL, ipmiOEMGetFanConfig, PRIVILEGE_USER);
1280
James Feist5f957ca2019-03-14 15:33:55 -07001281 ipmiPrintAndRegister(netfnIntcOEMGeneral,
1282 static_cast<ipmi_cmd_t>(
1283 IPMINetfnIntelOEMGeneralCmd::cmdSetFscParameter),
1284 NULL, ipmiOEMSetFscParameter, PRIVILEGE_USER);
1285
1286 ipmiPrintAndRegister(netfnIntcOEMGeneral,
1287 static_cast<ipmi_cmd_t>(
1288 IPMINetfnIntelOEMGeneralCmd::cmdGetFscParameter),
1289 NULL, ipmiOEMGetFscParameter, PRIVILEGE_USER);
1290
Kuiying Wang45f04982018-12-26 09:23:08 +08001291 ipmiPrintAndRegister(
1292 netfnIntcOEMGeneral,
1293 static_cast<ipmi_cmd_t>(IPMINetfnIntelOEMGeneralCmd::cmdGetLEDStatus),
1294 NULL, ipmiOEMGetLEDStatus, PRIVILEGE_ADMIN);
Yong Li23737fe2019-02-19 08:49:55 +08001295 ipmiPrintAndRegister(
1296 netfnIntcOEMPlatform,
1297 static_cast<ipmi_cmd_t>(
1298 IPMINetfnIntelOEMPlatformCmd::cmdCfgHostSerialPortSpeed),
1299 NULL, ipmiOEMCfgHostSerialPortSpeed, PRIVILEGE_ADMIN);
Jia, Chunhuia835eaa2018-09-05 09:00:41 +08001300 return;
1301}
1302
1303} // namespace ipmi