blob: 38c44d99c21a28d40ab3cd43597fab8ebea3b3b4 [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{
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700265 if (mtm.getAccessLvl() < MtmLvl::mtmAvailable)
Vernon Mauerya3702c12019-05-22 13:20:59 -0700266 {
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700267 return ipmi::responseInvalidCommand();
268 }
Vernon Mauerya3702c12019-05-22 13:20:59 -0700269
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700270 SmSignalGet signalType = static_cast<SmSignalGet>(signalTypeByte);
271 SmActionGet action = static_cast<SmActionGet>(actionByte);
272
273 switch (signalType)
274 {
275 case SmSignalGet::smFanPwmGet:
276 {
277 ipmi::Value reply;
Richard Marian Thomaiyar147daec2019-06-15 07:43:48 +0530278 std::string fullPath = fanPwmPath + std::to_string(instance + 1);
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700279 if (mtm.getProperty(fanService, fullPath, fanIntf, "Value",
280 &reply) < 0)
281 {
282 return ipmi::responseInvalidFieldRequest();
283 }
284 double* doubleVal = std::get_if<double>(&reply);
285 if (doubleVal == nullptr)
286 {
287 return ipmi::responseUnspecifiedError();
288 }
289 uint8_t sensorVal = std::round(*doubleVal);
Richard Marian Thomaiyar4cc10152019-08-02 23:21:45 +0530290 resetMtmTimer(yield);
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700291 return ipmi::responseSuccess(sensorVal, std::nullopt);
292 }
293 break;
294 case SmSignalGet::smFanTachometerGet:
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700295 {
Richard Marian Thomaiyar147daec2019-06-15 07:43:48 +0530296 auto sdbusp = getSdBus();
297 boost::system::error_code ec;
298 using objFlatMap = boost::container::flat_map<
299 std::string, boost::container::flat_map<
300 std::string, std::vector<std::string>>>;
301
302 auto flatMap = sdbusp->yield_method_call<objFlatMap>(
303 yield, ec, "xyz.openbmc_project.ObjectMapper",
304 "/xyz/openbmc_project/object_mapper",
305 "xyz.openbmc_project.ObjectMapper", "GetSubTree",
306 fanTachBasePath, 0, std::array<const char*, 1>{fanIntf});
307 if (ec)
308 {
309 phosphor::logging::log<phosphor::logging::level::ERR>(
310 "Failed to query fan tach sub tree objects");
311 return ipmi::responseUnspecifiedError();
312 }
313 if (instance >= flatMap.size())
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700314 {
315 return ipmi::responseInvalidFieldRequest();
316 }
Richard Marian Thomaiyar147daec2019-06-15 07:43:48 +0530317 auto itr = flatMap.nth(instance);
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700318 ipmi::Value reply;
Richard Marian Thomaiyar147daec2019-06-15 07:43:48 +0530319 if (mtm.getProperty(fanService, itr->first, fanIntf, "Value",
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700320 &reply) < 0)
321 {
322 return ipmi::responseInvalidFieldRequest();
323 }
324
325 double* doubleVal = std::get_if<double>(&reply);
326 if (doubleVal == nullptr)
327 {
328 return ipmi::responseUnspecifiedError();
329 }
330 uint8_t sensorVal = FAN_PRESENT | FAN_SENSOR_PRESENT;
331 std::optional<uint16_t> fanTach = std::round(*doubleVal);
332
Richard Marian Thomaiyar4cc10152019-08-02 23:21:45 +0530333 resetMtmTimer(yield);
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700334 return ipmi::responseSuccess(sensorVal, fanTach);
335 }
336 break;
Richard Marian Thomaiyar8e5e2b02019-08-01 07:50:55 +0530337 case SmSignalGet::smIdentifyButton:
338 {
339 if (action == SmActionGet::revert || action == SmActionGet::ignore)
340 {
341 // ButtonMasked property is not supported for ID button as it is
342 // unnecessary. Hence if requested for revert / ignore, override
343 // it to sample action to make tools happy.
344 action = SmActionGet::sample;
345 }
346 // fall-through
347 }
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700348 case SmSignalGet::smResetButton:
349 case SmSignalGet::smPowerButton:
350 case SmSignalGet::smNMIButton:
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700351 {
352 std::string path;
353 if (getGpioPathForSmSignal(signalType, path) < 0)
354 {
355 return ipmi::responseInvalidFieldRequest();
356 }
357
358 switch (action)
359 {
360 case SmActionGet::sample:
361 phosphor::logging::log<phosphor::logging::level::INFO>(
362 "case SmActionGet::sample");
363 break;
364 case SmActionGet::ignore:
365 {
366 phosphor::logging::log<phosphor::logging::level::INFO>(
367 "case SmActionGet::ignore");
368 if (mtm.setProperty(buttonService, path, buttonIntf,
369 "ButtonMasked", true) < 0)
370 {
371 return ipmi::responseUnspecifiedError();
372 }
373 }
374 break;
375 case SmActionGet::revert:
376 {
377 phosphor::logging::log<phosphor::logging::level::INFO>(
378 "case SmActionGet::revert");
379 if (mtm.setProperty(buttonService, path, buttonIntf,
380 "ButtonMasked", false) < 0)
381 {
382 return ipmi::responseUnspecifiedError();
383 }
384 }
385 break;
386
387 default:
388 return ipmi::responseInvalidFieldRequest();
389 break;
390 }
391
392 ipmi::Value reply;
393 if (mtm.getProperty(buttonService, path, buttonIntf,
394 "ButtonPressed", &reply) < 0)
395 {
396 return ipmi::responseUnspecifiedError();
397 }
398 bool* valPtr = std::get_if<bool>(&reply);
399 if (valPtr == nullptr)
400 {
401 return ipmi::responseUnspecifiedError();
402 }
Richard Marian Thomaiyar4cc10152019-08-02 23:21:45 +0530403 resetMtmTimer(yield);
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700404 uint8_t sensorVal = *valPtr;
405 return ipmi::responseSuccess(sensorVal, std::nullopt);
406 }
407 break;
Richard Marian Thomaiyar1b74a212019-08-03 13:26:17 +0530408 case SmSignalGet::smNcsiDiag:
409 {
410 constexpr const char* netBasePath = "/sys/class/net/eth";
411 constexpr const char* carrierSuffix = "/carrier";
412 std::ifstream netIfs(netBasePath + std::to_string(instance) +
413 carrierSuffix);
414 if (!netIfs.good())
415 {
416 return ipmi::responseInvalidFieldRequest();
417 }
418 std::string carrier;
419 netIfs >> carrier;
420 resetMtmTimer(yield);
421 return ipmi::responseSuccess(
422 static_cast<uint8_t>(std::stoi(carrier)), std::nullopt);
423 }
424 break;
Vernon Mauerya3702c12019-05-22 13:20:59 -0700425 default:
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700426 return ipmi::responseInvalidFieldRequest();
Vernon Mauerya3702c12019-05-22 13:20:59 -0700427 break;
428 }
Vernon Mauerya3702c12019-05-22 13:20:59 -0700429}
430
Richard Marian Thomaiyar4cc10152019-08-02 23:21:45 +0530431ipmi::RspType<> appMTMSetSignal(boost::asio::yield_context yield,
432 uint8_t signalTypeByte, uint8_t instance,
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000433 uint8_t actionByte,
434 std::optional<uint8_t> pwmSpeed)
Vernon Mauerya3702c12019-05-22 13:20:59 -0700435{
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000436 if (mtm.getAccessLvl() < MtmLvl::mtmAvailable)
Vernon Mauerya3702c12019-05-22 13:20:59 -0700437 {
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000438 return ipmi::responseInvalidCommand();
439 }
440
441 SmSignalSet signalType = static_cast<SmSignalSet>(signalTypeByte);
442 SmActionSet action = static_cast<SmActionSet>(actionByte);
443 Cc retCode = ccSuccess;
444 int8_t ret = 0;
445
446 switch (signalType)
447 {
448 case SmSignalSet::smPowerFaultLed:
449 case SmSignalSet::smSystemReadyLed:
450 case SmSignalSet::smIdentifyLed:
451 switch (action)
452 {
453 case SmActionSet::forceDeasserted:
Vernon Mauerya3702c12019-05-22 13:20:59 -0700454 {
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000455 phosphor::logging::log<phosphor::logging::level::INFO>(
456 "case SmActionSet::forceDeasserted");
Vernon Mauerya3702c12019-05-22 13:20:59 -0700457
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000458 retCode = ledStoreAndSet(signalType, std::string("Off"));
459 if (retCode != ccSuccess)
Vernon Mauerya3702c12019-05-22 13:20:59 -0700460 {
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000461 return ipmi::response(retCode);
Vernon Mauerya3702c12019-05-22 13:20:59 -0700462 }
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000463 mtm.revertTimer.start(revertTimeOut);
Vernon Mauerya3702c12019-05-22 13:20:59 -0700464 }
465 break;
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000466 case SmActionSet::forceAsserted:
Vernon Mauerya3702c12019-05-22 13:20:59 -0700467 {
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000468 phosphor::logging::log<phosphor::logging::level::INFO>(
469 "case SmActionSet::forceAsserted");
Vernon Mauerya3702c12019-05-22 13:20:59 -0700470
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000471 retCode = ledStoreAndSet(signalType, std::string("On"));
472 if (retCode != ccSuccess)
473 {
474 return ipmi::response(retCode);
475 }
476 mtm.revertTimer.start(revertTimeOut);
477 if (SmSignalSet::smPowerFaultLed == signalType)
478 {
479 // Deassert "system ready"
480 retCode = ledStoreAndSet(SmSignalSet::smSystemReadyLed,
481 std::string("Off"));
482 }
483 else if (SmSignalSet::smSystemReadyLed == signalType)
484 {
485 // Deassert "fault led"
486 retCode = ledStoreAndSet(SmSignalSet::smPowerFaultLed,
487 std::string("Off"));
488 }
489 }
490 break;
491 case SmActionSet::revert:
492 {
493 phosphor::logging::log<phosphor::logging::level::INFO>(
494 "case SmActionSet::revert");
495 retCode = ledRevert(signalType);
496 }
497 break;
498 default:
499 {
500 return ipmi::responseInvalidFieldRequest();
501 }
502 }
503 break;
504 case SmSignalSet::smFanPowerSpeed:
505 {
506 if ((action == SmActionSet::forceAsserted) && (!pwmSpeed))
507 {
508 return ipmi::responseReqDataLenInvalid();
509 }
510
511 if ((action == SmActionSet::forceAsserted) && (*pwmSpeed > 100))
512 {
513 return ipmi::responseInvalidFieldRequest();
514 }
515
516 uint8_t pwmValue = 0;
517 switch (action)
518 {
519 case SmActionSet::revert:
520 {
521 if (mtm.revertFanPWM)
522 {
523 ret = mtm.disablePidControlService(false);
Vernon Mauerya3702c12019-05-22 13:20:59 -0700524 if (ret < 0)
525 {
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000526 return ipmi::responseUnspecifiedError();
Vernon Mauerya3702c12019-05-22 13:20:59 -0700527 }
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000528 mtm.revertFanPWM = false;
Vernon Mauerya3702c12019-05-22 13:20:59 -0700529 }
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000530 }
531 break;
532 case SmActionSet::forceAsserted:
533 {
534 pwmValue = *pwmSpeed;
535 } // fall-through
536 case SmActionSet::forceDeasserted:
537 {
538 if (!mtm.revertFanPWM)
Vernon Mauerya3702c12019-05-22 13:20:59 -0700539 {
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000540 ret = mtm.disablePidControlService(true);
541 if (ret < 0)
542 {
543 return ipmi::responseUnspecifiedError();
544 }
545 mtm.revertFanPWM = true;
Vernon Mauerya3702c12019-05-22 13:20:59 -0700546 }
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000547 mtm.revertTimer.start(revertTimeOut);
548 std::string fanPwmInstancePath =
549 fanPwmPath + std::to_string(instance + 1);
550
551 ret =
552 mtm.setProperty(fanService, fanPwmInstancePath, fanIntf,
553 "Value", static_cast<double>(pwmValue));
554 if (ret < 0)
555 {
556 return ipmi::responseUnspecifiedError();
557 }
558 }
559 break;
560 default:
561 {
562 return ipmi::responseInvalidFieldRequest();
Vernon Mauerya3702c12019-05-22 13:20:59 -0700563 }
564 }
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000565 }
566 break;
567 default:
568 {
569 return ipmi::responseInvalidFieldRequest();
Vernon Mauerya3702c12019-05-22 13:20:59 -0700570 }
571 }
Richard Marian Thomaiyar4cc10152019-08-02 23:21:45 +0530572 if (retCode == ccSuccess)
573 {
574 resetMtmTimer(yield);
575 }
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000576 return ipmi::response(retCode);
Vernon Mauerya3702c12019-05-22 13:20:59 -0700577}
578
Richard Marian Thomaiyar666dd012019-08-02 20:55:37 +0530579ipmi::RspType<> mtmKeepAlive(boost::asio::yield_context yield, uint8_t reserved,
580 const std::array<char, 5>& intentionalSignature)
581{
582 // Allow MTM keep alive command only in manfacturing mode.
583 if (mtm.getAccessLvl() != MtmLvl::mtmAvailable)
584 {
585 return ipmi::responseInvalidCommand();
586 }
587 constexpr std::array<char, 5> signatureOk = {'I', 'N', 'T', 'E', 'L'};
588 if (intentionalSignature != signatureOk || reserved != 0)
589 {
590 return ipmi::responseInvalidFieldRequest();
591 }
592 return ipmi::response(resetMtmTimer(yield));
593}
594
Yong Li85feb132019-08-09 16:06:03 +0800595ipmi::Cc mfgFilterMessage(ipmi::message::Request::ptr request)
596{
597 // i2c master write read command needs additional checking
598 if ((request->ctx->netFn == ipmi::netFnApp) &&
599 (request->ctx->cmd == ipmi::app::cmdMasterWriteRead))
600 {
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 }
610
611 return ipmi::ccSuccess;
612}
613
Richard Marian Thomaiyar1f0839c2019-08-25 20:10:52 +0530614static constexpr uint8_t maxEthSize = 6;
615static constexpr uint8_t maxSupportedEth = 3;
616static constexpr const char* factoryEthAddrBaseFileName =
617 "/var/sofs/factory-settings/network/mac/eth";
618
619ipmi::RspType<> setManufacturingData(boost::asio::yield_context yield,
620 uint8_t dataType,
621 std::array<uint8_t, maxEthSize> ethData)
622{
623 // mfg filter logic will restrict this command executing only in mfg mode.
624 if (dataType >= maxSupportedEth)
625 {
626 return ipmi::responseParmOutOfRange();
627 }
628
629 constexpr uint8_t invalidData = 0;
630 constexpr uint8_t validData = 1;
631 constexpr uint8_t ethAddrStrSize =
632 19; // XX:XX:XX:XX:XX:XX + \n + null termination;
633 std::vector<uint8_t> buff(ethAddrStrSize);
634 std::snprintf(reinterpret_cast<char*>(buff.data()), ethAddrStrSize,
635 "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx\n", ethData.at(0),
636 ethData.at(1), ethData.at(2), ethData.at(3), ethData.at(4),
637 ethData.at(5));
638 std::ofstream oEthFile(factoryEthAddrBaseFileName +
639 std::to_string(dataType),
640 std::ofstream::out);
641 if (!oEthFile.good())
642 {
643 return ipmi::responseUnspecifiedError();
644 }
645
646 oEthFile << reinterpret_cast<char*>(buff.data());
647 oEthFile << fflush;
648 oEthFile.close();
649
650 resetMtmTimer(yield);
651 return ipmi::responseSuccess();
652}
653
654ipmi::RspType<uint8_t, std::array<uint8_t, maxEthSize>>
655 getManufacturingData(boost::asio::yield_context yield, uint8_t dataType)
656{
657 // mfg filter logic will restrict this command executing only in mfg mode.
658 if (dataType >= maxSupportedEth)
659 {
660 return ipmi::responseParmOutOfRange();
661 }
662 std::array<uint8_t, maxEthSize> ethData{0};
663 constexpr uint8_t invalidData = 0;
664 constexpr uint8_t validData = 1;
665
666 std::ifstream iEthFile(factoryEthAddrBaseFileName +
667 std::to_string(dataType),
668 std::ifstream::in);
669 if (!iEthFile.good())
670 {
671 return ipmi::responseSuccess(invalidData, ethData);
672 }
673 std::string ethStr;
674 iEthFile >> ethStr;
675 uint8_t* data = ethData.data();
676 std::sscanf(ethStr.c_str(), "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx",
677 data, (data + 1), (data + 2), (data + 3), (data + 4),
678 (data + 5));
679
680 resetMtmTimer(yield);
681 return ipmi::responseSuccess(validData, ethData);
682}
683
Yong Lif267a672019-08-29 11:16:07 +0800684/** @brief implements slot master write read IPMI command which can be used for
685 * low-level I2C/SMBus write, read or write-read access for PCIE slots
686 * @param reserved - skip 6 bit
687 * @param addressType - address type
688 * @param bbSlotNum - baseboard slot number
689 * @param riserSlotNum - riser slot number
690 * @param reserved2 - skip 2 bit
691 * @param slaveAddr - slave address
692 * @param readCount - number of bytes to be read
693 * @param writeData - data to be written
694 *
695 * @returns IPMI completion code plus response data
696 */
697ipmi::RspType<std::vector<uint8_t>>
698 appSlotI2CMasterWriteRead(uint6_t reserved, uint2_t addressType,
699 uint3_t bbSlotNum, uint3_t riserSlotNum,
700 uint2_t resvered2, uint8_t slaveAddr,
701 uint8_t readCount, std::vector<uint8_t> writeData)
702{
703 const size_t writeCount = writeData.size();
704 std::string i2cBus;
705 if (addressType == slotAddressTypeBus)
706 {
707 std::string path = "/dev/i2c-mux/Riser_" +
708 std::to_string(static_cast<uint8_t>(bbSlotNum)) +
709 "_Mux/Pcie_Slot_" +
710 std::to_string(static_cast<uint8_t>(riserSlotNum));
711
712 if (std::filesystem::exists(path) && std::filesystem::is_symlink(path))
713 {
714 i2cBus = std::filesystem::read_symlink(path);
715 }
716 else
717 {
718 phosphor::logging::log<phosphor::logging::level::ERR>(
719 "Master write read command: Cannot get BusID");
720 return ipmi::responseInvalidFieldRequest();
721 }
722 }
723 else if (addressType == slotAddressTypeUniqueid)
724 {
725 i2cBus = "/dev/i2c-" +
726 std::to_string(static_cast<uint8_t>(bbSlotNum) |
727 (static_cast<uint8_t>(riserSlotNum) << 3));
728 }
729 else
730 {
731 phosphor::logging::log<phosphor::logging::level::ERR>(
732 "Master write read command: invalid request");
733 return ipmi::responseInvalidFieldRequest();
734 }
735
736 // Allow single byte write as it is offset byte to read the data, rest allow
737 // only in MFG mode.
738 if (writeCount > 1)
739 {
740 if (mtm.getAccessLvl() < MtmLvl::mtmAvailable)
741 {
742 return ipmi::responseInsufficientPrivilege();
743 }
744 }
745
746 if (readCount > slotI2CMaxReadSize)
747 {
748 phosphor::logging::log<phosphor::logging::level::ERR>(
749 "Master write read command: Read count exceeds limit");
750 return ipmi::responseParmOutOfRange();
751 }
752
753 if (!readCount && !writeCount)
754 {
755 phosphor::logging::log<phosphor::logging::level::ERR>(
756 "Master write read command: Read & write count are 0");
757 return ipmi::responseInvalidFieldRequest();
758 }
759
760 std::vector<uint8_t> readBuf(readCount);
761
762 ipmi::Cc retI2C = ipmi::i2cWriteRead(i2cBus, slaveAddr, writeData, readBuf);
763 if (retI2C != ipmi::ccSuccess)
764 {
765 return ipmi::response(retI2C);
766 }
767
768 return ipmi::responseSuccess(readBuf);
769}
Vernon Mauerya3702c12019-05-22 13:20:59 -0700770} // namespace ipmi
771
772void register_mtm_commands() __attribute__((constructor));
773void register_mtm_commands()
774{
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700775 // <Get SM Signal>
Vernon Mauery98bbf692019-09-16 11:14:59 -0700776 ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnGeneral,
777 ipmi::intel::general::cmdGetSmSignal,
778 ipmi::Privilege::Admin, ipmi::appMTMGetSignal);
Vernon Mauerya3702c12019-05-22 13:20:59 -0700779
Vernon Mauery98bbf692019-09-16 11:14:59 -0700780 ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnGeneral,
781 ipmi::intel::general::cmdSetSmSignal,
782 ipmi::Privilege::Admin, ipmi::appMTMSetSignal);
Vernon Mauerya3702c12019-05-22 13:20:59 -0700783
Vernon Mauery98bbf692019-09-16 11:14:59 -0700784 ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnGeneral,
785 ipmi::intel::general::cmdMtmKeepAlive,
786 ipmi::Privilege::Admin, ipmi::mtmKeepAlive);
Richard Marian Thomaiyar666dd012019-08-02 20:55:37 +0530787
Vernon Mauery98bbf692019-09-16 11:14:59 -0700788 ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnGeneral,
789 ipmi::intel::general::cmdSetManufacturingData,
790 ipmi::Privilege::Admin, ipmi::setManufacturingData);
Richard Marian Thomaiyar1f0839c2019-08-25 20:10:52 +0530791
Vernon Mauery98bbf692019-09-16 11:14:59 -0700792 ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnGeneral,
793 ipmi::intel::general::cmdGetManufacturingData,
794 ipmi::Privilege::Admin, ipmi::getManufacturingData);
Richard Marian Thomaiyar1f0839c2019-08-25 20:10:52 +0530795
Vernon Mauery98bbf692019-09-16 11:14:59 -0700796 ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnApp,
797 ipmi::intel::general::cmdSlotI2CMasterWriteRead,
798 ipmi::Privilege::Admin,
799 ipmi::appSlotI2CMasterWriteRead);
Yong Lif267a672019-08-29 11:16:07 +0800800
Vernon Mauery98bbf692019-09-16 11:14:59 -0700801 ipmi::registerFilter(ipmi::prioOemBase,
Yong Li85feb132019-08-09 16:06:03 +0800802 [](ipmi::message::Request::ptr request) {
803 return ipmi::mfgFilterMessage(request);
804 });
Vernon Mauerya3702c12019-05-22 13:20:59 -0700805}