blob: 6fed043d1cfc617c3629321832e9d03e9982b71f [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{
Richard Marian Thomaiyarae13ac62019-12-17 15:45:12 +0530179
180#ifdef BMC_VALIDATION_UNSECURE_FEATURE
181 if (mtm.getMfgMode() == SpecialMode::valUnsecure)
182 {
183 // Don't revert the behaviour for validation unsecure mode.
184 return;
185 }
186#endif
Vernon Mauerya3702c12019-05-22 13:20:59 -0700187 if (revertFanPWM)
188 {
189 revertFanPWM = false;
190 disablePidControlService(false);
191 }
192
Ayushi Smritid872a4a2019-10-15 10:20:11 +0530193 if (mtmTestBeepFd != -1)
194 {
195 ::close(mtmTestBeepFd);
196 mtmTestBeepFd = -1;
197 }
198
Vernon Mauerya3702c12019-05-22 13:20:59 -0700199 for (const auto& ledProperty : ledPropertyList)
200 {
201 const std::string& ledName = ledProperty.getName();
202 ledRevert(ledProperty.getSignal());
203 }
204}
205
206Manufacturing::Manufacturing() :
207 revertTimer([&](void) { revertTimerHandler(); })
208{
209 initData();
210}
211
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700212int8_t Manufacturing::getProperty(const std::string& service,
213 const std::string& path,
214 const std::string& interface,
215 const std::string& propertyName,
216 ipmi::Value* reply)
Vernon Mauerya3702c12019-05-22 13:20:59 -0700217{
218 try
219 {
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700220 *reply = ipmi::getDbusProperty(*getSdBus(), service, path, interface,
221 propertyName);
Vernon Mauerya3702c12019-05-22 13:20:59 -0700222 }
223 catch (const sdbusplus::exception::SdBusError& e)
224 {
225 phosphor::logging::log<phosphor::logging::level::INFO>(
226 "ERROR: getProperty");
227 return -1;
228 }
229
230 return 0;
231}
232
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700233int8_t Manufacturing::setProperty(const std::string& service,
234 const std::string& path,
235 const std::string& interface,
236 const std::string& propertyName,
237 ipmi::Value value)
Vernon Mauerya3702c12019-05-22 13:20:59 -0700238{
239 try
240 {
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700241 ipmi::setDbusProperty(*getSdBus(), service, path, interface,
Vernon Mauerya3702c12019-05-22 13:20:59 -0700242 propertyName, value);
243 }
244 catch (const sdbusplus::exception::SdBusError& e)
245 {
246 phosphor::logging::log<phosphor::logging::level::INFO>(
247 "ERROR: setProperty");
248 return -1;
249 }
250
251 return 0;
252}
253
254int8_t Manufacturing::disablePidControlService(const bool disable)
255{
256 try
257 {
258 auto dbus = getSdBus();
259 auto method = dbus->new_method_call(systemDService, systemDObjPath,
260 systemDMgrIntf,
261 disable ? "StopUnit" : "StartUnit");
262 method.append(pidControlService, "replace");
263 auto reply = dbus->call(method);
264 }
265 catch (const sdbusplus::exception::SdBusError& e)
266 {
267 phosphor::logging::log<phosphor::logging::level::INFO>(
268 "ERROR: phosphor-pid-control service start or stop failed");
269 return -1;
270 }
271 return 0;
272}
273
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700274ipmi::RspType<uint8_t, // Signal value
275 std::optional<uint16_t> // Fan tach value
276 >
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +0530277 appMTMGetSignal(ipmi::Context::ptr ctx, uint8_t signalTypeByte,
Richard Marian Thomaiyar147daec2019-06-15 07:43:48 +0530278 uint8_t instance, uint8_t actionByte)
Vernon Mauerya3702c12019-05-22 13:20:59 -0700279{
Ayushi Smritie0511e52019-08-27 17:30:34 +0000280 // mfg filter logic is used to allow MTM get signal command only in
281 // manfacturing mode.
Vernon Mauerya3702c12019-05-22 13:20:59 -0700282
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700283 SmSignalGet signalType = static_cast<SmSignalGet>(signalTypeByte);
284 SmActionGet action = static_cast<SmActionGet>(actionByte);
285
286 switch (signalType)
287 {
anil kumar appana98705b32019-09-11 14:15:50 +0000288 case SmSignalGet::smChassisIntrusion:
289 {
290 ipmi::Value reply;
291 if (mtm.getProperty(intrusionService, intrusionPath, intrusionIntf,
292 "Status", &reply) < 0)
293 {
294 return ipmi::responseInvalidFieldRequest();
295 }
296 std::string* intrusionStatus = std::get_if<std::string>(&reply);
297 if (!intrusionStatus)
298 {
299 return ipmi::responseUnspecifiedError();
300 }
301
302 uint8_t status = 0;
303 if (!intrusionStatus->compare("Normal"))
304 {
305 status = static_cast<uint8_t>(IntrusionStatus::normal);
306 }
307 else if (!intrusionStatus->compare("HardwareIntrusion"))
308 {
309 status =
310 static_cast<uint8_t>(IntrusionStatus::hardwareIntrusion);
311 }
312 else if (!intrusionStatus->compare("TamperingDetected"))
313 {
314 status =
315 static_cast<uint8_t>(IntrusionStatus::tamperingDetected);
316 }
317 else
318 {
319 return ipmi::responseUnspecifiedError();
320 }
321 return ipmi::responseSuccess(status, std::nullopt);
322 }
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700323 case SmSignalGet::smFanPwmGet:
324 {
325 ipmi::Value reply;
Richard Marian Thomaiyar147daec2019-06-15 07:43:48 +0530326 std::string fullPath = fanPwmPath + std::to_string(instance + 1);
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700327 if (mtm.getProperty(fanService, fullPath, fanIntf, "Value",
328 &reply) < 0)
329 {
330 return ipmi::responseInvalidFieldRequest();
331 }
332 double* doubleVal = std::get_if<double>(&reply);
333 if (doubleVal == nullptr)
334 {
335 return ipmi::responseUnspecifiedError();
336 }
337 uint8_t sensorVal = std::round(*doubleVal);
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +0530338 resetMtmTimer(ctx);
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700339 return ipmi::responseSuccess(sensorVal, std::nullopt);
340 }
341 break;
342 case SmSignalGet::smFanTachometerGet:
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700343 {
Richard Marian Thomaiyar147daec2019-06-15 07:43:48 +0530344 boost::system::error_code ec;
345 using objFlatMap = boost::container::flat_map<
346 std::string, boost::container::flat_map<
347 std::string, std::vector<std::string>>>;
348
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +0530349 auto flatMap = ctx->bus->yield_method_call<objFlatMap>(
350 ctx->yield, ec, "xyz.openbmc_project.ObjectMapper",
Richard Marian Thomaiyar147daec2019-06-15 07:43:48 +0530351 "/xyz/openbmc_project/object_mapper",
352 "xyz.openbmc_project.ObjectMapper", "GetSubTree",
353 fanTachBasePath, 0, std::array<const char*, 1>{fanIntf});
354 if (ec)
355 {
356 phosphor::logging::log<phosphor::logging::level::ERR>(
357 "Failed to query fan tach sub tree objects");
358 return ipmi::responseUnspecifiedError();
359 }
360 if (instance >= flatMap.size())
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700361 {
362 return ipmi::responseInvalidFieldRequest();
363 }
Richard Marian Thomaiyar147daec2019-06-15 07:43:48 +0530364 auto itr = flatMap.nth(instance);
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700365 ipmi::Value reply;
Richard Marian Thomaiyar147daec2019-06-15 07:43:48 +0530366 if (mtm.getProperty(fanService, itr->first, fanIntf, "Value",
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700367 &reply) < 0)
368 {
369 return ipmi::responseInvalidFieldRequest();
370 }
371
372 double* doubleVal = std::get_if<double>(&reply);
373 if (doubleVal == nullptr)
374 {
375 return ipmi::responseUnspecifiedError();
376 }
377 uint8_t sensorVal = FAN_PRESENT | FAN_SENSOR_PRESENT;
378 std::optional<uint16_t> fanTach = std::round(*doubleVal);
379
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +0530380 resetMtmTimer(ctx);
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700381 return ipmi::responseSuccess(sensorVal, fanTach);
382 }
383 break;
Richard Marian Thomaiyar8e5e2b02019-08-01 07:50:55 +0530384 case SmSignalGet::smIdentifyButton:
385 {
386 if (action == SmActionGet::revert || action == SmActionGet::ignore)
387 {
388 // ButtonMasked property is not supported for ID button as it is
389 // unnecessary. Hence if requested for revert / ignore, override
390 // it to sample action to make tools happy.
391 action = SmActionGet::sample;
392 }
393 // fall-through
394 }
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700395 case SmSignalGet::smResetButton:
396 case SmSignalGet::smPowerButton:
397 case SmSignalGet::smNMIButton:
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700398 {
399 std::string path;
400 if (getGpioPathForSmSignal(signalType, path) < 0)
401 {
402 return ipmi::responseInvalidFieldRequest();
403 }
404
405 switch (action)
406 {
407 case SmActionGet::sample:
408 phosphor::logging::log<phosphor::logging::level::INFO>(
409 "case SmActionGet::sample");
410 break;
411 case SmActionGet::ignore:
412 {
413 phosphor::logging::log<phosphor::logging::level::INFO>(
414 "case SmActionGet::ignore");
415 if (mtm.setProperty(buttonService, path, buttonIntf,
416 "ButtonMasked", true) < 0)
417 {
418 return ipmi::responseUnspecifiedError();
419 }
420 }
421 break;
422 case SmActionGet::revert:
423 {
424 phosphor::logging::log<phosphor::logging::level::INFO>(
425 "case SmActionGet::revert");
426 if (mtm.setProperty(buttonService, path, buttonIntf,
427 "ButtonMasked", false) < 0)
428 {
429 return ipmi::responseUnspecifiedError();
430 }
431 }
432 break;
433
434 default:
435 return ipmi::responseInvalidFieldRequest();
436 break;
437 }
438
439 ipmi::Value reply;
440 if (mtm.getProperty(buttonService, path, buttonIntf,
441 "ButtonPressed", &reply) < 0)
442 {
443 return ipmi::responseUnspecifiedError();
444 }
445 bool* valPtr = std::get_if<bool>(&reply);
446 if (valPtr == nullptr)
447 {
448 return ipmi::responseUnspecifiedError();
449 }
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +0530450 resetMtmTimer(ctx);
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700451 uint8_t sensorVal = *valPtr;
452 return ipmi::responseSuccess(sensorVal, std::nullopt);
453 }
454 break;
Richard Marian Thomaiyar1b74a212019-08-03 13:26:17 +0530455 case SmSignalGet::smNcsiDiag:
456 {
457 constexpr const char* netBasePath = "/sys/class/net/eth";
458 constexpr const char* carrierSuffix = "/carrier";
459 std::ifstream netIfs(netBasePath + std::to_string(instance) +
460 carrierSuffix);
461 if (!netIfs.good())
462 {
463 return ipmi::responseInvalidFieldRequest();
464 }
465 std::string carrier;
466 netIfs >> carrier;
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +0530467 resetMtmTimer(ctx);
Richard Marian Thomaiyar1b74a212019-08-03 13:26:17 +0530468 return ipmi::responseSuccess(
469 static_cast<uint8_t>(std::stoi(carrier)), std::nullopt);
470 }
471 break;
Vernon Mauerya3702c12019-05-22 13:20:59 -0700472 default:
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700473 return ipmi::responseInvalidFieldRequest();
Vernon Mauerya3702c12019-05-22 13:20:59 -0700474 break;
475 }
Vernon Mauerya3702c12019-05-22 13:20:59 -0700476}
477
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +0530478ipmi::RspType<> appMTMSetSignal(ipmi::Context::ptr ctx, uint8_t signalTypeByte,
479 uint8_t instance, uint8_t actionByte,
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000480 std::optional<uint8_t> pwmSpeed)
Vernon Mauerya3702c12019-05-22 13:20:59 -0700481{
Ayushi Smritie0511e52019-08-27 17:30:34 +0000482 // mfg filter logic is used to allow MTM set signal command only in
483 // manfacturing mode.
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000484
485 SmSignalSet signalType = static_cast<SmSignalSet>(signalTypeByte);
486 SmActionSet action = static_cast<SmActionSet>(actionByte);
487 Cc retCode = ccSuccess;
488 int8_t ret = 0;
489
490 switch (signalType)
491 {
492 case SmSignalSet::smPowerFaultLed:
493 case SmSignalSet::smSystemReadyLed:
494 case SmSignalSet::smIdentifyLed:
495 switch (action)
496 {
497 case SmActionSet::forceDeasserted:
Vernon Mauerya3702c12019-05-22 13:20:59 -0700498 {
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000499 phosphor::logging::log<phosphor::logging::level::INFO>(
500 "case SmActionSet::forceDeasserted");
Vernon Mauerya3702c12019-05-22 13:20:59 -0700501
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000502 retCode = ledStoreAndSet(signalType, std::string("Off"));
503 if (retCode != ccSuccess)
Vernon Mauerya3702c12019-05-22 13:20:59 -0700504 {
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000505 return ipmi::response(retCode);
Vernon Mauerya3702c12019-05-22 13:20:59 -0700506 }
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000507 mtm.revertTimer.start(revertTimeOut);
Vernon Mauerya3702c12019-05-22 13:20:59 -0700508 }
509 break;
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000510 case SmActionSet::forceAsserted:
Vernon Mauerya3702c12019-05-22 13:20:59 -0700511 {
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000512 phosphor::logging::log<phosphor::logging::level::INFO>(
513 "case SmActionSet::forceAsserted");
Vernon Mauerya3702c12019-05-22 13:20:59 -0700514
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000515 retCode = ledStoreAndSet(signalType, std::string("On"));
516 if (retCode != ccSuccess)
517 {
518 return ipmi::response(retCode);
519 }
520 mtm.revertTimer.start(revertTimeOut);
521 if (SmSignalSet::smPowerFaultLed == signalType)
522 {
523 // Deassert "system ready"
524 retCode = ledStoreAndSet(SmSignalSet::smSystemReadyLed,
525 std::string("Off"));
526 }
527 else if (SmSignalSet::smSystemReadyLed == signalType)
528 {
529 // Deassert "fault led"
530 retCode = ledStoreAndSet(SmSignalSet::smPowerFaultLed,
531 std::string("Off"));
532 }
533 }
534 break;
535 case SmActionSet::revert:
536 {
537 phosphor::logging::log<phosphor::logging::level::INFO>(
538 "case SmActionSet::revert");
539 retCode = ledRevert(signalType);
540 }
541 break;
542 default:
543 {
544 return ipmi::responseInvalidFieldRequest();
545 }
546 }
547 break;
548 case SmSignalSet::smFanPowerSpeed:
549 {
550 if ((action == SmActionSet::forceAsserted) && (!pwmSpeed))
551 {
552 return ipmi::responseReqDataLenInvalid();
553 }
554
555 if ((action == SmActionSet::forceAsserted) && (*pwmSpeed > 100))
556 {
557 return ipmi::responseInvalidFieldRequest();
558 }
559
560 uint8_t pwmValue = 0;
561 switch (action)
562 {
563 case SmActionSet::revert:
564 {
565 if (mtm.revertFanPWM)
566 {
567 ret = mtm.disablePidControlService(false);
Vernon Mauerya3702c12019-05-22 13:20:59 -0700568 if (ret < 0)
569 {
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000570 return ipmi::responseUnspecifiedError();
Vernon Mauerya3702c12019-05-22 13:20:59 -0700571 }
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000572 mtm.revertFanPWM = false;
Vernon Mauerya3702c12019-05-22 13:20:59 -0700573 }
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000574 }
575 break;
576 case SmActionSet::forceAsserted:
577 {
578 pwmValue = *pwmSpeed;
579 } // fall-through
580 case SmActionSet::forceDeasserted:
581 {
582 if (!mtm.revertFanPWM)
Vernon Mauerya3702c12019-05-22 13:20:59 -0700583 {
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000584 ret = mtm.disablePidControlService(true);
585 if (ret < 0)
586 {
587 return ipmi::responseUnspecifiedError();
588 }
589 mtm.revertFanPWM = true;
Vernon Mauerya3702c12019-05-22 13:20:59 -0700590 }
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000591 mtm.revertTimer.start(revertTimeOut);
592 std::string fanPwmInstancePath =
593 fanPwmPath + std::to_string(instance + 1);
594
595 ret =
596 mtm.setProperty(fanService, fanPwmInstancePath, fanIntf,
597 "Value", static_cast<double>(pwmValue));
598 if (ret < 0)
599 {
600 return ipmi::responseUnspecifiedError();
601 }
602 }
603 break;
604 default:
605 {
606 return ipmi::responseInvalidFieldRequest();
Vernon Mauerya3702c12019-05-22 13:20:59 -0700607 }
608 }
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000609 }
610 break;
Ayushi Smritid872a4a2019-10-15 10:20:11 +0530611 case SmSignalSet::smSpeaker:
612 {
613 phosphor::logging::log<phosphor::logging::level::INFO>(
614 "Performing Speaker SmActionSet",
615 phosphor::logging::entry("ACTION=%d",
616 static_cast<uint8_t>(action)));
617 switch (action)
618 {
619 case SmActionSet::forceAsserted:
620 {
621 char beepDevName[] = "/dev/input/event0";
622 if (mtm.mtmTestBeepFd != -1)
623 {
624 phosphor::logging::log<phosphor::logging::level::INFO>(
625 "mtm beep device is opened already!");
626 // returning success as already beep is in progress
627 return ipmi::response(retCode);
628 }
629
630 if ((mtm.mtmTestBeepFd =
631 ::open(beepDevName, O_RDWR | O_CLOEXEC)) < 0)
632 {
633 phosphor::logging::log<phosphor::logging::level::ERR>(
634 "Failed to open input device");
635 return ipmi::responseUnspecifiedError();
636 }
637
638 struct input_event event;
639 event.type = EV_SND;
640 event.code = SND_TONE;
641 event.value = 2000;
642
643 if (::write(mtm.mtmTestBeepFd, &event,
644 sizeof(struct input_event)) !=
645 sizeof(struct input_event))
646 {
647 phosphor::logging::log<phosphor::logging::level::ERR>(
648 "Failed to write a tone sound event");
649 ::close(mtm.mtmTestBeepFd);
650 mtm.mtmTestBeepFd = -1;
651 return ipmi::responseUnspecifiedError();
652 }
653 mtm.revertTimer.start(revertTimeOut);
654 }
655 break;
656 case SmActionSet::revert:
657 case SmActionSet::forceDeasserted:
658 {
659 if (mtm.mtmTestBeepFd != -1)
660 {
661 ::close(mtm.mtmTestBeepFd);
662 mtm.mtmTestBeepFd = -1;
663 }
664 }
665 break;
666 default:
667 {
668 return ipmi::responseInvalidFieldRequest();
669 }
670 }
671 }
672 break;
Richard Marian Thomaiyar3594c6d2019-11-19 21:29:04 +0530673 case SmSignalSet::smDiskFaultLed:
674 {
675 boost::system::error_code ec;
676 using objPaths = std::vector<std::string>;
677 std::string driveBasePath =
678 "/xyz/openbmc_project/inventory/item/drive/";
679 static constexpr const char* driveLedIntf =
680 "xyz.openbmc_project.Led.Group";
681 static constexpr const char* hsbpService =
682 "xyz.openbmc_project.HsbpManager";
683
684 auto driveList = ctx->bus->yield_method_call<objPaths>(
685 ctx->yield, ec, "xyz.openbmc_project.ObjectMapper",
686 "/xyz/openbmc_project/object_mapper",
687 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths",
688 driveBasePath, 0, std::array<const char*, 1>{driveLedIntf});
689 if (ec)
690 {
691 phosphor::logging::log<phosphor::logging::level::ERR>(
692 "Failed to query HSBP drive sub tree objects");
693 return ipmi::responseUnspecifiedError();
694 }
695 std::string driveObjPath =
696 driveBasePath + "Drive_" + std::to_string(instance + 1);
697 if (std::find(driveList.begin(), driveList.end(), driveObjPath) ==
698 driveList.end())
699 {
700 return ipmi::responseInvalidFieldRequest();
701 }
702 bool driveLedState = false;
703 switch (action)
704 {
705 case SmActionSet::forceAsserted:
706 {
707 driveLedState = true;
708 }
709 break;
710 case SmActionSet::revert:
711 {
712 driveLedState = false;
713 }
714 break;
715 case SmActionSet::forceDeasserted:
716 {
717 driveLedState = false;
718 }
719 break;
720 default:
721 {
722 return ipmi::responseInvalidFieldRequest();
723 }
724 }
725 ret = mtm.setProperty(hsbpService, driveObjPath, driveLedIntf,
726 "Asserted", driveLedState);
727 if (ret < 0)
728 {
729 return ipmi::responseUnspecifiedError();
730 }
731 }
732 break;
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000733 default:
734 {
735 return ipmi::responseInvalidFieldRequest();
Vernon Mauerya3702c12019-05-22 13:20:59 -0700736 }
737 }
Richard Marian Thomaiyar4cc10152019-08-02 23:21:45 +0530738 if (retCode == ccSuccess)
739 {
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +0530740 resetMtmTimer(ctx);
Richard Marian Thomaiyar4cc10152019-08-02 23:21:45 +0530741 }
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000742 return ipmi::response(retCode);
Vernon Mauerya3702c12019-05-22 13:20:59 -0700743}
744
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +0530745ipmi::RspType<> mtmKeepAlive(ipmi::Context::ptr ctx, uint8_t reserved,
Richard Marian Thomaiyar666dd012019-08-02 20:55:37 +0530746 const std::array<char, 5>& intentionalSignature)
747{
Ayushi Smritie0511e52019-08-27 17:30:34 +0000748 // mfg filter logic is used to allow MTM keep alive command only in
749 // manfacturing mode
750
Richard Marian Thomaiyar666dd012019-08-02 20:55:37 +0530751 constexpr std::array<char, 5> signatureOk = {'I', 'N', 'T', 'E', 'L'};
752 if (intentionalSignature != signatureOk || reserved != 0)
753 {
754 return ipmi::responseInvalidFieldRequest();
755 }
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +0530756 return ipmi::response(resetMtmTimer(ctx));
Richard Marian Thomaiyar666dd012019-08-02 20:55:37 +0530757}
758
Ayushi Smritie0511e52019-08-27 17:30:34 +0000759static constexpr unsigned int makeCmdKey(unsigned int netFn, unsigned int cmd)
760{
761 return (netFn << 8) | cmd;
762}
763
Yong Li85feb132019-08-09 16:06:03 +0800764ipmi::Cc mfgFilterMessage(ipmi::message::Request::ptr request)
765{
Ayushi Smritie0511e52019-08-27 17:30:34 +0000766 // Restricted commands, must be executed only in Manufacturing mode
767 switch (makeCmdKey(request->ctx->netFn, request->ctx->cmd))
Yong Li85feb132019-08-09 16:06:03 +0800768 {
Ayushi Smritie0511e52019-08-27 17:30:34 +0000769 // i2c master write read command needs additional checking
770 case makeCmdKey(ipmi::netFnApp, ipmi::app::cmdMasterWriteRead):
771 if (request->payload.size() > 4)
772 {
Richard Marian Thomaiyarae13ac62019-12-17 15:45:12 +0530773 // Allow write data count > 1 only in Special mode
774 if (mtm.getMfgMode() == SpecialMode::none)
Ayushi Smritie0511e52019-08-27 17:30:34 +0000775 {
776 return ipmi::ccInsufficientPrivilege;
777 }
778 }
jayaprakash Mutyala2d4a0192019-11-11 22:12:45 +0000779 return ipmi::ccSuccess;
Ayushi Smritie0511e52019-08-27 17:30:34 +0000780 case makeCmdKey(ipmi::netFnOemOne,
781 ipmi::intel::general::cmdGetSmSignal):
782 case makeCmdKey(ipmi::netFnOemOne,
783 ipmi::intel::general::cmdSetSmSignal):
784 case makeCmdKey(ipmi::netFnOemOne,
785 ipmi::intel::general::cmdMtmKeepAlive):
786 case makeCmdKey(ipmi::netFnOemOne,
787 ipmi::intel::general::cmdSetManufacturingData):
788 case makeCmdKey(ipmi::netFnOemOne,
789 ipmi::intel::general::cmdGetManufacturingData):
790 case makeCmdKey(ipmi::netFnStorage, ipmi::storage::cmdWriteFruData):
791
Richard Marian Thomaiyarae13ac62019-12-17 15:45:12 +0530792 // Check for Special mode
793 if (mtm.getMfgMode() == SpecialMode::none)
Yong Li85feb132019-08-09 16:06:03 +0800794 {
Ayushi Smritie0511e52019-08-27 17:30:34 +0000795 return ipmi::ccInvalidCommand;
Yong Li85feb132019-08-09 16:06:03 +0800796 }
jayaprakash Mutyala2d4a0192019-11-11 22:12:45 +0000797 return ipmi::ccSuccess;
798 case makeCmdKey(ipmi::netFnStorage, ipmi::storage::cmdDeleteSelEntry):
799 {
800 return ipmi::ccInvalidCommand;
801 }
Yong Li85feb132019-08-09 16:06:03 +0800802 }
Yong Li85feb132019-08-09 16:06:03 +0800803 return ipmi::ccSuccess;
804}
805
Richard Marian Thomaiyar1f0839c2019-08-25 20:10:52 +0530806static constexpr uint8_t maxEthSize = 6;
807static constexpr uint8_t maxSupportedEth = 3;
808static constexpr const char* factoryEthAddrBaseFileName =
809 "/var/sofs/factory-settings/network/mac/eth";
810
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +0530811ipmi::RspType<> setManufacturingData(ipmi::Context::ptr ctx, uint8_t dataType,
Richard Marian Thomaiyar1f0839c2019-08-25 20:10:52 +0530812 std::array<uint8_t, maxEthSize> ethData)
813{
814 // mfg filter logic will restrict this command executing only in mfg mode.
815 if (dataType >= maxSupportedEth)
816 {
817 return ipmi::responseParmOutOfRange();
818 }
819
820 constexpr uint8_t invalidData = 0;
821 constexpr uint8_t validData = 1;
822 constexpr uint8_t ethAddrStrSize =
823 19; // XX:XX:XX:XX:XX:XX + \n + null termination;
824 std::vector<uint8_t> buff(ethAddrStrSize);
825 std::snprintf(reinterpret_cast<char*>(buff.data()), ethAddrStrSize,
826 "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx\n", ethData.at(0),
827 ethData.at(1), ethData.at(2), ethData.at(3), ethData.at(4),
828 ethData.at(5));
829 std::ofstream oEthFile(factoryEthAddrBaseFileName +
830 std::to_string(dataType),
831 std::ofstream::out);
832 if (!oEthFile.good())
833 {
834 return ipmi::responseUnspecifiedError();
835 }
836
837 oEthFile << reinterpret_cast<char*>(buff.data());
838 oEthFile << fflush;
839 oEthFile.close();
840
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +0530841 resetMtmTimer(ctx);
Richard Marian Thomaiyar1f0839c2019-08-25 20:10:52 +0530842 return ipmi::responseSuccess();
843}
844
845ipmi::RspType<uint8_t, std::array<uint8_t, maxEthSize>>
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +0530846 getManufacturingData(ipmi::Context::ptr ctx, uint8_t dataType)
Richard Marian Thomaiyar1f0839c2019-08-25 20:10:52 +0530847{
848 // mfg filter logic will restrict this command executing only in mfg mode.
849 if (dataType >= maxSupportedEth)
850 {
851 return ipmi::responseParmOutOfRange();
852 }
853 std::array<uint8_t, maxEthSize> ethData{0};
854 constexpr uint8_t invalidData = 0;
855 constexpr uint8_t validData = 1;
856
857 std::ifstream iEthFile(factoryEthAddrBaseFileName +
858 std::to_string(dataType),
859 std::ifstream::in);
860 if (!iEthFile.good())
861 {
862 return ipmi::responseSuccess(invalidData, ethData);
863 }
864 std::string ethStr;
865 iEthFile >> ethStr;
866 uint8_t* data = ethData.data();
867 std::sscanf(ethStr.c_str(), "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx",
868 data, (data + 1), (data + 2), (data + 3), (data + 4),
869 (data + 5));
870
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +0530871 resetMtmTimer(ctx);
Richard Marian Thomaiyar1f0839c2019-08-25 20:10:52 +0530872 return ipmi::responseSuccess(validData, ethData);
873}
874
Yong Lif267a672019-08-29 11:16:07 +0800875/** @brief implements slot master write read IPMI command which can be used for
876 * low-level I2C/SMBus write, read or write-read access for PCIE slots
877 * @param reserved - skip 6 bit
878 * @param addressType - address type
879 * @param bbSlotNum - baseboard slot number
880 * @param riserSlotNum - riser slot number
881 * @param reserved2 - skip 2 bit
882 * @param slaveAddr - slave address
883 * @param readCount - number of bytes to be read
884 * @param writeData - data to be written
885 *
886 * @returns IPMI completion code plus response data
887 */
888ipmi::RspType<std::vector<uint8_t>>
889 appSlotI2CMasterWriteRead(uint6_t reserved, uint2_t addressType,
890 uint3_t bbSlotNum, uint3_t riserSlotNum,
891 uint2_t resvered2, uint8_t slaveAddr,
892 uint8_t readCount, std::vector<uint8_t> writeData)
893{
894 const size_t writeCount = writeData.size();
895 std::string i2cBus;
896 if (addressType == slotAddressTypeBus)
897 {
898 std::string path = "/dev/i2c-mux/Riser_" +
899 std::to_string(static_cast<uint8_t>(bbSlotNum)) +
900 "_Mux/Pcie_Slot_" +
901 std::to_string(static_cast<uint8_t>(riserSlotNum));
902
903 if (std::filesystem::exists(path) && std::filesystem::is_symlink(path))
904 {
905 i2cBus = std::filesystem::read_symlink(path);
906 }
907 else
908 {
909 phosphor::logging::log<phosphor::logging::level::ERR>(
910 "Master write read command: Cannot get BusID");
911 return ipmi::responseInvalidFieldRequest();
912 }
913 }
914 else if (addressType == slotAddressTypeUniqueid)
915 {
916 i2cBus = "/dev/i2c-" +
917 std::to_string(static_cast<uint8_t>(bbSlotNum) |
918 (static_cast<uint8_t>(riserSlotNum) << 3));
919 }
920 else
921 {
922 phosphor::logging::log<phosphor::logging::level::ERR>(
923 "Master write read command: invalid request");
924 return ipmi::responseInvalidFieldRequest();
925 }
926
927 // Allow single byte write as it is offset byte to read the data, rest allow
Richard Marian Thomaiyarae13ac62019-12-17 15:45:12 +0530928 // only in Special mode.
Yong Lif267a672019-08-29 11:16:07 +0800929 if (writeCount > 1)
930 {
Richard Marian Thomaiyarae13ac62019-12-17 15:45:12 +0530931 if (mtm.getMfgMode() == SpecialMode::none)
Yong Lif267a672019-08-29 11:16:07 +0800932 {
933 return ipmi::responseInsufficientPrivilege();
934 }
935 }
936
937 if (readCount > slotI2CMaxReadSize)
938 {
939 phosphor::logging::log<phosphor::logging::level::ERR>(
940 "Master write read command: Read count exceeds limit");
941 return ipmi::responseParmOutOfRange();
942 }
943
944 if (!readCount && !writeCount)
945 {
946 phosphor::logging::log<phosphor::logging::level::ERR>(
947 "Master write read command: Read & write count are 0");
948 return ipmi::responseInvalidFieldRequest();
949 }
950
951 std::vector<uint8_t> readBuf(readCount);
952
953 ipmi::Cc retI2C = ipmi::i2cWriteRead(i2cBus, slaveAddr, writeData, readBuf);
954 if (retI2C != ipmi::ccSuccess)
955 {
956 return ipmi::response(retI2C);
957 }
958
959 return ipmi::responseSuccess(readBuf);
960}
Yong Li068b4f22019-09-17 16:32:18 +0800961
962ipmi::RspType<> clearCMOS()
963{
Yong Lid0d010b2019-11-18 12:15:21 +0800964 // There is an i2c device on bus 4, the slave address is 0x38. Based on the
Yong Lieaeb6cb2020-03-09 20:21:50 +0800965 // spec, writing 0x1 to address 0x61 on this device, will trigger the clear
Yong Li068b4f22019-09-17 16:32:18 +0800966 // CMOS action.
Yong Lid0d010b2019-11-18 12:15:21 +0800967 constexpr uint8_t slaveAddr = 0x38;
Yong Li068b4f22019-09-17 16:32:18 +0800968 std::string i2cBus = "/dev/i2c-4";
Yong Lieaeb6cb2020-03-09 20:21:50 +0800969 std::vector<uint8_t> writeData = {0x61, 0x1};
Yong Li068b4f22019-09-17 16:32:18 +0800970 std::vector<uint8_t> readBuf(0);
971
Richard Marian Thomaiyarae13ac62019-12-17 15:45:12 +0530972 if (mtm.getMfgMode() == SpecialMode::none)
Yong Li068b4f22019-09-17 16:32:18 +0800973 {
974 return ipmi::responseInsufficientPrivilege();
975 }
976
977 ipmi::Cc retI2C = ipmi::i2cWriteRead(i2cBus, slaveAddr, writeData, readBuf);
978 return ipmi::response(retI2C);
979}
Vernon Mauerya3702c12019-05-22 13:20:59 -0700980} // namespace ipmi
981
982void register_mtm_commands() __attribute__((constructor));
983void register_mtm_commands()
984{
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700985 // <Get SM Signal>
Vernon Mauery98bbf692019-09-16 11:14:59 -0700986 ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnGeneral,
987 ipmi::intel::general::cmdGetSmSignal,
988 ipmi::Privilege::Admin, ipmi::appMTMGetSignal);
Vernon Mauerya3702c12019-05-22 13:20:59 -0700989
Vernon Mauery98bbf692019-09-16 11:14:59 -0700990 ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnGeneral,
991 ipmi::intel::general::cmdSetSmSignal,
992 ipmi::Privilege::Admin, ipmi::appMTMSetSignal);
Vernon Mauerya3702c12019-05-22 13:20:59 -0700993
Vernon Mauery98bbf692019-09-16 11:14:59 -0700994 ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnGeneral,
995 ipmi::intel::general::cmdMtmKeepAlive,
996 ipmi::Privilege::Admin, ipmi::mtmKeepAlive);
Richard Marian Thomaiyar666dd012019-08-02 20:55:37 +0530997
Vernon Mauery98bbf692019-09-16 11:14:59 -0700998 ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnGeneral,
999 ipmi::intel::general::cmdSetManufacturingData,
1000 ipmi::Privilege::Admin, ipmi::setManufacturingData);
Richard Marian Thomaiyar1f0839c2019-08-25 20:10:52 +05301001
Vernon Mauery98bbf692019-09-16 11:14:59 -07001002 ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnGeneral,
1003 ipmi::intel::general::cmdGetManufacturingData,
1004 ipmi::Privilege::Admin, ipmi::getManufacturingData);
Richard Marian Thomaiyar1f0839c2019-08-25 20:10:52 +05301005
Vernon Mauery98bbf692019-09-16 11:14:59 -07001006 ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnApp,
1007 ipmi::intel::general::cmdSlotI2CMasterWriteRead,
1008 ipmi::Privilege::Admin,
1009 ipmi::appSlotI2CMasterWriteRead);
Yong Lif267a672019-08-29 11:16:07 +08001010
Yong Li068b4f22019-09-17 16:32:18 +08001011 ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnPlatform,
1012 ipmi::intel::platform::cmdClearCMOS,
1013 ipmi::Privilege::Admin, ipmi::clearCMOS);
1014
Vernon Mauery98bbf692019-09-16 11:14:59 -07001015 ipmi::registerFilter(ipmi::prioOemBase,
Yong Li85feb132019-08-09 16:06:03 +08001016 [](ipmi::message::Request::ptr request) {
1017 return ipmi::mfgFilterMessage(request);
1018 });
Vernon Mauerya3702c12019-05-22 13:20:59 -07001019}