blob: bf23241e260501317d22517676ba5d0c8f8e774e [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
Jia, Chunhuicc49b542019-03-20 15:41:07 +080021#include <systemd/sd-journal.h>
Jia, Chunhuia835eaa2018-09-05 09:00:41 +080022
Chen Yugang7a04f3a2019-10-08 11:12:35 +080023#include <appcommands.hpp>
James Feist91244a62019-02-19 15:04:54 -080024#include <boost/container/flat_map.hpp>
Yong Li23737fe2019-02-19 08:49:55 +080025#include <boost/process/child.hpp>
26#include <boost/process/io.hpp>
Yong Li0669d192019-05-06 14:01:46 +080027#include <com/intel/Control/OCOTShutdownPolicy/server.hpp>
Jason M. Bills64796042018-10-03 16:51:55 -070028#include <commandutils.hpp>
Zhikui Rence4e73f2019-12-06 13:59:47 -080029#include <gpiod.hpp>
Jia, Chunhuicc49b542019-03-20 15:41:07 +080030#include <ipmid/api.hpp>
Vernon Mauery5480ef62019-03-20 13:43:11 -070031#include <ipmid/utils.hpp>
James Feist63efafa2019-07-24 12:39:21 -070032#include <nlohmann/json.hpp>
Jia, Chunhuia835eaa2018-09-05 09:00:41 +080033#include <oemcommands.hpp>
Jia, Chunhuia835eaa2018-09-05 09:00:41 +080034#include <phosphor-logging/log.hpp>
35#include <sdbusplus/bus.hpp>
Suryakanth Sekard509eb92018-11-15 17:44:11 +053036#include <sdbusplus/message/types.hpp>
Chen Yugang97cf96e2019-11-01 08:55:11 +080037#include <xyz/openbmc_project/Chassis/Control/NMISource/server.hpp>
Chen,Yugang4f7e76b2019-08-20 09:28:06 +080038#include <xyz/openbmc_project/Control/Boot/Mode/server.hpp>
39#include <xyz/openbmc_project/Control/Boot/Source/server.hpp>
Cheng C Yang773703a2019-08-15 09:41:11 +080040#include <xyz/openbmc_project/Control/PowerSupplyRedundancy/server.hpp>
Richard Marian Thomaiyard801e462019-06-20 01:05:40 +053041#include <xyz/openbmc_project/Control/Security/RestrictionMode/server.hpp>
Richard Marian Thomaiyar8d4f8d72019-11-11 12:06:40 +053042#include <xyz/openbmc_project/Control/Security/SpecialMode/server.hpp>
Jia, Chunhuia835eaa2018-09-05 09:00:41 +080043
James Feistfcd2d3a2020-05-28 10:38:15 -070044#include <array>
45#include <filesystem>
46#include <iostream>
47#include <regex>
48#include <string>
49#include <variant>
50#include <vector>
51
Jia, Chunhuia835eaa2018-09-05 09:00:41 +080052namespace ipmi
53{
Jason M. Bills64796042018-10-03 16:51:55 -070054static void registerOEMFunctions() __attribute__((constructor));
Vernon Mauery4ac799d2019-05-20 15:50:37 -070055
Jason M. Bills64796042018-10-03 16:51:55 -070056static constexpr size_t maxFRUStringLength = 0x3F;
Jia, Chunhuia835eaa2018-09-05 09:00:41 +080057
Suryakanth Sekard509eb92018-11-15 17:44:11 +053058static constexpr auto ethernetIntf =
59 "xyz.openbmc_project.Network.EthernetInterface";
60static constexpr auto networkIPIntf = "xyz.openbmc_project.Network.IP";
61static constexpr auto networkService = "xyz.openbmc_project.Network";
62static constexpr auto networkRoot = "/xyz/openbmc_project/network";
63
Chen Yugang97cf96e2019-11-01 08:55:11 +080064static constexpr const char* oemNmiSourceIntf =
65 "xyz.openbmc_project.Chassis.Control.NMISource";
Chen Yugang39736d52019-07-12 16:24:33 +080066static constexpr const char* oemNmiSourceObjPath =
Chen Yugang97cf96e2019-11-01 08:55:11 +080067 "/xyz/openbmc_project/Chassis/Control/NMISource";
Chen Yugang39736d52019-07-12 16:24:33 +080068static constexpr const char* oemNmiBmcSourceObjPathProp = "BMCSource";
69static constexpr const char* oemNmiEnabledObjPathProp = "Enabled";
70
James Feist63efafa2019-07-24 12:39:21 -070071static constexpr const char* dimmOffsetFile = "/var/lib/ipmi/ipmi_dimms.json";
srikanta mondal2030d7c2020-05-03 17:25:25 +000072static constexpr const char* multiNodeObjPath =
73 "/xyz/openbmc_project/MultiNode/Status";
74static constexpr const char* multiNodeIntf =
75 "xyz.openbmc_project.Chassis.MultiNode";
James Feist63efafa2019-07-24 12:39:21 -070076
Chen Yugang39736d52019-07-12 16:24:33 +080077enum class NmiSource : uint8_t
78{
79 none = 0,
Chen Yugang97cf96e2019-11-01 08:55:11 +080080 frontPanelButton = 1,
81 watchdog = 2,
82 chassisCmd = 3,
83 memoryError = 4,
84 pciBusError = 5,
85 pch = 6,
86 chipset = 7,
Chen Yugang39736d52019-07-12 16:24:33 +080087};
88
Suryakanth Sekar822b0b42019-11-15 18:32:53 +053089enum class SpecialUserIndex : uint8_t
90{
91 rootUser = 0,
92 atScaleDebugUser = 1
93};
94
Richard Marian Thomaiyard801e462019-06-20 01:05:40 +053095static constexpr const char* restricionModeService =
96 "xyz.openbmc_project.RestrictionMode.Manager";
97static constexpr const char* restricionModeBasePath =
98 "/xyz/openbmc_project/control/security/restriction_mode";
99static constexpr const char* restricionModeIntf =
100 "xyz.openbmc_project.Control.Security.RestrictionMode";
101static constexpr const char* restricionModeProperty = "RestrictionMode";
102
103static constexpr const char* specialModeService =
104 "xyz.openbmc_project.SpecialMode";
105static constexpr const char* specialModeBasePath =
Richard Marian Thomaiyara7b74282019-09-22 21:53:14 +0530106 "/xyz/openbmc_project/security/special_mode";
Richard Marian Thomaiyard801e462019-06-20 01:05:40 +0530107static constexpr const char* specialModeIntf =
108 "xyz.openbmc_project.Security.SpecialMode";
109static constexpr const char* specialModeProperty = "SpecialMode";
110
111static constexpr const char* dBusPropertyIntf =
112 "org.freedesktop.DBus.Properties";
113static constexpr const char* dBusPropertyGetMethod = "Get";
114static constexpr const char* dBusPropertySetMethod = "Set";
115
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800116// return code: 0 successful
117int8_t getChassisSerialNumber(sdbusplus::bus::bus& bus, std::string& serial)
118{
119 std::string objpath = "/xyz/openbmc_project/FruDevice";
120 std::string intf = "xyz.openbmc_project.FruDeviceManager";
121 std::string service = getService(bus, intf, objpath);
122 ObjectValueTree valueTree = getManagedObjects(bus, service, "/");
123 if (valueTree.empty())
124 {
125 phosphor::logging::log<phosphor::logging::level::ERR>(
126 "No object implements interface",
127 phosphor::logging::entry("INTF=%s", intf.c_str()));
128 return -1;
129 }
130
Jason M. Bills64796042018-10-03 16:51:55 -0700131 for (const auto& item : valueTree)
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800132 {
133 auto interface = item.second.find("xyz.openbmc_project.FruDevice");
134 if (interface == item.second.end())
135 {
136 continue;
137 }
138
139 auto property = interface->second.find("CHASSIS_SERIAL_NUMBER");
140 if (property == interface->second.end())
141 {
142 continue;
143 }
144
145 try
146 {
147 Value variant = property->second;
Vernon Mauery8166c8d2019-05-23 11:22:30 -0700148 std::string& result = std::get<std::string>(variant);
Jason M. Bills64796042018-10-03 16:51:55 -0700149 if (result.size() > maxFRUStringLength)
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800150 {
151 phosphor::logging::log<phosphor::logging::level::ERR>(
152 "FRU serial number exceed maximum length");
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800153 return -1;
154 }
Jason M. Bills64796042018-10-03 16:51:55 -0700155 serial = result;
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800156 return 0;
157 }
Vernon Mauery8166c8d2019-05-23 11:22:30 -0700158 catch (std::bad_variant_access& e)
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800159 {
Jason M. Bills64796042018-10-03 16:51:55 -0700160 phosphor::logging::log<phosphor::logging::level::ERR>(e.what());
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800161 return -1;
162 }
163 }
164 return -1;
165}
Jason M. Bills64796042018-10-03 16:51:55 -0700166
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800167// Returns the Chassis Identifier (serial #)
168ipmi_ret_t ipmiOEMGetChassisIdentifier(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
169 ipmi_request_t request,
170 ipmi_response_t response,
Jason M. Bills64796042018-10-03 16:51:55 -0700171 ipmi_data_len_t dataLen,
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800172 ipmi_context_t context)
173{
174 std::string serial;
Jason M. Bills64796042018-10-03 16:51:55 -0700175 if (*dataLen != 0) // invalid request if there are extra parameters
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800176 {
Jason M. Bills64796042018-10-03 16:51:55 -0700177 *dataLen = 0;
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800178 return IPMI_CC_REQ_DATA_LEN_INVALID;
179 }
Vernon Mauery15419dd2019-05-24 09:40:30 -0700180 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
181 if (getChassisSerialNumber(*dbus, serial) == 0)
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800182 {
Jason M. Bills64796042018-10-03 16:51:55 -0700183 *dataLen = serial.size(); // length will never exceed response length
184 // as it is checked in getChassisSerialNumber
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800185 char* resp = static_cast<char*>(response);
Jason M. Bills64796042018-10-03 16:51:55 -0700186 serial.copy(resp, *dataLen);
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800187 return IPMI_CC_OK;
188 }
Jason M. Bills64796042018-10-03 16:51:55 -0700189 *dataLen = 0;
190 return IPMI_CC_RESPONSE_ERROR;
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800191}
192
193ipmi_ret_t ipmiOEMSetSystemGUID(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
194 ipmi_request_t request,
195 ipmi_response_t response,
Jason M. Bills64796042018-10-03 16:51:55 -0700196 ipmi_data_len_t dataLen, ipmi_context_t context)
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800197{
198 static constexpr size_t safeBufferLength = 50;
199 char buf[safeBufferLength] = {0};
200 GUIDData* Data = reinterpret_cast<GUIDData*>(request);
201
Jason M. Bills64796042018-10-03 16:51:55 -0700202 if (*dataLen != sizeof(GUIDData)) // 16bytes
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800203 {
Jason M. Bills64796042018-10-03 16:51:55 -0700204 *dataLen = 0;
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800205 return IPMI_CC_REQ_DATA_LEN_INVALID;
206 }
207
Jason M. Bills64796042018-10-03 16:51:55 -0700208 *dataLen = 0;
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800209
210 snprintf(
211 buf, safeBufferLength,
212 "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x",
213 Data->timeLow4, Data->timeLow3, Data->timeLow2, Data->timeLow1,
214 Data->timeMid2, Data->timeMid1, Data->timeHigh2, Data->timeHigh1,
215 Data->clock2, Data->clock1, Data->node6, Data->node5, Data->node4,
216 Data->node3, Data->node2, Data->node1);
217 // UUID is in RFC4122 format. Ex: 61a39523-78f2-11e5-9862-e6402cfc3223
218 std::string guid = buf;
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800219
220 std::string objpath = "/xyz/openbmc_project/control/host0/systemGUID";
221 std::string intf = "xyz.openbmc_project.Common.UUID";
Vernon Mauery15419dd2019-05-24 09:40:30 -0700222 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
223 std::string service = getService(*dbus, intf, objpath);
224 setDbusProperty(*dbus, service, objpath, intf, "UUID", guid);
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800225 return IPMI_CC_OK;
226}
227
Jason M. Billsb02bf092019-08-15 13:01:56 -0700228ipmi::RspType<> ipmiOEMDisableBMCSystemReset(bool disableResetOnSMI,
229 uint7_t reserved1)
230{
231 std::shared_ptr<sdbusplus::asio::connection> busp = getSdBus();
232
233 try
234 {
235 auto service =
236 ipmi::getService(*busp, bmcResetDisablesIntf, bmcResetDisablesPath);
237 ipmi::setDbusProperty(*busp, service, bmcResetDisablesPath,
238 bmcResetDisablesIntf, "ResetOnSMI",
239 !disableResetOnSMI);
240 }
241 catch (std::exception& e)
242 {
243 phosphor::logging::log<phosphor::logging::level::ERR>(
244 "Failed to set BMC reset disables",
245 phosphor::logging::entry("EXCEPTION=%s", e.what()));
246 return ipmi::responseUnspecifiedError();
247 }
248
249 return ipmi::responseSuccess();
250}
251
252ipmi::RspType<bool, // disableResetOnSMI
253 uint7_t // reserved
254 >
255 ipmiOEMGetBMCResetDisables()
256{
257 bool disableResetOnSMI = true;
258
259 std::shared_ptr<sdbusplus::asio::connection> busp = getSdBus();
260 try
261 {
262 auto service =
263 ipmi::getService(*busp, bmcResetDisablesIntf, bmcResetDisablesPath);
264 Value variant =
265 ipmi::getDbusProperty(*busp, service, bmcResetDisablesPath,
266 bmcResetDisablesIntf, "ResetOnSMI");
267 disableResetOnSMI = !std::get<bool>(variant);
268 }
269 catch (std::exception& e)
270 {
271 phosphor::logging::log<phosphor::logging::level::ERR>(
272 "Failed to get BMC reset disables",
273 phosphor::logging::entry("EXCEPTION=%s", e.what()));
274 return ipmi::responseUnspecifiedError();
275 }
276
277 return ipmi::responseSuccess(disableResetOnSMI, 0);
278}
279
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800280ipmi_ret_t ipmiOEMSetBIOSID(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
281 ipmi_request_t request, ipmi_response_t response,
282 ipmi_data_len_t dataLen, ipmi_context_t context)
283{
284 DeviceInfo* data = reinterpret_cast<DeviceInfo*>(request);
285
Jason M. Bills64796042018-10-03 16:51:55 -0700286 if ((*dataLen < 2) || (*dataLen != (1 + data->biosIDLength)))
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800287 {
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800288 *dataLen = 0;
289 return IPMI_CC_REQ_DATA_LEN_INVALID;
290 }
Jason M. Bills64796042018-10-03 16:51:55 -0700291 std::string idString((char*)data->biosId, data->biosIDLength);
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800292
Vernon Mauery15419dd2019-05-24 09:40:30 -0700293 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
Chalapathi899bfd12020-04-15 15:07:02 +0000294 std::string service = getService(*dbus, biosVersionIntf, biosActiveObjPath);
295 setDbusProperty(*dbus, service, biosActiveObjPath, biosVersionIntf,
Yong Li2742b852019-12-16 14:55:11 +0800296 biosVersionProp, idString);
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800297 uint8_t* bytesWritten = static_cast<uint8_t*>(response);
298 *bytesWritten =
Jason M. Bills64796042018-10-03 16:51:55 -0700299 data->biosIDLength; // how many bytes are written into storage
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800300 *dataLen = 1;
301 return IPMI_CC_OK;
302}
303
AppaRao Pulie99e7ed2020-01-17 12:27:10 +0530304bool getSwVerInfo(ipmi::Context::ptr ctx, uint8_t& bmcMajor, uint8_t& bmcMinor,
305 uint8_t& meMajor, uint8_t& meMinor)
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800306{
Chen Yugang7a04f3a2019-10-08 11:12:35 +0800307 // step 1 : get BMC Major and Minor numbers from its DBUS property
AppaRao Pulie99e7ed2020-01-17 12:27:10 +0530308 std::string bmcVersion;
309 if (getActiveSoftwareVersionInfo(ctx, versionPurposeBMC, bmcVersion))
Chen Yugang7a04f3a2019-10-08 11:12:35 +0800310 {
311 return false;
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800312 }
313
AppaRao Pulie99e7ed2020-01-17 12:27:10 +0530314 std::optional<MetaRevision> rev = convertIntelVersion(bmcVersion);
Chen Yugang7a04f3a2019-10-08 11:12:35 +0800315 if (rev.has_value())
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800316 {
Chen Yugang7a04f3a2019-10-08 11:12:35 +0800317 MetaRevision revision = rev.value();
318 bmcMajor = revision.major;
319
320 revision.minor = (revision.minor > 99 ? 99 : revision.minor);
321 bmcMinor = revision.minor % 10 + (revision.minor / 10) * 16;
322 }
323
324 // step 2 : get ME Major and Minor numbers from its DBUS property
AppaRao Puli32825a22020-01-17 15:52:41 +0530325 std::string meVersion;
326 if (getActiveSoftwareVersionInfo(ctx, versionPurposeME, meVersion))
Chen Yugang7a04f3a2019-10-08 11:12:35 +0800327 {
Chen Yugang7a04f3a2019-10-08 11:12:35 +0800328 return false;
329 }
AppaRao Puli32825a22020-01-17 15:52:41 +0530330 std::regex pattern1("(\\d+?).(\\d+?).(\\d+?).(\\d+?).(\\d+?)");
331 constexpr size_t matchedPhosphor = 6;
332 std::smatch results;
333 if (std::regex_match(meVersion, results, pattern1))
334 {
335 if (results.size() == matchedPhosphor)
336 {
337 meMajor = static_cast<uint8_t>(std::stoi(results[1]));
338 meMinor = static_cast<uint8_t>(std::stoi(results[2]));
339 }
340 }
Chen Yugang7a04f3a2019-10-08 11:12:35 +0800341 return true;
342}
343
344ipmi::RspType<
345 std::variant<std::string,
346 std::tuple<uint8_t, std::array<uint8_t, 2>,
347 std::array<uint8_t, 2>, std::array<uint8_t, 2>,
348 std::array<uint8_t, 2>, std::array<uint8_t, 2>>,
349 std::tuple<uint8_t, std::array<uint8_t, 2>>>>
AppaRao Pulie99e7ed2020-01-17 12:27:10 +0530350 ipmiOEMGetDeviceInfo(ipmi::Context::ptr ctx, uint8_t entityType,
351 std::optional<uint8_t> countToRead,
AppaRao Pulid46cb422020-01-21 18:40:21 +0530352 std::optional<uint8_t> offset)
Chen Yugang7a04f3a2019-10-08 11:12:35 +0800353{
Chen Yugang7a04f3a2019-10-08 11:12:35 +0800354 if (entityType > static_cast<uint8_t>(OEMDevEntityType::sdrVer))
355 {
356 return ipmi::responseInvalidFieldRequest();
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800357 }
358
359 // handle OEM command items
Chen Yugang7a04f3a2019-10-08 11:12:35 +0800360 switch (OEMDevEntityType(entityType))
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800361 {
362 case OEMDevEntityType::biosId:
363 {
AppaRao Pulid46cb422020-01-21 18:40:21 +0530364 // Byte 2&3, Only used with selecting BIOS
365 if (!countToRead || !offset)
366 {
367 return ipmi::responseReqDataLenInvalid();
368 }
369
Vernon Mauery15419dd2019-05-24 09:40:30 -0700370 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
Yong Li2742b852019-12-16 14:55:11 +0800371 std::string service =
Chalapathi899bfd12020-04-15 15:07:02 +0000372 getService(*dbus, biosVersionIntf, biosActiveObjPath);
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800373 try
374 {
Yong Li2742b852019-12-16 14:55:11 +0800375 Value variant =
Chalapathi899bfd12020-04-15 15:07:02 +0000376 getDbusProperty(*dbus, service, biosActiveObjPath,
Yong Li2742b852019-12-16 14:55:11 +0800377 biosVersionIntf, biosVersionProp);
Vernon Mauery8166c8d2019-05-23 11:22:30 -0700378 std::string& idString = std::get<std::string>(variant);
AppaRao Pulid46cb422020-01-21 18:40:21 +0530379 if (*offset >= idString.size())
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800380 {
Chen Yugang7a04f3a2019-10-08 11:12:35 +0800381 return ipmi::responseParmOutOfRange();
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800382 }
Jason M. Bills64796042018-10-03 16:51:55 -0700383 size_t length = 0;
AppaRao Pulid46cb422020-01-21 18:40:21 +0530384 if (*countToRead > (idString.size() - *offset))
Jason M. Bills64796042018-10-03 16:51:55 -0700385 {
AppaRao Pulid46cb422020-01-21 18:40:21 +0530386 length = idString.size() - *offset;
Jason M. Bills64796042018-10-03 16:51:55 -0700387 }
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800388 else
389 {
AppaRao Pulid46cb422020-01-21 18:40:21 +0530390 length = *countToRead;
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800391 }
Chen Yugang7a04f3a2019-10-08 11:12:35 +0800392
393 std::string readBuf = {0};
394 readBuf.resize(length);
AppaRao Pulid46cb422020-01-21 18:40:21 +0530395 std::copy_n(idString.begin() + *offset, length,
Chen Yugang7a04f3a2019-10-08 11:12:35 +0800396 (readBuf.begin()));
397 return ipmi::responseSuccess(readBuf);
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800398 }
Vernon Mauery8166c8d2019-05-23 11:22:30 -0700399 catch (std::bad_variant_access& e)
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800400 {
Chen Yugang7a04f3a2019-10-08 11:12:35 +0800401 return ipmi::responseUnspecifiedError();
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800402 }
403 }
404 break;
405
406 case OEMDevEntityType::devVer:
Chen Yugang7a04f3a2019-10-08 11:12:35 +0800407 {
AppaRao Pulid46cb422020-01-21 18:40:21 +0530408 // Byte 2&3, Only used with selecting BIOS
409 if (countToRead || offset)
410 {
411 return ipmi::responseReqDataLenInvalid();
412 }
413
Chen Yugang7a04f3a2019-10-08 11:12:35 +0800414 constexpr const size_t verLen = 2;
415 constexpr const size_t verTotalLen = 10;
416 std::array<uint8_t, verLen> bmcBuf = {0xff, 0xff};
417 std::array<uint8_t, verLen> hsc0Buf = {0xff, 0xff};
418 std::array<uint8_t, verLen> hsc1Buf = {0xff, 0xff};
419 std::array<uint8_t, verLen> meBuf = {0xff, 0xff};
420 std::array<uint8_t, verLen> hsc2Buf = {0xff, 0xff};
421 // data0/1: BMC version number; data6/7: ME version number
422 // the others: HSC0/1/2 version number, not avaible.
AppaRao Pulie99e7ed2020-01-17 12:27:10 +0530423 if (!getSwVerInfo(ctx, bmcBuf[0], bmcBuf[1], meBuf[0], meBuf[1]))
Chen Yugang7a04f3a2019-10-08 11:12:35 +0800424 {
425 return ipmi::responseUnspecifiedError();
426 }
427 return ipmi::responseSuccess(
428 std::tuple<
429 uint8_t, std::array<uint8_t, verLen>,
430 std::array<uint8_t, verLen>, std::array<uint8_t, verLen>,
431 std::array<uint8_t, verLen>, std::array<uint8_t, verLen>>{
432 verTotalLen, bmcBuf, hsc0Buf, hsc1Buf, meBuf, hsc2Buf});
433 }
434 break;
435
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800436 case OEMDevEntityType::sdrVer:
Chen Yugang7a04f3a2019-10-08 11:12:35 +0800437 {
AppaRao Pulid46cb422020-01-21 18:40:21 +0530438 // Byte 2&3, Only used with selecting BIOS
439 if (countToRead || offset)
440 {
441 return ipmi::responseReqDataLenInvalid();
442 }
443
Chen Yugang7a04f3a2019-10-08 11:12:35 +0800444 constexpr const size_t sdrLen = 2;
445 std::array<uint8_t, sdrLen> readBuf = {0x01, 0x0};
446 return ipmi::responseSuccess(
447 std::tuple<uint8_t, std::array<uint8_t, sdrLen>>{sdrLen,
448 readBuf});
449 }
450 break;
451
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800452 default:
Chen Yugang7a04f3a2019-10-08 11:12:35 +0800453 return ipmi::responseInvalidFieldRequest();
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800454 }
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800455}
456
457ipmi_ret_t ipmiOEMGetAICFRU(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
458 ipmi_request_t request, ipmi_response_t response,
459 ipmi_data_len_t dataLen, ipmi_context_t context)
460{
461 if (*dataLen != 0)
462 {
Jason M. Bills64796042018-10-03 16:51:55 -0700463 *dataLen = 0;
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800464 return IPMI_CC_REQ_DATA_LEN_INVALID;
465 }
466
467 *dataLen = 1;
468 uint8_t* res = reinterpret_cast<uint8_t*>(response);
469 // temporary fix. We don't support AIC FRU now. Just tell BIOS that no
470 // AIC is available so that BIOS will not timeout repeatly which leads to
471 // slow booting.
472 *res = 0; // Byte1=Count of SlotPosition/FruID records.
473 return IPMI_CC_OK;
474}
475
Jason M. Bills64796042018-10-03 16:51:55 -0700476ipmi_ret_t ipmiOEMGetPowerRestoreDelay(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
477 ipmi_request_t request,
478 ipmi_response_t response,
479 ipmi_data_len_t dataLen,
480 ipmi_context_t context)
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800481{
Jason M. Bills64796042018-10-03 16:51:55 -0700482 GetPowerRestoreDelayRes* resp =
483 reinterpret_cast<GetPowerRestoreDelayRes*>(response);
484
485 if (*dataLen != 0)
486 {
487 *dataLen = 0;
488 return IPMI_CC_REQ_DATA_LEN_INVALID;
489 }
490
Vernon Mauery15419dd2019-05-24 09:40:30 -0700491 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
Jason M. Bills64796042018-10-03 16:51:55 -0700492 std::string service =
Vernon Mauery15419dd2019-05-24 09:40:30 -0700493 getService(*dbus, powerRestoreDelayIntf, powerRestoreDelayObjPath);
Jason M. Bills64796042018-10-03 16:51:55 -0700494 Value variant =
Vernon Mauery15419dd2019-05-24 09:40:30 -0700495 getDbusProperty(*dbus, service, powerRestoreDelayObjPath,
Jason M. Bills64796042018-10-03 16:51:55 -0700496 powerRestoreDelayIntf, powerRestoreDelayProp);
497
Vernon Mauery8166c8d2019-05-23 11:22:30 -0700498 uint16_t delay = std::get<uint16_t>(variant);
Jason M. Bills64796042018-10-03 16:51:55 -0700499 resp->byteLSB = delay;
500 resp->byteMSB = delay >> 8;
501
502 *dataLen = sizeof(GetPowerRestoreDelayRes);
503
504 return IPMI_CC_OK;
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800505}
506
Jia, Chunhuicc49b542019-03-20 15:41:07 +0800507static uint8_t bcdToDec(uint8_t val)
508{
509 return ((val / 16 * 10) + (val % 16));
510}
511
512// Allows an update utility or system BIOS to send the status of an embedded
513// firmware update attempt to the BMC. After received, BMC will create a logging
514// record.
515ipmi::RspType<> ipmiOEMSendEmbeddedFwUpdStatus(uint8_t status, uint8_t target,
516 uint8_t majorRevision,
517 uint8_t minorRevision,
518 uint32_t auxInfo)
519{
520 std::string firmware;
Jason M. Billsdc249272019-04-03 09:58:40 -0700521 int instance = (target & targetInstanceMask) >> targetInstanceShift;
Jia, Chunhuicc49b542019-03-20 15:41:07 +0800522 target = (target & selEvtTargetMask) >> selEvtTargetShift;
523
524 /* make sure the status is 0, 1, or 2 as per the spec */
525 if (status > 2)
526 {
527 return ipmi::response(ipmi::ccInvalidFieldRequest);
528 }
Jason M. Billsdc249272019-04-03 09:58:40 -0700529 /* make sure the target is 0, 1, 2, or 4 as per the spec */
530 if (target > 4 || target == 3)
531 {
532 return ipmi::response(ipmi::ccInvalidFieldRequest);
533 }
Jia, Chunhuicc49b542019-03-20 15:41:07 +0800534 /*orignal OEM command is to record OEM SEL.
535 But openbmc does not support OEM SEL, so we redirect it to redfish event
536 logging. */
537 std::string buildInfo;
538 std::string action;
539 switch (FWUpdateTarget(target))
540 {
541 case FWUpdateTarget::targetBMC:
542 firmware = "BMC";
Jason M. Billsdc249272019-04-03 09:58:40 -0700543 buildInfo = "major: " + std::to_string(majorRevision) + " minor: " +
Jia, Chunhuicc49b542019-03-20 15:41:07 +0800544 std::to_string(bcdToDec(minorRevision)) + // BCD encoded
545 " BuildID: " + std::to_string(auxInfo);
546 buildInfo += std::to_string(auxInfo);
547 break;
548 case FWUpdateTarget::targetBIOS:
549 firmware = "BIOS";
550 buildInfo =
Jason M. Billsdc249272019-04-03 09:58:40 -0700551 "major: " +
Jia, Chunhuicc49b542019-03-20 15:41:07 +0800552 std::to_string(bcdToDec(majorRevision)) + // BCD encoded
553 " minor: " +
554 std::to_string(bcdToDec(minorRevision)) + // BCD encoded
555 " ReleaseNumber: " + // ASCII encoded
556 std::to_string(static_cast<uint8_t>(auxInfo >> 0) - '0') +
557 std::to_string(static_cast<uint8_t>(auxInfo >> 8) - '0') +
558 std::to_string(static_cast<uint8_t>(auxInfo >> 16) - '0') +
559 std::to_string(static_cast<uint8_t>(auxInfo >> 24) - '0');
560 break;
561 case FWUpdateTarget::targetME:
562 firmware = "ME";
563 buildInfo =
Jason M. Billsdc249272019-04-03 09:58:40 -0700564 "major: " + std::to_string(majorRevision) + " minor1: " +
Jia, Chunhuicc49b542019-03-20 15:41:07 +0800565 std::to_string(bcdToDec(minorRevision)) + // BCD encoded
566 " minor2: " +
567 std::to_string(bcdToDec(static_cast<uint8_t>(auxInfo >> 0))) +
568 " build1: " +
569 std::to_string(bcdToDec(static_cast<uint8_t>(auxInfo >> 8))) +
570 " build2: " +
571 std::to_string(bcdToDec(static_cast<uint8_t>(auxInfo >> 16)));
572 break;
573 case FWUpdateTarget::targetOEMEWS:
574 firmware = "EWS";
Jason M. Billsdc249272019-04-03 09:58:40 -0700575 buildInfo = "major: " + std::to_string(majorRevision) + " minor: " +
Jia, Chunhuicc49b542019-03-20 15:41:07 +0800576 std::to_string(bcdToDec(minorRevision)) + // BCD encoded
577 " BuildID: " + std::to_string(auxInfo);
578 break;
579 }
580
Jason M. Billsdc249272019-04-03 09:58:40 -0700581 static const std::string openBMCMessageRegistryVersion("0.1");
582 std::string redfishMsgID = "OpenBMC." + openBMCMessageRegistryVersion;
583
Jia, Chunhuicc49b542019-03-20 15:41:07 +0800584 switch (status)
585 {
586 case 0x0:
587 action = "update started";
Jason M. Billsdc249272019-04-03 09:58:40 -0700588 redfishMsgID += ".FirmwareUpdateStarted";
Jia, Chunhuicc49b542019-03-20 15:41:07 +0800589 break;
590 case 0x1:
591 action = "update completed successfully";
Jason M. Billsdc249272019-04-03 09:58:40 -0700592 redfishMsgID += ".FirmwareUpdateCompleted";
Jia, Chunhuicc49b542019-03-20 15:41:07 +0800593 break;
594 case 0x2:
595 action = "update failure";
Jason M. Billsdc249272019-04-03 09:58:40 -0700596 redfishMsgID += ".FirmwareUpdateFailed";
Jia, Chunhuicc49b542019-03-20 15:41:07 +0800597 break;
598 default:
599 action = "unknown";
600 break;
601 }
602
Jason M. Billsdc249272019-04-03 09:58:40 -0700603 std::string firmwareInstanceStr =
604 firmware + " instance: " + std::to_string(instance);
605 std::string message("[firmware update] " + firmwareInstanceStr +
606 " status: <" + action + "> " + buildInfo);
Jia, Chunhuicc49b542019-03-20 15:41:07 +0800607
608 sd_journal_send("MESSAGE=%s", message.c_str(), "PRIORITY=%i", LOG_INFO,
Jason M. Billsdc249272019-04-03 09:58:40 -0700609 "REDFISH_MESSAGE_ID=%s", redfishMsgID.c_str(),
610 "REDFISH_MESSAGE_ARGS=%s,%s", firmwareInstanceStr.c_str(),
611 buildInfo.c_str(), NULL);
Jia, Chunhuicc49b542019-03-20 15:41:07 +0800612 return ipmi::responseSuccess();
613}
614
Rajashekar Gade Reddy2b664d52020-03-23 22:09:00 +0530615ipmi::RspType<uint8_t, std::vector<uint8_t>>
616 ipmiOEMSlotIpmb(ipmi::Context::ptr ctx, uint6_t reserved1,
617 uint2_t slotNumber, uint3_t baseBoardSlotNum,
618 uint3_t riserSlotNum, uint2_t reserved2, uint8_t slaveAddr,
619 uint8_t netFn, uint8_t cmd,
620 std::optional<std::vector<uint8_t>> writeData)
621{
622 if (reserved1 || reserved2)
623 {
624 return ipmi::responseInvalidFieldRequest();
625 }
626
627 boost::system::error_code ec;
628 using ipmbResponse = std::tuple<int, uint8_t, uint8_t, uint8_t, uint8_t,
629 std::vector<uint8_t>>;
630 ipmbResponse res = ctx->bus->yield_method_call<ipmbResponse>(
631 ctx->yield, ec, "xyz.openbmc_project.Ipmi.Channel.Ipmb",
632 "/xyz/openbmc_project/Ipmi/Channel/Ipmb", "org.openbmc.Ipmb",
633 "SlotIpmbRequest", static_cast<uint8_t>(slotNumber),
634 static_cast<uint8_t>(baseBoardSlotNum), slaveAddr, netFn, cmd,
635 *writeData);
636 if (ec)
637 {
638 phosphor::logging::log<phosphor::logging::level::ERR>(
639 "Failed to call dbus method SlotIpmbRequest");
640 return ipmi::responseUnspecifiedError();
641 }
642
643 std::vector<uint8_t> dataReceived(0);
644 int status = -1;
645 uint8_t resNetFn = 0, resLun = 0, resCmd = 0, cc = 0;
646
647 std::tie(status, resNetFn, resLun, resCmd, cc, dataReceived) = res;
648
649 if (status)
650 {
651 phosphor::logging::log<phosphor::logging::level::ERR>(
652 "Failed to get response from SlotIpmbRequest");
653 return ipmi::responseResponseError();
654 }
655 return ipmi::responseSuccess(cc, dataReceived);
656}
657
Jason M. Bills64796042018-10-03 16:51:55 -0700658ipmi_ret_t ipmiOEMSetPowerRestoreDelay(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
659 ipmi_request_t request,
660 ipmi_response_t response,
661 ipmi_data_len_t dataLen,
662 ipmi_context_t context)
663{
664 SetPowerRestoreDelayReq* data =
665 reinterpret_cast<SetPowerRestoreDelayReq*>(request);
666 uint16_t delay = 0;
667
668 if (*dataLen != sizeof(SetPowerRestoreDelayReq))
669 {
670 *dataLen = 0;
671 return IPMI_CC_REQ_DATA_LEN_INVALID;
672 }
673 delay = data->byteMSB;
674 delay = (delay << 8) | data->byteLSB;
Vernon Mauery15419dd2019-05-24 09:40:30 -0700675 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
Jason M. Bills64796042018-10-03 16:51:55 -0700676 std::string service =
Vernon Mauery15419dd2019-05-24 09:40:30 -0700677 getService(*dbus, powerRestoreDelayIntf, powerRestoreDelayObjPath);
678 setDbusProperty(*dbus, service, powerRestoreDelayObjPath,
Jason M. Bills64796042018-10-03 16:51:55 -0700679 powerRestoreDelayIntf, powerRestoreDelayProp, delay);
680 *dataLen = 0;
681
682 return IPMI_CC_OK;
683}
684
Jason M. Bills42bd9c82019-06-28 16:39:34 -0700685static bool cpuPresent(const std::string& cpuName)
Jason M. Bills64796042018-10-03 16:51:55 -0700686{
Jason M. Bills42bd9c82019-06-28 16:39:34 -0700687 static constexpr const char* cpuPresencePathPrefix =
688 "/xyz/openbmc_project/inventory/system/chassis/motherboard/";
689 static constexpr const char* cpuPresenceIntf =
690 "xyz.openbmc_project.Inventory.Item";
691 std::string cpuPresencePath = cpuPresencePathPrefix + cpuName;
692 std::shared_ptr<sdbusplus::asio::connection> busp = getSdBus();
693 try
Jason M. Bills64796042018-10-03 16:51:55 -0700694 {
Jason M. Bills42bd9c82019-06-28 16:39:34 -0700695 auto service =
696 ipmi::getService(*busp, cpuPresenceIntf, cpuPresencePath);
697
698 ipmi::Value result = ipmi::getDbusProperty(
699 *busp, service, cpuPresencePath, cpuPresenceIntf, "Present");
700 return std::get<bool>(result);
701 }
702 catch (const std::exception& e)
703 {
704 phosphor::logging::log<phosphor::logging::level::INFO>(
705 "Cannot find processor presence",
706 phosphor::logging::entry("NAME=%s", cpuName.c_str()));
707 return false;
708 }
709}
710
711ipmi::RspType<bool, // CATERR Reset Enabled
712 bool, // ERR2 Reset Enabled
713 uint6_t, // reserved
714 uint8_t, // reserved, returns 0x3F
715 uint6_t, // CPU1 CATERR Count
716 uint2_t, // CPU1 Status
717 uint6_t, // CPU2 CATERR Count
718 uint2_t, // CPU2 Status
719 uint6_t, // CPU3 CATERR Count
720 uint2_t, // CPU3 Status
721 uint6_t, // CPU4 CATERR Count
722 uint2_t, // CPU4 Status
723 uint8_t // Crashdump Count
724 >
725 ipmiOEMGetProcessorErrConfig()
726{
727 bool resetOnCATERR = false;
728 bool resetOnERR2 = false;
729 uint6_t cpu1CATERRCount = 0;
730 uint6_t cpu2CATERRCount = 0;
731 uint6_t cpu3CATERRCount = 0;
732 uint6_t cpu4CATERRCount = 0;
733 uint8_t crashdumpCount = 0;
734 uint2_t cpu1Status =
735 cpuPresent("CPU_1") ? CPUStatus::enabled : CPUStatus::notPresent;
736 uint2_t cpu2Status =
737 cpuPresent("CPU_2") ? CPUStatus::enabled : CPUStatus::notPresent;
738 uint2_t cpu3Status =
739 cpuPresent("CPU_3") ? CPUStatus::enabled : CPUStatus::notPresent;
740 uint2_t cpu4Status =
741 cpuPresent("CPU_4") ? CPUStatus::enabled : CPUStatus::notPresent;
742
743 std::shared_ptr<sdbusplus::asio::connection> busp = getSdBus();
744 try
745 {
746 auto service = ipmi::getService(*busp, processorErrConfigIntf,
747 processorErrConfigObjPath);
748
749 ipmi::PropertyMap result = ipmi::getAllDbusProperties(
750 *busp, service, processorErrConfigObjPath, processorErrConfigIntf);
751 resetOnCATERR = std::get<bool>(result.at("ResetOnCATERR"));
752 resetOnERR2 = std::get<bool>(result.at("ResetOnERR2"));
753 cpu1CATERRCount = std::get<uint8_t>(result.at("ErrorCountCPU1"));
754 cpu2CATERRCount = std::get<uint8_t>(result.at("ErrorCountCPU2"));
755 cpu3CATERRCount = std::get<uint8_t>(result.at("ErrorCountCPU3"));
756 cpu4CATERRCount = std::get<uint8_t>(result.at("ErrorCountCPU4"));
757 crashdumpCount = std::get<uint8_t>(result.at("CrashdumpCount"));
758 }
759 catch (const std::exception& e)
760 {
761 phosphor::logging::log<phosphor::logging::level::ERR>(
762 "Failed to fetch processor error config",
763 phosphor::logging::entry("ERROR=%s", e.what()));
764 return ipmi::responseUnspecifiedError();
Jason M. Bills64796042018-10-03 16:51:55 -0700765 }
766
Jason M. Bills42bd9c82019-06-28 16:39:34 -0700767 return ipmi::responseSuccess(resetOnCATERR, resetOnERR2, 0, 0x3F,
768 cpu1CATERRCount, cpu1Status, cpu2CATERRCount,
769 cpu2Status, cpu3CATERRCount, cpu3Status,
770 cpu4CATERRCount, cpu4Status, crashdumpCount);
771}
Jason M. Bills64796042018-10-03 16:51:55 -0700772
Jason M. Bills42bd9c82019-06-28 16:39:34 -0700773ipmi::RspType<> ipmiOEMSetProcessorErrConfig(
774 bool resetOnCATERR, bool resetOnERR2, uint6_t reserved1, uint8_t reserved2,
775 std::optional<bool> clearCPUErrorCount,
776 std::optional<bool> clearCrashdumpCount, std::optional<uint6_t> reserved3)
777{
778 std::shared_ptr<sdbusplus::asio::connection> busp = getSdBus();
Jason M. Bills64796042018-10-03 16:51:55 -0700779
780 try
781 {
Jason M. Bills42bd9c82019-06-28 16:39:34 -0700782 auto service = ipmi::getService(*busp, processorErrConfigIntf,
783 processorErrConfigObjPath);
784 ipmi::setDbusProperty(*busp, service, processorErrConfigObjPath,
785 processorErrConfigIntf, "ResetOnCATERR",
786 resetOnCATERR);
787 ipmi::setDbusProperty(*busp, service, processorErrConfigObjPath,
788 processorErrConfigIntf, "ResetOnERR2",
789 resetOnERR2);
790 if (clearCPUErrorCount.value_or(false))
791 {
792 ipmi::setDbusProperty(*busp, service, processorErrConfigObjPath,
Jason M. Billsd3e19932019-08-15 12:39:03 -0700793 processorErrConfigIntf, "ErrorCountCPU1",
794 static_cast<uint8_t>(0));
Jason M. Bills42bd9c82019-06-28 16:39:34 -0700795 ipmi::setDbusProperty(*busp, service, processorErrConfigObjPath,
Jason M. Billsd3e19932019-08-15 12:39:03 -0700796 processorErrConfigIntf, "ErrorCountCPU2",
797 static_cast<uint8_t>(0));
798 ipmi::setDbusProperty(*busp, service, processorErrConfigObjPath,
799 processorErrConfigIntf, "ErrorCountCPU3",
800 static_cast<uint8_t>(0));
801 ipmi::setDbusProperty(*busp, service, processorErrConfigObjPath,
802 processorErrConfigIntf, "ErrorCountCPU4",
803 static_cast<uint8_t>(0));
Jason M. Bills42bd9c82019-06-28 16:39:34 -0700804 }
805 if (clearCrashdumpCount.value_or(false))
806 {
807 ipmi::setDbusProperty(*busp, service, processorErrConfigObjPath,
Jason M. Billsd3e19932019-08-15 12:39:03 -0700808 processorErrConfigIntf, "CrashdumpCount",
809 static_cast<uint8_t>(0));
Jason M. Bills42bd9c82019-06-28 16:39:34 -0700810 }
Jason M. Bills64796042018-10-03 16:51:55 -0700811 }
Jason M. Bills42bd9c82019-06-28 16:39:34 -0700812 catch (std::exception& e)
Jason M. Bills64796042018-10-03 16:51:55 -0700813 {
Kuiying Wangbc546672018-11-23 15:41:05 +0800814 phosphor::logging::log<phosphor::logging::level::ERR>(
Jason M. Bills42bd9c82019-06-28 16:39:34 -0700815 "Failed to set processor error config",
816 phosphor::logging::entry("EXCEPTION=%s", e.what()));
817 return ipmi::responseUnspecifiedError();
Jason M. Bills64796042018-10-03 16:51:55 -0700818 }
819
Jason M. Bills42bd9c82019-06-28 16:39:34 -0700820 return ipmi::responseSuccess();
Jason M. Bills64796042018-10-03 16:51:55 -0700821}
822
Yong Li703922d2018-11-06 13:25:31 +0800823ipmi_ret_t ipmiOEMGetShutdownPolicy(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
824 ipmi_request_t request,
825 ipmi_response_t response,
826 ipmi_data_len_t dataLen,
827 ipmi_context_t context)
828{
829 GetOEMShutdownPolicyRes* resp =
830 reinterpret_cast<GetOEMShutdownPolicyRes*>(response);
831
832 if (*dataLen != 0)
833 {
834 phosphor::logging::log<phosphor::logging::level::ERR>(
Kuiying Wang45f04982018-12-26 09:23:08 +0800835 "oem_get_shutdown_policy: invalid input len!");
Yong Li703922d2018-11-06 13:25:31 +0800836 *dataLen = 0;
837 return IPMI_CC_REQ_DATA_LEN_INVALID;
838 }
839
840 *dataLen = 0;
841
842 try
843 {
Vernon Mauery15419dd2019-05-24 09:40:30 -0700844 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
Yong Li703922d2018-11-06 13:25:31 +0800845 std::string service =
Vernon Mauery15419dd2019-05-24 09:40:30 -0700846 getService(*dbus, oemShutdownPolicyIntf, oemShutdownPolicyObjPath);
847 Value variant = getDbusProperty(
848 *dbus, service, oemShutdownPolicyObjPath, oemShutdownPolicyIntf,
849 oemShutdownPolicyObjPathProp);
Yong Li0669d192019-05-06 14:01:46 +0800850
851 if (sdbusplus::com::intel::Control::server::OCOTShutdownPolicy::
852 convertPolicyFromString(std::get<std::string>(variant)) ==
853 sdbusplus::com::intel::Control::server::OCOTShutdownPolicy::Policy::
854 NoShutdownOnOCOT)
855 {
856 resp->policy = 0;
857 }
858 else if (sdbusplus::com::intel::Control::server::OCOTShutdownPolicy::
859 convertPolicyFromString(std::get<std::string>(variant)) ==
860 sdbusplus::com::intel::Control::server::OCOTShutdownPolicy::
861 Policy::ShutdownOnOCOT)
862 {
863 resp->policy = 1;
864 }
865 else
866 {
867 phosphor::logging::log<phosphor::logging::level::ERR>(
868 "oem_set_shutdown_policy: invalid property!",
869 phosphor::logging::entry(
870 "PROP=%s", std::get<std::string>(variant).c_str()));
871 return IPMI_CC_UNSPECIFIED_ERROR;
872 }
Yong Li703922d2018-11-06 13:25:31 +0800873 // TODO needs to check if it is multi-node products,
874 // policy is only supported on node 3/4
875 resp->policySupport = shutdownPolicySupported;
876 }
877 catch (sdbusplus::exception_t& e)
878 {
879 phosphor::logging::log<phosphor::logging::level::ERR>(e.description());
880 return IPMI_CC_UNSPECIFIED_ERROR;
881 }
882
883 *dataLen = sizeof(GetOEMShutdownPolicyRes);
884 return IPMI_CC_OK;
885}
886
887ipmi_ret_t ipmiOEMSetShutdownPolicy(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
888 ipmi_request_t request,
889 ipmi_response_t response,
890 ipmi_data_len_t dataLen,
891 ipmi_context_t context)
892{
893 uint8_t* req = reinterpret_cast<uint8_t*>(request);
Yong Li0669d192019-05-06 14:01:46 +0800894 sdbusplus::com::intel::Control::server::OCOTShutdownPolicy::Policy policy =
895 sdbusplus::com::intel::Control::server::OCOTShutdownPolicy::Policy::
896 NoShutdownOnOCOT;
Yong Li703922d2018-11-06 13:25:31 +0800897
898 // TODO needs to check if it is multi-node products,
899 // policy is only supported on node 3/4
900 if (*dataLen != 1)
901 {
902 phosphor::logging::log<phosphor::logging::level::ERR>(
903 "oem_set_shutdown_policy: invalid input len!");
904 *dataLen = 0;
905 return IPMI_CC_REQ_DATA_LEN_INVALID;
906 }
907
908 *dataLen = 0;
909 if ((*req != noShutdownOnOCOT) && (*req != shutdownOnOCOT))
910 {
911 phosphor::logging::log<phosphor::logging::level::ERR>(
912 "oem_set_shutdown_policy: invalid input!");
913 return IPMI_CC_INVALID_FIELD_REQUEST;
914 }
915
Yong Li0669d192019-05-06 14:01:46 +0800916 if (*req == noShutdownOnOCOT)
917 {
918 policy = sdbusplus::com::intel::Control::server::OCOTShutdownPolicy::
919 Policy::NoShutdownOnOCOT;
920 }
921 else
922 {
923 policy = sdbusplus::com::intel::Control::server::OCOTShutdownPolicy::
924 Policy::ShutdownOnOCOT;
925 }
926
Yong Li703922d2018-11-06 13:25:31 +0800927 try
928 {
Vernon Mauery15419dd2019-05-24 09:40:30 -0700929 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
Yong Li703922d2018-11-06 13:25:31 +0800930 std::string service =
Vernon Mauery15419dd2019-05-24 09:40:30 -0700931 getService(*dbus, oemShutdownPolicyIntf, oemShutdownPolicyObjPath);
Yong Li0669d192019-05-06 14:01:46 +0800932 setDbusProperty(
Vernon Mauery15419dd2019-05-24 09:40:30 -0700933 *dbus, service, oemShutdownPolicyObjPath, oemShutdownPolicyIntf,
Yong Li0669d192019-05-06 14:01:46 +0800934 oemShutdownPolicyObjPathProp,
935 sdbusplus::com::intel::Control::server::convertForMessage(policy));
Yong Li703922d2018-11-06 13:25:31 +0800936 }
937 catch (sdbusplus::exception_t& e)
938 {
939 phosphor::logging::log<phosphor::logging::level::ERR>(e.description());
940 return IPMI_CC_UNSPECIFIED_ERROR;
941 }
942
943 return IPMI_CC_OK;
944}
945
Suryakanth Sekard509eb92018-11-15 17:44:11 +0530946/** @brief implementation for check the DHCP or not in IPv4
947 * @param[in] Channel - Channel number
948 * @returns true or false.
949 */
950static bool isDHCPEnabled(uint8_t Channel)
951{
952 try
953 {
954 auto ethdevice = getChannelName(Channel);
955 if (ethdevice.empty())
956 {
957 return false;
958 }
959 auto ethIP = ethdevice + "/ipv4";
Vernon Mauery15419dd2019-05-24 09:40:30 -0700960 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
Suryakanth Sekard509eb92018-11-15 17:44:11 +0530961 auto ethernetObj =
Vernon Mauery15419dd2019-05-24 09:40:30 -0700962 getDbusObject(*dbus, networkIPIntf, networkRoot, ethIP);
963 auto value = getDbusProperty(*dbus, networkService, ethernetObj.first,
Suryakanth Sekard509eb92018-11-15 17:44:11 +0530964 networkIPIntf, "Origin");
Vernon Mauery8166c8d2019-05-23 11:22:30 -0700965 if (std::get<std::string>(value) ==
Suryakanth Sekard509eb92018-11-15 17:44:11 +0530966 "xyz.openbmc_project.Network.IP.AddressOrigin.DHCP")
967 {
968 return true;
969 }
970 else
971 {
972 return false;
973 }
974 }
975 catch (sdbusplus::exception_t& e)
976 {
977 phosphor::logging::log<phosphor::logging::level::ERR>(e.description());
978 return true;
979 }
980}
981
982/** @brief implementes for check the DHCP or not in IPv6
983 * @param[in] Channel - Channel number
984 * @returns true or false.
985 */
986static bool isDHCPIPv6Enabled(uint8_t Channel)
987{
988
989 try
990 {
991 auto ethdevice = getChannelName(Channel);
992 if (ethdevice.empty())
993 {
994 return false;
995 }
996 auto ethIP = ethdevice + "/ipv6";
Vernon Mauery15419dd2019-05-24 09:40:30 -0700997 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
Suryakanth Sekard509eb92018-11-15 17:44:11 +0530998 auto objectInfo =
Vernon Mauery15419dd2019-05-24 09:40:30 -0700999 getDbusObject(*dbus, networkIPIntf, networkRoot, ethIP);
1000 auto properties = getAllDbusProperties(*dbus, objectInfo.second,
Suryakanth Sekard509eb92018-11-15 17:44:11 +05301001 objectInfo.first, networkIPIntf);
Vernon Mauery8166c8d2019-05-23 11:22:30 -07001002 if (std::get<std::string>(properties["Origin"]) ==
Suryakanth Sekard509eb92018-11-15 17:44:11 +05301003 "xyz.openbmc_project.Network.IP.AddressOrigin.DHCP")
1004 {
1005 return true;
1006 }
1007 else
1008 {
1009 return false;
1010 }
1011 }
1012 catch (sdbusplus::exception_t& e)
1013 {
1014 phosphor::logging::log<phosphor::logging::level::ERR>(e.description());
1015 return true;
1016 }
1017}
1018
1019/** @brief implementes the creating of default new user
1020 * @param[in] userName - new username in 16 bytes.
1021 * @param[in] userPassword - new password in 20 bytes
1022 * @returns ipmi completion code.
1023 */
1024ipmi::RspType<> ipmiOEMSetUser2Activation(
1025 std::array<uint8_t, ipmi::ipmiMaxUserName>& userName,
1026 std::array<uint8_t, ipmi::maxIpmi20PasswordSize>& userPassword)
1027{
1028 bool userState = false;
1029 // Check for System Interface not exist and LAN should be static
1030 for (uint8_t channel = 0; channel < maxIpmiChannels; channel++)
1031 {
1032 ChannelInfo chInfo;
1033 try
1034 {
1035 getChannelInfo(channel, chInfo);
1036 }
1037 catch (sdbusplus::exception_t& e)
1038 {
1039 phosphor::logging::log<phosphor::logging::level::ERR>(
1040 "ipmiOEMSetUser2Activation: Failed to get Channel Info",
1041 phosphor::logging::entry("MSG: %s", e.description()));
1042 return ipmi::response(ipmi::ccUnspecifiedError);
1043 }
1044 if (chInfo.mediumType ==
1045 static_cast<uint8_t>(EChannelMediumType::systemInterface))
1046 {
1047 phosphor::logging::log<phosphor::logging::level::ERR>(
1048 "ipmiOEMSetUser2Activation: system interface exist .");
1049 return ipmi::response(ipmi::ccCommandNotAvailable);
1050 }
1051 else
1052 {
1053
1054 if (chInfo.mediumType ==
1055 static_cast<uint8_t>(EChannelMediumType::lan8032))
1056 {
1057 if (isDHCPIPv6Enabled(channel) || isDHCPEnabled(channel))
1058 {
1059 phosphor::logging::log<phosphor::logging::level::ERR>(
1060 "ipmiOEMSetUser2Activation: DHCP enabled .");
1061 return ipmi::response(ipmi::ccCommandNotAvailable);
1062 }
1063 }
1064 }
1065 }
1066 uint8_t maxChUsers = 0, enabledUsers = 0, fixedUsers = 0;
1067 if (ipmi::ccSuccess ==
1068 ipmiUserGetAllCounts(maxChUsers, enabledUsers, fixedUsers))
1069 {
1070 if (enabledUsers > 1)
1071 {
1072 phosphor::logging::log<phosphor::logging::level::ERR>(
1073 "ipmiOEMSetUser2Activation: more than one user is enabled.");
1074 return ipmi::response(ipmi::ccCommandNotAvailable);
1075 }
1076 // Check the user 2 is enabled or not
1077 ipmiUserCheckEnabled(ipmiDefaultUserId, userState);
1078 if (userState == true)
1079 {
1080 phosphor::logging::log<phosphor::logging::level::ERR>(
1081 "ipmiOEMSetUser2Activation: user 2 already enabled .");
1082 return ipmi::response(ipmi::ccCommandNotAvailable);
1083 }
1084 }
1085 else
1086 {
1087 return ipmi::response(ipmi::ccUnspecifiedError);
1088 }
1089
1090#if BYTE_ORDER == LITTLE_ENDIAN
1091 PrivAccess privAccess = {PRIVILEGE_ADMIN, true, true, true, 0};
1092#endif
1093#if BYTE_ORDER == BIG_ENDIAN
1094 PrivAccess privAccess = {0, true, true, true, PRIVILEGE_ADMIN};
1095#endif
1096
Vernon Mauery037cabd2020-05-14 12:16:01 -07001097 // ipmiUserSetUserName correctly handles char*, possibly non-null
1098 // terminated strings using ipmiMaxUserName size
Jayaprakash Mutyala3fbe8d22020-10-29 14:42:59 +00001099 size_t nameLen = strnlen(reinterpret_cast<const char*>(userName.data()),
1100 sizeof(userName));
1101 const std::string userNameRaw(
1102 reinterpret_cast<const char*>(userName.data()), nameLen);
jayaprakash Mutyala1429d4f2020-03-04 18:20:16 +00001103
Vernon Mauery037cabd2020-05-14 12:16:01 -07001104 if (ipmi::ccSuccess == ipmiUserSetUserName(ipmiDefaultUserId, userNameRaw))
Suryakanth Sekard509eb92018-11-15 17:44:11 +05301105 {
1106 if (ipmi::ccSuccess ==
1107 ipmiUserSetUserPassword(
1108 ipmiDefaultUserId,
1109 reinterpret_cast<const char*>(userPassword.data())))
1110 {
1111 if (ipmi::ccSuccess ==
1112 ipmiUserSetPrivilegeAccess(
1113 ipmiDefaultUserId,
1114 static_cast<uint8_t>(ipmi::EChannelID::chanLan1),
1115 privAccess, true))
1116 {
1117 phosphor::logging::log<phosphor::logging::level::INFO>(
1118 "ipmiOEMSetUser2Activation: user created successfully ");
1119 return ipmi::responseSuccess();
1120 }
1121 }
1122 // we need to delete the default user id which added in this command as
1123 // password / priv setting is failed.
Jayaprakash Mutyala3fbe8d22020-10-29 14:42:59 +00001124 ipmiUserSetUserName(ipmiDefaultUserId, static_cast<std::string>(""));
Suryakanth Sekard509eb92018-11-15 17:44:11 +05301125 phosphor::logging::log<phosphor::logging::level::ERR>(
1126 "ipmiOEMSetUser2Activation: password / priv setting is failed.");
1127 }
1128 else
1129 {
1130 phosphor::logging::log<phosphor::logging::level::ERR>(
1131 "ipmiOEMSetUser2Activation: Setting username failed.");
1132 }
1133
1134 return ipmi::response(ipmi::ccCommandNotAvailable);
1135}
1136
Suryakanth Sekar822b0b42019-11-15 18:32:53 +05301137/** @brief implementes executing the linux command
1138 * @param[in] linux command
1139 * @returns status
1140 */
1141
1142static uint8_t executeCmd(const char* path)
1143{
1144 boost::process::child execProg(path);
1145 execProg.wait();
1146
1147 int retCode = execProg.exit_code();
1148 if (retCode)
1149 {
1150 return ipmi::ccUnspecifiedError;
1151 }
1152 return ipmi::ccSuccess;
1153}
1154
1155/** @brief implementes ASD Security event logging
1156 * @param[in] Event message string
1157 * @param[in] Event Severity
1158 * @returns status
1159 */
1160
1161static void atScaleDebugEventlog(std::string msg, int severity)
1162{
1163 std::string eventStr = "OpenBMC.0.1." + msg;
1164 sd_journal_send("MESSAGE=Security Event: %s", eventStr.c_str(),
1165 "PRIORITY=%i", severity, "REDFISH_MESSAGE_ID=%s",
1166 eventStr.c_str(), NULL);
1167}
1168
Richard Marian Thomaiyarfc5e9852019-04-14 15:06:27 +05301169/** @brief implementes setting password for special user
1170 * @param[in] specialUserIndex
1171 * @param[in] userPassword - new password in 20 bytes
1172 * @returns ipmi completion code.
1173 */
1174ipmi::RspType<> ipmiOEMSetSpecialUserPassword(ipmi::Context::ptr ctx,
1175 uint8_t specialUserIndex,
1176 std::vector<uint8_t> userPassword)
1177{
1178 ChannelInfo chInfo;
Suryakanth Sekar822b0b42019-11-15 18:32:53 +05301179 ipmi_ret_t status = ipmi::ccSuccess;
1180
Richard Marian Thomaiyarfc5e9852019-04-14 15:06:27 +05301181 try
1182 {
1183 getChannelInfo(ctx->channel, chInfo);
1184 }
1185 catch (sdbusplus::exception_t& e)
1186 {
1187 phosphor::logging::log<phosphor::logging::level::ERR>(
1188 "ipmiOEMSetSpecialUserPassword: Failed to get Channel Info",
1189 phosphor::logging::entry("MSG: %s", e.description()));
1190 return ipmi::responseUnspecifiedError();
1191 }
1192 if (chInfo.mediumType !=
1193 static_cast<uint8_t>(EChannelMediumType::systemInterface))
1194 {
1195 phosphor::logging::log<phosphor::logging::level::ERR>(
1196 "ipmiOEMSetSpecialUserPassword: Error - supported only in KCS "
1197 "interface");
1198 return ipmi::responseCommandNotAvailable();
1199 }
Suryakanth Sekar822b0b42019-11-15 18:32:53 +05301200
1201 // 0 for root user and 1 for AtScaleDebug is allowed
1202 if (specialUserIndex >
1203 static_cast<uint8_t>(SpecialUserIndex::atScaleDebugUser))
Richard Marian Thomaiyarfc5e9852019-04-14 15:06:27 +05301204 {
1205 phosphor::logging::log<phosphor::logging::level::ERR>(
1206 "ipmiOEMSetSpecialUserPassword: Invalid user account");
1207 return ipmi::responseParmOutOfRange();
1208 }
Suryakanth Sekar822b0b42019-11-15 18:32:53 +05301209 if (userPassword.size() != 0)
Richard Marian Thomaiyarfc5e9852019-04-14 15:06:27 +05301210 {
Suryakanth Sekar822b0b42019-11-15 18:32:53 +05301211 constexpr uint8_t minPasswordSizeRequired = 6;
1212 std::string passwd;
1213 if (userPassword.size() < minPasswordSizeRequired ||
1214 userPassword.size() > ipmi::maxIpmi20PasswordSize)
1215 {
1216 return ipmi::responseReqDataLenInvalid();
1217 }
1218 passwd.assign(reinterpret_cast<const char*>(userPassword.data()),
1219 userPassword.size());
1220 if (specialUserIndex ==
1221 static_cast<uint8_t>(SpecialUserIndex::atScaleDebugUser))
1222 {
1223 status = ipmiSetSpecialUserPassword("asdbg", passwd);
1224
1225 atScaleDebugEventlog("AtScaleDebugSpecialUserEnabled", LOG_CRIT);
1226 }
1227 else
1228 {
1229 status = ipmiSetSpecialUserPassword("root", passwd);
1230 }
1231 return ipmi::response(status);
Richard Marian Thomaiyarfc5e9852019-04-14 15:06:27 +05301232 }
Suryakanth Sekar822b0b42019-11-15 18:32:53 +05301233 else
1234 {
1235 if (specialUserIndex ==
1236 static_cast<uint8_t>(SpecialUserIndex::rootUser))
1237 {
1238 status = executeCmd("passwd -d root");
1239 }
1240 else
1241 {
1242
1243 status = executeCmd("passwd -d asdbg");
1244
1245 if (status == 0)
1246 {
1247 atScaleDebugEventlog("AtScaleDebugSpecialUserDisabled",
1248 LOG_INFO);
1249 }
1250 }
1251 return ipmi::response(status);
1252 }
Richard Marian Thomaiyarfc5e9852019-04-14 15:06:27 +05301253}
1254
Kuiying Wang45f04982018-12-26 09:23:08 +08001255namespace ledAction
1256{
1257using namespace sdbusplus::xyz::openbmc_project::Led::server;
1258std::map<Physical::Action, uint8_t> actionDbusToIpmi = {
jayaprakash Mutyala934ee9c2019-12-13 17:49:27 +00001259 {Physical::Action::Off, 0},
1260 {Physical::Action::On, 2},
1261 {Physical::Action::Blink, 1}};
Kuiying Wang45f04982018-12-26 09:23:08 +08001262
1263std::map<uint8_t, std::string> offsetObjPath = {
1264 {2, statusAmberObjPath}, {4, statusGreenObjPath}, {6, identifyLEDObjPath}};
1265
1266} // namespace ledAction
1267
1268int8_t getLEDState(sdbusplus::bus::bus& bus, const std::string& intf,
1269 const std::string& objPath, uint8_t& state)
1270{
1271 try
1272 {
1273 std::string service = getService(bus, intf, objPath);
1274 Value stateValue =
1275 getDbusProperty(bus, service, objPath, intf, "State");
Vernon Mauery8166c8d2019-05-23 11:22:30 -07001276 std::string strState = std::get<std::string>(stateValue);
Kuiying Wang45f04982018-12-26 09:23:08 +08001277 state = ledAction::actionDbusToIpmi.at(
1278 sdbusplus::xyz::openbmc_project::Led::server::Physical::
1279 convertActionFromString(strState));
1280 }
1281 catch (sdbusplus::exception::SdBusError& e)
1282 {
1283 phosphor::logging::log<phosphor::logging::level::ERR>(e.what());
1284 return -1;
1285 }
1286 return 0;
1287}
1288
NITIN SHARMAabd11ca2019-06-12 12:31:42 +00001289ipmi::RspType<uint8_t> ipmiOEMGetLEDStatus()
Kuiying Wang45f04982018-12-26 09:23:08 +08001290{
NITIN SHARMAabd11ca2019-06-12 12:31:42 +00001291 uint8_t ledstate = 0;
Kuiying Wang45f04982018-12-26 09:23:08 +08001292 phosphor::logging::log<phosphor::logging::level::DEBUG>("GET led status");
Vernon Mauery15419dd2019-05-24 09:40:30 -07001293 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
Kuiying Wang45f04982018-12-26 09:23:08 +08001294 for (auto it = ledAction::offsetObjPath.begin();
1295 it != ledAction::offsetObjPath.end(); ++it)
1296 {
1297 uint8_t state = 0;
NITIN SHARMAabd11ca2019-06-12 12:31:42 +00001298 if (getLEDState(*dbus, ledIntf, it->second, state) == -1)
Kuiying Wang45f04982018-12-26 09:23:08 +08001299 {
1300 phosphor::logging::log<phosphor::logging::level::ERR>(
1301 "oem_get_led_status: fail to get ID LED status!");
NITIN SHARMAabd11ca2019-06-12 12:31:42 +00001302 return ipmi::responseUnspecifiedError();
Kuiying Wang45f04982018-12-26 09:23:08 +08001303 }
NITIN SHARMAabd11ca2019-06-12 12:31:42 +00001304 ledstate |= state << it->first;
Kuiying Wang45f04982018-12-26 09:23:08 +08001305 }
NITIN SHARMAabd11ca2019-06-12 12:31:42 +00001306 return ipmi::responseSuccess(ledstate);
Kuiying Wang45f04982018-12-26 09:23:08 +08001307}
1308
Yong Li23737fe2019-02-19 08:49:55 +08001309ipmi_ret_t ipmiOEMCfgHostSerialPortSpeed(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
1310 ipmi_request_t request,
1311 ipmi_response_t response,
1312 ipmi_data_len_t dataLen,
1313 ipmi_context_t context)
1314{
1315 CfgHostSerialReq* req = reinterpret_cast<CfgHostSerialReq*>(request);
1316 uint8_t* resp = reinterpret_cast<uint8_t*>(response);
1317
1318 if (*dataLen == 0)
1319 {
1320 phosphor::logging::log<phosphor::logging::level::ERR>(
1321 "CfgHostSerial: invalid input len!",
1322 phosphor::logging::entry("LEN=%d", *dataLen));
1323 return IPMI_CC_REQ_DATA_LEN_INVALID;
1324 }
1325
1326 switch (req->command)
1327 {
1328 case getHostSerialCfgCmd:
1329 {
1330 if (*dataLen != 1)
1331 {
1332 phosphor::logging::log<phosphor::logging::level::ERR>(
1333 "CfgHostSerial: invalid input len!");
1334 *dataLen = 0;
1335 return IPMI_CC_REQ_DATA_LEN_INVALID;
1336 }
1337
1338 *dataLen = 0;
1339
1340 boost::process::ipstream is;
1341 std::vector<std::string> data;
1342 std::string line;
1343 boost::process::child c1(fwGetEnvCmd, "-n", fwHostSerailCfgEnvName,
1344 boost::process::std_out > is);
1345
1346 while (c1.running() && std::getline(is, line) && !line.empty())
1347 {
1348 data.push_back(line);
1349 }
1350
1351 c1.wait();
1352 if (c1.exit_code())
1353 {
1354 phosphor::logging::log<phosphor::logging::level::ERR>(
1355 "CfgHostSerial:: error on execute",
1356 phosphor::logging::entry("EXECUTE=%s", fwSetEnvCmd));
1357 // Using the default value
1358 *resp = 0;
1359 }
1360 else
1361 {
1362 if (data.size() != 1)
1363 {
1364 phosphor::logging::log<phosphor::logging::level::ERR>(
1365 "CfgHostSerial:: error on read env");
1366 return IPMI_CC_UNSPECIFIED_ERROR;
1367 }
1368 try
1369 {
1370 unsigned long tmp = std::stoul(data[0]);
1371 if (tmp > std::numeric_limits<uint8_t>::max())
1372 {
1373 throw std::out_of_range("Out of range");
1374 }
1375 *resp = static_cast<uint8_t>(tmp);
1376 }
1377 catch (const std::invalid_argument& e)
1378 {
1379 phosphor::logging::log<phosphor::logging::level::ERR>(
1380 "invalid config ",
1381 phosphor::logging::entry("ERR=%s", e.what()));
1382 return IPMI_CC_UNSPECIFIED_ERROR;
1383 }
1384 catch (const std::out_of_range& e)
1385 {
1386 phosphor::logging::log<phosphor::logging::level::ERR>(
1387 "out_of_range config ",
1388 phosphor::logging::entry("ERR=%s", e.what()));
1389 return IPMI_CC_UNSPECIFIED_ERROR;
1390 }
1391 }
1392
1393 *dataLen = 1;
1394 break;
1395 }
1396 case setHostSerialCfgCmd:
1397 {
1398 if (*dataLen != sizeof(CfgHostSerialReq))
1399 {
1400 phosphor::logging::log<phosphor::logging::level::ERR>(
1401 "CfgHostSerial: invalid input len!");
1402 *dataLen = 0;
1403 return IPMI_CC_REQ_DATA_LEN_INVALID;
1404 }
1405
1406 *dataLen = 0;
1407
1408 if (req->parameter > HostSerialCfgParamMax)
1409 {
1410 phosphor::logging::log<phosphor::logging::level::ERR>(
1411 "CfgHostSerial: invalid input!");
1412 return IPMI_CC_INVALID_FIELD_REQUEST;
1413 }
1414
1415 boost::process::child c1(fwSetEnvCmd, fwHostSerailCfgEnvName,
1416 std::to_string(req->parameter));
1417
1418 c1.wait();
1419 if (c1.exit_code())
1420 {
1421 phosphor::logging::log<phosphor::logging::level::ERR>(
1422 "CfgHostSerial:: error on execute",
1423 phosphor::logging::entry("EXECUTE=%s", fwGetEnvCmd));
1424 return IPMI_CC_UNSPECIFIED_ERROR;
1425 }
1426 break;
1427 }
1428 default:
1429 phosphor::logging::log<phosphor::logging::level::ERR>(
1430 "CfgHostSerial: invalid input!");
1431 *dataLen = 0;
1432 return IPMI_CC_INVALID_FIELD_REQUEST;
1433 }
1434
1435 return IPMI_CC_OK;
1436}
1437
James Feist91244a62019-02-19 15:04:54 -08001438constexpr const char* thermalModeInterface =
1439 "xyz.openbmc_project.Control.ThermalMode";
1440constexpr const char* thermalModePath =
1441 "/xyz/openbmc_project/control/thermal_mode";
1442
1443bool getFanProfileInterface(
1444 sdbusplus::bus::bus& bus,
1445 boost::container::flat_map<
1446 std::string, std::variant<std::vector<std::string>, std::string>>& resp)
1447{
1448 auto call = bus.new_method_call(settingsBusName, thermalModePath, PROP_INTF,
1449 "GetAll");
1450 call.append(thermalModeInterface);
1451 try
1452 {
1453 auto data = bus.call(call);
1454 data.read(resp);
1455 }
1456 catch (sdbusplus::exception_t& e)
1457 {
1458 phosphor::logging::log<phosphor::logging::level::ERR>(
1459 "getFanProfileInterface: can't get thermal mode!",
1460 phosphor::logging::entry("ERR=%s", e.what()));
1461 return false;
1462 }
1463 return true;
1464}
1465
anil kumar appanaf945eee2019-09-25 23:29:11 +00001466/**@brief implements the OEM set fan config.
1467 * @param selectedFanProfile - fan profile to enable
1468 * @param reserved1
1469 * @param performanceMode - Performance/Acoustic mode
1470 * @param reserved2
1471 * @param setPerformanceMode - set Performance/Acoustic mode
1472 * @param setFanProfile - set fan profile
1473 *
1474 * @return IPMI completion code.
1475 **/
1476ipmi::RspType<> ipmiOEMSetFanConfig(uint8_t selectedFanProfile,
1477
1478 uint2_t reserved1, bool performanceMode,
1479 uint3_t reserved2, bool setPerformanceMode,
Joshi-Mansi619186d2020-01-27 19:16:03 +05301480 bool setFanProfile,
1481 std::optional<uint8_t> dimmGroupId,
1482 std::optional<uint32_t> dimmPresenceBitmap)
James Feist91244a62019-02-19 15:04:54 -08001483{
anil kumar appanaf945eee2019-09-25 23:29:11 +00001484 if (reserved1 || reserved2)
James Feist91244a62019-02-19 15:04:54 -08001485 {
anil kumar appanaf945eee2019-09-25 23:29:11 +00001486 return ipmi::responseInvalidFieldRequest();
James Feist91244a62019-02-19 15:04:54 -08001487 }
Joshi-Mansi619186d2020-01-27 19:16:03 +05301488
1489 if (dimmGroupId)
1490 {
1491 if (*dimmGroupId >= maxCPUNum)
1492 {
1493 return ipmi::responseInvalidFieldRequest();
1494 }
1495 if (!cpuPresent("CPU_" + std::to_string(*dimmGroupId + 1)))
1496 {
1497 return ipmi::responseInvalidFieldRequest();
1498 }
1499 }
1500
James Feist91244a62019-02-19 15:04:54 -08001501 // todo: tell bios to only send first 2 bytes
James Feist91244a62019-02-19 15:04:54 -08001502 boost::container::flat_map<
1503 std::string, std::variant<std::vector<std::string>, std::string>>
1504 profileData;
Vernon Mauery15419dd2019-05-24 09:40:30 -07001505 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
1506 if (!getFanProfileInterface(*dbus, profileData))
James Feist91244a62019-02-19 15:04:54 -08001507 {
anil kumar appanaf945eee2019-09-25 23:29:11 +00001508 return ipmi::responseUnspecifiedError();
James Feist91244a62019-02-19 15:04:54 -08001509 }
1510
1511 std::vector<std::string>* supported =
1512 std::get_if<std::vector<std::string>>(&profileData["Supported"]);
1513 if (supported == nullptr)
1514 {
anil kumar appanaf945eee2019-09-25 23:29:11 +00001515 return ipmi::responseInvalidFieldRequest();
James Feist91244a62019-02-19 15:04:54 -08001516 }
1517 std::string mode;
anil kumar appanaf945eee2019-09-25 23:29:11 +00001518 if (setPerformanceMode)
James Feist91244a62019-02-19 15:04:54 -08001519 {
James Feist91244a62019-02-19 15:04:54 -08001520 if (performanceMode)
1521 {
1522
1523 if (std::find(supported->begin(), supported->end(),
1524 "Performance") != supported->end())
1525 {
1526 mode = "Performance";
1527 }
1528 }
1529 else
1530 {
James Feist91244a62019-02-19 15:04:54 -08001531 if (std::find(supported->begin(), supported->end(), "Acoustic") !=
1532 supported->end())
1533 {
1534 mode = "Acoustic";
1535 }
1536 }
1537 if (mode.empty())
1538 {
anil kumar appanaf945eee2019-09-25 23:29:11 +00001539 return ipmi::responseInvalidFieldRequest();
James Feist91244a62019-02-19 15:04:54 -08001540 }
anil kumar appanaf945eee2019-09-25 23:29:11 +00001541
1542 try
1543 {
1544 setDbusProperty(*dbus, settingsBusName, thermalModePath,
1545 thermalModeInterface, "Current", mode);
1546 }
1547 catch (sdbusplus::exception_t& e)
1548 {
1549 phosphor::logging::log<phosphor::logging::level::ERR>(
1550 "ipmiOEMSetFanConfig: can't set thermal mode!",
1551 phosphor::logging::entry("EXCEPTION=%s", e.what()));
1552 return ipmi::responseResponseError();
1553 }
James Feist91244a62019-02-19 15:04:54 -08001554 }
1555
anil kumar appanaf945eee2019-09-25 23:29:11 +00001556 return ipmi::responseSuccess();
James Feist91244a62019-02-19 15:04:54 -08001557}
1558
James Feist5b693632019-07-09 09:06:09 -07001559ipmi::RspType<uint8_t, // profile support map
1560 uint8_t, // fan control profile enable
1561 uint8_t, // flags
1562 uint32_t // dimm presence bit map
1563 >
1564 ipmiOEMGetFanConfig(uint8_t dimmGroupId)
James Feist91244a62019-02-19 15:04:54 -08001565{
Joshi-Mansi36f05ce2020-01-14 14:29:34 +05301566 if (dimmGroupId >= maxCPUNum)
1567 {
1568 return ipmi::responseInvalidFieldRequest();
1569 }
1570
1571 bool cpuStatus = cpuPresent("CPU_" + std::to_string(dimmGroupId + 1));
1572
1573 if (!cpuStatus)
1574 {
1575 return ipmi::responseInvalidFieldRequest();
1576 }
1577
James Feist91244a62019-02-19 15:04:54 -08001578 boost::container::flat_map<
1579 std::string, std::variant<std::vector<std::string>, std::string>>
1580 profileData;
1581
Vernon Mauery15419dd2019-05-24 09:40:30 -07001582 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
1583 if (!getFanProfileInterface(*dbus, profileData))
James Feist91244a62019-02-19 15:04:54 -08001584 {
James Feist5b693632019-07-09 09:06:09 -07001585 return ipmi::responseResponseError();
James Feist91244a62019-02-19 15:04:54 -08001586 }
1587
1588 std::string* current = std::get_if<std::string>(&profileData["Current"]);
1589
1590 if (current == nullptr)
1591 {
1592 phosphor::logging::log<phosphor::logging::level::ERR>(
1593 "ipmiOEMGetFanConfig: can't get current mode!");
James Feist5b693632019-07-09 09:06:09 -07001594 return ipmi::responseResponseError();
James Feist91244a62019-02-19 15:04:54 -08001595 }
1596 bool performance = (*current == "Performance");
1597
James Feist5b693632019-07-09 09:06:09 -07001598 uint8_t flags = 0;
James Feist91244a62019-02-19 15:04:54 -08001599 if (performance)
1600 {
James Feist5b693632019-07-09 09:06:09 -07001601 flags |= 1 << 2;
James Feist91244a62019-02-19 15:04:54 -08001602 }
1603
jayaprakash Mutyala4b1552d2020-02-11 12:07:29 +00001604 constexpr uint8_t fanControlDefaultProfile = 0x80;
1605 constexpr uint8_t fanControlProfileState = 0x00;
1606 constexpr uint32_t dimmPresenceBitmap = 0x00;
1607
1608 return ipmi::responseSuccess(fanControlDefaultProfile,
1609 fanControlProfileState, flags,
1610 dimmPresenceBitmap);
James Feist91244a62019-02-19 15:04:54 -08001611}
James Feist5f957ca2019-03-14 15:33:55 -07001612constexpr const char* cfmLimitSettingPath =
1613 "/xyz/openbmc_project/control/cfm_limit";
1614constexpr const char* cfmLimitIface = "xyz.openbmc_project.Control.CFMLimit";
James Feistfaa4f222019-03-21 16:21:55 -07001615constexpr const size_t legacyExitAirSensorNumber = 0x2e;
James Feist09f6b602019-08-08 11:30:03 -07001616constexpr const size_t legacyPCHSensorNumber = 0x22;
1617constexpr const char* exitAirPathName = "Exit_Air";
1618constexpr const char* pchPathName = "SSB_Temp";
James Feistacc8a4e2019-04-02 14:23:57 -07001619constexpr const char* pidConfigurationIface =
1620 "xyz.openbmc_project.Configuration.Pid";
James Feistfaa4f222019-03-21 16:21:55 -07001621
James Feist09f6b602019-08-08 11:30:03 -07001622static std::string getConfigPath(const std::string& name)
James Feistfaa4f222019-03-21 16:21:55 -07001623{
Vernon Mauery15419dd2019-05-24 09:40:30 -07001624 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
James Feistfaa4f222019-03-21 16:21:55 -07001625 auto method =
Vernon Mauery15419dd2019-05-24 09:40:30 -07001626 dbus->new_method_call("xyz.openbmc_project.ObjectMapper",
1627 "/xyz/openbmc_project/object_mapper",
1628 "xyz.openbmc_project.ObjectMapper", "GetSubTree");
James Feistfaa4f222019-03-21 16:21:55 -07001629
James Feistacc8a4e2019-04-02 14:23:57 -07001630 method.append("/", 0, std::array<const char*, 1>{pidConfigurationIface});
James Feistfaa4f222019-03-21 16:21:55 -07001631 std::string path;
1632 GetSubTreeType resp;
1633 try
1634 {
Vernon Mauery15419dd2019-05-24 09:40:30 -07001635 auto reply = dbus->call(method);
James Feistfaa4f222019-03-21 16:21:55 -07001636 reply.read(resp);
1637 }
1638 catch (sdbusplus::exception_t&)
1639 {
1640 phosphor::logging::log<phosphor::logging::level::ERR>(
1641 "ipmiOEMGetFscParameter: mapper error");
1642 };
James Feist09f6b602019-08-08 11:30:03 -07001643 auto config =
1644 std::find_if(resp.begin(), resp.end(), [&name](const auto& pair) {
1645 return pair.first.find(name) != std::string::npos;
1646 });
James Feistfaa4f222019-03-21 16:21:55 -07001647 if (config != resp.end())
1648 {
1649 path = std::move(config->first);
1650 }
1651 return path;
1652}
James Feist5f957ca2019-03-14 15:33:55 -07001653
James Feistacc8a4e2019-04-02 14:23:57 -07001654// flat map to make alphabetical
1655static boost::container::flat_map<std::string, PropertyMap> getPidConfigs()
1656{
1657 boost::container::flat_map<std::string, PropertyMap> ret;
Vernon Mauery15419dd2019-05-24 09:40:30 -07001658 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
James Feistacc8a4e2019-04-02 14:23:57 -07001659 auto method =
Vernon Mauery15419dd2019-05-24 09:40:30 -07001660 dbus->new_method_call("xyz.openbmc_project.ObjectMapper",
1661 "/xyz/openbmc_project/object_mapper",
1662 "xyz.openbmc_project.ObjectMapper", "GetSubTree");
James Feistacc8a4e2019-04-02 14:23:57 -07001663
1664 method.append("/", 0, std::array<const char*, 1>{pidConfigurationIface});
1665 GetSubTreeType resp;
1666
1667 try
1668 {
Vernon Mauery15419dd2019-05-24 09:40:30 -07001669 auto reply = dbus->call(method);
James Feistacc8a4e2019-04-02 14:23:57 -07001670 reply.read(resp);
1671 }
1672 catch (sdbusplus::exception_t&)
1673 {
1674 phosphor::logging::log<phosphor::logging::level::ERR>(
1675 "getFanConfigPaths: mapper error");
1676 };
1677 for (const auto& [path, objects] : resp)
1678 {
1679 if (objects.empty())
1680 {
1681 continue; // should be impossible
1682 }
Zhu, Yungebe560b02019-04-21 21:19:21 -04001683
1684 try
1685 {
Vernon Mauery15419dd2019-05-24 09:40:30 -07001686 ret.emplace(path,
1687 getAllDbusProperties(*dbus, objects[0].first, path,
1688 pidConfigurationIface));
Zhu, Yungebe560b02019-04-21 21:19:21 -04001689 }
1690 catch (sdbusplus::exception_t& e)
1691 {
1692 phosphor::logging::log<phosphor::logging::level::ERR>(
1693 "getPidConfigs: can't get DbusProperties!",
1694 phosphor::logging::entry("ERR=%s", e.what()));
1695 }
James Feistacc8a4e2019-04-02 14:23:57 -07001696 }
1697 return ret;
1698}
1699
1700ipmi::RspType<uint8_t> ipmiOEMGetFanSpeedOffset(void)
1701{
1702 boost::container::flat_map<std::string, PropertyMap> data = getPidConfigs();
1703 if (data.empty())
1704 {
1705 return ipmi::responseResponseError();
1706 }
1707 uint8_t minOffset = std::numeric_limits<uint8_t>::max();
1708 for (const auto& [_, pid] : data)
1709 {
1710 auto findClass = pid.find("Class");
1711 if (findClass == pid.end())
1712 {
1713 phosphor::logging::log<phosphor::logging::level::ERR>(
1714 "ipmiOEMGetFscParameter: found illegal pid "
1715 "configurations");
1716 return ipmi::responseResponseError();
1717 }
1718 std::string type = std::get<std::string>(findClass->second);
1719 if (type == "fan")
1720 {
1721 auto findOutLimit = pid.find("OutLimitMin");
1722 if (findOutLimit == pid.end())
1723 {
1724 phosphor::logging::log<phosphor::logging::level::ERR>(
1725 "ipmiOEMGetFscParameter: found illegal pid "
1726 "configurations");
1727 return ipmi::responseResponseError();
1728 }
1729 // get the min out of all the offsets
1730 minOffset = std::min(
1731 minOffset,
1732 static_cast<uint8_t>(std::get<double>(findOutLimit->second)));
1733 }
1734 }
1735 if (minOffset == std::numeric_limits<uint8_t>::max())
1736 {
1737 phosphor::logging::log<phosphor::logging::level::ERR>(
1738 "ipmiOEMGetFscParameter: found no fan configurations!");
1739 return ipmi::responseResponseError();
1740 }
1741
1742 return ipmi::responseSuccess(minOffset);
1743}
1744
1745ipmi::RspType<> ipmiOEMSetFanSpeedOffset(uint8_t offset)
1746{
1747 boost::container::flat_map<std::string, PropertyMap> data = getPidConfigs();
1748 if (data.empty())
1749 {
1750
1751 phosphor::logging::log<phosphor::logging::level::ERR>(
1752 "ipmiOEMSetFanSpeedOffset: found no pid configurations!");
1753 return ipmi::responseResponseError();
1754 }
1755
Vernon Mauery15419dd2019-05-24 09:40:30 -07001756 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
James Feistacc8a4e2019-04-02 14:23:57 -07001757 bool found = false;
1758 for (const auto& [path, pid] : data)
1759 {
1760 auto findClass = pid.find("Class");
1761 if (findClass == pid.end())
1762 {
1763
1764 phosphor::logging::log<phosphor::logging::level::ERR>(
1765 "ipmiOEMSetFanSpeedOffset: found illegal pid "
1766 "configurations");
1767 return ipmi::responseResponseError();
1768 }
1769 std::string type = std::get<std::string>(findClass->second);
1770 if (type == "fan")
1771 {
1772 auto findOutLimit = pid.find("OutLimitMin");
1773 if (findOutLimit == pid.end())
1774 {
1775
1776 phosphor::logging::log<phosphor::logging::level::ERR>(
1777 "ipmiOEMSetFanSpeedOffset: found illegal pid "
1778 "configurations");
1779 return ipmi::responseResponseError();
1780 }
Vernon Mauery15419dd2019-05-24 09:40:30 -07001781 ipmi::setDbusProperty(*dbus, "xyz.openbmc_project.EntityManager",
James Feistacc8a4e2019-04-02 14:23:57 -07001782 path, pidConfigurationIface, "OutLimitMin",
1783 static_cast<double>(offset));
1784 found = true;
1785 }
1786 }
1787 if (!found)
1788 {
1789 phosphor::logging::log<phosphor::logging::level::ERR>(
1790 "ipmiOEMSetFanSpeedOffset: set no fan offsets");
1791 return ipmi::responseResponseError();
1792 }
1793
1794 return ipmi::responseSuccess();
1795}
1796
1797ipmi::RspType<> ipmiOEMSetFscParameter(uint8_t command, uint8_t param1,
1798 uint8_t param2)
James Feist5f957ca2019-03-14 15:33:55 -07001799{
1800 constexpr const size_t disableLimiting = 0x0;
1801
Vernon Mauery15419dd2019-05-24 09:40:30 -07001802 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
James Feistacc8a4e2019-04-02 14:23:57 -07001803 if (command == static_cast<uint8_t>(setFscParamFlags::tcontrol))
James Feist5f957ca2019-03-14 15:33:55 -07001804 {
James Feist09f6b602019-08-08 11:30:03 -07001805 std::string pathName;
James Feistacc8a4e2019-04-02 14:23:57 -07001806 if (param1 == legacyExitAirSensorNumber)
James Feistfaa4f222019-03-21 16:21:55 -07001807 {
James Feist09f6b602019-08-08 11:30:03 -07001808 pathName = exitAirPathName;
1809 }
1810 else if (param1 == legacyPCHSensorNumber)
1811 {
1812 pathName = pchPathName;
James Feistfaa4f222019-03-21 16:21:55 -07001813 }
1814 else
1815 {
James Feistacc8a4e2019-04-02 14:23:57 -07001816 return ipmi::responseParmOutOfRange();
James Feistfaa4f222019-03-21 16:21:55 -07001817 }
James Feist09f6b602019-08-08 11:30:03 -07001818 std::string path = getConfigPath(pathName);
1819 ipmi::setDbusProperty(*dbus, "xyz.openbmc_project.EntityManager", path,
1820 pidConfigurationIface, "SetPoint",
1821 static_cast<double>(param2));
1822 return ipmi::responseSuccess();
James Feistfaa4f222019-03-21 16:21:55 -07001823 }
James Feistacc8a4e2019-04-02 14:23:57 -07001824 else if (command == static_cast<uint8_t>(setFscParamFlags::cfm))
James Feist5f957ca2019-03-14 15:33:55 -07001825 {
James Feistacc8a4e2019-04-02 14:23:57 -07001826 uint16_t cfm = param1 | (static_cast<uint16_t>(param2) << 8);
James Feist5f957ca2019-03-14 15:33:55 -07001827
1828 // must be greater than 50 based on eps
1829 if (cfm < 50 && cfm != disableLimiting)
1830 {
James Feistacc8a4e2019-04-02 14:23:57 -07001831 return ipmi::responseParmOutOfRange();
James Feist5f957ca2019-03-14 15:33:55 -07001832 }
1833
1834 try
1835 {
Vernon Mauery15419dd2019-05-24 09:40:30 -07001836 ipmi::setDbusProperty(*dbus, settingsBusName, cfmLimitSettingPath,
James Feist5f957ca2019-03-14 15:33:55 -07001837 cfmLimitIface, "Limit",
1838 static_cast<double>(cfm));
1839 }
1840 catch (sdbusplus::exception_t& e)
1841 {
1842 phosphor::logging::log<phosphor::logging::level::ERR>(
1843 "ipmiOEMSetFscParameter: can't set cfm setting!",
1844 phosphor::logging::entry("ERR=%s", e.what()));
James Feistacc8a4e2019-04-02 14:23:57 -07001845 return ipmi::responseResponseError();
James Feist5f957ca2019-03-14 15:33:55 -07001846 }
James Feistacc8a4e2019-04-02 14:23:57 -07001847 return ipmi::responseSuccess();
1848 }
1849 else if (command == static_cast<uint8_t>(setFscParamFlags::maxPwm))
1850 {
1851 constexpr const size_t maxDomainCount = 8;
1852 uint8_t requestedDomainMask = param1;
1853 boost::container::flat_map data = getPidConfigs();
1854 if (data.empty())
1855 {
1856
1857 phosphor::logging::log<phosphor::logging::level::ERR>(
1858 "ipmiOEMSetFscParameter: found no pid configurations!");
1859 return ipmi::responseResponseError();
1860 }
1861 size_t count = 0;
1862 for (const auto& [path, pid] : data)
1863 {
1864 auto findClass = pid.find("Class");
1865 if (findClass == pid.end())
1866 {
1867
1868 phosphor::logging::log<phosphor::logging::level::ERR>(
1869 "ipmiOEMSetFscParameter: found illegal pid "
1870 "configurations");
1871 return ipmi::responseResponseError();
1872 }
1873 std::string type = std::get<std::string>(findClass->second);
1874 if (type == "fan")
1875 {
1876 if (requestedDomainMask & (1 << count))
1877 {
1878 ipmi::setDbusProperty(
Vernon Mauery15419dd2019-05-24 09:40:30 -07001879 *dbus, "xyz.openbmc_project.EntityManager", path,
James Feistacc8a4e2019-04-02 14:23:57 -07001880 pidConfigurationIface, "OutLimitMax",
1881 static_cast<double>(param2));
1882 }
1883 count++;
1884 }
1885 }
1886 return ipmi::responseSuccess();
James Feist5f957ca2019-03-14 15:33:55 -07001887 }
1888 else
1889 {
1890 // todo other command parts possibly
1891 // tcontrol is handled in peci now
1892 // fan speed offset not implemented yet
1893 // domain pwm limit not implemented
James Feistacc8a4e2019-04-02 14:23:57 -07001894 return ipmi::responseParmOutOfRange();
James Feist5f957ca2019-03-14 15:33:55 -07001895 }
1896}
1897
James Feistacc8a4e2019-04-02 14:23:57 -07001898ipmi::RspType<
1899 std::variant<uint8_t, std::array<uint8_t, 2>, std::array<uint16_t, 2>>>
1900 ipmiOEMGetFscParameter(uint8_t command, std::optional<uint8_t> param)
James Feist5f957ca2019-03-14 15:33:55 -07001901{
James Feist09f6b602019-08-08 11:30:03 -07001902 constexpr uint8_t legacyDefaultSetpoint = -128;
James Feist5f957ca2019-03-14 15:33:55 -07001903
Vernon Mauery15419dd2019-05-24 09:40:30 -07001904 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
James Feistacc8a4e2019-04-02 14:23:57 -07001905 if (command == static_cast<uint8_t>(setFscParamFlags::tcontrol))
James Feist5f957ca2019-03-14 15:33:55 -07001906 {
James Feistacc8a4e2019-04-02 14:23:57 -07001907 if (!param)
James Feistfaa4f222019-03-21 16:21:55 -07001908 {
James Feistacc8a4e2019-04-02 14:23:57 -07001909 return ipmi::responseReqDataLenInvalid();
James Feistfaa4f222019-03-21 16:21:55 -07001910 }
1911
James Feist09f6b602019-08-08 11:30:03 -07001912 std::string pathName;
1913
1914 if (*param == legacyExitAirSensorNumber)
1915 {
1916 pathName = exitAirPathName;
1917 }
1918 else if (*param == legacyPCHSensorNumber)
1919 {
1920 pathName = pchPathName;
1921 }
1922 else
James Feistfaa4f222019-03-21 16:21:55 -07001923 {
James Feistacc8a4e2019-04-02 14:23:57 -07001924 return ipmi::responseParmOutOfRange();
James Feistfaa4f222019-03-21 16:21:55 -07001925 }
James Feist09f6b602019-08-08 11:30:03 -07001926
1927 uint8_t setpoint = legacyDefaultSetpoint;
1928 std::string path = getConfigPath(pathName);
James Feistfaa4f222019-03-21 16:21:55 -07001929 if (path.size())
1930 {
Vernon Mauery15419dd2019-05-24 09:40:30 -07001931 Value val = ipmi::getDbusProperty(
1932 *dbus, "xyz.openbmc_project.EntityManager", path,
1933 pidConfigurationIface, "SetPoint");
James Feistfaa4f222019-03-21 16:21:55 -07001934 setpoint = std::floor(std::get<double>(val) + 0.5);
1935 }
1936
1937 // old implementation used to return the "default" and current, we
1938 // don't make the default readily available so just make both the
1939 // same
James Feistfaa4f222019-03-21 16:21:55 -07001940
James Feistacc8a4e2019-04-02 14:23:57 -07001941 return ipmi::responseSuccess(
1942 std::array<uint8_t, 2>{setpoint, setpoint});
James Feistfaa4f222019-03-21 16:21:55 -07001943 }
James Feistacc8a4e2019-04-02 14:23:57 -07001944 else if (command == static_cast<uint8_t>(setFscParamFlags::maxPwm))
1945 {
1946 constexpr const size_t maxDomainCount = 8;
1947
1948 if (!param)
1949 {
1950 return ipmi::responseReqDataLenInvalid();
1951 }
1952 uint8_t requestedDomain = *param;
1953 if (requestedDomain >= maxDomainCount)
1954 {
1955 return ipmi::responseInvalidFieldRequest();
1956 }
1957
1958 boost::container::flat_map data = getPidConfigs();
1959 if (data.empty())
1960 {
1961 phosphor::logging::log<phosphor::logging::level::ERR>(
1962 "ipmiOEMGetFscParameter: found no pid configurations!");
1963 return ipmi::responseResponseError();
1964 }
1965 size_t count = 0;
1966 for (const auto& [_, pid] : data)
1967 {
1968 auto findClass = pid.find("Class");
1969 if (findClass == pid.end())
1970 {
1971 phosphor::logging::log<phosphor::logging::level::ERR>(
1972 "ipmiOEMGetFscParameter: found illegal pid "
1973 "configurations");
1974 return ipmi::responseResponseError();
1975 }
1976 std::string type = std::get<std::string>(findClass->second);
1977 if (type == "fan")
1978 {
1979 if (requestedDomain == count)
1980 {
1981 auto findOutLimit = pid.find("OutLimitMax");
1982 if (findOutLimit == pid.end())
1983 {
1984 phosphor::logging::log<phosphor::logging::level::ERR>(
1985 "ipmiOEMGetFscParameter: found illegal pid "
1986 "configurations");
1987 return ipmi::responseResponseError();
1988 }
1989
1990 return ipmi::responseSuccess(
1991 static_cast<uint8_t>(std::floor(
1992 std::get<double>(findOutLimit->second) + 0.5)));
1993 }
1994 else
1995 {
1996 count++;
1997 }
1998 }
1999 }
2000
2001 return ipmi::responseInvalidFieldRequest();
2002 }
2003 else if (command == static_cast<uint8_t>(setFscParamFlags::cfm))
James Feist5f957ca2019-03-14 15:33:55 -07002004 {
2005
2006 /*
2007 DataLen should be 1, but host is sending us an extra bit. As the
James Feistacc8a4e2019-04-02 14:23:57 -07002008 previous behavior didn't seem to prevent this, ignore the check for
2009 now.
James Feist5f957ca2019-03-14 15:33:55 -07002010
James Feistacc8a4e2019-04-02 14:23:57 -07002011 if (param)
James Feist5f957ca2019-03-14 15:33:55 -07002012 {
2013 phosphor::logging::log<phosphor::logging::level::ERR>(
2014 "ipmiOEMGetFscParameter: invalid input len!");
James Feist5f957ca2019-03-14 15:33:55 -07002015 return IPMI_CC_REQ_DATA_LEN_INVALID;
2016 }
2017 */
2018 Value cfmLimit;
2019 Value cfmMaximum;
2020 try
2021 {
Vernon Mauery15419dd2019-05-24 09:40:30 -07002022 cfmLimit = ipmi::getDbusProperty(*dbus, settingsBusName,
James Feist5f957ca2019-03-14 15:33:55 -07002023 cfmLimitSettingPath, cfmLimitIface,
2024 "Limit");
2025 cfmMaximum = ipmi::getDbusProperty(
Vernon Mauery15419dd2019-05-24 09:40:30 -07002026 *dbus, "xyz.openbmc_project.ExitAirTempSensor",
James Feist5f957ca2019-03-14 15:33:55 -07002027 "/xyz/openbmc_project/control/MaxCFM", cfmLimitIface, "Limit");
2028 }
2029 catch (sdbusplus::exception_t& e)
2030 {
2031 phosphor::logging::log<phosphor::logging::level::ERR>(
James Feistacc8a4e2019-04-02 14:23:57 -07002032 "ipmiOEMGetFscParameter: can't get cfm setting!",
James Feist5f957ca2019-03-14 15:33:55 -07002033 phosphor::logging::entry("ERR=%s", e.what()));
James Feistacc8a4e2019-04-02 14:23:57 -07002034 return ipmi::responseResponseError();
James Feist5f957ca2019-03-14 15:33:55 -07002035 }
2036
James Feistacc8a4e2019-04-02 14:23:57 -07002037 double cfmMax = std::get<double>(cfmMaximum);
2038 double cfmLim = std::get<double>(cfmLimit);
James Feist5f957ca2019-03-14 15:33:55 -07002039
James Feistacc8a4e2019-04-02 14:23:57 -07002040 cfmLim = std::floor(cfmLim + 0.5);
2041 cfmMax = std::floor(cfmMax + 0.5);
2042 uint16_t cfmLimResp = static_cast<uint16_t>(cfmLim);
2043 uint16_t cfmMaxResp = static_cast<uint16_t>(cfmMax);
James Feist5f957ca2019-03-14 15:33:55 -07002044
James Feistacc8a4e2019-04-02 14:23:57 -07002045 return ipmi::responseSuccess(
2046 std::array<uint16_t, 2>{cfmLimResp, cfmMaxResp});
James Feist5f957ca2019-03-14 15:33:55 -07002047 }
James Feistacc8a4e2019-04-02 14:23:57 -07002048
James Feist5f957ca2019-03-14 15:33:55 -07002049 else
2050 {
2051 // todo other command parts possibly
James Feist5f957ca2019-03-14 15:33:55 -07002052 // domain pwm limit not implemented
James Feistacc8a4e2019-04-02 14:23:57 -07002053 return ipmi::responseParmOutOfRange();
James Feist5f957ca2019-03-14 15:33:55 -07002054 }
2055}
2056
Cheng C Yang773703a2019-08-15 09:41:11 +08002057using crConfigVariant =
2058 std::variant<bool, uint8_t, uint32_t, std::vector<uint8_t>, std::string>;
2059
2060int setCRConfig(ipmi::Context::ptr ctx, const std::string& property,
2061 const crConfigVariant& value,
2062 std::chrono::microseconds timeout = ipmi::IPMI_DBUS_TIMEOUT)
2063{
2064 boost::system::error_code ec;
2065 ctx->bus->yield_method_call<void>(
Kuiying Wange45333a2020-07-22 22:06:37 +08002066 ctx->yield, ec, "xyz.openbmc_project.PSURedundancy",
Cheng C Yang773703a2019-08-15 09:41:11 +08002067 "/xyz/openbmc_project/control/power_supply_redundancy",
2068 "org.freedesktop.DBus.Properties", "Set",
2069 "xyz.openbmc_project.Control.PowerSupplyRedundancy", property, value);
2070 if (ec)
2071 {
2072 phosphor::logging::log<phosphor::logging::level::ERR>(
2073 "Failed to set dbus property to cold redundancy");
2074 return -1;
2075 }
2076
2077 return 0;
2078}
2079
Kuiying Wange45333a2020-07-22 22:06:37 +08002080int getCRConfig(
2081 ipmi::Context::ptr ctx, const std::string& property, crConfigVariant& value,
2082 const std::string& service = "xyz.openbmc_project.PSURedundancy",
2083 std::chrono::microseconds timeout = ipmi::IPMI_DBUS_TIMEOUT)
Cheng C Yang773703a2019-08-15 09:41:11 +08002084{
2085 boost::system::error_code ec;
2086 value = ctx->bus->yield_method_call<crConfigVariant>(
Yong Li19445ab2019-12-20 18:25:29 +08002087 ctx->yield, ec, service,
Cheng C Yang773703a2019-08-15 09:41:11 +08002088 "/xyz/openbmc_project/control/power_supply_redundancy",
2089 "org.freedesktop.DBus.Properties", "Get",
2090 "xyz.openbmc_project.Control.PowerSupplyRedundancy", property);
2091 if (ec)
2092 {
2093 phosphor::logging::log<phosphor::logging::level::ERR>(
2094 "Failed to get dbus property to cold redundancy");
2095 return -1;
2096 }
2097 return 0;
2098}
2099
2100uint8_t getPSUCount(void)
2101{
2102 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
2103 ipmi::Value num;
2104 try
2105 {
2106 num = ipmi::getDbusProperty(
2107 *dbus, "xyz.openbmc_project.PSURedundancy",
2108 "/xyz/openbmc_project/control/power_supply_redundancy",
2109 "xyz.openbmc_project.Control.PowerSupplyRedundancy", "PSUNumber");
2110 }
2111 catch (sdbusplus::exception_t& e)
2112 {
2113 phosphor::logging::log<phosphor::logging::level::ERR>(
2114 "Failed to get PSUNumber property from dbus interface");
2115 return 0;
2116 }
2117 uint8_t* pNum = std::get_if<uint8_t>(&num);
2118 if (!pNum)
2119 {
2120 phosphor::logging::log<phosphor::logging::level::ERR>(
2121 "Error to get PSU Number");
2122 return 0;
2123 }
2124 return *pNum;
2125}
2126
2127bool validateCRAlgo(std::vector<uint8_t>& conf, uint8_t num)
2128{
2129 if (conf.size() < num)
2130 {
2131 phosphor::logging::log<phosphor::logging::level::ERR>(
2132 "Invalid PSU Ranking");
2133 return false;
2134 }
2135 std::set<uint8_t> confSet;
2136 for (uint8_t i = 0; i < num; i++)
2137 {
2138 if (conf[i] > num)
2139 {
2140 phosphor::logging::log<phosphor::logging::level::ERR>(
2141 "PSU Ranking is larger than current PSU number");
2142 return false;
2143 }
2144 confSet.emplace(conf[i]);
2145 }
2146
2147 if (confSet.size() != num)
2148 {
2149 phosphor::logging::log<phosphor::logging::level::ERR>(
2150 "duplicate PSU Ranking");
2151 return false;
2152 }
2153 return true;
2154}
2155
2156enum class crParameter
2157{
2158 crStatus = 0,
2159 crFeature = 1,
2160 rotationFeature = 2,
2161 rotationAlgo = 3,
2162 rotationPeriod = 4,
Yong Li19445ab2019-12-20 18:25:29 +08002163 numOfPSU = 5,
2164 rotationRankOrderEffective = 6
Cheng C Yang773703a2019-08-15 09:41:11 +08002165};
2166
2167constexpr ipmi::Cc ccParameterNotSupported = 0x80;
2168static const constexpr uint32_t oneDay = 0x15180;
2169static const constexpr uint32_t oneMonth = 0xf53700;
2170static const constexpr uint8_t userSpecific = 0x01;
2171static const constexpr uint8_t crSetCompleted = 0;
2172ipmi::RspType<uint8_t> ipmiOEMSetCRConfig(ipmi::Context::ptr ctx,
2173 uint8_t parameter,
2174 ipmi::message::Payload& payload)
2175{
2176 switch (static_cast<crParameter>(parameter))
2177 {
Cheng C Yang773703a2019-08-15 09:41:11 +08002178 case crParameter::rotationFeature:
2179 {
2180 uint8_t param1;
2181 if (payload.unpack(param1) || !payload.fullyUnpacked())
2182 {
2183 return ipmi::responseReqDataLenInvalid();
2184 }
2185 // Rotation Enable can only be true or false
2186 if (param1 > 1)
2187 {
2188 return ipmi::responseInvalidFieldRequest();
2189 }
2190 if (setCRConfig(ctx, "RotationEnabled", static_cast<bool>(param1)))
2191 {
2192 return ipmi::responseResponseError();
2193 }
2194 break;
2195 }
2196 case crParameter::rotationAlgo:
2197 {
2198 // Rotation Algorithm can only be 0-BMC Specific or 1-User Specific
2199 std::string algoName;
2200 uint8_t param1;
2201 if (payload.unpack(param1))
2202 {
2203 return ipmi::responseReqDataLenInvalid();
2204 }
2205 switch (param1)
2206 {
2207 case 0:
2208 algoName = "xyz.openbmc_project.Control."
2209 "PowerSupplyRedundancy.Algo.bmcSpecific";
2210 break;
2211 case 1:
2212 algoName = "xyz.openbmc_project.Control."
2213 "PowerSupplyRedundancy.Algo.userSpecific";
2214 break;
2215 default:
2216 return ipmi::responseInvalidFieldRequest();
2217 }
2218 if (setCRConfig(ctx, "RotationAlgorithm", algoName))
2219 {
2220 return ipmi::responseResponseError();
2221 }
2222
2223 uint8_t numberOfPSU = getPSUCount();
2224 if (!numberOfPSU)
2225 {
2226 return ipmi::responseResponseError();
2227 }
2228 std::vector<uint8_t> rankOrder;
2229
2230 if (param1 == userSpecific)
2231 {
2232 if (payload.unpack(rankOrder) || !payload.fullyUnpacked())
2233 {
2234 ipmi::responseReqDataLenInvalid();
2235 }
Yong Li83315132019-10-23 17:42:24 +08002236 if (rankOrder.size() != numberOfPSU)
Cheng C Yang773703a2019-08-15 09:41:11 +08002237 {
2238 return ipmi::responseReqDataLenInvalid();
2239 }
2240
2241 if (!validateCRAlgo(rankOrder, numberOfPSU))
2242 {
2243 return ipmi::responseInvalidFieldRequest();
2244 }
2245 }
2246 else
2247 {
2248 if (rankOrder.size() > 0)
2249 {
2250 return ipmi::responseReqDataLenInvalid();
2251 }
2252 for (uint8_t i = 1; i <= numberOfPSU; i++)
2253 {
2254 rankOrder.emplace_back(i);
2255 }
2256 }
2257 if (setCRConfig(ctx, "RotationRankOrder", rankOrder))
2258 {
2259 return ipmi::responseResponseError();
2260 }
2261 break;
2262 }
2263 case crParameter::rotationPeriod:
2264 {
2265 // Minimum Rotation period is One day (86400 seconds) and Max
2266 // Rotation Period is 6 month (0xf53700 seconds)
2267 uint32_t period;
2268 if (payload.unpack(period) || !payload.fullyUnpacked())
2269 {
2270 return ipmi::responseReqDataLenInvalid();
2271 }
2272 if ((period < oneDay) || (period > oneMonth))
2273 {
2274 return ipmi::responseInvalidFieldRequest();
2275 }
2276 if (setCRConfig(ctx, "PeriodOfRotation", period))
2277 {
2278 return ipmi::responseResponseError();
2279 }
2280 break;
2281 }
2282 default:
2283 {
2284 return ipmi::response(ccParameterNotSupported);
2285 }
2286 }
2287
Cheng C Yang773703a2019-08-15 09:41:11 +08002288 return ipmi::responseSuccess(crSetCompleted);
2289}
2290
Yong Li83315132019-10-23 17:42:24 +08002291ipmi::RspType<uint8_t, std::variant<uint8_t, uint32_t, std::vector<uint8_t>>>
Cheng C Yang773703a2019-08-15 09:41:11 +08002292 ipmiOEMGetCRConfig(ipmi::Context::ptr ctx, uint8_t parameter)
2293{
2294 crConfigVariant value;
2295 switch (static_cast<crParameter>(parameter))
2296 {
2297 case crParameter::crStatus:
2298 {
2299 if (getCRConfig(ctx, "ColdRedundancyStatus", value))
2300 {
2301 return ipmi::responseResponseError();
2302 }
2303 std::string* pStatus = std::get_if<std::string>(&value);
2304 if (!pStatus)
2305 {
2306 phosphor::logging::log<phosphor::logging::level::ERR>(
2307 "Error to get ColdRedundancyStatus property");
2308 return ipmi::responseResponseError();
2309 }
2310 namespace server = sdbusplus::xyz::openbmc_project::Control::server;
2311 auto status =
2312 server::PowerSupplyRedundancy::convertStatusFromString(
2313 *pStatus);
2314 switch (status)
2315 {
2316 case server::PowerSupplyRedundancy::Status::inProgress:
Cheng C Yangf41e3342019-09-10 04:47:23 +08002317 return ipmi::responseSuccess(parameter,
Kuiying Wange45333a2020-07-22 22:06:37 +08002318 static_cast<uint8_t>(1));
Cheng C Yang773703a2019-08-15 09:41:11 +08002319
2320 case server::PowerSupplyRedundancy::Status::completed:
Cheng C Yangf41e3342019-09-10 04:47:23 +08002321 return ipmi::responseSuccess(parameter,
Kuiying Wange45333a2020-07-22 22:06:37 +08002322 static_cast<uint8_t>(0));
Cheng C Yang773703a2019-08-15 09:41:11 +08002323 default:
2324 phosphor::logging::log<phosphor::logging::level::ERR>(
2325 "Error to get valid status");
2326 return ipmi::responseResponseError();
2327 }
2328 }
2329 case crParameter::crFeature:
2330 {
Kuiying Wange45333a2020-07-22 22:06:37 +08002331 if (getCRConfig(ctx, "PowerSupplyRedundancyEnabled", value))
Cheng C Yang773703a2019-08-15 09:41:11 +08002332 {
2333 return ipmi::responseResponseError();
2334 }
2335 bool* pResponse = std::get_if<bool>(&value);
2336 if (!pResponse)
2337 {
2338 phosphor::logging::log<phosphor::logging::level::ERR>(
Kuiying Wange45333a2020-07-22 22:06:37 +08002339 "Error to get PowerSupplyRedundancyEnabled property");
Cheng C Yang773703a2019-08-15 09:41:11 +08002340 return ipmi::responseResponseError();
2341 }
2342
Cheng C Yangf41e3342019-09-10 04:47:23 +08002343 return ipmi::responseSuccess(parameter,
2344 static_cast<uint8_t>(*pResponse));
Cheng C Yang773703a2019-08-15 09:41:11 +08002345 }
2346 case crParameter::rotationFeature:
2347 {
2348 if (getCRConfig(ctx, "RotationEnabled", value))
2349 {
2350 return ipmi::responseResponseError();
2351 }
2352 bool* pResponse = std::get_if<bool>(&value);
2353 if (!pResponse)
2354 {
2355 phosphor::logging::log<phosphor::logging::level::ERR>(
2356 "Error to get RotationEnabled property");
2357 return ipmi::responseResponseError();
2358 }
Cheng C Yangf41e3342019-09-10 04:47:23 +08002359 return ipmi::responseSuccess(parameter,
2360 static_cast<uint8_t>(*pResponse));
Cheng C Yang773703a2019-08-15 09:41:11 +08002361 }
2362 case crParameter::rotationAlgo:
2363 {
2364 if (getCRConfig(ctx, "RotationAlgorithm", value))
2365 {
2366 return ipmi::responseResponseError();
2367 }
2368
2369 std::string* pAlgo = std::get_if<std::string>(&value);
2370 if (!pAlgo)
2371 {
2372 phosphor::logging::log<phosphor::logging::level::ERR>(
2373 "Error to get RotationAlgorithm property");
2374 return ipmi::responseResponseError();
2375 }
Yong Li83315132019-10-23 17:42:24 +08002376 std::vector<uint8_t> response;
Cheng C Yang773703a2019-08-15 09:41:11 +08002377 namespace server = sdbusplus::xyz::openbmc_project::Control::server;
2378 auto algo =
2379 server::PowerSupplyRedundancy::convertAlgoFromString(*pAlgo);
Yong Li83315132019-10-23 17:42:24 +08002380
Cheng C Yang773703a2019-08-15 09:41:11 +08002381 switch (algo)
2382 {
2383 case server::PowerSupplyRedundancy::Algo::bmcSpecific:
Yong Li83315132019-10-23 17:42:24 +08002384 response.push_back(0);
Cheng C Yang773703a2019-08-15 09:41:11 +08002385 break;
2386 case server::PowerSupplyRedundancy::Algo::userSpecific:
Yong Li83315132019-10-23 17:42:24 +08002387 response.push_back(1);
Cheng C Yang773703a2019-08-15 09:41:11 +08002388 break;
2389 default:
2390 phosphor::logging::log<phosphor::logging::level::ERR>(
2391 "Error to get valid algo");
2392 return ipmi::responseResponseError();
2393 }
2394
2395 if (getCRConfig(ctx, "RotationRankOrder", value))
2396 {
2397 return ipmi::responseResponseError();
2398 }
2399 std::vector<uint8_t>* pResponse =
2400 std::get_if<std::vector<uint8_t>>(&value);
2401 if (!pResponse)
2402 {
2403 phosphor::logging::log<phosphor::logging::level::ERR>(
2404 "Error to get RotationRankOrder property");
2405 return ipmi::responseResponseError();
2406 }
Yong Li83315132019-10-23 17:42:24 +08002407
Cheng C Yang773703a2019-08-15 09:41:11 +08002408 std::copy(pResponse->begin(), pResponse->end(),
Yong Li83315132019-10-23 17:42:24 +08002409 std::back_inserter(response));
2410
Cheng C Yangf41e3342019-09-10 04:47:23 +08002411 return ipmi::responseSuccess(parameter, response);
Cheng C Yang773703a2019-08-15 09:41:11 +08002412 }
2413 case crParameter::rotationPeriod:
2414 {
2415 if (getCRConfig(ctx, "PeriodOfRotation", value))
2416 {
2417 return ipmi::responseResponseError();
2418 }
2419 uint32_t* pResponse = std::get_if<uint32_t>(&value);
2420 if (!pResponse)
2421 {
2422 phosphor::logging::log<phosphor::logging::level::ERR>(
2423 "Error to get RotationAlgorithm property");
2424 return ipmi::responseResponseError();
2425 }
Cheng C Yangf41e3342019-09-10 04:47:23 +08002426 return ipmi::responseSuccess(parameter, *pResponse);
Cheng C Yang773703a2019-08-15 09:41:11 +08002427 }
2428 case crParameter::numOfPSU:
2429 {
2430 uint8_t numberOfPSU = getPSUCount();
2431 if (!numberOfPSU)
2432 {
2433 return ipmi::responseResponseError();
2434 }
Cheng C Yangf41e3342019-09-10 04:47:23 +08002435 return ipmi::responseSuccess(parameter, numberOfPSU);
Cheng C Yang773703a2019-08-15 09:41:11 +08002436 }
Yong Li19445ab2019-12-20 18:25:29 +08002437 case crParameter::rotationRankOrderEffective:
2438 {
2439 if (getCRConfig(ctx, "RotationRankOrder", value,
2440 "xyz.openbmc_project.PSURedundancy"))
2441 {
2442 return ipmi::responseResponseError();
2443 }
2444 std::vector<uint8_t>* pResponse =
2445 std::get_if<std::vector<uint8_t>>(&value);
2446 if (!pResponse)
2447 {
2448 phosphor::logging::log<phosphor::logging::level::ERR>(
2449 "Error to get effective RotationRankOrder property");
2450 return ipmi::responseResponseError();
2451 }
2452 return ipmi::responseSuccess(parameter, *pResponse);
2453 }
Cheng C Yang773703a2019-08-15 09:41:11 +08002454 default:
2455 {
2456 return ipmi::response(ccParameterNotSupported);
2457 }
2458 }
2459}
2460
Zhu, Yungebe560b02019-04-21 21:19:21 -04002461ipmi::RspType<> ipmiOEMSetFaultIndication(uint8_t sourceId, uint8_t faultType,
2462 uint8_t faultState,
2463 uint8_t faultGroup,
2464 std::array<uint8_t, 8>& ledStateData)
2465{
Zhu, Yungebe560b02019-04-21 21:19:21 -04002466 constexpr auto maxFaultType = static_cast<size_t>(RemoteFaultType::max);
2467 static const std::array<std::string, maxFaultType> faultNames = {
2468 "faultFan", "faultTemp", "faultPower",
2469 "faultDriveSlot", "faultSoftware", "faultMemory"};
Zhu, Yungebe560b02019-04-21 21:19:21 -04002470
2471 constexpr uint8_t maxFaultSource = 0x4;
2472 constexpr uint8_t skipLEDs = 0xFF;
2473 constexpr uint8_t pinSize = 64;
2474 constexpr uint8_t groupSize = 16;
Zhikui Rence4e73f2019-12-06 13:59:47 -08002475 constexpr uint8_t groupNum = 5; // 4 for fault memory, 1 for faultFan
Zhu, Yungebe560b02019-04-21 21:19:21 -04002476
Zhikui Rence4e73f2019-12-06 13:59:47 -08002477 // same pin names need to be defined in dts file
2478 static const std::array<std::array<std::string, groupSize>, groupNum>
2479 faultLedPinNames = {{
2480 "LED_CPU1_CH1_DIMM1_FAULT",
2481 "LED_CPU1_CH1_DIMM2_FAULT",
2482 "LED_CPU1_CH2_DIMM1_FAULT",
2483 "LED_CPU1_CH2_DIMM2_FAULT",
2484 "LED_CPU1_CH3_DIMM1_FAULT",
2485 "LED_CPU1_CH3_DIMM2_FAULT",
2486 "LED_CPU1_CH4_DIMM1_FAULT",
2487 "LED_CPU1_CH4_DIMM2_FAULT",
2488 "LED_CPU1_CH5_DIMM1_FAULT",
2489 "LED_CPU1_CH5_DIMM2_FAULT",
2490 "LED_CPU1_CH6_DIMM1_FAULT",
2491 "LED_CPU1_CH6_DIMM2_FAULT",
2492 "",
2493 "",
2494 "",
2495 "", // end of group1
2496 "LED_CPU2_CH1_DIMM1_FAULT",
2497 "LED_CPU2_CH1_DIMM2_FAULT",
2498 "LED_CPU2_CH2_DIMM1_FAULT",
2499 "LED_CPU2_CH2_DIMM2_FAULT",
2500 "LED_CPU2_CH3_DIMM1_FAULT",
2501 "LED_CPU2_CH3_DIMM2_FAULT",
2502 "LED_CPU2_CH4_DIMM1_FAULT",
2503 "LED_CPU2_CH4_DIMM2_FAULT",
2504 "LED_CPU2_CH5_DIMM1_FAULT",
2505 "LED_CPU2_CH5_DIMM2_FAULT",
2506 "LED_CPU2_CH6_DIMM1_FAULT",
2507 "LED_CPU2_CH6_DIMM2_FAULT",
2508 "",
2509 "",
2510 "",
2511 "", // endof group2
2512 "LED_CPU3_CH1_DIMM1_FAULT",
2513 "LED_CPU3_CH1_DIMM2_FAULT",
2514 "LED_CPU3_CH2_DIMM1_FAULT",
2515 "LED_CPU3_CH2_DIMM2_FAULT",
2516 "LED_CPU3_CH3_DIMM1_FAULT",
2517 "LED_CPU3_CH3_DIMM2_FAULT",
2518 "LED_CPU3_CH4_DIMM1_FAULT",
2519 "LED_CPU3_CH4_DIMM2_FAULT",
2520 "LED_CPU3_CH5_DIMM1_FAULT",
2521 "LED_CPU3_CH5_DIMM2_FAULT",
2522 "LED_CPU3_CH6_DIMM1_FAULT",
2523 "LED_CPU3_CH6_DIMM2_FAULT",
2524 "",
2525 "",
2526 "",
2527 "", // end of group3
2528 "LED_CPU4_CH1_DIMM1_FAULT",
2529 "LED_CPU4_CH1_DIMM2_FAULT",
2530 "LED_CPU4_CH2_DIMM1_FAULT",
2531 "LED_CPU4_CH2_DIMM2_FAULT",
2532 "LED_CPU4_CH3_DIMM1_FAULT",
2533 "LED_CPU4_CH3_DIMM2_FAULT",
2534 "LED_CPU4_CH4_DIMM1_FAULT",
2535 "LED_CPU4_CH4_DIMM2_FAULT",
2536 "LED_CPU4_CH5_DIMM1_FAULT",
2537 "LED_CPU4_CH5_DIMM2_FAULT",
2538 "LED_CPU4_CH6_DIMM1_FAULT",
2539 "LED_CPU4_CH6_DIMM2_FAULT",
2540 "",
2541 "",
2542 "",
2543 "", // end of group4
2544 "LED_FAN1_FAULT",
2545 "LED_FAN2_FAULT",
2546 "LED_FAN3_FAULT",
2547 "LED_FAN4_FAULT",
2548 "LED_FAN5_FAULT",
2549 "LED_FAN6_FAULT",
2550 "LED_FAN7_FAULT",
2551 "LED_FAN8_FAULT",
2552 "",
2553 "",
2554 "",
2555 "",
2556 "",
2557 "",
2558 "",
2559 "" // end of group5
2560 }};
Zhu, Yungebe560b02019-04-21 21:19:21 -04002561
Zhikui Rence4e73f2019-12-06 13:59:47 -08002562 // Validate the source, fault type --
2563 // (Byte 1) sourceId: Unspecified, Hot-Swap Controller 0, Hot-Swap
2564 // Controller 1, BIOS (Byte 2) fault type: fan, temperature, power,
2565 // driveslot, software, memory (Byte 3) FaultState: OK, Degraded,
2566 // Non-Critical, Critical, Non-Recoverable, (Byte 4) is faultGroup,
2567 // definition differs based on fault type (Byte 2)
2568 // Type Fan=> Group: 0=FanGroupID, FF-not used
2569 // Byte 5-11 00h, not used
2570 // Byte12 FanLedState [7:0]-Fans 7:0
2571 // Type Memory=> Group: 0 = DIMM GroupID, FF-not used
2572 // Byte 5:12 - DIMM LED state (64bit field, LS Byte first)
2573 // [63:48] = CPU4 channels 7:0, 2 bits per channel
2574 // [47:32] = CPU3 channels 7:0, 2 bits per channel
2575 // [31:16] = CPU2 channels 7:0, 2 bits per channel
2576 // [15:0] = CPU1 channels 7:0, 2 bits per channel
2577 // Type Other=> Component Fault LED Group ID, not used set to 0xFF
2578 // Byte[5:12]: reserved 0x00h
Zhu, Yungebe560b02019-04-21 21:19:21 -04002579 if ((sourceId >= maxFaultSource) ||
2580 (faultType >= static_cast<int8_t>(RemoteFaultType::max)) ||
2581 (faultState >= static_cast<int8_t>(RemoteFaultState::maxFaultState)) ||
2582 (faultGroup >= static_cast<int8_t>(DimmFaultType::maxFaultGroup)))
2583 {
2584 return ipmi::responseParmOutOfRange();
2585 }
2586
Zhikui Rence4e73f2019-12-06 13:59:47 -08002587 size_t pinGroupOffset = 0;
2588 size_t pinGroupMax = pinSize / groupSize;
2589 if (RemoteFaultType::fan == RemoteFaultType(faultType))
Zhu, Yungebe560b02019-04-21 21:19:21 -04002590 {
Zhikui Rence4e73f2019-12-06 13:59:47 -08002591 pinGroupOffset = 4;
2592 pinGroupMax = groupNum - pinSize / groupSize;
Zhu, Yungebe560b02019-04-21 21:19:21 -04002593 }
2594
2595 switch (RemoteFaultType(faultType))
2596 {
2597 case (RemoteFaultType::fan):
2598 case (RemoteFaultType::memory):
2599 {
2600 if (faultGroup == skipLEDs)
2601 {
2602 return ipmi::responseSuccess();
2603 }
Zhu, Yungebe560b02019-04-21 21:19:21 -04002604 // calculate led state bit filed count, each byte has 8bits
2605 // the maximum bits will be 8 * 8 bits
2606 constexpr uint8_t size = sizeof(ledStateData) * 8;
Zhikui Rence4e73f2019-12-06 13:59:47 -08002607
2608 // assemble ledState
2609 uint64_t ledState = 0;
2610 bool hasError = false;
Zhu, Yungebe560b02019-04-21 21:19:21 -04002611 for (int i = 0; i < sizeof(ledStateData); i++)
2612 {
2613 ledState = (uint64_t)(ledState << 8);
2614 ledState = (uint64_t)(ledState | (uint64_t)ledStateData[i]);
2615 }
Zhu, Yungebe560b02019-04-21 21:19:21 -04002616 std::bitset<size> ledStateBits(ledState);
Zhu, Yungebe560b02019-04-21 21:19:21 -04002617
Zhikui Rence4e73f2019-12-06 13:59:47 -08002618 for (int group = 0; group < pinGroupMax; group++)
2619 {
2620 for (int i = 0; i < groupSize; i++)
2621 { // skip non-existing pins
2622 if (0 == faultLedPinNames[group + pinGroupOffset][i].size())
2623 {
2624 continue;
2625 }
Zhu, Yungebe560b02019-04-21 21:19:21 -04002626
Zhikui Rence4e73f2019-12-06 13:59:47 -08002627 gpiod::line line = gpiod::find_line(
2628 faultLedPinNames[group + pinGroupOffset][i]);
2629 if (!line)
2630 {
2631 phosphor::logging::log<phosphor::logging::level::ERR>(
2632 "Not Find Led Gpio Device!",
2633 phosphor::logging::entry(
2634 "DEVICE=%s",
2635 faultLedPinNames[group + pinGroupOffset][i]
2636 .c_str()));
2637 hasError = true;
2638 continue;
2639 }
Zhu, Yungebe560b02019-04-21 21:19:21 -04002640
Zhikui Rence4e73f2019-12-06 13:59:47 -08002641 bool activeHigh =
2642 (line.active_state() == gpiod::line::ACTIVE_HIGH);
2643 try
2644 {
2645 line.request(
2646 {"faultLed", gpiod::line_request::DIRECTION_OUTPUT,
2647 activeHigh
2648 ? 0
2649 : gpiod::line_request::FLAG_ACTIVE_LOW});
2650 line.set_value(ledStateBits[i + group * groupSize]);
2651 }
2652 catch (std::system_error&)
2653 {
2654 phosphor::logging::log<phosphor::logging::level::ERR>(
2655 "Error write Led Gpio Device!",
2656 phosphor::logging::entry(
2657 "DEVICE=%s",
2658 faultLedPinNames[group + pinGroupOffset][i]
2659 .c_str()));
2660 hasError = true;
2661 continue;
2662 }
2663 } // for int i
2664 }
2665 if (hasError)
2666 {
2667 return ipmi::responseResponseError();
Zhu, Yungebe560b02019-04-21 21:19:21 -04002668 }
2669 break;
2670 }
2671 default:
2672 {
2673 // now only support two fault types
2674 return ipmi::responseParmOutOfRange();
2675 }
Zhikui Rence4e73f2019-12-06 13:59:47 -08002676 } // switch
Zhu, Yungebe560b02019-04-21 21:19:21 -04002677 return ipmi::responseSuccess();
2678}
2679
Richard Marian Thomaiyarea537d52019-04-24 21:33:48 +05302680ipmi::RspType<uint8_t> ipmiOEMReadBoardProductId()
2681{
2682 uint8_t prodId = 0;
2683 try
2684 {
Vernon Mauery15419dd2019-05-24 09:40:30 -07002685 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
Richard Marian Thomaiyarea537d52019-04-24 21:33:48 +05302686 const DbusObjectInfo& object = getDbusObject(
Vernon Mauery15419dd2019-05-24 09:40:30 -07002687 *dbus, "xyz.openbmc_project.Inventory.Item.Board",
Richard Marian Thomaiyarea537d52019-04-24 21:33:48 +05302688 "/xyz/openbmc_project/inventory/system/board/", "Baseboard");
2689 const Value& propValue = getDbusProperty(
Vernon Mauery15419dd2019-05-24 09:40:30 -07002690 *dbus, object.second, object.first,
Suryakanth Sekar6c57e5c2020-01-10 17:11:58 +05302691 "xyz.openbmc_project.Inventory.Item.Board.Motherboard",
2692 "ProductId");
Richard Marian Thomaiyarea537d52019-04-24 21:33:48 +05302693 prodId = static_cast<uint8_t>(std::get<uint64_t>(propValue));
2694 }
2695 catch (std::exception& e)
2696 {
2697 phosphor::logging::log<phosphor::logging::level::ERR>(
2698 "ipmiOEMReadBoardProductId: Product ID read failed!",
2699 phosphor::logging::entry("ERR=%s", e.what()));
2700 }
2701 return ipmi::responseSuccess(prodId);
2702}
2703
Richard Marian Thomaiyard801e462019-06-20 01:05:40 +05302704/** @brief implements the get security mode command
2705 * @param ctx - ctx pointer
2706 *
2707 * @returns IPMI completion code with following data
2708 * - restriction mode value - As specified in
2709 * xyz.openbmc_project.Control.Security.RestrictionMode.interface.yaml
2710 * - special mode value - As specified in
2711 * xyz.openbmc_project.Control.Security.SpecialMode.interface.yaml
2712 */
2713ipmi::RspType<uint8_t, uint8_t> ipmiGetSecurityMode(ipmi::Context::ptr ctx)
2714{
2715 namespace securityNameSpace =
2716 sdbusplus::xyz::openbmc_project::Control::Security::server;
2717 uint8_t restrictionModeValue = 0;
2718 uint8_t specialModeValue = 0;
2719
2720 boost::system::error_code ec;
2721 auto varRestrMode = ctx->bus->yield_method_call<std::variant<std::string>>(
James Feist28c72902019-09-16 10:34:07 -07002722 ctx->yield, ec, restricionModeService, restricionModeBasePath,
Richard Marian Thomaiyard801e462019-06-20 01:05:40 +05302723 dBusPropertyIntf, dBusPropertyGetMethod, restricionModeIntf,
2724 restricionModeProperty);
2725 if (ec)
2726 {
2727 phosphor::logging::log<phosphor::logging::level::ERR>(
2728 "ipmiGetSecurityMode: failed to get RestrictionMode property",
2729 phosphor::logging::entry("ERROR=%s", ec.message().c_str()));
2730 return ipmi::responseUnspecifiedError();
2731 }
2732 restrictionModeValue = static_cast<uint8_t>(
2733 securityNameSpace::RestrictionMode::convertModesFromString(
2734 std::get<std::string>(varRestrMode)));
Richard Marian Thomaiyar8d4f8d72019-11-11 12:06:40 +05302735 auto varSpecialMode =
2736 ctx->bus->yield_method_call<std::variant<std::string>>(
2737 ctx->yield, ec, specialModeService, specialModeBasePath,
2738 dBusPropertyIntf, dBusPropertyGetMethod, specialModeIntf,
2739 specialModeProperty);
Richard Marian Thomaiyard801e462019-06-20 01:05:40 +05302740 if (ec)
2741 {
2742 phosphor::logging::log<phosphor::logging::level::ERR>(
2743 "ipmiGetSecurityMode: failed to get SpecialMode property",
2744 phosphor::logging::entry("ERROR=%s", ec.message().c_str()));
2745 // fall through, let us not worry about SpecialMode property, which is
2746 // not required in user scenario
2747 }
2748 else
2749 {
Richard Marian Thomaiyar8d4f8d72019-11-11 12:06:40 +05302750 specialModeValue = static_cast<uint8_t>(
2751 securityNameSpace::SpecialMode::convertModesFromString(
2752 std::get<std::string>(varSpecialMode)));
Richard Marian Thomaiyard801e462019-06-20 01:05:40 +05302753 }
2754 return ipmi::responseSuccess(restrictionModeValue, specialModeValue);
2755}
2756
2757/** @brief implements the set security mode command
2758 * Command allows to upgrade the restriction mode and won't allow
2759 * to downgrade from system interface
2760 * @param ctx - ctx pointer
2761 * @param restrictionMode - restriction mode value to be set.
2762 *
2763 * @returns IPMI completion code
2764 */
2765ipmi::RspType<> ipmiSetSecurityMode(ipmi::Context::ptr ctx,
Richard Marian Thomaiyar10791062019-11-11 12:19:53 +05302766 uint8_t restrictionMode,
2767 std::optional<uint8_t> specialMode)
Richard Marian Thomaiyard801e462019-06-20 01:05:40 +05302768{
Richard Marian Thomaiyar10791062019-11-11 12:19:53 +05302769#ifndef BMC_VALIDATION_UNSECURE_FEATURE
2770 if (specialMode)
2771 {
2772 return ipmi::responseReqDataLenInvalid();
2773 }
2774#endif
Richard Marian Thomaiyard801e462019-06-20 01:05:40 +05302775 namespace securityNameSpace =
2776 sdbusplus::xyz::openbmc_project::Control::Security::server;
2777
2778 ChannelInfo chInfo;
2779 if (getChannelInfo(ctx->channel, chInfo) != ccSuccess)
2780 {
2781 phosphor::logging::log<phosphor::logging::level::ERR>(
2782 "ipmiSetSecurityMode: Failed to get Channel Info",
2783 phosphor::logging::entry("CHANNEL=%d", ctx->channel));
2784 return ipmi::responseUnspecifiedError();
2785 }
2786 auto reqMode =
2787 static_cast<securityNameSpace::RestrictionMode::Modes>(restrictionMode);
2788
2789 if ((reqMode < securityNameSpace::RestrictionMode::Modes::Provisioning) ||
2790 (reqMode >
2791 securityNameSpace::RestrictionMode::Modes::ProvisionedHostDisabled))
2792 {
2793 return ipmi::responseInvalidFieldRequest();
2794 }
2795
2796 boost::system::error_code ec;
2797 auto varRestrMode = ctx->bus->yield_method_call<std::variant<std::string>>(
James Feist28c72902019-09-16 10:34:07 -07002798 ctx->yield, ec, restricionModeService, restricionModeBasePath,
Richard Marian Thomaiyard801e462019-06-20 01:05:40 +05302799 dBusPropertyIntf, dBusPropertyGetMethod, restricionModeIntf,
2800 restricionModeProperty);
2801 if (ec)
2802 {
2803 phosphor::logging::log<phosphor::logging::level::ERR>(
2804 "ipmiSetSecurityMode: failed to get RestrictionMode property",
2805 phosphor::logging::entry("ERROR=%s", ec.message().c_str()));
2806 return ipmi::responseUnspecifiedError();
2807 }
2808 auto currentRestrictionMode =
2809 securityNameSpace::RestrictionMode::convertModesFromString(
2810 std::get<std::string>(varRestrMode));
2811
2812 if (chInfo.mediumType !=
2813 static_cast<uint8_t>(EChannelMediumType::lan8032) &&
2814 currentRestrictionMode > reqMode)
2815 {
2816 phosphor::logging::log<phosphor::logging::level::ERR>(
2817 "ipmiSetSecurityMode - Downgrading security mode not supported "
2818 "through system interface",
2819 phosphor::logging::entry(
2820 "CUR_MODE=%d", static_cast<uint8_t>(currentRestrictionMode)),
2821 phosphor::logging::entry("REQ_MODE=%d", restrictionMode));
2822 return ipmi::responseCommandNotAvailable();
2823 }
2824
2825 ec.clear();
2826 ctx->bus->yield_method_call<>(
James Feist28c72902019-09-16 10:34:07 -07002827 ctx->yield, ec, restricionModeService, restricionModeBasePath,
Richard Marian Thomaiyard801e462019-06-20 01:05:40 +05302828 dBusPropertyIntf, dBusPropertySetMethod, restricionModeIntf,
2829 restricionModeProperty,
2830 static_cast<std::variant<std::string>>(
2831 securityNameSpace::convertForMessage(reqMode)));
2832
2833 if (ec)
2834 {
2835 phosphor::logging::log<phosphor::logging::level::ERR>(
2836 "ipmiSetSecurityMode: failed to set RestrictionMode property",
2837 phosphor::logging::entry("ERROR=%s", ec.message().c_str()));
2838 return ipmi::responseUnspecifiedError();
2839 }
Richard Marian Thomaiyar10791062019-11-11 12:19:53 +05302840
2841#ifdef BMC_VALIDATION_UNSECURE_FEATURE
2842 if (specialMode)
2843 {
Jayaprakash Mutyalad77489f2020-09-05 01:00:04 +00002844 constexpr uint8_t mfgMode = 0x01;
2845 // Manufacturing mode is reserved. So can't enable this mode.
2846 if (specialMode.value() == mfgMode)
2847 {
2848 phosphor::logging::log<phosphor::logging::level::INFO>(
2849 "ipmiSetSecurityMode: Can't enable Manufacturing mode");
2850 return ipmi::responseInvalidFieldRequest();
2851 }
2852
Richard Marian Thomaiyar10791062019-11-11 12:19:53 +05302853 ec.clear();
2854 ctx->bus->yield_method_call<>(
2855 ctx->yield, ec, specialModeService, specialModeBasePath,
2856 dBusPropertyIntf, dBusPropertySetMethod, specialModeIntf,
2857 specialModeProperty,
2858 static_cast<std::variant<std::string>>(
2859 securityNameSpace::convertForMessage(
2860 static_cast<securityNameSpace::SpecialMode::Modes>(
2861 specialMode.value()))));
2862
2863 if (ec)
2864 {
2865 phosphor::logging::log<phosphor::logging::level::ERR>(
2866 "ipmiSetSecurityMode: failed to set SpecialMode property",
2867 phosphor::logging::entry("ERROR=%s", ec.message().c_str()));
2868 return ipmi::responseUnspecifiedError();
2869 }
2870 }
2871#endif
Richard Marian Thomaiyard801e462019-06-20 01:05:40 +05302872 return ipmi::responseSuccess();
2873}
2874
Vernon Mauery4ac799d2019-05-20 15:50:37 -07002875ipmi::RspType<uint8_t /* restore status */>
2876 ipmiRestoreConfiguration(const std::array<uint8_t, 3>& clr, uint8_t cmd)
2877{
2878 static constexpr std::array<uint8_t, 3> expClr = {'C', 'L', 'R'};
2879
2880 if (clr != expClr)
2881 {
2882 return ipmi::responseInvalidFieldRequest();
2883 }
2884 constexpr uint8_t cmdStatus = 0;
2885 constexpr uint8_t cmdDefaultRestore = 0xaa;
2886 constexpr uint8_t cmdFullRestore = 0xbb;
2887 constexpr uint8_t cmdFormat = 0xcc;
2888
2889 constexpr const char* restoreOpFname = "/tmp/.rwfs/.restore_op";
2890
2891 switch (cmd)
2892 {
2893 case cmdStatus:
2894 break;
2895 case cmdDefaultRestore:
2896 case cmdFullRestore:
2897 case cmdFormat:
2898 {
2899 // write file to rwfs root
2900 int value = (cmd - 1) & 0x03; // map aa, bb, cc => 1, 2, 3
2901 std::ofstream restoreFile(restoreOpFname);
2902 if (!restoreFile)
2903 {
2904 return ipmi::responseUnspecifiedError();
2905 }
2906 restoreFile << value << "\n";
2907 break;
2908 }
2909 default:
2910 return ipmi::responseInvalidFieldRequest();
2911 }
2912
2913 constexpr uint8_t restorePending = 0;
2914 constexpr uint8_t restoreComplete = 1;
2915
2916 uint8_t restoreStatus = std::filesystem::exists(restoreOpFname)
2917 ? restorePending
2918 : restoreComplete;
2919 return ipmi::responseSuccess(restoreStatus);
2920}
2921
Chen Yugang39736d52019-07-12 16:24:33 +08002922ipmi::RspType<uint8_t> ipmiOEMGetNmiSource(void)
2923{
2924 uint8_t bmcSource;
Chen Yugang97cf96e2019-11-01 08:55:11 +08002925 namespace nmi = sdbusplus::xyz::openbmc_project::Chassis::Control::server;
Chen Yugang39736d52019-07-12 16:24:33 +08002926
2927 try
2928 {
2929 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
2930 std::string service =
2931 getService(*dbus, oemNmiSourceIntf, oemNmiSourceObjPath);
2932 Value variant =
2933 getDbusProperty(*dbus, service, oemNmiSourceObjPath,
2934 oemNmiSourceIntf, oemNmiBmcSourceObjPathProp);
2935
2936 switch (nmi::NMISource::convertBMCSourceSignalFromString(
2937 std::get<std::string>(variant)))
2938 {
2939 case nmi::NMISource::BMCSourceSignal::None:
2940 bmcSource = static_cast<uint8_t>(NmiSource::none);
2941 break;
Chen Yugang97cf96e2019-11-01 08:55:11 +08002942 case nmi::NMISource::BMCSourceSignal::FrontPanelButton:
2943 bmcSource = static_cast<uint8_t>(NmiSource::frontPanelButton);
Chen Yugang39736d52019-07-12 16:24:33 +08002944 break;
Chen Yugang97cf96e2019-11-01 08:55:11 +08002945 case nmi::NMISource::BMCSourceSignal::Watchdog:
2946 bmcSource = static_cast<uint8_t>(NmiSource::watchdog);
Chen Yugang39736d52019-07-12 16:24:33 +08002947 break;
2948 case nmi::NMISource::BMCSourceSignal::ChassisCmd:
2949 bmcSource = static_cast<uint8_t>(NmiSource::chassisCmd);
2950 break;
2951 case nmi::NMISource::BMCSourceSignal::MemoryError:
2952 bmcSource = static_cast<uint8_t>(NmiSource::memoryError);
2953 break;
Chen Yugang97cf96e2019-11-01 08:55:11 +08002954 case nmi::NMISource::BMCSourceSignal::PciBusError:
2955 bmcSource = static_cast<uint8_t>(NmiSource::pciBusError);
Chen Yugang39736d52019-07-12 16:24:33 +08002956 break;
Chen Yugang97cf96e2019-11-01 08:55:11 +08002957 case nmi::NMISource::BMCSourceSignal::PCH:
2958 bmcSource = static_cast<uint8_t>(NmiSource::pch);
Chen Yugang39736d52019-07-12 16:24:33 +08002959 break;
Chen Yugang97cf96e2019-11-01 08:55:11 +08002960 case nmi::NMISource::BMCSourceSignal::Chipset:
2961 bmcSource = static_cast<uint8_t>(NmiSource::chipset);
Chen Yugang39736d52019-07-12 16:24:33 +08002962 break;
2963 default:
2964 phosphor::logging::log<phosphor::logging::level::ERR>(
2965 "NMI source: invalid property!",
2966 phosphor::logging::entry(
2967 "PROP=%s", std::get<std::string>(variant).c_str()));
2968 return ipmi::responseResponseError();
2969 }
2970 }
2971 catch (sdbusplus::exception::SdBusError& e)
2972 {
2973 phosphor::logging::log<phosphor::logging::level::ERR>(e.what());
2974 return ipmi::responseResponseError();
2975 }
2976
2977 return ipmi::responseSuccess(bmcSource);
2978}
2979
2980ipmi::RspType<> ipmiOEMSetNmiSource(uint8_t sourceId)
2981{
Chen Yugang97cf96e2019-11-01 08:55:11 +08002982 namespace nmi = sdbusplus::xyz::openbmc_project::Chassis::Control::server;
Chen Yugang39736d52019-07-12 16:24:33 +08002983
2984 nmi::NMISource::BMCSourceSignal bmcSourceSignal =
2985 nmi::NMISource::BMCSourceSignal::None;
2986
2987 switch (NmiSource(sourceId))
2988 {
2989 case NmiSource::none:
2990 bmcSourceSignal = nmi::NMISource::BMCSourceSignal::None;
2991 break;
Chen Yugang97cf96e2019-11-01 08:55:11 +08002992 case NmiSource::frontPanelButton:
2993 bmcSourceSignal = nmi::NMISource::BMCSourceSignal::FrontPanelButton;
Chen Yugang39736d52019-07-12 16:24:33 +08002994 break;
Chen Yugang97cf96e2019-11-01 08:55:11 +08002995 case NmiSource::watchdog:
2996 bmcSourceSignal = nmi::NMISource::BMCSourceSignal::Watchdog;
Chen Yugang39736d52019-07-12 16:24:33 +08002997 break;
2998 case NmiSource::chassisCmd:
2999 bmcSourceSignal = nmi::NMISource::BMCSourceSignal::ChassisCmd;
3000 break;
3001 case NmiSource::memoryError:
3002 bmcSourceSignal = nmi::NMISource::BMCSourceSignal::MemoryError;
3003 break;
Chen Yugang97cf96e2019-11-01 08:55:11 +08003004 case NmiSource::pciBusError:
3005 bmcSourceSignal = nmi::NMISource::BMCSourceSignal::PciBusError;
Chen Yugang39736d52019-07-12 16:24:33 +08003006 break;
Chen Yugang97cf96e2019-11-01 08:55:11 +08003007 case NmiSource::pch:
3008 bmcSourceSignal = nmi::NMISource::BMCSourceSignal::PCH;
Chen Yugang39736d52019-07-12 16:24:33 +08003009 break;
Chen Yugang97cf96e2019-11-01 08:55:11 +08003010 case NmiSource::chipset:
3011 bmcSourceSignal = nmi::NMISource::BMCSourceSignal::Chipset;
Chen Yugang39736d52019-07-12 16:24:33 +08003012 break;
3013 default:
3014 phosphor::logging::log<phosphor::logging::level::ERR>(
3015 "NMI source: invalid property!");
3016 return ipmi::responseResponseError();
3017 }
3018
3019 try
3020 {
3021 // keep NMI signal source
3022 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
3023 std::string service =
3024 getService(*dbus, oemNmiSourceIntf, oemNmiSourceObjPath);
Chen Yugang97cf96e2019-11-01 08:55:11 +08003025 setDbusProperty(*dbus, service, oemNmiSourceObjPath, oemNmiSourceIntf,
3026 oemNmiBmcSourceObjPathProp,
3027 nmi::convertForMessage(bmcSourceSignal));
Chen Yugang99be6332019-08-09 16:20:48 +08003028 // set Enabled property to inform NMI source handling
3029 // to trigger a NMI_OUT BSOD.
3030 // if it's triggered by NMI source property changed,
3031 // NMI_OUT BSOD could be missed if the same source occurs twice in a row
3032 if (bmcSourceSignal != nmi::NMISource::BMCSourceSignal::None)
3033 {
3034 setDbusProperty(*dbus, service, oemNmiSourceObjPath,
3035 oemNmiSourceIntf, oemNmiEnabledObjPathProp,
3036 static_cast<bool>(true));
3037 }
Chen Yugang39736d52019-07-12 16:24:33 +08003038 }
3039 catch (sdbusplus::exception_t& e)
3040 {
3041 phosphor::logging::log<phosphor::logging::level::ERR>(e.what());
3042 return ipmi::responseResponseError();
3043 }
3044
3045 return ipmi::responseSuccess();
3046}
3047
James Feist63efafa2019-07-24 12:39:21 -07003048namespace dimmOffset
3049{
3050constexpr const char* dimmPower = "DimmPower";
3051constexpr const char* staticCltt = "StaticCltt";
3052constexpr const char* offsetPath = "/xyz/openbmc_project/Inventory/Item/Dimm";
3053constexpr const char* offsetInterface =
3054 "xyz.openbmc_project.Inventory.Item.Dimm.Offset";
3055constexpr const char* property = "DimmOffset";
3056
3057}; // namespace dimmOffset
3058
3059ipmi::RspType<>
3060 ipmiOEMSetDimmOffset(uint8_t type,
3061 const std::vector<std::tuple<uint8_t, uint8_t>>& data)
3062{
3063 if (type != static_cast<uint8_t>(dimmOffsetTypes::dimmPower) &&
3064 type != static_cast<uint8_t>(dimmOffsetTypes::staticCltt))
3065 {
3066 return ipmi::responseInvalidFieldRequest();
3067 }
3068
3069 if (data.empty())
3070 {
3071 return ipmi::responseInvalidFieldRequest();
3072 }
3073 nlohmann::json json;
3074
3075 std::ifstream jsonStream(dimmOffsetFile);
3076 if (jsonStream.good())
3077 {
3078 json = nlohmann::json::parse(jsonStream, nullptr, false);
3079 if (json.is_discarded())
3080 {
3081 json = nlohmann::json();
3082 }
3083 jsonStream.close();
3084 }
3085
3086 std::string typeName;
3087 if (type == static_cast<uint8_t>(dimmOffsetTypes::dimmPower))
3088 {
3089 typeName = dimmOffset::dimmPower;
3090 }
3091 else
3092 {
3093 typeName = dimmOffset::staticCltt;
3094 }
3095
3096 nlohmann::json& field = json[typeName];
3097
3098 for (const auto& [index, value] : data)
3099 {
3100 field[index] = value;
3101 }
3102
3103 for (nlohmann::json& val : field)
3104 {
3105 if (val == nullptr)
3106 {
3107 val = static_cast<uint8_t>(0);
3108 }
3109 }
3110
3111 std::ofstream output(dimmOffsetFile);
3112 if (!output.good())
3113 {
3114 std::cerr << "Error writing json file\n";
3115 return ipmi::responseResponseError();
3116 }
3117
3118 output << json.dump(4);
3119
3120 if (type == static_cast<uint8_t>(dimmOffsetTypes::staticCltt))
3121 {
3122 std::shared_ptr<sdbusplus::asio::connection> bus = getSdBus();
3123
3124 std::variant<std::vector<uint8_t>> offsets =
3125 field.get<std::vector<uint8_t>>();
3126 auto call = bus->new_method_call(
3127 settingsBusName, dimmOffset::offsetPath, PROP_INTF, "Set");
3128 call.append(dimmOffset::offsetInterface, dimmOffset::property, offsets);
3129 try
3130 {
3131 bus->call(call);
3132 }
3133 catch (sdbusplus::exception_t& e)
3134 {
3135 phosphor::logging::log<phosphor::logging::level::ERR>(
3136 "ipmiOEMSetDimmOffset: can't set dimm offsets!",
3137 phosphor::logging::entry("ERR=%s", e.what()));
3138 return ipmi::responseResponseError();
3139 }
3140 }
3141
3142 return ipmi::responseSuccess();
3143}
3144
3145ipmi::RspType<uint8_t> ipmiOEMGetDimmOffset(uint8_t type, uint8_t index)
3146{
3147
3148 if (type != static_cast<uint8_t>(dimmOffsetTypes::dimmPower) &&
3149 type != static_cast<uint8_t>(dimmOffsetTypes::staticCltt))
3150 {
3151 return ipmi::responseInvalidFieldRequest();
3152 }
3153
3154 std::ifstream jsonStream(dimmOffsetFile);
3155
3156 auto json = nlohmann::json::parse(jsonStream, nullptr, false);
3157 if (json.is_discarded())
3158 {
3159 std::cerr << "File error in " << dimmOffsetFile << "\n";
3160 return ipmi::responseResponseError();
3161 }
3162
3163 std::string typeName;
3164 if (type == static_cast<uint8_t>(dimmOffsetTypes::dimmPower))
3165 {
3166 typeName = dimmOffset::dimmPower;
3167 }
3168 else
3169 {
3170 typeName = dimmOffset::staticCltt;
3171 }
3172
3173 auto it = json.find(typeName);
3174 if (it == json.end())
3175 {
3176 return ipmi::responseInvalidFieldRequest();
3177 }
3178
3179 if (it->size() <= index)
3180 {
3181 return ipmi::responseInvalidFieldRequest();
3182 }
3183
3184 uint8_t resp = it->at(index).get<uint8_t>();
3185 return ipmi::responseSuccess(resp);
3186}
3187
Chen,Yugang4f7e76b2019-08-20 09:28:06 +08003188namespace boot_options
3189{
3190
3191using namespace sdbusplus::xyz::openbmc_project::Control::Boot::server;
3192using IpmiValue = uint8_t;
3193constexpr auto ipmiDefault = 0;
3194
3195std::map<IpmiValue, Source::Sources> sourceIpmiToDbus = {
3196 {0x01, Source::Sources::Network},
3197 {0x02, Source::Sources::Disk},
3198 {0x05, Source::Sources::ExternalMedia},
3199 {0x0f, Source::Sources::RemovableMedia},
3200 {ipmiDefault, Source::Sources::Default}};
3201
3202std::map<IpmiValue, Mode::Modes> modeIpmiToDbus = {
Chen Yugangca12a7b2019-09-03 18:11:44 +08003203 {0x06, Mode::Modes::Setup}, {ipmiDefault, Mode::Modes::Regular}};
Chen,Yugang4f7e76b2019-08-20 09:28:06 +08003204
3205std::map<Source::Sources, IpmiValue> sourceDbusToIpmi = {
3206 {Source::Sources::Network, 0x01},
3207 {Source::Sources::Disk, 0x02},
3208 {Source::Sources::ExternalMedia, 0x05},
3209 {Source::Sources::RemovableMedia, 0x0f},
3210 {Source::Sources::Default, ipmiDefault}};
3211
3212std::map<Mode::Modes, IpmiValue> modeDbusToIpmi = {
Chen Yugangca12a7b2019-09-03 18:11:44 +08003213 {Mode::Modes::Setup, 0x06}, {Mode::Modes::Regular, ipmiDefault}};
Chen,Yugang4f7e76b2019-08-20 09:28:06 +08003214
3215static constexpr auto bootModeIntf = "xyz.openbmc_project.Control.Boot.Mode";
3216static constexpr auto bootSourceIntf =
3217 "xyz.openbmc_project.Control.Boot.Source";
3218static constexpr auto enabledIntf = "xyz.openbmc_project.Object.Enable";
3219static constexpr auto persistentObjPath =
3220 "/xyz/openbmc_project/control/host0/boot";
3221static constexpr auto oneTimePath =
3222 "/xyz/openbmc_project/control/host0/boot/one_time";
3223static constexpr auto bootSourceProp = "BootSource";
3224static constexpr auto bootModeProp = "BootMode";
3225static constexpr auto oneTimeBootEnableProp = "Enabled";
3226static constexpr auto httpBootMode =
3227 "xyz.openbmc_project.Control.Boot.Source.Sources.Http";
3228
3229enum class BootOptionParameter : size_t
3230{
3231 setInProgress = 0x0,
3232 bootFlags = 0x5,
3233};
3234static constexpr uint8_t setComplete = 0x0;
3235static constexpr uint8_t setInProgress = 0x1;
3236static uint8_t transferStatus = setComplete;
3237static constexpr uint8_t setParmVersion = 0x01;
3238static constexpr uint8_t setParmBootFlagsPermanent = 0x40;
3239static constexpr uint8_t setParmBootFlagsValidOneTime = 0x80;
3240static constexpr uint8_t setParmBootFlagsValidPermanent = 0xC0;
3241static constexpr uint8_t httpBoot = 0xd;
3242static constexpr uint8_t bootSourceMask = 0x3c;
3243
3244} // namespace boot_options
3245
3246ipmi::RspType<uint8_t, // version
3247 uint8_t, // param
3248 uint8_t, // data0, dependent on parameter
3249 std::optional<uint8_t> // data1, dependent on parameter
3250 >
3251 ipmiOemGetEfiBootOptions(uint8_t parameter, uint8_t set, uint8_t block)
3252{
3253 using namespace boot_options;
3254 uint8_t bootOption = 0;
3255
3256 if (parameter == static_cast<uint8_t>(BootOptionParameter::setInProgress))
3257 {
3258 return ipmi::responseSuccess(setParmVersion, parameter, transferStatus,
3259 std::nullopt);
3260 }
3261
3262 if (parameter != static_cast<uint8_t>(BootOptionParameter::bootFlags))
3263 {
3264 phosphor::logging::log<phosphor::logging::level::ERR>(
3265 "Unsupported parameter");
3266 return ipmi::responseResponseError();
3267 }
3268
3269 try
3270 {
3271 auto oneTimeEnabled = false;
3272 // read one time Enabled property
3273 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
3274 std::string service = getService(*dbus, enabledIntf, oneTimePath);
3275 Value variant = getDbusProperty(*dbus, service, oneTimePath,
3276 enabledIntf, oneTimeBootEnableProp);
3277 oneTimeEnabled = std::get<bool>(variant);
3278
3279 // get BootSource and BootMode properties
3280 // according to oneTimeEnable
3281 auto bootObjPath = oneTimePath;
3282 if (oneTimeEnabled == false)
3283 {
3284 bootObjPath = persistentObjPath;
3285 }
3286
3287 service = getService(*dbus, bootModeIntf, bootObjPath);
3288 variant = getDbusProperty(*dbus, service, bootObjPath, bootModeIntf,
3289 bootModeProp);
3290
3291 auto bootMode =
3292 Mode::convertModesFromString(std::get<std::string>(variant));
3293
3294 service = getService(*dbus, bootSourceIntf, bootObjPath);
3295 variant = getDbusProperty(*dbus, service, bootObjPath, bootSourceIntf,
3296 bootSourceProp);
3297
3298 if (std::get<std::string>(variant) == httpBootMode)
3299 {
3300 bootOption = httpBoot;
3301 }
3302 else
3303 {
3304 auto bootSource = Source::convertSourcesFromString(
3305 std::get<std::string>(variant));
3306 bootOption = sourceDbusToIpmi.at(bootSource);
3307 if (Source::Sources::Default == bootSource)
3308 {
3309 bootOption = modeDbusToIpmi.at(bootMode);
3310 }
3311 }
3312
3313 uint8_t oneTime = oneTimeEnabled ? setParmBootFlagsValidOneTime
3314 : setParmBootFlagsValidPermanent;
3315 bootOption <<= 2; // shift for responseconstexpr
3316 return ipmi::responseSuccess(setParmVersion, parameter, oneTime,
3317 bootOption);
3318 }
3319 catch (sdbusplus::exception_t& e)
3320 {
3321 phosphor::logging::log<phosphor::logging::level::ERR>(e.what());
3322 return ipmi::responseResponseError();
3323 }
3324}
3325
3326ipmi::RspType<> ipmiOemSetEfiBootOptions(uint8_t bootFlag, uint8_t bootParam,
3327 std::optional<uint8_t> bootOption)
3328{
3329 using namespace boot_options;
3330 auto oneTimeEnabled = false;
3331
3332 if (bootFlag == static_cast<uint8_t>(BootOptionParameter::setInProgress))
3333 {
3334 if (bootOption)
3335 {
3336 return ipmi::responseReqDataLenInvalid();
3337 }
3338
3339 if (transferStatus == setInProgress)
3340 {
3341 phosphor::logging::log<phosphor::logging::level::ERR>(
3342 "boot option set in progress!");
3343 return ipmi::responseResponseError();
3344 }
3345
3346 transferStatus = bootParam;
3347 return ipmi::responseSuccess();
3348 }
3349
3350 if (bootFlag != (uint8_t)BootOptionParameter::bootFlags)
3351 {
3352 phosphor::logging::log<phosphor::logging::level::ERR>(
3353 "Unsupported parameter");
3354 return ipmi::responseResponseError();
3355 }
3356
3357 if (!bootOption)
3358 {
3359 return ipmi::responseReqDataLenInvalid();
3360 }
3361
3362 if (((bootOption.value() & bootSourceMask) >> 2) !=
3363 httpBoot) // not http boot, exit
3364 {
3365 phosphor::logging::log<phosphor::logging::level::ERR>(
3366 "wrong boot option parameter!");
3367 return ipmi::responseParmOutOfRange();
3368 }
3369
3370 try
3371 {
3372 bool permanent = (bootParam & setParmBootFlagsPermanent) ==
3373 setParmBootFlagsPermanent;
3374
3375 // read one time Enabled property
3376 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
3377 std::string service = getService(*dbus, enabledIntf, oneTimePath);
3378 Value variant = getDbusProperty(*dbus, service, oneTimePath,
3379 enabledIntf, oneTimeBootEnableProp);
3380 oneTimeEnabled = std::get<bool>(variant);
3381
3382 /*
3383 * Check if the current boot setting is onetime or permanent, if the
3384 * request in the command is otherwise, then set the "Enabled"
3385 * property in one_time object path to 'True' to indicate onetime
3386 * and 'False' to indicate permanent.
3387 *
3388 * Once the onetime/permanent setting is applied, then the bootMode
3389 * and bootSource is updated for the corresponding object.
3390 */
3391 if (permanent == oneTimeEnabled)
3392 {
3393 setDbusProperty(*dbus, service, oneTimePath, enabledIntf,
3394 oneTimeBootEnableProp, !permanent);
3395 }
3396
3397 // set BootSource and BootMode properties
3398 // according to oneTimeEnable or persistent
3399 auto bootObjPath = oneTimePath;
3400 if (oneTimeEnabled == false)
3401 {
3402 bootObjPath = persistentObjPath;
3403 }
3404 std::string bootMode =
3405 "xyz.openbmc_project.Control.Boot.Mode.Modes.Regular";
3406 std::string bootSource = httpBootMode;
3407
3408 service = getService(*dbus, bootModeIntf, bootObjPath);
3409 setDbusProperty(*dbus, service, bootObjPath, bootModeIntf, bootModeProp,
3410 bootMode);
3411
3412 service = getService(*dbus, bootSourceIntf, bootObjPath);
3413 setDbusProperty(*dbus, service, bootObjPath, bootSourceIntf,
3414 bootSourceProp, bootSource);
3415 }
3416 catch (sdbusplus::exception_t& e)
3417 {
3418 phosphor::logging::log<phosphor::logging::level::ERR>(e.what());
3419 return ipmi::responseResponseError();
3420 }
3421
3422 return ipmi::responseSuccess();
3423}
3424
Cheng C Yang4e6ee152019-09-25 10:27:44 +08003425using BasicVariantType =
3426 std::variant<std::vector<std::string>, std::vector<uint64_t>, std::string,
3427 int64_t, uint64_t, double, int32_t, uint32_t, int16_t,
3428 uint16_t, uint8_t, bool>;
3429using PropertyMapType =
3430 boost::container::flat_map<std::string, BasicVariantType>;
3431static constexpr const std::array<const char*, 1> psuPresenceTypes = {
3432 "xyz.openbmc_project.Configuration.PSUPresence"};
3433int getPSUAddress(ipmi::Context::ptr ctx, uint8_t& bus,
3434 std::vector<uint64_t>& addrTable)
3435{
3436 boost::system::error_code ec;
3437 GetSubTreeType subtree = ctx->bus->yield_method_call<GetSubTreeType>(
3438 ctx->yield, ec, "xyz.openbmc_project.ObjectMapper",
3439 "/xyz/openbmc_project/object_mapper",
3440 "xyz.openbmc_project.ObjectMapper", "GetSubTree",
3441 "/xyz/openbmc_project/inventory/system", 3, psuPresenceTypes);
3442 if (ec)
3443 {
3444 phosphor::logging::log<phosphor::logging::level::ERR>(
3445 "Failed to set dbus property to cold redundancy");
3446 return -1;
3447 }
3448 for (const auto& object : subtree)
3449 {
3450 std::string pathName = object.first;
3451 for (const auto& serviceIface : object.second)
3452 {
3453 std::string serviceName = serviceIface.first;
3454
3455 ec.clear();
3456 PropertyMapType propMap =
3457 ctx->bus->yield_method_call<PropertyMapType>(
3458 ctx->yield, ec, serviceName, pathName,
3459 "org.freedesktop.DBus.Properties", "GetAll",
3460 "xyz.openbmc_project.Configuration.PSUPresence");
3461 if (ec)
3462 {
3463 phosphor::logging::log<phosphor::logging::level::ERR>(
3464 "Failed to set dbus property to cold redundancy");
3465 return -1;
3466 }
3467 auto psuBus = std::get_if<uint64_t>(&propMap["Bus"]);
3468 auto psuAddress =
3469 std::get_if<std::vector<uint64_t>>(&propMap["Address"]);
3470
3471 if (psuBus == nullptr || psuAddress == nullptr)
3472 {
3473 std::cerr << "error finding necessary "
3474 "entry in configuration\n";
3475 return -1;
3476 }
3477 bus = static_cast<uint8_t>(*psuBus);
3478 addrTable = *psuAddress;
3479 return 0;
3480 }
3481 }
3482 return -1;
3483}
3484
3485static const constexpr uint8_t addrOffset = 8;
3486static const constexpr uint8_t psuRevision = 0xd9;
3487static const constexpr uint8_t defaultPSUBus = 7;
3488// Second Minor, Primary Minor, Major
3489static const constexpr size_t verLen = 3;
3490ipmi::RspType<std::vector<uint8_t>> ipmiOEMGetPSUVersion(ipmi::Context::ptr ctx)
3491{
3492 uint8_t bus = defaultPSUBus;
3493 std::vector<uint64_t> addrTable;
3494 std::vector<uint8_t> result;
3495 if (getPSUAddress(ctx, bus, addrTable))
3496 {
3497 std::cerr << "Failed to get PSU bus and address\n";
3498 return ipmi::responseResponseError();
3499 }
3500
3501 for (const auto& slaveAddr : addrTable)
3502 {
3503 std::vector<uint8_t> writeData = {psuRevision};
3504 std::vector<uint8_t> readBuf(verLen);
3505 uint8_t addr = static_cast<uint8_t>(slaveAddr) + addrOffset;
3506 std::string i2cBus = "/dev/i2c-" + std::to_string(bus);
3507
3508 auto retI2C = ipmi::i2cWriteRead(i2cBus, addr, writeData, readBuf);
3509 if (retI2C != ipmi::ccSuccess)
3510 {
3511 for (size_t idx = 0; idx < verLen; idx++)
3512 {
3513 result.emplace_back(0x00);
3514 }
3515 }
3516 else
3517 {
3518 for (const uint8_t& data : readBuf)
3519 {
3520 result.emplace_back(data);
3521 }
3522 }
3523 }
3524
3525 return ipmi::responseSuccess(result);
3526}
3527
srikanta mondal2030d7c2020-05-03 17:25:25 +00003528std::optional<uint8_t> getMultiNodeInfoPresence(ipmi::Context::ptr ctx,
3529 const std::string& name)
3530{
3531 Value dbusValue = 0;
3532 std::string serviceName;
3533
3534 boost::system::error_code ec =
3535 ipmi::getService(ctx, multiNodeIntf, multiNodeObjPath, serviceName);
3536
3537 if (ec)
3538 {
3539 phosphor::logging::log<phosphor::logging::level::ERR>(
3540 "Failed to perform Multinode getService.");
3541 return std::nullopt;
3542 }
3543
3544 ec = ipmi::getDbusProperty(ctx, serviceName, multiNodeObjPath,
3545 multiNodeIntf, name, dbusValue);
3546 if (ec)
3547 {
3548 phosphor::logging::log<phosphor::logging::level::ERR>(
3549 "Failed to perform Multinode get property");
3550 return std::nullopt;
3551 }
3552
3553 auto multiNodeVal = std::get_if<uint8_t>(&dbusValue);
3554 if (!multiNodeVal)
3555 {
3556 phosphor::logging::log<phosphor::logging::level::ERR>(
3557 "getMultiNodeInfoPresence: error to get multinode");
3558 return std::nullopt;
3559 }
3560 return *multiNodeVal;
3561}
3562
3563/** @brief implements OEM get reading command
3564 * @param domain ID
3565 * @param reading Type
3566 * - 00h = platform Power Consumption
3567 * - 01h = inlet Air Temp
3568 * - 02h = icc_TDC from PECI
3569 * @param reserved, write as 0000h
3570 *
3571 * @returns IPMI completion code plus response data
3572 * - response
3573 * - domain ID
3574 * - reading Type
3575 * - 00h = platform Power Consumption
3576 * - 01h = inlet Air Temp
3577 * - 02h = icc_TDC from PECI
3578 * - reading
3579 */
3580ipmi::RspType<uint4_t, // domain ID
3581 uint4_t, // reading Type
3582 uint16_t // reading Value
3583 >
3584 ipmiOEMGetReading(ipmi::Context::ptr ctx, uint4_t domainId,
3585 uint4_t readingType, uint16_t reserved)
3586{
3587 constexpr uint8_t platformPower = 0;
3588 constexpr uint8_t inletAirTemp = 1;
3589 constexpr uint8_t iccTdc = 2;
3590
3591 if ((static_cast<uint8_t>(readingType) > iccTdc) || domainId || reserved)
3592 {
3593 return ipmi::responseInvalidFieldRequest();
3594 }
3595
3596 // This command should run only from multi-node product.
3597 // For all other platforms this command will return invalid.
3598
3599 std::optional<uint8_t> nodeInfo =
3600 getMultiNodeInfoPresence(ctx, "NodePresence");
3601 if (!nodeInfo || !*nodeInfo)
3602 {
3603 return ipmi::responseInvalidCommand();
3604 }
3605
3606 uint16_t oemReadingValue = 0;
3607 if (static_cast<uint8_t>(readingType) == inletAirTemp)
3608 {
3609 double value = 0;
3610 boost::system::error_code ec = ipmi::getDbusProperty(
3611 ctx, "xyz.openbmc_project.HwmonTempSensor",
3612 "/xyz/openbmc_project/sensors/temperature/Inlet_BRD_Temp",
3613 "xyz.openbmc_project.Sensor.Value", "Value", value);
3614 if (ec)
3615 {
3616 phosphor::logging::log<phosphor::logging::level::ERR>(
3617 "Failed to get BMC Get OEM temperature",
3618 phosphor::logging::entry("EXCEPTION=%s", ec.message().c_str()));
3619 return ipmi::responseUnspecifiedError();
3620 }
3621 // Take the Inlet temperature
3622 oemReadingValue = static_cast<uint16_t>(value);
3623 }
3624 else
3625 {
3626 phosphor::logging::log<phosphor::logging::level::ERR>(
3627 "Currently Get OEM Reading support only for Inlet Air Temp");
3628 return ipmi::responseParmOutOfRange();
3629 }
3630 return ipmi::responseSuccess(domainId, readingType, oemReadingValue);
3631}
3632
AppaRao Puli28972062019-11-11 02:04:45 +05303633/** @brief implements the maximum size of
3634 * bridgeable messages used between KCS and
3635 * IPMB interfacesget security mode command.
3636 *
3637 * @returns IPMI completion code with following data
3638 * - KCS Buffer Size (In multiples of four bytes)
3639 * - IPMB Buffer Size (In multiples of four bytes)
3640 **/
3641ipmi::RspType<uint8_t, uint8_t> ipmiOEMGetBufferSize()
3642{
3643 // for now this is hard coded; really this number is dependent on
3644 // the BMC kcs driver as well as the host kcs driver....
3645 // we can't know the latter.
3646 uint8_t kcsMaxBufferSize = 63 / 4;
3647 uint8_t ipmbMaxBufferSize = 128 / 4;
3648
3649 return ipmi::responseSuccess(kcsMaxBufferSize, ipmbMaxBufferSize);
3650}
3651
Jason M. Bills64796042018-10-03 16:51:55 -07003652static void registerOEMFunctions(void)
Jia, Chunhuia835eaa2018-09-05 09:00:41 +08003653{
3654 phosphor::logging::log<phosphor::logging::level::INFO>(
3655 "Registering OEM commands");
Vernon Mauery98bbf692019-09-16 11:14:59 -07003656 ipmiPrintAndRegister(intel::netFnGeneral,
3657 intel::general::cmdGetChassisIdentifier, NULL,
3658 ipmiOEMGetChassisIdentifier,
3659 PRIVILEGE_USER); // get chassis identifier
3660
3661 ipmiPrintAndRegister(intel::netFnGeneral, intel::general::cmdSetSystemGUID,
3662 NULL, ipmiOEMSetSystemGUID,
3663 PRIVILEGE_ADMIN); // set system guid
Jason M. Billsb02bf092019-08-15 13:01:56 -07003664
3665 // <Disable BMC System Reset Action>
Vernon Mauery98bbf692019-09-16 11:14:59 -07003666 registerHandler(prioOemBase, intel::netFnGeneral,
3667 intel::general::cmdDisableBMCSystemReset, Privilege::Admin,
3668 ipmiOEMDisableBMCSystemReset);
3669
Jason M. Billsb02bf092019-08-15 13:01:56 -07003670 // <Get BMC Reset Disables>
Vernon Mauery98bbf692019-09-16 11:14:59 -07003671 registerHandler(prioOemBase, intel::netFnGeneral,
3672 intel::general::cmdGetBMCResetDisables, Privilege::Admin,
3673 ipmiOEMGetBMCResetDisables);
Jason M. Billsb02bf092019-08-15 13:01:56 -07003674
Vernon Mauery98bbf692019-09-16 11:14:59 -07003675 ipmiPrintAndRegister(intel::netFnGeneral, intel::general::cmdSetBIOSID,
3676 NULL, ipmiOEMSetBIOSID, PRIVILEGE_ADMIN);
Jia, Chunhuicc49b542019-03-20 15:41:07 +08003677
Chen Yugang7a04f3a2019-10-08 11:12:35 +08003678 registerHandler(prioOemBase, intel::netFnGeneral,
3679 intel::general::cmdGetOEMDeviceInfo, Privilege::User,
3680 ipmiOEMGetDeviceInfo);
Jia, Chunhuicc49b542019-03-20 15:41:07 +08003681
Vernon Mauery98bbf692019-09-16 11:14:59 -07003682 ipmiPrintAndRegister(intel::netFnGeneral,
3683 intel::general::cmdGetAICSlotFRUIDSlotPosRecords, NULL,
3684 ipmiOEMGetAICFRU, PRIVILEGE_USER);
Suryakanth Sekard509eb92018-11-15 17:44:11 +05303685
Vernon Mauery98bbf692019-09-16 11:14:59 -07003686 registerHandler(prioOpenBmcBase, intel::netFnGeneral,
3687 intel::general::cmdSendEmbeddedFWUpdStatus,
3688 Privilege::Operator, ipmiOEMSendEmbeddedFwUpdStatus);
Suryakanth Sekard509eb92018-11-15 17:44:11 +05303689
Rajashekar Gade Reddy2b664d52020-03-23 22:09:00 +05303690 registerHandler(prioOpenBmcBase, intel::netFnApp, intel::app::cmdSlotIpmb,
3691 Privilege::Admin, ipmiOEMSlotIpmb);
3692
Vernon Mauery98bbf692019-09-16 11:14:59 -07003693 ipmiPrintAndRegister(intel::netFnGeneral,
3694 intel::general::cmdSetPowerRestoreDelay, NULL,
3695 ipmiOEMSetPowerRestoreDelay, PRIVILEGE_OPERATOR);
3696
3697 ipmiPrintAndRegister(intel::netFnGeneral,
3698 intel::general::cmdGetPowerRestoreDelay, NULL,
3699 ipmiOEMGetPowerRestoreDelay, PRIVILEGE_USER);
3700
3701 registerHandler(prioOpenBmcBase, intel::netFnGeneral,
3702 intel::general::cmdSetOEMUser2Activation,
3703 Privilege::Callback, ipmiOEMSetUser2Activation);
3704
3705 registerHandler(prioOpenBmcBase, intel::netFnGeneral,
3706 intel::general::cmdSetSpecialUserPassword,
3707 Privilege::Callback, ipmiOEMSetSpecialUserPassword);
Richard Marian Thomaiyarfc5e9852019-04-14 15:06:27 +05303708
Jason M. Bills42bd9c82019-06-28 16:39:34 -07003709 // <Get Processor Error Config>
Vernon Mauery98bbf692019-09-16 11:14:59 -07003710 registerHandler(prioOemBase, intel::netFnGeneral,
3711 intel::general::cmdGetProcessorErrConfig, Privilege::User,
3712 ipmiOEMGetProcessorErrConfig);
3713
Jason M. Bills42bd9c82019-06-28 16:39:34 -07003714 // <Set Processor Error Config>
Vernon Mauery98bbf692019-09-16 11:14:59 -07003715 registerHandler(prioOemBase, intel::netFnGeneral,
3716 intel::general::cmdSetProcessorErrConfig, Privilege::Admin,
3717 ipmiOEMSetProcessorErrConfig);
Jason M. Bills42bd9c82019-06-28 16:39:34 -07003718
Vernon Mauery98bbf692019-09-16 11:14:59 -07003719 ipmiPrintAndRegister(intel::netFnGeneral,
3720 intel::general::cmdSetShutdownPolicy, NULL,
3721 ipmiOEMSetShutdownPolicy, PRIVILEGE_ADMIN);
James Feist91244a62019-02-19 15:04:54 -08003722
Vernon Mauery98bbf692019-09-16 11:14:59 -07003723 ipmiPrintAndRegister(intel::netFnGeneral,
3724 intel::general::cmdGetShutdownPolicy, NULL,
3725 ipmiOEMGetShutdownPolicy, PRIVILEGE_ADMIN);
James Feist91244a62019-02-19 15:04:54 -08003726
anil kumar appanaf945eee2019-09-25 23:29:11 +00003727 registerHandler(prioOemBase, intel::netFnGeneral,
3728 intel::general::cmdSetFanConfig, Privilege::User,
3729 ipmiOEMSetFanConfig);
James Feist91244a62019-02-19 15:04:54 -08003730
Vernon Mauery98bbf692019-09-16 11:14:59 -07003731 registerHandler(prioOemBase, intel::netFnGeneral,
3732 intel::general::cmdGetFanConfig, Privilege::User,
3733 ipmiOEMGetFanConfig);
James Feist5f957ca2019-03-14 15:33:55 -07003734
Vernon Mauery98bbf692019-09-16 11:14:59 -07003735 registerHandler(prioOemBase, intel::netFnGeneral,
3736 intel::general::cmdGetFanSpeedOffset, Privilege::User,
3737 ipmiOEMGetFanSpeedOffset);
James Feistacc8a4e2019-04-02 14:23:57 -07003738
Vernon Mauery98bbf692019-09-16 11:14:59 -07003739 registerHandler(prioOemBase, intel::netFnGeneral,
3740 intel::general::cmdSetFanSpeedOffset, Privilege::User,
3741 ipmiOEMSetFanSpeedOffset);
James Feistacc8a4e2019-04-02 14:23:57 -07003742
Vernon Mauery98bbf692019-09-16 11:14:59 -07003743 registerHandler(prioOemBase, intel::netFnGeneral,
3744 intel::general::cmdSetFscParameter, Privilege::User,
3745 ipmiOEMSetFscParameter);
James Feist5f957ca2019-03-14 15:33:55 -07003746
Vernon Mauery98bbf692019-09-16 11:14:59 -07003747 registerHandler(prioOemBase, intel::netFnGeneral,
3748 intel::general::cmdGetFscParameter, Privilege::User,
3749 ipmiOEMGetFscParameter);
Richard Marian Thomaiyarea537d52019-04-24 21:33:48 +05303750
Vernon Mauery98bbf692019-09-16 11:14:59 -07003751 registerHandler(prioOpenBmcBase, intel::netFnGeneral,
3752 intel::general::cmdReadBaseBoardProductId, Privilege::Admin,
3753 ipmiOEMReadBoardProductId);
Chen Yugang39736d52019-07-12 16:24:33 +08003754
Vernon Mauery98bbf692019-09-16 11:14:59 -07003755 registerHandler(prioOemBase, intel::netFnGeneral,
3756 intel::general::cmdGetNmiStatus, Privilege::User,
3757 ipmiOEMGetNmiSource);
Chen Yugang39736d52019-07-12 16:24:33 +08003758
Vernon Mauery98bbf692019-09-16 11:14:59 -07003759 registerHandler(prioOemBase, intel::netFnGeneral,
3760 intel::general::cmdSetNmiStatus, Privilege::Operator,
3761 ipmiOEMSetNmiSource);
Chen,Yugang4f7e76b2019-08-20 09:28:06 +08003762
Vernon Mauery98bbf692019-09-16 11:14:59 -07003763 registerHandler(prioOemBase, intel::netFnGeneral,
3764 intel::general::cmdGetEfiBootOptions, Privilege::User,
3765 ipmiOemGetEfiBootOptions);
Chen,Yugang4f7e76b2019-08-20 09:28:06 +08003766
Vernon Mauery98bbf692019-09-16 11:14:59 -07003767 registerHandler(prioOemBase, intel::netFnGeneral,
3768 intel::general::cmdSetEfiBootOptions, Privilege::Operator,
3769 ipmiOemSetEfiBootOptions);
Richard Marian Thomaiyard801e462019-06-20 01:05:40 +05303770
Vernon Mauery98bbf692019-09-16 11:14:59 -07003771 registerHandler(prioOemBase, intel::netFnGeneral,
3772 intel::general::cmdGetSecurityMode, Privilege::User,
3773 ipmiGetSecurityMode);
Richard Marian Thomaiyard801e462019-06-20 01:05:40 +05303774
Vernon Mauery98bbf692019-09-16 11:14:59 -07003775 registerHandler(prioOemBase, intel::netFnGeneral,
3776 intel::general::cmdSetSecurityMode, Privilege::Admin,
3777 ipmiSetSecurityMode);
Vernon Mauery4ac799d2019-05-20 15:50:37 -07003778
NITIN SHARMAabd11ca2019-06-12 12:31:42 +00003779 registerHandler(prioOemBase, intel::netFnGeneral,
3780 intel::general::cmdGetLEDStatus, Privilege::Admin,
3781 ipmiOEMGetLEDStatus);
Cheng C Yang773703a2019-08-15 09:41:11 +08003782
Vernon Mauery98bbf692019-09-16 11:14:59 -07003783 ipmiPrintAndRegister(ipmi::intel::netFnPlatform,
3784 ipmi::intel::platform::cmdCfgHostSerialPortSpeed, NULL,
3785 ipmiOEMCfgHostSerialPortSpeed, PRIVILEGE_ADMIN);
3786
3787 registerHandler(prioOemBase, intel::netFnGeneral,
3788 intel::general::cmdSetFaultIndication, Privilege::Operator,
3789 ipmiOEMSetFaultIndication);
3790
3791 registerHandler(prioOemBase, intel::netFnGeneral,
3792 intel::general::cmdSetColdRedundancyConfig, Privilege::User,
3793 ipmiOEMSetCRConfig);
3794
3795 registerHandler(prioOemBase, intel::netFnGeneral,
3796 intel::general::cmdGetColdRedundancyConfig, Privilege::User,
3797 ipmiOEMGetCRConfig);
3798
3799 registerHandler(prioOemBase, intel::netFnGeneral,
3800 intel::general::cmdRestoreConfiguration, Privilege::Admin,
Vernon Mauery4ac799d2019-05-20 15:50:37 -07003801 ipmiRestoreConfiguration);
James Feist63efafa2019-07-24 12:39:21 -07003802
Vernon Mauery98bbf692019-09-16 11:14:59 -07003803 registerHandler(prioOemBase, intel::netFnGeneral,
3804 intel::general::cmdSetDimmOffset, Privilege::Operator,
3805 ipmiOEMSetDimmOffset);
James Feist63efafa2019-07-24 12:39:21 -07003806
Vernon Mauery98bbf692019-09-16 11:14:59 -07003807 registerHandler(prioOemBase, intel::netFnGeneral,
3808 intel::general::cmdGetDimmOffset, Privilege::Operator,
3809 ipmiOEMGetDimmOffset);
Chen Yugangca12a7b2019-09-03 18:11:44 +08003810
Cheng C Yang4e6ee152019-09-25 10:27:44 +08003811 registerHandler(prioOemBase, intel::netFnGeneral,
3812 intel::general::cmdGetPSUVersion, Privilege::User,
3813 ipmiOEMGetPSUVersion);
AppaRao Puli28972062019-11-11 02:04:45 +05303814
3815 registerHandler(prioOemBase, intel::netFnGeneral,
3816 intel::general::cmdGetBufferSize, Privilege::User,
3817 ipmiOEMGetBufferSize);
srikanta mondal2030d7c2020-05-03 17:25:25 +00003818
3819 registerHandler(prioOemBase, intel::netFnGeneral,
3820 intel::general::cmdOEMGetReading, Privilege::User,
3821 ipmiOEMGetReading);
Jia, Chunhuia835eaa2018-09-05 09:00:41 +08003822}
3823
3824} // namespace ipmi