1 /*
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 
17 #include <linux/input.h>
18 
19 #include <boost/algorithm/string.hpp>
20 #include <boost/container/flat_map.hpp>
21 #include <ipmid/api.hpp>
22 #include <manufacturingcommands.hpp>
23 #include <oemcommands.hpp>
24 #include <phosphor-logging/lg2.hpp>
25 #include <types.hpp>
26 
27 #include <filesystem>
28 #include <fstream>
29 
30 namespace ipmi
31 {
32 
33 Manufacturing mtm;
34 
35 static auto revertTimeOut =
36     std::chrono::duration_cast<std::chrono::microseconds>(
37         std::chrono::seconds(60)); // 1 minute timeout
38 
39 static constexpr uint8_t bbRiserMux = 0;
40 static constexpr uint8_t leftRiserMux = 1;
41 static constexpr uint8_t rightRiserMux = 2;
42 static constexpr uint8_t pcieMux = 3;
43 static constexpr uint8_t hsbpMux = 4;
44 
45 static constexpr uint8_t slotAddressTypeBus = 0;
46 static constexpr uint8_t slotAddressTypeUniqueid = 1;
47 static constexpr uint8_t slotI2CMaxReadSize = 35;
48 
49 static constexpr const char* callbackMgrService =
50     "xyz.openbmc_project.CallbackManager";
51 static constexpr const char* callbackMgrIntf =
52     "xyz.openbmc_project.CallbackManager";
53 static constexpr const char* callbackMgrObjPath =
54     "/xyz/openbmc_project/CallbackManager";
55 static constexpr const char* retriggerLedUpdate = "RetriggerLEDUpdate";
56 
57 const static constexpr char* systemDService = "org.freedesktop.systemd1";
58 const static constexpr char* systemDObjPath = "/org/freedesktop/systemd1";
59 const static constexpr char* systemDMgrIntf =
60     "org.freedesktop.systemd1.Manager";
61 const static constexpr char* pidControlService = "phosphor-pid-control.service";
62 
63 static inline Cc resetMtmTimer(ipmi::Context::ptr ctx)
64 {
65     boost::system::error_code ec;
66     ctx->bus->yield_method_call<>(ctx->yield, ec, specialModeService,
67                                   specialModeObjPath, specialModeIntf,
68                                   "ResetTimer");
69     if (ec)
70     {
71         lg2::error("Failed to reset the manufacturing mode timer");
72         return ccUnspecifiedError;
73     }
74     return ccSuccess;
75 }
76 
77 int getGpioPathForSmSignal(const SmSignalGet signal, std::string& path)
78 {
79     switch (signal)
80     {
81         case SmSignalGet::smPowerButton:
82             path = "/xyz/openbmc_project/chassis/buttons/power";
83             break;
84         case SmSignalGet::smResetButton:
85             path = "/xyz/openbmc_project/chassis/buttons/reset";
86             break;
87         case SmSignalGet::smNMIButton:
88             path = "/xyz/openbmc_project/chassis/buttons/nmi";
89             break;
90         case SmSignalGet::smIdentifyButton:
91             path = "/xyz/openbmc_project/chassis/buttons/id";
92             break;
93         default:
94             return -1;
95             break;
96     }
97     return 0;
98 }
99 
100 ipmi_ret_t ledStoreAndSet(SmSignalSet signal, const std::string& setState)
101 {
102     LedProperty* ledProp = mtm.findLedProperty(signal);
103     if (ledProp == nullptr)
104     {
105         return IPMI_CC_INVALID_FIELD_REQUEST;
106     }
107 
108     std::string ledName = ledProp->getName();
109     std::string ledService = ledServicePrefix + ledName;
110     std::string ledPath = ledPathPrefix + ledName;
111     ipmi::Value presentState;
112 
113     if (false == ledProp->getLock())
114     {
115         if (mtm.getProperty(ledService.c_str(), ledPath.c_str(), ledIntf,
116                             "State", &presentState) != 0)
117         {
118             return IPMI_CC_UNSPECIFIED_ERROR;
119         }
120         ledProp->setPrevState(std::get<std::string>(presentState));
121         ledProp->setLock(true);
122         if (signal == SmSignalSet::smPowerFaultLed ||
123             signal == SmSignalSet::smSystemReadyLed)
124         {
125             mtm.revertLedCallback = true;
126         }
127     }
128     if (mtm.setProperty(ledService, ledPath, ledIntf, "State",
129                         ledStateStr + setState) != 0)
130     {
131         return IPMI_CC_UNSPECIFIED_ERROR;
132     }
133     return IPMI_CC_OK;
134 }
135 
136 ipmi_ret_t ledRevert(SmSignalSet signal)
137 {
138     LedProperty* ledProp = mtm.findLedProperty(signal);
139     if (ledProp == nullptr)
140     {
141         return IPMI_CC_INVALID_FIELD_REQUEST;
142     }
143     if (true == ledProp->getLock())
144     {
145         ledProp->setLock(false);
146         if (signal == SmSignalSet::smPowerFaultLed ||
147             signal == SmSignalSet::smSystemReadyLed)
148         {
149             try
150             {
151                 ipmi::method_no_args::callDbusMethod(
152                     *getSdBus(), callbackMgrService, callbackMgrObjPath,
153                     callbackMgrIntf, retriggerLedUpdate);
154             }
155             catch (const sdbusplus::exception_t& e)
156             {
157                 return IPMI_CC_UNSPECIFIED_ERROR;
158             }
159             mtm.revertLedCallback = false;
160         }
161         else
162         {
163             std::string ledName = ledProp->getName();
164             std::string ledService = ledServicePrefix + ledName;
165             std::string ledPath = ledPathPrefix + ledName;
166             if (mtm.setProperty(ledService, ledPath, ledIntf, "State",
167                                 ledProp->getPrevState()) != 0)
168             {
169                 return IPMI_CC_UNSPECIFIED_ERROR;
170             }
171         }
172     }
173     return IPMI_CC_OK;
174 }
175 
176 void Manufacturing::initData()
177 {
178     ledPropertyList.push_back(
179         LedProperty(SmSignalSet::smPowerFaultLed, "status_amber"));
180     ledPropertyList.push_back(
181         LedProperty(SmSignalSet::smSystemReadyLed, "status_green"));
182     ledPropertyList.push_back(
183         LedProperty(SmSignalSet::smIdentifyLed, "identify"));
184 }
185 
186 void Manufacturing::revertTimerHandler()
187 {
188 
189 #ifdef BMC_VALIDATION_UNSECURE_FEATURE
190     if (mtm.getMfgMode() == SpecialMode::valUnsecure)
191     {
192         // Don't revert the behaviour for validation unsecure mode.
193         return;
194     }
195 #endif
196     if (revertFanPWM)
197     {
198         revertFanPWM = false;
199         disablePidControlService(false);
200     }
201 
202     if (mtmTestBeepFd != -1)
203     {
204         ::close(mtmTestBeepFd);
205         mtmTestBeepFd = -1;
206     }
207 
208     for (const auto& ledProperty : ledPropertyList)
209     {
210         const std::string& ledName = ledProperty.getName();
211         if (ledName == "identify" && mtm.getMfgMode() == SpecialMode::mfg)
212         {
213             // Don't revert the behaviour for manufacturing mode
214             continue;
215         }
216         ledRevert(ledProperty.getSignal());
217     }
218 }
219 
220 Manufacturing::Manufacturing() :
221     revertTimer([&](void) { revertTimerHandler(); })
222 {
223     initData();
224 }
225 
226 int8_t Manufacturing::getProperty(const std::string& service,
227                                   const std::string& path,
228                                   const std::string& interface,
229                                   const std::string& propertyName,
230                                   ipmi::Value* reply)
231 {
232     try
233     {
234         *reply = ipmi::getDbusProperty(*getSdBus(), service, path, interface,
235                                        propertyName);
236     }
237     catch (const sdbusplus::exception_t& e)
238     {
239         lg2::info("ERROR: getProperty");
240         return -1;
241     }
242 
243     return 0;
244 }
245 
246 int8_t Manufacturing::setProperty(const std::string& service,
247                                   const std::string& path,
248                                   const std::string& interface,
249                                   const std::string& propertyName,
250                                   ipmi::Value value)
251 {
252     try
253     {
254         ipmi::setDbusProperty(*getSdBus(), service, path, interface,
255                               propertyName, value);
256     }
257     catch (const sdbusplus::exception_t& e)
258     {
259         lg2::info("ERROR: setProperty");
260         return -1;
261     }
262 
263     return 0;
264 }
265 
266 int8_t Manufacturing::disablePidControlService(const bool disable)
267 {
268     try
269     {
270         auto dbus = getSdBus();
271         auto method = dbus->new_method_call(systemDService, systemDObjPath,
272                                             systemDMgrIntf,
273                                             disable ? "StopUnit" : "StartUnit");
274         method.append(pidControlService, "replace");
275         auto reply = dbus->call(method);
276     }
277     catch (const sdbusplus::exception_t& e)
278     {
279         lg2::info("ERROR: phosphor-pid-control service start or stop failed");
280         return -1;
281     }
282     return 0;
283 }
284 
285 static bool findPwmName(ipmi::Context::ptr& ctx, uint8_t instance,
286                         std::string& pwmName)
287 {
288     boost::system::error_code ec{};
289     ObjectValueTree obj;
290 
291     // GetAll the objects under service FruDevice
292     ec = getManagedObjects(ctx, "xyz.openbmc_project.EntityManager",
293                            "/xyz/openbmc_project/inventory", obj);
294     if (ec)
295     {
296         lg2::error("GetMangagedObjects failed", "ERROR", ec.message().c_str());
297         return false;
298     }
299     for (const auto& [path, objData] : obj)
300     {
301         for (const auto& [intf, propMap] : objData)
302         {
303             // Currently, these are the three different fan types supported.
304             if (intf == "xyz.openbmc_project.Configuration.AspeedFan" ||
305                 intf == "xyz.openbmc_project.Configuration.I2CFan" ||
306                 intf == "xyz.openbmc_project.Configuration.NuvotonFan")
307             {
308                 std::string fanPath = "/Fan_";
309 
310                 fanPath += std::to_string(instance);
311                 std::string objPath = path.str;
312                 objPath = objPath.substr(objPath.find_last_of("/"));
313                 if (objPath != fanPath)
314                 {
315                     continue;
316                 }
317                 auto connector = objData.find(intf + std::string(".Connector"));
318                 if (connector == objData.end())
319                 {
320                     return false;
321                 }
322                 auto findPwmName = connector->second.find("PwmName");
323                 if (findPwmName != connector->second.end())
324                 {
325                     auto fanPwmName =
326                         std::get_if<std::string>(&findPwmName->second);
327                     if (!fanPwmName)
328                     {
329                         lg2::error("PwmName parse ERROR.");
330                         return false;
331                     }
332                     pwmName = *fanPwmName;
333                     return true;
334                 }
335                 auto findPwm = connector->second.find("Pwm");
336                 if (findPwm == connector->second.end())
337                 {
338                     return false;
339                 }
340                 auto fanPwm = std::get_if<uint64_t>(&findPwm->second);
341                 if (!fanPwm)
342                 {
343                     return false;
344                 }
345                 pwmName = "Pwm_" + std::to_string(*fanPwm + 1);
346                 return true;
347             }
348         }
349     }
350     return false;
351 }
352 ipmi::RspType<uint8_t,                // Signal value
353               std::optional<uint16_t> // Fan tach value
354               >
355     appMTMGetSignal(ipmi::Context::ptr ctx, uint8_t signalTypeByte,
356                     uint8_t instance, uint8_t actionByte)
357 {
358     // mfg filter logic is used to allow MTM get signal command only in
359     // manfacturing mode.
360 
361     SmSignalGet signalType = static_cast<SmSignalGet>(signalTypeByte);
362     SmActionGet action = static_cast<SmActionGet>(actionByte);
363 
364     switch (signalType)
365     {
366         case SmSignalGet::smChassisIntrusion:
367         {
368             ipmi::Value reply;
369             if (mtm.getProperty(intrusionService, intrusionPath, intrusionIntf,
370                                 "Status", &reply) < 0)
371             {
372                 return ipmi::responseInvalidFieldRequest();
373             }
374             std::string* intrusionStatus = std::get_if<std::string>(&reply);
375             if (!intrusionStatus)
376             {
377                 return ipmi::responseUnspecifiedError();
378             }
379 
380             uint8_t status = 0;
381             if (!intrusionStatus->compare("Normal"))
382             {
383                 status = static_cast<uint8_t>(IntrusionStatus::normal);
384             }
385             else if (!intrusionStatus->compare("HardwareIntrusion"))
386             {
387                 status =
388                     static_cast<uint8_t>(IntrusionStatus::hardwareIntrusion);
389             }
390             else if (!intrusionStatus->compare("TamperingDetected"))
391             {
392                 status =
393                     static_cast<uint8_t>(IntrusionStatus::tamperingDetected);
394             }
395             else
396             {
397                 return ipmi::responseUnspecifiedError();
398             }
399             return ipmi::responseSuccess(status, std::nullopt);
400         }
401         case SmSignalGet::smFanPwmGet:
402         {
403             ipmi::Value reply;
404             std::string pwmName, fullPath;
405             if (!findPwmName(ctx, instance + 1, pwmName))
406             {
407                 // The default PWM name is Pwm_#
408                 pwmName = "Pwm_" + std::to_string(instance + 1);
409             }
410             fullPath = fanPwmPath + pwmName;
411             if (mtm.getProperty(fanService, fullPath, fanIntf, "Value",
412                                 &reply) < 0)
413             {
414                 return ipmi::responseInvalidFieldRequest();
415             }
416             double* doubleVal = std::get_if<double>(&reply);
417             if (doubleVal == nullptr)
418             {
419                 return ipmi::responseUnspecifiedError();
420             }
421             uint8_t sensorVal = std::round(*doubleVal);
422             resetMtmTimer(ctx);
423             return ipmi::responseSuccess(sensorVal, std::nullopt);
424         }
425         break;
426         case SmSignalGet::smFanTachometerGet:
427         {
428             boost::system::error_code ec;
429             using objFlatMap = boost::container::flat_map<
430                 std::string, boost::container::flat_map<
431                                  std::string, std::vector<std::string>>>;
432 
433             auto flatMap = ctx->bus->yield_method_call<objFlatMap>(
434                 ctx->yield, ec, "xyz.openbmc_project.ObjectMapper",
435                 "/xyz/openbmc_project/object_mapper",
436                 "xyz.openbmc_project.ObjectMapper", "GetSubTree",
437                 fanTachBasePath, 0, std::array<const char*, 1>{fanIntf});
438             if (ec)
439             {
440                 lg2::error("Failed to query fan tach sub tree objects");
441                 return ipmi::responseUnspecifiedError();
442             }
443             if (instance >= flatMap.size())
444             {
445                 return ipmi::responseInvalidFieldRequest();
446             }
447             auto itr = flatMap.nth(instance);
448             ipmi::Value reply;
449             if (mtm.getProperty(fanService, itr->first, fanIntf, "Value",
450                                 &reply) < 0)
451             {
452                 return ipmi::responseInvalidFieldRequest();
453             }
454 
455             double* doubleVal = std::get_if<double>(&reply);
456             if (doubleVal == nullptr)
457             {
458                 return ipmi::responseUnspecifiedError();
459             }
460             uint8_t sensorVal = FAN_PRESENT | FAN_SENSOR_PRESENT;
461             std::optional<uint16_t> fanTach = std::round(*doubleVal);
462 
463             resetMtmTimer(ctx);
464             return ipmi::responseSuccess(sensorVal, fanTach);
465         }
466         break;
467         case SmSignalGet::smIdentifyButton:
468         {
469             if (action == SmActionGet::revert || action == SmActionGet::ignore)
470             {
471                 // ButtonMasked property is not supported for ID button as it is
472                 // unnecessary. Hence if requested for revert / ignore, override
473                 // it to sample action to make tools happy.
474                 action = SmActionGet::sample;
475             }
476             // fall-through
477         }
478         case SmSignalGet::smResetButton:
479         case SmSignalGet::smPowerButton:
480         case SmSignalGet::smNMIButton:
481         {
482             std::string path;
483             if (getGpioPathForSmSignal(signalType, path) < 0)
484             {
485                 return ipmi::responseInvalidFieldRequest();
486             }
487 
488             switch (action)
489             {
490                 case SmActionGet::sample:
491                     lg2::info("case SmActionGet::sample");
492                     break;
493                 case SmActionGet::ignore:
494                 {
495                     lg2::info("case SmActionGet::ignore");
496                     if (mtm.setProperty(buttonService, path, buttonIntf,
497                                         "ButtonMasked", true) < 0)
498                     {
499                         return ipmi::responseUnspecifiedError();
500                     }
501                 }
502                 break;
503                 case SmActionGet::revert:
504                 {
505                     lg2::info("case SmActionGet::revert");
506                     if (mtm.setProperty(buttonService, path, buttonIntf,
507                                         "ButtonMasked", false) < 0)
508                     {
509                         return ipmi::responseUnspecifiedError();
510                     }
511                 }
512                 break;
513 
514                 default:
515                     return ipmi::responseInvalidFieldRequest();
516                     break;
517             }
518 
519             ipmi::Value reply;
520             if (mtm.getProperty(buttonService, path, buttonIntf,
521                                 "ButtonPressed", &reply) < 0)
522             {
523                 return ipmi::responseUnspecifiedError();
524             }
525             bool* valPtr = std::get_if<bool>(&reply);
526             if (valPtr == nullptr)
527             {
528                 return ipmi::responseUnspecifiedError();
529             }
530             resetMtmTimer(ctx);
531             uint8_t sensorVal = *valPtr;
532             return ipmi::responseSuccess(sensorVal, std::nullopt);
533         }
534         break;
535         case SmSignalGet::smNcsiDiag:
536         {
537             constexpr const char* netBasePath = "/sys/class/net/eth";
538             constexpr const char* carrierSuffix = "/carrier";
539             std::ifstream netIfs(netBasePath + std::to_string(instance) +
540                                  carrierSuffix);
541             if (!netIfs.good())
542             {
543                 return ipmi::responseInvalidFieldRequest();
544             }
545             std::string carrier;
546             netIfs >> carrier;
547             resetMtmTimer(ctx);
548             return ipmi::responseSuccess(
549                 static_cast<uint8_t>(std::stoi(carrier)), std::nullopt);
550         }
551         break;
552         default:
553             return ipmi::responseInvalidFieldRequest();
554             break;
555     }
556 }
557 
558 ipmi::RspType<> appMTMSetSignal(ipmi::Context::ptr ctx, uint8_t signalTypeByte,
559                                 uint8_t instance, uint8_t actionByte,
560                                 std::optional<uint8_t> pwmSpeed)
561 {
562     // mfg filter logic is used to allow MTM set signal command only in
563     // manfacturing mode.
564 
565     SmSignalSet signalType = static_cast<SmSignalSet>(signalTypeByte);
566     SmActionSet action = static_cast<SmActionSet>(actionByte);
567     Cc retCode = ccSuccess;
568     int8_t ret = 0;
569 
570     switch (signalType)
571     {
572         case SmSignalSet::smPowerFaultLed:
573         case SmSignalSet::smSystemReadyLed:
574         case SmSignalSet::smIdentifyLed:
575             switch (action)
576             {
577                 case SmActionSet::forceDeasserted:
578                 {
579                     lg2::info("case SmActionSet::forceDeasserted");
580 
581                     retCode = ledStoreAndSet(signalType, std::string("Off"));
582                     if (retCode != ccSuccess)
583                     {
584                         return ipmi::response(retCode);
585                     }
586                     mtm.revertTimer.start(revertTimeOut);
587                 }
588                 break;
589                 case SmActionSet::forceAsserted:
590                 {
591                     lg2::info("case SmActionSet::forceAsserted");
592 
593                     retCode = ledStoreAndSet(signalType, std::string("On"));
594                     if (retCode != ccSuccess)
595                     {
596                         return ipmi::response(retCode);
597                     }
598                     mtm.revertTimer.start(revertTimeOut);
599                     if (SmSignalSet::smPowerFaultLed == signalType)
600                     {
601                         // Deassert "system ready"
602                         retCode = ledStoreAndSet(SmSignalSet::smSystemReadyLed,
603                                                  std::string("Off"));
604                     }
605                     else if (SmSignalSet::smSystemReadyLed == signalType)
606                     {
607                         // Deassert "fault led"
608                         retCode = ledStoreAndSet(SmSignalSet::smPowerFaultLed,
609                                                  std::string("Off"));
610                     }
611                 }
612                 break;
613                 case SmActionSet::revert:
614                 {
615                     lg2::info("case SmActionSet::revert");
616                     retCode = ledRevert(signalType);
617                 }
618                 break;
619                 default:
620                 {
621                     return ipmi::responseInvalidFieldRequest();
622                 }
623             }
624             break;
625         case SmSignalSet::smFanPowerSpeed:
626         {
627             if ((action == SmActionSet::forceAsserted) && (!pwmSpeed))
628             {
629                 return ipmi::responseReqDataLenInvalid();
630             }
631 
632             if ((action == SmActionSet::forceAsserted) && (*pwmSpeed > 100))
633             {
634                 return ipmi::responseInvalidFieldRequest();
635             }
636 
637             uint8_t pwmValue = 0;
638             switch (action)
639             {
640                 case SmActionSet::revert:
641                 {
642                     if (mtm.revertFanPWM)
643                     {
644                         ret = mtm.disablePidControlService(false);
645                         if (ret < 0)
646                         {
647                             return ipmi::responseUnspecifiedError();
648                         }
649                         mtm.revertFanPWM = false;
650                     }
651                 }
652                 break;
653                 case SmActionSet::forceAsserted:
654                 {
655                     pwmValue = *pwmSpeed;
656                 } // fall-through
657                 case SmActionSet::forceDeasserted:
658                 {
659                     if (!mtm.revertFanPWM)
660                     {
661                         ret = mtm.disablePidControlService(true);
662                         if (ret < 0)
663                         {
664                             return ipmi::responseUnspecifiedError();
665                         }
666                         mtm.revertFanPWM = true;
667                     }
668                     mtm.revertTimer.start(revertTimeOut);
669                     std::string pwmName, fanPwmInstancePath;
670                     if (!findPwmName(ctx, instance + 1, pwmName))
671                     {
672                         pwmName = "Pwm_" + std::to_string(instance + 1);
673                     }
674                     fanPwmInstancePath = fanPwmPath + pwmName;
675                     ret =
676                         mtm.setProperty(fanService, fanPwmInstancePath, fanIntf,
677                                         "Value", static_cast<double>(pwmValue));
678                     if (ret < 0)
679                     {
680                         return ipmi::responseUnspecifiedError();
681                     }
682                 }
683                 break;
684                 default:
685                 {
686                     return ipmi::responseInvalidFieldRequest();
687                 }
688             }
689         }
690         break;
691         case SmSignalSet::smSpeaker:
692         {
693             lg2::info("Performing Speaker SmActionSet", "ACTION", lg2::dec,
694                       static_cast<uint8_t>(action));
695             switch (action)
696             {
697                 case SmActionSet::forceAsserted:
698                 {
699                     char beepDevName[] = "/dev/input/event0";
700                     if (mtm.mtmTestBeepFd != -1)
701                     {
702                         lg2::info("mtm beep device is opened already!");
703                         // returning success as already beep is in progress
704                         return ipmi::response(retCode);
705                     }
706 
707                     if ((mtm.mtmTestBeepFd =
708                              ::open(beepDevName, O_RDWR | O_CLOEXEC)) < 0)
709                     {
710                         lg2::error("Failed to open input device");
711                         return ipmi::responseUnspecifiedError();
712                     }
713 
714                     struct input_event event;
715                     event.type = EV_SND;
716                     event.code = SND_TONE;
717                     event.value = 2000;
718 
719                     if (::write(mtm.mtmTestBeepFd, &event,
720                                 sizeof(struct input_event)) !=
721                         sizeof(struct input_event))
722                     {
723                         lg2::error("Failed to write a tone sound event");
724                         ::close(mtm.mtmTestBeepFd);
725                         mtm.mtmTestBeepFd = -1;
726                         return ipmi::responseUnspecifiedError();
727                     }
728                     mtm.revertTimer.start(revertTimeOut);
729                 }
730                 break;
731                 case SmActionSet::revert:
732                 case SmActionSet::forceDeasserted:
733                 {
734                     if (mtm.mtmTestBeepFd != -1)
735                     {
736                         ::close(mtm.mtmTestBeepFd);
737                         mtm.mtmTestBeepFd = -1;
738                     }
739                 }
740                 break;
741                 default:
742                 {
743                     return ipmi::responseInvalidFieldRequest();
744                 }
745             }
746         }
747         break;
748         case SmSignalSet::smDiskFaultLed:
749         {
750             boost::system::error_code ec;
751             using objPaths = std::vector<std::string>;
752             std::string driveBasePath =
753                 "/xyz/openbmc_project/inventory/item/drive/";
754             static constexpr const char* driveLedIntf =
755                 "xyz.openbmc_project.Led.Group";
756             static constexpr const char* hsbpService =
757                 "xyz.openbmc_project.HsbpManager";
758 
759             auto driveList = ctx->bus->yield_method_call<objPaths>(
760                 ctx->yield, ec, "xyz.openbmc_project.ObjectMapper",
761                 "/xyz/openbmc_project/object_mapper",
762                 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths",
763                 driveBasePath, 0, std::array<const char*, 1>{driveLedIntf});
764             if (ec)
765             {
766                 lg2::error("Failed to query HSBP drive sub tree objects");
767                 return ipmi::responseUnspecifiedError();
768             }
769             std::string driveObjPath =
770                 driveBasePath + "Drive_" + std::to_string(instance + 1);
771             if (std::find(driveList.begin(), driveList.end(), driveObjPath) ==
772                 driveList.end())
773             {
774                 return ipmi::responseInvalidFieldRequest();
775             }
776             bool driveLedState = false;
777             switch (action)
778             {
779                 case SmActionSet::forceAsserted:
780                 {
781                     driveLedState = true;
782                 }
783                 break;
784                 case SmActionSet::revert:
785                 {
786                     driveLedState = false;
787                 }
788                 break;
789                 case SmActionSet::forceDeasserted:
790                 {
791                     driveLedState = false;
792                 }
793                 break;
794                 default:
795                 {
796                     return ipmi::responseInvalidFieldRequest();
797                 }
798             }
799             ret = mtm.setProperty(hsbpService, driveObjPath, driveLedIntf,
800                                   "Asserted", driveLedState);
801             if (ret < 0)
802             {
803                 return ipmi::responseUnspecifiedError();
804             }
805         }
806         break;
807         default:
808         {
809             return ipmi::responseInvalidFieldRequest();
810         }
811     }
812     if (retCode == ccSuccess)
813     {
814         resetMtmTimer(ctx);
815     }
816     return ipmi::response(retCode);
817 }
818 
819 ipmi::RspType<> mtmKeepAlive(ipmi::Context::ptr ctx, uint8_t reserved,
820                              const std::array<char, 5>& intentionalSignature)
821 {
822     // mfg filter logic is used to allow MTM keep alive command only in
823     // manfacturing mode
824 
825     constexpr std::array<char, 5> signatureOk = {'I', 'N', 'T', 'E', 'L'};
826     if (intentionalSignature != signatureOk || reserved != 0)
827     {
828         return ipmi::responseInvalidFieldRequest();
829     }
830     return ipmi::response(resetMtmTimer(ctx));
831 }
832 
833 static constexpr unsigned int makeCmdKey(unsigned int netFn, unsigned int cmd)
834 {
835     return (netFn << 8) | cmd;
836 }
837 
838 ipmi::Cc mfgFilterMessage(ipmi::message::Request::ptr request)
839 {
840     // Restricted commands, must be executed only in Manufacturing mode
841     switch (makeCmdKey(request->ctx->netFn, request->ctx->cmd))
842     {
843         // i2c controller write read command needs additional checking
844         case makeCmdKey(ipmi::netFnApp, ipmi::app::cmdMasterWriteRead):
845             if (request->payload.size() > 4)
846             {
847                 // Allow write data count > 1 only in Special mode
848                 if (mtm.getMfgMode() == SpecialMode::none)
849                 {
850                     return ipmi::ccInsufficientPrivilege;
851                 }
852             }
853             return ipmi::ccSuccess;
854         case makeCmdKey(ipmi::netFnOemOne,
855                         ipmi::intel::general::cmdGetSmSignal):
856         case makeCmdKey(ipmi::netFnOemOne,
857                         ipmi::intel::general::cmdSetSmSignal):
858         case makeCmdKey(ipmi::netFnOemOne,
859                         ipmi::intel::general::cmdMtmKeepAlive):
860         case makeCmdKey(ipmi::netFnOemOne,
861                         ipmi::intel::general::cmdSetManufacturingData):
862         case makeCmdKey(ipmi::netFnOemOne,
863                         ipmi::intel::general::cmdGetManufacturingData):
864         case makeCmdKey(ipmi::intel::netFnGeneral,
865                         ipmi::intel::general::cmdSetFITcLayout):
866         case makeCmdKey(ipmi::netFnOemOne,
867                         ipmi::intel::general::cmdMTMBMCFeatureControl):
868         case makeCmdKey(ipmi::netFnStorage, ipmi::storage::cmdWriteFruData):
869         case makeCmdKey(ipmi::netFnOemTwo, ipmi::intel::platform::cmdClearCMOS):
870 
871             // Check for Special mode
872             if (mtm.getMfgMode() == SpecialMode::none)
873             {
874                 return ipmi::ccInvalidCommand;
875             }
876             return ipmi::ccSuccess;
877         case makeCmdKey(ipmi::netFnStorage, ipmi::storage::cmdDeleteSelEntry):
878         {
879             return ipmi::ccInvalidCommand;
880         }
881     }
882     return ipmi::ccSuccess;
883 }
884 
885 static constexpr uint8_t maxEthSize = 6;
886 static constexpr uint8_t maxSupportedEth = 3;
887 static constexpr const char* factoryEthAddrBaseFileName =
888     "/var/sofs/factory-settings/network/mac/eth";
889 
890 bool findFruDevice(ipmi::Context::ptr& ctx, uint64_t& macOffset,
891                    uint64_t& busNum, uint64_t& address)
892 {
893     boost::system::error_code ec{};
894     ObjectValueTree obj;
895 
896     // GetAll the objects under service FruDevice
897     ec = getManagedObjects(ctx, "xyz.openbmc_project.EntityManager",
898                            "/xyz/openbmc_project/inventory", obj);
899     if (ec)
900     {
901         lg2::error("GetManagedObjects failed", "ERROR", ec.message().c_str());
902         return false;
903     }
904 
905     for (const auto& [path, fru] : obj)
906     {
907         for (const auto& [intf, propMap] : fru)
908         {
909             if (intf == "xyz.openbmc_project.Inventory.Item.Board.Motherboard")
910             {
911                 auto findBus = propMap.find("FruBus");
912                 auto findAddress = propMap.find("FruAddress");
913                 auto findMacOffset = propMap.find("MacOffset");
914                 if (findBus == propMap.end() || findAddress == propMap.end() ||
915                     findMacOffset == propMap.end())
916                 {
917                     continue;
918                 }
919 
920                 auto fruBus = std::get_if<uint64_t>(&findBus->second);
921                 auto fruAddress = std::get_if<uint64_t>(&findAddress->second);
922                 auto macFruOffset =
923                     std::get_if<uint64_t>(&findMacOffset->second);
924                 if (!fruBus || !fruAddress || !macFruOffset)
925                 {
926                     lg2::info("ERROR: MotherBoard FRU config data type "
927                               "invalid, not used");
928                     return false;
929                 }
930                 busNum = *fruBus;
931                 address = *fruAddress;
932                 macOffset = *macFruOffset;
933                 return true;
934             }
935         }
936     }
937     return false;
938 }
939 
940 static constexpr uint64_t fruEnd = 0xff;
941 // write rolls over within current page, need to keep mac within a page
942 static constexpr uint64_t fruPageSize = 0x8;
943 // MAC record struct: HEADER, MAC DATA, CheckSum
944 static constexpr uint64_t macRecordSize = maxEthSize + 2;
945 static_assert(fruPageSize >= macRecordSize,
946               "macRecordSize greater than eeprom page size");
947 static constexpr uint8_t macHeader = 0x40;
948 // Calculate new checksum for fru info area
949 static uint8_t calculateChecksum(std::vector<uint8_t>::const_iterator iter,
950                                  std::vector<uint8_t>::const_iterator end)
951 {
952     constexpr int checksumMod = 256;
953     uint8_t sum = std::accumulate(iter, end, static_cast<uint8_t>(0));
954     return (checksumMod - sum) % checksumMod;
955 }
956 
957 bool readMacFromFru(ipmi::Context::ptr ctx, uint8_t macIndex,
958                     std::array<uint8_t, maxEthSize>& ethData)
959 {
960     uint64_t macOffset = fruEnd;
961     uint64_t fruBus = 0;
962     uint64_t fruAddress = 0;
963 
964     if (findFruDevice(ctx, macOffset, fruBus, fruAddress))
965     {
966         lg2::info("Found mac fru", "BUS", lg2::dec,
967                   static_cast<uint8_t>(fruBus), "ADDRESS", lg2::dec,
968                   static_cast<uint8_t>(fruAddress));
969 
970         if (macOffset % fruPageSize)
971         {
972             macOffset = (macOffset / fruPageSize + 1) * fruPageSize;
973         }
974         macOffset += macIndex * fruPageSize;
975         if ((macOffset + macRecordSize) > fruEnd)
976         {
977             lg2::error("ERROR: read fru mac failed, offset invalid");
978             return false;
979         }
980         std::vector<uint8_t> writeData;
981         writeData.push_back(static_cast<uint8_t>(macOffset));
982         std::vector<uint8_t> readBuf(macRecordSize);
983         std::string i2cBus = "/dev/i2c-" + std::to_string(fruBus);
984         ipmi::Cc retI2C =
985             ipmi::i2cWriteRead(i2cBus, fruAddress, writeData, readBuf);
986         if (retI2C == ipmi::ccSuccess)
987         {
988             uint8_t cs = calculateChecksum(readBuf.cbegin(), readBuf.cend());
989             if (cs == 0)
990             {
991                 std::copy(++readBuf.begin(), --readBuf.end(), ethData.data());
992                 return true;
993             }
994         }
995     }
996     return false;
997 }
998 
999 ipmi::Cc writeMacToFru(ipmi::Context::ptr ctx, uint8_t macIndex,
1000                        std::array<uint8_t, maxEthSize>& ethData)
1001 {
1002     uint64_t macOffset = fruEnd;
1003     uint64_t fruBus = 0;
1004     uint64_t fruAddress = 0;
1005 
1006     if (findFruDevice(ctx, macOffset, fruBus, fruAddress))
1007     {
1008         lg2::info("Found mac fru", "BUS", lg2::dec,
1009                   static_cast<uint8_t>(fruBus), "ADDRESS", lg2::dec,
1010                   static_cast<uint8_t>(fruAddress));
1011 
1012         if (macOffset % fruPageSize)
1013         {
1014             macOffset = (macOffset / fruPageSize + 1) * fruPageSize;
1015         }
1016         macOffset += macIndex * fruPageSize;
1017         if ((macOffset + macRecordSize) > fruEnd)
1018         {
1019             lg2::error("ERROR: write mac fru failed, offset invalid.");
1020             return ipmi::ccParmOutOfRange;
1021         }
1022         std::vector<uint8_t> writeData;
1023         writeData.reserve(macRecordSize + 1); // include start location
1024         writeData.push_back(static_cast<uint8_t>(macOffset));
1025         writeData.push_back(macHeader);
1026         std::for_each(ethData.cbegin(), ethData.cend(),
1027                       [&](uint8_t i) { writeData.push_back(i); });
1028         uint8_t macCheckSum =
1029             calculateChecksum(++writeData.cbegin(), writeData.cend());
1030         writeData.push_back(macCheckSum);
1031 
1032         std::string i2cBus = "/dev/i2c-" + std::to_string(fruBus);
1033         std::vector<uint8_t> readBuf;
1034         ipmi::Cc ret =
1035             ipmi::i2cWriteRead(i2cBus, fruAddress, writeData, readBuf);
1036 
1037         // prepare for read to detect chip is write protected
1038         writeData.resize(1);
1039         readBuf.resize(maxEthSize + 1); // include macHeader
1040 
1041         switch (ret)
1042         {
1043             case ipmi::ccSuccess:
1044                 // Wait for internal write cycle to complete
1045                 // example: ATMEL 24c0x chip has Twr spec as 5ms
1046 
1047                 // Ideally we want yield wait, but currently following code
1048                 // crash with "thread not supported"
1049                 // boost::asio::deadline_timer timer(
1050                 //    boost::asio::get_associated_executor(ctx->yield),
1051                 //    boost::posix_time::seconds(1));
1052                 // timer.async_wait(ctx->yield);
1053                 // use usleep as temp WA
1054                 usleep(5000);
1055                 if (ipmi::i2cWriteRead(i2cBus, fruAddress, writeData,
1056                                        readBuf) == ipmi::ccSuccess)
1057                 {
1058                     if (std::equal(ethData.begin(), ethData.end(),
1059                                    ++readBuf.begin())) // skip macHeader
1060                     {
1061                         return ipmi::ccSuccess;
1062                     }
1063                     lg2::info("INFO: write mac fru verify failed, fru may be "
1064                               "write protected.");
1065                 }
1066                 return ipmi::ccCommandNotAvailable;
1067             default:
1068                 if (ipmi::i2cWriteRead(i2cBus, fruAddress, writeData,
1069                                        readBuf) == ipmi::ccSuccess)
1070                 {
1071                     lg2::info("INFO: write mac fru failed, but successfully "
1072                               "read from fru, fru may be write protected.");
1073                     return ipmi::ccCommandNotAvailable;
1074                 }
1075                 else // assume failure is due to no eeprom on board
1076                 {
1077                     lg2::error("ERROR: write mac fru failed, assume no eeprom "
1078                                "is available.");
1079                 }
1080                 break;
1081         }
1082     }
1083     // no FRU eeprom found
1084     return ipmi::ccDestinationUnavailable;
1085 }
1086 
1087 ipmi::RspType<> setManufacturingData(ipmi::Context::ptr ctx, uint8_t dataType,
1088                                      std::array<uint8_t, maxEthSize> ethData)
1089 {
1090     // mfg filter logic will restrict this command executing only in mfg
1091     // mode.
1092     if (dataType >= maxSupportedEth)
1093     {
1094         return ipmi::responseParmOutOfRange();
1095     }
1096 
1097     constexpr uint8_t invalidData = 0;
1098     constexpr uint8_t validData = 1;
1099 
1100     ipmi::Cc ret = writeMacToFru(ctx, dataType, ethData);
1101     if (ret != ipmi::ccDestinationUnavailable)
1102     {
1103         resetMtmTimer(ctx);
1104         return response(ret);
1105     }
1106 
1107     constexpr uint8_t ethAddrStrSize =
1108         19; // XX:XX:XX:XX:XX:XX + \n + null termination;
1109     std::vector<uint8_t> buff(ethAddrStrSize);
1110     std::snprintf(reinterpret_cast<char*>(buff.data()), ethAddrStrSize,
1111                   "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx\n", ethData.at(0),
1112                   ethData.at(1), ethData.at(2), ethData.at(3), ethData.at(4),
1113                   ethData.at(5));
1114     std::ofstream oEthFile(factoryEthAddrBaseFileName +
1115                                std::to_string(dataType),
1116                            std::ofstream::out);
1117     if (!oEthFile.good())
1118     {
1119         return ipmi::responseUnspecifiedError();
1120     }
1121 
1122     oEthFile << reinterpret_cast<char*>(buff.data());
1123     oEthFile.flush();
1124     oEthFile.close();
1125 
1126     resetMtmTimer(ctx);
1127     return ipmi::responseSuccess();
1128 }
1129 
1130 ipmi::RspType<uint8_t, std::array<uint8_t, maxEthSize>>
1131     getManufacturingData(ipmi::Context::ptr ctx, uint8_t dataType)
1132 {
1133     // mfg filter logic will restrict this command executing only in mfg
1134     // mode.
1135     if (dataType >= maxSupportedEth)
1136     {
1137         return ipmi::responseParmOutOfRange();
1138     }
1139     std::array<uint8_t, maxEthSize> ethData{0};
1140     constexpr uint8_t invalidData = 0;
1141     constexpr uint8_t validData = 1;
1142 
1143     std::ifstream iEthFile(factoryEthAddrBaseFileName +
1144                                std::to_string(dataType),
1145                            std::ifstream::in);
1146     if (!iEthFile.good())
1147     {
1148         if (readMacFromFru(ctx, dataType, ethData))
1149         {
1150             resetMtmTimer(ctx);
1151             return ipmi::responseSuccess(validData, ethData);
1152         }
1153         return ipmi::responseSuccess(invalidData, ethData);
1154     }
1155     std::string ethStr;
1156     iEthFile >> ethStr;
1157     uint8_t* data = ethData.data();
1158     std::sscanf(ethStr.c_str(), "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx",
1159                 data, (data + 1), (data + 2), (data + 3), (data + 4),
1160                 (data + 5));
1161 
1162     resetMtmTimer(ctx);
1163     return ipmi::responseSuccess(validData, ethData);
1164 }
1165 
1166 /** @brief implements slot controller write read IPMI command which can be used
1167  * for low-level I2C/SMBus write, read or write-read access for PCIE slots
1168  * @param reserved - skip 3 bit
1169  * @param muxType - mux type
1170  * @param addressType - address type
1171  * @param bbSlotNum - baseboard slot number
1172  * @param riserSlotNum - riser slot number
1173  * @param reserved2 - skip 2 bit
1174  * @param targetAddr - target address
1175  * @param readCount - number of bytes to be read
1176  * @param writeData - data to be written
1177  *
1178  * @returns IPMI completion code plus response data
1179  */
1180 
1181 ipmi::RspType<std::vector<uint8_t>> appSlotI2CControllerWriteRead(
1182     uint3_t reserved, uint3_t muxType, uint2_t addressType, uint3_t bbSlotNum,
1183     uint3_t riserSlotNum, uint2_t reserved2, uint8_t targetAddr,
1184     uint8_t readCount, std::vector<uint8_t> writeData)
1185 {
1186     if (reserved || reserved2)
1187     {
1188         return ipmi::responseInvalidFieldRequest();
1189     }
1190     const size_t writeCount = writeData.size();
1191     std::string i2cBus;
1192     if (addressType == slotAddressTypeBus)
1193     {
1194         std::string path = "/dev/i2c-mux/";
1195         if (muxType == bbRiserMux)
1196         {
1197             path += "Riser_" + std::to_string(static_cast<uint8_t>(bbSlotNum)) +
1198                     "_Mux/Pcie_Slot_" +
1199                     std::to_string(static_cast<uint8_t>(riserSlotNum));
1200         }
1201         else if (muxType == leftRiserMux)
1202         {
1203             path += "Left_Riser_Mux/Slot_" +
1204                     std::to_string(static_cast<uint8_t>(riserSlotNum));
1205         }
1206         else if (muxType == rightRiserMux)
1207         {
1208             path += "Right_Riser_Mux/Slot_" +
1209                     std::to_string(static_cast<uint8_t>(riserSlotNum));
1210         }
1211         else if (muxType == pcieMux)
1212         {
1213             path += "PCIe_Mux/Slot_" +
1214                     std::to_string(static_cast<uint8_t>(riserSlotNum));
1215         }
1216         else if (muxType == hsbpMux)
1217         {
1218             path += "HSBP_Mux/Slot" +
1219                     std::to_string(static_cast<uint8_t>(riserSlotNum));
1220         }
1221         phosphor::logging::log<phosphor::logging::level::DEBUG>(
1222             ("Path is: " + path).c_str());
1223         if (std::filesystem::exists(path) && std::filesystem::is_symlink(path))
1224         {
1225             i2cBus = std::filesystem::read_symlink(path);
1226         }
1227         else
1228         {
1229             lg2::error("Controller write read command: Cannot get BusID");
1230             return ipmi::responseInvalidFieldRequest();
1231         }
1232     }
1233     else if (addressType == slotAddressTypeUniqueid)
1234     {
1235         i2cBus = "/dev/i2c-" +
1236                  std::to_string(static_cast<uint8_t>(bbSlotNum) |
1237                                 (static_cast<uint8_t>(riserSlotNum) << 3));
1238     }
1239     else
1240     {
1241         lg2::error("Controller write read command: invalid request");
1242         return ipmi::responseInvalidFieldRequest();
1243     }
1244 
1245     // Allow single byte write as it is offset byte to read the data, rest
1246     // allow only in Special mode.
1247     if (writeCount > 1)
1248     {
1249         if (mtm.getMfgMode() == SpecialMode::none)
1250         {
1251             return ipmi::responseInsufficientPrivilege();
1252         }
1253     }
1254 
1255     if (readCount > slotI2CMaxReadSize)
1256     {
1257         lg2::error("Controller write read command: Read count exceeds limit");
1258         return ipmi::responseParmOutOfRange();
1259     }
1260 
1261     if (!readCount && !writeCount)
1262     {
1263         lg2::error("Controller write read command: Read & write count are 0");
1264         return ipmi::responseInvalidFieldRequest();
1265     }
1266 
1267     std::vector<uint8_t> readBuf(readCount);
1268 
1269     ipmi::Cc retI2C =
1270         ipmi::i2cWriteRead(i2cBus, targetAddr, writeData, readBuf);
1271     if (retI2C != ipmi::ccSuccess)
1272     {
1273         return ipmi::response(retI2C);
1274     }
1275 
1276     return ipmi::responseSuccess(readBuf);
1277 }
1278 
1279 ipmi::RspType<> clearCMOS()
1280 {
1281     // There is an i2c device on bus 4, the target address is 0x38. Based on
1282     // the spec, writing 0x1 to address 0x61 on this device, will trigger
1283     // the clear CMOS action.
1284     constexpr uint8_t targetAddr = 0x38;
1285     std::string i2cBus = "/dev/i2c-4";
1286     std::vector<uint8_t> writeData = {0x61, 0x1};
1287     std::vector<uint8_t> readBuf(0);
1288 
1289     ipmi::Cc retI2C =
1290         ipmi::i2cWriteRead(i2cBus, targetAddr, writeData, readBuf);
1291     return ipmi::response(retI2C);
1292 }
1293 
1294 ipmi::RspType<> setFITcLayout(uint32_t layout)
1295 {
1296     static constexpr const char* factoryFITcLayout =
1297         "/var/sofs/factory-settings/layout/fitc";
1298     std::filesystem::path fitcDir =
1299         std::filesystem::path(factoryFITcLayout).parent_path();
1300     std::error_code ec;
1301     std::filesystem::create_directories(fitcDir, ec);
1302     if (ec)
1303     {
1304         return ipmi::responseUnspecifiedError();
1305     }
1306     try
1307     {
1308         std::ofstream file(factoryFITcLayout);
1309         file << layout;
1310         file.flush();
1311         file.close();
1312     }
1313     catch (const std::exception& e)
1314     {
1315         return ipmi::responseUnspecifiedError();
1316     }
1317 
1318     return ipmi::responseSuccess();
1319 }
1320 
1321 static std::vector<std::string>
1322     getMCTPServiceConfigPaths(ipmi::Context::ptr& ctx)
1323 {
1324     boost::system::error_code ec;
1325     auto configPaths = ctx->bus->yield_method_call<std::vector<std::string>>(
1326         ctx->yield, ec, "xyz.openbmc_project.ObjectMapper",
1327         "/xyz/openbmc_project/object_mapper",
1328         "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths",
1329         "/xyz/openbmc_project/inventory/system/board", 2,
1330         std::array<const char*, 1>{
1331             "xyz.openbmc_project.Configuration.MctpConfiguration"});
1332     if (ec)
1333     {
1334         throw std::runtime_error(
1335             "Failed to query configuration sub tree objects");
1336     }
1337     return configPaths;
1338 }
1339 
1340 static ipmi::RspType<> startOrStopService(ipmi::Context::ptr& ctx,
1341                                           const uint8_t enable,
1342                                           const std::string& serviceName,
1343                                           bool disableOrEnableUnitFiles = true)
1344 {
1345     constexpr bool runtimeOnly = false;
1346     constexpr bool force = false;
1347 
1348     boost::system::error_code ec;
1349     switch (enable)
1350     {
1351         case ipmi::SupportedFeatureActions::stop:
1352             ctx->bus->yield_method_call(ctx->yield, ec, systemDService,
1353                                         systemDObjPath, systemDMgrIntf,
1354                                         "StopUnit", serviceName, "replace");
1355             break;
1356         case ipmi::SupportedFeatureActions::start:
1357             ctx->bus->yield_method_call(ctx->yield, ec, systemDService,
1358                                         systemDObjPath, systemDMgrIntf,
1359                                         "StartUnit", serviceName, "replace");
1360             break;
1361         case ipmi::SupportedFeatureActions::disable:
1362             if (disableOrEnableUnitFiles == true)
1363             {
1364                 ctx->bus->yield_method_call(
1365                     ctx->yield, ec, systemDService, systemDObjPath,
1366                     systemDMgrIntf, "DisableUnitFiles",
1367                     std::array<const char*, 1>{serviceName.c_str()},
1368                     runtimeOnly);
1369             }
1370             ctx->bus->yield_method_call(
1371                 ctx->yield, ec, systemDService, systemDObjPath, systemDMgrIntf,
1372                 "MaskUnitFiles",
1373                 std::array<const char*, 1>{serviceName.c_str()}, runtimeOnly,
1374                 force);
1375             break;
1376         case ipmi::SupportedFeatureActions::enable:
1377             ctx->bus->yield_method_call(
1378                 ctx->yield, ec, systemDService, systemDObjPath, systemDMgrIntf,
1379                 "UnmaskUnitFiles",
1380                 std::array<const char*, 1>{serviceName.c_str()}, runtimeOnly);
1381             if (disableOrEnableUnitFiles == true)
1382             {
1383                 ctx->bus->yield_method_call(
1384                     ctx->yield, ec, systemDService, systemDObjPath,
1385                     systemDMgrIntf, "EnableUnitFiles",
1386                     std::array<const char*, 1>{serviceName.c_str()},
1387                     runtimeOnly, force);
1388             }
1389             break;
1390         default:
1391             lg2::warning("ERROR: Invalid feature action selected", "ACTION",
1392                          lg2::dec, enable);
1393             return ipmi::responseInvalidFieldRequest();
1394     }
1395     if (ec)
1396     {
1397         lg2::warning("ERROR: Service start or stop failed", "SERVICE",
1398                      serviceName.c_str());
1399         return ipmi::responseUnspecifiedError();
1400     }
1401     return ipmi::responseSuccess();
1402 }
1403 
1404 static std::string getMCTPServiceName(const std::string& objectPath)
1405 {
1406     const auto serviceArgument = boost::algorithm::replace_all_copy(
1407         boost::algorithm::replace_first_copy(
1408             objectPath, "/xyz/openbmc_project/inventory/system/board/", ""),
1409         "/", "_2f");
1410     std::string unitName =
1411         "xyz.openbmc_project.mctpd@" + serviceArgument + ".service";
1412     return unitName;
1413 }
1414 
1415 static ipmi::RspType<> handleMCTPFeature(ipmi::Context::ptr& ctx,
1416                                          const uint8_t enable,
1417                                          const std::string& binding)
1418 {
1419     std::vector<std::string> configPaths;
1420     try
1421     {
1422         configPaths = getMCTPServiceConfigPaths(ctx);
1423     }
1424     catch (const std::exception& e)
1425     {
1426         lg2::error(e.what());
1427         return ipmi::responseUnspecifiedError();
1428     }
1429 
1430     for (const auto& objectPath : configPaths)
1431     {
1432         auto const pos = objectPath.find_last_of('/');
1433         if (binding == objectPath.substr(pos + 1))
1434         {
1435             return startOrStopService(ctx, enable,
1436                                       getMCTPServiceName(objectPath), false);
1437         }
1438     }
1439     return ipmi::responseSuccess();
1440 }
1441 
1442 /** @brief implements MTM BMC Feature Control IPMI command which can be
1443  * used to enable or disable the supported BMC features.
1444  * @param yield - context object that represents the currently executing
1445  * coroutine
1446  * @param feature - feature enum to enable or disable
1447  * @param enable - enable or disable the feature
1448  * @param featureArg - custom arguments for that feature
1449  * @param reserved - reserved for future use
1450  *
1451  * @returns IPMI completion code
1452  */
1453 ipmi::RspType<> mtmBMCFeatureControl(ipmi::Context::ptr ctx,
1454                                      const uint8_t feature,
1455                                      const uint8_t enable,
1456                                      const uint8_t featureArg,
1457                                      const uint16_t reserved)
1458 {
1459     if (reserved != 0)
1460     {
1461         return ipmi::responseInvalidFieldRequest();
1462     }
1463 
1464     switch (feature)
1465     {
1466         case ipmi::SupportedFeatureControls::mctp:
1467             switch (featureArg)
1468             {
1469                 case ipmi::SupportedMCTPBindings::mctpPCIe:
1470                     return handleMCTPFeature(ctx, enable, "MCTP_PCIe");
1471                 case ipmi::SupportedMCTPBindings::mctpSMBusHSBP:
1472                     return handleMCTPFeature(ctx, enable, "MCTP_SMBus_HSBP");
1473                 case ipmi::SupportedMCTPBindings::mctpSMBusPCIeSlot:
1474                     return handleMCTPFeature(ctx, enable,
1475                                              "MCTP_SMBus_PCIe_slot");
1476                 default:
1477                     return ipmi::responseInvalidFieldRequest();
1478             }
1479             break;
1480         case ipmi::SupportedFeatureControls::pcieScan:
1481             if (featureArg != 0)
1482             {
1483                 return ipmi::responseInvalidFieldRequest();
1484             }
1485             startOrStopService(ctx, enable, "xyz.openbmc_project.PCIe.service");
1486             break;
1487         default:
1488             return ipmi::responseInvalidFieldRequest();
1489     }
1490     return ipmi::responseSuccess();
1491 }
1492 } // namespace ipmi
1493 
1494 void register_mtm_commands() __attribute__((constructor));
1495 void register_mtm_commands()
1496 {
1497     // <Get SM Signal>
1498     ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnGeneral,
1499                           ipmi::intel::general::cmdGetSmSignal,
1500                           ipmi::Privilege::Admin, ipmi::appMTMGetSignal);
1501 
1502     ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnGeneral,
1503                           ipmi::intel::general::cmdSetSmSignal,
1504                           ipmi::Privilege::Admin, ipmi::appMTMSetSignal);
1505 
1506     ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnGeneral,
1507                           ipmi::intel::general::cmdMtmKeepAlive,
1508                           ipmi::Privilege::Admin, ipmi::mtmKeepAlive);
1509 
1510     ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnGeneral,
1511                           ipmi::intel::general::cmdSetManufacturingData,
1512                           ipmi::Privilege::Admin, ipmi::setManufacturingData);
1513 
1514     ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnGeneral,
1515                           ipmi::intel::general::cmdGetManufacturingData,
1516                           ipmi::Privilege::Admin, ipmi::getManufacturingData);
1517 
1518     ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnGeneral,
1519                           ipmi::intel::general::cmdSetFITcLayout,
1520                           ipmi::Privilege::Admin, ipmi::setFITcLayout);
1521 
1522     ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnGeneral,
1523                           ipmi::intel::general::cmdMTMBMCFeatureControl,
1524                           ipmi::Privilege::Admin, ipmi::mtmBMCFeatureControl);
1525 
1526     ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnApp,
1527                           ipmi::intel::general::cmdSlotI2CControllerWriteRead,
1528                           ipmi::Privilege::Admin,
1529                           ipmi::appSlotI2CControllerWriteRead);
1530 
1531     ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnPlatform,
1532                           ipmi::intel::platform::cmdClearCMOS,
1533                           ipmi::Privilege::Admin, ipmi::clearCMOS);
1534 
1535     ipmi::registerFilter(ipmi::prioOemBase,
1536                          [](ipmi::message::Request::ptr request) {
1537                              return ipmi::mfgFilterMessage(request);
1538                          });
1539 }
1540