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 <boost/container/flat_map.hpp>
18 #include <filesystem>
19 #include <fstream>
20 #include <ipmid/api.hpp>
21 #include <manufacturingcommands.hpp>
22 #include <oemcommands.hpp>
23 
24 namespace ipmi
25 {
26 
27 Manufacturing mtm;
28 
29 static auto revertTimeOut =
30     std::chrono::duration_cast<std::chrono::microseconds>(
31         std::chrono::seconds(60)); // 1 minute timeout
32 
33 static constexpr uint8_t slotAddressTypeBus = 0;
34 static constexpr uint8_t slotAddressTypeUniqueid = 1;
35 static constexpr uint8_t slotI2CMaxReadSize = 35;
36 
37 static constexpr const char* callbackMgrService =
38     "xyz.openbmc_project.CallbackManager";
39 static constexpr const char* callbackMgrIntf =
40     "xyz.openbmc_project.CallbackManager";
41 static constexpr const char* callbackMgrObjPath =
42     "/xyz/openbmc_project/CallbackManager";
43 static constexpr const char* retriggerLedUpdate = "RetriggerLEDUpdate";
44 
45 const static constexpr char* systemDService = "org.freedesktop.systemd1";
46 const static constexpr char* systemDObjPath = "/org/freedesktop/systemd1";
47 const static constexpr char* systemDMgrIntf =
48     "org.freedesktop.systemd1.Manager";
49 const static constexpr char* pidControlService = "phosphor-pid-control.service";
50 
51 static inline Cc resetMtmTimer(boost::asio::yield_context yield)
52 {
53     auto sdbusp = getSdBus();
54     boost::system::error_code ec;
55     sdbusp->yield_method_call<>(yield, ec, specialModeService,
56                                 specialModeObjPath, specialModeIntf,
57                                 "ResetTimer");
58     if (ec)
59     {
60         phosphor::logging::log<phosphor::logging::level::ERR>(
61             "Failed to reset the manufacturing mode timer");
62         return ccUnspecifiedError;
63     }
64     return ccSuccess;
65 }
66 
67 int getGpioPathForSmSignal(const SmSignalGet signal, std::string& path)
68 {
69     switch (signal)
70     {
71         case SmSignalGet::smPowerButton:
72             path = "/xyz/openbmc_project/chassis/buttons/power";
73             break;
74         case SmSignalGet::smResetButton:
75             path = "/xyz/openbmc_project/chassis/buttons/reset";
76             break;
77         case SmSignalGet::smNMIButton:
78             path = "/xyz/openbmc_project/chassis/buttons/nmi";
79             break;
80         case SmSignalGet::smIdentifyButton:
81             path = "/xyz/openbmc_project/chassis/buttons/id";
82             break;
83         default:
84             return -1;
85             break;
86     }
87     return 0;
88 }
89 
90 ipmi_ret_t ledStoreAndSet(SmSignalSet signal, std::string setState)
91 {
92     LedProperty* ledProp = mtm.findLedProperty(signal);
93     if (ledProp == nullptr)
94     {
95         return IPMI_CC_INVALID_FIELD_REQUEST;
96     }
97 
98     std::string ledName = ledProp->getName();
99     std::string ledService = ledServicePrefix + ledName;
100     std::string ledPath = ledPathPrefix + ledName;
101     ipmi::Value presentState;
102 
103     if (false == ledProp->getLock())
104     {
105         if (mtm.getProperty(ledService.c_str(), ledPath.c_str(), ledIntf,
106                             "State", &presentState) != 0)
107         {
108             return IPMI_CC_UNSPECIFIED_ERROR;
109         }
110         ledProp->setPrevState(std::get<std::string>(presentState));
111         ledProp->setLock(true);
112         if (signal == SmSignalSet::smPowerFaultLed ||
113             signal == SmSignalSet::smSystemReadyLed)
114         {
115             mtm.revertLedCallback = true;
116         }
117     }
118     if (mtm.setProperty(ledService, ledPath, ledIntf, "State",
119                         ledStateStr + setState) != 0)
120     {
121         return IPMI_CC_UNSPECIFIED_ERROR;
122     }
123     return IPMI_CC_OK;
124 }
125 
126 ipmi_ret_t ledRevert(SmSignalSet signal)
127 {
128     LedProperty* ledProp = mtm.findLedProperty(signal);
129     if (ledProp == nullptr)
130     {
131         return IPMI_CC_INVALID_FIELD_REQUEST;
132     }
133     if (true == ledProp->getLock())
134     {
135         ledProp->setLock(false);
136         if (signal == SmSignalSet::smPowerFaultLed ||
137             signal == SmSignalSet::smSystemReadyLed)
138         {
139             try
140             {
141                 ipmi::method_no_args::callDbusMethod(
142                     *getSdBus(), callbackMgrService, callbackMgrObjPath,
143                     callbackMgrIntf, retriggerLedUpdate);
144             }
145             catch (sdbusplus::exception_t& e)
146             {
147                 return IPMI_CC_UNSPECIFIED_ERROR;
148             }
149             mtm.revertLedCallback = false;
150         }
151         else
152         {
153             std::string ledName = ledProp->getName();
154             std::string ledService = ledServicePrefix + ledName;
155             std::string ledPath = ledPathPrefix + ledName;
156             if (mtm.setProperty(ledService, ledPath, ledIntf, "State",
157                                 ledProp->getPrevState()) != 0)
158             {
159                 return IPMI_CC_UNSPECIFIED_ERROR;
160             }
161         }
162     }
163     return IPMI_CC_OK;
164 }
165 
166 void Manufacturing::initData()
167 {
168     ledPropertyList.push_back(
169         LedProperty(SmSignalSet::smPowerFaultLed, "status_amber"));
170     ledPropertyList.push_back(
171         LedProperty(SmSignalSet::smSystemReadyLed, "status_green"));
172     ledPropertyList.push_back(
173         LedProperty(SmSignalSet::smIdentifyLed, "identify"));
174 }
175 
176 void Manufacturing::revertTimerHandler()
177 {
178     if (revertFanPWM)
179     {
180         revertFanPWM = false;
181         disablePidControlService(false);
182     }
183 
184     for (const auto& ledProperty : ledPropertyList)
185     {
186         const std::string& ledName = ledProperty.getName();
187         ledRevert(ledProperty.getSignal());
188     }
189 }
190 
191 Manufacturing::Manufacturing() :
192     revertTimer([&](void) { revertTimerHandler(); })
193 {
194     initData();
195 }
196 
197 int8_t Manufacturing::getProperty(const std::string& service,
198                                   const std::string& path,
199                                   const std::string& interface,
200                                   const std::string& propertyName,
201                                   ipmi::Value* reply)
202 {
203     try
204     {
205         *reply = ipmi::getDbusProperty(*getSdBus(), service, path, interface,
206                                        propertyName);
207     }
208     catch (const sdbusplus::exception::SdBusError& e)
209     {
210         phosphor::logging::log<phosphor::logging::level::INFO>(
211             "ERROR: getProperty");
212         return -1;
213     }
214 
215     return 0;
216 }
217 
218 int8_t Manufacturing::setProperty(const std::string& service,
219                                   const std::string& path,
220                                   const std::string& interface,
221                                   const std::string& propertyName,
222                                   ipmi::Value value)
223 {
224     try
225     {
226         ipmi::setDbusProperty(*getSdBus(), service, path, interface,
227                               propertyName, value);
228     }
229     catch (const sdbusplus::exception::SdBusError& e)
230     {
231         phosphor::logging::log<phosphor::logging::level::INFO>(
232             "ERROR: setProperty");
233         return -1;
234     }
235 
236     return 0;
237 }
238 
239 int8_t Manufacturing::disablePidControlService(const bool disable)
240 {
241     try
242     {
243         auto dbus = getSdBus();
244         auto method = dbus->new_method_call(systemDService, systemDObjPath,
245                                             systemDMgrIntf,
246                                             disable ? "StopUnit" : "StartUnit");
247         method.append(pidControlService, "replace");
248         auto reply = dbus->call(method);
249     }
250     catch (const sdbusplus::exception::SdBusError& e)
251     {
252         phosphor::logging::log<phosphor::logging::level::INFO>(
253             "ERROR: phosphor-pid-control service start or stop failed");
254         return -1;
255     }
256     return 0;
257 }
258 
259 ipmi::RspType<uint8_t,                // Signal value
260               std::optional<uint16_t> // Fan tach value
261               >
262     appMTMGetSignal(boost::asio::yield_context yield, uint8_t signalTypeByte,
263                     uint8_t instance, uint8_t actionByte)
264 {
265     if (mtm.getAccessLvl() < MtmLvl::mtmAvailable)
266     {
267         return ipmi::responseInvalidCommand();
268     }
269 
270     SmSignalGet signalType = static_cast<SmSignalGet>(signalTypeByte);
271     SmActionGet action = static_cast<SmActionGet>(actionByte);
272 
273     switch (signalType)
274     {
275         case SmSignalGet::smFanPwmGet:
276         {
277             ipmi::Value reply;
278             std::string fullPath = fanPwmPath + std::to_string(instance + 1);
279             if (mtm.getProperty(fanService, fullPath, fanIntf, "Value",
280                                 &reply) < 0)
281             {
282                 return ipmi::responseInvalidFieldRequest();
283             }
284             double* doubleVal = std::get_if<double>(&reply);
285             if (doubleVal == nullptr)
286             {
287                 return ipmi::responseUnspecifiedError();
288             }
289             uint8_t sensorVal = std::round(*doubleVal);
290             resetMtmTimer(yield);
291             return ipmi::responseSuccess(sensorVal, std::nullopt);
292         }
293         break;
294         case SmSignalGet::smFanTachometerGet:
295         {
296             auto sdbusp = getSdBus();
297             boost::system::error_code ec;
298             using objFlatMap = boost::container::flat_map<
299                 std::string, boost::container::flat_map<
300                                  std::string, std::vector<std::string>>>;
301 
302             auto flatMap = sdbusp->yield_method_call<objFlatMap>(
303                 yield, ec, "xyz.openbmc_project.ObjectMapper",
304                 "/xyz/openbmc_project/object_mapper",
305                 "xyz.openbmc_project.ObjectMapper", "GetSubTree",
306                 fanTachBasePath, 0, std::array<const char*, 1>{fanIntf});
307             if (ec)
308             {
309                 phosphor::logging::log<phosphor::logging::level::ERR>(
310                     "Failed to query fan tach sub tree objects");
311                 return ipmi::responseUnspecifiedError();
312             }
313             if (instance >= flatMap.size())
314             {
315                 return ipmi::responseInvalidFieldRequest();
316             }
317             auto itr = flatMap.nth(instance);
318             ipmi::Value reply;
319             if (mtm.getProperty(fanService, itr->first, fanIntf, "Value",
320                                 &reply) < 0)
321             {
322                 return ipmi::responseInvalidFieldRequest();
323             }
324 
325             double* doubleVal = std::get_if<double>(&reply);
326             if (doubleVal == nullptr)
327             {
328                 return ipmi::responseUnspecifiedError();
329             }
330             uint8_t sensorVal = FAN_PRESENT | FAN_SENSOR_PRESENT;
331             std::optional<uint16_t> fanTach = std::round(*doubleVal);
332 
333             resetMtmTimer(yield);
334             return ipmi::responseSuccess(sensorVal, fanTach);
335         }
336         break;
337         case SmSignalGet::smIdentifyButton:
338         {
339             if (action == SmActionGet::revert || action == SmActionGet::ignore)
340             {
341                 // ButtonMasked property is not supported for ID button as it is
342                 // unnecessary. Hence if requested for revert / ignore, override
343                 // it to sample action to make tools happy.
344                 action = SmActionGet::sample;
345             }
346             // fall-through
347         }
348         case SmSignalGet::smResetButton:
349         case SmSignalGet::smPowerButton:
350         case SmSignalGet::smNMIButton:
351         {
352             std::string path;
353             if (getGpioPathForSmSignal(signalType, path) < 0)
354             {
355                 return ipmi::responseInvalidFieldRequest();
356             }
357 
358             switch (action)
359             {
360                 case SmActionGet::sample:
361                     phosphor::logging::log<phosphor::logging::level::INFO>(
362                         "case SmActionGet::sample");
363                     break;
364                 case SmActionGet::ignore:
365                 {
366                     phosphor::logging::log<phosphor::logging::level::INFO>(
367                         "case SmActionGet::ignore");
368                     if (mtm.setProperty(buttonService, path, buttonIntf,
369                                         "ButtonMasked", true) < 0)
370                     {
371                         return ipmi::responseUnspecifiedError();
372                     }
373                 }
374                 break;
375                 case SmActionGet::revert:
376                 {
377                     phosphor::logging::log<phosphor::logging::level::INFO>(
378                         "case SmActionGet::revert");
379                     if (mtm.setProperty(buttonService, path, buttonIntf,
380                                         "ButtonMasked", false) < 0)
381                     {
382                         return ipmi::responseUnspecifiedError();
383                     }
384                 }
385                 break;
386 
387                 default:
388                     return ipmi::responseInvalidFieldRequest();
389                     break;
390             }
391 
392             ipmi::Value reply;
393             if (mtm.getProperty(buttonService, path, buttonIntf,
394                                 "ButtonPressed", &reply) < 0)
395             {
396                 return ipmi::responseUnspecifiedError();
397             }
398             bool* valPtr = std::get_if<bool>(&reply);
399             if (valPtr == nullptr)
400             {
401                 return ipmi::responseUnspecifiedError();
402             }
403             resetMtmTimer(yield);
404             uint8_t sensorVal = *valPtr;
405             return ipmi::responseSuccess(sensorVal, std::nullopt);
406         }
407         break;
408         case SmSignalGet::smNcsiDiag:
409         {
410             constexpr const char* netBasePath = "/sys/class/net/eth";
411             constexpr const char* carrierSuffix = "/carrier";
412             std::ifstream netIfs(netBasePath + std::to_string(instance) +
413                                  carrierSuffix);
414             if (!netIfs.good())
415             {
416                 return ipmi::responseInvalidFieldRequest();
417             }
418             std::string carrier;
419             netIfs >> carrier;
420             resetMtmTimer(yield);
421             return ipmi::responseSuccess(
422                 static_cast<uint8_t>(std::stoi(carrier)), std::nullopt);
423         }
424         break;
425         default:
426             return ipmi::responseInvalidFieldRequest();
427             break;
428     }
429 }
430 
431 ipmi::RspType<> appMTMSetSignal(boost::asio::yield_context yield,
432                                 uint8_t signalTypeByte, uint8_t instance,
433                                 uint8_t actionByte,
434                                 std::optional<uint8_t> pwmSpeed)
435 {
436     if (mtm.getAccessLvl() < MtmLvl::mtmAvailable)
437     {
438         return ipmi::responseInvalidCommand();
439     }
440 
441     SmSignalSet signalType = static_cast<SmSignalSet>(signalTypeByte);
442     SmActionSet action = static_cast<SmActionSet>(actionByte);
443     Cc retCode = ccSuccess;
444     int8_t ret = 0;
445 
446     switch (signalType)
447     {
448         case SmSignalSet::smPowerFaultLed:
449         case SmSignalSet::smSystemReadyLed:
450         case SmSignalSet::smIdentifyLed:
451             switch (action)
452             {
453                 case SmActionSet::forceDeasserted:
454                 {
455                     phosphor::logging::log<phosphor::logging::level::INFO>(
456                         "case SmActionSet::forceDeasserted");
457 
458                     retCode = ledStoreAndSet(signalType, std::string("Off"));
459                     if (retCode != ccSuccess)
460                     {
461                         return ipmi::response(retCode);
462                     }
463                     mtm.revertTimer.start(revertTimeOut);
464                 }
465                 break;
466                 case SmActionSet::forceAsserted:
467                 {
468                     phosphor::logging::log<phosphor::logging::level::INFO>(
469                         "case SmActionSet::forceAsserted");
470 
471                     retCode = ledStoreAndSet(signalType, std::string("On"));
472                     if (retCode != ccSuccess)
473                     {
474                         return ipmi::response(retCode);
475                     }
476                     mtm.revertTimer.start(revertTimeOut);
477                     if (SmSignalSet::smPowerFaultLed == signalType)
478                     {
479                         // Deassert "system ready"
480                         retCode = ledStoreAndSet(SmSignalSet::smSystemReadyLed,
481                                                  std::string("Off"));
482                     }
483                     else if (SmSignalSet::smSystemReadyLed == signalType)
484                     {
485                         // Deassert "fault led"
486                         retCode = ledStoreAndSet(SmSignalSet::smPowerFaultLed,
487                                                  std::string("Off"));
488                     }
489                 }
490                 break;
491                 case SmActionSet::revert:
492                 {
493                     phosphor::logging::log<phosphor::logging::level::INFO>(
494                         "case SmActionSet::revert");
495                     retCode = ledRevert(signalType);
496                 }
497                 break;
498                 default:
499                 {
500                     return ipmi::responseInvalidFieldRequest();
501                 }
502             }
503             break;
504         case SmSignalSet::smFanPowerSpeed:
505         {
506             if ((action == SmActionSet::forceAsserted) && (!pwmSpeed))
507             {
508                 return ipmi::responseReqDataLenInvalid();
509             }
510 
511             if ((action == SmActionSet::forceAsserted) && (*pwmSpeed > 100))
512             {
513                 return ipmi::responseInvalidFieldRequest();
514             }
515 
516             uint8_t pwmValue = 0;
517             switch (action)
518             {
519                 case SmActionSet::revert:
520                 {
521                     if (mtm.revertFanPWM)
522                     {
523                         ret = mtm.disablePidControlService(false);
524                         if (ret < 0)
525                         {
526                             return ipmi::responseUnspecifiedError();
527                         }
528                         mtm.revertFanPWM = false;
529                     }
530                 }
531                 break;
532                 case SmActionSet::forceAsserted:
533                 {
534                     pwmValue = *pwmSpeed;
535                 } // fall-through
536                 case SmActionSet::forceDeasserted:
537                 {
538                     if (!mtm.revertFanPWM)
539                     {
540                         ret = mtm.disablePidControlService(true);
541                         if (ret < 0)
542                         {
543                             return ipmi::responseUnspecifiedError();
544                         }
545                         mtm.revertFanPWM = true;
546                     }
547                     mtm.revertTimer.start(revertTimeOut);
548                     std::string fanPwmInstancePath =
549                         fanPwmPath + std::to_string(instance + 1);
550 
551                     ret =
552                         mtm.setProperty(fanService, fanPwmInstancePath, fanIntf,
553                                         "Value", static_cast<double>(pwmValue));
554                     if (ret < 0)
555                     {
556                         return ipmi::responseUnspecifiedError();
557                     }
558                 }
559                 break;
560                 default:
561                 {
562                     return ipmi::responseInvalidFieldRequest();
563                 }
564             }
565         }
566         break;
567         default:
568         {
569             return ipmi::responseInvalidFieldRequest();
570         }
571     }
572     if (retCode == ccSuccess)
573     {
574         resetMtmTimer(yield);
575     }
576     return ipmi::response(retCode);
577 }
578 
579 ipmi::RspType<> mtmKeepAlive(boost::asio::yield_context yield, uint8_t reserved,
580                              const std::array<char, 5>& intentionalSignature)
581 {
582     // Allow MTM keep alive command only in manfacturing mode.
583     if (mtm.getAccessLvl() != MtmLvl::mtmAvailable)
584     {
585         return ipmi::responseInvalidCommand();
586     }
587     constexpr std::array<char, 5> signatureOk = {'I', 'N', 'T', 'E', 'L'};
588     if (intentionalSignature != signatureOk || reserved != 0)
589     {
590         return ipmi::responseInvalidFieldRequest();
591     }
592     return ipmi::response(resetMtmTimer(yield));
593 }
594 
595 ipmi::Cc mfgFilterMessage(ipmi::message::Request::ptr request)
596 {
597     // i2c master write read command needs additional checking
598     if ((request->ctx->netFn == ipmi::netFnApp) &&
599         (request->ctx->cmd == ipmi::app::cmdMasterWriteRead))
600     {
601         if (request->payload.size() > 4)
602         {
603             // Allow write data count > 1, only if it is in MFG mode
604             if (mtm.getAccessLvl() != MtmLvl::mtmAvailable)
605             {
606                 return ipmi::ccInsufficientPrivilege;
607             }
608         }
609     }
610 
611     return ipmi::ccSuccess;
612 }
613 
614 static constexpr uint8_t maxEthSize = 6;
615 static constexpr uint8_t maxSupportedEth = 3;
616 static constexpr const char* factoryEthAddrBaseFileName =
617     "/var/sofs/factory-settings/network/mac/eth";
618 
619 ipmi::RspType<> setManufacturingData(boost::asio::yield_context yield,
620                                      uint8_t dataType,
621                                      std::array<uint8_t, maxEthSize> ethData)
622 {
623     // mfg filter logic will restrict this command executing only in mfg mode.
624     if (dataType >= maxSupportedEth)
625     {
626         return ipmi::responseParmOutOfRange();
627     }
628 
629     constexpr uint8_t invalidData = 0;
630     constexpr uint8_t validData = 1;
631     constexpr uint8_t ethAddrStrSize =
632         19; // XX:XX:XX:XX:XX:XX + \n + null termination;
633     std::vector<uint8_t> buff(ethAddrStrSize);
634     std::snprintf(reinterpret_cast<char*>(buff.data()), ethAddrStrSize,
635                   "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx\n", ethData.at(0),
636                   ethData.at(1), ethData.at(2), ethData.at(3), ethData.at(4),
637                   ethData.at(5));
638     std::ofstream oEthFile(factoryEthAddrBaseFileName +
639                                std::to_string(dataType),
640                            std::ofstream::out);
641     if (!oEthFile.good())
642     {
643         return ipmi::responseUnspecifiedError();
644     }
645 
646     oEthFile << reinterpret_cast<char*>(buff.data());
647     oEthFile << fflush;
648     oEthFile.close();
649 
650     resetMtmTimer(yield);
651     return ipmi::responseSuccess();
652 }
653 
654 ipmi::RspType<uint8_t, std::array<uint8_t, maxEthSize>>
655     getManufacturingData(boost::asio::yield_context yield, uint8_t dataType)
656 {
657     // mfg filter logic will restrict this command executing only in mfg mode.
658     if (dataType >= maxSupportedEth)
659     {
660         return ipmi::responseParmOutOfRange();
661     }
662     std::array<uint8_t, maxEthSize> ethData{0};
663     constexpr uint8_t invalidData = 0;
664     constexpr uint8_t validData = 1;
665 
666     std::ifstream iEthFile(factoryEthAddrBaseFileName +
667                                std::to_string(dataType),
668                            std::ifstream::in);
669     if (!iEthFile.good())
670     {
671         return ipmi::responseSuccess(invalidData, ethData);
672     }
673     std::string ethStr;
674     iEthFile >> ethStr;
675     uint8_t* data = ethData.data();
676     std::sscanf(ethStr.c_str(), "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx",
677                 data, (data + 1), (data + 2), (data + 3), (data + 4),
678                 (data + 5));
679 
680     resetMtmTimer(yield);
681     return ipmi::responseSuccess(validData, ethData);
682 }
683 
684 /** @brief implements slot master write read IPMI command which can be used for
685  * low-level I2C/SMBus write, read or write-read access for PCIE slots
686  * @param reserved - skip 6 bit
687  * @param addressType - address type
688  * @param bbSlotNum - baseboard slot number
689  * @param riserSlotNum - riser slot number
690  * @param reserved2 - skip 2 bit
691  * @param slaveAddr - slave address
692  * @param readCount - number of bytes to be read
693  * @param writeData - data to be written
694  *
695  * @returns IPMI completion code plus response data
696  */
697 ipmi::RspType<std::vector<uint8_t>>
698     appSlotI2CMasterWriteRead(uint6_t reserved, uint2_t addressType,
699                               uint3_t bbSlotNum, uint3_t riserSlotNum,
700                               uint2_t resvered2, uint8_t slaveAddr,
701                               uint8_t readCount, std::vector<uint8_t> writeData)
702 {
703     const size_t writeCount = writeData.size();
704     std::string i2cBus;
705     if (addressType == slotAddressTypeBus)
706     {
707         std::string path = "/dev/i2c-mux/Riser_" +
708                            std::to_string(static_cast<uint8_t>(bbSlotNum)) +
709                            "_Mux/Pcie_Slot_" +
710                            std::to_string(static_cast<uint8_t>(riserSlotNum));
711 
712         if (std::filesystem::exists(path) && std::filesystem::is_symlink(path))
713         {
714             i2cBus = std::filesystem::read_symlink(path);
715         }
716         else
717         {
718             phosphor::logging::log<phosphor::logging::level::ERR>(
719                 "Master write read command: Cannot get BusID");
720             return ipmi::responseInvalidFieldRequest();
721         }
722     }
723     else if (addressType == slotAddressTypeUniqueid)
724     {
725         i2cBus = "/dev/i2c-" +
726                  std::to_string(static_cast<uint8_t>(bbSlotNum) |
727                                 (static_cast<uint8_t>(riserSlotNum) << 3));
728     }
729     else
730     {
731         phosphor::logging::log<phosphor::logging::level::ERR>(
732             "Master write read command: invalid request");
733         return ipmi::responseInvalidFieldRequest();
734     }
735 
736     // Allow single byte write as it is offset byte to read the data, rest allow
737     // only in MFG mode.
738     if (writeCount > 1)
739     {
740         if (mtm.getAccessLvl() < MtmLvl::mtmAvailable)
741         {
742             return ipmi::responseInsufficientPrivilege();
743         }
744     }
745 
746     if (readCount > slotI2CMaxReadSize)
747     {
748         phosphor::logging::log<phosphor::logging::level::ERR>(
749             "Master write read command: Read count exceeds limit");
750         return ipmi::responseParmOutOfRange();
751     }
752 
753     if (!readCount && !writeCount)
754     {
755         phosphor::logging::log<phosphor::logging::level::ERR>(
756             "Master write read command: Read & write count are 0");
757         return ipmi::responseInvalidFieldRequest();
758     }
759 
760     std::vector<uint8_t> readBuf(readCount);
761 
762     ipmi::Cc retI2C = ipmi::i2cWriteRead(i2cBus, slaveAddr, writeData, readBuf);
763     if (retI2C != ipmi::ccSuccess)
764     {
765         return ipmi::response(retI2C);
766     }
767 
768     return ipmi::responseSuccess(readBuf);
769 }
770 } // namespace ipmi
771 
772 void register_mtm_commands() __attribute__((constructor));
773 void register_mtm_commands()
774 {
775     // <Get SM Signal>
776     ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnGeneral,
777                           ipmi::intel::general::cmdGetSmSignal,
778                           ipmi::Privilege::Admin, ipmi::appMTMGetSignal);
779 
780     ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnGeneral,
781                           ipmi::intel::general::cmdSetSmSignal,
782                           ipmi::Privilege::Admin, ipmi::appMTMSetSignal);
783 
784     ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnGeneral,
785                           ipmi::intel::general::cmdMtmKeepAlive,
786                           ipmi::Privilege::Admin, ipmi::mtmKeepAlive);
787 
788     ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnGeneral,
789                           ipmi::intel::general::cmdSetManufacturingData,
790                           ipmi::Privilege::Admin, ipmi::setManufacturingData);
791 
792     ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnGeneral,
793                           ipmi::intel::general::cmdGetManufacturingData,
794                           ipmi::Privilege::Admin, ipmi::getManufacturingData);
795 
796     ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnApp,
797                           ipmi::intel::general::cmdSlotI2CMasterWriteRead,
798                           ipmi::Privilege::Admin,
799                           ipmi::appSlotI2CMasterWriteRead);
800 
801     ipmi::registerFilter(ipmi::prioOemBase,
802                          [](ipmi::message::Request::ptr request) {
803                              return ipmi::mfgFilterMessage(request);
804                          });
805 }
806