blob: 3b48bb26cc7c8097df6ddfb6b0484a7d076b921e [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();
203 ledRevert(ledProperty.getSignal());
204 }
205}
206
207Manufacturing::Manufacturing() :
208 revertTimer([&](void) { revertTimerHandler(); })
209{
210 initData();
211}
212
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700213int8_t Manufacturing::getProperty(const std::string& service,
214 const std::string& path,
215 const std::string& interface,
216 const std::string& propertyName,
217 ipmi::Value* reply)
Vernon Mauerya3702c12019-05-22 13:20:59 -0700218{
219 try
220 {
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700221 *reply = ipmi::getDbusProperty(*getSdBus(), service, path, interface,
222 propertyName);
Vernon Mauerya3702c12019-05-22 13:20:59 -0700223 }
224 catch (const sdbusplus::exception::SdBusError& e)
225 {
226 phosphor::logging::log<phosphor::logging::level::INFO>(
227 "ERROR: getProperty");
228 return -1;
229 }
230
231 return 0;
232}
233
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700234int8_t Manufacturing::setProperty(const std::string& service,
235 const std::string& path,
236 const std::string& interface,
237 const std::string& propertyName,
238 ipmi::Value value)
Vernon Mauerya3702c12019-05-22 13:20:59 -0700239{
240 try
241 {
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700242 ipmi::setDbusProperty(*getSdBus(), service, path, interface,
Vernon Mauerya3702c12019-05-22 13:20:59 -0700243 propertyName, value);
244 }
245 catch (const sdbusplus::exception::SdBusError& e)
246 {
247 phosphor::logging::log<phosphor::logging::level::INFO>(
248 "ERROR: setProperty");
249 return -1;
250 }
251
252 return 0;
253}
254
255int8_t Manufacturing::disablePidControlService(const bool disable)
256{
257 try
258 {
259 auto dbus = getSdBus();
260 auto method = dbus->new_method_call(systemDService, systemDObjPath,
261 systemDMgrIntf,
262 disable ? "StopUnit" : "StartUnit");
263 method.append(pidControlService, "replace");
264 auto reply = dbus->call(method);
265 }
266 catch (const sdbusplus::exception::SdBusError& e)
267 {
268 phosphor::logging::log<phosphor::logging::level::INFO>(
269 "ERROR: phosphor-pid-control service start or stop failed");
270 return -1;
271 }
272 return 0;
273}
274
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700275ipmi::RspType<uint8_t, // Signal value
276 std::optional<uint16_t> // Fan tach value
277 >
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +0530278 appMTMGetSignal(ipmi::Context::ptr ctx, uint8_t signalTypeByte,
Richard Marian Thomaiyar147daec2019-06-15 07:43:48 +0530279 uint8_t instance, uint8_t actionByte)
Vernon Mauerya3702c12019-05-22 13:20:59 -0700280{
Ayushi Smritie0511e52019-08-27 17:30:34 +0000281 // mfg filter logic is used to allow MTM get signal command only in
282 // manfacturing mode.
Vernon Mauerya3702c12019-05-22 13:20:59 -0700283
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700284 SmSignalGet signalType = static_cast<SmSignalGet>(signalTypeByte);
285 SmActionGet action = static_cast<SmActionGet>(actionByte);
286
287 switch (signalType)
288 {
anil kumar appana98705b32019-09-11 14:15:50 +0000289 case SmSignalGet::smChassisIntrusion:
290 {
291 ipmi::Value reply;
292 if (mtm.getProperty(intrusionService, intrusionPath, intrusionIntf,
293 "Status", &reply) < 0)
294 {
295 return ipmi::responseInvalidFieldRequest();
296 }
297 std::string* intrusionStatus = std::get_if<std::string>(&reply);
298 if (!intrusionStatus)
299 {
300 return ipmi::responseUnspecifiedError();
301 }
302
303 uint8_t status = 0;
304 if (!intrusionStatus->compare("Normal"))
305 {
306 status = static_cast<uint8_t>(IntrusionStatus::normal);
307 }
308 else if (!intrusionStatus->compare("HardwareIntrusion"))
309 {
310 status =
311 static_cast<uint8_t>(IntrusionStatus::hardwareIntrusion);
312 }
313 else if (!intrusionStatus->compare("TamperingDetected"))
314 {
315 status =
316 static_cast<uint8_t>(IntrusionStatus::tamperingDetected);
317 }
318 else
319 {
320 return ipmi::responseUnspecifiedError();
321 }
322 return ipmi::responseSuccess(status, std::nullopt);
323 }
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700324 case SmSignalGet::smFanPwmGet:
325 {
326 ipmi::Value reply;
Richard Marian Thomaiyar147daec2019-06-15 07:43:48 +0530327 std::string fullPath = fanPwmPath + std::to_string(instance + 1);
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700328 if (mtm.getProperty(fanService, fullPath, fanIntf, "Value",
329 &reply) < 0)
330 {
331 return ipmi::responseInvalidFieldRequest();
332 }
333 double* doubleVal = std::get_if<double>(&reply);
334 if (doubleVal == nullptr)
335 {
336 return ipmi::responseUnspecifiedError();
337 }
338 uint8_t sensorVal = std::round(*doubleVal);
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +0530339 resetMtmTimer(ctx);
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700340 return ipmi::responseSuccess(sensorVal, std::nullopt);
341 }
342 break;
343 case SmSignalGet::smFanTachometerGet:
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700344 {
Richard Marian Thomaiyar147daec2019-06-15 07:43:48 +0530345 boost::system::error_code ec;
346 using objFlatMap = boost::container::flat_map<
347 std::string, boost::container::flat_map<
348 std::string, std::vector<std::string>>>;
349
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +0530350 auto flatMap = ctx->bus->yield_method_call<objFlatMap>(
351 ctx->yield, ec, "xyz.openbmc_project.ObjectMapper",
Richard Marian Thomaiyar147daec2019-06-15 07:43:48 +0530352 "/xyz/openbmc_project/object_mapper",
353 "xyz.openbmc_project.ObjectMapper", "GetSubTree",
354 fanTachBasePath, 0, std::array<const char*, 1>{fanIntf});
355 if (ec)
356 {
357 phosphor::logging::log<phosphor::logging::level::ERR>(
358 "Failed to query fan tach sub tree objects");
359 return ipmi::responseUnspecifiedError();
360 }
361 if (instance >= flatMap.size())
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700362 {
363 return ipmi::responseInvalidFieldRequest();
364 }
Richard Marian Thomaiyar147daec2019-06-15 07:43:48 +0530365 auto itr = flatMap.nth(instance);
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700366 ipmi::Value reply;
Richard Marian Thomaiyar147daec2019-06-15 07:43:48 +0530367 if (mtm.getProperty(fanService, itr->first, fanIntf, "Value",
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700368 &reply) < 0)
369 {
370 return ipmi::responseInvalidFieldRequest();
371 }
372
373 double* doubleVal = std::get_if<double>(&reply);
374 if (doubleVal == nullptr)
375 {
376 return ipmi::responseUnspecifiedError();
377 }
378 uint8_t sensorVal = FAN_PRESENT | FAN_SENSOR_PRESENT;
379 std::optional<uint16_t> fanTach = std::round(*doubleVal);
380
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +0530381 resetMtmTimer(ctx);
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700382 return ipmi::responseSuccess(sensorVal, fanTach);
383 }
384 break;
Richard Marian Thomaiyar8e5e2b02019-08-01 07:50:55 +0530385 case SmSignalGet::smIdentifyButton:
386 {
387 if (action == SmActionGet::revert || action == SmActionGet::ignore)
388 {
389 // ButtonMasked property is not supported for ID button as it is
390 // unnecessary. Hence if requested for revert / ignore, override
391 // it to sample action to make tools happy.
392 action = SmActionGet::sample;
393 }
394 // fall-through
395 }
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700396 case SmSignalGet::smResetButton:
397 case SmSignalGet::smPowerButton:
398 case SmSignalGet::smNMIButton:
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700399 {
400 std::string path;
401 if (getGpioPathForSmSignal(signalType, path) < 0)
402 {
403 return ipmi::responseInvalidFieldRequest();
404 }
405
406 switch (action)
407 {
408 case SmActionGet::sample:
409 phosphor::logging::log<phosphor::logging::level::INFO>(
410 "case SmActionGet::sample");
411 break;
412 case SmActionGet::ignore:
413 {
414 phosphor::logging::log<phosphor::logging::level::INFO>(
415 "case SmActionGet::ignore");
416 if (mtm.setProperty(buttonService, path, buttonIntf,
417 "ButtonMasked", true) < 0)
418 {
419 return ipmi::responseUnspecifiedError();
420 }
421 }
422 break;
423 case SmActionGet::revert:
424 {
425 phosphor::logging::log<phosphor::logging::level::INFO>(
426 "case SmActionGet::revert");
427 if (mtm.setProperty(buttonService, path, buttonIntf,
428 "ButtonMasked", false) < 0)
429 {
430 return ipmi::responseUnspecifiedError();
431 }
432 }
433 break;
434
435 default:
436 return ipmi::responseInvalidFieldRequest();
437 break;
438 }
439
440 ipmi::Value reply;
441 if (mtm.getProperty(buttonService, path, buttonIntf,
442 "ButtonPressed", &reply) < 0)
443 {
444 return ipmi::responseUnspecifiedError();
445 }
446 bool* valPtr = std::get_if<bool>(&reply);
447 if (valPtr == nullptr)
448 {
449 return ipmi::responseUnspecifiedError();
450 }
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +0530451 resetMtmTimer(ctx);
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700452 uint8_t sensorVal = *valPtr;
453 return ipmi::responseSuccess(sensorVal, std::nullopt);
454 }
455 break;
Richard Marian Thomaiyar1b74a212019-08-03 13:26:17 +0530456 case SmSignalGet::smNcsiDiag:
457 {
458 constexpr const char* netBasePath = "/sys/class/net/eth";
459 constexpr const char* carrierSuffix = "/carrier";
460 std::ifstream netIfs(netBasePath + std::to_string(instance) +
461 carrierSuffix);
462 if (!netIfs.good())
463 {
464 return ipmi::responseInvalidFieldRequest();
465 }
466 std::string carrier;
467 netIfs >> carrier;
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +0530468 resetMtmTimer(ctx);
Richard Marian Thomaiyar1b74a212019-08-03 13:26:17 +0530469 return ipmi::responseSuccess(
470 static_cast<uint8_t>(std::stoi(carrier)), std::nullopt);
471 }
472 break;
Vernon Mauerya3702c12019-05-22 13:20:59 -0700473 default:
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700474 return ipmi::responseInvalidFieldRequest();
Vernon Mauerya3702c12019-05-22 13:20:59 -0700475 break;
476 }
Vernon Mauerya3702c12019-05-22 13:20:59 -0700477}
478
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +0530479ipmi::RspType<> appMTMSetSignal(ipmi::Context::ptr ctx, uint8_t signalTypeByte,
480 uint8_t instance, uint8_t actionByte,
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000481 std::optional<uint8_t> pwmSpeed)
Vernon Mauerya3702c12019-05-22 13:20:59 -0700482{
Ayushi Smritie0511e52019-08-27 17:30:34 +0000483 // mfg filter logic is used to allow MTM set signal command only in
484 // manfacturing mode.
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000485
486 SmSignalSet signalType = static_cast<SmSignalSet>(signalTypeByte);
487 SmActionSet action = static_cast<SmActionSet>(actionByte);
488 Cc retCode = ccSuccess;
489 int8_t ret = 0;
490
491 switch (signalType)
492 {
493 case SmSignalSet::smPowerFaultLed:
494 case SmSignalSet::smSystemReadyLed:
495 case SmSignalSet::smIdentifyLed:
496 switch (action)
497 {
498 case SmActionSet::forceDeasserted:
Vernon Mauerya3702c12019-05-22 13:20:59 -0700499 {
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000500 phosphor::logging::log<phosphor::logging::level::INFO>(
501 "case SmActionSet::forceDeasserted");
Vernon Mauerya3702c12019-05-22 13:20:59 -0700502
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000503 retCode = ledStoreAndSet(signalType, std::string("Off"));
504 if (retCode != ccSuccess)
Vernon Mauerya3702c12019-05-22 13:20:59 -0700505 {
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000506 return ipmi::response(retCode);
Vernon Mauerya3702c12019-05-22 13:20:59 -0700507 }
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000508 mtm.revertTimer.start(revertTimeOut);
Vernon Mauerya3702c12019-05-22 13:20:59 -0700509 }
510 break;
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000511 case SmActionSet::forceAsserted:
Vernon Mauerya3702c12019-05-22 13:20:59 -0700512 {
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000513 phosphor::logging::log<phosphor::logging::level::INFO>(
514 "case SmActionSet::forceAsserted");
Vernon Mauerya3702c12019-05-22 13:20:59 -0700515
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000516 retCode = ledStoreAndSet(signalType, std::string("On"));
517 if (retCode != ccSuccess)
518 {
519 return ipmi::response(retCode);
520 }
521 mtm.revertTimer.start(revertTimeOut);
522 if (SmSignalSet::smPowerFaultLed == signalType)
523 {
524 // Deassert "system ready"
525 retCode = ledStoreAndSet(SmSignalSet::smSystemReadyLed,
526 std::string("Off"));
527 }
528 else if (SmSignalSet::smSystemReadyLed == signalType)
529 {
530 // Deassert "fault led"
531 retCode = ledStoreAndSet(SmSignalSet::smPowerFaultLed,
532 std::string("Off"));
533 }
534 }
535 break;
536 case SmActionSet::revert:
537 {
538 phosphor::logging::log<phosphor::logging::level::INFO>(
539 "case SmActionSet::revert");
540 retCode = ledRevert(signalType);
541 }
542 break;
543 default:
544 {
545 return ipmi::responseInvalidFieldRequest();
546 }
547 }
548 break;
549 case SmSignalSet::smFanPowerSpeed:
550 {
551 if ((action == SmActionSet::forceAsserted) && (!pwmSpeed))
552 {
553 return ipmi::responseReqDataLenInvalid();
554 }
555
556 if ((action == SmActionSet::forceAsserted) && (*pwmSpeed > 100))
557 {
558 return ipmi::responseInvalidFieldRequest();
559 }
560
561 uint8_t pwmValue = 0;
562 switch (action)
563 {
564 case SmActionSet::revert:
565 {
566 if (mtm.revertFanPWM)
567 {
568 ret = mtm.disablePidControlService(false);
Vernon Mauerya3702c12019-05-22 13:20:59 -0700569 if (ret < 0)
570 {
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000571 return ipmi::responseUnspecifiedError();
Vernon Mauerya3702c12019-05-22 13:20:59 -0700572 }
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000573 mtm.revertFanPWM = false;
Vernon Mauerya3702c12019-05-22 13:20:59 -0700574 }
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000575 }
576 break;
577 case SmActionSet::forceAsserted:
578 {
579 pwmValue = *pwmSpeed;
580 } // fall-through
581 case SmActionSet::forceDeasserted:
582 {
583 if (!mtm.revertFanPWM)
Vernon Mauerya3702c12019-05-22 13:20:59 -0700584 {
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000585 ret = mtm.disablePidControlService(true);
586 if (ret < 0)
587 {
588 return ipmi::responseUnspecifiedError();
589 }
590 mtm.revertFanPWM = true;
Vernon Mauerya3702c12019-05-22 13:20:59 -0700591 }
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000592 mtm.revertTimer.start(revertTimeOut);
593 std::string fanPwmInstancePath =
594 fanPwmPath + std::to_string(instance + 1);
595
596 ret =
597 mtm.setProperty(fanService, fanPwmInstancePath, fanIntf,
598 "Value", static_cast<double>(pwmValue));
599 if (ret < 0)
600 {
601 return ipmi::responseUnspecifiedError();
602 }
603 }
604 break;
605 default:
606 {
607 return ipmi::responseInvalidFieldRequest();
Vernon Mauerya3702c12019-05-22 13:20:59 -0700608 }
609 }
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000610 }
611 break;
Ayushi Smritid872a4a2019-10-15 10:20:11 +0530612 case SmSignalSet::smSpeaker:
613 {
614 phosphor::logging::log<phosphor::logging::level::INFO>(
615 "Performing Speaker SmActionSet",
616 phosphor::logging::entry("ACTION=%d",
617 static_cast<uint8_t>(action)));
618 switch (action)
619 {
620 case SmActionSet::forceAsserted:
621 {
622 char beepDevName[] = "/dev/input/event0";
623 if (mtm.mtmTestBeepFd != -1)
624 {
625 phosphor::logging::log<phosphor::logging::level::INFO>(
626 "mtm beep device is opened already!");
627 // returning success as already beep is in progress
628 return ipmi::response(retCode);
629 }
630
631 if ((mtm.mtmTestBeepFd =
632 ::open(beepDevName, O_RDWR | O_CLOEXEC)) < 0)
633 {
634 phosphor::logging::log<phosphor::logging::level::ERR>(
635 "Failed to open input device");
636 return ipmi::responseUnspecifiedError();
637 }
638
639 struct input_event event;
640 event.type = EV_SND;
641 event.code = SND_TONE;
642 event.value = 2000;
643
644 if (::write(mtm.mtmTestBeepFd, &event,
645 sizeof(struct input_event)) !=
646 sizeof(struct input_event))
647 {
648 phosphor::logging::log<phosphor::logging::level::ERR>(
649 "Failed to write a tone sound event");
650 ::close(mtm.mtmTestBeepFd);
651 mtm.mtmTestBeepFd = -1;
652 return ipmi::responseUnspecifiedError();
653 }
654 mtm.revertTimer.start(revertTimeOut);
655 }
656 break;
657 case SmActionSet::revert:
658 case SmActionSet::forceDeasserted:
659 {
660 if (mtm.mtmTestBeepFd != -1)
661 {
662 ::close(mtm.mtmTestBeepFd);
663 mtm.mtmTestBeepFd = -1;
664 }
665 }
666 break;
667 default:
668 {
669 return ipmi::responseInvalidFieldRequest();
670 }
671 }
672 }
673 break;
Richard Marian Thomaiyar3594c6d2019-11-19 21:29:04 +0530674 case SmSignalSet::smDiskFaultLed:
675 {
676 boost::system::error_code ec;
677 using objPaths = std::vector<std::string>;
678 std::string driveBasePath =
679 "/xyz/openbmc_project/inventory/item/drive/";
680 static constexpr const char* driveLedIntf =
681 "xyz.openbmc_project.Led.Group";
682 static constexpr const char* hsbpService =
683 "xyz.openbmc_project.HsbpManager";
684
685 auto driveList = ctx->bus->yield_method_call<objPaths>(
686 ctx->yield, ec, "xyz.openbmc_project.ObjectMapper",
687 "/xyz/openbmc_project/object_mapper",
688 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths",
689 driveBasePath, 0, std::array<const char*, 1>{driveLedIntf});
690 if (ec)
691 {
692 phosphor::logging::log<phosphor::logging::level::ERR>(
693 "Failed to query HSBP drive sub tree objects");
694 return ipmi::responseUnspecifiedError();
695 }
696 std::string driveObjPath =
697 driveBasePath + "Drive_" + std::to_string(instance + 1);
698 if (std::find(driveList.begin(), driveList.end(), driveObjPath) ==
699 driveList.end())
700 {
701 return ipmi::responseInvalidFieldRequest();
702 }
703 bool driveLedState = false;
704 switch (action)
705 {
706 case SmActionSet::forceAsserted:
707 {
708 driveLedState = true;
709 }
710 break;
711 case SmActionSet::revert:
712 {
713 driveLedState = false;
714 }
715 break;
716 case SmActionSet::forceDeasserted:
717 {
718 driveLedState = false;
719 }
720 break;
721 default:
722 {
723 return ipmi::responseInvalidFieldRequest();
724 }
725 }
726 ret = mtm.setProperty(hsbpService, driveObjPath, driveLedIntf,
727 "Asserted", driveLedState);
728 if (ret < 0)
729 {
730 return ipmi::responseUnspecifiedError();
731 }
732 }
733 break;
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000734 default:
735 {
736 return ipmi::responseInvalidFieldRequest();
Vernon Mauerya3702c12019-05-22 13:20:59 -0700737 }
738 }
Richard Marian Thomaiyar4cc10152019-08-02 23:21:45 +0530739 if (retCode == ccSuccess)
740 {
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +0530741 resetMtmTimer(ctx);
Richard Marian Thomaiyar4cc10152019-08-02 23:21:45 +0530742 }
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000743 return ipmi::response(retCode);
Vernon Mauerya3702c12019-05-22 13:20:59 -0700744}
745
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +0530746ipmi::RspType<> mtmKeepAlive(ipmi::Context::ptr ctx, uint8_t reserved,
Richard Marian Thomaiyar666dd012019-08-02 20:55:37 +0530747 const std::array<char, 5>& intentionalSignature)
748{
Ayushi Smritie0511e52019-08-27 17:30:34 +0000749 // mfg filter logic is used to allow MTM keep alive command only in
750 // manfacturing mode
751
Richard Marian Thomaiyar666dd012019-08-02 20:55:37 +0530752 constexpr std::array<char, 5> signatureOk = {'I', 'N', 'T', 'E', 'L'};
753 if (intentionalSignature != signatureOk || reserved != 0)
754 {
755 return ipmi::responseInvalidFieldRequest();
756 }
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +0530757 return ipmi::response(resetMtmTimer(ctx));
Richard Marian Thomaiyar666dd012019-08-02 20:55:37 +0530758}
759
Ayushi Smritie0511e52019-08-27 17:30:34 +0000760static constexpr unsigned int makeCmdKey(unsigned int netFn, unsigned int cmd)
761{
762 return (netFn << 8) | cmd;
763}
764
Yong Li85feb132019-08-09 16:06:03 +0800765ipmi::Cc mfgFilterMessage(ipmi::message::Request::ptr request)
766{
Ayushi Smritie0511e52019-08-27 17:30:34 +0000767 // Restricted commands, must be executed only in Manufacturing mode
768 switch (makeCmdKey(request->ctx->netFn, request->ctx->cmd))
Yong Li85feb132019-08-09 16:06:03 +0800769 {
Ayushi Smritie0511e52019-08-27 17:30:34 +0000770 // i2c master write read command needs additional checking
771 case makeCmdKey(ipmi::netFnApp, ipmi::app::cmdMasterWriteRead):
772 if (request->payload.size() > 4)
773 {
Richard Marian Thomaiyarae13ac62019-12-17 15:45:12 +0530774 // Allow write data count > 1 only in Special mode
775 if (mtm.getMfgMode() == SpecialMode::none)
Ayushi Smritie0511e52019-08-27 17:30:34 +0000776 {
777 return ipmi::ccInsufficientPrivilege;
778 }
779 }
jayaprakash Mutyala2d4a0192019-11-11 22:12:45 +0000780 return ipmi::ccSuccess;
Ayushi Smritie0511e52019-08-27 17:30:34 +0000781 case makeCmdKey(ipmi::netFnOemOne,
782 ipmi::intel::general::cmdGetSmSignal):
783 case makeCmdKey(ipmi::netFnOemOne,
784 ipmi::intel::general::cmdSetSmSignal):
785 case makeCmdKey(ipmi::netFnOemOne,
786 ipmi::intel::general::cmdMtmKeepAlive):
787 case makeCmdKey(ipmi::netFnOemOne,
788 ipmi::intel::general::cmdSetManufacturingData):
789 case makeCmdKey(ipmi::netFnOemOne,
790 ipmi::intel::general::cmdGetManufacturingData):
791 case makeCmdKey(ipmi::netFnStorage, ipmi::storage::cmdWriteFruData):
792
Richard Marian Thomaiyarae13ac62019-12-17 15:45:12 +0530793 // Check for Special mode
794 if (mtm.getMfgMode() == SpecialMode::none)
Yong Li85feb132019-08-09 16:06:03 +0800795 {
Ayushi Smritie0511e52019-08-27 17:30:34 +0000796 return ipmi::ccInvalidCommand;
Yong Li85feb132019-08-09 16:06:03 +0800797 }
jayaprakash Mutyala2d4a0192019-11-11 22:12:45 +0000798 return ipmi::ccSuccess;
799 case makeCmdKey(ipmi::netFnStorage, ipmi::storage::cmdDeleteSelEntry):
800 {
801 return ipmi::ccInvalidCommand;
802 }
Yong Li85feb132019-08-09 16:06:03 +0800803 }
Yong Li85feb132019-08-09 16:06:03 +0800804 return ipmi::ccSuccess;
805}
806
Richard Marian Thomaiyar1f0839c2019-08-25 20:10:52 +0530807static constexpr uint8_t maxEthSize = 6;
808static constexpr uint8_t maxSupportedEth = 3;
809static constexpr const char* factoryEthAddrBaseFileName =
810 "/var/sofs/factory-settings/network/mac/eth";
811
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +0530812ipmi::RspType<> setManufacturingData(ipmi::Context::ptr ctx, uint8_t dataType,
Richard Marian Thomaiyar1f0839c2019-08-25 20:10:52 +0530813 std::array<uint8_t, maxEthSize> ethData)
814{
815 // mfg filter logic will restrict this command executing only in mfg mode.
816 if (dataType >= maxSupportedEth)
817 {
818 return ipmi::responseParmOutOfRange();
819 }
820
821 constexpr uint8_t invalidData = 0;
822 constexpr uint8_t validData = 1;
823 constexpr uint8_t ethAddrStrSize =
824 19; // XX:XX:XX:XX:XX:XX + \n + null termination;
825 std::vector<uint8_t> buff(ethAddrStrSize);
826 std::snprintf(reinterpret_cast<char*>(buff.data()), ethAddrStrSize,
827 "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx\n", ethData.at(0),
828 ethData.at(1), ethData.at(2), ethData.at(3), ethData.at(4),
829 ethData.at(5));
830 std::ofstream oEthFile(factoryEthAddrBaseFileName +
831 std::to_string(dataType),
832 std::ofstream::out);
833 if (!oEthFile.good())
834 {
835 return ipmi::responseUnspecifiedError();
836 }
837
838 oEthFile << reinterpret_cast<char*>(buff.data());
Johnathan Mantey6d83e4e2020-05-08 11:01:01 -0700839 oEthFile.flush();
Richard Marian Thomaiyar1f0839c2019-08-25 20:10:52 +0530840 oEthFile.close();
841
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +0530842 resetMtmTimer(ctx);
Richard Marian Thomaiyar1f0839c2019-08-25 20:10:52 +0530843 return ipmi::responseSuccess();
844}
845
846ipmi::RspType<uint8_t, std::array<uint8_t, maxEthSize>>
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +0530847 getManufacturingData(ipmi::Context::ptr ctx, uint8_t dataType)
Richard Marian Thomaiyar1f0839c2019-08-25 20:10:52 +0530848{
849 // mfg filter logic will restrict this command executing only in mfg mode.
850 if (dataType >= maxSupportedEth)
851 {
852 return ipmi::responseParmOutOfRange();
853 }
854 std::array<uint8_t, maxEthSize> ethData{0};
855 constexpr uint8_t invalidData = 0;
856 constexpr uint8_t validData = 1;
857
858 std::ifstream iEthFile(factoryEthAddrBaseFileName +
859 std::to_string(dataType),
860 std::ifstream::in);
861 if (!iEthFile.good())
862 {
863 return ipmi::responseSuccess(invalidData, ethData);
864 }
865 std::string ethStr;
866 iEthFile >> ethStr;
867 uint8_t* data = ethData.data();
868 std::sscanf(ethStr.c_str(), "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx",
869 data, (data + 1), (data + 2), (data + 3), (data + 4),
870 (data + 5));
871
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +0530872 resetMtmTimer(ctx);
Richard Marian Thomaiyar1f0839c2019-08-25 20:10:52 +0530873 return ipmi::responseSuccess(validData, ethData);
874}
875
Yong Lif267a672019-08-29 11:16:07 +0800876/** @brief implements slot master write read IPMI command which can be used for
877 * low-level I2C/SMBus write, read or write-read access for PCIE slots
878 * @param reserved - skip 6 bit
879 * @param addressType - address type
880 * @param bbSlotNum - baseboard slot number
881 * @param riserSlotNum - riser slot number
882 * @param reserved2 - skip 2 bit
883 * @param slaveAddr - slave address
884 * @param readCount - number of bytes to be read
885 * @param writeData - data to be written
886 *
887 * @returns IPMI completion code plus response data
888 */
889ipmi::RspType<std::vector<uint8_t>>
890 appSlotI2CMasterWriteRead(uint6_t reserved, uint2_t addressType,
891 uint3_t bbSlotNum, uint3_t riserSlotNum,
892 uint2_t resvered2, uint8_t slaveAddr,
893 uint8_t readCount, std::vector<uint8_t> writeData)
894{
895 const size_t writeCount = writeData.size();
896 std::string i2cBus;
897 if (addressType == slotAddressTypeBus)
898 {
899 std::string path = "/dev/i2c-mux/Riser_" +
900 std::to_string(static_cast<uint8_t>(bbSlotNum)) +
901 "_Mux/Pcie_Slot_" +
902 std::to_string(static_cast<uint8_t>(riserSlotNum));
903
904 if (std::filesystem::exists(path) && std::filesystem::is_symlink(path))
905 {
906 i2cBus = std::filesystem::read_symlink(path);
907 }
908 else
909 {
910 phosphor::logging::log<phosphor::logging::level::ERR>(
911 "Master write read command: Cannot get BusID");
912 return ipmi::responseInvalidFieldRequest();
913 }
914 }
915 else if (addressType == slotAddressTypeUniqueid)
916 {
917 i2cBus = "/dev/i2c-" +
918 std::to_string(static_cast<uint8_t>(bbSlotNum) |
919 (static_cast<uint8_t>(riserSlotNum) << 3));
920 }
921 else
922 {
923 phosphor::logging::log<phosphor::logging::level::ERR>(
924 "Master write read command: invalid request");
925 return ipmi::responseInvalidFieldRequest();
926 }
927
928 // Allow single byte write as it is offset byte to read the data, rest allow
Richard Marian Thomaiyarae13ac62019-12-17 15:45:12 +0530929 // only in Special mode.
Yong Lif267a672019-08-29 11:16:07 +0800930 if (writeCount > 1)
931 {
Richard Marian Thomaiyarae13ac62019-12-17 15:45:12 +0530932 if (mtm.getMfgMode() == SpecialMode::none)
Yong Lif267a672019-08-29 11:16:07 +0800933 {
934 return ipmi::responseInsufficientPrivilege();
935 }
936 }
937
938 if (readCount > slotI2CMaxReadSize)
939 {
940 phosphor::logging::log<phosphor::logging::level::ERR>(
941 "Master write read command: Read count exceeds limit");
942 return ipmi::responseParmOutOfRange();
943 }
944
945 if (!readCount && !writeCount)
946 {
947 phosphor::logging::log<phosphor::logging::level::ERR>(
948 "Master write read command: Read & write count are 0");
949 return ipmi::responseInvalidFieldRequest();
950 }
951
952 std::vector<uint8_t> readBuf(readCount);
953
954 ipmi::Cc retI2C = ipmi::i2cWriteRead(i2cBus, slaveAddr, writeData, readBuf);
955 if (retI2C != ipmi::ccSuccess)
956 {
957 return ipmi::response(retI2C);
958 }
959
960 return ipmi::responseSuccess(readBuf);
961}
Yong Li068b4f22019-09-17 16:32:18 +0800962
963ipmi::RspType<> clearCMOS()
964{
Yong Lid0d010b2019-11-18 12:15:21 +0800965 // There is an i2c device on bus 4, the slave address is 0x38. Based on the
Yong Lieaeb6cb2020-03-09 20:21:50 +0800966 // spec, writing 0x1 to address 0x61 on this device, will trigger the clear
Yong Li068b4f22019-09-17 16:32:18 +0800967 // CMOS action.
Yong Lid0d010b2019-11-18 12:15:21 +0800968 constexpr uint8_t slaveAddr = 0x38;
Yong Li068b4f22019-09-17 16:32:18 +0800969 std::string i2cBus = "/dev/i2c-4";
Yong Lieaeb6cb2020-03-09 20:21:50 +0800970 std::vector<uint8_t> writeData = {0x61, 0x1};
Yong Li068b4f22019-09-17 16:32:18 +0800971 std::vector<uint8_t> readBuf(0);
972
Richard Marian Thomaiyarae13ac62019-12-17 15:45:12 +0530973 if (mtm.getMfgMode() == SpecialMode::none)
Yong Li068b4f22019-09-17 16:32:18 +0800974 {
975 return ipmi::responseInsufficientPrivilege();
976 }
977
978 ipmi::Cc retI2C = ipmi::i2cWriteRead(i2cBus, slaveAddr, writeData, readBuf);
979 return ipmi::response(retI2C);
980}
Vernon Mauerya3702c12019-05-22 13:20:59 -0700981} // namespace ipmi
982
983void register_mtm_commands() __attribute__((constructor));
984void register_mtm_commands()
985{
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700986 // <Get SM Signal>
Vernon Mauery98bbf692019-09-16 11:14:59 -0700987 ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnGeneral,
988 ipmi::intel::general::cmdGetSmSignal,
989 ipmi::Privilege::Admin, ipmi::appMTMGetSignal);
Vernon Mauerya3702c12019-05-22 13:20:59 -0700990
Vernon Mauery98bbf692019-09-16 11:14:59 -0700991 ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnGeneral,
992 ipmi::intel::general::cmdSetSmSignal,
993 ipmi::Privilege::Admin, ipmi::appMTMSetSignal);
Vernon Mauerya3702c12019-05-22 13:20:59 -0700994
Vernon Mauery98bbf692019-09-16 11:14:59 -0700995 ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnGeneral,
996 ipmi::intel::general::cmdMtmKeepAlive,
997 ipmi::Privilege::Admin, ipmi::mtmKeepAlive);
Richard Marian Thomaiyar666dd012019-08-02 20:55:37 +0530998
Vernon Mauery98bbf692019-09-16 11:14:59 -0700999 ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnGeneral,
1000 ipmi::intel::general::cmdSetManufacturingData,
1001 ipmi::Privilege::Admin, ipmi::setManufacturingData);
Richard Marian Thomaiyar1f0839c2019-08-25 20:10:52 +05301002
Vernon Mauery98bbf692019-09-16 11:14:59 -07001003 ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnGeneral,
1004 ipmi::intel::general::cmdGetManufacturingData,
1005 ipmi::Privilege::Admin, ipmi::getManufacturingData);
Richard Marian Thomaiyar1f0839c2019-08-25 20:10:52 +05301006
Vernon Mauery98bbf692019-09-16 11:14:59 -07001007 ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnApp,
1008 ipmi::intel::general::cmdSlotI2CMasterWriteRead,
1009 ipmi::Privilege::Admin,
1010 ipmi::appSlotI2CMasterWriteRead);
Yong Lif267a672019-08-29 11:16:07 +08001011
Yong Li068b4f22019-09-17 16:32:18 +08001012 ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnPlatform,
1013 ipmi::intel::platform::cmdClearCMOS,
1014 ipmi::Privilege::Admin, ipmi::clearCMOS);
1015
Vernon Mauery98bbf692019-09-16 11:14:59 -07001016 ipmi::registerFilter(ipmi::prioOemBase,
Yong Li85feb132019-08-09 16:06:03 +08001017 [](ipmi::message::Request::ptr request) {
1018 return ipmi::mfgFilterMessage(request);
1019 });
Vernon Mauerya3702c12019-05-22 13:20:59 -07001020}