blob: 483e077000bf9cb41b1a99ebf9c3c49234c5ebae [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
Vernon Mauerya3702c12019-05-22 13:20:59 -070017#include <ipmid/api.hpp>
18#include <manufacturingcommands.hpp>
19#include <oemcommands.hpp>
20
21namespace ipmi
22{
23
24Manufacturing mtm;
25
26static auto revertTimeOut =
27 std::chrono::duration_cast<std::chrono::microseconds>(
28 std::chrono::seconds(60)); // 1 minute timeout
29
Vernon Mauerya3702c12019-05-22 13:20:59 -070030static constexpr const char* callbackMgrService =
31 "xyz.openbmc_project.CallbackManager";
32static constexpr const char* callbackMgrIntf =
33 "xyz.openbmc_project.CallbackManager";
34static constexpr const char* callbackMgrObjPath =
35 "/xyz/openbmc_project/CallbackManager";
36static constexpr const char* retriggerLedUpdate = "RetriggerLEDUpdate";
37
38const static constexpr char* systemDService = "org.freedesktop.systemd1";
39const static constexpr char* systemDObjPath = "/org/freedesktop/systemd1";
40const static constexpr char* systemDMgrIntf =
41 "org.freedesktop.systemd1.Manager";
42const static constexpr char* pidControlService = "phosphor-pid-control.service";
43
Richard Marian Thomaiyar666dd012019-08-02 20:55:37 +053044static inline Cc resetMtmTimer(boost::asio::yield_context yield)
45{
46 auto sdbusp = getSdBus();
47 boost::system::error_code ec;
48 sdbusp->yield_method_call<>(yield, ec, specialModeService,
49 specialModeObjPath, specialModeIntf,
50 "ResetTimer");
51 if (ec)
52 {
53 phosphor::logging::log<phosphor::logging::level::ERR>(
54 "Failed to reset the manufacturing mode timer");
55 return ccUnspecifiedError;
56 }
57 return ccSuccess;
58}
59
Jason M. Bills38d2b5a2019-06-03 16:26:11 -070060int getGpioPathForSmSignal(const SmSignalGet signal, std::string& path)
Vernon Mauerya3702c12019-05-22 13:20:59 -070061{
Jason M. Bills38d2b5a2019-06-03 16:26:11 -070062 switch (signal)
63 {
64 case SmSignalGet::smPowerButton:
65 path = "/xyz/openbmc_project/chassis/buttons/power";
66 break;
67 case SmSignalGet::smResetButton:
68 path = "/xyz/openbmc_project/chassis/buttons/reset";
69 break;
70 case SmSignalGet::smNMIButton:
71 path = "/xyz/openbmc_project/chassis/buttons/nmi";
72 break;
Richard Marian Thomaiyar8e5e2b02019-08-01 07:50:55 +053073 case SmSignalGet::smIdentifyButton:
74 path = "/xyz/openbmc_project/chassis/buttons/id";
75 break;
Jason M. Bills38d2b5a2019-06-03 16:26:11 -070076 default:
77 return -1;
78 break;
79 }
80 return 0;
Vernon Mauerya3702c12019-05-22 13:20:59 -070081}
82
83ipmi_ret_t ledStoreAndSet(SmSignalSet signal, std::string setState)
84{
85 LedProperty* ledProp = mtm.findLedProperty(signal);
86 if (ledProp == nullptr)
87 {
88 return IPMI_CC_INVALID_FIELD_REQUEST;
89 }
90
91 std::string ledName = ledProp->getName();
92 std::string ledService = ledServicePrefix + ledName;
93 std::string ledPath = ledPathPrefix + ledName;
94 ipmi::Value presentState;
95
96 if (false == ledProp->getLock())
97 {
98 if (mtm.getProperty(ledService.c_str(), ledPath.c_str(), ledIntf,
99 "State", &presentState) != 0)
100 {
101 return IPMI_CC_UNSPECIFIED_ERROR;
102 }
103 ledProp->setPrevState(std::get<std::string>(presentState));
104 ledProp->setLock(true);
105 if (signal == SmSignalSet::smPowerFaultLed ||
106 signal == SmSignalSet::smSystemReadyLed)
107 {
108 mtm.revertLedCallback = true;
109 }
110 }
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700111 if (mtm.setProperty(ledService, ledPath, ledIntf, "State",
Vernon Mauerya3702c12019-05-22 13:20:59 -0700112 ledStateStr + setState) != 0)
113 {
114 return IPMI_CC_UNSPECIFIED_ERROR;
115 }
116 return IPMI_CC_OK;
117}
118
119ipmi_ret_t ledRevert(SmSignalSet signal)
120{
121 LedProperty* ledProp = mtm.findLedProperty(signal);
122 if (ledProp == nullptr)
123 {
124 return IPMI_CC_INVALID_FIELD_REQUEST;
125 }
126 if (true == ledProp->getLock())
127 {
128 ledProp->setLock(false);
129 if (signal == SmSignalSet::smPowerFaultLed ||
130 signal == SmSignalSet::smSystemReadyLed)
131 {
132 try
133 {
134 ipmi::method_no_args::callDbusMethod(
135 *getSdBus(), callbackMgrService, callbackMgrObjPath,
136 callbackMgrIntf, retriggerLedUpdate);
137 }
138 catch (sdbusplus::exception_t& e)
139 {
140 return IPMI_CC_UNSPECIFIED_ERROR;
141 }
142 mtm.revertLedCallback = false;
143 }
144 else
145 {
146 std::string ledName = ledProp->getName();
147 std::string ledService = ledServicePrefix + ledName;
148 std::string ledPath = ledPathPrefix + ledName;
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700149 if (mtm.setProperty(ledService, ledPath, ledIntf, "State",
150 ledProp->getPrevState()) != 0)
Vernon Mauerya3702c12019-05-22 13:20:59 -0700151 {
152 return IPMI_CC_UNSPECIFIED_ERROR;
153 }
154 }
155 }
156 return IPMI_CC_OK;
157}
158
159void Manufacturing::initData()
160{
Vernon Mauerya3702c12019-05-22 13:20:59 -0700161 ledPropertyList.push_back(
162 LedProperty(SmSignalSet::smPowerFaultLed, "status_amber"));
163 ledPropertyList.push_back(
164 LedProperty(SmSignalSet::smSystemReadyLed, "status_green"));
165 ledPropertyList.push_back(
166 LedProperty(SmSignalSet::smIdentifyLed, "identify"));
167}
168
169void Manufacturing::revertTimerHandler()
170{
Vernon Mauerya3702c12019-05-22 13:20:59 -0700171 if (revertFanPWM)
172 {
173 revertFanPWM = false;
174 disablePidControlService(false);
175 }
176
177 for (const auto& ledProperty : ledPropertyList)
178 {
179 const std::string& ledName = ledProperty.getName();
180 ledRevert(ledProperty.getSignal());
181 }
182}
183
184Manufacturing::Manufacturing() :
185 revertTimer([&](void) { revertTimerHandler(); })
186{
187 initData();
188}
189
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700190int8_t Manufacturing::getProperty(const std::string& service,
191 const std::string& path,
192 const std::string& interface,
193 const std::string& propertyName,
194 ipmi::Value* reply)
Vernon Mauerya3702c12019-05-22 13:20:59 -0700195{
196 try
197 {
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700198 *reply = ipmi::getDbusProperty(*getSdBus(), service, path, interface,
199 propertyName);
Vernon Mauerya3702c12019-05-22 13:20:59 -0700200 }
201 catch (const sdbusplus::exception::SdBusError& e)
202 {
203 phosphor::logging::log<phosphor::logging::level::INFO>(
204 "ERROR: getProperty");
205 return -1;
206 }
207
208 return 0;
209}
210
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700211int8_t Manufacturing::setProperty(const std::string& service,
212 const std::string& path,
213 const std::string& interface,
214 const std::string& propertyName,
215 ipmi::Value value)
Vernon Mauerya3702c12019-05-22 13:20:59 -0700216{
217 try
218 {
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700219 ipmi::setDbusProperty(*getSdBus(), service, path, interface,
Vernon Mauerya3702c12019-05-22 13:20:59 -0700220 propertyName, value);
221 }
222 catch (const sdbusplus::exception::SdBusError& e)
223 {
224 phosphor::logging::log<phosphor::logging::level::INFO>(
225 "ERROR: setProperty");
226 return -1;
227 }
228
229 return 0;
230}
231
232int8_t Manufacturing::disablePidControlService(const bool disable)
233{
234 try
235 {
236 auto dbus = getSdBus();
237 auto method = dbus->new_method_call(systemDService, systemDObjPath,
238 systemDMgrIntf,
239 disable ? "StopUnit" : "StartUnit");
240 method.append(pidControlService, "replace");
241 auto reply = dbus->call(method);
242 }
243 catch (const sdbusplus::exception::SdBusError& e)
244 {
245 phosphor::logging::log<phosphor::logging::level::INFO>(
246 "ERROR: phosphor-pid-control service start or stop failed");
247 return -1;
248 }
249 return 0;
250}
251
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700252ipmi::RspType<uint8_t, // Signal value
253 std::optional<uint16_t> // Fan tach value
254 >
255 appMTMGetSignal(uint8_t signalTypeByte, uint8_t instance,
256 uint8_t actionByte)
Vernon Mauerya3702c12019-05-22 13:20:59 -0700257{
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700258 if (mtm.getAccessLvl() < MtmLvl::mtmAvailable)
Vernon Mauerya3702c12019-05-22 13:20:59 -0700259 {
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700260 return ipmi::responseInvalidCommand();
261 }
Vernon Mauerya3702c12019-05-22 13:20:59 -0700262
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700263 SmSignalGet signalType = static_cast<SmSignalGet>(signalTypeByte);
264 SmActionGet action = static_cast<SmActionGet>(actionByte);
265
266 switch (signalType)
267 {
268 case SmSignalGet::smFanPwmGet:
269 {
270 ipmi::Value reply;
271 std::string fullPath = fanPwmPath + std::to_string(instance);
272 if (mtm.getProperty(fanService, fullPath, fanIntf, "Value",
273 &reply) < 0)
274 {
275 return ipmi::responseInvalidFieldRequest();
276 }
277 double* doubleVal = std::get_if<double>(&reply);
278 if (doubleVal == nullptr)
279 {
280 return ipmi::responseUnspecifiedError();
281 }
282 uint8_t sensorVal = std::round(*doubleVal);
283 return ipmi::responseSuccess(sensorVal, std::nullopt);
284 }
285 break;
286 case SmSignalGet::smFanTachometerGet:
287
288 {
289 // Full path calculation pattern:
290 // Instance 1 path is
291 // /xyz/openbmc_project/sensors/fan_tach/Fan_1a Instance 2 path
292 // is /xyz/openbmc_project/sensors/fan_tach/Fan_1b Instance 3
293 // path is /xyz/openbmc_project/sensors/fan_tach/Fan_2a
294 // and so on...
295 std::string fullPath = fanTachPathPrefix;
296 std::string fanAb = (instance % 2) == 0 ? "b" : "a";
297 if (0 == instance)
298 {
299 return ipmi::responseInvalidFieldRequest();
300 }
301 else if (0 == instance / 2)
302 {
303 fullPath += std::string("1") + fanAb;
304 }
305 else
306 {
307 fullPath += std::to_string(instance / 2) + fanAb;
308 }
309
310 ipmi::Value reply;
311 if (mtm.getProperty(fanService, fullPath, fanIntf, "Value",
312 &reply) < 0)
313 {
314 return ipmi::responseInvalidFieldRequest();
315 }
316
317 double* doubleVal = std::get_if<double>(&reply);
318 if (doubleVal == nullptr)
319 {
320 return ipmi::responseUnspecifiedError();
321 }
322 uint8_t sensorVal = FAN_PRESENT | FAN_SENSOR_PRESENT;
323 std::optional<uint16_t> fanTach = std::round(*doubleVal);
324
325 return ipmi::responseSuccess(sensorVal, fanTach);
326 }
327 break;
Richard Marian Thomaiyar8e5e2b02019-08-01 07:50:55 +0530328 case SmSignalGet::smIdentifyButton:
329 {
330 if (action == SmActionGet::revert || action == SmActionGet::ignore)
331 {
332 // ButtonMasked property is not supported for ID button as it is
333 // unnecessary. Hence if requested for revert / ignore, override
334 // it to sample action to make tools happy.
335 action = SmActionGet::sample;
336 }
337 // fall-through
338 }
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700339 case SmSignalGet::smResetButton:
340 case SmSignalGet::smPowerButton:
341 case SmSignalGet::smNMIButton:
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700342 {
343 std::string path;
344 if (getGpioPathForSmSignal(signalType, path) < 0)
345 {
346 return ipmi::responseInvalidFieldRequest();
347 }
348
349 switch (action)
350 {
351 case SmActionGet::sample:
352 phosphor::logging::log<phosphor::logging::level::INFO>(
353 "case SmActionGet::sample");
354 break;
355 case SmActionGet::ignore:
356 {
357 phosphor::logging::log<phosphor::logging::level::INFO>(
358 "case SmActionGet::ignore");
359 if (mtm.setProperty(buttonService, path, buttonIntf,
360 "ButtonMasked", true) < 0)
361 {
362 return ipmi::responseUnspecifiedError();
363 }
364 }
365 break;
366 case SmActionGet::revert:
367 {
368 phosphor::logging::log<phosphor::logging::level::INFO>(
369 "case SmActionGet::revert");
370 if (mtm.setProperty(buttonService, path, buttonIntf,
371 "ButtonMasked", false) < 0)
372 {
373 return ipmi::responseUnspecifiedError();
374 }
375 }
376 break;
377
378 default:
379 return ipmi::responseInvalidFieldRequest();
380 break;
381 }
382
383 ipmi::Value reply;
384 if (mtm.getProperty(buttonService, path, buttonIntf,
385 "ButtonPressed", &reply) < 0)
386 {
387 return ipmi::responseUnspecifiedError();
388 }
389 bool* valPtr = std::get_if<bool>(&reply);
390 if (valPtr == nullptr)
391 {
392 return ipmi::responseUnspecifiedError();
393 }
394 uint8_t sensorVal = *valPtr;
395 return ipmi::responseSuccess(sensorVal, std::nullopt);
396 }
397 break;
Vernon Mauerya3702c12019-05-22 13:20:59 -0700398 default:
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700399 return ipmi::responseInvalidFieldRequest();
Vernon Mauerya3702c12019-05-22 13:20:59 -0700400 break;
401 }
Vernon Mauerya3702c12019-05-22 13:20:59 -0700402}
403
404ipmi_ret_t ipmi_app_mtm_set_signal(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
405 ipmi_request_t request,
406 ipmi_response_t response,
407 ipmi_data_len_t data_len,
408 ipmi_context_t context)
409{
410 uint8_t ret = 0;
411 ipmi_ret_t retCode = IPMI_CC_OK;
412 SetSmSignalReq* pReq = static_cast<SetSmSignalReq*>(request);
413 std::string ledName;
414 /////////////////// Signal to led configuration ////////////////
415 // {SM_SYSTEM_READY_LED, STAT_GRN_LED}, GPIOS4 gpio148
416 // {SM_POWER_FAULT_LED, STAT_AMB_LED}, GPIOS5 gpio149
417 // {SM_IDENTIFY_LED, IDENTIFY_LED}, GPIOS6 gpio150
418 // {SM_SPEAKER, SPEAKER}, GPIOAB0 gpio216
419 /////////////////////////////////////////////////////////////////
420 if ((*data_len == sizeof(*pReq)) &&
421 (mtm.getAccessLvl() >= MtmLvl::mtmAvailable))
422 {
423 switch (pReq->Signal)
424 {
425 case SmSignalSet::smPowerFaultLed:
426 case SmSignalSet::smSystemReadyLed:
427 case SmSignalSet::smIdentifyLed:
428 switch (pReq->Action)
429 {
430 case SmActionSet::forceDeasserted:
431 {
432 phosphor::logging::log<phosphor::logging::level::INFO>(
433 "case SmActionSet::forceDeasserted");
434
435 retCode =
436 ledStoreAndSet(pReq->Signal, std::string("Off"));
437 if (retCode != IPMI_CC_OK)
438 {
439 break;
440 }
441 mtm.revertTimer.start(revertTimeOut);
442 }
443 break;
444 case SmActionSet::forceAsserted:
445 {
446 phosphor::logging::log<phosphor::logging::level::INFO>(
447 "case SmActionSet::forceAsserted");
448
449 retCode =
450 ledStoreAndSet(pReq->Signal, std::string("On"));
451 if (retCode != IPMI_CC_OK)
452 {
453 break;
454 }
455 mtm.revertTimer.start(revertTimeOut);
456 if (SmSignalSet::smPowerFaultLed == pReq->Signal)
457 {
458 // Deassert "system ready"
459 retCode =
460 ledStoreAndSet(SmSignalSet::smSystemReadyLed,
461 std::string("Off"));
462 if (retCode != IPMI_CC_OK)
463 {
464 break;
465 }
466 }
467 else if (SmSignalSet::smSystemReadyLed == pReq->Signal)
468 {
469 // Deassert "fault led"
470 retCode =
471 ledStoreAndSet(SmSignalSet::smPowerFaultLed,
472 std::string("Off"));
473 if (retCode != IPMI_CC_OK)
474 {
475 break;
476 }
477 }
478 }
479 break;
480 case SmActionSet::revert:
481 {
482 phosphor::logging::log<phosphor::logging::level::INFO>(
483 "case SmActionSet::revert");
484 retCode = ledRevert(pReq->Signal);
485 if (retCode != IPMI_CC_OK)
486 {
487 break;
488 }
489 }
490 break;
491 default:
492 {
493 retCode = IPMI_CC_INVALID_FIELD_REQUEST;
494 }
495 break;
496 }
497 break;
498 case SmSignalSet::smFanPowerSpeed:
499 {
500 if (((pReq->Action == SmActionSet::forceAsserted) &&
501 (*data_len != sizeof(*pReq)) && (pReq->Value > 100)) ||
502 pReq->Instance == 0)
503 {
504 retCode = IPMI_CC_INVALID_FIELD_REQUEST;
505 break;
506 }
507 uint8_t pwmValue = 0;
508 switch (pReq->Action)
509 {
510 case SmActionSet::revert:
511 {
512 if (mtm.revertFanPWM)
513 {
514 ret = mtm.disablePidControlService(false);
515 if (ret < 0)
516 {
517 retCode = IPMI_CC_UNSPECIFIED_ERROR;
518 break;
519 }
520 mtm.revertFanPWM = false;
521 }
522 }
523 break;
524 case SmActionSet::forceAsserted:
525 {
526 pwmValue = pReq->Value;
527 } // fall-through
528 case SmActionSet::forceDeasserted:
529 {
530 if (!mtm.revertFanPWM)
531 {
532 ret = mtm.disablePidControlService(true);
533 if (ret < 0)
534 {
535 retCode = IPMI_CC_UNSPECIFIED_ERROR;
536 break;
537 }
538 mtm.revertFanPWM = true;
539 }
540 mtm.revertTimer.start(revertTimeOut);
541 std::string fanPwmInstancePath =
542 fanPwmPath + std::to_string(pReq->Instance);
543
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700544 ret = mtm.setProperty(fanService, fanPwmInstancePath,
545 fanIntf, "Value",
546 static_cast<double>(pwmValue));
Vernon Mauerya3702c12019-05-22 13:20:59 -0700547 if (ret < 0)
548 {
549 retCode = IPMI_CC_UNSPECIFIED_ERROR;
550 }
551 }
552 break;
553 default:
554 {
555 retCode = IPMI_CC_INVALID_FIELD_REQUEST;
556 }
557 break;
558 }
559 }
560 break;
561 default:
562 {
563 retCode = IPMI_CC_INVALID_FIELD_REQUEST;
564 }
565 break;
566 }
567 }
568 else
569 {
Ayushi Smriti39f64b32019-07-04 10:41:22 +0000570 retCode = IPMI_CC_INVALID;
Vernon Mauerya3702c12019-05-22 13:20:59 -0700571 }
572
573 *data_len = 0; // Only CC is return for SetSmSignal cmd
574 return retCode;
575}
576
Richard Marian Thomaiyar666dd012019-08-02 20:55:37 +0530577ipmi::RspType<> mtmKeepAlive(boost::asio::yield_context yield, uint8_t reserved,
578 const std::array<char, 5>& intentionalSignature)
579{
580 // Allow MTM keep alive command only in manfacturing mode.
581 if (mtm.getAccessLvl() != MtmLvl::mtmAvailable)
582 {
583 return ipmi::responseInvalidCommand();
584 }
585 constexpr std::array<char, 5> signatureOk = {'I', 'N', 'T', 'E', 'L'};
586 if (intentionalSignature != signatureOk || reserved != 0)
587 {
588 return ipmi::responseInvalidFieldRequest();
589 }
590 return ipmi::response(resetMtmTimer(yield));
591}
592
Vernon Mauerya3702c12019-05-22 13:20:59 -0700593} // namespace ipmi
594
595void register_mtm_commands() __attribute__((constructor));
596void register_mtm_commands()
597{
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700598 // <Get SM Signal>
599 ipmi::registerHandler(
600 ipmi::prioOemBase, ipmi::netFnOemOne,
601 static_cast<ipmi::Cmd>(IPMINetFnIntelOemGeneralCmds::GetSmSignal),
602 ipmi::Privilege::User, ipmi::appMTMGetSignal);
Vernon Mauerya3702c12019-05-22 13:20:59 -0700603
604 ipmi_register_callback(
605 netfnIntcOEMGeneral,
606 static_cast<ipmi_cmd_t>(IPMINetFnIntelOemGeneralCmds::SetSmSignal),
607 NULL, ipmi::ipmi_app_mtm_set_signal, PRIVILEGE_USER);
608
Richard Marian Thomaiyar666dd012019-08-02 20:55:37 +0530609 ipmi::registerHandler(
610 ipmi::prioOemBase, ipmi::netFnOemOne,
611 static_cast<ipmi::Cmd>(IPMINetfnIntelOEMGeneralCmd::cmdMtmKeepAlive),
612 ipmi::Privilege::Admin, ipmi::mtmKeepAlive);
613
Vernon Mauerya3702c12019-05-22 13:20:59 -0700614 return;
615}