blob: 5e3dbdde39570ae25c6033c2c4a3b5869ba48c81 [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 Thomaiyar357ddc72019-09-01 22:40:07 +053051static inline Cc resetMtmTimer(ipmi::Context::ptr ctx)
Richard Marian Thomaiyar666dd012019-08-02 20:55:37 +053052{
Richard Marian Thomaiyar666dd012019-08-02 20:55:37 +053053 boost::system::error_code ec;
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +053054 ctx->bus->yield_method_call<>(ctx->yield, ec, specialModeService,
55 specialModeObjPath, specialModeIntf,
56 "ResetTimer");
Richard Marian Thomaiyar666dd012019-08-02 20:55:37 +053057 if (ec)
58 {
59 phosphor::logging::log<phosphor::logging::level::ERR>(
60 "Failed to reset the manufacturing mode timer");
61 return ccUnspecifiedError;
62 }
63 return ccSuccess;
64}
65
Jason M. Bills38d2b5a2019-06-03 16:26:11 -070066int getGpioPathForSmSignal(const SmSignalGet signal, std::string& path)
Vernon Mauerya3702c12019-05-22 13:20:59 -070067{
Jason M. Bills38d2b5a2019-06-03 16:26:11 -070068 switch (signal)
69 {
70 case SmSignalGet::smPowerButton:
71 path = "/xyz/openbmc_project/chassis/buttons/power";
72 break;
73 case SmSignalGet::smResetButton:
74 path = "/xyz/openbmc_project/chassis/buttons/reset";
75 break;
76 case SmSignalGet::smNMIButton:
77 path = "/xyz/openbmc_project/chassis/buttons/nmi";
78 break;
Richard Marian Thomaiyar8e5e2b02019-08-01 07:50:55 +053079 case SmSignalGet::smIdentifyButton:
80 path = "/xyz/openbmc_project/chassis/buttons/id";
81 break;
Jason M. Bills38d2b5a2019-06-03 16:26:11 -070082 default:
83 return -1;
84 break;
85 }
86 return 0;
Vernon Mauerya3702c12019-05-22 13:20:59 -070087}
88
89ipmi_ret_t ledStoreAndSet(SmSignalSet signal, std::string setState)
90{
91 LedProperty* ledProp = mtm.findLedProperty(signal);
92 if (ledProp == nullptr)
93 {
94 return IPMI_CC_INVALID_FIELD_REQUEST;
95 }
96
97 std::string ledName = ledProp->getName();
98 std::string ledService = ledServicePrefix + ledName;
99 std::string ledPath = ledPathPrefix + ledName;
100 ipmi::Value presentState;
101
102 if (false == ledProp->getLock())
103 {
104 if (mtm.getProperty(ledService.c_str(), ledPath.c_str(), ledIntf,
105 "State", &presentState) != 0)
106 {
107 return IPMI_CC_UNSPECIFIED_ERROR;
108 }
109 ledProp->setPrevState(std::get<std::string>(presentState));
110 ledProp->setLock(true);
111 if (signal == SmSignalSet::smPowerFaultLed ||
112 signal == SmSignalSet::smSystemReadyLed)
113 {
114 mtm.revertLedCallback = true;
115 }
116 }
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700117 if (mtm.setProperty(ledService, ledPath, ledIntf, "State",
Vernon Mauerya3702c12019-05-22 13:20:59 -0700118 ledStateStr + setState) != 0)
119 {
120 return IPMI_CC_UNSPECIFIED_ERROR;
121 }
122 return IPMI_CC_OK;
123}
124
125ipmi_ret_t ledRevert(SmSignalSet signal)
126{
127 LedProperty* ledProp = mtm.findLedProperty(signal);
128 if (ledProp == nullptr)
129 {
130 return IPMI_CC_INVALID_FIELD_REQUEST;
131 }
132 if (true == ledProp->getLock())
133 {
134 ledProp->setLock(false);
135 if (signal == SmSignalSet::smPowerFaultLed ||
136 signal == SmSignalSet::smSystemReadyLed)
137 {
138 try
139 {
140 ipmi::method_no_args::callDbusMethod(
141 *getSdBus(), callbackMgrService, callbackMgrObjPath,
142 callbackMgrIntf, retriggerLedUpdate);
143 }
144 catch (sdbusplus::exception_t& e)
145 {
146 return IPMI_CC_UNSPECIFIED_ERROR;
147 }
148 mtm.revertLedCallback = false;
149 }
150 else
151 {
152 std::string ledName = ledProp->getName();
153 std::string ledService = ledServicePrefix + ledName;
154 std::string ledPath = ledPathPrefix + ledName;
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700155 if (mtm.setProperty(ledService, ledPath, ledIntf, "State",
156 ledProp->getPrevState()) != 0)
Vernon Mauerya3702c12019-05-22 13:20:59 -0700157 {
158 return IPMI_CC_UNSPECIFIED_ERROR;
159 }
160 }
161 }
162 return IPMI_CC_OK;
163}
164
165void Manufacturing::initData()
166{
Vernon Mauerya3702c12019-05-22 13:20:59 -0700167 ledPropertyList.push_back(
168 LedProperty(SmSignalSet::smPowerFaultLed, "status_amber"));
169 ledPropertyList.push_back(
170 LedProperty(SmSignalSet::smSystemReadyLed, "status_green"));
171 ledPropertyList.push_back(
172 LedProperty(SmSignalSet::smIdentifyLed, "identify"));
173}
174
175void Manufacturing::revertTimerHandler()
176{
Vernon Mauerya3702c12019-05-22 13:20:59 -0700177 if (revertFanPWM)
178 {
179 revertFanPWM = false;
180 disablePidControlService(false);
181 }
182
183 for (const auto& ledProperty : ledPropertyList)
184 {
185 const std::string& ledName = ledProperty.getName();
186 ledRevert(ledProperty.getSignal());
187 }
188}
189
190Manufacturing::Manufacturing() :
191 revertTimer([&](void) { revertTimerHandler(); })
192{
193 initData();
194}
195
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700196int8_t Manufacturing::getProperty(const std::string& service,
197 const std::string& path,
198 const std::string& interface,
199 const std::string& propertyName,
200 ipmi::Value* reply)
Vernon Mauerya3702c12019-05-22 13:20:59 -0700201{
202 try
203 {
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700204 *reply = ipmi::getDbusProperty(*getSdBus(), service, path, interface,
205 propertyName);
Vernon Mauerya3702c12019-05-22 13:20:59 -0700206 }
207 catch (const sdbusplus::exception::SdBusError& e)
208 {
209 phosphor::logging::log<phosphor::logging::level::INFO>(
210 "ERROR: getProperty");
211 return -1;
212 }
213
214 return 0;
215}
216
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700217int8_t Manufacturing::setProperty(const std::string& service,
218 const std::string& path,
219 const std::string& interface,
220 const std::string& propertyName,
221 ipmi::Value value)
Vernon Mauerya3702c12019-05-22 13:20:59 -0700222{
223 try
224 {
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700225 ipmi::setDbusProperty(*getSdBus(), service, path, interface,
Vernon Mauerya3702c12019-05-22 13:20:59 -0700226 propertyName, value);
227 }
228 catch (const sdbusplus::exception::SdBusError& e)
229 {
230 phosphor::logging::log<phosphor::logging::level::INFO>(
231 "ERROR: setProperty");
232 return -1;
233 }
234
235 return 0;
236}
237
238int8_t Manufacturing::disablePidControlService(const bool disable)
239{
240 try
241 {
242 auto dbus = getSdBus();
243 auto method = dbus->new_method_call(systemDService, systemDObjPath,
244 systemDMgrIntf,
245 disable ? "StopUnit" : "StartUnit");
246 method.append(pidControlService, "replace");
247 auto reply = dbus->call(method);
248 }
249 catch (const sdbusplus::exception::SdBusError& e)
250 {
251 phosphor::logging::log<phosphor::logging::level::INFO>(
252 "ERROR: phosphor-pid-control service start or stop failed");
253 return -1;
254 }
255 return 0;
256}
257
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700258ipmi::RspType<uint8_t, // Signal value
259 std::optional<uint16_t> // Fan tach value
260 >
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +0530261 appMTMGetSignal(ipmi::Context::ptr ctx, uint8_t signalTypeByte,
Richard Marian Thomaiyar147daec2019-06-15 07:43:48 +0530262 uint8_t instance, uint8_t actionByte)
Vernon Mauerya3702c12019-05-22 13:20:59 -0700263{
Ayushi Smritie0511e52019-08-27 17:30:34 +0000264 // mfg filter logic is used to allow MTM get signal command only in
265 // manfacturing mode.
Vernon Mauerya3702c12019-05-22 13:20:59 -0700266
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700267 SmSignalGet signalType = static_cast<SmSignalGet>(signalTypeByte);
268 SmActionGet action = static_cast<SmActionGet>(actionByte);
269
270 switch (signalType)
271 {
272 case SmSignalGet::smFanPwmGet:
273 {
274 ipmi::Value reply;
Richard Marian Thomaiyar147daec2019-06-15 07:43:48 +0530275 std::string fullPath = fanPwmPath + std::to_string(instance + 1);
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700276 if (mtm.getProperty(fanService, fullPath, fanIntf, "Value",
277 &reply) < 0)
278 {
279 return ipmi::responseInvalidFieldRequest();
280 }
281 double* doubleVal = std::get_if<double>(&reply);
282 if (doubleVal == nullptr)
283 {
284 return ipmi::responseUnspecifiedError();
285 }
286 uint8_t sensorVal = std::round(*doubleVal);
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +0530287 resetMtmTimer(ctx);
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700288 return ipmi::responseSuccess(sensorVal, std::nullopt);
289 }
290 break;
291 case SmSignalGet::smFanTachometerGet:
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700292 {
Richard Marian Thomaiyar147daec2019-06-15 07:43:48 +0530293 boost::system::error_code ec;
294 using objFlatMap = boost::container::flat_map<
295 std::string, boost::container::flat_map<
296 std::string, std::vector<std::string>>>;
297
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +0530298 auto flatMap = ctx->bus->yield_method_call<objFlatMap>(
299 ctx->yield, ec, "xyz.openbmc_project.ObjectMapper",
Richard Marian Thomaiyar147daec2019-06-15 07:43:48 +0530300 "/xyz/openbmc_project/object_mapper",
301 "xyz.openbmc_project.ObjectMapper", "GetSubTree",
302 fanTachBasePath, 0, std::array<const char*, 1>{fanIntf});
303 if (ec)
304 {
305 phosphor::logging::log<phosphor::logging::level::ERR>(
306 "Failed to query fan tach sub tree objects");
307 return ipmi::responseUnspecifiedError();
308 }
309 if (instance >= flatMap.size())
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700310 {
311 return ipmi::responseInvalidFieldRequest();
312 }
Richard Marian Thomaiyar147daec2019-06-15 07:43:48 +0530313 auto itr = flatMap.nth(instance);
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700314 ipmi::Value reply;
Richard Marian Thomaiyar147daec2019-06-15 07:43:48 +0530315 if (mtm.getProperty(fanService, itr->first, fanIntf, "Value",
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700316 &reply) < 0)
317 {
318 return ipmi::responseInvalidFieldRequest();
319 }
320
321 double* doubleVal = std::get_if<double>(&reply);
322 if (doubleVal == nullptr)
323 {
324 return ipmi::responseUnspecifiedError();
325 }
326 uint8_t sensorVal = FAN_PRESENT | FAN_SENSOR_PRESENT;
327 std::optional<uint16_t> fanTach = std::round(*doubleVal);
328
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +0530329 resetMtmTimer(ctx);
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700330 return ipmi::responseSuccess(sensorVal, fanTach);
331 }
332 break;
Richard Marian Thomaiyar8e5e2b02019-08-01 07:50:55 +0530333 case SmSignalGet::smIdentifyButton:
334 {
335 if (action == SmActionGet::revert || action == SmActionGet::ignore)
336 {
337 // ButtonMasked property is not supported for ID button as it is
338 // unnecessary. Hence if requested for revert / ignore, override
339 // it to sample action to make tools happy.
340 action = SmActionGet::sample;
341 }
342 // fall-through
343 }
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700344 case SmSignalGet::smResetButton:
345 case SmSignalGet::smPowerButton:
346 case SmSignalGet::smNMIButton:
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700347 {
348 std::string path;
349 if (getGpioPathForSmSignal(signalType, path) < 0)
350 {
351 return ipmi::responseInvalidFieldRequest();
352 }
353
354 switch (action)
355 {
356 case SmActionGet::sample:
357 phosphor::logging::log<phosphor::logging::level::INFO>(
358 "case SmActionGet::sample");
359 break;
360 case SmActionGet::ignore:
361 {
362 phosphor::logging::log<phosphor::logging::level::INFO>(
363 "case SmActionGet::ignore");
364 if (mtm.setProperty(buttonService, path, buttonIntf,
365 "ButtonMasked", true) < 0)
366 {
367 return ipmi::responseUnspecifiedError();
368 }
369 }
370 break;
371 case SmActionGet::revert:
372 {
373 phosphor::logging::log<phosphor::logging::level::INFO>(
374 "case SmActionGet::revert");
375 if (mtm.setProperty(buttonService, path, buttonIntf,
376 "ButtonMasked", false) < 0)
377 {
378 return ipmi::responseUnspecifiedError();
379 }
380 }
381 break;
382
383 default:
384 return ipmi::responseInvalidFieldRequest();
385 break;
386 }
387
388 ipmi::Value reply;
389 if (mtm.getProperty(buttonService, path, buttonIntf,
390 "ButtonPressed", &reply) < 0)
391 {
392 return ipmi::responseUnspecifiedError();
393 }
394 bool* valPtr = std::get_if<bool>(&reply);
395 if (valPtr == nullptr)
396 {
397 return ipmi::responseUnspecifiedError();
398 }
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +0530399 resetMtmTimer(ctx);
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700400 uint8_t sensorVal = *valPtr;
401 return ipmi::responseSuccess(sensorVal, std::nullopt);
402 }
403 break;
Richard Marian Thomaiyar1b74a212019-08-03 13:26:17 +0530404 case SmSignalGet::smNcsiDiag:
405 {
406 constexpr const char* netBasePath = "/sys/class/net/eth";
407 constexpr const char* carrierSuffix = "/carrier";
408 std::ifstream netIfs(netBasePath + std::to_string(instance) +
409 carrierSuffix);
410 if (!netIfs.good())
411 {
412 return ipmi::responseInvalidFieldRequest();
413 }
414 std::string carrier;
415 netIfs >> carrier;
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +0530416 resetMtmTimer(ctx);
Richard Marian Thomaiyar1b74a212019-08-03 13:26:17 +0530417 return ipmi::responseSuccess(
418 static_cast<uint8_t>(std::stoi(carrier)), std::nullopt);
419 }
420 break;
Vernon Mauerya3702c12019-05-22 13:20:59 -0700421 default:
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700422 return ipmi::responseInvalidFieldRequest();
Vernon Mauerya3702c12019-05-22 13:20:59 -0700423 break;
424 }
Vernon Mauerya3702c12019-05-22 13:20:59 -0700425}
426
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +0530427ipmi::RspType<> appMTMSetSignal(ipmi::Context::ptr ctx, uint8_t signalTypeByte,
428 uint8_t instance, uint8_t actionByte,
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000429 std::optional<uint8_t> pwmSpeed)
Vernon Mauerya3702c12019-05-22 13:20:59 -0700430{
Ayushi Smritie0511e52019-08-27 17:30:34 +0000431 // mfg filter logic is used to allow MTM set signal command only in
432 // manfacturing mode.
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000433
434 SmSignalSet signalType = static_cast<SmSignalSet>(signalTypeByte);
435 SmActionSet action = static_cast<SmActionSet>(actionByte);
436 Cc retCode = ccSuccess;
437 int8_t ret = 0;
438
439 switch (signalType)
440 {
441 case SmSignalSet::smPowerFaultLed:
442 case SmSignalSet::smSystemReadyLed:
443 case SmSignalSet::smIdentifyLed:
444 switch (action)
445 {
446 case SmActionSet::forceDeasserted:
Vernon Mauerya3702c12019-05-22 13:20:59 -0700447 {
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000448 phosphor::logging::log<phosphor::logging::level::INFO>(
449 "case SmActionSet::forceDeasserted");
Vernon Mauerya3702c12019-05-22 13:20:59 -0700450
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000451 retCode = ledStoreAndSet(signalType, std::string("Off"));
452 if (retCode != ccSuccess)
Vernon Mauerya3702c12019-05-22 13:20:59 -0700453 {
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000454 return ipmi::response(retCode);
Vernon Mauerya3702c12019-05-22 13:20:59 -0700455 }
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000456 mtm.revertTimer.start(revertTimeOut);
Vernon Mauerya3702c12019-05-22 13:20:59 -0700457 }
458 break;
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000459 case SmActionSet::forceAsserted:
Vernon Mauerya3702c12019-05-22 13:20:59 -0700460 {
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000461 phosphor::logging::log<phosphor::logging::level::INFO>(
462 "case SmActionSet::forceAsserted");
Vernon Mauerya3702c12019-05-22 13:20:59 -0700463
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000464 retCode = ledStoreAndSet(signalType, std::string("On"));
465 if (retCode != ccSuccess)
466 {
467 return ipmi::response(retCode);
468 }
469 mtm.revertTimer.start(revertTimeOut);
470 if (SmSignalSet::smPowerFaultLed == signalType)
471 {
472 // Deassert "system ready"
473 retCode = ledStoreAndSet(SmSignalSet::smSystemReadyLed,
474 std::string("Off"));
475 }
476 else if (SmSignalSet::smSystemReadyLed == signalType)
477 {
478 // Deassert "fault led"
479 retCode = ledStoreAndSet(SmSignalSet::smPowerFaultLed,
480 std::string("Off"));
481 }
482 }
483 break;
484 case SmActionSet::revert:
485 {
486 phosphor::logging::log<phosphor::logging::level::INFO>(
487 "case SmActionSet::revert");
488 retCode = ledRevert(signalType);
489 }
490 break;
491 default:
492 {
493 return ipmi::responseInvalidFieldRequest();
494 }
495 }
496 break;
497 case SmSignalSet::smFanPowerSpeed:
498 {
499 if ((action == SmActionSet::forceAsserted) && (!pwmSpeed))
500 {
501 return ipmi::responseReqDataLenInvalid();
502 }
503
504 if ((action == SmActionSet::forceAsserted) && (*pwmSpeed > 100))
505 {
506 return ipmi::responseInvalidFieldRequest();
507 }
508
509 uint8_t pwmValue = 0;
510 switch (action)
511 {
512 case SmActionSet::revert:
513 {
514 if (mtm.revertFanPWM)
515 {
516 ret = mtm.disablePidControlService(false);
Vernon Mauerya3702c12019-05-22 13:20:59 -0700517 if (ret < 0)
518 {
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000519 return ipmi::responseUnspecifiedError();
Vernon Mauerya3702c12019-05-22 13:20:59 -0700520 }
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000521 mtm.revertFanPWM = false;
Vernon Mauerya3702c12019-05-22 13:20:59 -0700522 }
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000523 }
524 break;
525 case SmActionSet::forceAsserted:
526 {
527 pwmValue = *pwmSpeed;
528 } // fall-through
529 case SmActionSet::forceDeasserted:
530 {
531 if (!mtm.revertFanPWM)
Vernon Mauerya3702c12019-05-22 13:20:59 -0700532 {
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000533 ret = mtm.disablePidControlService(true);
534 if (ret < 0)
535 {
536 return ipmi::responseUnspecifiedError();
537 }
538 mtm.revertFanPWM = true;
Vernon Mauerya3702c12019-05-22 13:20:59 -0700539 }
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000540 mtm.revertTimer.start(revertTimeOut);
541 std::string fanPwmInstancePath =
542 fanPwmPath + std::to_string(instance + 1);
543
544 ret =
545 mtm.setProperty(fanService, fanPwmInstancePath, fanIntf,
546 "Value", static_cast<double>(pwmValue));
547 if (ret < 0)
548 {
549 return ipmi::responseUnspecifiedError();
550 }
551 }
552 break;
553 default:
554 {
555 return ipmi::responseInvalidFieldRequest();
Vernon Mauerya3702c12019-05-22 13:20:59 -0700556 }
557 }
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000558 }
559 break;
560 default:
561 {
562 return ipmi::responseInvalidFieldRequest();
Vernon Mauerya3702c12019-05-22 13:20:59 -0700563 }
564 }
Richard Marian Thomaiyar4cc10152019-08-02 23:21:45 +0530565 if (retCode == ccSuccess)
566 {
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +0530567 resetMtmTimer(ctx);
Richard Marian Thomaiyar4cc10152019-08-02 23:21:45 +0530568 }
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000569 return ipmi::response(retCode);
Vernon Mauerya3702c12019-05-22 13:20:59 -0700570}
571
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +0530572ipmi::RspType<> mtmKeepAlive(ipmi::Context::ptr ctx, uint8_t reserved,
Richard Marian Thomaiyar666dd012019-08-02 20:55:37 +0530573 const std::array<char, 5>& intentionalSignature)
574{
Ayushi Smritie0511e52019-08-27 17:30:34 +0000575 // mfg filter logic is used to allow MTM keep alive command only in
576 // manfacturing mode
577
Richard Marian Thomaiyar666dd012019-08-02 20:55:37 +0530578 constexpr std::array<char, 5> signatureOk = {'I', 'N', 'T', 'E', 'L'};
579 if (intentionalSignature != signatureOk || reserved != 0)
580 {
581 return ipmi::responseInvalidFieldRequest();
582 }
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +0530583 return ipmi::response(resetMtmTimer(ctx));
Richard Marian Thomaiyar666dd012019-08-02 20:55:37 +0530584}
585
Ayushi Smritie0511e52019-08-27 17:30:34 +0000586static constexpr unsigned int makeCmdKey(unsigned int netFn, unsigned int cmd)
587{
588 return (netFn << 8) | cmd;
589}
590
Yong Li85feb132019-08-09 16:06:03 +0800591ipmi::Cc mfgFilterMessage(ipmi::message::Request::ptr request)
592{
Ayushi Smritie0511e52019-08-27 17:30:34 +0000593 // Restricted commands, must be executed only in Manufacturing mode
594 switch (makeCmdKey(request->ctx->netFn, request->ctx->cmd))
Yong Li85feb132019-08-09 16:06:03 +0800595 {
Ayushi Smritie0511e52019-08-27 17:30:34 +0000596 // i2c master write read command needs additional checking
597 case makeCmdKey(ipmi::netFnApp, ipmi::app::cmdMasterWriteRead):
598 if (request->payload.size() > 4)
599 {
600 // Allow write data count > 1, only if it is in MFG mode
601 if (mtm.getAccessLvl() != MtmLvl::mtmAvailable)
602 {
603 return ipmi::ccInsufficientPrivilege;
604 }
605 }
606 break;
607 case makeCmdKey(ipmi::netFnOemOne,
608 ipmi::intel::general::cmdGetSmSignal):
609 case makeCmdKey(ipmi::netFnOemOne,
610 ipmi::intel::general::cmdSetSmSignal):
611 case makeCmdKey(ipmi::netFnOemOne,
612 ipmi::intel::general::cmdMtmKeepAlive):
613 case makeCmdKey(ipmi::netFnOemOne,
614 ipmi::intel::general::cmdSetManufacturingData):
615 case makeCmdKey(ipmi::netFnOemOne,
616 ipmi::intel::general::cmdGetManufacturingData):
617 case makeCmdKey(ipmi::netFnStorage, ipmi::storage::cmdWriteFruData):
618
619 // Check for MTM mode
Yong Li85feb132019-08-09 16:06:03 +0800620 if (mtm.getAccessLvl() != MtmLvl::mtmAvailable)
621 {
Ayushi Smritie0511e52019-08-27 17:30:34 +0000622 return ipmi::ccInvalidCommand;
Yong Li85feb132019-08-09 16:06:03 +0800623 }
Yong Li85feb132019-08-09 16:06:03 +0800624 }
Yong Li85feb132019-08-09 16:06:03 +0800625 return ipmi::ccSuccess;
626}
627
Richard Marian Thomaiyar1f0839c2019-08-25 20:10:52 +0530628static constexpr uint8_t maxEthSize = 6;
629static constexpr uint8_t maxSupportedEth = 3;
630static constexpr const char* factoryEthAddrBaseFileName =
631 "/var/sofs/factory-settings/network/mac/eth";
632
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +0530633ipmi::RspType<> setManufacturingData(ipmi::Context::ptr ctx, uint8_t dataType,
Richard Marian Thomaiyar1f0839c2019-08-25 20:10:52 +0530634 std::array<uint8_t, maxEthSize> ethData)
635{
636 // mfg filter logic will restrict this command executing only in mfg mode.
637 if (dataType >= maxSupportedEth)
638 {
639 return ipmi::responseParmOutOfRange();
640 }
641
642 constexpr uint8_t invalidData = 0;
643 constexpr uint8_t validData = 1;
644 constexpr uint8_t ethAddrStrSize =
645 19; // XX:XX:XX:XX:XX:XX + \n + null termination;
646 std::vector<uint8_t> buff(ethAddrStrSize);
647 std::snprintf(reinterpret_cast<char*>(buff.data()), ethAddrStrSize,
648 "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx\n", ethData.at(0),
649 ethData.at(1), ethData.at(2), ethData.at(3), ethData.at(4),
650 ethData.at(5));
651 std::ofstream oEthFile(factoryEthAddrBaseFileName +
652 std::to_string(dataType),
653 std::ofstream::out);
654 if (!oEthFile.good())
655 {
656 return ipmi::responseUnspecifiedError();
657 }
658
659 oEthFile << reinterpret_cast<char*>(buff.data());
660 oEthFile << fflush;
661 oEthFile.close();
662
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +0530663 resetMtmTimer(ctx);
Richard Marian Thomaiyar1f0839c2019-08-25 20:10:52 +0530664 return ipmi::responseSuccess();
665}
666
667ipmi::RspType<uint8_t, std::array<uint8_t, maxEthSize>>
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +0530668 getManufacturingData(ipmi::Context::ptr ctx, uint8_t dataType)
Richard Marian Thomaiyar1f0839c2019-08-25 20:10:52 +0530669{
670 // mfg filter logic will restrict this command executing only in mfg mode.
671 if (dataType >= maxSupportedEth)
672 {
673 return ipmi::responseParmOutOfRange();
674 }
675 std::array<uint8_t, maxEthSize> ethData{0};
676 constexpr uint8_t invalidData = 0;
677 constexpr uint8_t validData = 1;
678
679 std::ifstream iEthFile(factoryEthAddrBaseFileName +
680 std::to_string(dataType),
681 std::ifstream::in);
682 if (!iEthFile.good())
683 {
684 return ipmi::responseSuccess(invalidData, ethData);
685 }
686 std::string ethStr;
687 iEthFile >> ethStr;
688 uint8_t* data = ethData.data();
689 std::sscanf(ethStr.c_str(), "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx",
690 data, (data + 1), (data + 2), (data + 3), (data + 4),
691 (data + 5));
692
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +0530693 resetMtmTimer(ctx);
Richard Marian Thomaiyar1f0839c2019-08-25 20:10:52 +0530694 return ipmi::responseSuccess(validData, ethData);
695}
696
Yong Lif267a672019-08-29 11:16:07 +0800697/** @brief implements slot master write read IPMI command which can be used for
698 * low-level I2C/SMBus write, read or write-read access for PCIE slots
699 * @param reserved - skip 6 bit
700 * @param addressType - address type
701 * @param bbSlotNum - baseboard slot number
702 * @param riserSlotNum - riser slot number
703 * @param reserved2 - skip 2 bit
704 * @param slaveAddr - slave address
705 * @param readCount - number of bytes to be read
706 * @param writeData - data to be written
707 *
708 * @returns IPMI completion code plus response data
709 */
710ipmi::RspType<std::vector<uint8_t>>
711 appSlotI2CMasterWriteRead(uint6_t reserved, uint2_t addressType,
712 uint3_t bbSlotNum, uint3_t riserSlotNum,
713 uint2_t resvered2, uint8_t slaveAddr,
714 uint8_t readCount, std::vector<uint8_t> writeData)
715{
716 const size_t writeCount = writeData.size();
717 std::string i2cBus;
718 if (addressType == slotAddressTypeBus)
719 {
720 std::string path = "/dev/i2c-mux/Riser_" +
721 std::to_string(static_cast<uint8_t>(bbSlotNum)) +
722 "_Mux/Pcie_Slot_" +
723 std::to_string(static_cast<uint8_t>(riserSlotNum));
724
725 if (std::filesystem::exists(path) && std::filesystem::is_symlink(path))
726 {
727 i2cBus = std::filesystem::read_symlink(path);
728 }
729 else
730 {
731 phosphor::logging::log<phosphor::logging::level::ERR>(
732 "Master write read command: Cannot get BusID");
733 return ipmi::responseInvalidFieldRequest();
734 }
735 }
736 else if (addressType == slotAddressTypeUniqueid)
737 {
738 i2cBus = "/dev/i2c-" +
739 std::to_string(static_cast<uint8_t>(bbSlotNum) |
740 (static_cast<uint8_t>(riserSlotNum) << 3));
741 }
742 else
743 {
744 phosphor::logging::log<phosphor::logging::level::ERR>(
745 "Master write read command: invalid request");
746 return ipmi::responseInvalidFieldRequest();
747 }
748
749 // Allow single byte write as it is offset byte to read the data, rest allow
750 // only in MFG mode.
751 if (writeCount > 1)
752 {
753 if (mtm.getAccessLvl() < MtmLvl::mtmAvailable)
754 {
755 return ipmi::responseInsufficientPrivilege();
756 }
757 }
758
759 if (readCount > slotI2CMaxReadSize)
760 {
761 phosphor::logging::log<phosphor::logging::level::ERR>(
762 "Master write read command: Read count exceeds limit");
763 return ipmi::responseParmOutOfRange();
764 }
765
766 if (!readCount && !writeCount)
767 {
768 phosphor::logging::log<phosphor::logging::level::ERR>(
769 "Master write read command: Read & write count are 0");
770 return ipmi::responseInvalidFieldRequest();
771 }
772
773 std::vector<uint8_t> readBuf(readCount);
774
775 ipmi::Cc retI2C = ipmi::i2cWriteRead(i2cBus, slaveAddr, writeData, readBuf);
776 if (retI2C != ipmi::ccSuccess)
777 {
778 return ipmi::response(retI2C);
779 }
780
781 return ipmi::responseSuccess(readBuf);
782}
Yong Li068b4f22019-09-17 16:32:18 +0800783
784ipmi::RspType<> clearCMOS()
785{
786 // There is an i2c device on bus 4, the slave address is 0x70. Based on the
787 // spec, writing 0x1 to address 0x60 on this device, will trigger the clear
788 // CMOS action.
789 constexpr uint8_t slaveAddr = 0x70;
790 std::string i2cBus = "/dev/i2c-4";
791 std::vector<uint8_t> writeData = {0x60, 0x1};
792 std::vector<uint8_t> readBuf(0);
793
794 // TODO Needs to enhance the below security checking
795 if (mtm.getAccessLvl() < MtmLvl::mtmAvailable)
796 {
797 return ipmi::responseInsufficientPrivilege();
798 }
799
800 ipmi::Cc retI2C = ipmi::i2cWriteRead(i2cBus, slaveAddr, writeData, readBuf);
801 return ipmi::response(retI2C);
802}
Vernon Mauerya3702c12019-05-22 13:20:59 -0700803} // namespace ipmi
804
805void register_mtm_commands() __attribute__((constructor));
806void register_mtm_commands()
807{
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700808 // <Get SM Signal>
Vernon Mauery98bbf692019-09-16 11:14:59 -0700809 ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnGeneral,
810 ipmi::intel::general::cmdGetSmSignal,
811 ipmi::Privilege::Admin, ipmi::appMTMGetSignal);
Vernon Mauerya3702c12019-05-22 13:20:59 -0700812
Vernon Mauery98bbf692019-09-16 11:14:59 -0700813 ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnGeneral,
814 ipmi::intel::general::cmdSetSmSignal,
815 ipmi::Privilege::Admin, ipmi::appMTMSetSignal);
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::cmdMtmKeepAlive,
819 ipmi::Privilege::Admin, ipmi::mtmKeepAlive);
Richard Marian Thomaiyar666dd012019-08-02 20:55:37 +0530820
Vernon Mauery98bbf692019-09-16 11:14:59 -0700821 ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnGeneral,
822 ipmi::intel::general::cmdSetManufacturingData,
823 ipmi::Privilege::Admin, ipmi::setManufacturingData);
Richard Marian Thomaiyar1f0839c2019-08-25 20:10:52 +0530824
Vernon Mauery98bbf692019-09-16 11:14:59 -0700825 ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnGeneral,
826 ipmi::intel::general::cmdGetManufacturingData,
827 ipmi::Privilege::Admin, ipmi::getManufacturingData);
Richard Marian Thomaiyar1f0839c2019-08-25 20:10:52 +0530828
Vernon Mauery98bbf692019-09-16 11:14:59 -0700829 ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnApp,
830 ipmi::intel::general::cmdSlotI2CMasterWriteRead,
831 ipmi::Privilege::Admin,
832 ipmi::appSlotI2CMasterWriteRead);
Yong Lif267a672019-08-29 11:16:07 +0800833
Yong Li068b4f22019-09-17 16:32:18 +0800834 ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnPlatform,
835 ipmi::intel::platform::cmdClearCMOS,
836 ipmi::Privilege::Admin, ipmi::clearCMOS);
837
Vernon Mauery98bbf692019-09-16 11:14:59 -0700838 ipmi::registerFilter(ipmi::prioOemBase,
Yong Li85feb132019-08-09 16:06:03 +0800839 [](ipmi::message::Request::ptr request) {
840 return ipmi::mfgFilterMessage(request);
841 });
Vernon Mauerya3702c12019-05-22 13:20:59 -0700842}