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