blob: d8b49dc3b65f5cb32e59a22c3a34adc6485eda23 [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
Richard Marian Thomaiyar147daec2019-06-15 07:43:48 +053019#include <boost/container/flat_map.hpp>
Yong Lif267a672019-08-29 11:16:07 +080020#include <filesystem>
Richard Marian Thomaiyar1b74a212019-08-03 13:26:17 +053021#include <fstream>
Vernon Mauerya3702c12019-05-22 13:20:59 -070022#include <ipmid/api.hpp>
23#include <manufacturingcommands.hpp>
24#include <oemcommands.hpp>
25
26namespace ipmi
27{
28
29Manufacturing mtm;
30
31static auto revertTimeOut =
32 std::chrono::duration_cast<std::chrono::microseconds>(
33 std::chrono::seconds(60)); // 1 minute timeout
34
Yong Lif267a672019-08-29 11:16:07 +080035static constexpr uint8_t slotAddressTypeBus = 0;
36static constexpr uint8_t slotAddressTypeUniqueid = 1;
37static constexpr uint8_t slotI2CMaxReadSize = 35;
38
Vernon Mauerya3702c12019-05-22 13:20:59 -070039static constexpr const char* callbackMgrService =
40 "xyz.openbmc_project.CallbackManager";
41static constexpr const char* callbackMgrIntf =
42 "xyz.openbmc_project.CallbackManager";
43static constexpr const char* callbackMgrObjPath =
44 "/xyz/openbmc_project/CallbackManager";
45static constexpr const char* retriggerLedUpdate = "RetriggerLEDUpdate";
46
47const static constexpr char* systemDService = "org.freedesktop.systemd1";
48const static constexpr char* systemDObjPath = "/org/freedesktop/systemd1";
49const static constexpr char* systemDMgrIntf =
50 "org.freedesktop.systemd1.Manager";
51const static constexpr char* pidControlService = "phosphor-pid-control.service";
52
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +053053static inline Cc resetMtmTimer(ipmi::Context::ptr ctx)
Richard Marian Thomaiyar666dd012019-08-02 20:55:37 +053054{
Richard Marian Thomaiyar666dd012019-08-02 20:55:37 +053055 boost::system::error_code ec;
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +053056 ctx->bus->yield_method_call<>(ctx->yield, ec, specialModeService,
57 specialModeObjPath, specialModeIntf,
58 "ResetTimer");
Richard Marian Thomaiyar666dd012019-08-02 20:55:37 +053059 if (ec)
60 {
61 phosphor::logging::log<phosphor::logging::level::ERR>(
62 "Failed to reset the manufacturing mode timer");
63 return ccUnspecifiedError;
64 }
65 return ccSuccess;
66}
67
Jason M. Bills38d2b5a2019-06-03 16:26:11 -070068int getGpioPathForSmSignal(const SmSignalGet signal, std::string& path)
Vernon Mauerya3702c12019-05-22 13:20:59 -070069{
Jason M. Bills38d2b5a2019-06-03 16:26:11 -070070 switch (signal)
71 {
72 case SmSignalGet::smPowerButton:
73 path = "/xyz/openbmc_project/chassis/buttons/power";
74 break;
75 case SmSignalGet::smResetButton:
76 path = "/xyz/openbmc_project/chassis/buttons/reset";
77 break;
78 case SmSignalGet::smNMIButton:
79 path = "/xyz/openbmc_project/chassis/buttons/nmi";
80 break;
Richard Marian Thomaiyar8e5e2b02019-08-01 07:50:55 +053081 case SmSignalGet::smIdentifyButton:
82 path = "/xyz/openbmc_project/chassis/buttons/id";
83 break;
Jason M. Bills38d2b5a2019-06-03 16:26:11 -070084 default:
85 return -1;
86 break;
87 }
88 return 0;
Vernon Mauerya3702c12019-05-22 13:20:59 -070089}
90
Patrick Venture37890392019-09-25 17:05:03 -070091ipmi_ret_t ledStoreAndSet(SmSignalSet signal, const std::string& setState)
Vernon Mauerya3702c12019-05-22 13:20:59 -070092{
93 LedProperty* ledProp = mtm.findLedProperty(signal);
94 if (ledProp == nullptr)
95 {
96 return IPMI_CC_INVALID_FIELD_REQUEST;
97 }
98
99 std::string ledName = ledProp->getName();
100 std::string ledService = ledServicePrefix + ledName;
101 std::string ledPath = ledPathPrefix + ledName;
102 ipmi::Value presentState;
103
104 if (false == ledProp->getLock())
105 {
106 if (mtm.getProperty(ledService.c_str(), ledPath.c_str(), ledIntf,
107 "State", &presentState) != 0)
108 {
109 return IPMI_CC_UNSPECIFIED_ERROR;
110 }
111 ledProp->setPrevState(std::get<std::string>(presentState));
112 ledProp->setLock(true);
113 if (signal == SmSignalSet::smPowerFaultLed ||
114 signal == SmSignalSet::smSystemReadyLed)
115 {
116 mtm.revertLedCallback = true;
117 }
118 }
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700119 if (mtm.setProperty(ledService, ledPath, ledIntf, "State",
Vernon Mauerya3702c12019-05-22 13:20:59 -0700120 ledStateStr + setState) != 0)
121 {
122 return IPMI_CC_UNSPECIFIED_ERROR;
123 }
124 return IPMI_CC_OK;
125}
126
127ipmi_ret_t ledRevert(SmSignalSet signal)
128{
129 LedProperty* ledProp = mtm.findLedProperty(signal);
130 if (ledProp == nullptr)
131 {
132 return IPMI_CC_INVALID_FIELD_REQUEST;
133 }
134 if (true == ledProp->getLock())
135 {
136 ledProp->setLock(false);
137 if (signal == SmSignalSet::smPowerFaultLed ||
138 signal == SmSignalSet::smSystemReadyLed)
139 {
140 try
141 {
142 ipmi::method_no_args::callDbusMethod(
143 *getSdBus(), callbackMgrService, callbackMgrObjPath,
144 callbackMgrIntf, retriggerLedUpdate);
145 }
146 catch (sdbusplus::exception_t& e)
147 {
148 return IPMI_CC_UNSPECIFIED_ERROR;
149 }
150 mtm.revertLedCallback = false;
151 }
152 else
153 {
154 std::string ledName = ledProp->getName();
155 std::string ledService = ledServicePrefix + ledName;
156 std::string ledPath = ledPathPrefix + ledName;
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700157 if (mtm.setProperty(ledService, ledPath, ledIntf, "State",
158 ledProp->getPrevState()) != 0)
Vernon Mauerya3702c12019-05-22 13:20:59 -0700159 {
160 return IPMI_CC_UNSPECIFIED_ERROR;
161 }
162 }
163 }
164 return IPMI_CC_OK;
165}
166
167void Manufacturing::initData()
168{
Vernon Mauerya3702c12019-05-22 13:20:59 -0700169 ledPropertyList.push_back(
170 LedProperty(SmSignalSet::smPowerFaultLed, "status_amber"));
171 ledPropertyList.push_back(
172 LedProperty(SmSignalSet::smSystemReadyLed, "status_green"));
173 ledPropertyList.push_back(
174 LedProperty(SmSignalSet::smIdentifyLed, "identify"));
175}
176
177void Manufacturing::revertTimerHandler()
178{
Vernon Mauerya3702c12019-05-22 13:20:59 -0700179 if (revertFanPWM)
180 {
181 revertFanPWM = false;
182 disablePidControlService(false);
183 }
184
Ayushi Smritid872a4a2019-10-15 10:20:11 +0530185 if (mtmTestBeepFd != -1)
186 {
187 ::close(mtmTestBeepFd);
188 mtmTestBeepFd = -1;
189 }
190
Vernon Mauerya3702c12019-05-22 13:20:59 -0700191 for (const auto& ledProperty : ledPropertyList)
192 {
193 const std::string& ledName = ledProperty.getName();
194 ledRevert(ledProperty.getSignal());
195 }
196}
197
198Manufacturing::Manufacturing() :
199 revertTimer([&](void) { revertTimerHandler(); })
200{
201 initData();
202}
203
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700204int8_t Manufacturing::getProperty(const std::string& service,
205 const std::string& path,
206 const std::string& interface,
207 const std::string& propertyName,
208 ipmi::Value* reply)
Vernon Mauerya3702c12019-05-22 13:20:59 -0700209{
210 try
211 {
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700212 *reply = ipmi::getDbusProperty(*getSdBus(), service, path, interface,
213 propertyName);
Vernon Mauerya3702c12019-05-22 13:20:59 -0700214 }
215 catch (const sdbusplus::exception::SdBusError& e)
216 {
217 phosphor::logging::log<phosphor::logging::level::INFO>(
218 "ERROR: getProperty");
219 return -1;
220 }
221
222 return 0;
223}
224
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700225int8_t Manufacturing::setProperty(const std::string& service,
226 const std::string& path,
227 const std::string& interface,
228 const std::string& propertyName,
229 ipmi::Value value)
Vernon Mauerya3702c12019-05-22 13:20:59 -0700230{
231 try
232 {
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700233 ipmi::setDbusProperty(*getSdBus(), service, path, interface,
Vernon Mauerya3702c12019-05-22 13:20:59 -0700234 propertyName, value);
235 }
236 catch (const sdbusplus::exception::SdBusError& e)
237 {
238 phosphor::logging::log<phosphor::logging::level::INFO>(
239 "ERROR: setProperty");
240 return -1;
241 }
242
243 return 0;
244}
245
246int8_t Manufacturing::disablePidControlService(const bool disable)
247{
248 try
249 {
250 auto dbus = getSdBus();
251 auto method = dbus->new_method_call(systemDService, systemDObjPath,
252 systemDMgrIntf,
253 disable ? "StopUnit" : "StartUnit");
254 method.append(pidControlService, "replace");
255 auto reply = dbus->call(method);
256 }
257 catch (const sdbusplus::exception::SdBusError& e)
258 {
259 phosphor::logging::log<phosphor::logging::level::INFO>(
260 "ERROR: phosphor-pid-control service start or stop failed");
261 return -1;
262 }
263 return 0;
264}
265
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700266ipmi::RspType<uint8_t, // Signal value
267 std::optional<uint16_t> // Fan tach value
268 >
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +0530269 appMTMGetSignal(ipmi::Context::ptr ctx, uint8_t signalTypeByte,
Richard Marian Thomaiyar147daec2019-06-15 07:43:48 +0530270 uint8_t instance, uint8_t actionByte)
Vernon Mauerya3702c12019-05-22 13:20:59 -0700271{
Ayushi Smritie0511e52019-08-27 17:30:34 +0000272 // mfg filter logic is used to allow MTM get signal command only in
273 // manfacturing mode.
Vernon Mauerya3702c12019-05-22 13:20:59 -0700274
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700275 SmSignalGet signalType = static_cast<SmSignalGet>(signalTypeByte);
276 SmActionGet action = static_cast<SmActionGet>(actionByte);
277
278 switch (signalType)
279 {
anil kumar appana98705b32019-09-11 14:15:50 +0000280 case SmSignalGet::smChassisIntrusion:
281 {
282 ipmi::Value reply;
283 if (mtm.getProperty(intrusionService, intrusionPath, intrusionIntf,
284 "Status", &reply) < 0)
285 {
286 return ipmi::responseInvalidFieldRequest();
287 }
288 std::string* intrusionStatus = std::get_if<std::string>(&reply);
289 if (!intrusionStatus)
290 {
291 return ipmi::responseUnspecifiedError();
292 }
293
294 uint8_t status = 0;
295 if (!intrusionStatus->compare("Normal"))
296 {
297 status = static_cast<uint8_t>(IntrusionStatus::normal);
298 }
299 else if (!intrusionStatus->compare("HardwareIntrusion"))
300 {
301 status =
302 static_cast<uint8_t>(IntrusionStatus::hardwareIntrusion);
303 }
304 else if (!intrusionStatus->compare("TamperingDetected"))
305 {
306 status =
307 static_cast<uint8_t>(IntrusionStatus::tamperingDetected);
308 }
309 else
310 {
311 return ipmi::responseUnspecifiedError();
312 }
313 return ipmi::responseSuccess(status, std::nullopt);
314 }
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700315 case SmSignalGet::smFanPwmGet:
316 {
317 ipmi::Value reply;
Richard Marian Thomaiyar147daec2019-06-15 07:43:48 +0530318 std::string fullPath = fanPwmPath + std::to_string(instance + 1);
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700319 if (mtm.getProperty(fanService, fullPath, fanIntf, "Value",
320 &reply) < 0)
321 {
322 return ipmi::responseInvalidFieldRequest();
323 }
324 double* doubleVal = std::get_if<double>(&reply);
325 if (doubleVal == nullptr)
326 {
327 return ipmi::responseUnspecifiedError();
328 }
329 uint8_t sensorVal = std::round(*doubleVal);
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +0530330 resetMtmTimer(ctx);
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700331 return ipmi::responseSuccess(sensorVal, std::nullopt);
332 }
333 break;
334 case SmSignalGet::smFanTachometerGet:
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700335 {
Richard Marian Thomaiyar147daec2019-06-15 07:43:48 +0530336 boost::system::error_code ec;
337 using objFlatMap = boost::container::flat_map<
338 std::string, boost::container::flat_map<
339 std::string, std::vector<std::string>>>;
340
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +0530341 auto flatMap = ctx->bus->yield_method_call<objFlatMap>(
342 ctx->yield, ec, "xyz.openbmc_project.ObjectMapper",
Richard Marian Thomaiyar147daec2019-06-15 07:43:48 +0530343 "/xyz/openbmc_project/object_mapper",
344 "xyz.openbmc_project.ObjectMapper", "GetSubTree",
345 fanTachBasePath, 0, std::array<const char*, 1>{fanIntf});
346 if (ec)
347 {
348 phosphor::logging::log<phosphor::logging::level::ERR>(
349 "Failed to query fan tach sub tree objects");
350 return ipmi::responseUnspecifiedError();
351 }
352 if (instance >= flatMap.size())
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700353 {
354 return ipmi::responseInvalidFieldRequest();
355 }
Richard Marian Thomaiyar147daec2019-06-15 07:43:48 +0530356 auto itr = flatMap.nth(instance);
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700357 ipmi::Value reply;
Richard Marian Thomaiyar147daec2019-06-15 07:43:48 +0530358 if (mtm.getProperty(fanService, itr->first, fanIntf, "Value",
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700359 &reply) < 0)
360 {
361 return ipmi::responseInvalidFieldRequest();
362 }
363
364 double* doubleVal = std::get_if<double>(&reply);
365 if (doubleVal == nullptr)
366 {
367 return ipmi::responseUnspecifiedError();
368 }
369 uint8_t sensorVal = FAN_PRESENT | FAN_SENSOR_PRESENT;
370 std::optional<uint16_t> fanTach = std::round(*doubleVal);
371
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +0530372 resetMtmTimer(ctx);
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700373 return ipmi::responseSuccess(sensorVal, fanTach);
374 }
375 break;
Richard Marian Thomaiyar8e5e2b02019-08-01 07:50:55 +0530376 case SmSignalGet::smIdentifyButton:
377 {
378 if (action == SmActionGet::revert || action == SmActionGet::ignore)
379 {
380 // ButtonMasked property is not supported for ID button as it is
381 // unnecessary. Hence if requested for revert / ignore, override
382 // it to sample action to make tools happy.
383 action = SmActionGet::sample;
384 }
385 // fall-through
386 }
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700387 case SmSignalGet::smResetButton:
388 case SmSignalGet::smPowerButton:
389 case SmSignalGet::smNMIButton:
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700390 {
391 std::string path;
392 if (getGpioPathForSmSignal(signalType, path) < 0)
393 {
394 return ipmi::responseInvalidFieldRequest();
395 }
396
397 switch (action)
398 {
399 case SmActionGet::sample:
400 phosphor::logging::log<phosphor::logging::level::INFO>(
401 "case SmActionGet::sample");
402 break;
403 case SmActionGet::ignore:
404 {
405 phosphor::logging::log<phosphor::logging::level::INFO>(
406 "case SmActionGet::ignore");
407 if (mtm.setProperty(buttonService, path, buttonIntf,
408 "ButtonMasked", true) < 0)
409 {
410 return ipmi::responseUnspecifiedError();
411 }
412 }
413 break;
414 case SmActionGet::revert:
415 {
416 phosphor::logging::log<phosphor::logging::level::INFO>(
417 "case SmActionGet::revert");
418 if (mtm.setProperty(buttonService, path, buttonIntf,
419 "ButtonMasked", false) < 0)
420 {
421 return ipmi::responseUnspecifiedError();
422 }
423 }
424 break;
425
426 default:
427 return ipmi::responseInvalidFieldRequest();
428 break;
429 }
430
431 ipmi::Value reply;
432 if (mtm.getProperty(buttonService, path, buttonIntf,
433 "ButtonPressed", &reply) < 0)
434 {
435 return ipmi::responseUnspecifiedError();
436 }
437 bool* valPtr = std::get_if<bool>(&reply);
438 if (valPtr == nullptr)
439 {
440 return ipmi::responseUnspecifiedError();
441 }
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +0530442 resetMtmTimer(ctx);
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700443 uint8_t sensorVal = *valPtr;
444 return ipmi::responseSuccess(sensorVal, std::nullopt);
445 }
446 break;
Richard Marian Thomaiyar1b74a212019-08-03 13:26:17 +0530447 case SmSignalGet::smNcsiDiag:
448 {
449 constexpr const char* netBasePath = "/sys/class/net/eth";
450 constexpr const char* carrierSuffix = "/carrier";
451 std::ifstream netIfs(netBasePath + std::to_string(instance) +
452 carrierSuffix);
453 if (!netIfs.good())
454 {
455 return ipmi::responseInvalidFieldRequest();
456 }
457 std::string carrier;
458 netIfs >> carrier;
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +0530459 resetMtmTimer(ctx);
Richard Marian Thomaiyar1b74a212019-08-03 13:26:17 +0530460 return ipmi::responseSuccess(
461 static_cast<uint8_t>(std::stoi(carrier)), std::nullopt);
462 }
463 break;
Vernon Mauerya3702c12019-05-22 13:20:59 -0700464 default:
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700465 return ipmi::responseInvalidFieldRequest();
Vernon Mauerya3702c12019-05-22 13:20:59 -0700466 break;
467 }
Vernon Mauerya3702c12019-05-22 13:20:59 -0700468}
469
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +0530470ipmi::RspType<> appMTMSetSignal(ipmi::Context::ptr ctx, uint8_t signalTypeByte,
471 uint8_t instance, uint8_t actionByte,
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000472 std::optional<uint8_t> pwmSpeed)
Vernon Mauerya3702c12019-05-22 13:20:59 -0700473{
Ayushi Smritie0511e52019-08-27 17:30:34 +0000474 // mfg filter logic is used to allow MTM set signal command only in
475 // manfacturing mode.
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000476
477 SmSignalSet signalType = static_cast<SmSignalSet>(signalTypeByte);
478 SmActionSet action = static_cast<SmActionSet>(actionByte);
479 Cc retCode = ccSuccess;
480 int8_t ret = 0;
481
482 switch (signalType)
483 {
484 case SmSignalSet::smPowerFaultLed:
485 case SmSignalSet::smSystemReadyLed:
486 case SmSignalSet::smIdentifyLed:
487 switch (action)
488 {
489 case SmActionSet::forceDeasserted:
Vernon Mauerya3702c12019-05-22 13:20:59 -0700490 {
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000491 phosphor::logging::log<phosphor::logging::level::INFO>(
492 "case SmActionSet::forceDeasserted");
Vernon Mauerya3702c12019-05-22 13:20:59 -0700493
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000494 retCode = ledStoreAndSet(signalType, std::string("Off"));
495 if (retCode != ccSuccess)
Vernon Mauerya3702c12019-05-22 13:20:59 -0700496 {
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000497 return ipmi::response(retCode);
Vernon Mauerya3702c12019-05-22 13:20:59 -0700498 }
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000499 mtm.revertTimer.start(revertTimeOut);
Vernon Mauerya3702c12019-05-22 13:20:59 -0700500 }
501 break;
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000502 case SmActionSet::forceAsserted:
Vernon Mauerya3702c12019-05-22 13:20:59 -0700503 {
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000504 phosphor::logging::log<phosphor::logging::level::INFO>(
505 "case SmActionSet::forceAsserted");
Vernon Mauerya3702c12019-05-22 13:20:59 -0700506
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000507 retCode = ledStoreAndSet(signalType, std::string("On"));
508 if (retCode != ccSuccess)
509 {
510 return ipmi::response(retCode);
511 }
512 mtm.revertTimer.start(revertTimeOut);
513 if (SmSignalSet::smPowerFaultLed == signalType)
514 {
515 // Deassert "system ready"
516 retCode = ledStoreAndSet(SmSignalSet::smSystemReadyLed,
517 std::string("Off"));
518 }
519 else if (SmSignalSet::smSystemReadyLed == signalType)
520 {
521 // Deassert "fault led"
522 retCode = ledStoreAndSet(SmSignalSet::smPowerFaultLed,
523 std::string("Off"));
524 }
525 }
526 break;
527 case SmActionSet::revert:
528 {
529 phosphor::logging::log<phosphor::logging::level::INFO>(
530 "case SmActionSet::revert");
531 retCode = ledRevert(signalType);
532 }
533 break;
534 default:
535 {
536 return ipmi::responseInvalidFieldRequest();
537 }
538 }
539 break;
540 case SmSignalSet::smFanPowerSpeed:
541 {
542 if ((action == SmActionSet::forceAsserted) && (!pwmSpeed))
543 {
544 return ipmi::responseReqDataLenInvalid();
545 }
546
547 if ((action == SmActionSet::forceAsserted) && (*pwmSpeed > 100))
548 {
549 return ipmi::responseInvalidFieldRequest();
550 }
551
552 uint8_t pwmValue = 0;
553 switch (action)
554 {
555 case SmActionSet::revert:
556 {
557 if (mtm.revertFanPWM)
558 {
559 ret = mtm.disablePidControlService(false);
Vernon Mauerya3702c12019-05-22 13:20:59 -0700560 if (ret < 0)
561 {
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000562 return ipmi::responseUnspecifiedError();
Vernon Mauerya3702c12019-05-22 13:20:59 -0700563 }
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000564 mtm.revertFanPWM = false;
Vernon Mauerya3702c12019-05-22 13:20:59 -0700565 }
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000566 }
567 break;
568 case SmActionSet::forceAsserted:
569 {
570 pwmValue = *pwmSpeed;
571 } // fall-through
572 case SmActionSet::forceDeasserted:
573 {
574 if (!mtm.revertFanPWM)
Vernon Mauerya3702c12019-05-22 13:20:59 -0700575 {
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000576 ret = mtm.disablePidControlService(true);
577 if (ret < 0)
578 {
579 return ipmi::responseUnspecifiedError();
580 }
581 mtm.revertFanPWM = true;
Vernon Mauerya3702c12019-05-22 13:20:59 -0700582 }
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000583 mtm.revertTimer.start(revertTimeOut);
584 std::string fanPwmInstancePath =
585 fanPwmPath + std::to_string(instance + 1);
586
587 ret =
588 mtm.setProperty(fanService, fanPwmInstancePath, fanIntf,
589 "Value", static_cast<double>(pwmValue));
590 if (ret < 0)
591 {
592 return ipmi::responseUnspecifiedError();
593 }
594 }
595 break;
596 default:
597 {
598 return ipmi::responseInvalidFieldRequest();
Vernon Mauerya3702c12019-05-22 13:20:59 -0700599 }
600 }
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000601 }
602 break;
Ayushi Smritid872a4a2019-10-15 10:20:11 +0530603 case SmSignalSet::smSpeaker:
604 {
605 phosphor::logging::log<phosphor::logging::level::INFO>(
606 "Performing Speaker SmActionSet",
607 phosphor::logging::entry("ACTION=%d",
608 static_cast<uint8_t>(action)));
609 switch (action)
610 {
611 case SmActionSet::forceAsserted:
612 {
613 char beepDevName[] = "/dev/input/event0";
614 if (mtm.mtmTestBeepFd != -1)
615 {
616 phosphor::logging::log<phosphor::logging::level::INFO>(
617 "mtm beep device is opened already!");
618 // returning success as already beep is in progress
619 return ipmi::response(retCode);
620 }
621
622 if ((mtm.mtmTestBeepFd =
623 ::open(beepDevName, O_RDWR | O_CLOEXEC)) < 0)
624 {
625 phosphor::logging::log<phosphor::logging::level::ERR>(
626 "Failed to open input device");
627 return ipmi::responseUnspecifiedError();
628 }
629
630 struct input_event event;
631 event.type = EV_SND;
632 event.code = SND_TONE;
633 event.value = 2000;
634
635 if (::write(mtm.mtmTestBeepFd, &event,
636 sizeof(struct input_event)) !=
637 sizeof(struct input_event))
638 {
639 phosphor::logging::log<phosphor::logging::level::ERR>(
640 "Failed to write a tone sound event");
641 ::close(mtm.mtmTestBeepFd);
642 mtm.mtmTestBeepFd = -1;
643 return ipmi::responseUnspecifiedError();
644 }
645 mtm.revertTimer.start(revertTimeOut);
646 }
647 break;
648 case SmActionSet::revert:
649 case SmActionSet::forceDeasserted:
650 {
651 if (mtm.mtmTestBeepFd != -1)
652 {
653 ::close(mtm.mtmTestBeepFd);
654 mtm.mtmTestBeepFd = -1;
655 }
656 }
657 break;
658 default:
659 {
660 return ipmi::responseInvalidFieldRequest();
661 }
662 }
663 }
664 break;
Richard Marian Thomaiyar3594c6d2019-11-19 21:29:04 +0530665 case SmSignalSet::smDiskFaultLed:
666 {
667 boost::system::error_code ec;
668 using objPaths = std::vector<std::string>;
669 std::string driveBasePath =
670 "/xyz/openbmc_project/inventory/item/drive/";
671 static constexpr const char* driveLedIntf =
672 "xyz.openbmc_project.Led.Group";
673 static constexpr const char* hsbpService =
674 "xyz.openbmc_project.HsbpManager";
675
676 auto driveList = ctx->bus->yield_method_call<objPaths>(
677 ctx->yield, ec, "xyz.openbmc_project.ObjectMapper",
678 "/xyz/openbmc_project/object_mapper",
679 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths",
680 driveBasePath, 0, std::array<const char*, 1>{driveLedIntf});
681 if (ec)
682 {
683 phosphor::logging::log<phosphor::logging::level::ERR>(
684 "Failed to query HSBP drive sub tree objects");
685 return ipmi::responseUnspecifiedError();
686 }
687 std::string driveObjPath =
688 driveBasePath + "Drive_" + std::to_string(instance + 1);
689 if (std::find(driveList.begin(), driveList.end(), driveObjPath) ==
690 driveList.end())
691 {
692 return ipmi::responseInvalidFieldRequest();
693 }
694 bool driveLedState = false;
695 switch (action)
696 {
697 case SmActionSet::forceAsserted:
698 {
699 driveLedState = true;
700 }
701 break;
702 case SmActionSet::revert:
703 {
704 driveLedState = false;
705 }
706 break;
707 case SmActionSet::forceDeasserted:
708 {
709 driveLedState = false;
710 }
711 break;
712 default:
713 {
714 return ipmi::responseInvalidFieldRequest();
715 }
716 }
717 ret = mtm.setProperty(hsbpService, driveObjPath, driveLedIntf,
718 "Asserted", driveLedState);
719 if (ret < 0)
720 {
721 return ipmi::responseUnspecifiedError();
722 }
723 }
724 break;
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000725 default:
726 {
727 return ipmi::responseInvalidFieldRequest();
Vernon Mauerya3702c12019-05-22 13:20:59 -0700728 }
729 }
Richard Marian Thomaiyar4cc10152019-08-02 23:21:45 +0530730 if (retCode == ccSuccess)
731 {
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +0530732 resetMtmTimer(ctx);
Richard Marian Thomaiyar4cc10152019-08-02 23:21:45 +0530733 }
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000734 return ipmi::response(retCode);
Vernon Mauerya3702c12019-05-22 13:20:59 -0700735}
736
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +0530737ipmi::RspType<> mtmKeepAlive(ipmi::Context::ptr ctx, uint8_t reserved,
Richard Marian Thomaiyar666dd012019-08-02 20:55:37 +0530738 const std::array<char, 5>& intentionalSignature)
739{
Ayushi Smritie0511e52019-08-27 17:30:34 +0000740 // mfg filter logic is used to allow MTM keep alive command only in
741 // manfacturing mode
742
Richard Marian Thomaiyar666dd012019-08-02 20:55:37 +0530743 constexpr std::array<char, 5> signatureOk = {'I', 'N', 'T', 'E', 'L'};
744 if (intentionalSignature != signatureOk || reserved != 0)
745 {
746 return ipmi::responseInvalidFieldRequest();
747 }
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +0530748 return ipmi::response(resetMtmTimer(ctx));
Richard Marian Thomaiyar666dd012019-08-02 20:55:37 +0530749}
750
Ayushi Smritie0511e52019-08-27 17:30:34 +0000751static constexpr unsigned int makeCmdKey(unsigned int netFn, unsigned int cmd)
752{
753 return (netFn << 8) | cmd;
754}
755
Yong Li85feb132019-08-09 16:06:03 +0800756ipmi::Cc mfgFilterMessage(ipmi::message::Request::ptr request)
757{
Ayushi Smritie0511e52019-08-27 17:30:34 +0000758 // Restricted commands, must be executed only in Manufacturing mode
759 switch (makeCmdKey(request->ctx->netFn, request->ctx->cmd))
Yong Li85feb132019-08-09 16:06:03 +0800760 {
Ayushi Smritie0511e52019-08-27 17:30:34 +0000761 // i2c master write read command needs additional checking
762 case makeCmdKey(ipmi::netFnApp, ipmi::app::cmdMasterWriteRead):
763 if (request->payload.size() > 4)
764 {
765 // Allow write data count > 1, only if it is in MFG mode
Richard Marian Thomaiyar8d4f8d72019-11-11 12:06:40 +0530766 if (!mtm.isMfgMode())
Ayushi Smritie0511e52019-08-27 17:30:34 +0000767 {
768 return ipmi::ccInsufficientPrivilege;
769 }
770 }
jayaprakash Mutyala2d4a0192019-11-11 22:12:45 +0000771 return ipmi::ccSuccess;
Ayushi Smritie0511e52019-08-27 17:30:34 +0000772 case makeCmdKey(ipmi::netFnOemOne,
773 ipmi::intel::general::cmdGetSmSignal):
774 case makeCmdKey(ipmi::netFnOemOne,
775 ipmi::intel::general::cmdSetSmSignal):
776 case makeCmdKey(ipmi::netFnOemOne,
777 ipmi::intel::general::cmdMtmKeepAlive):
778 case makeCmdKey(ipmi::netFnOemOne,
779 ipmi::intel::general::cmdSetManufacturingData):
780 case makeCmdKey(ipmi::netFnOemOne,
781 ipmi::intel::general::cmdGetManufacturingData):
782 case makeCmdKey(ipmi::netFnStorage, ipmi::storage::cmdWriteFruData):
783
784 // Check for MTM mode
Richard Marian Thomaiyar8d4f8d72019-11-11 12:06:40 +0530785 if (!mtm.isMfgMode())
Yong Li85feb132019-08-09 16:06:03 +0800786 {
Ayushi Smritie0511e52019-08-27 17:30:34 +0000787 return ipmi::ccInvalidCommand;
Yong Li85feb132019-08-09 16:06:03 +0800788 }
jayaprakash Mutyala2d4a0192019-11-11 22:12:45 +0000789 return ipmi::ccSuccess;
790 case makeCmdKey(ipmi::netFnStorage, ipmi::storage::cmdDeleteSelEntry):
791 {
792 return ipmi::ccInvalidCommand;
793 }
Yong Li85feb132019-08-09 16:06:03 +0800794 }
Yong Li85feb132019-08-09 16:06:03 +0800795 return ipmi::ccSuccess;
796}
797
Richard Marian Thomaiyar1f0839c2019-08-25 20:10:52 +0530798static constexpr uint8_t maxEthSize = 6;
799static constexpr uint8_t maxSupportedEth = 3;
800static constexpr const char* factoryEthAddrBaseFileName =
801 "/var/sofs/factory-settings/network/mac/eth";
802
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +0530803ipmi::RspType<> setManufacturingData(ipmi::Context::ptr ctx, uint8_t dataType,
Richard Marian Thomaiyar1f0839c2019-08-25 20:10:52 +0530804 std::array<uint8_t, maxEthSize> ethData)
805{
806 // mfg filter logic will restrict this command executing only in mfg mode.
807 if (dataType >= maxSupportedEth)
808 {
809 return ipmi::responseParmOutOfRange();
810 }
811
812 constexpr uint8_t invalidData = 0;
813 constexpr uint8_t validData = 1;
814 constexpr uint8_t ethAddrStrSize =
815 19; // XX:XX:XX:XX:XX:XX + \n + null termination;
816 std::vector<uint8_t> buff(ethAddrStrSize);
817 std::snprintf(reinterpret_cast<char*>(buff.data()), ethAddrStrSize,
818 "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx\n", ethData.at(0),
819 ethData.at(1), ethData.at(2), ethData.at(3), ethData.at(4),
820 ethData.at(5));
821 std::ofstream oEthFile(factoryEthAddrBaseFileName +
822 std::to_string(dataType),
823 std::ofstream::out);
824 if (!oEthFile.good())
825 {
826 return ipmi::responseUnspecifiedError();
827 }
828
829 oEthFile << reinterpret_cast<char*>(buff.data());
830 oEthFile << fflush;
831 oEthFile.close();
832
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +0530833 resetMtmTimer(ctx);
Richard Marian Thomaiyar1f0839c2019-08-25 20:10:52 +0530834 return ipmi::responseSuccess();
835}
836
837ipmi::RspType<uint8_t, std::array<uint8_t, maxEthSize>>
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +0530838 getManufacturingData(ipmi::Context::ptr ctx, uint8_t dataType)
Richard Marian Thomaiyar1f0839c2019-08-25 20:10:52 +0530839{
840 // mfg filter logic will restrict this command executing only in mfg mode.
841 if (dataType >= maxSupportedEth)
842 {
843 return ipmi::responseParmOutOfRange();
844 }
845 std::array<uint8_t, maxEthSize> ethData{0};
846 constexpr uint8_t invalidData = 0;
847 constexpr uint8_t validData = 1;
848
849 std::ifstream iEthFile(factoryEthAddrBaseFileName +
850 std::to_string(dataType),
851 std::ifstream::in);
852 if (!iEthFile.good())
853 {
854 return ipmi::responseSuccess(invalidData, ethData);
855 }
856 std::string ethStr;
857 iEthFile >> ethStr;
858 uint8_t* data = ethData.data();
859 std::sscanf(ethStr.c_str(), "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx",
860 data, (data + 1), (data + 2), (data + 3), (data + 4),
861 (data + 5));
862
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +0530863 resetMtmTimer(ctx);
Richard Marian Thomaiyar1f0839c2019-08-25 20:10:52 +0530864 return ipmi::responseSuccess(validData, ethData);
865}
866
Yong Lif267a672019-08-29 11:16:07 +0800867/** @brief implements slot master write read IPMI command which can be used for
868 * low-level I2C/SMBus write, read or write-read access for PCIE slots
869 * @param reserved - skip 6 bit
870 * @param addressType - address type
871 * @param bbSlotNum - baseboard slot number
872 * @param riserSlotNum - riser slot number
873 * @param reserved2 - skip 2 bit
874 * @param slaveAddr - slave address
875 * @param readCount - number of bytes to be read
876 * @param writeData - data to be written
877 *
878 * @returns IPMI completion code plus response data
879 */
880ipmi::RspType<std::vector<uint8_t>>
881 appSlotI2CMasterWriteRead(uint6_t reserved, uint2_t addressType,
882 uint3_t bbSlotNum, uint3_t riserSlotNum,
883 uint2_t resvered2, uint8_t slaveAddr,
884 uint8_t readCount, std::vector<uint8_t> writeData)
885{
886 const size_t writeCount = writeData.size();
887 std::string i2cBus;
888 if (addressType == slotAddressTypeBus)
889 {
890 std::string path = "/dev/i2c-mux/Riser_" +
891 std::to_string(static_cast<uint8_t>(bbSlotNum)) +
892 "_Mux/Pcie_Slot_" +
893 std::to_string(static_cast<uint8_t>(riserSlotNum));
894
895 if (std::filesystem::exists(path) && std::filesystem::is_symlink(path))
896 {
897 i2cBus = std::filesystem::read_symlink(path);
898 }
899 else
900 {
901 phosphor::logging::log<phosphor::logging::level::ERR>(
902 "Master write read command: Cannot get BusID");
903 return ipmi::responseInvalidFieldRequest();
904 }
905 }
906 else if (addressType == slotAddressTypeUniqueid)
907 {
908 i2cBus = "/dev/i2c-" +
909 std::to_string(static_cast<uint8_t>(bbSlotNum) |
910 (static_cast<uint8_t>(riserSlotNum) << 3));
911 }
912 else
913 {
914 phosphor::logging::log<phosphor::logging::level::ERR>(
915 "Master write read command: invalid request");
916 return ipmi::responseInvalidFieldRequest();
917 }
918
919 // Allow single byte write as it is offset byte to read the data, rest allow
920 // only in MFG mode.
921 if (writeCount > 1)
922 {
Richard Marian Thomaiyar8d4f8d72019-11-11 12:06:40 +0530923 if (!mtm.isMfgMode())
Yong Lif267a672019-08-29 11:16:07 +0800924 {
925 return ipmi::responseInsufficientPrivilege();
926 }
927 }
928
929 if (readCount > slotI2CMaxReadSize)
930 {
931 phosphor::logging::log<phosphor::logging::level::ERR>(
932 "Master write read command: Read count exceeds limit");
933 return ipmi::responseParmOutOfRange();
934 }
935
936 if (!readCount && !writeCount)
937 {
938 phosphor::logging::log<phosphor::logging::level::ERR>(
939 "Master write read command: Read & write count are 0");
940 return ipmi::responseInvalidFieldRequest();
941 }
942
943 std::vector<uint8_t> readBuf(readCount);
944
945 ipmi::Cc retI2C = ipmi::i2cWriteRead(i2cBus, slaveAddr, writeData, readBuf);
946 if (retI2C != ipmi::ccSuccess)
947 {
948 return ipmi::response(retI2C);
949 }
950
951 return ipmi::responseSuccess(readBuf);
952}
Yong Li068b4f22019-09-17 16:32:18 +0800953
954ipmi::RspType<> clearCMOS()
955{
956 // There is an i2c device on bus 4, the slave address is 0x70. Based on the
957 // spec, writing 0x1 to address 0x60 on this device, will trigger the clear
958 // CMOS action.
959 constexpr uint8_t slaveAddr = 0x70;
960 std::string i2cBus = "/dev/i2c-4";
961 std::vector<uint8_t> writeData = {0x60, 0x1};
962 std::vector<uint8_t> readBuf(0);
963
Richard Marian Thomaiyar8d4f8d72019-11-11 12:06:40 +0530964 if (!mtm.isMfgMode())
Yong Li068b4f22019-09-17 16:32:18 +0800965 {
966 return ipmi::responseInsufficientPrivilege();
967 }
968
969 ipmi::Cc retI2C = ipmi::i2cWriteRead(i2cBus, slaveAddr, writeData, readBuf);
970 return ipmi::response(retI2C);
971}
Vernon Mauerya3702c12019-05-22 13:20:59 -0700972} // namespace ipmi
973
974void register_mtm_commands() __attribute__((constructor));
975void register_mtm_commands()
976{
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700977 // <Get SM Signal>
Vernon Mauery98bbf692019-09-16 11:14:59 -0700978 ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnGeneral,
979 ipmi::intel::general::cmdGetSmSignal,
980 ipmi::Privilege::Admin, ipmi::appMTMGetSignal);
Vernon Mauerya3702c12019-05-22 13:20:59 -0700981
Vernon Mauery98bbf692019-09-16 11:14:59 -0700982 ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnGeneral,
983 ipmi::intel::general::cmdSetSmSignal,
984 ipmi::Privilege::Admin, ipmi::appMTMSetSignal);
Vernon Mauerya3702c12019-05-22 13:20:59 -0700985
Vernon Mauery98bbf692019-09-16 11:14:59 -0700986 ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnGeneral,
987 ipmi::intel::general::cmdMtmKeepAlive,
988 ipmi::Privilege::Admin, ipmi::mtmKeepAlive);
Richard Marian Thomaiyar666dd012019-08-02 20:55:37 +0530989
Vernon Mauery98bbf692019-09-16 11:14:59 -0700990 ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnGeneral,
991 ipmi::intel::general::cmdSetManufacturingData,
992 ipmi::Privilege::Admin, ipmi::setManufacturingData);
Richard Marian Thomaiyar1f0839c2019-08-25 20:10:52 +0530993
Vernon Mauery98bbf692019-09-16 11:14:59 -0700994 ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnGeneral,
995 ipmi::intel::general::cmdGetManufacturingData,
996 ipmi::Privilege::Admin, ipmi::getManufacturingData);
Richard Marian Thomaiyar1f0839c2019-08-25 20:10:52 +0530997
Vernon Mauery98bbf692019-09-16 11:14:59 -0700998 ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnApp,
999 ipmi::intel::general::cmdSlotI2CMasterWriteRead,
1000 ipmi::Privilege::Admin,
1001 ipmi::appSlotI2CMasterWriteRead);
Yong Lif267a672019-08-29 11:16:07 +08001002
Yong Li068b4f22019-09-17 16:32:18 +08001003 ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnPlatform,
1004 ipmi::intel::platform::cmdClearCMOS,
1005 ipmi::Privilege::Admin, ipmi::clearCMOS);
1006
Vernon Mauery98bbf692019-09-16 11:14:59 -07001007 ipmi::registerFilter(ipmi::prioOemBase,
Yong Li85feb132019-08-09 16:06:03 +08001008 [](ipmi::message::Request::ptr request) {
1009 return ipmi::mfgFilterMessage(request);
1010 });
Vernon Mauerya3702c12019-05-22 13:20:59 -07001011}