blob: 9d8193034977e4757049332b02f638825fb58ae1 [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{
Vernon Mauerya3702c12019-05-22 13:20:59 -0700179 if (revertFanPWM)
180 {
181 revertFanPWM = false;
182 disablePidControlService(false);
183 }
184
Ayushi Smritid872a4a2019-10-15 10:20:11 +0530185 if (mtmTestBeepFd != -1)
186 {
187 ::close(mtmTestBeepFd);
188 mtmTestBeepFd = -1;
189 }
190
Vernon Mauerya3702c12019-05-22 13:20:59 -0700191 for (const auto& ledProperty : ledPropertyList)
192 {
193 const std::string& ledName = ledProperty.getName();
194 ledRevert(ledProperty.getSignal());
195 }
196}
197
198Manufacturing::Manufacturing() :
199 revertTimer([&](void) { revertTimerHandler(); })
200{
201 initData();
202}
203
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700204int8_t Manufacturing::getProperty(const std::string& service,
205 const std::string& path,
206 const std::string& interface,
207 const std::string& propertyName,
208 ipmi::Value* reply)
Vernon Mauerya3702c12019-05-22 13:20:59 -0700209{
210 try
211 {
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700212 *reply = ipmi::getDbusProperty(*getSdBus(), service, path, interface,
213 propertyName);
Vernon Mauerya3702c12019-05-22 13:20:59 -0700214 }
215 catch (const sdbusplus::exception::SdBusError& e)
216 {
217 phosphor::logging::log<phosphor::logging::level::INFO>(
218 "ERROR: getProperty");
219 return -1;
220 }
221
222 return 0;
223}
224
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700225int8_t Manufacturing::setProperty(const std::string& service,
226 const std::string& path,
227 const std::string& interface,
228 const std::string& propertyName,
229 ipmi::Value value)
Vernon Mauerya3702c12019-05-22 13:20:59 -0700230{
231 try
232 {
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700233 ipmi::setDbusProperty(*getSdBus(), service, path, interface,
Vernon Mauerya3702c12019-05-22 13:20:59 -0700234 propertyName, value);
235 }
236 catch (const sdbusplus::exception::SdBusError& e)
237 {
238 phosphor::logging::log<phosphor::logging::level::INFO>(
239 "ERROR: setProperty");
240 return -1;
241 }
242
243 return 0;
244}
245
246int8_t Manufacturing::disablePidControlService(const bool disable)
247{
248 try
249 {
250 auto dbus = getSdBus();
251 auto method = dbus->new_method_call(systemDService, systemDObjPath,
252 systemDMgrIntf,
253 disable ? "StopUnit" : "StartUnit");
254 method.append(pidControlService, "replace");
255 auto reply = dbus->call(method);
256 }
257 catch (const sdbusplus::exception::SdBusError& e)
258 {
259 phosphor::logging::log<phosphor::logging::level::INFO>(
260 "ERROR: phosphor-pid-control service start or stop failed");
261 return -1;
262 }
263 return 0;
264}
265
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700266ipmi::RspType<uint8_t, // Signal value
267 std::optional<uint16_t> // Fan tach value
268 >
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +0530269 appMTMGetSignal(ipmi::Context::ptr ctx, uint8_t signalTypeByte,
Richard Marian Thomaiyar147daec2019-06-15 07:43:48 +0530270 uint8_t instance, uint8_t actionByte)
Vernon Mauerya3702c12019-05-22 13:20:59 -0700271{
Ayushi Smritie0511e52019-08-27 17:30:34 +0000272 // mfg filter logic is used to allow MTM get signal command only in
273 // manfacturing mode.
Vernon Mauerya3702c12019-05-22 13:20:59 -0700274
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700275 SmSignalGet signalType = static_cast<SmSignalGet>(signalTypeByte);
276 SmActionGet action = static_cast<SmActionGet>(actionByte);
277
278 switch (signalType)
279 {
280 case SmSignalGet::smFanPwmGet:
281 {
282 ipmi::Value reply;
Richard Marian Thomaiyar147daec2019-06-15 07:43:48 +0530283 std::string fullPath = fanPwmPath + std::to_string(instance + 1);
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700284 if (mtm.getProperty(fanService, fullPath, fanIntf, "Value",
285 &reply) < 0)
286 {
287 return ipmi::responseInvalidFieldRequest();
288 }
289 double* doubleVal = std::get_if<double>(&reply);
290 if (doubleVal == nullptr)
291 {
292 return ipmi::responseUnspecifiedError();
293 }
294 uint8_t sensorVal = std::round(*doubleVal);
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +0530295 resetMtmTimer(ctx);
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700296 return ipmi::responseSuccess(sensorVal, std::nullopt);
297 }
298 break;
299 case SmSignalGet::smFanTachometerGet:
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700300 {
Richard Marian Thomaiyar147daec2019-06-15 07:43:48 +0530301 boost::system::error_code ec;
302 using objFlatMap = boost::container::flat_map<
303 std::string, boost::container::flat_map<
304 std::string, std::vector<std::string>>>;
305
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +0530306 auto flatMap = ctx->bus->yield_method_call<objFlatMap>(
307 ctx->yield, ec, "xyz.openbmc_project.ObjectMapper",
Richard Marian Thomaiyar147daec2019-06-15 07:43:48 +0530308 "/xyz/openbmc_project/object_mapper",
309 "xyz.openbmc_project.ObjectMapper", "GetSubTree",
310 fanTachBasePath, 0, std::array<const char*, 1>{fanIntf});
311 if (ec)
312 {
313 phosphor::logging::log<phosphor::logging::level::ERR>(
314 "Failed to query fan tach sub tree objects");
315 return ipmi::responseUnspecifiedError();
316 }
317 if (instance >= flatMap.size())
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700318 {
319 return ipmi::responseInvalidFieldRequest();
320 }
Richard Marian Thomaiyar147daec2019-06-15 07:43:48 +0530321 auto itr = flatMap.nth(instance);
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700322 ipmi::Value reply;
Richard Marian Thomaiyar147daec2019-06-15 07:43:48 +0530323 if (mtm.getProperty(fanService, itr->first, fanIntf, "Value",
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700324 &reply) < 0)
325 {
326 return ipmi::responseInvalidFieldRequest();
327 }
328
329 double* doubleVal = std::get_if<double>(&reply);
330 if (doubleVal == nullptr)
331 {
332 return ipmi::responseUnspecifiedError();
333 }
334 uint8_t sensorVal = FAN_PRESENT | FAN_SENSOR_PRESENT;
335 std::optional<uint16_t> fanTach = std::round(*doubleVal);
336
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +0530337 resetMtmTimer(ctx);
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700338 return ipmi::responseSuccess(sensorVal, fanTach);
339 }
340 break;
Richard Marian Thomaiyar8e5e2b02019-08-01 07:50:55 +0530341 case SmSignalGet::smIdentifyButton:
342 {
343 if (action == SmActionGet::revert || action == SmActionGet::ignore)
344 {
345 // ButtonMasked property is not supported for ID button as it is
346 // unnecessary. Hence if requested for revert / ignore, override
347 // it to sample action to make tools happy.
348 action = SmActionGet::sample;
349 }
350 // fall-through
351 }
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700352 case SmSignalGet::smResetButton:
353 case SmSignalGet::smPowerButton:
354 case SmSignalGet::smNMIButton:
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700355 {
356 std::string path;
357 if (getGpioPathForSmSignal(signalType, path) < 0)
358 {
359 return ipmi::responseInvalidFieldRequest();
360 }
361
362 switch (action)
363 {
364 case SmActionGet::sample:
365 phosphor::logging::log<phosphor::logging::level::INFO>(
366 "case SmActionGet::sample");
367 break;
368 case SmActionGet::ignore:
369 {
370 phosphor::logging::log<phosphor::logging::level::INFO>(
371 "case SmActionGet::ignore");
372 if (mtm.setProperty(buttonService, path, buttonIntf,
373 "ButtonMasked", true) < 0)
374 {
375 return ipmi::responseUnspecifiedError();
376 }
377 }
378 break;
379 case SmActionGet::revert:
380 {
381 phosphor::logging::log<phosphor::logging::level::INFO>(
382 "case SmActionGet::revert");
383 if (mtm.setProperty(buttonService, path, buttonIntf,
384 "ButtonMasked", false) < 0)
385 {
386 return ipmi::responseUnspecifiedError();
387 }
388 }
389 break;
390
391 default:
392 return ipmi::responseInvalidFieldRequest();
393 break;
394 }
395
396 ipmi::Value reply;
397 if (mtm.getProperty(buttonService, path, buttonIntf,
398 "ButtonPressed", &reply) < 0)
399 {
400 return ipmi::responseUnspecifiedError();
401 }
402 bool* valPtr = std::get_if<bool>(&reply);
403 if (valPtr == nullptr)
404 {
405 return ipmi::responseUnspecifiedError();
406 }
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +0530407 resetMtmTimer(ctx);
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700408 uint8_t sensorVal = *valPtr;
409 return ipmi::responseSuccess(sensorVal, std::nullopt);
410 }
411 break;
Richard Marian Thomaiyar1b74a212019-08-03 13:26:17 +0530412 case SmSignalGet::smNcsiDiag:
413 {
414 constexpr const char* netBasePath = "/sys/class/net/eth";
415 constexpr const char* carrierSuffix = "/carrier";
416 std::ifstream netIfs(netBasePath + std::to_string(instance) +
417 carrierSuffix);
418 if (!netIfs.good())
419 {
420 return ipmi::responseInvalidFieldRequest();
421 }
422 std::string carrier;
423 netIfs >> carrier;
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +0530424 resetMtmTimer(ctx);
Richard Marian Thomaiyar1b74a212019-08-03 13:26:17 +0530425 return ipmi::responseSuccess(
426 static_cast<uint8_t>(std::stoi(carrier)), std::nullopt);
427 }
428 break;
Vernon Mauerya3702c12019-05-22 13:20:59 -0700429 default:
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700430 return ipmi::responseInvalidFieldRequest();
Vernon Mauerya3702c12019-05-22 13:20:59 -0700431 break;
432 }
Vernon Mauerya3702c12019-05-22 13:20:59 -0700433}
434
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +0530435ipmi::RspType<> appMTMSetSignal(ipmi::Context::ptr ctx, uint8_t signalTypeByte,
436 uint8_t instance, uint8_t actionByte,
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000437 std::optional<uint8_t> pwmSpeed)
Vernon Mauerya3702c12019-05-22 13:20:59 -0700438{
Ayushi Smritie0511e52019-08-27 17:30:34 +0000439 // mfg filter logic is used to allow MTM set signal command only in
440 // manfacturing mode.
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000441
442 SmSignalSet signalType = static_cast<SmSignalSet>(signalTypeByte);
443 SmActionSet action = static_cast<SmActionSet>(actionByte);
444 Cc retCode = ccSuccess;
445 int8_t ret = 0;
446
447 switch (signalType)
448 {
449 case SmSignalSet::smPowerFaultLed:
450 case SmSignalSet::smSystemReadyLed:
451 case SmSignalSet::smIdentifyLed:
452 switch (action)
453 {
454 case SmActionSet::forceDeasserted:
Vernon Mauerya3702c12019-05-22 13:20:59 -0700455 {
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000456 phosphor::logging::log<phosphor::logging::level::INFO>(
457 "case SmActionSet::forceDeasserted");
Vernon Mauerya3702c12019-05-22 13:20:59 -0700458
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000459 retCode = ledStoreAndSet(signalType, std::string("Off"));
460 if (retCode != ccSuccess)
Vernon Mauerya3702c12019-05-22 13:20:59 -0700461 {
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000462 return ipmi::response(retCode);
Vernon Mauerya3702c12019-05-22 13:20:59 -0700463 }
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000464 mtm.revertTimer.start(revertTimeOut);
Vernon Mauerya3702c12019-05-22 13:20:59 -0700465 }
466 break;
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000467 case SmActionSet::forceAsserted:
Vernon Mauerya3702c12019-05-22 13:20:59 -0700468 {
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000469 phosphor::logging::log<phosphor::logging::level::INFO>(
470 "case SmActionSet::forceAsserted");
Vernon Mauerya3702c12019-05-22 13:20:59 -0700471
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000472 retCode = ledStoreAndSet(signalType, std::string("On"));
473 if (retCode != ccSuccess)
474 {
475 return ipmi::response(retCode);
476 }
477 mtm.revertTimer.start(revertTimeOut);
478 if (SmSignalSet::smPowerFaultLed == signalType)
479 {
480 // Deassert "system ready"
481 retCode = ledStoreAndSet(SmSignalSet::smSystemReadyLed,
482 std::string("Off"));
483 }
484 else if (SmSignalSet::smSystemReadyLed == signalType)
485 {
486 // Deassert "fault led"
487 retCode = ledStoreAndSet(SmSignalSet::smPowerFaultLed,
488 std::string("Off"));
489 }
490 }
491 break;
492 case SmActionSet::revert:
493 {
494 phosphor::logging::log<phosphor::logging::level::INFO>(
495 "case SmActionSet::revert");
496 retCode = ledRevert(signalType);
497 }
498 break;
499 default:
500 {
501 return ipmi::responseInvalidFieldRequest();
502 }
503 }
504 break;
505 case SmSignalSet::smFanPowerSpeed:
506 {
507 if ((action == SmActionSet::forceAsserted) && (!pwmSpeed))
508 {
509 return ipmi::responseReqDataLenInvalid();
510 }
511
512 if ((action == SmActionSet::forceAsserted) && (*pwmSpeed > 100))
513 {
514 return ipmi::responseInvalidFieldRequest();
515 }
516
517 uint8_t pwmValue = 0;
518 switch (action)
519 {
520 case SmActionSet::revert:
521 {
522 if (mtm.revertFanPWM)
523 {
524 ret = mtm.disablePidControlService(false);
Vernon Mauerya3702c12019-05-22 13:20:59 -0700525 if (ret < 0)
526 {
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000527 return ipmi::responseUnspecifiedError();
Vernon Mauerya3702c12019-05-22 13:20:59 -0700528 }
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000529 mtm.revertFanPWM = false;
Vernon Mauerya3702c12019-05-22 13:20:59 -0700530 }
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000531 }
532 break;
533 case SmActionSet::forceAsserted:
534 {
535 pwmValue = *pwmSpeed;
536 } // fall-through
537 case SmActionSet::forceDeasserted:
538 {
539 if (!mtm.revertFanPWM)
Vernon Mauerya3702c12019-05-22 13:20:59 -0700540 {
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000541 ret = mtm.disablePidControlService(true);
542 if (ret < 0)
543 {
544 return ipmi::responseUnspecifiedError();
545 }
546 mtm.revertFanPWM = true;
Vernon Mauerya3702c12019-05-22 13:20:59 -0700547 }
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000548 mtm.revertTimer.start(revertTimeOut);
549 std::string fanPwmInstancePath =
550 fanPwmPath + std::to_string(instance + 1);
551
552 ret =
553 mtm.setProperty(fanService, fanPwmInstancePath, fanIntf,
554 "Value", static_cast<double>(pwmValue));
555 if (ret < 0)
556 {
557 return ipmi::responseUnspecifiedError();
558 }
559 }
560 break;
561 default:
562 {
563 return ipmi::responseInvalidFieldRequest();
Vernon Mauerya3702c12019-05-22 13:20:59 -0700564 }
565 }
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000566 }
567 break;
Ayushi Smritid872a4a2019-10-15 10:20:11 +0530568 case SmSignalSet::smSpeaker:
569 {
570 phosphor::logging::log<phosphor::logging::level::INFO>(
571 "Performing Speaker SmActionSet",
572 phosphor::logging::entry("ACTION=%d",
573 static_cast<uint8_t>(action)));
574 switch (action)
575 {
576 case SmActionSet::forceAsserted:
577 {
578 char beepDevName[] = "/dev/input/event0";
579 if (mtm.mtmTestBeepFd != -1)
580 {
581 phosphor::logging::log<phosphor::logging::level::INFO>(
582 "mtm beep device is opened already!");
583 // returning success as already beep is in progress
584 return ipmi::response(retCode);
585 }
586
587 if ((mtm.mtmTestBeepFd =
588 ::open(beepDevName, O_RDWR | O_CLOEXEC)) < 0)
589 {
590 phosphor::logging::log<phosphor::logging::level::ERR>(
591 "Failed to open input device");
592 return ipmi::responseUnspecifiedError();
593 }
594
595 struct input_event event;
596 event.type = EV_SND;
597 event.code = SND_TONE;
598 event.value = 2000;
599
600 if (::write(mtm.mtmTestBeepFd, &event,
601 sizeof(struct input_event)) !=
602 sizeof(struct input_event))
603 {
604 phosphor::logging::log<phosphor::logging::level::ERR>(
605 "Failed to write a tone sound event");
606 ::close(mtm.mtmTestBeepFd);
607 mtm.mtmTestBeepFd = -1;
608 return ipmi::responseUnspecifiedError();
609 }
610 mtm.revertTimer.start(revertTimeOut);
611 }
612 break;
613 case SmActionSet::revert:
614 case SmActionSet::forceDeasserted:
615 {
616 if (mtm.mtmTestBeepFd != -1)
617 {
618 ::close(mtm.mtmTestBeepFd);
619 mtm.mtmTestBeepFd = -1;
620 }
621 }
622 break;
623 default:
624 {
625 return ipmi::responseInvalidFieldRequest();
626 }
627 }
628 }
629 break;
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000630 default:
631 {
632 return ipmi::responseInvalidFieldRequest();
Vernon Mauerya3702c12019-05-22 13:20:59 -0700633 }
634 }
Richard Marian Thomaiyar4cc10152019-08-02 23:21:45 +0530635 if (retCode == ccSuccess)
636 {
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +0530637 resetMtmTimer(ctx);
Richard Marian Thomaiyar4cc10152019-08-02 23:21:45 +0530638 }
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000639 return ipmi::response(retCode);
Vernon Mauerya3702c12019-05-22 13:20:59 -0700640}
641
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +0530642ipmi::RspType<> mtmKeepAlive(ipmi::Context::ptr ctx, uint8_t reserved,
Richard Marian Thomaiyar666dd012019-08-02 20:55:37 +0530643 const std::array<char, 5>& intentionalSignature)
644{
Ayushi Smritie0511e52019-08-27 17:30:34 +0000645 // mfg filter logic is used to allow MTM keep alive command only in
646 // manfacturing mode
647
Richard Marian Thomaiyar666dd012019-08-02 20:55:37 +0530648 constexpr std::array<char, 5> signatureOk = {'I', 'N', 'T', 'E', 'L'};
649 if (intentionalSignature != signatureOk || reserved != 0)
650 {
651 return ipmi::responseInvalidFieldRequest();
652 }
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +0530653 return ipmi::response(resetMtmTimer(ctx));
Richard Marian Thomaiyar666dd012019-08-02 20:55:37 +0530654}
655
Ayushi Smritie0511e52019-08-27 17:30:34 +0000656static constexpr unsigned int makeCmdKey(unsigned int netFn, unsigned int cmd)
657{
658 return (netFn << 8) | cmd;
659}
660
Yong Li85feb132019-08-09 16:06:03 +0800661ipmi::Cc mfgFilterMessage(ipmi::message::Request::ptr request)
662{
Ayushi Smritie0511e52019-08-27 17:30:34 +0000663 // Restricted commands, must be executed only in Manufacturing mode
664 switch (makeCmdKey(request->ctx->netFn, request->ctx->cmd))
Yong Li85feb132019-08-09 16:06:03 +0800665 {
Ayushi Smritie0511e52019-08-27 17:30:34 +0000666 // i2c master write read command needs additional checking
667 case makeCmdKey(ipmi::netFnApp, ipmi::app::cmdMasterWriteRead):
668 if (request->payload.size() > 4)
669 {
670 // Allow write data count > 1, only if it is in MFG mode
671 if (mtm.getAccessLvl() != MtmLvl::mtmAvailable)
672 {
673 return ipmi::ccInsufficientPrivilege;
674 }
675 }
676 break;
677 case makeCmdKey(ipmi::netFnOemOne,
678 ipmi::intel::general::cmdGetSmSignal):
679 case makeCmdKey(ipmi::netFnOemOne,
680 ipmi::intel::general::cmdSetSmSignal):
681 case makeCmdKey(ipmi::netFnOemOne,
682 ipmi::intel::general::cmdMtmKeepAlive):
683 case makeCmdKey(ipmi::netFnOemOne,
684 ipmi::intel::general::cmdSetManufacturingData):
685 case makeCmdKey(ipmi::netFnOemOne,
686 ipmi::intel::general::cmdGetManufacturingData):
687 case makeCmdKey(ipmi::netFnStorage, ipmi::storage::cmdWriteFruData):
688
689 // Check for MTM mode
Yong Li85feb132019-08-09 16:06:03 +0800690 if (mtm.getAccessLvl() != MtmLvl::mtmAvailable)
691 {
Ayushi Smritie0511e52019-08-27 17:30:34 +0000692 return ipmi::ccInvalidCommand;
Yong Li85feb132019-08-09 16:06:03 +0800693 }
Yong Li85feb132019-08-09 16:06:03 +0800694 }
Yong Li85feb132019-08-09 16:06:03 +0800695 return ipmi::ccSuccess;
696}
697
Richard Marian Thomaiyar1f0839c2019-08-25 20:10:52 +0530698static constexpr uint8_t maxEthSize = 6;
699static constexpr uint8_t maxSupportedEth = 3;
700static constexpr const char* factoryEthAddrBaseFileName =
701 "/var/sofs/factory-settings/network/mac/eth";
702
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +0530703ipmi::RspType<> setManufacturingData(ipmi::Context::ptr ctx, uint8_t dataType,
Richard Marian Thomaiyar1f0839c2019-08-25 20:10:52 +0530704 std::array<uint8_t, maxEthSize> ethData)
705{
706 // mfg filter logic will restrict this command executing only in mfg mode.
707 if (dataType >= maxSupportedEth)
708 {
709 return ipmi::responseParmOutOfRange();
710 }
711
712 constexpr uint8_t invalidData = 0;
713 constexpr uint8_t validData = 1;
714 constexpr uint8_t ethAddrStrSize =
715 19; // XX:XX:XX:XX:XX:XX + \n + null termination;
716 std::vector<uint8_t> buff(ethAddrStrSize);
717 std::snprintf(reinterpret_cast<char*>(buff.data()), ethAddrStrSize,
718 "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx\n", ethData.at(0),
719 ethData.at(1), ethData.at(2), ethData.at(3), ethData.at(4),
720 ethData.at(5));
721 std::ofstream oEthFile(factoryEthAddrBaseFileName +
722 std::to_string(dataType),
723 std::ofstream::out);
724 if (!oEthFile.good())
725 {
726 return ipmi::responseUnspecifiedError();
727 }
728
729 oEthFile << reinterpret_cast<char*>(buff.data());
730 oEthFile << fflush;
731 oEthFile.close();
732
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +0530733 resetMtmTimer(ctx);
Richard Marian Thomaiyar1f0839c2019-08-25 20:10:52 +0530734 return ipmi::responseSuccess();
735}
736
737ipmi::RspType<uint8_t, std::array<uint8_t, maxEthSize>>
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +0530738 getManufacturingData(ipmi::Context::ptr ctx, uint8_t dataType)
Richard Marian Thomaiyar1f0839c2019-08-25 20:10:52 +0530739{
740 // mfg filter logic will restrict this command executing only in mfg mode.
741 if (dataType >= maxSupportedEth)
742 {
743 return ipmi::responseParmOutOfRange();
744 }
745 std::array<uint8_t, maxEthSize> ethData{0};
746 constexpr uint8_t invalidData = 0;
747 constexpr uint8_t validData = 1;
748
749 std::ifstream iEthFile(factoryEthAddrBaseFileName +
750 std::to_string(dataType),
751 std::ifstream::in);
752 if (!iEthFile.good())
753 {
754 return ipmi::responseSuccess(invalidData, ethData);
755 }
756 std::string ethStr;
757 iEthFile >> ethStr;
758 uint8_t* data = ethData.data();
759 std::sscanf(ethStr.c_str(), "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx",
760 data, (data + 1), (data + 2), (data + 3), (data + 4),
761 (data + 5));
762
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +0530763 resetMtmTimer(ctx);
Richard Marian Thomaiyar1f0839c2019-08-25 20:10:52 +0530764 return ipmi::responseSuccess(validData, ethData);
765}
766
Yong Lif267a672019-08-29 11:16:07 +0800767/** @brief implements slot master write read IPMI command which can be used for
768 * low-level I2C/SMBus write, read or write-read access for PCIE slots
769 * @param reserved - skip 6 bit
770 * @param addressType - address type
771 * @param bbSlotNum - baseboard slot number
772 * @param riserSlotNum - riser slot number
773 * @param reserved2 - skip 2 bit
774 * @param slaveAddr - slave address
775 * @param readCount - number of bytes to be read
776 * @param writeData - data to be written
777 *
778 * @returns IPMI completion code plus response data
779 */
780ipmi::RspType<std::vector<uint8_t>>
781 appSlotI2CMasterWriteRead(uint6_t reserved, uint2_t addressType,
782 uint3_t bbSlotNum, uint3_t riserSlotNum,
783 uint2_t resvered2, uint8_t slaveAddr,
784 uint8_t readCount, std::vector<uint8_t> writeData)
785{
786 const size_t writeCount = writeData.size();
787 std::string i2cBus;
788 if (addressType == slotAddressTypeBus)
789 {
790 std::string path = "/dev/i2c-mux/Riser_" +
791 std::to_string(static_cast<uint8_t>(bbSlotNum)) +
792 "_Mux/Pcie_Slot_" +
793 std::to_string(static_cast<uint8_t>(riserSlotNum));
794
795 if (std::filesystem::exists(path) && std::filesystem::is_symlink(path))
796 {
797 i2cBus = std::filesystem::read_symlink(path);
798 }
799 else
800 {
801 phosphor::logging::log<phosphor::logging::level::ERR>(
802 "Master write read command: Cannot get BusID");
803 return ipmi::responseInvalidFieldRequest();
804 }
805 }
806 else if (addressType == slotAddressTypeUniqueid)
807 {
808 i2cBus = "/dev/i2c-" +
809 std::to_string(static_cast<uint8_t>(bbSlotNum) |
810 (static_cast<uint8_t>(riserSlotNum) << 3));
811 }
812 else
813 {
814 phosphor::logging::log<phosphor::logging::level::ERR>(
815 "Master write read command: invalid request");
816 return ipmi::responseInvalidFieldRequest();
817 }
818
819 // Allow single byte write as it is offset byte to read the data, rest allow
820 // only in MFG mode.
821 if (writeCount > 1)
822 {
823 if (mtm.getAccessLvl() < MtmLvl::mtmAvailable)
824 {
825 return ipmi::responseInsufficientPrivilege();
826 }
827 }
828
829 if (readCount > slotI2CMaxReadSize)
830 {
831 phosphor::logging::log<phosphor::logging::level::ERR>(
832 "Master write read command: Read count exceeds limit");
833 return ipmi::responseParmOutOfRange();
834 }
835
836 if (!readCount && !writeCount)
837 {
838 phosphor::logging::log<phosphor::logging::level::ERR>(
839 "Master write read command: Read & write count are 0");
840 return ipmi::responseInvalidFieldRequest();
841 }
842
843 std::vector<uint8_t> readBuf(readCount);
844
845 ipmi::Cc retI2C = ipmi::i2cWriteRead(i2cBus, slaveAddr, writeData, readBuf);
846 if (retI2C != ipmi::ccSuccess)
847 {
848 return ipmi::response(retI2C);
849 }
850
851 return ipmi::responseSuccess(readBuf);
852}
Yong Li068b4f22019-09-17 16:32:18 +0800853
854ipmi::RspType<> clearCMOS()
855{
856 // There is an i2c device on bus 4, the slave address is 0x70. Based on the
857 // spec, writing 0x1 to address 0x60 on this device, will trigger the clear
858 // CMOS action.
859 constexpr uint8_t slaveAddr = 0x70;
860 std::string i2cBus = "/dev/i2c-4";
861 std::vector<uint8_t> writeData = {0x60, 0x1};
862 std::vector<uint8_t> readBuf(0);
863
864 // TODO Needs to enhance the below security checking
865 if (mtm.getAccessLvl() < MtmLvl::mtmAvailable)
866 {
867 return ipmi::responseInsufficientPrivilege();
868 }
869
870 ipmi::Cc retI2C = ipmi::i2cWriteRead(i2cBus, slaveAddr, writeData, readBuf);
871 return ipmi::response(retI2C);
872}
Vernon Mauerya3702c12019-05-22 13:20:59 -0700873} // namespace ipmi
874
875void register_mtm_commands() __attribute__((constructor));
876void register_mtm_commands()
877{
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700878 // <Get SM Signal>
Vernon Mauery98bbf692019-09-16 11:14:59 -0700879 ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnGeneral,
880 ipmi::intel::general::cmdGetSmSignal,
881 ipmi::Privilege::Admin, ipmi::appMTMGetSignal);
Vernon Mauerya3702c12019-05-22 13:20:59 -0700882
Vernon Mauery98bbf692019-09-16 11:14:59 -0700883 ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnGeneral,
884 ipmi::intel::general::cmdSetSmSignal,
885 ipmi::Privilege::Admin, ipmi::appMTMSetSignal);
Vernon Mauerya3702c12019-05-22 13:20:59 -0700886
Vernon Mauery98bbf692019-09-16 11:14:59 -0700887 ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnGeneral,
888 ipmi::intel::general::cmdMtmKeepAlive,
889 ipmi::Privilege::Admin, ipmi::mtmKeepAlive);
Richard Marian Thomaiyar666dd012019-08-02 20:55:37 +0530890
Vernon Mauery98bbf692019-09-16 11:14:59 -0700891 ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnGeneral,
892 ipmi::intel::general::cmdSetManufacturingData,
893 ipmi::Privilege::Admin, ipmi::setManufacturingData);
Richard Marian Thomaiyar1f0839c2019-08-25 20:10:52 +0530894
Vernon Mauery98bbf692019-09-16 11:14:59 -0700895 ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnGeneral,
896 ipmi::intel::general::cmdGetManufacturingData,
897 ipmi::Privilege::Admin, ipmi::getManufacturingData);
Richard Marian Thomaiyar1f0839c2019-08-25 20:10:52 +0530898
Vernon Mauery98bbf692019-09-16 11:14:59 -0700899 ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnApp,
900 ipmi::intel::general::cmdSlotI2CMasterWriteRead,
901 ipmi::Privilege::Admin,
902 ipmi::appSlotI2CMasterWriteRead);
Yong Lif267a672019-08-29 11:16:07 +0800903
Yong Li068b4f22019-09-17 16:32:18 +0800904 ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnPlatform,
905 ipmi::intel::platform::cmdClearCMOS,
906 ipmi::Privilege::Admin, ipmi::clearCMOS);
907
Vernon Mauery98bbf692019-09-16 11:14:59 -0700908 ipmi::registerFilter(ipmi::prioOemBase,
Yong Li85feb132019-08-09 16:06:03 +0800909 [](ipmi::message::Request::ptr request) {
910 return ipmi::mfgFilterMessage(request);
911 });
Vernon Mauerya3702c12019-05-22 13:20:59 -0700912}