blob: 8bfc1ce27428f1fddf2859119b6a22d6d34fbfde [file] [log] [blame]
Jia, Chunhuia835eaa2018-09-05 09:00:41 +08001/*
2// Copyright (c) 2018 Intel Corporation
3//
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at
7//
8// http://www.apache.org/licenses/LICENSE-2.0
9//
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15*/
16
Jason M. Bills64796042018-10-03 16:51:55 -070017#include "xyz/openbmc_project/Common/error.hpp"
Kuiying Wang45f04982018-12-26 09:23:08 +080018#include "xyz/openbmc_project/Led/Physical/server.hpp"
Jason M. Bills64796042018-10-03 16:51:55 -070019
Jia, Chunhuicc49b542019-03-20 15:41:07 +080020#include <systemd/sd-journal.h>
Jia, Chunhuia835eaa2018-09-05 09:00:41 +080021
22#include <array>
James Feist91244a62019-02-19 15:04:54 -080023#include <boost/container/flat_map.hpp>
Yong Li23737fe2019-02-19 08:49:55 +080024#include <boost/process/child.hpp>
25#include <boost/process/io.hpp>
Jason M. Bills64796042018-10-03 16:51:55 -070026#include <commandutils.hpp>
Jia, Chunhuia835eaa2018-09-05 09:00:41 +080027#include <iostream>
Jia, Chunhuicc49b542019-03-20 15:41:07 +080028#include <ipmid/api.hpp>
Vernon Mauery5480ef62019-03-20 13:43:11 -070029#include <ipmid/utils.hpp>
Jia, Chunhuia835eaa2018-09-05 09:00:41 +080030#include <oemcommands.hpp>
Jia, Chunhuia835eaa2018-09-05 09:00:41 +080031#include <phosphor-logging/log.hpp>
32#include <sdbusplus/bus.hpp>
Suryakanth Sekard509eb92018-11-15 17:44:11 +053033#include <sdbusplus/message/types.hpp>
Jia, Chunhuia835eaa2018-09-05 09:00:41 +080034#include <string>
James Feist91244a62019-02-19 15:04:54 -080035#include <variant>
Jia, Chunhuia835eaa2018-09-05 09:00:41 +080036#include <vector>
Jia, Chunhuia835eaa2018-09-05 09:00:41 +080037
38namespace ipmi
39{
Jason M. Bills64796042018-10-03 16:51:55 -070040static void registerOEMFunctions() __attribute__((constructor));
Jason M. Bills6d9c83f2019-02-08 14:02:19 -080041sdbusplus::bus::bus dbus(ipmid_get_sd_bus_connection()); // from ipmid/api.h
Jason M. Bills64796042018-10-03 16:51:55 -070042static constexpr size_t maxFRUStringLength = 0x3F;
Jia, Chunhuia835eaa2018-09-05 09:00:41 +080043
Suryakanth Sekard509eb92018-11-15 17:44:11 +053044static constexpr auto ethernetIntf =
45 "xyz.openbmc_project.Network.EthernetInterface";
46static constexpr auto networkIPIntf = "xyz.openbmc_project.Network.IP";
47static constexpr auto networkService = "xyz.openbmc_project.Network";
48static constexpr auto networkRoot = "/xyz/openbmc_project/network";
49
Jia, Chunhuia835eaa2018-09-05 09:00:41 +080050// return code: 0 successful
51int8_t getChassisSerialNumber(sdbusplus::bus::bus& bus, std::string& serial)
52{
53 std::string objpath = "/xyz/openbmc_project/FruDevice";
54 std::string intf = "xyz.openbmc_project.FruDeviceManager";
55 std::string service = getService(bus, intf, objpath);
56 ObjectValueTree valueTree = getManagedObjects(bus, service, "/");
57 if (valueTree.empty())
58 {
59 phosphor::logging::log<phosphor::logging::level::ERR>(
60 "No object implements interface",
61 phosphor::logging::entry("INTF=%s", intf.c_str()));
62 return -1;
63 }
64
Jason M. Bills64796042018-10-03 16:51:55 -070065 for (const auto& item : valueTree)
Jia, Chunhuia835eaa2018-09-05 09:00:41 +080066 {
67 auto interface = item.second.find("xyz.openbmc_project.FruDevice");
68 if (interface == item.second.end())
69 {
70 continue;
71 }
72
73 auto property = interface->second.find("CHASSIS_SERIAL_NUMBER");
74 if (property == interface->second.end())
75 {
76 continue;
77 }
78
79 try
80 {
81 Value variant = property->second;
Jason M. Bills64796042018-10-03 16:51:55 -070082 std::string& result =
83 sdbusplus::message::variant_ns::get<std::string>(variant);
84 if (result.size() > maxFRUStringLength)
Jia, Chunhuia835eaa2018-09-05 09:00:41 +080085 {
86 phosphor::logging::log<phosphor::logging::level::ERR>(
87 "FRU serial number exceed maximum length");
Jia, Chunhuia835eaa2018-09-05 09:00:41 +080088 return -1;
89 }
Jason M. Bills64796042018-10-03 16:51:55 -070090 serial = result;
Jia, Chunhuia835eaa2018-09-05 09:00:41 +080091 return 0;
92 }
Jason M. Bills64796042018-10-03 16:51:55 -070093 catch (sdbusplus::message::variant_ns::bad_variant_access& e)
Jia, Chunhuia835eaa2018-09-05 09:00:41 +080094 {
Jason M. Bills64796042018-10-03 16:51:55 -070095 phosphor::logging::log<phosphor::logging::level::ERR>(e.what());
Jia, Chunhuia835eaa2018-09-05 09:00:41 +080096 return -1;
97 }
98 }
99 return -1;
100}
Jason M. Bills64796042018-10-03 16:51:55 -0700101
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800102ipmi_ret_t ipmiOEMWildcard(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
103 ipmi_request_t request, ipmi_response_t response,
Jason M. Bills64796042018-10-03 16:51:55 -0700104 ipmi_data_len_t dataLen, ipmi_context_t context)
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800105{
Jason M. Bills64796042018-10-03 16:51:55 -0700106 printCommand(+netfn, +cmd);
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800107 // Status code.
108 ipmi_ret_t rc = IPMI_CC_INVALID;
Jason M. Bills64796042018-10-03 16:51:55 -0700109 *dataLen = 0;
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800110 return rc;
111}
112
113// Returns the Chassis Identifier (serial #)
114ipmi_ret_t ipmiOEMGetChassisIdentifier(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
115 ipmi_request_t request,
116 ipmi_response_t response,
Jason M. Bills64796042018-10-03 16:51:55 -0700117 ipmi_data_len_t dataLen,
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800118 ipmi_context_t context)
119{
120 std::string serial;
Jason M. Bills64796042018-10-03 16:51:55 -0700121 if (*dataLen != 0) // invalid request if there are extra parameters
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800122 {
Jason M. Bills64796042018-10-03 16:51:55 -0700123 *dataLen = 0;
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800124 return IPMI_CC_REQ_DATA_LEN_INVALID;
125 }
Jason M. Bills64796042018-10-03 16:51:55 -0700126 if (getChassisSerialNumber(dbus, serial) == 0)
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800127 {
Jason M. Bills64796042018-10-03 16:51:55 -0700128 *dataLen = serial.size(); // length will never exceed response length
129 // as it is checked in getChassisSerialNumber
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800130 char* resp = static_cast<char*>(response);
Jason M. Bills64796042018-10-03 16:51:55 -0700131 serial.copy(resp, *dataLen);
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800132 return IPMI_CC_OK;
133 }
Jason M. Bills64796042018-10-03 16:51:55 -0700134 *dataLen = 0;
135 return IPMI_CC_RESPONSE_ERROR;
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800136}
137
138ipmi_ret_t ipmiOEMSetSystemGUID(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
139 ipmi_request_t request,
140 ipmi_response_t response,
Jason M. Bills64796042018-10-03 16:51:55 -0700141 ipmi_data_len_t dataLen, ipmi_context_t context)
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800142{
143 static constexpr size_t safeBufferLength = 50;
144 char buf[safeBufferLength] = {0};
145 GUIDData* Data = reinterpret_cast<GUIDData*>(request);
146
Jason M. Bills64796042018-10-03 16:51:55 -0700147 if (*dataLen != sizeof(GUIDData)) // 16bytes
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800148 {
Jason M. Bills64796042018-10-03 16:51:55 -0700149 *dataLen = 0;
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800150 return IPMI_CC_REQ_DATA_LEN_INVALID;
151 }
152
Jason M. Bills64796042018-10-03 16:51:55 -0700153 *dataLen = 0;
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800154
155 snprintf(
156 buf, safeBufferLength,
157 "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x",
158 Data->timeLow4, Data->timeLow3, Data->timeLow2, Data->timeLow1,
159 Data->timeMid2, Data->timeMid1, Data->timeHigh2, Data->timeHigh1,
160 Data->clock2, Data->clock1, Data->node6, Data->node5, Data->node4,
161 Data->node3, Data->node2, Data->node1);
162 // UUID is in RFC4122 format. Ex: 61a39523-78f2-11e5-9862-e6402cfc3223
163 std::string guid = buf;
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800164
165 std::string objpath = "/xyz/openbmc_project/control/host0/systemGUID";
166 std::string intf = "xyz.openbmc_project.Common.UUID";
Jason M. Bills64796042018-10-03 16:51:55 -0700167 std::string service = getService(dbus, intf, objpath);
168 setDbusProperty(dbus, service, objpath, intf, "UUID", guid);
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800169 return IPMI_CC_OK;
170}
171
172ipmi_ret_t ipmiOEMSetBIOSID(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
173 ipmi_request_t request, ipmi_response_t response,
174 ipmi_data_len_t dataLen, ipmi_context_t context)
175{
176 DeviceInfo* data = reinterpret_cast<DeviceInfo*>(request);
177
Jason M. Bills64796042018-10-03 16:51:55 -0700178 if ((*dataLen < 2) || (*dataLen != (1 + data->biosIDLength)))
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800179 {
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800180 *dataLen = 0;
181 return IPMI_CC_REQ_DATA_LEN_INVALID;
182 }
Jason M. Bills64796042018-10-03 16:51:55 -0700183 std::string idString((char*)data->biosId, data->biosIDLength);
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800184
Jason M. Bills64796042018-10-03 16:51:55 -0700185 std::string service = getService(dbus, biosIntf, biosObjPath);
186 setDbusProperty(dbus, service, biosObjPath, biosIntf, biosProp, idString);
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800187 uint8_t* bytesWritten = static_cast<uint8_t*>(response);
188 *bytesWritten =
Jason M. Bills64796042018-10-03 16:51:55 -0700189 data->biosIDLength; // how many bytes are written into storage
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800190 *dataLen = 1;
191 return IPMI_CC_OK;
192}
193
194ipmi_ret_t ipmiOEMGetDeviceInfo(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
195 ipmi_request_t request,
196 ipmi_response_t response,
197 ipmi_data_len_t dataLen, ipmi_context_t context)
198{
199 GetOemDeviceInfoReq* req = reinterpret_cast<GetOemDeviceInfoReq*>(request);
200 GetOemDeviceInfoRes* res = reinterpret_cast<GetOemDeviceInfoRes*>(response);
201
202 if (*dataLen == 0)
203 {
Jason M. Bills64796042018-10-03 16:51:55 -0700204 *dataLen = 0;
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800205 return IPMI_CC_REQ_DATA_LEN_INVALID;
206 }
207
208 size_t reqDataLen = *dataLen;
209 *dataLen = 0;
Jason M. Bills64796042018-10-03 16:51:55 -0700210 if (req->entityType > static_cast<uint8_t>(OEMDevEntityType::sdrVer))
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800211 {
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800212 return IPMI_CC_INVALID_FIELD_REQUEST;
213 }
214
215 // handle OEM command items
Jason M. Bills64796042018-10-03 16:51:55 -0700216 switch (OEMDevEntityType(req->entityType))
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800217 {
218 case OEMDevEntityType::biosId:
219 {
220 if (sizeof(GetOemDeviceInfoReq) != reqDataLen)
221 {
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800222 return IPMI_CC_REQ_DATA_LEN_INVALID;
223 }
224
Jason M. Bills64796042018-10-03 16:51:55 -0700225 std::string service = getService(dbus, biosIntf, biosObjPath);
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800226 try
227 {
Jason M. Bills64796042018-10-03 16:51:55 -0700228 Value variant = getDbusProperty(dbus, service, biosObjPath,
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800229 biosIntf, biosProp);
Jason M. Bills64796042018-10-03 16:51:55 -0700230 std::string& idString =
231 sdbusplus::message::variant_ns::get<std::string>(variant);
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800232 if (req->offset >= idString.size())
233 {
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800234 return IPMI_CC_PARM_OUT_OF_RANGE;
235 }
Jason M. Bills64796042018-10-03 16:51:55 -0700236 size_t length = 0;
237 if (req->countToRead > (idString.size() - req->offset))
238 {
239 length = idString.size() - req->offset;
240 }
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800241 else
242 {
Jason M. Bills64796042018-10-03 16:51:55 -0700243 length = req->countToRead;
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800244 }
Jason M. Bills64796042018-10-03 16:51:55 -0700245 std::copy(idString.begin() + req->offset, idString.end(),
246 res->data);
247 res->resDatalen = length;
248 *dataLen = res->resDatalen + 1;
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800249 }
Jason M. Bills64796042018-10-03 16:51:55 -0700250 catch (sdbusplus::message::variant_ns::bad_variant_access& e)
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800251 {
Jason M. Bills64796042018-10-03 16:51:55 -0700252 phosphor::logging::log<phosphor::logging::level::ERR>(e.what());
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800253 return IPMI_CC_UNSPECIFIED_ERROR;
254 }
255 }
256 break;
257
258 case OEMDevEntityType::devVer:
259 case OEMDevEntityType::sdrVer:
260 // TODO:
261 return IPMI_CC_ILLEGAL_COMMAND;
262 default:
263 return IPMI_CC_INVALID_FIELD_REQUEST;
264 }
265 return IPMI_CC_OK;
266}
267
268ipmi_ret_t ipmiOEMGetAICFRU(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
269 ipmi_request_t request, ipmi_response_t response,
270 ipmi_data_len_t dataLen, ipmi_context_t context)
271{
272 if (*dataLen != 0)
273 {
Jason M. Bills64796042018-10-03 16:51:55 -0700274 *dataLen = 0;
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800275 return IPMI_CC_REQ_DATA_LEN_INVALID;
276 }
277
278 *dataLen = 1;
279 uint8_t* res = reinterpret_cast<uint8_t*>(response);
280 // temporary fix. We don't support AIC FRU now. Just tell BIOS that no
281 // AIC is available so that BIOS will not timeout repeatly which leads to
282 // slow booting.
283 *res = 0; // Byte1=Count of SlotPosition/FruID records.
284 return IPMI_CC_OK;
285}
286
Jason M. Bills64796042018-10-03 16:51:55 -0700287ipmi_ret_t ipmiOEMGetPowerRestoreDelay(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
288 ipmi_request_t request,
289 ipmi_response_t response,
290 ipmi_data_len_t dataLen,
291 ipmi_context_t context)
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800292{
Jason M. Bills64796042018-10-03 16:51:55 -0700293 GetPowerRestoreDelayRes* resp =
294 reinterpret_cast<GetPowerRestoreDelayRes*>(response);
295
296 if (*dataLen != 0)
297 {
298 *dataLen = 0;
299 return IPMI_CC_REQ_DATA_LEN_INVALID;
300 }
301
302 std::string service =
303 getService(dbus, powerRestoreDelayIntf, powerRestoreDelayObjPath);
304 Value variant =
305 getDbusProperty(dbus, service, powerRestoreDelayObjPath,
306 powerRestoreDelayIntf, powerRestoreDelayProp);
307
308 uint16_t delay = sdbusplus::message::variant_ns::get<uint16_t>(variant);
309 resp->byteLSB = delay;
310 resp->byteMSB = delay >> 8;
311
312 *dataLen = sizeof(GetPowerRestoreDelayRes);
313
314 return IPMI_CC_OK;
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800315}
316
Jia, Chunhuicc49b542019-03-20 15:41:07 +0800317static uint8_t bcdToDec(uint8_t val)
318{
319 return ((val / 16 * 10) + (val % 16));
320}
321
322// Allows an update utility or system BIOS to send the status of an embedded
323// firmware update attempt to the BMC. After received, BMC will create a logging
324// record.
325ipmi::RspType<> ipmiOEMSendEmbeddedFwUpdStatus(uint8_t status, uint8_t target,
326 uint8_t majorRevision,
327 uint8_t minorRevision,
328 uint32_t auxInfo)
329{
330 std::string firmware;
331 target = (target & selEvtTargetMask) >> selEvtTargetShift;
332
333 /* make sure the status is 0, 1, or 2 as per the spec */
334 if (status > 2)
335 {
336 return ipmi::response(ipmi::ccInvalidFieldRequest);
337 }
338 /*orignal OEM command is to record OEM SEL.
339 But openbmc does not support OEM SEL, so we redirect it to redfish event
340 logging. */
341 std::string buildInfo;
342 std::string action;
343 switch (FWUpdateTarget(target))
344 {
345 case FWUpdateTarget::targetBMC:
346 firmware = "BMC";
347 buildInfo = " major: " + std::to_string(majorRevision) +
348 " minor: " +
349 std::to_string(bcdToDec(minorRevision)) + // BCD encoded
350 " BuildID: " + std::to_string(auxInfo);
351 buildInfo += std::to_string(auxInfo);
352 break;
353 case FWUpdateTarget::targetBIOS:
354 firmware = "BIOS";
355 buildInfo =
356 " major: " +
357 std::to_string(bcdToDec(majorRevision)) + // BCD encoded
358 " minor: " +
359 std::to_string(bcdToDec(minorRevision)) + // BCD encoded
360 " ReleaseNumber: " + // ASCII encoded
361 std::to_string(static_cast<uint8_t>(auxInfo >> 0) - '0') +
362 std::to_string(static_cast<uint8_t>(auxInfo >> 8) - '0') +
363 std::to_string(static_cast<uint8_t>(auxInfo >> 16) - '0') +
364 std::to_string(static_cast<uint8_t>(auxInfo >> 24) - '0');
365 break;
366 case FWUpdateTarget::targetME:
367 firmware = "ME";
368 buildInfo =
369 " major: " + std::to_string(majorRevision) + " minor1: " +
370 std::to_string(bcdToDec(minorRevision)) + // BCD encoded
371 " minor2: " +
372 std::to_string(bcdToDec(static_cast<uint8_t>(auxInfo >> 0))) +
373 " build1: " +
374 std::to_string(bcdToDec(static_cast<uint8_t>(auxInfo >> 8))) +
375 " build2: " +
376 std::to_string(bcdToDec(static_cast<uint8_t>(auxInfo >> 16)));
377 break;
378 case FWUpdateTarget::targetOEMEWS:
379 firmware = "EWS";
380 buildInfo = " major: " + std::to_string(majorRevision) +
381 " minor: " +
382 std::to_string(bcdToDec(minorRevision)) + // BCD encoded
383 " BuildID: " + std::to_string(auxInfo);
384 break;
385 }
386
387 switch (status)
388 {
389 case 0x0:
390 action = "update started";
391 break;
392 case 0x1:
393 action = "update completed successfully";
394 break;
395 case 0x2:
396 action = "update failure";
397 break;
398 default:
399 action = "unknown";
400 break;
401 }
402
403 std::string message(
404 "[firmware update] " + firmware + " instance: " +
405 std::to_string((target & targetInstanceMask) >> targetInstanceShift) +
406 " status: <" + action + ">" + buildInfo);
407 static constexpr const char* redfishMsgId = "FirmwareUpdate";
408
409 sd_journal_send("MESSAGE=%s", message.c_str(), "PRIORITY=%i", LOG_INFO,
410 "REDFISH_MESSAGE_ID=%s", redfishMsgId, NULL);
411 return ipmi::responseSuccess();
412}
413
Jason M. Bills64796042018-10-03 16:51:55 -0700414ipmi_ret_t ipmiOEMSetPowerRestoreDelay(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
415 ipmi_request_t request,
416 ipmi_response_t response,
417 ipmi_data_len_t dataLen,
418 ipmi_context_t context)
419{
420 SetPowerRestoreDelayReq* data =
421 reinterpret_cast<SetPowerRestoreDelayReq*>(request);
422 uint16_t delay = 0;
423
424 if (*dataLen != sizeof(SetPowerRestoreDelayReq))
425 {
426 *dataLen = 0;
427 return IPMI_CC_REQ_DATA_LEN_INVALID;
428 }
429 delay = data->byteMSB;
430 delay = (delay << 8) | data->byteLSB;
431 std::string service =
432 getService(dbus, powerRestoreDelayIntf, powerRestoreDelayObjPath);
433 setDbusProperty(dbus, service, powerRestoreDelayObjPath,
434 powerRestoreDelayIntf, powerRestoreDelayProp, delay);
435 *dataLen = 0;
436
437 return IPMI_CC_OK;
438}
439
440ipmi_ret_t ipmiOEMGetProcessorErrConfig(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
441 ipmi_request_t request,
442 ipmi_response_t response,
443 ipmi_data_len_t dataLen,
444 ipmi_context_t context)
445{
446 GetProcessorErrConfigRes* resp =
447 reinterpret_cast<GetProcessorErrConfigRes*>(response);
448
449 if (*dataLen != 0)
450 {
451 *dataLen = 0;
452 return IPMI_CC_REQ_DATA_LEN_INVALID;
453 }
454
455 std::string service =
456 getService(dbus, processorErrConfigIntf, processorErrConfigObjPath);
457 Value variant = getDbusProperty(dbus, service, processorErrConfigObjPath,
458 processorErrConfigIntf, "ResetCfg");
459 resp->resetCfg = sdbusplus::message::variant_ns::get<uint8_t>(variant);
460
461 std::vector<uint8_t> caterrStatus;
Kuiying Wangbc546672018-11-23 15:41:05 +0800462 sdbusplus::message::variant<std::vector<uint8_t>> message;
Jason M. Bills64796042018-10-03 16:51:55 -0700463
464 auto method =
465 dbus.new_method_call(service.c_str(), processorErrConfigObjPath,
466 "org.freedesktop.DBus.Properties", "Get");
467
468 method.append(processorErrConfigIntf, "CATERRStatus");
Kuiying Wangbc546672018-11-23 15:41:05 +0800469 auto reply = dbus.call(method);
Jason M. Bills64796042018-10-03 16:51:55 -0700470
471 try
472 {
Kuiying Wangbc546672018-11-23 15:41:05 +0800473 reply.read(message);
474 caterrStatus =
475 sdbusplus::message::variant_ns::get<std::vector<uint8_t>>(message);
Jason M. Bills64796042018-10-03 16:51:55 -0700476 }
477 catch (sdbusplus::exception_t&)
478 {
Kuiying Wangbc546672018-11-23 15:41:05 +0800479 phosphor::logging::log<phosphor::logging::level::ERR>(
Jason M. Bills64796042018-10-03 16:51:55 -0700480 "ipmiOEMGetProcessorErrConfig: error on dbus",
481 phosphor::logging::entry("PRORPERTY=CATERRStatus"),
482 phosphor::logging::entry("PATH=%s", processorErrConfigObjPath),
483 phosphor::logging::entry("INTERFACE=%s", processorErrConfigIntf));
484 return IPMI_CC_UNSPECIFIED_ERROR;
485 }
486
487 size_t len =
488 maxCPUNum <= caterrStatus.size() ? maxCPUNum : caterrStatus.size();
489 caterrStatus.resize(len);
490 std::copy(caterrStatus.begin(), caterrStatus.end(), resp->caterrStatus);
491 *dataLen = sizeof(GetProcessorErrConfigRes);
492
493 return IPMI_CC_OK;
494}
495
496ipmi_ret_t ipmiOEMSetProcessorErrConfig(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
497 ipmi_request_t request,
498 ipmi_response_t response,
499 ipmi_data_len_t dataLen,
500 ipmi_context_t context)
501{
502 SetProcessorErrConfigReq* req =
503 reinterpret_cast<SetProcessorErrConfigReq*>(request);
504
505 if (*dataLen != sizeof(SetProcessorErrConfigReq))
506 {
507 *dataLen = 0;
508 return IPMI_CC_REQ_DATA_LEN_INVALID;
509 }
510 std::string service =
511 getService(dbus, processorErrConfigIntf, processorErrConfigObjPath);
512 setDbusProperty(dbus, service, processorErrConfigObjPath,
513 processorErrConfigIntf, "ResetCfg", req->resetCfg);
514
515 setDbusProperty(dbus, service, processorErrConfigObjPath,
516 processorErrConfigIntf, "ResetErrorOccurrenceCounts",
517 req->resetErrorOccurrenceCounts);
518 *dataLen = 0;
519
520 return IPMI_CC_OK;
521}
522
Yong Li703922d2018-11-06 13:25:31 +0800523ipmi_ret_t ipmiOEMGetShutdownPolicy(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
524 ipmi_request_t request,
525 ipmi_response_t response,
526 ipmi_data_len_t dataLen,
527 ipmi_context_t context)
528{
529 GetOEMShutdownPolicyRes* resp =
530 reinterpret_cast<GetOEMShutdownPolicyRes*>(response);
531
532 if (*dataLen != 0)
533 {
534 phosphor::logging::log<phosphor::logging::level::ERR>(
Kuiying Wang45f04982018-12-26 09:23:08 +0800535 "oem_get_shutdown_policy: invalid input len!");
Yong Li703922d2018-11-06 13:25:31 +0800536 *dataLen = 0;
537 return IPMI_CC_REQ_DATA_LEN_INVALID;
538 }
539
540 *dataLen = 0;
541
542 try
543 {
544 std::string service =
545 getService(dbus, oemShutdownPolicyIntf, oemShutdownPolicyObjPath);
546 Value variant = getDbusProperty(dbus, service, oemShutdownPolicyObjPath,
547 oemShutdownPolicyIntf,
548 oemShutdownPolicyObjPathProp);
549 resp->policy = sdbusplus::message::variant_ns::get<uint8_t>(variant);
550 // TODO needs to check if it is multi-node products,
551 // policy is only supported on node 3/4
552 resp->policySupport = shutdownPolicySupported;
553 }
554 catch (sdbusplus::exception_t& e)
555 {
556 phosphor::logging::log<phosphor::logging::level::ERR>(e.description());
557 return IPMI_CC_UNSPECIFIED_ERROR;
558 }
559
560 *dataLen = sizeof(GetOEMShutdownPolicyRes);
561 return IPMI_CC_OK;
562}
563
564ipmi_ret_t ipmiOEMSetShutdownPolicy(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
565 ipmi_request_t request,
566 ipmi_response_t response,
567 ipmi_data_len_t dataLen,
568 ipmi_context_t context)
569{
570 uint8_t* req = reinterpret_cast<uint8_t*>(request);
571
572 // TODO needs to check if it is multi-node products,
573 // policy is only supported on node 3/4
574 if (*dataLen != 1)
575 {
576 phosphor::logging::log<phosphor::logging::level::ERR>(
577 "oem_set_shutdown_policy: invalid input len!");
578 *dataLen = 0;
579 return IPMI_CC_REQ_DATA_LEN_INVALID;
580 }
581
582 *dataLen = 0;
583 if ((*req != noShutdownOnOCOT) && (*req != shutdownOnOCOT))
584 {
585 phosphor::logging::log<phosphor::logging::level::ERR>(
586 "oem_set_shutdown_policy: invalid input!");
587 return IPMI_CC_INVALID_FIELD_REQUEST;
588 }
589
590 try
591 {
592 std::string service =
593 getService(dbus, oemShutdownPolicyIntf, oemShutdownPolicyObjPath);
594 setDbusProperty(dbus, service, oemShutdownPolicyObjPath,
595 oemShutdownPolicyIntf, oemShutdownPolicyObjPathProp,
596 *req);
597 }
598 catch (sdbusplus::exception_t& e)
599 {
600 phosphor::logging::log<phosphor::logging::level::ERR>(e.description());
601 return IPMI_CC_UNSPECIFIED_ERROR;
602 }
603
604 return IPMI_CC_OK;
605}
606
Suryakanth Sekard509eb92018-11-15 17:44:11 +0530607/** @brief implementation for check the DHCP or not in IPv4
608 * @param[in] Channel - Channel number
609 * @returns true or false.
610 */
611static bool isDHCPEnabled(uint8_t Channel)
612{
613 try
614 {
615 auto ethdevice = getChannelName(Channel);
616 if (ethdevice.empty())
617 {
618 return false;
619 }
620 auto ethIP = ethdevice + "/ipv4";
621 auto ethernetObj =
622 getDbusObject(dbus, networkIPIntf, networkRoot, ethIP);
623 auto value = getDbusProperty(dbus, networkService, ethernetObj.first,
624 networkIPIntf, "Origin");
625 if (sdbusplus::message::variant_ns::get<std::string>(value) ==
626 "xyz.openbmc_project.Network.IP.AddressOrigin.DHCP")
627 {
628 return true;
629 }
630 else
631 {
632 return false;
633 }
634 }
635 catch (sdbusplus::exception_t& e)
636 {
637 phosphor::logging::log<phosphor::logging::level::ERR>(e.description());
638 return true;
639 }
640}
641
642/** @brief implementes for check the DHCP or not in IPv6
643 * @param[in] Channel - Channel number
644 * @returns true or false.
645 */
646static bool isDHCPIPv6Enabled(uint8_t Channel)
647{
648
649 try
650 {
651 auto ethdevice = getChannelName(Channel);
652 if (ethdevice.empty())
653 {
654 return false;
655 }
656 auto ethIP = ethdevice + "/ipv6";
657 auto objectInfo =
658 getDbusObject(dbus, networkIPIntf, networkRoot, ethIP);
659 auto properties = getAllDbusProperties(dbus, objectInfo.second,
660 objectInfo.first, networkIPIntf);
661 if (sdbusplus::message::variant_ns::get<std::string>(
662 properties["Origin"]) ==
663 "xyz.openbmc_project.Network.IP.AddressOrigin.DHCP")
664 {
665 return true;
666 }
667 else
668 {
669 return false;
670 }
671 }
672 catch (sdbusplus::exception_t& e)
673 {
674 phosphor::logging::log<phosphor::logging::level::ERR>(e.description());
675 return true;
676 }
677}
678
679/** @brief implementes the creating of default new user
680 * @param[in] userName - new username in 16 bytes.
681 * @param[in] userPassword - new password in 20 bytes
682 * @returns ipmi completion code.
683 */
684ipmi::RspType<> ipmiOEMSetUser2Activation(
685 std::array<uint8_t, ipmi::ipmiMaxUserName>& userName,
686 std::array<uint8_t, ipmi::maxIpmi20PasswordSize>& userPassword)
687{
688 bool userState = false;
689 // Check for System Interface not exist and LAN should be static
690 for (uint8_t channel = 0; channel < maxIpmiChannels; channel++)
691 {
692 ChannelInfo chInfo;
693 try
694 {
695 getChannelInfo(channel, chInfo);
696 }
697 catch (sdbusplus::exception_t& e)
698 {
699 phosphor::logging::log<phosphor::logging::level::ERR>(
700 "ipmiOEMSetUser2Activation: Failed to get Channel Info",
701 phosphor::logging::entry("MSG: %s", e.description()));
702 return ipmi::response(ipmi::ccUnspecifiedError);
703 }
704 if (chInfo.mediumType ==
705 static_cast<uint8_t>(EChannelMediumType::systemInterface))
706 {
707 phosphor::logging::log<phosphor::logging::level::ERR>(
708 "ipmiOEMSetUser2Activation: system interface exist .");
709 return ipmi::response(ipmi::ccCommandNotAvailable);
710 }
711 else
712 {
713
714 if (chInfo.mediumType ==
715 static_cast<uint8_t>(EChannelMediumType::lan8032))
716 {
717 if (isDHCPIPv6Enabled(channel) || isDHCPEnabled(channel))
718 {
719 phosphor::logging::log<phosphor::logging::level::ERR>(
720 "ipmiOEMSetUser2Activation: DHCP enabled .");
721 return ipmi::response(ipmi::ccCommandNotAvailable);
722 }
723 }
724 }
725 }
726 uint8_t maxChUsers = 0, enabledUsers = 0, fixedUsers = 0;
727 if (ipmi::ccSuccess ==
728 ipmiUserGetAllCounts(maxChUsers, enabledUsers, fixedUsers))
729 {
730 if (enabledUsers > 1)
731 {
732 phosphor::logging::log<phosphor::logging::level::ERR>(
733 "ipmiOEMSetUser2Activation: more than one user is enabled.");
734 return ipmi::response(ipmi::ccCommandNotAvailable);
735 }
736 // Check the user 2 is enabled or not
737 ipmiUserCheckEnabled(ipmiDefaultUserId, userState);
738 if (userState == true)
739 {
740 phosphor::logging::log<phosphor::logging::level::ERR>(
741 "ipmiOEMSetUser2Activation: user 2 already enabled .");
742 return ipmi::response(ipmi::ccCommandNotAvailable);
743 }
744 }
745 else
746 {
747 return ipmi::response(ipmi::ccUnspecifiedError);
748 }
749
750#if BYTE_ORDER == LITTLE_ENDIAN
751 PrivAccess privAccess = {PRIVILEGE_ADMIN, true, true, true, 0};
752#endif
753#if BYTE_ORDER == BIG_ENDIAN
754 PrivAccess privAccess = {0, true, true, true, PRIVILEGE_ADMIN};
755#endif
756
757 if (ipmi::ccSuccess ==
758 ipmiUserSetUserName(ipmiDefaultUserId,
759 reinterpret_cast<const char*>(userName.data())))
760 {
761 if (ipmi::ccSuccess ==
762 ipmiUserSetUserPassword(
763 ipmiDefaultUserId,
764 reinterpret_cast<const char*>(userPassword.data())))
765 {
766 if (ipmi::ccSuccess ==
767 ipmiUserSetPrivilegeAccess(
768 ipmiDefaultUserId,
769 static_cast<uint8_t>(ipmi::EChannelID::chanLan1),
770 privAccess, true))
771 {
772 phosphor::logging::log<phosphor::logging::level::INFO>(
773 "ipmiOEMSetUser2Activation: user created successfully ");
774 return ipmi::responseSuccess();
775 }
776 }
777 // we need to delete the default user id which added in this command as
778 // password / priv setting is failed.
779 ipmiUserSetUserName(ipmiDefaultUserId, "");
780 phosphor::logging::log<phosphor::logging::level::ERR>(
781 "ipmiOEMSetUser2Activation: password / priv setting is failed.");
782 }
783 else
784 {
785 phosphor::logging::log<phosphor::logging::level::ERR>(
786 "ipmiOEMSetUser2Activation: Setting username failed.");
787 }
788
789 return ipmi::response(ipmi::ccCommandNotAvailable);
790}
791
Kuiying Wang45f04982018-12-26 09:23:08 +0800792namespace ledAction
793{
794using namespace sdbusplus::xyz::openbmc_project::Led::server;
795std::map<Physical::Action, uint8_t> actionDbusToIpmi = {
796 {Physical::Action::Off, 0x00},
797 {Physical::Action::On, 0x10},
798 {Physical::Action::Blink, 0x01}};
799
800std::map<uint8_t, std::string> offsetObjPath = {
801 {2, statusAmberObjPath}, {4, statusGreenObjPath}, {6, identifyLEDObjPath}};
802
803} // namespace ledAction
804
805int8_t getLEDState(sdbusplus::bus::bus& bus, const std::string& intf,
806 const std::string& objPath, uint8_t& state)
807{
808 try
809 {
810 std::string service = getService(bus, intf, objPath);
811 Value stateValue =
812 getDbusProperty(bus, service, objPath, intf, "State");
813 std::string strState =
814 sdbusplus::message::variant_ns::get<std::string>(stateValue);
815 state = ledAction::actionDbusToIpmi.at(
816 sdbusplus::xyz::openbmc_project::Led::server::Physical::
817 convertActionFromString(strState));
818 }
819 catch (sdbusplus::exception::SdBusError& e)
820 {
821 phosphor::logging::log<phosphor::logging::level::ERR>(e.what());
822 return -1;
823 }
824 return 0;
825}
826
827ipmi_ret_t ipmiOEMGetLEDStatus(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
828 ipmi_request_t request, ipmi_response_t response,
829 ipmi_data_len_t dataLen, ipmi_context_t context)
830{
831 uint8_t* resp = reinterpret_cast<uint8_t*>(response);
832 // LED Status
833 //[1:0] = Reserved
834 //[3:2] = Status(Amber)
835 //[5:4] = Status(Green)
836 //[7:6] = System Identify
837 // Status definitions:
838 // 00b = Off
839 // 01b = Blink
840 // 10b = On
841 // 11b = invalid
842 if (*dataLen != 0)
843 {
844 phosphor::logging::log<phosphor::logging::level::ERR>(
845 "oem_get_led_status: invalid input len!");
846 *dataLen = 0;
847 return IPMI_CC_REQ_DATA_LEN_INVALID;
848 }
849
850 phosphor::logging::log<phosphor::logging::level::DEBUG>("GET led status");
851 *resp = 0;
852 *dataLen = 0;
853 for (auto it = ledAction::offsetObjPath.begin();
854 it != ledAction::offsetObjPath.end(); ++it)
855 {
856 uint8_t state = 0;
857 if (-1 == getLEDState(dbus, ledIntf, it->second, state))
858 {
859 phosphor::logging::log<phosphor::logging::level::ERR>(
860 "oem_get_led_status: fail to get ID LED status!");
861 return IPMI_CC_UNSPECIFIED_ERROR;
862 }
863 *resp |= state << it->first;
864 }
865
866 *dataLen = sizeof(*resp);
867 return IPMI_CC_OK;
868}
869
Yong Li23737fe2019-02-19 08:49:55 +0800870ipmi_ret_t ipmiOEMCfgHostSerialPortSpeed(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
871 ipmi_request_t request,
872 ipmi_response_t response,
873 ipmi_data_len_t dataLen,
874 ipmi_context_t context)
875{
876 CfgHostSerialReq* req = reinterpret_cast<CfgHostSerialReq*>(request);
877 uint8_t* resp = reinterpret_cast<uint8_t*>(response);
878
879 if (*dataLen == 0)
880 {
881 phosphor::logging::log<phosphor::logging::level::ERR>(
882 "CfgHostSerial: invalid input len!",
883 phosphor::logging::entry("LEN=%d", *dataLen));
884 return IPMI_CC_REQ_DATA_LEN_INVALID;
885 }
886
887 switch (req->command)
888 {
889 case getHostSerialCfgCmd:
890 {
891 if (*dataLen != 1)
892 {
893 phosphor::logging::log<phosphor::logging::level::ERR>(
894 "CfgHostSerial: invalid input len!");
895 *dataLen = 0;
896 return IPMI_CC_REQ_DATA_LEN_INVALID;
897 }
898
899 *dataLen = 0;
900
901 boost::process::ipstream is;
902 std::vector<std::string> data;
903 std::string line;
904 boost::process::child c1(fwGetEnvCmd, "-n", fwHostSerailCfgEnvName,
905 boost::process::std_out > is);
906
907 while (c1.running() && std::getline(is, line) && !line.empty())
908 {
909 data.push_back(line);
910 }
911
912 c1.wait();
913 if (c1.exit_code())
914 {
915 phosphor::logging::log<phosphor::logging::level::ERR>(
916 "CfgHostSerial:: error on execute",
917 phosphor::logging::entry("EXECUTE=%s", fwSetEnvCmd));
918 // Using the default value
919 *resp = 0;
920 }
921 else
922 {
923 if (data.size() != 1)
924 {
925 phosphor::logging::log<phosphor::logging::level::ERR>(
926 "CfgHostSerial:: error on read env");
927 return IPMI_CC_UNSPECIFIED_ERROR;
928 }
929 try
930 {
931 unsigned long tmp = std::stoul(data[0]);
932 if (tmp > std::numeric_limits<uint8_t>::max())
933 {
934 throw std::out_of_range("Out of range");
935 }
936 *resp = static_cast<uint8_t>(tmp);
937 }
938 catch (const std::invalid_argument& e)
939 {
940 phosphor::logging::log<phosphor::logging::level::ERR>(
941 "invalid config ",
942 phosphor::logging::entry("ERR=%s", e.what()));
943 return IPMI_CC_UNSPECIFIED_ERROR;
944 }
945 catch (const std::out_of_range& e)
946 {
947 phosphor::logging::log<phosphor::logging::level::ERR>(
948 "out_of_range config ",
949 phosphor::logging::entry("ERR=%s", e.what()));
950 return IPMI_CC_UNSPECIFIED_ERROR;
951 }
952 }
953
954 *dataLen = 1;
955 break;
956 }
957 case setHostSerialCfgCmd:
958 {
959 if (*dataLen != sizeof(CfgHostSerialReq))
960 {
961 phosphor::logging::log<phosphor::logging::level::ERR>(
962 "CfgHostSerial: invalid input len!");
963 *dataLen = 0;
964 return IPMI_CC_REQ_DATA_LEN_INVALID;
965 }
966
967 *dataLen = 0;
968
969 if (req->parameter > HostSerialCfgParamMax)
970 {
971 phosphor::logging::log<phosphor::logging::level::ERR>(
972 "CfgHostSerial: invalid input!");
973 return IPMI_CC_INVALID_FIELD_REQUEST;
974 }
975
976 boost::process::child c1(fwSetEnvCmd, fwHostSerailCfgEnvName,
977 std::to_string(req->parameter));
978
979 c1.wait();
980 if (c1.exit_code())
981 {
982 phosphor::logging::log<phosphor::logging::level::ERR>(
983 "CfgHostSerial:: error on execute",
984 phosphor::logging::entry("EXECUTE=%s", fwGetEnvCmd));
985 return IPMI_CC_UNSPECIFIED_ERROR;
986 }
987 break;
988 }
989 default:
990 phosphor::logging::log<phosphor::logging::level::ERR>(
991 "CfgHostSerial: invalid input!");
992 *dataLen = 0;
993 return IPMI_CC_INVALID_FIELD_REQUEST;
994 }
995
996 return IPMI_CC_OK;
997}
998
James Feist91244a62019-02-19 15:04:54 -0800999constexpr const char* thermalModeInterface =
1000 "xyz.openbmc_project.Control.ThermalMode";
1001constexpr const char* thermalModePath =
1002 "/xyz/openbmc_project/control/thermal_mode";
1003
1004bool getFanProfileInterface(
1005 sdbusplus::bus::bus& bus,
1006 boost::container::flat_map<
1007 std::string, std::variant<std::vector<std::string>, std::string>>& resp)
1008{
1009 auto call = bus.new_method_call(settingsBusName, thermalModePath, PROP_INTF,
1010 "GetAll");
1011 call.append(thermalModeInterface);
1012 try
1013 {
1014 auto data = bus.call(call);
1015 data.read(resp);
1016 }
1017 catch (sdbusplus::exception_t& e)
1018 {
1019 phosphor::logging::log<phosphor::logging::level::ERR>(
1020 "getFanProfileInterface: can't get thermal mode!",
1021 phosphor::logging::entry("ERR=%s", e.what()));
1022 return false;
1023 }
1024 return true;
1025}
1026
1027ipmi_ret_t ipmiOEMSetFanConfig(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
1028 ipmi_request_t request, ipmi_response_t response,
1029 ipmi_data_len_t dataLen, ipmi_context_t context)
1030{
1031
1032 if (*dataLen < 2 || *dataLen > 7)
1033 {
1034 phosphor::logging::log<phosphor::logging::level::ERR>(
1035 "ipmiOEMSetFanConfig: invalid input len!");
1036 *dataLen = 0;
1037 return IPMI_CC_REQ_DATA_LEN_INVALID;
1038 }
1039
1040 // todo: tell bios to only send first 2 bytes
1041
1042 SetFanConfigReq* req = reinterpret_cast<SetFanConfigReq*>(request);
1043 boost::container::flat_map<
1044 std::string, std::variant<std::vector<std::string>, std::string>>
1045 profileData;
1046 if (!getFanProfileInterface(dbus, profileData))
1047 {
1048 return IPMI_CC_UNSPECIFIED_ERROR;
1049 }
1050
1051 std::vector<std::string>* supported =
1052 std::get_if<std::vector<std::string>>(&profileData["Supported"]);
1053 if (supported == nullptr)
1054 {
1055 return IPMI_CC_INVALID_FIELD_REQUEST;
1056 }
1057 std::string mode;
1058 if (req->flags &
1059 (1 << static_cast<uint8_t>(setFanProfileFlags::setPerfAcousMode)))
1060 {
1061 bool performanceMode =
1062 (req->flags & (1 << static_cast<uint8_t>(
1063 setFanProfileFlags::performAcousSelect))) > 0;
1064
1065 if (performanceMode)
1066 {
1067
1068 if (std::find(supported->begin(), supported->end(),
1069 "Performance") != supported->end())
1070 {
1071 mode = "Performance";
1072 }
1073 }
1074 else
1075 {
1076
1077 if (std::find(supported->begin(), supported->end(), "Acoustic") !=
1078 supported->end())
1079 {
1080 mode = "Acoustic";
1081 }
1082 }
1083 if (mode.empty())
1084 {
1085 return IPMI_CC_INVALID_FIELD_REQUEST;
1086 }
1087 setDbusProperty(dbus, settingsBusName, thermalModePath,
1088 thermalModeInterface, "Current", mode);
1089 }
1090
1091 return IPMI_CC_OK;
1092}
1093
1094ipmi_ret_t ipmiOEMGetFanConfig(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
1095 ipmi_request_t request, ipmi_response_t response,
1096 ipmi_data_len_t dataLen, ipmi_context_t context)
1097{
1098
1099 if (*dataLen > 1)
1100 {
1101 phosphor::logging::log<phosphor::logging::level::ERR>(
1102 "ipmiOEMGetFanConfig: invalid input len!");
1103 *dataLen = 0;
1104 return IPMI_CC_REQ_DATA_LEN_INVALID;
1105 }
1106
1107 // todo: talk to bios about needing less information
1108
1109 GetFanConfigResp* resp = reinterpret_cast<GetFanConfigResp*>(response);
1110 *dataLen = sizeof(GetFanConfigResp);
1111
1112 boost::container::flat_map<
1113 std::string, std::variant<std::vector<std::string>, std::string>>
1114 profileData;
1115
1116 if (!getFanProfileInterface(dbus, profileData))
1117 {
1118 return IPMI_CC_UNSPECIFIED_ERROR;
1119 }
1120
1121 std::string* current = std::get_if<std::string>(&profileData["Current"]);
1122
1123 if (current == nullptr)
1124 {
1125 phosphor::logging::log<phosphor::logging::level::ERR>(
1126 "ipmiOEMGetFanConfig: can't get current mode!");
1127 return IPMI_CC_UNSPECIFIED_ERROR;
1128 }
1129 bool performance = (*current == "Performance");
1130
1131 if (performance)
1132 {
1133 resp->flags |= 1 << 2;
1134 }
1135
1136 return IPMI_CC_OK;
1137}
1138
James Feist5f957ca2019-03-14 15:33:55 -07001139constexpr const char* cfmLimitSettingPath =
1140 "/xyz/openbmc_project/control/cfm_limit";
1141constexpr const char* cfmLimitIface = "xyz.openbmc_project.Control.CFMLimit";
James Feistfaa4f222019-03-21 16:21:55 -07001142constexpr const size_t legacyExitAirSensorNumber = 0x2e;
1143
1144static std::string getExitAirConfigPath()
1145{
1146
1147 auto method =
1148 dbus.new_method_call("xyz.openbmc_project.ObjectMapper",
1149 "/xyz/openbmc_project/object_mapper",
1150 "xyz.openbmc_project.ObjectMapper", "GetSubTree");
1151
1152 method.append(
1153 "/", 0,
1154 std::array<const char*, 1>{"xyz.openbmc_project.Configuration.Pid"});
1155 std::string path;
1156 GetSubTreeType resp;
1157 try
1158 {
1159 auto reply = dbus.call(method);
1160 reply.read(resp);
1161 }
1162 catch (sdbusplus::exception_t&)
1163 {
1164 phosphor::logging::log<phosphor::logging::level::ERR>(
1165 "ipmiOEMGetFscParameter: mapper error");
1166 };
1167 auto config = std::find_if(resp.begin(), resp.end(), [](const auto& pair) {
1168 return pair.first.find("Exit_Air") != std::string::npos;
1169 });
1170 if (config != resp.end())
1171 {
1172 path = std::move(config->first);
1173 }
1174 return path;
1175}
James Feist5f957ca2019-03-14 15:33:55 -07001176
1177ipmi_ret_t ipmiOEMSetFscParameter(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
1178 ipmi_request_t request,
1179 ipmi_response_t response,
1180 ipmi_data_len_t dataLen,
1181 ipmi_context_t context)
1182{
1183 constexpr const size_t disableLimiting = 0x0;
1184
1185 if (*dataLen < 2)
1186 {
1187 phosphor::logging::log<phosphor::logging::level::ERR>(
1188 "ipmiOEMSetFscParameter: invalid input len!");
1189 *dataLen = 0;
1190 return IPMI_CC_REQ_DATA_LEN_INVALID;
1191 }
1192
1193 uint8_t* req = static_cast<uint8_t*>(request);
1194
James Feistfaa4f222019-03-21 16:21:55 -07001195 if (*req == static_cast<uint8_t>(setFscParamFlags::tcontrol))
1196 {
1197 if (*dataLen == 3 && req[1] == legacyExitAirSensorNumber)
1198 {
1199 *dataLen = 0;
1200 std::string path = getExitAirConfigPath();
1201 ipmi::setDbusProperty(dbus, "xyz.openbmc_project.EntityManager",
1202 path, "xyz.openbmc_project.Configuration.Pid",
1203 "SetPoint", static_cast<double>(req[2]));
1204 return IPMI_CC_OK;
1205 }
1206 else if (*dataLen == 3)
1207 {
1208 *dataLen = 0;
1209 return IPMI_CC_INVALID_FIELD_REQUEST;
1210 }
1211 else
1212 {
1213 *dataLen = 0;
1214 return IPMI_CC_REQ_DATA_LEN_INVALID;
1215 }
1216 }
1217 else if (*req == static_cast<uint8_t>(setFscParamFlags::cfm))
James Feist5f957ca2019-03-14 15:33:55 -07001218 {
1219 if (*dataLen != 3)
1220 {
1221 phosphor::logging::log<phosphor::logging::level::ERR>(
1222 "ipmiOEMSetFscParameter: invalid input len!");
1223 *dataLen = 0;
1224 return IPMI_CC_REQ_DATA_LEN_INVALID;
1225 }
1226 *dataLen = 0;
1227
1228 uint16_t cfm = req[1] | (static_cast<uint16_t>(req[2]) << 8);
1229
1230 // must be greater than 50 based on eps
1231 if (cfm < 50 && cfm != disableLimiting)
1232 {
1233 return IPMI_CC_PARM_OUT_OF_RANGE;
1234 }
1235
1236 try
1237 {
1238 ipmi::setDbusProperty(dbus, settingsBusName, cfmLimitSettingPath,
1239 cfmLimitIface, "Limit",
1240 static_cast<double>(cfm));
1241 }
1242 catch (sdbusplus::exception_t& e)
1243 {
1244 phosphor::logging::log<phosphor::logging::level::ERR>(
1245 "ipmiOEMSetFscParameter: can't set cfm setting!",
1246 phosphor::logging::entry("ERR=%s", e.what()));
1247 return IPMI_CC_UNSPECIFIED_ERROR;
1248 }
1249 return IPMI_CC_OK;
1250 }
1251 else
1252 {
1253 // todo other command parts possibly
1254 // tcontrol is handled in peci now
1255 // fan speed offset not implemented yet
1256 // domain pwm limit not implemented
1257 *dataLen = 0;
1258 return IPMI_CC_PARM_OUT_OF_RANGE;
1259 }
1260}
1261
1262ipmi_ret_t ipmiOEMGetFscParameter(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
1263 ipmi_request_t request,
1264 ipmi_response_t response,
1265 ipmi_data_len_t dataLen,
1266 ipmi_context_t context)
1267{
James Feistfaa4f222019-03-21 16:21:55 -07001268 constexpr uint8_t legacyDefaultExitAirLimit = -128;
James Feist5f957ca2019-03-14 15:33:55 -07001269
1270 if (*dataLen < 1)
1271 {
1272 phosphor::logging::log<phosphor::logging::level::ERR>(
1273 "ipmiOEMGetFscParameter: invalid input len!");
1274 *dataLen = 0;
1275 return IPMI_CC_REQ_DATA_LEN_INVALID;
1276 }
1277
1278 uint8_t* req = static_cast<uint8_t*>(request);
1279
James Feistfaa4f222019-03-21 16:21:55 -07001280 if (*req == static_cast<uint8_t>(setFscParamFlags::tcontrol))
1281 {
1282 if (*dataLen != 2)
1283 {
1284 phosphor::logging::log<phosphor::logging::level::ERR>(
1285 "ipmiOEMGetFscParameter: invalid input len!");
1286 *dataLen = 0;
1287 return IPMI_CC_REQ_DATA_LEN_INVALID;
1288 }
1289
1290 if (req[1] != legacyExitAirSensorNumber)
1291 {
1292 return IPMI_CC_PARM_OUT_OF_RANGE;
1293 }
1294 uint8_t setpoint = legacyDefaultExitAirLimit;
1295 std::string path = getExitAirConfigPath();
1296 if (path.size())
1297 {
1298 Value val = ipmi::getDbusProperty(
1299 dbus, "xyz.openbmc_project.EntityManager", path,
1300 "xyz.openbmc_project.Configuration.Pid", "SetPoint");
1301 setpoint = std::floor(std::get<double>(val) + 0.5);
1302 }
1303
1304 // old implementation used to return the "default" and current, we
1305 // don't make the default readily available so just make both the
1306 // same
1307 auto resp = static_cast<uint8_t*>(response);
1308 resp[0] = setpoint;
1309 resp[1] = setpoint;
1310
1311 *dataLen = 2;
1312 return IPMI_CC_OK;
1313 }
1314 else if (*req == static_cast<uint8_t>(setFscParamFlags::cfm))
James Feist5f957ca2019-03-14 15:33:55 -07001315 {
1316
1317 /*
1318 DataLen should be 1, but host is sending us an extra bit. As the
1319 previous behavior didn't seem to prevent this, ignore the check for now.
1320
1321 if (*dataLen != 1)
1322 {
1323 phosphor::logging::log<phosphor::logging::level::ERR>(
1324 "ipmiOEMGetFscParameter: invalid input len!");
1325 *dataLen = 0;
1326 return IPMI_CC_REQ_DATA_LEN_INVALID;
1327 }
1328 */
1329 Value cfmLimit;
1330 Value cfmMaximum;
1331 try
1332 {
1333 cfmLimit = ipmi::getDbusProperty(dbus, settingsBusName,
1334 cfmLimitSettingPath, cfmLimitIface,
1335 "Limit");
1336 cfmMaximum = ipmi::getDbusProperty(
1337 dbus, "xyz.openbmc_project.ExitAirTempSensor",
1338 "/xyz/openbmc_project/control/MaxCFM", cfmLimitIface, "Limit");
1339 }
1340 catch (sdbusplus::exception_t& e)
1341 {
1342 phosphor::logging::log<phosphor::logging::level::ERR>(
1343 "ipmiOEMSetFscParameter: can't get cfm setting!",
1344 phosphor::logging::entry("ERR=%s", e.what()));
1345 *dataLen = 0;
1346 return IPMI_CC_UNSPECIFIED_ERROR;
1347 }
1348
1349 auto cfmLim = std::get_if<double>(&cfmLimit);
1350 if (cfmLim == nullptr ||
1351 *cfmLim > std::numeric_limits<uint16_t>::max() || *cfmLim < 0)
1352 {
1353 phosphor::logging::log<phosphor::logging::level::ERR>(
1354 "ipmiOEMSetFscParameter: cfm limit out of range!");
1355 *dataLen = 0;
1356 return IPMI_CC_UNSPECIFIED_ERROR;
1357 }
1358
1359 auto cfmMax = std::get_if<double>(&cfmMaximum);
1360 if (cfmMax == nullptr ||
1361 *cfmMax > std::numeric_limits<uint16_t>::max() || *cfmMax < 0)
1362 {
1363 phosphor::logging::log<phosphor::logging::level::ERR>(
1364 "ipmiOEMSetFscParameter: cfm max out of range!");
1365 *dataLen = 0;
1366 return IPMI_CC_UNSPECIFIED_ERROR;
1367 }
1368 *cfmLim = std::floor(*cfmLim + 0.5);
1369 *cfmMax = std::floor(*cfmMax + 0.5);
1370 uint16_t resp = static_cast<uint16_t>(*cfmLim);
1371 uint16_t* ptr = static_cast<uint16_t*>(response);
1372 ptr[0] = resp;
1373 resp = static_cast<uint16_t>(*cfmMax);
1374 ptr[1] = resp;
1375
1376 *dataLen = 4;
1377
1378 return IPMI_CC_OK;
1379 }
1380 else
1381 {
1382 // todo other command parts possibly
James Feist5f957ca2019-03-14 15:33:55 -07001383 // fan speed offset not implemented yet
1384 // domain pwm limit not implemented
1385 *dataLen = 0;
James Feistfaa4f222019-03-21 16:21:55 -07001386
James Feist5f957ca2019-03-14 15:33:55 -07001387 return IPMI_CC_PARM_OUT_OF_RANGE;
1388 }
1389}
1390
Jason M. Bills64796042018-10-03 16:51:55 -07001391static void registerOEMFunctions(void)
Jia, Chunhuia835eaa2018-09-05 09:00:41 +08001392{
1393 phosphor::logging::log<phosphor::logging::level::INFO>(
1394 "Registering OEM commands");
Jason M. Bills64796042018-10-03 16:51:55 -07001395 ipmiPrintAndRegister(netfnIntcOEMGeneral, IPMI_CMD_WILDCARD, NULL,
1396 ipmiOEMWildcard,
1397 PRIVILEGE_USER); // wildcard default handler
1398 ipmiPrintAndRegister(netfunIntelAppOEM, IPMI_CMD_WILDCARD, NULL,
1399 ipmiOEMWildcard,
1400 PRIVILEGE_USER); // wildcard default handler
1401 ipmiPrintAndRegister(
1402 netfnIntcOEMGeneral,
1403 static_cast<ipmi_cmd_t>(
1404 IPMINetfnIntelOEMGeneralCmd::cmdGetChassisIdentifier),
1405 NULL, ipmiOEMGetChassisIdentifier,
1406 PRIVILEGE_USER); // get chassis identifier
1407 ipmiPrintAndRegister(
1408 netfnIntcOEMGeneral,
1409 static_cast<ipmi_cmd_t>(IPMINetfnIntelOEMGeneralCmd::cmdSetSystemGUID),
1410 NULL, ipmiOEMSetSystemGUID,
1411 PRIVILEGE_ADMIN); // set system guid
1412 ipmiPrintAndRegister(
1413 netfnIntcOEMGeneral,
1414 static_cast<ipmi_cmd_t>(IPMINetfnIntelOEMGeneralCmd::cmdSetBIOSID),
1415 NULL, ipmiOEMSetBIOSID, PRIVILEGE_ADMIN);
1416 ipmiPrintAndRegister(netfnIntcOEMGeneral,
1417 static_cast<ipmi_cmd_t>(
1418 IPMINetfnIntelOEMGeneralCmd::cmdGetOEMDeviceInfo),
1419 NULL, ipmiOEMGetDeviceInfo, PRIVILEGE_USER);
1420 ipmiPrintAndRegister(
1421 netfnIntcOEMGeneral,
1422 static_cast<ipmi_cmd_t>(
1423 IPMINetfnIntelOEMGeneralCmd::cmdGetAICSlotFRUIDSlotPosRecords),
1424 NULL, ipmiOEMGetAICFRU, PRIVILEGE_USER);
Jia, Chunhuicc49b542019-03-20 15:41:07 +08001425
1426 ipmi::registerHandler(
1427 ipmi::prioOpenBmcBase, ipmi::netFnOemOne,
1428 static_cast<ipmi::Cmd>(
1429 IPMINetfnIntelOEMGeneralCmd::cmdSendEmbeddedFWUpdStatus),
1430 ipmi::Privilege::Operator, ipmiOEMSendEmbeddedFwUpdStatus);
1431
Jason M. Bills64796042018-10-03 16:51:55 -07001432 ipmiPrintAndRegister(
1433 netfnIntcOEMGeneral,
1434 static_cast<ipmi_cmd_t>(
1435 IPMINetfnIntelOEMGeneralCmd::cmdSetPowerRestoreDelay),
1436 NULL, ipmiOEMSetPowerRestoreDelay, PRIVILEGE_OPERATOR);
1437 ipmiPrintAndRegister(
1438 netfnIntcOEMGeneral,
1439 static_cast<ipmi_cmd_t>(
1440 IPMINetfnIntelOEMGeneralCmd::cmdGetPowerRestoreDelay),
1441 NULL, ipmiOEMGetPowerRestoreDelay, PRIVILEGE_USER);
Suryakanth Sekard509eb92018-11-15 17:44:11 +05301442
1443 ipmi::registerHandler(
1444 ipmi::prioOpenBmcBase, ipmi::netFnOemOne,
1445 static_cast<ipmi::Cmd>(
1446 IPMINetfnIntelOEMGeneralCmd::cmdSetOEMUser2Activation),
1447 ipmi::Privilege::Callback, ipmiOEMSetUser2Activation);
1448
Jason M. Bills64796042018-10-03 16:51:55 -07001449 ipmiPrintAndRegister(
1450 netfnIntcOEMGeneral,
1451 static_cast<ipmi_cmd_t>(
1452 IPMINetfnIntelOEMGeneralCmd::cmdGetProcessorErrConfig),
1453 NULL, ipmiOEMGetProcessorErrConfig, PRIVILEGE_USER);
1454 ipmiPrintAndRegister(
1455 netfnIntcOEMGeneral,
1456 static_cast<ipmi_cmd_t>(
1457 IPMINetfnIntelOEMGeneralCmd::cmdSetProcessorErrConfig),
1458 NULL, ipmiOEMSetProcessorErrConfig, PRIVILEGE_ADMIN);
Yong Li703922d2018-11-06 13:25:31 +08001459 ipmiPrintAndRegister(netfnIntcOEMGeneral,
1460 static_cast<ipmi_cmd_t>(
1461 IPMINetfnIntelOEMGeneralCmd::cmdSetShutdownPolicy),
1462 NULL, ipmiOEMSetShutdownPolicy, PRIVILEGE_ADMIN);
1463 ipmiPrintAndRegister(netfnIntcOEMGeneral,
1464 static_cast<ipmi_cmd_t>(
1465 IPMINetfnIntelOEMGeneralCmd::cmdGetShutdownPolicy),
1466 NULL, ipmiOEMGetShutdownPolicy, PRIVILEGE_ADMIN);
James Feist91244a62019-02-19 15:04:54 -08001467
1468 ipmiPrintAndRegister(
1469 netfnIntcOEMGeneral,
1470 static_cast<ipmi_cmd_t>(IPMINetfnIntelOEMGeneralCmd::cmdSetFanConfig),
1471 NULL, ipmiOEMSetFanConfig, PRIVILEGE_USER);
1472
1473 ipmiPrintAndRegister(
1474 netfnIntcOEMGeneral,
1475 static_cast<ipmi_cmd_t>(IPMINetfnIntelOEMGeneralCmd::cmdGetFanConfig),
1476 NULL, ipmiOEMGetFanConfig, PRIVILEGE_USER);
1477
James Feist5f957ca2019-03-14 15:33:55 -07001478 ipmiPrintAndRegister(netfnIntcOEMGeneral,
1479 static_cast<ipmi_cmd_t>(
1480 IPMINetfnIntelOEMGeneralCmd::cmdSetFscParameter),
1481 NULL, ipmiOEMSetFscParameter, PRIVILEGE_USER);
1482
1483 ipmiPrintAndRegister(netfnIntcOEMGeneral,
1484 static_cast<ipmi_cmd_t>(
1485 IPMINetfnIntelOEMGeneralCmd::cmdGetFscParameter),
1486 NULL, ipmiOEMGetFscParameter, PRIVILEGE_USER);
1487
Kuiying Wang45f04982018-12-26 09:23:08 +08001488 ipmiPrintAndRegister(
1489 netfnIntcOEMGeneral,
1490 static_cast<ipmi_cmd_t>(IPMINetfnIntelOEMGeneralCmd::cmdGetLEDStatus),
1491 NULL, ipmiOEMGetLEDStatus, PRIVILEGE_ADMIN);
Yong Li23737fe2019-02-19 08:49:55 +08001492 ipmiPrintAndRegister(
1493 netfnIntcOEMPlatform,
1494 static_cast<ipmi_cmd_t>(
1495 IPMINetfnIntelOEMPlatformCmd::cmdCfgHostSerialPortSpeed),
1496 NULL, ipmiOEMCfgHostSerialPortSpeed, PRIVILEGE_ADMIN);
Jia, Chunhuia835eaa2018-09-05 09:00:41 +08001497 return;
1498}
1499
1500} // namespace ipmi