blob: a85b87239c4492cda4eed9617a200951b0dace28 [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
Jason M. Bills38d2b5a2019-06-03 16:26:11 -070044int getGpioPathForSmSignal(const SmSignalGet signal, std::string& path)
Vernon Mauerya3702c12019-05-22 13:20:59 -070045{
Jason M. Bills38d2b5a2019-06-03 16:26:11 -070046 switch (signal)
47 {
48 case SmSignalGet::smPowerButton:
49 path = "/xyz/openbmc_project/chassis/buttons/power";
50 break;
51 case SmSignalGet::smResetButton:
52 path = "/xyz/openbmc_project/chassis/buttons/reset";
53 break;
54 case SmSignalGet::smNMIButton:
55 path = "/xyz/openbmc_project/chassis/buttons/nmi";
56 break;
Richard Marian Thomaiyar8e5e2b02019-08-01 07:50:55 +053057 case SmSignalGet::smIdentifyButton:
58 path = "/xyz/openbmc_project/chassis/buttons/id";
59 break;
Jason M. Bills38d2b5a2019-06-03 16:26:11 -070060 default:
61 return -1;
62 break;
63 }
64 return 0;
Vernon Mauerya3702c12019-05-22 13:20:59 -070065}
66
67ipmi_ret_t ledStoreAndSet(SmSignalSet signal, std::string setState)
68{
69 LedProperty* ledProp = mtm.findLedProperty(signal);
70 if (ledProp == nullptr)
71 {
72 return IPMI_CC_INVALID_FIELD_REQUEST;
73 }
74
75 std::string ledName = ledProp->getName();
76 std::string ledService = ledServicePrefix + ledName;
77 std::string ledPath = ledPathPrefix + ledName;
78 ipmi::Value presentState;
79
80 if (false == ledProp->getLock())
81 {
82 if (mtm.getProperty(ledService.c_str(), ledPath.c_str(), ledIntf,
83 "State", &presentState) != 0)
84 {
85 return IPMI_CC_UNSPECIFIED_ERROR;
86 }
87 ledProp->setPrevState(std::get<std::string>(presentState));
88 ledProp->setLock(true);
89 if (signal == SmSignalSet::smPowerFaultLed ||
90 signal == SmSignalSet::smSystemReadyLed)
91 {
92 mtm.revertLedCallback = true;
93 }
94 }
Jason M. Bills38d2b5a2019-06-03 16:26:11 -070095 if (mtm.setProperty(ledService, ledPath, ledIntf, "State",
Vernon Mauerya3702c12019-05-22 13:20:59 -070096 ledStateStr + setState) != 0)
97 {
98 return IPMI_CC_UNSPECIFIED_ERROR;
99 }
100 return IPMI_CC_OK;
101}
102
103ipmi_ret_t ledRevert(SmSignalSet signal)
104{
105 LedProperty* ledProp = mtm.findLedProperty(signal);
106 if (ledProp == nullptr)
107 {
108 return IPMI_CC_INVALID_FIELD_REQUEST;
109 }
110 if (true == ledProp->getLock())
111 {
112 ledProp->setLock(false);
113 if (signal == SmSignalSet::smPowerFaultLed ||
114 signal == SmSignalSet::smSystemReadyLed)
115 {
116 try
117 {
118 ipmi::method_no_args::callDbusMethod(
119 *getSdBus(), callbackMgrService, callbackMgrObjPath,
120 callbackMgrIntf, retriggerLedUpdate);
121 }
122 catch (sdbusplus::exception_t& e)
123 {
124 return IPMI_CC_UNSPECIFIED_ERROR;
125 }
126 mtm.revertLedCallback = false;
127 }
128 else
129 {
130 std::string ledName = ledProp->getName();
131 std::string ledService = ledServicePrefix + ledName;
132 std::string ledPath = ledPathPrefix + ledName;
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700133 if (mtm.setProperty(ledService, ledPath, ledIntf, "State",
134 ledProp->getPrevState()) != 0)
Vernon Mauerya3702c12019-05-22 13:20:59 -0700135 {
136 return IPMI_CC_UNSPECIFIED_ERROR;
137 }
138 }
139 }
140 return IPMI_CC_OK;
141}
142
143void Manufacturing::initData()
144{
Vernon Mauerya3702c12019-05-22 13:20:59 -0700145 ledPropertyList.push_back(
146 LedProperty(SmSignalSet::smPowerFaultLed, "status_amber"));
147 ledPropertyList.push_back(
148 LedProperty(SmSignalSet::smSystemReadyLed, "status_green"));
149 ledPropertyList.push_back(
150 LedProperty(SmSignalSet::smIdentifyLed, "identify"));
151}
152
153void Manufacturing::revertTimerHandler()
154{
Vernon Mauerya3702c12019-05-22 13:20:59 -0700155 if (revertFanPWM)
156 {
157 revertFanPWM = false;
158 disablePidControlService(false);
159 }
160
161 for (const auto& ledProperty : ledPropertyList)
162 {
163 const std::string& ledName = ledProperty.getName();
164 ledRevert(ledProperty.getSignal());
165 }
166}
167
168Manufacturing::Manufacturing() :
169 revertTimer([&](void) { revertTimerHandler(); })
170{
171 initData();
172}
173
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700174int8_t Manufacturing::getProperty(const std::string& service,
175 const std::string& path,
176 const std::string& interface,
177 const std::string& propertyName,
178 ipmi::Value* reply)
Vernon Mauerya3702c12019-05-22 13:20:59 -0700179{
180 try
181 {
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700182 *reply = ipmi::getDbusProperty(*getSdBus(), service, path, interface,
183 propertyName);
Vernon Mauerya3702c12019-05-22 13:20:59 -0700184 }
185 catch (const sdbusplus::exception::SdBusError& e)
186 {
187 phosphor::logging::log<phosphor::logging::level::INFO>(
188 "ERROR: getProperty");
189 return -1;
190 }
191
192 return 0;
193}
194
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700195int8_t Manufacturing::setProperty(const std::string& service,
196 const std::string& path,
197 const std::string& interface,
198 const std::string& propertyName,
199 ipmi::Value value)
Vernon Mauerya3702c12019-05-22 13:20:59 -0700200{
201 try
202 {
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700203 ipmi::setDbusProperty(*getSdBus(), service, path, interface,
Vernon Mauerya3702c12019-05-22 13:20:59 -0700204 propertyName, value);
205 }
206 catch (const sdbusplus::exception::SdBusError& e)
207 {
208 phosphor::logging::log<phosphor::logging::level::INFO>(
209 "ERROR: setProperty");
210 return -1;
211 }
212
213 return 0;
214}
215
216int8_t Manufacturing::disablePidControlService(const bool disable)
217{
218 try
219 {
220 auto dbus = getSdBus();
221 auto method = dbus->new_method_call(systemDService, systemDObjPath,
222 systemDMgrIntf,
223 disable ? "StopUnit" : "StartUnit");
224 method.append(pidControlService, "replace");
225 auto reply = dbus->call(method);
226 }
227 catch (const sdbusplus::exception::SdBusError& e)
228 {
229 phosphor::logging::log<phosphor::logging::level::INFO>(
230 "ERROR: phosphor-pid-control service start or stop failed");
231 return -1;
232 }
233 return 0;
234}
235
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700236ipmi::RspType<uint8_t, // Signal value
237 std::optional<uint16_t> // Fan tach value
238 >
239 appMTMGetSignal(uint8_t signalTypeByte, uint8_t instance,
240 uint8_t actionByte)
Vernon Mauerya3702c12019-05-22 13:20:59 -0700241{
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700242 if (mtm.getAccessLvl() < MtmLvl::mtmAvailable)
Vernon Mauerya3702c12019-05-22 13:20:59 -0700243 {
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700244 return ipmi::responseInvalidCommand();
245 }
Vernon Mauerya3702c12019-05-22 13:20:59 -0700246
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700247 SmSignalGet signalType = static_cast<SmSignalGet>(signalTypeByte);
248 SmActionGet action = static_cast<SmActionGet>(actionByte);
249
250 switch (signalType)
251 {
252 case SmSignalGet::smFanPwmGet:
253 {
254 ipmi::Value reply;
255 std::string fullPath = fanPwmPath + std::to_string(instance);
256 if (mtm.getProperty(fanService, fullPath, fanIntf, "Value",
257 &reply) < 0)
258 {
259 return ipmi::responseInvalidFieldRequest();
260 }
261 double* doubleVal = std::get_if<double>(&reply);
262 if (doubleVal == nullptr)
263 {
264 return ipmi::responseUnspecifiedError();
265 }
266 uint8_t sensorVal = std::round(*doubleVal);
267 return ipmi::responseSuccess(sensorVal, std::nullopt);
268 }
269 break;
270 case SmSignalGet::smFanTachometerGet:
271
272 {
273 // Full path calculation pattern:
274 // Instance 1 path is
275 // /xyz/openbmc_project/sensors/fan_tach/Fan_1a Instance 2 path
276 // is /xyz/openbmc_project/sensors/fan_tach/Fan_1b Instance 3
277 // path is /xyz/openbmc_project/sensors/fan_tach/Fan_2a
278 // and so on...
279 std::string fullPath = fanTachPathPrefix;
280 std::string fanAb = (instance % 2) == 0 ? "b" : "a";
281 if (0 == instance)
282 {
283 return ipmi::responseInvalidFieldRequest();
284 }
285 else if (0 == instance / 2)
286 {
287 fullPath += std::string("1") + fanAb;
288 }
289 else
290 {
291 fullPath += std::to_string(instance / 2) + fanAb;
292 }
293
294 ipmi::Value reply;
295 if (mtm.getProperty(fanService, fullPath, fanIntf, "Value",
296 &reply) < 0)
297 {
298 return ipmi::responseInvalidFieldRequest();
299 }
300
301 double* doubleVal = std::get_if<double>(&reply);
302 if (doubleVal == nullptr)
303 {
304 return ipmi::responseUnspecifiedError();
305 }
306 uint8_t sensorVal = FAN_PRESENT | FAN_SENSOR_PRESENT;
307 std::optional<uint16_t> fanTach = std::round(*doubleVal);
308
309 return ipmi::responseSuccess(sensorVal, fanTach);
310 }
311 break;
Richard Marian Thomaiyar8e5e2b02019-08-01 07:50:55 +0530312 case SmSignalGet::smIdentifyButton:
313 {
314 if (action == SmActionGet::revert || action == SmActionGet::ignore)
315 {
316 // ButtonMasked property is not supported for ID button as it is
317 // unnecessary. Hence if requested for revert / ignore, override
318 // it to sample action to make tools happy.
319 action = SmActionGet::sample;
320 }
321 // fall-through
322 }
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700323 case SmSignalGet::smResetButton:
324 case SmSignalGet::smPowerButton:
325 case SmSignalGet::smNMIButton:
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700326 {
327 std::string path;
328 if (getGpioPathForSmSignal(signalType, path) < 0)
329 {
330 return ipmi::responseInvalidFieldRequest();
331 }
332
333 switch (action)
334 {
335 case SmActionGet::sample:
336 phosphor::logging::log<phosphor::logging::level::INFO>(
337 "case SmActionGet::sample");
338 break;
339 case SmActionGet::ignore:
340 {
341 phosphor::logging::log<phosphor::logging::level::INFO>(
342 "case SmActionGet::ignore");
343 if (mtm.setProperty(buttonService, path, buttonIntf,
344 "ButtonMasked", true) < 0)
345 {
346 return ipmi::responseUnspecifiedError();
347 }
348 }
349 break;
350 case SmActionGet::revert:
351 {
352 phosphor::logging::log<phosphor::logging::level::INFO>(
353 "case SmActionGet::revert");
354 if (mtm.setProperty(buttonService, path, buttonIntf,
355 "ButtonMasked", false) < 0)
356 {
357 return ipmi::responseUnspecifiedError();
358 }
359 }
360 break;
361
362 default:
363 return ipmi::responseInvalidFieldRequest();
364 break;
365 }
366
367 ipmi::Value reply;
368 if (mtm.getProperty(buttonService, path, buttonIntf,
369 "ButtonPressed", &reply) < 0)
370 {
371 return ipmi::responseUnspecifiedError();
372 }
373 bool* valPtr = std::get_if<bool>(&reply);
374 if (valPtr == nullptr)
375 {
376 return ipmi::responseUnspecifiedError();
377 }
378 uint8_t sensorVal = *valPtr;
379 return ipmi::responseSuccess(sensorVal, std::nullopt);
380 }
381 break;
Vernon Mauerya3702c12019-05-22 13:20:59 -0700382 default:
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700383 return ipmi::responseInvalidFieldRequest();
Vernon Mauerya3702c12019-05-22 13:20:59 -0700384 break;
385 }
Vernon Mauerya3702c12019-05-22 13:20:59 -0700386}
387
388ipmi_ret_t ipmi_app_mtm_set_signal(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
389 ipmi_request_t request,
390 ipmi_response_t response,
391 ipmi_data_len_t data_len,
392 ipmi_context_t context)
393{
394 uint8_t ret = 0;
395 ipmi_ret_t retCode = IPMI_CC_OK;
396 SetSmSignalReq* pReq = static_cast<SetSmSignalReq*>(request);
397 std::string ledName;
398 /////////////////// Signal to led configuration ////////////////
399 // {SM_SYSTEM_READY_LED, STAT_GRN_LED}, GPIOS4 gpio148
400 // {SM_POWER_FAULT_LED, STAT_AMB_LED}, GPIOS5 gpio149
401 // {SM_IDENTIFY_LED, IDENTIFY_LED}, GPIOS6 gpio150
402 // {SM_SPEAKER, SPEAKER}, GPIOAB0 gpio216
403 /////////////////////////////////////////////////////////////////
404 if ((*data_len == sizeof(*pReq)) &&
405 (mtm.getAccessLvl() >= MtmLvl::mtmAvailable))
406 {
407 switch (pReq->Signal)
408 {
409 case SmSignalSet::smPowerFaultLed:
410 case SmSignalSet::smSystemReadyLed:
411 case SmSignalSet::smIdentifyLed:
412 switch (pReq->Action)
413 {
414 case SmActionSet::forceDeasserted:
415 {
416 phosphor::logging::log<phosphor::logging::level::INFO>(
417 "case SmActionSet::forceDeasserted");
418
419 retCode =
420 ledStoreAndSet(pReq->Signal, std::string("Off"));
421 if (retCode != IPMI_CC_OK)
422 {
423 break;
424 }
425 mtm.revertTimer.start(revertTimeOut);
426 }
427 break;
428 case SmActionSet::forceAsserted:
429 {
430 phosphor::logging::log<phosphor::logging::level::INFO>(
431 "case SmActionSet::forceAsserted");
432
433 retCode =
434 ledStoreAndSet(pReq->Signal, std::string("On"));
435 if (retCode != IPMI_CC_OK)
436 {
437 break;
438 }
439 mtm.revertTimer.start(revertTimeOut);
440 if (SmSignalSet::smPowerFaultLed == pReq->Signal)
441 {
442 // Deassert "system ready"
443 retCode =
444 ledStoreAndSet(SmSignalSet::smSystemReadyLed,
445 std::string("Off"));
446 if (retCode != IPMI_CC_OK)
447 {
448 break;
449 }
450 }
451 else if (SmSignalSet::smSystemReadyLed == pReq->Signal)
452 {
453 // Deassert "fault led"
454 retCode =
455 ledStoreAndSet(SmSignalSet::smPowerFaultLed,
456 std::string("Off"));
457 if (retCode != IPMI_CC_OK)
458 {
459 break;
460 }
461 }
462 }
463 break;
464 case SmActionSet::revert:
465 {
466 phosphor::logging::log<phosphor::logging::level::INFO>(
467 "case SmActionSet::revert");
468 retCode = ledRevert(pReq->Signal);
469 if (retCode != IPMI_CC_OK)
470 {
471 break;
472 }
473 }
474 break;
475 default:
476 {
477 retCode = IPMI_CC_INVALID_FIELD_REQUEST;
478 }
479 break;
480 }
481 break;
482 case SmSignalSet::smFanPowerSpeed:
483 {
484 if (((pReq->Action == SmActionSet::forceAsserted) &&
485 (*data_len != sizeof(*pReq)) && (pReq->Value > 100)) ||
486 pReq->Instance == 0)
487 {
488 retCode = IPMI_CC_INVALID_FIELD_REQUEST;
489 break;
490 }
491 uint8_t pwmValue = 0;
492 switch (pReq->Action)
493 {
494 case SmActionSet::revert:
495 {
496 if (mtm.revertFanPWM)
497 {
498 ret = mtm.disablePidControlService(false);
499 if (ret < 0)
500 {
501 retCode = IPMI_CC_UNSPECIFIED_ERROR;
502 break;
503 }
504 mtm.revertFanPWM = false;
505 }
506 }
507 break;
508 case SmActionSet::forceAsserted:
509 {
510 pwmValue = pReq->Value;
511 } // fall-through
512 case SmActionSet::forceDeasserted:
513 {
514 if (!mtm.revertFanPWM)
515 {
516 ret = mtm.disablePidControlService(true);
517 if (ret < 0)
518 {
519 retCode = IPMI_CC_UNSPECIFIED_ERROR;
520 break;
521 }
522 mtm.revertFanPWM = true;
523 }
524 mtm.revertTimer.start(revertTimeOut);
525 std::string fanPwmInstancePath =
526 fanPwmPath + std::to_string(pReq->Instance);
527
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700528 ret = mtm.setProperty(fanService, fanPwmInstancePath,
529 fanIntf, "Value",
530 static_cast<double>(pwmValue));
Vernon Mauerya3702c12019-05-22 13:20:59 -0700531 if (ret < 0)
532 {
533 retCode = IPMI_CC_UNSPECIFIED_ERROR;
534 }
535 }
536 break;
537 default:
538 {
539 retCode = IPMI_CC_INVALID_FIELD_REQUEST;
540 }
541 break;
542 }
543 }
544 break;
545 default:
546 {
547 retCode = IPMI_CC_INVALID_FIELD_REQUEST;
548 }
549 break;
550 }
551 }
552 else
553 {
Ayushi Smriti39f64b32019-07-04 10:41:22 +0000554 retCode = IPMI_CC_INVALID;
Vernon Mauerya3702c12019-05-22 13:20:59 -0700555 }
556
557 *data_len = 0; // Only CC is return for SetSmSignal cmd
558 return retCode;
559}
560
561} // namespace ipmi
562
563void register_mtm_commands() __attribute__((constructor));
564void register_mtm_commands()
565{
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700566 // <Get SM Signal>
567 ipmi::registerHandler(
568 ipmi::prioOemBase, ipmi::netFnOemOne,
569 static_cast<ipmi::Cmd>(IPMINetFnIntelOemGeneralCmds::GetSmSignal),
570 ipmi::Privilege::User, ipmi::appMTMGetSignal);
Vernon Mauerya3702c12019-05-22 13:20:59 -0700571
572 ipmi_register_callback(
573 netfnIntcOEMGeneral,
574 static_cast<ipmi_cmd_t>(IPMINetFnIntelOemGeneralCmds::SetSmSignal),
575 NULL, ipmi::ipmi_app_mtm_set_signal, PRIVILEGE_USER);
576
577 return;
578}