blob: 7928943c0b1eabae4b4307eac3c3ef00ea655f22 [file] [log] [blame]
Vernon Mauerya3702c12019-05-22 13:20:59 -07001/*
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
Ayushi Smritid872a4a2019-10-15 10:20:11 +053017#include <linux/input.h>
18
Arun P. Mohanan06584cd2021-08-13 20:56:01 +053019#include <boost/algorithm/string.hpp>
Richard Marian Thomaiyar147daec2019-06-15 07:43:48 +053020#include <boost/container/flat_map.hpp>
Vernon Mauerya3702c12019-05-22 13:20:59 -070021#include <ipmid/api.hpp>
22#include <manufacturingcommands.hpp>
23#include <oemcommands.hpp>
Jason M. Bills0748c692022-09-08 15:34:08 -070024#include <types.hpp>
Vernon Mauerya3702c12019-05-22 13:20:59 -070025
James Feistfcd2d3a2020-05-28 10:38:15 -070026#include <filesystem>
27#include <fstream>
28
Vernon Mauerya3702c12019-05-22 13:20:59 -070029namespace ipmi
30{
31
32Manufacturing mtm;
33
34static auto revertTimeOut =
35 std::chrono::duration_cast<std::chrono::microseconds>(
36 std::chrono::seconds(60)); // 1 minute timeout
37
Yong Lif267a672019-08-29 11:16:07 +080038static constexpr uint8_t slotAddressTypeBus = 0;
39static constexpr uint8_t slotAddressTypeUniqueid = 1;
40static constexpr uint8_t slotI2CMaxReadSize = 35;
41
Vernon Mauerya3702c12019-05-22 13:20:59 -070042static constexpr const char* callbackMgrService =
43 "xyz.openbmc_project.CallbackManager";
44static constexpr const char* callbackMgrIntf =
45 "xyz.openbmc_project.CallbackManager";
46static constexpr const char* callbackMgrObjPath =
47 "/xyz/openbmc_project/CallbackManager";
48static constexpr const char* retriggerLedUpdate = "RetriggerLEDUpdate";
49
50const static constexpr char* systemDService = "org.freedesktop.systemd1";
51const static constexpr char* systemDObjPath = "/org/freedesktop/systemd1";
52const static constexpr char* systemDMgrIntf =
53 "org.freedesktop.systemd1.Manager";
54const static constexpr char* pidControlService = "phosphor-pid-control.service";
55
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +053056static inline Cc resetMtmTimer(ipmi::Context::ptr ctx)
Richard Marian Thomaiyar666dd012019-08-02 20:55:37 +053057{
Richard Marian Thomaiyar666dd012019-08-02 20:55:37 +053058 boost::system::error_code ec;
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +053059 ctx->bus->yield_method_call<>(ctx->yield, ec, specialModeService,
60 specialModeObjPath, specialModeIntf,
61 "ResetTimer");
Richard Marian Thomaiyar666dd012019-08-02 20:55:37 +053062 if (ec)
63 {
64 phosphor::logging::log<phosphor::logging::level::ERR>(
65 "Failed to reset the manufacturing mode timer");
66 return ccUnspecifiedError;
67 }
68 return ccSuccess;
69}
70
Jason M. Bills38d2b5a2019-06-03 16:26:11 -070071int getGpioPathForSmSignal(const SmSignalGet signal, std::string& path)
Vernon Mauerya3702c12019-05-22 13:20:59 -070072{
Jason M. Bills38d2b5a2019-06-03 16:26:11 -070073 switch (signal)
74 {
75 case SmSignalGet::smPowerButton:
76 path = "/xyz/openbmc_project/chassis/buttons/power";
77 break;
78 case SmSignalGet::smResetButton:
79 path = "/xyz/openbmc_project/chassis/buttons/reset";
80 break;
81 case SmSignalGet::smNMIButton:
82 path = "/xyz/openbmc_project/chassis/buttons/nmi";
83 break;
Richard Marian Thomaiyar8e5e2b02019-08-01 07:50:55 +053084 case SmSignalGet::smIdentifyButton:
85 path = "/xyz/openbmc_project/chassis/buttons/id";
86 break;
Jason M. Bills38d2b5a2019-06-03 16:26:11 -070087 default:
88 return -1;
89 break;
90 }
91 return 0;
Vernon Mauerya3702c12019-05-22 13:20:59 -070092}
93
Patrick Venture37890392019-09-25 17:05:03 -070094ipmi_ret_t ledStoreAndSet(SmSignalSet signal, const std::string& setState)
Vernon Mauerya3702c12019-05-22 13:20:59 -070095{
96 LedProperty* ledProp = mtm.findLedProperty(signal);
97 if (ledProp == nullptr)
98 {
99 return IPMI_CC_INVALID_FIELD_REQUEST;
100 }
101
102 std::string ledName = ledProp->getName();
103 std::string ledService = ledServicePrefix + ledName;
104 std::string ledPath = ledPathPrefix + ledName;
105 ipmi::Value presentState;
106
107 if (false == ledProp->getLock())
108 {
109 if (mtm.getProperty(ledService.c_str(), ledPath.c_str(), ledIntf,
110 "State", &presentState) != 0)
111 {
112 return IPMI_CC_UNSPECIFIED_ERROR;
113 }
114 ledProp->setPrevState(std::get<std::string>(presentState));
115 ledProp->setLock(true);
116 if (signal == SmSignalSet::smPowerFaultLed ||
117 signal == SmSignalSet::smSystemReadyLed)
118 {
119 mtm.revertLedCallback = true;
120 }
121 }
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700122 if (mtm.setProperty(ledService, ledPath, ledIntf, "State",
Vernon Mauerya3702c12019-05-22 13:20:59 -0700123 ledStateStr + setState) != 0)
124 {
125 return IPMI_CC_UNSPECIFIED_ERROR;
126 }
127 return IPMI_CC_OK;
128}
129
130ipmi_ret_t ledRevert(SmSignalSet signal)
131{
132 LedProperty* ledProp = mtm.findLedProperty(signal);
133 if (ledProp == nullptr)
134 {
135 return IPMI_CC_INVALID_FIELD_REQUEST;
136 }
137 if (true == ledProp->getLock())
138 {
139 ledProp->setLock(false);
140 if (signal == SmSignalSet::smPowerFaultLed ||
141 signal == SmSignalSet::smSystemReadyLed)
142 {
143 try
144 {
145 ipmi::method_no_args::callDbusMethod(
146 *getSdBus(), callbackMgrService, callbackMgrObjPath,
147 callbackMgrIntf, retriggerLedUpdate);
148 }
Patrick Williamsbd51e6a2021-10-06 13:09:44 -0500149 catch (const sdbusplus::exception_t& e)
Vernon Mauerya3702c12019-05-22 13:20:59 -0700150 {
151 return IPMI_CC_UNSPECIFIED_ERROR;
152 }
153 mtm.revertLedCallback = false;
154 }
155 else
156 {
157 std::string ledName = ledProp->getName();
158 std::string ledService = ledServicePrefix + ledName;
159 std::string ledPath = ledPathPrefix + ledName;
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700160 if (mtm.setProperty(ledService, ledPath, ledIntf, "State",
161 ledProp->getPrevState()) != 0)
Vernon Mauerya3702c12019-05-22 13:20:59 -0700162 {
163 return IPMI_CC_UNSPECIFIED_ERROR;
164 }
165 }
166 }
167 return IPMI_CC_OK;
168}
169
170void Manufacturing::initData()
171{
Vernon Mauerya3702c12019-05-22 13:20:59 -0700172 ledPropertyList.push_back(
173 LedProperty(SmSignalSet::smPowerFaultLed, "status_amber"));
174 ledPropertyList.push_back(
175 LedProperty(SmSignalSet::smSystemReadyLed, "status_green"));
176 ledPropertyList.push_back(
177 LedProperty(SmSignalSet::smIdentifyLed, "identify"));
178}
179
180void Manufacturing::revertTimerHandler()
181{
Richard Marian Thomaiyarae13ac62019-12-17 15:45:12 +0530182
183#ifdef BMC_VALIDATION_UNSECURE_FEATURE
184 if (mtm.getMfgMode() == SpecialMode::valUnsecure)
185 {
186 // Don't revert the behaviour for validation unsecure mode.
187 return;
188 }
189#endif
Vernon Mauerya3702c12019-05-22 13:20:59 -0700190 if (revertFanPWM)
191 {
192 revertFanPWM = false;
193 disablePidControlService(false);
194 }
195
Ayushi Smritid872a4a2019-10-15 10:20:11 +0530196 if (mtmTestBeepFd != -1)
197 {
198 ::close(mtmTestBeepFd);
199 mtmTestBeepFd = -1;
200 }
201
Vernon Mauerya3702c12019-05-22 13:20:59 -0700202 for (const auto& ledProperty : ledPropertyList)
203 {
204 const std::string& ledName = ledProperty.getName();
Jayaprakash Mutyalaf3656142021-01-24 01:04:19 +0000205 if (ledName == "identify" && mtm.getMfgMode() == SpecialMode::mfg)
206 {
207 // Don't revert the behaviour for manufacturing mode
208 continue;
209 }
Vernon Mauerya3702c12019-05-22 13:20:59 -0700210 ledRevert(ledProperty.getSignal());
211 }
212}
213
214Manufacturing::Manufacturing() :
215 revertTimer([&](void) { revertTimerHandler(); })
216{
217 initData();
218}
219
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700220int8_t Manufacturing::getProperty(const std::string& service,
221 const std::string& path,
222 const std::string& interface,
223 const std::string& propertyName,
224 ipmi::Value* reply)
Vernon Mauerya3702c12019-05-22 13:20:59 -0700225{
226 try
227 {
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700228 *reply = ipmi::getDbusProperty(*getSdBus(), service, path, interface,
229 propertyName);
Vernon Mauerya3702c12019-05-22 13:20:59 -0700230 }
Patrick Williamsf944d2e2022-07-22 19:26:52 -0500231 catch (const sdbusplus::exception_t& e)
Vernon Mauerya3702c12019-05-22 13:20:59 -0700232 {
233 phosphor::logging::log<phosphor::logging::level::INFO>(
234 "ERROR: getProperty");
235 return -1;
236 }
237
238 return 0;
239}
240
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700241int8_t Manufacturing::setProperty(const std::string& service,
242 const std::string& path,
243 const std::string& interface,
244 const std::string& propertyName,
245 ipmi::Value value)
Vernon Mauerya3702c12019-05-22 13:20:59 -0700246{
247 try
248 {
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700249 ipmi::setDbusProperty(*getSdBus(), service, path, interface,
Vernon Mauerya3702c12019-05-22 13:20:59 -0700250 propertyName, value);
251 }
Patrick Williamsf944d2e2022-07-22 19:26:52 -0500252 catch (const sdbusplus::exception_t& e)
Vernon Mauerya3702c12019-05-22 13:20:59 -0700253 {
254 phosphor::logging::log<phosphor::logging::level::INFO>(
255 "ERROR: setProperty");
256 return -1;
257 }
258
259 return 0;
260}
261
262int8_t Manufacturing::disablePidControlService(const bool disable)
263{
264 try
265 {
266 auto dbus = getSdBus();
267 auto method = dbus->new_method_call(systemDService, systemDObjPath,
268 systemDMgrIntf,
269 disable ? "StopUnit" : "StartUnit");
270 method.append(pidControlService, "replace");
271 auto reply = dbus->call(method);
272 }
Patrick Williamsf944d2e2022-07-22 19:26:52 -0500273 catch (const sdbusplus::exception_t& e)
Vernon Mauerya3702c12019-05-22 13:20:59 -0700274 {
275 phosphor::logging::log<phosphor::logging::level::INFO>(
276 "ERROR: phosphor-pid-control service start or stop failed");
277 return -1;
278 }
279 return 0;
280}
281
Alex Schendel90eb7872022-09-01 11:56:52 -0700282static bool findPwmName(ipmi::Context::ptr& ctx, uint8_t instance,
283 std::string& pwmName)
284{
285 boost::system::error_code ec{};
286 ObjectValueTree obj;
287
288 // GetAll the objects under service FruDevice
289 ec = getManagedObjects(ctx, "xyz.openbmc_project.EntityManager",
290 "/xyz/openbmc_project/inventory", obj);
291 if (ec)
292 {
293 phosphor::logging::log<phosphor::logging::level::ERR>(
294 "GetMangagedObjects failed",
295 phosphor::logging::entry("ERROR=%s", ec.message().c_str()));
296 return false;
297 }
298 for (const auto& [path, objData] : obj)
299 {
300 for (const auto& [intf, propMap] : objData)
301 {
302 // Currently, these are the three different fan types supported.
303 if (intf == "xyz.openbmc_project.Configuration.AspeedFan" ||
304 intf == "xyz.openbmc_project.Configuration.I2CFan" ||
305 intf == "xyz.openbmc_project.Configuration.NuvotonFan")
306 {
307 auto findIndex = propMap.find("Index");
308 if (findIndex == propMap.end())
309 {
310 continue;
311 }
312
313 auto fanIndex = std::get_if<uint64_t>(&findIndex->second);
314 if (!fanIndex || *fanIndex != instance)
315 {
316 continue;
317 }
318 auto connector = objData.find(intf + std::string(".Connector"));
319 if (connector == objData.end())
320 {
321 return false;
322 }
323 auto findPwmName = connector->second.find("PwmName");
324 if (findPwmName != connector->second.end())
325 {
326 auto fanPwmName =
327 std::get_if<std::string>(&findPwmName->second);
328 if (!fanPwmName)
329 {
330 phosphor::logging::log<phosphor::logging::level::INFO>(
331 "PwmName parse ERROR.");
332 return false;
333 }
334 pwmName = *fanPwmName;
335 return true;
336 }
337 auto findPwm = connector->second.find("Pwm");
338 if (findPwm == connector->second.end())
339 {
340 return false;
341 }
342 auto fanPwm = std::get_if<uint64_t>(&findPwm->second);
343 if (!fanPwm)
344 {
345 return false;
346 }
347 pwmName = "Pwm_" + std::to_string(*fanPwm + 1);
348 return true;
349 }
350 }
351 }
352 return false;
353}
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700354ipmi::RspType<uint8_t, // Signal value
355 std::optional<uint16_t> // Fan tach value
356 >
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +0530357 appMTMGetSignal(ipmi::Context::ptr ctx, uint8_t signalTypeByte,
Richard Marian Thomaiyar147daec2019-06-15 07:43:48 +0530358 uint8_t instance, uint8_t actionByte)
Vernon Mauerya3702c12019-05-22 13:20:59 -0700359{
Ayushi Smritie0511e52019-08-27 17:30:34 +0000360 // mfg filter logic is used to allow MTM get signal command only in
361 // manfacturing mode.
Vernon Mauerya3702c12019-05-22 13:20:59 -0700362
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700363 SmSignalGet signalType = static_cast<SmSignalGet>(signalTypeByte);
364 SmActionGet action = static_cast<SmActionGet>(actionByte);
365
366 switch (signalType)
367 {
anil kumar appana98705b32019-09-11 14:15:50 +0000368 case SmSignalGet::smChassisIntrusion:
369 {
370 ipmi::Value reply;
371 if (mtm.getProperty(intrusionService, intrusionPath, intrusionIntf,
372 "Status", &reply) < 0)
373 {
374 return ipmi::responseInvalidFieldRequest();
375 }
376 std::string* intrusionStatus = std::get_if<std::string>(&reply);
377 if (!intrusionStatus)
378 {
379 return ipmi::responseUnspecifiedError();
380 }
381
382 uint8_t status = 0;
383 if (!intrusionStatus->compare("Normal"))
384 {
385 status = static_cast<uint8_t>(IntrusionStatus::normal);
386 }
387 else if (!intrusionStatus->compare("HardwareIntrusion"))
388 {
389 status =
390 static_cast<uint8_t>(IntrusionStatus::hardwareIntrusion);
391 }
392 else if (!intrusionStatus->compare("TamperingDetected"))
393 {
394 status =
395 static_cast<uint8_t>(IntrusionStatus::tamperingDetected);
396 }
397 else
398 {
399 return ipmi::responseUnspecifiedError();
400 }
401 return ipmi::responseSuccess(status, std::nullopt);
402 }
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700403 case SmSignalGet::smFanPwmGet:
404 {
405 ipmi::Value reply;
Alex Schendel90eb7872022-09-01 11:56:52 -0700406 std::string pwmName, fullPath;
407 if (!findPwmName(ctx, instance, pwmName))
408 {
409 // The default PWM name is Pwm_#
410 pwmName = "Pwm_" + std::to_string(instance + 1);
411 }
412 fullPath = fanPwmPath + pwmName;
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700413 if (mtm.getProperty(fanService, fullPath, fanIntf, "Value",
414 &reply) < 0)
415 {
416 return ipmi::responseInvalidFieldRequest();
417 }
418 double* doubleVal = std::get_if<double>(&reply);
419 if (doubleVal == nullptr)
420 {
421 return ipmi::responseUnspecifiedError();
422 }
423 uint8_t sensorVal = std::round(*doubleVal);
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +0530424 resetMtmTimer(ctx);
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700425 return ipmi::responseSuccess(sensorVal, std::nullopt);
426 }
427 break;
428 case SmSignalGet::smFanTachometerGet:
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700429 {
Richard Marian Thomaiyar147daec2019-06-15 07:43:48 +0530430 boost::system::error_code ec;
431 using objFlatMap = boost::container::flat_map<
432 std::string, boost::container::flat_map<
433 std::string, std::vector<std::string>>>;
434
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +0530435 auto flatMap = ctx->bus->yield_method_call<objFlatMap>(
436 ctx->yield, ec, "xyz.openbmc_project.ObjectMapper",
Richard Marian Thomaiyar147daec2019-06-15 07:43:48 +0530437 "/xyz/openbmc_project/object_mapper",
438 "xyz.openbmc_project.ObjectMapper", "GetSubTree",
439 fanTachBasePath, 0, std::array<const char*, 1>{fanIntf});
440 if (ec)
441 {
442 phosphor::logging::log<phosphor::logging::level::ERR>(
443 "Failed to query fan tach sub tree objects");
444 return ipmi::responseUnspecifiedError();
445 }
446 if (instance >= flatMap.size())
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700447 {
448 return ipmi::responseInvalidFieldRequest();
449 }
Richard Marian Thomaiyar147daec2019-06-15 07:43:48 +0530450 auto itr = flatMap.nth(instance);
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700451 ipmi::Value reply;
Richard Marian Thomaiyar147daec2019-06-15 07:43:48 +0530452 if (mtm.getProperty(fanService, itr->first, fanIntf, "Value",
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700453 &reply) < 0)
454 {
455 return ipmi::responseInvalidFieldRequest();
456 }
457
458 double* doubleVal = std::get_if<double>(&reply);
459 if (doubleVal == nullptr)
460 {
461 return ipmi::responseUnspecifiedError();
462 }
463 uint8_t sensorVal = FAN_PRESENT | FAN_SENSOR_PRESENT;
464 std::optional<uint16_t> fanTach = std::round(*doubleVal);
465
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +0530466 resetMtmTimer(ctx);
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700467 return ipmi::responseSuccess(sensorVal, fanTach);
468 }
469 break;
Richard Marian Thomaiyar8e5e2b02019-08-01 07:50:55 +0530470 case SmSignalGet::smIdentifyButton:
471 {
472 if (action == SmActionGet::revert || action == SmActionGet::ignore)
473 {
474 // ButtonMasked property is not supported for ID button as it is
475 // unnecessary. Hence if requested for revert / ignore, override
476 // it to sample action to make tools happy.
477 action = SmActionGet::sample;
478 }
479 // fall-through
480 }
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700481 case SmSignalGet::smResetButton:
482 case SmSignalGet::smPowerButton:
483 case SmSignalGet::smNMIButton:
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700484 {
485 std::string path;
486 if (getGpioPathForSmSignal(signalType, path) < 0)
487 {
488 return ipmi::responseInvalidFieldRequest();
489 }
490
491 switch (action)
492 {
493 case SmActionGet::sample:
494 phosphor::logging::log<phosphor::logging::level::INFO>(
495 "case SmActionGet::sample");
496 break;
497 case SmActionGet::ignore:
498 {
499 phosphor::logging::log<phosphor::logging::level::INFO>(
500 "case SmActionGet::ignore");
501 if (mtm.setProperty(buttonService, path, buttonIntf,
502 "ButtonMasked", true) < 0)
503 {
504 return ipmi::responseUnspecifiedError();
505 }
506 }
507 break;
508 case SmActionGet::revert:
509 {
510 phosphor::logging::log<phosphor::logging::level::INFO>(
511 "case SmActionGet::revert");
512 if (mtm.setProperty(buttonService, path, buttonIntf,
513 "ButtonMasked", false) < 0)
514 {
515 return ipmi::responseUnspecifiedError();
516 }
517 }
518 break;
519
520 default:
521 return ipmi::responseInvalidFieldRequest();
522 break;
523 }
524
525 ipmi::Value reply;
526 if (mtm.getProperty(buttonService, path, buttonIntf,
527 "ButtonPressed", &reply) < 0)
528 {
529 return ipmi::responseUnspecifiedError();
530 }
531 bool* valPtr = std::get_if<bool>(&reply);
532 if (valPtr == nullptr)
533 {
534 return ipmi::responseUnspecifiedError();
535 }
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +0530536 resetMtmTimer(ctx);
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700537 uint8_t sensorVal = *valPtr;
538 return ipmi::responseSuccess(sensorVal, std::nullopt);
539 }
540 break;
Richard Marian Thomaiyar1b74a212019-08-03 13:26:17 +0530541 case SmSignalGet::smNcsiDiag:
542 {
543 constexpr const char* netBasePath = "/sys/class/net/eth";
544 constexpr const char* carrierSuffix = "/carrier";
545 std::ifstream netIfs(netBasePath + std::to_string(instance) +
546 carrierSuffix);
547 if (!netIfs.good())
548 {
549 return ipmi::responseInvalidFieldRequest();
550 }
551 std::string carrier;
552 netIfs >> carrier;
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +0530553 resetMtmTimer(ctx);
Richard Marian Thomaiyar1b74a212019-08-03 13:26:17 +0530554 return ipmi::responseSuccess(
555 static_cast<uint8_t>(std::stoi(carrier)), std::nullopt);
556 }
557 break;
Vernon Mauerya3702c12019-05-22 13:20:59 -0700558 default:
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700559 return ipmi::responseInvalidFieldRequest();
Vernon Mauerya3702c12019-05-22 13:20:59 -0700560 break;
561 }
Vernon Mauerya3702c12019-05-22 13:20:59 -0700562}
563
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +0530564ipmi::RspType<> appMTMSetSignal(ipmi::Context::ptr ctx, uint8_t signalTypeByte,
565 uint8_t instance, uint8_t actionByte,
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000566 std::optional<uint8_t> pwmSpeed)
Vernon Mauerya3702c12019-05-22 13:20:59 -0700567{
Ayushi Smritie0511e52019-08-27 17:30:34 +0000568 // mfg filter logic is used to allow MTM set signal command only in
569 // manfacturing mode.
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000570
571 SmSignalSet signalType = static_cast<SmSignalSet>(signalTypeByte);
572 SmActionSet action = static_cast<SmActionSet>(actionByte);
573 Cc retCode = ccSuccess;
574 int8_t ret = 0;
575
576 switch (signalType)
577 {
578 case SmSignalSet::smPowerFaultLed:
579 case SmSignalSet::smSystemReadyLed:
580 case SmSignalSet::smIdentifyLed:
581 switch (action)
582 {
583 case SmActionSet::forceDeasserted:
Vernon Mauerya3702c12019-05-22 13:20:59 -0700584 {
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000585 phosphor::logging::log<phosphor::logging::level::INFO>(
586 "case SmActionSet::forceDeasserted");
Vernon Mauerya3702c12019-05-22 13:20:59 -0700587
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000588 retCode = ledStoreAndSet(signalType, std::string("Off"));
589 if (retCode != ccSuccess)
Vernon Mauerya3702c12019-05-22 13:20:59 -0700590 {
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000591 return ipmi::response(retCode);
Vernon Mauerya3702c12019-05-22 13:20:59 -0700592 }
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000593 mtm.revertTimer.start(revertTimeOut);
Vernon Mauerya3702c12019-05-22 13:20:59 -0700594 }
595 break;
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000596 case SmActionSet::forceAsserted:
Vernon Mauerya3702c12019-05-22 13:20:59 -0700597 {
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000598 phosphor::logging::log<phosphor::logging::level::INFO>(
599 "case SmActionSet::forceAsserted");
Vernon Mauerya3702c12019-05-22 13:20:59 -0700600
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000601 retCode = ledStoreAndSet(signalType, std::string("On"));
602 if (retCode != ccSuccess)
603 {
604 return ipmi::response(retCode);
605 }
606 mtm.revertTimer.start(revertTimeOut);
607 if (SmSignalSet::smPowerFaultLed == signalType)
608 {
609 // Deassert "system ready"
610 retCode = ledStoreAndSet(SmSignalSet::smSystemReadyLed,
611 std::string("Off"));
612 }
613 else if (SmSignalSet::smSystemReadyLed == signalType)
614 {
615 // Deassert "fault led"
616 retCode = ledStoreAndSet(SmSignalSet::smPowerFaultLed,
617 std::string("Off"));
618 }
619 }
620 break;
621 case SmActionSet::revert:
622 {
623 phosphor::logging::log<phosphor::logging::level::INFO>(
624 "case SmActionSet::revert");
625 retCode = ledRevert(signalType);
626 }
627 break;
628 default:
629 {
630 return ipmi::responseInvalidFieldRequest();
631 }
632 }
633 break;
634 case SmSignalSet::smFanPowerSpeed:
635 {
636 if ((action == SmActionSet::forceAsserted) && (!pwmSpeed))
637 {
638 return ipmi::responseReqDataLenInvalid();
639 }
640
641 if ((action == SmActionSet::forceAsserted) && (*pwmSpeed > 100))
642 {
643 return ipmi::responseInvalidFieldRequest();
644 }
645
646 uint8_t pwmValue = 0;
647 switch (action)
648 {
649 case SmActionSet::revert:
650 {
651 if (mtm.revertFanPWM)
652 {
653 ret = mtm.disablePidControlService(false);
Vernon Mauerya3702c12019-05-22 13:20:59 -0700654 if (ret < 0)
655 {
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000656 return ipmi::responseUnspecifiedError();
Vernon Mauerya3702c12019-05-22 13:20:59 -0700657 }
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000658 mtm.revertFanPWM = false;
Vernon Mauerya3702c12019-05-22 13:20:59 -0700659 }
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000660 }
661 break;
662 case SmActionSet::forceAsserted:
663 {
664 pwmValue = *pwmSpeed;
665 } // fall-through
666 case SmActionSet::forceDeasserted:
667 {
668 if (!mtm.revertFanPWM)
Vernon Mauerya3702c12019-05-22 13:20:59 -0700669 {
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000670 ret = mtm.disablePidControlService(true);
671 if (ret < 0)
672 {
673 return ipmi::responseUnspecifiedError();
674 }
675 mtm.revertFanPWM = true;
Vernon Mauerya3702c12019-05-22 13:20:59 -0700676 }
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000677 mtm.revertTimer.start(revertTimeOut);
Alex Schendel90eb7872022-09-01 11:56:52 -0700678 std::string pwmName, fanPwmInstancePath;
679 if (!findPwmName(ctx, instance, pwmName))
680 {
681 pwmName = "Pwm_" + std::to_string(instance + 1);
682 }
683 fanPwmInstancePath = fanPwmPath + pwmName;
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000684 ret =
685 mtm.setProperty(fanService, fanPwmInstancePath, fanIntf,
686 "Value", static_cast<double>(pwmValue));
687 if (ret < 0)
688 {
689 return ipmi::responseUnspecifiedError();
690 }
691 }
692 break;
693 default:
694 {
695 return ipmi::responseInvalidFieldRequest();
Vernon Mauerya3702c12019-05-22 13:20:59 -0700696 }
697 }
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000698 }
699 break;
Ayushi Smritid872a4a2019-10-15 10:20:11 +0530700 case SmSignalSet::smSpeaker:
701 {
702 phosphor::logging::log<phosphor::logging::level::INFO>(
703 "Performing Speaker SmActionSet",
704 phosphor::logging::entry("ACTION=%d",
705 static_cast<uint8_t>(action)));
706 switch (action)
707 {
708 case SmActionSet::forceAsserted:
709 {
710 char beepDevName[] = "/dev/input/event0";
711 if (mtm.mtmTestBeepFd != -1)
712 {
713 phosphor::logging::log<phosphor::logging::level::INFO>(
714 "mtm beep device is opened already!");
715 // returning success as already beep is in progress
716 return ipmi::response(retCode);
717 }
718
719 if ((mtm.mtmTestBeepFd =
720 ::open(beepDevName, O_RDWR | O_CLOEXEC)) < 0)
721 {
722 phosphor::logging::log<phosphor::logging::level::ERR>(
723 "Failed to open input device");
724 return ipmi::responseUnspecifiedError();
725 }
726
727 struct input_event event;
728 event.type = EV_SND;
729 event.code = SND_TONE;
730 event.value = 2000;
731
732 if (::write(mtm.mtmTestBeepFd, &event,
733 sizeof(struct input_event)) !=
734 sizeof(struct input_event))
735 {
736 phosphor::logging::log<phosphor::logging::level::ERR>(
737 "Failed to write a tone sound event");
738 ::close(mtm.mtmTestBeepFd);
739 mtm.mtmTestBeepFd = -1;
740 return ipmi::responseUnspecifiedError();
741 }
742 mtm.revertTimer.start(revertTimeOut);
743 }
744 break;
745 case SmActionSet::revert:
746 case SmActionSet::forceDeasserted:
747 {
748 if (mtm.mtmTestBeepFd != -1)
749 {
750 ::close(mtm.mtmTestBeepFd);
751 mtm.mtmTestBeepFd = -1;
752 }
753 }
754 break;
755 default:
756 {
757 return ipmi::responseInvalidFieldRequest();
758 }
759 }
760 }
761 break;
Richard Marian Thomaiyar3594c6d2019-11-19 21:29:04 +0530762 case SmSignalSet::smDiskFaultLed:
763 {
764 boost::system::error_code ec;
765 using objPaths = std::vector<std::string>;
766 std::string driveBasePath =
767 "/xyz/openbmc_project/inventory/item/drive/";
768 static constexpr const char* driveLedIntf =
769 "xyz.openbmc_project.Led.Group";
770 static constexpr const char* hsbpService =
771 "xyz.openbmc_project.HsbpManager";
772
773 auto driveList = ctx->bus->yield_method_call<objPaths>(
774 ctx->yield, ec, "xyz.openbmc_project.ObjectMapper",
775 "/xyz/openbmc_project/object_mapper",
776 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths",
777 driveBasePath, 0, std::array<const char*, 1>{driveLedIntf});
778 if (ec)
779 {
780 phosphor::logging::log<phosphor::logging::level::ERR>(
781 "Failed to query HSBP drive sub tree objects");
782 return ipmi::responseUnspecifiedError();
783 }
784 std::string driveObjPath =
785 driveBasePath + "Drive_" + std::to_string(instance + 1);
786 if (std::find(driveList.begin(), driveList.end(), driveObjPath) ==
787 driveList.end())
788 {
789 return ipmi::responseInvalidFieldRequest();
790 }
791 bool driveLedState = false;
792 switch (action)
793 {
794 case SmActionSet::forceAsserted:
795 {
796 driveLedState = true;
797 }
798 break;
799 case SmActionSet::revert:
800 {
801 driveLedState = false;
802 }
803 break;
804 case SmActionSet::forceDeasserted:
805 {
806 driveLedState = false;
807 }
808 break;
809 default:
810 {
811 return ipmi::responseInvalidFieldRequest();
812 }
813 }
814 ret = mtm.setProperty(hsbpService, driveObjPath, driveLedIntf,
815 "Asserted", driveLedState);
816 if (ret < 0)
817 {
818 return ipmi::responseUnspecifiedError();
819 }
820 }
821 break;
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000822 default:
823 {
824 return ipmi::responseInvalidFieldRequest();
Vernon Mauerya3702c12019-05-22 13:20:59 -0700825 }
826 }
Richard Marian Thomaiyar4cc10152019-08-02 23:21:45 +0530827 if (retCode == ccSuccess)
828 {
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +0530829 resetMtmTimer(ctx);
Richard Marian Thomaiyar4cc10152019-08-02 23:21:45 +0530830 }
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000831 return ipmi::response(retCode);
Vernon Mauerya3702c12019-05-22 13:20:59 -0700832}
833
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +0530834ipmi::RspType<> mtmKeepAlive(ipmi::Context::ptr ctx, uint8_t reserved,
Richard Marian Thomaiyar666dd012019-08-02 20:55:37 +0530835 const std::array<char, 5>& intentionalSignature)
836{
Ayushi Smritie0511e52019-08-27 17:30:34 +0000837 // mfg filter logic is used to allow MTM keep alive command only in
838 // manfacturing mode
839
Richard Marian Thomaiyar666dd012019-08-02 20:55:37 +0530840 constexpr std::array<char, 5> signatureOk = {'I', 'N', 'T', 'E', 'L'};
841 if (intentionalSignature != signatureOk || reserved != 0)
842 {
843 return ipmi::responseInvalidFieldRequest();
844 }
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +0530845 return ipmi::response(resetMtmTimer(ctx));
Richard Marian Thomaiyar666dd012019-08-02 20:55:37 +0530846}
847
Ayushi Smritie0511e52019-08-27 17:30:34 +0000848static constexpr unsigned int makeCmdKey(unsigned int netFn, unsigned int cmd)
849{
850 return (netFn << 8) | cmd;
851}
852
Yong Li85feb132019-08-09 16:06:03 +0800853ipmi::Cc mfgFilterMessage(ipmi::message::Request::ptr request)
854{
Ayushi Smritie0511e52019-08-27 17:30:34 +0000855 // Restricted commands, must be executed only in Manufacturing mode
856 switch (makeCmdKey(request->ctx->netFn, request->ctx->cmd))
Yong Li85feb132019-08-09 16:06:03 +0800857 {
Ayushi Smritie0511e52019-08-27 17:30:34 +0000858 // i2c master write read command needs additional checking
859 case makeCmdKey(ipmi::netFnApp, ipmi::app::cmdMasterWriteRead):
860 if (request->payload.size() > 4)
861 {
Richard Marian Thomaiyarae13ac62019-12-17 15:45:12 +0530862 // Allow write data count > 1 only in Special mode
863 if (mtm.getMfgMode() == SpecialMode::none)
Ayushi Smritie0511e52019-08-27 17:30:34 +0000864 {
865 return ipmi::ccInsufficientPrivilege;
866 }
867 }
jayaprakash Mutyala2d4a0192019-11-11 22:12:45 +0000868 return ipmi::ccSuccess;
Ayushi Smritie0511e52019-08-27 17:30:34 +0000869 case makeCmdKey(ipmi::netFnOemOne,
870 ipmi::intel::general::cmdGetSmSignal):
871 case makeCmdKey(ipmi::netFnOemOne,
872 ipmi::intel::general::cmdSetSmSignal):
873 case makeCmdKey(ipmi::netFnOemOne,
874 ipmi::intel::general::cmdMtmKeepAlive):
875 case makeCmdKey(ipmi::netFnOemOne,
876 ipmi::intel::general::cmdSetManufacturingData):
877 case makeCmdKey(ipmi::netFnOemOne,
878 ipmi::intel::general::cmdGetManufacturingData):
Vernon Mauery27d23562021-08-12 10:43:39 -0700879 case makeCmdKey(ipmi::intel::netFnGeneral,
880 ipmi::intel::general::cmdSetFITcLayout):
Arun P. Mohanan06584cd2021-08-13 20:56:01 +0530881 case makeCmdKey(ipmi::netFnOemOne,
882 ipmi::intel::general::cmdMTMBMCFeatureControl):
Ayushi Smritie0511e52019-08-27 17:30:34 +0000883 case makeCmdKey(ipmi::netFnStorage, ipmi::storage::cmdWriteFruData):
AppaRao Puli9a13daa2020-07-13 17:53:00 +0530884 case makeCmdKey(ipmi::netFnOemTwo, ipmi::intel::platform::cmdClearCMOS):
Ayushi Smritie0511e52019-08-27 17:30:34 +0000885
Richard Marian Thomaiyarae13ac62019-12-17 15:45:12 +0530886 // Check for Special mode
887 if (mtm.getMfgMode() == SpecialMode::none)
Yong Li85feb132019-08-09 16:06:03 +0800888 {
Ayushi Smritie0511e52019-08-27 17:30:34 +0000889 return ipmi::ccInvalidCommand;
Yong Li85feb132019-08-09 16:06:03 +0800890 }
jayaprakash Mutyala2d4a0192019-11-11 22:12:45 +0000891 return ipmi::ccSuccess;
892 case makeCmdKey(ipmi::netFnStorage, ipmi::storage::cmdDeleteSelEntry):
893 {
894 return ipmi::ccInvalidCommand;
895 }
Yong Li85feb132019-08-09 16:06:03 +0800896 }
Yong Li85feb132019-08-09 16:06:03 +0800897 return ipmi::ccSuccess;
898}
899
Richard Marian Thomaiyar1f0839c2019-08-25 20:10:52 +0530900static constexpr uint8_t maxEthSize = 6;
901static constexpr uint8_t maxSupportedEth = 3;
902static constexpr const char* factoryEthAddrBaseFileName =
903 "/var/sofs/factory-settings/network/mac/eth";
904
Zhikui Renad129c62022-04-05 20:11:24 -0700905using ObjectType = boost::container::flat_map<
906 std::string, boost::container::flat_map<std::string, DbusVariant>>;
907using ManagedObjectType =
908 boost::container::flat_map<sdbusplus::message::object_path, ObjectType>;
909
910bool findFruDevice(const std::shared_ptr<sdbusplus::asio::connection>& bus,
911 boost::asio::yield_context& yield, uint64_t& macOffset,
912 uint64_t& busNum, uint64_t& address)
913{
914 boost::system::error_code ec;
915
916 // GetAll the objects under service FruDevice
917 ec = boost::system::errc::make_error_code(boost::system::errc::success);
918 auto obj = bus->yield_method_call<ManagedObjectType>(
Nan Zhou9d2894d2022-09-20 22:22:00 +0000919 yield, ec, "xyz.openbmc_project.EntityManager",
920 "/xyz/openbmc_project/inventory", "org.freedesktop.DBus.ObjectManager",
921 "GetManagedObjects");
Zhikui Renad129c62022-04-05 20:11:24 -0700922 if (ec)
923 {
924 phosphor::logging::log<phosphor::logging::level::ERR>(
925 "GetMangagedObjects failed",
926 phosphor::logging::entry("ERROR=%s", ec.message().c_str()));
927 return false;
928 }
929
930 for (const auto& [path, fru] : obj)
931 {
932 for (const auto& [intf, propMap] : fru)
933 {
934 if (intf == "xyz.openbmc_project.Inventory.Item.Board.Motherboard")
935 {
936 auto findBus = propMap.find("FruBus");
937 auto findAddress = propMap.find("FruAddress");
938 auto findMacOffset = propMap.find("MacOffset");
939 if (findBus == propMap.end() || findAddress == propMap.end() ||
940 findMacOffset == propMap.end())
941 {
942 continue;
943 }
944
945 auto fruBus = std::get_if<uint64_t>(&findBus->second);
946 auto fruAddress = std::get_if<uint64_t>(&findAddress->second);
947 auto macFruOffset =
948 std::get_if<uint64_t>(&findMacOffset->second);
949 if (!fruBus || !fruAddress || !macFruOffset)
950 {
951 phosphor::logging::log<phosphor::logging::level::INFO>(
952 "ERROR: MotherBoard FRU config data type invalid, not "
953 "used");
954 return false;
955 }
956 busNum = *fruBus;
957 address = *fruAddress;
958 macOffset = *macFruOffset;
959 return true;
960 }
961 }
962 }
963 return false;
964}
965
Zhikui Ren98fa87e2022-05-04 08:10:29 -0700966static constexpr uint64_t fruEnd = 0xff;
967// write rolls over within current page, need to keep mac within a page
968static constexpr uint64_t fruPageSize = 0x8;
969// MAC record struct: HEADER, MAC DATA, CheckSum
970static constexpr uint64_t macRecordSize = maxEthSize + 2;
971static_assert(fruPageSize >= macRecordSize,
972 "macRecordSize greater than eeprom page size");
973static constexpr uint8_t macHeader = 0x40;
974// Calculate new checksum for fru info area
975static uint8_t calculateChecksum(std::vector<uint8_t>::const_iterator iter,
976 std::vector<uint8_t>::const_iterator end)
977{
978 constexpr int checksumMod = 256;
979 uint8_t sum = std::accumulate(iter, end, static_cast<uint8_t>(0));
980 return (checksumMod - sum) % checksumMod;
981}
982
Zhikui Renad129c62022-04-05 20:11:24 -0700983bool readMacFromFru(ipmi::Context::ptr ctx, uint8_t macIndex,
984 std::array<uint8_t, maxEthSize>& ethData)
985{
Zhikui Renad129c62022-04-05 20:11:24 -0700986 uint64_t macOffset = fruEnd;
987 uint64_t fruBus = 0;
988 uint64_t fruAddress = 0;
989
990 if (findFruDevice(ctx->bus, ctx->yield, macOffset, fruBus, fruAddress))
991 {
992 phosphor::logging::log<phosphor::logging::level::INFO>(
993 "Found mac fru",
994 phosphor::logging::entry("BUS=%d", static_cast<uint8_t>(fruBus)),
995 phosphor::logging::entry("ADDRESS=%d",
996 static_cast<uint8_t>(fruAddress)));
997
Zhikui Ren98fa87e2022-05-04 08:10:29 -0700998 if (macOffset % fruPageSize)
999 {
1000 macOffset = (macOffset / fruPageSize + 1) * fruPageSize;
1001 }
1002 macOffset += macIndex * fruPageSize;
1003 if ((macOffset + macRecordSize) > fruEnd)
Zhikui Renad129c62022-04-05 20:11:24 -07001004 {
1005 phosphor::logging::log<phosphor::logging::level::ERR>(
1006 "ERROR: read fru mac failed, offset invalid");
1007 return false;
1008 }
1009 std::vector<uint8_t> writeData;
Zhikui Ren98fa87e2022-05-04 08:10:29 -07001010 writeData.push_back(static_cast<uint8_t>(macOffset));
1011 std::vector<uint8_t> readBuf(macRecordSize);
Zhikui Renad129c62022-04-05 20:11:24 -07001012 std::string i2cBus = "/dev/i2c-" + std::to_string(fruBus);
1013 ipmi::Cc retI2C =
1014 ipmi::i2cWriteRead(i2cBus, fruAddress, writeData, readBuf);
1015 if (retI2C == ipmi::ccSuccess)
1016 {
Zhikui Ren98fa87e2022-05-04 08:10:29 -07001017 uint8_t cs = calculateChecksum(readBuf.cbegin(), readBuf.cend());
1018 if (cs == 0)
1019 {
1020 std::copy(++readBuf.begin(), --readBuf.end(), ethData.data());
1021 return true;
1022 }
Zhikui Renad129c62022-04-05 20:11:24 -07001023 }
1024 }
1025 return false;
1026}
1027
1028ipmi::Cc writeMacToFru(ipmi::Context::ptr ctx, uint8_t macIndex,
1029 std::array<uint8_t, maxEthSize>& ethData)
1030{
Zhikui Renad129c62022-04-05 20:11:24 -07001031 uint64_t macOffset = fruEnd;
1032 uint64_t fruBus = 0;
1033 uint64_t fruAddress = 0;
1034
1035 if (findFruDevice(ctx->bus, ctx->yield, macOffset, fruBus, fruAddress))
1036 {
1037 phosphor::logging::log<phosphor::logging::level::INFO>(
1038 "Found mac fru",
1039 phosphor::logging::entry("BUS=%d", static_cast<uint8_t>(fruBus)),
1040 phosphor::logging::entry("ADDRESS=%d",
1041 static_cast<uint8_t>(fruAddress)));
1042
Zhikui Ren98fa87e2022-05-04 08:10:29 -07001043 if (macOffset % fruPageSize)
1044 {
1045 macOffset = (macOffset / fruPageSize + 1) * fruPageSize;
1046 }
1047 macOffset += macIndex * fruPageSize;
1048 if ((macOffset + macRecordSize) > fruEnd)
Zhikui Renad129c62022-04-05 20:11:24 -07001049 {
1050 phosphor::logging::log<phosphor::logging::level::ERR>(
1051 "ERROR: write mac fru failed, offset invalid.");
1052 return ipmi::ccParmOutOfRange;
1053 }
1054 std::vector<uint8_t> writeData;
Zhikui Ren98fa87e2022-05-04 08:10:29 -07001055 writeData.reserve(macRecordSize + 1); // include start location
1056 writeData.push_back(static_cast<uint8_t>(macOffset));
1057 writeData.push_back(macHeader);
Zhikui Renad129c62022-04-05 20:11:24 -07001058 std::for_each(ethData.cbegin(), ethData.cend(),
1059 [&](uint8_t i) { writeData.push_back(i); });
Zhikui Ren98fa87e2022-05-04 08:10:29 -07001060 uint8_t macCheckSum =
1061 calculateChecksum(++writeData.cbegin(), writeData.cend());
1062 writeData.push_back(macCheckSum);
Zhikui Renad129c62022-04-05 20:11:24 -07001063
1064 std::string i2cBus = "/dev/i2c-" + std::to_string(fruBus);
1065 std::vector<uint8_t> readBuf;
1066 ipmi::Cc ret =
1067 ipmi::i2cWriteRead(i2cBus, fruAddress, writeData, readBuf);
Zhikui Ren98fa87e2022-05-04 08:10:29 -07001068
Zhikui Ren0408e792022-06-07 21:03:33 -07001069 // prepare for read to detect chip is write protected
1070 writeData.resize(1);
1071 readBuf.resize(maxEthSize + 1); // include macHeader
1072
Zhikui Renad129c62022-04-05 20:11:24 -07001073 switch (ret)
1074 {
1075 case ipmi::ccSuccess:
Zhikui Ren98fa87e2022-05-04 08:10:29 -07001076 // Wait for internal write cycle to complete
1077 // example: ATMEL 24c0x chip has Twr spec as 5ms
1078
1079 // Ideally we want yield wait, but currently following code
1080 // crash with "thread not supported"
1081 // boost::asio::deadline_timer timer(
1082 // boost::asio::get_associated_executor(ctx->yield),
1083 // boost::posix_time::seconds(1));
1084 // timer.async_wait(ctx->yield);
1085 // use usleep as temp WA
1086 usleep(5000);
1087 if (ipmi::i2cWriteRead(i2cBus, fruAddress, writeData,
1088 readBuf) == ipmi::ccSuccess)
Zhikui Renad129c62022-04-05 20:11:24 -07001089 {
1090 if (std::equal(ethData.begin(), ethData.end(),
Zhikui Ren98fa87e2022-05-04 08:10:29 -07001091 ++readBuf.begin())) // skip macHeader
Zhikui Renad129c62022-04-05 20:11:24 -07001092 {
1093 return ipmi::ccSuccess;
1094 }
1095 phosphor::logging::log<phosphor::logging::level::INFO>(
1096 "INFO: write mac fru verify failed, fru may be write "
1097 "protected.");
1098 }
1099 return ipmi::ccCommandNotAvailable;
Zhikui Ren0408e792022-06-07 21:03:33 -07001100 default:
1101 if (ipmi::i2cWriteRead(i2cBus, fruAddress, writeData,
1102 readBuf) == ipmi::ccSuccess)
1103 {
1104 phosphor::logging::log<phosphor::logging::level::INFO>(
1105 "INFO: write mac fru failed, but successfully read "
1106 "from fru, fru may be write protected.");
1107 return ipmi::ccCommandNotAvailable;
1108 }
1109 else // assume failure is due to no eeprom on board
1110 {
1111 phosphor::logging::log<phosphor::logging::level::ERR>(
1112 "ERROR: write mac fru failed, assume no eeprom is "
1113 "available.");
1114 }
Zhikui Renad129c62022-04-05 20:11:24 -07001115 break;
1116 }
1117 }
1118 // no FRU eeprom found
1119 return ipmi::ccDestinationUnavailable;
1120}
1121
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +05301122ipmi::RspType<> setManufacturingData(ipmi::Context::ptr ctx, uint8_t dataType,
Richard Marian Thomaiyar1f0839c2019-08-25 20:10:52 +05301123 std::array<uint8_t, maxEthSize> ethData)
1124{
Zhikui Renad129c62022-04-05 20:11:24 -07001125 // mfg filter logic will restrict this command executing only in mfg
1126 // mode.
Richard Marian Thomaiyar1f0839c2019-08-25 20:10:52 +05301127 if (dataType >= maxSupportedEth)
1128 {
1129 return ipmi::responseParmOutOfRange();
1130 }
1131
1132 constexpr uint8_t invalidData = 0;
1133 constexpr uint8_t validData = 1;
Zhikui Renad129c62022-04-05 20:11:24 -07001134
1135 ipmi::Cc ret = writeMacToFru(ctx, dataType, ethData);
1136 if (ret != ipmi::ccDestinationUnavailable)
1137 {
1138 resetMtmTimer(ctx);
1139 return response(ret);
1140 }
1141
Richard Marian Thomaiyar1f0839c2019-08-25 20:10:52 +05301142 constexpr uint8_t ethAddrStrSize =
1143 19; // XX:XX:XX:XX:XX:XX + \n + null termination;
1144 std::vector<uint8_t> buff(ethAddrStrSize);
1145 std::snprintf(reinterpret_cast<char*>(buff.data()), ethAddrStrSize,
1146 "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx\n", ethData.at(0),
1147 ethData.at(1), ethData.at(2), ethData.at(3), ethData.at(4),
1148 ethData.at(5));
1149 std::ofstream oEthFile(factoryEthAddrBaseFileName +
1150 std::to_string(dataType),
1151 std::ofstream::out);
1152 if (!oEthFile.good())
1153 {
1154 return ipmi::responseUnspecifiedError();
1155 }
1156
1157 oEthFile << reinterpret_cast<char*>(buff.data());
Johnathan Mantey6d83e4e2020-05-08 11:01:01 -07001158 oEthFile.flush();
Richard Marian Thomaiyar1f0839c2019-08-25 20:10:52 +05301159 oEthFile.close();
1160
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +05301161 resetMtmTimer(ctx);
Richard Marian Thomaiyar1f0839c2019-08-25 20:10:52 +05301162 return ipmi::responseSuccess();
1163}
1164
1165ipmi::RspType<uint8_t, std::array<uint8_t, maxEthSize>>
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +05301166 getManufacturingData(ipmi::Context::ptr ctx, uint8_t dataType)
Richard Marian Thomaiyar1f0839c2019-08-25 20:10:52 +05301167{
Zhikui Renad129c62022-04-05 20:11:24 -07001168 // mfg filter logic will restrict this command executing only in mfg
1169 // mode.
Richard Marian Thomaiyar1f0839c2019-08-25 20:10:52 +05301170 if (dataType >= maxSupportedEth)
1171 {
1172 return ipmi::responseParmOutOfRange();
1173 }
1174 std::array<uint8_t, maxEthSize> ethData{0};
1175 constexpr uint8_t invalidData = 0;
1176 constexpr uint8_t validData = 1;
1177
1178 std::ifstream iEthFile(factoryEthAddrBaseFileName +
1179 std::to_string(dataType),
1180 std::ifstream::in);
1181 if (!iEthFile.good())
1182 {
Zhikui Renad129c62022-04-05 20:11:24 -07001183 if (readMacFromFru(ctx, dataType, ethData))
1184 {
1185 resetMtmTimer(ctx);
1186 return ipmi::responseSuccess(validData, ethData);
1187 }
Richard Marian Thomaiyar1f0839c2019-08-25 20:10:52 +05301188 return ipmi::responseSuccess(invalidData, ethData);
1189 }
1190 std::string ethStr;
1191 iEthFile >> ethStr;
1192 uint8_t* data = ethData.data();
1193 std::sscanf(ethStr.c_str(), "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx",
1194 data, (data + 1), (data + 2), (data + 3), (data + 4),
1195 (data + 5));
1196
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +05301197 resetMtmTimer(ctx);
Richard Marian Thomaiyar1f0839c2019-08-25 20:10:52 +05301198 return ipmi::responseSuccess(validData, ethData);
1199}
1200
Zhikui Renad129c62022-04-05 20:11:24 -07001201/** @brief implements slot master write read IPMI command which can be used
1202 * for low-level I2C/SMBus write, read or write-read access for PCIE slots
Yong Lif267a672019-08-29 11:16:07 +08001203 * @param reserved - skip 6 bit
1204 * @param addressType - address type
1205 * @param bbSlotNum - baseboard slot number
1206 * @param riserSlotNum - riser slot number
1207 * @param reserved2 - skip 2 bit
1208 * @param slaveAddr - slave address
1209 * @param readCount - number of bytes to be read
1210 * @param writeData - data to be written
1211 *
1212 * @returns IPMI completion code plus response data
1213 */
1214ipmi::RspType<std::vector<uint8_t>>
1215 appSlotI2CMasterWriteRead(uint6_t reserved, uint2_t addressType,
1216 uint3_t bbSlotNum, uint3_t riserSlotNum,
Vernon Maueryfc3bc382022-06-02 13:52:04 -07001217 uint2_t reserved2, uint8_t slaveAddr,
Yong Lif267a672019-08-29 11:16:07 +08001218 uint8_t readCount, std::vector<uint8_t> writeData)
1219{
Vernon Maueryfc3bc382022-06-02 13:52:04 -07001220 if (reserved || reserved2)
1221 {
1222 return ipmi::responseInvalidFieldRequest();
1223 }
Yong Lif267a672019-08-29 11:16:07 +08001224 const size_t writeCount = writeData.size();
1225 std::string i2cBus;
1226 if (addressType == slotAddressTypeBus)
1227 {
1228 std::string path = "/dev/i2c-mux/Riser_" +
1229 std::to_string(static_cast<uint8_t>(bbSlotNum)) +
1230 "_Mux/Pcie_Slot_" +
1231 std::to_string(static_cast<uint8_t>(riserSlotNum));
1232
1233 if (std::filesystem::exists(path) && std::filesystem::is_symlink(path))
1234 {
1235 i2cBus = std::filesystem::read_symlink(path);
1236 }
1237 else
1238 {
1239 phosphor::logging::log<phosphor::logging::level::ERR>(
1240 "Master write read command: Cannot get BusID");
1241 return ipmi::responseInvalidFieldRequest();
1242 }
1243 }
1244 else if (addressType == slotAddressTypeUniqueid)
1245 {
1246 i2cBus = "/dev/i2c-" +
1247 std::to_string(static_cast<uint8_t>(bbSlotNum) |
1248 (static_cast<uint8_t>(riserSlotNum) << 3));
1249 }
1250 else
1251 {
1252 phosphor::logging::log<phosphor::logging::level::ERR>(
1253 "Master write read command: invalid request");
1254 return ipmi::responseInvalidFieldRequest();
1255 }
1256
Zhikui Renad129c62022-04-05 20:11:24 -07001257 // Allow single byte write as it is offset byte to read the data, rest
1258 // allow only in Special mode.
Yong Lif267a672019-08-29 11:16:07 +08001259 if (writeCount > 1)
1260 {
Richard Marian Thomaiyarae13ac62019-12-17 15:45:12 +05301261 if (mtm.getMfgMode() == SpecialMode::none)
Yong Lif267a672019-08-29 11:16:07 +08001262 {
1263 return ipmi::responseInsufficientPrivilege();
1264 }
1265 }
1266
1267 if (readCount > slotI2CMaxReadSize)
1268 {
1269 phosphor::logging::log<phosphor::logging::level::ERR>(
1270 "Master write read command: Read count exceeds limit");
1271 return ipmi::responseParmOutOfRange();
1272 }
1273
1274 if (!readCount && !writeCount)
1275 {
1276 phosphor::logging::log<phosphor::logging::level::ERR>(
1277 "Master write read command: Read & write count are 0");
1278 return ipmi::responseInvalidFieldRequest();
1279 }
1280
1281 std::vector<uint8_t> readBuf(readCount);
1282
1283 ipmi::Cc retI2C = ipmi::i2cWriteRead(i2cBus, slaveAddr, writeData, readBuf);
1284 if (retI2C != ipmi::ccSuccess)
1285 {
1286 return ipmi::response(retI2C);
1287 }
1288
1289 return ipmi::responseSuccess(readBuf);
1290}
Yong Li068b4f22019-09-17 16:32:18 +08001291
1292ipmi::RspType<> clearCMOS()
1293{
Zhikui Renad129c62022-04-05 20:11:24 -07001294 // There is an i2c device on bus 4, the slave address is 0x38. Based on
1295 // the spec, writing 0x1 to address 0x61 on this device, will trigger
1296 // the clear CMOS action.
Yong Lid0d010b2019-11-18 12:15:21 +08001297 constexpr uint8_t slaveAddr = 0x38;
Yong Li068b4f22019-09-17 16:32:18 +08001298 std::string i2cBus = "/dev/i2c-4";
Yong Lieaeb6cb2020-03-09 20:21:50 +08001299 std::vector<uint8_t> writeData = {0x61, 0x1};
Yong Li068b4f22019-09-17 16:32:18 +08001300 std::vector<uint8_t> readBuf(0);
1301
Yong Li068b4f22019-09-17 16:32:18 +08001302 ipmi::Cc retI2C = ipmi::i2cWriteRead(i2cBus, slaveAddr, writeData, readBuf);
1303 return ipmi::response(retI2C);
1304}
Vernon Mauery27d23562021-08-12 10:43:39 -07001305
1306ipmi::RspType<> setFITcLayout(uint32_t layout)
1307{
1308 static constexpr const char* factoryFITcLayout =
1309 "/var/sofs/factory-settings/layout/fitc";
1310 std::filesystem::path fitcDir =
1311 std::filesystem::path(factoryFITcLayout).parent_path();
1312 std::error_code ec;
1313 std::filesystem::create_directories(fitcDir, ec);
1314 if (ec)
1315 {
1316 return ipmi::responseUnspecifiedError();
1317 }
1318 try
1319 {
1320 std::ofstream file(factoryFITcLayout);
1321 file << layout;
1322 file.flush();
1323 file.close();
1324 }
1325 catch (const std::exception& e)
1326 {
1327 return ipmi::responseUnspecifiedError();
1328 }
1329
1330 return ipmi::responseSuccess();
1331}
1332
Arun P. Mohanan06584cd2021-08-13 20:56:01 +05301333static std::vector<std::string>
1334 getMCTPServiceConfigPaths(ipmi::Context::ptr& ctx)
1335{
1336 boost::system::error_code ec;
1337 auto configPaths = ctx->bus->yield_method_call<std::vector<std::string>>(
1338 ctx->yield, ec, "xyz.openbmc_project.ObjectMapper",
1339 "/xyz/openbmc_project/object_mapper",
1340 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths",
1341 "/xyz/openbmc_project/inventory/system/board", 2,
1342 std::array<const char*, 1>{
1343 "xyz.openbmc_project.Configuration.MctpConfiguration"});
1344 if (ec)
1345 {
1346 throw std::runtime_error(
1347 "Failed to query configuration sub tree objects");
1348 }
1349 return configPaths;
1350}
1351
1352static ipmi::RspType<> startOrStopService(ipmi::Context::ptr& ctx,
1353 const uint8_t enable,
ANJALI RAY1fe485c2021-12-17 16:41:30 +00001354 const std::string& serviceName,
1355 bool disableOrEnableUnitFiles = true)
Arun P. Mohanan06584cd2021-08-13 20:56:01 +05301356{
1357 constexpr bool runtimeOnly = false;
1358 constexpr bool force = false;
1359
1360 boost::system::error_code ec;
1361 switch (enable)
1362 {
1363 case ipmi::SupportedFeatureActions::stop:
1364 ctx->bus->yield_method_call(ctx->yield, ec, systemDService,
1365 systemDObjPath, systemDMgrIntf,
1366 "StopUnit", serviceName, "replace");
1367 break;
1368 case ipmi::SupportedFeatureActions::start:
1369 ctx->bus->yield_method_call(ctx->yield, ec, systemDService,
1370 systemDObjPath, systemDMgrIntf,
1371 "StartUnit", serviceName, "replace");
1372 break;
1373 case ipmi::SupportedFeatureActions::disable:
ANJALI RAY1fe485c2021-12-17 16:41:30 +00001374 if (disableOrEnableUnitFiles == true)
1375 {
1376 ctx->bus->yield_method_call(
1377 ctx->yield, ec, systemDService, systemDObjPath,
1378 systemDMgrIntf, "DisableUnitFiles",
1379 std::array<const char*, 1>{serviceName.c_str()},
1380 runtimeOnly);
1381 }
Arun P. Mohanan06584cd2021-08-13 20:56:01 +05301382 ctx->bus->yield_method_call(
1383 ctx->yield, ec, systemDService, systemDObjPath, systemDMgrIntf,
1384 "MaskUnitFiles",
1385 std::array<const char*, 1>{serviceName.c_str()}, runtimeOnly,
1386 force);
Arun P. Mohanan06584cd2021-08-13 20:56:01 +05301387 break;
1388 case ipmi::SupportedFeatureActions::enable:
1389 ctx->bus->yield_method_call(
1390 ctx->yield, ec, systemDService, systemDObjPath, systemDMgrIntf,
1391 "UnmaskUnitFiles",
1392 std::array<const char*, 1>{serviceName.c_str()}, runtimeOnly);
ANJALI RAY1fe485c2021-12-17 16:41:30 +00001393 if (disableOrEnableUnitFiles == true)
1394 {
1395 ctx->bus->yield_method_call(
1396 ctx->yield, ec, systemDService, systemDObjPath,
1397 systemDMgrIntf, "EnableUnitFiles",
1398 std::array<const char*, 1>{serviceName.c_str()},
1399 runtimeOnly, force);
1400 }
Arun P. Mohanan06584cd2021-08-13 20:56:01 +05301401 break;
1402 default:
1403 phosphor::logging::log<phosphor::logging::level::WARNING>(
1404 "ERROR: Invalid feature action selected",
1405 phosphor::logging::entry("ACTION=%d", enable));
1406 return ipmi::responseInvalidFieldRequest();
1407 }
1408 if (ec)
1409 {
1410 phosphor::logging::log<phosphor::logging::level::WARNING>(
1411 "ERROR: Service start or stop failed",
1412 phosphor::logging::entry("SERVICE=%s", serviceName.c_str()));
1413 return ipmi::responseUnspecifiedError();
1414 }
1415 return ipmi::responseSuccess();
1416}
1417
1418static std::string getMCTPServiceName(const std::string& objectPath)
1419{
1420 const auto serviceArgument = boost::algorithm::replace_all_copy(
1421 boost::algorithm::replace_first_copy(
1422 objectPath, "/xyz/openbmc_project/inventory/system/board/", ""),
1423 "/", "_2f");
1424 std::string unitName =
1425 "xyz.openbmc_project.mctpd@" + serviceArgument + ".service";
1426 return unitName;
1427}
1428
1429static ipmi::RspType<> handleMCTPFeature(ipmi::Context::ptr& ctx,
1430 const uint8_t enable,
1431 const std::string& binding)
1432{
1433 std::vector<std::string> configPaths;
1434 try
1435 {
1436 configPaths = getMCTPServiceConfigPaths(ctx);
1437 }
1438 catch (const std::exception& e)
1439 {
1440 phosphor::logging::log<phosphor::logging::level::ERR>(e.what());
1441 return ipmi::responseUnspecifiedError();
1442 }
1443
1444 for (const auto& objectPath : configPaths)
1445 {
1446 auto const pos = objectPath.find_last_of('/');
1447 if (binding == objectPath.substr(pos + 1))
1448 {
1449 return startOrStopService(ctx, enable,
ANJALI RAY1fe485c2021-12-17 16:41:30 +00001450 getMCTPServiceName(objectPath), false);
Arun P. Mohanan06584cd2021-08-13 20:56:01 +05301451 }
1452 }
1453 return ipmi::responseSuccess();
1454}
1455
1456/** @brief implements MTM BMC Feature Control IPMI command which can be
1457 * used to enable or disable the supported BMC features.
1458 * @param yield - context object that represents the currently executing
1459 * coroutine
1460 * @param feature - feature enum to enable or disable
1461 * @param enable - enable or disable the feature
1462 * @param featureArg - custom arguments for that feature
1463 * @param reserved - reserved for future use
1464 *
1465 * @returns IPMI completion code
1466 */
1467ipmi::RspType<> mtmBMCFeatureControl(ipmi::Context::ptr ctx,
1468 const uint8_t feature,
1469 const uint8_t enable,
1470 const uint8_t featureArg,
1471 const uint16_t reserved)
1472{
1473 if (reserved != 0)
1474 {
1475 return ipmi::responseInvalidFieldRequest();
1476 }
1477
1478 switch (feature)
1479 {
1480 case ipmi::SupportedFeatureControls::mctp:
1481 switch (featureArg)
1482 {
1483 case ipmi::SupportedMCTPBindings::mctpPCIe:
1484 return handleMCTPFeature(ctx, enable, "MCTP_PCIe");
1485 case ipmi::SupportedMCTPBindings::mctpSMBusHSBP:
1486 return handleMCTPFeature(ctx, enable, "MCTP_SMBus_HSBP");
1487 case ipmi::SupportedMCTPBindings::mctpSMBusPCIeSlot:
1488 return handleMCTPFeature(ctx, enable,
1489 "MCTP_SMBus_PCIe_slot");
1490 default:
1491 return ipmi::responseInvalidFieldRequest();
1492 }
1493 break;
Jason M. Bills5cb2c042021-08-17 12:03:39 -07001494 case ipmi::SupportedFeatureControls::pcieScan:
1495 if (featureArg != 0)
1496 {
1497 return ipmi::responseInvalidFieldRequest();
1498 }
1499 startOrStopService(ctx, enable, "xyz.openbmc_project.PCIe.service");
1500 break;
Arun P. Mohanan06584cd2021-08-13 20:56:01 +05301501 default:
1502 return ipmi::responseInvalidFieldRequest();
1503 }
1504 return ipmi::responseSuccess();
1505}
Vernon Mauerya3702c12019-05-22 13:20:59 -07001506} // namespace ipmi
1507
1508void register_mtm_commands() __attribute__((constructor));
1509void register_mtm_commands()
1510{
Jason M. Bills38d2b5a2019-06-03 16:26:11 -07001511 // <Get SM Signal>
Vernon Mauery98bbf692019-09-16 11:14:59 -07001512 ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnGeneral,
1513 ipmi::intel::general::cmdGetSmSignal,
1514 ipmi::Privilege::Admin, ipmi::appMTMGetSignal);
Vernon Mauerya3702c12019-05-22 13:20:59 -07001515
Vernon Mauery98bbf692019-09-16 11:14:59 -07001516 ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnGeneral,
1517 ipmi::intel::general::cmdSetSmSignal,
1518 ipmi::Privilege::Admin, ipmi::appMTMSetSignal);
Vernon Mauerya3702c12019-05-22 13:20:59 -07001519
Vernon Mauery98bbf692019-09-16 11:14:59 -07001520 ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnGeneral,
1521 ipmi::intel::general::cmdMtmKeepAlive,
1522 ipmi::Privilege::Admin, ipmi::mtmKeepAlive);
Richard Marian Thomaiyar666dd012019-08-02 20:55:37 +05301523
Vernon Mauery98bbf692019-09-16 11:14:59 -07001524 ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnGeneral,
1525 ipmi::intel::general::cmdSetManufacturingData,
1526 ipmi::Privilege::Admin, ipmi::setManufacturingData);
Richard Marian Thomaiyar1f0839c2019-08-25 20:10:52 +05301527
Vernon Mauery98bbf692019-09-16 11:14:59 -07001528 ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnGeneral,
1529 ipmi::intel::general::cmdGetManufacturingData,
1530 ipmi::Privilege::Admin, ipmi::getManufacturingData);
Richard Marian Thomaiyar1f0839c2019-08-25 20:10:52 +05301531
Vernon Mauery27d23562021-08-12 10:43:39 -07001532 ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnGeneral,
1533 ipmi::intel::general::cmdSetFITcLayout,
1534 ipmi::Privilege::Admin, ipmi::setFITcLayout);
1535
Arun P. Mohanan06584cd2021-08-13 20:56:01 +05301536 ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnGeneral,
1537 ipmi::intel::general::cmdMTMBMCFeatureControl,
1538 ipmi::Privilege::Admin, ipmi::mtmBMCFeatureControl);
1539
Vernon Mauery98bbf692019-09-16 11:14:59 -07001540 ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnApp,
1541 ipmi::intel::general::cmdSlotI2CMasterWriteRead,
1542 ipmi::Privilege::Admin,
1543 ipmi::appSlotI2CMasterWriteRead);
Yong Lif267a672019-08-29 11:16:07 +08001544
Yong Li068b4f22019-09-17 16:32:18 +08001545 ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnPlatform,
1546 ipmi::intel::platform::cmdClearCMOS,
1547 ipmi::Privilege::Admin, ipmi::clearCMOS);
1548
Vernon Mauery98bbf692019-09-16 11:14:59 -07001549 ipmi::registerFilter(ipmi::prioOemBase,
Yong Li85feb132019-08-09 16:06:03 +08001550 [](ipmi::message::Request::ptr request) {
1551 return ipmi::mfgFilterMessage(request);
1552 });
Vernon Mauerya3702c12019-05-22 13:20:59 -07001553}