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