blob: c92dd00a4db93cf0dd7543b93f6df99c13a1eaf3 [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>
Vernon Mauerya3702c12019-05-22 13:20:59 -070020#include <ipmid/api.hpp>
21#include <manufacturingcommands.hpp>
22#include <oemcommands.hpp>
23
James Feistfcd2d3a2020-05-28 10:38:15 -070024#include <filesystem>
25#include <fstream>
26
Vernon Mauerya3702c12019-05-22 13:20:59 -070027namespace ipmi
28{
29
30Manufacturing mtm;
31
32static auto revertTimeOut =
33 std::chrono::duration_cast<std::chrono::microseconds>(
34 std::chrono::seconds(60)); // 1 minute timeout
35
Yong Lif267a672019-08-29 11:16:07 +080036static constexpr uint8_t slotAddressTypeBus = 0;
37static constexpr uint8_t slotAddressTypeUniqueid = 1;
38static constexpr uint8_t slotI2CMaxReadSize = 35;
39
Vernon Mauerya3702c12019-05-22 13:20:59 -070040static constexpr const char* callbackMgrService =
41 "xyz.openbmc_project.CallbackManager";
42static constexpr const char* callbackMgrIntf =
43 "xyz.openbmc_project.CallbackManager";
44static constexpr const char* callbackMgrObjPath =
45 "/xyz/openbmc_project/CallbackManager";
46static constexpr const char* retriggerLedUpdate = "RetriggerLEDUpdate";
47
48const static constexpr char* systemDService = "org.freedesktop.systemd1";
49const static constexpr char* systemDObjPath = "/org/freedesktop/systemd1";
50const static constexpr char* systemDMgrIntf =
51 "org.freedesktop.systemd1.Manager";
52const static constexpr char* pidControlService = "phosphor-pid-control.service";
53
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +053054static inline Cc resetMtmTimer(ipmi::Context::ptr ctx)
Richard Marian Thomaiyar666dd012019-08-02 20:55:37 +053055{
Richard Marian Thomaiyar666dd012019-08-02 20:55:37 +053056 boost::system::error_code ec;
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +053057 ctx->bus->yield_method_call<>(ctx->yield, ec, specialModeService,
58 specialModeObjPath, specialModeIntf,
59 "ResetTimer");
Richard Marian Thomaiyar666dd012019-08-02 20:55:37 +053060 if (ec)
61 {
62 phosphor::logging::log<phosphor::logging::level::ERR>(
63 "Failed to reset the manufacturing mode timer");
64 return ccUnspecifiedError;
65 }
66 return ccSuccess;
67}
68
Jason M. Bills38d2b5a2019-06-03 16:26:11 -070069int getGpioPathForSmSignal(const SmSignalGet signal, std::string& path)
Vernon Mauerya3702c12019-05-22 13:20:59 -070070{
Jason M. Bills38d2b5a2019-06-03 16:26:11 -070071 switch (signal)
72 {
73 case SmSignalGet::smPowerButton:
74 path = "/xyz/openbmc_project/chassis/buttons/power";
75 break;
76 case SmSignalGet::smResetButton:
77 path = "/xyz/openbmc_project/chassis/buttons/reset";
78 break;
79 case SmSignalGet::smNMIButton:
80 path = "/xyz/openbmc_project/chassis/buttons/nmi";
81 break;
Richard Marian Thomaiyar8e5e2b02019-08-01 07:50:55 +053082 case SmSignalGet::smIdentifyButton:
83 path = "/xyz/openbmc_project/chassis/buttons/id";
84 break;
Jason M. Bills38d2b5a2019-06-03 16:26:11 -070085 default:
86 return -1;
87 break;
88 }
89 return 0;
Vernon Mauerya3702c12019-05-22 13:20:59 -070090}
91
Patrick Venture37890392019-09-25 17:05:03 -070092ipmi_ret_t ledStoreAndSet(SmSignalSet signal, const std::string& setState)
Vernon Mauerya3702c12019-05-22 13:20:59 -070093{
94 LedProperty* ledProp = mtm.findLedProperty(signal);
95 if (ledProp == nullptr)
96 {
97 return IPMI_CC_INVALID_FIELD_REQUEST;
98 }
99
100 std::string ledName = ledProp->getName();
101 std::string ledService = ledServicePrefix + ledName;
102 std::string ledPath = ledPathPrefix + ledName;
103 ipmi::Value presentState;
104
105 if (false == ledProp->getLock())
106 {
107 if (mtm.getProperty(ledService.c_str(), ledPath.c_str(), ledIntf,
108 "State", &presentState) != 0)
109 {
110 return IPMI_CC_UNSPECIFIED_ERROR;
111 }
112 ledProp->setPrevState(std::get<std::string>(presentState));
113 ledProp->setLock(true);
114 if (signal == SmSignalSet::smPowerFaultLed ||
115 signal == SmSignalSet::smSystemReadyLed)
116 {
117 mtm.revertLedCallback = true;
118 }
119 }
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700120 if (mtm.setProperty(ledService, ledPath, ledIntf, "State",
Vernon Mauerya3702c12019-05-22 13:20:59 -0700121 ledStateStr + setState) != 0)
122 {
123 return IPMI_CC_UNSPECIFIED_ERROR;
124 }
125 return IPMI_CC_OK;
126}
127
128ipmi_ret_t ledRevert(SmSignalSet signal)
129{
130 LedProperty* ledProp = mtm.findLedProperty(signal);
131 if (ledProp == nullptr)
132 {
133 return IPMI_CC_INVALID_FIELD_REQUEST;
134 }
135 if (true == ledProp->getLock())
136 {
137 ledProp->setLock(false);
138 if (signal == SmSignalSet::smPowerFaultLed ||
139 signal == SmSignalSet::smSystemReadyLed)
140 {
141 try
142 {
143 ipmi::method_no_args::callDbusMethod(
144 *getSdBus(), callbackMgrService, callbackMgrObjPath,
145 callbackMgrIntf, retriggerLedUpdate);
146 }
147 catch (sdbusplus::exception_t& e)
148 {
149 return IPMI_CC_UNSPECIFIED_ERROR;
150 }
151 mtm.revertLedCallback = false;
152 }
153 else
154 {
155 std::string ledName = ledProp->getName();
156 std::string ledService = ledServicePrefix + ledName;
157 std::string ledPath = ledPathPrefix + ledName;
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700158 if (mtm.setProperty(ledService, ledPath, ledIntf, "State",
159 ledProp->getPrevState()) != 0)
Vernon Mauerya3702c12019-05-22 13:20:59 -0700160 {
161 return IPMI_CC_UNSPECIFIED_ERROR;
162 }
163 }
164 }
165 return IPMI_CC_OK;
166}
167
168void Manufacturing::initData()
169{
Vernon Mauerya3702c12019-05-22 13:20:59 -0700170 ledPropertyList.push_back(
171 LedProperty(SmSignalSet::smPowerFaultLed, "status_amber"));
172 ledPropertyList.push_back(
173 LedProperty(SmSignalSet::smSystemReadyLed, "status_green"));
174 ledPropertyList.push_back(
175 LedProperty(SmSignalSet::smIdentifyLed, "identify"));
176}
177
178void Manufacturing::revertTimerHandler()
179{
Richard Marian Thomaiyarae13ac62019-12-17 15:45:12 +0530180
181#ifdef BMC_VALIDATION_UNSECURE_FEATURE
182 if (mtm.getMfgMode() == SpecialMode::valUnsecure)
183 {
184 // Don't revert the behaviour for validation unsecure mode.
185 return;
186 }
187#endif
Vernon Mauerya3702c12019-05-22 13:20:59 -0700188 if (revertFanPWM)
189 {
190 revertFanPWM = false;
191 disablePidControlService(false);
192 }
193
Ayushi Smritid872a4a2019-10-15 10:20:11 +0530194 if (mtmTestBeepFd != -1)
195 {
196 ::close(mtmTestBeepFd);
197 mtmTestBeepFd = -1;
198 }
199
Vernon Mauerya3702c12019-05-22 13:20:59 -0700200 for (const auto& ledProperty : ledPropertyList)
201 {
202 const std::string& ledName = ledProperty.getName();
Jayaprakash Mutyalaf3656142021-01-24 01:04:19 +0000203 if (ledName == "identify" && mtm.getMfgMode() == SpecialMode::mfg)
204 {
205 // Don't revert the behaviour for manufacturing mode
206 continue;
207 }
Vernon Mauerya3702c12019-05-22 13:20:59 -0700208 ledRevert(ledProperty.getSignal());
209 }
210}
211
212Manufacturing::Manufacturing() :
213 revertTimer([&](void) { revertTimerHandler(); })
214{
215 initData();
216}
217
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700218int8_t Manufacturing::getProperty(const std::string& service,
219 const std::string& path,
220 const std::string& interface,
221 const std::string& propertyName,
222 ipmi::Value* reply)
Vernon Mauerya3702c12019-05-22 13:20:59 -0700223{
224 try
225 {
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700226 *reply = ipmi::getDbusProperty(*getSdBus(), service, path, interface,
227 propertyName);
Vernon Mauerya3702c12019-05-22 13:20:59 -0700228 }
229 catch (const sdbusplus::exception::SdBusError& e)
230 {
231 phosphor::logging::log<phosphor::logging::level::INFO>(
232 "ERROR: getProperty");
233 return -1;
234 }
235
236 return 0;
237}
238
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700239int8_t Manufacturing::setProperty(const std::string& service,
240 const std::string& path,
241 const std::string& interface,
242 const std::string& propertyName,
243 ipmi::Value value)
Vernon Mauerya3702c12019-05-22 13:20:59 -0700244{
245 try
246 {
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700247 ipmi::setDbusProperty(*getSdBus(), service, path, interface,
Vernon Mauerya3702c12019-05-22 13:20:59 -0700248 propertyName, value);
249 }
250 catch (const sdbusplus::exception::SdBusError& e)
251 {
252 phosphor::logging::log<phosphor::logging::level::INFO>(
253 "ERROR: setProperty");
254 return -1;
255 }
256
257 return 0;
258}
259
260int8_t Manufacturing::disablePidControlService(const bool disable)
261{
262 try
263 {
264 auto dbus = getSdBus();
265 auto method = dbus->new_method_call(systemDService, systemDObjPath,
266 systemDMgrIntf,
267 disable ? "StopUnit" : "StartUnit");
268 method.append(pidControlService, "replace");
269 auto reply = dbus->call(method);
270 }
271 catch (const sdbusplus::exception::SdBusError& e)
272 {
273 phosphor::logging::log<phosphor::logging::level::INFO>(
274 "ERROR: phosphor-pid-control service start or stop failed");
275 return -1;
276 }
277 return 0;
278}
279
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700280ipmi::RspType<uint8_t, // Signal value
281 std::optional<uint16_t> // Fan tach value
282 >
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +0530283 appMTMGetSignal(ipmi::Context::ptr ctx, uint8_t signalTypeByte,
Richard Marian Thomaiyar147daec2019-06-15 07:43:48 +0530284 uint8_t instance, uint8_t actionByte)
Vernon Mauerya3702c12019-05-22 13:20:59 -0700285{
Ayushi Smritie0511e52019-08-27 17:30:34 +0000286 // mfg filter logic is used to allow MTM get signal command only in
287 // manfacturing mode.
Vernon Mauerya3702c12019-05-22 13:20:59 -0700288
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700289 SmSignalGet signalType = static_cast<SmSignalGet>(signalTypeByte);
290 SmActionGet action = static_cast<SmActionGet>(actionByte);
291
292 switch (signalType)
293 {
anil kumar appana98705b32019-09-11 14:15:50 +0000294 case SmSignalGet::smChassisIntrusion:
295 {
296 ipmi::Value reply;
297 if (mtm.getProperty(intrusionService, intrusionPath, intrusionIntf,
298 "Status", &reply) < 0)
299 {
300 return ipmi::responseInvalidFieldRequest();
301 }
302 std::string* intrusionStatus = std::get_if<std::string>(&reply);
303 if (!intrusionStatus)
304 {
305 return ipmi::responseUnspecifiedError();
306 }
307
308 uint8_t status = 0;
309 if (!intrusionStatus->compare("Normal"))
310 {
311 status = static_cast<uint8_t>(IntrusionStatus::normal);
312 }
313 else if (!intrusionStatus->compare("HardwareIntrusion"))
314 {
315 status =
316 static_cast<uint8_t>(IntrusionStatus::hardwareIntrusion);
317 }
318 else if (!intrusionStatus->compare("TamperingDetected"))
319 {
320 status =
321 static_cast<uint8_t>(IntrusionStatus::tamperingDetected);
322 }
323 else
324 {
325 return ipmi::responseUnspecifiedError();
326 }
327 return ipmi::responseSuccess(status, std::nullopt);
328 }
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700329 case SmSignalGet::smFanPwmGet:
330 {
331 ipmi::Value reply;
Richard Marian Thomaiyar147daec2019-06-15 07:43:48 +0530332 std::string fullPath = fanPwmPath + std::to_string(instance + 1);
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700333 if (mtm.getProperty(fanService, fullPath, fanIntf, "Value",
334 &reply) < 0)
335 {
336 return ipmi::responseInvalidFieldRequest();
337 }
338 double* doubleVal = std::get_if<double>(&reply);
339 if (doubleVal == nullptr)
340 {
341 return ipmi::responseUnspecifiedError();
342 }
343 uint8_t sensorVal = std::round(*doubleVal);
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +0530344 resetMtmTimer(ctx);
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700345 return ipmi::responseSuccess(sensorVal, std::nullopt);
346 }
347 break;
348 case SmSignalGet::smFanTachometerGet:
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700349 {
Richard Marian Thomaiyar147daec2019-06-15 07:43:48 +0530350 boost::system::error_code ec;
351 using objFlatMap = boost::container::flat_map<
352 std::string, boost::container::flat_map<
353 std::string, std::vector<std::string>>>;
354
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +0530355 auto flatMap = ctx->bus->yield_method_call<objFlatMap>(
356 ctx->yield, ec, "xyz.openbmc_project.ObjectMapper",
Richard Marian Thomaiyar147daec2019-06-15 07:43:48 +0530357 "/xyz/openbmc_project/object_mapper",
358 "xyz.openbmc_project.ObjectMapper", "GetSubTree",
359 fanTachBasePath, 0, std::array<const char*, 1>{fanIntf});
360 if (ec)
361 {
362 phosphor::logging::log<phosphor::logging::level::ERR>(
363 "Failed to query fan tach sub tree objects");
364 return ipmi::responseUnspecifiedError();
365 }
366 if (instance >= flatMap.size())
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700367 {
368 return ipmi::responseInvalidFieldRequest();
369 }
Richard Marian Thomaiyar147daec2019-06-15 07:43:48 +0530370 auto itr = flatMap.nth(instance);
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700371 ipmi::Value reply;
Richard Marian Thomaiyar147daec2019-06-15 07:43:48 +0530372 if (mtm.getProperty(fanService, itr->first, fanIntf, "Value",
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700373 &reply) < 0)
374 {
375 return ipmi::responseInvalidFieldRequest();
376 }
377
378 double* doubleVal = std::get_if<double>(&reply);
379 if (doubleVal == nullptr)
380 {
381 return ipmi::responseUnspecifiedError();
382 }
383 uint8_t sensorVal = FAN_PRESENT | FAN_SENSOR_PRESENT;
384 std::optional<uint16_t> fanTach = std::round(*doubleVal);
385
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +0530386 resetMtmTimer(ctx);
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700387 return ipmi::responseSuccess(sensorVal, fanTach);
388 }
389 break;
Richard Marian Thomaiyar8e5e2b02019-08-01 07:50:55 +0530390 case SmSignalGet::smIdentifyButton:
391 {
392 if (action == SmActionGet::revert || action == SmActionGet::ignore)
393 {
394 // ButtonMasked property is not supported for ID button as it is
395 // unnecessary. Hence if requested for revert / ignore, override
396 // it to sample action to make tools happy.
397 action = SmActionGet::sample;
398 }
399 // fall-through
400 }
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700401 case SmSignalGet::smResetButton:
402 case SmSignalGet::smPowerButton:
403 case SmSignalGet::smNMIButton:
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700404 {
405 std::string path;
406 if (getGpioPathForSmSignal(signalType, path) < 0)
407 {
408 return ipmi::responseInvalidFieldRequest();
409 }
410
411 switch (action)
412 {
413 case SmActionGet::sample:
414 phosphor::logging::log<phosphor::logging::level::INFO>(
415 "case SmActionGet::sample");
416 break;
417 case SmActionGet::ignore:
418 {
419 phosphor::logging::log<phosphor::logging::level::INFO>(
420 "case SmActionGet::ignore");
421 if (mtm.setProperty(buttonService, path, buttonIntf,
422 "ButtonMasked", true) < 0)
423 {
424 return ipmi::responseUnspecifiedError();
425 }
426 }
427 break;
428 case SmActionGet::revert:
429 {
430 phosphor::logging::log<phosphor::logging::level::INFO>(
431 "case SmActionGet::revert");
432 if (mtm.setProperty(buttonService, path, buttonIntf,
433 "ButtonMasked", false) < 0)
434 {
435 return ipmi::responseUnspecifiedError();
436 }
437 }
438 break;
439
440 default:
441 return ipmi::responseInvalidFieldRequest();
442 break;
443 }
444
445 ipmi::Value reply;
446 if (mtm.getProperty(buttonService, path, buttonIntf,
447 "ButtonPressed", &reply) < 0)
448 {
449 return ipmi::responseUnspecifiedError();
450 }
451 bool* valPtr = std::get_if<bool>(&reply);
452 if (valPtr == nullptr)
453 {
454 return ipmi::responseUnspecifiedError();
455 }
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +0530456 resetMtmTimer(ctx);
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700457 uint8_t sensorVal = *valPtr;
458 return ipmi::responseSuccess(sensorVal, std::nullopt);
459 }
460 break;
Richard Marian Thomaiyar1b74a212019-08-03 13:26:17 +0530461 case SmSignalGet::smNcsiDiag:
462 {
463 constexpr const char* netBasePath = "/sys/class/net/eth";
464 constexpr const char* carrierSuffix = "/carrier";
465 std::ifstream netIfs(netBasePath + std::to_string(instance) +
466 carrierSuffix);
467 if (!netIfs.good())
468 {
469 return ipmi::responseInvalidFieldRequest();
470 }
471 std::string carrier;
472 netIfs >> carrier;
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +0530473 resetMtmTimer(ctx);
Richard Marian Thomaiyar1b74a212019-08-03 13:26:17 +0530474 return ipmi::responseSuccess(
475 static_cast<uint8_t>(std::stoi(carrier)), std::nullopt);
476 }
477 break;
Vernon Mauerya3702c12019-05-22 13:20:59 -0700478 default:
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700479 return ipmi::responseInvalidFieldRequest();
Vernon Mauerya3702c12019-05-22 13:20:59 -0700480 break;
481 }
Vernon Mauerya3702c12019-05-22 13:20:59 -0700482}
483
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +0530484ipmi::RspType<> appMTMSetSignal(ipmi::Context::ptr ctx, uint8_t signalTypeByte,
485 uint8_t instance, uint8_t actionByte,
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000486 std::optional<uint8_t> pwmSpeed)
Vernon Mauerya3702c12019-05-22 13:20:59 -0700487{
Ayushi Smritie0511e52019-08-27 17:30:34 +0000488 // mfg filter logic is used to allow MTM set signal command only in
489 // manfacturing mode.
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000490
491 SmSignalSet signalType = static_cast<SmSignalSet>(signalTypeByte);
492 SmActionSet action = static_cast<SmActionSet>(actionByte);
493 Cc retCode = ccSuccess;
494 int8_t ret = 0;
495
496 switch (signalType)
497 {
498 case SmSignalSet::smPowerFaultLed:
499 case SmSignalSet::smSystemReadyLed:
500 case SmSignalSet::smIdentifyLed:
501 switch (action)
502 {
503 case SmActionSet::forceDeasserted:
Vernon Mauerya3702c12019-05-22 13:20:59 -0700504 {
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000505 phosphor::logging::log<phosphor::logging::level::INFO>(
506 "case SmActionSet::forceDeasserted");
Vernon Mauerya3702c12019-05-22 13:20:59 -0700507
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000508 retCode = ledStoreAndSet(signalType, std::string("Off"));
509 if (retCode != ccSuccess)
Vernon Mauerya3702c12019-05-22 13:20:59 -0700510 {
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000511 return ipmi::response(retCode);
Vernon Mauerya3702c12019-05-22 13:20:59 -0700512 }
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000513 mtm.revertTimer.start(revertTimeOut);
Vernon Mauerya3702c12019-05-22 13:20:59 -0700514 }
515 break;
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000516 case SmActionSet::forceAsserted:
Vernon Mauerya3702c12019-05-22 13:20:59 -0700517 {
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000518 phosphor::logging::log<phosphor::logging::level::INFO>(
519 "case SmActionSet::forceAsserted");
Vernon Mauerya3702c12019-05-22 13:20:59 -0700520
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000521 retCode = ledStoreAndSet(signalType, std::string("On"));
522 if (retCode != ccSuccess)
523 {
524 return ipmi::response(retCode);
525 }
526 mtm.revertTimer.start(revertTimeOut);
527 if (SmSignalSet::smPowerFaultLed == signalType)
528 {
529 // Deassert "system ready"
530 retCode = ledStoreAndSet(SmSignalSet::smSystemReadyLed,
531 std::string("Off"));
532 }
533 else if (SmSignalSet::smSystemReadyLed == signalType)
534 {
535 // Deassert "fault led"
536 retCode = ledStoreAndSet(SmSignalSet::smPowerFaultLed,
537 std::string("Off"));
538 }
539 }
540 break;
541 case SmActionSet::revert:
542 {
543 phosphor::logging::log<phosphor::logging::level::INFO>(
544 "case SmActionSet::revert");
545 retCode = ledRevert(signalType);
546 }
547 break;
548 default:
549 {
550 return ipmi::responseInvalidFieldRequest();
551 }
552 }
553 break;
554 case SmSignalSet::smFanPowerSpeed:
555 {
556 if ((action == SmActionSet::forceAsserted) && (!pwmSpeed))
557 {
558 return ipmi::responseReqDataLenInvalid();
559 }
560
561 if ((action == SmActionSet::forceAsserted) && (*pwmSpeed > 100))
562 {
563 return ipmi::responseInvalidFieldRequest();
564 }
565
566 uint8_t pwmValue = 0;
567 switch (action)
568 {
569 case SmActionSet::revert:
570 {
571 if (mtm.revertFanPWM)
572 {
573 ret = mtm.disablePidControlService(false);
Vernon Mauerya3702c12019-05-22 13:20:59 -0700574 if (ret < 0)
575 {
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000576 return ipmi::responseUnspecifiedError();
Vernon Mauerya3702c12019-05-22 13:20:59 -0700577 }
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000578 mtm.revertFanPWM = false;
Vernon Mauerya3702c12019-05-22 13:20:59 -0700579 }
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000580 }
581 break;
582 case SmActionSet::forceAsserted:
583 {
584 pwmValue = *pwmSpeed;
585 } // fall-through
586 case SmActionSet::forceDeasserted:
587 {
588 if (!mtm.revertFanPWM)
Vernon Mauerya3702c12019-05-22 13:20:59 -0700589 {
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000590 ret = mtm.disablePidControlService(true);
591 if (ret < 0)
592 {
593 return ipmi::responseUnspecifiedError();
594 }
595 mtm.revertFanPWM = true;
Vernon Mauerya3702c12019-05-22 13:20:59 -0700596 }
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000597 mtm.revertTimer.start(revertTimeOut);
598 std::string fanPwmInstancePath =
599 fanPwmPath + std::to_string(instance + 1);
600
601 ret =
602 mtm.setProperty(fanService, fanPwmInstancePath, fanIntf,
603 "Value", static_cast<double>(pwmValue));
604 if (ret < 0)
605 {
606 return ipmi::responseUnspecifiedError();
607 }
608 }
609 break;
610 default:
611 {
612 return ipmi::responseInvalidFieldRequest();
Vernon Mauerya3702c12019-05-22 13:20:59 -0700613 }
614 }
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000615 }
616 break;
Ayushi Smritid872a4a2019-10-15 10:20:11 +0530617 case SmSignalSet::smSpeaker:
618 {
619 phosphor::logging::log<phosphor::logging::level::INFO>(
620 "Performing Speaker SmActionSet",
621 phosphor::logging::entry("ACTION=%d",
622 static_cast<uint8_t>(action)));
623 switch (action)
624 {
625 case SmActionSet::forceAsserted:
626 {
627 char beepDevName[] = "/dev/input/event0";
628 if (mtm.mtmTestBeepFd != -1)
629 {
630 phosphor::logging::log<phosphor::logging::level::INFO>(
631 "mtm beep device is opened already!");
632 // returning success as already beep is in progress
633 return ipmi::response(retCode);
634 }
635
636 if ((mtm.mtmTestBeepFd =
637 ::open(beepDevName, O_RDWR | O_CLOEXEC)) < 0)
638 {
639 phosphor::logging::log<phosphor::logging::level::ERR>(
640 "Failed to open input device");
641 return ipmi::responseUnspecifiedError();
642 }
643
644 struct input_event event;
645 event.type = EV_SND;
646 event.code = SND_TONE;
647 event.value = 2000;
648
649 if (::write(mtm.mtmTestBeepFd, &event,
650 sizeof(struct input_event)) !=
651 sizeof(struct input_event))
652 {
653 phosphor::logging::log<phosphor::logging::level::ERR>(
654 "Failed to write a tone sound event");
655 ::close(mtm.mtmTestBeepFd);
656 mtm.mtmTestBeepFd = -1;
657 return ipmi::responseUnspecifiedError();
658 }
659 mtm.revertTimer.start(revertTimeOut);
660 }
661 break;
662 case SmActionSet::revert:
663 case SmActionSet::forceDeasserted:
664 {
665 if (mtm.mtmTestBeepFd != -1)
666 {
667 ::close(mtm.mtmTestBeepFd);
668 mtm.mtmTestBeepFd = -1;
669 }
670 }
671 break;
672 default:
673 {
674 return ipmi::responseInvalidFieldRequest();
675 }
676 }
677 }
678 break;
Richard Marian Thomaiyar3594c6d2019-11-19 21:29:04 +0530679 case SmSignalSet::smDiskFaultLed:
680 {
681 boost::system::error_code ec;
682 using objPaths = std::vector<std::string>;
683 std::string driveBasePath =
684 "/xyz/openbmc_project/inventory/item/drive/";
685 static constexpr const char* driveLedIntf =
686 "xyz.openbmc_project.Led.Group";
687 static constexpr const char* hsbpService =
688 "xyz.openbmc_project.HsbpManager";
689
690 auto driveList = ctx->bus->yield_method_call<objPaths>(
691 ctx->yield, ec, "xyz.openbmc_project.ObjectMapper",
692 "/xyz/openbmc_project/object_mapper",
693 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths",
694 driveBasePath, 0, std::array<const char*, 1>{driveLedIntf});
695 if (ec)
696 {
697 phosphor::logging::log<phosphor::logging::level::ERR>(
698 "Failed to query HSBP drive sub tree objects");
699 return ipmi::responseUnspecifiedError();
700 }
701 std::string driveObjPath =
702 driveBasePath + "Drive_" + std::to_string(instance + 1);
703 if (std::find(driveList.begin(), driveList.end(), driveObjPath) ==
704 driveList.end())
705 {
706 return ipmi::responseInvalidFieldRequest();
707 }
708 bool driveLedState = false;
709 switch (action)
710 {
711 case SmActionSet::forceAsserted:
712 {
713 driveLedState = true;
714 }
715 break;
716 case SmActionSet::revert:
717 {
718 driveLedState = false;
719 }
720 break;
721 case SmActionSet::forceDeasserted:
722 {
723 driveLedState = false;
724 }
725 break;
726 default:
727 {
728 return ipmi::responseInvalidFieldRequest();
729 }
730 }
731 ret = mtm.setProperty(hsbpService, driveObjPath, driveLedIntf,
732 "Asserted", driveLedState);
733 if (ret < 0)
734 {
735 return ipmi::responseUnspecifiedError();
736 }
737 }
738 break;
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000739 default:
740 {
741 return ipmi::responseInvalidFieldRequest();
Vernon Mauerya3702c12019-05-22 13:20:59 -0700742 }
743 }
Richard Marian Thomaiyar4cc10152019-08-02 23:21:45 +0530744 if (retCode == ccSuccess)
745 {
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +0530746 resetMtmTimer(ctx);
Richard Marian Thomaiyar4cc10152019-08-02 23:21:45 +0530747 }
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000748 return ipmi::response(retCode);
Vernon Mauerya3702c12019-05-22 13:20:59 -0700749}
750
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +0530751ipmi::RspType<> mtmKeepAlive(ipmi::Context::ptr ctx, uint8_t reserved,
Richard Marian Thomaiyar666dd012019-08-02 20:55:37 +0530752 const std::array<char, 5>& intentionalSignature)
753{
Ayushi Smritie0511e52019-08-27 17:30:34 +0000754 // mfg filter logic is used to allow MTM keep alive command only in
755 // manfacturing mode
756
Richard Marian Thomaiyar666dd012019-08-02 20:55:37 +0530757 constexpr std::array<char, 5> signatureOk = {'I', 'N', 'T', 'E', 'L'};
758 if (intentionalSignature != signatureOk || reserved != 0)
759 {
760 return ipmi::responseInvalidFieldRequest();
761 }
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +0530762 return ipmi::response(resetMtmTimer(ctx));
Richard Marian Thomaiyar666dd012019-08-02 20:55:37 +0530763}
764
Ayushi Smritie0511e52019-08-27 17:30:34 +0000765static constexpr unsigned int makeCmdKey(unsigned int netFn, unsigned int cmd)
766{
767 return (netFn << 8) | cmd;
768}
769
Yong Li85feb132019-08-09 16:06:03 +0800770ipmi::Cc mfgFilterMessage(ipmi::message::Request::ptr request)
771{
Ayushi Smritie0511e52019-08-27 17:30:34 +0000772 // Restricted commands, must be executed only in Manufacturing mode
773 switch (makeCmdKey(request->ctx->netFn, request->ctx->cmd))
Yong Li85feb132019-08-09 16:06:03 +0800774 {
Ayushi Smritie0511e52019-08-27 17:30:34 +0000775 // i2c master write read command needs additional checking
776 case makeCmdKey(ipmi::netFnApp, ipmi::app::cmdMasterWriteRead):
777 if (request->payload.size() > 4)
778 {
Richard Marian Thomaiyarae13ac62019-12-17 15:45:12 +0530779 // Allow write data count > 1 only in Special mode
780 if (mtm.getMfgMode() == SpecialMode::none)
Ayushi Smritie0511e52019-08-27 17:30:34 +0000781 {
782 return ipmi::ccInsufficientPrivilege;
783 }
784 }
jayaprakash Mutyala2d4a0192019-11-11 22:12:45 +0000785 return ipmi::ccSuccess;
Ayushi Smritie0511e52019-08-27 17:30:34 +0000786 case makeCmdKey(ipmi::netFnOemOne,
787 ipmi::intel::general::cmdGetSmSignal):
788 case makeCmdKey(ipmi::netFnOemOne,
789 ipmi::intel::general::cmdSetSmSignal):
790 case makeCmdKey(ipmi::netFnOemOne,
791 ipmi::intel::general::cmdMtmKeepAlive):
792 case makeCmdKey(ipmi::netFnOemOne,
793 ipmi::intel::general::cmdSetManufacturingData):
794 case makeCmdKey(ipmi::netFnOemOne,
795 ipmi::intel::general::cmdGetManufacturingData):
796 case makeCmdKey(ipmi::netFnStorage, ipmi::storage::cmdWriteFruData):
AppaRao Puli9a13daa2020-07-13 17:53:00 +0530797 case makeCmdKey(ipmi::netFnOemTwo, ipmi::intel::platform::cmdClearCMOS):
Ayushi Smritie0511e52019-08-27 17:30:34 +0000798
Richard Marian Thomaiyarae13ac62019-12-17 15:45:12 +0530799 // Check for Special mode
800 if (mtm.getMfgMode() == SpecialMode::none)
Yong Li85feb132019-08-09 16:06:03 +0800801 {
Ayushi Smritie0511e52019-08-27 17:30:34 +0000802 return ipmi::ccInvalidCommand;
Yong Li85feb132019-08-09 16:06:03 +0800803 }
jayaprakash Mutyala2d4a0192019-11-11 22:12:45 +0000804 return ipmi::ccSuccess;
805 case makeCmdKey(ipmi::netFnStorage, ipmi::storage::cmdDeleteSelEntry):
806 {
807 return ipmi::ccInvalidCommand;
808 }
Yong Li85feb132019-08-09 16:06:03 +0800809 }
Yong Li85feb132019-08-09 16:06:03 +0800810 return ipmi::ccSuccess;
811}
812
Richard Marian Thomaiyar1f0839c2019-08-25 20:10:52 +0530813static constexpr uint8_t maxEthSize = 6;
814static constexpr uint8_t maxSupportedEth = 3;
815static constexpr const char* factoryEthAddrBaseFileName =
816 "/var/sofs/factory-settings/network/mac/eth";
817
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +0530818ipmi::RspType<> setManufacturingData(ipmi::Context::ptr ctx, uint8_t dataType,
Richard Marian Thomaiyar1f0839c2019-08-25 20:10:52 +0530819 std::array<uint8_t, maxEthSize> ethData)
820{
821 // mfg filter logic will restrict this command executing only in mfg mode.
822 if (dataType >= maxSupportedEth)
823 {
824 return ipmi::responseParmOutOfRange();
825 }
826
827 constexpr uint8_t invalidData = 0;
828 constexpr uint8_t validData = 1;
829 constexpr uint8_t ethAddrStrSize =
830 19; // XX:XX:XX:XX:XX:XX + \n + null termination;
831 std::vector<uint8_t> buff(ethAddrStrSize);
832 std::snprintf(reinterpret_cast<char*>(buff.data()), ethAddrStrSize,
833 "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx\n", ethData.at(0),
834 ethData.at(1), ethData.at(2), ethData.at(3), ethData.at(4),
835 ethData.at(5));
836 std::ofstream oEthFile(factoryEthAddrBaseFileName +
837 std::to_string(dataType),
838 std::ofstream::out);
839 if (!oEthFile.good())
840 {
841 return ipmi::responseUnspecifiedError();
842 }
843
844 oEthFile << reinterpret_cast<char*>(buff.data());
Johnathan Mantey6d83e4e2020-05-08 11:01:01 -0700845 oEthFile.flush();
Richard Marian Thomaiyar1f0839c2019-08-25 20:10:52 +0530846 oEthFile.close();
847
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +0530848 resetMtmTimer(ctx);
Richard Marian Thomaiyar1f0839c2019-08-25 20:10:52 +0530849 return ipmi::responseSuccess();
850}
851
852ipmi::RspType<uint8_t, std::array<uint8_t, maxEthSize>>
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +0530853 getManufacturingData(ipmi::Context::ptr ctx, uint8_t dataType)
Richard Marian Thomaiyar1f0839c2019-08-25 20:10:52 +0530854{
855 // mfg filter logic will restrict this command executing only in mfg mode.
856 if (dataType >= maxSupportedEth)
857 {
858 return ipmi::responseParmOutOfRange();
859 }
860 std::array<uint8_t, maxEthSize> ethData{0};
861 constexpr uint8_t invalidData = 0;
862 constexpr uint8_t validData = 1;
863
864 std::ifstream iEthFile(factoryEthAddrBaseFileName +
865 std::to_string(dataType),
866 std::ifstream::in);
867 if (!iEthFile.good())
868 {
869 return ipmi::responseSuccess(invalidData, ethData);
870 }
871 std::string ethStr;
872 iEthFile >> ethStr;
873 uint8_t* data = ethData.data();
874 std::sscanf(ethStr.c_str(), "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx",
875 data, (data + 1), (data + 2), (data + 3), (data + 4),
876 (data + 5));
877
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +0530878 resetMtmTimer(ctx);
Richard Marian Thomaiyar1f0839c2019-08-25 20:10:52 +0530879 return ipmi::responseSuccess(validData, ethData);
880}
881
Yong Lif267a672019-08-29 11:16:07 +0800882/** @brief implements slot master write read IPMI command which can be used for
883 * low-level I2C/SMBus write, read or write-read access for PCIE slots
884 * @param reserved - skip 6 bit
885 * @param addressType - address type
886 * @param bbSlotNum - baseboard slot number
887 * @param riserSlotNum - riser slot number
888 * @param reserved2 - skip 2 bit
889 * @param slaveAddr - slave address
890 * @param readCount - number of bytes to be read
891 * @param writeData - data to be written
892 *
893 * @returns IPMI completion code plus response data
894 */
895ipmi::RspType<std::vector<uint8_t>>
896 appSlotI2CMasterWriteRead(uint6_t reserved, uint2_t addressType,
897 uint3_t bbSlotNum, uint3_t riserSlotNum,
898 uint2_t resvered2, uint8_t slaveAddr,
899 uint8_t readCount, std::vector<uint8_t> writeData)
900{
901 const size_t writeCount = writeData.size();
902 std::string i2cBus;
903 if (addressType == slotAddressTypeBus)
904 {
905 std::string path = "/dev/i2c-mux/Riser_" +
906 std::to_string(static_cast<uint8_t>(bbSlotNum)) +
907 "_Mux/Pcie_Slot_" +
908 std::to_string(static_cast<uint8_t>(riserSlotNum));
909
910 if (std::filesystem::exists(path) && std::filesystem::is_symlink(path))
911 {
912 i2cBus = std::filesystem::read_symlink(path);
913 }
914 else
915 {
916 phosphor::logging::log<phosphor::logging::level::ERR>(
917 "Master write read command: Cannot get BusID");
918 return ipmi::responseInvalidFieldRequest();
919 }
920 }
921 else if (addressType == slotAddressTypeUniqueid)
922 {
923 i2cBus = "/dev/i2c-" +
924 std::to_string(static_cast<uint8_t>(bbSlotNum) |
925 (static_cast<uint8_t>(riserSlotNum) << 3));
926 }
927 else
928 {
929 phosphor::logging::log<phosphor::logging::level::ERR>(
930 "Master write read command: invalid request");
931 return ipmi::responseInvalidFieldRequest();
932 }
933
934 // Allow single byte write as it is offset byte to read the data, rest allow
Richard Marian Thomaiyarae13ac62019-12-17 15:45:12 +0530935 // only in Special mode.
Yong Lif267a672019-08-29 11:16:07 +0800936 if (writeCount > 1)
937 {
Richard Marian Thomaiyarae13ac62019-12-17 15:45:12 +0530938 if (mtm.getMfgMode() == SpecialMode::none)
Yong Lif267a672019-08-29 11:16:07 +0800939 {
940 return ipmi::responseInsufficientPrivilege();
941 }
942 }
943
944 if (readCount > slotI2CMaxReadSize)
945 {
946 phosphor::logging::log<phosphor::logging::level::ERR>(
947 "Master write read command: Read count exceeds limit");
948 return ipmi::responseParmOutOfRange();
949 }
950
951 if (!readCount && !writeCount)
952 {
953 phosphor::logging::log<phosphor::logging::level::ERR>(
954 "Master write read command: Read & write count are 0");
955 return ipmi::responseInvalidFieldRequest();
956 }
957
958 std::vector<uint8_t> readBuf(readCount);
959
960 ipmi::Cc retI2C = ipmi::i2cWriteRead(i2cBus, slaveAddr, writeData, readBuf);
961 if (retI2C != ipmi::ccSuccess)
962 {
963 return ipmi::response(retI2C);
964 }
965
966 return ipmi::responseSuccess(readBuf);
967}
Yong Li068b4f22019-09-17 16:32:18 +0800968
969ipmi::RspType<> clearCMOS()
970{
Yong Lid0d010b2019-11-18 12:15:21 +0800971 // There is an i2c device on bus 4, the slave address is 0x38. Based on the
Yong Lieaeb6cb2020-03-09 20:21:50 +0800972 // spec, writing 0x1 to address 0x61 on this device, will trigger the clear
Yong Li068b4f22019-09-17 16:32:18 +0800973 // CMOS action.
Yong Lid0d010b2019-11-18 12:15:21 +0800974 constexpr uint8_t slaveAddr = 0x38;
Yong Li068b4f22019-09-17 16:32:18 +0800975 std::string i2cBus = "/dev/i2c-4";
Yong Lieaeb6cb2020-03-09 20:21:50 +0800976 std::vector<uint8_t> writeData = {0x61, 0x1};
Yong Li068b4f22019-09-17 16:32:18 +0800977 std::vector<uint8_t> readBuf(0);
978
Yong Li068b4f22019-09-17 16:32:18 +0800979 ipmi::Cc retI2C = ipmi::i2cWriteRead(i2cBus, slaveAddr, writeData, readBuf);
980 return ipmi::response(retI2C);
981}
Vernon Mauerya3702c12019-05-22 13:20:59 -0700982} // namespace ipmi
983
984void register_mtm_commands() __attribute__((constructor));
985void register_mtm_commands()
986{
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700987 // <Get SM Signal>
Vernon Mauery98bbf692019-09-16 11:14:59 -0700988 ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnGeneral,
989 ipmi::intel::general::cmdGetSmSignal,
990 ipmi::Privilege::Admin, ipmi::appMTMGetSignal);
Vernon Mauerya3702c12019-05-22 13:20:59 -0700991
Vernon Mauery98bbf692019-09-16 11:14:59 -0700992 ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnGeneral,
993 ipmi::intel::general::cmdSetSmSignal,
994 ipmi::Privilege::Admin, ipmi::appMTMSetSignal);
Vernon Mauerya3702c12019-05-22 13:20:59 -0700995
Vernon Mauery98bbf692019-09-16 11:14:59 -0700996 ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnGeneral,
997 ipmi::intel::general::cmdMtmKeepAlive,
998 ipmi::Privilege::Admin, ipmi::mtmKeepAlive);
Richard Marian Thomaiyar666dd012019-08-02 20:55:37 +0530999
Vernon Mauery98bbf692019-09-16 11:14:59 -07001000 ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnGeneral,
1001 ipmi::intel::general::cmdSetManufacturingData,
1002 ipmi::Privilege::Admin, ipmi::setManufacturingData);
Richard Marian Thomaiyar1f0839c2019-08-25 20:10:52 +05301003
Vernon Mauery98bbf692019-09-16 11:14:59 -07001004 ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnGeneral,
1005 ipmi::intel::general::cmdGetManufacturingData,
1006 ipmi::Privilege::Admin, ipmi::getManufacturingData);
Richard Marian Thomaiyar1f0839c2019-08-25 20:10:52 +05301007
Vernon Mauery98bbf692019-09-16 11:14:59 -07001008 ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnApp,
1009 ipmi::intel::general::cmdSlotI2CMasterWriteRead,
1010 ipmi::Privilege::Admin,
1011 ipmi::appSlotI2CMasterWriteRead);
Yong Lif267a672019-08-29 11:16:07 +08001012
Yong Li068b4f22019-09-17 16:32:18 +08001013 ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnPlatform,
1014 ipmi::intel::platform::cmdClearCMOS,
1015 ipmi::Privilege::Admin, ipmi::clearCMOS);
1016
Vernon Mauery98bbf692019-09-16 11:14:59 -07001017 ipmi::registerFilter(ipmi::prioOemBase,
Yong Li85feb132019-08-09 16:06:03 +08001018 [](ipmi::message::Request::ptr request) {
1019 return ipmi::mfgFilterMessage(request);
1020 });
Vernon Mauerya3702c12019-05-22 13:20:59 -07001021}