1a3702c1fSVernon Mauery /*
2a3702c1fSVernon Mauery // Copyright (c) 2018 Intel Corporation
3a3702c1fSVernon Mauery //
4a3702c1fSVernon Mauery // Licensed under the Apache License, Version 2.0 (the "License");
5a3702c1fSVernon Mauery // you may not use this file except in compliance with the License.
6a3702c1fSVernon Mauery // You may obtain a copy of the License at
7a3702c1fSVernon Mauery //
8a3702c1fSVernon Mauery //      http://www.apache.org/licenses/LICENSE-2.0
9a3702c1fSVernon Mauery //
10a3702c1fSVernon Mauery // Unless required by applicable law or agreed to in writing, software
11a3702c1fSVernon Mauery // distributed under the License is distributed on an "AS IS" BASIS,
12a3702c1fSVernon Mauery // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13a3702c1fSVernon Mauery // See the License for the specific language governing permissions and
14a3702c1fSVernon Mauery // limitations under the License.
15a3702c1fSVernon Mauery */
16a3702c1fSVernon Mauery 
17d872a4a4SAyushi Smriti #include <linux/input.h>
18d872a4a4SAyushi Smriti 
1906584cd0SArun P. Mohanan #include <boost/algorithm/string.hpp>
20147daec5SRichard Marian Thomaiyar #include <boost/container/flat_map.hpp>
21a3702c1fSVernon Mauery #include <ipmid/api.hpp>
22a3702c1fSVernon Mauery #include <manufacturingcommands.hpp>
23a3702c1fSVernon Mauery #include <oemcommands.hpp>
244180cfe9SAlex Schendel #include <phosphor-logging/lg2.hpp>
250748c69dSJason M. Bills #include <types.hpp>
26a3702c1fSVernon Mauery 
2797c30909SVasu V #include <charconv>
28fcd2d3a9SJames Feist #include <filesystem>
29fcd2d3a9SJames Feist #include <fstream>
30fcd2d3a9SJames Feist 
31a3702c1fSVernon Mauery namespace ipmi
32a3702c1fSVernon Mauery {
33a3702c1fSVernon Mauery 
34a3702c1fSVernon Mauery Manufacturing mtm;
35a3702c1fSVernon Mauery 
36a3702c1fSVernon Mauery static auto revertTimeOut =
37a3702c1fSVernon Mauery     std::chrono::duration_cast<std::chrono::microseconds>(
38a3702c1fSVernon Mauery         std::chrono::seconds(60)); // 1 minute timeout
39a3702c1fSVernon Mauery 
407acb2d27SV-Sanjana static constexpr uint8_t bbRiserMux = 0;
417acb2d27SV-Sanjana static constexpr uint8_t leftRiserMux = 1;
427acb2d27SV-Sanjana static constexpr uint8_t rightRiserMux = 2;
437acb2d27SV-Sanjana static constexpr uint8_t pcieMux = 3;
447acb2d27SV-Sanjana static constexpr uint8_t hsbpMux = 4;
457acb2d27SV-Sanjana 
46f267a67dSYong Li static constexpr uint8_t slotAddressTypeBus = 0;
47f267a67dSYong Li static constexpr uint8_t slotAddressTypeUniqueid = 1;
48f267a67dSYong Li static constexpr uint8_t slotI2CMaxReadSize = 35;
49f267a67dSYong Li 
50a3702c1fSVernon Mauery static constexpr const char* callbackMgrService =
51a3702c1fSVernon Mauery     "xyz.openbmc_project.CallbackManager";
52a3702c1fSVernon Mauery static constexpr const char* callbackMgrIntf =
53a3702c1fSVernon Mauery     "xyz.openbmc_project.CallbackManager";
54a3702c1fSVernon Mauery static constexpr const char* callbackMgrObjPath =
55a3702c1fSVernon Mauery     "/xyz/openbmc_project/CallbackManager";
56a3702c1fSVernon Mauery static constexpr const char* retriggerLedUpdate = "RetriggerLEDUpdate";
57a3702c1fSVernon Mauery 
58a3702c1fSVernon Mauery const static constexpr char* systemDService = "org.freedesktop.systemd1";
59a3702c1fSVernon Mauery const static constexpr char* systemDObjPath = "/org/freedesktop/systemd1";
60a3702c1fSVernon Mauery const static constexpr char* systemDMgrIntf =
61a3702c1fSVernon Mauery     "org.freedesktop.systemd1.Manager";
62a3702c1fSVernon Mauery const static constexpr char* pidControlService = "phosphor-pid-control.service";
63a3702c1fSVernon Mauery 
resetMtmTimer(ipmi::Context::ptr ctx)64357ddc74SRichard Marian Thomaiyar static inline Cc resetMtmTimer(ipmi::Context::ptr ctx)
65666dd01cSRichard Marian Thomaiyar {
66666dd01cSRichard Marian Thomaiyar     boost::system::error_code ec;
67357ddc74SRichard Marian Thomaiyar     ctx->bus->yield_method_call<>(ctx->yield, ec, specialModeService,
68666dd01cSRichard Marian Thomaiyar                                   specialModeObjPath, specialModeIntf,
69666dd01cSRichard Marian Thomaiyar                                   "ResetTimer");
70666dd01cSRichard Marian Thomaiyar     if (ec)
71666dd01cSRichard Marian Thomaiyar     {
724180cfe9SAlex Schendel         lg2::error("Failed to reset the manufacturing mode timer");
73666dd01cSRichard Marian Thomaiyar         return ccUnspecifiedError;
74666dd01cSRichard Marian Thomaiyar     }
75666dd01cSRichard Marian Thomaiyar     return ccSuccess;
76666dd01cSRichard Marian Thomaiyar }
77666dd01cSRichard Marian Thomaiyar 
getGpioPathForSmSignal(const SmSignalGet signal,std::string & path)7838d2b5a6SJason M. Bills int getGpioPathForSmSignal(const SmSignalGet signal, std::string& path)
79a3702c1fSVernon Mauery {
8038d2b5a6SJason M. Bills     switch (signal)
8138d2b5a6SJason M. Bills     {
8238d2b5a6SJason M. Bills         case SmSignalGet::smPowerButton:
8338d2b5a6SJason M. Bills             path = "/xyz/openbmc_project/chassis/buttons/power";
8438d2b5a6SJason M. Bills             break;
8538d2b5a6SJason M. Bills         case SmSignalGet::smResetButton:
8638d2b5a6SJason M. Bills             path = "/xyz/openbmc_project/chassis/buttons/reset";
8738d2b5a6SJason M. Bills             break;
8838d2b5a6SJason M. Bills         case SmSignalGet::smNMIButton:
8938d2b5a6SJason M. Bills             path = "/xyz/openbmc_project/chassis/buttons/nmi";
9038d2b5a6SJason M. Bills             break;
918e5e2b04SRichard Marian Thomaiyar         case SmSignalGet::smIdentifyButton:
928e5e2b04SRichard Marian Thomaiyar             path = "/xyz/openbmc_project/chassis/buttons/id";
938e5e2b04SRichard Marian Thomaiyar             break;
9438d2b5a6SJason M. Bills         default:
9538d2b5a6SJason M. Bills             return -1;
9638d2b5a6SJason M. Bills             break;
9738d2b5a6SJason M. Bills     }
9838d2b5a6SJason M. Bills     return 0;
99a3702c1fSVernon Mauery }
100a3702c1fSVernon Mauery 
ledStoreAndSet(SmSignalSet signal,const std::string & setState)10137890392SPatrick Venture ipmi_ret_t ledStoreAndSet(SmSignalSet signal, const std::string& setState)
102a3702c1fSVernon Mauery {
103a3702c1fSVernon Mauery     LedProperty* ledProp = mtm.findLedProperty(signal);
104a3702c1fSVernon Mauery     if (ledProp == nullptr)
105a3702c1fSVernon Mauery     {
106a3702c1fSVernon Mauery         return IPMI_CC_INVALID_FIELD_REQUEST;
107a3702c1fSVernon Mauery     }
108a3702c1fSVernon Mauery 
109a3702c1fSVernon Mauery     std::string ledName = ledProp->getName();
110a3702c1fSVernon Mauery     std::string ledService = ledServicePrefix + ledName;
111a3702c1fSVernon Mauery     std::string ledPath = ledPathPrefix + ledName;
112a3702c1fSVernon Mauery     ipmi::Value presentState;
113a3702c1fSVernon Mauery 
114a3702c1fSVernon Mauery     if (false == ledProp->getLock())
115a3702c1fSVernon Mauery     {
116a3702c1fSVernon Mauery         if (mtm.getProperty(ledService.c_str(), ledPath.c_str(), ledIntf,
117a3702c1fSVernon Mauery                             "State", &presentState) != 0)
118a3702c1fSVernon Mauery         {
119a3702c1fSVernon Mauery             return IPMI_CC_UNSPECIFIED_ERROR;
120a3702c1fSVernon Mauery         }
121a3702c1fSVernon Mauery         ledProp->setPrevState(std::get<std::string>(presentState));
122a3702c1fSVernon Mauery         ledProp->setLock(true);
123a3702c1fSVernon Mauery         if (signal == SmSignalSet::smPowerFaultLed ||
124a3702c1fSVernon Mauery             signal == SmSignalSet::smSystemReadyLed)
125a3702c1fSVernon Mauery         {
126a3702c1fSVernon Mauery             mtm.revertLedCallback = true;
127a3702c1fSVernon Mauery         }
128a3702c1fSVernon Mauery     }
12938d2b5a6SJason M. Bills     if (mtm.setProperty(ledService, ledPath, ledIntf, "State",
130a3702c1fSVernon Mauery                         ledStateStr + setState) != 0)
131a3702c1fSVernon Mauery     {
132a3702c1fSVernon Mauery         return IPMI_CC_UNSPECIFIED_ERROR;
133a3702c1fSVernon Mauery     }
134a3702c1fSVernon Mauery     return IPMI_CC_OK;
135a3702c1fSVernon Mauery }
136a3702c1fSVernon Mauery 
ledRevert(SmSignalSet signal)137a3702c1fSVernon Mauery ipmi_ret_t ledRevert(SmSignalSet signal)
138a3702c1fSVernon Mauery {
139a3702c1fSVernon Mauery     LedProperty* ledProp = mtm.findLedProperty(signal);
140a3702c1fSVernon Mauery     if (ledProp == nullptr)
141a3702c1fSVernon Mauery     {
142a3702c1fSVernon Mauery         return IPMI_CC_INVALID_FIELD_REQUEST;
143a3702c1fSVernon Mauery     }
144a3702c1fSVernon Mauery     if (true == ledProp->getLock())
145a3702c1fSVernon Mauery     {
146a3702c1fSVernon Mauery         ledProp->setLock(false);
147a3702c1fSVernon Mauery         if (signal == SmSignalSet::smPowerFaultLed ||
148a3702c1fSVernon Mauery             signal == SmSignalSet::smSystemReadyLed)
149a3702c1fSVernon Mauery         {
150a3702c1fSVernon Mauery             try
151a3702c1fSVernon Mauery             {
152a3702c1fSVernon Mauery                 ipmi::method_no_args::callDbusMethod(
153a3702c1fSVernon Mauery                     *getSdBus(), callbackMgrService, callbackMgrObjPath,
154a3702c1fSVernon Mauery                     callbackMgrIntf, retriggerLedUpdate);
155a3702c1fSVernon Mauery             }
156bd51e6a9SPatrick Williams             catch (const sdbusplus::exception_t& e)
157a3702c1fSVernon Mauery             {
158a3702c1fSVernon Mauery                 return IPMI_CC_UNSPECIFIED_ERROR;
159a3702c1fSVernon Mauery             }
160a3702c1fSVernon Mauery             mtm.revertLedCallback = false;
161a3702c1fSVernon Mauery         }
162a3702c1fSVernon Mauery         else
163a3702c1fSVernon Mauery         {
164a3702c1fSVernon Mauery             std::string ledName = ledProp->getName();
165a3702c1fSVernon Mauery             std::string ledService = ledServicePrefix + ledName;
166a3702c1fSVernon Mauery             std::string ledPath = ledPathPrefix + ledName;
16738d2b5a6SJason M. Bills             if (mtm.setProperty(ledService, ledPath, ledIntf, "State",
16838d2b5a6SJason M. Bills                                 ledProp->getPrevState()) != 0)
169a3702c1fSVernon Mauery             {
170a3702c1fSVernon Mauery                 return IPMI_CC_UNSPECIFIED_ERROR;
171a3702c1fSVernon Mauery             }
172a3702c1fSVernon Mauery         }
173a3702c1fSVernon Mauery     }
174a3702c1fSVernon Mauery     return IPMI_CC_OK;
175a3702c1fSVernon Mauery }
176a3702c1fSVernon Mauery 
initData()177a3702c1fSVernon Mauery void Manufacturing::initData()
178a3702c1fSVernon Mauery {
179a3702c1fSVernon Mauery     ledPropertyList.push_back(
180a3702c1fSVernon Mauery         LedProperty(SmSignalSet::smPowerFaultLed, "status_amber"));
181a3702c1fSVernon Mauery     ledPropertyList.push_back(
182a3702c1fSVernon Mauery         LedProperty(SmSignalSet::smSystemReadyLed, "status_green"));
183a3702c1fSVernon Mauery     ledPropertyList.push_back(
184a3702c1fSVernon Mauery         LedProperty(SmSignalSet::smIdentifyLed, "identify"));
185a3702c1fSVernon Mauery }
186a3702c1fSVernon Mauery 
revertTimerHandler()187a3702c1fSVernon Mauery void Manufacturing::revertTimerHandler()
188a3702c1fSVernon Mauery {
189ae13ac62SRichard Marian Thomaiyar #ifdef BMC_VALIDATION_UNSECURE_FEATURE
190ae13ac62SRichard Marian Thomaiyar     if (mtm.getMfgMode() == SpecialMode::valUnsecure)
191ae13ac62SRichard Marian Thomaiyar     {
192ae13ac62SRichard Marian Thomaiyar         // Don't revert the behaviour for validation unsecure mode.
193ae13ac62SRichard Marian Thomaiyar         return;
194ae13ac62SRichard Marian Thomaiyar     }
195ae13ac62SRichard Marian Thomaiyar #endif
196a3702c1fSVernon Mauery     if (revertFanPWM)
197a3702c1fSVernon Mauery     {
198a3702c1fSVernon Mauery         revertFanPWM = false;
199a3702c1fSVernon Mauery         disablePidControlService(false);
200a3702c1fSVernon Mauery     }
201a3702c1fSVernon Mauery 
202d872a4a4SAyushi Smriti     if (mtmTestBeepFd != -1)
203d872a4a4SAyushi Smriti     {
204d872a4a4SAyushi Smriti         ::close(mtmTestBeepFd);
205d872a4a4SAyushi Smriti         mtmTestBeepFd = -1;
206d872a4a4SAyushi Smriti     }
207d872a4a4SAyushi Smriti 
208a3702c1fSVernon Mauery     for (const auto& ledProperty : ledPropertyList)
209a3702c1fSVernon Mauery     {
210a3702c1fSVernon Mauery         const std::string& ledName = ledProperty.getName();
211f365614cSJayaprakash Mutyala         if (ledName == "identify" && mtm.getMfgMode() == SpecialMode::mfg)
212f365614cSJayaprakash Mutyala         {
213f365614cSJayaprakash Mutyala             // Don't revert the behaviour for manufacturing mode
214f365614cSJayaprakash Mutyala             continue;
215f365614cSJayaprakash Mutyala         }
216a3702c1fSVernon Mauery         ledRevert(ledProperty.getSignal());
217a3702c1fSVernon Mauery     }
218a3702c1fSVernon Mauery }
219a3702c1fSVernon Mauery 
Manufacturing()220a3702c1fSVernon Mauery Manufacturing::Manufacturing() :
221a3702c1fSVernon Mauery     revertTimer([&](void) { revertTimerHandler(); })
222a3702c1fSVernon Mauery {
223a3702c1fSVernon Mauery     initData();
224a3702c1fSVernon Mauery }
225a3702c1fSVernon Mauery 
getProperty(const std::string & service,const std::string & path,const std::string & interface,const std::string & propertyName,ipmi::Value * reply)22638d2b5a6SJason M. Bills int8_t Manufacturing::getProperty(const std::string& service,
22738d2b5a6SJason M. Bills                                   const std::string& path,
22838d2b5a6SJason M. Bills                                   const std::string& interface,
22938d2b5a6SJason M. Bills                                   const std::string& propertyName,
23038d2b5a6SJason M. Bills                                   ipmi::Value* reply)
231a3702c1fSVernon Mauery {
232a3702c1fSVernon Mauery     try
233a3702c1fSVernon Mauery     {
23438d2b5a6SJason M. Bills         *reply = ipmi::getDbusProperty(*getSdBus(), service, path, interface,
23538d2b5a6SJason M. Bills                                        propertyName);
236a3702c1fSVernon Mauery     }
237f944d2e5SPatrick Williams     catch (const sdbusplus::exception_t& e)
238a3702c1fSVernon Mauery     {
2394180cfe9SAlex Schendel         lg2::info("ERROR: getProperty");
240a3702c1fSVernon Mauery         return -1;
241a3702c1fSVernon Mauery     }
242a3702c1fSVernon Mauery 
243a3702c1fSVernon Mauery     return 0;
244a3702c1fSVernon Mauery }
245a3702c1fSVernon Mauery 
setProperty(const std::string & service,const std::string & path,const std::string & interface,const std::string & propertyName,ipmi::Value value)24638d2b5a6SJason M. Bills int8_t Manufacturing::setProperty(const std::string& service,
24738d2b5a6SJason M. Bills                                   const std::string& path,
24838d2b5a6SJason M. Bills                                   const std::string& interface,
24938d2b5a6SJason M. Bills                                   const std::string& propertyName,
25038d2b5a6SJason M. Bills                                   ipmi::Value value)
251a3702c1fSVernon Mauery {
252a3702c1fSVernon Mauery     try
253a3702c1fSVernon Mauery     {
25438d2b5a6SJason M. Bills         ipmi::setDbusProperty(*getSdBus(), service, path, interface,
255a3702c1fSVernon Mauery                               propertyName, value);
256a3702c1fSVernon Mauery     }
257f944d2e5SPatrick Williams     catch (const sdbusplus::exception_t& e)
258a3702c1fSVernon Mauery     {
2594180cfe9SAlex Schendel         lg2::info("ERROR: setProperty");
260a3702c1fSVernon Mauery         return -1;
261a3702c1fSVernon Mauery     }
262a3702c1fSVernon Mauery 
263a3702c1fSVernon Mauery     return 0;
264a3702c1fSVernon Mauery }
265a3702c1fSVernon Mauery 
disablePidControlService(const bool disable)266a3702c1fSVernon Mauery int8_t Manufacturing::disablePidControlService(const bool disable)
267a3702c1fSVernon Mauery {
268a3702c1fSVernon Mauery     try
269a3702c1fSVernon Mauery     {
270a3702c1fSVernon Mauery         auto dbus = getSdBus();
271a3702c1fSVernon Mauery         auto method = dbus->new_method_call(systemDService, systemDObjPath,
272a3702c1fSVernon Mauery                                             systemDMgrIntf,
273a3702c1fSVernon Mauery                                             disable ? "StopUnit" : "StartUnit");
274a3702c1fSVernon Mauery         method.append(pidControlService, "replace");
275a3702c1fSVernon Mauery         auto reply = dbus->call(method);
276a3702c1fSVernon Mauery     }
277f944d2e5SPatrick Williams     catch (const sdbusplus::exception_t& e)
278a3702c1fSVernon Mauery     {
2794180cfe9SAlex Schendel         lg2::info("ERROR: phosphor-pid-control service start or stop failed");
280a3702c1fSVernon Mauery         return -1;
281a3702c1fSVernon Mauery     }
282a3702c1fSVernon Mauery     return 0;
283a3702c1fSVernon Mauery }
284a3702c1fSVernon Mauery 
findPwmName(ipmi::Context::ptr & ctx,uint8_t instance,std::string & pwmName)28590eb7876SAlex Schendel static bool findPwmName(ipmi::Context::ptr& ctx, uint8_t instance,
28690eb7876SAlex Schendel                         std::string& pwmName)
28790eb7876SAlex Schendel {
28890eb7876SAlex Schendel     boost::system::error_code ec{};
28990eb7876SAlex Schendel     ObjectValueTree obj;
29090eb7876SAlex Schendel 
29190eb7876SAlex Schendel     // GetAll the objects under service FruDevice
29290eb7876SAlex Schendel     ec = getManagedObjects(ctx, "xyz.openbmc_project.EntityManager",
29390eb7876SAlex Schendel                            "/xyz/openbmc_project/inventory", obj);
29490eb7876SAlex Schendel     if (ec)
29590eb7876SAlex Schendel     {
2964180cfe9SAlex Schendel         lg2::error("GetMangagedObjects failed", "ERROR", ec.message().c_str());
29790eb7876SAlex Schendel         return false;
29890eb7876SAlex Schendel     }
29990eb7876SAlex Schendel     for (const auto& [path, objData] : obj)
30090eb7876SAlex Schendel     {
30190eb7876SAlex Schendel         for (const auto& [intf, propMap] : objData)
30290eb7876SAlex Schendel         {
30390eb7876SAlex Schendel             // Currently, these are the three different fan types supported.
30490eb7876SAlex Schendel             if (intf == "xyz.openbmc_project.Configuration.AspeedFan" ||
30590eb7876SAlex Schendel                 intf == "xyz.openbmc_project.Configuration.I2CFan" ||
30690eb7876SAlex Schendel                 intf == "xyz.openbmc_project.Configuration.NuvotonFan")
30790eb7876SAlex Schendel             {
3084180cfe9SAlex Schendel                 std::string fanPath = "/Fan_";
30990eb7876SAlex Schendel 
3104180cfe9SAlex Schendel                 fanPath += std::to_string(instance);
3114180cfe9SAlex Schendel                 std::string objPath = path.str;
3124180cfe9SAlex Schendel                 objPath = objPath.substr(objPath.find_last_of("/"));
3134180cfe9SAlex Schendel                 if (objPath != fanPath)
31490eb7876SAlex Schendel                 {
31590eb7876SAlex Schendel                     continue;
31690eb7876SAlex Schendel                 }
31790eb7876SAlex Schendel                 auto connector = objData.find(intf + std::string(".Connector"));
31890eb7876SAlex Schendel                 if (connector == objData.end())
31990eb7876SAlex Schendel                 {
32090eb7876SAlex Schendel                     return false;
32190eb7876SAlex Schendel                 }
32290eb7876SAlex Schendel                 auto findPwmName = connector->second.find("PwmName");
32390eb7876SAlex Schendel                 if (findPwmName != connector->second.end())
32490eb7876SAlex Schendel                 {
32590eb7876SAlex Schendel                     auto fanPwmName =
32690eb7876SAlex Schendel                         std::get_if<std::string>(&findPwmName->second);
32790eb7876SAlex Schendel                     if (!fanPwmName)
32890eb7876SAlex Schendel                     {
3294180cfe9SAlex Schendel                         lg2::error("PwmName parse ERROR.");
33090eb7876SAlex Schendel                         return false;
33190eb7876SAlex Schendel                     }
33290eb7876SAlex Schendel                     pwmName = *fanPwmName;
33390eb7876SAlex Schendel                     return true;
33490eb7876SAlex Schendel                 }
33590eb7876SAlex Schendel                 auto findPwm = connector->second.find("Pwm");
33690eb7876SAlex Schendel                 if (findPwm == connector->second.end())
33790eb7876SAlex Schendel                 {
33890eb7876SAlex Schendel                     return false;
33990eb7876SAlex Schendel                 }
34090eb7876SAlex Schendel                 auto fanPwm = std::get_if<uint64_t>(&findPwm->second);
34190eb7876SAlex Schendel                 if (!fanPwm)
34290eb7876SAlex Schendel                 {
34390eb7876SAlex Schendel                     return false;
34490eb7876SAlex Schendel                 }
34590eb7876SAlex Schendel                 pwmName = "Pwm_" + std::to_string(*fanPwm + 1);
34690eb7876SAlex Schendel                 return true;
34790eb7876SAlex Schendel             }
34890eb7876SAlex Schendel         }
34990eb7876SAlex Schendel     }
35090eb7876SAlex Schendel     return false;
35190eb7876SAlex Schendel }
35238d2b5a6SJason M. Bills ipmi::RspType<uint8_t,                // Signal value
35338d2b5a6SJason M. Bills               std::optional<uint16_t> // Fan tach value
35438d2b5a6SJason M. Bills               >
appMTMGetSignal(ipmi::Context::ptr ctx,uint8_t signalTypeByte,uint8_t instance,uint8_t actionByte)355357ddc74SRichard Marian Thomaiyar     appMTMGetSignal(ipmi::Context::ptr ctx, uint8_t signalTypeByte,
356147daec5SRichard Marian Thomaiyar                     uint8_t instance, uint8_t actionByte)
357a3702c1fSVernon Mauery {
358e0511e5fSAyushi Smriti     // mfg filter logic is used to allow MTM get signal command only in
359e0511e5fSAyushi Smriti     // manfacturing mode.
36038d2b5a6SJason M. Bills 
36138d2b5a6SJason M. Bills     SmSignalGet signalType = static_cast<SmSignalGet>(signalTypeByte);
36238d2b5a6SJason M. Bills     SmActionGet action = static_cast<SmActionGet>(actionByte);
36338d2b5a6SJason M. Bills 
36438d2b5a6SJason M. Bills     switch (signalType)
36538d2b5a6SJason M. Bills     {
36698705b39Sanil kumar appana         case SmSignalGet::smChassisIntrusion:
36798705b39Sanil kumar appana         {
36898705b39Sanil kumar appana             ipmi::Value reply;
36998705b39Sanil kumar appana             if (mtm.getProperty(intrusionService, intrusionPath, intrusionIntf,
37098705b39Sanil kumar appana                                 "Status", &reply) < 0)
37198705b39Sanil kumar appana             {
37298705b39Sanil kumar appana                 return ipmi::responseInvalidFieldRequest();
37398705b39Sanil kumar appana             }
37498705b39Sanil kumar appana             std::string* intrusionStatus = std::get_if<std::string>(&reply);
37598705b39Sanil kumar appana             if (!intrusionStatus)
37698705b39Sanil kumar appana             {
37798705b39Sanil kumar appana                 return ipmi::responseUnspecifiedError();
37898705b39Sanil kumar appana             }
37998705b39Sanil kumar appana 
38098705b39Sanil kumar appana             uint8_t status = 0;
38198705b39Sanil kumar appana             if (!intrusionStatus->compare("Normal"))
38298705b39Sanil kumar appana             {
38398705b39Sanil kumar appana                 status = static_cast<uint8_t>(IntrusionStatus::normal);
38498705b39Sanil kumar appana             }
38598705b39Sanil kumar appana             else if (!intrusionStatus->compare("HardwareIntrusion"))
38698705b39Sanil kumar appana             {
38798705b39Sanil kumar appana                 status =
38898705b39Sanil kumar appana                     static_cast<uint8_t>(IntrusionStatus::hardwareIntrusion);
38998705b39Sanil kumar appana             }
39098705b39Sanil kumar appana             else if (!intrusionStatus->compare("TamperingDetected"))
39198705b39Sanil kumar appana             {
39298705b39Sanil kumar appana                 status =
39398705b39Sanil kumar appana                     static_cast<uint8_t>(IntrusionStatus::tamperingDetected);
39498705b39Sanil kumar appana             }
39598705b39Sanil kumar appana             else
39698705b39Sanil kumar appana             {
39798705b39Sanil kumar appana                 return ipmi::responseUnspecifiedError();
39898705b39Sanil kumar appana             }
39998705b39Sanil kumar appana             return ipmi::responseSuccess(status, std::nullopt);
40098705b39Sanil kumar appana         }
40138d2b5a6SJason M. Bills         case SmSignalGet::smFanPwmGet:
40238d2b5a6SJason M. Bills         {
403a3702c1fSVernon Mauery             ipmi::Value reply;
40490eb7876SAlex Schendel             std::string pwmName, fullPath;
4054180cfe9SAlex Schendel             if (!findPwmName(ctx, instance + 1, pwmName))
40690eb7876SAlex Schendel             {
40790eb7876SAlex Schendel                 // The default PWM name is Pwm_#
40890eb7876SAlex Schendel                 pwmName = "Pwm_" + std::to_string(instance + 1);
40990eb7876SAlex Schendel             }
41090eb7876SAlex Schendel             fullPath = fanPwmPath + pwmName;
41138d2b5a6SJason M. Bills             if (mtm.getProperty(fanService, fullPath, fanIntf, "Value",
41238d2b5a6SJason M. Bills                                 &reply) < 0)
41338d2b5a6SJason M. Bills             {
41438d2b5a6SJason M. Bills                 return ipmi::responseInvalidFieldRequest();
41538d2b5a6SJason M. Bills             }
41638d2b5a6SJason M. Bills             double* doubleVal = std::get_if<double>(&reply);
41738d2b5a6SJason M. Bills             if (doubleVal == nullptr)
41838d2b5a6SJason M. Bills             {
41938d2b5a6SJason M. Bills                 return ipmi::responseUnspecifiedError();
42038d2b5a6SJason M. Bills             }
42138d2b5a6SJason M. Bills             uint8_t sensorVal = std::round(*doubleVal);
422357ddc74SRichard Marian Thomaiyar             resetMtmTimer(ctx);
42338d2b5a6SJason M. Bills             return ipmi::responseSuccess(sensorVal, std::nullopt);
42438d2b5a6SJason M. Bills         }
42538d2b5a6SJason M. Bills         break;
42638d2b5a6SJason M. Bills         case SmSignalGet::smFanTachometerGet:
42738d2b5a6SJason M. Bills         {
428147daec5SRichard Marian Thomaiyar             boost::system::error_code ec;
429147daec5SRichard Marian Thomaiyar             using objFlatMap = boost::container::flat_map<
430147daec5SRichard Marian Thomaiyar                 std::string, boost::container::flat_map<
431147daec5SRichard Marian Thomaiyar                                  std::string, std::vector<std::string>>>;
432147daec5SRichard Marian Thomaiyar 
433357ddc74SRichard Marian Thomaiyar             auto flatMap = ctx->bus->yield_method_call<objFlatMap>(
434357ddc74SRichard Marian Thomaiyar                 ctx->yield, ec, "xyz.openbmc_project.ObjectMapper",
435147daec5SRichard Marian Thomaiyar                 "/xyz/openbmc_project/object_mapper",
436147daec5SRichard Marian Thomaiyar                 "xyz.openbmc_project.ObjectMapper", "GetSubTree",
437147daec5SRichard Marian Thomaiyar                 fanTachBasePath, 0, std::array<const char*, 1>{fanIntf});
438147daec5SRichard Marian Thomaiyar             if (ec)
439147daec5SRichard Marian Thomaiyar             {
4404180cfe9SAlex Schendel                 lg2::error("Failed to query fan tach sub tree objects");
441147daec5SRichard Marian Thomaiyar                 return ipmi::responseUnspecifiedError();
442147daec5SRichard Marian Thomaiyar             }
443147daec5SRichard Marian Thomaiyar             if (instance >= flatMap.size())
44438d2b5a6SJason M. Bills             {
44538d2b5a6SJason M. Bills                 return ipmi::responseInvalidFieldRequest();
44638d2b5a6SJason M. Bills             }
447147daec5SRichard Marian Thomaiyar             auto itr = flatMap.nth(instance);
44838d2b5a6SJason M. Bills             ipmi::Value reply;
449147daec5SRichard Marian Thomaiyar             if (mtm.getProperty(fanService, itr->first, fanIntf, "Value",
45038d2b5a6SJason M. Bills                                 &reply) < 0)
45138d2b5a6SJason M. Bills             {
45238d2b5a6SJason M. Bills                 return ipmi::responseInvalidFieldRequest();
45338d2b5a6SJason M. Bills             }
45438d2b5a6SJason M. Bills 
45538d2b5a6SJason M. Bills             double* doubleVal = std::get_if<double>(&reply);
45638d2b5a6SJason M. Bills             if (doubleVal == nullptr)
45738d2b5a6SJason M. Bills             {
45838d2b5a6SJason M. Bills                 return ipmi::responseUnspecifiedError();
45938d2b5a6SJason M. Bills             }
46038d2b5a6SJason M. Bills             uint8_t sensorVal = FAN_PRESENT | FAN_SENSOR_PRESENT;
46138d2b5a6SJason M. Bills             std::optional<uint16_t> fanTach = std::round(*doubleVal);
46238d2b5a6SJason M. Bills 
463357ddc74SRichard Marian Thomaiyar             resetMtmTimer(ctx);
46438d2b5a6SJason M. Bills             return ipmi::responseSuccess(sensorVal, fanTach);
46538d2b5a6SJason M. Bills         }
46638d2b5a6SJason M. Bills         break;
4678e5e2b04SRichard Marian Thomaiyar         case SmSignalGet::smIdentifyButton:
4688e5e2b04SRichard Marian Thomaiyar         {
4698e5e2b04SRichard Marian Thomaiyar             if (action == SmActionGet::revert || action == SmActionGet::ignore)
4708e5e2b04SRichard Marian Thomaiyar             {
4718e5e2b04SRichard Marian Thomaiyar                 // ButtonMasked property is not supported for ID button as it is
4728e5e2b04SRichard Marian Thomaiyar                 // unnecessary. Hence if requested for revert / ignore, override
4738e5e2b04SRichard Marian Thomaiyar                 // it to sample action to make tools happy.
4748e5e2b04SRichard Marian Thomaiyar                 action = SmActionGet::sample;
4758e5e2b04SRichard Marian Thomaiyar             }
476*dcff1506SVernon Mauery             [[fallthrough]];
4778e5e2b04SRichard Marian Thomaiyar         }
47838d2b5a6SJason M. Bills         case SmSignalGet::smResetButton:
47938d2b5a6SJason M. Bills         case SmSignalGet::smPowerButton:
48038d2b5a6SJason M. Bills         case SmSignalGet::smNMIButton:
48138d2b5a6SJason M. Bills         {
48238d2b5a6SJason M. Bills             std::string path;
48338d2b5a6SJason M. Bills             if (getGpioPathForSmSignal(signalType, path) < 0)
48438d2b5a6SJason M. Bills             {
48538d2b5a6SJason M. Bills                 return ipmi::responseInvalidFieldRequest();
48638d2b5a6SJason M. Bills             }
487a3702c1fSVernon Mauery 
488a3702c1fSVernon Mauery             switch (action)
489a3702c1fSVernon Mauery             {
490a3702c1fSVernon Mauery                 case SmActionGet::sample:
4914180cfe9SAlex Schendel                     lg2::info("case SmActionGet::sample");
492a3702c1fSVernon Mauery                     break;
493a3702c1fSVernon Mauery                 case SmActionGet::ignore:
494a3702c1fSVernon Mauery                 {
4954180cfe9SAlex Schendel                     lg2::info("case SmActionGet::ignore");
49638d2b5a6SJason M. Bills                     if (mtm.setProperty(buttonService, path, buttonIntf,
49738d2b5a6SJason M. Bills                                         "ButtonMasked", true) < 0)
498a3702c1fSVernon Mauery                     {
49938d2b5a6SJason M. Bills                         return ipmi::responseUnspecifiedError();
500a3702c1fSVernon Mauery                     }
501a3702c1fSVernon Mauery                 }
502a3702c1fSVernon Mauery                 break;
503a3702c1fSVernon Mauery                 case SmActionGet::revert:
504a3702c1fSVernon Mauery                 {
5054180cfe9SAlex Schendel                     lg2::info("case SmActionGet::revert");
50638d2b5a6SJason M. Bills                     if (mtm.setProperty(buttonService, path, buttonIntf,
50738d2b5a6SJason M. Bills                                         "ButtonMasked", false) < 0)
508a3702c1fSVernon Mauery                     {
50938d2b5a6SJason M. Bills                         return ipmi::responseUnspecifiedError();
510a3702c1fSVernon Mauery                     }
511a3702c1fSVernon Mauery                 }
512a3702c1fSVernon Mauery                 break;
513a3702c1fSVernon Mauery 
514a3702c1fSVernon Mauery                 default:
51538d2b5a6SJason M. Bills                     return ipmi::responseInvalidFieldRequest();
516a3702c1fSVernon Mauery                     break;
517a3702c1fSVernon Mauery             }
518a3702c1fSVernon Mauery 
519a3702c1fSVernon Mauery             ipmi::Value reply;
52038d2b5a6SJason M. Bills             if (mtm.getProperty(buttonService, path, buttonIntf,
52138d2b5a6SJason M. Bills                                 "ButtonPressed", &reply) < 0)
522a3702c1fSVernon Mauery             {
52338d2b5a6SJason M. Bills                 return ipmi::responseUnspecifiedError();
524a3702c1fSVernon Mauery             }
52538d2b5a6SJason M. Bills             bool* valPtr = std::get_if<bool>(&reply);
52638d2b5a6SJason M. Bills             if (valPtr == nullptr)
527a3702c1fSVernon Mauery             {
52838d2b5a6SJason M. Bills                 return ipmi::responseUnspecifiedError();
529a3702c1fSVernon Mauery             }
530357ddc74SRichard Marian Thomaiyar             resetMtmTimer(ctx);
53138d2b5a6SJason M. Bills             uint8_t sensorVal = *valPtr;
53238d2b5a6SJason M. Bills             return ipmi::responseSuccess(sensorVal, std::nullopt);
533a3702c1fSVernon Mauery         }
534a3702c1fSVernon Mauery         break;
5351b74a210SRichard Marian Thomaiyar         case SmSignalGet::smNcsiDiag:
5361b74a210SRichard Marian Thomaiyar         {
5371b74a210SRichard Marian Thomaiyar             constexpr const char* netBasePath = "/sys/class/net/eth";
5381b74a210SRichard Marian Thomaiyar             constexpr const char* carrierSuffix = "/carrier";
5391b74a210SRichard Marian Thomaiyar             std::ifstream netIfs(netBasePath + std::to_string(instance) +
5401b74a210SRichard Marian Thomaiyar                                  carrierSuffix);
5411b74a210SRichard Marian Thomaiyar             if (!netIfs.good())
5421b74a210SRichard Marian Thomaiyar             {
5431b74a210SRichard Marian Thomaiyar                 return ipmi::responseInvalidFieldRequest();
5441b74a210SRichard Marian Thomaiyar             }
5451b74a210SRichard Marian Thomaiyar             std::string carrier;
5461b74a210SRichard Marian Thomaiyar             netIfs >> carrier;
547357ddc74SRichard Marian Thomaiyar             resetMtmTimer(ctx);
5481b74a210SRichard Marian Thomaiyar             return ipmi::responseSuccess(
5491b74a210SRichard Marian Thomaiyar                 static_cast<uint8_t>(std::stoi(carrier)), std::nullopt);
5501b74a210SRichard Marian Thomaiyar         }
5511b74a210SRichard Marian Thomaiyar         break;
552a3702c1fSVernon Mauery         default:
55338d2b5a6SJason M. Bills             return ipmi::responseInvalidFieldRequest();
554a3702c1fSVernon Mauery             break;
555a3702c1fSVernon Mauery     }
556a3702c1fSVernon Mauery }
557a3702c1fSVernon Mauery 
appMTMSetSignal(ipmi::Context::ptr ctx,uint8_t signalTypeByte,uint8_t instance,uint8_t actionByte,std::optional<uint8_t> pwmSpeed)558357ddc74SRichard Marian Thomaiyar ipmi::RspType<> appMTMSetSignal(ipmi::Context::ptr ctx, uint8_t signalTypeByte,
559357ddc74SRichard Marian Thomaiyar                                 uint8_t instance, uint8_t actionByte,
5605e3bf557SAyushi Smriti                                 std::optional<uint8_t> pwmSpeed)
561a3702c1fSVernon Mauery {
562e0511e5fSAyushi Smriti     // mfg filter logic is used to allow MTM set signal command only in
563e0511e5fSAyushi Smriti     // manfacturing mode.
5645e3bf557SAyushi Smriti 
5655e3bf557SAyushi Smriti     SmSignalSet signalType = static_cast<SmSignalSet>(signalTypeByte);
5665e3bf557SAyushi Smriti     SmActionSet action = static_cast<SmActionSet>(actionByte);
5675e3bf557SAyushi Smriti     Cc retCode = ccSuccess;
56813b0039dSJames Feist     int8_t ret = 0;
5695e3bf557SAyushi Smriti 
5705e3bf557SAyushi Smriti     switch (signalType)
571a3702c1fSVernon Mauery     {
572a3702c1fSVernon Mauery         case SmSignalSet::smPowerFaultLed:
573a3702c1fSVernon Mauery         case SmSignalSet::smSystemReadyLed:
574a3702c1fSVernon Mauery         case SmSignalSet::smIdentifyLed:
5755e3bf557SAyushi Smriti             switch (action)
576a3702c1fSVernon Mauery             {
577a3702c1fSVernon Mauery                 case SmActionSet::forceDeasserted:
578a3702c1fSVernon Mauery                 {
5794180cfe9SAlex Schendel                     lg2::info("case SmActionSet::forceDeasserted");
580a3702c1fSVernon Mauery 
5815e3bf557SAyushi Smriti                     retCode = ledStoreAndSet(signalType, std::string("Off"));
5825e3bf557SAyushi Smriti                     if (retCode != ccSuccess)
583a3702c1fSVernon Mauery                     {
5845e3bf557SAyushi Smriti                         return ipmi::response(retCode);
585a3702c1fSVernon Mauery                     }
586a3702c1fSVernon Mauery                     mtm.revertTimer.start(revertTimeOut);
587a3702c1fSVernon Mauery                 }
588a3702c1fSVernon Mauery                 break;
589a3702c1fSVernon Mauery                 case SmActionSet::forceAsserted:
590a3702c1fSVernon Mauery                 {
5914180cfe9SAlex Schendel                     lg2::info("case SmActionSet::forceAsserted");
592a3702c1fSVernon Mauery 
5935e3bf557SAyushi Smriti                     retCode = ledStoreAndSet(signalType, std::string("On"));
5945e3bf557SAyushi Smriti                     if (retCode != ccSuccess)
595a3702c1fSVernon Mauery                     {
5965e3bf557SAyushi Smriti                         return ipmi::response(retCode);
597a3702c1fSVernon Mauery                     }
598a3702c1fSVernon Mauery                     mtm.revertTimer.start(revertTimeOut);
5995e3bf557SAyushi Smriti                     if (SmSignalSet::smPowerFaultLed == signalType)
600a3702c1fSVernon Mauery                     {
601a3702c1fSVernon Mauery                         // Deassert "system ready"
6025e3bf557SAyushi Smriti                         retCode = ledStoreAndSet(SmSignalSet::smSystemReadyLed,
603a3702c1fSVernon Mauery                                                  std::string("Off"));
604a3702c1fSVernon Mauery                     }
6055e3bf557SAyushi Smriti                     else if (SmSignalSet::smSystemReadyLed == signalType)
606a3702c1fSVernon Mauery                     {
607a3702c1fSVernon Mauery                         // Deassert "fault led"
6085e3bf557SAyushi Smriti                         retCode = ledStoreAndSet(SmSignalSet::smPowerFaultLed,
609a3702c1fSVernon Mauery                                                  std::string("Off"));
610a3702c1fSVernon Mauery                     }
611a3702c1fSVernon Mauery                 }
612a3702c1fSVernon Mauery                 break;
613a3702c1fSVernon Mauery                 case SmActionSet::revert:
614a3702c1fSVernon Mauery                 {
6154180cfe9SAlex Schendel                     lg2::info("case SmActionSet::revert");
6165e3bf557SAyushi Smriti                     retCode = ledRevert(signalType);
617a3702c1fSVernon Mauery                 }
618a3702c1fSVernon Mauery                 break;
619a3702c1fSVernon Mauery                 default:
620a3702c1fSVernon Mauery                 {
6215e3bf557SAyushi Smriti                     return ipmi::responseInvalidFieldRequest();
622a3702c1fSVernon Mauery                 }
623a3702c1fSVernon Mauery             }
624a3702c1fSVernon Mauery             break;
625a3702c1fSVernon Mauery         case SmSignalSet::smFanPowerSpeed:
626a3702c1fSVernon Mauery         {
6275e3bf557SAyushi Smriti             if ((action == SmActionSet::forceAsserted) && (!pwmSpeed))
628a3702c1fSVernon Mauery             {
6295e3bf557SAyushi Smriti                 return ipmi::responseReqDataLenInvalid();
630a3702c1fSVernon Mauery             }
6315e3bf557SAyushi Smriti 
6325e3bf557SAyushi Smriti             if ((action == SmActionSet::forceAsserted) && (*pwmSpeed > 100))
6335e3bf557SAyushi Smriti             {
6345e3bf557SAyushi Smriti                 return ipmi::responseInvalidFieldRequest();
6355e3bf557SAyushi Smriti             }
6365e3bf557SAyushi Smriti 
637a3702c1fSVernon Mauery             uint8_t pwmValue = 0;
6385e3bf557SAyushi Smriti             switch (action)
639a3702c1fSVernon Mauery             {
640a3702c1fSVernon Mauery                 case SmActionSet::revert:
641a3702c1fSVernon Mauery                 {
642a3702c1fSVernon Mauery                     if (mtm.revertFanPWM)
643a3702c1fSVernon Mauery                     {
644a3702c1fSVernon Mauery                         ret = mtm.disablePidControlService(false);
645a3702c1fSVernon Mauery                         if (ret < 0)
646a3702c1fSVernon Mauery                         {
6475e3bf557SAyushi Smriti                             return ipmi::responseUnspecifiedError();
648a3702c1fSVernon Mauery                         }
649a3702c1fSVernon Mauery                         mtm.revertFanPWM = false;
650a3702c1fSVernon Mauery                     }
651a3702c1fSVernon Mauery                 }
652a3702c1fSVernon Mauery                 break;
653a3702c1fSVernon Mauery                 case SmActionSet::forceAsserted:
654a3702c1fSVernon Mauery                 {
6555e3bf557SAyushi Smriti                     pwmValue = *pwmSpeed;
656a3702c1fSVernon Mauery                 } // fall-through
657a3702c1fSVernon Mauery                 case SmActionSet::forceDeasserted:
658a3702c1fSVernon Mauery                 {
659a3702c1fSVernon Mauery                     if (!mtm.revertFanPWM)
660a3702c1fSVernon Mauery                     {
661a3702c1fSVernon Mauery                         ret = mtm.disablePidControlService(true);
662a3702c1fSVernon Mauery                         if (ret < 0)
663a3702c1fSVernon Mauery                         {
6645e3bf557SAyushi Smriti                             return ipmi::responseUnspecifiedError();
665a3702c1fSVernon Mauery                         }
666a3702c1fSVernon Mauery                         mtm.revertFanPWM = true;
667a3702c1fSVernon Mauery                     }
668a3702c1fSVernon Mauery                     mtm.revertTimer.start(revertTimeOut);
66990eb7876SAlex Schendel                     std::string pwmName, fanPwmInstancePath;
6704180cfe9SAlex Schendel                     if (!findPwmName(ctx, instance + 1, pwmName))
67190eb7876SAlex Schendel                     {
67290eb7876SAlex Schendel                         pwmName = "Pwm_" + std::to_string(instance + 1);
67390eb7876SAlex Schendel                     }
67490eb7876SAlex Schendel                     fanPwmInstancePath = fanPwmPath + pwmName;
675b37abfb2SPatrick Williams                     ret = mtm.setProperty(fanService, fanPwmInstancePath,
676b37abfb2SPatrick Williams                                           fanIntf, "Value",
677b37abfb2SPatrick Williams                                           static_cast<double>(pwmValue));
678a3702c1fSVernon Mauery                     if (ret < 0)
679a3702c1fSVernon Mauery                     {
6805e3bf557SAyushi Smriti                         return ipmi::responseUnspecifiedError();
681a3702c1fSVernon Mauery                     }
682a3702c1fSVernon Mauery                 }
683a3702c1fSVernon Mauery                 break;
684a3702c1fSVernon Mauery                 default:
685a3702c1fSVernon Mauery                 {
6865e3bf557SAyushi Smriti                     return ipmi::responseInvalidFieldRequest();
687a3702c1fSVernon Mauery                 }
688a3702c1fSVernon Mauery             }
689a3702c1fSVernon Mauery         }
690a3702c1fSVernon Mauery         break;
691d872a4a4SAyushi Smriti         case SmSignalSet::smSpeaker:
692d872a4a4SAyushi Smriti         {
6934180cfe9SAlex Schendel             lg2::info("Performing Speaker SmActionSet", "ACTION", lg2::dec,
6944180cfe9SAlex Schendel                       static_cast<uint8_t>(action));
695d872a4a4SAyushi Smriti             switch (action)
696d872a4a4SAyushi Smriti             {
697d872a4a4SAyushi Smriti                 case SmActionSet::forceAsserted:
698d872a4a4SAyushi Smriti                 {
699d872a4a4SAyushi Smriti                     char beepDevName[] = "/dev/input/event0";
700d872a4a4SAyushi Smriti                     if (mtm.mtmTestBeepFd != -1)
701d872a4a4SAyushi Smriti                     {
7024180cfe9SAlex Schendel                         lg2::info("mtm beep device is opened already!");
703d872a4a4SAyushi Smriti                         // returning success as already beep is in progress
704d872a4a4SAyushi Smriti                         return ipmi::response(retCode);
705d872a4a4SAyushi Smriti                     }
706d872a4a4SAyushi Smriti 
707b37abfb2SPatrick Williams                     if ((mtm.mtmTestBeepFd = ::open(beepDevName,
708b37abfb2SPatrick Williams                                                     O_RDWR | O_CLOEXEC)) < 0)
709d872a4a4SAyushi Smriti                     {
7104180cfe9SAlex Schendel                         lg2::error("Failed to open input device");
711d872a4a4SAyushi Smriti                         return ipmi::responseUnspecifiedError();
712d872a4a4SAyushi Smriti                     }
713d872a4a4SAyushi Smriti 
714d872a4a4SAyushi Smriti                     struct input_event event;
715d872a4a4SAyushi Smriti                     event.type = EV_SND;
716d872a4a4SAyushi Smriti                     event.code = SND_TONE;
717d872a4a4SAyushi Smriti                     event.value = 2000;
718d872a4a4SAyushi Smriti 
719d872a4a4SAyushi Smriti                     if (::write(mtm.mtmTestBeepFd, &event,
720d872a4a4SAyushi Smriti                                 sizeof(struct input_event)) !=
721d872a4a4SAyushi Smriti                         sizeof(struct input_event))
722d872a4a4SAyushi Smriti                     {
7234180cfe9SAlex Schendel                         lg2::error("Failed to write a tone sound event");
724d872a4a4SAyushi Smriti                         ::close(mtm.mtmTestBeepFd);
725d872a4a4SAyushi Smriti                         mtm.mtmTestBeepFd = -1;
726d872a4a4SAyushi Smriti                         return ipmi::responseUnspecifiedError();
727d872a4a4SAyushi Smriti                     }
728d872a4a4SAyushi Smriti                     mtm.revertTimer.start(revertTimeOut);
729d872a4a4SAyushi Smriti                 }
730d872a4a4SAyushi Smriti                 break;
731d872a4a4SAyushi Smriti                 case SmActionSet::revert:
732d872a4a4SAyushi Smriti                 case SmActionSet::forceDeasserted:
733d872a4a4SAyushi Smriti                 {
734d872a4a4SAyushi Smriti                     if (mtm.mtmTestBeepFd != -1)
735d872a4a4SAyushi Smriti                     {
736d872a4a4SAyushi Smriti                         ::close(mtm.mtmTestBeepFd);
737d872a4a4SAyushi Smriti                         mtm.mtmTestBeepFd = -1;
738d872a4a4SAyushi Smriti                     }
739d872a4a4SAyushi Smriti                 }
740d872a4a4SAyushi Smriti                 break;
741d872a4a4SAyushi Smriti                 default:
742d872a4a4SAyushi Smriti                 {
743d872a4a4SAyushi Smriti                     return ipmi::responseInvalidFieldRequest();
744d872a4a4SAyushi Smriti                 }
745d872a4a4SAyushi Smriti             }
746d872a4a4SAyushi Smriti         }
747d872a4a4SAyushi Smriti         break;
7483594c6d6SRichard Marian Thomaiyar         case SmSignalSet::smDiskFaultLed:
7493594c6d6SRichard Marian Thomaiyar         {
7503594c6d6SRichard Marian Thomaiyar             boost::system::error_code ec;
7513594c6d6SRichard Marian Thomaiyar             using objPaths = std::vector<std::string>;
7523594c6d6SRichard Marian Thomaiyar             std::string driveBasePath =
7533594c6d6SRichard Marian Thomaiyar                 "/xyz/openbmc_project/inventory/item/drive/";
7543594c6d6SRichard Marian Thomaiyar             static constexpr const char* driveLedIntf =
7553594c6d6SRichard Marian Thomaiyar                 "xyz.openbmc_project.Led.Group";
7563594c6d6SRichard Marian Thomaiyar             static constexpr const char* hsbpService =
7573594c6d6SRichard Marian Thomaiyar                 "xyz.openbmc_project.HsbpManager";
7583594c6d6SRichard Marian Thomaiyar 
7593594c6d6SRichard Marian Thomaiyar             auto driveList = ctx->bus->yield_method_call<objPaths>(
7603594c6d6SRichard Marian Thomaiyar                 ctx->yield, ec, "xyz.openbmc_project.ObjectMapper",
7613594c6d6SRichard Marian Thomaiyar                 "/xyz/openbmc_project/object_mapper",
7623594c6d6SRichard Marian Thomaiyar                 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths",
7633594c6d6SRichard Marian Thomaiyar                 driveBasePath, 0, std::array<const char*, 1>{driveLedIntf});
7643594c6d6SRichard Marian Thomaiyar             if (ec)
7653594c6d6SRichard Marian Thomaiyar             {
7664180cfe9SAlex Schendel                 lg2::error("Failed to query HSBP drive sub tree objects");
7673594c6d6SRichard Marian Thomaiyar                 return ipmi::responseUnspecifiedError();
7683594c6d6SRichard Marian Thomaiyar             }
769b37abfb2SPatrick Williams             std::string driveObjPath = driveBasePath + "Drive_" +
770b37abfb2SPatrick Williams                                        std::to_string(instance + 1);
7713594c6d6SRichard Marian Thomaiyar             if (std::find(driveList.begin(), driveList.end(), driveObjPath) ==
7723594c6d6SRichard Marian Thomaiyar                 driveList.end())
7733594c6d6SRichard Marian Thomaiyar             {
7743594c6d6SRichard Marian Thomaiyar                 return ipmi::responseInvalidFieldRequest();
7753594c6d6SRichard Marian Thomaiyar             }
7763594c6d6SRichard Marian Thomaiyar             bool driveLedState = false;
7773594c6d6SRichard Marian Thomaiyar             switch (action)
7783594c6d6SRichard Marian Thomaiyar             {
7793594c6d6SRichard Marian Thomaiyar                 case SmActionSet::forceAsserted:
7803594c6d6SRichard Marian Thomaiyar                 {
7813594c6d6SRichard Marian Thomaiyar                     driveLedState = true;
7823594c6d6SRichard Marian Thomaiyar                 }
7833594c6d6SRichard Marian Thomaiyar                 break;
7843594c6d6SRichard Marian Thomaiyar                 case SmActionSet::revert:
7853594c6d6SRichard Marian Thomaiyar                 {
7863594c6d6SRichard Marian Thomaiyar                     driveLedState = false;
7873594c6d6SRichard Marian Thomaiyar                 }
7883594c6d6SRichard Marian Thomaiyar                 break;
7893594c6d6SRichard Marian Thomaiyar                 case SmActionSet::forceDeasserted:
7903594c6d6SRichard Marian Thomaiyar                 {
7913594c6d6SRichard Marian Thomaiyar                     driveLedState = false;
7923594c6d6SRichard Marian Thomaiyar                 }
7933594c6d6SRichard Marian Thomaiyar                 break;
7943594c6d6SRichard Marian Thomaiyar                 default:
7953594c6d6SRichard Marian Thomaiyar                 {
7963594c6d6SRichard Marian Thomaiyar                     return ipmi::responseInvalidFieldRequest();
7973594c6d6SRichard Marian Thomaiyar                 }
7983594c6d6SRichard Marian Thomaiyar             }
7993594c6d6SRichard Marian Thomaiyar             ret = mtm.setProperty(hsbpService, driveObjPath, driveLedIntf,
8003594c6d6SRichard Marian Thomaiyar                                   "Asserted", driveLedState);
8013594c6d6SRichard Marian Thomaiyar             if (ret < 0)
8023594c6d6SRichard Marian Thomaiyar             {
8033594c6d6SRichard Marian Thomaiyar                 return ipmi::responseUnspecifiedError();
8043594c6d6SRichard Marian Thomaiyar             }
8053594c6d6SRichard Marian Thomaiyar         }
8063594c6d6SRichard Marian Thomaiyar         break;
807a3702c1fSVernon Mauery         default:
808a3702c1fSVernon Mauery         {
8095e3bf557SAyushi Smriti             return ipmi::responseInvalidFieldRequest();
810a3702c1fSVernon Mauery         }
811a3702c1fSVernon Mauery     }
8124cc10159SRichard Marian Thomaiyar     if (retCode == ccSuccess)
8134cc10159SRichard Marian Thomaiyar     {
814357ddc74SRichard Marian Thomaiyar         resetMtmTimer(ctx);
8154cc10159SRichard Marian Thomaiyar     }
8165e3bf557SAyushi Smriti     return ipmi::response(retCode);
817a3702c1fSVernon Mauery }
818a3702c1fSVernon Mauery 
mtmKeepAlive(ipmi::Context::ptr ctx,uint8_t reserved,const std::array<char,5> & intentionalSignature)819357ddc74SRichard Marian Thomaiyar ipmi::RspType<> mtmKeepAlive(ipmi::Context::ptr ctx, uint8_t reserved,
820666dd01cSRichard Marian Thomaiyar                              const std::array<char, 5>& intentionalSignature)
821666dd01cSRichard Marian Thomaiyar {
822e0511e5fSAyushi Smriti     // mfg filter logic is used to allow MTM keep alive command only in
823e0511e5fSAyushi Smriti     // manfacturing mode
824e0511e5fSAyushi Smriti 
825666dd01cSRichard Marian Thomaiyar     constexpr std::array<char, 5> signatureOk = {'I', 'N', 'T', 'E', 'L'};
826666dd01cSRichard Marian Thomaiyar     if (intentionalSignature != signatureOk || reserved != 0)
827666dd01cSRichard Marian Thomaiyar     {
828666dd01cSRichard Marian Thomaiyar         return ipmi::responseInvalidFieldRequest();
829666dd01cSRichard Marian Thomaiyar     }
830357ddc74SRichard Marian Thomaiyar     return ipmi::response(resetMtmTimer(ctx));
831666dd01cSRichard Marian Thomaiyar }
832666dd01cSRichard Marian Thomaiyar 
makeCmdKey(unsigned int netFn,unsigned int cmd)833e0511e5fSAyushi Smriti static constexpr unsigned int makeCmdKey(unsigned int netFn, unsigned int cmd)
834e0511e5fSAyushi Smriti {
835e0511e5fSAyushi Smriti     return (netFn << 8) | cmd;
836e0511e5fSAyushi Smriti }
837e0511e5fSAyushi Smriti 
mfgFilterMessage(ipmi::message::Request::ptr request)83885feb130SYong Li ipmi::Cc mfgFilterMessage(ipmi::message::Request::ptr request)
83985feb130SYong Li {
840e0511e5fSAyushi Smriti     // Restricted commands, must be executed only in Manufacturing mode
841e0511e5fSAyushi Smriti     switch (makeCmdKey(request->ctx->netFn, request->ctx->cmd))
84285feb130SYong Li     {
84380d4d5f9SMatt Simmering         // i2c controller write read command needs additional checking
844e0511e5fSAyushi Smriti         case makeCmdKey(ipmi::netFnApp, ipmi::app::cmdMasterWriteRead):
84585feb130SYong Li             if (request->payload.size() > 4)
84685feb130SYong Li             {
847ae13ac62SRichard Marian Thomaiyar                 // Allow write data count > 1 only in Special mode
848ae13ac62SRichard Marian Thomaiyar                 if (mtm.getMfgMode() == SpecialMode::none)
84985feb130SYong Li                 {
85085feb130SYong Li                     return ipmi::ccInsufficientPrivilege;
85185feb130SYong Li                 }
85285feb130SYong Li             }
8532d4a0198Sjayaprakash Mutyala             return ipmi::ccSuccess;
854e0511e5fSAyushi Smriti         case makeCmdKey(ipmi::netFnOemOne,
855e0511e5fSAyushi Smriti                         ipmi::intel::general::cmdGetSmSignal):
856e0511e5fSAyushi Smriti         case makeCmdKey(ipmi::netFnOemOne,
857e0511e5fSAyushi Smriti                         ipmi::intel::general::cmdSetSmSignal):
858e0511e5fSAyushi Smriti         case makeCmdKey(ipmi::netFnOemOne,
859e0511e5fSAyushi Smriti                         ipmi::intel::general::cmdMtmKeepAlive):
860e0511e5fSAyushi Smriti         case makeCmdKey(ipmi::netFnOemOne,
861e0511e5fSAyushi Smriti                         ipmi::intel::general::cmdSetManufacturingData):
862e0511e5fSAyushi Smriti         case makeCmdKey(ipmi::netFnOemOne,
863e0511e5fSAyushi Smriti                         ipmi::intel::general::cmdGetManufacturingData):
86427d2356eSVernon Mauery         case makeCmdKey(ipmi::intel::netFnGeneral,
86527d2356eSVernon Mauery                         ipmi::intel::general::cmdSetFITcLayout):
86606584cd0SArun P. Mohanan         case makeCmdKey(ipmi::netFnOemOne,
86706584cd0SArun P. Mohanan                         ipmi::intel::general::cmdMTMBMCFeatureControl):
868e0511e5fSAyushi Smriti         case makeCmdKey(ipmi::netFnStorage, ipmi::storage::cmdWriteFruData):
8699a13daaeSAppaRao Puli         case makeCmdKey(ipmi::netFnOemTwo, ipmi::intel::platform::cmdClearCMOS):
87085feb130SYong Li 
871ae13ac62SRichard Marian Thomaiyar             // Check for Special mode
872ae13ac62SRichard Marian Thomaiyar             if (mtm.getMfgMode() == SpecialMode::none)
873e0511e5fSAyushi Smriti             {
874e0511e5fSAyushi Smriti                 return ipmi::ccInvalidCommand;
875e0511e5fSAyushi Smriti             }
8762d4a0198Sjayaprakash Mutyala             return ipmi::ccSuccess;
8772d4a0198Sjayaprakash Mutyala         case makeCmdKey(ipmi::netFnStorage, ipmi::storage::cmdDeleteSelEntry):
8782d4a0198Sjayaprakash Mutyala         {
8792d4a0198Sjayaprakash Mutyala             return ipmi::ccInvalidCommand;
8802d4a0198Sjayaprakash Mutyala         }
881e0511e5fSAyushi Smriti     }
88285feb130SYong Li     return ipmi::ccSuccess;
88385feb130SYong Li }
88485feb130SYong Li 
8851f0839c2SRichard Marian Thomaiyar static constexpr uint8_t maxEthSize = 6;
8861f0839c2SRichard Marian Thomaiyar static constexpr uint8_t maxSupportedEth = 3;
8871f0839c2SRichard Marian Thomaiyar static constexpr const char* factoryEthAddrBaseFileName =
8881f0839c2SRichard Marian Thomaiyar     "/var/sofs/factory-settings/network/mac/eth";
8891f0839c2SRichard Marian Thomaiyar 
findFruDevice(ipmi::Context::ptr & ctx,uint64_t & macOffset,uint64_t & busNum,uint64_t & address)890097497fbSAlex Schendel bool findFruDevice(ipmi::Context::ptr& ctx, uint64_t& macOffset,
891ad129c68SZhikui Ren                    uint64_t& busNum, uint64_t& address)
892ad129c68SZhikui Ren {
893097497fbSAlex Schendel     boost::system::error_code ec{};
894097497fbSAlex Schendel     ObjectValueTree obj;
895ad129c68SZhikui Ren 
896ad129c68SZhikui Ren     // GetAll the objects under service FruDevice
897097497fbSAlex Schendel     ec = getManagedObjects(ctx, "xyz.openbmc_project.EntityManager",
898097497fbSAlex Schendel                            "/xyz/openbmc_project/inventory", obj);
899ad129c68SZhikui Ren     if (ec)
900ad129c68SZhikui Ren     {
9014180cfe9SAlex Schendel         lg2::error("GetManagedObjects failed", "ERROR", ec.message().c_str());
902ad129c68SZhikui Ren         return false;
903ad129c68SZhikui Ren     }
904ad129c68SZhikui Ren 
905ad129c68SZhikui Ren     for (const auto& [path, fru] : obj)
906ad129c68SZhikui Ren     {
907ad129c68SZhikui Ren         for (const auto& [intf, propMap] : fru)
908ad129c68SZhikui Ren         {
909ad129c68SZhikui Ren             if (intf == "xyz.openbmc_project.Inventory.Item.Board.Motherboard")
910ad129c68SZhikui Ren             {
911ad129c68SZhikui Ren                 auto findBus = propMap.find("FruBus");
912ad129c68SZhikui Ren                 auto findAddress = propMap.find("FruAddress");
913ad129c68SZhikui Ren                 auto findMacOffset = propMap.find("MacOffset");
914ad129c68SZhikui Ren                 if (findBus == propMap.end() || findAddress == propMap.end() ||
915ad129c68SZhikui Ren                     findMacOffset == propMap.end())
916ad129c68SZhikui Ren                 {
917ad129c68SZhikui Ren                     continue;
918ad129c68SZhikui Ren                 }
919ad129c68SZhikui Ren 
920ad129c68SZhikui Ren                 auto fruBus = std::get_if<uint64_t>(&findBus->second);
921ad129c68SZhikui Ren                 auto fruAddress = std::get_if<uint64_t>(&findAddress->second);
922ad129c68SZhikui Ren                 auto macFruOffset =
923ad129c68SZhikui Ren                     std::get_if<uint64_t>(&findMacOffset->second);
924ad129c68SZhikui Ren                 if (!fruBus || !fruAddress || !macFruOffset)
925ad129c68SZhikui Ren                 {
9264180cfe9SAlex Schendel                     lg2::info("ERROR: MotherBoard FRU config data type "
9274180cfe9SAlex Schendel                               "invalid, not used");
928ad129c68SZhikui Ren                     return false;
929ad129c68SZhikui Ren                 }
930ad129c68SZhikui Ren                 busNum = *fruBus;
931ad129c68SZhikui Ren                 address = *fruAddress;
932ad129c68SZhikui Ren                 macOffset = *macFruOffset;
933ad129c68SZhikui Ren                 return true;
934ad129c68SZhikui Ren             }
935ad129c68SZhikui Ren         }
936ad129c68SZhikui Ren     }
937ad129c68SZhikui Ren     return false;
938ad129c68SZhikui Ren }
939ad129c68SZhikui Ren 
94098fa87e6SZhikui Ren static constexpr uint64_t fruEnd = 0xff;
94198fa87e6SZhikui Ren // write rolls over within current page, need to keep mac within a page
94298fa87e6SZhikui Ren static constexpr uint64_t fruPageSize = 0x8;
94398fa87e6SZhikui Ren // MAC record struct: HEADER, MAC DATA, CheckSum
94498fa87e6SZhikui Ren static constexpr uint64_t macRecordSize = maxEthSize + 2;
94598fa87e6SZhikui Ren static_assert(fruPageSize >= macRecordSize,
94698fa87e6SZhikui Ren               "macRecordSize greater than eeprom page size");
94798fa87e6SZhikui Ren static constexpr uint8_t macHeader = 0x40;
94898fa87e6SZhikui Ren // Calculate new checksum for fru info area
calculateChecksum(std::vector<uint8_t>::const_iterator iter,std::vector<uint8_t>::const_iterator end)94998fa87e6SZhikui Ren static uint8_t calculateChecksum(std::vector<uint8_t>::const_iterator iter,
95098fa87e6SZhikui Ren                                  std::vector<uint8_t>::const_iterator end)
95198fa87e6SZhikui Ren {
95298fa87e6SZhikui Ren     constexpr int checksumMod = 256;
95398fa87e6SZhikui Ren     uint8_t sum = std::accumulate(iter, end, static_cast<uint8_t>(0));
95498fa87e6SZhikui Ren     return (checksumMod - sum) % checksumMod;
95598fa87e6SZhikui Ren }
95698fa87e6SZhikui Ren 
readMacFromFru(ipmi::Context::ptr ctx,uint8_t macIndex,std::array<uint8_t,maxEthSize> & ethData)957ad129c68SZhikui Ren bool readMacFromFru(ipmi::Context::ptr ctx, uint8_t macIndex,
958ad129c68SZhikui Ren                     std::array<uint8_t, maxEthSize>& ethData)
959ad129c68SZhikui Ren {
960ad129c68SZhikui Ren     uint64_t macOffset = fruEnd;
961ad129c68SZhikui Ren     uint64_t fruBus = 0;
962ad129c68SZhikui Ren     uint64_t fruAddress = 0;
963ad129c68SZhikui Ren 
964097497fbSAlex Schendel     if (findFruDevice(ctx, macOffset, fruBus, fruAddress))
965ad129c68SZhikui Ren     {
9664180cfe9SAlex Schendel         lg2::info("Found mac fru", "BUS", lg2::dec,
9674180cfe9SAlex Schendel                   static_cast<uint8_t>(fruBus), "ADDRESS", lg2::dec,
9684180cfe9SAlex Schendel                   static_cast<uint8_t>(fruAddress));
969ad129c68SZhikui Ren 
97098fa87e6SZhikui Ren         if (macOffset % fruPageSize)
97198fa87e6SZhikui Ren         {
97298fa87e6SZhikui Ren             macOffset = (macOffset / fruPageSize + 1) * fruPageSize;
97398fa87e6SZhikui Ren         }
97498fa87e6SZhikui Ren         macOffset += macIndex * fruPageSize;
97598fa87e6SZhikui Ren         if ((macOffset + macRecordSize) > fruEnd)
976ad129c68SZhikui Ren         {
9774180cfe9SAlex Schendel             lg2::error("ERROR: read fru mac failed, offset invalid");
978ad129c68SZhikui Ren             return false;
979ad129c68SZhikui Ren         }
980ad129c68SZhikui Ren         std::vector<uint8_t> writeData;
98198fa87e6SZhikui Ren         writeData.push_back(static_cast<uint8_t>(macOffset));
98298fa87e6SZhikui Ren         std::vector<uint8_t> readBuf(macRecordSize);
983ad129c68SZhikui Ren         std::string i2cBus = "/dev/i2c-" + std::to_string(fruBus);
984b37abfb2SPatrick Williams         ipmi::Cc retI2C = ipmi::i2cWriteRead(i2cBus, fruAddress, writeData,
985b37abfb2SPatrick Williams                                              readBuf);
986ad129c68SZhikui Ren         if (retI2C == ipmi::ccSuccess)
987ad129c68SZhikui Ren         {
98898fa87e6SZhikui Ren             uint8_t cs = calculateChecksum(readBuf.cbegin(), readBuf.cend());
98998fa87e6SZhikui Ren             if (cs == 0)
99098fa87e6SZhikui Ren             {
99198fa87e6SZhikui Ren                 std::copy(++readBuf.begin(), --readBuf.end(), ethData.data());
992ad129c68SZhikui Ren                 return true;
993ad129c68SZhikui Ren             }
994ad129c68SZhikui Ren         }
99598fa87e6SZhikui Ren     }
996ad129c68SZhikui Ren     return false;
997ad129c68SZhikui Ren }
998ad129c68SZhikui Ren 
writeMacToFru(ipmi::Context::ptr ctx,uint8_t macIndex,std::array<uint8_t,maxEthSize> & ethData)999ad129c68SZhikui Ren ipmi::Cc writeMacToFru(ipmi::Context::ptr ctx, uint8_t macIndex,
1000ad129c68SZhikui Ren                        std::array<uint8_t, maxEthSize>& ethData)
1001ad129c68SZhikui Ren {
1002ad129c68SZhikui Ren     uint64_t macOffset = fruEnd;
1003ad129c68SZhikui Ren     uint64_t fruBus = 0;
1004ad129c68SZhikui Ren     uint64_t fruAddress = 0;
1005ad129c68SZhikui Ren 
1006097497fbSAlex Schendel     if (findFruDevice(ctx, macOffset, fruBus, fruAddress))
1007ad129c68SZhikui Ren     {
10084180cfe9SAlex Schendel         lg2::info("Found mac fru", "BUS", lg2::dec,
10094180cfe9SAlex Schendel                   static_cast<uint8_t>(fruBus), "ADDRESS", lg2::dec,
10104180cfe9SAlex Schendel                   static_cast<uint8_t>(fruAddress));
1011ad129c68SZhikui Ren 
101298fa87e6SZhikui Ren         if (macOffset % fruPageSize)
101398fa87e6SZhikui Ren         {
101498fa87e6SZhikui Ren             macOffset = (macOffset / fruPageSize + 1) * fruPageSize;
101598fa87e6SZhikui Ren         }
101698fa87e6SZhikui Ren         macOffset += macIndex * fruPageSize;
101798fa87e6SZhikui Ren         if ((macOffset + macRecordSize) > fruEnd)
1018ad129c68SZhikui Ren         {
10194180cfe9SAlex Schendel             lg2::error("ERROR: write mac fru failed, offset invalid.");
1020ad129c68SZhikui Ren             return ipmi::ccParmOutOfRange;
1021ad129c68SZhikui Ren         }
1022ad129c68SZhikui Ren         std::vector<uint8_t> writeData;
102398fa87e6SZhikui Ren         writeData.reserve(macRecordSize + 1); // include start location
102498fa87e6SZhikui Ren         writeData.push_back(static_cast<uint8_t>(macOffset));
102598fa87e6SZhikui Ren         writeData.push_back(macHeader);
1026ad129c68SZhikui Ren         std::for_each(ethData.cbegin(), ethData.cend(),
1027ad129c68SZhikui Ren                       [&](uint8_t i) { writeData.push_back(i); });
1028b37abfb2SPatrick Williams         uint8_t macCheckSum = calculateChecksum(++writeData.cbegin(),
1029b37abfb2SPatrick Williams                                                 writeData.cend());
103098fa87e6SZhikui Ren         writeData.push_back(macCheckSum);
1031ad129c68SZhikui Ren 
1032ad129c68SZhikui Ren         std::string i2cBus = "/dev/i2c-" + std::to_string(fruBus);
1033ad129c68SZhikui Ren         std::vector<uint8_t> readBuf;
1034b37abfb2SPatrick Williams         ipmi::Cc ret = ipmi::i2cWriteRead(i2cBus, fruAddress, writeData,
1035b37abfb2SPatrick Williams                                           readBuf);
103698fa87e6SZhikui Ren 
10370408e79eSZhikui Ren         // prepare for read to detect chip is write protected
10380408e79eSZhikui Ren         writeData.resize(1);
10390408e79eSZhikui Ren         readBuf.resize(maxEthSize + 1); // include macHeader
10400408e79eSZhikui Ren 
1041ad129c68SZhikui Ren         switch (ret)
1042ad129c68SZhikui Ren         {
1043ad129c68SZhikui Ren             case ipmi::ccSuccess:
104498fa87e6SZhikui Ren                 // Wait for internal write cycle to complete
104598fa87e6SZhikui Ren                 // example: ATMEL 24c0x chip has Twr spec as 5ms
104698fa87e6SZhikui Ren 
104798fa87e6SZhikui Ren                 // Ideally we want yield wait, but currently following code
104898fa87e6SZhikui Ren                 // crash with "thread not supported"
104998fa87e6SZhikui Ren                 // boost::asio::deadline_timer timer(
105098fa87e6SZhikui Ren                 //    boost::asio::get_associated_executor(ctx->yield),
105198fa87e6SZhikui Ren                 //    boost::posix_time::seconds(1));
105298fa87e6SZhikui Ren                 // timer.async_wait(ctx->yield);
105398fa87e6SZhikui Ren                 // use usleep as temp WA
105498fa87e6SZhikui Ren                 usleep(5000);
105598fa87e6SZhikui Ren                 if (ipmi::i2cWriteRead(i2cBus, fruAddress, writeData,
105698fa87e6SZhikui Ren                                        readBuf) == ipmi::ccSuccess)
1057ad129c68SZhikui Ren                 {
1058ad129c68SZhikui Ren                     if (std::equal(ethData.begin(), ethData.end(),
105998fa87e6SZhikui Ren                                    ++readBuf.begin())) // skip macHeader
1060ad129c68SZhikui Ren                     {
1061ad129c68SZhikui Ren                         return ipmi::ccSuccess;
1062ad129c68SZhikui Ren                     }
10634180cfe9SAlex Schendel                     lg2::info("INFO: write mac fru verify failed, fru may be "
10644180cfe9SAlex Schendel                               "write protected.");
1065ad129c68SZhikui Ren                 }
1066ad129c68SZhikui Ren                 return ipmi::ccCommandNotAvailable;
10670408e79eSZhikui Ren             default:
10680408e79eSZhikui Ren                 if (ipmi::i2cWriteRead(i2cBus, fruAddress, writeData,
10690408e79eSZhikui Ren                                        readBuf) == ipmi::ccSuccess)
10700408e79eSZhikui Ren                 {
10714180cfe9SAlex Schendel                     lg2::info("INFO: write mac fru failed, but successfully "
10724180cfe9SAlex Schendel                               "read from fru, fru may be write protected.");
10730408e79eSZhikui Ren                     return ipmi::ccCommandNotAvailable;
10740408e79eSZhikui Ren                 }
10750408e79eSZhikui Ren                 else // assume failure is due to no eeprom on board
10760408e79eSZhikui Ren                 {
10774180cfe9SAlex Schendel                     lg2::error("ERROR: write mac fru failed, assume no eeprom "
10784180cfe9SAlex Schendel                                "is available.");
10790408e79eSZhikui Ren                 }
1080ad129c68SZhikui Ren                 break;
1081ad129c68SZhikui Ren         }
1082ad129c68SZhikui Ren     }
1083ad129c68SZhikui Ren     // no FRU eeprom found
1084ad129c68SZhikui Ren     return ipmi::ccDestinationUnavailable;
1085ad129c68SZhikui Ren }
1086ad129c68SZhikui Ren 
setManufacturingData(ipmi::Context::ptr ctx,uint8_t dataType,std::array<uint8_t,maxEthSize> ethData)1087357ddc74SRichard Marian Thomaiyar ipmi::RspType<> setManufacturingData(ipmi::Context::ptr ctx, uint8_t dataType,
10881f0839c2SRichard Marian Thomaiyar                                      std::array<uint8_t, maxEthSize> ethData)
10891f0839c2SRichard Marian Thomaiyar {
1090ad129c68SZhikui Ren     // mfg filter logic will restrict this command executing only in mfg
1091ad129c68SZhikui Ren     // mode.
10921f0839c2SRichard Marian Thomaiyar     if (dataType >= maxSupportedEth)
10931f0839c2SRichard Marian Thomaiyar     {
10941f0839c2SRichard Marian Thomaiyar         return ipmi::responseParmOutOfRange();
10951f0839c2SRichard Marian Thomaiyar     }
10961f0839c2SRichard Marian Thomaiyar 
1097ad129c68SZhikui Ren     ipmi::Cc ret = writeMacToFru(ctx, dataType, ethData);
1098ad129c68SZhikui Ren     if (ret != ipmi::ccDestinationUnavailable)
1099ad129c68SZhikui Ren     {
1100ad129c68SZhikui Ren         resetMtmTimer(ctx);
1101ad129c68SZhikui Ren         return response(ret);
1102ad129c68SZhikui Ren     }
1103ad129c68SZhikui Ren 
11041f0839c2SRichard Marian Thomaiyar     constexpr uint8_t ethAddrStrSize =
11051f0839c2SRichard Marian Thomaiyar         19; // XX:XX:XX:XX:XX:XX + \n + null termination;
11061f0839c2SRichard Marian Thomaiyar     std::vector<uint8_t> buff(ethAddrStrSize);
11071f0839c2SRichard Marian Thomaiyar     std::snprintf(reinterpret_cast<char*>(buff.data()), ethAddrStrSize,
11081f0839c2SRichard Marian Thomaiyar                   "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx\n", ethData.at(0),
11091f0839c2SRichard Marian Thomaiyar                   ethData.at(1), ethData.at(2), ethData.at(3), ethData.at(4),
11101f0839c2SRichard Marian Thomaiyar                   ethData.at(5));
11111f0839c2SRichard Marian Thomaiyar     std::ofstream oEthFile(factoryEthAddrBaseFileName +
11121f0839c2SRichard Marian Thomaiyar                                std::to_string(dataType),
11131f0839c2SRichard Marian Thomaiyar                            std::ofstream::out);
11141f0839c2SRichard Marian Thomaiyar     if (!oEthFile.good())
11151f0839c2SRichard Marian Thomaiyar     {
11161f0839c2SRichard Marian Thomaiyar         return ipmi::responseUnspecifiedError();
11171f0839c2SRichard Marian Thomaiyar     }
11181f0839c2SRichard Marian Thomaiyar 
11191f0839c2SRichard Marian Thomaiyar     oEthFile << reinterpret_cast<char*>(buff.data());
11206d83e4eeSJohnathan Mantey     oEthFile.flush();
11211f0839c2SRichard Marian Thomaiyar     oEthFile.close();
11221f0839c2SRichard Marian Thomaiyar 
1123357ddc74SRichard Marian Thomaiyar     resetMtmTimer(ctx);
11241f0839c2SRichard Marian Thomaiyar     return ipmi::responseSuccess();
11251f0839c2SRichard Marian Thomaiyar }
11261f0839c2SRichard Marian Thomaiyar 
11271f0839c2SRichard Marian Thomaiyar ipmi::RspType<uint8_t, std::array<uint8_t, maxEthSize>>
getManufacturingData(ipmi::Context::ptr ctx,uint8_t dataType)1128357ddc74SRichard Marian Thomaiyar     getManufacturingData(ipmi::Context::ptr ctx, uint8_t dataType)
11291f0839c2SRichard Marian Thomaiyar {
1130ad129c68SZhikui Ren     // mfg filter logic will restrict this command executing only in mfg
1131ad129c68SZhikui Ren     // mode.
11321f0839c2SRichard Marian Thomaiyar     if (dataType >= maxSupportedEth)
11331f0839c2SRichard Marian Thomaiyar     {
11341f0839c2SRichard Marian Thomaiyar         return ipmi::responseParmOutOfRange();
11351f0839c2SRichard Marian Thomaiyar     }
11361f0839c2SRichard Marian Thomaiyar     std::array<uint8_t, maxEthSize> ethData{0};
11371f0839c2SRichard Marian Thomaiyar     constexpr uint8_t invalidData = 0;
11381f0839c2SRichard Marian Thomaiyar     constexpr uint8_t validData = 1;
11391f0839c2SRichard Marian Thomaiyar 
11401f0839c2SRichard Marian Thomaiyar     std::ifstream iEthFile(factoryEthAddrBaseFileName +
11411f0839c2SRichard Marian Thomaiyar                                std::to_string(dataType),
11421f0839c2SRichard Marian Thomaiyar                            std::ifstream::in);
11431f0839c2SRichard Marian Thomaiyar     if (!iEthFile.good())
11441f0839c2SRichard Marian Thomaiyar     {
1145ad129c68SZhikui Ren         if (readMacFromFru(ctx, dataType, ethData))
1146ad129c68SZhikui Ren         {
1147ad129c68SZhikui Ren             resetMtmTimer(ctx);
1148ad129c68SZhikui Ren             return ipmi::responseSuccess(validData, ethData);
1149ad129c68SZhikui Ren         }
11501f0839c2SRichard Marian Thomaiyar         return ipmi::responseSuccess(invalidData, ethData);
11511f0839c2SRichard Marian Thomaiyar     }
11521f0839c2SRichard Marian Thomaiyar     std::string ethStr;
11531f0839c2SRichard Marian Thomaiyar     iEthFile >> ethStr;
11541f0839c2SRichard Marian Thomaiyar     uint8_t* data = ethData.data();
11551f0839c2SRichard Marian Thomaiyar     std::sscanf(ethStr.c_str(), "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx",
11561f0839c2SRichard Marian Thomaiyar                 data, (data + 1), (data + 2), (data + 3), (data + 4),
11571f0839c2SRichard Marian Thomaiyar                 (data + 5));
11581f0839c2SRichard Marian Thomaiyar 
1159357ddc74SRichard Marian Thomaiyar     resetMtmTimer(ctx);
11601f0839c2SRichard Marian Thomaiyar     return ipmi::responseSuccess(validData, ethData);
11611f0839c2SRichard Marian Thomaiyar }
11621f0839c2SRichard Marian Thomaiyar 
116380d4d5f9SMatt Simmering /** @brief implements slot controller write read IPMI command which can be used
1164ad129c68SZhikui Ren  * for low-level I2C/SMBus write, read or write-read access for PCIE slots
11657acb2d27SV-Sanjana  * @param reserved - skip 3 bit
11667acb2d27SV-Sanjana  * @param muxType - mux type
1167f267a67dSYong Li  * @param addressType - address type
1168f267a67dSYong Li  * @param bbSlotNum - baseboard slot number
1169f267a67dSYong Li  * @param riserSlotNum - riser slot number
1170f267a67dSYong Li  * @param reserved2 - skip 2 bit
117180d4d5f9SMatt Simmering  * @param targetAddr - target address
1172f267a67dSYong Li  * @param readCount - number of bytes to be read
1173f267a67dSYong Li  * @param writeData - data to be written
1174f267a67dSYong Li  *
1175f267a67dSYong Li  * @returns IPMI completion code plus response data
1176f267a67dSYong Li  */
11777acb2d27SV-Sanjana 
appSlotI2CControllerWriteRead(uint3_t reserved,uint3_t muxType,uint2_t addressType,uint3_t bbSlotNum,uint3_t riserSlotNum,uint2_t reserved2,uint8_t targetAddr,uint8_t readCount,std::vector<uint8_t> writeData)117880d4d5f9SMatt Simmering ipmi::RspType<std::vector<uint8_t>> appSlotI2CControllerWriteRead(
11797acb2d27SV-Sanjana     uint3_t reserved, uint3_t muxType, uint2_t addressType, uint3_t bbSlotNum,
118080d4d5f9SMatt Simmering     uint3_t riserSlotNum, uint2_t reserved2, uint8_t targetAddr,
1181f267a67dSYong Li     uint8_t readCount, std::vector<uint8_t> writeData)
1182f267a67dSYong Li {
1183fc3bc381SVernon Mauery     if (reserved || reserved2)
1184fc3bc381SVernon Mauery     {
1185fc3bc381SVernon Mauery         return ipmi::responseInvalidFieldRequest();
1186fc3bc381SVernon Mauery     }
1187f267a67dSYong Li     const size_t writeCount = writeData.size();
1188f267a67dSYong Li     std::string i2cBus;
1189f267a67dSYong Li     if (addressType == slotAddressTypeBus)
1190f267a67dSYong Li     {
11917acb2d27SV-Sanjana         std::string path = "/dev/i2c-mux/";
11927acb2d27SV-Sanjana         if (muxType == bbRiserMux)
11937acb2d27SV-Sanjana         {
11947acb2d27SV-Sanjana             path += "Riser_" + std::to_string(static_cast<uint8_t>(bbSlotNum)) +
1195f267a67dSYong Li                     "_Mux/Pcie_Slot_" +
1196f267a67dSYong Li                     std::to_string(static_cast<uint8_t>(riserSlotNum));
11977acb2d27SV-Sanjana         }
11987acb2d27SV-Sanjana         else if (muxType == leftRiserMux)
11997acb2d27SV-Sanjana         {
12007acb2d27SV-Sanjana             path += "Left_Riser_Mux/Slot_" +
12017acb2d27SV-Sanjana                     std::to_string(static_cast<uint8_t>(riserSlotNum));
12027acb2d27SV-Sanjana         }
12037acb2d27SV-Sanjana         else if (muxType == rightRiserMux)
12047acb2d27SV-Sanjana         {
12057acb2d27SV-Sanjana             path += "Right_Riser_Mux/Slot_" +
12067acb2d27SV-Sanjana                     std::to_string(static_cast<uint8_t>(riserSlotNum));
12077acb2d27SV-Sanjana         }
12087acb2d27SV-Sanjana         else if (muxType == pcieMux)
12097acb2d27SV-Sanjana         {
12107acb2d27SV-Sanjana             path += "PCIe_Mux/Slot_" +
12117acb2d27SV-Sanjana                     std::to_string(static_cast<uint8_t>(riserSlotNum));
12127acb2d27SV-Sanjana         }
12137acb2d27SV-Sanjana         else if (muxType == hsbpMux)
12147acb2d27SV-Sanjana         {
12157acb2d27SV-Sanjana             path += "HSBP_Mux/Slot" +
12167acb2d27SV-Sanjana                     std::to_string(static_cast<uint8_t>(riserSlotNum));
12177acb2d27SV-Sanjana         }
12187acb2d27SV-Sanjana         phosphor::logging::log<phosphor::logging::level::DEBUG>(
12197acb2d27SV-Sanjana             ("Path is: " + path).c_str());
1220f267a67dSYong Li         if (std::filesystem::exists(path) && std::filesystem::is_symlink(path))
1221f267a67dSYong Li         {
1222f267a67dSYong Li             i2cBus = std::filesystem::read_symlink(path);
1223f267a67dSYong Li         }
1224f267a67dSYong Li         else
1225f267a67dSYong Li         {
122680d4d5f9SMatt Simmering             lg2::error("Controller write read command: Cannot get BusID");
1227f267a67dSYong Li             return ipmi::responseInvalidFieldRequest();
1228f267a67dSYong Li         }
1229f267a67dSYong Li     }
1230f267a67dSYong Li     else if (addressType == slotAddressTypeUniqueid)
1231f267a67dSYong Li     {
1232f267a67dSYong Li         i2cBus = "/dev/i2c-" +
1233f267a67dSYong Li                  std::to_string(static_cast<uint8_t>(bbSlotNum) |
1234f267a67dSYong Li                                 (static_cast<uint8_t>(riserSlotNum) << 3));
1235f267a67dSYong Li     }
1236f267a67dSYong Li     else
1237f267a67dSYong Li     {
123880d4d5f9SMatt Simmering         lg2::error("Controller write read command: invalid request");
1239f267a67dSYong Li         return ipmi::responseInvalidFieldRequest();
1240f267a67dSYong Li     }
1241f267a67dSYong Li 
1242ad129c68SZhikui Ren     // Allow single byte write as it is offset byte to read the data, rest
1243ad129c68SZhikui Ren     // allow only in Special mode.
1244f267a67dSYong Li     if (writeCount > 1)
1245f267a67dSYong Li     {
1246ae13ac62SRichard Marian Thomaiyar         if (mtm.getMfgMode() == SpecialMode::none)
1247f267a67dSYong Li         {
1248f267a67dSYong Li             return ipmi::responseInsufficientPrivilege();
1249f267a67dSYong Li         }
1250f267a67dSYong Li     }
1251f267a67dSYong Li 
1252f267a67dSYong Li     if (readCount > slotI2CMaxReadSize)
1253f267a67dSYong Li     {
125480d4d5f9SMatt Simmering         lg2::error("Controller write read command: Read count exceeds limit");
1255f267a67dSYong Li         return ipmi::responseParmOutOfRange();
1256f267a67dSYong Li     }
1257f267a67dSYong Li 
1258f267a67dSYong Li     if (!readCount && !writeCount)
1259f267a67dSYong Li     {
126080d4d5f9SMatt Simmering         lg2::error("Controller write read command: Read & write count are 0");
1261f267a67dSYong Li         return ipmi::responseInvalidFieldRequest();
1262f267a67dSYong Li     }
1263f267a67dSYong Li 
1264f267a67dSYong Li     std::vector<uint8_t> readBuf(readCount);
1265f267a67dSYong Li 
1266b37abfb2SPatrick Williams     ipmi::Cc retI2C = ipmi::i2cWriteRead(i2cBus, targetAddr, writeData,
1267b37abfb2SPatrick Williams                                          readBuf);
1268f267a67dSYong Li     if (retI2C != ipmi::ccSuccess)
1269f267a67dSYong Li     {
1270f267a67dSYong Li         return ipmi::response(retI2C);
1271f267a67dSYong Li     }
1272f267a67dSYong Li 
1273f267a67dSYong Li     return ipmi::responseSuccess(readBuf);
1274f267a67dSYong Li }
1275068b4f2cSYong Li 
clearCMOS()1276068b4f2cSYong Li ipmi::RspType<> clearCMOS()
1277068b4f2cSYong Li {
127880d4d5f9SMatt Simmering     // There is an i2c device on bus 4, the target address is 0x38. Based on
1279ad129c68SZhikui Ren     // the spec, writing 0x1 to address 0x61 on this device, will trigger
1280ad129c68SZhikui Ren     // the clear CMOS action.
128180d4d5f9SMatt Simmering     constexpr uint8_t targetAddr = 0x38;
1282068b4f2cSYong Li     std::string i2cBus = "/dev/i2c-4";
1283eaeb6cb0SYong Li     std::vector<uint8_t> writeData = {0x61, 0x1};
1284068b4f2cSYong Li     std::vector<uint8_t> readBuf(0);
1285068b4f2cSYong Li 
1286b37abfb2SPatrick Williams     ipmi::Cc retI2C = ipmi::i2cWriteRead(i2cBus, targetAddr, writeData,
1287b37abfb2SPatrick Williams                                          readBuf);
1288068b4f2cSYong Li     return ipmi::response(retI2C);
1289068b4f2cSYong Li }
129027d2356eSVernon Mauery 
setFITcLayout(uint32_t layout)129127d2356eSVernon Mauery ipmi::RspType<> setFITcLayout(uint32_t layout)
129227d2356eSVernon Mauery {
129327d2356eSVernon Mauery     static constexpr const char* factoryFITcLayout =
129427d2356eSVernon Mauery         "/var/sofs/factory-settings/layout/fitc";
129527d2356eSVernon Mauery     std::filesystem::path fitcDir =
129627d2356eSVernon Mauery         std::filesystem::path(factoryFITcLayout).parent_path();
129727d2356eSVernon Mauery     std::error_code ec;
129827d2356eSVernon Mauery     std::filesystem::create_directories(fitcDir, ec);
129927d2356eSVernon Mauery     if (ec)
130027d2356eSVernon Mauery     {
130127d2356eSVernon Mauery         return ipmi::responseUnspecifiedError();
130227d2356eSVernon Mauery     }
130327d2356eSVernon Mauery     try
130427d2356eSVernon Mauery     {
130527d2356eSVernon Mauery         std::ofstream file(factoryFITcLayout);
130627d2356eSVernon Mauery         file << layout;
130727d2356eSVernon Mauery         file.flush();
130827d2356eSVernon Mauery         file.close();
130927d2356eSVernon Mauery     }
131027d2356eSVernon Mauery     catch (const std::exception& e)
131127d2356eSVernon Mauery     {
131227d2356eSVernon Mauery         return ipmi::responseUnspecifiedError();
131327d2356eSVernon Mauery     }
131427d2356eSVernon Mauery 
131527d2356eSVernon Mauery     return ipmi::responseSuccess();
131627d2356eSVernon Mauery }
131727d2356eSVernon Mauery 
131806584cd0SArun P. Mohanan static std::vector<std::string>
getMCTPServiceConfigPaths(ipmi::Context::ptr & ctx)131906584cd0SArun P. Mohanan     getMCTPServiceConfigPaths(ipmi::Context::ptr& ctx)
132006584cd0SArun P. Mohanan {
132106584cd0SArun P. Mohanan     boost::system::error_code ec;
132206584cd0SArun P. Mohanan     auto configPaths = ctx->bus->yield_method_call<std::vector<std::string>>(
132306584cd0SArun P. Mohanan         ctx->yield, ec, "xyz.openbmc_project.ObjectMapper",
132406584cd0SArun P. Mohanan         "/xyz/openbmc_project/object_mapper",
132506584cd0SArun P. Mohanan         "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths",
132606584cd0SArun P. Mohanan         "/xyz/openbmc_project/inventory/system/board", 2,
132706584cd0SArun P. Mohanan         std::array<const char*, 1>{
132806584cd0SArun P. Mohanan             "xyz.openbmc_project.Configuration.MctpConfiguration"});
132906584cd0SArun P. Mohanan     if (ec)
133006584cd0SArun P. Mohanan     {
133106584cd0SArun P. Mohanan         throw std::runtime_error(
133206584cd0SArun P. Mohanan             "Failed to query configuration sub tree objects");
133306584cd0SArun P. Mohanan     }
133406584cd0SArun P. Mohanan     return configPaths;
133506584cd0SArun P. Mohanan }
133606584cd0SArun P. Mohanan 
startOrStopService(ipmi::Context::ptr & ctx,const uint8_t enable,const std::string & serviceName,bool disableOrEnableUnitFiles=true)133706584cd0SArun P. Mohanan static ipmi::RspType<> startOrStopService(ipmi::Context::ptr& ctx,
133806584cd0SArun P. Mohanan                                           const uint8_t enable,
13391fe485cdSANJALI RAY                                           const std::string& serviceName,
13401fe485cdSANJALI RAY                                           bool disableOrEnableUnitFiles = true)
134106584cd0SArun P. Mohanan {
134206584cd0SArun P. Mohanan     constexpr bool runtimeOnly = false;
134306584cd0SArun P. Mohanan     constexpr bool force = false;
134406584cd0SArun P. Mohanan 
134506584cd0SArun P. Mohanan     boost::system::error_code ec;
134606584cd0SArun P. Mohanan     switch (enable)
134706584cd0SArun P. Mohanan     {
134806584cd0SArun P. Mohanan         case ipmi::SupportedFeatureActions::stop:
134906584cd0SArun P. Mohanan             ctx->bus->yield_method_call(ctx->yield, ec, systemDService,
135006584cd0SArun P. Mohanan                                         systemDObjPath, systemDMgrIntf,
135106584cd0SArun P. Mohanan                                         "StopUnit", serviceName, "replace");
135206584cd0SArun P. Mohanan             break;
135306584cd0SArun P. Mohanan         case ipmi::SupportedFeatureActions::start:
135406584cd0SArun P. Mohanan             ctx->bus->yield_method_call(ctx->yield, ec, systemDService,
135506584cd0SArun P. Mohanan                                         systemDObjPath, systemDMgrIntf,
135606584cd0SArun P. Mohanan                                         "StartUnit", serviceName, "replace");
135706584cd0SArun P. Mohanan             break;
135806584cd0SArun P. Mohanan         case ipmi::SupportedFeatureActions::disable:
13591fe485cdSANJALI RAY             if (disableOrEnableUnitFiles == true)
13601fe485cdSANJALI RAY             {
13611fe485cdSANJALI RAY                 ctx->bus->yield_method_call(
13621fe485cdSANJALI RAY                     ctx->yield, ec, systemDService, systemDObjPath,
13631fe485cdSANJALI RAY                     systemDMgrIntf, "DisableUnitFiles",
13641fe485cdSANJALI RAY                     std::array<const char*, 1>{serviceName.c_str()},
13651fe485cdSANJALI RAY                     runtimeOnly);
13661fe485cdSANJALI RAY             }
136706584cd0SArun P. Mohanan             ctx->bus->yield_method_call(
136806584cd0SArun P. Mohanan                 ctx->yield, ec, systemDService, systemDObjPath, systemDMgrIntf,
136906584cd0SArun P. Mohanan                 "MaskUnitFiles",
137006584cd0SArun P. Mohanan                 std::array<const char*, 1>{serviceName.c_str()}, runtimeOnly,
137106584cd0SArun P. Mohanan                 force);
137206584cd0SArun P. Mohanan             break;
137306584cd0SArun P. Mohanan         case ipmi::SupportedFeatureActions::enable:
137406584cd0SArun P. Mohanan             ctx->bus->yield_method_call(
137506584cd0SArun P. Mohanan                 ctx->yield, ec, systemDService, systemDObjPath, systemDMgrIntf,
137606584cd0SArun P. Mohanan                 "UnmaskUnitFiles",
137706584cd0SArun P. Mohanan                 std::array<const char*, 1>{serviceName.c_str()}, runtimeOnly);
13781fe485cdSANJALI RAY             if (disableOrEnableUnitFiles == true)
13791fe485cdSANJALI RAY             {
138006584cd0SArun P. Mohanan                 ctx->bus->yield_method_call(
13811fe485cdSANJALI RAY                     ctx->yield, ec, systemDService, systemDObjPath,
13821fe485cdSANJALI RAY                     systemDMgrIntf, "EnableUnitFiles",
13831fe485cdSANJALI RAY                     std::array<const char*, 1>{serviceName.c_str()},
13841fe485cdSANJALI RAY                     runtimeOnly, force);
13851fe485cdSANJALI RAY             }
138606584cd0SArun P. Mohanan             break;
138706584cd0SArun P. Mohanan         default:
13884180cfe9SAlex Schendel             lg2::warning("ERROR: Invalid feature action selected", "ACTION",
13894180cfe9SAlex Schendel                          lg2::dec, enable);
139006584cd0SArun P. Mohanan             return ipmi::responseInvalidFieldRequest();
139106584cd0SArun P. Mohanan     }
139206584cd0SArun P. Mohanan     if (ec)
139306584cd0SArun P. Mohanan     {
13944180cfe9SAlex Schendel         lg2::warning("ERROR: Service start or stop failed", "SERVICE",
13954180cfe9SAlex Schendel                      serviceName.c_str());
139606584cd0SArun P. Mohanan         return ipmi::responseUnspecifiedError();
139706584cd0SArun P. Mohanan     }
139806584cd0SArun P. Mohanan     return ipmi::responseSuccess();
139906584cd0SArun P. Mohanan }
140006584cd0SArun P. Mohanan 
getMCTPServiceName(const std::string & objectPath)140106584cd0SArun P. Mohanan static std::string getMCTPServiceName(const std::string& objectPath)
140206584cd0SArun P. Mohanan {
140306584cd0SArun P. Mohanan     const auto serviceArgument = boost::algorithm::replace_all_copy(
140406584cd0SArun P. Mohanan         boost::algorithm::replace_first_copy(
140506584cd0SArun P. Mohanan             objectPath, "/xyz/openbmc_project/inventory/system/board/", ""),
140606584cd0SArun P. Mohanan         "/", "_2f");
1407b37abfb2SPatrick Williams     std::string unitName = "xyz.openbmc_project.mctpd@" + serviceArgument +
1408b37abfb2SPatrick Williams                            ".service";
140906584cd0SArun P. Mohanan     return unitName;
141006584cd0SArun P. Mohanan }
141106584cd0SArun P. Mohanan 
handleMCTPFeature(ipmi::Context::ptr & ctx,const uint8_t enable,const std::string & binding)141206584cd0SArun P. Mohanan static ipmi::RspType<> handleMCTPFeature(ipmi::Context::ptr& ctx,
141306584cd0SArun P. Mohanan                                          const uint8_t enable,
141406584cd0SArun P. Mohanan                                          const std::string& binding)
141506584cd0SArun P. Mohanan {
141606584cd0SArun P. Mohanan     std::vector<std::string> configPaths;
141706584cd0SArun P. Mohanan     try
141806584cd0SArun P. Mohanan     {
141906584cd0SArun P. Mohanan         configPaths = getMCTPServiceConfigPaths(ctx);
142006584cd0SArun P. Mohanan     }
142106584cd0SArun P. Mohanan     catch (const std::exception& e)
142206584cd0SArun P. Mohanan     {
14234180cfe9SAlex Schendel         lg2::error(e.what());
142406584cd0SArun P. Mohanan         return ipmi::responseUnspecifiedError();
142506584cd0SArun P. Mohanan     }
142606584cd0SArun P. Mohanan 
142706584cd0SArun P. Mohanan     for (const auto& objectPath : configPaths)
142806584cd0SArun P. Mohanan     {
1429b37abfb2SPatrick Williams         const auto pos = objectPath.find_last_of('/');
143006584cd0SArun P. Mohanan         if (binding == objectPath.substr(pos + 1))
143106584cd0SArun P. Mohanan         {
143206584cd0SArun P. Mohanan             return startOrStopService(ctx, enable,
14331fe485cdSANJALI RAY                                       getMCTPServiceName(objectPath), false);
143406584cd0SArun P. Mohanan         }
143506584cd0SArun P. Mohanan     }
143606584cd0SArun P. Mohanan     return ipmi::responseSuccess();
143706584cd0SArun P. Mohanan }
143806584cd0SArun P. Mohanan 
isNum(const std::string & s)143997c30909SVasu V static bool isNum(const std::string& s)
144097c30909SVasu V {
144197c30909SVasu V     if (s.empty())
144297c30909SVasu V     {
144397c30909SVasu V         return false;
144497c30909SVasu V     }
144597c30909SVasu V     uint8_t busNumber;
144697c30909SVasu V     const auto sEnd = s.data() + s.size();
144797c30909SVasu V     const auto& [ptr, ec] = std::from_chars(s.data(), sEnd, busNumber);
144897c30909SVasu V     if (ec == std::errc() || ptr == sEnd)
144997c30909SVasu V     {
145097c30909SVasu V         return true;
145197c30909SVasu V     }
145297c30909SVasu V     return false;
145397c30909SVasu V }
145497c30909SVasu V 
getBusNumFromPath(const std::string & path,std::string & busStr)145597c30909SVasu V bool getBusNumFromPath(const std::string& path, std::string& busStr)
145697c30909SVasu V {
145797c30909SVasu V     std::vector<std::string> parts;
145897c30909SVasu V     boost::split(parts, path, boost::is_any_of("-"));
145997c30909SVasu V     if (parts.size() == 2)
146097c30909SVasu V     {
146197c30909SVasu V         busStr = parts[1];
146297c30909SVasu V         if (isNum(busStr))
146397c30909SVasu V         {
146497c30909SVasu V             return true;
146597c30909SVasu V         }
146697c30909SVasu V     }
146797c30909SVasu V     return false;
146897c30909SVasu V }
146997c30909SVasu V 
muxSlotDisable(ipmi::Context::ptr & ctx,std::string service,std::string muxName,uint8_t action,uint8_t slotNum)147097c30909SVasu V static ipmi::RspType<> muxSlotDisable(ipmi::Context::ptr& ctx,
147197c30909SVasu V                                       std::string service, std::string muxName,
147297c30909SVasu V                                       uint8_t action, uint8_t slotNum)
147397c30909SVasu V {
147497c30909SVasu V     boost::system::error_code ec;
147597c30909SVasu V     const std::filesystem::path muxSymlinkDirPath =
147697c30909SVasu V         "/dev/i2c-mux/" + muxName + "/Slot_" + std::to_string(slotNum + 1);
147797c30909SVasu V     if (!std::filesystem::is_symlink(muxSymlinkDirPath))
147897c30909SVasu V     {
147997c30909SVasu V         return ipmi::responseInvalidFieldRequest();
148097c30909SVasu V     }
148197c30909SVasu V     std::string linkPath = std::filesystem::read_symlink(muxSymlinkDirPath);
148297c30909SVasu V 
148397c30909SVasu V     std::string portNum;
148497c30909SVasu V     if (!getBusNumFromPath(linkPath, portNum))
148597c30909SVasu V     {
148697c30909SVasu V         return ipmi::responseInvalidFieldRequest();
148797c30909SVasu V     }
148897c30909SVasu V     auto res = ctx->bus->yield_method_call<int>(
148997c30909SVasu V         ctx->yield, ec, service, mctpObjPath, mctpBaseIntf, "SkipList",
149097c30909SVasu V         std::vector<uint8_t>{action, static_cast<uint8_t>(std::stoi(portNum))});
149197c30909SVasu V     if (ec)
149297c30909SVasu V     {
149397c30909SVasu V         lg2::error("Failed to set mctp skiplist");
149497c30909SVasu V         return ipmi::responseUnspecifiedError();
149597c30909SVasu V     }
149697c30909SVasu V 
149797c30909SVasu V     if (!res)
149897c30909SVasu V     {
149997c30909SVasu V         return ipmi::responseResponseError();
150097c30909SVasu V     }
150197c30909SVasu V     return ipmi::responseSuccess();
150297c30909SVasu V }
150397c30909SVasu V 
handleMCTPSlotFeature(ipmi::Context::ptr & ctx,const uint8_t enable,const uint8_t featureArg)150497c30909SVasu V static ipmi::RspType<> handleMCTPSlotFeature(ipmi::Context::ptr& ctx,
150597c30909SVasu V                                              const uint8_t enable,
150697c30909SVasu V                                              const uint8_t featureArg)
150797c30909SVasu V {
150897c30909SVasu V     uint8_t slotNum = (featureArg & slotNumMask);
150997c30909SVasu V     switch ((featureArg & muxTypeMask) >> muxTypeShift)
151097c30909SVasu V     {
151197c30909SVasu V         case ipmi::SupportedFeatureMuxs::pcieMuxSlot:
151297c30909SVasu V             return muxSlotDisable(ctx, mctpPcieSlotService, "PCIe_Mux", enable,
151397c30909SVasu V                                   slotNum);
151497c30909SVasu V             break;
151597c30909SVasu V         case ipmi::SupportedFeatureMuxs::pcieMcioMuxSlot:
151697c30909SVasu V             return muxSlotDisable(ctx, mctpPcieSlotService, "PCIe_MCIO_Mux",
151797c30909SVasu V                                   enable, slotNum);
151897c30909SVasu V             break;
151997c30909SVasu V         case ipmi::SupportedFeatureMuxs::pcieM2EdSffMuxSlot:
152097c30909SVasu V             return muxSlotDisable(ctx, mctpPcieSlotService, "M2_EDSFF_Mux",
152197c30909SVasu V                                   enable, slotNum);
152297c30909SVasu V             break;
152397c30909SVasu V         case ipmi::SupportedFeatureMuxs::leftRaiserMuxSlot:
152497c30909SVasu V             return muxSlotDisable(ctx, mctpPcieSlotService, "Left_Riser_Mux",
152597c30909SVasu V                                   enable, slotNum);
152697c30909SVasu V             break;
152797c30909SVasu V         case ipmi::SupportedFeatureMuxs::rightRaiserMuxSlot:
152897c30909SVasu V             return muxSlotDisable(ctx, mctpPcieSlotService, "Right_Riser_Mux",
152997c30909SVasu V                                   enable, slotNum);
153097c30909SVasu V             break;
153197c30909SVasu V         case ipmi::SupportedFeatureMuxs::HsbpMuxSlot:
153297c30909SVasu V             return muxSlotDisable(ctx, mctpHsbpService, "HSBP_Mux", enable,
153397c30909SVasu V                                   slotNum);
153497c30909SVasu V             break;
153597c30909SVasu V         default:
153697c30909SVasu V             lg2::warning("ERROR: Invalid Mux slot selected");
153797c30909SVasu V             return ipmi::responseInvalidFieldRequest();
153897c30909SVasu V     }
153997c30909SVasu V }
154097c30909SVasu V 
154106584cd0SArun P. Mohanan /** @brief implements MTM BMC Feature Control IPMI command which can be
154206584cd0SArun P. Mohanan  * used to enable or disable the supported BMC features.
154306584cd0SArun P. Mohanan  * @param yield - context object that represents the currently executing
154406584cd0SArun P. Mohanan  * coroutine
154506584cd0SArun P. Mohanan  * @param feature - feature enum to enable or disable
154606584cd0SArun P. Mohanan  * @param enable - enable or disable the feature
154706584cd0SArun P. Mohanan  * @param featureArg - custom arguments for that feature
154806584cd0SArun P. Mohanan  * @param reserved - reserved for future use
154906584cd0SArun P. Mohanan  *
155006584cd0SArun P. Mohanan  * @returns IPMI completion code
155106584cd0SArun P. Mohanan  */
mtmBMCFeatureControl(ipmi::Context::ptr ctx,const uint8_t feature,const uint8_t enable,const uint8_t featureArg,const uint16_t reserved)155206584cd0SArun P. Mohanan ipmi::RspType<> mtmBMCFeatureControl(ipmi::Context::ptr ctx,
155306584cd0SArun P. Mohanan                                      const uint8_t feature,
155406584cd0SArun P. Mohanan                                      const uint8_t enable,
155506584cd0SArun P. Mohanan                                      const uint8_t featureArg,
155606584cd0SArun P. Mohanan                                      const uint16_t reserved)
155706584cd0SArun P. Mohanan {
155806584cd0SArun P. Mohanan     if (reserved != 0)
155906584cd0SArun P. Mohanan     {
156006584cd0SArun P. Mohanan         return ipmi::responseInvalidFieldRequest();
156106584cd0SArun P. Mohanan     }
156206584cd0SArun P. Mohanan 
156306584cd0SArun P. Mohanan     switch (feature)
156406584cd0SArun P. Mohanan     {
156506584cd0SArun P. Mohanan         case ipmi::SupportedFeatureControls::mctp:
156606584cd0SArun P. Mohanan             switch (featureArg)
156706584cd0SArun P. Mohanan             {
156806584cd0SArun P. Mohanan                 case ipmi::SupportedMCTPBindings::mctpPCIe:
156906584cd0SArun P. Mohanan                     return handleMCTPFeature(ctx, enable, "MCTP_PCIe");
157006584cd0SArun P. Mohanan                 case ipmi::SupportedMCTPBindings::mctpSMBusHSBP:
157106584cd0SArun P. Mohanan                     return handleMCTPFeature(ctx, enable, "MCTP_SMBus_HSBP");
157206584cd0SArun P. Mohanan                 case ipmi::SupportedMCTPBindings::mctpSMBusPCIeSlot:
157306584cd0SArun P. Mohanan                     return handleMCTPFeature(ctx, enable,
157406584cd0SArun P. Mohanan                                              "MCTP_SMBus_PCIe_slot");
157506584cd0SArun P. Mohanan                 default:
157606584cd0SArun P. Mohanan                     return ipmi::responseInvalidFieldRequest();
157706584cd0SArun P. Mohanan             }
157806584cd0SArun P. Mohanan             break;
15795cb2c045SJason M. Bills         case ipmi::SupportedFeatureControls::pcieScan:
15805cb2c045SJason M. Bills             if (featureArg != 0)
15815cb2c045SJason M. Bills             {
15825cb2c045SJason M. Bills                 return ipmi::responseInvalidFieldRequest();
15835cb2c045SJason M. Bills             }
15845cb2c045SJason M. Bills             startOrStopService(ctx, enable, "xyz.openbmc_project.PCIe.service");
15855cb2c045SJason M. Bills             break;
158697c30909SVasu V         case ipmi::SupportedFeatureControls::mctpSlotSupport:
158797c30909SVasu V             return handleMCTPSlotFeature(ctx, enable, featureArg);
158897c30909SVasu V             break;
158906584cd0SArun P. Mohanan         default:
159006584cd0SArun P. Mohanan             return ipmi::responseInvalidFieldRequest();
159106584cd0SArun P. Mohanan     }
159206584cd0SArun P. Mohanan     return ipmi::responseSuccess();
159306584cd0SArun P. Mohanan }
1594a3702c1fSVernon Mauery } // namespace ipmi
1595a3702c1fSVernon Mauery 
1596a3702c1fSVernon Mauery void register_mtm_commands() __attribute__((constructor));
register_mtm_commands()1597a3702c1fSVernon Mauery void register_mtm_commands()
1598a3702c1fSVernon Mauery {
159938d2b5a6SJason M. Bills     // <Get SM Signal>
160098bbf69aSVernon Mauery     ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnGeneral,
160198bbf69aSVernon Mauery                           ipmi::intel::general::cmdGetSmSignal,
16025e3bf557SAyushi Smriti                           ipmi::Privilege::Admin, ipmi::appMTMGetSignal);
1603a3702c1fSVernon Mauery 
160498bbf69aSVernon Mauery     ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnGeneral,
160598bbf69aSVernon Mauery                           ipmi::intel::general::cmdSetSmSignal,
16065e3bf557SAyushi Smriti                           ipmi::Privilege::Admin, ipmi::appMTMSetSignal);
1607a3702c1fSVernon Mauery 
160898bbf69aSVernon Mauery     ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnGeneral,
160998bbf69aSVernon Mauery                           ipmi::intel::general::cmdMtmKeepAlive,
1610666dd01cSRichard Marian Thomaiyar                           ipmi::Privilege::Admin, ipmi::mtmKeepAlive);
1611666dd01cSRichard Marian Thomaiyar 
161298bbf69aSVernon Mauery     ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnGeneral,
161398bbf69aSVernon Mauery                           ipmi::intel::general::cmdSetManufacturingData,
16141f0839c2SRichard Marian Thomaiyar                           ipmi::Privilege::Admin, ipmi::setManufacturingData);
16151f0839c2SRichard Marian Thomaiyar 
161698bbf69aSVernon Mauery     ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnGeneral,
161798bbf69aSVernon Mauery                           ipmi::intel::general::cmdGetManufacturingData,
16181f0839c2SRichard Marian Thomaiyar                           ipmi::Privilege::Admin, ipmi::getManufacturingData);
16191f0839c2SRichard Marian Thomaiyar 
162027d2356eSVernon Mauery     ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnGeneral,
162127d2356eSVernon Mauery                           ipmi::intel::general::cmdSetFITcLayout,
162227d2356eSVernon Mauery                           ipmi::Privilege::Admin, ipmi::setFITcLayout);
162327d2356eSVernon Mauery 
162406584cd0SArun P. Mohanan     ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnGeneral,
162506584cd0SArun P. Mohanan                           ipmi::intel::general::cmdMTMBMCFeatureControl,
162606584cd0SArun P. Mohanan                           ipmi::Privilege::Admin, ipmi::mtmBMCFeatureControl);
162706584cd0SArun P. Mohanan 
162898bbf69aSVernon Mauery     ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnApp,
162980d4d5f9SMatt Simmering                           ipmi::intel::general::cmdSlotI2CControllerWriteRead,
163098bbf69aSVernon Mauery                           ipmi::Privilege::Admin,
163180d4d5f9SMatt Simmering                           ipmi::appSlotI2CControllerWriteRead);
1632f267a67dSYong Li 
1633068b4f2cSYong Li     ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnPlatform,
1634068b4f2cSYong Li                           ipmi::intel::platform::cmdClearCMOS,
1635068b4f2cSYong Li                           ipmi::Privilege::Admin, ipmi::clearCMOS);
1636068b4f2cSYong Li 
163798bbf69aSVernon Mauery     ipmi::registerFilter(ipmi::prioOemBase,
163885feb130SYong Li                          [](ipmi::message::Request::ptr request) {
163985feb130SYong Li         return ipmi::mfgFilterMessage(request);
164085feb130SYong Li     });
1641a3702c1fSVernon Mauery }
1642