blob: 09b6dabd2ee303b0a3642f4b1e08ca95373e286c [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
Alex Schendel097497f2022-10-07 14:37:15 -0700905bool findFruDevice(ipmi::Context::ptr& ctx, uint64_t& macOffset,
Zhikui Renad129c62022-04-05 20:11:24 -0700906 uint64_t& busNum, uint64_t& address)
907{
Alex Schendel097497f2022-10-07 14:37:15 -0700908 boost::system::error_code ec{};
909 ObjectValueTree obj;
Zhikui Renad129c62022-04-05 20:11:24 -0700910
911 // GetAll the objects under service FruDevice
Alex Schendel097497f2022-10-07 14:37:15 -0700912 ec = getManagedObjects(ctx, "xyz.openbmc_project.EntityManager",
913 "/xyz/openbmc_project/inventory", obj);
Zhikui Renad129c62022-04-05 20:11:24 -0700914 if (ec)
915 {
916 phosphor::logging::log<phosphor::logging::level::ERR>(
917 "GetMangagedObjects failed",
918 phosphor::logging::entry("ERROR=%s", ec.message().c_str()));
919 return false;
920 }
921
922 for (const auto& [path, fru] : obj)
923 {
924 for (const auto& [intf, propMap] : fru)
925 {
926 if (intf == "xyz.openbmc_project.Inventory.Item.Board.Motherboard")
927 {
928 auto findBus = propMap.find("FruBus");
929 auto findAddress = propMap.find("FruAddress");
930 auto findMacOffset = propMap.find("MacOffset");
931 if (findBus == propMap.end() || findAddress == propMap.end() ||
932 findMacOffset == propMap.end())
933 {
934 continue;
935 }
936
937 auto fruBus = std::get_if<uint64_t>(&findBus->second);
938 auto fruAddress = std::get_if<uint64_t>(&findAddress->second);
939 auto macFruOffset =
940 std::get_if<uint64_t>(&findMacOffset->second);
941 if (!fruBus || !fruAddress || !macFruOffset)
942 {
943 phosphor::logging::log<phosphor::logging::level::INFO>(
944 "ERROR: MotherBoard FRU config data type invalid, not "
945 "used");
946 return false;
947 }
948 busNum = *fruBus;
949 address = *fruAddress;
950 macOffset = *macFruOffset;
951 return true;
952 }
953 }
954 }
955 return false;
956}
957
Zhikui Ren98fa87e2022-05-04 08:10:29 -0700958static constexpr uint64_t fruEnd = 0xff;
959// write rolls over within current page, need to keep mac within a page
960static constexpr uint64_t fruPageSize = 0x8;
961// MAC record struct: HEADER, MAC DATA, CheckSum
962static constexpr uint64_t macRecordSize = maxEthSize + 2;
963static_assert(fruPageSize >= macRecordSize,
964 "macRecordSize greater than eeprom page size");
965static constexpr uint8_t macHeader = 0x40;
966// Calculate new checksum for fru info area
967static uint8_t calculateChecksum(std::vector<uint8_t>::const_iterator iter,
968 std::vector<uint8_t>::const_iterator end)
969{
970 constexpr int checksumMod = 256;
971 uint8_t sum = std::accumulate(iter, end, static_cast<uint8_t>(0));
972 return (checksumMod - sum) % checksumMod;
973}
974
Zhikui Renad129c62022-04-05 20:11:24 -0700975bool readMacFromFru(ipmi::Context::ptr ctx, uint8_t macIndex,
976 std::array<uint8_t, maxEthSize>& ethData)
977{
Zhikui Renad129c62022-04-05 20:11:24 -0700978 uint64_t macOffset = fruEnd;
979 uint64_t fruBus = 0;
980 uint64_t fruAddress = 0;
981
Alex Schendel097497f2022-10-07 14:37:15 -0700982 if (findFruDevice(ctx, macOffset, fruBus, fruAddress))
Zhikui Renad129c62022-04-05 20:11:24 -0700983 {
984 phosphor::logging::log<phosphor::logging::level::INFO>(
985 "Found mac fru",
986 phosphor::logging::entry("BUS=%d", static_cast<uint8_t>(fruBus)),
987 phosphor::logging::entry("ADDRESS=%d",
988 static_cast<uint8_t>(fruAddress)));
989
Zhikui Ren98fa87e2022-05-04 08:10:29 -0700990 if (macOffset % fruPageSize)
991 {
992 macOffset = (macOffset / fruPageSize + 1) * fruPageSize;
993 }
994 macOffset += macIndex * fruPageSize;
995 if ((macOffset + macRecordSize) > fruEnd)
Zhikui Renad129c62022-04-05 20:11:24 -0700996 {
997 phosphor::logging::log<phosphor::logging::level::ERR>(
998 "ERROR: read fru mac failed, offset invalid");
999 return false;
1000 }
1001 std::vector<uint8_t> writeData;
Zhikui Ren98fa87e2022-05-04 08:10:29 -07001002 writeData.push_back(static_cast<uint8_t>(macOffset));
1003 std::vector<uint8_t> readBuf(macRecordSize);
Zhikui Renad129c62022-04-05 20:11:24 -07001004 std::string i2cBus = "/dev/i2c-" + std::to_string(fruBus);
1005 ipmi::Cc retI2C =
1006 ipmi::i2cWriteRead(i2cBus, fruAddress, writeData, readBuf);
1007 if (retI2C == ipmi::ccSuccess)
1008 {
Zhikui Ren98fa87e2022-05-04 08:10:29 -07001009 uint8_t cs = calculateChecksum(readBuf.cbegin(), readBuf.cend());
1010 if (cs == 0)
1011 {
1012 std::copy(++readBuf.begin(), --readBuf.end(), ethData.data());
1013 return true;
1014 }
Zhikui Renad129c62022-04-05 20:11:24 -07001015 }
1016 }
1017 return false;
1018}
1019
1020ipmi::Cc writeMacToFru(ipmi::Context::ptr ctx, uint8_t macIndex,
1021 std::array<uint8_t, maxEthSize>& ethData)
1022{
Zhikui Renad129c62022-04-05 20:11:24 -07001023 uint64_t macOffset = fruEnd;
1024 uint64_t fruBus = 0;
1025 uint64_t fruAddress = 0;
1026
Alex Schendel097497f2022-10-07 14:37:15 -07001027 if (findFruDevice(ctx, macOffset, fruBus, fruAddress))
Zhikui Renad129c62022-04-05 20:11:24 -07001028 {
1029 phosphor::logging::log<phosphor::logging::level::INFO>(
1030 "Found mac fru",
1031 phosphor::logging::entry("BUS=%d", static_cast<uint8_t>(fruBus)),
1032 phosphor::logging::entry("ADDRESS=%d",
1033 static_cast<uint8_t>(fruAddress)));
1034
Zhikui Ren98fa87e2022-05-04 08:10:29 -07001035 if (macOffset % fruPageSize)
1036 {
1037 macOffset = (macOffset / fruPageSize + 1) * fruPageSize;
1038 }
1039 macOffset += macIndex * fruPageSize;
1040 if ((macOffset + macRecordSize) > fruEnd)
Zhikui Renad129c62022-04-05 20:11:24 -07001041 {
1042 phosphor::logging::log<phosphor::logging::level::ERR>(
1043 "ERROR: write mac fru failed, offset invalid.");
1044 return ipmi::ccParmOutOfRange;
1045 }
1046 std::vector<uint8_t> writeData;
Zhikui Ren98fa87e2022-05-04 08:10:29 -07001047 writeData.reserve(macRecordSize + 1); // include start location
1048 writeData.push_back(static_cast<uint8_t>(macOffset));
1049 writeData.push_back(macHeader);
Zhikui Renad129c62022-04-05 20:11:24 -07001050 std::for_each(ethData.cbegin(), ethData.cend(),
1051 [&](uint8_t i) { writeData.push_back(i); });
Zhikui Ren98fa87e2022-05-04 08:10:29 -07001052 uint8_t macCheckSum =
1053 calculateChecksum(++writeData.cbegin(), writeData.cend());
1054 writeData.push_back(macCheckSum);
Zhikui Renad129c62022-04-05 20:11:24 -07001055
1056 std::string i2cBus = "/dev/i2c-" + std::to_string(fruBus);
1057 std::vector<uint8_t> readBuf;
1058 ipmi::Cc ret =
1059 ipmi::i2cWriteRead(i2cBus, fruAddress, writeData, readBuf);
Zhikui Ren98fa87e2022-05-04 08:10:29 -07001060
Zhikui Ren0408e792022-06-07 21:03:33 -07001061 // prepare for read to detect chip is write protected
1062 writeData.resize(1);
1063 readBuf.resize(maxEthSize + 1); // include macHeader
1064
Zhikui Renad129c62022-04-05 20:11:24 -07001065 switch (ret)
1066 {
1067 case ipmi::ccSuccess:
Zhikui Ren98fa87e2022-05-04 08:10:29 -07001068 // Wait for internal write cycle to complete
1069 // example: ATMEL 24c0x chip has Twr spec as 5ms
1070
1071 // Ideally we want yield wait, but currently following code
1072 // crash with "thread not supported"
1073 // boost::asio::deadline_timer timer(
1074 // boost::asio::get_associated_executor(ctx->yield),
1075 // boost::posix_time::seconds(1));
1076 // timer.async_wait(ctx->yield);
1077 // use usleep as temp WA
1078 usleep(5000);
1079 if (ipmi::i2cWriteRead(i2cBus, fruAddress, writeData,
1080 readBuf) == ipmi::ccSuccess)
Zhikui Renad129c62022-04-05 20:11:24 -07001081 {
1082 if (std::equal(ethData.begin(), ethData.end(),
Zhikui Ren98fa87e2022-05-04 08:10:29 -07001083 ++readBuf.begin())) // skip macHeader
Zhikui Renad129c62022-04-05 20:11:24 -07001084 {
1085 return ipmi::ccSuccess;
1086 }
1087 phosphor::logging::log<phosphor::logging::level::INFO>(
1088 "INFO: write mac fru verify failed, fru may be write "
1089 "protected.");
1090 }
1091 return ipmi::ccCommandNotAvailable;
Zhikui Ren0408e792022-06-07 21:03:33 -07001092 default:
1093 if (ipmi::i2cWriteRead(i2cBus, fruAddress, writeData,
1094 readBuf) == ipmi::ccSuccess)
1095 {
1096 phosphor::logging::log<phosphor::logging::level::INFO>(
1097 "INFO: write mac fru failed, but successfully read "
1098 "from fru, fru may be write protected.");
1099 return ipmi::ccCommandNotAvailable;
1100 }
1101 else // assume failure is due to no eeprom on board
1102 {
1103 phosphor::logging::log<phosphor::logging::level::ERR>(
1104 "ERROR: write mac fru failed, assume no eeprom is "
1105 "available.");
1106 }
Zhikui Renad129c62022-04-05 20:11:24 -07001107 break;
1108 }
1109 }
1110 // no FRU eeprom found
1111 return ipmi::ccDestinationUnavailable;
1112}
1113
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +05301114ipmi::RspType<> setManufacturingData(ipmi::Context::ptr ctx, uint8_t dataType,
Richard Marian Thomaiyar1f0839c2019-08-25 20:10:52 +05301115 std::array<uint8_t, maxEthSize> ethData)
1116{
Zhikui Renad129c62022-04-05 20:11:24 -07001117 // mfg filter logic will restrict this command executing only in mfg
1118 // mode.
Richard Marian Thomaiyar1f0839c2019-08-25 20:10:52 +05301119 if (dataType >= maxSupportedEth)
1120 {
1121 return ipmi::responseParmOutOfRange();
1122 }
1123
1124 constexpr uint8_t invalidData = 0;
1125 constexpr uint8_t validData = 1;
Zhikui Renad129c62022-04-05 20:11:24 -07001126
1127 ipmi::Cc ret = writeMacToFru(ctx, dataType, ethData);
1128 if (ret != ipmi::ccDestinationUnavailable)
1129 {
1130 resetMtmTimer(ctx);
1131 return response(ret);
1132 }
1133
Richard Marian Thomaiyar1f0839c2019-08-25 20:10:52 +05301134 constexpr uint8_t ethAddrStrSize =
1135 19; // XX:XX:XX:XX:XX:XX + \n + null termination;
1136 std::vector<uint8_t> buff(ethAddrStrSize);
1137 std::snprintf(reinterpret_cast<char*>(buff.data()), ethAddrStrSize,
1138 "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx\n", ethData.at(0),
1139 ethData.at(1), ethData.at(2), ethData.at(3), ethData.at(4),
1140 ethData.at(5));
1141 std::ofstream oEthFile(factoryEthAddrBaseFileName +
1142 std::to_string(dataType),
1143 std::ofstream::out);
1144 if (!oEthFile.good())
1145 {
1146 return ipmi::responseUnspecifiedError();
1147 }
1148
1149 oEthFile << reinterpret_cast<char*>(buff.data());
Johnathan Mantey6d83e4e2020-05-08 11:01:01 -07001150 oEthFile.flush();
Richard Marian Thomaiyar1f0839c2019-08-25 20:10:52 +05301151 oEthFile.close();
1152
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +05301153 resetMtmTimer(ctx);
Richard Marian Thomaiyar1f0839c2019-08-25 20:10:52 +05301154 return ipmi::responseSuccess();
1155}
1156
1157ipmi::RspType<uint8_t, std::array<uint8_t, maxEthSize>>
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +05301158 getManufacturingData(ipmi::Context::ptr ctx, uint8_t dataType)
Richard Marian Thomaiyar1f0839c2019-08-25 20:10:52 +05301159{
Zhikui Renad129c62022-04-05 20:11:24 -07001160 // mfg filter logic will restrict this command executing only in mfg
1161 // mode.
Richard Marian Thomaiyar1f0839c2019-08-25 20:10:52 +05301162 if (dataType >= maxSupportedEth)
1163 {
1164 return ipmi::responseParmOutOfRange();
1165 }
1166 std::array<uint8_t, maxEthSize> ethData{0};
1167 constexpr uint8_t invalidData = 0;
1168 constexpr uint8_t validData = 1;
1169
1170 std::ifstream iEthFile(factoryEthAddrBaseFileName +
1171 std::to_string(dataType),
1172 std::ifstream::in);
1173 if (!iEthFile.good())
1174 {
Zhikui Renad129c62022-04-05 20:11:24 -07001175 if (readMacFromFru(ctx, dataType, ethData))
1176 {
1177 resetMtmTimer(ctx);
1178 return ipmi::responseSuccess(validData, ethData);
1179 }
Richard Marian Thomaiyar1f0839c2019-08-25 20:10:52 +05301180 return ipmi::responseSuccess(invalidData, ethData);
1181 }
1182 std::string ethStr;
1183 iEthFile >> ethStr;
1184 uint8_t* data = ethData.data();
1185 std::sscanf(ethStr.c_str(), "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx",
1186 data, (data + 1), (data + 2), (data + 3), (data + 4),
1187 (data + 5));
1188
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +05301189 resetMtmTimer(ctx);
Richard Marian Thomaiyar1f0839c2019-08-25 20:10:52 +05301190 return ipmi::responseSuccess(validData, ethData);
1191}
1192
Zhikui Renad129c62022-04-05 20:11:24 -07001193/** @brief implements slot master write read IPMI command which can be used
1194 * for low-level I2C/SMBus write, read or write-read access for PCIE slots
Yong Lif267a672019-08-29 11:16:07 +08001195 * @param reserved - skip 6 bit
1196 * @param addressType - address type
1197 * @param bbSlotNum - baseboard slot number
1198 * @param riserSlotNum - riser slot number
1199 * @param reserved2 - skip 2 bit
1200 * @param slaveAddr - slave address
1201 * @param readCount - number of bytes to be read
1202 * @param writeData - data to be written
1203 *
1204 * @returns IPMI completion code plus response data
1205 */
1206ipmi::RspType<std::vector<uint8_t>>
1207 appSlotI2CMasterWriteRead(uint6_t reserved, uint2_t addressType,
1208 uint3_t bbSlotNum, uint3_t riserSlotNum,
Vernon Maueryfc3bc382022-06-02 13:52:04 -07001209 uint2_t reserved2, uint8_t slaveAddr,
Yong Lif267a672019-08-29 11:16:07 +08001210 uint8_t readCount, std::vector<uint8_t> writeData)
1211{
Vernon Maueryfc3bc382022-06-02 13:52:04 -07001212 if (reserved || reserved2)
1213 {
1214 return ipmi::responseInvalidFieldRequest();
1215 }
Yong Lif267a672019-08-29 11:16:07 +08001216 const size_t writeCount = writeData.size();
1217 std::string i2cBus;
1218 if (addressType == slotAddressTypeBus)
1219 {
1220 std::string path = "/dev/i2c-mux/Riser_" +
1221 std::to_string(static_cast<uint8_t>(bbSlotNum)) +
1222 "_Mux/Pcie_Slot_" +
1223 std::to_string(static_cast<uint8_t>(riserSlotNum));
1224
1225 if (std::filesystem::exists(path) && std::filesystem::is_symlink(path))
1226 {
1227 i2cBus = std::filesystem::read_symlink(path);
1228 }
1229 else
1230 {
1231 phosphor::logging::log<phosphor::logging::level::ERR>(
1232 "Master write read command: Cannot get BusID");
1233 return ipmi::responseInvalidFieldRequest();
1234 }
1235 }
1236 else if (addressType == slotAddressTypeUniqueid)
1237 {
1238 i2cBus = "/dev/i2c-" +
1239 std::to_string(static_cast<uint8_t>(bbSlotNum) |
1240 (static_cast<uint8_t>(riserSlotNum) << 3));
1241 }
1242 else
1243 {
1244 phosphor::logging::log<phosphor::logging::level::ERR>(
1245 "Master write read command: invalid request");
1246 return ipmi::responseInvalidFieldRequest();
1247 }
1248
Zhikui Renad129c62022-04-05 20:11:24 -07001249 // Allow single byte write as it is offset byte to read the data, rest
1250 // allow only in Special mode.
Yong Lif267a672019-08-29 11:16:07 +08001251 if (writeCount > 1)
1252 {
Richard Marian Thomaiyarae13ac62019-12-17 15:45:12 +05301253 if (mtm.getMfgMode() == SpecialMode::none)
Yong Lif267a672019-08-29 11:16:07 +08001254 {
1255 return ipmi::responseInsufficientPrivilege();
1256 }
1257 }
1258
1259 if (readCount > slotI2CMaxReadSize)
1260 {
1261 phosphor::logging::log<phosphor::logging::level::ERR>(
1262 "Master write read command: Read count exceeds limit");
1263 return ipmi::responseParmOutOfRange();
1264 }
1265
1266 if (!readCount && !writeCount)
1267 {
1268 phosphor::logging::log<phosphor::logging::level::ERR>(
1269 "Master write read command: Read & write count are 0");
1270 return ipmi::responseInvalidFieldRequest();
1271 }
1272
1273 std::vector<uint8_t> readBuf(readCount);
1274
1275 ipmi::Cc retI2C = ipmi::i2cWriteRead(i2cBus, slaveAddr, writeData, readBuf);
1276 if (retI2C != ipmi::ccSuccess)
1277 {
1278 return ipmi::response(retI2C);
1279 }
1280
1281 return ipmi::responseSuccess(readBuf);
1282}
Yong Li068b4f22019-09-17 16:32:18 +08001283
1284ipmi::RspType<> clearCMOS()
1285{
Zhikui Renad129c62022-04-05 20:11:24 -07001286 // There is an i2c device on bus 4, the slave address is 0x38. Based on
1287 // the spec, writing 0x1 to address 0x61 on this device, will trigger
1288 // the clear CMOS action.
Yong Lid0d010b2019-11-18 12:15:21 +08001289 constexpr uint8_t slaveAddr = 0x38;
Yong Li068b4f22019-09-17 16:32:18 +08001290 std::string i2cBus = "/dev/i2c-4";
Yong Lieaeb6cb2020-03-09 20:21:50 +08001291 std::vector<uint8_t> writeData = {0x61, 0x1};
Yong Li068b4f22019-09-17 16:32:18 +08001292 std::vector<uint8_t> readBuf(0);
1293
Yong Li068b4f22019-09-17 16:32:18 +08001294 ipmi::Cc retI2C = ipmi::i2cWriteRead(i2cBus, slaveAddr, writeData, readBuf);
1295 return ipmi::response(retI2C);
1296}
Vernon Mauery27d23562021-08-12 10:43:39 -07001297
1298ipmi::RspType<> setFITcLayout(uint32_t layout)
1299{
1300 static constexpr const char* factoryFITcLayout =
1301 "/var/sofs/factory-settings/layout/fitc";
1302 std::filesystem::path fitcDir =
1303 std::filesystem::path(factoryFITcLayout).parent_path();
1304 std::error_code ec;
1305 std::filesystem::create_directories(fitcDir, ec);
1306 if (ec)
1307 {
1308 return ipmi::responseUnspecifiedError();
1309 }
1310 try
1311 {
1312 std::ofstream file(factoryFITcLayout);
1313 file << layout;
1314 file.flush();
1315 file.close();
1316 }
1317 catch (const std::exception& e)
1318 {
1319 return ipmi::responseUnspecifiedError();
1320 }
1321
1322 return ipmi::responseSuccess();
1323}
1324
Arun P. Mohanan06584cd2021-08-13 20:56:01 +05301325static std::vector<std::string>
1326 getMCTPServiceConfigPaths(ipmi::Context::ptr& ctx)
1327{
1328 boost::system::error_code ec;
1329 auto configPaths = ctx->bus->yield_method_call<std::vector<std::string>>(
1330 ctx->yield, ec, "xyz.openbmc_project.ObjectMapper",
1331 "/xyz/openbmc_project/object_mapper",
1332 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths",
1333 "/xyz/openbmc_project/inventory/system/board", 2,
1334 std::array<const char*, 1>{
1335 "xyz.openbmc_project.Configuration.MctpConfiguration"});
1336 if (ec)
1337 {
1338 throw std::runtime_error(
1339 "Failed to query configuration sub tree objects");
1340 }
1341 return configPaths;
1342}
1343
1344static ipmi::RspType<> startOrStopService(ipmi::Context::ptr& ctx,
1345 const uint8_t enable,
ANJALI RAY1fe485c2021-12-17 16:41:30 +00001346 const std::string& serviceName,
1347 bool disableOrEnableUnitFiles = true)
Arun P. Mohanan06584cd2021-08-13 20:56:01 +05301348{
1349 constexpr bool runtimeOnly = false;
1350 constexpr bool force = false;
1351
1352 boost::system::error_code ec;
1353 switch (enable)
1354 {
1355 case ipmi::SupportedFeatureActions::stop:
1356 ctx->bus->yield_method_call(ctx->yield, ec, systemDService,
1357 systemDObjPath, systemDMgrIntf,
1358 "StopUnit", serviceName, "replace");
1359 break;
1360 case ipmi::SupportedFeatureActions::start:
1361 ctx->bus->yield_method_call(ctx->yield, ec, systemDService,
1362 systemDObjPath, systemDMgrIntf,
1363 "StartUnit", serviceName, "replace");
1364 break;
1365 case ipmi::SupportedFeatureActions::disable:
ANJALI RAY1fe485c2021-12-17 16:41:30 +00001366 if (disableOrEnableUnitFiles == true)
1367 {
1368 ctx->bus->yield_method_call(
1369 ctx->yield, ec, systemDService, systemDObjPath,
1370 systemDMgrIntf, "DisableUnitFiles",
1371 std::array<const char*, 1>{serviceName.c_str()},
1372 runtimeOnly);
1373 }
Arun P. Mohanan06584cd2021-08-13 20:56:01 +05301374 ctx->bus->yield_method_call(
1375 ctx->yield, ec, systemDService, systemDObjPath, systemDMgrIntf,
1376 "MaskUnitFiles",
1377 std::array<const char*, 1>{serviceName.c_str()}, runtimeOnly,
1378 force);
Arun P. Mohanan06584cd2021-08-13 20:56:01 +05301379 break;
1380 case ipmi::SupportedFeatureActions::enable:
1381 ctx->bus->yield_method_call(
1382 ctx->yield, ec, systemDService, systemDObjPath, systemDMgrIntf,
1383 "UnmaskUnitFiles",
1384 std::array<const char*, 1>{serviceName.c_str()}, runtimeOnly);
ANJALI RAY1fe485c2021-12-17 16:41:30 +00001385 if (disableOrEnableUnitFiles == true)
1386 {
1387 ctx->bus->yield_method_call(
1388 ctx->yield, ec, systemDService, systemDObjPath,
1389 systemDMgrIntf, "EnableUnitFiles",
1390 std::array<const char*, 1>{serviceName.c_str()},
1391 runtimeOnly, force);
1392 }
Arun P. Mohanan06584cd2021-08-13 20:56:01 +05301393 break;
1394 default:
1395 phosphor::logging::log<phosphor::logging::level::WARNING>(
1396 "ERROR: Invalid feature action selected",
1397 phosphor::logging::entry("ACTION=%d", enable));
1398 return ipmi::responseInvalidFieldRequest();
1399 }
1400 if (ec)
1401 {
1402 phosphor::logging::log<phosphor::logging::level::WARNING>(
1403 "ERROR: Service start or stop failed",
1404 phosphor::logging::entry("SERVICE=%s", serviceName.c_str()));
1405 return ipmi::responseUnspecifiedError();
1406 }
1407 return ipmi::responseSuccess();
1408}
1409
1410static std::string getMCTPServiceName(const std::string& objectPath)
1411{
1412 const auto serviceArgument = boost::algorithm::replace_all_copy(
1413 boost::algorithm::replace_first_copy(
1414 objectPath, "/xyz/openbmc_project/inventory/system/board/", ""),
1415 "/", "_2f");
1416 std::string unitName =
1417 "xyz.openbmc_project.mctpd@" + serviceArgument + ".service";
1418 return unitName;
1419}
1420
1421static ipmi::RspType<> handleMCTPFeature(ipmi::Context::ptr& ctx,
1422 const uint8_t enable,
1423 const std::string& binding)
1424{
1425 std::vector<std::string> configPaths;
1426 try
1427 {
1428 configPaths = getMCTPServiceConfigPaths(ctx);
1429 }
1430 catch (const std::exception& e)
1431 {
1432 phosphor::logging::log<phosphor::logging::level::ERR>(e.what());
1433 return ipmi::responseUnspecifiedError();
1434 }
1435
1436 for (const auto& objectPath : configPaths)
1437 {
1438 auto const pos = objectPath.find_last_of('/');
1439 if (binding == objectPath.substr(pos + 1))
1440 {
1441 return startOrStopService(ctx, enable,
ANJALI RAY1fe485c2021-12-17 16:41:30 +00001442 getMCTPServiceName(objectPath), false);
Arun P. Mohanan06584cd2021-08-13 20:56:01 +05301443 }
1444 }
1445 return ipmi::responseSuccess();
1446}
1447
1448/** @brief implements MTM BMC Feature Control IPMI command which can be
1449 * used to enable or disable the supported BMC features.
1450 * @param yield - context object that represents the currently executing
1451 * coroutine
1452 * @param feature - feature enum to enable or disable
1453 * @param enable - enable or disable the feature
1454 * @param featureArg - custom arguments for that feature
1455 * @param reserved - reserved for future use
1456 *
1457 * @returns IPMI completion code
1458 */
1459ipmi::RspType<> mtmBMCFeatureControl(ipmi::Context::ptr ctx,
1460 const uint8_t feature,
1461 const uint8_t enable,
1462 const uint8_t featureArg,
1463 const uint16_t reserved)
1464{
1465 if (reserved != 0)
1466 {
1467 return ipmi::responseInvalidFieldRequest();
1468 }
1469
1470 switch (feature)
1471 {
1472 case ipmi::SupportedFeatureControls::mctp:
1473 switch (featureArg)
1474 {
1475 case ipmi::SupportedMCTPBindings::mctpPCIe:
1476 return handleMCTPFeature(ctx, enable, "MCTP_PCIe");
1477 case ipmi::SupportedMCTPBindings::mctpSMBusHSBP:
1478 return handleMCTPFeature(ctx, enable, "MCTP_SMBus_HSBP");
1479 case ipmi::SupportedMCTPBindings::mctpSMBusPCIeSlot:
1480 return handleMCTPFeature(ctx, enable,
1481 "MCTP_SMBus_PCIe_slot");
1482 default:
1483 return ipmi::responseInvalidFieldRequest();
1484 }
1485 break;
Jason M. Bills5cb2c042021-08-17 12:03:39 -07001486 case ipmi::SupportedFeatureControls::pcieScan:
1487 if (featureArg != 0)
1488 {
1489 return ipmi::responseInvalidFieldRequest();
1490 }
1491 startOrStopService(ctx, enable, "xyz.openbmc_project.PCIe.service");
1492 break;
Arun P. Mohanan06584cd2021-08-13 20:56:01 +05301493 default:
1494 return ipmi::responseInvalidFieldRequest();
1495 }
1496 return ipmi::responseSuccess();
1497}
Vernon Mauerya3702c12019-05-22 13:20:59 -07001498} // namespace ipmi
1499
1500void register_mtm_commands() __attribute__((constructor));
1501void register_mtm_commands()
1502{
Jason M. Bills38d2b5a2019-06-03 16:26:11 -07001503 // <Get SM Signal>
Vernon Mauery98bbf692019-09-16 11:14:59 -07001504 ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnGeneral,
1505 ipmi::intel::general::cmdGetSmSignal,
1506 ipmi::Privilege::Admin, ipmi::appMTMGetSignal);
Vernon Mauerya3702c12019-05-22 13:20:59 -07001507
Vernon Mauery98bbf692019-09-16 11:14:59 -07001508 ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnGeneral,
1509 ipmi::intel::general::cmdSetSmSignal,
1510 ipmi::Privilege::Admin, ipmi::appMTMSetSignal);
Vernon Mauerya3702c12019-05-22 13:20:59 -07001511
Vernon Mauery98bbf692019-09-16 11:14:59 -07001512 ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnGeneral,
1513 ipmi::intel::general::cmdMtmKeepAlive,
1514 ipmi::Privilege::Admin, ipmi::mtmKeepAlive);
Richard Marian Thomaiyar666dd012019-08-02 20:55:37 +05301515
Vernon Mauery98bbf692019-09-16 11:14:59 -07001516 ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnGeneral,
1517 ipmi::intel::general::cmdSetManufacturingData,
1518 ipmi::Privilege::Admin, ipmi::setManufacturingData);
Richard Marian Thomaiyar1f0839c2019-08-25 20:10:52 +05301519
Vernon Mauery98bbf692019-09-16 11:14:59 -07001520 ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnGeneral,
1521 ipmi::intel::general::cmdGetManufacturingData,
1522 ipmi::Privilege::Admin, ipmi::getManufacturingData);
Richard Marian Thomaiyar1f0839c2019-08-25 20:10:52 +05301523
Vernon Mauery27d23562021-08-12 10:43:39 -07001524 ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnGeneral,
1525 ipmi::intel::general::cmdSetFITcLayout,
1526 ipmi::Privilege::Admin, ipmi::setFITcLayout);
1527
Arun P. Mohanan06584cd2021-08-13 20:56:01 +05301528 ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnGeneral,
1529 ipmi::intel::general::cmdMTMBMCFeatureControl,
1530 ipmi::Privilege::Admin, ipmi::mtmBMCFeatureControl);
1531
Vernon Mauery98bbf692019-09-16 11:14:59 -07001532 ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnApp,
1533 ipmi::intel::general::cmdSlotI2CMasterWriteRead,
1534 ipmi::Privilege::Admin,
1535 ipmi::appSlotI2CMasterWriteRead);
Yong Lif267a672019-08-29 11:16:07 +08001536
Yong Li068b4f22019-09-17 16:32:18 +08001537 ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnPlatform,
1538 ipmi::intel::platform::cmdClearCMOS,
1539 ipmi::Privilege::Admin, ipmi::clearCMOS);
1540
Vernon Mauery98bbf692019-09-16 11:14:59 -07001541 ipmi::registerFilter(ipmi::prioOemBase,
Yong Li85feb132019-08-09 16:06:03 +08001542 [](ipmi::message::Request::ptr request) {
1543 return ipmi::mfgFilterMessage(request);
1544 });
Vernon Mauerya3702c12019-05-22 13:20:59 -07001545}