blob: eefdc767232527806112a621c0a9f7da25684354 [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>
Yong Lif267a672019-08-29 11:16:07 +080018#include <filesystem>
Richard Marian Thomaiyar1b74a212019-08-03 13:26:17 +053019#include <fstream>
Vernon Mauerya3702c12019-05-22 13:20:59 -070020#include <ipmid/api.hpp>
21#include <manufacturingcommands.hpp>
22#include <oemcommands.hpp>
23
24namespace ipmi
25{
26
27Manufacturing mtm;
28
29static auto revertTimeOut =
30 std::chrono::duration_cast<std::chrono::microseconds>(
31 std::chrono::seconds(60)); // 1 minute timeout
32
Yong Lif267a672019-08-29 11:16:07 +080033static constexpr uint8_t slotAddressTypeBus = 0;
34static constexpr uint8_t slotAddressTypeUniqueid = 1;
35static constexpr uint8_t slotI2CMaxReadSize = 35;
36
Vernon Mauerya3702c12019-05-22 13:20:59 -070037static constexpr const char* callbackMgrService =
38 "xyz.openbmc_project.CallbackManager";
39static constexpr const char* callbackMgrIntf =
40 "xyz.openbmc_project.CallbackManager";
41static constexpr const char* callbackMgrObjPath =
42 "/xyz/openbmc_project/CallbackManager";
43static constexpr const char* retriggerLedUpdate = "RetriggerLEDUpdate";
44
45const static constexpr char* systemDService = "org.freedesktop.systemd1";
46const static constexpr char* systemDObjPath = "/org/freedesktop/systemd1";
47const static constexpr char* systemDMgrIntf =
48 "org.freedesktop.systemd1.Manager";
49const static constexpr char* pidControlService = "phosphor-pid-control.service";
50
Richard Marian Thomaiyar666dd012019-08-02 20:55:37 +053051static inline Cc resetMtmTimer(boost::asio::yield_context yield)
52{
53 auto sdbusp = getSdBus();
54 boost::system::error_code ec;
55 sdbusp->yield_method_call<>(yield, ec, specialModeService,
56 specialModeObjPath, specialModeIntf,
57 "ResetTimer");
58 if (ec)
59 {
60 phosphor::logging::log<phosphor::logging::level::ERR>(
61 "Failed to reset the manufacturing mode timer");
62 return ccUnspecifiedError;
63 }
64 return ccSuccess;
65}
66
Jason M. Bills38d2b5a2019-06-03 16:26:11 -070067int getGpioPathForSmSignal(const SmSignalGet signal, std::string& path)
Vernon Mauerya3702c12019-05-22 13:20:59 -070068{
Jason M. Bills38d2b5a2019-06-03 16:26:11 -070069 switch (signal)
70 {
71 case SmSignalGet::smPowerButton:
72 path = "/xyz/openbmc_project/chassis/buttons/power";
73 break;
74 case SmSignalGet::smResetButton:
75 path = "/xyz/openbmc_project/chassis/buttons/reset";
76 break;
77 case SmSignalGet::smNMIButton:
78 path = "/xyz/openbmc_project/chassis/buttons/nmi";
79 break;
Richard Marian Thomaiyar8e5e2b02019-08-01 07:50:55 +053080 case SmSignalGet::smIdentifyButton:
81 path = "/xyz/openbmc_project/chassis/buttons/id";
82 break;
Jason M. Bills38d2b5a2019-06-03 16:26:11 -070083 default:
84 return -1;
85 break;
86 }
87 return 0;
Vernon Mauerya3702c12019-05-22 13:20:59 -070088}
89
90ipmi_ret_t ledStoreAndSet(SmSignalSet signal, std::string setState)
91{
92 LedProperty* ledProp = mtm.findLedProperty(signal);
93 if (ledProp == nullptr)
94 {
95 return IPMI_CC_INVALID_FIELD_REQUEST;
96 }
97
98 std::string ledName = ledProp->getName();
99 std::string ledService = ledServicePrefix + ledName;
100 std::string ledPath = ledPathPrefix + ledName;
101 ipmi::Value presentState;
102
103 if (false == ledProp->getLock())
104 {
105 if (mtm.getProperty(ledService.c_str(), ledPath.c_str(), ledIntf,
106 "State", &presentState) != 0)
107 {
108 return IPMI_CC_UNSPECIFIED_ERROR;
109 }
110 ledProp->setPrevState(std::get<std::string>(presentState));
111 ledProp->setLock(true);
112 if (signal == SmSignalSet::smPowerFaultLed ||
113 signal == SmSignalSet::smSystemReadyLed)
114 {
115 mtm.revertLedCallback = true;
116 }
117 }
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700118 if (mtm.setProperty(ledService, ledPath, ledIntf, "State",
Vernon Mauerya3702c12019-05-22 13:20:59 -0700119 ledStateStr + setState) != 0)
120 {
121 return IPMI_CC_UNSPECIFIED_ERROR;
122 }
123 return IPMI_CC_OK;
124}
125
126ipmi_ret_t ledRevert(SmSignalSet signal)
127{
128 LedProperty* ledProp = mtm.findLedProperty(signal);
129 if (ledProp == nullptr)
130 {
131 return IPMI_CC_INVALID_FIELD_REQUEST;
132 }
133 if (true == ledProp->getLock())
134 {
135 ledProp->setLock(false);
136 if (signal == SmSignalSet::smPowerFaultLed ||
137 signal == SmSignalSet::smSystemReadyLed)
138 {
139 try
140 {
141 ipmi::method_no_args::callDbusMethod(
142 *getSdBus(), callbackMgrService, callbackMgrObjPath,
143 callbackMgrIntf, retriggerLedUpdate);
144 }
145 catch (sdbusplus::exception_t& e)
146 {
147 return IPMI_CC_UNSPECIFIED_ERROR;
148 }
149 mtm.revertLedCallback = false;
150 }
151 else
152 {
153 std::string ledName = ledProp->getName();
154 std::string ledService = ledServicePrefix + ledName;
155 std::string ledPath = ledPathPrefix + ledName;
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700156 if (mtm.setProperty(ledService, ledPath, ledIntf, "State",
157 ledProp->getPrevState()) != 0)
Vernon Mauerya3702c12019-05-22 13:20:59 -0700158 {
159 return IPMI_CC_UNSPECIFIED_ERROR;
160 }
161 }
162 }
163 return IPMI_CC_OK;
164}
165
166void Manufacturing::initData()
167{
Vernon Mauerya3702c12019-05-22 13:20:59 -0700168 ledPropertyList.push_back(
169 LedProperty(SmSignalSet::smPowerFaultLed, "status_amber"));
170 ledPropertyList.push_back(
171 LedProperty(SmSignalSet::smSystemReadyLed, "status_green"));
172 ledPropertyList.push_back(
173 LedProperty(SmSignalSet::smIdentifyLed, "identify"));
174}
175
176void Manufacturing::revertTimerHandler()
177{
Vernon Mauerya3702c12019-05-22 13:20:59 -0700178 if (revertFanPWM)
179 {
180 revertFanPWM = false;
181 disablePidControlService(false);
182 }
183
184 for (const auto& ledProperty : ledPropertyList)
185 {
186 const std::string& ledName = ledProperty.getName();
187 ledRevert(ledProperty.getSignal());
188 }
189}
190
191Manufacturing::Manufacturing() :
192 revertTimer([&](void) { revertTimerHandler(); })
193{
194 initData();
195}
196
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700197int8_t Manufacturing::getProperty(const std::string& service,
198 const std::string& path,
199 const std::string& interface,
200 const std::string& propertyName,
201 ipmi::Value* reply)
Vernon Mauerya3702c12019-05-22 13:20:59 -0700202{
203 try
204 {
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700205 *reply = ipmi::getDbusProperty(*getSdBus(), service, path, interface,
206 propertyName);
Vernon Mauerya3702c12019-05-22 13:20:59 -0700207 }
208 catch (const sdbusplus::exception::SdBusError& e)
209 {
210 phosphor::logging::log<phosphor::logging::level::INFO>(
211 "ERROR: getProperty");
212 return -1;
213 }
214
215 return 0;
216}
217
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700218int8_t Manufacturing::setProperty(const std::string& service,
219 const std::string& path,
220 const std::string& interface,
221 const std::string& propertyName,
222 ipmi::Value value)
Vernon Mauerya3702c12019-05-22 13:20:59 -0700223{
224 try
225 {
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700226 ipmi::setDbusProperty(*getSdBus(), service, path, interface,
Vernon Mauerya3702c12019-05-22 13:20:59 -0700227 propertyName, value);
228 }
229 catch (const sdbusplus::exception::SdBusError& e)
230 {
231 phosphor::logging::log<phosphor::logging::level::INFO>(
232 "ERROR: setProperty");
233 return -1;
234 }
235
236 return 0;
237}
238
239int8_t Manufacturing::disablePidControlService(const bool disable)
240{
241 try
242 {
243 auto dbus = getSdBus();
244 auto method = dbus->new_method_call(systemDService, systemDObjPath,
245 systemDMgrIntf,
246 disable ? "StopUnit" : "StartUnit");
247 method.append(pidControlService, "replace");
248 auto reply = dbus->call(method);
249 }
250 catch (const sdbusplus::exception::SdBusError& e)
251 {
252 phosphor::logging::log<phosphor::logging::level::INFO>(
253 "ERROR: phosphor-pid-control service start or stop failed");
254 return -1;
255 }
256 return 0;
257}
258
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700259ipmi::RspType<uint8_t, // Signal value
260 std::optional<uint16_t> // Fan tach value
261 >
Richard Marian Thomaiyar147daec2019-06-15 07:43:48 +0530262 appMTMGetSignal(boost::asio::yield_context yield, uint8_t signalTypeByte,
263 uint8_t instance, uint8_t actionByte)
Vernon Mauerya3702c12019-05-22 13:20:59 -0700264{
Ayushi Smritie0511e52019-08-27 17:30:34 +0000265 // mfg filter logic is used to allow MTM get signal command only in
266 // manfacturing mode.
Vernon Mauerya3702c12019-05-22 13:20:59 -0700267
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700268 SmSignalGet signalType = static_cast<SmSignalGet>(signalTypeByte);
269 SmActionGet action = static_cast<SmActionGet>(actionByte);
270
271 switch (signalType)
272 {
273 case SmSignalGet::smFanPwmGet:
274 {
275 ipmi::Value reply;
Richard Marian Thomaiyar147daec2019-06-15 07:43:48 +0530276 std::string fullPath = fanPwmPath + std::to_string(instance + 1);
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700277 if (mtm.getProperty(fanService, fullPath, fanIntf, "Value",
278 &reply) < 0)
279 {
280 return ipmi::responseInvalidFieldRequest();
281 }
282 double* doubleVal = std::get_if<double>(&reply);
283 if (doubleVal == nullptr)
284 {
285 return ipmi::responseUnspecifiedError();
286 }
287 uint8_t sensorVal = std::round(*doubleVal);
Richard Marian Thomaiyar4cc10152019-08-02 23:21:45 +0530288 resetMtmTimer(yield);
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700289 return ipmi::responseSuccess(sensorVal, std::nullopt);
290 }
291 break;
292 case SmSignalGet::smFanTachometerGet:
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700293 {
Richard Marian Thomaiyar147daec2019-06-15 07:43:48 +0530294 auto sdbusp = getSdBus();
295 boost::system::error_code ec;
296 using objFlatMap = boost::container::flat_map<
297 std::string, boost::container::flat_map<
298 std::string, std::vector<std::string>>>;
299
300 auto flatMap = sdbusp->yield_method_call<objFlatMap>(
301 yield, ec, "xyz.openbmc_project.ObjectMapper",
302 "/xyz/openbmc_project/object_mapper",
303 "xyz.openbmc_project.ObjectMapper", "GetSubTree",
304 fanTachBasePath, 0, std::array<const char*, 1>{fanIntf});
305 if (ec)
306 {
307 phosphor::logging::log<phosphor::logging::level::ERR>(
308 "Failed to query fan tach sub tree objects");
309 return ipmi::responseUnspecifiedError();
310 }
311 if (instance >= flatMap.size())
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700312 {
313 return ipmi::responseInvalidFieldRequest();
314 }
Richard Marian Thomaiyar147daec2019-06-15 07:43:48 +0530315 auto itr = flatMap.nth(instance);
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700316 ipmi::Value reply;
Richard Marian Thomaiyar147daec2019-06-15 07:43:48 +0530317 if (mtm.getProperty(fanService, itr->first, fanIntf, "Value",
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700318 &reply) < 0)
319 {
320 return ipmi::responseInvalidFieldRequest();
321 }
322
323 double* doubleVal = std::get_if<double>(&reply);
324 if (doubleVal == nullptr)
325 {
326 return ipmi::responseUnspecifiedError();
327 }
328 uint8_t sensorVal = FAN_PRESENT | FAN_SENSOR_PRESENT;
329 std::optional<uint16_t> fanTach = std::round(*doubleVal);
330
Richard Marian Thomaiyar4cc10152019-08-02 23:21:45 +0530331 resetMtmTimer(yield);
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700332 return ipmi::responseSuccess(sensorVal, fanTach);
333 }
334 break;
Richard Marian Thomaiyar8e5e2b02019-08-01 07:50:55 +0530335 case SmSignalGet::smIdentifyButton:
336 {
337 if (action == SmActionGet::revert || action == SmActionGet::ignore)
338 {
339 // ButtonMasked property is not supported for ID button as it is
340 // unnecessary. Hence if requested for revert / ignore, override
341 // it to sample action to make tools happy.
342 action = SmActionGet::sample;
343 }
344 // fall-through
345 }
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700346 case SmSignalGet::smResetButton:
347 case SmSignalGet::smPowerButton:
348 case SmSignalGet::smNMIButton:
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700349 {
350 std::string path;
351 if (getGpioPathForSmSignal(signalType, path) < 0)
352 {
353 return ipmi::responseInvalidFieldRequest();
354 }
355
356 switch (action)
357 {
358 case SmActionGet::sample:
359 phosphor::logging::log<phosphor::logging::level::INFO>(
360 "case SmActionGet::sample");
361 break;
362 case SmActionGet::ignore:
363 {
364 phosphor::logging::log<phosphor::logging::level::INFO>(
365 "case SmActionGet::ignore");
366 if (mtm.setProperty(buttonService, path, buttonIntf,
367 "ButtonMasked", true) < 0)
368 {
369 return ipmi::responseUnspecifiedError();
370 }
371 }
372 break;
373 case SmActionGet::revert:
374 {
375 phosphor::logging::log<phosphor::logging::level::INFO>(
376 "case SmActionGet::revert");
377 if (mtm.setProperty(buttonService, path, buttonIntf,
378 "ButtonMasked", false) < 0)
379 {
380 return ipmi::responseUnspecifiedError();
381 }
382 }
383 break;
384
385 default:
386 return ipmi::responseInvalidFieldRequest();
387 break;
388 }
389
390 ipmi::Value reply;
391 if (mtm.getProperty(buttonService, path, buttonIntf,
392 "ButtonPressed", &reply) < 0)
393 {
394 return ipmi::responseUnspecifiedError();
395 }
396 bool* valPtr = std::get_if<bool>(&reply);
397 if (valPtr == nullptr)
398 {
399 return ipmi::responseUnspecifiedError();
400 }
Richard Marian Thomaiyar4cc10152019-08-02 23:21:45 +0530401 resetMtmTimer(yield);
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700402 uint8_t sensorVal = *valPtr;
403 return ipmi::responseSuccess(sensorVal, std::nullopt);
404 }
405 break;
Richard Marian Thomaiyar1b74a212019-08-03 13:26:17 +0530406 case SmSignalGet::smNcsiDiag:
407 {
408 constexpr const char* netBasePath = "/sys/class/net/eth";
409 constexpr const char* carrierSuffix = "/carrier";
410 std::ifstream netIfs(netBasePath + std::to_string(instance) +
411 carrierSuffix);
412 if (!netIfs.good())
413 {
414 return ipmi::responseInvalidFieldRequest();
415 }
416 std::string carrier;
417 netIfs >> carrier;
418 resetMtmTimer(yield);
419 return ipmi::responseSuccess(
420 static_cast<uint8_t>(std::stoi(carrier)), std::nullopt);
421 }
422 break;
Vernon Mauerya3702c12019-05-22 13:20:59 -0700423 default:
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700424 return ipmi::responseInvalidFieldRequest();
Vernon Mauerya3702c12019-05-22 13:20:59 -0700425 break;
426 }
Vernon Mauerya3702c12019-05-22 13:20:59 -0700427}
428
Richard Marian Thomaiyar4cc10152019-08-02 23:21:45 +0530429ipmi::RspType<> appMTMSetSignal(boost::asio::yield_context yield,
430 uint8_t signalTypeByte, uint8_t instance,
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000431 uint8_t actionByte,
432 std::optional<uint8_t> pwmSpeed)
Vernon Mauerya3702c12019-05-22 13:20:59 -0700433{
Ayushi Smritie0511e52019-08-27 17:30:34 +0000434 // mfg filter logic is used to allow MTM set signal command only in
435 // manfacturing mode.
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000436
437 SmSignalSet signalType = static_cast<SmSignalSet>(signalTypeByte);
438 SmActionSet action = static_cast<SmActionSet>(actionByte);
439 Cc retCode = ccSuccess;
440 int8_t ret = 0;
441
442 switch (signalType)
443 {
444 case SmSignalSet::smPowerFaultLed:
445 case SmSignalSet::smSystemReadyLed:
446 case SmSignalSet::smIdentifyLed:
447 switch (action)
448 {
449 case SmActionSet::forceDeasserted:
Vernon Mauerya3702c12019-05-22 13:20:59 -0700450 {
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000451 phosphor::logging::log<phosphor::logging::level::INFO>(
452 "case SmActionSet::forceDeasserted");
Vernon Mauerya3702c12019-05-22 13:20:59 -0700453
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000454 retCode = ledStoreAndSet(signalType, std::string("Off"));
455 if (retCode != ccSuccess)
Vernon Mauerya3702c12019-05-22 13:20:59 -0700456 {
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000457 return ipmi::response(retCode);
Vernon Mauerya3702c12019-05-22 13:20:59 -0700458 }
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000459 mtm.revertTimer.start(revertTimeOut);
Vernon Mauerya3702c12019-05-22 13:20:59 -0700460 }
461 break;
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000462 case SmActionSet::forceAsserted:
Vernon Mauerya3702c12019-05-22 13:20:59 -0700463 {
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000464 phosphor::logging::log<phosphor::logging::level::INFO>(
465 "case SmActionSet::forceAsserted");
Vernon Mauerya3702c12019-05-22 13:20:59 -0700466
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000467 retCode = ledStoreAndSet(signalType, std::string("On"));
468 if (retCode != ccSuccess)
469 {
470 return ipmi::response(retCode);
471 }
472 mtm.revertTimer.start(revertTimeOut);
473 if (SmSignalSet::smPowerFaultLed == signalType)
474 {
475 // Deassert "system ready"
476 retCode = ledStoreAndSet(SmSignalSet::smSystemReadyLed,
477 std::string("Off"));
478 }
479 else if (SmSignalSet::smSystemReadyLed == signalType)
480 {
481 // Deassert "fault led"
482 retCode = ledStoreAndSet(SmSignalSet::smPowerFaultLed,
483 std::string("Off"));
484 }
485 }
486 break;
487 case SmActionSet::revert:
488 {
489 phosphor::logging::log<phosphor::logging::level::INFO>(
490 "case SmActionSet::revert");
491 retCode = ledRevert(signalType);
492 }
493 break;
494 default:
495 {
496 return ipmi::responseInvalidFieldRequest();
497 }
498 }
499 break;
500 case SmSignalSet::smFanPowerSpeed:
501 {
502 if ((action == SmActionSet::forceAsserted) && (!pwmSpeed))
503 {
504 return ipmi::responseReqDataLenInvalid();
505 }
506
507 if ((action == SmActionSet::forceAsserted) && (*pwmSpeed > 100))
508 {
509 return ipmi::responseInvalidFieldRequest();
510 }
511
512 uint8_t pwmValue = 0;
513 switch (action)
514 {
515 case SmActionSet::revert:
516 {
517 if (mtm.revertFanPWM)
518 {
519 ret = mtm.disablePidControlService(false);
Vernon Mauerya3702c12019-05-22 13:20:59 -0700520 if (ret < 0)
521 {
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000522 return ipmi::responseUnspecifiedError();
Vernon Mauerya3702c12019-05-22 13:20:59 -0700523 }
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000524 mtm.revertFanPWM = false;
Vernon Mauerya3702c12019-05-22 13:20:59 -0700525 }
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000526 }
527 break;
528 case SmActionSet::forceAsserted:
529 {
530 pwmValue = *pwmSpeed;
531 } // fall-through
532 case SmActionSet::forceDeasserted:
533 {
534 if (!mtm.revertFanPWM)
Vernon Mauerya3702c12019-05-22 13:20:59 -0700535 {
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000536 ret = mtm.disablePidControlService(true);
537 if (ret < 0)
538 {
539 return ipmi::responseUnspecifiedError();
540 }
541 mtm.revertFanPWM = true;
Vernon Mauerya3702c12019-05-22 13:20:59 -0700542 }
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000543 mtm.revertTimer.start(revertTimeOut);
544 std::string fanPwmInstancePath =
545 fanPwmPath + std::to_string(instance + 1);
546
547 ret =
548 mtm.setProperty(fanService, fanPwmInstancePath, fanIntf,
549 "Value", static_cast<double>(pwmValue));
550 if (ret < 0)
551 {
552 return ipmi::responseUnspecifiedError();
553 }
554 }
555 break;
556 default:
557 {
558 return ipmi::responseInvalidFieldRequest();
Vernon Mauerya3702c12019-05-22 13:20:59 -0700559 }
560 }
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000561 }
562 break;
563 default:
564 {
565 return ipmi::responseInvalidFieldRequest();
Vernon Mauerya3702c12019-05-22 13:20:59 -0700566 }
567 }
Richard Marian Thomaiyar4cc10152019-08-02 23:21:45 +0530568 if (retCode == ccSuccess)
569 {
570 resetMtmTimer(yield);
571 }
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000572 return ipmi::response(retCode);
Vernon Mauerya3702c12019-05-22 13:20:59 -0700573}
574
Richard Marian Thomaiyar666dd012019-08-02 20:55:37 +0530575ipmi::RspType<> mtmKeepAlive(boost::asio::yield_context yield, uint8_t reserved,
576 const std::array<char, 5>& intentionalSignature)
577{
Ayushi Smritie0511e52019-08-27 17:30:34 +0000578 // mfg filter logic is used to allow MTM keep alive command only in
579 // manfacturing mode
580
Richard Marian Thomaiyar666dd012019-08-02 20:55:37 +0530581 constexpr std::array<char, 5> signatureOk = {'I', 'N', 'T', 'E', 'L'};
582 if (intentionalSignature != signatureOk || reserved != 0)
583 {
584 return ipmi::responseInvalidFieldRequest();
585 }
586 return ipmi::response(resetMtmTimer(yield));
587}
588
Ayushi Smritie0511e52019-08-27 17:30:34 +0000589static constexpr unsigned int makeCmdKey(unsigned int netFn, unsigned int cmd)
590{
591 return (netFn << 8) | cmd;
592}
593
Yong Li85feb132019-08-09 16:06:03 +0800594ipmi::Cc mfgFilterMessage(ipmi::message::Request::ptr request)
595{
Ayushi Smritie0511e52019-08-27 17:30:34 +0000596 // Restricted commands, must be executed only in Manufacturing mode
597 switch (makeCmdKey(request->ctx->netFn, request->ctx->cmd))
Yong Li85feb132019-08-09 16:06:03 +0800598 {
Ayushi Smritie0511e52019-08-27 17:30:34 +0000599 // i2c master write read command needs additional checking
600 case makeCmdKey(ipmi::netFnApp, ipmi::app::cmdMasterWriteRead):
601 if (request->payload.size() > 4)
602 {
603 // Allow write data count > 1, only if it is in MFG mode
604 if (mtm.getAccessLvl() != MtmLvl::mtmAvailable)
605 {
606 return ipmi::ccInsufficientPrivilege;
607 }
608 }
609 break;
610 case makeCmdKey(ipmi::netFnOemOne,
611 ipmi::intel::general::cmdGetSmSignal):
612 case makeCmdKey(ipmi::netFnOemOne,
613 ipmi::intel::general::cmdSetSmSignal):
614 case makeCmdKey(ipmi::netFnOemOne,
615 ipmi::intel::general::cmdMtmKeepAlive):
616 case makeCmdKey(ipmi::netFnOemOne,
617 ipmi::intel::general::cmdSetManufacturingData):
618 case makeCmdKey(ipmi::netFnOemOne,
619 ipmi::intel::general::cmdGetManufacturingData):
620 case makeCmdKey(ipmi::netFnStorage, ipmi::storage::cmdWriteFruData):
621
622 // Check for MTM mode
Yong Li85feb132019-08-09 16:06:03 +0800623 if (mtm.getAccessLvl() != MtmLvl::mtmAvailable)
624 {
Ayushi Smritie0511e52019-08-27 17:30:34 +0000625 return ipmi::ccInvalidCommand;
Yong Li85feb132019-08-09 16:06:03 +0800626 }
Yong Li85feb132019-08-09 16:06:03 +0800627 }
Yong Li85feb132019-08-09 16:06:03 +0800628 return ipmi::ccSuccess;
629}
630
Richard Marian Thomaiyar1f0839c2019-08-25 20:10:52 +0530631static constexpr uint8_t maxEthSize = 6;
632static constexpr uint8_t maxSupportedEth = 3;
633static constexpr const char* factoryEthAddrBaseFileName =
634 "/var/sofs/factory-settings/network/mac/eth";
635
636ipmi::RspType<> setManufacturingData(boost::asio::yield_context yield,
637 uint8_t dataType,
638 std::array<uint8_t, maxEthSize> ethData)
639{
640 // mfg filter logic will restrict this command executing only in mfg mode.
641 if (dataType >= maxSupportedEth)
642 {
643 return ipmi::responseParmOutOfRange();
644 }
645
646 constexpr uint8_t invalidData = 0;
647 constexpr uint8_t validData = 1;
648 constexpr uint8_t ethAddrStrSize =
649 19; // XX:XX:XX:XX:XX:XX + \n + null termination;
650 std::vector<uint8_t> buff(ethAddrStrSize);
651 std::snprintf(reinterpret_cast<char*>(buff.data()), ethAddrStrSize,
652 "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx\n", ethData.at(0),
653 ethData.at(1), ethData.at(2), ethData.at(3), ethData.at(4),
654 ethData.at(5));
655 std::ofstream oEthFile(factoryEthAddrBaseFileName +
656 std::to_string(dataType),
657 std::ofstream::out);
658 if (!oEthFile.good())
659 {
660 return ipmi::responseUnspecifiedError();
661 }
662
663 oEthFile << reinterpret_cast<char*>(buff.data());
664 oEthFile << fflush;
665 oEthFile.close();
666
667 resetMtmTimer(yield);
668 return ipmi::responseSuccess();
669}
670
671ipmi::RspType<uint8_t, std::array<uint8_t, maxEthSize>>
672 getManufacturingData(boost::asio::yield_context yield, uint8_t dataType)
673{
674 // mfg filter logic will restrict this command executing only in mfg mode.
675 if (dataType >= maxSupportedEth)
676 {
677 return ipmi::responseParmOutOfRange();
678 }
679 std::array<uint8_t, maxEthSize> ethData{0};
680 constexpr uint8_t invalidData = 0;
681 constexpr uint8_t validData = 1;
682
683 std::ifstream iEthFile(factoryEthAddrBaseFileName +
684 std::to_string(dataType),
685 std::ifstream::in);
686 if (!iEthFile.good())
687 {
688 return ipmi::responseSuccess(invalidData, ethData);
689 }
690 std::string ethStr;
691 iEthFile >> ethStr;
692 uint8_t* data = ethData.data();
693 std::sscanf(ethStr.c_str(), "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx",
694 data, (data + 1), (data + 2), (data + 3), (data + 4),
695 (data + 5));
696
697 resetMtmTimer(yield);
698 return ipmi::responseSuccess(validData, ethData);
699}
700
Yong Lif267a672019-08-29 11:16:07 +0800701/** @brief implements slot master write read IPMI command which can be used for
702 * low-level I2C/SMBus write, read or write-read access for PCIE slots
703 * @param reserved - skip 6 bit
704 * @param addressType - address type
705 * @param bbSlotNum - baseboard slot number
706 * @param riserSlotNum - riser slot number
707 * @param reserved2 - skip 2 bit
708 * @param slaveAddr - slave address
709 * @param readCount - number of bytes to be read
710 * @param writeData - data to be written
711 *
712 * @returns IPMI completion code plus response data
713 */
714ipmi::RspType<std::vector<uint8_t>>
715 appSlotI2CMasterWriteRead(uint6_t reserved, uint2_t addressType,
716 uint3_t bbSlotNum, uint3_t riserSlotNum,
717 uint2_t resvered2, uint8_t slaveAddr,
718 uint8_t readCount, std::vector<uint8_t> writeData)
719{
720 const size_t writeCount = writeData.size();
721 std::string i2cBus;
722 if (addressType == slotAddressTypeBus)
723 {
724 std::string path = "/dev/i2c-mux/Riser_" +
725 std::to_string(static_cast<uint8_t>(bbSlotNum)) +
726 "_Mux/Pcie_Slot_" +
727 std::to_string(static_cast<uint8_t>(riserSlotNum));
728
729 if (std::filesystem::exists(path) && std::filesystem::is_symlink(path))
730 {
731 i2cBus = std::filesystem::read_symlink(path);
732 }
733 else
734 {
735 phosphor::logging::log<phosphor::logging::level::ERR>(
736 "Master write read command: Cannot get BusID");
737 return ipmi::responseInvalidFieldRequest();
738 }
739 }
740 else if (addressType == slotAddressTypeUniqueid)
741 {
742 i2cBus = "/dev/i2c-" +
743 std::to_string(static_cast<uint8_t>(bbSlotNum) |
744 (static_cast<uint8_t>(riserSlotNum) << 3));
745 }
746 else
747 {
748 phosphor::logging::log<phosphor::logging::level::ERR>(
749 "Master write read command: invalid request");
750 return ipmi::responseInvalidFieldRequest();
751 }
752
753 // Allow single byte write as it is offset byte to read the data, rest allow
754 // only in MFG mode.
755 if (writeCount > 1)
756 {
757 if (mtm.getAccessLvl() < MtmLvl::mtmAvailable)
758 {
759 return ipmi::responseInsufficientPrivilege();
760 }
761 }
762
763 if (readCount > slotI2CMaxReadSize)
764 {
765 phosphor::logging::log<phosphor::logging::level::ERR>(
766 "Master write read command: Read count exceeds limit");
767 return ipmi::responseParmOutOfRange();
768 }
769
770 if (!readCount && !writeCount)
771 {
772 phosphor::logging::log<phosphor::logging::level::ERR>(
773 "Master write read command: Read & write count are 0");
774 return ipmi::responseInvalidFieldRequest();
775 }
776
777 std::vector<uint8_t> readBuf(readCount);
778
779 ipmi::Cc retI2C = ipmi::i2cWriteRead(i2cBus, slaveAddr, writeData, readBuf);
780 if (retI2C != ipmi::ccSuccess)
781 {
782 return ipmi::response(retI2C);
783 }
784
785 return ipmi::responseSuccess(readBuf);
786}
Yong Li068b4f22019-09-17 16:32:18 +0800787
788ipmi::RspType<> clearCMOS()
789{
790 // There is an i2c device on bus 4, the slave address is 0x70. Based on the
791 // spec, writing 0x1 to address 0x60 on this device, will trigger the clear
792 // CMOS action.
793 constexpr uint8_t slaveAddr = 0x70;
794 std::string i2cBus = "/dev/i2c-4";
795 std::vector<uint8_t> writeData = {0x60, 0x1};
796 std::vector<uint8_t> readBuf(0);
797
798 // TODO Needs to enhance the below security checking
799 if (mtm.getAccessLvl() < MtmLvl::mtmAvailable)
800 {
801 return ipmi::responseInsufficientPrivilege();
802 }
803
804 ipmi::Cc retI2C = ipmi::i2cWriteRead(i2cBus, slaveAddr, writeData, readBuf);
805 return ipmi::response(retI2C);
806}
Vernon Mauerya3702c12019-05-22 13:20:59 -0700807} // namespace ipmi
808
809void register_mtm_commands() __attribute__((constructor));
810void register_mtm_commands()
811{
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700812 // <Get SM Signal>
Vernon Mauery98bbf692019-09-16 11:14:59 -0700813 ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnGeneral,
814 ipmi::intel::general::cmdGetSmSignal,
815 ipmi::Privilege::Admin, ipmi::appMTMGetSignal);
Vernon Mauerya3702c12019-05-22 13:20:59 -0700816
Vernon Mauery98bbf692019-09-16 11:14:59 -0700817 ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnGeneral,
818 ipmi::intel::general::cmdSetSmSignal,
819 ipmi::Privilege::Admin, ipmi::appMTMSetSignal);
Vernon Mauerya3702c12019-05-22 13:20:59 -0700820
Vernon Mauery98bbf692019-09-16 11:14:59 -0700821 ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnGeneral,
822 ipmi::intel::general::cmdMtmKeepAlive,
823 ipmi::Privilege::Admin, ipmi::mtmKeepAlive);
Richard Marian Thomaiyar666dd012019-08-02 20:55:37 +0530824
Vernon Mauery98bbf692019-09-16 11:14:59 -0700825 ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnGeneral,
826 ipmi::intel::general::cmdSetManufacturingData,
827 ipmi::Privilege::Admin, ipmi::setManufacturingData);
Richard Marian Thomaiyar1f0839c2019-08-25 20:10:52 +0530828
Vernon Mauery98bbf692019-09-16 11:14:59 -0700829 ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnGeneral,
830 ipmi::intel::general::cmdGetManufacturingData,
831 ipmi::Privilege::Admin, ipmi::getManufacturingData);
Richard Marian Thomaiyar1f0839c2019-08-25 20:10:52 +0530832
Vernon Mauery98bbf692019-09-16 11:14:59 -0700833 ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnApp,
834 ipmi::intel::general::cmdSlotI2CMasterWriteRead,
835 ipmi::Privilege::Admin,
836 ipmi::appSlotI2CMasterWriteRead);
Yong Lif267a672019-08-29 11:16:07 +0800837
Yong Li068b4f22019-09-17 16:32:18 +0800838 ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnPlatform,
839 ipmi::intel::platform::cmdClearCMOS,
840 ipmi::Privilege::Admin, ipmi::clearCMOS);
841
Vernon Mauery98bbf692019-09-16 11:14:59 -0700842 ipmi::registerFilter(ipmi::prioOemBase,
Yong Li85feb132019-08-09 16:06:03 +0800843 [](ipmi::message::Request::ptr request) {
844 return ipmi::mfgFilterMessage(request);
845 });
Vernon Mauerya3702c12019-05-22 13:20:59 -0700846}