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/container/flat_map.hpp>
20 #include <ipmid/api.hpp>
21 #include <manufacturingcommands.hpp>
22 #include <oemcommands.hpp>
23 
24 #include <filesystem>
25 #include <fstream>
26 
27 namespace ipmi
28 {
29 
30 Manufacturing mtm;
31 
32 static auto revertTimeOut =
33     std::chrono::duration_cast<std::chrono::microseconds>(
34         std::chrono::seconds(60)); // 1 minute timeout
35 
36 static constexpr uint8_t slotAddressTypeBus = 0;
37 static constexpr uint8_t slotAddressTypeUniqueid = 1;
38 static constexpr uint8_t slotI2CMaxReadSize = 35;
39 
40 static constexpr const char* callbackMgrService =
41     "xyz.openbmc_project.CallbackManager";
42 static constexpr const char* callbackMgrIntf =
43     "xyz.openbmc_project.CallbackManager";
44 static constexpr const char* callbackMgrObjPath =
45     "/xyz/openbmc_project/CallbackManager";
46 static constexpr const char* retriggerLedUpdate = "RetriggerLEDUpdate";
47 
48 const static constexpr char* systemDService = "org.freedesktop.systemd1";
49 const static constexpr char* systemDObjPath = "/org/freedesktop/systemd1";
50 const static constexpr char* systemDMgrIntf =
51     "org.freedesktop.systemd1.Manager";
52 const static constexpr char* pidControlService = "phosphor-pid-control.service";
53 
54 static inline Cc resetMtmTimer(ipmi::Context::ptr ctx)
55 {
56     boost::system::error_code ec;
57     ctx->bus->yield_method_call<>(ctx->yield, ec, specialModeService,
58                                   specialModeObjPath, specialModeIntf,
59                                   "ResetTimer");
60     if (ec)
61     {
62         phosphor::logging::log<phosphor::logging::level::ERR>(
63             "Failed to reset the manufacturing mode timer");
64         return ccUnspecifiedError;
65     }
66     return ccSuccess;
67 }
68 
69 int getGpioPathForSmSignal(const SmSignalGet signal, std::string& path)
70 {
71     switch (signal)
72     {
73         case SmSignalGet::smPowerButton:
74             path = "/xyz/openbmc_project/chassis/buttons/power";
75             break;
76         case SmSignalGet::smResetButton:
77             path = "/xyz/openbmc_project/chassis/buttons/reset";
78             break;
79         case SmSignalGet::smNMIButton:
80             path = "/xyz/openbmc_project/chassis/buttons/nmi";
81             break;
82         case SmSignalGet::smIdentifyButton:
83             path = "/xyz/openbmc_project/chassis/buttons/id";
84             break;
85         default:
86             return -1;
87             break;
88     }
89     return 0;
90 }
91 
92 ipmi_ret_t ledStoreAndSet(SmSignalSet signal, const std::string& setState)
93 {
94     LedProperty* ledProp = mtm.findLedProperty(signal);
95     if (ledProp == nullptr)
96     {
97         return IPMI_CC_INVALID_FIELD_REQUEST;
98     }
99 
100     std::string ledName = ledProp->getName();
101     std::string ledService = ledServicePrefix + ledName;
102     std::string ledPath = ledPathPrefix + ledName;
103     ipmi::Value presentState;
104 
105     if (false == ledProp->getLock())
106     {
107         if (mtm.getProperty(ledService.c_str(), ledPath.c_str(), ledIntf,
108                             "State", &presentState) != 0)
109         {
110             return IPMI_CC_UNSPECIFIED_ERROR;
111         }
112         ledProp->setPrevState(std::get<std::string>(presentState));
113         ledProp->setLock(true);
114         if (signal == SmSignalSet::smPowerFaultLed ||
115             signal == SmSignalSet::smSystemReadyLed)
116         {
117             mtm.revertLedCallback = true;
118         }
119     }
120     if (mtm.setProperty(ledService, ledPath, ledIntf, "State",
121                         ledStateStr + setState) != 0)
122     {
123         return IPMI_CC_UNSPECIFIED_ERROR;
124     }
125     return IPMI_CC_OK;
126 }
127 
128 ipmi_ret_t ledRevert(SmSignalSet signal)
129 {
130     LedProperty* ledProp = mtm.findLedProperty(signal);
131     if (ledProp == nullptr)
132     {
133         return IPMI_CC_INVALID_FIELD_REQUEST;
134     }
135     if (true == ledProp->getLock())
136     {
137         ledProp->setLock(false);
138         if (signal == SmSignalSet::smPowerFaultLed ||
139             signal == SmSignalSet::smSystemReadyLed)
140         {
141             try
142             {
143                 ipmi::method_no_args::callDbusMethod(
144                     *getSdBus(), callbackMgrService, callbackMgrObjPath,
145                     callbackMgrIntf, retriggerLedUpdate);
146             }
147             catch (sdbusplus::exception_t& e)
148             {
149                 return IPMI_CC_UNSPECIFIED_ERROR;
150             }
151             mtm.revertLedCallback = false;
152         }
153         else
154         {
155             std::string ledName = ledProp->getName();
156             std::string ledService = ledServicePrefix + ledName;
157             std::string ledPath = ledPathPrefix + ledName;
158             if (mtm.setProperty(ledService, ledPath, ledIntf, "State",
159                                 ledProp->getPrevState()) != 0)
160             {
161                 return IPMI_CC_UNSPECIFIED_ERROR;
162             }
163         }
164     }
165     return IPMI_CC_OK;
166 }
167 
168 void Manufacturing::initData()
169 {
170     ledPropertyList.push_back(
171         LedProperty(SmSignalSet::smPowerFaultLed, "status_amber"));
172     ledPropertyList.push_back(
173         LedProperty(SmSignalSet::smSystemReadyLed, "status_green"));
174     ledPropertyList.push_back(
175         LedProperty(SmSignalSet::smIdentifyLed, "identify"));
176 }
177 
178 void Manufacturing::revertTimerHandler()
179 {
180 
181 #ifdef BMC_VALIDATION_UNSECURE_FEATURE
182     if (mtm.getMfgMode() == SpecialMode::valUnsecure)
183     {
184         // Don't revert the behaviour for validation unsecure mode.
185         return;
186     }
187 #endif
188     if (revertFanPWM)
189     {
190         revertFanPWM = false;
191         disablePidControlService(false);
192     }
193 
194     if (mtmTestBeepFd != -1)
195     {
196         ::close(mtmTestBeepFd);
197         mtmTestBeepFd = -1;
198     }
199 
200     for (const auto& ledProperty : ledPropertyList)
201     {
202         const std::string& ledName = ledProperty.getName();
203         ledRevert(ledProperty.getSignal());
204     }
205 }
206 
207 Manufacturing::Manufacturing() :
208     revertTimer([&](void) { revertTimerHandler(); })
209 {
210     initData();
211 }
212 
213 int8_t Manufacturing::getProperty(const std::string& service,
214                                   const std::string& path,
215                                   const std::string& interface,
216                                   const std::string& propertyName,
217                                   ipmi::Value* reply)
218 {
219     try
220     {
221         *reply = ipmi::getDbusProperty(*getSdBus(), service, path, interface,
222                                        propertyName);
223     }
224     catch (const sdbusplus::exception::SdBusError& e)
225     {
226         phosphor::logging::log<phosphor::logging::level::INFO>(
227             "ERROR: getProperty");
228         return -1;
229     }
230 
231     return 0;
232 }
233 
234 int8_t Manufacturing::setProperty(const std::string& service,
235                                   const std::string& path,
236                                   const std::string& interface,
237                                   const std::string& propertyName,
238                                   ipmi::Value value)
239 {
240     try
241     {
242         ipmi::setDbusProperty(*getSdBus(), service, path, interface,
243                               propertyName, value);
244     }
245     catch (const sdbusplus::exception::SdBusError& e)
246     {
247         phosphor::logging::log<phosphor::logging::level::INFO>(
248             "ERROR: setProperty");
249         return -1;
250     }
251 
252     return 0;
253 }
254 
255 int8_t Manufacturing::disablePidControlService(const bool disable)
256 {
257     try
258     {
259         auto dbus = getSdBus();
260         auto method = dbus->new_method_call(systemDService, systemDObjPath,
261                                             systemDMgrIntf,
262                                             disable ? "StopUnit" : "StartUnit");
263         method.append(pidControlService, "replace");
264         auto reply = dbus->call(method);
265     }
266     catch (const sdbusplus::exception::SdBusError& e)
267     {
268         phosphor::logging::log<phosphor::logging::level::INFO>(
269             "ERROR: phosphor-pid-control service start or stop failed");
270         return -1;
271     }
272     return 0;
273 }
274 
275 ipmi::RspType<uint8_t,                // Signal value
276               std::optional<uint16_t> // Fan tach value
277               >
278     appMTMGetSignal(ipmi::Context::ptr ctx, uint8_t signalTypeByte,
279                     uint8_t instance, uint8_t actionByte)
280 {
281     // mfg filter logic is used to allow MTM get signal command only in
282     // manfacturing mode.
283 
284     SmSignalGet signalType = static_cast<SmSignalGet>(signalTypeByte);
285     SmActionGet action = static_cast<SmActionGet>(actionByte);
286 
287     switch (signalType)
288     {
289         case SmSignalGet::smChassisIntrusion:
290         {
291             ipmi::Value reply;
292             if (mtm.getProperty(intrusionService, intrusionPath, intrusionIntf,
293                                 "Status", &reply) < 0)
294             {
295                 return ipmi::responseInvalidFieldRequest();
296             }
297             std::string* intrusionStatus = std::get_if<std::string>(&reply);
298             if (!intrusionStatus)
299             {
300                 return ipmi::responseUnspecifiedError();
301             }
302 
303             uint8_t status = 0;
304             if (!intrusionStatus->compare("Normal"))
305             {
306                 status = static_cast<uint8_t>(IntrusionStatus::normal);
307             }
308             else if (!intrusionStatus->compare("HardwareIntrusion"))
309             {
310                 status =
311                     static_cast<uint8_t>(IntrusionStatus::hardwareIntrusion);
312             }
313             else if (!intrusionStatus->compare("TamperingDetected"))
314             {
315                 status =
316                     static_cast<uint8_t>(IntrusionStatus::tamperingDetected);
317             }
318             else
319             {
320                 return ipmi::responseUnspecifiedError();
321             }
322             return ipmi::responseSuccess(status, std::nullopt);
323         }
324         case SmSignalGet::smFanPwmGet:
325         {
326             ipmi::Value reply;
327             std::string fullPath = fanPwmPath + std::to_string(instance + 1);
328             if (mtm.getProperty(fanService, fullPath, fanIntf, "Value",
329                                 &reply) < 0)
330             {
331                 return ipmi::responseInvalidFieldRequest();
332             }
333             double* doubleVal = std::get_if<double>(&reply);
334             if (doubleVal == nullptr)
335             {
336                 return ipmi::responseUnspecifiedError();
337             }
338             uint8_t sensorVal = std::round(*doubleVal);
339             resetMtmTimer(ctx);
340             return ipmi::responseSuccess(sensorVal, std::nullopt);
341         }
342         break;
343         case SmSignalGet::smFanTachometerGet:
344         {
345             boost::system::error_code ec;
346             using objFlatMap = boost::container::flat_map<
347                 std::string, boost::container::flat_map<
348                                  std::string, std::vector<std::string>>>;
349 
350             auto flatMap = ctx->bus->yield_method_call<objFlatMap>(
351                 ctx->yield, ec, "xyz.openbmc_project.ObjectMapper",
352                 "/xyz/openbmc_project/object_mapper",
353                 "xyz.openbmc_project.ObjectMapper", "GetSubTree",
354                 fanTachBasePath, 0, std::array<const char*, 1>{fanIntf});
355             if (ec)
356             {
357                 phosphor::logging::log<phosphor::logging::level::ERR>(
358                     "Failed to query fan tach sub tree objects");
359                 return ipmi::responseUnspecifiedError();
360             }
361             if (instance >= flatMap.size())
362             {
363                 return ipmi::responseInvalidFieldRequest();
364             }
365             auto itr = flatMap.nth(instance);
366             ipmi::Value reply;
367             if (mtm.getProperty(fanService, itr->first, fanIntf, "Value",
368                                 &reply) < 0)
369             {
370                 return ipmi::responseInvalidFieldRequest();
371             }
372 
373             double* doubleVal = std::get_if<double>(&reply);
374             if (doubleVal == nullptr)
375             {
376                 return ipmi::responseUnspecifiedError();
377             }
378             uint8_t sensorVal = FAN_PRESENT | FAN_SENSOR_PRESENT;
379             std::optional<uint16_t> fanTach = std::round(*doubleVal);
380 
381             resetMtmTimer(ctx);
382             return ipmi::responseSuccess(sensorVal, fanTach);
383         }
384         break;
385         case SmSignalGet::smIdentifyButton:
386         {
387             if (action == SmActionGet::revert || action == SmActionGet::ignore)
388             {
389                 // ButtonMasked property is not supported for ID button as it is
390                 // unnecessary. Hence if requested for revert / ignore, override
391                 // it to sample action to make tools happy.
392                 action = SmActionGet::sample;
393             }
394             // fall-through
395         }
396         case SmSignalGet::smResetButton:
397         case SmSignalGet::smPowerButton:
398         case SmSignalGet::smNMIButton:
399         {
400             std::string path;
401             if (getGpioPathForSmSignal(signalType, path) < 0)
402             {
403                 return ipmi::responseInvalidFieldRequest();
404             }
405 
406             switch (action)
407             {
408                 case SmActionGet::sample:
409                     phosphor::logging::log<phosphor::logging::level::INFO>(
410                         "case SmActionGet::sample");
411                     break;
412                 case SmActionGet::ignore:
413                 {
414                     phosphor::logging::log<phosphor::logging::level::INFO>(
415                         "case SmActionGet::ignore");
416                     if (mtm.setProperty(buttonService, path, buttonIntf,
417                                         "ButtonMasked", true) < 0)
418                     {
419                         return ipmi::responseUnspecifiedError();
420                     }
421                 }
422                 break;
423                 case SmActionGet::revert:
424                 {
425                     phosphor::logging::log<phosphor::logging::level::INFO>(
426                         "case SmActionGet::revert");
427                     if (mtm.setProperty(buttonService, path, buttonIntf,
428                                         "ButtonMasked", false) < 0)
429                     {
430                         return ipmi::responseUnspecifiedError();
431                     }
432                 }
433                 break;
434 
435                 default:
436                     return ipmi::responseInvalidFieldRequest();
437                     break;
438             }
439 
440             ipmi::Value reply;
441             if (mtm.getProperty(buttonService, path, buttonIntf,
442                                 "ButtonPressed", &reply) < 0)
443             {
444                 return ipmi::responseUnspecifiedError();
445             }
446             bool* valPtr = std::get_if<bool>(&reply);
447             if (valPtr == nullptr)
448             {
449                 return ipmi::responseUnspecifiedError();
450             }
451             resetMtmTimer(ctx);
452             uint8_t sensorVal = *valPtr;
453             return ipmi::responseSuccess(sensorVal, std::nullopt);
454         }
455         break;
456         case SmSignalGet::smNcsiDiag:
457         {
458             constexpr const char* netBasePath = "/sys/class/net/eth";
459             constexpr const char* carrierSuffix = "/carrier";
460             std::ifstream netIfs(netBasePath + std::to_string(instance) +
461                                  carrierSuffix);
462             if (!netIfs.good())
463             {
464                 return ipmi::responseInvalidFieldRequest();
465             }
466             std::string carrier;
467             netIfs >> carrier;
468             resetMtmTimer(ctx);
469             return ipmi::responseSuccess(
470                 static_cast<uint8_t>(std::stoi(carrier)), std::nullopt);
471         }
472         break;
473         default:
474             return ipmi::responseInvalidFieldRequest();
475             break;
476     }
477 }
478 
479 ipmi::RspType<> appMTMSetSignal(ipmi::Context::ptr ctx, uint8_t signalTypeByte,
480                                 uint8_t instance, uint8_t actionByte,
481                                 std::optional<uint8_t> pwmSpeed)
482 {
483     // mfg filter logic is used to allow MTM set signal command only in
484     // manfacturing mode.
485 
486     SmSignalSet signalType = static_cast<SmSignalSet>(signalTypeByte);
487     SmActionSet action = static_cast<SmActionSet>(actionByte);
488     Cc retCode = ccSuccess;
489     int8_t ret = 0;
490 
491     switch (signalType)
492     {
493         case SmSignalSet::smPowerFaultLed:
494         case SmSignalSet::smSystemReadyLed:
495         case SmSignalSet::smIdentifyLed:
496             switch (action)
497             {
498                 case SmActionSet::forceDeasserted:
499                 {
500                     phosphor::logging::log<phosphor::logging::level::INFO>(
501                         "case SmActionSet::forceDeasserted");
502 
503                     retCode = ledStoreAndSet(signalType, std::string("Off"));
504                     if (retCode != ccSuccess)
505                     {
506                         return ipmi::response(retCode);
507                     }
508                     mtm.revertTimer.start(revertTimeOut);
509                 }
510                 break;
511                 case SmActionSet::forceAsserted:
512                 {
513                     phosphor::logging::log<phosphor::logging::level::INFO>(
514                         "case SmActionSet::forceAsserted");
515 
516                     retCode = ledStoreAndSet(signalType, std::string("On"));
517                     if (retCode != ccSuccess)
518                     {
519                         return ipmi::response(retCode);
520                     }
521                     mtm.revertTimer.start(revertTimeOut);
522                     if (SmSignalSet::smPowerFaultLed == signalType)
523                     {
524                         // Deassert "system ready"
525                         retCode = ledStoreAndSet(SmSignalSet::smSystemReadyLed,
526                                                  std::string("Off"));
527                     }
528                     else if (SmSignalSet::smSystemReadyLed == signalType)
529                     {
530                         // Deassert "fault led"
531                         retCode = ledStoreAndSet(SmSignalSet::smPowerFaultLed,
532                                                  std::string("Off"));
533                     }
534                 }
535                 break;
536                 case SmActionSet::revert:
537                 {
538                     phosphor::logging::log<phosphor::logging::level::INFO>(
539                         "case SmActionSet::revert");
540                     retCode = ledRevert(signalType);
541                 }
542                 break;
543                 default:
544                 {
545                     return ipmi::responseInvalidFieldRequest();
546                 }
547             }
548             break;
549         case SmSignalSet::smFanPowerSpeed:
550         {
551             if ((action == SmActionSet::forceAsserted) && (!pwmSpeed))
552             {
553                 return ipmi::responseReqDataLenInvalid();
554             }
555 
556             if ((action == SmActionSet::forceAsserted) && (*pwmSpeed > 100))
557             {
558                 return ipmi::responseInvalidFieldRequest();
559             }
560 
561             uint8_t pwmValue = 0;
562             switch (action)
563             {
564                 case SmActionSet::revert:
565                 {
566                     if (mtm.revertFanPWM)
567                     {
568                         ret = mtm.disablePidControlService(false);
569                         if (ret < 0)
570                         {
571                             return ipmi::responseUnspecifiedError();
572                         }
573                         mtm.revertFanPWM = false;
574                     }
575                 }
576                 break;
577                 case SmActionSet::forceAsserted:
578                 {
579                     pwmValue = *pwmSpeed;
580                 } // fall-through
581                 case SmActionSet::forceDeasserted:
582                 {
583                     if (!mtm.revertFanPWM)
584                     {
585                         ret = mtm.disablePidControlService(true);
586                         if (ret < 0)
587                         {
588                             return ipmi::responseUnspecifiedError();
589                         }
590                         mtm.revertFanPWM = true;
591                     }
592                     mtm.revertTimer.start(revertTimeOut);
593                     std::string fanPwmInstancePath =
594                         fanPwmPath + std::to_string(instance + 1);
595 
596                     ret =
597                         mtm.setProperty(fanService, fanPwmInstancePath, fanIntf,
598                                         "Value", static_cast<double>(pwmValue));
599                     if (ret < 0)
600                     {
601                         return ipmi::responseUnspecifiedError();
602                     }
603                 }
604                 break;
605                 default:
606                 {
607                     return ipmi::responseInvalidFieldRequest();
608                 }
609             }
610         }
611         break;
612         case SmSignalSet::smSpeaker:
613         {
614             phosphor::logging::log<phosphor::logging::level::INFO>(
615                 "Performing Speaker SmActionSet",
616                 phosphor::logging::entry("ACTION=%d",
617                                          static_cast<uint8_t>(action)));
618             switch (action)
619             {
620                 case SmActionSet::forceAsserted:
621                 {
622                     char beepDevName[] = "/dev/input/event0";
623                     if (mtm.mtmTestBeepFd != -1)
624                     {
625                         phosphor::logging::log<phosphor::logging::level::INFO>(
626                             "mtm beep device is opened already!");
627                         // returning success as already beep is in progress
628                         return ipmi::response(retCode);
629                     }
630 
631                     if ((mtm.mtmTestBeepFd =
632                              ::open(beepDevName, O_RDWR | O_CLOEXEC)) < 0)
633                     {
634                         phosphor::logging::log<phosphor::logging::level::ERR>(
635                             "Failed to open input device");
636                         return ipmi::responseUnspecifiedError();
637                     }
638 
639                     struct input_event event;
640                     event.type = EV_SND;
641                     event.code = SND_TONE;
642                     event.value = 2000;
643 
644                     if (::write(mtm.mtmTestBeepFd, &event,
645                                 sizeof(struct input_event)) !=
646                         sizeof(struct input_event))
647                     {
648                         phosphor::logging::log<phosphor::logging::level::ERR>(
649                             "Failed to write a tone sound event");
650                         ::close(mtm.mtmTestBeepFd);
651                         mtm.mtmTestBeepFd = -1;
652                         return ipmi::responseUnspecifiedError();
653                     }
654                     mtm.revertTimer.start(revertTimeOut);
655                 }
656                 break;
657                 case SmActionSet::revert:
658                 case SmActionSet::forceDeasserted:
659                 {
660                     if (mtm.mtmTestBeepFd != -1)
661                     {
662                         ::close(mtm.mtmTestBeepFd);
663                         mtm.mtmTestBeepFd = -1;
664                     }
665                 }
666                 break;
667                 default:
668                 {
669                     return ipmi::responseInvalidFieldRequest();
670                 }
671             }
672         }
673         break;
674         case SmSignalSet::smDiskFaultLed:
675         {
676             boost::system::error_code ec;
677             using objPaths = std::vector<std::string>;
678             std::string driveBasePath =
679                 "/xyz/openbmc_project/inventory/item/drive/";
680             static constexpr const char* driveLedIntf =
681                 "xyz.openbmc_project.Led.Group";
682             static constexpr const char* hsbpService =
683                 "xyz.openbmc_project.HsbpManager";
684 
685             auto driveList = ctx->bus->yield_method_call<objPaths>(
686                 ctx->yield, ec, "xyz.openbmc_project.ObjectMapper",
687                 "/xyz/openbmc_project/object_mapper",
688                 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths",
689                 driveBasePath, 0, std::array<const char*, 1>{driveLedIntf});
690             if (ec)
691             {
692                 phosphor::logging::log<phosphor::logging::level::ERR>(
693                     "Failed to query HSBP drive sub tree objects");
694                 return ipmi::responseUnspecifiedError();
695             }
696             std::string driveObjPath =
697                 driveBasePath + "Drive_" + std::to_string(instance + 1);
698             if (std::find(driveList.begin(), driveList.end(), driveObjPath) ==
699                 driveList.end())
700             {
701                 return ipmi::responseInvalidFieldRequest();
702             }
703             bool driveLedState = false;
704             switch (action)
705             {
706                 case SmActionSet::forceAsserted:
707                 {
708                     driveLedState = true;
709                 }
710                 break;
711                 case SmActionSet::revert:
712                 {
713                     driveLedState = false;
714                 }
715                 break;
716                 case SmActionSet::forceDeasserted:
717                 {
718                     driveLedState = false;
719                 }
720                 break;
721                 default:
722                 {
723                     return ipmi::responseInvalidFieldRequest();
724                 }
725             }
726             ret = mtm.setProperty(hsbpService, driveObjPath, driveLedIntf,
727                                   "Asserted", driveLedState);
728             if (ret < 0)
729             {
730                 return ipmi::responseUnspecifiedError();
731             }
732         }
733         break;
734         default:
735         {
736             return ipmi::responseInvalidFieldRequest();
737         }
738     }
739     if (retCode == ccSuccess)
740     {
741         resetMtmTimer(ctx);
742     }
743     return ipmi::response(retCode);
744 }
745 
746 ipmi::RspType<> mtmKeepAlive(ipmi::Context::ptr ctx, uint8_t reserved,
747                              const std::array<char, 5>& intentionalSignature)
748 {
749     // mfg filter logic is used to allow MTM keep alive command only in
750     // manfacturing mode
751 
752     constexpr std::array<char, 5> signatureOk = {'I', 'N', 'T', 'E', 'L'};
753     if (intentionalSignature != signatureOk || reserved != 0)
754     {
755         return ipmi::responseInvalidFieldRequest();
756     }
757     return ipmi::response(resetMtmTimer(ctx));
758 }
759 
760 static constexpr unsigned int makeCmdKey(unsigned int netFn, unsigned int cmd)
761 {
762     return (netFn << 8) | cmd;
763 }
764 
765 ipmi::Cc mfgFilterMessage(ipmi::message::Request::ptr request)
766 {
767     // Restricted commands, must be executed only in Manufacturing mode
768     switch (makeCmdKey(request->ctx->netFn, request->ctx->cmd))
769     {
770         // i2c master write read command needs additional checking
771         case makeCmdKey(ipmi::netFnApp, ipmi::app::cmdMasterWriteRead):
772             if (request->payload.size() > 4)
773             {
774                 // Allow write data count > 1 only in Special mode
775                 if (mtm.getMfgMode() == SpecialMode::none)
776                 {
777                     return ipmi::ccInsufficientPrivilege;
778                 }
779             }
780             return ipmi::ccSuccess;
781         case makeCmdKey(ipmi::netFnOemOne,
782                         ipmi::intel::general::cmdGetSmSignal):
783         case makeCmdKey(ipmi::netFnOemOne,
784                         ipmi::intel::general::cmdSetSmSignal):
785         case makeCmdKey(ipmi::netFnOemOne,
786                         ipmi::intel::general::cmdMtmKeepAlive):
787         case makeCmdKey(ipmi::netFnOemOne,
788                         ipmi::intel::general::cmdSetManufacturingData):
789         case makeCmdKey(ipmi::netFnOemOne,
790                         ipmi::intel::general::cmdGetManufacturingData):
791         case makeCmdKey(ipmi::netFnStorage, ipmi::storage::cmdWriteFruData):
792 
793             // Check for Special mode
794             if (mtm.getMfgMode() == SpecialMode::none)
795             {
796                 return ipmi::ccInvalidCommand;
797             }
798             return ipmi::ccSuccess;
799         case makeCmdKey(ipmi::netFnStorage, ipmi::storage::cmdDeleteSelEntry):
800         {
801             return ipmi::ccInvalidCommand;
802         }
803     }
804     return ipmi::ccSuccess;
805 }
806 
807 static constexpr uint8_t maxEthSize = 6;
808 static constexpr uint8_t maxSupportedEth = 3;
809 static constexpr const char* factoryEthAddrBaseFileName =
810     "/var/sofs/factory-settings/network/mac/eth";
811 
812 ipmi::RspType<> setManufacturingData(ipmi::Context::ptr ctx, uint8_t dataType,
813                                      std::array<uint8_t, maxEthSize> ethData)
814 {
815     // mfg filter logic will restrict this command executing only in mfg mode.
816     if (dataType >= maxSupportedEth)
817     {
818         return ipmi::responseParmOutOfRange();
819     }
820 
821     constexpr uint8_t invalidData = 0;
822     constexpr uint8_t validData = 1;
823     constexpr uint8_t ethAddrStrSize =
824         19; // XX:XX:XX:XX:XX:XX + \n + null termination;
825     std::vector<uint8_t> buff(ethAddrStrSize);
826     std::snprintf(reinterpret_cast<char*>(buff.data()), ethAddrStrSize,
827                   "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx\n", ethData.at(0),
828                   ethData.at(1), ethData.at(2), ethData.at(3), ethData.at(4),
829                   ethData.at(5));
830     std::ofstream oEthFile(factoryEthAddrBaseFileName +
831                                std::to_string(dataType),
832                            std::ofstream::out);
833     if (!oEthFile.good())
834     {
835         return ipmi::responseUnspecifiedError();
836     }
837 
838     oEthFile << reinterpret_cast<char*>(buff.data());
839     oEthFile.flush();
840     oEthFile.close();
841 
842     resetMtmTimer(ctx);
843     return ipmi::responseSuccess();
844 }
845 
846 ipmi::RspType<uint8_t, std::array<uint8_t, maxEthSize>>
847     getManufacturingData(ipmi::Context::ptr ctx, uint8_t dataType)
848 {
849     // mfg filter logic will restrict this command executing only in mfg mode.
850     if (dataType >= maxSupportedEth)
851     {
852         return ipmi::responseParmOutOfRange();
853     }
854     std::array<uint8_t, maxEthSize> ethData{0};
855     constexpr uint8_t invalidData = 0;
856     constexpr uint8_t validData = 1;
857 
858     std::ifstream iEthFile(factoryEthAddrBaseFileName +
859                                std::to_string(dataType),
860                            std::ifstream::in);
861     if (!iEthFile.good())
862     {
863         return ipmi::responseSuccess(invalidData, ethData);
864     }
865     std::string ethStr;
866     iEthFile >> ethStr;
867     uint8_t* data = ethData.data();
868     std::sscanf(ethStr.c_str(), "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx",
869                 data, (data + 1), (data + 2), (data + 3), (data + 4),
870                 (data + 5));
871 
872     resetMtmTimer(ctx);
873     return ipmi::responseSuccess(validData, ethData);
874 }
875 
876 /** @brief implements slot master write read IPMI command which can be used for
877  * low-level I2C/SMBus write, read or write-read access for PCIE slots
878  * @param reserved - skip 6 bit
879  * @param addressType - address type
880  * @param bbSlotNum - baseboard slot number
881  * @param riserSlotNum - riser slot number
882  * @param reserved2 - skip 2 bit
883  * @param slaveAddr - slave address
884  * @param readCount - number of bytes to be read
885  * @param writeData - data to be written
886  *
887  * @returns IPMI completion code plus response data
888  */
889 ipmi::RspType<std::vector<uint8_t>>
890     appSlotI2CMasterWriteRead(uint6_t reserved, uint2_t addressType,
891                               uint3_t bbSlotNum, uint3_t riserSlotNum,
892                               uint2_t resvered2, uint8_t slaveAddr,
893                               uint8_t readCount, std::vector<uint8_t> writeData)
894 {
895     const size_t writeCount = writeData.size();
896     std::string i2cBus;
897     if (addressType == slotAddressTypeBus)
898     {
899         std::string path = "/dev/i2c-mux/Riser_" +
900                            std::to_string(static_cast<uint8_t>(bbSlotNum)) +
901                            "_Mux/Pcie_Slot_" +
902                            std::to_string(static_cast<uint8_t>(riserSlotNum));
903 
904         if (std::filesystem::exists(path) && std::filesystem::is_symlink(path))
905         {
906             i2cBus = std::filesystem::read_symlink(path);
907         }
908         else
909         {
910             phosphor::logging::log<phosphor::logging::level::ERR>(
911                 "Master write read command: Cannot get BusID");
912             return ipmi::responseInvalidFieldRequest();
913         }
914     }
915     else if (addressType == slotAddressTypeUniqueid)
916     {
917         i2cBus = "/dev/i2c-" +
918                  std::to_string(static_cast<uint8_t>(bbSlotNum) |
919                                 (static_cast<uint8_t>(riserSlotNum) << 3));
920     }
921     else
922     {
923         phosphor::logging::log<phosphor::logging::level::ERR>(
924             "Master write read command: invalid request");
925         return ipmi::responseInvalidFieldRequest();
926     }
927 
928     // Allow single byte write as it is offset byte to read the data, rest allow
929     // only in Special mode.
930     if (writeCount > 1)
931     {
932         if (mtm.getMfgMode() == SpecialMode::none)
933         {
934             return ipmi::responseInsufficientPrivilege();
935         }
936     }
937 
938     if (readCount > slotI2CMaxReadSize)
939     {
940         phosphor::logging::log<phosphor::logging::level::ERR>(
941             "Master write read command: Read count exceeds limit");
942         return ipmi::responseParmOutOfRange();
943     }
944 
945     if (!readCount && !writeCount)
946     {
947         phosphor::logging::log<phosphor::logging::level::ERR>(
948             "Master write read command: Read & write count are 0");
949         return ipmi::responseInvalidFieldRequest();
950     }
951 
952     std::vector<uint8_t> readBuf(readCount);
953 
954     ipmi::Cc retI2C = ipmi::i2cWriteRead(i2cBus, slaveAddr, writeData, readBuf);
955     if (retI2C != ipmi::ccSuccess)
956     {
957         return ipmi::response(retI2C);
958     }
959 
960     return ipmi::responseSuccess(readBuf);
961 }
962 
963 ipmi::RspType<> clearCMOS()
964 {
965     // There is an i2c device on bus 4, the slave address is 0x38. Based on the
966     // spec, writing 0x1 to address 0x61 on this device, will trigger the clear
967     // CMOS action.
968     constexpr uint8_t slaveAddr = 0x38;
969     std::string i2cBus = "/dev/i2c-4";
970     std::vector<uint8_t> writeData = {0x61, 0x1};
971     std::vector<uint8_t> readBuf(0);
972 
973     if (mtm.getMfgMode() == SpecialMode::none)
974     {
975         return ipmi::responseInsufficientPrivilege();
976     }
977 
978     ipmi::Cc retI2C = ipmi::i2cWriteRead(i2cBus, slaveAddr, writeData, readBuf);
979     return ipmi::response(retI2C);
980 }
981 } // namespace ipmi
982 
983 void register_mtm_commands() __attribute__((constructor));
984 void register_mtm_commands()
985 {
986     // <Get SM Signal>
987     ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnGeneral,
988                           ipmi::intel::general::cmdGetSmSignal,
989                           ipmi::Privilege::Admin, ipmi::appMTMGetSignal);
990 
991     ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnGeneral,
992                           ipmi::intel::general::cmdSetSmSignal,
993                           ipmi::Privilege::Admin, ipmi::appMTMSetSignal);
994 
995     ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnGeneral,
996                           ipmi::intel::general::cmdMtmKeepAlive,
997                           ipmi::Privilege::Admin, ipmi::mtmKeepAlive);
998 
999     ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnGeneral,
1000                           ipmi::intel::general::cmdSetManufacturingData,
1001                           ipmi::Privilege::Admin, ipmi::setManufacturingData);
1002 
1003     ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnGeneral,
1004                           ipmi::intel::general::cmdGetManufacturingData,
1005                           ipmi::Privilege::Admin, ipmi::getManufacturingData);
1006 
1007     ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnApp,
1008                           ipmi::intel::general::cmdSlotI2CMasterWriteRead,
1009                           ipmi::Privilege::Admin,
1010                           ipmi::appSlotI2CMasterWriteRead);
1011 
1012     ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnPlatform,
1013                           ipmi::intel::platform::cmdClearCMOS,
1014                           ipmi::Privilege::Admin, ipmi::clearCMOS);
1015 
1016     ipmi::registerFilter(ipmi::prioOemBase,
1017                          [](ipmi::message::Request::ptr request) {
1018                              return ipmi::mfgFilterMessage(request);
1019                          });
1020 }
1021