blob: 7ed7506b52bfec3cbb181d090786d093d71fb22b [file] [log] [blame]
Vernon Mauerya3702c12019-05-22 13:20:59 -07001/*
2// Copyright (c) 2018 Intel Corporation
3//
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at
7//
8// http://www.apache.org/licenses/LICENSE-2.0
9//
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15*/
16
Ayushi Smritid872a4a2019-10-15 10:20:11 +053017#include <linux/input.h>
18
Arun P. Mohanan06584cd2021-08-13 20:56:01 +053019#include <boost/algorithm/string.hpp>
Richard Marian Thomaiyar147daec2019-06-15 07:43:48 +053020#include <boost/container/flat_map.hpp>
Vernon Mauerya3702c12019-05-22 13:20:59 -070021#include <ipmid/api.hpp>
22#include <manufacturingcommands.hpp>
23#include <oemcommands.hpp>
24
James Feistfcd2d3a2020-05-28 10:38:15 -070025#include <filesystem>
26#include <fstream>
27
Vernon Mauerya3702c12019-05-22 13:20:59 -070028namespace ipmi
29{
30
31Manufacturing mtm;
32
33static auto revertTimeOut =
34 std::chrono::duration_cast<std::chrono::microseconds>(
35 std::chrono::seconds(60)); // 1 minute timeout
36
Yong Lif267a672019-08-29 11:16:07 +080037static constexpr uint8_t slotAddressTypeBus = 0;
38static constexpr uint8_t slotAddressTypeUniqueid = 1;
39static constexpr uint8_t slotI2CMaxReadSize = 35;
40
Vernon Mauerya3702c12019-05-22 13:20:59 -070041static constexpr const char* callbackMgrService =
42 "xyz.openbmc_project.CallbackManager";
43static constexpr const char* callbackMgrIntf =
44 "xyz.openbmc_project.CallbackManager";
45static constexpr const char* callbackMgrObjPath =
46 "/xyz/openbmc_project/CallbackManager";
47static constexpr const char* retriggerLedUpdate = "RetriggerLEDUpdate";
48
49const static constexpr char* systemDService = "org.freedesktop.systemd1";
50const static constexpr char* systemDObjPath = "/org/freedesktop/systemd1";
51const static constexpr char* systemDMgrIntf =
52 "org.freedesktop.systemd1.Manager";
53const static constexpr char* pidControlService = "phosphor-pid-control.service";
54
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +053055static inline Cc resetMtmTimer(ipmi::Context::ptr ctx)
Richard Marian Thomaiyar666dd012019-08-02 20:55:37 +053056{
Richard Marian Thomaiyar666dd012019-08-02 20:55:37 +053057 boost::system::error_code ec;
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +053058 ctx->bus->yield_method_call<>(ctx->yield, ec, specialModeService,
59 specialModeObjPath, specialModeIntf,
60 "ResetTimer");
Richard Marian Thomaiyar666dd012019-08-02 20:55:37 +053061 if (ec)
62 {
63 phosphor::logging::log<phosphor::logging::level::ERR>(
64 "Failed to reset the manufacturing mode timer");
65 return ccUnspecifiedError;
66 }
67 return ccSuccess;
68}
69
Jason M. Bills38d2b5a2019-06-03 16:26:11 -070070int getGpioPathForSmSignal(const SmSignalGet signal, std::string& path)
Vernon Mauerya3702c12019-05-22 13:20:59 -070071{
Jason M. Bills38d2b5a2019-06-03 16:26:11 -070072 switch (signal)
73 {
74 case SmSignalGet::smPowerButton:
75 path = "/xyz/openbmc_project/chassis/buttons/power";
76 break;
77 case SmSignalGet::smResetButton:
78 path = "/xyz/openbmc_project/chassis/buttons/reset";
79 break;
80 case SmSignalGet::smNMIButton:
81 path = "/xyz/openbmc_project/chassis/buttons/nmi";
82 break;
Richard Marian Thomaiyar8e5e2b02019-08-01 07:50:55 +053083 case SmSignalGet::smIdentifyButton:
84 path = "/xyz/openbmc_project/chassis/buttons/id";
85 break;
Jason M. Bills38d2b5a2019-06-03 16:26:11 -070086 default:
87 return -1;
88 break;
89 }
90 return 0;
Vernon Mauerya3702c12019-05-22 13:20:59 -070091}
92
Patrick Venture37890392019-09-25 17:05:03 -070093ipmi_ret_t ledStoreAndSet(SmSignalSet signal, const std::string& setState)
Vernon Mauerya3702c12019-05-22 13:20:59 -070094{
95 LedProperty* ledProp = mtm.findLedProperty(signal);
96 if (ledProp == nullptr)
97 {
98 return IPMI_CC_INVALID_FIELD_REQUEST;
99 }
100
101 std::string ledName = ledProp->getName();
102 std::string ledService = ledServicePrefix + ledName;
103 std::string ledPath = ledPathPrefix + ledName;
104 ipmi::Value presentState;
105
106 if (false == ledProp->getLock())
107 {
108 if (mtm.getProperty(ledService.c_str(), ledPath.c_str(), ledIntf,
109 "State", &presentState) != 0)
110 {
111 return IPMI_CC_UNSPECIFIED_ERROR;
112 }
113 ledProp->setPrevState(std::get<std::string>(presentState));
114 ledProp->setLock(true);
115 if (signal == SmSignalSet::smPowerFaultLed ||
116 signal == SmSignalSet::smSystemReadyLed)
117 {
118 mtm.revertLedCallback = true;
119 }
120 }
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700121 if (mtm.setProperty(ledService, ledPath, ledIntf, "State",
Vernon Mauerya3702c12019-05-22 13:20:59 -0700122 ledStateStr + setState) != 0)
123 {
124 return IPMI_CC_UNSPECIFIED_ERROR;
125 }
126 return IPMI_CC_OK;
127}
128
129ipmi_ret_t ledRevert(SmSignalSet signal)
130{
131 LedProperty* ledProp = mtm.findLedProperty(signal);
132 if (ledProp == nullptr)
133 {
134 return IPMI_CC_INVALID_FIELD_REQUEST;
135 }
136 if (true == ledProp->getLock())
137 {
138 ledProp->setLock(false);
139 if (signal == SmSignalSet::smPowerFaultLed ||
140 signal == SmSignalSet::smSystemReadyLed)
141 {
142 try
143 {
144 ipmi::method_no_args::callDbusMethod(
145 *getSdBus(), callbackMgrService, callbackMgrObjPath,
146 callbackMgrIntf, retriggerLedUpdate);
147 }
Patrick Williamsbd51e6a2021-10-06 13:09:44 -0500148 catch (const sdbusplus::exception_t& e)
Vernon Mauerya3702c12019-05-22 13:20:59 -0700149 {
150 return IPMI_CC_UNSPECIFIED_ERROR;
151 }
152 mtm.revertLedCallback = false;
153 }
154 else
155 {
156 std::string ledName = ledProp->getName();
157 std::string ledService = ledServicePrefix + ledName;
158 std::string ledPath = ledPathPrefix + ledName;
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700159 if (mtm.setProperty(ledService, ledPath, ledIntf, "State",
160 ledProp->getPrevState()) != 0)
Vernon Mauerya3702c12019-05-22 13:20:59 -0700161 {
162 return IPMI_CC_UNSPECIFIED_ERROR;
163 }
164 }
165 }
166 return IPMI_CC_OK;
167}
168
169void Manufacturing::initData()
170{
Vernon Mauerya3702c12019-05-22 13:20:59 -0700171 ledPropertyList.push_back(
172 LedProperty(SmSignalSet::smPowerFaultLed, "status_amber"));
173 ledPropertyList.push_back(
174 LedProperty(SmSignalSet::smSystemReadyLed, "status_green"));
175 ledPropertyList.push_back(
176 LedProperty(SmSignalSet::smIdentifyLed, "identify"));
177}
178
179void Manufacturing::revertTimerHandler()
180{
Richard Marian Thomaiyarae13ac62019-12-17 15:45:12 +0530181
182#ifdef BMC_VALIDATION_UNSECURE_FEATURE
183 if (mtm.getMfgMode() == SpecialMode::valUnsecure)
184 {
185 // Don't revert the behaviour for validation unsecure mode.
186 return;
187 }
188#endif
Vernon Mauerya3702c12019-05-22 13:20:59 -0700189 if (revertFanPWM)
190 {
191 revertFanPWM = false;
192 disablePidControlService(false);
193 }
194
Ayushi Smritid872a4a2019-10-15 10:20:11 +0530195 if (mtmTestBeepFd != -1)
196 {
197 ::close(mtmTestBeepFd);
198 mtmTestBeepFd = -1;
199 }
200
Vernon Mauerya3702c12019-05-22 13:20:59 -0700201 for (const auto& ledProperty : ledPropertyList)
202 {
203 const std::string& ledName = ledProperty.getName();
Jayaprakash Mutyalaf3656142021-01-24 01:04:19 +0000204 if (ledName == "identify" && mtm.getMfgMode() == SpecialMode::mfg)
205 {
206 // Don't revert the behaviour for manufacturing mode
207 continue;
208 }
Vernon Mauerya3702c12019-05-22 13:20:59 -0700209 ledRevert(ledProperty.getSignal());
210 }
211}
212
213Manufacturing::Manufacturing() :
214 revertTimer([&](void) { revertTimerHandler(); })
215{
216 initData();
217}
218
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700219int8_t Manufacturing::getProperty(const std::string& service,
220 const std::string& path,
221 const std::string& interface,
222 const std::string& propertyName,
223 ipmi::Value* reply)
Vernon Mauerya3702c12019-05-22 13:20:59 -0700224{
225 try
226 {
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700227 *reply = ipmi::getDbusProperty(*getSdBus(), service, path, interface,
228 propertyName);
Vernon Mauerya3702c12019-05-22 13:20:59 -0700229 }
Patrick Williams69245b72021-09-02 09:23:01 -0500230 catch (const sdbusplus::exception::exception& e)
Vernon Mauerya3702c12019-05-22 13:20:59 -0700231 {
232 phosphor::logging::log<phosphor::logging::level::INFO>(
233 "ERROR: getProperty");
234 return -1;
235 }
236
237 return 0;
238}
239
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700240int8_t Manufacturing::setProperty(const std::string& service,
241 const std::string& path,
242 const std::string& interface,
243 const std::string& propertyName,
244 ipmi::Value value)
Vernon Mauerya3702c12019-05-22 13:20:59 -0700245{
246 try
247 {
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700248 ipmi::setDbusProperty(*getSdBus(), service, path, interface,
Vernon Mauerya3702c12019-05-22 13:20:59 -0700249 propertyName, value);
250 }
Patrick Williams69245b72021-09-02 09:23:01 -0500251 catch (const sdbusplus::exception::exception& e)
Vernon Mauerya3702c12019-05-22 13:20:59 -0700252 {
253 phosphor::logging::log<phosphor::logging::level::INFO>(
254 "ERROR: setProperty");
255 return -1;
256 }
257
258 return 0;
259}
260
261int8_t Manufacturing::disablePidControlService(const bool disable)
262{
263 try
264 {
265 auto dbus = getSdBus();
266 auto method = dbus->new_method_call(systemDService, systemDObjPath,
267 systemDMgrIntf,
268 disable ? "StopUnit" : "StartUnit");
269 method.append(pidControlService, "replace");
270 auto reply = dbus->call(method);
271 }
Patrick Williams69245b72021-09-02 09:23:01 -0500272 catch (const sdbusplus::exception::exception& e)
Vernon Mauerya3702c12019-05-22 13:20:59 -0700273 {
274 phosphor::logging::log<phosphor::logging::level::INFO>(
275 "ERROR: phosphor-pid-control service start or stop failed");
276 return -1;
277 }
278 return 0;
279}
280
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700281ipmi::RspType<uint8_t, // Signal value
282 std::optional<uint16_t> // Fan tach value
283 >
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +0530284 appMTMGetSignal(ipmi::Context::ptr ctx, uint8_t signalTypeByte,
Richard Marian Thomaiyar147daec2019-06-15 07:43:48 +0530285 uint8_t instance, uint8_t actionByte)
Vernon Mauerya3702c12019-05-22 13:20:59 -0700286{
Ayushi Smritie0511e52019-08-27 17:30:34 +0000287 // mfg filter logic is used to allow MTM get signal command only in
288 // manfacturing mode.
Vernon Mauerya3702c12019-05-22 13:20:59 -0700289
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700290 SmSignalGet signalType = static_cast<SmSignalGet>(signalTypeByte);
291 SmActionGet action = static_cast<SmActionGet>(actionByte);
292
293 switch (signalType)
294 {
anil kumar appana98705b32019-09-11 14:15:50 +0000295 case SmSignalGet::smChassisIntrusion:
296 {
297 ipmi::Value reply;
298 if (mtm.getProperty(intrusionService, intrusionPath, intrusionIntf,
299 "Status", &reply) < 0)
300 {
301 return ipmi::responseInvalidFieldRequest();
302 }
303 std::string* intrusionStatus = std::get_if<std::string>(&reply);
304 if (!intrusionStatus)
305 {
306 return ipmi::responseUnspecifiedError();
307 }
308
309 uint8_t status = 0;
310 if (!intrusionStatus->compare("Normal"))
311 {
312 status = static_cast<uint8_t>(IntrusionStatus::normal);
313 }
314 else if (!intrusionStatus->compare("HardwareIntrusion"))
315 {
316 status =
317 static_cast<uint8_t>(IntrusionStatus::hardwareIntrusion);
318 }
319 else if (!intrusionStatus->compare("TamperingDetected"))
320 {
321 status =
322 static_cast<uint8_t>(IntrusionStatus::tamperingDetected);
323 }
324 else
325 {
326 return ipmi::responseUnspecifiedError();
327 }
328 return ipmi::responseSuccess(status, std::nullopt);
329 }
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700330 case SmSignalGet::smFanPwmGet:
331 {
332 ipmi::Value reply;
Richard Marian Thomaiyar147daec2019-06-15 07:43:48 +0530333 std::string fullPath = fanPwmPath + std::to_string(instance + 1);
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700334 if (mtm.getProperty(fanService, fullPath, fanIntf, "Value",
335 &reply) < 0)
336 {
337 return ipmi::responseInvalidFieldRequest();
338 }
339 double* doubleVal = std::get_if<double>(&reply);
340 if (doubleVal == nullptr)
341 {
342 return ipmi::responseUnspecifiedError();
343 }
344 uint8_t sensorVal = std::round(*doubleVal);
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +0530345 resetMtmTimer(ctx);
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700346 return ipmi::responseSuccess(sensorVal, std::nullopt);
347 }
348 break;
349 case SmSignalGet::smFanTachometerGet:
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700350 {
Richard Marian Thomaiyar147daec2019-06-15 07:43:48 +0530351 boost::system::error_code ec;
352 using objFlatMap = boost::container::flat_map<
353 std::string, boost::container::flat_map<
354 std::string, std::vector<std::string>>>;
355
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +0530356 auto flatMap = ctx->bus->yield_method_call<objFlatMap>(
357 ctx->yield, ec, "xyz.openbmc_project.ObjectMapper",
Richard Marian Thomaiyar147daec2019-06-15 07:43:48 +0530358 "/xyz/openbmc_project/object_mapper",
359 "xyz.openbmc_project.ObjectMapper", "GetSubTree",
360 fanTachBasePath, 0, std::array<const char*, 1>{fanIntf});
361 if (ec)
362 {
363 phosphor::logging::log<phosphor::logging::level::ERR>(
364 "Failed to query fan tach sub tree objects");
365 return ipmi::responseUnspecifiedError();
366 }
367 if (instance >= flatMap.size())
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700368 {
369 return ipmi::responseInvalidFieldRequest();
370 }
Richard Marian Thomaiyar147daec2019-06-15 07:43:48 +0530371 auto itr = flatMap.nth(instance);
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700372 ipmi::Value reply;
Richard Marian Thomaiyar147daec2019-06-15 07:43:48 +0530373 if (mtm.getProperty(fanService, itr->first, fanIntf, "Value",
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700374 &reply) < 0)
375 {
376 return ipmi::responseInvalidFieldRequest();
377 }
378
379 double* doubleVal = std::get_if<double>(&reply);
380 if (doubleVal == nullptr)
381 {
382 return ipmi::responseUnspecifiedError();
383 }
384 uint8_t sensorVal = FAN_PRESENT | FAN_SENSOR_PRESENT;
385 std::optional<uint16_t> fanTach = std::round(*doubleVal);
386
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +0530387 resetMtmTimer(ctx);
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700388 return ipmi::responseSuccess(sensorVal, fanTach);
389 }
390 break;
Richard Marian Thomaiyar8e5e2b02019-08-01 07:50:55 +0530391 case SmSignalGet::smIdentifyButton:
392 {
393 if (action == SmActionGet::revert || action == SmActionGet::ignore)
394 {
395 // ButtonMasked property is not supported for ID button as it is
396 // unnecessary. Hence if requested for revert / ignore, override
397 // it to sample action to make tools happy.
398 action = SmActionGet::sample;
399 }
400 // fall-through
401 }
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700402 case SmSignalGet::smResetButton:
403 case SmSignalGet::smPowerButton:
404 case SmSignalGet::smNMIButton:
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700405 {
406 std::string path;
407 if (getGpioPathForSmSignal(signalType, path) < 0)
408 {
409 return ipmi::responseInvalidFieldRequest();
410 }
411
412 switch (action)
413 {
414 case SmActionGet::sample:
415 phosphor::logging::log<phosphor::logging::level::INFO>(
416 "case SmActionGet::sample");
417 break;
418 case SmActionGet::ignore:
419 {
420 phosphor::logging::log<phosphor::logging::level::INFO>(
421 "case SmActionGet::ignore");
422 if (mtm.setProperty(buttonService, path, buttonIntf,
423 "ButtonMasked", true) < 0)
424 {
425 return ipmi::responseUnspecifiedError();
426 }
427 }
428 break;
429 case SmActionGet::revert:
430 {
431 phosphor::logging::log<phosphor::logging::level::INFO>(
432 "case SmActionGet::revert");
433 if (mtm.setProperty(buttonService, path, buttonIntf,
434 "ButtonMasked", false) < 0)
435 {
436 return ipmi::responseUnspecifiedError();
437 }
438 }
439 break;
440
441 default:
442 return ipmi::responseInvalidFieldRequest();
443 break;
444 }
445
446 ipmi::Value reply;
447 if (mtm.getProperty(buttonService, path, buttonIntf,
448 "ButtonPressed", &reply) < 0)
449 {
450 return ipmi::responseUnspecifiedError();
451 }
452 bool* valPtr = std::get_if<bool>(&reply);
453 if (valPtr == nullptr)
454 {
455 return ipmi::responseUnspecifiedError();
456 }
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +0530457 resetMtmTimer(ctx);
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700458 uint8_t sensorVal = *valPtr;
459 return ipmi::responseSuccess(sensorVal, std::nullopt);
460 }
461 break;
Richard Marian Thomaiyar1b74a212019-08-03 13:26:17 +0530462 case SmSignalGet::smNcsiDiag:
463 {
464 constexpr const char* netBasePath = "/sys/class/net/eth";
465 constexpr const char* carrierSuffix = "/carrier";
466 std::ifstream netIfs(netBasePath + std::to_string(instance) +
467 carrierSuffix);
468 if (!netIfs.good())
469 {
470 return ipmi::responseInvalidFieldRequest();
471 }
472 std::string carrier;
473 netIfs >> carrier;
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +0530474 resetMtmTimer(ctx);
Richard Marian Thomaiyar1b74a212019-08-03 13:26:17 +0530475 return ipmi::responseSuccess(
476 static_cast<uint8_t>(std::stoi(carrier)), std::nullopt);
477 }
478 break;
Vernon Mauerya3702c12019-05-22 13:20:59 -0700479 default:
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700480 return ipmi::responseInvalidFieldRequest();
Vernon Mauerya3702c12019-05-22 13:20:59 -0700481 break;
482 }
Vernon Mauerya3702c12019-05-22 13:20:59 -0700483}
484
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +0530485ipmi::RspType<> appMTMSetSignal(ipmi::Context::ptr ctx, uint8_t signalTypeByte,
486 uint8_t instance, uint8_t actionByte,
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000487 std::optional<uint8_t> pwmSpeed)
Vernon Mauerya3702c12019-05-22 13:20:59 -0700488{
Ayushi Smritie0511e52019-08-27 17:30:34 +0000489 // mfg filter logic is used to allow MTM set signal command only in
490 // manfacturing mode.
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000491
492 SmSignalSet signalType = static_cast<SmSignalSet>(signalTypeByte);
493 SmActionSet action = static_cast<SmActionSet>(actionByte);
494 Cc retCode = ccSuccess;
495 int8_t ret = 0;
496
497 switch (signalType)
498 {
499 case SmSignalSet::smPowerFaultLed:
500 case SmSignalSet::smSystemReadyLed:
501 case SmSignalSet::smIdentifyLed:
502 switch (action)
503 {
504 case SmActionSet::forceDeasserted:
Vernon Mauerya3702c12019-05-22 13:20:59 -0700505 {
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000506 phosphor::logging::log<phosphor::logging::level::INFO>(
507 "case SmActionSet::forceDeasserted");
Vernon Mauerya3702c12019-05-22 13:20:59 -0700508
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000509 retCode = ledStoreAndSet(signalType, std::string("Off"));
510 if (retCode != ccSuccess)
Vernon Mauerya3702c12019-05-22 13:20:59 -0700511 {
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000512 return ipmi::response(retCode);
Vernon Mauerya3702c12019-05-22 13:20:59 -0700513 }
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000514 mtm.revertTimer.start(revertTimeOut);
Vernon Mauerya3702c12019-05-22 13:20:59 -0700515 }
516 break;
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000517 case SmActionSet::forceAsserted:
Vernon Mauerya3702c12019-05-22 13:20:59 -0700518 {
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000519 phosphor::logging::log<phosphor::logging::level::INFO>(
520 "case SmActionSet::forceAsserted");
Vernon Mauerya3702c12019-05-22 13:20:59 -0700521
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000522 retCode = ledStoreAndSet(signalType, std::string("On"));
523 if (retCode != ccSuccess)
524 {
525 return ipmi::response(retCode);
526 }
527 mtm.revertTimer.start(revertTimeOut);
528 if (SmSignalSet::smPowerFaultLed == signalType)
529 {
530 // Deassert "system ready"
531 retCode = ledStoreAndSet(SmSignalSet::smSystemReadyLed,
532 std::string("Off"));
533 }
534 else if (SmSignalSet::smSystemReadyLed == signalType)
535 {
536 // Deassert "fault led"
537 retCode = ledStoreAndSet(SmSignalSet::smPowerFaultLed,
538 std::string("Off"));
539 }
540 }
541 break;
542 case SmActionSet::revert:
543 {
544 phosphor::logging::log<phosphor::logging::level::INFO>(
545 "case SmActionSet::revert");
546 retCode = ledRevert(signalType);
547 }
548 break;
549 default:
550 {
551 return ipmi::responseInvalidFieldRequest();
552 }
553 }
554 break;
555 case SmSignalSet::smFanPowerSpeed:
556 {
557 if ((action == SmActionSet::forceAsserted) && (!pwmSpeed))
558 {
559 return ipmi::responseReqDataLenInvalid();
560 }
561
562 if ((action == SmActionSet::forceAsserted) && (*pwmSpeed > 100))
563 {
564 return ipmi::responseInvalidFieldRequest();
565 }
566
567 uint8_t pwmValue = 0;
568 switch (action)
569 {
570 case SmActionSet::revert:
571 {
572 if (mtm.revertFanPWM)
573 {
574 ret = mtm.disablePidControlService(false);
Vernon Mauerya3702c12019-05-22 13:20:59 -0700575 if (ret < 0)
576 {
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000577 return ipmi::responseUnspecifiedError();
Vernon Mauerya3702c12019-05-22 13:20:59 -0700578 }
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000579 mtm.revertFanPWM = false;
Vernon Mauerya3702c12019-05-22 13:20:59 -0700580 }
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000581 }
582 break;
583 case SmActionSet::forceAsserted:
584 {
585 pwmValue = *pwmSpeed;
586 } // fall-through
587 case SmActionSet::forceDeasserted:
588 {
589 if (!mtm.revertFanPWM)
Vernon Mauerya3702c12019-05-22 13:20:59 -0700590 {
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000591 ret = mtm.disablePidControlService(true);
592 if (ret < 0)
593 {
594 return ipmi::responseUnspecifiedError();
595 }
596 mtm.revertFanPWM = true;
Vernon Mauerya3702c12019-05-22 13:20:59 -0700597 }
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000598 mtm.revertTimer.start(revertTimeOut);
599 std::string fanPwmInstancePath =
600 fanPwmPath + std::to_string(instance + 1);
601
602 ret =
603 mtm.setProperty(fanService, fanPwmInstancePath, fanIntf,
604 "Value", static_cast<double>(pwmValue));
605 if (ret < 0)
606 {
607 return ipmi::responseUnspecifiedError();
608 }
609 }
610 break;
611 default:
612 {
613 return ipmi::responseInvalidFieldRequest();
Vernon Mauerya3702c12019-05-22 13:20:59 -0700614 }
615 }
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000616 }
617 break;
Ayushi Smritid872a4a2019-10-15 10:20:11 +0530618 case SmSignalSet::smSpeaker:
619 {
620 phosphor::logging::log<phosphor::logging::level::INFO>(
621 "Performing Speaker SmActionSet",
622 phosphor::logging::entry("ACTION=%d",
623 static_cast<uint8_t>(action)));
624 switch (action)
625 {
626 case SmActionSet::forceAsserted:
627 {
628 char beepDevName[] = "/dev/input/event0";
629 if (mtm.mtmTestBeepFd != -1)
630 {
631 phosphor::logging::log<phosphor::logging::level::INFO>(
632 "mtm beep device is opened already!");
633 // returning success as already beep is in progress
634 return ipmi::response(retCode);
635 }
636
637 if ((mtm.mtmTestBeepFd =
638 ::open(beepDevName, O_RDWR | O_CLOEXEC)) < 0)
639 {
640 phosphor::logging::log<phosphor::logging::level::ERR>(
641 "Failed to open input device");
642 return ipmi::responseUnspecifiedError();
643 }
644
645 struct input_event event;
646 event.type = EV_SND;
647 event.code = SND_TONE;
648 event.value = 2000;
649
650 if (::write(mtm.mtmTestBeepFd, &event,
651 sizeof(struct input_event)) !=
652 sizeof(struct input_event))
653 {
654 phosphor::logging::log<phosphor::logging::level::ERR>(
655 "Failed to write a tone sound event");
656 ::close(mtm.mtmTestBeepFd);
657 mtm.mtmTestBeepFd = -1;
658 return ipmi::responseUnspecifiedError();
659 }
660 mtm.revertTimer.start(revertTimeOut);
661 }
662 break;
663 case SmActionSet::revert:
664 case SmActionSet::forceDeasserted:
665 {
666 if (mtm.mtmTestBeepFd != -1)
667 {
668 ::close(mtm.mtmTestBeepFd);
669 mtm.mtmTestBeepFd = -1;
670 }
671 }
672 break;
673 default:
674 {
675 return ipmi::responseInvalidFieldRequest();
676 }
677 }
678 }
679 break;
Richard Marian Thomaiyar3594c6d2019-11-19 21:29:04 +0530680 case SmSignalSet::smDiskFaultLed:
681 {
682 boost::system::error_code ec;
683 using objPaths = std::vector<std::string>;
684 std::string driveBasePath =
685 "/xyz/openbmc_project/inventory/item/drive/";
686 static constexpr const char* driveLedIntf =
687 "xyz.openbmc_project.Led.Group";
688 static constexpr const char* hsbpService =
689 "xyz.openbmc_project.HsbpManager";
690
691 auto driveList = ctx->bus->yield_method_call<objPaths>(
692 ctx->yield, ec, "xyz.openbmc_project.ObjectMapper",
693 "/xyz/openbmc_project/object_mapper",
694 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths",
695 driveBasePath, 0, std::array<const char*, 1>{driveLedIntf});
696 if (ec)
697 {
698 phosphor::logging::log<phosphor::logging::level::ERR>(
699 "Failed to query HSBP drive sub tree objects");
700 return ipmi::responseUnspecifiedError();
701 }
702 std::string driveObjPath =
703 driveBasePath + "Drive_" + std::to_string(instance + 1);
704 if (std::find(driveList.begin(), driveList.end(), driveObjPath) ==
705 driveList.end())
706 {
707 return ipmi::responseInvalidFieldRequest();
708 }
709 bool driveLedState = false;
710 switch (action)
711 {
712 case SmActionSet::forceAsserted:
713 {
714 driveLedState = true;
715 }
716 break;
717 case SmActionSet::revert:
718 {
719 driveLedState = false;
720 }
721 break;
722 case SmActionSet::forceDeasserted:
723 {
724 driveLedState = false;
725 }
726 break;
727 default:
728 {
729 return ipmi::responseInvalidFieldRequest();
730 }
731 }
732 ret = mtm.setProperty(hsbpService, driveObjPath, driveLedIntf,
733 "Asserted", driveLedState);
734 if (ret < 0)
735 {
736 return ipmi::responseUnspecifiedError();
737 }
738 }
739 break;
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000740 default:
741 {
742 return ipmi::responseInvalidFieldRequest();
Vernon Mauerya3702c12019-05-22 13:20:59 -0700743 }
744 }
Richard Marian Thomaiyar4cc10152019-08-02 23:21:45 +0530745 if (retCode == ccSuccess)
746 {
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +0530747 resetMtmTimer(ctx);
Richard Marian Thomaiyar4cc10152019-08-02 23:21:45 +0530748 }
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000749 return ipmi::response(retCode);
Vernon Mauerya3702c12019-05-22 13:20:59 -0700750}
751
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +0530752ipmi::RspType<> mtmKeepAlive(ipmi::Context::ptr ctx, uint8_t reserved,
Richard Marian Thomaiyar666dd012019-08-02 20:55:37 +0530753 const std::array<char, 5>& intentionalSignature)
754{
Ayushi Smritie0511e52019-08-27 17:30:34 +0000755 // mfg filter logic is used to allow MTM keep alive command only in
756 // manfacturing mode
757
Richard Marian Thomaiyar666dd012019-08-02 20:55:37 +0530758 constexpr std::array<char, 5> signatureOk = {'I', 'N', 'T', 'E', 'L'};
759 if (intentionalSignature != signatureOk || reserved != 0)
760 {
761 return ipmi::responseInvalidFieldRequest();
762 }
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +0530763 return ipmi::response(resetMtmTimer(ctx));
Richard Marian Thomaiyar666dd012019-08-02 20:55:37 +0530764}
765
Ayushi Smritie0511e52019-08-27 17:30:34 +0000766static constexpr unsigned int makeCmdKey(unsigned int netFn, unsigned int cmd)
767{
768 return (netFn << 8) | cmd;
769}
770
Yong Li85feb132019-08-09 16:06:03 +0800771ipmi::Cc mfgFilterMessage(ipmi::message::Request::ptr request)
772{
Ayushi Smritie0511e52019-08-27 17:30:34 +0000773 // Restricted commands, must be executed only in Manufacturing mode
774 switch (makeCmdKey(request->ctx->netFn, request->ctx->cmd))
Yong Li85feb132019-08-09 16:06:03 +0800775 {
Ayushi Smritie0511e52019-08-27 17:30:34 +0000776 // i2c master write read command needs additional checking
777 case makeCmdKey(ipmi::netFnApp, ipmi::app::cmdMasterWriteRead):
778 if (request->payload.size() > 4)
779 {
Richard Marian Thomaiyarae13ac62019-12-17 15:45:12 +0530780 // Allow write data count > 1 only in Special mode
781 if (mtm.getMfgMode() == SpecialMode::none)
Ayushi Smritie0511e52019-08-27 17:30:34 +0000782 {
783 return ipmi::ccInsufficientPrivilege;
784 }
785 }
jayaprakash Mutyala2d4a0192019-11-11 22:12:45 +0000786 return ipmi::ccSuccess;
Ayushi Smritie0511e52019-08-27 17:30:34 +0000787 case makeCmdKey(ipmi::netFnOemOne,
788 ipmi::intel::general::cmdGetSmSignal):
789 case makeCmdKey(ipmi::netFnOemOne,
790 ipmi::intel::general::cmdSetSmSignal):
791 case makeCmdKey(ipmi::netFnOemOne,
792 ipmi::intel::general::cmdMtmKeepAlive):
793 case makeCmdKey(ipmi::netFnOemOne,
794 ipmi::intel::general::cmdSetManufacturingData):
795 case makeCmdKey(ipmi::netFnOemOne,
796 ipmi::intel::general::cmdGetManufacturingData):
Vernon Mauery27d23562021-08-12 10:43:39 -0700797 case makeCmdKey(ipmi::intel::netFnGeneral,
798 ipmi::intel::general::cmdSetFITcLayout):
Arun P. Mohanan06584cd2021-08-13 20:56:01 +0530799 case makeCmdKey(ipmi::netFnOemOne,
800 ipmi::intel::general::cmdMTMBMCFeatureControl):
Ayushi Smritie0511e52019-08-27 17:30:34 +0000801 case makeCmdKey(ipmi::netFnStorage, ipmi::storage::cmdWriteFruData):
AppaRao Puli9a13daa2020-07-13 17:53:00 +0530802 case makeCmdKey(ipmi::netFnOemTwo, ipmi::intel::platform::cmdClearCMOS):
Ayushi Smritie0511e52019-08-27 17:30:34 +0000803
Richard Marian Thomaiyarae13ac62019-12-17 15:45:12 +0530804 // Check for Special mode
805 if (mtm.getMfgMode() == SpecialMode::none)
Yong Li85feb132019-08-09 16:06:03 +0800806 {
Ayushi Smritie0511e52019-08-27 17:30:34 +0000807 return ipmi::ccInvalidCommand;
Yong Li85feb132019-08-09 16:06:03 +0800808 }
jayaprakash Mutyala2d4a0192019-11-11 22:12:45 +0000809 return ipmi::ccSuccess;
810 case makeCmdKey(ipmi::netFnStorage, ipmi::storage::cmdDeleteSelEntry):
811 {
812 return ipmi::ccInvalidCommand;
813 }
Yong Li85feb132019-08-09 16:06:03 +0800814 }
Yong Li85feb132019-08-09 16:06:03 +0800815 return ipmi::ccSuccess;
816}
817
Richard Marian Thomaiyar1f0839c2019-08-25 20:10:52 +0530818static constexpr uint8_t maxEthSize = 6;
819static constexpr uint8_t maxSupportedEth = 3;
820static constexpr const char* factoryEthAddrBaseFileName =
821 "/var/sofs/factory-settings/network/mac/eth";
822
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +0530823ipmi::RspType<> setManufacturingData(ipmi::Context::ptr ctx, uint8_t dataType,
Richard Marian Thomaiyar1f0839c2019-08-25 20:10:52 +0530824 std::array<uint8_t, maxEthSize> ethData)
825{
826 // mfg filter logic will restrict this command executing only in mfg mode.
827 if (dataType >= maxSupportedEth)
828 {
829 return ipmi::responseParmOutOfRange();
830 }
831
832 constexpr uint8_t invalidData = 0;
833 constexpr uint8_t validData = 1;
834 constexpr uint8_t ethAddrStrSize =
835 19; // XX:XX:XX:XX:XX:XX + \n + null termination;
836 std::vector<uint8_t> buff(ethAddrStrSize);
837 std::snprintf(reinterpret_cast<char*>(buff.data()), ethAddrStrSize,
838 "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx\n", ethData.at(0),
839 ethData.at(1), ethData.at(2), ethData.at(3), ethData.at(4),
840 ethData.at(5));
841 std::ofstream oEthFile(factoryEthAddrBaseFileName +
842 std::to_string(dataType),
843 std::ofstream::out);
844 if (!oEthFile.good())
845 {
846 return ipmi::responseUnspecifiedError();
847 }
848
849 oEthFile << reinterpret_cast<char*>(buff.data());
Johnathan Mantey6d83e4e2020-05-08 11:01:01 -0700850 oEthFile.flush();
Richard Marian Thomaiyar1f0839c2019-08-25 20:10:52 +0530851 oEthFile.close();
852
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +0530853 resetMtmTimer(ctx);
Richard Marian Thomaiyar1f0839c2019-08-25 20:10:52 +0530854 return ipmi::responseSuccess();
855}
856
857ipmi::RspType<uint8_t, std::array<uint8_t, maxEthSize>>
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +0530858 getManufacturingData(ipmi::Context::ptr ctx, uint8_t dataType)
Richard Marian Thomaiyar1f0839c2019-08-25 20:10:52 +0530859{
860 // mfg filter logic will restrict this command executing only in mfg mode.
861 if (dataType >= maxSupportedEth)
862 {
863 return ipmi::responseParmOutOfRange();
864 }
865 std::array<uint8_t, maxEthSize> ethData{0};
866 constexpr uint8_t invalidData = 0;
867 constexpr uint8_t validData = 1;
868
869 std::ifstream iEthFile(factoryEthAddrBaseFileName +
870 std::to_string(dataType),
871 std::ifstream::in);
872 if (!iEthFile.good())
873 {
874 return ipmi::responseSuccess(invalidData, ethData);
875 }
876 std::string ethStr;
877 iEthFile >> ethStr;
878 uint8_t* data = ethData.data();
879 std::sscanf(ethStr.c_str(), "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx",
880 data, (data + 1), (data + 2), (data + 3), (data + 4),
881 (data + 5));
882
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +0530883 resetMtmTimer(ctx);
Richard Marian Thomaiyar1f0839c2019-08-25 20:10:52 +0530884 return ipmi::responseSuccess(validData, ethData);
885}
886
Yong Lif267a672019-08-29 11:16:07 +0800887/** @brief implements slot master write read IPMI command which can be used for
888 * low-level I2C/SMBus write, read or write-read access for PCIE slots
889 * @param reserved - skip 6 bit
890 * @param addressType - address type
891 * @param bbSlotNum - baseboard slot number
892 * @param riserSlotNum - riser slot number
893 * @param reserved2 - skip 2 bit
894 * @param slaveAddr - slave address
895 * @param readCount - number of bytes to be read
896 * @param writeData - data to be written
897 *
898 * @returns IPMI completion code plus response data
899 */
900ipmi::RspType<std::vector<uint8_t>>
901 appSlotI2CMasterWriteRead(uint6_t reserved, uint2_t addressType,
902 uint3_t bbSlotNum, uint3_t riserSlotNum,
903 uint2_t resvered2, uint8_t slaveAddr,
904 uint8_t readCount, std::vector<uint8_t> writeData)
905{
906 const size_t writeCount = writeData.size();
907 std::string i2cBus;
908 if (addressType == slotAddressTypeBus)
909 {
910 std::string path = "/dev/i2c-mux/Riser_" +
911 std::to_string(static_cast<uint8_t>(bbSlotNum)) +
912 "_Mux/Pcie_Slot_" +
913 std::to_string(static_cast<uint8_t>(riserSlotNum));
914
915 if (std::filesystem::exists(path) && std::filesystem::is_symlink(path))
916 {
917 i2cBus = std::filesystem::read_symlink(path);
918 }
919 else
920 {
921 phosphor::logging::log<phosphor::logging::level::ERR>(
922 "Master write read command: Cannot get BusID");
923 return ipmi::responseInvalidFieldRequest();
924 }
925 }
926 else if (addressType == slotAddressTypeUniqueid)
927 {
928 i2cBus = "/dev/i2c-" +
929 std::to_string(static_cast<uint8_t>(bbSlotNum) |
930 (static_cast<uint8_t>(riserSlotNum) << 3));
931 }
932 else
933 {
934 phosphor::logging::log<phosphor::logging::level::ERR>(
935 "Master write read command: invalid request");
936 return ipmi::responseInvalidFieldRequest();
937 }
938
939 // Allow single byte write as it is offset byte to read the data, rest allow
Richard Marian Thomaiyarae13ac62019-12-17 15:45:12 +0530940 // only in Special mode.
Yong Lif267a672019-08-29 11:16:07 +0800941 if (writeCount > 1)
942 {
Richard Marian Thomaiyarae13ac62019-12-17 15:45:12 +0530943 if (mtm.getMfgMode() == SpecialMode::none)
Yong Lif267a672019-08-29 11:16:07 +0800944 {
945 return ipmi::responseInsufficientPrivilege();
946 }
947 }
948
949 if (readCount > slotI2CMaxReadSize)
950 {
951 phosphor::logging::log<phosphor::logging::level::ERR>(
952 "Master write read command: Read count exceeds limit");
953 return ipmi::responseParmOutOfRange();
954 }
955
956 if (!readCount && !writeCount)
957 {
958 phosphor::logging::log<phosphor::logging::level::ERR>(
959 "Master write read command: Read & write count are 0");
960 return ipmi::responseInvalidFieldRequest();
961 }
962
963 std::vector<uint8_t> readBuf(readCount);
964
965 ipmi::Cc retI2C = ipmi::i2cWriteRead(i2cBus, slaveAddr, writeData, readBuf);
966 if (retI2C != ipmi::ccSuccess)
967 {
968 return ipmi::response(retI2C);
969 }
970
971 return ipmi::responseSuccess(readBuf);
972}
Yong Li068b4f22019-09-17 16:32:18 +0800973
974ipmi::RspType<> clearCMOS()
975{
Yong Lid0d010b2019-11-18 12:15:21 +0800976 // There is an i2c device on bus 4, the slave address is 0x38. Based on the
Yong Lieaeb6cb2020-03-09 20:21:50 +0800977 // spec, writing 0x1 to address 0x61 on this device, will trigger the clear
Yong Li068b4f22019-09-17 16:32:18 +0800978 // CMOS action.
Yong Lid0d010b2019-11-18 12:15:21 +0800979 constexpr uint8_t slaveAddr = 0x38;
Yong Li068b4f22019-09-17 16:32:18 +0800980 std::string i2cBus = "/dev/i2c-4";
Yong Lieaeb6cb2020-03-09 20:21:50 +0800981 std::vector<uint8_t> writeData = {0x61, 0x1};
Yong Li068b4f22019-09-17 16:32:18 +0800982 std::vector<uint8_t> readBuf(0);
983
Yong Li068b4f22019-09-17 16:32:18 +0800984 ipmi::Cc retI2C = ipmi::i2cWriteRead(i2cBus, slaveAddr, writeData, readBuf);
985 return ipmi::response(retI2C);
986}
Vernon Mauery27d23562021-08-12 10:43:39 -0700987
988ipmi::RspType<> setFITcLayout(uint32_t layout)
989{
990 static constexpr const char* factoryFITcLayout =
991 "/var/sofs/factory-settings/layout/fitc";
992 std::filesystem::path fitcDir =
993 std::filesystem::path(factoryFITcLayout).parent_path();
994 std::error_code ec;
995 std::filesystem::create_directories(fitcDir, ec);
996 if (ec)
997 {
998 return ipmi::responseUnspecifiedError();
999 }
1000 try
1001 {
1002 std::ofstream file(factoryFITcLayout);
1003 file << layout;
1004 file.flush();
1005 file.close();
1006 }
1007 catch (const std::exception& e)
1008 {
1009 return ipmi::responseUnspecifiedError();
1010 }
1011
1012 return ipmi::responseSuccess();
1013}
1014
Arun P. Mohanan06584cd2021-08-13 20:56:01 +05301015static std::vector<std::string>
1016 getMCTPServiceConfigPaths(ipmi::Context::ptr& ctx)
1017{
1018 boost::system::error_code ec;
1019 auto configPaths = ctx->bus->yield_method_call<std::vector<std::string>>(
1020 ctx->yield, ec, "xyz.openbmc_project.ObjectMapper",
1021 "/xyz/openbmc_project/object_mapper",
1022 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths",
1023 "/xyz/openbmc_project/inventory/system/board", 2,
1024 std::array<const char*, 1>{
1025 "xyz.openbmc_project.Configuration.MctpConfiguration"});
1026 if (ec)
1027 {
1028 throw std::runtime_error(
1029 "Failed to query configuration sub tree objects");
1030 }
1031 return configPaths;
1032}
1033
1034static ipmi::RspType<> startOrStopService(ipmi::Context::ptr& ctx,
1035 const uint8_t enable,
1036 const std::string& serviceName)
1037{
1038 constexpr bool runtimeOnly = false;
1039 constexpr bool force = false;
1040
1041 boost::system::error_code ec;
1042 switch (enable)
1043 {
1044 case ipmi::SupportedFeatureActions::stop:
1045 ctx->bus->yield_method_call(ctx->yield, ec, systemDService,
1046 systemDObjPath, systemDMgrIntf,
1047 "StopUnit", serviceName, "replace");
1048 break;
1049 case ipmi::SupportedFeatureActions::start:
1050 ctx->bus->yield_method_call(ctx->yield, ec, systemDService,
1051 systemDObjPath, systemDMgrIntf,
1052 "StartUnit", serviceName, "replace");
1053 break;
1054 case ipmi::SupportedFeatureActions::disable:
1055 ctx->bus->yield_method_call(
1056 ctx->yield, ec, systemDService, systemDObjPath, systemDMgrIntf,
1057 "MaskUnitFiles",
1058 std::array<const char*, 1>{serviceName.c_str()}, runtimeOnly,
1059 force);
1060 ctx->bus->yield_method_call(
1061 ctx->yield, ec, systemDService, systemDObjPath, systemDMgrIntf,
1062 "DisableUnitFiles",
1063 std::array<const char*, 1>{serviceName.c_str()}, runtimeOnly);
1064 break;
1065 case ipmi::SupportedFeatureActions::enable:
1066 ctx->bus->yield_method_call(
1067 ctx->yield, ec, systemDService, systemDObjPath, systemDMgrIntf,
1068 "UnmaskUnitFiles",
1069 std::array<const char*, 1>{serviceName.c_str()}, runtimeOnly);
1070 ctx->bus->yield_method_call(
1071 ctx->yield, ec, systemDService, systemDObjPath, systemDMgrIntf,
1072 "EnableUnitFiles",
1073 std::array<const char*, 1>{serviceName.c_str()}, runtimeOnly,
1074 force);
1075 break;
1076 default:
1077 phosphor::logging::log<phosphor::logging::level::WARNING>(
1078 "ERROR: Invalid feature action selected",
1079 phosphor::logging::entry("ACTION=%d", enable));
1080 return ipmi::responseInvalidFieldRequest();
1081 }
1082 if (ec)
1083 {
1084 phosphor::logging::log<phosphor::logging::level::WARNING>(
1085 "ERROR: Service start or stop failed",
1086 phosphor::logging::entry("SERVICE=%s", serviceName.c_str()));
1087 return ipmi::responseUnspecifiedError();
1088 }
1089 return ipmi::responseSuccess();
1090}
1091
1092static std::string getMCTPServiceName(const std::string& objectPath)
1093{
1094 const auto serviceArgument = boost::algorithm::replace_all_copy(
1095 boost::algorithm::replace_first_copy(
1096 objectPath, "/xyz/openbmc_project/inventory/system/board/", ""),
1097 "/", "_2f");
1098 std::string unitName =
1099 "xyz.openbmc_project.mctpd@" + serviceArgument + ".service";
1100 return unitName;
1101}
1102
1103static ipmi::RspType<> handleMCTPFeature(ipmi::Context::ptr& ctx,
1104 const uint8_t enable,
1105 const std::string& binding)
1106{
1107 std::vector<std::string> configPaths;
1108 try
1109 {
1110 configPaths = getMCTPServiceConfigPaths(ctx);
1111 }
1112 catch (const std::exception& e)
1113 {
1114 phosphor::logging::log<phosphor::logging::level::ERR>(e.what());
1115 return ipmi::responseUnspecifiedError();
1116 }
1117
1118 for (const auto& objectPath : configPaths)
1119 {
1120 auto const pos = objectPath.find_last_of('/');
1121 if (binding == objectPath.substr(pos + 1))
1122 {
1123 return startOrStopService(ctx, enable,
1124 getMCTPServiceName(objectPath));
1125 }
1126 }
1127 return ipmi::responseSuccess();
1128}
1129
1130/** @brief implements MTM BMC Feature Control IPMI command which can be
1131 * used to enable or disable the supported BMC features.
1132 * @param yield - context object that represents the currently executing
1133 * coroutine
1134 * @param feature - feature enum to enable or disable
1135 * @param enable - enable or disable the feature
1136 * @param featureArg - custom arguments for that feature
1137 * @param reserved - reserved for future use
1138 *
1139 * @returns IPMI completion code
1140 */
1141ipmi::RspType<> mtmBMCFeatureControl(ipmi::Context::ptr ctx,
1142 const uint8_t feature,
1143 const uint8_t enable,
1144 const uint8_t featureArg,
1145 const uint16_t reserved)
1146{
1147 if (reserved != 0)
1148 {
1149 return ipmi::responseInvalidFieldRequest();
1150 }
1151
1152 switch (feature)
1153 {
1154 case ipmi::SupportedFeatureControls::mctp:
1155 switch (featureArg)
1156 {
1157 case ipmi::SupportedMCTPBindings::mctpPCIe:
1158 return handleMCTPFeature(ctx, enable, "MCTP_PCIe");
1159 case ipmi::SupportedMCTPBindings::mctpSMBusHSBP:
1160 return handleMCTPFeature(ctx, enable, "MCTP_SMBus_HSBP");
1161 case ipmi::SupportedMCTPBindings::mctpSMBusPCIeSlot:
1162 return handleMCTPFeature(ctx, enable,
1163 "MCTP_SMBus_PCIe_slot");
1164 default:
1165 return ipmi::responseInvalidFieldRequest();
1166 }
1167 break;
Jason M. Bills5cb2c042021-08-17 12:03:39 -07001168 case ipmi::SupportedFeatureControls::pcieScan:
1169 if (featureArg != 0)
1170 {
1171 return ipmi::responseInvalidFieldRequest();
1172 }
1173 startOrStopService(ctx, enable, "xyz.openbmc_project.PCIe.service");
1174 break;
Arun P. Mohanan06584cd2021-08-13 20:56:01 +05301175 default:
1176 return ipmi::responseInvalidFieldRequest();
1177 }
1178 return ipmi::responseSuccess();
1179}
Vernon Mauerya3702c12019-05-22 13:20:59 -07001180} // namespace ipmi
1181
1182void register_mtm_commands() __attribute__((constructor));
1183void register_mtm_commands()
1184{
Jason M. Bills38d2b5a2019-06-03 16:26:11 -07001185 // <Get SM Signal>
Vernon Mauery98bbf692019-09-16 11:14:59 -07001186 ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnGeneral,
1187 ipmi::intel::general::cmdGetSmSignal,
1188 ipmi::Privilege::Admin, ipmi::appMTMGetSignal);
Vernon Mauerya3702c12019-05-22 13:20:59 -07001189
Vernon Mauery98bbf692019-09-16 11:14:59 -07001190 ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnGeneral,
1191 ipmi::intel::general::cmdSetSmSignal,
1192 ipmi::Privilege::Admin, ipmi::appMTMSetSignal);
Vernon Mauerya3702c12019-05-22 13:20:59 -07001193
Vernon Mauery98bbf692019-09-16 11:14:59 -07001194 ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnGeneral,
1195 ipmi::intel::general::cmdMtmKeepAlive,
1196 ipmi::Privilege::Admin, ipmi::mtmKeepAlive);
Richard Marian Thomaiyar666dd012019-08-02 20:55:37 +05301197
Vernon Mauery98bbf692019-09-16 11:14:59 -07001198 ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnGeneral,
1199 ipmi::intel::general::cmdSetManufacturingData,
1200 ipmi::Privilege::Admin, ipmi::setManufacturingData);
Richard Marian Thomaiyar1f0839c2019-08-25 20:10:52 +05301201
Vernon Mauery98bbf692019-09-16 11:14:59 -07001202 ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnGeneral,
1203 ipmi::intel::general::cmdGetManufacturingData,
1204 ipmi::Privilege::Admin, ipmi::getManufacturingData);
Richard Marian Thomaiyar1f0839c2019-08-25 20:10:52 +05301205
Vernon Mauery27d23562021-08-12 10:43:39 -07001206 ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnGeneral,
1207 ipmi::intel::general::cmdSetFITcLayout,
1208 ipmi::Privilege::Admin, ipmi::setFITcLayout);
1209
Arun P. Mohanan06584cd2021-08-13 20:56:01 +05301210 ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnGeneral,
1211 ipmi::intel::general::cmdMTMBMCFeatureControl,
1212 ipmi::Privilege::Admin, ipmi::mtmBMCFeatureControl);
1213
Vernon Mauery98bbf692019-09-16 11:14:59 -07001214 ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnApp,
1215 ipmi::intel::general::cmdSlotI2CMasterWriteRead,
1216 ipmi::Privilege::Admin,
1217 ipmi::appSlotI2CMasterWriteRead);
Yong Lif267a672019-08-29 11:16:07 +08001218
Yong Li068b4f22019-09-17 16:32:18 +08001219 ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnPlatform,
1220 ipmi::intel::platform::cmdClearCMOS,
1221 ipmi::Privilege::Admin, ipmi::clearCMOS);
1222
Vernon Mauery98bbf692019-09-16 11:14:59 -07001223 ipmi::registerFilter(ipmi::prioOemBase,
Yong Li85feb132019-08-09 16:06:03 +08001224 [](ipmi::message::Request::ptr request) {
1225 return ipmi::mfgFilterMessage(request);
1226 });
Vernon Mauerya3702c12019-05-22 13:20:59 -07001227}