blob: a3fcab148858571c6ea51f6df65582e06e2aeee9 [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
Richard Marian Thomaiyar147daec2019-06-15 07:43:48 +053017#include <boost/container/flat_map.hpp>
Richard Marian Thomaiyar1b74a212019-08-03 13:26:17 +053018#include <fstream>
Vernon Mauerya3702c12019-05-22 13:20:59 -070019#include <ipmid/api.hpp>
20#include <manufacturingcommands.hpp>
21#include <oemcommands.hpp>
22
23namespace ipmi
24{
25
26Manufacturing mtm;
27
28static auto revertTimeOut =
29 std::chrono::duration_cast<std::chrono::microseconds>(
30 std::chrono::seconds(60)); // 1 minute timeout
31
Vernon Mauerya3702c12019-05-22 13:20:59 -070032static constexpr const char* callbackMgrService =
33 "xyz.openbmc_project.CallbackManager";
34static constexpr const char* callbackMgrIntf =
35 "xyz.openbmc_project.CallbackManager";
36static constexpr const char* callbackMgrObjPath =
37 "/xyz/openbmc_project/CallbackManager";
38static constexpr const char* retriggerLedUpdate = "RetriggerLEDUpdate";
39
40const static constexpr char* systemDService = "org.freedesktop.systemd1";
41const static constexpr char* systemDObjPath = "/org/freedesktop/systemd1";
42const static constexpr char* systemDMgrIntf =
43 "org.freedesktop.systemd1.Manager";
44const static constexpr char* pidControlService = "phosphor-pid-control.service";
45
Richard Marian Thomaiyar666dd012019-08-02 20:55:37 +053046static inline Cc resetMtmTimer(boost::asio::yield_context yield)
47{
48 auto sdbusp = getSdBus();
49 boost::system::error_code ec;
50 sdbusp->yield_method_call<>(yield, ec, specialModeService,
51 specialModeObjPath, specialModeIntf,
52 "ResetTimer");
53 if (ec)
54 {
55 phosphor::logging::log<phosphor::logging::level::ERR>(
56 "Failed to reset the manufacturing mode timer");
57 return ccUnspecifiedError;
58 }
59 return ccSuccess;
60}
61
Jason M. Bills38d2b5a2019-06-03 16:26:11 -070062int getGpioPathForSmSignal(const SmSignalGet signal, std::string& path)
Vernon Mauerya3702c12019-05-22 13:20:59 -070063{
Jason M. Bills38d2b5a2019-06-03 16:26:11 -070064 switch (signal)
65 {
66 case SmSignalGet::smPowerButton:
67 path = "/xyz/openbmc_project/chassis/buttons/power";
68 break;
69 case SmSignalGet::smResetButton:
70 path = "/xyz/openbmc_project/chassis/buttons/reset";
71 break;
72 case SmSignalGet::smNMIButton:
73 path = "/xyz/openbmc_project/chassis/buttons/nmi";
74 break;
Richard Marian Thomaiyar8e5e2b02019-08-01 07:50:55 +053075 case SmSignalGet::smIdentifyButton:
76 path = "/xyz/openbmc_project/chassis/buttons/id";
77 break;
Jason M. Bills38d2b5a2019-06-03 16:26:11 -070078 default:
79 return -1;
80 break;
81 }
82 return 0;
Vernon Mauerya3702c12019-05-22 13:20:59 -070083}
84
85ipmi_ret_t ledStoreAndSet(SmSignalSet signal, std::string setState)
86{
87 LedProperty* ledProp = mtm.findLedProperty(signal);
88 if (ledProp == nullptr)
89 {
90 return IPMI_CC_INVALID_FIELD_REQUEST;
91 }
92
93 std::string ledName = ledProp->getName();
94 std::string ledService = ledServicePrefix + ledName;
95 std::string ledPath = ledPathPrefix + ledName;
96 ipmi::Value presentState;
97
98 if (false == ledProp->getLock())
99 {
100 if (mtm.getProperty(ledService.c_str(), ledPath.c_str(), ledIntf,
101 "State", &presentState) != 0)
102 {
103 return IPMI_CC_UNSPECIFIED_ERROR;
104 }
105 ledProp->setPrevState(std::get<std::string>(presentState));
106 ledProp->setLock(true);
107 if (signal == SmSignalSet::smPowerFaultLed ||
108 signal == SmSignalSet::smSystemReadyLed)
109 {
110 mtm.revertLedCallback = true;
111 }
112 }
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700113 if (mtm.setProperty(ledService, ledPath, ledIntf, "State",
Vernon Mauerya3702c12019-05-22 13:20:59 -0700114 ledStateStr + setState) != 0)
115 {
116 return IPMI_CC_UNSPECIFIED_ERROR;
117 }
118 return IPMI_CC_OK;
119}
120
121ipmi_ret_t ledRevert(SmSignalSet signal)
122{
123 LedProperty* ledProp = mtm.findLedProperty(signal);
124 if (ledProp == nullptr)
125 {
126 return IPMI_CC_INVALID_FIELD_REQUEST;
127 }
128 if (true == ledProp->getLock())
129 {
130 ledProp->setLock(false);
131 if (signal == SmSignalSet::smPowerFaultLed ||
132 signal == SmSignalSet::smSystemReadyLed)
133 {
134 try
135 {
136 ipmi::method_no_args::callDbusMethod(
137 *getSdBus(), callbackMgrService, callbackMgrObjPath,
138 callbackMgrIntf, retriggerLedUpdate);
139 }
140 catch (sdbusplus::exception_t& e)
141 {
142 return IPMI_CC_UNSPECIFIED_ERROR;
143 }
144 mtm.revertLedCallback = false;
145 }
146 else
147 {
148 std::string ledName = ledProp->getName();
149 std::string ledService = ledServicePrefix + ledName;
150 std::string ledPath = ledPathPrefix + ledName;
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700151 if (mtm.setProperty(ledService, ledPath, ledIntf, "State",
152 ledProp->getPrevState()) != 0)
Vernon Mauerya3702c12019-05-22 13:20:59 -0700153 {
154 return IPMI_CC_UNSPECIFIED_ERROR;
155 }
156 }
157 }
158 return IPMI_CC_OK;
159}
160
161void Manufacturing::initData()
162{
Vernon Mauerya3702c12019-05-22 13:20:59 -0700163 ledPropertyList.push_back(
164 LedProperty(SmSignalSet::smPowerFaultLed, "status_amber"));
165 ledPropertyList.push_back(
166 LedProperty(SmSignalSet::smSystemReadyLed, "status_green"));
167 ledPropertyList.push_back(
168 LedProperty(SmSignalSet::smIdentifyLed, "identify"));
169}
170
171void Manufacturing::revertTimerHandler()
172{
Vernon Mauerya3702c12019-05-22 13:20:59 -0700173 if (revertFanPWM)
174 {
175 revertFanPWM = false;
176 disablePidControlService(false);
177 }
178
179 for (const auto& ledProperty : ledPropertyList)
180 {
181 const std::string& ledName = ledProperty.getName();
182 ledRevert(ledProperty.getSignal());
183 }
184}
185
186Manufacturing::Manufacturing() :
187 revertTimer([&](void) { revertTimerHandler(); })
188{
189 initData();
190}
191
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700192int8_t Manufacturing::getProperty(const std::string& service,
193 const std::string& path,
194 const std::string& interface,
195 const std::string& propertyName,
196 ipmi::Value* reply)
Vernon Mauerya3702c12019-05-22 13:20:59 -0700197{
198 try
199 {
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700200 *reply = ipmi::getDbusProperty(*getSdBus(), service, path, interface,
201 propertyName);
Vernon Mauerya3702c12019-05-22 13:20:59 -0700202 }
203 catch (const sdbusplus::exception::SdBusError& e)
204 {
205 phosphor::logging::log<phosphor::logging::level::INFO>(
206 "ERROR: getProperty");
207 return -1;
208 }
209
210 return 0;
211}
212
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700213int8_t Manufacturing::setProperty(const std::string& service,
214 const std::string& path,
215 const std::string& interface,
216 const std::string& propertyName,
217 ipmi::Value value)
Vernon Mauerya3702c12019-05-22 13:20:59 -0700218{
219 try
220 {
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700221 ipmi::setDbusProperty(*getSdBus(), service, path, interface,
Vernon Mauerya3702c12019-05-22 13:20:59 -0700222 propertyName, value);
223 }
224 catch (const sdbusplus::exception::SdBusError& e)
225 {
226 phosphor::logging::log<phosphor::logging::level::INFO>(
227 "ERROR: setProperty");
228 return -1;
229 }
230
231 return 0;
232}
233
234int8_t Manufacturing::disablePidControlService(const bool disable)
235{
236 try
237 {
238 auto dbus = getSdBus();
239 auto method = dbus->new_method_call(systemDService, systemDObjPath,
240 systemDMgrIntf,
241 disable ? "StopUnit" : "StartUnit");
242 method.append(pidControlService, "replace");
243 auto reply = dbus->call(method);
244 }
245 catch (const sdbusplus::exception::SdBusError& e)
246 {
247 phosphor::logging::log<phosphor::logging::level::INFO>(
248 "ERROR: phosphor-pid-control service start or stop failed");
249 return -1;
250 }
251 return 0;
252}
253
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700254ipmi::RspType<uint8_t, // Signal value
255 std::optional<uint16_t> // Fan tach value
256 >
Richard Marian Thomaiyar147daec2019-06-15 07:43:48 +0530257 appMTMGetSignal(boost::asio::yield_context yield, uint8_t signalTypeByte,
258 uint8_t instance, uint8_t actionByte)
Vernon Mauerya3702c12019-05-22 13:20:59 -0700259{
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700260 if (mtm.getAccessLvl() < MtmLvl::mtmAvailable)
Vernon Mauerya3702c12019-05-22 13:20:59 -0700261 {
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700262 return ipmi::responseInvalidCommand();
263 }
Vernon Mauerya3702c12019-05-22 13:20:59 -0700264
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700265 SmSignalGet signalType = static_cast<SmSignalGet>(signalTypeByte);
266 SmActionGet action = static_cast<SmActionGet>(actionByte);
267
268 switch (signalType)
269 {
270 case SmSignalGet::smFanPwmGet:
271 {
272 ipmi::Value reply;
Richard Marian Thomaiyar147daec2019-06-15 07:43:48 +0530273 std::string fullPath = fanPwmPath + std::to_string(instance + 1);
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700274 if (mtm.getProperty(fanService, fullPath, fanIntf, "Value",
275 &reply) < 0)
276 {
277 return ipmi::responseInvalidFieldRequest();
278 }
279 double* doubleVal = std::get_if<double>(&reply);
280 if (doubleVal == nullptr)
281 {
282 return ipmi::responseUnspecifiedError();
283 }
284 uint8_t sensorVal = std::round(*doubleVal);
Richard Marian Thomaiyar4cc10152019-08-02 23:21:45 +0530285 resetMtmTimer(yield);
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700286 return ipmi::responseSuccess(sensorVal, std::nullopt);
287 }
288 break;
289 case SmSignalGet::smFanTachometerGet:
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700290 {
Richard Marian Thomaiyar147daec2019-06-15 07:43:48 +0530291 auto sdbusp = getSdBus();
292 boost::system::error_code ec;
293 using objFlatMap = boost::container::flat_map<
294 std::string, boost::container::flat_map<
295 std::string, std::vector<std::string>>>;
296
297 auto flatMap = sdbusp->yield_method_call<objFlatMap>(
298 yield, ec, "xyz.openbmc_project.ObjectMapper",
299 "/xyz/openbmc_project/object_mapper",
300 "xyz.openbmc_project.ObjectMapper", "GetSubTree",
301 fanTachBasePath, 0, std::array<const char*, 1>{fanIntf});
302 if (ec)
303 {
304 phosphor::logging::log<phosphor::logging::level::ERR>(
305 "Failed to query fan tach sub tree objects");
306 return ipmi::responseUnspecifiedError();
307 }
308 if (instance >= flatMap.size())
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700309 {
310 return ipmi::responseInvalidFieldRequest();
311 }
Richard Marian Thomaiyar147daec2019-06-15 07:43:48 +0530312 auto itr = flatMap.nth(instance);
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700313 ipmi::Value reply;
Richard Marian Thomaiyar147daec2019-06-15 07:43:48 +0530314 if (mtm.getProperty(fanService, itr->first, fanIntf, "Value",
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700315 &reply) < 0)
316 {
317 return ipmi::responseInvalidFieldRequest();
318 }
319
320 double* doubleVal = std::get_if<double>(&reply);
321 if (doubleVal == nullptr)
322 {
323 return ipmi::responseUnspecifiedError();
324 }
325 uint8_t sensorVal = FAN_PRESENT | FAN_SENSOR_PRESENT;
326 std::optional<uint16_t> fanTach = std::round(*doubleVal);
327
Richard Marian Thomaiyar4cc10152019-08-02 23:21:45 +0530328 resetMtmTimer(yield);
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700329 return ipmi::responseSuccess(sensorVal, fanTach);
330 }
331 break;
Richard Marian Thomaiyar8e5e2b02019-08-01 07:50:55 +0530332 case SmSignalGet::smIdentifyButton:
333 {
334 if (action == SmActionGet::revert || action == SmActionGet::ignore)
335 {
336 // ButtonMasked property is not supported for ID button as it is
337 // unnecessary. Hence if requested for revert / ignore, override
338 // it to sample action to make tools happy.
339 action = SmActionGet::sample;
340 }
341 // fall-through
342 }
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700343 case SmSignalGet::smResetButton:
344 case SmSignalGet::smPowerButton:
345 case SmSignalGet::smNMIButton:
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700346 {
347 std::string path;
348 if (getGpioPathForSmSignal(signalType, path) < 0)
349 {
350 return ipmi::responseInvalidFieldRequest();
351 }
352
353 switch (action)
354 {
355 case SmActionGet::sample:
356 phosphor::logging::log<phosphor::logging::level::INFO>(
357 "case SmActionGet::sample");
358 break;
359 case SmActionGet::ignore:
360 {
361 phosphor::logging::log<phosphor::logging::level::INFO>(
362 "case SmActionGet::ignore");
363 if (mtm.setProperty(buttonService, path, buttonIntf,
364 "ButtonMasked", true) < 0)
365 {
366 return ipmi::responseUnspecifiedError();
367 }
368 }
369 break;
370 case SmActionGet::revert:
371 {
372 phosphor::logging::log<phosphor::logging::level::INFO>(
373 "case SmActionGet::revert");
374 if (mtm.setProperty(buttonService, path, buttonIntf,
375 "ButtonMasked", false) < 0)
376 {
377 return ipmi::responseUnspecifiedError();
378 }
379 }
380 break;
381
382 default:
383 return ipmi::responseInvalidFieldRequest();
384 break;
385 }
386
387 ipmi::Value reply;
388 if (mtm.getProperty(buttonService, path, buttonIntf,
389 "ButtonPressed", &reply) < 0)
390 {
391 return ipmi::responseUnspecifiedError();
392 }
393 bool* valPtr = std::get_if<bool>(&reply);
394 if (valPtr == nullptr)
395 {
396 return ipmi::responseUnspecifiedError();
397 }
Richard Marian Thomaiyar4cc10152019-08-02 23:21:45 +0530398 resetMtmTimer(yield);
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700399 uint8_t sensorVal = *valPtr;
400 return ipmi::responseSuccess(sensorVal, std::nullopt);
401 }
402 break;
Richard Marian Thomaiyar1b74a212019-08-03 13:26:17 +0530403 case SmSignalGet::smNcsiDiag:
404 {
405 constexpr const char* netBasePath = "/sys/class/net/eth";
406 constexpr const char* carrierSuffix = "/carrier";
407 std::ifstream netIfs(netBasePath + std::to_string(instance) +
408 carrierSuffix);
409 if (!netIfs.good())
410 {
411 return ipmi::responseInvalidFieldRequest();
412 }
413 std::string carrier;
414 netIfs >> carrier;
415 resetMtmTimer(yield);
416 return ipmi::responseSuccess(
417 static_cast<uint8_t>(std::stoi(carrier)), std::nullopt);
418 }
419 break;
Vernon Mauerya3702c12019-05-22 13:20:59 -0700420 default:
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700421 return ipmi::responseInvalidFieldRequest();
Vernon Mauerya3702c12019-05-22 13:20:59 -0700422 break;
423 }
Vernon Mauerya3702c12019-05-22 13:20:59 -0700424}
425
Richard Marian Thomaiyar4cc10152019-08-02 23:21:45 +0530426ipmi::RspType<> appMTMSetSignal(boost::asio::yield_context yield,
427 uint8_t signalTypeByte, uint8_t instance,
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000428 uint8_t actionByte,
429 std::optional<uint8_t> pwmSpeed)
Vernon Mauerya3702c12019-05-22 13:20:59 -0700430{
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000431 if (mtm.getAccessLvl() < MtmLvl::mtmAvailable)
Vernon Mauerya3702c12019-05-22 13:20:59 -0700432 {
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000433 return ipmi::responseInvalidCommand();
434 }
435
436 SmSignalSet signalType = static_cast<SmSignalSet>(signalTypeByte);
437 SmActionSet action = static_cast<SmActionSet>(actionByte);
438 Cc retCode = ccSuccess;
439 int8_t ret = 0;
440
441 switch (signalType)
442 {
443 case SmSignalSet::smPowerFaultLed:
444 case SmSignalSet::smSystemReadyLed:
445 case SmSignalSet::smIdentifyLed:
446 switch (action)
447 {
448 case SmActionSet::forceDeasserted:
Vernon Mauerya3702c12019-05-22 13:20:59 -0700449 {
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000450 phosphor::logging::log<phosphor::logging::level::INFO>(
451 "case SmActionSet::forceDeasserted");
Vernon Mauerya3702c12019-05-22 13:20:59 -0700452
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000453 retCode = ledStoreAndSet(signalType, std::string("Off"));
454 if (retCode != ccSuccess)
Vernon Mauerya3702c12019-05-22 13:20:59 -0700455 {
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000456 return ipmi::response(retCode);
Vernon Mauerya3702c12019-05-22 13:20:59 -0700457 }
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000458 mtm.revertTimer.start(revertTimeOut);
Vernon Mauerya3702c12019-05-22 13:20:59 -0700459 }
460 break;
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000461 case SmActionSet::forceAsserted:
Vernon Mauerya3702c12019-05-22 13:20:59 -0700462 {
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000463 phosphor::logging::log<phosphor::logging::level::INFO>(
464 "case SmActionSet::forceAsserted");
Vernon Mauerya3702c12019-05-22 13:20:59 -0700465
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000466 retCode = ledStoreAndSet(signalType, std::string("On"));
467 if (retCode != ccSuccess)
468 {
469 return ipmi::response(retCode);
470 }
471 mtm.revertTimer.start(revertTimeOut);
472 if (SmSignalSet::smPowerFaultLed == signalType)
473 {
474 // Deassert "system ready"
475 retCode = ledStoreAndSet(SmSignalSet::smSystemReadyLed,
476 std::string("Off"));
477 }
478 else if (SmSignalSet::smSystemReadyLed == signalType)
479 {
480 // Deassert "fault led"
481 retCode = ledStoreAndSet(SmSignalSet::smPowerFaultLed,
482 std::string("Off"));
483 }
484 }
485 break;
486 case SmActionSet::revert:
487 {
488 phosphor::logging::log<phosphor::logging::level::INFO>(
489 "case SmActionSet::revert");
490 retCode = ledRevert(signalType);
491 }
492 break;
493 default:
494 {
495 return ipmi::responseInvalidFieldRequest();
496 }
497 }
498 break;
499 case SmSignalSet::smFanPowerSpeed:
500 {
501 if ((action == SmActionSet::forceAsserted) && (!pwmSpeed))
502 {
503 return ipmi::responseReqDataLenInvalid();
504 }
505
506 if ((action == SmActionSet::forceAsserted) && (*pwmSpeed > 100))
507 {
508 return ipmi::responseInvalidFieldRequest();
509 }
510
511 uint8_t pwmValue = 0;
512 switch (action)
513 {
514 case SmActionSet::revert:
515 {
516 if (mtm.revertFanPWM)
517 {
518 ret = mtm.disablePidControlService(false);
Vernon Mauerya3702c12019-05-22 13:20:59 -0700519 if (ret < 0)
520 {
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000521 return ipmi::responseUnspecifiedError();
Vernon Mauerya3702c12019-05-22 13:20:59 -0700522 }
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000523 mtm.revertFanPWM = false;
Vernon Mauerya3702c12019-05-22 13:20:59 -0700524 }
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000525 }
526 break;
527 case SmActionSet::forceAsserted:
528 {
529 pwmValue = *pwmSpeed;
530 } // fall-through
531 case SmActionSet::forceDeasserted:
532 {
533 if (!mtm.revertFanPWM)
Vernon Mauerya3702c12019-05-22 13:20:59 -0700534 {
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000535 ret = mtm.disablePidControlService(true);
536 if (ret < 0)
537 {
538 return ipmi::responseUnspecifiedError();
539 }
540 mtm.revertFanPWM = true;
Vernon Mauerya3702c12019-05-22 13:20:59 -0700541 }
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000542 mtm.revertTimer.start(revertTimeOut);
543 std::string fanPwmInstancePath =
544 fanPwmPath + std::to_string(instance + 1);
545
546 ret =
547 mtm.setProperty(fanService, fanPwmInstancePath, fanIntf,
548 "Value", static_cast<double>(pwmValue));
549 if (ret < 0)
550 {
551 return ipmi::responseUnspecifiedError();
552 }
553 }
554 break;
555 default:
556 {
557 return ipmi::responseInvalidFieldRequest();
Vernon Mauerya3702c12019-05-22 13:20:59 -0700558 }
559 }
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000560 }
561 break;
562 default:
563 {
564 return ipmi::responseInvalidFieldRequest();
Vernon Mauerya3702c12019-05-22 13:20:59 -0700565 }
566 }
Richard Marian Thomaiyar4cc10152019-08-02 23:21:45 +0530567 if (retCode == ccSuccess)
568 {
569 resetMtmTimer(yield);
570 }
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000571 return ipmi::response(retCode);
Vernon Mauerya3702c12019-05-22 13:20:59 -0700572}
573
Richard Marian Thomaiyar666dd012019-08-02 20:55:37 +0530574ipmi::RspType<> mtmKeepAlive(boost::asio::yield_context yield, uint8_t reserved,
575 const std::array<char, 5>& intentionalSignature)
576{
577 // Allow MTM keep alive command only in manfacturing mode.
578 if (mtm.getAccessLvl() != MtmLvl::mtmAvailable)
579 {
580 return ipmi::responseInvalidCommand();
581 }
582 constexpr std::array<char, 5> signatureOk = {'I', 'N', 'T', 'E', 'L'};
583 if (intentionalSignature != signatureOk || reserved != 0)
584 {
585 return ipmi::responseInvalidFieldRequest();
586 }
587 return ipmi::response(resetMtmTimer(yield));
588}
589
Yong Li85feb132019-08-09 16:06:03 +0800590ipmi::Cc mfgFilterMessage(ipmi::message::Request::ptr request)
591{
592 // i2c master write read command needs additional checking
593 if ((request->ctx->netFn == ipmi::netFnApp) &&
594 (request->ctx->cmd == ipmi::app::cmdMasterWriteRead))
595 {
596 if (request->payload.size() > 4)
597 {
598 // Allow write data count > 1, only if it is in MFG mode
599 if (mtm.getAccessLvl() != MtmLvl::mtmAvailable)
600 {
601 return ipmi::ccInsufficientPrivilege;
602 }
603 }
604 }
605
606 return ipmi::ccSuccess;
607}
608
Richard Marian Thomaiyar1f0839c2019-08-25 20:10:52 +0530609static constexpr uint8_t maxEthSize = 6;
610static constexpr uint8_t maxSupportedEth = 3;
611static constexpr const char* factoryEthAddrBaseFileName =
612 "/var/sofs/factory-settings/network/mac/eth";
613
614ipmi::RspType<> setManufacturingData(boost::asio::yield_context yield,
615 uint8_t dataType,
616 std::array<uint8_t, maxEthSize> ethData)
617{
618 // mfg filter logic will restrict this command executing only in mfg mode.
619 if (dataType >= maxSupportedEth)
620 {
621 return ipmi::responseParmOutOfRange();
622 }
623
624 constexpr uint8_t invalidData = 0;
625 constexpr uint8_t validData = 1;
626 constexpr uint8_t ethAddrStrSize =
627 19; // XX:XX:XX:XX:XX:XX + \n + null termination;
628 std::vector<uint8_t> buff(ethAddrStrSize);
629 std::snprintf(reinterpret_cast<char*>(buff.data()), ethAddrStrSize,
630 "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx\n", ethData.at(0),
631 ethData.at(1), ethData.at(2), ethData.at(3), ethData.at(4),
632 ethData.at(5));
633 std::ofstream oEthFile(factoryEthAddrBaseFileName +
634 std::to_string(dataType),
635 std::ofstream::out);
636 if (!oEthFile.good())
637 {
638 return ipmi::responseUnspecifiedError();
639 }
640
641 oEthFile << reinterpret_cast<char*>(buff.data());
642 oEthFile << fflush;
643 oEthFile.close();
644
645 resetMtmTimer(yield);
646 return ipmi::responseSuccess();
647}
648
649ipmi::RspType<uint8_t, std::array<uint8_t, maxEthSize>>
650 getManufacturingData(boost::asio::yield_context yield, uint8_t dataType)
651{
652 // mfg filter logic will restrict this command executing only in mfg mode.
653 if (dataType >= maxSupportedEth)
654 {
655 return ipmi::responseParmOutOfRange();
656 }
657 std::array<uint8_t, maxEthSize> ethData{0};
658 constexpr uint8_t invalidData = 0;
659 constexpr uint8_t validData = 1;
660
661 std::ifstream iEthFile(factoryEthAddrBaseFileName +
662 std::to_string(dataType),
663 std::ifstream::in);
664 if (!iEthFile.good())
665 {
666 return ipmi::responseSuccess(invalidData, ethData);
667 }
668 std::string ethStr;
669 iEthFile >> ethStr;
670 uint8_t* data = ethData.data();
671 std::sscanf(ethStr.c_str(), "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx",
672 data, (data + 1), (data + 2), (data + 3), (data + 4),
673 (data + 5));
674
675 resetMtmTimer(yield);
676 return ipmi::responseSuccess(validData, ethData);
677}
678
Vernon Mauerya3702c12019-05-22 13:20:59 -0700679} // namespace ipmi
680
681void register_mtm_commands() __attribute__((constructor));
682void register_mtm_commands()
683{
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700684 // <Get SM Signal>
685 ipmi::registerHandler(
686 ipmi::prioOemBase, ipmi::netFnOemOne,
Richard Marian Thomaiyar390b4352019-08-24 17:39:46 +0530687 static_cast<ipmi::Cmd>(IPMINetfnIntelOEMGeneralCmd::cmdGetSmSignal),
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000688 ipmi::Privilege::Admin, ipmi::appMTMGetSignal);
Vernon Mauerya3702c12019-05-22 13:20:59 -0700689
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000690 ipmi::registerHandler(
691 ipmi::prioOemBase, ipmi::netFnOemOne,
Richard Marian Thomaiyar390b4352019-08-24 17:39:46 +0530692 static_cast<ipmi::Cmd>(IPMINetfnIntelOEMGeneralCmd::cmdSetSmSignal),
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000693 ipmi::Privilege::Admin, ipmi::appMTMSetSignal);
Vernon Mauerya3702c12019-05-22 13:20:59 -0700694
Richard Marian Thomaiyar666dd012019-08-02 20:55:37 +0530695 ipmi::registerHandler(
696 ipmi::prioOemBase, ipmi::netFnOemOne,
697 static_cast<ipmi::Cmd>(IPMINetfnIntelOEMGeneralCmd::cmdMtmKeepAlive),
698 ipmi::Privilege::Admin, ipmi::mtmKeepAlive);
699
Richard Marian Thomaiyar1f0839c2019-08-25 20:10:52 +0530700 ipmi::registerHandler(
701 ipmi::prioOemBase, ipmi::netFnOemOne,
702 static_cast<ipmi::Cmd>(
703 IPMINetfnIntelOEMGeneralCmd::cmdSetManufacturingData),
704 ipmi::Privilege::Admin, ipmi::setManufacturingData);
705
706 ipmi::registerHandler(
707 ipmi::prioOemBase, ipmi::netFnOemOne,
708 static_cast<ipmi::Cmd>(
709 IPMINetfnIntelOEMGeneralCmd::cmdGetManufacturingData),
710 ipmi::Privilege::Admin, ipmi::getManufacturingData);
711
Yong Li85feb132019-08-09 16:06:03 +0800712 ipmi::registerFilter(ipmi::netFnOemOne,
713 [](ipmi::message::Request::ptr request) {
714 return ipmi::mfgFilterMessage(request);
715 });
716
Vernon Mauerya3702c12019-05-22 13:20:59 -0700717 return;
718}