blob: 3ca7caa24187f175f33f64a9f1e50ad30f055295 [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
Patrick Venturec2a07d42020-05-30 16:35:03 -070017#include "types.hpp"
Jason M. Bills64796042018-10-03 16:51:55 -070018#include "xyz/openbmc_project/Common/error.hpp"
Kuiying Wang45f04982018-12-26 09:23:08 +080019#include "xyz/openbmc_project/Led/Physical/server.hpp"
Jason M. Bills64796042018-10-03 16:51:55 -070020
Jayaprakash Mutyala94204162020-10-23 06:17:56 +000021#include <openssl/crypto.h>
Jia, Chunhuicc49b542019-03-20 15:41:07 +080022#include <systemd/sd-journal.h>
Jia, Chunhuia835eaa2018-09-05 09:00:41 +080023
Chen Yugang7a04f3a2019-10-08 11:12:35 +080024#include <appcommands.hpp>
James Feist91244a62019-02-19 15:04:54 -080025#include <boost/container/flat_map.hpp>
Yong Li23737fe2019-02-19 08:49:55 +080026#include <boost/process/child.hpp>
27#include <boost/process/io.hpp>
Yong Li0669d192019-05-06 14:01:46 +080028#include <com/intel/Control/OCOTShutdownPolicy/server.hpp>
Jason M. Bills64796042018-10-03 16:51:55 -070029#include <commandutils.hpp>
Zhikui Rence4e73f2019-12-06 13:59:47 -080030#include <gpiod.hpp>
Jia, Chunhuicc49b542019-03-20 15:41:07 +080031#include <ipmid/api.hpp>
Vernon Mauery5480ef62019-03-20 13:43:11 -070032#include <ipmid/utils.hpp>
James Feist63efafa2019-07-24 12:39:21 -070033#include <nlohmann/json.hpp>
Jia, Chunhuia835eaa2018-09-05 09:00:41 +080034#include <oemcommands.hpp>
Jia, Chunhuia835eaa2018-09-05 09:00:41 +080035#include <phosphor-logging/log.hpp>
36#include <sdbusplus/bus.hpp>
Suryakanth Sekard509eb92018-11-15 17:44:11 +053037#include <sdbusplus/message/types.hpp>
Chen Yugang97cf96e2019-11-01 08:55:11 +080038#include <xyz/openbmc_project/Chassis/Control/NMISource/server.hpp>
Chen,Yugang4f7e76b2019-08-20 09:28:06 +080039#include <xyz/openbmc_project/Control/Boot/Mode/server.hpp>
40#include <xyz/openbmc_project/Control/Boot/Source/server.hpp>
Cheng C Yang773703a2019-08-15 09:41:11 +080041#include <xyz/openbmc_project/Control/PowerSupplyRedundancy/server.hpp>
Richard Marian Thomaiyard801e462019-06-20 01:05:40 +053042#include <xyz/openbmc_project/Control/Security/RestrictionMode/server.hpp>
Richard Marian Thomaiyar8d4f8d72019-11-11 12:06:40 +053043#include <xyz/openbmc_project/Control/Security/SpecialMode/server.hpp>
Jia, Chunhuia835eaa2018-09-05 09:00:41 +080044
James Feistfcd2d3a2020-05-28 10:38:15 -070045#include <array>
46#include <filesystem>
47#include <iostream>
48#include <regex>
49#include <string>
50#include <variant>
51#include <vector>
52
Jia, Chunhuia835eaa2018-09-05 09:00:41 +080053namespace ipmi
54{
Jason M. Bills64796042018-10-03 16:51:55 -070055static void registerOEMFunctions() __attribute__((constructor));
Vernon Mauery4ac799d2019-05-20 15:50:37 -070056
Jason M. Bills64796042018-10-03 16:51:55 -070057static constexpr size_t maxFRUStringLength = 0x3F;
Jia, Chunhuia835eaa2018-09-05 09:00:41 +080058
Suryakanth Sekard509eb92018-11-15 17:44:11 +053059static constexpr auto ethernetIntf =
60 "xyz.openbmc_project.Network.EthernetInterface";
61static constexpr auto networkIPIntf = "xyz.openbmc_project.Network.IP";
62static constexpr auto networkService = "xyz.openbmc_project.Network";
63static constexpr auto networkRoot = "/xyz/openbmc_project/network";
64
Chen Yugang97cf96e2019-11-01 08:55:11 +080065static constexpr const char* oemNmiSourceIntf =
66 "xyz.openbmc_project.Chassis.Control.NMISource";
Chen Yugang39736d52019-07-12 16:24:33 +080067static constexpr const char* oemNmiSourceObjPath =
Chen Yugang97cf96e2019-11-01 08:55:11 +080068 "/xyz/openbmc_project/Chassis/Control/NMISource";
Chen Yugang39736d52019-07-12 16:24:33 +080069static constexpr const char* oemNmiBmcSourceObjPathProp = "BMCSource";
70static constexpr const char* oemNmiEnabledObjPathProp = "Enabled";
71
James Feist63efafa2019-07-24 12:39:21 -070072static constexpr const char* dimmOffsetFile = "/var/lib/ipmi/ipmi_dimms.json";
srikanta mondal2030d7c2020-05-03 17:25:25 +000073static constexpr const char* multiNodeObjPath =
74 "/xyz/openbmc_project/MultiNode/Status";
75static constexpr const char* multiNodeIntf =
76 "xyz.openbmc_project.Chassis.MultiNode";
James Feist63efafa2019-07-24 12:39:21 -070077
Chen Yugang39736d52019-07-12 16:24:33 +080078enum class NmiSource : uint8_t
79{
80 none = 0,
Chen Yugang97cf96e2019-11-01 08:55:11 +080081 frontPanelButton = 1,
82 watchdog = 2,
83 chassisCmd = 3,
84 memoryError = 4,
85 pciBusError = 5,
86 pch = 6,
87 chipset = 7,
Chen Yugang39736d52019-07-12 16:24:33 +080088};
89
Suryakanth Sekar822b0b42019-11-15 18:32:53 +053090enum class SpecialUserIndex : uint8_t
91{
92 rootUser = 0,
93 atScaleDebugUser = 1
94};
95
Richard Marian Thomaiyard801e462019-06-20 01:05:40 +053096static constexpr const char* restricionModeService =
97 "xyz.openbmc_project.RestrictionMode.Manager";
98static constexpr const char* restricionModeBasePath =
99 "/xyz/openbmc_project/control/security/restriction_mode";
100static constexpr const char* restricionModeIntf =
101 "xyz.openbmc_project.Control.Security.RestrictionMode";
102static constexpr const char* restricionModeProperty = "RestrictionMode";
103
104static constexpr const char* specialModeService =
105 "xyz.openbmc_project.SpecialMode";
106static constexpr const char* specialModeBasePath =
Richard Marian Thomaiyara7b74282019-09-22 21:53:14 +0530107 "/xyz/openbmc_project/security/special_mode";
Richard Marian Thomaiyard801e462019-06-20 01:05:40 +0530108static constexpr const char* specialModeIntf =
109 "xyz.openbmc_project.Security.SpecialMode";
110static constexpr const char* specialModeProperty = "SpecialMode";
111
112static constexpr const char* dBusPropertyIntf =
113 "org.freedesktop.DBus.Properties";
114static constexpr const char* dBusPropertyGetMethod = "Get";
115static constexpr const char* dBusPropertySetMethod = "Set";
116
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800117// return code: 0 successful
118int8_t getChassisSerialNumber(sdbusplus::bus::bus& bus, std::string& serial)
119{
120 std::string objpath = "/xyz/openbmc_project/FruDevice";
121 std::string intf = "xyz.openbmc_project.FruDeviceManager";
122 std::string service = getService(bus, intf, objpath);
123 ObjectValueTree valueTree = getManagedObjects(bus, service, "/");
124 if (valueTree.empty())
125 {
126 phosphor::logging::log<phosphor::logging::level::ERR>(
127 "No object implements interface",
128 phosphor::logging::entry("INTF=%s", intf.c_str()));
129 return -1;
130 }
131
Jason M. Bills64796042018-10-03 16:51:55 -0700132 for (const auto& item : valueTree)
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800133 {
134 auto interface = item.second.find("xyz.openbmc_project.FruDevice");
135 if (interface == item.second.end())
136 {
137 continue;
138 }
139
140 auto property = interface->second.find("CHASSIS_SERIAL_NUMBER");
141 if (property == interface->second.end())
142 {
143 continue;
144 }
145
146 try
147 {
148 Value variant = property->second;
Vernon Mauery8166c8d2019-05-23 11:22:30 -0700149 std::string& result = std::get<std::string>(variant);
Jason M. Bills64796042018-10-03 16:51:55 -0700150 if (result.size() > maxFRUStringLength)
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800151 {
152 phosphor::logging::log<phosphor::logging::level::ERR>(
153 "FRU serial number exceed maximum length");
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800154 return -1;
155 }
Jason M. Bills64796042018-10-03 16:51:55 -0700156 serial = result;
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800157 return 0;
158 }
Vernon Mauery8166c8d2019-05-23 11:22:30 -0700159 catch (std::bad_variant_access& e)
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800160 {
Jason M. Bills64796042018-10-03 16:51:55 -0700161 phosphor::logging::log<phosphor::logging::level::ERR>(e.what());
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800162 return -1;
163 }
164 }
165 return -1;
166}
Jason M. Bills64796042018-10-03 16:51:55 -0700167
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800168// Returns the Chassis Identifier (serial #)
169ipmi_ret_t ipmiOEMGetChassisIdentifier(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
170 ipmi_request_t request,
171 ipmi_response_t response,
Jason M. Bills64796042018-10-03 16:51:55 -0700172 ipmi_data_len_t dataLen,
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800173 ipmi_context_t context)
174{
175 std::string serial;
Jason M. Bills64796042018-10-03 16:51:55 -0700176 if (*dataLen != 0) // invalid request if there are extra parameters
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800177 {
Jason M. Bills64796042018-10-03 16:51:55 -0700178 *dataLen = 0;
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800179 return IPMI_CC_REQ_DATA_LEN_INVALID;
180 }
Vernon Mauery15419dd2019-05-24 09:40:30 -0700181 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
182 if (getChassisSerialNumber(*dbus, serial) == 0)
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800183 {
Jason M. Bills64796042018-10-03 16:51:55 -0700184 *dataLen = serial.size(); // length will never exceed response length
185 // as it is checked in getChassisSerialNumber
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800186 char* resp = static_cast<char*>(response);
Jason M. Bills64796042018-10-03 16:51:55 -0700187 serial.copy(resp, *dataLen);
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800188 return IPMI_CC_OK;
189 }
Jason M. Bills64796042018-10-03 16:51:55 -0700190 *dataLen = 0;
191 return IPMI_CC_RESPONSE_ERROR;
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800192}
193
194ipmi_ret_t ipmiOEMSetSystemGUID(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
195 ipmi_request_t request,
196 ipmi_response_t response,
Jason M. Bills64796042018-10-03 16:51:55 -0700197 ipmi_data_len_t dataLen, ipmi_context_t context)
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800198{
199 static constexpr size_t safeBufferLength = 50;
200 char buf[safeBufferLength] = {0};
201 GUIDData* Data = reinterpret_cast<GUIDData*>(request);
202
Jason M. Bills64796042018-10-03 16:51:55 -0700203 if (*dataLen != sizeof(GUIDData)) // 16bytes
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800204 {
Jason M. Bills64796042018-10-03 16:51:55 -0700205 *dataLen = 0;
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800206 return IPMI_CC_REQ_DATA_LEN_INVALID;
207 }
208
Jason M. Bills64796042018-10-03 16:51:55 -0700209 *dataLen = 0;
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800210
211 snprintf(
212 buf, safeBufferLength,
213 "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x",
214 Data->timeLow4, Data->timeLow3, Data->timeLow2, Data->timeLow1,
215 Data->timeMid2, Data->timeMid1, Data->timeHigh2, Data->timeHigh1,
216 Data->clock2, Data->clock1, Data->node6, Data->node5, Data->node4,
217 Data->node3, Data->node2, Data->node1);
218 // UUID is in RFC4122 format. Ex: 61a39523-78f2-11e5-9862-e6402cfc3223
219 std::string guid = buf;
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800220
221 std::string objpath = "/xyz/openbmc_project/control/host0/systemGUID";
222 std::string intf = "xyz.openbmc_project.Common.UUID";
Vernon Mauery15419dd2019-05-24 09:40:30 -0700223 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
224 std::string service = getService(*dbus, intf, objpath);
225 setDbusProperty(*dbus, service, objpath, intf, "UUID", guid);
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800226 return IPMI_CC_OK;
227}
228
Jason M. Billsb02bf092019-08-15 13:01:56 -0700229ipmi::RspType<> ipmiOEMDisableBMCSystemReset(bool disableResetOnSMI,
230 uint7_t reserved1)
231{
232 std::shared_ptr<sdbusplus::asio::connection> busp = getSdBus();
233
234 try
235 {
236 auto service =
237 ipmi::getService(*busp, bmcResetDisablesIntf, bmcResetDisablesPath);
238 ipmi::setDbusProperty(*busp, service, bmcResetDisablesPath,
239 bmcResetDisablesIntf, "ResetOnSMI",
240 !disableResetOnSMI);
241 }
242 catch (std::exception& e)
243 {
244 phosphor::logging::log<phosphor::logging::level::ERR>(
245 "Failed to set BMC reset disables",
246 phosphor::logging::entry("EXCEPTION=%s", e.what()));
247 return ipmi::responseUnspecifiedError();
248 }
249
250 return ipmi::responseSuccess();
251}
252
253ipmi::RspType<bool, // disableResetOnSMI
254 uint7_t // reserved
255 >
256 ipmiOEMGetBMCResetDisables()
257{
258 bool disableResetOnSMI = true;
259
260 std::shared_ptr<sdbusplus::asio::connection> busp = getSdBus();
261 try
262 {
263 auto service =
264 ipmi::getService(*busp, bmcResetDisablesIntf, bmcResetDisablesPath);
265 Value variant =
266 ipmi::getDbusProperty(*busp, service, bmcResetDisablesPath,
267 bmcResetDisablesIntf, "ResetOnSMI");
268 disableResetOnSMI = !std::get<bool>(variant);
269 }
270 catch (std::exception& e)
271 {
272 phosphor::logging::log<phosphor::logging::level::ERR>(
273 "Failed to get BMC reset disables",
274 phosphor::logging::entry("EXCEPTION=%s", e.what()));
275 return ipmi::responseUnspecifiedError();
276 }
277
278 return ipmi::responseSuccess(disableResetOnSMI, 0);
279}
280
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800281ipmi_ret_t ipmiOEMSetBIOSID(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
282 ipmi_request_t request, ipmi_response_t response,
283 ipmi_data_len_t dataLen, ipmi_context_t context)
284{
285 DeviceInfo* data = reinterpret_cast<DeviceInfo*>(request);
286
Jason M. Bills64796042018-10-03 16:51:55 -0700287 if ((*dataLen < 2) || (*dataLen != (1 + data->biosIDLength)))
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800288 {
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800289 *dataLen = 0;
290 return IPMI_CC_REQ_DATA_LEN_INVALID;
291 }
Jason M. Bills64796042018-10-03 16:51:55 -0700292 std::string idString((char*)data->biosId, data->biosIDLength);
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800293
Vernon Mauery15419dd2019-05-24 09:40:30 -0700294 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
Chalapathi899bfd12020-04-15 15:07:02 +0000295 std::string service = getService(*dbus, biosVersionIntf, biosActiveObjPath);
296 setDbusProperty(*dbus, service, biosActiveObjPath, biosVersionIntf,
Yong Li2742b852019-12-16 14:55:11 +0800297 biosVersionProp, idString);
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800298 uint8_t* bytesWritten = static_cast<uint8_t*>(response);
299 *bytesWritten =
Jason M. Bills64796042018-10-03 16:51:55 -0700300 data->biosIDLength; // how many bytes are written into storage
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800301 *dataLen = 1;
302 return IPMI_CC_OK;
303}
304
AppaRao Pulie99e7ed2020-01-17 12:27:10 +0530305bool getSwVerInfo(ipmi::Context::ptr ctx, uint8_t& bmcMajor, uint8_t& bmcMinor,
306 uint8_t& meMajor, uint8_t& meMinor)
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800307{
Chen Yugang7a04f3a2019-10-08 11:12:35 +0800308 // step 1 : get BMC Major and Minor numbers from its DBUS property
AppaRao Pulie99e7ed2020-01-17 12:27:10 +0530309 std::string bmcVersion;
310 if (getActiveSoftwareVersionInfo(ctx, versionPurposeBMC, bmcVersion))
Chen Yugang7a04f3a2019-10-08 11:12:35 +0800311 {
312 return false;
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800313 }
314
AppaRao Pulie99e7ed2020-01-17 12:27:10 +0530315 std::optional<MetaRevision> rev = convertIntelVersion(bmcVersion);
Chen Yugang7a04f3a2019-10-08 11:12:35 +0800316 if (rev.has_value())
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800317 {
Chen Yugang7a04f3a2019-10-08 11:12:35 +0800318 MetaRevision revision = rev.value();
319 bmcMajor = revision.major;
320
321 revision.minor = (revision.minor > 99 ? 99 : revision.minor);
322 bmcMinor = revision.minor % 10 + (revision.minor / 10) * 16;
323 }
324
325 // step 2 : get ME Major and Minor numbers from its DBUS property
AppaRao Puli32825a22020-01-17 15:52:41 +0530326 std::string meVersion;
327 if (getActiveSoftwareVersionInfo(ctx, versionPurposeME, meVersion))
Chen Yugang7a04f3a2019-10-08 11:12:35 +0800328 {
Chen Yugang7a04f3a2019-10-08 11:12:35 +0800329 return false;
330 }
AppaRao Puli32825a22020-01-17 15:52:41 +0530331 std::regex pattern1("(\\d+?).(\\d+?).(\\d+?).(\\d+?).(\\d+?)");
332 constexpr size_t matchedPhosphor = 6;
333 std::smatch results;
334 if (std::regex_match(meVersion, results, pattern1))
335 {
336 if (results.size() == matchedPhosphor)
337 {
338 meMajor = static_cast<uint8_t>(std::stoi(results[1]));
339 meMinor = static_cast<uint8_t>(std::stoi(results[2]));
340 }
341 }
Chen Yugang7a04f3a2019-10-08 11:12:35 +0800342 return true;
343}
344
345ipmi::RspType<
346 std::variant<std::string,
347 std::tuple<uint8_t, std::array<uint8_t, 2>,
348 std::array<uint8_t, 2>, std::array<uint8_t, 2>,
349 std::array<uint8_t, 2>, std::array<uint8_t, 2>>,
350 std::tuple<uint8_t, std::array<uint8_t, 2>>>>
AppaRao Pulie99e7ed2020-01-17 12:27:10 +0530351 ipmiOEMGetDeviceInfo(ipmi::Context::ptr ctx, uint8_t entityType,
352 std::optional<uint8_t> countToRead,
AppaRao Pulid46cb422020-01-21 18:40:21 +0530353 std::optional<uint8_t> offset)
Chen Yugang7a04f3a2019-10-08 11:12:35 +0800354{
Chen Yugang7a04f3a2019-10-08 11:12:35 +0800355 if (entityType > static_cast<uint8_t>(OEMDevEntityType::sdrVer))
356 {
357 return ipmi::responseInvalidFieldRequest();
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800358 }
359
360 // handle OEM command items
Chen Yugang7a04f3a2019-10-08 11:12:35 +0800361 switch (OEMDevEntityType(entityType))
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800362 {
363 case OEMDevEntityType::biosId:
364 {
AppaRao Pulid46cb422020-01-21 18:40:21 +0530365 // Byte 2&3, Only used with selecting BIOS
366 if (!countToRead || !offset)
367 {
368 return ipmi::responseReqDataLenInvalid();
369 }
370
Vernon Mauery15419dd2019-05-24 09:40:30 -0700371 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
Yong Li2742b852019-12-16 14:55:11 +0800372 std::string service =
Chalapathi899bfd12020-04-15 15:07:02 +0000373 getService(*dbus, biosVersionIntf, biosActiveObjPath);
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800374 try
375 {
Yong Li2742b852019-12-16 14:55:11 +0800376 Value variant =
Chalapathi899bfd12020-04-15 15:07:02 +0000377 getDbusProperty(*dbus, service, biosActiveObjPath,
Yong Li2742b852019-12-16 14:55:11 +0800378 biosVersionIntf, biosVersionProp);
Vernon Mauery8166c8d2019-05-23 11:22:30 -0700379 std::string& idString = std::get<std::string>(variant);
AppaRao Pulid46cb422020-01-21 18:40:21 +0530380 if (*offset >= idString.size())
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800381 {
Chen Yugang7a04f3a2019-10-08 11:12:35 +0800382 return ipmi::responseParmOutOfRange();
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800383 }
Jason M. Bills64796042018-10-03 16:51:55 -0700384 size_t length = 0;
AppaRao Pulid46cb422020-01-21 18:40:21 +0530385 if (*countToRead > (idString.size() - *offset))
Jason M. Bills64796042018-10-03 16:51:55 -0700386 {
AppaRao Pulid46cb422020-01-21 18:40:21 +0530387 length = idString.size() - *offset;
Jason M. Bills64796042018-10-03 16:51:55 -0700388 }
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800389 else
390 {
AppaRao Pulid46cb422020-01-21 18:40:21 +0530391 length = *countToRead;
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800392 }
Chen Yugang7a04f3a2019-10-08 11:12:35 +0800393
394 std::string readBuf = {0};
395 readBuf.resize(length);
AppaRao Pulid46cb422020-01-21 18:40:21 +0530396 std::copy_n(idString.begin() + *offset, length,
Chen Yugang7a04f3a2019-10-08 11:12:35 +0800397 (readBuf.begin()));
398 return ipmi::responseSuccess(readBuf);
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800399 }
Vernon Mauery8166c8d2019-05-23 11:22:30 -0700400 catch (std::bad_variant_access& e)
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800401 {
Chen Yugang7a04f3a2019-10-08 11:12:35 +0800402 return ipmi::responseUnspecifiedError();
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800403 }
404 }
405 break;
406
407 case OEMDevEntityType::devVer:
Chen Yugang7a04f3a2019-10-08 11:12:35 +0800408 {
AppaRao Pulid46cb422020-01-21 18:40:21 +0530409 // Byte 2&3, Only used with selecting BIOS
410 if (countToRead || offset)
411 {
412 return ipmi::responseReqDataLenInvalid();
413 }
414
Chen Yugang7a04f3a2019-10-08 11:12:35 +0800415 constexpr const size_t verLen = 2;
416 constexpr const size_t verTotalLen = 10;
417 std::array<uint8_t, verLen> bmcBuf = {0xff, 0xff};
418 std::array<uint8_t, verLen> hsc0Buf = {0xff, 0xff};
419 std::array<uint8_t, verLen> hsc1Buf = {0xff, 0xff};
420 std::array<uint8_t, verLen> meBuf = {0xff, 0xff};
421 std::array<uint8_t, verLen> hsc2Buf = {0xff, 0xff};
422 // data0/1: BMC version number; data6/7: ME version number
423 // the others: HSC0/1/2 version number, not avaible.
AppaRao Pulie99e7ed2020-01-17 12:27:10 +0530424 if (!getSwVerInfo(ctx, bmcBuf[0], bmcBuf[1], meBuf[0], meBuf[1]))
Chen Yugang7a04f3a2019-10-08 11:12:35 +0800425 {
426 return ipmi::responseUnspecifiedError();
427 }
428 return ipmi::responseSuccess(
429 std::tuple<
430 uint8_t, std::array<uint8_t, verLen>,
431 std::array<uint8_t, verLen>, std::array<uint8_t, verLen>,
432 std::array<uint8_t, verLen>, std::array<uint8_t, verLen>>{
433 verTotalLen, bmcBuf, hsc0Buf, hsc1Buf, meBuf, hsc2Buf});
434 }
435 break;
436
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800437 case OEMDevEntityType::sdrVer:
Chen Yugang7a04f3a2019-10-08 11:12:35 +0800438 {
AppaRao Pulid46cb422020-01-21 18:40:21 +0530439 // Byte 2&3, Only used with selecting BIOS
440 if (countToRead || offset)
441 {
442 return ipmi::responseReqDataLenInvalid();
443 }
444
Chen Yugang7a04f3a2019-10-08 11:12:35 +0800445 constexpr const size_t sdrLen = 2;
446 std::array<uint8_t, sdrLen> readBuf = {0x01, 0x0};
447 return ipmi::responseSuccess(
448 std::tuple<uint8_t, std::array<uint8_t, sdrLen>>{sdrLen,
449 readBuf});
450 }
451 break;
452
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800453 default:
Chen Yugang7a04f3a2019-10-08 11:12:35 +0800454 return ipmi::responseInvalidFieldRequest();
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800455 }
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800456}
457
458ipmi_ret_t ipmiOEMGetAICFRU(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
459 ipmi_request_t request, ipmi_response_t response,
460 ipmi_data_len_t dataLen, ipmi_context_t context)
461{
462 if (*dataLen != 0)
463 {
Jason M. Bills64796042018-10-03 16:51:55 -0700464 *dataLen = 0;
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800465 return IPMI_CC_REQ_DATA_LEN_INVALID;
466 }
467
468 *dataLen = 1;
469 uint8_t* res = reinterpret_cast<uint8_t*>(response);
470 // temporary fix. We don't support AIC FRU now. Just tell BIOS that no
471 // AIC is available so that BIOS will not timeout repeatly which leads to
472 // slow booting.
473 *res = 0; // Byte1=Count of SlotPosition/FruID records.
474 return IPMI_CC_OK;
475}
476
Jason M. Bills64796042018-10-03 16:51:55 -0700477ipmi_ret_t ipmiOEMGetPowerRestoreDelay(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
478 ipmi_request_t request,
479 ipmi_response_t response,
480 ipmi_data_len_t dataLen,
481 ipmi_context_t context)
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800482{
Jason M. Bills64796042018-10-03 16:51:55 -0700483 GetPowerRestoreDelayRes* resp =
484 reinterpret_cast<GetPowerRestoreDelayRes*>(response);
485
486 if (*dataLen != 0)
487 {
488 *dataLen = 0;
489 return IPMI_CC_REQ_DATA_LEN_INVALID;
490 }
491
Vernon Mauery15419dd2019-05-24 09:40:30 -0700492 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
Jason M. Bills64796042018-10-03 16:51:55 -0700493 std::string service =
Vernon Mauery15419dd2019-05-24 09:40:30 -0700494 getService(*dbus, powerRestoreDelayIntf, powerRestoreDelayObjPath);
Jason M. Bills64796042018-10-03 16:51:55 -0700495 Value variant =
Vernon Mauery15419dd2019-05-24 09:40:30 -0700496 getDbusProperty(*dbus, service, powerRestoreDelayObjPath,
Jason M. Bills64796042018-10-03 16:51:55 -0700497 powerRestoreDelayIntf, powerRestoreDelayProp);
498
Vernon Mauery8166c8d2019-05-23 11:22:30 -0700499 uint16_t delay = std::get<uint16_t>(variant);
Jason M. Bills64796042018-10-03 16:51:55 -0700500 resp->byteLSB = delay;
501 resp->byteMSB = delay >> 8;
502
503 *dataLen = sizeof(GetPowerRestoreDelayRes);
504
505 return IPMI_CC_OK;
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800506}
507
Jia, Chunhuicc49b542019-03-20 15:41:07 +0800508static uint8_t bcdToDec(uint8_t val)
509{
510 return ((val / 16 * 10) + (val % 16));
511}
512
513// Allows an update utility or system BIOS to send the status of an embedded
514// firmware update attempt to the BMC. After received, BMC will create a logging
515// record.
516ipmi::RspType<> ipmiOEMSendEmbeddedFwUpdStatus(uint8_t status, uint8_t target,
517 uint8_t majorRevision,
518 uint8_t minorRevision,
519 uint32_t auxInfo)
520{
521 std::string firmware;
Jason M. Billsdc249272019-04-03 09:58:40 -0700522 int instance = (target & targetInstanceMask) >> targetInstanceShift;
Jia, Chunhuicc49b542019-03-20 15:41:07 +0800523 target = (target & selEvtTargetMask) >> selEvtTargetShift;
524
525 /* make sure the status is 0, 1, or 2 as per the spec */
526 if (status > 2)
527 {
528 return ipmi::response(ipmi::ccInvalidFieldRequest);
529 }
Jason M. Billsdc249272019-04-03 09:58:40 -0700530 /* make sure the target is 0, 1, 2, or 4 as per the spec */
531 if (target > 4 || target == 3)
532 {
533 return ipmi::response(ipmi::ccInvalidFieldRequest);
534 }
Jia, Chunhuicc49b542019-03-20 15:41:07 +0800535 /*orignal OEM command is to record OEM SEL.
536 But openbmc does not support OEM SEL, so we redirect it to redfish event
537 logging. */
538 std::string buildInfo;
539 std::string action;
540 switch (FWUpdateTarget(target))
541 {
542 case FWUpdateTarget::targetBMC:
543 firmware = "BMC";
Jason M. Billsdc249272019-04-03 09:58:40 -0700544 buildInfo = "major: " + std::to_string(majorRevision) + " minor: " +
Jia, Chunhuicc49b542019-03-20 15:41:07 +0800545 std::to_string(bcdToDec(minorRevision)) + // BCD encoded
546 " BuildID: " + std::to_string(auxInfo);
547 buildInfo += std::to_string(auxInfo);
548 break;
549 case FWUpdateTarget::targetBIOS:
550 firmware = "BIOS";
551 buildInfo =
Jason M. Billsdc249272019-04-03 09:58:40 -0700552 "major: " +
Jia, Chunhuicc49b542019-03-20 15:41:07 +0800553 std::to_string(bcdToDec(majorRevision)) + // BCD encoded
554 " minor: " +
555 std::to_string(bcdToDec(minorRevision)) + // BCD encoded
556 " ReleaseNumber: " + // ASCII encoded
557 std::to_string(static_cast<uint8_t>(auxInfo >> 0) - '0') +
558 std::to_string(static_cast<uint8_t>(auxInfo >> 8) - '0') +
559 std::to_string(static_cast<uint8_t>(auxInfo >> 16) - '0') +
560 std::to_string(static_cast<uint8_t>(auxInfo >> 24) - '0');
561 break;
562 case FWUpdateTarget::targetME:
563 firmware = "ME";
564 buildInfo =
Jason M. Billsdc249272019-04-03 09:58:40 -0700565 "major: " + std::to_string(majorRevision) + " minor1: " +
Jia, Chunhuicc49b542019-03-20 15:41:07 +0800566 std::to_string(bcdToDec(minorRevision)) + // BCD encoded
567 " minor2: " +
568 std::to_string(bcdToDec(static_cast<uint8_t>(auxInfo >> 0))) +
569 " build1: " +
570 std::to_string(bcdToDec(static_cast<uint8_t>(auxInfo >> 8))) +
571 " build2: " +
572 std::to_string(bcdToDec(static_cast<uint8_t>(auxInfo >> 16)));
573 break;
574 case FWUpdateTarget::targetOEMEWS:
575 firmware = "EWS";
Jason M. Billsdc249272019-04-03 09:58:40 -0700576 buildInfo = "major: " + std::to_string(majorRevision) + " minor: " +
Jia, Chunhuicc49b542019-03-20 15:41:07 +0800577 std::to_string(bcdToDec(minorRevision)) + // BCD encoded
578 " BuildID: " + std::to_string(auxInfo);
579 break;
580 }
581
Jason M. Billsdc249272019-04-03 09:58:40 -0700582 static const std::string openBMCMessageRegistryVersion("0.1");
583 std::string redfishMsgID = "OpenBMC." + openBMCMessageRegistryVersion;
584
Jia, Chunhuicc49b542019-03-20 15:41:07 +0800585 switch (status)
586 {
587 case 0x0:
588 action = "update started";
Jason M. Billsdc249272019-04-03 09:58:40 -0700589 redfishMsgID += ".FirmwareUpdateStarted";
Jia, Chunhuicc49b542019-03-20 15:41:07 +0800590 break;
591 case 0x1:
592 action = "update completed successfully";
Jason M. Billsdc249272019-04-03 09:58:40 -0700593 redfishMsgID += ".FirmwareUpdateCompleted";
Jia, Chunhuicc49b542019-03-20 15:41:07 +0800594 break;
595 case 0x2:
596 action = "update failure";
Jason M. Billsdc249272019-04-03 09:58:40 -0700597 redfishMsgID += ".FirmwareUpdateFailed";
Jia, Chunhuicc49b542019-03-20 15:41:07 +0800598 break;
599 default:
600 action = "unknown";
601 break;
602 }
603
Jason M. Billsdc249272019-04-03 09:58:40 -0700604 std::string firmwareInstanceStr =
605 firmware + " instance: " + std::to_string(instance);
606 std::string message("[firmware update] " + firmwareInstanceStr +
607 " status: <" + action + "> " + buildInfo);
Jia, Chunhuicc49b542019-03-20 15:41:07 +0800608
609 sd_journal_send("MESSAGE=%s", message.c_str(), "PRIORITY=%i", LOG_INFO,
Jason M. Billsdc249272019-04-03 09:58:40 -0700610 "REDFISH_MESSAGE_ID=%s", redfishMsgID.c_str(),
611 "REDFISH_MESSAGE_ARGS=%s,%s", firmwareInstanceStr.c_str(),
612 buildInfo.c_str(), NULL);
Jia, Chunhuicc49b542019-03-20 15:41:07 +0800613 return ipmi::responseSuccess();
614}
615
Rajashekar Gade Reddy2b664d52020-03-23 22:09:00 +0530616ipmi::RspType<uint8_t, std::vector<uint8_t>>
617 ipmiOEMSlotIpmb(ipmi::Context::ptr ctx, uint6_t reserved1,
618 uint2_t slotNumber, uint3_t baseBoardSlotNum,
619 uint3_t riserSlotNum, uint2_t reserved2, uint8_t slaveAddr,
620 uint8_t netFn, uint8_t cmd,
621 std::optional<std::vector<uint8_t>> writeData)
622{
623 if (reserved1 || reserved2)
624 {
625 return ipmi::responseInvalidFieldRequest();
626 }
627
628 boost::system::error_code ec;
629 using ipmbResponse = std::tuple<int, uint8_t, uint8_t, uint8_t, uint8_t,
630 std::vector<uint8_t>>;
631 ipmbResponse res = ctx->bus->yield_method_call<ipmbResponse>(
632 ctx->yield, ec, "xyz.openbmc_project.Ipmi.Channel.Ipmb",
633 "/xyz/openbmc_project/Ipmi/Channel/Ipmb", "org.openbmc.Ipmb",
634 "SlotIpmbRequest", static_cast<uint8_t>(slotNumber),
635 static_cast<uint8_t>(baseBoardSlotNum), slaveAddr, netFn, cmd,
636 *writeData);
637 if (ec)
638 {
639 phosphor::logging::log<phosphor::logging::level::ERR>(
640 "Failed to call dbus method SlotIpmbRequest");
641 return ipmi::responseUnspecifiedError();
642 }
643
644 std::vector<uint8_t> dataReceived(0);
645 int status = -1;
646 uint8_t resNetFn = 0, resLun = 0, resCmd = 0, cc = 0;
647
648 std::tie(status, resNetFn, resLun, resCmd, cc, dataReceived) = res;
649
650 if (status)
651 {
652 phosphor::logging::log<phosphor::logging::level::ERR>(
653 "Failed to get response from SlotIpmbRequest");
654 return ipmi::responseResponseError();
655 }
656 return ipmi::responseSuccess(cc, dataReceived);
657}
658
Jason M. Bills64796042018-10-03 16:51:55 -0700659ipmi_ret_t ipmiOEMSetPowerRestoreDelay(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
660 ipmi_request_t request,
661 ipmi_response_t response,
662 ipmi_data_len_t dataLen,
663 ipmi_context_t context)
664{
665 SetPowerRestoreDelayReq* data =
666 reinterpret_cast<SetPowerRestoreDelayReq*>(request);
667 uint16_t delay = 0;
668
669 if (*dataLen != sizeof(SetPowerRestoreDelayReq))
670 {
671 *dataLen = 0;
672 return IPMI_CC_REQ_DATA_LEN_INVALID;
673 }
674 delay = data->byteMSB;
675 delay = (delay << 8) | data->byteLSB;
Vernon Mauery15419dd2019-05-24 09:40:30 -0700676 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
Jason M. Bills64796042018-10-03 16:51:55 -0700677 std::string service =
Vernon Mauery15419dd2019-05-24 09:40:30 -0700678 getService(*dbus, powerRestoreDelayIntf, powerRestoreDelayObjPath);
679 setDbusProperty(*dbus, service, powerRestoreDelayObjPath,
Jason M. Bills64796042018-10-03 16:51:55 -0700680 powerRestoreDelayIntf, powerRestoreDelayProp, delay);
681 *dataLen = 0;
682
683 return IPMI_CC_OK;
684}
685
Jason M. Bills42bd9c82019-06-28 16:39:34 -0700686static bool cpuPresent(const std::string& cpuName)
Jason M. Bills64796042018-10-03 16:51:55 -0700687{
Jason M. Bills42bd9c82019-06-28 16:39:34 -0700688 static constexpr const char* cpuPresencePathPrefix =
689 "/xyz/openbmc_project/inventory/system/chassis/motherboard/";
690 static constexpr const char* cpuPresenceIntf =
691 "xyz.openbmc_project.Inventory.Item";
692 std::string cpuPresencePath = cpuPresencePathPrefix + cpuName;
693 std::shared_ptr<sdbusplus::asio::connection> busp = getSdBus();
694 try
Jason M. Bills64796042018-10-03 16:51:55 -0700695 {
Jason M. Bills42bd9c82019-06-28 16:39:34 -0700696 auto service =
697 ipmi::getService(*busp, cpuPresenceIntf, cpuPresencePath);
698
699 ipmi::Value result = ipmi::getDbusProperty(
700 *busp, service, cpuPresencePath, cpuPresenceIntf, "Present");
701 return std::get<bool>(result);
702 }
703 catch (const std::exception& e)
704 {
705 phosphor::logging::log<phosphor::logging::level::INFO>(
706 "Cannot find processor presence",
707 phosphor::logging::entry("NAME=%s", cpuName.c_str()));
708 return false;
709 }
710}
711
712ipmi::RspType<bool, // CATERR Reset Enabled
713 bool, // ERR2 Reset Enabled
714 uint6_t, // reserved
715 uint8_t, // reserved, returns 0x3F
716 uint6_t, // CPU1 CATERR Count
717 uint2_t, // CPU1 Status
718 uint6_t, // CPU2 CATERR Count
719 uint2_t, // CPU2 Status
720 uint6_t, // CPU3 CATERR Count
721 uint2_t, // CPU3 Status
722 uint6_t, // CPU4 CATERR Count
723 uint2_t, // CPU4 Status
724 uint8_t // Crashdump Count
725 >
726 ipmiOEMGetProcessorErrConfig()
727{
728 bool resetOnCATERR = false;
729 bool resetOnERR2 = false;
730 uint6_t cpu1CATERRCount = 0;
731 uint6_t cpu2CATERRCount = 0;
732 uint6_t cpu3CATERRCount = 0;
733 uint6_t cpu4CATERRCount = 0;
734 uint8_t crashdumpCount = 0;
735 uint2_t cpu1Status =
736 cpuPresent("CPU_1") ? CPUStatus::enabled : CPUStatus::notPresent;
737 uint2_t cpu2Status =
738 cpuPresent("CPU_2") ? CPUStatus::enabled : CPUStatus::notPresent;
739 uint2_t cpu3Status =
740 cpuPresent("CPU_3") ? CPUStatus::enabled : CPUStatus::notPresent;
741 uint2_t cpu4Status =
742 cpuPresent("CPU_4") ? CPUStatus::enabled : CPUStatus::notPresent;
743
744 std::shared_ptr<sdbusplus::asio::connection> busp = getSdBus();
745 try
746 {
747 auto service = ipmi::getService(*busp, processorErrConfigIntf,
748 processorErrConfigObjPath);
749
750 ipmi::PropertyMap result = ipmi::getAllDbusProperties(
751 *busp, service, processorErrConfigObjPath, processorErrConfigIntf);
752 resetOnCATERR = std::get<bool>(result.at("ResetOnCATERR"));
753 resetOnERR2 = std::get<bool>(result.at("ResetOnERR2"));
754 cpu1CATERRCount = std::get<uint8_t>(result.at("ErrorCountCPU1"));
755 cpu2CATERRCount = std::get<uint8_t>(result.at("ErrorCountCPU2"));
756 cpu3CATERRCount = std::get<uint8_t>(result.at("ErrorCountCPU3"));
757 cpu4CATERRCount = std::get<uint8_t>(result.at("ErrorCountCPU4"));
758 crashdumpCount = std::get<uint8_t>(result.at("CrashdumpCount"));
759 }
760 catch (const std::exception& e)
761 {
762 phosphor::logging::log<phosphor::logging::level::ERR>(
763 "Failed to fetch processor error config",
764 phosphor::logging::entry("ERROR=%s", e.what()));
765 return ipmi::responseUnspecifiedError();
Jason M. Bills64796042018-10-03 16:51:55 -0700766 }
767
Jason M. Bills42bd9c82019-06-28 16:39:34 -0700768 return ipmi::responseSuccess(resetOnCATERR, resetOnERR2, 0, 0x3F,
769 cpu1CATERRCount, cpu1Status, cpu2CATERRCount,
770 cpu2Status, cpu3CATERRCount, cpu3Status,
771 cpu4CATERRCount, cpu4Status, crashdumpCount);
772}
Jason M. Bills64796042018-10-03 16:51:55 -0700773
Jason M. Bills42bd9c82019-06-28 16:39:34 -0700774ipmi::RspType<> ipmiOEMSetProcessorErrConfig(
775 bool resetOnCATERR, bool resetOnERR2, uint6_t reserved1, uint8_t reserved2,
776 std::optional<bool> clearCPUErrorCount,
777 std::optional<bool> clearCrashdumpCount, std::optional<uint6_t> reserved3)
778{
779 std::shared_ptr<sdbusplus::asio::connection> busp = getSdBus();
Jason M. Bills64796042018-10-03 16:51:55 -0700780
781 try
782 {
Jason M. Bills42bd9c82019-06-28 16:39:34 -0700783 auto service = ipmi::getService(*busp, processorErrConfigIntf,
784 processorErrConfigObjPath);
785 ipmi::setDbusProperty(*busp, service, processorErrConfigObjPath,
786 processorErrConfigIntf, "ResetOnCATERR",
787 resetOnCATERR);
788 ipmi::setDbusProperty(*busp, service, processorErrConfigObjPath,
789 processorErrConfigIntf, "ResetOnERR2",
790 resetOnERR2);
791 if (clearCPUErrorCount.value_or(false))
792 {
793 ipmi::setDbusProperty(*busp, service, processorErrConfigObjPath,
Jason M. Billsd3e19932019-08-15 12:39:03 -0700794 processorErrConfigIntf, "ErrorCountCPU1",
795 static_cast<uint8_t>(0));
Jason M. Bills42bd9c82019-06-28 16:39:34 -0700796 ipmi::setDbusProperty(*busp, service, processorErrConfigObjPath,
Jason M. Billsd3e19932019-08-15 12:39:03 -0700797 processorErrConfigIntf, "ErrorCountCPU2",
798 static_cast<uint8_t>(0));
799 ipmi::setDbusProperty(*busp, service, processorErrConfigObjPath,
800 processorErrConfigIntf, "ErrorCountCPU3",
801 static_cast<uint8_t>(0));
802 ipmi::setDbusProperty(*busp, service, processorErrConfigObjPath,
803 processorErrConfigIntf, "ErrorCountCPU4",
804 static_cast<uint8_t>(0));
Jason M. Bills42bd9c82019-06-28 16:39:34 -0700805 }
806 if (clearCrashdumpCount.value_or(false))
807 {
808 ipmi::setDbusProperty(*busp, service, processorErrConfigObjPath,
Jason M. Billsd3e19932019-08-15 12:39:03 -0700809 processorErrConfigIntf, "CrashdumpCount",
810 static_cast<uint8_t>(0));
Jason M. Bills42bd9c82019-06-28 16:39:34 -0700811 }
Jason M. Bills64796042018-10-03 16:51:55 -0700812 }
Jason M. Bills42bd9c82019-06-28 16:39:34 -0700813 catch (std::exception& e)
Jason M. Bills64796042018-10-03 16:51:55 -0700814 {
Kuiying Wangbc546672018-11-23 15:41:05 +0800815 phosphor::logging::log<phosphor::logging::level::ERR>(
Jason M. Bills42bd9c82019-06-28 16:39:34 -0700816 "Failed to set processor error config",
817 phosphor::logging::entry("EXCEPTION=%s", e.what()));
818 return ipmi::responseUnspecifiedError();
Jason M. Bills64796042018-10-03 16:51:55 -0700819 }
820
Jason M. Bills42bd9c82019-06-28 16:39:34 -0700821 return ipmi::responseSuccess();
Jason M. Bills64796042018-10-03 16:51:55 -0700822}
823
Yong Li703922d2018-11-06 13:25:31 +0800824ipmi_ret_t ipmiOEMGetShutdownPolicy(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
825 ipmi_request_t request,
826 ipmi_response_t response,
827 ipmi_data_len_t dataLen,
828 ipmi_context_t context)
829{
830 GetOEMShutdownPolicyRes* resp =
831 reinterpret_cast<GetOEMShutdownPolicyRes*>(response);
832
833 if (*dataLen != 0)
834 {
835 phosphor::logging::log<phosphor::logging::level::ERR>(
Kuiying Wang45f04982018-12-26 09:23:08 +0800836 "oem_get_shutdown_policy: invalid input len!");
Yong Li703922d2018-11-06 13:25:31 +0800837 *dataLen = 0;
838 return IPMI_CC_REQ_DATA_LEN_INVALID;
839 }
840
841 *dataLen = 0;
842
843 try
844 {
Vernon Mauery15419dd2019-05-24 09:40:30 -0700845 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
Yong Li703922d2018-11-06 13:25:31 +0800846 std::string service =
Vernon Mauery15419dd2019-05-24 09:40:30 -0700847 getService(*dbus, oemShutdownPolicyIntf, oemShutdownPolicyObjPath);
848 Value variant = getDbusProperty(
849 *dbus, service, oemShutdownPolicyObjPath, oemShutdownPolicyIntf,
850 oemShutdownPolicyObjPathProp);
Yong Li0669d192019-05-06 14:01:46 +0800851
852 if (sdbusplus::com::intel::Control::server::OCOTShutdownPolicy::
853 convertPolicyFromString(std::get<std::string>(variant)) ==
854 sdbusplus::com::intel::Control::server::OCOTShutdownPolicy::Policy::
855 NoShutdownOnOCOT)
856 {
857 resp->policy = 0;
858 }
859 else if (sdbusplus::com::intel::Control::server::OCOTShutdownPolicy::
860 convertPolicyFromString(std::get<std::string>(variant)) ==
861 sdbusplus::com::intel::Control::server::OCOTShutdownPolicy::
862 Policy::ShutdownOnOCOT)
863 {
864 resp->policy = 1;
865 }
866 else
867 {
868 phosphor::logging::log<phosphor::logging::level::ERR>(
869 "oem_set_shutdown_policy: invalid property!",
870 phosphor::logging::entry(
871 "PROP=%s", std::get<std::string>(variant).c_str()));
872 return IPMI_CC_UNSPECIFIED_ERROR;
873 }
Yong Li703922d2018-11-06 13:25:31 +0800874 // TODO needs to check if it is multi-node products,
875 // policy is only supported on node 3/4
876 resp->policySupport = shutdownPolicySupported;
877 }
878 catch (sdbusplus::exception_t& e)
879 {
880 phosphor::logging::log<phosphor::logging::level::ERR>(e.description());
881 return IPMI_CC_UNSPECIFIED_ERROR;
882 }
883
884 *dataLen = sizeof(GetOEMShutdownPolicyRes);
885 return IPMI_CC_OK;
886}
887
888ipmi_ret_t ipmiOEMSetShutdownPolicy(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
889 ipmi_request_t request,
890 ipmi_response_t response,
891 ipmi_data_len_t dataLen,
892 ipmi_context_t context)
893{
894 uint8_t* req = reinterpret_cast<uint8_t*>(request);
Yong Li0669d192019-05-06 14:01:46 +0800895 sdbusplus::com::intel::Control::server::OCOTShutdownPolicy::Policy policy =
896 sdbusplus::com::intel::Control::server::OCOTShutdownPolicy::Policy::
897 NoShutdownOnOCOT;
Yong Li703922d2018-11-06 13:25:31 +0800898
899 // TODO needs to check if it is multi-node products,
900 // policy is only supported on node 3/4
901 if (*dataLen != 1)
902 {
903 phosphor::logging::log<phosphor::logging::level::ERR>(
904 "oem_set_shutdown_policy: invalid input len!");
905 *dataLen = 0;
906 return IPMI_CC_REQ_DATA_LEN_INVALID;
907 }
908
909 *dataLen = 0;
910 if ((*req != noShutdownOnOCOT) && (*req != shutdownOnOCOT))
911 {
912 phosphor::logging::log<phosphor::logging::level::ERR>(
913 "oem_set_shutdown_policy: invalid input!");
914 return IPMI_CC_INVALID_FIELD_REQUEST;
915 }
916
Yong Li0669d192019-05-06 14:01:46 +0800917 if (*req == noShutdownOnOCOT)
918 {
919 policy = sdbusplus::com::intel::Control::server::OCOTShutdownPolicy::
920 Policy::NoShutdownOnOCOT;
921 }
922 else
923 {
924 policy = sdbusplus::com::intel::Control::server::OCOTShutdownPolicy::
925 Policy::ShutdownOnOCOT;
926 }
927
Yong Li703922d2018-11-06 13:25:31 +0800928 try
929 {
Vernon Mauery15419dd2019-05-24 09:40:30 -0700930 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
Yong Li703922d2018-11-06 13:25:31 +0800931 std::string service =
Vernon Mauery15419dd2019-05-24 09:40:30 -0700932 getService(*dbus, oemShutdownPolicyIntf, oemShutdownPolicyObjPath);
Yong Li0669d192019-05-06 14:01:46 +0800933 setDbusProperty(
Vernon Mauery15419dd2019-05-24 09:40:30 -0700934 *dbus, service, oemShutdownPolicyObjPath, oemShutdownPolicyIntf,
Yong Li0669d192019-05-06 14:01:46 +0800935 oemShutdownPolicyObjPathProp,
936 sdbusplus::com::intel::Control::server::convertForMessage(policy));
Yong Li703922d2018-11-06 13:25:31 +0800937 }
938 catch (sdbusplus::exception_t& e)
939 {
940 phosphor::logging::log<phosphor::logging::level::ERR>(e.description());
941 return IPMI_CC_UNSPECIFIED_ERROR;
942 }
943
944 return IPMI_CC_OK;
945}
946
Suryakanth Sekard509eb92018-11-15 17:44:11 +0530947/** @brief implementation for check the DHCP or not in IPv4
948 * @param[in] Channel - Channel number
949 * @returns true or false.
950 */
951static bool isDHCPEnabled(uint8_t Channel)
952{
953 try
954 {
955 auto ethdevice = getChannelName(Channel);
956 if (ethdevice.empty())
957 {
958 return false;
959 }
960 auto ethIP = ethdevice + "/ipv4";
Vernon Mauery15419dd2019-05-24 09:40:30 -0700961 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
Suryakanth Sekard509eb92018-11-15 17:44:11 +0530962 auto ethernetObj =
Vernon Mauery15419dd2019-05-24 09:40:30 -0700963 getDbusObject(*dbus, networkIPIntf, networkRoot, ethIP);
964 auto value = getDbusProperty(*dbus, networkService, ethernetObj.first,
Suryakanth Sekard509eb92018-11-15 17:44:11 +0530965 networkIPIntf, "Origin");
Vernon Mauery8166c8d2019-05-23 11:22:30 -0700966 if (std::get<std::string>(value) ==
Suryakanth Sekard509eb92018-11-15 17:44:11 +0530967 "xyz.openbmc_project.Network.IP.AddressOrigin.DHCP")
968 {
969 return true;
970 }
971 else
972 {
973 return false;
974 }
975 }
976 catch (sdbusplus::exception_t& e)
977 {
978 phosphor::logging::log<phosphor::logging::level::ERR>(e.description());
979 return true;
980 }
981}
982
983/** @brief implementes for check the DHCP or not in IPv6
984 * @param[in] Channel - Channel number
985 * @returns true or false.
986 */
987static bool isDHCPIPv6Enabled(uint8_t Channel)
988{
989
990 try
991 {
992 auto ethdevice = getChannelName(Channel);
993 if (ethdevice.empty())
994 {
995 return false;
996 }
997 auto ethIP = ethdevice + "/ipv6";
Vernon Mauery15419dd2019-05-24 09:40:30 -0700998 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
Suryakanth Sekard509eb92018-11-15 17:44:11 +0530999 auto objectInfo =
Vernon Mauery15419dd2019-05-24 09:40:30 -07001000 getDbusObject(*dbus, networkIPIntf, networkRoot, ethIP);
1001 auto properties = getAllDbusProperties(*dbus, objectInfo.second,
Suryakanth Sekard509eb92018-11-15 17:44:11 +05301002 objectInfo.first, networkIPIntf);
Vernon Mauery8166c8d2019-05-23 11:22:30 -07001003 if (std::get<std::string>(properties["Origin"]) ==
Suryakanth Sekard509eb92018-11-15 17:44:11 +05301004 "xyz.openbmc_project.Network.IP.AddressOrigin.DHCP")
1005 {
1006 return true;
1007 }
1008 else
1009 {
1010 return false;
1011 }
1012 }
1013 catch (sdbusplus::exception_t& e)
1014 {
1015 phosphor::logging::log<phosphor::logging::level::ERR>(e.description());
1016 return true;
1017 }
1018}
1019
1020/** @brief implementes the creating of default new user
1021 * @param[in] userName - new username in 16 bytes.
1022 * @param[in] userPassword - new password in 20 bytes
1023 * @returns ipmi completion code.
1024 */
1025ipmi::RspType<> ipmiOEMSetUser2Activation(
1026 std::array<uint8_t, ipmi::ipmiMaxUserName>& userName,
1027 std::array<uint8_t, ipmi::maxIpmi20PasswordSize>& userPassword)
1028{
1029 bool userState = false;
1030 // Check for System Interface not exist and LAN should be static
1031 for (uint8_t channel = 0; channel < maxIpmiChannels; channel++)
1032 {
1033 ChannelInfo chInfo;
1034 try
1035 {
1036 getChannelInfo(channel, chInfo);
1037 }
1038 catch (sdbusplus::exception_t& e)
1039 {
1040 phosphor::logging::log<phosphor::logging::level::ERR>(
1041 "ipmiOEMSetUser2Activation: Failed to get Channel Info",
1042 phosphor::logging::entry("MSG: %s", e.description()));
1043 return ipmi::response(ipmi::ccUnspecifiedError);
1044 }
1045 if (chInfo.mediumType ==
1046 static_cast<uint8_t>(EChannelMediumType::systemInterface))
1047 {
1048 phosphor::logging::log<phosphor::logging::level::ERR>(
1049 "ipmiOEMSetUser2Activation: system interface exist .");
1050 return ipmi::response(ipmi::ccCommandNotAvailable);
1051 }
1052 else
1053 {
1054
1055 if (chInfo.mediumType ==
1056 static_cast<uint8_t>(EChannelMediumType::lan8032))
1057 {
1058 if (isDHCPIPv6Enabled(channel) || isDHCPEnabled(channel))
1059 {
1060 phosphor::logging::log<phosphor::logging::level::ERR>(
1061 "ipmiOEMSetUser2Activation: DHCP enabled .");
1062 return ipmi::response(ipmi::ccCommandNotAvailable);
1063 }
1064 }
1065 }
1066 }
1067 uint8_t maxChUsers = 0, enabledUsers = 0, fixedUsers = 0;
1068 if (ipmi::ccSuccess ==
1069 ipmiUserGetAllCounts(maxChUsers, enabledUsers, fixedUsers))
1070 {
1071 if (enabledUsers > 1)
1072 {
1073 phosphor::logging::log<phosphor::logging::level::ERR>(
1074 "ipmiOEMSetUser2Activation: more than one user is enabled.");
1075 return ipmi::response(ipmi::ccCommandNotAvailable);
1076 }
1077 // Check the user 2 is enabled or not
1078 ipmiUserCheckEnabled(ipmiDefaultUserId, userState);
1079 if (userState == true)
1080 {
1081 phosphor::logging::log<phosphor::logging::level::ERR>(
1082 "ipmiOEMSetUser2Activation: user 2 already enabled .");
1083 return ipmi::response(ipmi::ccCommandNotAvailable);
1084 }
1085 }
1086 else
1087 {
1088 return ipmi::response(ipmi::ccUnspecifiedError);
1089 }
1090
1091#if BYTE_ORDER == LITTLE_ENDIAN
1092 PrivAccess privAccess = {PRIVILEGE_ADMIN, true, true, true, 0};
1093#endif
1094#if BYTE_ORDER == BIG_ENDIAN
1095 PrivAccess privAccess = {0, true, true, true, PRIVILEGE_ADMIN};
1096#endif
1097
Vernon Mauery037cabd2020-05-14 12:16:01 -07001098 // ipmiUserSetUserName correctly handles char*, possibly non-null
1099 // terminated strings using ipmiMaxUserName size
Jayaprakash Mutyala3fbe8d22020-10-29 14:42:59 +00001100 size_t nameLen = strnlen(reinterpret_cast<const char*>(userName.data()),
1101 sizeof(userName));
1102 const std::string userNameRaw(
1103 reinterpret_cast<const char*>(userName.data()), nameLen);
jayaprakash Mutyala1429d4f2020-03-04 18:20:16 +00001104
Vernon Mauery037cabd2020-05-14 12:16:01 -07001105 if (ipmi::ccSuccess == ipmiUserSetUserName(ipmiDefaultUserId, userNameRaw))
Suryakanth Sekard509eb92018-11-15 17:44:11 +05301106 {
1107 if (ipmi::ccSuccess ==
1108 ipmiUserSetUserPassword(
1109 ipmiDefaultUserId,
1110 reinterpret_cast<const char*>(userPassword.data())))
1111 {
1112 if (ipmi::ccSuccess ==
1113 ipmiUserSetPrivilegeAccess(
1114 ipmiDefaultUserId,
1115 static_cast<uint8_t>(ipmi::EChannelID::chanLan1),
1116 privAccess, true))
1117 {
1118 phosphor::logging::log<phosphor::logging::level::INFO>(
1119 "ipmiOEMSetUser2Activation: user created successfully ");
Jayaprakash Mutyala94204162020-10-23 06:17:56 +00001120 OPENSSL_cleanse(userPassword.data(), userPassword.size());
1121
Suryakanth Sekard509eb92018-11-15 17:44:11 +05301122 return ipmi::responseSuccess();
1123 }
1124 }
1125 // we need to delete the default user id which added in this command as
1126 // password / priv setting is failed.
Jayaprakash Mutyala3fbe8d22020-10-29 14:42:59 +00001127 ipmiUserSetUserName(ipmiDefaultUserId, static_cast<std::string>(""));
Suryakanth Sekard509eb92018-11-15 17:44:11 +05301128 phosphor::logging::log<phosphor::logging::level::ERR>(
1129 "ipmiOEMSetUser2Activation: password / priv setting is failed.");
Jayaprakash Mutyala94204162020-10-23 06:17:56 +00001130 OPENSSL_cleanse(userPassword.data(), userPassword.size());
Suryakanth Sekard509eb92018-11-15 17:44:11 +05301131 }
1132 else
1133 {
1134 phosphor::logging::log<phosphor::logging::level::ERR>(
1135 "ipmiOEMSetUser2Activation: Setting username failed.");
1136 }
1137
1138 return ipmi::response(ipmi::ccCommandNotAvailable);
1139}
1140
Suryakanth Sekar822b0b42019-11-15 18:32:53 +05301141/** @brief implementes executing the linux command
1142 * @param[in] linux command
1143 * @returns status
1144 */
1145
1146static uint8_t executeCmd(const char* path)
1147{
1148 boost::process::child execProg(path);
1149 execProg.wait();
1150
1151 int retCode = execProg.exit_code();
1152 if (retCode)
1153 {
1154 return ipmi::ccUnspecifiedError;
1155 }
1156 return ipmi::ccSuccess;
1157}
1158
1159/** @brief implementes ASD Security event logging
1160 * @param[in] Event message string
1161 * @param[in] Event Severity
1162 * @returns status
1163 */
1164
1165static void atScaleDebugEventlog(std::string msg, int severity)
1166{
1167 std::string eventStr = "OpenBMC.0.1." + msg;
1168 sd_journal_send("MESSAGE=Security Event: %s", eventStr.c_str(),
1169 "PRIORITY=%i", severity, "REDFISH_MESSAGE_ID=%s",
1170 eventStr.c_str(), NULL);
1171}
1172
Richard Marian Thomaiyarfc5e9852019-04-14 15:06:27 +05301173/** @brief implementes setting password for special user
1174 * @param[in] specialUserIndex
1175 * @param[in] userPassword - new password in 20 bytes
1176 * @returns ipmi completion code.
1177 */
1178ipmi::RspType<> ipmiOEMSetSpecialUserPassword(ipmi::Context::ptr ctx,
1179 uint8_t specialUserIndex,
1180 std::vector<uint8_t> userPassword)
1181{
1182 ChannelInfo chInfo;
Suryakanth Sekar822b0b42019-11-15 18:32:53 +05301183 ipmi_ret_t status = ipmi::ccSuccess;
1184
Richard Marian Thomaiyarfc5e9852019-04-14 15:06:27 +05301185 try
1186 {
1187 getChannelInfo(ctx->channel, chInfo);
1188 }
1189 catch (sdbusplus::exception_t& e)
1190 {
1191 phosphor::logging::log<phosphor::logging::level::ERR>(
1192 "ipmiOEMSetSpecialUserPassword: Failed to get Channel Info",
1193 phosphor::logging::entry("MSG: %s", e.description()));
1194 return ipmi::responseUnspecifiedError();
1195 }
1196 if (chInfo.mediumType !=
1197 static_cast<uint8_t>(EChannelMediumType::systemInterface))
1198 {
1199 phosphor::logging::log<phosphor::logging::level::ERR>(
1200 "ipmiOEMSetSpecialUserPassword: Error - supported only in KCS "
1201 "interface");
1202 return ipmi::responseCommandNotAvailable();
1203 }
Suryakanth Sekar822b0b42019-11-15 18:32:53 +05301204
1205 // 0 for root user and 1 for AtScaleDebug is allowed
1206 if (specialUserIndex >
1207 static_cast<uint8_t>(SpecialUserIndex::atScaleDebugUser))
Richard Marian Thomaiyarfc5e9852019-04-14 15:06:27 +05301208 {
1209 phosphor::logging::log<phosphor::logging::level::ERR>(
1210 "ipmiOEMSetSpecialUserPassword: Invalid user account");
1211 return ipmi::responseParmOutOfRange();
1212 }
Suryakanth Sekar822b0b42019-11-15 18:32:53 +05301213 if (userPassword.size() != 0)
Richard Marian Thomaiyarfc5e9852019-04-14 15:06:27 +05301214 {
Suryakanth Sekar822b0b42019-11-15 18:32:53 +05301215 constexpr uint8_t minPasswordSizeRequired = 6;
1216 std::string passwd;
1217 if (userPassword.size() < minPasswordSizeRequired ||
1218 userPassword.size() > ipmi::maxIpmi20PasswordSize)
1219 {
Jayaprakash Mutyala94204162020-10-23 06:17:56 +00001220 OPENSSL_cleanse(userPassword.data(), userPassword.size());
Suryakanth Sekar822b0b42019-11-15 18:32:53 +05301221 return ipmi::responseReqDataLenInvalid();
1222 }
1223 passwd.assign(reinterpret_cast<const char*>(userPassword.data()),
1224 userPassword.size());
Jayaprakash Mutyala94204162020-10-23 06:17:56 +00001225 // Clear sensitive data
1226 OPENSSL_cleanse(userPassword.data(), userPassword.size());
Suryakanth Sekar822b0b42019-11-15 18:32:53 +05301227 if (specialUserIndex ==
1228 static_cast<uint8_t>(SpecialUserIndex::atScaleDebugUser))
1229 {
1230 status = ipmiSetSpecialUserPassword("asdbg", passwd);
1231
1232 atScaleDebugEventlog("AtScaleDebugSpecialUserEnabled", LOG_CRIT);
1233 }
1234 else
1235 {
1236 status = ipmiSetSpecialUserPassword("root", passwd);
1237 }
Jayaprakash Mutyala94204162020-10-23 06:17:56 +00001238 // Clear sensitive data
1239 OPENSSL_cleanse(&passwd, passwd.length());
1240
Suryakanth Sekar822b0b42019-11-15 18:32:53 +05301241 return ipmi::response(status);
Richard Marian Thomaiyarfc5e9852019-04-14 15:06:27 +05301242 }
Suryakanth Sekar822b0b42019-11-15 18:32:53 +05301243 else
1244 {
1245 if (specialUserIndex ==
1246 static_cast<uint8_t>(SpecialUserIndex::rootUser))
1247 {
1248 status = executeCmd("passwd -d root");
1249 }
1250 else
1251 {
1252
1253 status = executeCmd("passwd -d asdbg");
1254
1255 if (status == 0)
1256 {
1257 atScaleDebugEventlog("AtScaleDebugSpecialUserDisabled",
1258 LOG_INFO);
1259 }
1260 }
1261 return ipmi::response(status);
1262 }
Richard Marian Thomaiyarfc5e9852019-04-14 15:06:27 +05301263}
1264
Kuiying Wang45f04982018-12-26 09:23:08 +08001265namespace ledAction
1266{
1267using namespace sdbusplus::xyz::openbmc_project::Led::server;
1268std::map<Physical::Action, uint8_t> actionDbusToIpmi = {
jayaprakash Mutyala934ee9c2019-12-13 17:49:27 +00001269 {Physical::Action::Off, 0},
1270 {Physical::Action::On, 2},
1271 {Physical::Action::Blink, 1}};
Kuiying Wang45f04982018-12-26 09:23:08 +08001272
1273std::map<uint8_t, std::string> offsetObjPath = {
1274 {2, statusAmberObjPath}, {4, statusGreenObjPath}, {6, identifyLEDObjPath}};
1275
1276} // namespace ledAction
1277
1278int8_t getLEDState(sdbusplus::bus::bus& bus, const std::string& intf,
1279 const std::string& objPath, uint8_t& state)
1280{
1281 try
1282 {
1283 std::string service = getService(bus, intf, objPath);
1284 Value stateValue =
1285 getDbusProperty(bus, service, objPath, intf, "State");
Vernon Mauery8166c8d2019-05-23 11:22:30 -07001286 std::string strState = std::get<std::string>(stateValue);
Kuiying Wang45f04982018-12-26 09:23:08 +08001287 state = ledAction::actionDbusToIpmi.at(
1288 sdbusplus::xyz::openbmc_project::Led::server::Physical::
1289 convertActionFromString(strState));
1290 }
1291 catch (sdbusplus::exception::SdBusError& e)
1292 {
1293 phosphor::logging::log<phosphor::logging::level::ERR>(e.what());
1294 return -1;
1295 }
1296 return 0;
1297}
1298
NITIN SHARMAabd11ca2019-06-12 12:31:42 +00001299ipmi::RspType<uint8_t> ipmiOEMGetLEDStatus()
Kuiying Wang45f04982018-12-26 09:23:08 +08001300{
NITIN SHARMAabd11ca2019-06-12 12:31:42 +00001301 uint8_t ledstate = 0;
Kuiying Wang45f04982018-12-26 09:23:08 +08001302 phosphor::logging::log<phosphor::logging::level::DEBUG>("GET led status");
Vernon Mauery15419dd2019-05-24 09:40:30 -07001303 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
Kuiying Wang45f04982018-12-26 09:23:08 +08001304 for (auto it = ledAction::offsetObjPath.begin();
1305 it != ledAction::offsetObjPath.end(); ++it)
1306 {
1307 uint8_t state = 0;
NITIN SHARMAabd11ca2019-06-12 12:31:42 +00001308 if (getLEDState(*dbus, ledIntf, it->second, state) == -1)
Kuiying Wang45f04982018-12-26 09:23:08 +08001309 {
1310 phosphor::logging::log<phosphor::logging::level::ERR>(
1311 "oem_get_led_status: fail to get ID LED status!");
NITIN SHARMAabd11ca2019-06-12 12:31:42 +00001312 return ipmi::responseUnspecifiedError();
Kuiying Wang45f04982018-12-26 09:23:08 +08001313 }
NITIN SHARMAabd11ca2019-06-12 12:31:42 +00001314 ledstate |= state << it->first;
Kuiying Wang45f04982018-12-26 09:23:08 +08001315 }
NITIN SHARMAabd11ca2019-06-12 12:31:42 +00001316 return ipmi::responseSuccess(ledstate);
Kuiying Wang45f04982018-12-26 09:23:08 +08001317}
1318
Yong Li23737fe2019-02-19 08:49:55 +08001319ipmi_ret_t ipmiOEMCfgHostSerialPortSpeed(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
1320 ipmi_request_t request,
1321 ipmi_response_t response,
1322 ipmi_data_len_t dataLen,
1323 ipmi_context_t context)
1324{
1325 CfgHostSerialReq* req = reinterpret_cast<CfgHostSerialReq*>(request);
1326 uint8_t* resp = reinterpret_cast<uint8_t*>(response);
1327
1328 if (*dataLen == 0)
1329 {
1330 phosphor::logging::log<phosphor::logging::level::ERR>(
1331 "CfgHostSerial: invalid input len!",
1332 phosphor::logging::entry("LEN=%d", *dataLen));
1333 return IPMI_CC_REQ_DATA_LEN_INVALID;
1334 }
1335
1336 switch (req->command)
1337 {
1338 case getHostSerialCfgCmd:
1339 {
1340 if (*dataLen != 1)
1341 {
1342 phosphor::logging::log<phosphor::logging::level::ERR>(
1343 "CfgHostSerial: invalid input len!");
1344 *dataLen = 0;
1345 return IPMI_CC_REQ_DATA_LEN_INVALID;
1346 }
1347
1348 *dataLen = 0;
1349
1350 boost::process::ipstream is;
1351 std::vector<std::string> data;
1352 std::string line;
1353 boost::process::child c1(fwGetEnvCmd, "-n", fwHostSerailCfgEnvName,
1354 boost::process::std_out > is);
1355
1356 while (c1.running() && std::getline(is, line) && !line.empty())
1357 {
1358 data.push_back(line);
1359 }
1360
1361 c1.wait();
1362 if (c1.exit_code())
1363 {
1364 phosphor::logging::log<phosphor::logging::level::ERR>(
1365 "CfgHostSerial:: error on execute",
1366 phosphor::logging::entry("EXECUTE=%s", fwSetEnvCmd));
1367 // Using the default value
1368 *resp = 0;
1369 }
1370 else
1371 {
1372 if (data.size() != 1)
1373 {
1374 phosphor::logging::log<phosphor::logging::level::ERR>(
1375 "CfgHostSerial:: error on read env");
1376 return IPMI_CC_UNSPECIFIED_ERROR;
1377 }
1378 try
1379 {
1380 unsigned long tmp = std::stoul(data[0]);
1381 if (tmp > std::numeric_limits<uint8_t>::max())
1382 {
1383 throw std::out_of_range("Out of range");
1384 }
1385 *resp = static_cast<uint8_t>(tmp);
1386 }
1387 catch (const std::invalid_argument& e)
1388 {
1389 phosphor::logging::log<phosphor::logging::level::ERR>(
1390 "invalid config ",
1391 phosphor::logging::entry("ERR=%s", e.what()));
1392 return IPMI_CC_UNSPECIFIED_ERROR;
1393 }
1394 catch (const std::out_of_range& e)
1395 {
1396 phosphor::logging::log<phosphor::logging::level::ERR>(
1397 "out_of_range config ",
1398 phosphor::logging::entry("ERR=%s", e.what()));
1399 return IPMI_CC_UNSPECIFIED_ERROR;
1400 }
1401 }
1402
1403 *dataLen = 1;
1404 break;
1405 }
1406 case setHostSerialCfgCmd:
1407 {
1408 if (*dataLen != sizeof(CfgHostSerialReq))
1409 {
1410 phosphor::logging::log<phosphor::logging::level::ERR>(
1411 "CfgHostSerial: invalid input len!");
1412 *dataLen = 0;
1413 return IPMI_CC_REQ_DATA_LEN_INVALID;
1414 }
1415
1416 *dataLen = 0;
1417
1418 if (req->parameter > HostSerialCfgParamMax)
1419 {
1420 phosphor::logging::log<phosphor::logging::level::ERR>(
1421 "CfgHostSerial: invalid input!");
1422 return IPMI_CC_INVALID_FIELD_REQUEST;
1423 }
1424
1425 boost::process::child c1(fwSetEnvCmd, fwHostSerailCfgEnvName,
1426 std::to_string(req->parameter));
1427
1428 c1.wait();
1429 if (c1.exit_code())
1430 {
1431 phosphor::logging::log<phosphor::logging::level::ERR>(
1432 "CfgHostSerial:: error on execute",
1433 phosphor::logging::entry("EXECUTE=%s", fwGetEnvCmd));
1434 return IPMI_CC_UNSPECIFIED_ERROR;
1435 }
1436 break;
1437 }
1438 default:
1439 phosphor::logging::log<phosphor::logging::level::ERR>(
1440 "CfgHostSerial: invalid input!");
1441 *dataLen = 0;
1442 return IPMI_CC_INVALID_FIELD_REQUEST;
1443 }
1444
1445 return IPMI_CC_OK;
1446}
1447
James Feist91244a62019-02-19 15:04:54 -08001448constexpr const char* thermalModeInterface =
1449 "xyz.openbmc_project.Control.ThermalMode";
1450constexpr const char* thermalModePath =
1451 "/xyz/openbmc_project/control/thermal_mode";
1452
1453bool getFanProfileInterface(
1454 sdbusplus::bus::bus& bus,
1455 boost::container::flat_map<
1456 std::string, std::variant<std::vector<std::string>, std::string>>& resp)
1457{
1458 auto call = bus.new_method_call(settingsBusName, thermalModePath, PROP_INTF,
1459 "GetAll");
1460 call.append(thermalModeInterface);
1461 try
1462 {
1463 auto data = bus.call(call);
1464 data.read(resp);
1465 }
1466 catch (sdbusplus::exception_t& e)
1467 {
1468 phosphor::logging::log<phosphor::logging::level::ERR>(
1469 "getFanProfileInterface: can't get thermal mode!",
1470 phosphor::logging::entry("ERR=%s", e.what()));
1471 return false;
1472 }
1473 return true;
1474}
1475
anil kumar appanaf945eee2019-09-25 23:29:11 +00001476/**@brief implements the OEM set fan config.
1477 * @param selectedFanProfile - fan profile to enable
1478 * @param reserved1
1479 * @param performanceMode - Performance/Acoustic mode
1480 * @param reserved2
1481 * @param setPerformanceMode - set Performance/Acoustic mode
1482 * @param setFanProfile - set fan profile
1483 *
1484 * @return IPMI completion code.
1485 **/
1486ipmi::RspType<> ipmiOEMSetFanConfig(uint8_t selectedFanProfile,
1487
1488 uint2_t reserved1, bool performanceMode,
1489 uint3_t reserved2, bool setPerformanceMode,
Joshi-Mansi619186d2020-01-27 19:16:03 +05301490 bool setFanProfile,
1491 std::optional<uint8_t> dimmGroupId,
1492 std::optional<uint32_t> dimmPresenceBitmap)
James Feist91244a62019-02-19 15:04:54 -08001493{
anil kumar appanaf945eee2019-09-25 23:29:11 +00001494 if (reserved1 || reserved2)
James Feist91244a62019-02-19 15:04:54 -08001495 {
anil kumar appanaf945eee2019-09-25 23:29:11 +00001496 return ipmi::responseInvalidFieldRequest();
James Feist91244a62019-02-19 15:04:54 -08001497 }
Joshi-Mansi619186d2020-01-27 19:16:03 +05301498
1499 if (dimmGroupId)
1500 {
1501 if (*dimmGroupId >= maxCPUNum)
1502 {
1503 return ipmi::responseInvalidFieldRequest();
1504 }
1505 if (!cpuPresent("CPU_" + std::to_string(*dimmGroupId + 1)))
1506 {
1507 return ipmi::responseInvalidFieldRequest();
1508 }
1509 }
1510
James Feist91244a62019-02-19 15:04:54 -08001511 // todo: tell bios to only send first 2 bytes
James Feist91244a62019-02-19 15:04:54 -08001512 boost::container::flat_map<
1513 std::string, std::variant<std::vector<std::string>, std::string>>
1514 profileData;
Vernon Mauery15419dd2019-05-24 09:40:30 -07001515 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
1516 if (!getFanProfileInterface(*dbus, profileData))
James Feist91244a62019-02-19 15:04:54 -08001517 {
anil kumar appanaf945eee2019-09-25 23:29:11 +00001518 return ipmi::responseUnspecifiedError();
James Feist91244a62019-02-19 15:04:54 -08001519 }
1520
1521 std::vector<std::string>* supported =
1522 std::get_if<std::vector<std::string>>(&profileData["Supported"]);
1523 if (supported == nullptr)
1524 {
anil kumar appanaf945eee2019-09-25 23:29:11 +00001525 return ipmi::responseInvalidFieldRequest();
James Feist91244a62019-02-19 15:04:54 -08001526 }
1527 std::string mode;
anil kumar appanaf945eee2019-09-25 23:29:11 +00001528 if (setPerformanceMode)
James Feist91244a62019-02-19 15:04:54 -08001529 {
James Feist91244a62019-02-19 15:04:54 -08001530 if (performanceMode)
1531 {
1532
1533 if (std::find(supported->begin(), supported->end(),
1534 "Performance") != supported->end())
1535 {
1536 mode = "Performance";
1537 }
1538 }
1539 else
1540 {
James Feist91244a62019-02-19 15:04:54 -08001541 if (std::find(supported->begin(), supported->end(), "Acoustic") !=
1542 supported->end())
1543 {
1544 mode = "Acoustic";
1545 }
1546 }
1547 if (mode.empty())
1548 {
anil kumar appanaf945eee2019-09-25 23:29:11 +00001549 return ipmi::responseInvalidFieldRequest();
James Feist91244a62019-02-19 15:04:54 -08001550 }
anil kumar appanaf945eee2019-09-25 23:29:11 +00001551
1552 try
1553 {
1554 setDbusProperty(*dbus, settingsBusName, thermalModePath,
1555 thermalModeInterface, "Current", mode);
1556 }
1557 catch (sdbusplus::exception_t& e)
1558 {
1559 phosphor::logging::log<phosphor::logging::level::ERR>(
1560 "ipmiOEMSetFanConfig: can't set thermal mode!",
1561 phosphor::logging::entry("EXCEPTION=%s", e.what()));
1562 return ipmi::responseResponseError();
1563 }
James Feist91244a62019-02-19 15:04:54 -08001564 }
1565
anil kumar appanaf945eee2019-09-25 23:29:11 +00001566 return ipmi::responseSuccess();
James Feist91244a62019-02-19 15:04:54 -08001567}
1568
James Feist5b693632019-07-09 09:06:09 -07001569ipmi::RspType<uint8_t, // profile support map
1570 uint8_t, // fan control profile enable
1571 uint8_t, // flags
1572 uint32_t // dimm presence bit map
1573 >
1574 ipmiOEMGetFanConfig(uint8_t dimmGroupId)
James Feist91244a62019-02-19 15:04:54 -08001575{
Joshi-Mansi36f05ce2020-01-14 14:29:34 +05301576 if (dimmGroupId >= maxCPUNum)
1577 {
1578 return ipmi::responseInvalidFieldRequest();
1579 }
1580
1581 bool cpuStatus = cpuPresent("CPU_" + std::to_string(dimmGroupId + 1));
1582
1583 if (!cpuStatus)
1584 {
1585 return ipmi::responseInvalidFieldRequest();
1586 }
1587
James Feist91244a62019-02-19 15:04:54 -08001588 boost::container::flat_map<
1589 std::string, std::variant<std::vector<std::string>, std::string>>
1590 profileData;
1591
Vernon Mauery15419dd2019-05-24 09:40:30 -07001592 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
1593 if (!getFanProfileInterface(*dbus, profileData))
James Feist91244a62019-02-19 15:04:54 -08001594 {
James Feist5b693632019-07-09 09:06:09 -07001595 return ipmi::responseResponseError();
James Feist91244a62019-02-19 15:04:54 -08001596 }
1597
1598 std::string* current = std::get_if<std::string>(&profileData["Current"]);
1599
1600 if (current == nullptr)
1601 {
1602 phosphor::logging::log<phosphor::logging::level::ERR>(
1603 "ipmiOEMGetFanConfig: can't get current mode!");
James Feist5b693632019-07-09 09:06:09 -07001604 return ipmi::responseResponseError();
James Feist91244a62019-02-19 15:04:54 -08001605 }
1606 bool performance = (*current == "Performance");
1607
James Feist5b693632019-07-09 09:06:09 -07001608 uint8_t flags = 0;
James Feist91244a62019-02-19 15:04:54 -08001609 if (performance)
1610 {
James Feist5b693632019-07-09 09:06:09 -07001611 flags |= 1 << 2;
James Feist91244a62019-02-19 15:04:54 -08001612 }
1613
jayaprakash Mutyala4b1552d2020-02-11 12:07:29 +00001614 constexpr uint8_t fanControlDefaultProfile = 0x80;
1615 constexpr uint8_t fanControlProfileState = 0x00;
1616 constexpr uint32_t dimmPresenceBitmap = 0x00;
1617
1618 return ipmi::responseSuccess(fanControlDefaultProfile,
1619 fanControlProfileState, flags,
1620 dimmPresenceBitmap);
James Feist91244a62019-02-19 15:04:54 -08001621}
James Feist5f957ca2019-03-14 15:33:55 -07001622constexpr const char* cfmLimitSettingPath =
1623 "/xyz/openbmc_project/control/cfm_limit";
1624constexpr const char* cfmLimitIface = "xyz.openbmc_project.Control.CFMLimit";
James Feistfaa4f222019-03-21 16:21:55 -07001625constexpr const size_t legacyExitAirSensorNumber = 0x2e;
James Feist09f6b602019-08-08 11:30:03 -07001626constexpr const size_t legacyPCHSensorNumber = 0x22;
1627constexpr const char* exitAirPathName = "Exit_Air";
1628constexpr const char* pchPathName = "SSB_Temp";
James Feistacc8a4e2019-04-02 14:23:57 -07001629constexpr const char* pidConfigurationIface =
1630 "xyz.openbmc_project.Configuration.Pid";
James Feistfaa4f222019-03-21 16:21:55 -07001631
James Feist09f6b602019-08-08 11:30:03 -07001632static std::string getConfigPath(const std::string& name)
James Feistfaa4f222019-03-21 16:21:55 -07001633{
Vernon Mauery15419dd2019-05-24 09:40:30 -07001634 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
James Feistfaa4f222019-03-21 16:21:55 -07001635 auto method =
Vernon Mauery15419dd2019-05-24 09:40:30 -07001636 dbus->new_method_call("xyz.openbmc_project.ObjectMapper",
1637 "/xyz/openbmc_project/object_mapper",
1638 "xyz.openbmc_project.ObjectMapper", "GetSubTree");
James Feistfaa4f222019-03-21 16:21:55 -07001639
James Feistacc8a4e2019-04-02 14:23:57 -07001640 method.append("/", 0, std::array<const char*, 1>{pidConfigurationIface});
James Feistfaa4f222019-03-21 16:21:55 -07001641 std::string path;
1642 GetSubTreeType resp;
1643 try
1644 {
Vernon Mauery15419dd2019-05-24 09:40:30 -07001645 auto reply = dbus->call(method);
James Feistfaa4f222019-03-21 16:21:55 -07001646 reply.read(resp);
1647 }
1648 catch (sdbusplus::exception_t&)
1649 {
1650 phosphor::logging::log<phosphor::logging::level::ERR>(
1651 "ipmiOEMGetFscParameter: mapper error");
1652 };
James Feist09f6b602019-08-08 11:30:03 -07001653 auto config =
1654 std::find_if(resp.begin(), resp.end(), [&name](const auto& pair) {
1655 return pair.first.find(name) != std::string::npos;
1656 });
James Feistfaa4f222019-03-21 16:21:55 -07001657 if (config != resp.end())
1658 {
1659 path = std::move(config->first);
1660 }
1661 return path;
1662}
James Feist5f957ca2019-03-14 15:33:55 -07001663
James Feistacc8a4e2019-04-02 14:23:57 -07001664// flat map to make alphabetical
1665static boost::container::flat_map<std::string, PropertyMap> getPidConfigs()
1666{
1667 boost::container::flat_map<std::string, PropertyMap> ret;
Vernon Mauery15419dd2019-05-24 09:40:30 -07001668 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
James Feistacc8a4e2019-04-02 14:23:57 -07001669 auto method =
Vernon Mauery15419dd2019-05-24 09:40:30 -07001670 dbus->new_method_call("xyz.openbmc_project.ObjectMapper",
1671 "/xyz/openbmc_project/object_mapper",
1672 "xyz.openbmc_project.ObjectMapper", "GetSubTree");
James Feistacc8a4e2019-04-02 14:23:57 -07001673
1674 method.append("/", 0, std::array<const char*, 1>{pidConfigurationIface});
1675 GetSubTreeType resp;
1676
1677 try
1678 {
Vernon Mauery15419dd2019-05-24 09:40:30 -07001679 auto reply = dbus->call(method);
James Feistacc8a4e2019-04-02 14:23:57 -07001680 reply.read(resp);
1681 }
1682 catch (sdbusplus::exception_t&)
1683 {
1684 phosphor::logging::log<phosphor::logging::level::ERR>(
1685 "getFanConfigPaths: mapper error");
1686 };
1687 for (const auto& [path, objects] : resp)
1688 {
1689 if (objects.empty())
1690 {
1691 continue; // should be impossible
1692 }
Zhu, Yungebe560b02019-04-21 21:19:21 -04001693
1694 try
1695 {
Vernon Mauery15419dd2019-05-24 09:40:30 -07001696 ret.emplace(path,
1697 getAllDbusProperties(*dbus, objects[0].first, path,
1698 pidConfigurationIface));
Zhu, Yungebe560b02019-04-21 21:19:21 -04001699 }
1700 catch (sdbusplus::exception_t& e)
1701 {
1702 phosphor::logging::log<phosphor::logging::level::ERR>(
1703 "getPidConfigs: can't get DbusProperties!",
1704 phosphor::logging::entry("ERR=%s", e.what()));
1705 }
James Feistacc8a4e2019-04-02 14:23:57 -07001706 }
1707 return ret;
1708}
1709
1710ipmi::RspType<uint8_t> ipmiOEMGetFanSpeedOffset(void)
1711{
1712 boost::container::flat_map<std::string, PropertyMap> data = getPidConfigs();
1713 if (data.empty())
1714 {
1715 return ipmi::responseResponseError();
1716 }
1717 uint8_t minOffset = std::numeric_limits<uint8_t>::max();
1718 for (const auto& [_, pid] : data)
1719 {
1720 auto findClass = pid.find("Class");
1721 if (findClass == pid.end())
1722 {
1723 phosphor::logging::log<phosphor::logging::level::ERR>(
1724 "ipmiOEMGetFscParameter: found illegal pid "
1725 "configurations");
1726 return ipmi::responseResponseError();
1727 }
1728 std::string type = std::get<std::string>(findClass->second);
1729 if (type == "fan")
1730 {
1731 auto findOutLimit = pid.find("OutLimitMin");
1732 if (findOutLimit == pid.end())
1733 {
1734 phosphor::logging::log<phosphor::logging::level::ERR>(
1735 "ipmiOEMGetFscParameter: found illegal pid "
1736 "configurations");
1737 return ipmi::responseResponseError();
1738 }
1739 // get the min out of all the offsets
1740 minOffset = std::min(
1741 minOffset,
1742 static_cast<uint8_t>(std::get<double>(findOutLimit->second)));
1743 }
1744 }
1745 if (minOffset == std::numeric_limits<uint8_t>::max())
1746 {
1747 phosphor::logging::log<phosphor::logging::level::ERR>(
1748 "ipmiOEMGetFscParameter: found no fan configurations!");
1749 return ipmi::responseResponseError();
1750 }
1751
1752 return ipmi::responseSuccess(minOffset);
1753}
1754
1755ipmi::RspType<> ipmiOEMSetFanSpeedOffset(uint8_t offset)
1756{
1757 boost::container::flat_map<std::string, PropertyMap> data = getPidConfigs();
1758 if (data.empty())
1759 {
1760
1761 phosphor::logging::log<phosphor::logging::level::ERR>(
1762 "ipmiOEMSetFanSpeedOffset: found no pid configurations!");
1763 return ipmi::responseResponseError();
1764 }
1765
Vernon Mauery15419dd2019-05-24 09:40:30 -07001766 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
James Feistacc8a4e2019-04-02 14:23:57 -07001767 bool found = false;
1768 for (const auto& [path, pid] : data)
1769 {
1770 auto findClass = pid.find("Class");
1771 if (findClass == pid.end())
1772 {
1773
1774 phosphor::logging::log<phosphor::logging::level::ERR>(
1775 "ipmiOEMSetFanSpeedOffset: found illegal pid "
1776 "configurations");
1777 return ipmi::responseResponseError();
1778 }
1779 std::string type = std::get<std::string>(findClass->second);
1780 if (type == "fan")
1781 {
1782 auto findOutLimit = pid.find("OutLimitMin");
1783 if (findOutLimit == pid.end())
1784 {
1785
1786 phosphor::logging::log<phosphor::logging::level::ERR>(
1787 "ipmiOEMSetFanSpeedOffset: found illegal pid "
1788 "configurations");
1789 return ipmi::responseResponseError();
1790 }
Vernon Mauery15419dd2019-05-24 09:40:30 -07001791 ipmi::setDbusProperty(*dbus, "xyz.openbmc_project.EntityManager",
James Feistacc8a4e2019-04-02 14:23:57 -07001792 path, pidConfigurationIface, "OutLimitMin",
1793 static_cast<double>(offset));
1794 found = true;
1795 }
1796 }
1797 if (!found)
1798 {
1799 phosphor::logging::log<phosphor::logging::level::ERR>(
1800 "ipmiOEMSetFanSpeedOffset: set no fan offsets");
1801 return ipmi::responseResponseError();
1802 }
1803
1804 return ipmi::responseSuccess();
1805}
1806
1807ipmi::RspType<> ipmiOEMSetFscParameter(uint8_t command, uint8_t param1,
1808 uint8_t param2)
James Feist5f957ca2019-03-14 15:33:55 -07001809{
1810 constexpr const size_t disableLimiting = 0x0;
1811
Vernon Mauery15419dd2019-05-24 09:40:30 -07001812 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
James Feistacc8a4e2019-04-02 14:23:57 -07001813 if (command == static_cast<uint8_t>(setFscParamFlags::tcontrol))
James Feist5f957ca2019-03-14 15:33:55 -07001814 {
James Feist09f6b602019-08-08 11:30:03 -07001815 std::string pathName;
James Feistacc8a4e2019-04-02 14:23:57 -07001816 if (param1 == legacyExitAirSensorNumber)
James Feistfaa4f222019-03-21 16:21:55 -07001817 {
James Feist09f6b602019-08-08 11:30:03 -07001818 pathName = exitAirPathName;
1819 }
1820 else if (param1 == legacyPCHSensorNumber)
1821 {
1822 pathName = pchPathName;
James Feistfaa4f222019-03-21 16:21:55 -07001823 }
1824 else
1825 {
James Feistacc8a4e2019-04-02 14:23:57 -07001826 return ipmi::responseParmOutOfRange();
James Feistfaa4f222019-03-21 16:21:55 -07001827 }
James Feist09f6b602019-08-08 11:30:03 -07001828 std::string path = getConfigPath(pathName);
1829 ipmi::setDbusProperty(*dbus, "xyz.openbmc_project.EntityManager", path,
1830 pidConfigurationIface, "SetPoint",
1831 static_cast<double>(param2));
1832 return ipmi::responseSuccess();
James Feistfaa4f222019-03-21 16:21:55 -07001833 }
James Feistacc8a4e2019-04-02 14:23:57 -07001834 else if (command == static_cast<uint8_t>(setFscParamFlags::cfm))
James Feist5f957ca2019-03-14 15:33:55 -07001835 {
James Feistacc8a4e2019-04-02 14:23:57 -07001836 uint16_t cfm = param1 | (static_cast<uint16_t>(param2) << 8);
James Feist5f957ca2019-03-14 15:33:55 -07001837
1838 // must be greater than 50 based on eps
1839 if (cfm < 50 && cfm != disableLimiting)
1840 {
James Feistacc8a4e2019-04-02 14:23:57 -07001841 return ipmi::responseParmOutOfRange();
James Feist5f957ca2019-03-14 15:33:55 -07001842 }
1843
1844 try
1845 {
Vernon Mauery15419dd2019-05-24 09:40:30 -07001846 ipmi::setDbusProperty(*dbus, settingsBusName, cfmLimitSettingPath,
James Feist5f957ca2019-03-14 15:33:55 -07001847 cfmLimitIface, "Limit",
1848 static_cast<double>(cfm));
1849 }
1850 catch (sdbusplus::exception_t& e)
1851 {
1852 phosphor::logging::log<phosphor::logging::level::ERR>(
1853 "ipmiOEMSetFscParameter: can't set cfm setting!",
1854 phosphor::logging::entry("ERR=%s", e.what()));
James Feistacc8a4e2019-04-02 14:23:57 -07001855 return ipmi::responseResponseError();
James Feist5f957ca2019-03-14 15:33:55 -07001856 }
James Feistacc8a4e2019-04-02 14:23:57 -07001857 return ipmi::responseSuccess();
1858 }
1859 else if (command == static_cast<uint8_t>(setFscParamFlags::maxPwm))
1860 {
1861 constexpr const size_t maxDomainCount = 8;
1862 uint8_t requestedDomainMask = param1;
1863 boost::container::flat_map data = getPidConfigs();
1864 if (data.empty())
1865 {
1866
1867 phosphor::logging::log<phosphor::logging::level::ERR>(
1868 "ipmiOEMSetFscParameter: found no pid configurations!");
1869 return ipmi::responseResponseError();
1870 }
1871 size_t count = 0;
1872 for (const auto& [path, pid] : data)
1873 {
1874 auto findClass = pid.find("Class");
1875 if (findClass == pid.end())
1876 {
1877
1878 phosphor::logging::log<phosphor::logging::level::ERR>(
1879 "ipmiOEMSetFscParameter: found illegal pid "
1880 "configurations");
1881 return ipmi::responseResponseError();
1882 }
1883 std::string type = std::get<std::string>(findClass->second);
1884 if (type == "fan")
1885 {
1886 if (requestedDomainMask & (1 << count))
1887 {
1888 ipmi::setDbusProperty(
Vernon Mauery15419dd2019-05-24 09:40:30 -07001889 *dbus, "xyz.openbmc_project.EntityManager", path,
James Feistacc8a4e2019-04-02 14:23:57 -07001890 pidConfigurationIface, "OutLimitMax",
1891 static_cast<double>(param2));
1892 }
1893 count++;
1894 }
1895 }
1896 return ipmi::responseSuccess();
James Feist5f957ca2019-03-14 15:33:55 -07001897 }
1898 else
1899 {
1900 // todo other command parts possibly
1901 // tcontrol is handled in peci now
1902 // fan speed offset not implemented yet
1903 // domain pwm limit not implemented
James Feistacc8a4e2019-04-02 14:23:57 -07001904 return ipmi::responseParmOutOfRange();
James Feist5f957ca2019-03-14 15:33:55 -07001905 }
1906}
1907
James Feistacc8a4e2019-04-02 14:23:57 -07001908ipmi::RspType<
1909 std::variant<uint8_t, std::array<uint8_t, 2>, std::array<uint16_t, 2>>>
1910 ipmiOEMGetFscParameter(uint8_t command, std::optional<uint8_t> param)
James Feist5f957ca2019-03-14 15:33:55 -07001911{
James Feist09f6b602019-08-08 11:30:03 -07001912 constexpr uint8_t legacyDefaultSetpoint = -128;
James Feist5f957ca2019-03-14 15:33:55 -07001913
Vernon Mauery15419dd2019-05-24 09:40:30 -07001914 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
James Feistacc8a4e2019-04-02 14:23:57 -07001915 if (command == static_cast<uint8_t>(setFscParamFlags::tcontrol))
James Feist5f957ca2019-03-14 15:33:55 -07001916 {
James Feistacc8a4e2019-04-02 14:23:57 -07001917 if (!param)
James Feistfaa4f222019-03-21 16:21:55 -07001918 {
James Feistacc8a4e2019-04-02 14:23:57 -07001919 return ipmi::responseReqDataLenInvalid();
James Feistfaa4f222019-03-21 16:21:55 -07001920 }
1921
James Feist09f6b602019-08-08 11:30:03 -07001922 std::string pathName;
1923
1924 if (*param == legacyExitAirSensorNumber)
1925 {
1926 pathName = exitAirPathName;
1927 }
1928 else if (*param == legacyPCHSensorNumber)
1929 {
1930 pathName = pchPathName;
1931 }
1932 else
James Feistfaa4f222019-03-21 16:21:55 -07001933 {
James Feistacc8a4e2019-04-02 14:23:57 -07001934 return ipmi::responseParmOutOfRange();
James Feistfaa4f222019-03-21 16:21:55 -07001935 }
James Feist09f6b602019-08-08 11:30:03 -07001936
1937 uint8_t setpoint = legacyDefaultSetpoint;
1938 std::string path = getConfigPath(pathName);
James Feistfaa4f222019-03-21 16:21:55 -07001939 if (path.size())
1940 {
Vernon Mauery15419dd2019-05-24 09:40:30 -07001941 Value val = ipmi::getDbusProperty(
1942 *dbus, "xyz.openbmc_project.EntityManager", path,
1943 pidConfigurationIface, "SetPoint");
James Feistfaa4f222019-03-21 16:21:55 -07001944 setpoint = std::floor(std::get<double>(val) + 0.5);
1945 }
1946
1947 // old implementation used to return the "default" and current, we
1948 // don't make the default readily available so just make both the
1949 // same
James Feistfaa4f222019-03-21 16:21:55 -07001950
James Feistacc8a4e2019-04-02 14:23:57 -07001951 return ipmi::responseSuccess(
1952 std::array<uint8_t, 2>{setpoint, setpoint});
James Feistfaa4f222019-03-21 16:21:55 -07001953 }
James Feistacc8a4e2019-04-02 14:23:57 -07001954 else if (command == static_cast<uint8_t>(setFscParamFlags::maxPwm))
1955 {
1956 constexpr const size_t maxDomainCount = 8;
1957
1958 if (!param)
1959 {
1960 return ipmi::responseReqDataLenInvalid();
1961 }
1962 uint8_t requestedDomain = *param;
1963 if (requestedDomain >= maxDomainCount)
1964 {
1965 return ipmi::responseInvalidFieldRequest();
1966 }
1967
1968 boost::container::flat_map data = getPidConfigs();
1969 if (data.empty())
1970 {
1971 phosphor::logging::log<phosphor::logging::level::ERR>(
1972 "ipmiOEMGetFscParameter: found no pid configurations!");
1973 return ipmi::responseResponseError();
1974 }
1975 size_t count = 0;
1976 for (const auto& [_, pid] : data)
1977 {
1978 auto findClass = pid.find("Class");
1979 if (findClass == pid.end())
1980 {
1981 phosphor::logging::log<phosphor::logging::level::ERR>(
1982 "ipmiOEMGetFscParameter: found illegal pid "
1983 "configurations");
1984 return ipmi::responseResponseError();
1985 }
1986 std::string type = std::get<std::string>(findClass->second);
1987 if (type == "fan")
1988 {
1989 if (requestedDomain == count)
1990 {
1991 auto findOutLimit = pid.find("OutLimitMax");
1992 if (findOutLimit == pid.end())
1993 {
1994 phosphor::logging::log<phosphor::logging::level::ERR>(
1995 "ipmiOEMGetFscParameter: found illegal pid "
1996 "configurations");
1997 return ipmi::responseResponseError();
1998 }
1999
2000 return ipmi::responseSuccess(
2001 static_cast<uint8_t>(std::floor(
2002 std::get<double>(findOutLimit->second) + 0.5)));
2003 }
2004 else
2005 {
2006 count++;
2007 }
2008 }
2009 }
2010
2011 return ipmi::responseInvalidFieldRequest();
2012 }
2013 else if (command == static_cast<uint8_t>(setFscParamFlags::cfm))
James Feist5f957ca2019-03-14 15:33:55 -07002014 {
2015
2016 /*
2017 DataLen should be 1, but host is sending us an extra bit. As the
James Feistacc8a4e2019-04-02 14:23:57 -07002018 previous behavior didn't seem to prevent this, ignore the check for
2019 now.
James Feist5f957ca2019-03-14 15:33:55 -07002020
James Feistacc8a4e2019-04-02 14:23:57 -07002021 if (param)
James Feist5f957ca2019-03-14 15:33:55 -07002022 {
2023 phosphor::logging::log<phosphor::logging::level::ERR>(
2024 "ipmiOEMGetFscParameter: invalid input len!");
James Feist5f957ca2019-03-14 15:33:55 -07002025 return IPMI_CC_REQ_DATA_LEN_INVALID;
2026 }
2027 */
2028 Value cfmLimit;
2029 Value cfmMaximum;
2030 try
2031 {
Vernon Mauery15419dd2019-05-24 09:40:30 -07002032 cfmLimit = ipmi::getDbusProperty(*dbus, settingsBusName,
James Feist5f957ca2019-03-14 15:33:55 -07002033 cfmLimitSettingPath, cfmLimitIface,
2034 "Limit");
2035 cfmMaximum = ipmi::getDbusProperty(
Vernon Mauery15419dd2019-05-24 09:40:30 -07002036 *dbus, "xyz.openbmc_project.ExitAirTempSensor",
James Feist5f957ca2019-03-14 15:33:55 -07002037 "/xyz/openbmc_project/control/MaxCFM", cfmLimitIface, "Limit");
2038 }
2039 catch (sdbusplus::exception_t& e)
2040 {
2041 phosphor::logging::log<phosphor::logging::level::ERR>(
James Feistacc8a4e2019-04-02 14:23:57 -07002042 "ipmiOEMGetFscParameter: can't get cfm setting!",
James Feist5f957ca2019-03-14 15:33:55 -07002043 phosphor::logging::entry("ERR=%s", e.what()));
James Feistacc8a4e2019-04-02 14:23:57 -07002044 return ipmi::responseResponseError();
James Feist5f957ca2019-03-14 15:33:55 -07002045 }
2046
James Feistacc8a4e2019-04-02 14:23:57 -07002047 double cfmMax = std::get<double>(cfmMaximum);
2048 double cfmLim = std::get<double>(cfmLimit);
James Feist5f957ca2019-03-14 15:33:55 -07002049
James Feistacc8a4e2019-04-02 14:23:57 -07002050 cfmLim = std::floor(cfmLim + 0.5);
2051 cfmMax = std::floor(cfmMax + 0.5);
2052 uint16_t cfmLimResp = static_cast<uint16_t>(cfmLim);
2053 uint16_t cfmMaxResp = static_cast<uint16_t>(cfmMax);
James Feist5f957ca2019-03-14 15:33:55 -07002054
James Feistacc8a4e2019-04-02 14:23:57 -07002055 return ipmi::responseSuccess(
2056 std::array<uint16_t, 2>{cfmLimResp, cfmMaxResp});
James Feist5f957ca2019-03-14 15:33:55 -07002057 }
James Feistacc8a4e2019-04-02 14:23:57 -07002058
James Feist5f957ca2019-03-14 15:33:55 -07002059 else
2060 {
2061 // todo other command parts possibly
James Feist5f957ca2019-03-14 15:33:55 -07002062 // domain pwm limit not implemented
James Feistacc8a4e2019-04-02 14:23:57 -07002063 return ipmi::responseParmOutOfRange();
James Feist5f957ca2019-03-14 15:33:55 -07002064 }
2065}
2066
Cheng C Yang773703a2019-08-15 09:41:11 +08002067using crConfigVariant =
2068 std::variant<bool, uint8_t, uint32_t, std::vector<uint8_t>, std::string>;
2069
2070int setCRConfig(ipmi::Context::ptr ctx, const std::string& property,
2071 const crConfigVariant& value,
2072 std::chrono::microseconds timeout = ipmi::IPMI_DBUS_TIMEOUT)
2073{
2074 boost::system::error_code ec;
2075 ctx->bus->yield_method_call<void>(
Kuiying Wange45333a2020-07-22 22:06:37 +08002076 ctx->yield, ec, "xyz.openbmc_project.PSURedundancy",
Cheng C Yang773703a2019-08-15 09:41:11 +08002077 "/xyz/openbmc_project/control/power_supply_redundancy",
2078 "org.freedesktop.DBus.Properties", "Set",
2079 "xyz.openbmc_project.Control.PowerSupplyRedundancy", property, value);
2080 if (ec)
2081 {
2082 phosphor::logging::log<phosphor::logging::level::ERR>(
2083 "Failed to set dbus property to cold redundancy");
2084 return -1;
2085 }
2086
2087 return 0;
2088}
2089
Kuiying Wange45333a2020-07-22 22:06:37 +08002090int getCRConfig(
2091 ipmi::Context::ptr ctx, const std::string& property, crConfigVariant& value,
2092 const std::string& service = "xyz.openbmc_project.PSURedundancy",
2093 std::chrono::microseconds timeout = ipmi::IPMI_DBUS_TIMEOUT)
Cheng C Yang773703a2019-08-15 09:41:11 +08002094{
2095 boost::system::error_code ec;
2096 value = ctx->bus->yield_method_call<crConfigVariant>(
Yong Li19445ab2019-12-20 18:25:29 +08002097 ctx->yield, ec, service,
Cheng C Yang773703a2019-08-15 09:41:11 +08002098 "/xyz/openbmc_project/control/power_supply_redundancy",
2099 "org.freedesktop.DBus.Properties", "Get",
2100 "xyz.openbmc_project.Control.PowerSupplyRedundancy", property);
2101 if (ec)
2102 {
2103 phosphor::logging::log<phosphor::logging::level::ERR>(
2104 "Failed to get dbus property to cold redundancy");
2105 return -1;
2106 }
2107 return 0;
2108}
2109
2110uint8_t getPSUCount(void)
2111{
2112 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
2113 ipmi::Value num;
2114 try
2115 {
2116 num = ipmi::getDbusProperty(
2117 *dbus, "xyz.openbmc_project.PSURedundancy",
2118 "/xyz/openbmc_project/control/power_supply_redundancy",
2119 "xyz.openbmc_project.Control.PowerSupplyRedundancy", "PSUNumber");
2120 }
2121 catch (sdbusplus::exception_t& e)
2122 {
2123 phosphor::logging::log<phosphor::logging::level::ERR>(
2124 "Failed to get PSUNumber property from dbus interface");
2125 return 0;
2126 }
2127 uint8_t* pNum = std::get_if<uint8_t>(&num);
2128 if (!pNum)
2129 {
2130 phosphor::logging::log<phosphor::logging::level::ERR>(
2131 "Error to get PSU Number");
2132 return 0;
2133 }
2134 return *pNum;
2135}
2136
2137bool validateCRAlgo(std::vector<uint8_t>& conf, uint8_t num)
2138{
2139 if (conf.size() < num)
2140 {
2141 phosphor::logging::log<phosphor::logging::level::ERR>(
2142 "Invalid PSU Ranking");
2143 return false;
2144 }
2145 std::set<uint8_t> confSet;
2146 for (uint8_t i = 0; i < num; i++)
2147 {
2148 if (conf[i] > num)
2149 {
2150 phosphor::logging::log<phosphor::logging::level::ERR>(
2151 "PSU Ranking is larger than current PSU number");
2152 return false;
2153 }
2154 confSet.emplace(conf[i]);
2155 }
2156
2157 if (confSet.size() != num)
2158 {
2159 phosphor::logging::log<phosphor::logging::level::ERR>(
2160 "duplicate PSU Ranking");
2161 return false;
2162 }
2163 return true;
2164}
2165
2166enum class crParameter
2167{
2168 crStatus = 0,
2169 crFeature = 1,
2170 rotationFeature = 2,
2171 rotationAlgo = 3,
2172 rotationPeriod = 4,
Yong Li19445ab2019-12-20 18:25:29 +08002173 numOfPSU = 5,
2174 rotationRankOrderEffective = 6
Cheng C Yang773703a2019-08-15 09:41:11 +08002175};
2176
2177constexpr ipmi::Cc ccParameterNotSupported = 0x80;
2178static const constexpr uint32_t oneDay = 0x15180;
2179static const constexpr uint32_t oneMonth = 0xf53700;
2180static const constexpr uint8_t userSpecific = 0x01;
2181static const constexpr uint8_t crSetCompleted = 0;
2182ipmi::RspType<uint8_t> ipmiOEMSetCRConfig(ipmi::Context::ptr ctx,
2183 uint8_t parameter,
2184 ipmi::message::Payload& payload)
2185{
2186 switch (static_cast<crParameter>(parameter))
2187 {
Cheng C Yang773703a2019-08-15 09:41:11 +08002188 case crParameter::rotationFeature:
2189 {
2190 uint8_t param1;
2191 if (payload.unpack(param1) || !payload.fullyUnpacked())
2192 {
2193 return ipmi::responseReqDataLenInvalid();
2194 }
2195 // Rotation Enable can only be true or false
2196 if (param1 > 1)
2197 {
2198 return ipmi::responseInvalidFieldRequest();
2199 }
2200 if (setCRConfig(ctx, "RotationEnabled", static_cast<bool>(param1)))
2201 {
2202 return ipmi::responseResponseError();
2203 }
2204 break;
2205 }
2206 case crParameter::rotationAlgo:
2207 {
2208 // Rotation Algorithm can only be 0-BMC Specific or 1-User Specific
2209 std::string algoName;
2210 uint8_t param1;
2211 if (payload.unpack(param1))
2212 {
2213 return ipmi::responseReqDataLenInvalid();
2214 }
2215 switch (param1)
2216 {
2217 case 0:
2218 algoName = "xyz.openbmc_project.Control."
2219 "PowerSupplyRedundancy.Algo.bmcSpecific";
2220 break;
2221 case 1:
2222 algoName = "xyz.openbmc_project.Control."
2223 "PowerSupplyRedundancy.Algo.userSpecific";
2224 break;
2225 default:
2226 return ipmi::responseInvalidFieldRequest();
2227 }
2228 if (setCRConfig(ctx, "RotationAlgorithm", algoName))
2229 {
2230 return ipmi::responseResponseError();
2231 }
2232
2233 uint8_t numberOfPSU = getPSUCount();
2234 if (!numberOfPSU)
2235 {
2236 return ipmi::responseResponseError();
2237 }
2238 std::vector<uint8_t> rankOrder;
2239
2240 if (param1 == userSpecific)
2241 {
2242 if (payload.unpack(rankOrder) || !payload.fullyUnpacked())
2243 {
2244 ipmi::responseReqDataLenInvalid();
2245 }
Yong Li83315132019-10-23 17:42:24 +08002246 if (rankOrder.size() != numberOfPSU)
Cheng C Yang773703a2019-08-15 09:41:11 +08002247 {
2248 return ipmi::responseReqDataLenInvalid();
2249 }
2250
2251 if (!validateCRAlgo(rankOrder, numberOfPSU))
2252 {
2253 return ipmi::responseInvalidFieldRequest();
2254 }
2255 }
2256 else
2257 {
2258 if (rankOrder.size() > 0)
2259 {
2260 return ipmi::responseReqDataLenInvalid();
2261 }
2262 for (uint8_t i = 1; i <= numberOfPSU; i++)
2263 {
2264 rankOrder.emplace_back(i);
2265 }
2266 }
2267 if (setCRConfig(ctx, "RotationRankOrder", rankOrder))
2268 {
2269 return ipmi::responseResponseError();
2270 }
2271 break;
2272 }
2273 case crParameter::rotationPeriod:
2274 {
2275 // Minimum Rotation period is One day (86400 seconds) and Max
2276 // Rotation Period is 6 month (0xf53700 seconds)
2277 uint32_t period;
2278 if (payload.unpack(period) || !payload.fullyUnpacked())
2279 {
2280 return ipmi::responseReqDataLenInvalid();
2281 }
2282 if ((period < oneDay) || (period > oneMonth))
2283 {
2284 return ipmi::responseInvalidFieldRequest();
2285 }
2286 if (setCRConfig(ctx, "PeriodOfRotation", period))
2287 {
2288 return ipmi::responseResponseError();
2289 }
2290 break;
2291 }
2292 default:
2293 {
2294 return ipmi::response(ccParameterNotSupported);
2295 }
2296 }
2297
Cheng C Yang773703a2019-08-15 09:41:11 +08002298 return ipmi::responseSuccess(crSetCompleted);
2299}
2300
Yong Li83315132019-10-23 17:42:24 +08002301ipmi::RspType<uint8_t, std::variant<uint8_t, uint32_t, std::vector<uint8_t>>>
Cheng C Yang773703a2019-08-15 09:41:11 +08002302 ipmiOEMGetCRConfig(ipmi::Context::ptr ctx, uint8_t parameter)
2303{
2304 crConfigVariant value;
2305 switch (static_cast<crParameter>(parameter))
2306 {
2307 case crParameter::crStatus:
2308 {
2309 if (getCRConfig(ctx, "ColdRedundancyStatus", value))
2310 {
2311 return ipmi::responseResponseError();
2312 }
2313 std::string* pStatus = std::get_if<std::string>(&value);
2314 if (!pStatus)
2315 {
2316 phosphor::logging::log<phosphor::logging::level::ERR>(
2317 "Error to get ColdRedundancyStatus property");
2318 return ipmi::responseResponseError();
2319 }
2320 namespace server = sdbusplus::xyz::openbmc_project::Control::server;
2321 auto status =
2322 server::PowerSupplyRedundancy::convertStatusFromString(
2323 *pStatus);
2324 switch (status)
2325 {
2326 case server::PowerSupplyRedundancy::Status::inProgress:
Cheng C Yangf41e3342019-09-10 04:47:23 +08002327 return ipmi::responseSuccess(parameter,
Kuiying Wange45333a2020-07-22 22:06:37 +08002328 static_cast<uint8_t>(1));
Cheng C Yang773703a2019-08-15 09:41:11 +08002329
2330 case server::PowerSupplyRedundancy::Status::completed:
Cheng C Yangf41e3342019-09-10 04:47:23 +08002331 return ipmi::responseSuccess(parameter,
Kuiying Wange45333a2020-07-22 22:06:37 +08002332 static_cast<uint8_t>(0));
Cheng C Yang773703a2019-08-15 09:41:11 +08002333 default:
2334 phosphor::logging::log<phosphor::logging::level::ERR>(
2335 "Error to get valid status");
2336 return ipmi::responseResponseError();
2337 }
2338 }
2339 case crParameter::crFeature:
2340 {
Kuiying Wange45333a2020-07-22 22:06:37 +08002341 if (getCRConfig(ctx, "PowerSupplyRedundancyEnabled", value))
Cheng C Yang773703a2019-08-15 09:41:11 +08002342 {
2343 return ipmi::responseResponseError();
2344 }
2345 bool* pResponse = std::get_if<bool>(&value);
2346 if (!pResponse)
2347 {
2348 phosphor::logging::log<phosphor::logging::level::ERR>(
Kuiying Wange45333a2020-07-22 22:06:37 +08002349 "Error to get PowerSupplyRedundancyEnabled property");
Cheng C Yang773703a2019-08-15 09:41:11 +08002350 return ipmi::responseResponseError();
2351 }
2352
Cheng C Yangf41e3342019-09-10 04:47:23 +08002353 return ipmi::responseSuccess(parameter,
2354 static_cast<uint8_t>(*pResponse));
Cheng C Yang773703a2019-08-15 09:41:11 +08002355 }
2356 case crParameter::rotationFeature:
2357 {
2358 if (getCRConfig(ctx, "RotationEnabled", value))
2359 {
2360 return ipmi::responseResponseError();
2361 }
2362 bool* pResponse = std::get_if<bool>(&value);
2363 if (!pResponse)
2364 {
2365 phosphor::logging::log<phosphor::logging::level::ERR>(
2366 "Error to get RotationEnabled property");
2367 return ipmi::responseResponseError();
2368 }
Cheng C Yangf41e3342019-09-10 04:47:23 +08002369 return ipmi::responseSuccess(parameter,
2370 static_cast<uint8_t>(*pResponse));
Cheng C Yang773703a2019-08-15 09:41:11 +08002371 }
2372 case crParameter::rotationAlgo:
2373 {
2374 if (getCRConfig(ctx, "RotationAlgorithm", value))
2375 {
2376 return ipmi::responseResponseError();
2377 }
2378
2379 std::string* pAlgo = std::get_if<std::string>(&value);
2380 if (!pAlgo)
2381 {
2382 phosphor::logging::log<phosphor::logging::level::ERR>(
2383 "Error to get RotationAlgorithm property");
2384 return ipmi::responseResponseError();
2385 }
Yong Li83315132019-10-23 17:42:24 +08002386 std::vector<uint8_t> response;
Cheng C Yang773703a2019-08-15 09:41:11 +08002387 namespace server = sdbusplus::xyz::openbmc_project::Control::server;
2388 auto algo =
2389 server::PowerSupplyRedundancy::convertAlgoFromString(*pAlgo);
Yong Li83315132019-10-23 17:42:24 +08002390
Cheng C Yang773703a2019-08-15 09:41:11 +08002391 switch (algo)
2392 {
2393 case server::PowerSupplyRedundancy::Algo::bmcSpecific:
Yong Li83315132019-10-23 17:42:24 +08002394 response.push_back(0);
Cheng C Yang773703a2019-08-15 09:41:11 +08002395 break;
2396 case server::PowerSupplyRedundancy::Algo::userSpecific:
Yong Li83315132019-10-23 17:42:24 +08002397 response.push_back(1);
Cheng C Yang773703a2019-08-15 09:41:11 +08002398 break;
2399 default:
2400 phosphor::logging::log<phosphor::logging::level::ERR>(
2401 "Error to get valid algo");
2402 return ipmi::responseResponseError();
2403 }
2404
2405 if (getCRConfig(ctx, "RotationRankOrder", value))
2406 {
2407 return ipmi::responseResponseError();
2408 }
2409 std::vector<uint8_t>* pResponse =
2410 std::get_if<std::vector<uint8_t>>(&value);
2411 if (!pResponse)
2412 {
2413 phosphor::logging::log<phosphor::logging::level::ERR>(
2414 "Error to get RotationRankOrder property");
2415 return ipmi::responseResponseError();
2416 }
Yong Li83315132019-10-23 17:42:24 +08002417
Cheng C Yang773703a2019-08-15 09:41:11 +08002418 std::copy(pResponse->begin(), pResponse->end(),
Yong Li83315132019-10-23 17:42:24 +08002419 std::back_inserter(response));
2420
Cheng C Yangf41e3342019-09-10 04:47:23 +08002421 return ipmi::responseSuccess(parameter, response);
Cheng C Yang773703a2019-08-15 09:41:11 +08002422 }
2423 case crParameter::rotationPeriod:
2424 {
2425 if (getCRConfig(ctx, "PeriodOfRotation", value))
2426 {
2427 return ipmi::responseResponseError();
2428 }
2429 uint32_t* pResponse = std::get_if<uint32_t>(&value);
2430 if (!pResponse)
2431 {
2432 phosphor::logging::log<phosphor::logging::level::ERR>(
2433 "Error to get RotationAlgorithm property");
2434 return ipmi::responseResponseError();
2435 }
Cheng C Yangf41e3342019-09-10 04:47:23 +08002436 return ipmi::responseSuccess(parameter, *pResponse);
Cheng C Yang773703a2019-08-15 09:41:11 +08002437 }
2438 case crParameter::numOfPSU:
2439 {
2440 uint8_t numberOfPSU = getPSUCount();
2441 if (!numberOfPSU)
2442 {
2443 return ipmi::responseResponseError();
2444 }
Cheng C Yangf41e3342019-09-10 04:47:23 +08002445 return ipmi::responseSuccess(parameter, numberOfPSU);
Cheng C Yang773703a2019-08-15 09:41:11 +08002446 }
Yong Li19445ab2019-12-20 18:25:29 +08002447 case crParameter::rotationRankOrderEffective:
2448 {
2449 if (getCRConfig(ctx, "RotationRankOrder", value,
2450 "xyz.openbmc_project.PSURedundancy"))
2451 {
2452 return ipmi::responseResponseError();
2453 }
2454 std::vector<uint8_t>* pResponse =
2455 std::get_if<std::vector<uint8_t>>(&value);
2456 if (!pResponse)
2457 {
2458 phosphor::logging::log<phosphor::logging::level::ERR>(
2459 "Error to get effective RotationRankOrder property");
2460 return ipmi::responseResponseError();
2461 }
2462 return ipmi::responseSuccess(parameter, *pResponse);
2463 }
Cheng C Yang773703a2019-08-15 09:41:11 +08002464 default:
2465 {
2466 return ipmi::response(ccParameterNotSupported);
2467 }
2468 }
2469}
2470
Zhu, Yungebe560b02019-04-21 21:19:21 -04002471ipmi::RspType<> ipmiOEMSetFaultIndication(uint8_t sourceId, uint8_t faultType,
2472 uint8_t faultState,
2473 uint8_t faultGroup,
2474 std::array<uint8_t, 8>& ledStateData)
2475{
Zhu, Yungebe560b02019-04-21 21:19:21 -04002476 constexpr auto maxFaultType = static_cast<size_t>(RemoteFaultType::max);
2477 static const std::array<std::string, maxFaultType> faultNames = {
2478 "faultFan", "faultTemp", "faultPower",
2479 "faultDriveSlot", "faultSoftware", "faultMemory"};
Zhu, Yungebe560b02019-04-21 21:19:21 -04002480
2481 constexpr uint8_t maxFaultSource = 0x4;
2482 constexpr uint8_t skipLEDs = 0xFF;
2483 constexpr uint8_t pinSize = 64;
2484 constexpr uint8_t groupSize = 16;
Zhikui Rence4e73f2019-12-06 13:59:47 -08002485 constexpr uint8_t groupNum = 5; // 4 for fault memory, 1 for faultFan
Zhu, Yungebe560b02019-04-21 21:19:21 -04002486
Zhikui Rence4e73f2019-12-06 13:59:47 -08002487 // same pin names need to be defined in dts file
2488 static const std::array<std::array<std::string, groupSize>, groupNum>
2489 faultLedPinNames = {{
2490 "LED_CPU1_CH1_DIMM1_FAULT",
2491 "LED_CPU1_CH1_DIMM2_FAULT",
2492 "LED_CPU1_CH2_DIMM1_FAULT",
2493 "LED_CPU1_CH2_DIMM2_FAULT",
2494 "LED_CPU1_CH3_DIMM1_FAULT",
2495 "LED_CPU1_CH3_DIMM2_FAULT",
2496 "LED_CPU1_CH4_DIMM1_FAULT",
2497 "LED_CPU1_CH4_DIMM2_FAULT",
2498 "LED_CPU1_CH5_DIMM1_FAULT",
2499 "LED_CPU1_CH5_DIMM2_FAULT",
2500 "LED_CPU1_CH6_DIMM1_FAULT",
2501 "LED_CPU1_CH6_DIMM2_FAULT",
2502 "",
2503 "",
2504 "",
2505 "", // end of group1
2506 "LED_CPU2_CH1_DIMM1_FAULT",
2507 "LED_CPU2_CH1_DIMM2_FAULT",
2508 "LED_CPU2_CH2_DIMM1_FAULT",
2509 "LED_CPU2_CH2_DIMM2_FAULT",
2510 "LED_CPU2_CH3_DIMM1_FAULT",
2511 "LED_CPU2_CH3_DIMM2_FAULT",
2512 "LED_CPU2_CH4_DIMM1_FAULT",
2513 "LED_CPU2_CH4_DIMM2_FAULT",
2514 "LED_CPU2_CH5_DIMM1_FAULT",
2515 "LED_CPU2_CH5_DIMM2_FAULT",
2516 "LED_CPU2_CH6_DIMM1_FAULT",
2517 "LED_CPU2_CH6_DIMM2_FAULT",
2518 "",
2519 "",
2520 "",
2521 "", // endof group2
2522 "LED_CPU3_CH1_DIMM1_FAULT",
2523 "LED_CPU3_CH1_DIMM2_FAULT",
2524 "LED_CPU3_CH2_DIMM1_FAULT",
2525 "LED_CPU3_CH2_DIMM2_FAULT",
2526 "LED_CPU3_CH3_DIMM1_FAULT",
2527 "LED_CPU3_CH3_DIMM2_FAULT",
2528 "LED_CPU3_CH4_DIMM1_FAULT",
2529 "LED_CPU3_CH4_DIMM2_FAULT",
2530 "LED_CPU3_CH5_DIMM1_FAULT",
2531 "LED_CPU3_CH5_DIMM2_FAULT",
2532 "LED_CPU3_CH6_DIMM1_FAULT",
2533 "LED_CPU3_CH6_DIMM2_FAULT",
2534 "",
2535 "",
2536 "",
2537 "", // end of group3
2538 "LED_CPU4_CH1_DIMM1_FAULT",
2539 "LED_CPU4_CH1_DIMM2_FAULT",
2540 "LED_CPU4_CH2_DIMM1_FAULT",
2541 "LED_CPU4_CH2_DIMM2_FAULT",
2542 "LED_CPU4_CH3_DIMM1_FAULT",
2543 "LED_CPU4_CH3_DIMM2_FAULT",
2544 "LED_CPU4_CH4_DIMM1_FAULT",
2545 "LED_CPU4_CH4_DIMM2_FAULT",
2546 "LED_CPU4_CH5_DIMM1_FAULT",
2547 "LED_CPU4_CH5_DIMM2_FAULT",
2548 "LED_CPU4_CH6_DIMM1_FAULT",
2549 "LED_CPU4_CH6_DIMM2_FAULT",
2550 "",
2551 "",
2552 "",
2553 "", // end of group4
2554 "LED_FAN1_FAULT",
2555 "LED_FAN2_FAULT",
2556 "LED_FAN3_FAULT",
2557 "LED_FAN4_FAULT",
2558 "LED_FAN5_FAULT",
2559 "LED_FAN6_FAULT",
2560 "LED_FAN7_FAULT",
2561 "LED_FAN8_FAULT",
2562 "",
2563 "",
2564 "",
2565 "",
2566 "",
2567 "",
2568 "",
2569 "" // end of group5
2570 }};
Zhu, Yungebe560b02019-04-21 21:19:21 -04002571
Zhikui Rence4e73f2019-12-06 13:59:47 -08002572 // Validate the source, fault type --
2573 // (Byte 1) sourceId: Unspecified, Hot-Swap Controller 0, Hot-Swap
2574 // Controller 1, BIOS (Byte 2) fault type: fan, temperature, power,
2575 // driveslot, software, memory (Byte 3) FaultState: OK, Degraded,
2576 // Non-Critical, Critical, Non-Recoverable, (Byte 4) is faultGroup,
2577 // definition differs based on fault type (Byte 2)
2578 // Type Fan=> Group: 0=FanGroupID, FF-not used
2579 // Byte 5-11 00h, not used
2580 // Byte12 FanLedState [7:0]-Fans 7:0
2581 // Type Memory=> Group: 0 = DIMM GroupID, FF-not used
2582 // Byte 5:12 - DIMM LED state (64bit field, LS Byte first)
2583 // [63:48] = CPU4 channels 7:0, 2 bits per channel
2584 // [47:32] = CPU3 channels 7:0, 2 bits per channel
2585 // [31:16] = CPU2 channels 7:0, 2 bits per channel
2586 // [15:0] = CPU1 channels 7:0, 2 bits per channel
2587 // Type Other=> Component Fault LED Group ID, not used set to 0xFF
2588 // Byte[5:12]: reserved 0x00h
Zhu, Yungebe560b02019-04-21 21:19:21 -04002589 if ((sourceId >= maxFaultSource) ||
2590 (faultType >= static_cast<int8_t>(RemoteFaultType::max)) ||
2591 (faultState >= static_cast<int8_t>(RemoteFaultState::maxFaultState)) ||
2592 (faultGroup >= static_cast<int8_t>(DimmFaultType::maxFaultGroup)))
2593 {
2594 return ipmi::responseParmOutOfRange();
2595 }
2596
Zhikui Rence4e73f2019-12-06 13:59:47 -08002597 size_t pinGroupOffset = 0;
2598 size_t pinGroupMax = pinSize / groupSize;
2599 if (RemoteFaultType::fan == RemoteFaultType(faultType))
Zhu, Yungebe560b02019-04-21 21:19:21 -04002600 {
Zhikui Rence4e73f2019-12-06 13:59:47 -08002601 pinGroupOffset = 4;
2602 pinGroupMax = groupNum - pinSize / groupSize;
Zhu, Yungebe560b02019-04-21 21:19:21 -04002603 }
2604
2605 switch (RemoteFaultType(faultType))
2606 {
2607 case (RemoteFaultType::fan):
2608 case (RemoteFaultType::memory):
2609 {
2610 if (faultGroup == skipLEDs)
2611 {
2612 return ipmi::responseSuccess();
2613 }
Zhu, Yungebe560b02019-04-21 21:19:21 -04002614 // calculate led state bit filed count, each byte has 8bits
2615 // the maximum bits will be 8 * 8 bits
2616 constexpr uint8_t size = sizeof(ledStateData) * 8;
Zhikui Rence4e73f2019-12-06 13:59:47 -08002617
2618 // assemble ledState
2619 uint64_t ledState = 0;
2620 bool hasError = false;
Zhu, Yungebe560b02019-04-21 21:19:21 -04002621 for (int i = 0; i < sizeof(ledStateData); i++)
2622 {
2623 ledState = (uint64_t)(ledState << 8);
2624 ledState = (uint64_t)(ledState | (uint64_t)ledStateData[i]);
2625 }
Zhu, Yungebe560b02019-04-21 21:19:21 -04002626 std::bitset<size> ledStateBits(ledState);
Zhu, Yungebe560b02019-04-21 21:19:21 -04002627
Zhikui Rence4e73f2019-12-06 13:59:47 -08002628 for (int group = 0; group < pinGroupMax; group++)
2629 {
2630 for (int i = 0; i < groupSize; i++)
2631 { // skip non-existing pins
2632 if (0 == faultLedPinNames[group + pinGroupOffset][i].size())
2633 {
2634 continue;
2635 }
Zhu, Yungebe560b02019-04-21 21:19:21 -04002636
Zhikui Rence4e73f2019-12-06 13:59:47 -08002637 gpiod::line line = gpiod::find_line(
2638 faultLedPinNames[group + pinGroupOffset][i]);
2639 if (!line)
2640 {
2641 phosphor::logging::log<phosphor::logging::level::ERR>(
2642 "Not Find Led Gpio Device!",
2643 phosphor::logging::entry(
2644 "DEVICE=%s",
2645 faultLedPinNames[group + pinGroupOffset][i]
2646 .c_str()));
2647 hasError = true;
2648 continue;
2649 }
Zhu, Yungebe560b02019-04-21 21:19:21 -04002650
Zhikui Rence4e73f2019-12-06 13:59:47 -08002651 bool activeHigh =
2652 (line.active_state() == gpiod::line::ACTIVE_HIGH);
2653 try
2654 {
2655 line.request(
2656 {"faultLed", gpiod::line_request::DIRECTION_OUTPUT,
2657 activeHigh
2658 ? 0
2659 : gpiod::line_request::FLAG_ACTIVE_LOW});
2660 line.set_value(ledStateBits[i + group * groupSize]);
2661 }
2662 catch (std::system_error&)
2663 {
2664 phosphor::logging::log<phosphor::logging::level::ERR>(
2665 "Error write Led Gpio Device!",
2666 phosphor::logging::entry(
2667 "DEVICE=%s",
2668 faultLedPinNames[group + pinGroupOffset][i]
2669 .c_str()));
2670 hasError = true;
2671 continue;
2672 }
2673 } // for int i
2674 }
2675 if (hasError)
2676 {
2677 return ipmi::responseResponseError();
Zhu, Yungebe560b02019-04-21 21:19:21 -04002678 }
2679 break;
2680 }
2681 default:
2682 {
2683 // now only support two fault types
2684 return ipmi::responseParmOutOfRange();
2685 }
Zhikui Rence4e73f2019-12-06 13:59:47 -08002686 } // switch
Zhu, Yungebe560b02019-04-21 21:19:21 -04002687 return ipmi::responseSuccess();
2688}
2689
Richard Marian Thomaiyarea537d52019-04-24 21:33:48 +05302690ipmi::RspType<uint8_t> ipmiOEMReadBoardProductId()
2691{
2692 uint8_t prodId = 0;
2693 try
2694 {
Vernon Mauery15419dd2019-05-24 09:40:30 -07002695 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
Richard Marian Thomaiyarea537d52019-04-24 21:33:48 +05302696 const DbusObjectInfo& object = getDbusObject(
Vernon Mauery15419dd2019-05-24 09:40:30 -07002697 *dbus, "xyz.openbmc_project.Inventory.Item.Board",
Richard Marian Thomaiyarea537d52019-04-24 21:33:48 +05302698 "/xyz/openbmc_project/inventory/system/board/", "Baseboard");
2699 const Value& propValue = getDbusProperty(
Vernon Mauery15419dd2019-05-24 09:40:30 -07002700 *dbus, object.second, object.first,
Suryakanth Sekar6c57e5c2020-01-10 17:11:58 +05302701 "xyz.openbmc_project.Inventory.Item.Board.Motherboard",
2702 "ProductId");
Richard Marian Thomaiyarea537d52019-04-24 21:33:48 +05302703 prodId = static_cast<uint8_t>(std::get<uint64_t>(propValue));
2704 }
2705 catch (std::exception& e)
2706 {
2707 phosphor::logging::log<phosphor::logging::level::ERR>(
2708 "ipmiOEMReadBoardProductId: Product ID read failed!",
2709 phosphor::logging::entry("ERR=%s", e.what()));
2710 }
2711 return ipmi::responseSuccess(prodId);
2712}
2713
Richard Marian Thomaiyard801e462019-06-20 01:05:40 +05302714/** @brief implements the get security mode command
2715 * @param ctx - ctx pointer
2716 *
2717 * @returns IPMI completion code with following data
2718 * - restriction mode value - As specified in
2719 * xyz.openbmc_project.Control.Security.RestrictionMode.interface.yaml
2720 * - special mode value - As specified in
2721 * xyz.openbmc_project.Control.Security.SpecialMode.interface.yaml
2722 */
2723ipmi::RspType<uint8_t, uint8_t> ipmiGetSecurityMode(ipmi::Context::ptr ctx)
2724{
2725 namespace securityNameSpace =
2726 sdbusplus::xyz::openbmc_project::Control::Security::server;
2727 uint8_t restrictionModeValue = 0;
2728 uint8_t specialModeValue = 0;
2729
2730 boost::system::error_code ec;
2731 auto varRestrMode = ctx->bus->yield_method_call<std::variant<std::string>>(
James Feist28c72902019-09-16 10:34:07 -07002732 ctx->yield, ec, restricionModeService, restricionModeBasePath,
Richard Marian Thomaiyard801e462019-06-20 01:05:40 +05302733 dBusPropertyIntf, dBusPropertyGetMethod, restricionModeIntf,
2734 restricionModeProperty);
2735 if (ec)
2736 {
2737 phosphor::logging::log<phosphor::logging::level::ERR>(
2738 "ipmiGetSecurityMode: failed to get RestrictionMode property",
2739 phosphor::logging::entry("ERROR=%s", ec.message().c_str()));
2740 return ipmi::responseUnspecifiedError();
2741 }
2742 restrictionModeValue = static_cast<uint8_t>(
2743 securityNameSpace::RestrictionMode::convertModesFromString(
2744 std::get<std::string>(varRestrMode)));
Richard Marian Thomaiyar8d4f8d72019-11-11 12:06:40 +05302745 auto varSpecialMode =
2746 ctx->bus->yield_method_call<std::variant<std::string>>(
2747 ctx->yield, ec, specialModeService, specialModeBasePath,
2748 dBusPropertyIntf, dBusPropertyGetMethod, specialModeIntf,
2749 specialModeProperty);
Richard Marian Thomaiyard801e462019-06-20 01:05:40 +05302750 if (ec)
2751 {
2752 phosphor::logging::log<phosphor::logging::level::ERR>(
2753 "ipmiGetSecurityMode: failed to get SpecialMode property",
2754 phosphor::logging::entry("ERROR=%s", ec.message().c_str()));
2755 // fall through, let us not worry about SpecialMode property, which is
2756 // not required in user scenario
2757 }
2758 else
2759 {
Richard Marian Thomaiyar8d4f8d72019-11-11 12:06:40 +05302760 specialModeValue = static_cast<uint8_t>(
2761 securityNameSpace::SpecialMode::convertModesFromString(
2762 std::get<std::string>(varSpecialMode)));
Richard Marian Thomaiyard801e462019-06-20 01:05:40 +05302763 }
2764 return ipmi::responseSuccess(restrictionModeValue, specialModeValue);
2765}
2766
2767/** @brief implements the set security mode command
2768 * Command allows to upgrade the restriction mode and won't allow
2769 * to downgrade from system interface
2770 * @param ctx - ctx pointer
2771 * @param restrictionMode - restriction mode value to be set.
2772 *
2773 * @returns IPMI completion code
2774 */
2775ipmi::RspType<> ipmiSetSecurityMode(ipmi::Context::ptr ctx,
Richard Marian Thomaiyar10791062019-11-11 12:19:53 +05302776 uint8_t restrictionMode,
2777 std::optional<uint8_t> specialMode)
Richard Marian Thomaiyard801e462019-06-20 01:05:40 +05302778{
Richard Marian Thomaiyar10791062019-11-11 12:19:53 +05302779#ifndef BMC_VALIDATION_UNSECURE_FEATURE
2780 if (specialMode)
2781 {
2782 return ipmi::responseReqDataLenInvalid();
2783 }
2784#endif
Richard Marian Thomaiyard801e462019-06-20 01:05:40 +05302785 namespace securityNameSpace =
2786 sdbusplus::xyz::openbmc_project::Control::Security::server;
2787
2788 ChannelInfo chInfo;
2789 if (getChannelInfo(ctx->channel, chInfo) != ccSuccess)
2790 {
2791 phosphor::logging::log<phosphor::logging::level::ERR>(
2792 "ipmiSetSecurityMode: Failed to get Channel Info",
2793 phosphor::logging::entry("CHANNEL=%d", ctx->channel));
2794 return ipmi::responseUnspecifiedError();
2795 }
2796 auto reqMode =
2797 static_cast<securityNameSpace::RestrictionMode::Modes>(restrictionMode);
2798
2799 if ((reqMode < securityNameSpace::RestrictionMode::Modes::Provisioning) ||
2800 (reqMode >
2801 securityNameSpace::RestrictionMode::Modes::ProvisionedHostDisabled))
2802 {
2803 return ipmi::responseInvalidFieldRequest();
2804 }
2805
2806 boost::system::error_code ec;
2807 auto varRestrMode = ctx->bus->yield_method_call<std::variant<std::string>>(
James Feist28c72902019-09-16 10:34:07 -07002808 ctx->yield, ec, restricionModeService, restricionModeBasePath,
Richard Marian Thomaiyard801e462019-06-20 01:05:40 +05302809 dBusPropertyIntf, dBusPropertyGetMethod, restricionModeIntf,
2810 restricionModeProperty);
2811 if (ec)
2812 {
2813 phosphor::logging::log<phosphor::logging::level::ERR>(
2814 "ipmiSetSecurityMode: failed to get RestrictionMode property",
2815 phosphor::logging::entry("ERROR=%s", ec.message().c_str()));
2816 return ipmi::responseUnspecifiedError();
2817 }
2818 auto currentRestrictionMode =
2819 securityNameSpace::RestrictionMode::convertModesFromString(
2820 std::get<std::string>(varRestrMode));
2821
2822 if (chInfo.mediumType !=
2823 static_cast<uint8_t>(EChannelMediumType::lan8032) &&
2824 currentRestrictionMode > reqMode)
2825 {
2826 phosphor::logging::log<phosphor::logging::level::ERR>(
2827 "ipmiSetSecurityMode - Downgrading security mode not supported "
2828 "through system interface",
2829 phosphor::logging::entry(
2830 "CUR_MODE=%d", static_cast<uint8_t>(currentRestrictionMode)),
2831 phosphor::logging::entry("REQ_MODE=%d", restrictionMode));
2832 return ipmi::responseCommandNotAvailable();
2833 }
2834
2835 ec.clear();
2836 ctx->bus->yield_method_call<>(
James Feist28c72902019-09-16 10:34:07 -07002837 ctx->yield, ec, restricionModeService, restricionModeBasePath,
Richard Marian Thomaiyard801e462019-06-20 01:05:40 +05302838 dBusPropertyIntf, dBusPropertySetMethod, restricionModeIntf,
2839 restricionModeProperty,
2840 static_cast<std::variant<std::string>>(
2841 securityNameSpace::convertForMessage(reqMode)));
2842
2843 if (ec)
2844 {
2845 phosphor::logging::log<phosphor::logging::level::ERR>(
2846 "ipmiSetSecurityMode: failed to set RestrictionMode property",
2847 phosphor::logging::entry("ERROR=%s", ec.message().c_str()));
2848 return ipmi::responseUnspecifiedError();
2849 }
Richard Marian Thomaiyar10791062019-11-11 12:19:53 +05302850
2851#ifdef BMC_VALIDATION_UNSECURE_FEATURE
2852 if (specialMode)
2853 {
Jayaprakash Mutyalad77489f2020-09-05 01:00:04 +00002854 constexpr uint8_t mfgMode = 0x01;
2855 // Manufacturing mode is reserved. So can't enable this mode.
2856 if (specialMode.value() == mfgMode)
2857 {
2858 phosphor::logging::log<phosphor::logging::level::INFO>(
2859 "ipmiSetSecurityMode: Can't enable Manufacturing mode");
2860 return ipmi::responseInvalidFieldRequest();
2861 }
2862
Richard Marian Thomaiyar10791062019-11-11 12:19:53 +05302863 ec.clear();
2864 ctx->bus->yield_method_call<>(
2865 ctx->yield, ec, specialModeService, specialModeBasePath,
2866 dBusPropertyIntf, dBusPropertySetMethod, specialModeIntf,
2867 specialModeProperty,
2868 static_cast<std::variant<std::string>>(
2869 securityNameSpace::convertForMessage(
2870 static_cast<securityNameSpace::SpecialMode::Modes>(
2871 specialMode.value()))));
2872
2873 if (ec)
2874 {
2875 phosphor::logging::log<phosphor::logging::level::ERR>(
2876 "ipmiSetSecurityMode: failed to set SpecialMode property",
2877 phosphor::logging::entry("ERROR=%s", ec.message().c_str()));
2878 return ipmi::responseUnspecifiedError();
2879 }
2880 }
2881#endif
Richard Marian Thomaiyard801e462019-06-20 01:05:40 +05302882 return ipmi::responseSuccess();
2883}
2884
Vernon Mauery4ac799d2019-05-20 15:50:37 -07002885ipmi::RspType<uint8_t /* restore status */>
2886 ipmiRestoreConfiguration(const std::array<uint8_t, 3>& clr, uint8_t cmd)
2887{
2888 static constexpr std::array<uint8_t, 3> expClr = {'C', 'L', 'R'};
2889
2890 if (clr != expClr)
2891 {
2892 return ipmi::responseInvalidFieldRequest();
2893 }
2894 constexpr uint8_t cmdStatus = 0;
2895 constexpr uint8_t cmdDefaultRestore = 0xaa;
2896 constexpr uint8_t cmdFullRestore = 0xbb;
2897 constexpr uint8_t cmdFormat = 0xcc;
2898
2899 constexpr const char* restoreOpFname = "/tmp/.rwfs/.restore_op";
2900
2901 switch (cmd)
2902 {
2903 case cmdStatus:
2904 break;
2905 case cmdDefaultRestore:
2906 case cmdFullRestore:
2907 case cmdFormat:
2908 {
2909 // write file to rwfs root
2910 int value = (cmd - 1) & 0x03; // map aa, bb, cc => 1, 2, 3
2911 std::ofstream restoreFile(restoreOpFname);
2912 if (!restoreFile)
2913 {
2914 return ipmi::responseUnspecifiedError();
2915 }
2916 restoreFile << value << "\n";
Arun P. Mohananba1fbc82021-04-26 11:26:53 +05302917
2918 phosphor::logging::log<phosphor::logging::level::WARNING>(
2919 "Restore to default will be performed on next BMC boot",
2920 phosphor::logging::entry("ACTION=0x%0X", cmd));
2921
Vernon Mauery4ac799d2019-05-20 15:50:37 -07002922 break;
2923 }
2924 default:
2925 return ipmi::responseInvalidFieldRequest();
2926 }
2927
2928 constexpr uint8_t restorePending = 0;
2929 constexpr uint8_t restoreComplete = 1;
2930
2931 uint8_t restoreStatus = std::filesystem::exists(restoreOpFname)
2932 ? restorePending
2933 : restoreComplete;
2934 return ipmi::responseSuccess(restoreStatus);
2935}
2936
Chen Yugang39736d52019-07-12 16:24:33 +08002937ipmi::RspType<uint8_t> ipmiOEMGetNmiSource(void)
2938{
2939 uint8_t bmcSource;
Chen Yugang97cf96e2019-11-01 08:55:11 +08002940 namespace nmi = sdbusplus::xyz::openbmc_project::Chassis::Control::server;
Chen Yugang39736d52019-07-12 16:24:33 +08002941
2942 try
2943 {
2944 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
2945 std::string service =
2946 getService(*dbus, oemNmiSourceIntf, oemNmiSourceObjPath);
2947 Value variant =
2948 getDbusProperty(*dbus, service, oemNmiSourceObjPath,
2949 oemNmiSourceIntf, oemNmiBmcSourceObjPathProp);
2950
2951 switch (nmi::NMISource::convertBMCSourceSignalFromString(
2952 std::get<std::string>(variant)))
2953 {
2954 case nmi::NMISource::BMCSourceSignal::None:
2955 bmcSource = static_cast<uint8_t>(NmiSource::none);
2956 break;
Chen Yugang97cf96e2019-11-01 08:55:11 +08002957 case nmi::NMISource::BMCSourceSignal::FrontPanelButton:
2958 bmcSource = static_cast<uint8_t>(NmiSource::frontPanelButton);
Chen Yugang39736d52019-07-12 16:24:33 +08002959 break;
Chen Yugang97cf96e2019-11-01 08:55:11 +08002960 case nmi::NMISource::BMCSourceSignal::Watchdog:
2961 bmcSource = static_cast<uint8_t>(NmiSource::watchdog);
Chen Yugang39736d52019-07-12 16:24:33 +08002962 break;
2963 case nmi::NMISource::BMCSourceSignal::ChassisCmd:
2964 bmcSource = static_cast<uint8_t>(NmiSource::chassisCmd);
2965 break;
2966 case nmi::NMISource::BMCSourceSignal::MemoryError:
2967 bmcSource = static_cast<uint8_t>(NmiSource::memoryError);
2968 break;
Chen Yugang97cf96e2019-11-01 08:55:11 +08002969 case nmi::NMISource::BMCSourceSignal::PciBusError:
2970 bmcSource = static_cast<uint8_t>(NmiSource::pciBusError);
Chen Yugang39736d52019-07-12 16:24:33 +08002971 break;
Chen Yugang97cf96e2019-11-01 08:55:11 +08002972 case nmi::NMISource::BMCSourceSignal::PCH:
2973 bmcSource = static_cast<uint8_t>(NmiSource::pch);
Chen Yugang39736d52019-07-12 16:24:33 +08002974 break;
Chen Yugang97cf96e2019-11-01 08:55:11 +08002975 case nmi::NMISource::BMCSourceSignal::Chipset:
2976 bmcSource = static_cast<uint8_t>(NmiSource::chipset);
Chen Yugang39736d52019-07-12 16:24:33 +08002977 break;
2978 default:
2979 phosphor::logging::log<phosphor::logging::level::ERR>(
2980 "NMI source: invalid property!",
2981 phosphor::logging::entry(
2982 "PROP=%s", std::get<std::string>(variant).c_str()));
2983 return ipmi::responseResponseError();
2984 }
2985 }
2986 catch (sdbusplus::exception::SdBusError& e)
2987 {
2988 phosphor::logging::log<phosphor::logging::level::ERR>(e.what());
2989 return ipmi::responseResponseError();
2990 }
2991
2992 return ipmi::responseSuccess(bmcSource);
2993}
2994
2995ipmi::RspType<> ipmiOEMSetNmiSource(uint8_t sourceId)
2996{
Chen Yugang97cf96e2019-11-01 08:55:11 +08002997 namespace nmi = sdbusplus::xyz::openbmc_project::Chassis::Control::server;
Chen Yugang39736d52019-07-12 16:24:33 +08002998
2999 nmi::NMISource::BMCSourceSignal bmcSourceSignal =
3000 nmi::NMISource::BMCSourceSignal::None;
3001
3002 switch (NmiSource(sourceId))
3003 {
3004 case NmiSource::none:
3005 bmcSourceSignal = nmi::NMISource::BMCSourceSignal::None;
3006 break;
Chen Yugang97cf96e2019-11-01 08:55:11 +08003007 case NmiSource::frontPanelButton:
3008 bmcSourceSignal = nmi::NMISource::BMCSourceSignal::FrontPanelButton;
Chen Yugang39736d52019-07-12 16:24:33 +08003009 break;
Chen Yugang97cf96e2019-11-01 08:55:11 +08003010 case NmiSource::watchdog:
3011 bmcSourceSignal = nmi::NMISource::BMCSourceSignal::Watchdog;
Chen Yugang39736d52019-07-12 16:24:33 +08003012 break;
3013 case NmiSource::chassisCmd:
3014 bmcSourceSignal = nmi::NMISource::BMCSourceSignal::ChassisCmd;
3015 break;
3016 case NmiSource::memoryError:
3017 bmcSourceSignal = nmi::NMISource::BMCSourceSignal::MemoryError;
3018 break;
Chen Yugang97cf96e2019-11-01 08:55:11 +08003019 case NmiSource::pciBusError:
3020 bmcSourceSignal = nmi::NMISource::BMCSourceSignal::PciBusError;
Chen Yugang39736d52019-07-12 16:24:33 +08003021 break;
Chen Yugang97cf96e2019-11-01 08:55:11 +08003022 case NmiSource::pch:
3023 bmcSourceSignal = nmi::NMISource::BMCSourceSignal::PCH;
Chen Yugang39736d52019-07-12 16:24:33 +08003024 break;
Chen Yugang97cf96e2019-11-01 08:55:11 +08003025 case NmiSource::chipset:
3026 bmcSourceSignal = nmi::NMISource::BMCSourceSignal::Chipset;
Chen Yugang39736d52019-07-12 16:24:33 +08003027 break;
3028 default:
3029 phosphor::logging::log<phosphor::logging::level::ERR>(
3030 "NMI source: invalid property!");
3031 return ipmi::responseResponseError();
3032 }
3033
3034 try
3035 {
3036 // keep NMI signal source
3037 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
3038 std::string service =
3039 getService(*dbus, oemNmiSourceIntf, oemNmiSourceObjPath);
Chen Yugang97cf96e2019-11-01 08:55:11 +08003040 setDbusProperty(*dbus, service, oemNmiSourceObjPath, oemNmiSourceIntf,
3041 oemNmiBmcSourceObjPathProp,
3042 nmi::convertForMessage(bmcSourceSignal));
Chen Yugang99be6332019-08-09 16:20:48 +08003043 // set Enabled property to inform NMI source handling
3044 // to trigger a NMI_OUT BSOD.
3045 // if it's triggered by NMI source property changed,
3046 // NMI_OUT BSOD could be missed if the same source occurs twice in a row
3047 if (bmcSourceSignal != nmi::NMISource::BMCSourceSignal::None)
3048 {
3049 setDbusProperty(*dbus, service, oemNmiSourceObjPath,
3050 oemNmiSourceIntf, oemNmiEnabledObjPathProp,
3051 static_cast<bool>(true));
3052 }
Chen Yugang39736d52019-07-12 16:24:33 +08003053 }
3054 catch (sdbusplus::exception_t& e)
3055 {
3056 phosphor::logging::log<phosphor::logging::level::ERR>(e.what());
3057 return ipmi::responseResponseError();
3058 }
3059
3060 return ipmi::responseSuccess();
3061}
3062
James Feist63efafa2019-07-24 12:39:21 -07003063namespace dimmOffset
3064{
3065constexpr const char* dimmPower = "DimmPower";
3066constexpr const char* staticCltt = "StaticCltt";
3067constexpr const char* offsetPath = "/xyz/openbmc_project/Inventory/Item/Dimm";
3068constexpr const char* offsetInterface =
3069 "xyz.openbmc_project.Inventory.Item.Dimm.Offset";
3070constexpr const char* property = "DimmOffset";
3071
3072}; // namespace dimmOffset
3073
3074ipmi::RspType<>
3075 ipmiOEMSetDimmOffset(uint8_t type,
3076 const std::vector<std::tuple<uint8_t, uint8_t>>& data)
3077{
3078 if (type != static_cast<uint8_t>(dimmOffsetTypes::dimmPower) &&
3079 type != static_cast<uint8_t>(dimmOffsetTypes::staticCltt))
3080 {
3081 return ipmi::responseInvalidFieldRequest();
3082 }
3083
3084 if (data.empty())
3085 {
3086 return ipmi::responseInvalidFieldRequest();
3087 }
3088 nlohmann::json json;
3089
3090 std::ifstream jsonStream(dimmOffsetFile);
3091 if (jsonStream.good())
3092 {
3093 json = nlohmann::json::parse(jsonStream, nullptr, false);
3094 if (json.is_discarded())
3095 {
3096 json = nlohmann::json();
3097 }
3098 jsonStream.close();
3099 }
3100
3101 std::string typeName;
3102 if (type == static_cast<uint8_t>(dimmOffsetTypes::dimmPower))
3103 {
3104 typeName = dimmOffset::dimmPower;
3105 }
3106 else
3107 {
3108 typeName = dimmOffset::staticCltt;
3109 }
3110
3111 nlohmann::json& field = json[typeName];
3112
3113 for (const auto& [index, value] : data)
3114 {
3115 field[index] = value;
3116 }
3117
3118 for (nlohmann::json& val : field)
3119 {
3120 if (val == nullptr)
3121 {
3122 val = static_cast<uint8_t>(0);
3123 }
3124 }
3125
3126 std::ofstream output(dimmOffsetFile);
3127 if (!output.good())
3128 {
3129 std::cerr << "Error writing json file\n";
3130 return ipmi::responseResponseError();
3131 }
3132
3133 output << json.dump(4);
3134
3135 if (type == static_cast<uint8_t>(dimmOffsetTypes::staticCltt))
3136 {
3137 std::shared_ptr<sdbusplus::asio::connection> bus = getSdBus();
3138
3139 std::variant<std::vector<uint8_t>> offsets =
3140 field.get<std::vector<uint8_t>>();
3141 auto call = bus->new_method_call(
3142 settingsBusName, dimmOffset::offsetPath, PROP_INTF, "Set");
3143 call.append(dimmOffset::offsetInterface, dimmOffset::property, offsets);
3144 try
3145 {
3146 bus->call(call);
3147 }
3148 catch (sdbusplus::exception_t& e)
3149 {
3150 phosphor::logging::log<phosphor::logging::level::ERR>(
3151 "ipmiOEMSetDimmOffset: can't set dimm offsets!",
3152 phosphor::logging::entry("ERR=%s", e.what()));
3153 return ipmi::responseResponseError();
3154 }
3155 }
3156
3157 return ipmi::responseSuccess();
3158}
3159
3160ipmi::RspType<uint8_t> ipmiOEMGetDimmOffset(uint8_t type, uint8_t index)
3161{
3162
3163 if (type != static_cast<uint8_t>(dimmOffsetTypes::dimmPower) &&
3164 type != static_cast<uint8_t>(dimmOffsetTypes::staticCltt))
3165 {
3166 return ipmi::responseInvalidFieldRequest();
3167 }
3168
3169 std::ifstream jsonStream(dimmOffsetFile);
3170
3171 auto json = nlohmann::json::parse(jsonStream, nullptr, false);
3172 if (json.is_discarded())
3173 {
3174 std::cerr << "File error in " << dimmOffsetFile << "\n";
3175 return ipmi::responseResponseError();
3176 }
3177
3178 std::string typeName;
3179 if (type == static_cast<uint8_t>(dimmOffsetTypes::dimmPower))
3180 {
3181 typeName = dimmOffset::dimmPower;
3182 }
3183 else
3184 {
3185 typeName = dimmOffset::staticCltt;
3186 }
3187
3188 auto it = json.find(typeName);
3189 if (it == json.end())
3190 {
3191 return ipmi::responseInvalidFieldRequest();
3192 }
3193
3194 if (it->size() <= index)
3195 {
3196 return ipmi::responseInvalidFieldRequest();
3197 }
3198
3199 uint8_t resp = it->at(index).get<uint8_t>();
3200 return ipmi::responseSuccess(resp);
3201}
3202
Chen,Yugang4f7e76b2019-08-20 09:28:06 +08003203namespace boot_options
3204{
3205
3206using namespace sdbusplus::xyz::openbmc_project::Control::Boot::server;
3207using IpmiValue = uint8_t;
3208constexpr auto ipmiDefault = 0;
3209
3210std::map<IpmiValue, Source::Sources> sourceIpmiToDbus = {
3211 {0x01, Source::Sources::Network},
3212 {0x02, Source::Sources::Disk},
3213 {0x05, Source::Sources::ExternalMedia},
3214 {0x0f, Source::Sources::RemovableMedia},
3215 {ipmiDefault, Source::Sources::Default}};
3216
3217std::map<IpmiValue, Mode::Modes> modeIpmiToDbus = {
Chen Yugangca12a7b2019-09-03 18:11:44 +08003218 {0x06, Mode::Modes::Setup}, {ipmiDefault, Mode::Modes::Regular}};
Chen,Yugang4f7e76b2019-08-20 09:28:06 +08003219
3220std::map<Source::Sources, IpmiValue> sourceDbusToIpmi = {
3221 {Source::Sources::Network, 0x01},
3222 {Source::Sources::Disk, 0x02},
3223 {Source::Sources::ExternalMedia, 0x05},
3224 {Source::Sources::RemovableMedia, 0x0f},
3225 {Source::Sources::Default, ipmiDefault}};
3226
3227std::map<Mode::Modes, IpmiValue> modeDbusToIpmi = {
Chen Yugangca12a7b2019-09-03 18:11:44 +08003228 {Mode::Modes::Setup, 0x06}, {Mode::Modes::Regular, ipmiDefault}};
Chen,Yugang4f7e76b2019-08-20 09:28:06 +08003229
3230static constexpr auto bootModeIntf = "xyz.openbmc_project.Control.Boot.Mode";
3231static constexpr auto bootSourceIntf =
3232 "xyz.openbmc_project.Control.Boot.Source";
3233static constexpr auto enabledIntf = "xyz.openbmc_project.Object.Enable";
3234static constexpr auto persistentObjPath =
3235 "/xyz/openbmc_project/control/host0/boot";
3236static constexpr auto oneTimePath =
3237 "/xyz/openbmc_project/control/host0/boot/one_time";
3238static constexpr auto bootSourceProp = "BootSource";
3239static constexpr auto bootModeProp = "BootMode";
3240static constexpr auto oneTimeBootEnableProp = "Enabled";
3241static constexpr auto httpBootMode =
3242 "xyz.openbmc_project.Control.Boot.Source.Sources.Http";
3243
3244enum class BootOptionParameter : size_t
3245{
3246 setInProgress = 0x0,
3247 bootFlags = 0x5,
3248};
3249static constexpr uint8_t setComplete = 0x0;
3250static constexpr uint8_t setInProgress = 0x1;
3251static uint8_t transferStatus = setComplete;
3252static constexpr uint8_t setParmVersion = 0x01;
3253static constexpr uint8_t setParmBootFlagsPermanent = 0x40;
3254static constexpr uint8_t setParmBootFlagsValidOneTime = 0x80;
3255static constexpr uint8_t setParmBootFlagsValidPermanent = 0xC0;
3256static constexpr uint8_t httpBoot = 0xd;
3257static constexpr uint8_t bootSourceMask = 0x3c;
3258
3259} // namespace boot_options
3260
3261ipmi::RspType<uint8_t, // version
3262 uint8_t, // param
3263 uint8_t, // data0, dependent on parameter
3264 std::optional<uint8_t> // data1, dependent on parameter
3265 >
3266 ipmiOemGetEfiBootOptions(uint8_t parameter, uint8_t set, uint8_t block)
3267{
3268 using namespace boot_options;
3269 uint8_t bootOption = 0;
3270
3271 if (parameter == static_cast<uint8_t>(BootOptionParameter::setInProgress))
3272 {
3273 return ipmi::responseSuccess(setParmVersion, parameter, transferStatus,
3274 std::nullopt);
3275 }
3276
3277 if (parameter != static_cast<uint8_t>(BootOptionParameter::bootFlags))
3278 {
3279 phosphor::logging::log<phosphor::logging::level::ERR>(
3280 "Unsupported parameter");
3281 return ipmi::responseResponseError();
3282 }
3283
3284 try
3285 {
3286 auto oneTimeEnabled = false;
3287 // read one time Enabled property
3288 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
3289 std::string service = getService(*dbus, enabledIntf, oneTimePath);
3290 Value variant = getDbusProperty(*dbus, service, oneTimePath,
3291 enabledIntf, oneTimeBootEnableProp);
3292 oneTimeEnabled = std::get<bool>(variant);
3293
3294 // get BootSource and BootMode properties
3295 // according to oneTimeEnable
3296 auto bootObjPath = oneTimePath;
3297 if (oneTimeEnabled == false)
3298 {
3299 bootObjPath = persistentObjPath;
3300 }
3301
3302 service = getService(*dbus, bootModeIntf, bootObjPath);
3303 variant = getDbusProperty(*dbus, service, bootObjPath, bootModeIntf,
3304 bootModeProp);
3305
3306 auto bootMode =
3307 Mode::convertModesFromString(std::get<std::string>(variant));
3308
3309 service = getService(*dbus, bootSourceIntf, bootObjPath);
3310 variant = getDbusProperty(*dbus, service, bootObjPath, bootSourceIntf,
3311 bootSourceProp);
3312
3313 if (std::get<std::string>(variant) == httpBootMode)
3314 {
3315 bootOption = httpBoot;
3316 }
3317 else
3318 {
3319 auto bootSource = Source::convertSourcesFromString(
3320 std::get<std::string>(variant));
3321 bootOption = sourceDbusToIpmi.at(bootSource);
3322 if (Source::Sources::Default == bootSource)
3323 {
3324 bootOption = modeDbusToIpmi.at(bootMode);
3325 }
3326 }
3327
3328 uint8_t oneTime = oneTimeEnabled ? setParmBootFlagsValidOneTime
3329 : setParmBootFlagsValidPermanent;
3330 bootOption <<= 2; // shift for responseconstexpr
3331 return ipmi::responseSuccess(setParmVersion, parameter, oneTime,
3332 bootOption);
3333 }
3334 catch (sdbusplus::exception_t& e)
3335 {
3336 phosphor::logging::log<phosphor::logging::level::ERR>(e.what());
3337 return ipmi::responseResponseError();
3338 }
3339}
3340
3341ipmi::RspType<> ipmiOemSetEfiBootOptions(uint8_t bootFlag, uint8_t bootParam,
3342 std::optional<uint8_t> bootOption)
3343{
3344 using namespace boot_options;
3345 auto oneTimeEnabled = false;
3346
3347 if (bootFlag == static_cast<uint8_t>(BootOptionParameter::setInProgress))
3348 {
3349 if (bootOption)
3350 {
3351 return ipmi::responseReqDataLenInvalid();
3352 }
3353
3354 if (transferStatus == setInProgress)
3355 {
3356 phosphor::logging::log<phosphor::logging::level::ERR>(
3357 "boot option set in progress!");
3358 return ipmi::responseResponseError();
3359 }
3360
3361 transferStatus = bootParam;
3362 return ipmi::responseSuccess();
3363 }
3364
3365 if (bootFlag != (uint8_t)BootOptionParameter::bootFlags)
3366 {
3367 phosphor::logging::log<phosphor::logging::level::ERR>(
3368 "Unsupported parameter");
3369 return ipmi::responseResponseError();
3370 }
3371
3372 if (!bootOption)
3373 {
3374 return ipmi::responseReqDataLenInvalid();
3375 }
3376
3377 if (((bootOption.value() & bootSourceMask) >> 2) !=
3378 httpBoot) // not http boot, exit
3379 {
3380 phosphor::logging::log<phosphor::logging::level::ERR>(
3381 "wrong boot option parameter!");
3382 return ipmi::responseParmOutOfRange();
3383 }
3384
3385 try
3386 {
3387 bool permanent = (bootParam & setParmBootFlagsPermanent) ==
3388 setParmBootFlagsPermanent;
3389
3390 // read one time Enabled property
3391 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
3392 std::string service = getService(*dbus, enabledIntf, oneTimePath);
3393 Value variant = getDbusProperty(*dbus, service, oneTimePath,
3394 enabledIntf, oneTimeBootEnableProp);
3395 oneTimeEnabled = std::get<bool>(variant);
3396
3397 /*
3398 * Check if the current boot setting is onetime or permanent, if the
3399 * request in the command is otherwise, then set the "Enabled"
3400 * property in one_time object path to 'True' to indicate onetime
3401 * and 'False' to indicate permanent.
3402 *
3403 * Once the onetime/permanent setting is applied, then the bootMode
3404 * and bootSource is updated for the corresponding object.
3405 */
3406 if (permanent == oneTimeEnabled)
3407 {
3408 setDbusProperty(*dbus, service, oneTimePath, enabledIntf,
3409 oneTimeBootEnableProp, !permanent);
3410 }
3411
3412 // set BootSource and BootMode properties
3413 // according to oneTimeEnable or persistent
3414 auto bootObjPath = oneTimePath;
3415 if (oneTimeEnabled == false)
3416 {
3417 bootObjPath = persistentObjPath;
3418 }
3419 std::string bootMode =
3420 "xyz.openbmc_project.Control.Boot.Mode.Modes.Regular";
3421 std::string bootSource = httpBootMode;
3422
3423 service = getService(*dbus, bootModeIntf, bootObjPath);
3424 setDbusProperty(*dbus, service, bootObjPath, bootModeIntf, bootModeProp,
3425 bootMode);
3426
3427 service = getService(*dbus, bootSourceIntf, bootObjPath);
3428 setDbusProperty(*dbus, service, bootObjPath, bootSourceIntf,
3429 bootSourceProp, bootSource);
3430 }
3431 catch (sdbusplus::exception_t& e)
3432 {
3433 phosphor::logging::log<phosphor::logging::level::ERR>(e.what());
3434 return ipmi::responseResponseError();
3435 }
3436
3437 return ipmi::responseSuccess();
3438}
3439
Cheng C Yang4e6ee152019-09-25 10:27:44 +08003440using BasicVariantType =
3441 std::variant<std::vector<std::string>, std::vector<uint64_t>, std::string,
3442 int64_t, uint64_t, double, int32_t, uint32_t, int16_t,
3443 uint16_t, uint8_t, bool>;
3444using PropertyMapType =
3445 boost::container::flat_map<std::string, BasicVariantType>;
3446static constexpr const std::array<const char*, 1> psuPresenceTypes = {
3447 "xyz.openbmc_project.Configuration.PSUPresence"};
3448int getPSUAddress(ipmi::Context::ptr ctx, uint8_t& bus,
3449 std::vector<uint64_t>& addrTable)
3450{
3451 boost::system::error_code ec;
3452 GetSubTreeType subtree = ctx->bus->yield_method_call<GetSubTreeType>(
3453 ctx->yield, ec, "xyz.openbmc_project.ObjectMapper",
3454 "/xyz/openbmc_project/object_mapper",
3455 "xyz.openbmc_project.ObjectMapper", "GetSubTree",
3456 "/xyz/openbmc_project/inventory/system", 3, psuPresenceTypes);
3457 if (ec)
3458 {
3459 phosphor::logging::log<phosphor::logging::level::ERR>(
3460 "Failed to set dbus property to cold redundancy");
3461 return -1;
3462 }
3463 for (const auto& object : subtree)
3464 {
3465 std::string pathName = object.first;
3466 for (const auto& serviceIface : object.second)
3467 {
3468 std::string serviceName = serviceIface.first;
3469
3470 ec.clear();
3471 PropertyMapType propMap =
3472 ctx->bus->yield_method_call<PropertyMapType>(
3473 ctx->yield, ec, serviceName, pathName,
3474 "org.freedesktop.DBus.Properties", "GetAll",
3475 "xyz.openbmc_project.Configuration.PSUPresence");
3476 if (ec)
3477 {
3478 phosphor::logging::log<phosphor::logging::level::ERR>(
3479 "Failed to set dbus property to cold redundancy");
3480 return -1;
3481 }
3482 auto psuBus = std::get_if<uint64_t>(&propMap["Bus"]);
3483 auto psuAddress =
3484 std::get_if<std::vector<uint64_t>>(&propMap["Address"]);
3485
3486 if (psuBus == nullptr || psuAddress == nullptr)
3487 {
3488 std::cerr << "error finding necessary "
3489 "entry in configuration\n";
3490 return -1;
3491 }
3492 bus = static_cast<uint8_t>(*psuBus);
3493 addrTable = *psuAddress;
3494 return 0;
3495 }
3496 }
3497 return -1;
3498}
3499
3500static const constexpr uint8_t addrOffset = 8;
3501static const constexpr uint8_t psuRevision = 0xd9;
3502static const constexpr uint8_t defaultPSUBus = 7;
3503// Second Minor, Primary Minor, Major
3504static const constexpr size_t verLen = 3;
3505ipmi::RspType<std::vector<uint8_t>> ipmiOEMGetPSUVersion(ipmi::Context::ptr ctx)
3506{
3507 uint8_t bus = defaultPSUBus;
3508 std::vector<uint64_t> addrTable;
3509 std::vector<uint8_t> result;
3510 if (getPSUAddress(ctx, bus, addrTable))
3511 {
3512 std::cerr << "Failed to get PSU bus and address\n";
3513 return ipmi::responseResponseError();
3514 }
3515
3516 for (const auto& slaveAddr : addrTable)
3517 {
3518 std::vector<uint8_t> writeData = {psuRevision};
3519 std::vector<uint8_t> readBuf(verLen);
3520 uint8_t addr = static_cast<uint8_t>(slaveAddr) + addrOffset;
3521 std::string i2cBus = "/dev/i2c-" + std::to_string(bus);
3522
3523 auto retI2C = ipmi::i2cWriteRead(i2cBus, addr, writeData, readBuf);
3524 if (retI2C != ipmi::ccSuccess)
3525 {
3526 for (size_t idx = 0; idx < verLen; idx++)
3527 {
3528 result.emplace_back(0x00);
3529 }
3530 }
3531 else
3532 {
3533 for (const uint8_t& data : readBuf)
3534 {
3535 result.emplace_back(data);
3536 }
3537 }
3538 }
3539
3540 return ipmi::responseSuccess(result);
3541}
3542
srikanta mondal2030d7c2020-05-03 17:25:25 +00003543std::optional<uint8_t> getMultiNodeInfoPresence(ipmi::Context::ptr ctx,
3544 const std::string& name)
3545{
3546 Value dbusValue = 0;
3547 std::string serviceName;
3548
3549 boost::system::error_code ec =
3550 ipmi::getService(ctx, multiNodeIntf, multiNodeObjPath, serviceName);
3551
3552 if (ec)
3553 {
3554 phosphor::logging::log<phosphor::logging::level::ERR>(
3555 "Failed to perform Multinode getService.");
3556 return std::nullopt;
3557 }
3558
3559 ec = ipmi::getDbusProperty(ctx, serviceName, multiNodeObjPath,
3560 multiNodeIntf, name, dbusValue);
3561 if (ec)
3562 {
3563 phosphor::logging::log<phosphor::logging::level::ERR>(
3564 "Failed to perform Multinode get property");
3565 return std::nullopt;
3566 }
3567
3568 auto multiNodeVal = std::get_if<uint8_t>(&dbusValue);
3569 if (!multiNodeVal)
3570 {
3571 phosphor::logging::log<phosphor::logging::level::ERR>(
3572 "getMultiNodeInfoPresence: error to get multinode");
3573 return std::nullopt;
3574 }
3575 return *multiNodeVal;
3576}
3577
3578/** @brief implements OEM get reading command
3579 * @param domain ID
3580 * @param reading Type
3581 * - 00h = platform Power Consumption
3582 * - 01h = inlet Air Temp
3583 * - 02h = icc_TDC from PECI
3584 * @param reserved, write as 0000h
3585 *
3586 * @returns IPMI completion code plus response data
3587 * - response
3588 * - domain ID
3589 * - reading Type
3590 * - 00h = platform Power Consumption
3591 * - 01h = inlet Air Temp
3592 * - 02h = icc_TDC from PECI
3593 * - reading
3594 */
3595ipmi::RspType<uint4_t, // domain ID
3596 uint4_t, // reading Type
3597 uint16_t // reading Value
3598 >
3599 ipmiOEMGetReading(ipmi::Context::ptr ctx, uint4_t domainId,
3600 uint4_t readingType, uint16_t reserved)
3601{
3602 constexpr uint8_t platformPower = 0;
3603 constexpr uint8_t inletAirTemp = 1;
3604 constexpr uint8_t iccTdc = 2;
3605
3606 if ((static_cast<uint8_t>(readingType) > iccTdc) || domainId || reserved)
3607 {
3608 return ipmi::responseInvalidFieldRequest();
3609 }
3610
3611 // This command should run only from multi-node product.
3612 // For all other platforms this command will return invalid.
3613
3614 std::optional<uint8_t> nodeInfo =
3615 getMultiNodeInfoPresence(ctx, "NodePresence");
3616 if (!nodeInfo || !*nodeInfo)
3617 {
3618 return ipmi::responseInvalidCommand();
3619 }
3620
3621 uint16_t oemReadingValue = 0;
3622 if (static_cast<uint8_t>(readingType) == inletAirTemp)
3623 {
3624 double value = 0;
3625 boost::system::error_code ec = ipmi::getDbusProperty(
3626 ctx, "xyz.openbmc_project.HwmonTempSensor",
3627 "/xyz/openbmc_project/sensors/temperature/Inlet_BRD_Temp",
3628 "xyz.openbmc_project.Sensor.Value", "Value", value);
3629 if (ec)
3630 {
3631 phosphor::logging::log<phosphor::logging::level::ERR>(
3632 "Failed to get BMC Get OEM temperature",
3633 phosphor::logging::entry("EXCEPTION=%s", ec.message().c_str()));
3634 return ipmi::responseUnspecifiedError();
3635 }
3636 // Take the Inlet temperature
3637 oemReadingValue = static_cast<uint16_t>(value);
3638 }
3639 else
3640 {
3641 phosphor::logging::log<phosphor::logging::level::ERR>(
3642 "Currently Get OEM Reading support only for Inlet Air Temp");
3643 return ipmi::responseParmOutOfRange();
3644 }
3645 return ipmi::responseSuccess(domainId, readingType, oemReadingValue);
3646}
3647
AppaRao Puli28972062019-11-11 02:04:45 +05303648/** @brief implements the maximum size of
3649 * bridgeable messages used between KCS and
3650 * IPMB interfacesget security mode command.
3651 *
3652 * @returns IPMI completion code with following data
3653 * - KCS Buffer Size (In multiples of four bytes)
3654 * - IPMB Buffer Size (In multiples of four bytes)
3655 **/
3656ipmi::RspType<uint8_t, uint8_t> ipmiOEMGetBufferSize()
3657{
3658 // for now this is hard coded; really this number is dependent on
3659 // the BMC kcs driver as well as the host kcs driver....
3660 // we can't know the latter.
3661 uint8_t kcsMaxBufferSize = 63 / 4;
3662 uint8_t ipmbMaxBufferSize = 128 / 4;
3663
3664 return ipmi::responseSuccess(kcsMaxBufferSize, ipmbMaxBufferSize);
3665}
3666
Jason M. Bills64796042018-10-03 16:51:55 -07003667static void registerOEMFunctions(void)
Jia, Chunhuia835eaa2018-09-05 09:00:41 +08003668{
3669 phosphor::logging::log<phosphor::logging::level::INFO>(
3670 "Registering OEM commands");
Vernon Mauery98bbf692019-09-16 11:14:59 -07003671 ipmiPrintAndRegister(intel::netFnGeneral,
3672 intel::general::cmdGetChassisIdentifier, NULL,
3673 ipmiOEMGetChassisIdentifier,
3674 PRIVILEGE_USER); // get chassis identifier
3675
3676 ipmiPrintAndRegister(intel::netFnGeneral, intel::general::cmdSetSystemGUID,
3677 NULL, ipmiOEMSetSystemGUID,
3678 PRIVILEGE_ADMIN); // set system guid
Jason M. Billsb02bf092019-08-15 13:01:56 -07003679
3680 // <Disable BMC System Reset Action>
Vernon Mauery98bbf692019-09-16 11:14:59 -07003681 registerHandler(prioOemBase, intel::netFnGeneral,
3682 intel::general::cmdDisableBMCSystemReset, Privilege::Admin,
3683 ipmiOEMDisableBMCSystemReset);
3684
Jason M. Billsb02bf092019-08-15 13:01:56 -07003685 // <Get BMC Reset Disables>
Vernon Mauery98bbf692019-09-16 11:14:59 -07003686 registerHandler(prioOemBase, intel::netFnGeneral,
3687 intel::general::cmdGetBMCResetDisables, Privilege::Admin,
3688 ipmiOEMGetBMCResetDisables);
Jason M. Billsb02bf092019-08-15 13:01:56 -07003689
Vernon Mauery98bbf692019-09-16 11:14:59 -07003690 ipmiPrintAndRegister(intel::netFnGeneral, intel::general::cmdSetBIOSID,
3691 NULL, ipmiOEMSetBIOSID, PRIVILEGE_ADMIN);
Jia, Chunhuicc49b542019-03-20 15:41:07 +08003692
Chen Yugang7a04f3a2019-10-08 11:12:35 +08003693 registerHandler(prioOemBase, intel::netFnGeneral,
3694 intel::general::cmdGetOEMDeviceInfo, Privilege::User,
3695 ipmiOEMGetDeviceInfo);
Jia, Chunhuicc49b542019-03-20 15:41:07 +08003696
Vernon Mauery98bbf692019-09-16 11:14:59 -07003697 ipmiPrintAndRegister(intel::netFnGeneral,
3698 intel::general::cmdGetAICSlotFRUIDSlotPosRecords, NULL,
3699 ipmiOEMGetAICFRU, PRIVILEGE_USER);
Suryakanth Sekard509eb92018-11-15 17:44:11 +05303700
Vernon Mauery98bbf692019-09-16 11:14:59 -07003701 registerHandler(prioOpenBmcBase, intel::netFnGeneral,
3702 intel::general::cmdSendEmbeddedFWUpdStatus,
3703 Privilege::Operator, ipmiOEMSendEmbeddedFwUpdStatus);
Suryakanth Sekard509eb92018-11-15 17:44:11 +05303704
Rajashekar Gade Reddy2b664d52020-03-23 22:09:00 +05303705 registerHandler(prioOpenBmcBase, intel::netFnApp, intel::app::cmdSlotIpmb,
3706 Privilege::Admin, ipmiOEMSlotIpmb);
3707
Vernon Mauery98bbf692019-09-16 11:14:59 -07003708 ipmiPrintAndRegister(intel::netFnGeneral,
3709 intel::general::cmdSetPowerRestoreDelay, NULL,
3710 ipmiOEMSetPowerRestoreDelay, PRIVILEGE_OPERATOR);
3711
3712 ipmiPrintAndRegister(intel::netFnGeneral,
3713 intel::general::cmdGetPowerRestoreDelay, NULL,
3714 ipmiOEMGetPowerRestoreDelay, PRIVILEGE_USER);
3715
3716 registerHandler(prioOpenBmcBase, intel::netFnGeneral,
3717 intel::general::cmdSetOEMUser2Activation,
3718 Privilege::Callback, ipmiOEMSetUser2Activation);
3719
3720 registerHandler(prioOpenBmcBase, intel::netFnGeneral,
3721 intel::general::cmdSetSpecialUserPassword,
3722 Privilege::Callback, ipmiOEMSetSpecialUserPassword);
Richard Marian Thomaiyarfc5e9852019-04-14 15:06:27 +05303723
Jason M. Bills42bd9c82019-06-28 16:39:34 -07003724 // <Get Processor Error Config>
Vernon Mauery98bbf692019-09-16 11:14:59 -07003725 registerHandler(prioOemBase, intel::netFnGeneral,
3726 intel::general::cmdGetProcessorErrConfig, Privilege::User,
3727 ipmiOEMGetProcessorErrConfig);
3728
Jason M. Bills42bd9c82019-06-28 16:39:34 -07003729 // <Set Processor Error Config>
Vernon Mauery98bbf692019-09-16 11:14:59 -07003730 registerHandler(prioOemBase, intel::netFnGeneral,
3731 intel::general::cmdSetProcessorErrConfig, Privilege::Admin,
3732 ipmiOEMSetProcessorErrConfig);
Jason M. Bills42bd9c82019-06-28 16:39:34 -07003733
Vernon Mauery98bbf692019-09-16 11:14:59 -07003734 ipmiPrintAndRegister(intel::netFnGeneral,
3735 intel::general::cmdSetShutdownPolicy, NULL,
3736 ipmiOEMSetShutdownPolicy, PRIVILEGE_ADMIN);
James Feist91244a62019-02-19 15:04:54 -08003737
Vernon Mauery98bbf692019-09-16 11:14:59 -07003738 ipmiPrintAndRegister(intel::netFnGeneral,
3739 intel::general::cmdGetShutdownPolicy, NULL,
3740 ipmiOEMGetShutdownPolicy, PRIVILEGE_ADMIN);
James Feist91244a62019-02-19 15:04:54 -08003741
anil kumar appanaf945eee2019-09-25 23:29:11 +00003742 registerHandler(prioOemBase, intel::netFnGeneral,
3743 intel::general::cmdSetFanConfig, Privilege::User,
3744 ipmiOEMSetFanConfig);
James Feist91244a62019-02-19 15:04:54 -08003745
Vernon Mauery98bbf692019-09-16 11:14:59 -07003746 registerHandler(prioOemBase, intel::netFnGeneral,
3747 intel::general::cmdGetFanConfig, Privilege::User,
3748 ipmiOEMGetFanConfig);
James Feist5f957ca2019-03-14 15:33:55 -07003749
Vernon Mauery98bbf692019-09-16 11:14:59 -07003750 registerHandler(prioOemBase, intel::netFnGeneral,
3751 intel::general::cmdGetFanSpeedOffset, Privilege::User,
3752 ipmiOEMGetFanSpeedOffset);
James Feistacc8a4e2019-04-02 14:23:57 -07003753
Vernon Mauery98bbf692019-09-16 11:14:59 -07003754 registerHandler(prioOemBase, intel::netFnGeneral,
3755 intel::general::cmdSetFanSpeedOffset, Privilege::User,
3756 ipmiOEMSetFanSpeedOffset);
James Feistacc8a4e2019-04-02 14:23:57 -07003757
Vernon Mauery98bbf692019-09-16 11:14:59 -07003758 registerHandler(prioOemBase, intel::netFnGeneral,
3759 intel::general::cmdSetFscParameter, Privilege::User,
3760 ipmiOEMSetFscParameter);
James Feist5f957ca2019-03-14 15:33:55 -07003761
Vernon Mauery98bbf692019-09-16 11:14:59 -07003762 registerHandler(prioOemBase, intel::netFnGeneral,
3763 intel::general::cmdGetFscParameter, Privilege::User,
3764 ipmiOEMGetFscParameter);
Richard Marian Thomaiyarea537d52019-04-24 21:33:48 +05303765
Vernon Mauery98bbf692019-09-16 11:14:59 -07003766 registerHandler(prioOpenBmcBase, intel::netFnGeneral,
3767 intel::general::cmdReadBaseBoardProductId, Privilege::Admin,
3768 ipmiOEMReadBoardProductId);
Chen Yugang39736d52019-07-12 16:24:33 +08003769
Vernon Mauery98bbf692019-09-16 11:14:59 -07003770 registerHandler(prioOemBase, intel::netFnGeneral,
3771 intel::general::cmdGetNmiStatus, Privilege::User,
3772 ipmiOEMGetNmiSource);
Chen Yugang39736d52019-07-12 16:24:33 +08003773
Vernon Mauery98bbf692019-09-16 11:14:59 -07003774 registerHandler(prioOemBase, intel::netFnGeneral,
3775 intel::general::cmdSetNmiStatus, Privilege::Operator,
3776 ipmiOEMSetNmiSource);
Chen,Yugang4f7e76b2019-08-20 09:28:06 +08003777
Vernon Mauery98bbf692019-09-16 11:14:59 -07003778 registerHandler(prioOemBase, intel::netFnGeneral,
3779 intel::general::cmdGetEfiBootOptions, Privilege::User,
3780 ipmiOemGetEfiBootOptions);
Chen,Yugang4f7e76b2019-08-20 09:28:06 +08003781
Vernon Mauery98bbf692019-09-16 11:14:59 -07003782 registerHandler(prioOemBase, intel::netFnGeneral,
3783 intel::general::cmdSetEfiBootOptions, Privilege::Operator,
3784 ipmiOemSetEfiBootOptions);
Richard Marian Thomaiyard801e462019-06-20 01:05:40 +05303785
Vernon Mauery98bbf692019-09-16 11:14:59 -07003786 registerHandler(prioOemBase, intel::netFnGeneral,
3787 intel::general::cmdGetSecurityMode, Privilege::User,
3788 ipmiGetSecurityMode);
Richard Marian Thomaiyard801e462019-06-20 01:05:40 +05303789
Vernon Mauery98bbf692019-09-16 11:14:59 -07003790 registerHandler(prioOemBase, intel::netFnGeneral,
3791 intel::general::cmdSetSecurityMode, Privilege::Admin,
3792 ipmiSetSecurityMode);
Vernon Mauery4ac799d2019-05-20 15:50:37 -07003793
NITIN SHARMAabd11ca2019-06-12 12:31:42 +00003794 registerHandler(prioOemBase, intel::netFnGeneral,
3795 intel::general::cmdGetLEDStatus, Privilege::Admin,
3796 ipmiOEMGetLEDStatus);
Cheng C Yang773703a2019-08-15 09:41:11 +08003797
Vernon Mauery98bbf692019-09-16 11:14:59 -07003798 ipmiPrintAndRegister(ipmi::intel::netFnPlatform,
3799 ipmi::intel::platform::cmdCfgHostSerialPortSpeed, NULL,
3800 ipmiOEMCfgHostSerialPortSpeed, PRIVILEGE_ADMIN);
3801
3802 registerHandler(prioOemBase, intel::netFnGeneral,
3803 intel::general::cmdSetFaultIndication, Privilege::Operator,
3804 ipmiOEMSetFaultIndication);
3805
3806 registerHandler(prioOemBase, intel::netFnGeneral,
3807 intel::general::cmdSetColdRedundancyConfig, Privilege::User,
3808 ipmiOEMSetCRConfig);
3809
3810 registerHandler(prioOemBase, intel::netFnGeneral,
3811 intel::general::cmdGetColdRedundancyConfig, Privilege::User,
3812 ipmiOEMGetCRConfig);
3813
3814 registerHandler(prioOemBase, intel::netFnGeneral,
3815 intel::general::cmdRestoreConfiguration, Privilege::Admin,
Vernon Mauery4ac799d2019-05-20 15:50:37 -07003816 ipmiRestoreConfiguration);
James Feist63efafa2019-07-24 12:39:21 -07003817
Vernon Mauery98bbf692019-09-16 11:14:59 -07003818 registerHandler(prioOemBase, intel::netFnGeneral,
3819 intel::general::cmdSetDimmOffset, Privilege::Operator,
3820 ipmiOEMSetDimmOffset);
James Feist63efafa2019-07-24 12:39:21 -07003821
Vernon Mauery98bbf692019-09-16 11:14:59 -07003822 registerHandler(prioOemBase, intel::netFnGeneral,
3823 intel::general::cmdGetDimmOffset, Privilege::Operator,
3824 ipmiOEMGetDimmOffset);
Chen Yugangca12a7b2019-09-03 18:11:44 +08003825
Cheng C Yang4e6ee152019-09-25 10:27:44 +08003826 registerHandler(prioOemBase, intel::netFnGeneral,
3827 intel::general::cmdGetPSUVersion, Privilege::User,
3828 ipmiOEMGetPSUVersion);
AppaRao Puli28972062019-11-11 02:04:45 +05303829
3830 registerHandler(prioOemBase, intel::netFnGeneral,
3831 intel::general::cmdGetBufferSize, Privilege::User,
3832 ipmiOEMGetBufferSize);
srikanta mondal2030d7c2020-05-03 17:25:25 +00003833
3834 registerHandler(prioOemBase, intel::netFnGeneral,
3835 intel::general::cmdOEMGetReading, Privilege::User,
3836 ipmiOEMGetReading);
Jia, Chunhuia835eaa2018-09-05 09:00:41 +08003837}
3838
3839} // namespace ipmi