blob: df478878d4205492cce28b358816db73047bc266 [file] [log] [blame]
Vernon Mauerya3702c12019-05-22 13:20:59 -07001/*
2// Copyright (c) 2018 Intel Corporation
3//
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at
7//
8// http://www.apache.org/licenses/LICENSE-2.0
9//
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15*/
16
Ayushi Smritid872a4a2019-10-15 10:20:11 +053017#include <linux/input.h>
18
Arun P. Mohanan06584cd2021-08-13 20:56:01 +053019#include <boost/algorithm/string.hpp>
Richard Marian Thomaiyar147daec2019-06-15 07:43:48 +053020#include <boost/container/flat_map.hpp>
Vernon Mauerya3702c12019-05-22 13:20:59 -070021#include <ipmid/api.hpp>
22#include <manufacturingcommands.hpp>
23#include <oemcommands.hpp>
Jason M. Bills0748c692022-09-08 15:34:08 -070024#include <types.hpp>
Vernon Mauerya3702c12019-05-22 13:20:59 -070025
James Feistfcd2d3a2020-05-28 10:38:15 -070026#include <filesystem>
27#include <fstream>
28
Vernon Mauerya3702c12019-05-22 13:20:59 -070029namespace ipmi
30{
31
32Manufacturing mtm;
33
34static auto revertTimeOut =
35 std::chrono::duration_cast<std::chrono::microseconds>(
36 std::chrono::seconds(60)); // 1 minute timeout
37
Yong Lif267a672019-08-29 11:16:07 +080038static constexpr uint8_t slotAddressTypeBus = 0;
39static constexpr uint8_t slotAddressTypeUniqueid = 1;
40static constexpr uint8_t slotI2CMaxReadSize = 35;
41
Vernon Mauerya3702c12019-05-22 13:20:59 -070042static constexpr const char* callbackMgrService =
43 "xyz.openbmc_project.CallbackManager";
44static constexpr const char* callbackMgrIntf =
45 "xyz.openbmc_project.CallbackManager";
46static constexpr const char* callbackMgrObjPath =
47 "/xyz/openbmc_project/CallbackManager";
48static constexpr const char* retriggerLedUpdate = "RetriggerLEDUpdate";
49
50const static constexpr char* systemDService = "org.freedesktop.systemd1";
51const static constexpr char* systemDObjPath = "/org/freedesktop/systemd1";
52const static constexpr char* systemDMgrIntf =
53 "org.freedesktop.systemd1.Manager";
54const static constexpr char* pidControlService = "phosphor-pid-control.service";
55
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +053056static inline Cc resetMtmTimer(ipmi::Context::ptr ctx)
Richard Marian Thomaiyar666dd012019-08-02 20:55:37 +053057{
Richard Marian Thomaiyar666dd012019-08-02 20:55:37 +053058 boost::system::error_code ec;
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +053059 ctx->bus->yield_method_call<>(ctx->yield, ec, specialModeService,
60 specialModeObjPath, specialModeIntf,
61 "ResetTimer");
Richard Marian Thomaiyar666dd012019-08-02 20:55:37 +053062 if (ec)
63 {
64 phosphor::logging::log<phosphor::logging::level::ERR>(
65 "Failed to reset the manufacturing mode timer");
66 return ccUnspecifiedError;
67 }
68 return ccSuccess;
69}
70
Jason M. Bills38d2b5a2019-06-03 16:26:11 -070071int getGpioPathForSmSignal(const SmSignalGet signal, std::string& path)
Vernon Mauerya3702c12019-05-22 13:20:59 -070072{
Jason M. Bills38d2b5a2019-06-03 16:26:11 -070073 switch (signal)
74 {
75 case SmSignalGet::smPowerButton:
76 path = "/xyz/openbmc_project/chassis/buttons/power";
77 break;
78 case SmSignalGet::smResetButton:
79 path = "/xyz/openbmc_project/chassis/buttons/reset";
80 break;
81 case SmSignalGet::smNMIButton:
82 path = "/xyz/openbmc_project/chassis/buttons/nmi";
83 break;
Richard Marian Thomaiyar8e5e2b02019-08-01 07:50:55 +053084 case SmSignalGet::smIdentifyButton:
85 path = "/xyz/openbmc_project/chassis/buttons/id";
86 break;
Jason M. Bills38d2b5a2019-06-03 16:26:11 -070087 default:
88 return -1;
89 break;
90 }
91 return 0;
Vernon Mauerya3702c12019-05-22 13:20:59 -070092}
93
Patrick Venture37890392019-09-25 17:05:03 -070094ipmi_ret_t ledStoreAndSet(SmSignalSet signal, const std::string& setState)
Vernon Mauerya3702c12019-05-22 13:20:59 -070095{
96 LedProperty* ledProp = mtm.findLedProperty(signal);
97 if (ledProp == nullptr)
98 {
99 return IPMI_CC_INVALID_FIELD_REQUEST;
100 }
101
102 std::string ledName = ledProp->getName();
103 std::string ledService = ledServicePrefix + ledName;
104 std::string ledPath = ledPathPrefix + ledName;
105 ipmi::Value presentState;
106
107 if (false == ledProp->getLock())
108 {
109 if (mtm.getProperty(ledService.c_str(), ledPath.c_str(), ledIntf,
110 "State", &presentState) != 0)
111 {
112 return IPMI_CC_UNSPECIFIED_ERROR;
113 }
114 ledProp->setPrevState(std::get<std::string>(presentState));
115 ledProp->setLock(true);
116 if (signal == SmSignalSet::smPowerFaultLed ||
117 signal == SmSignalSet::smSystemReadyLed)
118 {
119 mtm.revertLedCallback = true;
120 }
121 }
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700122 if (mtm.setProperty(ledService, ledPath, ledIntf, "State",
Vernon Mauerya3702c12019-05-22 13:20:59 -0700123 ledStateStr + setState) != 0)
124 {
125 return IPMI_CC_UNSPECIFIED_ERROR;
126 }
127 return IPMI_CC_OK;
128}
129
130ipmi_ret_t ledRevert(SmSignalSet signal)
131{
132 LedProperty* ledProp = mtm.findLedProperty(signal);
133 if (ledProp == nullptr)
134 {
135 return IPMI_CC_INVALID_FIELD_REQUEST;
136 }
137 if (true == ledProp->getLock())
138 {
139 ledProp->setLock(false);
140 if (signal == SmSignalSet::smPowerFaultLed ||
141 signal == SmSignalSet::smSystemReadyLed)
142 {
143 try
144 {
145 ipmi::method_no_args::callDbusMethod(
146 *getSdBus(), callbackMgrService, callbackMgrObjPath,
147 callbackMgrIntf, retriggerLedUpdate);
148 }
Patrick Williamsbd51e6a2021-10-06 13:09:44 -0500149 catch (const sdbusplus::exception_t& e)
Vernon Mauerya3702c12019-05-22 13:20:59 -0700150 {
151 return IPMI_CC_UNSPECIFIED_ERROR;
152 }
153 mtm.revertLedCallback = false;
154 }
155 else
156 {
157 std::string ledName = ledProp->getName();
158 std::string ledService = ledServicePrefix + ledName;
159 std::string ledPath = ledPathPrefix + ledName;
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700160 if (mtm.setProperty(ledService, ledPath, ledIntf, "State",
161 ledProp->getPrevState()) != 0)
Vernon Mauerya3702c12019-05-22 13:20:59 -0700162 {
163 return IPMI_CC_UNSPECIFIED_ERROR;
164 }
165 }
166 }
167 return IPMI_CC_OK;
168}
169
170void Manufacturing::initData()
171{
Vernon Mauerya3702c12019-05-22 13:20:59 -0700172 ledPropertyList.push_back(
173 LedProperty(SmSignalSet::smPowerFaultLed, "status_amber"));
174 ledPropertyList.push_back(
175 LedProperty(SmSignalSet::smSystemReadyLed, "status_green"));
176 ledPropertyList.push_back(
177 LedProperty(SmSignalSet::smIdentifyLed, "identify"));
178}
179
180void Manufacturing::revertTimerHandler()
181{
Richard Marian Thomaiyarae13ac62019-12-17 15:45:12 +0530182
183#ifdef BMC_VALIDATION_UNSECURE_FEATURE
184 if (mtm.getMfgMode() == SpecialMode::valUnsecure)
185 {
186 // Don't revert the behaviour for validation unsecure mode.
187 return;
188 }
189#endif
Vernon Mauerya3702c12019-05-22 13:20:59 -0700190 if (revertFanPWM)
191 {
192 revertFanPWM = false;
193 disablePidControlService(false);
194 }
195
Ayushi Smritid872a4a2019-10-15 10:20:11 +0530196 if (mtmTestBeepFd != -1)
197 {
198 ::close(mtmTestBeepFd);
199 mtmTestBeepFd = -1;
200 }
201
Vernon Mauerya3702c12019-05-22 13:20:59 -0700202 for (const auto& ledProperty : ledPropertyList)
203 {
204 const std::string& ledName = ledProperty.getName();
Jayaprakash Mutyalaf3656142021-01-24 01:04:19 +0000205 if (ledName == "identify" && mtm.getMfgMode() == SpecialMode::mfg)
206 {
207 // Don't revert the behaviour for manufacturing mode
208 continue;
209 }
Vernon Mauerya3702c12019-05-22 13:20:59 -0700210 ledRevert(ledProperty.getSignal());
211 }
212}
213
214Manufacturing::Manufacturing() :
215 revertTimer([&](void) { revertTimerHandler(); })
216{
217 initData();
218}
219
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700220int8_t Manufacturing::getProperty(const std::string& service,
221 const std::string& path,
222 const std::string& interface,
223 const std::string& propertyName,
224 ipmi::Value* reply)
Vernon Mauerya3702c12019-05-22 13:20:59 -0700225{
226 try
227 {
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700228 *reply = ipmi::getDbusProperty(*getSdBus(), service, path, interface,
229 propertyName);
Vernon Mauerya3702c12019-05-22 13:20:59 -0700230 }
Patrick Williamsf944d2e2022-07-22 19:26:52 -0500231 catch (const sdbusplus::exception_t& e)
Vernon Mauerya3702c12019-05-22 13:20:59 -0700232 {
233 phosphor::logging::log<phosphor::logging::level::INFO>(
234 "ERROR: getProperty");
235 return -1;
236 }
237
238 return 0;
239}
240
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700241int8_t Manufacturing::setProperty(const std::string& service,
242 const std::string& path,
243 const std::string& interface,
244 const std::string& propertyName,
245 ipmi::Value value)
Vernon Mauerya3702c12019-05-22 13:20:59 -0700246{
247 try
248 {
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700249 ipmi::setDbusProperty(*getSdBus(), service, path, interface,
Vernon Mauerya3702c12019-05-22 13:20:59 -0700250 propertyName, value);
251 }
Patrick Williamsf944d2e2022-07-22 19:26:52 -0500252 catch (const sdbusplus::exception_t& e)
Vernon Mauerya3702c12019-05-22 13:20:59 -0700253 {
254 phosphor::logging::log<phosphor::logging::level::INFO>(
255 "ERROR: setProperty");
256 return -1;
257 }
258
259 return 0;
260}
261
262int8_t Manufacturing::disablePidControlService(const bool disable)
263{
264 try
265 {
266 auto dbus = getSdBus();
267 auto method = dbus->new_method_call(systemDService, systemDObjPath,
268 systemDMgrIntf,
269 disable ? "StopUnit" : "StartUnit");
270 method.append(pidControlService, "replace");
271 auto reply = dbus->call(method);
272 }
Patrick Williamsf944d2e2022-07-22 19:26:52 -0500273 catch (const sdbusplus::exception_t& e)
Vernon Mauerya3702c12019-05-22 13:20:59 -0700274 {
275 phosphor::logging::log<phosphor::logging::level::INFO>(
276 "ERROR: phosphor-pid-control service start or stop failed");
277 return -1;
278 }
279 return 0;
280}
281
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700282ipmi::RspType<uint8_t, // Signal value
283 std::optional<uint16_t> // Fan tach value
284 >
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +0530285 appMTMGetSignal(ipmi::Context::ptr ctx, uint8_t signalTypeByte,
Richard Marian Thomaiyar147daec2019-06-15 07:43:48 +0530286 uint8_t instance, uint8_t actionByte)
Vernon Mauerya3702c12019-05-22 13:20:59 -0700287{
Ayushi Smritie0511e52019-08-27 17:30:34 +0000288 // mfg filter logic is used to allow MTM get signal command only in
289 // manfacturing mode.
Vernon Mauerya3702c12019-05-22 13:20:59 -0700290
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700291 SmSignalGet signalType = static_cast<SmSignalGet>(signalTypeByte);
292 SmActionGet action = static_cast<SmActionGet>(actionByte);
293
294 switch (signalType)
295 {
anil kumar appana98705b32019-09-11 14:15:50 +0000296 case SmSignalGet::smChassisIntrusion:
297 {
298 ipmi::Value reply;
299 if (mtm.getProperty(intrusionService, intrusionPath, intrusionIntf,
300 "Status", &reply) < 0)
301 {
302 return ipmi::responseInvalidFieldRequest();
303 }
304 std::string* intrusionStatus = std::get_if<std::string>(&reply);
305 if (!intrusionStatus)
306 {
307 return ipmi::responseUnspecifiedError();
308 }
309
310 uint8_t status = 0;
311 if (!intrusionStatus->compare("Normal"))
312 {
313 status = static_cast<uint8_t>(IntrusionStatus::normal);
314 }
315 else if (!intrusionStatus->compare("HardwareIntrusion"))
316 {
317 status =
318 static_cast<uint8_t>(IntrusionStatus::hardwareIntrusion);
319 }
320 else if (!intrusionStatus->compare("TamperingDetected"))
321 {
322 status =
323 static_cast<uint8_t>(IntrusionStatus::tamperingDetected);
324 }
325 else
326 {
327 return ipmi::responseUnspecifiedError();
328 }
329 return ipmi::responseSuccess(status, std::nullopt);
330 }
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700331 case SmSignalGet::smFanPwmGet:
332 {
333 ipmi::Value reply;
Richard Marian Thomaiyar147daec2019-06-15 07:43:48 +0530334 std::string fullPath = fanPwmPath + std::to_string(instance + 1);
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700335 if (mtm.getProperty(fanService, fullPath, fanIntf, "Value",
336 &reply) < 0)
337 {
338 return ipmi::responseInvalidFieldRequest();
339 }
340 double* doubleVal = std::get_if<double>(&reply);
341 if (doubleVal == nullptr)
342 {
343 return ipmi::responseUnspecifiedError();
344 }
345 uint8_t sensorVal = std::round(*doubleVal);
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +0530346 resetMtmTimer(ctx);
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700347 return ipmi::responseSuccess(sensorVal, std::nullopt);
348 }
349 break;
350 case SmSignalGet::smFanTachometerGet:
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700351 {
Richard Marian Thomaiyar147daec2019-06-15 07:43:48 +0530352 boost::system::error_code ec;
353 using objFlatMap = boost::container::flat_map<
354 std::string, boost::container::flat_map<
355 std::string, std::vector<std::string>>>;
356
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +0530357 auto flatMap = ctx->bus->yield_method_call<objFlatMap>(
358 ctx->yield, ec, "xyz.openbmc_project.ObjectMapper",
Richard Marian Thomaiyar147daec2019-06-15 07:43:48 +0530359 "/xyz/openbmc_project/object_mapper",
360 "xyz.openbmc_project.ObjectMapper", "GetSubTree",
361 fanTachBasePath, 0, std::array<const char*, 1>{fanIntf});
362 if (ec)
363 {
364 phosphor::logging::log<phosphor::logging::level::ERR>(
365 "Failed to query fan tach sub tree objects");
366 return ipmi::responseUnspecifiedError();
367 }
368 if (instance >= flatMap.size())
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700369 {
370 return ipmi::responseInvalidFieldRequest();
371 }
Richard Marian Thomaiyar147daec2019-06-15 07:43:48 +0530372 auto itr = flatMap.nth(instance);
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700373 ipmi::Value reply;
Richard Marian Thomaiyar147daec2019-06-15 07:43:48 +0530374 if (mtm.getProperty(fanService, itr->first, fanIntf, "Value",
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700375 &reply) < 0)
376 {
377 return ipmi::responseInvalidFieldRequest();
378 }
379
380 double* doubleVal = std::get_if<double>(&reply);
381 if (doubleVal == nullptr)
382 {
383 return ipmi::responseUnspecifiedError();
384 }
385 uint8_t sensorVal = FAN_PRESENT | FAN_SENSOR_PRESENT;
386 std::optional<uint16_t> fanTach = std::round(*doubleVal);
387
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +0530388 resetMtmTimer(ctx);
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700389 return ipmi::responseSuccess(sensorVal, fanTach);
390 }
391 break;
Richard Marian Thomaiyar8e5e2b02019-08-01 07:50:55 +0530392 case SmSignalGet::smIdentifyButton:
393 {
394 if (action == SmActionGet::revert || action == SmActionGet::ignore)
395 {
396 // ButtonMasked property is not supported for ID button as it is
397 // unnecessary. Hence if requested for revert / ignore, override
398 // it to sample action to make tools happy.
399 action = SmActionGet::sample;
400 }
401 // fall-through
402 }
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700403 case SmSignalGet::smResetButton:
404 case SmSignalGet::smPowerButton:
405 case SmSignalGet::smNMIButton:
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700406 {
407 std::string path;
408 if (getGpioPathForSmSignal(signalType, path) < 0)
409 {
410 return ipmi::responseInvalidFieldRequest();
411 }
412
413 switch (action)
414 {
415 case SmActionGet::sample:
416 phosphor::logging::log<phosphor::logging::level::INFO>(
417 "case SmActionGet::sample");
418 break;
419 case SmActionGet::ignore:
420 {
421 phosphor::logging::log<phosphor::logging::level::INFO>(
422 "case SmActionGet::ignore");
423 if (mtm.setProperty(buttonService, path, buttonIntf,
424 "ButtonMasked", true) < 0)
425 {
426 return ipmi::responseUnspecifiedError();
427 }
428 }
429 break;
430 case SmActionGet::revert:
431 {
432 phosphor::logging::log<phosphor::logging::level::INFO>(
433 "case SmActionGet::revert");
434 if (mtm.setProperty(buttonService, path, buttonIntf,
435 "ButtonMasked", false) < 0)
436 {
437 return ipmi::responseUnspecifiedError();
438 }
439 }
440 break;
441
442 default:
443 return ipmi::responseInvalidFieldRequest();
444 break;
445 }
446
447 ipmi::Value reply;
448 if (mtm.getProperty(buttonService, path, buttonIntf,
449 "ButtonPressed", &reply) < 0)
450 {
451 return ipmi::responseUnspecifiedError();
452 }
453 bool* valPtr = std::get_if<bool>(&reply);
454 if (valPtr == nullptr)
455 {
456 return ipmi::responseUnspecifiedError();
457 }
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +0530458 resetMtmTimer(ctx);
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700459 uint8_t sensorVal = *valPtr;
460 return ipmi::responseSuccess(sensorVal, std::nullopt);
461 }
462 break;
Richard Marian Thomaiyar1b74a212019-08-03 13:26:17 +0530463 case SmSignalGet::smNcsiDiag:
464 {
465 constexpr const char* netBasePath = "/sys/class/net/eth";
466 constexpr const char* carrierSuffix = "/carrier";
467 std::ifstream netIfs(netBasePath + std::to_string(instance) +
468 carrierSuffix);
469 if (!netIfs.good())
470 {
471 return ipmi::responseInvalidFieldRequest();
472 }
473 std::string carrier;
474 netIfs >> carrier;
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +0530475 resetMtmTimer(ctx);
Richard Marian Thomaiyar1b74a212019-08-03 13:26:17 +0530476 return ipmi::responseSuccess(
477 static_cast<uint8_t>(std::stoi(carrier)), std::nullopt);
478 }
479 break;
Vernon Mauerya3702c12019-05-22 13:20:59 -0700480 default:
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700481 return ipmi::responseInvalidFieldRequest();
Vernon Mauerya3702c12019-05-22 13:20:59 -0700482 break;
483 }
Vernon Mauerya3702c12019-05-22 13:20:59 -0700484}
485
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +0530486ipmi::RspType<> appMTMSetSignal(ipmi::Context::ptr ctx, uint8_t signalTypeByte,
487 uint8_t instance, uint8_t actionByte,
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000488 std::optional<uint8_t> pwmSpeed)
Vernon Mauerya3702c12019-05-22 13:20:59 -0700489{
Ayushi Smritie0511e52019-08-27 17:30:34 +0000490 // mfg filter logic is used to allow MTM set signal command only in
491 // manfacturing mode.
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000492
493 SmSignalSet signalType = static_cast<SmSignalSet>(signalTypeByte);
494 SmActionSet action = static_cast<SmActionSet>(actionByte);
495 Cc retCode = ccSuccess;
496 int8_t ret = 0;
497
498 switch (signalType)
499 {
500 case SmSignalSet::smPowerFaultLed:
501 case SmSignalSet::smSystemReadyLed:
502 case SmSignalSet::smIdentifyLed:
503 switch (action)
504 {
505 case SmActionSet::forceDeasserted:
Vernon Mauerya3702c12019-05-22 13:20:59 -0700506 {
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000507 phosphor::logging::log<phosphor::logging::level::INFO>(
508 "case SmActionSet::forceDeasserted");
Vernon Mauerya3702c12019-05-22 13:20:59 -0700509
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000510 retCode = ledStoreAndSet(signalType, std::string("Off"));
511 if (retCode != ccSuccess)
Vernon Mauerya3702c12019-05-22 13:20:59 -0700512 {
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000513 return ipmi::response(retCode);
Vernon Mauerya3702c12019-05-22 13:20:59 -0700514 }
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000515 mtm.revertTimer.start(revertTimeOut);
Vernon Mauerya3702c12019-05-22 13:20:59 -0700516 }
517 break;
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000518 case SmActionSet::forceAsserted:
Vernon Mauerya3702c12019-05-22 13:20:59 -0700519 {
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000520 phosphor::logging::log<phosphor::logging::level::INFO>(
521 "case SmActionSet::forceAsserted");
Vernon Mauerya3702c12019-05-22 13:20:59 -0700522
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000523 retCode = ledStoreAndSet(signalType, std::string("On"));
524 if (retCode != ccSuccess)
525 {
526 return ipmi::response(retCode);
527 }
528 mtm.revertTimer.start(revertTimeOut);
529 if (SmSignalSet::smPowerFaultLed == signalType)
530 {
531 // Deassert "system ready"
532 retCode = ledStoreAndSet(SmSignalSet::smSystemReadyLed,
533 std::string("Off"));
534 }
535 else if (SmSignalSet::smSystemReadyLed == signalType)
536 {
537 // Deassert "fault led"
538 retCode = ledStoreAndSet(SmSignalSet::smPowerFaultLed,
539 std::string("Off"));
540 }
541 }
542 break;
543 case SmActionSet::revert:
544 {
545 phosphor::logging::log<phosphor::logging::level::INFO>(
546 "case SmActionSet::revert");
547 retCode = ledRevert(signalType);
548 }
549 break;
550 default:
551 {
552 return ipmi::responseInvalidFieldRequest();
553 }
554 }
555 break;
556 case SmSignalSet::smFanPowerSpeed:
557 {
558 if ((action == SmActionSet::forceAsserted) && (!pwmSpeed))
559 {
560 return ipmi::responseReqDataLenInvalid();
561 }
562
563 if ((action == SmActionSet::forceAsserted) && (*pwmSpeed > 100))
564 {
565 return ipmi::responseInvalidFieldRequest();
566 }
567
568 uint8_t pwmValue = 0;
569 switch (action)
570 {
571 case SmActionSet::revert:
572 {
573 if (mtm.revertFanPWM)
574 {
575 ret = mtm.disablePidControlService(false);
Vernon Mauerya3702c12019-05-22 13:20:59 -0700576 if (ret < 0)
577 {
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000578 return ipmi::responseUnspecifiedError();
Vernon Mauerya3702c12019-05-22 13:20:59 -0700579 }
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000580 mtm.revertFanPWM = false;
Vernon Mauerya3702c12019-05-22 13:20:59 -0700581 }
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000582 }
583 break;
584 case SmActionSet::forceAsserted:
585 {
586 pwmValue = *pwmSpeed;
587 } // fall-through
588 case SmActionSet::forceDeasserted:
589 {
590 if (!mtm.revertFanPWM)
Vernon Mauerya3702c12019-05-22 13:20:59 -0700591 {
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000592 ret = mtm.disablePidControlService(true);
593 if (ret < 0)
594 {
595 return ipmi::responseUnspecifiedError();
596 }
597 mtm.revertFanPWM = true;
Vernon Mauerya3702c12019-05-22 13:20:59 -0700598 }
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000599 mtm.revertTimer.start(revertTimeOut);
600 std::string fanPwmInstancePath =
601 fanPwmPath + std::to_string(instance + 1);
602
603 ret =
604 mtm.setProperty(fanService, fanPwmInstancePath, fanIntf,
605 "Value", static_cast<double>(pwmValue));
606 if (ret < 0)
607 {
608 return ipmi::responseUnspecifiedError();
609 }
610 }
611 break;
612 default:
613 {
614 return ipmi::responseInvalidFieldRequest();
Vernon Mauerya3702c12019-05-22 13:20:59 -0700615 }
616 }
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000617 }
618 break;
Ayushi Smritid872a4a2019-10-15 10:20:11 +0530619 case SmSignalSet::smSpeaker:
620 {
621 phosphor::logging::log<phosphor::logging::level::INFO>(
622 "Performing Speaker SmActionSet",
623 phosphor::logging::entry("ACTION=%d",
624 static_cast<uint8_t>(action)));
625 switch (action)
626 {
627 case SmActionSet::forceAsserted:
628 {
629 char beepDevName[] = "/dev/input/event0";
630 if (mtm.mtmTestBeepFd != -1)
631 {
632 phosphor::logging::log<phosphor::logging::level::INFO>(
633 "mtm beep device is opened already!");
634 // returning success as already beep is in progress
635 return ipmi::response(retCode);
636 }
637
638 if ((mtm.mtmTestBeepFd =
639 ::open(beepDevName, O_RDWR | O_CLOEXEC)) < 0)
640 {
641 phosphor::logging::log<phosphor::logging::level::ERR>(
642 "Failed to open input device");
643 return ipmi::responseUnspecifiedError();
644 }
645
646 struct input_event event;
647 event.type = EV_SND;
648 event.code = SND_TONE;
649 event.value = 2000;
650
651 if (::write(mtm.mtmTestBeepFd, &event,
652 sizeof(struct input_event)) !=
653 sizeof(struct input_event))
654 {
655 phosphor::logging::log<phosphor::logging::level::ERR>(
656 "Failed to write a tone sound event");
657 ::close(mtm.mtmTestBeepFd);
658 mtm.mtmTestBeepFd = -1;
659 return ipmi::responseUnspecifiedError();
660 }
661 mtm.revertTimer.start(revertTimeOut);
662 }
663 break;
664 case SmActionSet::revert:
665 case SmActionSet::forceDeasserted:
666 {
667 if (mtm.mtmTestBeepFd != -1)
668 {
669 ::close(mtm.mtmTestBeepFd);
670 mtm.mtmTestBeepFd = -1;
671 }
672 }
673 break;
674 default:
675 {
676 return ipmi::responseInvalidFieldRequest();
677 }
678 }
679 }
680 break;
Richard Marian Thomaiyar3594c6d2019-11-19 21:29:04 +0530681 case SmSignalSet::smDiskFaultLed:
682 {
683 boost::system::error_code ec;
684 using objPaths = std::vector<std::string>;
685 std::string driveBasePath =
686 "/xyz/openbmc_project/inventory/item/drive/";
687 static constexpr const char* driveLedIntf =
688 "xyz.openbmc_project.Led.Group";
689 static constexpr const char* hsbpService =
690 "xyz.openbmc_project.HsbpManager";
691
692 auto driveList = ctx->bus->yield_method_call<objPaths>(
693 ctx->yield, ec, "xyz.openbmc_project.ObjectMapper",
694 "/xyz/openbmc_project/object_mapper",
695 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths",
696 driveBasePath, 0, std::array<const char*, 1>{driveLedIntf});
697 if (ec)
698 {
699 phosphor::logging::log<phosphor::logging::level::ERR>(
700 "Failed to query HSBP drive sub tree objects");
701 return ipmi::responseUnspecifiedError();
702 }
703 std::string driveObjPath =
704 driveBasePath + "Drive_" + std::to_string(instance + 1);
705 if (std::find(driveList.begin(), driveList.end(), driveObjPath) ==
706 driveList.end())
707 {
708 return ipmi::responseInvalidFieldRequest();
709 }
710 bool driveLedState = false;
711 switch (action)
712 {
713 case SmActionSet::forceAsserted:
714 {
715 driveLedState = true;
716 }
717 break;
718 case SmActionSet::revert:
719 {
720 driveLedState = false;
721 }
722 break;
723 case SmActionSet::forceDeasserted:
724 {
725 driveLedState = false;
726 }
727 break;
728 default:
729 {
730 return ipmi::responseInvalidFieldRequest();
731 }
732 }
733 ret = mtm.setProperty(hsbpService, driveObjPath, driveLedIntf,
734 "Asserted", driveLedState);
735 if (ret < 0)
736 {
737 return ipmi::responseUnspecifiedError();
738 }
739 }
740 break;
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000741 default:
742 {
743 return ipmi::responseInvalidFieldRequest();
Vernon Mauerya3702c12019-05-22 13:20:59 -0700744 }
745 }
Richard Marian Thomaiyar4cc10152019-08-02 23:21:45 +0530746 if (retCode == ccSuccess)
747 {
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +0530748 resetMtmTimer(ctx);
Richard Marian Thomaiyar4cc10152019-08-02 23:21:45 +0530749 }
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000750 return ipmi::response(retCode);
Vernon Mauerya3702c12019-05-22 13:20:59 -0700751}
752
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +0530753ipmi::RspType<> mtmKeepAlive(ipmi::Context::ptr ctx, uint8_t reserved,
Richard Marian Thomaiyar666dd012019-08-02 20:55:37 +0530754 const std::array<char, 5>& intentionalSignature)
755{
Ayushi Smritie0511e52019-08-27 17:30:34 +0000756 // mfg filter logic is used to allow MTM keep alive command only in
757 // manfacturing mode
758
Richard Marian Thomaiyar666dd012019-08-02 20:55:37 +0530759 constexpr std::array<char, 5> signatureOk = {'I', 'N', 'T', 'E', 'L'};
760 if (intentionalSignature != signatureOk || reserved != 0)
761 {
762 return ipmi::responseInvalidFieldRequest();
763 }
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +0530764 return ipmi::response(resetMtmTimer(ctx));
Richard Marian Thomaiyar666dd012019-08-02 20:55:37 +0530765}
766
Ayushi Smritie0511e52019-08-27 17:30:34 +0000767static constexpr unsigned int makeCmdKey(unsigned int netFn, unsigned int cmd)
768{
769 return (netFn << 8) | cmd;
770}
771
Yong Li85feb132019-08-09 16:06:03 +0800772ipmi::Cc mfgFilterMessage(ipmi::message::Request::ptr request)
773{
Ayushi Smritie0511e52019-08-27 17:30:34 +0000774 // Restricted commands, must be executed only in Manufacturing mode
775 switch (makeCmdKey(request->ctx->netFn, request->ctx->cmd))
Yong Li85feb132019-08-09 16:06:03 +0800776 {
Ayushi Smritie0511e52019-08-27 17:30:34 +0000777 // i2c master write read command needs additional checking
778 case makeCmdKey(ipmi::netFnApp, ipmi::app::cmdMasterWriteRead):
779 if (request->payload.size() > 4)
780 {
Richard Marian Thomaiyarae13ac62019-12-17 15:45:12 +0530781 // Allow write data count > 1 only in Special mode
782 if (mtm.getMfgMode() == SpecialMode::none)
Ayushi Smritie0511e52019-08-27 17:30:34 +0000783 {
784 return ipmi::ccInsufficientPrivilege;
785 }
786 }
jayaprakash Mutyala2d4a0192019-11-11 22:12:45 +0000787 return ipmi::ccSuccess;
Ayushi Smritie0511e52019-08-27 17:30:34 +0000788 case makeCmdKey(ipmi::netFnOemOne,
789 ipmi::intel::general::cmdGetSmSignal):
790 case makeCmdKey(ipmi::netFnOemOne,
791 ipmi::intel::general::cmdSetSmSignal):
792 case makeCmdKey(ipmi::netFnOemOne,
793 ipmi::intel::general::cmdMtmKeepAlive):
794 case makeCmdKey(ipmi::netFnOemOne,
795 ipmi::intel::general::cmdSetManufacturingData):
796 case makeCmdKey(ipmi::netFnOemOne,
797 ipmi::intel::general::cmdGetManufacturingData):
Vernon Mauery27d23562021-08-12 10:43:39 -0700798 case makeCmdKey(ipmi::intel::netFnGeneral,
799 ipmi::intel::general::cmdSetFITcLayout):
Arun P. Mohanan06584cd2021-08-13 20:56:01 +0530800 case makeCmdKey(ipmi::netFnOemOne,
801 ipmi::intel::general::cmdMTMBMCFeatureControl):
Ayushi Smritie0511e52019-08-27 17:30:34 +0000802 case makeCmdKey(ipmi::netFnStorage, ipmi::storage::cmdWriteFruData):
AppaRao Puli9a13daa2020-07-13 17:53:00 +0530803 case makeCmdKey(ipmi::netFnOemTwo, ipmi::intel::platform::cmdClearCMOS):
Ayushi Smritie0511e52019-08-27 17:30:34 +0000804
Richard Marian Thomaiyarae13ac62019-12-17 15:45:12 +0530805 // Check for Special mode
806 if (mtm.getMfgMode() == SpecialMode::none)
Yong Li85feb132019-08-09 16:06:03 +0800807 {
Ayushi Smritie0511e52019-08-27 17:30:34 +0000808 return ipmi::ccInvalidCommand;
Yong Li85feb132019-08-09 16:06:03 +0800809 }
jayaprakash Mutyala2d4a0192019-11-11 22:12:45 +0000810 return ipmi::ccSuccess;
811 case makeCmdKey(ipmi::netFnStorage, ipmi::storage::cmdDeleteSelEntry):
812 {
813 return ipmi::ccInvalidCommand;
814 }
Yong Li85feb132019-08-09 16:06:03 +0800815 }
Yong Li85feb132019-08-09 16:06:03 +0800816 return ipmi::ccSuccess;
817}
818
Richard Marian Thomaiyar1f0839c2019-08-25 20:10:52 +0530819static constexpr uint8_t maxEthSize = 6;
820static constexpr uint8_t maxSupportedEth = 3;
821static constexpr const char* factoryEthAddrBaseFileName =
822 "/var/sofs/factory-settings/network/mac/eth";
823
Zhikui Renad129c62022-04-05 20:11:24 -0700824using ObjectType = boost::container::flat_map<
825 std::string, boost::container::flat_map<std::string, DbusVariant>>;
826using ManagedObjectType =
827 boost::container::flat_map<sdbusplus::message::object_path, ObjectType>;
828
829bool findFruDevice(const std::shared_ptr<sdbusplus::asio::connection>& bus,
830 boost::asio::yield_context& yield, uint64_t& macOffset,
831 uint64_t& busNum, uint64_t& address)
832{
833 boost::system::error_code ec;
834
835 // GetAll the objects under service FruDevice
836 ec = boost::system::errc::make_error_code(boost::system::errc::success);
837 auto obj = bus->yield_method_call<ManagedObjectType>(
838 yield, ec, "xyz.openbmc_project.EntityManager", "/",
839 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
840 if (ec)
841 {
842 phosphor::logging::log<phosphor::logging::level::ERR>(
843 "GetMangagedObjects failed",
844 phosphor::logging::entry("ERROR=%s", ec.message().c_str()));
845 return false;
846 }
847
848 for (const auto& [path, fru] : obj)
849 {
850 for (const auto& [intf, propMap] : fru)
851 {
852 if (intf == "xyz.openbmc_project.Inventory.Item.Board.Motherboard")
853 {
854 auto findBus = propMap.find("FruBus");
855 auto findAddress = propMap.find("FruAddress");
856 auto findMacOffset = propMap.find("MacOffset");
857 if (findBus == propMap.end() || findAddress == propMap.end() ||
858 findMacOffset == propMap.end())
859 {
860 continue;
861 }
862
863 auto fruBus = std::get_if<uint64_t>(&findBus->second);
864 auto fruAddress = std::get_if<uint64_t>(&findAddress->second);
865 auto macFruOffset =
866 std::get_if<uint64_t>(&findMacOffset->second);
867 if (!fruBus || !fruAddress || !macFruOffset)
868 {
869 phosphor::logging::log<phosphor::logging::level::INFO>(
870 "ERROR: MotherBoard FRU config data type invalid, not "
871 "used");
872 return false;
873 }
874 busNum = *fruBus;
875 address = *fruAddress;
876 macOffset = *macFruOffset;
877 return true;
878 }
879 }
880 }
881 return false;
882}
883
Zhikui Ren98fa87e2022-05-04 08:10:29 -0700884static constexpr uint64_t fruEnd = 0xff;
885// write rolls over within current page, need to keep mac within a page
886static constexpr uint64_t fruPageSize = 0x8;
887// MAC record struct: HEADER, MAC DATA, CheckSum
888static constexpr uint64_t macRecordSize = maxEthSize + 2;
889static_assert(fruPageSize >= macRecordSize,
890 "macRecordSize greater than eeprom page size");
891static constexpr uint8_t macHeader = 0x40;
892// Calculate new checksum for fru info area
893static uint8_t calculateChecksum(std::vector<uint8_t>::const_iterator iter,
894 std::vector<uint8_t>::const_iterator end)
895{
896 constexpr int checksumMod = 256;
897 uint8_t sum = std::accumulate(iter, end, static_cast<uint8_t>(0));
898 return (checksumMod - sum) % checksumMod;
899}
900
Zhikui Renad129c62022-04-05 20:11:24 -0700901bool readMacFromFru(ipmi::Context::ptr ctx, uint8_t macIndex,
902 std::array<uint8_t, maxEthSize>& ethData)
903{
Zhikui Renad129c62022-04-05 20:11:24 -0700904 uint64_t macOffset = fruEnd;
905 uint64_t fruBus = 0;
906 uint64_t fruAddress = 0;
907
908 if (findFruDevice(ctx->bus, ctx->yield, macOffset, fruBus, fruAddress))
909 {
910 phosphor::logging::log<phosphor::logging::level::INFO>(
911 "Found mac fru",
912 phosphor::logging::entry("BUS=%d", static_cast<uint8_t>(fruBus)),
913 phosphor::logging::entry("ADDRESS=%d",
914 static_cast<uint8_t>(fruAddress)));
915
Zhikui Ren98fa87e2022-05-04 08:10:29 -0700916 if (macOffset % fruPageSize)
917 {
918 macOffset = (macOffset / fruPageSize + 1) * fruPageSize;
919 }
920 macOffset += macIndex * fruPageSize;
921 if ((macOffset + macRecordSize) > fruEnd)
Zhikui Renad129c62022-04-05 20:11:24 -0700922 {
923 phosphor::logging::log<phosphor::logging::level::ERR>(
924 "ERROR: read fru mac failed, offset invalid");
925 return false;
926 }
927 std::vector<uint8_t> writeData;
Zhikui Ren98fa87e2022-05-04 08:10:29 -0700928 writeData.push_back(static_cast<uint8_t>(macOffset));
929 std::vector<uint8_t> readBuf(macRecordSize);
Zhikui Renad129c62022-04-05 20:11:24 -0700930 std::string i2cBus = "/dev/i2c-" + std::to_string(fruBus);
931 ipmi::Cc retI2C =
932 ipmi::i2cWriteRead(i2cBus, fruAddress, writeData, readBuf);
933 if (retI2C == ipmi::ccSuccess)
934 {
Zhikui Ren98fa87e2022-05-04 08:10:29 -0700935 uint8_t cs = calculateChecksum(readBuf.cbegin(), readBuf.cend());
936 if (cs == 0)
937 {
938 std::copy(++readBuf.begin(), --readBuf.end(), ethData.data());
939 return true;
940 }
Zhikui Renad129c62022-04-05 20:11:24 -0700941 }
942 }
943 return false;
944}
945
946ipmi::Cc writeMacToFru(ipmi::Context::ptr ctx, uint8_t macIndex,
947 std::array<uint8_t, maxEthSize>& ethData)
948{
Zhikui Renad129c62022-04-05 20:11:24 -0700949 uint64_t macOffset = fruEnd;
950 uint64_t fruBus = 0;
951 uint64_t fruAddress = 0;
952
953 if (findFruDevice(ctx->bus, ctx->yield, macOffset, fruBus, fruAddress))
954 {
955 phosphor::logging::log<phosphor::logging::level::INFO>(
956 "Found mac fru",
957 phosphor::logging::entry("BUS=%d", static_cast<uint8_t>(fruBus)),
958 phosphor::logging::entry("ADDRESS=%d",
959 static_cast<uint8_t>(fruAddress)));
960
Zhikui Ren98fa87e2022-05-04 08:10:29 -0700961 if (macOffset % fruPageSize)
962 {
963 macOffset = (macOffset / fruPageSize + 1) * fruPageSize;
964 }
965 macOffset += macIndex * fruPageSize;
966 if ((macOffset + macRecordSize) > fruEnd)
Zhikui Renad129c62022-04-05 20:11:24 -0700967 {
968 phosphor::logging::log<phosphor::logging::level::ERR>(
969 "ERROR: write mac fru failed, offset invalid.");
970 return ipmi::ccParmOutOfRange;
971 }
972 std::vector<uint8_t> writeData;
Zhikui Ren98fa87e2022-05-04 08:10:29 -0700973 writeData.reserve(macRecordSize + 1); // include start location
974 writeData.push_back(static_cast<uint8_t>(macOffset));
975 writeData.push_back(macHeader);
Zhikui Renad129c62022-04-05 20:11:24 -0700976 std::for_each(ethData.cbegin(), ethData.cend(),
977 [&](uint8_t i) { writeData.push_back(i); });
Zhikui Ren98fa87e2022-05-04 08:10:29 -0700978 uint8_t macCheckSum =
979 calculateChecksum(++writeData.cbegin(), writeData.cend());
980 writeData.push_back(macCheckSum);
Zhikui Renad129c62022-04-05 20:11:24 -0700981
982 std::string i2cBus = "/dev/i2c-" + std::to_string(fruBus);
983 std::vector<uint8_t> readBuf;
984 ipmi::Cc ret =
985 ipmi::i2cWriteRead(i2cBus, fruAddress, writeData, readBuf);
Zhikui Ren98fa87e2022-05-04 08:10:29 -0700986
Zhikui Ren0408e792022-06-07 21:03:33 -0700987 // prepare for read to detect chip is write protected
988 writeData.resize(1);
989 readBuf.resize(maxEthSize + 1); // include macHeader
990
Zhikui Renad129c62022-04-05 20:11:24 -0700991 switch (ret)
992 {
993 case ipmi::ccSuccess:
Zhikui Ren98fa87e2022-05-04 08:10:29 -0700994 // Wait for internal write cycle to complete
995 // example: ATMEL 24c0x chip has Twr spec as 5ms
996
997 // Ideally we want yield wait, but currently following code
998 // crash with "thread not supported"
999 // boost::asio::deadline_timer timer(
1000 // boost::asio::get_associated_executor(ctx->yield),
1001 // boost::posix_time::seconds(1));
1002 // timer.async_wait(ctx->yield);
1003 // use usleep as temp WA
1004 usleep(5000);
1005 if (ipmi::i2cWriteRead(i2cBus, fruAddress, writeData,
1006 readBuf) == ipmi::ccSuccess)
Zhikui Renad129c62022-04-05 20:11:24 -07001007 {
1008 if (std::equal(ethData.begin(), ethData.end(),
Zhikui Ren98fa87e2022-05-04 08:10:29 -07001009 ++readBuf.begin())) // skip macHeader
Zhikui Renad129c62022-04-05 20:11:24 -07001010 {
1011 return ipmi::ccSuccess;
1012 }
1013 phosphor::logging::log<phosphor::logging::level::INFO>(
1014 "INFO: write mac fru verify failed, fru may be write "
1015 "protected.");
1016 }
1017 return ipmi::ccCommandNotAvailable;
Zhikui Ren0408e792022-06-07 21:03:33 -07001018 default:
1019 if (ipmi::i2cWriteRead(i2cBus, fruAddress, writeData,
1020 readBuf) == ipmi::ccSuccess)
1021 {
1022 phosphor::logging::log<phosphor::logging::level::INFO>(
1023 "INFO: write mac fru failed, but successfully read "
1024 "from fru, fru may be write protected.");
1025 return ipmi::ccCommandNotAvailable;
1026 }
1027 else // assume failure is due to no eeprom on board
1028 {
1029 phosphor::logging::log<phosphor::logging::level::ERR>(
1030 "ERROR: write mac fru failed, assume no eeprom is "
1031 "available.");
1032 }
Zhikui Renad129c62022-04-05 20:11:24 -07001033 break;
1034 }
1035 }
1036 // no FRU eeprom found
1037 return ipmi::ccDestinationUnavailable;
1038}
1039
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +05301040ipmi::RspType<> setManufacturingData(ipmi::Context::ptr ctx, uint8_t dataType,
Richard Marian Thomaiyar1f0839c2019-08-25 20:10:52 +05301041 std::array<uint8_t, maxEthSize> ethData)
1042{
Zhikui Renad129c62022-04-05 20:11:24 -07001043 // mfg filter logic will restrict this command executing only in mfg
1044 // mode.
Richard Marian Thomaiyar1f0839c2019-08-25 20:10:52 +05301045 if (dataType >= maxSupportedEth)
1046 {
1047 return ipmi::responseParmOutOfRange();
1048 }
1049
1050 constexpr uint8_t invalidData = 0;
1051 constexpr uint8_t validData = 1;
Zhikui Renad129c62022-04-05 20:11:24 -07001052
1053 ipmi::Cc ret = writeMacToFru(ctx, dataType, ethData);
1054 if (ret != ipmi::ccDestinationUnavailable)
1055 {
1056 resetMtmTimer(ctx);
1057 return response(ret);
1058 }
1059
Richard Marian Thomaiyar1f0839c2019-08-25 20:10:52 +05301060 constexpr uint8_t ethAddrStrSize =
1061 19; // XX:XX:XX:XX:XX:XX + \n + null termination;
1062 std::vector<uint8_t> buff(ethAddrStrSize);
1063 std::snprintf(reinterpret_cast<char*>(buff.data()), ethAddrStrSize,
1064 "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx\n", ethData.at(0),
1065 ethData.at(1), ethData.at(2), ethData.at(3), ethData.at(4),
1066 ethData.at(5));
1067 std::ofstream oEthFile(factoryEthAddrBaseFileName +
1068 std::to_string(dataType),
1069 std::ofstream::out);
1070 if (!oEthFile.good())
1071 {
1072 return ipmi::responseUnspecifiedError();
1073 }
1074
1075 oEthFile << reinterpret_cast<char*>(buff.data());
Johnathan Mantey6d83e4e2020-05-08 11:01:01 -07001076 oEthFile.flush();
Richard Marian Thomaiyar1f0839c2019-08-25 20:10:52 +05301077 oEthFile.close();
1078
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +05301079 resetMtmTimer(ctx);
Richard Marian Thomaiyar1f0839c2019-08-25 20:10:52 +05301080 return ipmi::responseSuccess();
1081}
1082
1083ipmi::RspType<uint8_t, std::array<uint8_t, maxEthSize>>
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +05301084 getManufacturingData(ipmi::Context::ptr ctx, uint8_t dataType)
Richard Marian Thomaiyar1f0839c2019-08-25 20:10:52 +05301085{
Zhikui Renad129c62022-04-05 20:11:24 -07001086 // mfg filter logic will restrict this command executing only in mfg
1087 // mode.
Richard Marian Thomaiyar1f0839c2019-08-25 20:10:52 +05301088 if (dataType >= maxSupportedEth)
1089 {
1090 return ipmi::responseParmOutOfRange();
1091 }
1092 std::array<uint8_t, maxEthSize> ethData{0};
1093 constexpr uint8_t invalidData = 0;
1094 constexpr uint8_t validData = 1;
1095
1096 std::ifstream iEthFile(factoryEthAddrBaseFileName +
1097 std::to_string(dataType),
1098 std::ifstream::in);
1099 if (!iEthFile.good())
1100 {
Zhikui Renad129c62022-04-05 20:11:24 -07001101 if (readMacFromFru(ctx, dataType, ethData))
1102 {
1103 resetMtmTimer(ctx);
1104 return ipmi::responseSuccess(validData, ethData);
1105 }
Richard Marian Thomaiyar1f0839c2019-08-25 20:10:52 +05301106 return ipmi::responseSuccess(invalidData, ethData);
1107 }
1108 std::string ethStr;
1109 iEthFile >> ethStr;
1110 uint8_t* data = ethData.data();
1111 std::sscanf(ethStr.c_str(), "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx",
1112 data, (data + 1), (data + 2), (data + 3), (data + 4),
1113 (data + 5));
1114
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +05301115 resetMtmTimer(ctx);
Richard Marian Thomaiyar1f0839c2019-08-25 20:10:52 +05301116 return ipmi::responseSuccess(validData, ethData);
1117}
1118
Zhikui Renad129c62022-04-05 20:11:24 -07001119/** @brief implements slot master write read IPMI command which can be used
1120 * for low-level I2C/SMBus write, read or write-read access for PCIE slots
Yong Lif267a672019-08-29 11:16:07 +08001121 * @param reserved - skip 6 bit
1122 * @param addressType - address type
1123 * @param bbSlotNum - baseboard slot number
1124 * @param riserSlotNum - riser slot number
1125 * @param reserved2 - skip 2 bit
1126 * @param slaveAddr - slave address
1127 * @param readCount - number of bytes to be read
1128 * @param writeData - data to be written
1129 *
1130 * @returns IPMI completion code plus response data
1131 */
1132ipmi::RspType<std::vector<uint8_t>>
1133 appSlotI2CMasterWriteRead(uint6_t reserved, uint2_t addressType,
1134 uint3_t bbSlotNum, uint3_t riserSlotNum,
Vernon Maueryfc3bc382022-06-02 13:52:04 -07001135 uint2_t reserved2, uint8_t slaveAddr,
Yong Lif267a672019-08-29 11:16:07 +08001136 uint8_t readCount, std::vector<uint8_t> writeData)
1137{
Vernon Maueryfc3bc382022-06-02 13:52:04 -07001138 if (reserved || reserved2)
1139 {
1140 return ipmi::responseInvalidFieldRequest();
1141 }
Yong Lif267a672019-08-29 11:16:07 +08001142 const size_t writeCount = writeData.size();
1143 std::string i2cBus;
1144 if (addressType == slotAddressTypeBus)
1145 {
1146 std::string path = "/dev/i2c-mux/Riser_" +
1147 std::to_string(static_cast<uint8_t>(bbSlotNum)) +
1148 "_Mux/Pcie_Slot_" +
1149 std::to_string(static_cast<uint8_t>(riserSlotNum));
1150
1151 if (std::filesystem::exists(path) && std::filesystem::is_symlink(path))
1152 {
1153 i2cBus = std::filesystem::read_symlink(path);
1154 }
1155 else
1156 {
1157 phosphor::logging::log<phosphor::logging::level::ERR>(
1158 "Master write read command: Cannot get BusID");
1159 return ipmi::responseInvalidFieldRequest();
1160 }
1161 }
1162 else if (addressType == slotAddressTypeUniqueid)
1163 {
1164 i2cBus = "/dev/i2c-" +
1165 std::to_string(static_cast<uint8_t>(bbSlotNum) |
1166 (static_cast<uint8_t>(riserSlotNum) << 3));
1167 }
1168 else
1169 {
1170 phosphor::logging::log<phosphor::logging::level::ERR>(
1171 "Master write read command: invalid request");
1172 return ipmi::responseInvalidFieldRequest();
1173 }
1174
Zhikui Renad129c62022-04-05 20:11:24 -07001175 // Allow single byte write as it is offset byte to read the data, rest
1176 // allow only in Special mode.
Yong Lif267a672019-08-29 11:16:07 +08001177 if (writeCount > 1)
1178 {
Richard Marian Thomaiyarae13ac62019-12-17 15:45:12 +05301179 if (mtm.getMfgMode() == SpecialMode::none)
Yong Lif267a672019-08-29 11:16:07 +08001180 {
1181 return ipmi::responseInsufficientPrivilege();
1182 }
1183 }
1184
1185 if (readCount > slotI2CMaxReadSize)
1186 {
1187 phosphor::logging::log<phosphor::logging::level::ERR>(
1188 "Master write read command: Read count exceeds limit");
1189 return ipmi::responseParmOutOfRange();
1190 }
1191
1192 if (!readCount && !writeCount)
1193 {
1194 phosphor::logging::log<phosphor::logging::level::ERR>(
1195 "Master write read command: Read & write count are 0");
1196 return ipmi::responseInvalidFieldRequest();
1197 }
1198
1199 std::vector<uint8_t> readBuf(readCount);
1200
1201 ipmi::Cc retI2C = ipmi::i2cWriteRead(i2cBus, slaveAddr, writeData, readBuf);
1202 if (retI2C != ipmi::ccSuccess)
1203 {
1204 return ipmi::response(retI2C);
1205 }
1206
1207 return ipmi::responseSuccess(readBuf);
1208}
Yong Li068b4f22019-09-17 16:32:18 +08001209
1210ipmi::RspType<> clearCMOS()
1211{
Zhikui Renad129c62022-04-05 20:11:24 -07001212 // There is an i2c device on bus 4, the slave address is 0x38. Based on
1213 // the spec, writing 0x1 to address 0x61 on this device, will trigger
1214 // the clear CMOS action.
Yong Lid0d010b2019-11-18 12:15:21 +08001215 constexpr uint8_t slaveAddr = 0x38;
Yong Li068b4f22019-09-17 16:32:18 +08001216 std::string i2cBus = "/dev/i2c-4";
Yong Lieaeb6cb2020-03-09 20:21:50 +08001217 std::vector<uint8_t> writeData = {0x61, 0x1};
Yong Li068b4f22019-09-17 16:32:18 +08001218 std::vector<uint8_t> readBuf(0);
1219
Yong Li068b4f22019-09-17 16:32:18 +08001220 ipmi::Cc retI2C = ipmi::i2cWriteRead(i2cBus, slaveAddr, writeData, readBuf);
1221 return ipmi::response(retI2C);
1222}
Vernon Mauery27d23562021-08-12 10:43:39 -07001223
1224ipmi::RspType<> setFITcLayout(uint32_t layout)
1225{
1226 static constexpr const char* factoryFITcLayout =
1227 "/var/sofs/factory-settings/layout/fitc";
1228 std::filesystem::path fitcDir =
1229 std::filesystem::path(factoryFITcLayout).parent_path();
1230 std::error_code ec;
1231 std::filesystem::create_directories(fitcDir, ec);
1232 if (ec)
1233 {
1234 return ipmi::responseUnspecifiedError();
1235 }
1236 try
1237 {
1238 std::ofstream file(factoryFITcLayout);
1239 file << layout;
1240 file.flush();
1241 file.close();
1242 }
1243 catch (const std::exception& e)
1244 {
1245 return ipmi::responseUnspecifiedError();
1246 }
1247
1248 return ipmi::responseSuccess();
1249}
1250
Arun P. Mohanan06584cd2021-08-13 20:56:01 +05301251static std::vector<std::string>
1252 getMCTPServiceConfigPaths(ipmi::Context::ptr& ctx)
1253{
1254 boost::system::error_code ec;
1255 auto configPaths = ctx->bus->yield_method_call<std::vector<std::string>>(
1256 ctx->yield, ec, "xyz.openbmc_project.ObjectMapper",
1257 "/xyz/openbmc_project/object_mapper",
1258 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths",
1259 "/xyz/openbmc_project/inventory/system/board", 2,
1260 std::array<const char*, 1>{
1261 "xyz.openbmc_project.Configuration.MctpConfiguration"});
1262 if (ec)
1263 {
1264 throw std::runtime_error(
1265 "Failed to query configuration sub tree objects");
1266 }
1267 return configPaths;
1268}
1269
1270static ipmi::RspType<> startOrStopService(ipmi::Context::ptr& ctx,
1271 const uint8_t enable,
ANJALI RAY1fe485c2021-12-17 16:41:30 +00001272 const std::string& serviceName,
1273 bool disableOrEnableUnitFiles = true)
Arun P. Mohanan06584cd2021-08-13 20:56:01 +05301274{
1275 constexpr bool runtimeOnly = false;
1276 constexpr bool force = false;
1277
1278 boost::system::error_code ec;
1279 switch (enable)
1280 {
1281 case ipmi::SupportedFeatureActions::stop:
1282 ctx->bus->yield_method_call(ctx->yield, ec, systemDService,
1283 systemDObjPath, systemDMgrIntf,
1284 "StopUnit", serviceName, "replace");
1285 break;
1286 case ipmi::SupportedFeatureActions::start:
1287 ctx->bus->yield_method_call(ctx->yield, ec, systemDService,
1288 systemDObjPath, systemDMgrIntf,
1289 "StartUnit", serviceName, "replace");
1290 break;
1291 case ipmi::SupportedFeatureActions::disable:
ANJALI RAY1fe485c2021-12-17 16:41:30 +00001292 if (disableOrEnableUnitFiles == true)
1293 {
1294 ctx->bus->yield_method_call(
1295 ctx->yield, ec, systemDService, systemDObjPath,
1296 systemDMgrIntf, "DisableUnitFiles",
1297 std::array<const char*, 1>{serviceName.c_str()},
1298 runtimeOnly);
1299 }
Arun P. Mohanan06584cd2021-08-13 20:56:01 +05301300 ctx->bus->yield_method_call(
1301 ctx->yield, ec, systemDService, systemDObjPath, systemDMgrIntf,
1302 "MaskUnitFiles",
1303 std::array<const char*, 1>{serviceName.c_str()}, runtimeOnly,
1304 force);
Arun P. Mohanan06584cd2021-08-13 20:56:01 +05301305 break;
1306 case ipmi::SupportedFeatureActions::enable:
1307 ctx->bus->yield_method_call(
1308 ctx->yield, ec, systemDService, systemDObjPath, systemDMgrIntf,
1309 "UnmaskUnitFiles",
1310 std::array<const char*, 1>{serviceName.c_str()}, runtimeOnly);
ANJALI RAY1fe485c2021-12-17 16:41:30 +00001311 if (disableOrEnableUnitFiles == true)
1312 {
1313 ctx->bus->yield_method_call(
1314 ctx->yield, ec, systemDService, systemDObjPath,
1315 systemDMgrIntf, "EnableUnitFiles",
1316 std::array<const char*, 1>{serviceName.c_str()},
1317 runtimeOnly, force);
1318 }
Arun P. Mohanan06584cd2021-08-13 20:56:01 +05301319 break;
1320 default:
1321 phosphor::logging::log<phosphor::logging::level::WARNING>(
1322 "ERROR: Invalid feature action selected",
1323 phosphor::logging::entry("ACTION=%d", enable));
1324 return ipmi::responseInvalidFieldRequest();
1325 }
1326 if (ec)
1327 {
1328 phosphor::logging::log<phosphor::logging::level::WARNING>(
1329 "ERROR: Service start or stop failed",
1330 phosphor::logging::entry("SERVICE=%s", serviceName.c_str()));
1331 return ipmi::responseUnspecifiedError();
1332 }
1333 return ipmi::responseSuccess();
1334}
1335
1336static std::string getMCTPServiceName(const std::string& objectPath)
1337{
1338 const auto serviceArgument = boost::algorithm::replace_all_copy(
1339 boost::algorithm::replace_first_copy(
1340 objectPath, "/xyz/openbmc_project/inventory/system/board/", ""),
1341 "/", "_2f");
1342 std::string unitName =
1343 "xyz.openbmc_project.mctpd@" + serviceArgument + ".service";
1344 return unitName;
1345}
1346
1347static ipmi::RspType<> handleMCTPFeature(ipmi::Context::ptr& ctx,
1348 const uint8_t enable,
1349 const std::string& binding)
1350{
1351 std::vector<std::string> configPaths;
1352 try
1353 {
1354 configPaths = getMCTPServiceConfigPaths(ctx);
1355 }
1356 catch (const std::exception& e)
1357 {
1358 phosphor::logging::log<phosphor::logging::level::ERR>(e.what());
1359 return ipmi::responseUnspecifiedError();
1360 }
1361
1362 for (const auto& objectPath : configPaths)
1363 {
1364 auto const pos = objectPath.find_last_of('/');
1365 if (binding == objectPath.substr(pos + 1))
1366 {
1367 return startOrStopService(ctx, enable,
ANJALI RAY1fe485c2021-12-17 16:41:30 +00001368 getMCTPServiceName(objectPath), false);
Arun P. Mohanan06584cd2021-08-13 20:56:01 +05301369 }
1370 }
1371 return ipmi::responseSuccess();
1372}
1373
1374/** @brief implements MTM BMC Feature Control IPMI command which can be
1375 * used to enable or disable the supported BMC features.
1376 * @param yield - context object that represents the currently executing
1377 * coroutine
1378 * @param feature - feature enum to enable or disable
1379 * @param enable - enable or disable the feature
1380 * @param featureArg - custom arguments for that feature
1381 * @param reserved - reserved for future use
1382 *
1383 * @returns IPMI completion code
1384 */
1385ipmi::RspType<> mtmBMCFeatureControl(ipmi::Context::ptr ctx,
1386 const uint8_t feature,
1387 const uint8_t enable,
1388 const uint8_t featureArg,
1389 const uint16_t reserved)
1390{
1391 if (reserved != 0)
1392 {
1393 return ipmi::responseInvalidFieldRequest();
1394 }
1395
1396 switch (feature)
1397 {
1398 case ipmi::SupportedFeatureControls::mctp:
1399 switch (featureArg)
1400 {
1401 case ipmi::SupportedMCTPBindings::mctpPCIe:
1402 return handleMCTPFeature(ctx, enable, "MCTP_PCIe");
1403 case ipmi::SupportedMCTPBindings::mctpSMBusHSBP:
1404 return handleMCTPFeature(ctx, enable, "MCTP_SMBus_HSBP");
1405 case ipmi::SupportedMCTPBindings::mctpSMBusPCIeSlot:
1406 return handleMCTPFeature(ctx, enable,
1407 "MCTP_SMBus_PCIe_slot");
1408 default:
1409 return ipmi::responseInvalidFieldRequest();
1410 }
1411 break;
Jason M. Bills5cb2c042021-08-17 12:03:39 -07001412 case ipmi::SupportedFeatureControls::pcieScan:
1413 if (featureArg != 0)
1414 {
1415 return ipmi::responseInvalidFieldRequest();
1416 }
1417 startOrStopService(ctx, enable, "xyz.openbmc_project.PCIe.service");
1418 break;
Arun P. Mohanan06584cd2021-08-13 20:56:01 +05301419 default:
1420 return ipmi::responseInvalidFieldRequest();
1421 }
1422 return ipmi::responseSuccess();
1423}
Vernon Mauerya3702c12019-05-22 13:20:59 -07001424} // namespace ipmi
1425
1426void register_mtm_commands() __attribute__((constructor));
1427void register_mtm_commands()
1428{
Jason M. Bills38d2b5a2019-06-03 16:26:11 -07001429 // <Get SM Signal>
Vernon Mauery98bbf692019-09-16 11:14:59 -07001430 ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnGeneral,
1431 ipmi::intel::general::cmdGetSmSignal,
1432 ipmi::Privilege::Admin, ipmi::appMTMGetSignal);
Vernon Mauerya3702c12019-05-22 13:20:59 -07001433
Vernon Mauery98bbf692019-09-16 11:14:59 -07001434 ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnGeneral,
1435 ipmi::intel::general::cmdSetSmSignal,
1436 ipmi::Privilege::Admin, ipmi::appMTMSetSignal);
Vernon Mauerya3702c12019-05-22 13:20:59 -07001437
Vernon Mauery98bbf692019-09-16 11:14:59 -07001438 ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnGeneral,
1439 ipmi::intel::general::cmdMtmKeepAlive,
1440 ipmi::Privilege::Admin, ipmi::mtmKeepAlive);
Richard Marian Thomaiyar666dd012019-08-02 20:55:37 +05301441
Vernon Mauery98bbf692019-09-16 11:14:59 -07001442 ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnGeneral,
1443 ipmi::intel::general::cmdSetManufacturingData,
1444 ipmi::Privilege::Admin, ipmi::setManufacturingData);
Richard Marian Thomaiyar1f0839c2019-08-25 20:10:52 +05301445
Vernon Mauery98bbf692019-09-16 11:14:59 -07001446 ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnGeneral,
1447 ipmi::intel::general::cmdGetManufacturingData,
1448 ipmi::Privilege::Admin, ipmi::getManufacturingData);
Richard Marian Thomaiyar1f0839c2019-08-25 20:10:52 +05301449
Vernon Mauery27d23562021-08-12 10:43:39 -07001450 ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnGeneral,
1451 ipmi::intel::general::cmdSetFITcLayout,
1452 ipmi::Privilege::Admin, ipmi::setFITcLayout);
1453
Arun P. Mohanan06584cd2021-08-13 20:56:01 +05301454 ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnGeneral,
1455 ipmi::intel::general::cmdMTMBMCFeatureControl,
1456 ipmi::Privilege::Admin, ipmi::mtmBMCFeatureControl);
1457
Vernon Mauery98bbf692019-09-16 11:14:59 -07001458 ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnApp,
1459 ipmi::intel::general::cmdSlotI2CMasterWriteRead,
1460 ipmi::Privilege::Admin,
1461 ipmi::appSlotI2CMasterWriteRead);
Yong Lif267a672019-08-29 11:16:07 +08001462
Yong Li068b4f22019-09-17 16:32:18 +08001463 ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnPlatform,
1464 ipmi::intel::platform::cmdClearCMOS,
1465 ipmi::Privilege::Admin, ipmi::clearCMOS);
1466
Vernon Mauery98bbf692019-09-16 11:14:59 -07001467 ipmi::registerFilter(ipmi::prioOemBase,
Yong Li85feb132019-08-09 16:06:03 +08001468 [](ipmi::message::Request::ptr request) {
1469 return ipmi::mfgFilterMessage(request);
1470 });
Vernon Mauerya3702c12019-05-22 13:20:59 -07001471}