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     // mfg filter logic is used to allow MTM get signal command only in
266     // manfacturing mode.
267 
268     SmSignalGet signalType = static_cast<SmSignalGet>(signalTypeByte);
269     SmActionGet action = static_cast<SmActionGet>(actionByte);
270 
271     switch (signalType)
272     {
273         case SmSignalGet::smFanPwmGet:
274         {
275             ipmi::Value reply;
276             std::string fullPath = fanPwmPath + std::to_string(instance + 1);
277             if (mtm.getProperty(fanService, fullPath, fanIntf, "Value",
278                                 &reply) < 0)
279             {
280                 return ipmi::responseInvalidFieldRequest();
281             }
282             double* doubleVal = std::get_if<double>(&reply);
283             if (doubleVal == nullptr)
284             {
285                 return ipmi::responseUnspecifiedError();
286             }
287             uint8_t sensorVal = std::round(*doubleVal);
288             resetMtmTimer(yield);
289             return ipmi::responseSuccess(sensorVal, std::nullopt);
290         }
291         break;
292         case SmSignalGet::smFanTachometerGet:
293         {
294             auto sdbusp = getSdBus();
295             boost::system::error_code ec;
296             using objFlatMap = boost::container::flat_map<
297                 std::string, boost::container::flat_map<
298                                  std::string, std::vector<std::string>>>;
299 
300             auto flatMap = sdbusp->yield_method_call<objFlatMap>(
301                 yield, ec, "xyz.openbmc_project.ObjectMapper",
302                 "/xyz/openbmc_project/object_mapper",
303                 "xyz.openbmc_project.ObjectMapper", "GetSubTree",
304                 fanTachBasePath, 0, std::array<const char*, 1>{fanIntf});
305             if (ec)
306             {
307                 phosphor::logging::log<phosphor::logging::level::ERR>(
308                     "Failed to query fan tach sub tree objects");
309                 return ipmi::responseUnspecifiedError();
310             }
311             if (instance >= flatMap.size())
312             {
313                 return ipmi::responseInvalidFieldRequest();
314             }
315             auto itr = flatMap.nth(instance);
316             ipmi::Value reply;
317             if (mtm.getProperty(fanService, itr->first, fanIntf, "Value",
318                                 &reply) < 0)
319             {
320                 return ipmi::responseInvalidFieldRequest();
321             }
322 
323             double* doubleVal = std::get_if<double>(&reply);
324             if (doubleVal == nullptr)
325             {
326                 return ipmi::responseUnspecifiedError();
327             }
328             uint8_t sensorVal = FAN_PRESENT | FAN_SENSOR_PRESENT;
329             std::optional<uint16_t> fanTach = std::round(*doubleVal);
330 
331             resetMtmTimer(yield);
332             return ipmi::responseSuccess(sensorVal, fanTach);
333         }
334         break;
335         case SmSignalGet::smIdentifyButton:
336         {
337             if (action == SmActionGet::revert || action == SmActionGet::ignore)
338             {
339                 // ButtonMasked property is not supported for ID button as it is
340                 // unnecessary. Hence if requested for revert / ignore, override
341                 // it to sample action to make tools happy.
342                 action = SmActionGet::sample;
343             }
344             // fall-through
345         }
346         case SmSignalGet::smResetButton:
347         case SmSignalGet::smPowerButton:
348         case SmSignalGet::smNMIButton:
349         {
350             std::string path;
351             if (getGpioPathForSmSignal(signalType, path) < 0)
352             {
353                 return ipmi::responseInvalidFieldRequest();
354             }
355 
356             switch (action)
357             {
358                 case SmActionGet::sample:
359                     phosphor::logging::log<phosphor::logging::level::INFO>(
360                         "case SmActionGet::sample");
361                     break;
362                 case SmActionGet::ignore:
363                 {
364                     phosphor::logging::log<phosphor::logging::level::INFO>(
365                         "case SmActionGet::ignore");
366                     if (mtm.setProperty(buttonService, path, buttonIntf,
367                                         "ButtonMasked", true) < 0)
368                     {
369                         return ipmi::responseUnspecifiedError();
370                     }
371                 }
372                 break;
373                 case SmActionGet::revert:
374                 {
375                     phosphor::logging::log<phosphor::logging::level::INFO>(
376                         "case SmActionGet::revert");
377                     if (mtm.setProperty(buttonService, path, buttonIntf,
378                                         "ButtonMasked", false) < 0)
379                     {
380                         return ipmi::responseUnspecifiedError();
381                     }
382                 }
383                 break;
384 
385                 default:
386                     return ipmi::responseInvalidFieldRequest();
387                     break;
388             }
389 
390             ipmi::Value reply;
391             if (mtm.getProperty(buttonService, path, buttonIntf,
392                                 "ButtonPressed", &reply) < 0)
393             {
394                 return ipmi::responseUnspecifiedError();
395             }
396             bool* valPtr = std::get_if<bool>(&reply);
397             if (valPtr == nullptr)
398             {
399                 return ipmi::responseUnspecifiedError();
400             }
401             resetMtmTimer(yield);
402             uint8_t sensorVal = *valPtr;
403             return ipmi::responseSuccess(sensorVal, std::nullopt);
404         }
405         break;
406         case SmSignalGet::smNcsiDiag:
407         {
408             constexpr const char* netBasePath = "/sys/class/net/eth";
409             constexpr const char* carrierSuffix = "/carrier";
410             std::ifstream netIfs(netBasePath + std::to_string(instance) +
411                                  carrierSuffix);
412             if (!netIfs.good())
413             {
414                 return ipmi::responseInvalidFieldRequest();
415             }
416             std::string carrier;
417             netIfs >> carrier;
418             resetMtmTimer(yield);
419             return ipmi::responseSuccess(
420                 static_cast<uint8_t>(std::stoi(carrier)), std::nullopt);
421         }
422         break;
423         default:
424             return ipmi::responseInvalidFieldRequest();
425             break;
426     }
427 }
428 
429 ipmi::RspType<> appMTMSetSignal(boost::asio::yield_context yield,
430                                 uint8_t signalTypeByte, uint8_t instance,
431                                 uint8_t actionByte,
432                                 std::optional<uint8_t> pwmSpeed)
433 {
434     // mfg filter logic is used to allow MTM set signal command only in
435     // manfacturing mode.
436 
437     SmSignalSet signalType = static_cast<SmSignalSet>(signalTypeByte);
438     SmActionSet action = static_cast<SmActionSet>(actionByte);
439     Cc retCode = ccSuccess;
440     int8_t ret = 0;
441 
442     switch (signalType)
443     {
444         case SmSignalSet::smPowerFaultLed:
445         case SmSignalSet::smSystemReadyLed:
446         case SmSignalSet::smIdentifyLed:
447             switch (action)
448             {
449                 case SmActionSet::forceDeasserted:
450                 {
451                     phosphor::logging::log<phosphor::logging::level::INFO>(
452                         "case SmActionSet::forceDeasserted");
453 
454                     retCode = ledStoreAndSet(signalType, std::string("Off"));
455                     if (retCode != ccSuccess)
456                     {
457                         return ipmi::response(retCode);
458                     }
459                     mtm.revertTimer.start(revertTimeOut);
460                 }
461                 break;
462                 case SmActionSet::forceAsserted:
463                 {
464                     phosphor::logging::log<phosphor::logging::level::INFO>(
465                         "case SmActionSet::forceAsserted");
466 
467                     retCode = ledStoreAndSet(signalType, std::string("On"));
468                     if (retCode != ccSuccess)
469                     {
470                         return ipmi::response(retCode);
471                     }
472                     mtm.revertTimer.start(revertTimeOut);
473                     if (SmSignalSet::smPowerFaultLed == signalType)
474                     {
475                         // Deassert "system ready"
476                         retCode = ledStoreAndSet(SmSignalSet::smSystemReadyLed,
477                                                  std::string("Off"));
478                     }
479                     else if (SmSignalSet::smSystemReadyLed == signalType)
480                     {
481                         // Deassert "fault led"
482                         retCode = ledStoreAndSet(SmSignalSet::smPowerFaultLed,
483                                                  std::string("Off"));
484                     }
485                 }
486                 break;
487                 case SmActionSet::revert:
488                 {
489                     phosphor::logging::log<phosphor::logging::level::INFO>(
490                         "case SmActionSet::revert");
491                     retCode = ledRevert(signalType);
492                 }
493                 break;
494                 default:
495                 {
496                     return ipmi::responseInvalidFieldRequest();
497                 }
498             }
499             break;
500         case SmSignalSet::smFanPowerSpeed:
501         {
502             if ((action == SmActionSet::forceAsserted) && (!pwmSpeed))
503             {
504                 return ipmi::responseReqDataLenInvalid();
505             }
506 
507             if ((action == SmActionSet::forceAsserted) && (*pwmSpeed > 100))
508             {
509                 return ipmi::responseInvalidFieldRequest();
510             }
511 
512             uint8_t pwmValue = 0;
513             switch (action)
514             {
515                 case SmActionSet::revert:
516                 {
517                     if (mtm.revertFanPWM)
518                     {
519                         ret = mtm.disablePidControlService(false);
520                         if (ret < 0)
521                         {
522                             return ipmi::responseUnspecifiedError();
523                         }
524                         mtm.revertFanPWM = false;
525                     }
526                 }
527                 break;
528                 case SmActionSet::forceAsserted:
529                 {
530                     pwmValue = *pwmSpeed;
531                 } // fall-through
532                 case SmActionSet::forceDeasserted:
533                 {
534                     if (!mtm.revertFanPWM)
535                     {
536                         ret = mtm.disablePidControlService(true);
537                         if (ret < 0)
538                         {
539                             return ipmi::responseUnspecifiedError();
540                         }
541                         mtm.revertFanPWM = true;
542                     }
543                     mtm.revertTimer.start(revertTimeOut);
544                     std::string fanPwmInstancePath =
545                         fanPwmPath + std::to_string(instance + 1);
546 
547                     ret =
548                         mtm.setProperty(fanService, fanPwmInstancePath, fanIntf,
549                                         "Value", static_cast<double>(pwmValue));
550                     if (ret < 0)
551                     {
552                         return ipmi::responseUnspecifiedError();
553                     }
554                 }
555                 break;
556                 default:
557                 {
558                     return ipmi::responseInvalidFieldRequest();
559                 }
560             }
561         }
562         break;
563         default:
564         {
565             return ipmi::responseInvalidFieldRequest();
566         }
567     }
568     if (retCode == ccSuccess)
569     {
570         resetMtmTimer(yield);
571     }
572     return ipmi::response(retCode);
573 }
574 
575 ipmi::RspType<> mtmKeepAlive(boost::asio::yield_context yield, uint8_t reserved,
576                              const std::array<char, 5>& intentionalSignature)
577 {
578     // mfg filter logic is used to allow MTM keep alive command only in
579     // manfacturing mode
580 
581     constexpr std::array<char, 5> signatureOk = {'I', 'N', 'T', 'E', 'L'};
582     if (intentionalSignature != signatureOk || reserved != 0)
583     {
584         return ipmi::responseInvalidFieldRequest();
585     }
586     return ipmi::response(resetMtmTimer(yield));
587 }
588 
589 static constexpr unsigned int makeCmdKey(unsigned int netFn, unsigned int cmd)
590 {
591     return (netFn << 8) | cmd;
592 }
593 
594 ipmi::Cc mfgFilterMessage(ipmi::message::Request::ptr request)
595 {
596     // Restricted commands, must be executed only in Manufacturing mode
597     switch (makeCmdKey(request->ctx->netFn, request->ctx->cmd))
598     {
599         // i2c master write read command needs additional checking
600         case makeCmdKey(ipmi::netFnApp, ipmi::app::cmdMasterWriteRead):
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             break;
610         case makeCmdKey(ipmi::netFnOemOne,
611                         ipmi::intel::general::cmdGetSmSignal):
612         case makeCmdKey(ipmi::netFnOemOne,
613                         ipmi::intel::general::cmdSetSmSignal):
614         case makeCmdKey(ipmi::netFnOemOne,
615                         ipmi::intel::general::cmdMtmKeepAlive):
616         case makeCmdKey(ipmi::netFnOemOne,
617                         ipmi::intel::general::cmdSetManufacturingData):
618         case makeCmdKey(ipmi::netFnOemOne,
619                         ipmi::intel::general::cmdGetManufacturingData):
620         case makeCmdKey(ipmi::netFnStorage, ipmi::storage::cmdWriteFruData):
621 
622             // Check for MTM mode
623             if (mtm.getAccessLvl() != MtmLvl::mtmAvailable)
624             {
625                 return ipmi::ccInvalidCommand;
626             }
627     }
628     return ipmi::ccSuccess;
629 }
630 
631 static constexpr uint8_t maxEthSize = 6;
632 static constexpr uint8_t maxSupportedEth = 3;
633 static constexpr const char* factoryEthAddrBaseFileName =
634     "/var/sofs/factory-settings/network/mac/eth";
635 
636 ipmi::RspType<> setManufacturingData(boost::asio::yield_context yield,
637                                      uint8_t dataType,
638                                      std::array<uint8_t, maxEthSize> ethData)
639 {
640     // mfg filter logic will restrict this command executing only in mfg mode.
641     if (dataType >= maxSupportedEth)
642     {
643         return ipmi::responseParmOutOfRange();
644     }
645 
646     constexpr uint8_t invalidData = 0;
647     constexpr uint8_t validData = 1;
648     constexpr uint8_t ethAddrStrSize =
649         19; // XX:XX:XX:XX:XX:XX + \n + null termination;
650     std::vector<uint8_t> buff(ethAddrStrSize);
651     std::snprintf(reinterpret_cast<char*>(buff.data()), ethAddrStrSize,
652                   "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx\n", ethData.at(0),
653                   ethData.at(1), ethData.at(2), ethData.at(3), ethData.at(4),
654                   ethData.at(5));
655     std::ofstream oEthFile(factoryEthAddrBaseFileName +
656                                std::to_string(dataType),
657                            std::ofstream::out);
658     if (!oEthFile.good())
659     {
660         return ipmi::responseUnspecifiedError();
661     }
662 
663     oEthFile << reinterpret_cast<char*>(buff.data());
664     oEthFile << fflush;
665     oEthFile.close();
666 
667     resetMtmTimer(yield);
668     return ipmi::responseSuccess();
669 }
670 
671 ipmi::RspType<uint8_t, std::array<uint8_t, maxEthSize>>
672     getManufacturingData(boost::asio::yield_context yield, uint8_t dataType)
673 {
674     // mfg filter logic will restrict this command executing only in mfg mode.
675     if (dataType >= maxSupportedEth)
676     {
677         return ipmi::responseParmOutOfRange();
678     }
679     std::array<uint8_t, maxEthSize> ethData{0};
680     constexpr uint8_t invalidData = 0;
681     constexpr uint8_t validData = 1;
682 
683     std::ifstream iEthFile(factoryEthAddrBaseFileName +
684                                std::to_string(dataType),
685                            std::ifstream::in);
686     if (!iEthFile.good())
687     {
688         return ipmi::responseSuccess(invalidData, ethData);
689     }
690     std::string ethStr;
691     iEthFile >> ethStr;
692     uint8_t* data = ethData.data();
693     std::sscanf(ethStr.c_str(), "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx",
694                 data, (data + 1), (data + 2), (data + 3), (data + 4),
695                 (data + 5));
696 
697     resetMtmTimer(yield);
698     return ipmi::responseSuccess(validData, ethData);
699 }
700 
701 /** @brief implements slot master write read IPMI command which can be used for
702  * low-level I2C/SMBus write, read or write-read access for PCIE slots
703  * @param reserved - skip 6 bit
704  * @param addressType - address type
705  * @param bbSlotNum - baseboard slot number
706  * @param riserSlotNum - riser slot number
707  * @param reserved2 - skip 2 bit
708  * @param slaveAddr - slave address
709  * @param readCount - number of bytes to be read
710  * @param writeData - data to be written
711  *
712  * @returns IPMI completion code plus response data
713  */
714 ipmi::RspType<std::vector<uint8_t>>
715     appSlotI2CMasterWriteRead(uint6_t reserved, uint2_t addressType,
716                               uint3_t bbSlotNum, uint3_t riserSlotNum,
717                               uint2_t resvered2, uint8_t slaveAddr,
718                               uint8_t readCount, std::vector<uint8_t> writeData)
719 {
720     const size_t writeCount = writeData.size();
721     std::string i2cBus;
722     if (addressType == slotAddressTypeBus)
723     {
724         std::string path = "/dev/i2c-mux/Riser_" +
725                            std::to_string(static_cast<uint8_t>(bbSlotNum)) +
726                            "_Mux/Pcie_Slot_" +
727                            std::to_string(static_cast<uint8_t>(riserSlotNum));
728 
729         if (std::filesystem::exists(path) && std::filesystem::is_symlink(path))
730         {
731             i2cBus = std::filesystem::read_symlink(path);
732         }
733         else
734         {
735             phosphor::logging::log<phosphor::logging::level::ERR>(
736                 "Master write read command: Cannot get BusID");
737             return ipmi::responseInvalidFieldRequest();
738         }
739     }
740     else if (addressType == slotAddressTypeUniqueid)
741     {
742         i2cBus = "/dev/i2c-" +
743                  std::to_string(static_cast<uint8_t>(bbSlotNum) |
744                                 (static_cast<uint8_t>(riserSlotNum) << 3));
745     }
746     else
747     {
748         phosphor::logging::log<phosphor::logging::level::ERR>(
749             "Master write read command: invalid request");
750         return ipmi::responseInvalidFieldRequest();
751     }
752 
753     // Allow single byte write as it is offset byte to read the data, rest allow
754     // only in MFG mode.
755     if (writeCount > 1)
756     {
757         if (mtm.getAccessLvl() < MtmLvl::mtmAvailable)
758         {
759             return ipmi::responseInsufficientPrivilege();
760         }
761     }
762 
763     if (readCount > slotI2CMaxReadSize)
764     {
765         phosphor::logging::log<phosphor::logging::level::ERR>(
766             "Master write read command: Read count exceeds limit");
767         return ipmi::responseParmOutOfRange();
768     }
769 
770     if (!readCount && !writeCount)
771     {
772         phosphor::logging::log<phosphor::logging::level::ERR>(
773             "Master write read command: Read & write count are 0");
774         return ipmi::responseInvalidFieldRequest();
775     }
776 
777     std::vector<uint8_t> readBuf(readCount);
778 
779     ipmi::Cc retI2C = ipmi::i2cWriteRead(i2cBus, slaveAddr, writeData, readBuf);
780     if (retI2C != ipmi::ccSuccess)
781     {
782         return ipmi::response(retI2C);
783     }
784 
785     return ipmi::responseSuccess(readBuf);
786 }
787 
788 ipmi::RspType<> clearCMOS()
789 {
790     // There is an i2c device on bus 4, the slave address is 0x70. Based on the
791     // spec, writing 0x1 to address 0x60 on this device, will trigger the clear
792     // CMOS action.
793     constexpr uint8_t slaveAddr = 0x70;
794     std::string i2cBus = "/dev/i2c-4";
795     std::vector<uint8_t> writeData = {0x60, 0x1};
796     std::vector<uint8_t> readBuf(0);
797 
798     // TODO Needs to enhance the below security checking
799     if (mtm.getAccessLvl() < MtmLvl::mtmAvailable)
800     {
801         return ipmi::responseInsufficientPrivilege();
802     }
803 
804     ipmi::Cc retI2C = ipmi::i2cWriteRead(i2cBus, slaveAddr, writeData, readBuf);
805     return ipmi::response(retI2C);
806 }
807 } // namespace ipmi
808 
809 void register_mtm_commands() __attribute__((constructor));
810 void register_mtm_commands()
811 {
812     // <Get SM Signal>
813     ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnGeneral,
814                           ipmi::intel::general::cmdGetSmSignal,
815                           ipmi::Privilege::Admin, ipmi::appMTMGetSignal);
816 
817     ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnGeneral,
818                           ipmi::intel::general::cmdSetSmSignal,
819                           ipmi::Privilege::Admin, ipmi::appMTMSetSignal);
820 
821     ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnGeneral,
822                           ipmi::intel::general::cmdMtmKeepAlive,
823                           ipmi::Privilege::Admin, ipmi::mtmKeepAlive);
824 
825     ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnGeneral,
826                           ipmi::intel::general::cmdSetManufacturingData,
827                           ipmi::Privilege::Admin, ipmi::setManufacturingData);
828 
829     ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnGeneral,
830                           ipmi::intel::general::cmdGetManufacturingData,
831                           ipmi::Privilege::Admin, ipmi::getManufacturingData);
832 
833     ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnApp,
834                           ipmi::intel::general::cmdSlotI2CMasterWriteRead,
835                           ipmi::Privilege::Admin,
836                           ipmi::appSlotI2CMasterWriteRead);
837 
838     ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnPlatform,
839                           ipmi::intel::platform::cmdClearCMOS,
840                           ipmi::Privilege::Admin, ipmi::clearCMOS);
841 
842     ipmi::registerFilter(ipmi::prioOemBase,
843                          [](ipmi::message::Request::ptr request) {
844                              return ipmi::mfgFilterMessage(request);
845                          });
846 }
847