blob: 55f95689c60d86cd00ec87edb5713ae0af9cc2b3 [file] [log] [blame]
Jia, Chunhuia835eaa2018-09-05 09:00:41 +08001/*
2// Copyright (c) 2018 Intel Corporation
3//
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at
7//
8// http://www.apache.org/licenses/LICENSE-2.0
9//
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15*/
16
Jason M. Bills64796042018-10-03 16:51:55 -070017#include "xyz/openbmc_project/Common/error.hpp"
Kuiying Wang45f04982018-12-26 09:23:08 +080018#include "xyz/openbmc_project/Led/Physical/server.hpp"
Jason M. Bills64796042018-10-03 16:51:55 -070019
Jia, Chunhuicc49b542019-03-20 15:41:07 +080020#include <systemd/sd-journal.h>
Jia, Chunhuia835eaa2018-09-05 09:00:41 +080021
Chen Yugang7a04f3a2019-10-08 11:12:35 +080022#include <appcommands.hpp>
Jia, Chunhuia835eaa2018-09-05 09:00:41 +080023#include <array>
James Feist91244a62019-02-19 15:04:54 -080024#include <boost/container/flat_map.hpp>
Yong Li23737fe2019-02-19 08:49:55 +080025#include <boost/process/child.hpp>
26#include <boost/process/io.hpp>
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>
Vernon Mauery4ac799d2019-05-20 15:50:37 -070029#include <filesystem>
Zhikui Rence4e73f2019-12-06 13:59:47 -080030#include <gpiod.hpp>
Jia, Chunhuia835eaa2018-09-05 09:00:41 +080031#include <iostream>
Jia, Chunhuicc49b542019-03-20 15:41:07 +080032#include <ipmid/api.hpp>
Vernon Mauery5480ef62019-03-20 13:43:11 -070033#include <ipmid/utils.hpp>
James Feist63efafa2019-07-24 12:39:21 -070034#include <nlohmann/json.hpp>
Jia, Chunhuia835eaa2018-09-05 09:00:41 +080035#include <oemcommands.hpp>
Jia, Chunhuia835eaa2018-09-05 09:00:41 +080036#include <phosphor-logging/log.hpp>
Chen Yugang7a04f3a2019-10-08 11:12:35 +080037#include <regex>
Jia, Chunhuia835eaa2018-09-05 09:00:41 +080038#include <sdbusplus/bus.hpp>
Suryakanth Sekard509eb92018-11-15 17:44:11 +053039#include <sdbusplus/message/types.hpp>
Jia, Chunhuia835eaa2018-09-05 09:00:41 +080040#include <string>
James Feist91244a62019-02-19 15:04:54 -080041#include <variant>
Jia, Chunhuia835eaa2018-09-05 09:00:41 +080042#include <vector>
Chen Yugang97cf96e2019-11-01 08:55:11 +080043#include <xyz/openbmc_project/Chassis/Control/NMISource/server.hpp>
Chen,Yugang4f7e76b2019-08-20 09:28:06 +080044#include <xyz/openbmc_project/Control/Boot/Mode/server.hpp>
45#include <xyz/openbmc_project/Control/Boot/Source/server.hpp>
Cheng C Yang773703a2019-08-15 09:41:11 +080046#include <xyz/openbmc_project/Control/PowerSupplyRedundancy/server.hpp>
Richard Marian Thomaiyard801e462019-06-20 01:05:40 +053047#include <xyz/openbmc_project/Control/Security/RestrictionMode/server.hpp>
Richard Marian Thomaiyar8d4f8d72019-11-11 12:06:40 +053048#include <xyz/openbmc_project/Control/Security/SpecialMode/server.hpp>
Jia, Chunhuia835eaa2018-09-05 09:00:41 +080049
50namespace ipmi
51{
Jason M. Bills64796042018-10-03 16:51:55 -070052static void registerOEMFunctions() __attribute__((constructor));
Vernon Mauery4ac799d2019-05-20 15:50:37 -070053
Jason M. Bills64796042018-10-03 16:51:55 -070054static constexpr size_t maxFRUStringLength = 0x3F;
Jia, Chunhuia835eaa2018-09-05 09:00:41 +080055
Suryakanth Sekard509eb92018-11-15 17:44:11 +053056static constexpr auto ethernetIntf =
57 "xyz.openbmc_project.Network.EthernetInterface";
58static constexpr auto networkIPIntf = "xyz.openbmc_project.Network.IP";
59static constexpr auto networkService = "xyz.openbmc_project.Network";
60static constexpr auto networkRoot = "/xyz/openbmc_project/network";
61
Chen Yugang97cf96e2019-11-01 08:55:11 +080062static constexpr const char* oemNmiSourceIntf =
63 "xyz.openbmc_project.Chassis.Control.NMISource";
Chen Yugang39736d52019-07-12 16:24:33 +080064static constexpr const char* oemNmiSourceObjPath =
Chen Yugang97cf96e2019-11-01 08:55:11 +080065 "/xyz/openbmc_project/Chassis/Control/NMISource";
Chen Yugang39736d52019-07-12 16:24:33 +080066static constexpr const char* oemNmiBmcSourceObjPathProp = "BMCSource";
67static constexpr const char* oemNmiEnabledObjPathProp = "Enabled";
68
James Feist63efafa2019-07-24 12:39:21 -070069static constexpr const char* dimmOffsetFile = "/var/lib/ipmi/ipmi_dimms.json";
70
Chen Yugang39736d52019-07-12 16:24:33 +080071enum class NmiSource : uint8_t
72{
73 none = 0,
Chen Yugang97cf96e2019-11-01 08:55:11 +080074 frontPanelButton = 1,
75 watchdog = 2,
76 chassisCmd = 3,
77 memoryError = 4,
78 pciBusError = 5,
79 pch = 6,
80 chipset = 7,
Chen Yugang39736d52019-07-12 16:24:33 +080081};
82
Suryakanth Sekar822b0b42019-11-15 18:32:53 +053083enum class SpecialUserIndex : uint8_t
84{
85 rootUser = 0,
86 atScaleDebugUser = 1
87};
88
Richard Marian Thomaiyard801e462019-06-20 01:05:40 +053089static constexpr const char* restricionModeService =
90 "xyz.openbmc_project.RestrictionMode.Manager";
91static constexpr const char* restricionModeBasePath =
92 "/xyz/openbmc_project/control/security/restriction_mode";
93static constexpr const char* restricionModeIntf =
94 "xyz.openbmc_project.Control.Security.RestrictionMode";
95static constexpr const char* restricionModeProperty = "RestrictionMode";
96
97static constexpr const char* specialModeService =
98 "xyz.openbmc_project.SpecialMode";
99static constexpr const char* specialModeBasePath =
Richard Marian Thomaiyara7b74282019-09-22 21:53:14 +0530100 "/xyz/openbmc_project/security/special_mode";
Richard Marian Thomaiyard801e462019-06-20 01:05:40 +0530101static constexpr const char* specialModeIntf =
102 "xyz.openbmc_project.Security.SpecialMode";
103static constexpr const char* specialModeProperty = "SpecialMode";
104
105static constexpr const char* dBusPropertyIntf =
106 "org.freedesktop.DBus.Properties";
107static constexpr const char* dBusPropertyGetMethod = "Get";
108static constexpr const char* dBusPropertySetMethod = "Set";
109
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800110// return code: 0 successful
111int8_t getChassisSerialNumber(sdbusplus::bus::bus& bus, std::string& serial)
112{
113 std::string objpath = "/xyz/openbmc_project/FruDevice";
114 std::string intf = "xyz.openbmc_project.FruDeviceManager";
115 std::string service = getService(bus, intf, objpath);
116 ObjectValueTree valueTree = getManagedObjects(bus, service, "/");
117 if (valueTree.empty())
118 {
119 phosphor::logging::log<phosphor::logging::level::ERR>(
120 "No object implements interface",
121 phosphor::logging::entry("INTF=%s", intf.c_str()));
122 return -1;
123 }
124
Jason M. Bills64796042018-10-03 16:51:55 -0700125 for (const auto& item : valueTree)
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800126 {
127 auto interface = item.second.find("xyz.openbmc_project.FruDevice");
128 if (interface == item.second.end())
129 {
130 continue;
131 }
132
133 auto property = interface->second.find("CHASSIS_SERIAL_NUMBER");
134 if (property == interface->second.end())
135 {
136 continue;
137 }
138
139 try
140 {
141 Value variant = property->second;
Vernon Mauery8166c8d2019-05-23 11:22:30 -0700142 std::string& result = std::get<std::string>(variant);
Jason M. Bills64796042018-10-03 16:51:55 -0700143 if (result.size() > maxFRUStringLength)
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800144 {
145 phosphor::logging::log<phosphor::logging::level::ERR>(
146 "FRU serial number exceed maximum length");
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800147 return -1;
148 }
Jason M. Bills64796042018-10-03 16:51:55 -0700149 serial = result;
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800150 return 0;
151 }
Vernon Mauery8166c8d2019-05-23 11:22:30 -0700152 catch (std::bad_variant_access& e)
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800153 {
Jason M. Bills64796042018-10-03 16:51:55 -0700154 phosphor::logging::log<phosphor::logging::level::ERR>(e.what());
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800155 return -1;
156 }
157 }
158 return -1;
159}
Jason M. Bills64796042018-10-03 16:51:55 -0700160
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800161// Returns the Chassis Identifier (serial #)
162ipmi_ret_t ipmiOEMGetChassisIdentifier(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
163 ipmi_request_t request,
164 ipmi_response_t response,
Jason M. Bills64796042018-10-03 16:51:55 -0700165 ipmi_data_len_t dataLen,
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800166 ipmi_context_t context)
167{
168 std::string serial;
Jason M. Bills64796042018-10-03 16:51:55 -0700169 if (*dataLen != 0) // invalid request if there are extra parameters
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800170 {
Jason M. Bills64796042018-10-03 16:51:55 -0700171 *dataLen = 0;
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800172 return IPMI_CC_REQ_DATA_LEN_INVALID;
173 }
Vernon Mauery15419dd2019-05-24 09:40:30 -0700174 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
175 if (getChassisSerialNumber(*dbus, serial) == 0)
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800176 {
Jason M. Bills64796042018-10-03 16:51:55 -0700177 *dataLen = serial.size(); // length will never exceed response length
178 // as it is checked in getChassisSerialNumber
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800179 char* resp = static_cast<char*>(response);
Jason M. Bills64796042018-10-03 16:51:55 -0700180 serial.copy(resp, *dataLen);
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800181 return IPMI_CC_OK;
182 }
Jason M. Bills64796042018-10-03 16:51:55 -0700183 *dataLen = 0;
184 return IPMI_CC_RESPONSE_ERROR;
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800185}
186
187ipmi_ret_t ipmiOEMSetSystemGUID(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
188 ipmi_request_t request,
189 ipmi_response_t response,
Jason M. Bills64796042018-10-03 16:51:55 -0700190 ipmi_data_len_t dataLen, ipmi_context_t context)
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800191{
192 static constexpr size_t safeBufferLength = 50;
193 char buf[safeBufferLength] = {0};
194 GUIDData* Data = reinterpret_cast<GUIDData*>(request);
195
Jason M. Bills64796042018-10-03 16:51:55 -0700196 if (*dataLen != sizeof(GUIDData)) // 16bytes
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800197 {
Jason M. Bills64796042018-10-03 16:51:55 -0700198 *dataLen = 0;
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800199 return IPMI_CC_REQ_DATA_LEN_INVALID;
200 }
201
Jason M. Bills64796042018-10-03 16:51:55 -0700202 *dataLen = 0;
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800203
204 snprintf(
205 buf, safeBufferLength,
206 "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x",
207 Data->timeLow4, Data->timeLow3, Data->timeLow2, Data->timeLow1,
208 Data->timeMid2, Data->timeMid1, Data->timeHigh2, Data->timeHigh1,
209 Data->clock2, Data->clock1, Data->node6, Data->node5, Data->node4,
210 Data->node3, Data->node2, Data->node1);
211 // UUID is in RFC4122 format. Ex: 61a39523-78f2-11e5-9862-e6402cfc3223
212 std::string guid = buf;
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800213
214 std::string objpath = "/xyz/openbmc_project/control/host0/systemGUID";
215 std::string intf = "xyz.openbmc_project.Common.UUID";
Vernon Mauery15419dd2019-05-24 09:40:30 -0700216 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
217 std::string service = getService(*dbus, intf, objpath);
218 setDbusProperty(*dbus, service, objpath, intf, "UUID", guid);
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800219 return IPMI_CC_OK;
220}
221
Jason M. Billsb02bf092019-08-15 13:01:56 -0700222ipmi::RspType<> ipmiOEMDisableBMCSystemReset(bool disableResetOnSMI,
223 uint7_t reserved1)
224{
225 std::shared_ptr<sdbusplus::asio::connection> busp = getSdBus();
226
227 try
228 {
229 auto service =
230 ipmi::getService(*busp, bmcResetDisablesIntf, bmcResetDisablesPath);
231 ipmi::setDbusProperty(*busp, service, bmcResetDisablesPath,
232 bmcResetDisablesIntf, "ResetOnSMI",
233 !disableResetOnSMI);
234 }
235 catch (std::exception& e)
236 {
237 phosphor::logging::log<phosphor::logging::level::ERR>(
238 "Failed to set BMC reset disables",
239 phosphor::logging::entry("EXCEPTION=%s", e.what()));
240 return ipmi::responseUnspecifiedError();
241 }
242
243 return ipmi::responseSuccess();
244}
245
246ipmi::RspType<bool, // disableResetOnSMI
247 uint7_t // reserved
248 >
249 ipmiOEMGetBMCResetDisables()
250{
251 bool disableResetOnSMI = true;
252
253 std::shared_ptr<sdbusplus::asio::connection> busp = getSdBus();
254 try
255 {
256 auto service =
257 ipmi::getService(*busp, bmcResetDisablesIntf, bmcResetDisablesPath);
258 Value variant =
259 ipmi::getDbusProperty(*busp, service, bmcResetDisablesPath,
260 bmcResetDisablesIntf, "ResetOnSMI");
261 disableResetOnSMI = !std::get<bool>(variant);
262 }
263 catch (std::exception& e)
264 {
265 phosphor::logging::log<phosphor::logging::level::ERR>(
266 "Failed to get BMC reset disables",
267 phosphor::logging::entry("EXCEPTION=%s", e.what()));
268 return ipmi::responseUnspecifiedError();
269 }
270
271 return ipmi::responseSuccess(disableResetOnSMI, 0);
272}
273
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800274ipmi_ret_t ipmiOEMSetBIOSID(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
275 ipmi_request_t request, ipmi_response_t response,
276 ipmi_data_len_t dataLen, ipmi_context_t context)
277{
278 DeviceInfo* data = reinterpret_cast<DeviceInfo*>(request);
279
Jason M. Bills64796042018-10-03 16:51:55 -0700280 if ((*dataLen < 2) || (*dataLen != (1 + data->biosIDLength)))
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800281 {
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800282 *dataLen = 0;
283 return IPMI_CC_REQ_DATA_LEN_INVALID;
284 }
Jason M. Bills64796042018-10-03 16:51:55 -0700285 std::string idString((char*)data->biosId, data->biosIDLength);
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800286
Vernon Mauery15419dd2019-05-24 09:40:30 -0700287 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
Yong Li2742b852019-12-16 14:55:11 +0800288 std::string service = getService(*dbus, biosVersionIntf, biosObjPath);
289 setDbusProperty(*dbus, service, biosObjPath, biosVersionIntf,
290 biosVersionProp, idString);
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800291 uint8_t* bytesWritten = static_cast<uint8_t*>(response);
292 *bytesWritten =
Jason M. Bills64796042018-10-03 16:51:55 -0700293 data->biosIDLength; // how many bytes are written into storage
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800294 *dataLen = 1;
295 return IPMI_CC_OK;
296}
297
AppaRao Pulie99e7ed2020-01-17 12:27:10 +0530298bool getSwVerInfo(ipmi::Context::ptr ctx, uint8_t& bmcMajor, uint8_t& bmcMinor,
299 uint8_t& meMajor, uint8_t& meMinor)
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800300{
Chen Yugang7a04f3a2019-10-08 11:12:35 +0800301 // step 1 : get BMC Major and Minor numbers from its DBUS property
AppaRao Pulie99e7ed2020-01-17 12:27:10 +0530302 std::string bmcVersion;
303 if (getActiveSoftwareVersionInfo(ctx, versionPurposeBMC, bmcVersion))
Chen Yugang7a04f3a2019-10-08 11:12:35 +0800304 {
305 return false;
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800306 }
307
AppaRao Pulie99e7ed2020-01-17 12:27:10 +0530308 std::optional<MetaRevision> rev = convertIntelVersion(bmcVersion);
Chen Yugang7a04f3a2019-10-08 11:12:35 +0800309 if (rev.has_value())
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800310 {
Chen Yugang7a04f3a2019-10-08 11:12:35 +0800311 MetaRevision revision = rev.value();
312 bmcMajor = revision.major;
313
314 revision.minor = (revision.minor > 99 ? 99 : revision.minor);
315 bmcMinor = revision.minor % 10 + (revision.minor / 10) * 16;
316 }
317
318 // step 2 : get ME Major and Minor numbers from its DBUS property
319 try
320 {
321 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
322 std::string service =
323 getService(*dbus, "xyz.openbmc_project.Software.Version",
AppaRao Pulid46cb422020-01-21 18:40:21 +0530324 "/xyz/openbmc_project/software/me");
Chen Yugang7a04f3a2019-10-08 11:12:35 +0800325 Value variant =
AppaRao Pulid46cb422020-01-21 18:40:21 +0530326 getDbusProperty(*dbus, service, "/xyz/openbmc_project/software/me",
Chen Yugang7a04f3a2019-10-08 11:12:35 +0800327 "xyz.openbmc_project.Software.Version", "Version");
328
AppaRao Pulid46cb422020-01-21 18:40:21 +0530329 std::string& meVersion = std::get<std::string>(variant);
Chen Yugang7a04f3a2019-10-08 11:12:35 +0800330
331 // get ME major number
332 std::regex pattern1("(\\d+?).(\\d+?).(\\d+?).(\\d+?).(\\d+?)");
333 constexpr size_t matchedPhosphor = 6;
334 std::smatch results;
AppaRao Pulid46cb422020-01-21 18:40:21 +0530335 if (std::regex_match(meVersion, results, pattern1))
Chen Yugang7a04f3a2019-10-08 11:12:35 +0800336 {
337 if (results.size() == matchedPhosphor)
338 {
339 meMajor = static_cast<uint8_t>(std::stoi(results[1]));
340 meMinor = static_cast<uint8_t>(std::stoi(results[2]));
341 }
342 }
343 }
344 catch (sdbusplus::exception::SdBusError& e)
345 {
AppaRao Pulie99e7ed2020-01-17 12:27:10 +0530346 phosphor::logging::log<phosphor::logging::level::ERR>(
347 "Exception caught in ME version conversion",
348 phosphor::logging::entry("MSG=%s", e.what()));
Chen Yugang7a04f3a2019-10-08 11:12:35 +0800349 return false;
350 }
351 return true;
352}
353
354ipmi::RspType<
355 std::variant<std::string,
356 std::tuple<uint8_t, std::array<uint8_t, 2>,
357 std::array<uint8_t, 2>, std::array<uint8_t, 2>,
358 std::array<uint8_t, 2>, std::array<uint8_t, 2>>,
359 std::tuple<uint8_t, std::array<uint8_t, 2>>>>
AppaRao Pulie99e7ed2020-01-17 12:27:10 +0530360 ipmiOEMGetDeviceInfo(ipmi::Context::ptr ctx, uint8_t entityType,
361 std::optional<uint8_t> countToRead,
AppaRao Pulid46cb422020-01-21 18:40:21 +0530362 std::optional<uint8_t> offset)
Chen Yugang7a04f3a2019-10-08 11:12:35 +0800363{
Chen Yugang7a04f3a2019-10-08 11:12:35 +0800364 if (entityType > static_cast<uint8_t>(OEMDevEntityType::sdrVer))
365 {
366 return ipmi::responseInvalidFieldRequest();
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800367 }
368
369 // handle OEM command items
Chen Yugang7a04f3a2019-10-08 11:12:35 +0800370 switch (OEMDevEntityType(entityType))
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800371 {
372 case OEMDevEntityType::biosId:
373 {
AppaRao Pulid46cb422020-01-21 18:40:21 +0530374 // Byte 2&3, Only used with selecting BIOS
375 if (!countToRead || !offset)
376 {
377 return ipmi::responseReqDataLenInvalid();
378 }
379
Vernon Mauery15419dd2019-05-24 09:40:30 -0700380 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
Yong Li2742b852019-12-16 14:55:11 +0800381 std::string service =
382 getService(*dbus, biosVersionIntf, biosObjPath);
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800383 try
384 {
Yong Li2742b852019-12-16 14:55:11 +0800385 Value variant =
386 getDbusProperty(*dbus, service, biosObjPath,
387 biosVersionIntf, biosVersionProp);
Vernon Mauery8166c8d2019-05-23 11:22:30 -0700388 std::string& idString = std::get<std::string>(variant);
AppaRao Pulid46cb422020-01-21 18:40:21 +0530389 if (*offset >= idString.size())
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800390 {
Chen Yugang7a04f3a2019-10-08 11:12:35 +0800391 return ipmi::responseParmOutOfRange();
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800392 }
Jason M. Bills64796042018-10-03 16:51:55 -0700393 size_t length = 0;
AppaRao Pulid46cb422020-01-21 18:40:21 +0530394 if (*countToRead > (idString.size() - *offset))
Jason M. Bills64796042018-10-03 16:51:55 -0700395 {
AppaRao Pulid46cb422020-01-21 18:40:21 +0530396 length = idString.size() - *offset;
Jason M. Bills64796042018-10-03 16:51:55 -0700397 }
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800398 else
399 {
AppaRao Pulid46cb422020-01-21 18:40:21 +0530400 length = *countToRead;
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800401 }
Chen Yugang7a04f3a2019-10-08 11:12:35 +0800402
403 std::string readBuf = {0};
404 readBuf.resize(length);
AppaRao Pulid46cb422020-01-21 18:40:21 +0530405 std::copy_n(idString.begin() + *offset, length,
Chen Yugang7a04f3a2019-10-08 11:12:35 +0800406 (readBuf.begin()));
407 return ipmi::responseSuccess(readBuf);
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800408 }
Vernon Mauery8166c8d2019-05-23 11:22:30 -0700409 catch (std::bad_variant_access& e)
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800410 {
Chen Yugang7a04f3a2019-10-08 11:12:35 +0800411 return ipmi::responseUnspecifiedError();
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800412 }
413 }
414 break;
415
416 case OEMDevEntityType::devVer:
Chen Yugang7a04f3a2019-10-08 11:12:35 +0800417 {
AppaRao Pulid46cb422020-01-21 18:40:21 +0530418 // Byte 2&3, Only used with selecting BIOS
419 if (countToRead || offset)
420 {
421 return ipmi::responseReqDataLenInvalid();
422 }
423
Chen Yugang7a04f3a2019-10-08 11:12:35 +0800424 constexpr const size_t verLen = 2;
425 constexpr const size_t verTotalLen = 10;
426 std::array<uint8_t, verLen> bmcBuf = {0xff, 0xff};
427 std::array<uint8_t, verLen> hsc0Buf = {0xff, 0xff};
428 std::array<uint8_t, verLen> hsc1Buf = {0xff, 0xff};
429 std::array<uint8_t, verLen> meBuf = {0xff, 0xff};
430 std::array<uint8_t, verLen> hsc2Buf = {0xff, 0xff};
431 // data0/1: BMC version number; data6/7: ME version number
432 // the others: HSC0/1/2 version number, not avaible.
AppaRao Pulie99e7ed2020-01-17 12:27:10 +0530433 if (!getSwVerInfo(ctx, bmcBuf[0], bmcBuf[1], meBuf[0], meBuf[1]))
Chen Yugang7a04f3a2019-10-08 11:12:35 +0800434 {
435 return ipmi::responseUnspecifiedError();
436 }
437 return ipmi::responseSuccess(
438 std::tuple<
439 uint8_t, std::array<uint8_t, verLen>,
440 std::array<uint8_t, verLen>, std::array<uint8_t, verLen>,
441 std::array<uint8_t, verLen>, std::array<uint8_t, verLen>>{
442 verTotalLen, bmcBuf, hsc0Buf, hsc1Buf, meBuf, hsc2Buf});
443 }
444 break;
445
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800446 case OEMDevEntityType::sdrVer:
Chen Yugang7a04f3a2019-10-08 11:12:35 +0800447 {
AppaRao Pulid46cb422020-01-21 18:40:21 +0530448 // Byte 2&3, Only used with selecting BIOS
449 if (countToRead || offset)
450 {
451 return ipmi::responseReqDataLenInvalid();
452 }
453
Chen Yugang7a04f3a2019-10-08 11:12:35 +0800454 constexpr const size_t sdrLen = 2;
455 std::array<uint8_t, sdrLen> readBuf = {0x01, 0x0};
456 return ipmi::responseSuccess(
457 std::tuple<uint8_t, std::array<uint8_t, sdrLen>>{sdrLen,
458 readBuf});
459 }
460 break;
461
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800462 default:
Chen Yugang7a04f3a2019-10-08 11:12:35 +0800463 return ipmi::responseInvalidFieldRequest();
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800464 }
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800465}
466
467ipmi_ret_t ipmiOEMGetAICFRU(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
468 ipmi_request_t request, ipmi_response_t response,
469 ipmi_data_len_t dataLen, ipmi_context_t context)
470{
471 if (*dataLen != 0)
472 {
Jason M. Bills64796042018-10-03 16:51:55 -0700473 *dataLen = 0;
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800474 return IPMI_CC_REQ_DATA_LEN_INVALID;
475 }
476
477 *dataLen = 1;
478 uint8_t* res = reinterpret_cast<uint8_t*>(response);
479 // temporary fix. We don't support AIC FRU now. Just tell BIOS that no
480 // AIC is available so that BIOS will not timeout repeatly which leads to
481 // slow booting.
482 *res = 0; // Byte1=Count of SlotPosition/FruID records.
483 return IPMI_CC_OK;
484}
485
Jason M. Bills64796042018-10-03 16:51:55 -0700486ipmi_ret_t ipmiOEMGetPowerRestoreDelay(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
487 ipmi_request_t request,
488 ipmi_response_t response,
489 ipmi_data_len_t dataLen,
490 ipmi_context_t context)
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800491{
Jason M. Bills64796042018-10-03 16:51:55 -0700492 GetPowerRestoreDelayRes* resp =
493 reinterpret_cast<GetPowerRestoreDelayRes*>(response);
494
495 if (*dataLen != 0)
496 {
497 *dataLen = 0;
498 return IPMI_CC_REQ_DATA_LEN_INVALID;
499 }
500
Vernon Mauery15419dd2019-05-24 09:40:30 -0700501 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
Jason M. Bills64796042018-10-03 16:51:55 -0700502 std::string service =
Vernon Mauery15419dd2019-05-24 09:40:30 -0700503 getService(*dbus, powerRestoreDelayIntf, powerRestoreDelayObjPath);
Jason M. Bills64796042018-10-03 16:51:55 -0700504 Value variant =
Vernon Mauery15419dd2019-05-24 09:40:30 -0700505 getDbusProperty(*dbus, service, powerRestoreDelayObjPath,
Jason M. Bills64796042018-10-03 16:51:55 -0700506 powerRestoreDelayIntf, powerRestoreDelayProp);
507
Vernon Mauery8166c8d2019-05-23 11:22:30 -0700508 uint16_t delay = std::get<uint16_t>(variant);
Jason M. Bills64796042018-10-03 16:51:55 -0700509 resp->byteLSB = delay;
510 resp->byteMSB = delay >> 8;
511
512 *dataLen = sizeof(GetPowerRestoreDelayRes);
513
514 return IPMI_CC_OK;
Jia, Chunhuia835eaa2018-09-05 09:00:41 +0800515}
516
Jia, Chunhuicc49b542019-03-20 15:41:07 +0800517static uint8_t bcdToDec(uint8_t val)
518{
519 return ((val / 16 * 10) + (val % 16));
520}
521
522// Allows an update utility or system BIOS to send the status of an embedded
523// firmware update attempt to the BMC. After received, BMC will create a logging
524// record.
525ipmi::RspType<> ipmiOEMSendEmbeddedFwUpdStatus(uint8_t status, uint8_t target,
526 uint8_t majorRevision,
527 uint8_t minorRevision,
528 uint32_t auxInfo)
529{
530 std::string firmware;
Jason M. Billsdc249272019-04-03 09:58:40 -0700531 int instance = (target & targetInstanceMask) >> targetInstanceShift;
Jia, Chunhuicc49b542019-03-20 15:41:07 +0800532 target = (target & selEvtTargetMask) >> selEvtTargetShift;
533
534 /* make sure the status is 0, 1, or 2 as per the spec */
535 if (status > 2)
536 {
537 return ipmi::response(ipmi::ccInvalidFieldRequest);
538 }
Jason M. Billsdc249272019-04-03 09:58:40 -0700539 /* make sure the target is 0, 1, 2, or 4 as per the spec */
540 if (target > 4 || target == 3)
541 {
542 return ipmi::response(ipmi::ccInvalidFieldRequest);
543 }
Jia, Chunhuicc49b542019-03-20 15:41:07 +0800544 /*orignal OEM command is to record OEM SEL.
545 But openbmc does not support OEM SEL, so we redirect it to redfish event
546 logging. */
547 std::string buildInfo;
548 std::string action;
549 switch (FWUpdateTarget(target))
550 {
551 case FWUpdateTarget::targetBMC:
552 firmware = "BMC";
Jason M. Billsdc249272019-04-03 09:58:40 -0700553 buildInfo = "major: " + std::to_string(majorRevision) + " minor: " +
Jia, Chunhuicc49b542019-03-20 15:41:07 +0800554 std::to_string(bcdToDec(minorRevision)) + // BCD encoded
555 " BuildID: " + std::to_string(auxInfo);
556 buildInfo += std::to_string(auxInfo);
557 break;
558 case FWUpdateTarget::targetBIOS:
559 firmware = "BIOS";
560 buildInfo =
Jason M. Billsdc249272019-04-03 09:58:40 -0700561 "major: " +
Jia, Chunhuicc49b542019-03-20 15:41:07 +0800562 std::to_string(bcdToDec(majorRevision)) + // BCD encoded
563 " minor: " +
564 std::to_string(bcdToDec(minorRevision)) + // BCD encoded
565 " ReleaseNumber: " + // ASCII encoded
566 std::to_string(static_cast<uint8_t>(auxInfo >> 0) - '0') +
567 std::to_string(static_cast<uint8_t>(auxInfo >> 8) - '0') +
568 std::to_string(static_cast<uint8_t>(auxInfo >> 16) - '0') +
569 std::to_string(static_cast<uint8_t>(auxInfo >> 24) - '0');
570 break;
571 case FWUpdateTarget::targetME:
572 firmware = "ME";
573 buildInfo =
Jason M. Billsdc249272019-04-03 09:58:40 -0700574 "major: " + std::to_string(majorRevision) + " minor1: " +
Jia, Chunhuicc49b542019-03-20 15:41:07 +0800575 std::to_string(bcdToDec(minorRevision)) + // BCD encoded
576 " minor2: " +
577 std::to_string(bcdToDec(static_cast<uint8_t>(auxInfo >> 0))) +
578 " build1: " +
579 std::to_string(bcdToDec(static_cast<uint8_t>(auxInfo >> 8))) +
580 " build2: " +
581 std::to_string(bcdToDec(static_cast<uint8_t>(auxInfo >> 16)));
582 break;
583 case FWUpdateTarget::targetOEMEWS:
584 firmware = "EWS";
Jason M. Billsdc249272019-04-03 09:58:40 -0700585 buildInfo = "major: " + std::to_string(majorRevision) + " minor: " +
Jia, Chunhuicc49b542019-03-20 15:41:07 +0800586 std::to_string(bcdToDec(minorRevision)) + // BCD encoded
587 " BuildID: " + std::to_string(auxInfo);
588 break;
589 }
590
Jason M. Billsdc249272019-04-03 09:58:40 -0700591 static const std::string openBMCMessageRegistryVersion("0.1");
592 std::string redfishMsgID = "OpenBMC." + openBMCMessageRegistryVersion;
593
Jia, Chunhuicc49b542019-03-20 15:41:07 +0800594 switch (status)
595 {
596 case 0x0:
597 action = "update started";
Jason M. Billsdc249272019-04-03 09:58:40 -0700598 redfishMsgID += ".FirmwareUpdateStarted";
Jia, Chunhuicc49b542019-03-20 15:41:07 +0800599 break;
600 case 0x1:
601 action = "update completed successfully";
Jason M. Billsdc249272019-04-03 09:58:40 -0700602 redfishMsgID += ".FirmwareUpdateCompleted";
Jia, Chunhuicc49b542019-03-20 15:41:07 +0800603 break;
604 case 0x2:
605 action = "update failure";
Jason M. Billsdc249272019-04-03 09:58:40 -0700606 redfishMsgID += ".FirmwareUpdateFailed";
Jia, Chunhuicc49b542019-03-20 15:41:07 +0800607 break;
608 default:
609 action = "unknown";
610 break;
611 }
612
Jason M. Billsdc249272019-04-03 09:58:40 -0700613 std::string firmwareInstanceStr =
614 firmware + " instance: " + std::to_string(instance);
615 std::string message("[firmware update] " + firmwareInstanceStr +
616 " status: <" + action + "> " + buildInfo);
Jia, Chunhuicc49b542019-03-20 15:41:07 +0800617
618 sd_journal_send("MESSAGE=%s", message.c_str(), "PRIORITY=%i", LOG_INFO,
Jason M. Billsdc249272019-04-03 09:58:40 -0700619 "REDFISH_MESSAGE_ID=%s", redfishMsgID.c_str(),
620 "REDFISH_MESSAGE_ARGS=%s,%s", firmwareInstanceStr.c_str(),
621 buildInfo.c_str(), NULL);
Jia, Chunhuicc49b542019-03-20 15:41:07 +0800622 return ipmi::responseSuccess();
623}
624
Jason M. Bills64796042018-10-03 16:51:55 -0700625ipmi_ret_t ipmiOEMSetPowerRestoreDelay(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
626 ipmi_request_t request,
627 ipmi_response_t response,
628 ipmi_data_len_t dataLen,
629 ipmi_context_t context)
630{
631 SetPowerRestoreDelayReq* data =
632 reinterpret_cast<SetPowerRestoreDelayReq*>(request);
633 uint16_t delay = 0;
634
635 if (*dataLen != sizeof(SetPowerRestoreDelayReq))
636 {
637 *dataLen = 0;
638 return IPMI_CC_REQ_DATA_LEN_INVALID;
639 }
640 delay = data->byteMSB;
641 delay = (delay << 8) | data->byteLSB;
Vernon Mauery15419dd2019-05-24 09:40:30 -0700642 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
Jason M. Bills64796042018-10-03 16:51:55 -0700643 std::string service =
Vernon Mauery15419dd2019-05-24 09:40:30 -0700644 getService(*dbus, powerRestoreDelayIntf, powerRestoreDelayObjPath);
645 setDbusProperty(*dbus, service, powerRestoreDelayObjPath,
Jason M. Bills64796042018-10-03 16:51:55 -0700646 powerRestoreDelayIntf, powerRestoreDelayProp, delay);
647 *dataLen = 0;
648
649 return IPMI_CC_OK;
650}
651
Jason M. Bills42bd9c82019-06-28 16:39:34 -0700652static bool cpuPresent(const std::string& cpuName)
Jason M. Bills64796042018-10-03 16:51:55 -0700653{
Jason M. Bills42bd9c82019-06-28 16:39:34 -0700654 static constexpr const char* cpuPresencePathPrefix =
655 "/xyz/openbmc_project/inventory/system/chassis/motherboard/";
656 static constexpr const char* cpuPresenceIntf =
657 "xyz.openbmc_project.Inventory.Item";
658 std::string cpuPresencePath = cpuPresencePathPrefix + cpuName;
659 std::shared_ptr<sdbusplus::asio::connection> busp = getSdBus();
660 try
Jason M. Bills64796042018-10-03 16:51:55 -0700661 {
Jason M. Bills42bd9c82019-06-28 16:39:34 -0700662 auto service =
663 ipmi::getService(*busp, cpuPresenceIntf, cpuPresencePath);
664
665 ipmi::Value result = ipmi::getDbusProperty(
666 *busp, service, cpuPresencePath, cpuPresenceIntf, "Present");
667 return std::get<bool>(result);
668 }
669 catch (const std::exception& e)
670 {
671 phosphor::logging::log<phosphor::logging::level::INFO>(
672 "Cannot find processor presence",
673 phosphor::logging::entry("NAME=%s", cpuName.c_str()));
674 return false;
675 }
676}
677
678ipmi::RspType<bool, // CATERR Reset Enabled
679 bool, // ERR2 Reset Enabled
680 uint6_t, // reserved
681 uint8_t, // reserved, returns 0x3F
682 uint6_t, // CPU1 CATERR Count
683 uint2_t, // CPU1 Status
684 uint6_t, // CPU2 CATERR Count
685 uint2_t, // CPU2 Status
686 uint6_t, // CPU3 CATERR Count
687 uint2_t, // CPU3 Status
688 uint6_t, // CPU4 CATERR Count
689 uint2_t, // CPU4 Status
690 uint8_t // Crashdump Count
691 >
692 ipmiOEMGetProcessorErrConfig()
693{
694 bool resetOnCATERR = false;
695 bool resetOnERR2 = false;
696 uint6_t cpu1CATERRCount = 0;
697 uint6_t cpu2CATERRCount = 0;
698 uint6_t cpu3CATERRCount = 0;
699 uint6_t cpu4CATERRCount = 0;
700 uint8_t crashdumpCount = 0;
701 uint2_t cpu1Status =
702 cpuPresent("CPU_1") ? CPUStatus::enabled : CPUStatus::notPresent;
703 uint2_t cpu2Status =
704 cpuPresent("CPU_2") ? CPUStatus::enabled : CPUStatus::notPresent;
705 uint2_t cpu3Status =
706 cpuPresent("CPU_3") ? CPUStatus::enabled : CPUStatus::notPresent;
707 uint2_t cpu4Status =
708 cpuPresent("CPU_4") ? CPUStatus::enabled : CPUStatus::notPresent;
709
710 std::shared_ptr<sdbusplus::asio::connection> busp = getSdBus();
711 try
712 {
713 auto service = ipmi::getService(*busp, processorErrConfigIntf,
714 processorErrConfigObjPath);
715
716 ipmi::PropertyMap result = ipmi::getAllDbusProperties(
717 *busp, service, processorErrConfigObjPath, processorErrConfigIntf);
718 resetOnCATERR = std::get<bool>(result.at("ResetOnCATERR"));
719 resetOnERR2 = std::get<bool>(result.at("ResetOnERR2"));
720 cpu1CATERRCount = std::get<uint8_t>(result.at("ErrorCountCPU1"));
721 cpu2CATERRCount = std::get<uint8_t>(result.at("ErrorCountCPU2"));
722 cpu3CATERRCount = std::get<uint8_t>(result.at("ErrorCountCPU3"));
723 cpu4CATERRCount = std::get<uint8_t>(result.at("ErrorCountCPU4"));
724 crashdumpCount = std::get<uint8_t>(result.at("CrashdumpCount"));
725 }
726 catch (const std::exception& e)
727 {
728 phosphor::logging::log<phosphor::logging::level::ERR>(
729 "Failed to fetch processor error config",
730 phosphor::logging::entry("ERROR=%s", e.what()));
731 return ipmi::responseUnspecifiedError();
Jason M. Bills64796042018-10-03 16:51:55 -0700732 }
733
Jason M. Bills42bd9c82019-06-28 16:39:34 -0700734 return ipmi::responseSuccess(resetOnCATERR, resetOnERR2, 0, 0x3F,
735 cpu1CATERRCount, cpu1Status, cpu2CATERRCount,
736 cpu2Status, cpu3CATERRCount, cpu3Status,
737 cpu4CATERRCount, cpu4Status, crashdumpCount);
738}
Jason M. Bills64796042018-10-03 16:51:55 -0700739
Jason M. Bills42bd9c82019-06-28 16:39:34 -0700740ipmi::RspType<> ipmiOEMSetProcessorErrConfig(
741 bool resetOnCATERR, bool resetOnERR2, uint6_t reserved1, uint8_t reserved2,
742 std::optional<bool> clearCPUErrorCount,
743 std::optional<bool> clearCrashdumpCount, std::optional<uint6_t> reserved3)
744{
745 std::shared_ptr<sdbusplus::asio::connection> busp = getSdBus();
Jason M. Bills64796042018-10-03 16:51:55 -0700746
747 try
748 {
Jason M. Bills42bd9c82019-06-28 16:39:34 -0700749 auto service = ipmi::getService(*busp, processorErrConfigIntf,
750 processorErrConfigObjPath);
751 ipmi::setDbusProperty(*busp, service, processorErrConfigObjPath,
752 processorErrConfigIntf, "ResetOnCATERR",
753 resetOnCATERR);
754 ipmi::setDbusProperty(*busp, service, processorErrConfigObjPath,
755 processorErrConfigIntf, "ResetOnERR2",
756 resetOnERR2);
757 if (clearCPUErrorCount.value_or(false))
758 {
759 ipmi::setDbusProperty(*busp, service, processorErrConfigObjPath,
Jason M. Billsd3e19932019-08-15 12:39:03 -0700760 processorErrConfigIntf, "ErrorCountCPU1",
761 static_cast<uint8_t>(0));
Jason M. Bills42bd9c82019-06-28 16:39:34 -0700762 ipmi::setDbusProperty(*busp, service, processorErrConfigObjPath,
Jason M. Billsd3e19932019-08-15 12:39:03 -0700763 processorErrConfigIntf, "ErrorCountCPU2",
764 static_cast<uint8_t>(0));
765 ipmi::setDbusProperty(*busp, service, processorErrConfigObjPath,
766 processorErrConfigIntf, "ErrorCountCPU3",
767 static_cast<uint8_t>(0));
768 ipmi::setDbusProperty(*busp, service, processorErrConfigObjPath,
769 processorErrConfigIntf, "ErrorCountCPU4",
770 static_cast<uint8_t>(0));
Jason M. Bills42bd9c82019-06-28 16:39:34 -0700771 }
772 if (clearCrashdumpCount.value_or(false))
773 {
774 ipmi::setDbusProperty(*busp, service, processorErrConfigObjPath,
Jason M. Billsd3e19932019-08-15 12:39:03 -0700775 processorErrConfigIntf, "CrashdumpCount",
776 static_cast<uint8_t>(0));
Jason M. Bills42bd9c82019-06-28 16:39:34 -0700777 }
Jason M. Bills64796042018-10-03 16:51:55 -0700778 }
Jason M. Bills42bd9c82019-06-28 16:39:34 -0700779 catch (std::exception& e)
Jason M. Bills64796042018-10-03 16:51:55 -0700780 {
Kuiying Wangbc546672018-11-23 15:41:05 +0800781 phosphor::logging::log<phosphor::logging::level::ERR>(
Jason M. Bills42bd9c82019-06-28 16:39:34 -0700782 "Failed to set processor error config",
783 phosphor::logging::entry("EXCEPTION=%s", e.what()));
784 return ipmi::responseUnspecifiedError();
Jason M. Bills64796042018-10-03 16:51:55 -0700785 }
786
Jason M. Bills42bd9c82019-06-28 16:39:34 -0700787 return ipmi::responseSuccess();
Jason M. Bills64796042018-10-03 16:51:55 -0700788}
789
Yong Li703922d2018-11-06 13:25:31 +0800790ipmi_ret_t ipmiOEMGetShutdownPolicy(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
791 ipmi_request_t request,
792 ipmi_response_t response,
793 ipmi_data_len_t dataLen,
794 ipmi_context_t context)
795{
796 GetOEMShutdownPolicyRes* resp =
797 reinterpret_cast<GetOEMShutdownPolicyRes*>(response);
798
799 if (*dataLen != 0)
800 {
801 phosphor::logging::log<phosphor::logging::level::ERR>(
Kuiying Wang45f04982018-12-26 09:23:08 +0800802 "oem_get_shutdown_policy: invalid input len!");
Yong Li703922d2018-11-06 13:25:31 +0800803 *dataLen = 0;
804 return IPMI_CC_REQ_DATA_LEN_INVALID;
805 }
806
807 *dataLen = 0;
808
809 try
810 {
Vernon Mauery15419dd2019-05-24 09:40:30 -0700811 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
Yong Li703922d2018-11-06 13:25:31 +0800812 std::string service =
Vernon Mauery15419dd2019-05-24 09:40:30 -0700813 getService(*dbus, oemShutdownPolicyIntf, oemShutdownPolicyObjPath);
814 Value variant = getDbusProperty(
815 *dbus, service, oemShutdownPolicyObjPath, oemShutdownPolicyIntf,
816 oemShutdownPolicyObjPathProp);
Yong Li0669d192019-05-06 14:01:46 +0800817
818 if (sdbusplus::com::intel::Control::server::OCOTShutdownPolicy::
819 convertPolicyFromString(std::get<std::string>(variant)) ==
820 sdbusplus::com::intel::Control::server::OCOTShutdownPolicy::Policy::
821 NoShutdownOnOCOT)
822 {
823 resp->policy = 0;
824 }
825 else if (sdbusplus::com::intel::Control::server::OCOTShutdownPolicy::
826 convertPolicyFromString(std::get<std::string>(variant)) ==
827 sdbusplus::com::intel::Control::server::OCOTShutdownPolicy::
828 Policy::ShutdownOnOCOT)
829 {
830 resp->policy = 1;
831 }
832 else
833 {
834 phosphor::logging::log<phosphor::logging::level::ERR>(
835 "oem_set_shutdown_policy: invalid property!",
836 phosphor::logging::entry(
837 "PROP=%s", std::get<std::string>(variant).c_str()));
838 return IPMI_CC_UNSPECIFIED_ERROR;
839 }
Yong Li703922d2018-11-06 13:25:31 +0800840 // TODO needs to check if it is multi-node products,
841 // policy is only supported on node 3/4
842 resp->policySupport = shutdownPolicySupported;
843 }
844 catch (sdbusplus::exception_t& e)
845 {
846 phosphor::logging::log<phosphor::logging::level::ERR>(e.description());
847 return IPMI_CC_UNSPECIFIED_ERROR;
848 }
849
850 *dataLen = sizeof(GetOEMShutdownPolicyRes);
851 return IPMI_CC_OK;
852}
853
854ipmi_ret_t ipmiOEMSetShutdownPolicy(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
855 ipmi_request_t request,
856 ipmi_response_t response,
857 ipmi_data_len_t dataLen,
858 ipmi_context_t context)
859{
860 uint8_t* req = reinterpret_cast<uint8_t*>(request);
Yong Li0669d192019-05-06 14:01:46 +0800861 sdbusplus::com::intel::Control::server::OCOTShutdownPolicy::Policy policy =
862 sdbusplus::com::intel::Control::server::OCOTShutdownPolicy::Policy::
863 NoShutdownOnOCOT;
Yong Li703922d2018-11-06 13:25:31 +0800864
865 // TODO needs to check if it is multi-node products,
866 // policy is only supported on node 3/4
867 if (*dataLen != 1)
868 {
869 phosphor::logging::log<phosphor::logging::level::ERR>(
870 "oem_set_shutdown_policy: invalid input len!");
871 *dataLen = 0;
872 return IPMI_CC_REQ_DATA_LEN_INVALID;
873 }
874
875 *dataLen = 0;
876 if ((*req != noShutdownOnOCOT) && (*req != shutdownOnOCOT))
877 {
878 phosphor::logging::log<phosphor::logging::level::ERR>(
879 "oem_set_shutdown_policy: invalid input!");
880 return IPMI_CC_INVALID_FIELD_REQUEST;
881 }
882
Yong Li0669d192019-05-06 14:01:46 +0800883 if (*req == noShutdownOnOCOT)
884 {
885 policy = sdbusplus::com::intel::Control::server::OCOTShutdownPolicy::
886 Policy::NoShutdownOnOCOT;
887 }
888 else
889 {
890 policy = sdbusplus::com::intel::Control::server::OCOTShutdownPolicy::
891 Policy::ShutdownOnOCOT;
892 }
893
Yong Li703922d2018-11-06 13:25:31 +0800894 try
895 {
Vernon Mauery15419dd2019-05-24 09:40:30 -0700896 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
Yong Li703922d2018-11-06 13:25:31 +0800897 std::string service =
Vernon Mauery15419dd2019-05-24 09:40:30 -0700898 getService(*dbus, oemShutdownPolicyIntf, oemShutdownPolicyObjPath);
Yong Li0669d192019-05-06 14:01:46 +0800899 setDbusProperty(
Vernon Mauery15419dd2019-05-24 09:40:30 -0700900 *dbus, service, oemShutdownPolicyObjPath, oemShutdownPolicyIntf,
Yong Li0669d192019-05-06 14:01:46 +0800901 oemShutdownPolicyObjPathProp,
902 sdbusplus::com::intel::Control::server::convertForMessage(policy));
Yong Li703922d2018-11-06 13:25:31 +0800903 }
904 catch (sdbusplus::exception_t& e)
905 {
906 phosphor::logging::log<phosphor::logging::level::ERR>(e.description());
907 return IPMI_CC_UNSPECIFIED_ERROR;
908 }
909
910 return IPMI_CC_OK;
911}
912
Suryakanth Sekard509eb92018-11-15 17:44:11 +0530913/** @brief implementation for check the DHCP or not in IPv4
914 * @param[in] Channel - Channel number
915 * @returns true or false.
916 */
917static bool isDHCPEnabled(uint8_t Channel)
918{
919 try
920 {
921 auto ethdevice = getChannelName(Channel);
922 if (ethdevice.empty())
923 {
924 return false;
925 }
926 auto ethIP = ethdevice + "/ipv4";
Vernon Mauery15419dd2019-05-24 09:40:30 -0700927 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
Suryakanth Sekard509eb92018-11-15 17:44:11 +0530928 auto ethernetObj =
Vernon Mauery15419dd2019-05-24 09:40:30 -0700929 getDbusObject(*dbus, networkIPIntf, networkRoot, ethIP);
930 auto value = getDbusProperty(*dbus, networkService, ethernetObj.first,
Suryakanth Sekard509eb92018-11-15 17:44:11 +0530931 networkIPIntf, "Origin");
Vernon Mauery8166c8d2019-05-23 11:22:30 -0700932 if (std::get<std::string>(value) ==
Suryakanth Sekard509eb92018-11-15 17:44:11 +0530933 "xyz.openbmc_project.Network.IP.AddressOrigin.DHCP")
934 {
935 return true;
936 }
937 else
938 {
939 return false;
940 }
941 }
942 catch (sdbusplus::exception_t& e)
943 {
944 phosphor::logging::log<phosphor::logging::level::ERR>(e.description());
945 return true;
946 }
947}
948
949/** @brief implementes for check the DHCP or not in IPv6
950 * @param[in] Channel - Channel number
951 * @returns true or false.
952 */
953static bool isDHCPIPv6Enabled(uint8_t Channel)
954{
955
956 try
957 {
958 auto ethdevice = getChannelName(Channel);
959 if (ethdevice.empty())
960 {
961 return false;
962 }
963 auto ethIP = ethdevice + "/ipv6";
Vernon Mauery15419dd2019-05-24 09:40:30 -0700964 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
Suryakanth Sekard509eb92018-11-15 17:44:11 +0530965 auto objectInfo =
Vernon Mauery15419dd2019-05-24 09:40:30 -0700966 getDbusObject(*dbus, networkIPIntf, networkRoot, ethIP);
967 auto properties = getAllDbusProperties(*dbus, objectInfo.second,
Suryakanth Sekard509eb92018-11-15 17:44:11 +0530968 objectInfo.first, networkIPIntf);
Vernon Mauery8166c8d2019-05-23 11:22:30 -0700969 if (std::get<std::string>(properties["Origin"]) ==
Suryakanth Sekard509eb92018-11-15 17:44:11 +0530970 "xyz.openbmc_project.Network.IP.AddressOrigin.DHCP")
971 {
972 return true;
973 }
974 else
975 {
976 return false;
977 }
978 }
979 catch (sdbusplus::exception_t& e)
980 {
981 phosphor::logging::log<phosphor::logging::level::ERR>(e.description());
982 return true;
983 }
984}
985
986/** @brief implementes the creating of default new user
987 * @param[in] userName - new username in 16 bytes.
988 * @param[in] userPassword - new password in 20 bytes
989 * @returns ipmi completion code.
990 */
991ipmi::RspType<> ipmiOEMSetUser2Activation(
992 std::array<uint8_t, ipmi::ipmiMaxUserName>& userName,
993 std::array<uint8_t, ipmi::maxIpmi20PasswordSize>& userPassword)
994{
995 bool userState = false;
996 // Check for System Interface not exist and LAN should be static
997 for (uint8_t channel = 0; channel < maxIpmiChannels; channel++)
998 {
999 ChannelInfo chInfo;
1000 try
1001 {
1002 getChannelInfo(channel, chInfo);
1003 }
1004 catch (sdbusplus::exception_t& e)
1005 {
1006 phosphor::logging::log<phosphor::logging::level::ERR>(
1007 "ipmiOEMSetUser2Activation: Failed to get Channel Info",
1008 phosphor::logging::entry("MSG: %s", e.description()));
1009 return ipmi::response(ipmi::ccUnspecifiedError);
1010 }
1011 if (chInfo.mediumType ==
1012 static_cast<uint8_t>(EChannelMediumType::systemInterface))
1013 {
1014 phosphor::logging::log<phosphor::logging::level::ERR>(
1015 "ipmiOEMSetUser2Activation: system interface exist .");
1016 return ipmi::response(ipmi::ccCommandNotAvailable);
1017 }
1018 else
1019 {
1020
1021 if (chInfo.mediumType ==
1022 static_cast<uint8_t>(EChannelMediumType::lan8032))
1023 {
1024 if (isDHCPIPv6Enabled(channel) || isDHCPEnabled(channel))
1025 {
1026 phosphor::logging::log<phosphor::logging::level::ERR>(
1027 "ipmiOEMSetUser2Activation: DHCP enabled .");
1028 return ipmi::response(ipmi::ccCommandNotAvailable);
1029 }
1030 }
1031 }
1032 }
1033 uint8_t maxChUsers = 0, enabledUsers = 0, fixedUsers = 0;
1034 if (ipmi::ccSuccess ==
1035 ipmiUserGetAllCounts(maxChUsers, enabledUsers, fixedUsers))
1036 {
1037 if (enabledUsers > 1)
1038 {
1039 phosphor::logging::log<phosphor::logging::level::ERR>(
1040 "ipmiOEMSetUser2Activation: more than one user is enabled.");
1041 return ipmi::response(ipmi::ccCommandNotAvailable);
1042 }
1043 // Check the user 2 is enabled or not
1044 ipmiUserCheckEnabled(ipmiDefaultUserId, userState);
1045 if (userState == true)
1046 {
1047 phosphor::logging::log<phosphor::logging::level::ERR>(
1048 "ipmiOEMSetUser2Activation: user 2 already enabled .");
1049 return ipmi::response(ipmi::ccCommandNotAvailable);
1050 }
1051 }
1052 else
1053 {
1054 return ipmi::response(ipmi::ccUnspecifiedError);
1055 }
1056
1057#if BYTE_ORDER == LITTLE_ENDIAN
1058 PrivAccess privAccess = {PRIVILEGE_ADMIN, true, true, true, 0};
1059#endif
1060#if BYTE_ORDER == BIG_ENDIAN
1061 PrivAccess privAccess = {0, true, true, true, PRIVILEGE_ADMIN};
1062#endif
1063
1064 if (ipmi::ccSuccess ==
1065 ipmiUserSetUserName(ipmiDefaultUserId,
1066 reinterpret_cast<const char*>(userName.data())))
1067 {
1068 if (ipmi::ccSuccess ==
1069 ipmiUserSetUserPassword(
1070 ipmiDefaultUserId,
1071 reinterpret_cast<const char*>(userPassword.data())))
1072 {
1073 if (ipmi::ccSuccess ==
1074 ipmiUserSetPrivilegeAccess(
1075 ipmiDefaultUserId,
1076 static_cast<uint8_t>(ipmi::EChannelID::chanLan1),
1077 privAccess, true))
1078 {
1079 phosphor::logging::log<phosphor::logging::level::INFO>(
1080 "ipmiOEMSetUser2Activation: user created successfully ");
1081 return ipmi::responseSuccess();
1082 }
1083 }
1084 // we need to delete the default user id which added in this command as
1085 // password / priv setting is failed.
1086 ipmiUserSetUserName(ipmiDefaultUserId, "");
1087 phosphor::logging::log<phosphor::logging::level::ERR>(
1088 "ipmiOEMSetUser2Activation: password / priv setting is failed.");
1089 }
1090 else
1091 {
1092 phosphor::logging::log<phosphor::logging::level::ERR>(
1093 "ipmiOEMSetUser2Activation: Setting username failed.");
1094 }
1095
1096 return ipmi::response(ipmi::ccCommandNotAvailable);
1097}
1098
Suryakanth Sekar822b0b42019-11-15 18:32:53 +05301099/** @brief implementes executing the linux command
1100 * @param[in] linux command
1101 * @returns status
1102 */
1103
1104static uint8_t executeCmd(const char* path)
1105{
1106 boost::process::child execProg(path);
1107 execProg.wait();
1108
1109 int retCode = execProg.exit_code();
1110 if (retCode)
1111 {
1112 return ipmi::ccUnspecifiedError;
1113 }
1114 return ipmi::ccSuccess;
1115}
1116
1117/** @brief implementes ASD Security event logging
1118 * @param[in] Event message string
1119 * @param[in] Event Severity
1120 * @returns status
1121 */
1122
1123static void atScaleDebugEventlog(std::string msg, int severity)
1124{
1125 std::string eventStr = "OpenBMC.0.1." + msg;
1126 sd_journal_send("MESSAGE=Security Event: %s", eventStr.c_str(),
1127 "PRIORITY=%i", severity, "REDFISH_MESSAGE_ID=%s",
1128 eventStr.c_str(), NULL);
1129}
1130
Richard Marian Thomaiyarfc5e9852019-04-14 15:06:27 +05301131/** @brief implementes setting password for special user
1132 * @param[in] specialUserIndex
1133 * @param[in] userPassword - new password in 20 bytes
1134 * @returns ipmi completion code.
1135 */
1136ipmi::RspType<> ipmiOEMSetSpecialUserPassword(ipmi::Context::ptr ctx,
1137 uint8_t specialUserIndex,
1138 std::vector<uint8_t> userPassword)
1139{
1140 ChannelInfo chInfo;
Suryakanth Sekar822b0b42019-11-15 18:32:53 +05301141 ipmi_ret_t status = ipmi::ccSuccess;
1142
Richard Marian Thomaiyarfc5e9852019-04-14 15:06:27 +05301143 try
1144 {
1145 getChannelInfo(ctx->channel, chInfo);
1146 }
1147 catch (sdbusplus::exception_t& e)
1148 {
1149 phosphor::logging::log<phosphor::logging::level::ERR>(
1150 "ipmiOEMSetSpecialUserPassword: Failed to get Channel Info",
1151 phosphor::logging::entry("MSG: %s", e.description()));
1152 return ipmi::responseUnspecifiedError();
1153 }
1154 if (chInfo.mediumType !=
1155 static_cast<uint8_t>(EChannelMediumType::systemInterface))
1156 {
1157 phosphor::logging::log<phosphor::logging::level::ERR>(
1158 "ipmiOEMSetSpecialUserPassword: Error - supported only in KCS "
1159 "interface");
1160 return ipmi::responseCommandNotAvailable();
1161 }
Suryakanth Sekar822b0b42019-11-15 18:32:53 +05301162
1163 // 0 for root user and 1 for AtScaleDebug is allowed
1164 if (specialUserIndex >
1165 static_cast<uint8_t>(SpecialUserIndex::atScaleDebugUser))
Richard Marian Thomaiyarfc5e9852019-04-14 15:06:27 +05301166 {
1167 phosphor::logging::log<phosphor::logging::level::ERR>(
1168 "ipmiOEMSetSpecialUserPassword: Invalid user account");
1169 return ipmi::responseParmOutOfRange();
1170 }
Suryakanth Sekar822b0b42019-11-15 18:32:53 +05301171 if (userPassword.size() != 0)
Richard Marian Thomaiyarfc5e9852019-04-14 15:06:27 +05301172 {
Suryakanth Sekar822b0b42019-11-15 18:32:53 +05301173 constexpr uint8_t minPasswordSizeRequired = 6;
1174 std::string passwd;
1175 if (userPassword.size() < minPasswordSizeRequired ||
1176 userPassword.size() > ipmi::maxIpmi20PasswordSize)
1177 {
1178 return ipmi::responseReqDataLenInvalid();
1179 }
1180 passwd.assign(reinterpret_cast<const char*>(userPassword.data()),
1181 userPassword.size());
1182 if (specialUserIndex ==
1183 static_cast<uint8_t>(SpecialUserIndex::atScaleDebugUser))
1184 {
1185 status = ipmiSetSpecialUserPassword("asdbg", passwd);
1186
1187 atScaleDebugEventlog("AtScaleDebugSpecialUserEnabled", LOG_CRIT);
1188 }
1189 else
1190 {
1191 status = ipmiSetSpecialUserPassword("root", passwd);
1192 }
1193 return ipmi::response(status);
Richard Marian Thomaiyarfc5e9852019-04-14 15:06:27 +05301194 }
Suryakanth Sekar822b0b42019-11-15 18:32:53 +05301195 else
1196 {
1197 if (specialUserIndex ==
1198 static_cast<uint8_t>(SpecialUserIndex::rootUser))
1199 {
1200 status = executeCmd("passwd -d root");
1201 }
1202 else
1203 {
1204
1205 status = executeCmd("passwd -d asdbg");
1206
1207 if (status == 0)
1208 {
1209 atScaleDebugEventlog("AtScaleDebugSpecialUserDisabled",
1210 LOG_INFO);
1211 }
1212 }
1213 return ipmi::response(status);
1214 }
Richard Marian Thomaiyarfc5e9852019-04-14 15:06:27 +05301215}
1216
Kuiying Wang45f04982018-12-26 09:23:08 +08001217namespace ledAction
1218{
1219using namespace sdbusplus::xyz::openbmc_project::Led::server;
1220std::map<Physical::Action, uint8_t> actionDbusToIpmi = {
jayaprakash Mutyala934ee9c2019-12-13 17:49:27 +00001221 {Physical::Action::Off, 0},
1222 {Physical::Action::On, 2},
1223 {Physical::Action::Blink, 1}};
Kuiying Wang45f04982018-12-26 09:23:08 +08001224
1225std::map<uint8_t, std::string> offsetObjPath = {
1226 {2, statusAmberObjPath}, {4, statusGreenObjPath}, {6, identifyLEDObjPath}};
1227
1228} // namespace ledAction
1229
1230int8_t getLEDState(sdbusplus::bus::bus& bus, const std::string& intf,
1231 const std::string& objPath, uint8_t& state)
1232{
1233 try
1234 {
1235 std::string service = getService(bus, intf, objPath);
1236 Value stateValue =
1237 getDbusProperty(bus, service, objPath, intf, "State");
Vernon Mauery8166c8d2019-05-23 11:22:30 -07001238 std::string strState = std::get<std::string>(stateValue);
Kuiying Wang45f04982018-12-26 09:23:08 +08001239 state = ledAction::actionDbusToIpmi.at(
1240 sdbusplus::xyz::openbmc_project::Led::server::Physical::
1241 convertActionFromString(strState));
1242 }
1243 catch (sdbusplus::exception::SdBusError& e)
1244 {
1245 phosphor::logging::log<phosphor::logging::level::ERR>(e.what());
1246 return -1;
1247 }
1248 return 0;
1249}
1250
1251ipmi_ret_t ipmiOEMGetLEDStatus(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
1252 ipmi_request_t request, ipmi_response_t response,
1253 ipmi_data_len_t dataLen, ipmi_context_t context)
1254{
1255 uint8_t* resp = reinterpret_cast<uint8_t*>(response);
1256 // LED Status
1257 //[1:0] = Reserved
1258 //[3:2] = Status(Amber)
1259 //[5:4] = Status(Green)
1260 //[7:6] = System Identify
1261 // Status definitions:
1262 // 00b = Off
1263 // 01b = Blink
1264 // 10b = On
1265 // 11b = invalid
1266 if (*dataLen != 0)
1267 {
1268 phosphor::logging::log<phosphor::logging::level::ERR>(
1269 "oem_get_led_status: invalid input len!");
1270 *dataLen = 0;
1271 return IPMI_CC_REQ_DATA_LEN_INVALID;
1272 }
1273
1274 phosphor::logging::log<phosphor::logging::level::DEBUG>("GET led status");
1275 *resp = 0;
1276 *dataLen = 0;
Vernon Mauery15419dd2019-05-24 09:40:30 -07001277 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
Kuiying Wang45f04982018-12-26 09:23:08 +08001278 for (auto it = ledAction::offsetObjPath.begin();
1279 it != ledAction::offsetObjPath.end(); ++it)
1280 {
1281 uint8_t state = 0;
Vernon Mauery15419dd2019-05-24 09:40:30 -07001282 if (-1 == getLEDState(*dbus, ledIntf, it->second, state))
Kuiying Wang45f04982018-12-26 09:23:08 +08001283 {
1284 phosphor::logging::log<phosphor::logging::level::ERR>(
1285 "oem_get_led_status: fail to get ID LED status!");
1286 return IPMI_CC_UNSPECIFIED_ERROR;
1287 }
1288 *resp |= state << it->first;
1289 }
1290
1291 *dataLen = sizeof(*resp);
1292 return IPMI_CC_OK;
1293}
1294
Yong Li23737fe2019-02-19 08:49:55 +08001295ipmi_ret_t ipmiOEMCfgHostSerialPortSpeed(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
1296 ipmi_request_t request,
1297 ipmi_response_t response,
1298 ipmi_data_len_t dataLen,
1299 ipmi_context_t context)
1300{
1301 CfgHostSerialReq* req = reinterpret_cast<CfgHostSerialReq*>(request);
1302 uint8_t* resp = reinterpret_cast<uint8_t*>(response);
1303
1304 if (*dataLen == 0)
1305 {
1306 phosphor::logging::log<phosphor::logging::level::ERR>(
1307 "CfgHostSerial: invalid input len!",
1308 phosphor::logging::entry("LEN=%d", *dataLen));
1309 return IPMI_CC_REQ_DATA_LEN_INVALID;
1310 }
1311
1312 switch (req->command)
1313 {
1314 case getHostSerialCfgCmd:
1315 {
1316 if (*dataLen != 1)
1317 {
1318 phosphor::logging::log<phosphor::logging::level::ERR>(
1319 "CfgHostSerial: invalid input len!");
1320 *dataLen = 0;
1321 return IPMI_CC_REQ_DATA_LEN_INVALID;
1322 }
1323
1324 *dataLen = 0;
1325
1326 boost::process::ipstream is;
1327 std::vector<std::string> data;
1328 std::string line;
1329 boost::process::child c1(fwGetEnvCmd, "-n", fwHostSerailCfgEnvName,
1330 boost::process::std_out > is);
1331
1332 while (c1.running() && std::getline(is, line) && !line.empty())
1333 {
1334 data.push_back(line);
1335 }
1336
1337 c1.wait();
1338 if (c1.exit_code())
1339 {
1340 phosphor::logging::log<phosphor::logging::level::ERR>(
1341 "CfgHostSerial:: error on execute",
1342 phosphor::logging::entry("EXECUTE=%s", fwSetEnvCmd));
1343 // Using the default value
1344 *resp = 0;
1345 }
1346 else
1347 {
1348 if (data.size() != 1)
1349 {
1350 phosphor::logging::log<phosphor::logging::level::ERR>(
1351 "CfgHostSerial:: error on read env");
1352 return IPMI_CC_UNSPECIFIED_ERROR;
1353 }
1354 try
1355 {
1356 unsigned long tmp = std::stoul(data[0]);
1357 if (tmp > std::numeric_limits<uint8_t>::max())
1358 {
1359 throw std::out_of_range("Out of range");
1360 }
1361 *resp = static_cast<uint8_t>(tmp);
1362 }
1363 catch (const std::invalid_argument& e)
1364 {
1365 phosphor::logging::log<phosphor::logging::level::ERR>(
1366 "invalid config ",
1367 phosphor::logging::entry("ERR=%s", e.what()));
1368 return IPMI_CC_UNSPECIFIED_ERROR;
1369 }
1370 catch (const std::out_of_range& e)
1371 {
1372 phosphor::logging::log<phosphor::logging::level::ERR>(
1373 "out_of_range config ",
1374 phosphor::logging::entry("ERR=%s", e.what()));
1375 return IPMI_CC_UNSPECIFIED_ERROR;
1376 }
1377 }
1378
1379 *dataLen = 1;
1380 break;
1381 }
1382 case setHostSerialCfgCmd:
1383 {
1384 if (*dataLen != sizeof(CfgHostSerialReq))
1385 {
1386 phosphor::logging::log<phosphor::logging::level::ERR>(
1387 "CfgHostSerial: invalid input len!");
1388 *dataLen = 0;
1389 return IPMI_CC_REQ_DATA_LEN_INVALID;
1390 }
1391
1392 *dataLen = 0;
1393
1394 if (req->parameter > HostSerialCfgParamMax)
1395 {
1396 phosphor::logging::log<phosphor::logging::level::ERR>(
1397 "CfgHostSerial: invalid input!");
1398 return IPMI_CC_INVALID_FIELD_REQUEST;
1399 }
1400
1401 boost::process::child c1(fwSetEnvCmd, fwHostSerailCfgEnvName,
1402 std::to_string(req->parameter));
1403
1404 c1.wait();
1405 if (c1.exit_code())
1406 {
1407 phosphor::logging::log<phosphor::logging::level::ERR>(
1408 "CfgHostSerial:: error on execute",
1409 phosphor::logging::entry("EXECUTE=%s", fwGetEnvCmd));
1410 return IPMI_CC_UNSPECIFIED_ERROR;
1411 }
1412 break;
1413 }
1414 default:
1415 phosphor::logging::log<phosphor::logging::level::ERR>(
1416 "CfgHostSerial: invalid input!");
1417 *dataLen = 0;
1418 return IPMI_CC_INVALID_FIELD_REQUEST;
1419 }
1420
1421 return IPMI_CC_OK;
1422}
1423
James Feist91244a62019-02-19 15:04:54 -08001424constexpr const char* thermalModeInterface =
1425 "xyz.openbmc_project.Control.ThermalMode";
1426constexpr const char* thermalModePath =
1427 "/xyz/openbmc_project/control/thermal_mode";
1428
1429bool getFanProfileInterface(
1430 sdbusplus::bus::bus& bus,
1431 boost::container::flat_map<
1432 std::string, std::variant<std::vector<std::string>, std::string>>& resp)
1433{
1434 auto call = bus.new_method_call(settingsBusName, thermalModePath, PROP_INTF,
1435 "GetAll");
1436 call.append(thermalModeInterface);
1437 try
1438 {
1439 auto data = bus.call(call);
1440 data.read(resp);
1441 }
1442 catch (sdbusplus::exception_t& e)
1443 {
1444 phosphor::logging::log<phosphor::logging::level::ERR>(
1445 "getFanProfileInterface: can't get thermal mode!",
1446 phosphor::logging::entry("ERR=%s", e.what()));
1447 return false;
1448 }
1449 return true;
1450}
1451
anil kumar appanaf945eee2019-09-25 23:29:11 +00001452/**@brief implements the OEM set fan config.
1453 * @param selectedFanProfile - fan profile to enable
1454 * @param reserved1
1455 * @param performanceMode - Performance/Acoustic mode
1456 * @param reserved2
1457 * @param setPerformanceMode - set Performance/Acoustic mode
1458 * @param setFanProfile - set fan profile
1459 *
1460 * @return IPMI completion code.
1461 **/
1462ipmi::RspType<> ipmiOEMSetFanConfig(uint8_t selectedFanProfile,
1463
1464 uint2_t reserved1, bool performanceMode,
1465 uint3_t reserved2, bool setPerformanceMode,
Joshi-Mansi619186d2020-01-27 19:16:03 +05301466 bool setFanProfile,
1467 std::optional<uint8_t> dimmGroupId,
1468 std::optional<uint32_t> dimmPresenceBitmap)
James Feist91244a62019-02-19 15:04:54 -08001469{
anil kumar appanaf945eee2019-09-25 23:29:11 +00001470 if (reserved1 || reserved2)
James Feist91244a62019-02-19 15:04:54 -08001471 {
anil kumar appanaf945eee2019-09-25 23:29:11 +00001472 return ipmi::responseInvalidFieldRequest();
James Feist91244a62019-02-19 15:04:54 -08001473 }
Joshi-Mansi619186d2020-01-27 19:16:03 +05301474
1475 if (dimmGroupId)
1476 {
1477 if (*dimmGroupId >= maxCPUNum)
1478 {
1479 return ipmi::responseInvalidFieldRequest();
1480 }
1481 if (!cpuPresent("CPU_" + std::to_string(*dimmGroupId + 1)))
1482 {
1483 return ipmi::responseInvalidFieldRequest();
1484 }
1485 }
1486
James Feist91244a62019-02-19 15:04:54 -08001487 // todo: tell bios to only send first 2 bytes
James Feist91244a62019-02-19 15:04:54 -08001488 boost::container::flat_map<
1489 std::string, std::variant<std::vector<std::string>, std::string>>
1490 profileData;
Vernon Mauery15419dd2019-05-24 09:40:30 -07001491 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
1492 if (!getFanProfileInterface(*dbus, profileData))
James Feist91244a62019-02-19 15:04:54 -08001493 {
anil kumar appanaf945eee2019-09-25 23:29:11 +00001494 return ipmi::responseUnspecifiedError();
James Feist91244a62019-02-19 15:04:54 -08001495 }
1496
1497 std::vector<std::string>* supported =
1498 std::get_if<std::vector<std::string>>(&profileData["Supported"]);
1499 if (supported == nullptr)
1500 {
anil kumar appanaf945eee2019-09-25 23:29:11 +00001501 return ipmi::responseInvalidFieldRequest();
James Feist91244a62019-02-19 15:04:54 -08001502 }
1503 std::string mode;
anil kumar appanaf945eee2019-09-25 23:29:11 +00001504 if (setPerformanceMode)
James Feist91244a62019-02-19 15:04:54 -08001505 {
James Feist91244a62019-02-19 15:04:54 -08001506 if (performanceMode)
1507 {
1508
1509 if (std::find(supported->begin(), supported->end(),
1510 "Performance") != supported->end())
1511 {
1512 mode = "Performance";
1513 }
1514 }
1515 else
1516 {
James Feist91244a62019-02-19 15:04:54 -08001517 if (std::find(supported->begin(), supported->end(), "Acoustic") !=
1518 supported->end())
1519 {
1520 mode = "Acoustic";
1521 }
1522 }
1523 if (mode.empty())
1524 {
anil kumar appanaf945eee2019-09-25 23:29:11 +00001525 return ipmi::responseInvalidFieldRequest();
James Feist91244a62019-02-19 15:04:54 -08001526 }
anil kumar appanaf945eee2019-09-25 23:29:11 +00001527
1528 try
1529 {
1530 setDbusProperty(*dbus, settingsBusName, thermalModePath,
1531 thermalModeInterface, "Current", mode);
1532 }
1533 catch (sdbusplus::exception_t& e)
1534 {
1535 phosphor::logging::log<phosphor::logging::level::ERR>(
1536 "ipmiOEMSetFanConfig: can't set thermal mode!",
1537 phosphor::logging::entry("EXCEPTION=%s", e.what()));
1538 return ipmi::responseResponseError();
1539 }
James Feist91244a62019-02-19 15:04:54 -08001540 }
1541
anil kumar appanaf945eee2019-09-25 23:29:11 +00001542 return ipmi::responseSuccess();
James Feist91244a62019-02-19 15:04:54 -08001543}
1544
James Feist5b693632019-07-09 09:06:09 -07001545ipmi::RspType<uint8_t, // profile support map
1546 uint8_t, // fan control profile enable
1547 uint8_t, // flags
1548 uint32_t // dimm presence bit map
1549 >
1550 ipmiOEMGetFanConfig(uint8_t dimmGroupId)
James Feist91244a62019-02-19 15:04:54 -08001551{
Joshi-Mansi36f05ce2020-01-14 14:29:34 +05301552 if (dimmGroupId >= maxCPUNum)
1553 {
1554 return ipmi::responseInvalidFieldRequest();
1555 }
1556
1557 bool cpuStatus = cpuPresent("CPU_" + std::to_string(dimmGroupId + 1));
1558
1559 if (!cpuStatus)
1560 {
1561 return ipmi::responseInvalidFieldRequest();
1562 }
1563
James Feist91244a62019-02-19 15:04:54 -08001564 boost::container::flat_map<
1565 std::string, std::variant<std::vector<std::string>, std::string>>
1566 profileData;
1567
Vernon Mauery15419dd2019-05-24 09:40:30 -07001568 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
1569 if (!getFanProfileInterface(*dbus, profileData))
James Feist91244a62019-02-19 15:04:54 -08001570 {
James Feist5b693632019-07-09 09:06:09 -07001571 return ipmi::responseResponseError();
James Feist91244a62019-02-19 15:04:54 -08001572 }
1573
1574 std::string* current = std::get_if<std::string>(&profileData["Current"]);
1575
1576 if (current == nullptr)
1577 {
1578 phosphor::logging::log<phosphor::logging::level::ERR>(
1579 "ipmiOEMGetFanConfig: can't get current mode!");
James Feist5b693632019-07-09 09:06:09 -07001580 return ipmi::responseResponseError();
James Feist91244a62019-02-19 15:04:54 -08001581 }
1582 bool performance = (*current == "Performance");
1583
James Feist5b693632019-07-09 09:06:09 -07001584 uint8_t flags = 0;
James Feist91244a62019-02-19 15:04:54 -08001585 if (performance)
1586 {
James Feist5b693632019-07-09 09:06:09 -07001587 flags |= 1 << 2;
James Feist91244a62019-02-19 15:04:54 -08001588 }
1589
jayaprakash Mutyala4b1552d2020-02-11 12:07:29 +00001590 constexpr uint8_t fanControlDefaultProfile = 0x80;
1591 constexpr uint8_t fanControlProfileState = 0x00;
1592 constexpr uint32_t dimmPresenceBitmap = 0x00;
1593
1594 return ipmi::responseSuccess(fanControlDefaultProfile,
1595 fanControlProfileState, flags,
1596 dimmPresenceBitmap);
James Feist91244a62019-02-19 15:04:54 -08001597}
James Feist5f957ca2019-03-14 15:33:55 -07001598constexpr const char* cfmLimitSettingPath =
1599 "/xyz/openbmc_project/control/cfm_limit";
1600constexpr const char* cfmLimitIface = "xyz.openbmc_project.Control.CFMLimit";
James Feistfaa4f222019-03-21 16:21:55 -07001601constexpr const size_t legacyExitAirSensorNumber = 0x2e;
James Feist09f6b602019-08-08 11:30:03 -07001602constexpr const size_t legacyPCHSensorNumber = 0x22;
1603constexpr const char* exitAirPathName = "Exit_Air";
1604constexpr const char* pchPathName = "SSB_Temp";
James Feistacc8a4e2019-04-02 14:23:57 -07001605constexpr const char* pidConfigurationIface =
1606 "xyz.openbmc_project.Configuration.Pid";
James Feistfaa4f222019-03-21 16:21:55 -07001607
James Feist09f6b602019-08-08 11:30:03 -07001608static std::string getConfigPath(const std::string& name)
James Feistfaa4f222019-03-21 16:21:55 -07001609{
Vernon Mauery15419dd2019-05-24 09:40:30 -07001610 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
James Feistfaa4f222019-03-21 16:21:55 -07001611 auto method =
Vernon Mauery15419dd2019-05-24 09:40:30 -07001612 dbus->new_method_call("xyz.openbmc_project.ObjectMapper",
1613 "/xyz/openbmc_project/object_mapper",
1614 "xyz.openbmc_project.ObjectMapper", "GetSubTree");
James Feistfaa4f222019-03-21 16:21:55 -07001615
James Feistacc8a4e2019-04-02 14:23:57 -07001616 method.append("/", 0, std::array<const char*, 1>{pidConfigurationIface});
James Feistfaa4f222019-03-21 16:21:55 -07001617 std::string path;
1618 GetSubTreeType resp;
1619 try
1620 {
Vernon Mauery15419dd2019-05-24 09:40:30 -07001621 auto reply = dbus->call(method);
James Feistfaa4f222019-03-21 16:21:55 -07001622 reply.read(resp);
1623 }
1624 catch (sdbusplus::exception_t&)
1625 {
1626 phosphor::logging::log<phosphor::logging::level::ERR>(
1627 "ipmiOEMGetFscParameter: mapper error");
1628 };
James Feist09f6b602019-08-08 11:30:03 -07001629 auto config =
1630 std::find_if(resp.begin(), resp.end(), [&name](const auto& pair) {
1631 return pair.first.find(name) != std::string::npos;
1632 });
James Feistfaa4f222019-03-21 16:21:55 -07001633 if (config != resp.end())
1634 {
1635 path = std::move(config->first);
1636 }
1637 return path;
1638}
James Feist5f957ca2019-03-14 15:33:55 -07001639
James Feistacc8a4e2019-04-02 14:23:57 -07001640// flat map to make alphabetical
1641static boost::container::flat_map<std::string, PropertyMap> getPidConfigs()
1642{
1643 boost::container::flat_map<std::string, PropertyMap> ret;
Vernon Mauery15419dd2019-05-24 09:40:30 -07001644 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
James Feistacc8a4e2019-04-02 14:23:57 -07001645 auto method =
Vernon Mauery15419dd2019-05-24 09:40:30 -07001646 dbus->new_method_call("xyz.openbmc_project.ObjectMapper",
1647 "/xyz/openbmc_project/object_mapper",
1648 "xyz.openbmc_project.ObjectMapper", "GetSubTree");
James Feistacc8a4e2019-04-02 14:23:57 -07001649
1650 method.append("/", 0, std::array<const char*, 1>{pidConfigurationIface});
1651 GetSubTreeType resp;
1652
1653 try
1654 {
Vernon Mauery15419dd2019-05-24 09:40:30 -07001655 auto reply = dbus->call(method);
James Feistacc8a4e2019-04-02 14:23:57 -07001656 reply.read(resp);
1657 }
1658 catch (sdbusplus::exception_t&)
1659 {
1660 phosphor::logging::log<phosphor::logging::level::ERR>(
1661 "getFanConfigPaths: mapper error");
1662 };
1663 for (const auto& [path, objects] : resp)
1664 {
1665 if (objects.empty())
1666 {
1667 continue; // should be impossible
1668 }
Zhu, Yungebe560b02019-04-21 21:19:21 -04001669
1670 try
1671 {
Vernon Mauery15419dd2019-05-24 09:40:30 -07001672 ret.emplace(path,
1673 getAllDbusProperties(*dbus, objects[0].first, path,
1674 pidConfigurationIface));
Zhu, Yungebe560b02019-04-21 21:19:21 -04001675 }
1676 catch (sdbusplus::exception_t& e)
1677 {
1678 phosphor::logging::log<phosphor::logging::level::ERR>(
1679 "getPidConfigs: can't get DbusProperties!",
1680 phosphor::logging::entry("ERR=%s", e.what()));
1681 }
James Feistacc8a4e2019-04-02 14:23:57 -07001682 }
1683 return ret;
1684}
1685
1686ipmi::RspType<uint8_t> ipmiOEMGetFanSpeedOffset(void)
1687{
1688 boost::container::flat_map<std::string, PropertyMap> data = getPidConfigs();
1689 if (data.empty())
1690 {
1691 return ipmi::responseResponseError();
1692 }
1693 uint8_t minOffset = std::numeric_limits<uint8_t>::max();
1694 for (const auto& [_, pid] : data)
1695 {
1696 auto findClass = pid.find("Class");
1697 if (findClass == pid.end())
1698 {
1699 phosphor::logging::log<phosphor::logging::level::ERR>(
1700 "ipmiOEMGetFscParameter: found illegal pid "
1701 "configurations");
1702 return ipmi::responseResponseError();
1703 }
1704 std::string type = std::get<std::string>(findClass->second);
1705 if (type == "fan")
1706 {
1707 auto findOutLimit = pid.find("OutLimitMin");
1708 if (findOutLimit == pid.end())
1709 {
1710 phosphor::logging::log<phosphor::logging::level::ERR>(
1711 "ipmiOEMGetFscParameter: found illegal pid "
1712 "configurations");
1713 return ipmi::responseResponseError();
1714 }
1715 // get the min out of all the offsets
1716 minOffset = std::min(
1717 minOffset,
1718 static_cast<uint8_t>(std::get<double>(findOutLimit->second)));
1719 }
1720 }
1721 if (minOffset == std::numeric_limits<uint8_t>::max())
1722 {
1723 phosphor::logging::log<phosphor::logging::level::ERR>(
1724 "ipmiOEMGetFscParameter: found no fan configurations!");
1725 return ipmi::responseResponseError();
1726 }
1727
1728 return ipmi::responseSuccess(minOffset);
1729}
1730
1731ipmi::RspType<> ipmiOEMSetFanSpeedOffset(uint8_t offset)
1732{
1733 boost::container::flat_map<std::string, PropertyMap> data = getPidConfigs();
1734 if (data.empty())
1735 {
1736
1737 phosphor::logging::log<phosphor::logging::level::ERR>(
1738 "ipmiOEMSetFanSpeedOffset: found no pid configurations!");
1739 return ipmi::responseResponseError();
1740 }
1741
Vernon Mauery15419dd2019-05-24 09:40:30 -07001742 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
James Feistacc8a4e2019-04-02 14:23:57 -07001743 bool found = false;
1744 for (const auto& [path, pid] : data)
1745 {
1746 auto findClass = pid.find("Class");
1747 if (findClass == pid.end())
1748 {
1749
1750 phosphor::logging::log<phosphor::logging::level::ERR>(
1751 "ipmiOEMSetFanSpeedOffset: found illegal pid "
1752 "configurations");
1753 return ipmi::responseResponseError();
1754 }
1755 std::string type = std::get<std::string>(findClass->second);
1756 if (type == "fan")
1757 {
1758 auto findOutLimit = pid.find("OutLimitMin");
1759 if (findOutLimit == pid.end())
1760 {
1761
1762 phosphor::logging::log<phosphor::logging::level::ERR>(
1763 "ipmiOEMSetFanSpeedOffset: found illegal pid "
1764 "configurations");
1765 return ipmi::responseResponseError();
1766 }
Vernon Mauery15419dd2019-05-24 09:40:30 -07001767 ipmi::setDbusProperty(*dbus, "xyz.openbmc_project.EntityManager",
James Feistacc8a4e2019-04-02 14:23:57 -07001768 path, pidConfigurationIface, "OutLimitMin",
1769 static_cast<double>(offset));
1770 found = true;
1771 }
1772 }
1773 if (!found)
1774 {
1775 phosphor::logging::log<phosphor::logging::level::ERR>(
1776 "ipmiOEMSetFanSpeedOffset: set no fan offsets");
1777 return ipmi::responseResponseError();
1778 }
1779
1780 return ipmi::responseSuccess();
1781}
1782
1783ipmi::RspType<> ipmiOEMSetFscParameter(uint8_t command, uint8_t param1,
1784 uint8_t param2)
James Feist5f957ca2019-03-14 15:33:55 -07001785{
1786 constexpr const size_t disableLimiting = 0x0;
1787
Vernon Mauery15419dd2019-05-24 09:40:30 -07001788 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
James Feistacc8a4e2019-04-02 14:23:57 -07001789 if (command == static_cast<uint8_t>(setFscParamFlags::tcontrol))
James Feist5f957ca2019-03-14 15:33:55 -07001790 {
James Feist09f6b602019-08-08 11:30:03 -07001791 std::string pathName;
James Feistacc8a4e2019-04-02 14:23:57 -07001792 if (param1 == legacyExitAirSensorNumber)
James Feistfaa4f222019-03-21 16:21:55 -07001793 {
James Feist09f6b602019-08-08 11:30:03 -07001794 pathName = exitAirPathName;
1795 }
1796 else if (param1 == legacyPCHSensorNumber)
1797 {
1798 pathName = pchPathName;
James Feistfaa4f222019-03-21 16:21:55 -07001799 }
1800 else
1801 {
James Feistacc8a4e2019-04-02 14:23:57 -07001802 return ipmi::responseParmOutOfRange();
James Feistfaa4f222019-03-21 16:21:55 -07001803 }
James Feist09f6b602019-08-08 11:30:03 -07001804 std::string path = getConfigPath(pathName);
1805 ipmi::setDbusProperty(*dbus, "xyz.openbmc_project.EntityManager", path,
1806 pidConfigurationIface, "SetPoint",
1807 static_cast<double>(param2));
1808 return ipmi::responseSuccess();
James Feistfaa4f222019-03-21 16:21:55 -07001809 }
James Feistacc8a4e2019-04-02 14:23:57 -07001810 else if (command == static_cast<uint8_t>(setFscParamFlags::cfm))
James Feist5f957ca2019-03-14 15:33:55 -07001811 {
James Feistacc8a4e2019-04-02 14:23:57 -07001812 uint16_t cfm = param1 | (static_cast<uint16_t>(param2) << 8);
James Feist5f957ca2019-03-14 15:33:55 -07001813
1814 // must be greater than 50 based on eps
1815 if (cfm < 50 && cfm != disableLimiting)
1816 {
James Feistacc8a4e2019-04-02 14:23:57 -07001817 return ipmi::responseParmOutOfRange();
James Feist5f957ca2019-03-14 15:33:55 -07001818 }
1819
1820 try
1821 {
Vernon Mauery15419dd2019-05-24 09:40:30 -07001822 ipmi::setDbusProperty(*dbus, settingsBusName, cfmLimitSettingPath,
James Feist5f957ca2019-03-14 15:33:55 -07001823 cfmLimitIface, "Limit",
1824 static_cast<double>(cfm));
1825 }
1826 catch (sdbusplus::exception_t& e)
1827 {
1828 phosphor::logging::log<phosphor::logging::level::ERR>(
1829 "ipmiOEMSetFscParameter: can't set cfm setting!",
1830 phosphor::logging::entry("ERR=%s", e.what()));
James Feistacc8a4e2019-04-02 14:23:57 -07001831 return ipmi::responseResponseError();
James Feist5f957ca2019-03-14 15:33:55 -07001832 }
James Feistacc8a4e2019-04-02 14:23:57 -07001833 return ipmi::responseSuccess();
1834 }
1835 else if (command == static_cast<uint8_t>(setFscParamFlags::maxPwm))
1836 {
1837 constexpr const size_t maxDomainCount = 8;
1838 uint8_t requestedDomainMask = param1;
1839 boost::container::flat_map data = getPidConfigs();
1840 if (data.empty())
1841 {
1842
1843 phosphor::logging::log<phosphor::logging::level::ERR>(
1844 "ipmiOEMSetFscParameter: found no pid configurations!");
1845 return ipmi::responseResponseError();
1846 }
1847 size_t count = 0;
1848 for (const auto& [path, pid] : data)
1849 {
1850 auto findClass = pid.find("Class");
1851 if (findClass == pid.end())
1852 {
1853
1854 phosphor::logging::log<phosphor::logging::level::ERR>(
1855 "ipmiOEMSetFscParameter: found illegal pid "
1856 "configurations");
1857 return ipmi::responseResponseError();
1858 }
1859 std::string type = std::get<std::string>(findClass->second);
1860 if (type == "fan")
1861 {
1862 if (requestedDomainMask & (1 << count))
1863 {
1864 ipmi::setDbusProperty(
Vernon Mauery15419dd2019-05-24 09:40:30 -07001865 *dbus, "xyz.openbmc_project.EntityManager", path,
James Feistacc8a4e2019-04-02 14:23:57 -07001866 pidConfigurationIface, "OutLimitMax",
1867 static_cast<double>(param2));
1868 }
1869 count++;
1870 }
1871 }
1872 return ipmi::responseSuccess();
James Feist5f957ca2019-03-14 15:33:55 -07001873 }
1874 else
1875 {
1876 // todo other command parts possibly
1877 // tcontrol is handled in peci now
1878 // fan speed offset not implemented yet
1879 // domain pwm limit not implemented
James Feistacc8a4e2019-04-02 14:23:57 -07001880 return ipmi::responseParmOutOfRange();
James Feist5f957ca2019-03-14 15:33:55 -07001881 }
1882}
1883
James Feistacc8a4e2019-04-02 14:23:57 -07001884ipmi::RspType<
1885 std::variant<uint8_t, std::array<uint8_t, 2>, std::array<uint16_t, 2>>>
1886 ipmiOEMGetFscParameter(uint8_t command, std::optional<uint8_t> param)
James Feist5f957ca2019-03-14 15:33:55 -07001887{
James Feist09f6b602019-08-08 11:30:03 -07001888 constexpr uint8_t legacyDefaultSetpoint = -128;
James Feist5f957ca2019-03-14 15:33:55 -07001889
Vernon Mauery15419dd2019-05-24 09:40:30 -07001890 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
James Feistacc8a4e2019-04-02 14:23:57 -07001891 if (command == static_cast<uint8_t>(setFscParamFlags::tcontrol))
James Feist5f957ca2019-03-14 15:33:55 -07001892 {
James Feistacc8a4e2019-04-02 14:23:57 -07001893 if (!param)
James Feistfaa4f222019-03-21 16:21:55 -07001894 {
James Feistacc8a4e2019-04-02 14:23:57 -07001895 return ipmi::responseReqDataLenInvalid();
James Feistfaa4f222019-03-21 16:21:55 -07001896 }
1897
James Feist09f6b602019-08-08 11:30:03 -07001898 std::string pathName;
1899
1900 if (*param == legacyExitAirSensorNumber)
1901 {
1902 pathName = exitAirPathName;
1903 }
1904 else if (*param == legacyPCHSensorNumber)
1905 {
1906 pathName = pchPathName;
1907 }
1908 else
James Feistfaa4f222019-03-21 16:21:55 -07001909 {
James Feistacc8a4e2019-04-02 14:23:57 -07001910 return ipmi::responseParmOutOfRange();
James Feistfaa4f222019-03-21 16:21:55 -07001911 }
James Feist09f6b602019-08-08 11:30:03 -07001912
1913 uint8_t setpoint = legacyDefaultSetpoint;
1914 std::string path = getConfigPath(pathName);
James Feistfaa4f222019-03-21 16:21:55 -07001915 if (path.size())
1916 {
Vernon Mauery15419dd2019-05-24 09:40:30 -07001917 Value val = ipmi::getDbusProperty(
1918 *dbus, "xyz.openbmc_project.EntityManager", path,
1919 pidConfigurationIface, "SetPoint");
James Feistfaa4f222019-03-21 16:21:55 -07001920 setpoint = std::floor(std::get<double>(val) + 0.5);
1921 }
1922
1923 // old implementation used to return the "default" and current, we
1924 // don't make the default readily available so just make both the
1925 // same
James Feistfaa4f222019-03-21 16:21:55 -07001926
James Feistacc8a4e2019-04-02 14:23:57 -07001927 return ipmi::responseSuccess(
1928 std::array<uint8_t, 2>{setpoint, setpoint});
James Feistfaa4f222019-03-21 16:21:55 -07001929 }
James Feistacc8a4e2019-04-02 14:23:57 -07001930 else if (command == static_cast<uint8_t>(setFscParamFlags::maxPwm))
1931 {
1932 constexpr const size_t maxDomainCount = 8;
1933
1934 if (!param)
1935 {
1936 return ipmi::responseReqDataLenInvalid();
1937 }
1938 uint8_t requestedDomain = *param;
1939 if (requestedDomain >= maxDomainCount)
1940 {
1941 return ipmi::responseInvalidFieldRequest();
1942 }
1943
1944 boost::container::flat_map data = getPidConfigs();
1945 if (data.empty())
1946 {
1947 phosphor::logging::log<phosphor::logging::level::ERR>(
1948 "ipmiOEMGetFscParameter: found no pid configurations!");
1949 return ipmi::responseResponseError();
1950 }
1951 size_t count = 0;
1952 for (const auto& [_, pid] : data)
1953 {
1954 auto findClass = pid.find("Class");
1955 if (findClass == pid.end())
1956 {
1957 phosphor::logging::log<phosphor::logging::level::ERR>(
1958 "ipmiOEMGetFscParameter: found illegal pid "
1959 "configurations");
1960 return ipmi::responseResponseError();
1961 }
1962 std::string type = std::get<std::string>(findClass->second);
1963 if (type == "fan")
1964 {
1965 if (requestedDomain == count)
1966 {
1967 auto findOutLimit = pid.find("OutLimitMax");
1968 if (findOutLimit == pid.end())
1969 {
1970 phosphor::logging::log<phosphor::logging::level::ERR>(
1971 "ipmiOEMGetFscParameter: found illegal pid "
1972 "configurations");
1973 return ipmi::responseResponseError();
1974 }
1975
1976 return ipmi::responseSuccess(
1977 static_cast<uint8_t>(std::floor(
1978 std::get<double>(findOutLimit->second) + 0.5)));
1979 }
1980 else
1981 {
1982 count++;
1983 }
1984 }
1985 }
1986
1987 return ipmi::responseInvalidFieldRequest();
1988 }
1989 else if (command == static_cast<uint8_t>(setFscParamFlags::cfm))
James Feist5f957ca2019-03-14 15:33:55 -07001990 {
1991
1992 /*
1993 DataLen should be 1, but host is sending us an extra bit. As the
James Feistacc8a4e2019-04-02 14:23:57 -07001994 previous behavior didn't seem to prevent this, ignore the check for
1995 now.
James Feist5f957ca2019-03-14 15:33:55 -07001996
James Feistacc8a4e2019-04-02 14:23:57 -07001997 if (param)
James Feist5f957ca2019-03-14 15:33:55 -07001998 {
1999 phosphor::logging::log<phosphor::logging::level::ERR>(
2000 "ipmiOEMGetFscParameter: invalid input len!");
James Feist5f957ca2019-03-14 15:33:55 -07002001 return IPMI_CC_REQ_DATA_LEN_INVALID;
2002 }
2003 */
2004 Value cfmLimit;
2005 Value cfmMaximum;
2006 try
2007 {
Vernon Mauery15419dd2019-05-24 09:40:30 -07002008 cfmLimit = ipmi::getDbusProperty(*dbus, settingsBusName,
James Feist5f957ca2019-03-14 15:33:55 -07002009 cfmLimitSettingPath, cfmLimitIface,
2010 "Limit");
2011 cfmMaximum = ipmi::getDbusProperty(
Vernon Mauery15419dd2019-05-24 09:40:30 -07002012 *dbus, "xyz.openbmc_project.ExitAirTempSensor",
James Feist5f957ca2019-03-14 15:33:55 -07002013 "/xyz/openbmc_project/control/MaxCFM", cfmLimitIface, "Limit");
2014 }
2015 catch (sdbusplus::exception_t& e)
2016 {
2017 phosphor::logging::log<phosphor::logging::level::ERR>(
James Feistacc8a4e2019-04-02 14:23:57 -07002018 "ipmiOEMGetFscParameter: can't get cfm setting!",
James Feist5f957ca2019-03-14 15:33:55 -07002019 phosphor::logging::entry("ERR=%s", e.what()));
James Feistacc8a4e2019-04-02 14:23:57 -07002020 return ipmi::responseResponseError();
James Feist5f957ca2019-03-14 15:33:55 -07002021 }
2022
James Feistacc8a4e2019-04-02 14:23:57 -07002023 double cfmMax = std::get<double>(cfmMaximum);
2024 double cfmLim = std::get<double>(cfmLimit);
James Feist5f957ca2019-03-14 15:33:55 -07002025
James Feistacc8a4e2019-04-02 14:23:57 -07002026 cfmLim = std::floor(cfmLim + 0.5);
2027 cfmMax = std::floor(cfmMax + 0.5);
2028 uint16_t cfmLimResp = static_cast<uint16_t>(cfmLim);
2029 uint16_t cfmMaxResp = static_cast<uint16_t>(cfmMax);
James Feist5f957ca2019-03-14 15:33:55 -07002030
James Feistacc8a4e2019-04-02 14:23:57 -07002031 return ipmi::responseSuccess(
2032 std::array<uint16_t, 2>{cfmLimResp, cfmMaxResp});
James Feist5f957ca2019-03-14 15:33:55 -07002033 }
James Feistacc8a4e2019-04-02 14:23:57 -07002034
James Feist5f957ca2019-03-14 15:33:55 -07002035 else
2036 {
2037 // todo other command parts possibly
James Feist5f957ca2019-03-14 15:33:55 -07002038 // domain pwm limit not implemented
James Feistacc8a4e2019-04-02 14:23:57 -07002039 return ipmi::responseParmOutOfRange();
James Feist5f957ca2019-03-14 15:33:55 -07002040 }
2041}
2042
Cheng C Yang773703a2019-08-15 09:41:11 +08002043using crConfigVariant =
2044 std::variant<bool, uint8_t, uint32_t, std::vector<uint8_t>, std::string>;
2045
2046int setCRConfig(ipmi::Context::ptr ctx, const std::string& property,
2047 const crConfigVariant& value,
2048 std::chrono::microseconds timeout = ipmi::IPMI_DBUS_TIMEOUT)
2049{
2050 boost::system::error_code ec;
2051 ctx->bus->yield_method_call<void>(
James Feist28c72902019-09-16 10:34:07 -07002052 ctx->yield, ec, "xyz.openbmc_project.Settings",
Cheng C Yang773703a2019-08-15 09:41:11 +08002053 "/xyz/openbmc_project/control/power_supply_redundancy",
2054 "org.freedesktop.DBus.Properties", "Set",
2055 "xyz.openbmc_project.Control.PowerSupplyRedundancy", property, value);
2056 if (ec)
2057 {
2058 phosphor::logging::log<phosphor::logging::level::ERR>(
2059 "Failed to set dbus property to cold redundancy");
2060 return -1;
2061 }
2062
2063 return 0;
2064}
2065
2066int getCRConfig(ipmi::Context::ptr ctx, const std::string& property,
2067 crConfigVariant& value,
Yong Li19445ab2019-12-20 18:25:29 +08002068 const std::string& service = "xyz.openbmc_project.Settings",
Cheng C Yang773703a2019-08-15 09:41:11 +08002069 std::chrono::microseconds timeout = ipmi::IPMI_DBUS_TIMEOUT)
2070{
2071 boost::system::error_code ec;
2072 value = ctx->bus->yield_method_call<crConfigVariant>(
Yong Li19445ab2019-12-20 18:25:29 +08002073 ctx->yield, ec, service,
Cheng C Yang773703a2019-08-15 09:41:11 +08002074 "/xyz/openbmc_project/control/power_supply_redundancy",
2075 "org.freedesktop.DBus.Properties", "Get",
2076 "xyz.openbmc_project.Control.PowerSupplyRedundancy", property);
2077 if (ec)
2078 {
2079 phosphor::logging::log<phosphor::logging::level::ERR>(
2080 "Failed to get dbus property to cold redundancy");
2081 return -1;
2082 }
2083 return 0;
2084}
2085
2086uint8_t getPSUCount(void)
2087{
2088 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
2089 ipmi::Value num;
2090 try
2091 {
2092 num = ipmi::getDbusProperty(
2093 *dbus, "xyz.openbmc_project.PSURedundancy",
2094 "/xyz/openbmc_project/control/power_supply_redundancy",
2095 "xyz.openbmc_project.Control.PowerSupplyRedundancy", "PSUNumber");
2096 }
2097 catch (sdbusplus::exception_t& e)
2098 {
2099 phosphor::logging::log<phosphor::logging::level::ERR>(
2100 "Failed to get PSUNumber property from dbus interface");
2101 return 0;
2102 }
2103 uint8_t* pNum = std::get_if<uint8_t>(&num);
2104 if (!pNum)
2105 {
2106 phosphor::logging::log<phosphor::logging::level::ERR>(
2107 "Error to get PSU Number");
2108 return 0;
2109 }
2110 return *pNum;
2111}
2112
2113bool validateCRAlgo(std::vector<uint8_t>& conf, uint8_t num)
2114{
2115 if (conf.size() < num)
2116 {
2117 phosphor::logging::log<phosphor::logging::level::ERR>(
2118 "Invalid PSU Ranking");
2119 return false;
2120 }
2121 std::set<uint8_t> confSet;
2122 for (uint8_t i = 0; i < num; i++)
2123 {
2124 if (conf[i] > num)
2125 {
2126 phosphor::logging::log<phosphor::logging::level::ERR>(
2127 "PSU Ranking is larger than current PSU number");
2128 return false;
2129 }
2130 confSet.emplace(conf[i]);
2131 }
2132
2133 if (confSet.size() != num)
2134 {
2135 phosphor::logging::log<phosphor::logging::level::ERR>(
2136 "duplicate PSU Ranking");
2137 return false;
2138 }
2139 return true;
2140}
2141
2142enum class crParameter
2143{
2144 crStatus = 0,
2145 crFeature = 1,
2146 rotationFeature = 2,
2147 rotationAlgo = 3,
2148 rotationPeriod = 4,
Yong Li19445ab2019-12-20 18:25:29 +08002149 numOfPSU = 5,
2150 rotationRankOrderEffective = 6
Cheng C Yang773703a2019-08-15 09:41:11 +08002151};
2152
2153constexpr ipmi::Cc ccParameterNotSupported = 0x80;
2154static const constexpr uint32_t oneDay = 0x15180;
2155static const constexpr uint32_t oneMonth = 0xf53700;
2156static const constexpr uint8_t userSpecific = 0x01;
2157static const constexpr uint8_t crSetCompleted = 0;
2158ipmi::RspType<uint8_t> ipmiOEMSetCRConfig(ipmi::Context::ptr ctx,
2159 uint8_t parameter,
2160 ipmi::message::Payload& payload)
2161{
2162 switch (static_cast<crParameter>(parameter))
2163 {
2164 case crParameter::crFeature:
2165 {
2166 uint8_t param1;
2167 if (payload.unpack(param1) || !payload.fullyUnpacked())
2168 {
2169 return ipmi::responseReqDataLenInvalid();
2170 }
2171 // ColdRedundancy Enable can only be true or flase
2172 if (param1 > 1)
2173 {
2174 return ipmi::responseInvalidFieldRequest();
2175 }
2176 if (setCRConfig(ctx, "ColdRedundancyEnabled",
2177 static_cast<bool>(param1)))
2178 {
2179 return ipmi::responseResponseError();
2180 }
2181 break;
2182 }
2183 case crParameter::rotationFeature:
2184 {
2185 uint8_t param1;
2186 if (payload.unpack(param1) || !payload.fullyUnpacked())
2187 {
2188 return ipmi::responseReqDataLenInvalid();
2189 }
2190 // Rotation Enable can only be true or false
2191 if (param1 > 1)
2192 {
2193 return ipmi::responseInvalidFieldRequest();
2194 }
2195 if (setCRConfig(ctx, "RotationEnabled", static_cast<bool>(param1)))
2196 {
2197 return ipmi::responseResponseError();
2198 }
2199 break;
2200 }
2201 case crParameter::rotationAlgo:
2202 {
2203 // Rotation Algorithm can only be 0-BMC Specific or 1-User Specific
2204 std::string algoName;
2205 uint8_t param1;
2206 if (payload.unpack(param1))
2207 {
2208 return ipmi::responseReqDataLenInvalid();
2209 }
2210 switch (param1)
2211 {
2212 case 0:
2213 algoName = "xyz.openbmc_project.Control."
2214 "PowerSupplyRedundancy.Algo.bmcSpecific";
2215 break;
2216 case 1:
2217 algoName = "xyz.openbmc_project.Control."
2218 "PowerSupplyRedundancy.Algo.userSpecific";
2219 break;
2220 default:
2221 return ipmi::responseInvalidFieldRequest();
2222 }
2223 if (setCRConfig(ctx, "RotationAlgorithm", algoName))
2224 {
2225 return ipmi::responseResponseError();
2226 }
2227
2228 uint8_t numberOfPSU = getPSUCount();
2229 if (!numberOfPSU)
2230 {
2231 return ipmi::responseResponseError();
2232 }
2233 std::vector<uint8_t> rankOrder;
2234
2235 if (param1 == userSpecific)
2236 {
2237 if (payload.unpack(rankOrder) || !payload.fullyUnpacked())
2238 {
2239 ipmi::responseReqDataLenInvalid();
2240 }
Yong Li83315132019-10-23 17:42:24 +08002241 if (rankOrder.size() != numberOfPSU)
Cheng C Yang773703a2019-08-15 09:41:11 +08002242 {
2243 return ipmi::responseReqDataLenInvalid();
2244 }
2245
2246 if (!validateCRAlgo(rankOrder, numberOfPSU))
2247 {
2248 return ipmi::responseInvalidFieldRequest();
2249 }
2250 }
2251 else
2252 {
2253 if (rankOrder.size() > 0)
2254 {
2255 return ipmi::responseReqDataLenInvalid();
2256 }
2257 for (uint8_t i = 1; i <= numberOfPSU; i++)
2258 {
2259 rankOrder.emplace_back(i);
2260 }
2261 }
2262 if (setCRConfig(ctx, "RotationRankOrder", rankOrder))
2263 {
2264 return ipmi::responseResponseError();
2265 }
2266 break;
2267 }
2268 case crParameter::rotationPeriod:
2269 {
2270 // Minimum Rotation period is One day (86400 seconds) and Max
2271 // Rotation Period is 6 month (0xf53700 seconds)
2272 uint32_t period;
2273 if (payload.unpack(period) || !payload.fullyUnpacked())
2274 {
2275 return ipmi::responseReqDataLenInvalid();
2276 }
2277 if ((period < oneDay) || (period > oneMonth))
2278 {
2279 return ipmi::responseInvalidFieldRequest();
2280 }
2281 if (setCRConfig(ctx, "PeriodOfRotation", period))
2282 {
2283 return ipmi::responseResponseError();
2284 }
2285 break;
2286 }
2287 default:
2288 {
2289 return ipmi::response(ccParameterNotSupported);
2290 }
2291 }
2292
2293 // TODO Halfwidth needs to set SetInProgress
2294 if (setCRConfig(ctx, "ColdRedundancyStatus",
Cheng C Yange8cecdf2019-08-26 23:48:08 +08002295 std::string("xyz.openbmc_project.Control."
2296 "PowerSupplyRedundancy.Status.completed")))
Cheng C Yang773703a2019-08-15 09:41:11 +08002297 {
2298 return ipmi::responseResponseError();
2299 }
2300 return ipmi::responseSuccess(crSetCompleted);
2301}
2302
Yong Li83315132019-10-23 17:42:24 +08002303ipmi::RspType<uint8_t, std::variant<uint8_t, uint32_t, std::vector<uint8_t>>>
Cheng C Yang773703a2019-08-15 09:41:11 +08002304 ipmiOEMGetCRConfig(ipmi::Context::ptr ctx, uint8_t parameter)
2305{
2306 crConfigVariant value;
2307 switch (static_cast<crParameter>(parameter))
2308 {
2309 case crParameter::crStatus:
2310 {
2311 if (getCRConfig(ctx, "ColdRedundancyStatus", value))
2312 {
2313 return ipmi::responseResponseError();
2314 }
2315 std::string* pStatus = std::get_if<std::string>(&value);
2316 if (!pStatus)
2317 {
2318 phosphor::logging::log<phosphor::logging::level::ERR>(
2319 "Error to get ColdRedundancyStatus property");
2320 return ipmi::responseResponseError();
2321 }
2322 namespace server = sdbusplus::xyz::openbmc_project::Control::server;
2323 auto status =
2324 server::PowerSupplyRedundancy::convertStatusFromString(
2325 *pStatus);
2326 switch (status)
2327 {
2328 case server::PowerSupplyRedundancy::Status::inProgress:
Cheng C Yangf41e3342019-09-10 04:47:23 +08002329 return ipmi::responseSuccess(parameter,
2330 static_cast<uint8_t>(0));
Cheng C Yang773703a2019-08-15 09:41:11 +08002331
2332 case server::PowerSupplyRedundancy::Status::completed:
Cheng C Yangf41e3342019-09-10 04:47:23 +08002333 return ipmi::responseSuccess(parameter,
2334 static_cast<uint8_t>(1));
Cheng C Yang773703a2019-08-15 09:41:11 +08002335 default:
2336 phosphor::logging::log<phosphor::logging::level::ERR>(
2337 "Error to get valid status");
2338 return ipmi::responseResponseError();
2339 }
2340 }
2341 case crParameter::crFeature:
2342 {
2343 if (getCRConfig(ctx, "ColdRedundancyEnabled", value))
2344 {
2345 return ipmi::responseResponseError();
2346 }
2347 bool* pResponse = std::get_if<bool>(&value);
2348 if (!pResponse)
2349 {
2350 phosphor::logging::log<phosphor::logging::level::ERR>(
2351 "Error to get ColdRedundancyEnable property");
2352 return ipmi::responseResponseError();
2353 }
2354
Cheng C Yangf41e3342019-09-10 04:47:23 +08002355 return ipmi::responseSuccess(parameter,
2356 static_cast<uint8_t>(*pResponse));
Cheng C Yang773703a2019-08-15 09:41:11 +08002357 }
2358 case crParameter::rotationFeature:
2359 {
2360 if (getCRConfig(ctx, "RotationEnabled", value))
2361 {
2362 return ipmi::responseResponseError();
2363 }
2364 bool* pResponse = std::get_if<bool>(&value);
2365 if (!pResponse)
2366 {
2367 phosphor::logging::log<phosphor::logging::level::ERR>(
2368 "Error to get RotationEnabled property");
2369 return ipmi::responseResponseError();
2370 }
Cheng C Yangf41e3342019-09-10 04:47:23 +08002371 return ipmi::responseSuccess(parameter,
2372 static_cast<uint8_t>(*pResponse));
Cheng C Yang773703a2019-08-15 09:41:11 +08002373 }
2374 case crParameter::rotationAlgo:
2375 {
2376 if (getCRConfig(ctx, "RotationAlgorithm", value))
2377 {
2378 return ipmi::responseResponseError();
2379 }
2380
2381 std::string* pAlgo = std::get_if<std::string>(&value);
2382 if (!pAlgo)
2383 {
2384 phosphor::logging::log<phosphor::logging::level::ERR>(
2385 "Error to get RotationAlgorithm property");
2386 return ipmi::responseResponseError();
2387 }
Yong Li83315132019-10-23 17:42:24 +08002388 std::vector<uint8_t> response;
Cheng C Yang773703a2019-08-15 09:41:11 +08002389 namespace server = sdbusplus::xyz::openbmc_project::Control::server;
2390 auto algo =
2391 server::PowerSupplyRedundancy::convertAlgoFromString(*pAlgo);
Yong Li83315132019-10-23 17:42:24 +08002392
Cheng C Yang773703a2019-08-15 09:41:11 +08002393 switch (algo)
2394 {
2395 case server::PowerSupplyRedundancy::Algo::bmcSpecific:
Yong Li83315132019-10-23 17:42:24 +08002396 response.push_back(0);
Cheng C Yang773703a2019-08-15 09:41:11 +08002397 break;
2398 case server::PowerSupplyRedundancy::Algo::userSpecific:
Yong Li83315132019-10-23 17:42:24 +08002399 response.push_back(1);
Cheng C Yang773703a2019-08-15 09:41:11 +08002400 break;
2401 default:
2402 phosphor::logging::log<phosphor::logging::level::ERR>(
2403 "Error to get valid algo");
2404 return ipmi::responseResponseError();
2405 }
2406
2407 if (getCRConfig(ctx, "RotationRankOrder", value))
2408 {
2409 return ipmi::responseResponseError();
2410 }
2411 std::vector<uint8_t>* pResponse =
2412 std::get_if<std::vector<uint8_t>>(&value);
2413 if (!pResponse)
2414 {
2415 phosphor::logging::log<phosphor::logging::level::ERR>(
2416 "Error to get RotationRankOrder property");
2417 return ipmi::responseResponseError();
2418 }
Yong Li83315132019-10-23 17:42:24 +08002419
Cheng C Yang773703a2019-08-15 09:41:11 +08002420 std::copy(pResponse->begin(), pResponse->end(),
Yong Li83315132019-10-23 17:42:24 +08002421 std::back_inserter(response));
2422
Cheng C Yangf41e3342019-09-10 04:47:23 +08002423 return ipmi::responseSuccess(parameter, response);
Cheng C Yang773703a2019-08-15 09:41:11 +08002424 }
2425 case crParameter::rotationPeriod:
2426 {
2427 if (getCRConfig(ctx, "PeriodOfRotation", value))
2428 {
2429 return ipmi::responseResponseError();
2430 }
2431 uint32_t* pResponse = std::get_if<uint32_t>(&value);
2432 if (!pResponse)
2433 {
2434 phosphor::logging::log<phosphor::logging::level::ERR>(
2435 "Error to get RotationAlgorithm property");
2436 return ipmi::responseResponseError();
2437 }
Cheng C Yangf41e3342019-09-10 04:47:23 +08002438 return ipmi::responseSuccess(parameter, *pResponse);
Cheng C Yang773703a2019-08-15 09:41:11 +08002439 }
2440 case crParameter::numOfPSU:
2441 {
2442 uint8_t numberOfPSU = getPSUCount();
2443 if (!numberOfPSU)
2444 {
2445 return ipmi::responseResponseError();
2446 }
Cheng C Yangf41e3342019-09-10 04:47:23 +08002447 return ipmi::responseSuccess(parameter, numberOfPSU);
Cheng C Yang773703a2019-08-15 09:41:11 +08002448 }
Yong Li19445ab2019-12-20 18:25:29 +08002449 case crParameter::rotationRankOrderEffective:
2450 {
2451 if (getCRConfig(ctx, "RotationRankOrder", value,
2452 "xyz.openbmc_project.PSURedundancy"))
2453 {
2454 return ipmi::responseResponseError();
2455 }
2456 std::vector<uint8_t>* pResponse =
2457 std::get_if<std::vector<uint8_t>>(&value);
2458 if (!pResponse)
2459 {
2460 phosphor::logging::log<phosphor::logging::level::ERR>(
2461 "Error to get effective RotationRankOrder property");
2462 return ipmi::responseResponseError();
2463 }
2464 return ipmi::responseSuccess(parameter, *pResponse);
2465 }
Cheng C Yang773703a2019-08-15 09:41:11 +08002466 default:
2467 {
2468 return ipmi::response(ccParameterNotSupported);
2469 }
2470 }
2471}
2472
Zhu, Yungebe560b02019-04-21 21:19:21 -04002473ipmi::RspType<> ipmiOEMSetFaultIndication(uint8_t sourceId, uint8_t faultType,
2474 uint8_t faultState,
2475 uint8_t faultGroup,
2476 std::array<uint8_t, 8>& ledStateData)
2477{
Zhu, Yungebe560b02019-04-21 21:19:21 -04002478 constexpr auto maxFaultType = static_cast<size_t>(RemoteFaultType::max);
2479 static const std::array<std::string, maxFaultType> faultNames = {
2480 "faultFan", "faultTemp", "faultPower",
2481 "faultDriveSlot", "faultSoftware", "faultMemory"};
Zhu, Yungebe560b02019-04-21 21:19:21 -04002482
2483 constexpr uint8_t maxFaultSource = 0x4;
2484 constexpr uint8_t skipLEDs = 0xFF;
2485 constexpr uint8_t pinSize = 64;
2486 constexpr uint8_t groupSize = 16;
Zhikui Rence4e73f2019-12-06 13:59:47 -08002487 constexpr uint8_t groupNum = 5; // 4 for fault memory, 1 for faultFan
Zhu, Yungebe560b02019-04-21 21:19:21 -04002488
Zhikui Rence4e73f2019-12-06 13:59:47 -08002489 // same pin names need to be defined in dts file
2490 static const std::array<std::array<std::string, groupSize>, groupNum>
2491 faultLedPinNames = {{
2492 "LED_CPU1_CH1_DIMM1_FAULT",
2493 "LED_CPU1_CH1_DIMM2_FAULT",
2494 "LED_CPU1_CH2_DIMM1_FAULT",
2495 "LED_CPU1_CH2_DIMM2_FAULT",
2496 "LED_CPU1_CH3_DIMM1_FAULT",
2497 "LED_CPU1_CH3_DIMM2_FAULT",
2498 "LED_CPU1_CH4_DIMM1_FAULT",
2499 "LED_CPU1_CH4_DIMM2_FAULT",
2500 "LED_CPU1_CH5_DIMM1_FAULT",
2501 "LED_CPU1_CH5_DIMM2_FAULT",
2502 "LED_CPU1_CH6_DIMM1_FAULT",
2503 "LED_CPU1_CH6_DIMM2_FAULT",
2504 "",
2505 "",
2506 "",
2507 "", // end of group1
2508 "LED_CPU2_CH1_DIMM1_FAULT",
2509 "LED_CPU2_CH1_DIMM2_FAULT",
2510 "LED_CPU2_CH2_DIMM1_FAULT",
2511 "LED_CPU2_CH2_DIMM2_FAULT",
2512 "LED_CPU2_CH3_DIMM1_FAULT",
2513 "LED_CPU2_CH3_DIMM2_FAULT",
2514 "LED_CPU2_CH4_DIMM1_FAULT",
2515 "LED_CPU2_CH4_DIMM2_FAULT",
2516 "LED_CPU2_CH5_DIMM1_FAULT",
2517 "LED_CPU2_CH5_DIMM2_FAULT",
2518 "LED_CPU2_CH6_DIMM1_FAULT",
2519 "LED_CPU2_CH6_DIMM2_FAULT",
2520 "",
2521 "",
2522 "",
2523 "", // endof group2
2524 "LED_CPU3_CH1_DIMM1_FAULT",
2525 "LED_CPU3_CH1_DIMM2_FAULT",
2526 "LED_CPU3_CH2_DIMM1_FAULT",
2527 "LED_CPU3_CH2_DIMM2_FAULT",
2528 "LED_CPU3_CH3_DIMM1_FAULT",
2529 "LED_CPU3_CH3_DIMM2_FAULT",
2530 "LED_CPU3_CH4_DIMM1_FAULT",
2531 "LED_CPU3_CH4_DIMM2_FAULT",
2532 "LED_CPU3_CH5_DIMM1_FAULT",
2533 "LED_CPU3_CH5_DIMM2_FAULT",
2534 "LED_CPU3_CH6_DIMM1_FAULT",
2535 "LED_CPU3_CH6_DIMM2_FAULT",
2536 "",
2537 "",
2538 "",
2539 "", // end of group3
2540 "LED_CPU4_CH1_DIMM1_FAULT",
2541 "LED_CPU4_CH1_DIMM2_FAULT",
2542 "LED_CPU4_CH2_DIMM1_FAULT",
2543 "LED_CPU4_CH2_DIMM2_FAULT",
2544 "LED_CPU4_CH3_DIMM1_FAULT",
2545 "LED_CPU4_CH3_DIMM2_FAULT",
2546 "LED_CPU4_CH4_DIMM1_FAULT",
2547 "LED_CPU4_CH4_DIMM2_FAULT",
2548 "LED_CPU4_CH5_DIMM1_FAULT",
2549 "LED_CPU4_CH5_DIMM2_FAULT",
2550 "LED_CPU4_CH6_DIMM1_FAULT",
2551 "LED_CPU4_CH6_DIMM2_FAULT",
2552 "",
2553 "",
2554 "",
2555 "", // end of group4
2556 "LED_FAN1_FAULT",
2557 "LED_FAN2_FAULT",
2558 "LED_FAN3_FAULT",
2559 "LED_FAN4_FAULT",
2560 "LED_FAN5_FAULT",
2561 "LED_FAN6_FAULT",
2562 "LED_FAN7_FAULT",
2563 "LED_FAN8_FAULT",
2564 "",
2565 "",
2566 "",
2567 "",
2568 "",
2569 "",
2570 "",
2571 "" // end of group5
2572 }};
Zhu, Yungebe560b02019-04-21 21:19:21 -04002573
Zhikui Rence4e73f2019-12-06 13:59:47 -08002574 // Validate the source, fault type --
2575 // (Byte 1) sourceId: Unspecified, Hot-Swap Controller 0, Hot-Swap
2576 // Controller 1, BIOS (Byte 2) fault type: fan, temperature, power,
2577 // driveslot, software, memory (Byte 3) FaultState: OK, Degraded,
2578 // Non-Critical, Critical, Non-Recoverable, (Byte 4) is faultGroup,
2579 // definition differs based on fault type (Byte 2)
2580 // Type Fan=> Group: 0=FanGroupID, FF-not used
2581 // Byte 5-11 00h, not used
2582 // Byte12 FanLedState [7:0]-Fans 7:0
2583 // Type Memory=> Group: 0 = DIMM GroupID, FF-not used
2584 // Byte 5:12 - DIMM LED state (64bit field, LS Byte first)
2585 // [63:48] = CPU4 channels 7:0, 2 bits per channel
2586 // [47:32] = CPU3 channels 7:0, 2 bits per channel
2587 // [31:16] = CPU2 channels 7:0, 2 bits per channel
2588 // [15:0] = CPU1 channels 7:0, 2 bits per channel
2589 // Type Other=> Component Fault LED Group ID, not used set to 0xFF
2590 // Byte[5:12]: reserved 0x00h
Zhu, Yungebe560b02019-04-21 21:19:21 -04002591 if ((sourceId >= maxFaultSource) ||
2592 (faultType >= static_cast<int8_t>(RemoteFaultType::max)) ||
2593 (faultState >= static_cast<int8_t>(RemoteFaultState::maxFaultState)) ||
2594 (faultGroup >= static_cast<int8_t>(DimmFaultType::maxFaultGroup)))
2595 {
2596 return ipmi::responseParmOutOfRange();
2597 }
2598
Zhikui Rence4e73f2019-12-06 13:59:47 -08002599 size_t pinGroupOffset = 0;
2600 size_t pinGroupMax = pinSize / groupSize;
2601 if (RemoteFaultType::fan == RemoteFaultType(faultType))
Zhu, Yungebe560b02019-04-21 21:19:21 -04002602 {
Zhikui Rence4e73f2019-12-06 13:59:47 -08002603 pinGroupOffset = 4;
2604 pinGroupMax = groupNum - pinSize / groupSize;
Zhu, Yungebe560b02019-04-21 21:19:21 -04002605 }
2606
2607 switch (RemoteFaultType(faultType))
2608 {
2609 case (RemoteFaultType::fan):
2610 case (RemoteFaultType::memory):
2611 {
2612 if (faultGroup == skipLEDs)
2613 {
2614 return ipmi::responseSuccess();
2615 }
Zhu, Yungebe560b02019-04-21 21:19:21 -04002616 // calculate led state bit filed count, each byte has 8bits
2617 // the maximum bits will be 8 * 8 bits
2618 constexpr uint8_t size = sizeof(ledStateData) * 8;
Zhikui Rence4e73f2019-12-06 13:59:47 -08002619
2620 // assemble ledState
2621 uint64_t ledState = 0;
2622 bool hasError = false;
Zhu, Yungebe560b02019-04-21 21:19:21 -04002623 for (int i = 0; i < sizeof(ledStateData); i++)
2624 {
2625 ledState = (uint64_t)(ledState << 8);
2626 ledState = (uint64_t)(ledState | (uint64_t)ledStateData[i]);
2627 }
Zhu, Yungebe560b02019-04-21 21:19:21 -04002628 std::bitset<size> ledStateBits(ledState);
Zhu, Yungebe560b02019-04-21 21:19:21 -04002629
Zhikui Rence4e73f2019-12-06 13:59:47 -08002630 for (int group = 0; group < pinGroupMax; group++)
2631 {
2632 for (int i = 0; i < groupSize; i++)
2633 { // skip non-existing pins
2634 if (0 == faultLedPinNames[group + pinGroupOffset][i].size())
2635 {
2636 continue;
2637 }
Zhu, Yungebe560b02019-04-21 21:19:21 -04002638
Zhikui Rence4e73f2019-12-06 13:59:47 -08002639 gpiod::line line = gpiod::find_line(
2640 faultLedPinNames[group + pinGroupOffset][i]);
2641 if (!line)
2642 {
2643 phosphor::logging::log<phosphor::logging::level::ERR>(
2644 "Not Find Led Gpio Device!",
2645 phosphor::logging::entry(
2646 "DEVICE=%s",
2647 faultLedPinNames[group + pinGroupOffset][i]
2648 .c_str()));
2649 hasError = true;
2650 continue;
2651 }
Zhu, Yungebe560b02019-04-21 21:19:21 -04002652
Zhikui Rence4e73f2019-12-06 13:59:47 -08002653 bool activeHigh =
2654 (line.active_state() == gpiod::line::ACTIVE_HIGH);
2655 try
2656 {
2657 line.request(
2658 {"faultLed", gpiod::line_request::DIRECTION_OUTPUT,
2659 activeHigh
2660 ? 0
2661 : gpiod::line_request::FLAG_ACTIVE_LOW});
2662 line.set_value(ledStateBits[i + group * groupSize]);
2663 }
2664 catch (std::system_error&)
2665 {
2666 phosphor::logging::log<phosphor::logging::level::ERR>(
2667 "Error write Led Gpio Device!",
2668 phosphor::logging::entry(
2669 "DEVICE=%s",
2670 faultLedPinNames[group + pinGroupOffset][i]
2671 .c_str()));
2672 hasError = true;
2673 continue;
2674 }
2675 } // for int i
2676 }
2677 if (hasError)
2678 {
2679 return ipmi::responseResponseError();
Zhu, Yungebe560b02019-04-21 21:19:21 -04002680 }
2681 break;
2682 }
2683 default:
2684 {
2685 // now only support two fault types
2686 return ipmi::responseParmOutOfRange();
2687 }
Zhikui Rence4e73f2019-12-06 13:59:47 -08002688 } // switch
Zhu, Yungebe560b02019-04-21 21:19:21 -04002689 return ipmi::responseSuccess();
2690}
2691
Richard Marian Thomaiyarea537d52019-04-24 21:33:48 +05302692ipmi::RspType<uint8_t> ipmiOEMReadBoardProductId()
2693{
2694 uint8_t prodId = 0;
2695 try
2696 {
Vernon Mauery15419dd2019-05-24 09:40:30 -07002697 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
Richard Marian Thomaiyarea537d52019-04-24 21:33:48 +05302698 const DbusObjectInfo& object = getDbusObject(
Vernon Mauery15419dd2019-05-24 09:40:30 -07002699 *dbus, "xyz.openbmc_project.Inventory.Item.Board",
Richard Marian Thomaiyarea537d52019-04-24 21:33:48 +05302700 "/xyz/openbmc_project/inventory/system/board/", "Baseboard");
2701 const Value& propValue = getDbusProperty(
Vernon Mauery15419dd2019-05-24 09:40:30 -07002702 *dbus, object.second, object.first,
Suryakanth Sekar6c57e5c2020-01-10 17:11:58 +05302703 "xyz.openbmc_project.Inventory.Item.Board.Motherboard",
2704 "ProductId");
Richard Marian Thomaiyarea537d52019-04-24 21:33:48 +05302705 prodId = static_cast<uint8_t>(std::get<uint64_t>(propValue));
2706 }
2707 catch (std::exception& e)
2708 {
2709 phosphor::logging::log<phosphor::logging::level::ERR>(
2710 "ipmiOEMReadBoardProductId: Product ID read failed!",
2711 phosphor::logging::entry("ERR=%s", e.what()));
2712 }
2713 return ipmi::responseSuccess(prodId);
2714}
2715
Richard Marian Thomaiyard801e462019-06-20 01:05:40 +05302716/** @brief implements the get security mode command
2717 * @param ctx - ctx pointer
2718 *
2719 * @returns IPMI completion code with following data
2720 * - restriction mode value - As specified in
2721 * xyz.openbmc_project.Control.Security.RestrictionMode.interface.yaml
2722 * - special mode value - As specified in
2723 * xyz.openbmc_project.Control.Security.SpecialMode.interface.yaml
2724 */
2725ipmi::RspType<uint8_t, uint8_t> ipmiGetSecurityMode(ipmi::Context::ptr ctx)
2726{
2727 namespace securityNameSpace =
2728 sdbusplus::xyz::openbmc_project::Control::Security::server;
2729 uint8_t restrictionModeValue = 0;
2730 uint8_t specialModeValue = 0;
2731
2732 boost::system::error_code ec;
2733 auto varRestrMode = ctx->bus->yield_method_call<std::variant<std::string>>(
James Feist28c72902019-09-16 10:34:07 -07002734 ctx->yield, ec, restricionModeService, restricionModeBasePath,
Richard Marian Thomaiyard801e462019-06-20 01:05:40 +05302735 dBusPropertyIntf, dBusPropertyGetMethod, restricionModeIntf,
2736 restricionModeProperty);
2737 if (ec)
2738 {
2739 phosphor::logging::log<phosphor::logging::level::ERR>(
2740 "ipmiGetSecurityMode: failed to get RestrictionMode property",
2741 phosphor::logging::entry("ERROR=%s", ec.message().c_str()));
2742 return ipmi::responseUnspecifiedError();
2743 }
2744 restrictionModeValue = static_cast<uint8_t>(
2745 securityNameSpace::RestrictionMode::convertModesFromString(
2746 std::get<std::string>(varRestrMode)));
Richard Marian Thomaiyar8d4f8d72019-11-11 12:06:40 +05302747 auto varSpecialMode =
2748 ctx->bus->yield_method_call<std::variant<std::string>>(
2749 ctx->yield, ec, specialModeService, specialModeBasePath,
2750 dBusPropertyIntf, dBusPropertyGetMethod, specialModeIntf,
2751 specialModeProperty);
Richard Marian Thomaiyard801e462019-06-20 01:05:40 +05302752 if (ec)
2753 {
2754 phosphor::logging::log<phosphor::logging::level::ERR>(
2755 "ipmiGetSecurityMode: failed to get SpecialMode property",
2756 phosphor::logging::entry("ERROR=%s", ec.message().c_str()));
2757 // fall through, let us not worry about SpecialMode property, which is
2758 // not required in user scenario
2759 }
2760 else
2761 {
Richard Marian Thomaiyar8d4f8d72019-11-11 12:06:40 +05302762 specialModeValue = static_cast<uint8_t>(
2763 securityNameSpace::SpecialMode::convertModesFromString(
2764 std::get<std::string>(varSpecialMode)));
Richard Marian Thomaiyard801e462019-06-20 01:05:40 +05302765 }
2766 return ipmi::responseSuccess(restrictionModeValue, specialModeValue);
2767}
2768
2769/** @brief implements the set security mode command
2770 * Command allows to upgrade the restriction mode and won't allow
2771 * to downgrade from system interface
2772 * @param ctx - ctx pointer
2773 * @param restrictionMode - restriction mode value to be set.
2774 *
2775 * @returns IPMI completion code
2776 */
2777ipmi::RspType<> ipmiSetSecurityMode(ipmi::Context::ptr ctx,
Richard Marian Thomaiyar10791062019-11-11 12:19:53 +05302778 uint8_t restrictionMode,
2779 std::optional<uint8_t> specialMode)
Richard Marian Thomaiyard801e462019-06-20 01:05:40 +05302780{
Richard Marian Thomaiyar10791062019-11-11 12:19:53 +05302781#ifndef BMC_VALIDATION_UNSECURE_FEATURE
2782 if (specialMode)
2783 {
2784 return ipmi::responseReqDataLenInvalid();
2785 }
2786#endif
Richard Marian Thomaiyard801e462019-06-20 01:05:40 +05302787 namespace securityNameSpace =
2788 sdbusplus::xyz::openbmc_project::Control::Security::server;
2789
2790 ChannelInfo chInfo;
2791 if (getChannelInfo(ctx->channel, chInfo) != ccSuccess)
2792 {
2793 phosphor::logging::log<phosphor::logging::level::ERR>(
2794 "ipmiSetSecurityMode: Failed to get Channel Info",
2795 phosphor::logging::entry("CHANNEL=%d", ctx->channel));
2796 return ipmi::responseUnspecifiedError();
2797 }
2798 auto reqMode =
2799 static_cast<securityNameSpace::RestrictionMode::Modes>(restrictionMode);
2800
2801 if ((reqMode < securityNameSpace::RestrictionMode::Modes::Provisioning) ||
2802 (reqMode >
2803 securityNameSpace::RestrictionMode::Modes::ProvisionedHostDisabled))
2804 {
2805 return ipmi::responseInvalidFieldRequest();
2806 }
2807
2808 boost::system::error_code ec;
2809 auto varRestrMode = ctx->bus->yield_method_call<std::variant<std::string>>(
James Feist28c72902019-09-16 10:34:07 -07002810 ctx->yield, ec, restricionModeService, restricionModeBasePath,
Richard Marian Thomaiyard801e462019-06-20 01:05:40 +05302811 dBusPropertyIntf, dBusPropertyGetMethod, restricionModeIntf,
2812 restricionModeProperty);
2813 if (ec)
2814 {
2815 phosphor::logging::log<phosphor::logging::level::ERR>(
2816 "ipmiSetSecurityMode: failed to get RestrictionMode property",
2817 phosphor::logging::entry("ERROR=%s", ec.message().c_str()));
2818 return ipmi::responseUnspecifiedError();
2819 }
2820 auto currentRestrictionMode =
2821 securityNameSpace::RestrictionMode::convertModesFromString(
2822 std::get<std::string>(varRestrMode));
2823
2824 if (chInfo.mediumType !=
2825 static_cast<uint8_t>(EChannelMediumType::lan8032) &&
2826 currentRestrictionMode > reqMode)
2827 {
2828 phosphor::logging::log<phosphor::logging::level::ERR>(
2829 "ipmiSetSecurityMode - Downgrading security mode not supported "
2830 "through system interface",
2831 phosphor::logging::entry(
2832 "CUR_MODE=%d", static_cast<uint8_t>(currentRestrictionMode)),
2833 phosphor::logging::entry("REQ_MODE=%d", restrictionMode));
2834 return ipmi::responseCommandNotAvailable();
2835 }
2836
2837 ec.clear();
2838 ctx->bus->yield_method_call<>(
James Feist28c72902019-09-16 10:34:07 -07002839 ctx->yield, ec, restricionModeService, restricionModeBasePath,
Richard Marian Thomaiyard801e462019-06-20 01:05:40 +05302840 dBusPropertyIntf, dBusPropertySetMethod, restricionModeIntf,
2841 restricionModeProperty,
2842 static_cast<std::variant<std::string>>(
2843 securityNameSpace::convertForMessage(reqMode)));
2844
2845 if (ec)
2846 {
2847 phosphor::logging::log<phosphor::logging::level::ERR>(
2848 "ipmiSetSecurityMode: failed to set RestrictionMode property",
2849 phosphor::logging::entry("ERROR=%s", ec.message().c_str()));
2850 return ipmi::responseUnspecifiedError();
2851 }
Richard Marian Thomaiyar10791062019-11-11 12:19:53 +05302852
2853#ifdef BMC_VALIDATION_UNSECURE_FEATURE
2854 if (specialMode)
2855 {
2856 ec.clear();
2857 ctx->bus->yield_method_call<>(
2858 ctx->yield, ec, specialModeService, specialModeBasePath,
2859 dBusPropertyIntf, dBusPropertySetMethod, specialModeIntf,
2860 specialModeProperty,
2861 static_cast<std::variant<std::string>>(
2862 securityNameSpace::convertForMessage(
2863 static_cast<securityNameSpace::SpecialMode::Modes>(
2864 specialMode.value()))));
2865
2866 if (ec)
2867 {
2868 phosphor::logging::log<phosphor::logging::level::ERR>(
2869 "ipmiSetSecurityMode: failed to set SpecialMode property",
2870 phosphor::logging::entry("ERROR=%s", ec.message().c_str()));
2871 return ipmi::responseUnspecifiedError();
2872 }
2873 }
2874#endif
Richard Marian Thomaiyard801e462019-06-20 01:05:40 +05302875 return ipmi::responseSuccess();
2876}
2877
Vernon Mauery4ac799d2019-05-20 15:50:37 -07002878ipmi::RspType<uint8_t /* restore status */>
2879 ipmiRestoreConfiguration(const std::array<uint8_t, 3>& clr, uint8_t cmd)
2880{
2881 static constexpr std::array<uint8_t, 3> expClr = {'C', 'L', 'R'};
2882
2883 if (clr != expClr)
2884 {
2885 return ipmi::responseInvalidFieldRequest();
2886 }
2887 constexpr uint8_t cmdStatus = 0;
2888 constexpr uint8_t cmdDefaultRestore = 0xaa;
2889 constexpr uint8_t cmdFullRestore = 0xbb;
2890 constexpr uint8_t cmdFormat = 0xcc;
2891
2892 constexpr const char* restoreOpFname = "/tmp/.rwfs/.restore_op";
2893
2894 switch (cmd)
2895 {
2896 case cmdStatus:
2897 break;
2898 case cmdDefaultRestore:
2899 case cmdFullRestore:
2900 case cmdFormat:
2901 {
2902 // write file to rwfs root
2903 int value = (cmd - 1) & 0x03; // map aa, bb, cc => 1, 2, 3
2904 std::ofstream restoreFile(restoreOpFname);
2905 if (!restoreFile)
2906 {
2907 return ipmi::responseUnspecifiedError();
2908 }
2909 restoreFile << value << "\n";
2910 break;
2911 }
2912 default:
2913 return ipmi::responseInvalidFieldRequest();
2914 }
2915
2916 constexpr uint8_t restorePending = 0;
2917 constexpr uint8_t restoreComplete = 1;
2918
2919 uint8_t restoreStatus = std::filesystem::exists(restoreOpFname)
2920 ? restorePending
2921 : restoreComplete;
2922 return ipmi::responseSuccess(restoreStatus);
2923}
2924
Chen Yugang39736d52019-07-12 16:24:33 +08002925ipmi::RspType<uint8_t> ipmiOEMGetNmiSource(void)
2926{
2927 uint8_t bmcSource;
Chen Yugang97cf96e2019-11-01 08:55:11 +08002928 namespace nmi = sdbusplus::xyz::openbmc_project::Chassis::Control::server;
Chen Yugang39736d52019-07-12 16:24:33 +08002929
2930 try
2931 {
2932 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
2933 std::string service =
2934 getService(*dbus, oemNmiSourceIntf, oemNmiSourceObjPath);
2935 Value variant =
2936 getDbusProperty(*dbus, service, oemNmiSourceObjPath,
2937 oemNmiSourceIntf, oemNmiBmcSourceObjPathProp);
2938
2939 switch (nmi::NMISource::convertBMCSourceSignalFromString(
2940 std::get<std::string>(variant)))
2941 {
2942 case nmi::NMISource::BMCSourceSignal::None:
2943 bmcSource = static_cast<uint8_t>(NmiSource::none);
2944 break;
Chen Yugang97cf96e2019-11-01 08:55:11 +08002945 case nmi::NMISource::BMCSourceSignal::FrontPanelButton:
2946 bmcSource = static_cast<uint8_t>(NmiSource::frontPanelButton);
Chen Yugang39736d52019-07-12 16:24:33 +08002947 break;
Chen Yugang97cf96e2019-11-01 08:55:11 +08002948 case nmi::NMISource::BMCSourceSignal::Watchdog:
2949 bmcSource = static_cast<uint8_t>(NmiSource::watchdog);
Chen Yugang39736d52019-07-12 16:24:33 +08002950 break;
2951 case nmi::NMISource::BMCSourceSignal::ChassisCmd:
2952 bmcSource = static_cast<uint8_t>(NmiSource::chassisCmd);
2953 break;
2954 case nmi::NMISource::BMCSourceSignal::MemoryError:
2955 bmcSource = static_cast<uint8_t>(NmiSource::memoryError);
2956 break;
Chen Yugang97cf96e2019-11-01 08:55:11 +08002957 case nmi::NMISource::BMCSourceSignal::PciBusError:
2958 bmcSource = static_cast<uint8_t>(NmiSource::pciBusError);
Chen Yugang39736d52019-07-12 16:24:33 +08002959 break;
Chen Yugang97cf96e2019-11-01 08:55:11 +08002960 case nmi::NMISource::BMCSourceSignal::PCH:
2961 bmcSource = static_cast<uint8_t>(NmiSource::pch);
Chen Yugang39736d52019-07-12 16:24:33 +08002962 break;
Chen Yugang97cf96e2019-11-01 08:55:11 +08002963 case nmi::NMISource::BMCSourceSignal::Chipset:
2964 bmcSource = static_cast<uint8_t>(NmiSource::chipset);
Chen Yugang39736d52019-07-12 16:24:33 +08002965 break;
2966 default:
2967 phosphor::logging::log<phosphor::logging::level::ERR>(
2968 "NMI source: invalid property!",
2969 phosphor::logging::entry(
2970 "PROP=%s", std::get<std::string>(variant).c_str()));
2971 return ipmi::responseResponseError();
2972 }
2973 }
2974 catch (sdbusplus::exception::SdBusError& e)
2975 {
2976 phosphor::logging::log<phosphor::logging::level::ERR>(e.what());
2977 return ipmi::responseResponseError();
2978 }
2979
2980 return ipmi::responseSuccess(bmcSource);
2981}
2982
2983ipmi::RspType<> ipmiOEMSetNmiSource(uint8_t sourceId)
2984{
Chen Yugang97cf96e2019-11-01 08:55:11 +08002985 namespace nmi = sdbusplus::xyz::openbmc_project::Chassis::Control::server;
Chen Yugang39736d52019-07-12 16:24:33 +08002986
2987 nmi::NMISource::BMCSourceSignal bmcSourceSignal =
2988 nmi::NMISource::BMCSourceSignal::None;
2989
2990 switch (NmiSource(sourceId))
2991 {
2992 case NmiSource::none:
2993 bmcSourceSignal = nmi::NMISource::BMCSourceSignal::None;
2994 break;
Chen Yugang97cf96e2019-11-01 08:55:11 +08002995 case NmiSource::frontPanelButton:
2996 bmcSourceSignal = nmi::NMISource::BMCSourceSignal::FrontPanelButton;
Chen Yugang39736d52019-07-12 16:24:33 +08002997 break;
Chen Yugang97cf96e2019-11-01 08:55:11 +08002998 case NmiSource::watchdog:
2999 bmcSourceSignal = nmi::NMISource::BMCSourceSignal::Watchdog;
Chen Yugang39736d52019-07-12 16:24:33 +08003000 break;
3001 case NmiSource::chassisCmd:
3002 bmcSourceSignal = nmi::NMISource::BMCSourceSignal::ChassisCmd;
3003 break;
3004 case NmiSource::memoryError:
3005 bmcSourceSignal = nmi::NMISource::BMCSourceSignal::MemoryError;
3006 break;
Chen Yugang97cf96e2019-11-01 08:55:11 +08003007 case NmiSource::pciBusError:
3008 bmcSourceSignal = nmi::NMISource::BMCSourceSignal::PciBusError;
Chen Yugang39736d52019-07-12 16:24:33 +08003009 break;
Chen Yugang97cf96e2019-11-01 08:55:11 +08003010 case NmiSource::pch:
3011 bmcSourceSignal = nmi::NMISource::BMCSourceSignal::PCH;
Chen Yugang39736d52019-07-12 16:24:33 +08003012 break;
Chen Yugang97cf96e2019-11-01 08:55:11 +08003013 case NmiSource::chipset:
3014 bmcSourceSignal = nmi::NMISource::BMCSourceSignal::Chipset;
Chen Yugang39736d52019-07-12 16:24:33 +08003015 break;
3016 default:
3017 phosphor::logging::log<phosphor::logging::level::ERR>(
3018 "NMI source: invalid property!");
3019 return ipmi::responseResponseError();
3020 }
3021
3022 try
3023 {
3024 // keep NMI signal source
3025 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
3026 std::string service =
3027 getService(*dbus, oemNmiSourceIntf, oemNmiSourceObjPath);
Chen Yugang97cf96e2019-11-01 08:55:11 +08003028 setDbusProperty(*dbus, service, oemNmiSourceObjPath, oemNmiSourceIntf,
3029 oemNmiBmcSourceObjPathProp,
3030 nmi::convertForMessage(bmcSourceSignal));
Chen Yugang99be6332019-08-09 16:20:48 +08003031 // set Enabled property to inform NMI source handling
3032 // to trigger a NMI_OUT BSOD.
3033 // if it's triggered by NMI source property changed,
3034 // NMI_OUT BSOD could be missed if the same source occurs twice in a row
3035 if (bmcSourceSignal != nmi::NMISource::BMCSourceSignal::None)
3036 {
3037 setDbusProperty(*dbus, service, oemNmiSourceObjPath,
3038 oemNmiSourceIntf, oemNmiEnabledObjPathProp,
3039 static_cast<bool>(true));
3040 }
Chen Yugang39736d52019-07-12 16:24:33 +08003041 }
3042 catch (sdbusplus::exception_t& e)
3043 {
3044 phosphor::logging::log<phosphor::logging::level::ERR>(e.what());
3045 return ipmi::responseResponseError();
3046 }
3047
3048 return ipmi::responseSuccess();
3049}
3050
James Feist63efafa2019-07-24 12:39:21 -07003051namespace dimmOffset
3052{
3053constexpr const char* dimmPower = "DimmPower";
3054constexpr const char* staticCltt = "StaticCltt";
3055constexpr const char* offsetPath = "/xyz/openbmc_project/Inventory/Item/Dimm";
3056constexpr const char* offsetInterface =
3057 "xyz.openbmc_project.Inventory.Item.Dimm.Offset";
3058constexpr const char* property = "DimmOffset";
3059
3060}; // namespace dimmOffset
3061
3062ipmi::RspType<>
3063 ipmiOEMSetDimmOffset(uint8_t type,
3064 const std::vector<std::tuple<uint8_t, uint8_t>>& data)
3065{
3066 if (type != static_cast<uint8_t>(dimmOffsetTypes::dimmPower) &&
3067 type != static_cast<uint8_t>(dimmOffsetTypes::staticCltt))
3068 {
3069 return ipmi::responseInvalidFieldRequest();
3070 }
3071
3072 if (data.empty())
3073 {
3074 return ipmi::responseInvalidFieldRequest();
3075 }
3076 nlohmann::json json;
3077
3078 std::ifstream jsonStream(dimmOffsetFile);
3079 if (jsonStream.good())
3080 {
3081 json = nlohmann::json::parse(jsonStream, nullptr, false);
3082 if (json.is_discarded())
3083 {
3084 json = nlohmann::json();
3085 }
3086 jsonStream.close();
3087 }
3088
3089 std::string typeName;
3090 if (type == static_cast<uint8_t>(dimmOffsetTypes::dimmPower))
3091 {
3092 typeName = dimmOffset::dimmPower;
3093 }
3094 else
3095 {
3096 typeName = dimmOffset::staticCltt;
3097 }
3098
3099 nlohmann::json& field = json[typeName];
3100
3101 for (const auto& [index, value] : data)
3102 {
3103 field[index] = value;
3104 }
3105
3106 for (nlohmann::json& val : field)
3107 {
3108 if (val == nullptr)
3109 {
3110 val = static_cast<uint8_t>(0);
3111 }
3112 }
3113
3114 std::ofstream output(dimmOffsetFile);
3115 if (!output.good())
3116 {
3117 std::cerr << "Error writing json file\n";
3118 return ipmi::responseResponseError();
3119 }
3120
3121 output << json.dump(4);
3122
3123 if (type == static_cast<uint8_t>(dimmOffsetTypes::staticCltt))
3124 {
3125 std::shared_ptr<sdbusplus::asio::connection> bus = getSdBus();
3126
3127 std::variant<std::vector<uint8_t>> offsets =
3128 field.get<std::vector<uint8_t>>();
3129 auto call = bus->new_method_call(
3130 settingsBusName, dimmOffset::offsetPath, PROP_INTF, "Set");
3131 call.append(dimmOffset::offsetInterface, dimmOffset::property, offsets);
3132 try
3133 {
3134 bus->call(call);
3135 }
3136 catch (sdbusplus::exception_t& e)
3137 {
3138 phosphor::logging::log<phosphor::logging::level::ERR>(
3139 "ipmiOEMSetDimmOffset: can't set dimm offsets!",
3140 phosphor::logging::entry("ERR=%s", e.what()));
3141 return ipmi::responseResponseError();
3142 }
3143 }
3144
3145 return ipmi::responseSuccess();
3146}
3147
3148ipmi::RspType<uint8_t> ipmiOEMGetDimmOffset(uint8_t type, uint8_t index)
3149{
3150
3151 if (type != static_cast<uint8_t>(dimmOffsetTypes::dimmPower) &&
3152 type != static_cast<uint8_t>(dimmOffsetTypes::staticCltt))
3153 {
3154 return ipmi::responseInvalidFieldRequest();
3155 }
3156
3157 std::ifstream jsonStream(dimmOffsetFile);
3158
3159 auto json = nlohmann::json::parse(jsonStream, nullptr, false);
3160 if (json.is_discarded())
3161 {
3162 std::cerr << "File error in " << dimmOffsetFile << "\n";
3163 return ipmi::responseResponseError();
3164 }
3165
3166 std::string typeName;
3167 if (type == static_cast<uint8_t>(dimmOffsetTypes::dimmPower))
3168 {
3169 typeName = dimmOffset::dimmPower;
3170 }
3171 else
3172 {
3173 typeName = dimmOffset::staticCltt;
3174 }
3175
3176 auto it = json.find(typeName);
3177 if (it == json.end())
3178 {
3179 return ipmi::responseInvalidFieldRequest();
3180 }
3181
3182 if (it->size() <= index)
3183 {
3184 return ipmi::responseInvalidFieldRequest();
3185 }
3186
3187 uint8_t resp = it->at(index).get<uint8_t>();
3188 return ipmi::responseSuccess(resp);
3189}
3190
Chen,Yugang4f7e76b2019-08-20 09:28:06 +08003191namespace boot_options
3192{
3193
3194using namespace sdbusplus::xyz::openbmc_project::Control::Boot::server;
3195using IpmiValue = uint8_t;
3196constexpr auto ipmiDefault = 0;
3197
3198std::map<IpmiValue, Source::Sources> sourceIpmiToDbus = {
3199 {0x01, Source::Sources::Network},
3200 {0x02, Source::Sources::Disk},
3201 {0x05, Source::Sources::ExternalMedia},
3202 {0x0f, Source::Sources::RemovableMedia},
3203 {ipmiDefault, Source::Sources::Default}};
3204
3205std::map<IpmiValue, Mode::Modes> modeIpmiToDbus = {
Chen Yugangca12a7b2019-09-03 18:11:44 +08003206 {0x06, Mode::Modes::Setup}, {ipmiDefault, Mode::Modes::Regular}};
Chen,Yugang4f7e76b2019-08-20 09:28:06 +08003207
3208std::map<Source::Sources, IpmiValue> sourceDbusToIpmi = {
3209 {Source::Sources::Network, 0x01},
3210 {Source::Sources::Disk, 0x02},
3211 {Source::Sources::ExternalMedia, 0x05},
3212 {Source::Sources::RemovableMedia, 0x0f},
3213 {Source::Sources::Default, ipmiDefault}};
3214
3215std::map<Mode::Modes, IpmiValue> modeDbusToIpmi = {
Chen Yugangca12a7b2019-09-03 18:11:44 +08003216 {Mode::Modes::Setup, 0x06}, {Mode::Modes::Regular, ipmiDefault}};
Chen,Yugang4f7e76b2019-08-20 09:28:06 +08003217
3218static constexpr auto bootModeIntf = "xyz.openbmc_project.Control.Boot.Mode";
3219static constexpr auto bootSourceIntf =
3220 "xyz.openbmc_project.Control.Boot.Source";
3221static constexpr auto enabledIntf = "xyz.openbmc_project.Object.Enable";
3222static constexpr auto persistentObjPath =
3223 "/xyz/openbmc_project/control/host0/boot";
3224static constexpr auto oneTimePath =
3225 "/xyz/openbmc_project/control/host0/boot/one_time";
3226static constexpr auto bootSourceProp = "BootSource";
3227static constexpr auto bootModeProp = "BootMode";
3228static constexpr auto oneTimeBootEnableProp = "Enabled";
3229static constexpr auto httpBootMode =
3230 "xyz.openbmc_project.Control.Boot.Source.Sources.Http";
3231
3232enum class BootOptionParameter : size_t
3233{
3234 setInProgress = 0x0,
3235 bootFlags = 0x5,
3236};
3237static constexpr uint8_t setComplete = 0x0;
3238static constexpr uint8_t setInProgress = 0x1;
3239static uint8_t transferStatus = setComplete;
3240static constexpr uint8_t setParmVersion = 0x01;
3241static constexpr uint8_t setParmBootFlagsPermanent = 0x40;
3242static constexpr uint8_t setParmBootFlagsValidOneTime = 0x80;
3243static constexpr uint8_t setParmBootFlagsValidPermanent = 0xC0;
3244static constexpr uint8_t httpBoot = 0xd;
3245static constexpr uint8_t bootSourceMask = 0x3c;
3246
3247} // namespace boot_options
3248
3249ipmi::RspType<uint8_t, // version
3250 uint8_t, // param
3251 uint8_t, // data0, dependent on parameter
3252 std::optional<uint8_t> // data1, dependent on parameter
3253 >
3254 ipmiOemGetEfiBootOptions(uint8_t parameter, uint8_t set, uint8_t block)
3255{
3256 using namespace boot_options;
3257 uint8_t bootOption = 0;
3258
3259 if (parameter == static_cast<uint8_t>(BootOptionParameter::setInProgress))
3260 {
3261 return ipmi::responseSuccess(setParmVersion, parameter, transferStatus,
3262 std::nullopt);
3263 }
3264
3265 if (parameter != static_cast<uint8_t>(BootOptionParameter::bootFlags))
3266 {
3267 phosphor::logging::log<phosphor::logging::level::ERR>(
3268 "Unsupported parameter");
3269 return ipmi::responseResponseError();
3270 }
3271
3272 try
3273 {
3274 auto oneTimeEnabled = false;
3275 // read one time Enabled property
3276 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
3277 std::string service = getService(*dbus, enabledIntf, oneTimePath);
3278 Value variant = getDbusProperty(*dbus, service, oneTimePath,
3279 enabledIntf, oneTimeBootEnableProp);
3280 oneTimeEnabled = std::get<bool>(variant);
3281
3282 // get BootSource and BootMode properties
3283 // according to oneTimeEnable
3284 auto bootObjPath = oneTimePath;
3285 if (oneTimeEnabled == false)
3286 {
3287 bootObjPath = persistentObjPath;
3288 }
3289
3290 service = getService(*dbus, bootModeIntf, bootObjPath);
3291 variant = getDbusProperty(*dbus, service, bootObjPath, bootModeIntf,
3292 bootModeProp);
3293
3294 auto bootMode =
3295 Mode::convertModesFromString(std::get<std::string>(variant));
3296
3297 service = getService(*dbus, bootSourceIntf, bootObjPath);
3298 variant = getDbusProperty(*dbus, service, bootObjPath, bootSourceIntf,
3299 bootSourceProp);
3300
3301 if (std::get<std::string>(variant) == httpBootMode)
3302 {
3303 bootOption = httpBoot;
3304 }
3305 else
3306 {
3307 auto bootSource = Source::convertSourcesFromString(
3308 std::get<std::string>(variant));
3309 bootOption = sourceDbusToIpmi.at(bootSource);
3310 if (Source::Sources::Default == bootSource)
3311 {
3312 bootOption = modeDbusToIpmi.at(bootMode);
3313 }
3314 }
3315
3316 uint8_t oneTime = oneTimeEnabled ? setParmBootFlagsValidOneTime
3317 : setParmBootFlagsValidPermanent;
3318 bootOption <<= 2; // shift for responseconstexpr
3319 return ipmi::responseSuccess(setParmVersion, parameter, oneTime,
3320 bootOption);
3321 }
3322 catch (sdbusplus::exception_t& e)
3323 {
3324 phosphor::logging::log<phosphor::logging::level::ERR>(e.what());
3325 return ipmi::responseResponseError();
3326 }
3327}
3328
3329ipmi::RspType<> ipmiOemSetEfiBootOptions(uint8_t bootFlag, uint8_t bootParam,
3330 std::optional<uint8_t> bootOption)
3331{
3332 using namespace boot_options;
3333 auto oneTimeEnabled = false;
3334
3335 if (bootFlag == static_cast<uint8_t>(BootOptionParameter::setInProgress))
3336 {
3337 if (bootOption)
3338 {
3339 return ipmi::responseReqDataLenInvalid();
3340 }
3341
3342 if (transferStatus == setInProgress)
3343 {
3344 phosphor::logging::log<phosphor::logging::level::ERR>(
3345 "boot option set in progress!");
3346 return ipmi::responseResponseError();
3347 }
3348
3349 transferStatus = bootParam;
3350 return ipmi::responseSuccess();
3351 }
3352
3353 if (bootFlag != (uint8_t)BootOptionParameter::bootFlags)
3354 {
3355 phosphor::logging::log<phosphor::logging::level::ERR>(
3356 "Unsupported parameter");
3357 return ipmi::responseResponseError();
3358 }
3359
3360 if (!bootOption)
3361 {
3362 return ipmi::responseReqDataLenInvalid();
3363 }
3364
3365 if (((bootOption.value() & bootSourceMask) >> 2) !=
3366 httpBoot) // not http boot, exit
3367 {
3368 phosphor::logging::log<phosphor::logging::level::ERR>(
3369 "wrong boot option parameter!");
3370 return ipmi::responseParmOutOfRange();
3371 }
3372
3373 try
3374 {
3375 bool permanent = (bootParam & setParmBootFlagsPermanent) ==
3376 setParmBootFlagsPermanent;
3377
3378 // read one time Enabled property
3379 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
3380 std::string service = getService(*dbus, enabledIntf, oneTimePath);
3381 Value variant = getDbusProperty(*dbus, service, oneTimePath,
3382 enabledIntf, oneTimeBootEnableProp);
3383 oneTimeEnabled = std::get<bool>(variant);
3384
3385 /*
3386 * Check if the current boot setting is onetime or permanent, if the
3387 * request in the command is otherwise, then set the "Enabled"
3388 * property in one_time object path to 'True' to indicate onetime
3389 * and 'False' to indicate permanent.
3390 *
3391 * Once the onetime/permanent setting is applied, then the bootMode
3392 * and bootSource is updated for the corresponding object.
3393 */
3394 if (permanent == oneTimeEnabled)
3395 {
3396 setDbusProperty(*dbus, service, oneTimePath, enabledIntf,
3397 oneTimeBootEnableProp, !permanent);
3398 }
3399
3400 // set BootSource and BootMode properties
3401 // according to oneTimeEnable or persistent
3402 auto bootObjPath = oneTimePath;
3403 if (oneTimeEnabled == false)
3404 {
3405 bootObjPath = persistentObjPath;
3406 }
3407 std::string bootMode =
3408 "xyz.openbmc_project.Control.Boot.Mode.Modes.Regular";
3409 std::string bootSource = httpBootMode;
3410
3411 service = getService(*dbus, bootModeIntf, bootObjPath);
3412 setDbusProperty(*dbus, service, bootObjPath, bootModeIntf, bootModeProp,
3413 bootMode);
3414
3415 service = getService(*dbus, bootSourceIntf, bootObjPath);
3416 setDbusProperty(*dbus, service, bootObjPath, bootSourceIntf,
3417 bootSourceProp, bootSource);
3418 }
3419 catch (sdbusplus::exception_t& e)
3420 {
3421 phosphor::logging::log<phosphor::logging::level::ERR>(e.what());
3422 return ipmi::responseResponseError();
3423 }
3424
3425 return ipmi::responseSuccess();
3426}
3427
Cheng C Yang4e6ee152019-09-25 10:27:44 +08003428using BasicVariantType =
3429 std::variant<std::vector<std::string>, std::vector<uint64_t>, std::string,
3430 int64_t, uint64_t, double, int32_t, uint32_t, int16_t,
3431 uint16_t, uint8_t, bool>;
3432using PropertyMapType =
3433 boost::container::flat_map<std::string, BasicVariantType>;
3434static constexpr const std::array<const char*, 1> psuPresenceTypes = {
3435 "xyz.openbmc_project.Configuration.PSUPresence"};
3436int getPSUAddress(ipmi::Context::ptr ctx, uint8_t& bus,
3437 std::vector<uint64_t>& addrTable)
3438{
3439 boost::system::error_code ec;
3440 GetSubTreeType subtree = ctx->bus->yield_method_call<GetSubTreeType>(
3441 ctx->yield, ec, "xyz.openbmc_project.ObjectMapper",
3442 "/xyz/openbmc_project/object_mapper",
3443 "xyz.openbmc_project.ObjectMapper", "GetSubTree",
3444 "/xyz/openbmc_project/inventory/system", 3, psuPresenceTypes);
3445 if (ec)
3446 {
3447 phosphor::logging::log<phosphor::logging::level::ERR>(
3448 "Failed to set dbus property to cold redundancy");
3449 return -1;
3450 }
3451 for (const auto& object : subtree)
3452 {
3453 std::string pathName = object.first;
3454 for (const auto& serviceIface : object.second)
3455 {
3456 std::string serviceName = serviceIface.first;
3457
3458 ec.clear();
3459 PropertyMapType propMap =
3460 ctx->bus->yield_method_call<PropertyMapType>(
3461 ctx->yield, ec, serviceName, pathName,
3462 "org.freedesktop.DBus.Properties", "GetAll",
3463 "xyz.openbmc_project.Configuration.PSUPresence");
3464 if (ec)
3465 {
3466 phosphor::logging::log<phosphor::logging::level::ERR>(
3467 "Failed to set dbus property to cold redundancy");
3468 return -1;
3469 }
3470 auto psuBus = std::get_if<uint64_t>(&propMap["Bus"]);
3471 auto psuAddress =
3472 std::get_if<std::vector<uint64_t>>(&propMap["Address"]);
3473
3474 if (psuBus == nullptr || psuAddress == nullptr)
3475 {
3476 std::cerr << "error finding necessary "
3477 "entry in configuration\n";
3478 return -1;
3479 }
3480 bus = static_cast<uint8_t>(*psuBus);
3481 addrTable = *psuAddress;
3482 return 0;
3483 }
3484 }
3485 return -1;
3486}
3487
3488static const constexpr uint8_t addrOffset = 8;
3489static const constexpr uint8_t psuRevision = 0xd9;
3490static const constexpr uint8_t defaultPSUBus = 7;
3491// Second Minor, Primary Minor, Major
3492static const constexpr size_t verLen = 3;
3493ipmi::RspType<std::vector<uint8_t>> ipmiOEMGetPSUVersion(ipmi::Context::ptr ctx)
3494{
3495 uint8_t bus = defaultPSUBus;
3496 std::vector<uint64_t> addrTable;
3497 std::vector<uint8_t> result;
3498 if (getPSUAddress(ctx, bus, addrTable))
3499 {
3500 std::cerr << "Failed to get PSU bus and address\n";
3501 return ipmi::responseResponseError();
3502 }
3503
3504 for (const auto& slaveAddr : addrTable)
3505 {
3506 std::vector<uint8_t> writeData = {psuRevision};
3507 std::vector<uint8_t> readBuf(verLen);
3508 uint8_t addr = static_cast<uint8_t>(slaveAddr) + addrOffset;
3509 std::string i2cBus = "/dev/i2c-" + std::to_string(bus);
3510
3511 auto retI2C = ipmi::i2cWriteRead(i2cBus, addr, writeData, readBuf);
3512 if (retI2C != ipmi::ccSuccess)
3513 {
3514 for (size_t idx = 0; idx < verLen; idx++)
3515 {
3516 result.emplace_back(0x00);
3517 }
3518 }
3519 else
3520 {
3521 for (const uint8_t& data : readBuf)
3522 {
3523 result.emplace_back(data);
3524 }
3525 }
3526 }
3527
3528 return ipmi::responseSuccess(result);
3529}
3530
AppaRao Puli28972062019-11-11 02:04:45 +05303531/** @brief implements the maximum size of
3532 * bridgeable messages used between KCS and
3533 * IPMB interfacesget security mode command.
3534 *
3535 * @returns IPMI completion code with following data
3536 * - KCS Buffer Size (In multiples of four bytes)
3537 * - IPMB Buffer Size (In multiples of four bytes)
3538 **/
3539ipmi::RspType<uint8_t, uint8_t> ipmiOEMGetBufferSize()
3540{
3541 // for now this is hard coded; really this number is dependent on
3542 // the BMC kcs driver as well as the host kcs driver....
3543 // we can't know the latter.
3544 uint8_t kcsMaxBufferSize = 63 / 4;
3545 uint8_t ipmbMaxBufferSize = 128 / 4;
3546
3547 return ipmi::responseSuccess(kcsMaxBufferSize, ipmbMaxBufferSize);
3548}
3549
Jason M. Bills64796042018-10-03 16:51:55 -07003550static void registerOEMFunctions(void)
Jia, Chunhuia835eaa2018-09-05 09:00:41 +08003551{
3552 phosphor::logging::log<phosphor::logging::level::INFO>(
3553 "Registering OEM commands");
Vernon Mauery98bbf692019-09-16 11:14:59 -07003554 ipmiPrintAndRegister(intel::netFnGeneral,
3555 intel::general::cmdGetChassisIdentifier, NULL,
3556 ipmiOEMGetChassisIdentifier,
3557 PRIVILEGE_USER); // get chassis identifier
3558
3559 ipmiPrintAndRegister(intel::netFnGeneral, intel::general::cmdSetSystemGUID,
3560 NULL, ipmiOEMSetSystemGUID,
3561 PRIVILEGE_ADMIN); // set system guid
Jason M. Billsb02bf092019-08-15 13:01:56 -07003562
3563 // <Disable BMC System Reset Action>
Vernon Mauery98bbf692019-09-16 11:14:59 -07003564 registerHandler(prioOemBase, intel::netFnGeneral,
3565 intel::general::cmdDisableBMCSystemReset, Privilege::Admin,
3566 ipmiOEMDisableBMCSystemReset);
3567
Jason M. Billsb02bf092019-08-15 13:01:56 -07003568 // <Get BMC Reset Disables>
Vernon Mauery98bbf692019-09-16 11:14:59 -07003569 registerHandler(prioOemBase, intel::netFnGeneral,
3570 intel::general::cmdGetBMCResetDisables, Privilege::Admin,
3571 ipmiOEMGetBMCResetDisables);
Jason M. Billsb02bf092019-08-15 13:01:56 -07003572
Vernon Mauery98bbf692019-09-16 11:14:59 -07003573 ipmiPrintAndRegister(intel::netFnGeneral, intel::general::cmdSetBIOSID,
3574 NULL, ipmiOEMSetBIOSID, PRIVILEGE_ADMIN);
Jia, Chunhuicc49b542019-03-20 15:41:07 +08003575
Chen Yugang7a04f3a2019-10-08 11:12:35 +08003576 registerHandler(prioOemBase, intel::netFnGeneral,
3577 intel::general::cmdGetOEMDeviceInfo, Privilege::User,
3578 ipmiOEMGetDeviceInfo);
Jia, Chunhuicc49b542019-03-20 15:41:07 +08003579
Vernon Mauery98bbf692019-09-16 11:14:59 -07003580 ipmiPrintAndRegister(intel::netFnGeneral,
3581 intel::general::cmdGetAICSlotFRUIDSlotPosRecords, NULL,
3582 ipmiOEMGetAICFRU, PRIVILEGE_USER);
Suryakanth Sekard509eb92018-11-15 17:44:11 +05303583
Vernon Mauery98bbf692019-09-16 11:14:59 -07003584 registerHandler(prioOpenBmcBase, intel::netFnGeneral,
3585 intel::general::cmdSendEmbeddedFWUpdStatus,
3586 Privilege::Operator, ipmiOEMSendEmbeddedFwUpdStatus);
Suryakanth Sekard509eb92018-11-15 17:44:11 +05303587
Vernon Mauery98bbf692019-09-16 11:14:59 -07003588 ipmiPrintAndRegister(intel::netFnGeneral,
3589 intel::general::cmdSetPowerRestoreDelay, NULL,
3590 ipmiOEMSetPowerRestoreDelay, PRIVILEGE_OPERATOR);
3591
3592 ipmiPrintAndRegister(intel::netFnGeneral,
3593 intel::general::cmdGetPowerRestoreDelay, NULL,
3594 ipmiOEMGetPowerRestoreDelay, PRIVILEGE_USER);
3595
3596 registerHandler(prioOpenBmcBase, intel::netFnGeneral,
3597 intel::general::cmdSetOEMUser2Activation,
3598 Privilege::Callback, ipmiOEMSetUser2Activation);
3599
3600 registerHandler(prioOpenBmcBase, intel::netFnGeneral,
3601 intel::general::cmdSetSpecialUserPassword,
3602 Privilege::Callback, ipmiOEMSetSpecialUserPassword);
Richard Marian Thomaiyarfc5e9852019-04-14 15:06:27 +05303603
Jason M. Bills42bd9c82019-06-28 16:39:34 -07003604 // <Get Processor Error Config>
Vernon Mauery98bbf692019-09-16 11:14:59 -07003605 registerHandler(prioOemBase, intel::netFnGeneral,
3606 intel::general::cmdGetProcessorErrConfig, Privilege::User,
3607 ipmiOEMGetProcessorErrConfig);
3608
Jason M. Bills42bd9c82019-06-28 16:39:34 -07003609 // <Set Processor Error Config>
Vernon Mauery98bbf692019-09-16 11:14:59 -07003610 registerHandler(prioOemBase, intel::netFnGeneral,
3611 intel::general::cmdSetProcessorErrConfig, Privilege::Admin,
3612 ipmiOEMSetProcessorErrConfig);
Jason M. Bills42bd9c82019-06-28 16:39:34 -07003613
Vernon Mauery98bbf692019-09-16 11:14:59 -07003614 ipmiPrintAndRegister(intel::netFnGeneral,
3615 intel::general::cmdSetShutdownPolicy, NULL,
3616 ipmiOEMSetShutdownPolicy, PRIVILEGE_ADMIN);
James Feist91244a62019-02-19 15:04:54 -08003617
Vernon Mauery98bbf692019-09-16 11:14:59 -07003618 ipmiPrintAndRegister(intel::netFnGeneral,
3619 intel::general::cmdGetShutdownPolicy, NULL,
3620 ipmiOEMGetShutdownPolicy, PRIVILEGE_ADMIN);
James Feist91244a62019-02-19 15:04:54 -08003621
anil kumar appanaf945eee2019-09-25 23:29:11 +00003622 registerHandler(prioOemBase, intel::netFnGeneral,
3623 intel::general::cmdSetFanConfig, Privilege::User,
3624 ipmiOEMSetFanConfig);
James Feist91244a62019-02-19 15:04:54 -08003625
Vernon Mauery98bbf692019-09-16 11:14:59 -07003626 registerHandler(prioOemBase, intel::netFnGeneral,
3627 intel::general::cmdGetFanConfig, Privilege::User,
3628 ipmiOEMGetFanConfig);
James Feist5f957ca2019-03-14 15:33:55 -07003629
Vernon Mauery98bbf692019-09-16 11:14:59 -07003630 registerHandler(prioOemBase, intel::netFnGeneral,
3631 intel::general::cmdGetFanSpeedOffset, Privilege::User,
3632 ipmiOEMGetFanSpeedOffset);
James Feistacc8a4e2019-04-02 14:23:57 -07003633
Vernon Mauery98bbf692019-09-16 11:14:59 -07003634 registerHandler(prioOemBase, intel::netFnGeneral,
3635 intel::general::cmdSetFanSpeedOffset, Privilege::User,
3636 ipmiOEMSetFanSpeedOffset);
James Feistacc8a4e2019-04-02 14:23:57 -07003637
Vernon Mauery98bbf692019-09-16 11:14:59 -07003638 registerHandler(prioOemBase, intel::netFnGeneral,
3639 intel::general::cmdSetFscParameter, Privilege::User,
3640 ipmiOEMSetFscParameter);
James Feist5f957ca2019-03-14 15:33:55 -07003641
Vernon Mauery98bbf692019-09-16 11:14:59 -07003642 registerHandler(prioOemBase, intel::netFnGeneral,
3643 intel::general::cmdGetFscParameter, Privilege::User,
3644 ipmiOEMGetFscParameter);
Richard Marian Thomaiyarea537d52019-04-24 21:33:48 +05303645
Vernon Mauery98bbf692019-09-16 11:14:59 -07003646 registerHandler(prioOpenBmcBase, intel::netFnGeneral,
3647 intel::general::cmdReadBaseBoardProductId, Privilege::Admin,
3648 ipmiOEMReadBoardProductId);
Chen Yugang39736d52019-07-12 16:24:33 +08003649
Vernon Mauery98bbf692019-09-16 11:14:59 -07003650 registerHandler(prioOemBase, intel::netFnGeneral,
3651 intel::general::cmdGetNmiStatus, Privilege::User,
3652 ipmiOEMGetNmiSource);
Chen Yugang39736d52019-07-12 16:24:33 +08003653
Vernon Mauery98bbf692019-09-16 11:14:59 -07003654 registerHandler(prioOemBase, intel::netFnGeneral,
3655 intel::general::cmdSetNmiStatus, Privilege::Operator,
3656 ipmiOEMSetNmiSource);
Chen,Yugang4f7e76b2019-08-20 09:28:06 +08003657
Vernon Mauery98bbf692019-09-16 11:14:59 -07003658 registerHandler(prioOemBase, intel::netFnGeneral,
3659 intel::general::cmdGetEfiBootOptions, Privilege::User,
3660 ipmiOemGetEfiBootOptions);
Chen,Yugang4f7e76b2019-08-20 09:28:06 +08003661
Vernon Mauery98bbf692019-09-16 11:14:59 -07003662 registerHandler(prioOemBase, intel::netFnGeneral,
3663 intel::general::cmdSetEfiBootOptions, Privilege::Operator,
3664 ipmiOemSetEfiBootOptions);
Richard Marian Thomaiyard801e462019-06-20 01:05:40 +05303665
Vernon Mauery98bbf692019-09-16 11:14:59 -07003666 registerHandler(prioOemBase, intel::netFnGeneral,
3667 intel::general::cmdGetSecurityMode, Privilege::User,
3668 ipmiGetSecurityMode);
Richard Marian Thomaiyard801e462019-06-20 01:05:40 +05303669
Vernon Mauery98bbf692019-09-16 11:14:59 -07003670 registerHandler(prioOemBase, intel::netFnGeneral,
3671 intel::general::cmdSetSecurityMode, Privilege::Admin,
3672 ipmiSetSecurityMode);
Vernon Mauery4ac799d2019-05-20 15:50:37 -07003673
Vernon Mauery98bbf692019-09-16 11:14:59 -07003674 ipmiPrintAndRegister(intel::netFnGeneral, intel::general::cmdGetLEDStatus,
3675 NULL, ipmiOEMGetLEDStatus, PRIVILEGE_ADMIN);
Cheng C Yang773703a2019-08-15 09:41:11 +08003676
Vernon Mauery98bbf692019-09-16 11:14:59 -07003677 ipmiPrintAndRegister(ipmi::intel::netFnPlatform,
3678 ipmi::intel::platform::cmdCfgHostSerialPortSpeed, NULL,
3679 ipmiOEMCfgHostSerialPortSpeed, PRIVILEGE_ADMIN);
3680
3681 registerHandler(prioOemBase, intel::netFnGeneral,
3682 intel::general::cmdSetFaultIndication, Privilege::Operator,
3683 ipmiOEMSetFaultIndication);
3684
3685 registerHandler(prioOemBase, intel::netFnGeneral,
3686 intel::general::cmdSetColdRedundancyConfig, Privilege::User,
3687 ipmiOEMSetCRConfig);
3688
3689 registerHandler(prioOemBase, intel::netFnGeneral,
3690 intel::general::cmdGetColdRedundancyConfig, Privilege::User,
3691 ipmiOEMGetCRConfig);
3692
3693 registerHandler(prioOemBase, intel::netFnGeneral,
3694 intel::general::cmdRestoreConfiguration, Privilege::Admin,
Vernon Mauery4ac799d2019-05-20 15:50:37 -07003695 ipmiRestoreConfiguration);
James Feist63efafa2019-07-24 12:39:21 -07003696
Vernon Mauery98bbf692019-09-16 11:14:59 -07003697 registerHandler(prioOemBase, intel::netFnGeneral,
3698 intel::general::cmdSetDimmOffset, Privilege::Operator,
3699 ipmiOEMSetDimmOffset);
James Feist63efafa2019-07-24 12:39:21 -07003700
Vernon Mauery98bbf692019-09-16 11:14:59 -07003701 registerHandler(prioOemBase, intel::netFnGeneral,
3702 intel::general::cmdGetDimmOffset, Privilege::Operator,
3703 ipmiOEMGetDimmOffset);
Chen Yugangca12a7b2019-09-03 18:11:44 +08003704
Cheng C Yang4e6ee152019-09-25 10:27:44 +08003705 registerHandler(prioOemBase, intel::netFnGeneral,
3706 intel::general::cmdGetPSUVersion, Privilege::User,
3707 ipmiOEMGetPSUVersion);
AppaRao Puli28972062019-11-11 02:04:45 +05303708
3709 registerHandler(prioOemBase, intel::netFnGeneral,
3710 intel::general::cmdGetBufferSize, Privilege::User,
3711 ipmiOEMGetBufferSize);
Jia, Chunhuia835eaa2018-09-05 09:00:41 +08003712}
3713
3714} // namespace ipmi