blob: 42185a74e5741335cd3c727b4538d27cb0774a68 [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>
24
James Feistfcd2d3a2020-05-28 10:38:15 -070025#include <filesystem>
26#include <fstream>
27
Vernon Mauerya3702c12019-05-22 13:20:59 -070028namespace ipmi
29{
30
31Manufacturing mtm;
32
33static auto revertTimeOut =
34 std::chrono::duration_cast<std::chrono::microseconds>(
35 std::chrono::seconds(60)); // 1 minute timeout
36
Yong Lif267a672019-08-29 11:16:07 +080037static constexpr uint8_t slotAddressTypeBus = 0;
38static constexpr uint8_t slotAddressTypeUniqueid = 1;
39static constexpr uint8_t slotI2CMaxReadSize = 35;
40
Vernon Mauerya3702c12019-05-22 13:20:59 -070041static constexpr const char* callbackMgrService =
42 "xyz.openbmc_project.CallbackManager";
43static constexpr const char* callbackMgrIntf =
44 "xyz.openbmc_project.CallbackManager";
45static constexpr const char* callbackMgrObjPath =
46 "/xyz/openbmc_project/CallbackManager";
47static constexpr const char* retriggerLedUpdate = "RetriggerLEDUpdate";
48
49const static constexpr char* systemDService = "org.freedesktop.systemd1";
50const static constexpr char* systemDObjPath = "/org/freedesktop/systemd1";
51const static constexpr char* systemDMgrIntf =
52 "org.freedesktop.systemd1.Manager";
53const static constexpr char* pidControlService = "phosphor-pid-control.service";
54
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +053055static inline Cc resetMtmTimer(ipmi::Context::ptr ctx)
Richard Marian Thomaiyar666dd012019-08-02 20:55:37 +053056{
Richard Marian Thomaiyar666dd012019-08-02 20:55:37 +053057 boost::system::error_code ec;
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +053058 ctx->bus->yield_method_call<>(ctx->yield, ec, specialModeService,
59 specialModeObjPath, specialModeIntf,
60 "ResetTimer");
Richard Marian Thomaiyar666dd012019-08-02 20:55:37 +053061 if (ec)
62 {
63 phosphor::logging::log<phosphor::logging::level::ERR>(
64 "Failed to reset the manufacturing mode timer");
65 return ccUnspecifiedError;
66 }
67 return ccSuccess;
68}
69
Jason M. Bills38d2b5a2019-06-03 16:26:11 -070070int getGpioPathForSmSignal(const SmSignalGet signal, std::string& path)
Vernon Mauerya3702c12019-05-22 13:20:59 -070071{
Jason M. Bills38d2b5a2019-06-03 16:26:11 -070072 switch (signal)
73 {
74 case SmSignalGet::smPowerButton:
75 path = "/xyz/openbmc_project/chassis/buttons/power";
76 break;
77 case SmSignalGet::smResetButton:
78 path = "/xyz/openbmc_project/chassis/buttons/reset";
79 break;
80 case SmSignalGet::smNMIButton:
81 path = "/xyz/openbmc_project/chassis/buttons/nmi";
82 break;
Richard Marian Thomaiyar8e5e2b02019-08-01 07:50:55 +053083 case SmSignalGet::smIdentifyButton:
84 path = "/xyz/openbmc_project/chassis/buttons/id";
85 break;
Jason M. Bills38d2b5a2019-06-03 16:26:11 -070086 default:
87 return -1;
88 break;
89 }
90 return 0;
Vernon Mauerya3702c12019-05-22 13:20:59 -070091}
92
Patrick Venture37890392019-09-25 17:05:03 -070093ipmi_ret_t ledStoreAndSet(SmSignalSet signal, const std::string& setState)
Vernon Mauerya3702c12019-05-22 13:20:59 -070094{
95 LedProperty* ledProp = mtm.findLedProperty(signal);
96 if (ledProp == nullptr)
97 {
98 return IPMI_CC_INVALID_FIELD_REQUEST;
99 }
100
101 std::string ledName = ledProp->getName();
102 std::string ledService = ledServicePrefix + ledName;
103 std::string ledPath = ledPathPrefix + ledName;
104 ipmi::Value presentState;
105
106 if (false == ledProp->getLock())
107 {
108 if (mtm.getProperty(ledService.c_str(), ledPath.c_str(), ledIntf,
109 "State", &presentState) != 0)
110 {
111 return IPMI_CC_UNSPECIFIED_ERROR;
112 }
113 ledProp->setPrevState(std::get<std::string>(presentState));
114 ledProp->setLock(true);
115 if (signal == SmSignalSet::smPowerFaultLed ||
116 signal == SmSignalSet::smSystemReadyLed)
117 {
118 mtm.revertLedCallback = true;
119 }
120 }
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700121 if (mtm.setProperty(ledService, ledPath, ledIntf, "State",
Vernon Mauerya3702c12019-05-22 13:20:59 -0700122 ledStateStr + setState) != 0)
123 {
124 return IPMI_CC_UNSPECIFIED_ERROR;
125 }
126 return IPMI_CC_OK;
127}
128
129ipmi_ret_t ledRevert(SmSignalSet signal)
130{
131 LedProperty* ledProp = mtm.findLedProperty(signal);
132 if (ledProp == nullptr)
133 {
134 return IPMI_CC_INVALID_FIELD_REQUEST;
135 }
136 if (true == ledProp->getLock())
137 {
138 ledProp->setLock(false);
139 if (signal == SmSignalSet::smPowerFaultLed ||
140 signal == SmSignalSet::smSystemReadyLed)
141 {
142 try
143 {
144 ipmi::method_no_args::callDbusMethod(
145 *getSdBus(), callbackMgrService, callbackMgrObjPath,
146 callbackMgrIntf, retriggerLedUpdate);
147 }
Patrick Williamsbd51e6a2021-10-06 13:09:44 -0500148 catch (const sdbusplus::exception_t& e)
Vernon Mauerya3702c12019-05-22 13:20:59 -0700149 {
150 return IPMI_CC_UNSPECIFIED_ERROR;
151 }
152 mtm.revertLedCallback = false;
153 }
154 else
155 {
156 std::string ledName = ledProp->getName();
157 std::string ledService = ledServicePrefix + ledName;
158 std::string ledPath = ledPathPrefix + ledName;
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700159 if (mtm.setProperty(ledService, ledPath, ledIntf, "State",
160 ledProp->getPrevState()) != 0)
Vernon Mauerya3702c12019-05-22 13:20:59 -0700161 {
162 return IPMI_CC_UNSPECIFIED_ERROR;
163 }
164 }
165 }
166 return IPMI_CC_OK;
167}
168
169void Manufacturing::initData()
170{
Vernon Mauerya3702c12019-05-22 13:20:59 -0700171 ledPropertyList.push_back(
172 LedProperty(SmSignalSet::smPowerFaultLed, "status_amber"));
173 ledPropertyList.push_back(
174 LedProperty(SmSignalSet::smSystemReadyLed, "status_green"));
175 ledPropertyList.push_back(
176 LedProperty(SmSignalSet::smIdentifyLed, "identify"));
177}
178
179void Manufacturing::revertTimerHandler()
180{
Richard Marian Thomaiyarae13ac62019-12-17 15:45:12 +0530181
182#ifdef BMC_VALIDATION_UNSECURE_FEATURE
183 if (mtm.getMfgMode() == SpecialMode::valUnsecure)
184 {
185 // Don't revert the behaviour for validation unsecure mode.
186 return;
187 }
188#endif
Vernon Mauerya3702c12019-05-22 13:20:59 -0700189 if (revertFanPWM)
190 {
191 revertFanPWM = false;
192 disablePidControlService(false);
193 }
194
Ayushi Smritid872a4a2019-10-15 10:20:11 +0530195 if (mtmTestBeepFd != -1)
196 {
197 ::close(mtmTestBeepFd);
198 mtmTestBeepFd = -1;
199 }
200
Vernon Mauerya3702c12019-05-22 13:20:59 -0700201 for (const auto& ledProperty : ledPropertyList)
202 {
203 const std::string& ledName = ledProperty.getName();
Jayaprakash Mutyalaf3656142021-01-24 01:04:19 +0000204 if (ledName == "identify" && mtm.getMfgMode() == SpecialMode::mfg)
205 {
206 // Don't revert the behaviour for manufacturing mode
207 continue;
208 }
Vernon Mauerya3702c12019-05-22 13:20:59 -0700209 ledRevert(ledProperty.getSignal());
210 }
211}
212
213Manufacturing::Manufacturing() :
214 revertTimer([&](void) { revertTimerHandler(); })
215{
216 initData();
217}
218
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700219int8_t Manufacturing::getProperty(const std::string& service,
220 const std::string& path,
221 const std::string& interface,
222 const std::string& propertyName,
223 ipmi::Value* reply)
Vernon Mauerya3702c12019-05-22 13:20:59 -0700224{
225 try
226 {
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700227 *reply = ipmi::getDbusProperty(*getSdBus(), service, path, interface,
228 propertyName);
Vernon Mauerya3702c12019-05-22 13:20:59 -0700229 }
Patrick Williamsf944d2e2022-07-22 19:26:52 -0500230 catch (const sdbusplus::exception_t& e)
Vernon Mauerya3702c12019-05-22 13:20:59 -0700231 {
232 phosphor::logging::log<phosphor::logging::level::INFO>(
233 "ERROR: getProperty");
234 return -1;
235 }
236
237 return 0;
238}
239
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700240int8_t Manufacturing::setProperty(const std::string& service,
241 const std::string& path,
242 const std::string& interface,
243 const std::string& propertyName,
244 ipmi::Value value)
Vernon Mauerya3702c12019-05-22 13:20:59 -0700245{
246 try
247 {
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700248 ipmi::setDbusProperty(*getSdBus(), service, path, interface,
Vernon Mauerya3702c12019-05-22 13:20:59 -0700249 propertyName, value);
250 }
Patrick Williamsf944d2e2022-07-22 19:26:52 -0500251 catch (const sdbusplus::exception_t& e)
Vernon Mauerya3702c12019-05-22 13:20:59 -0700252 {
253 phosphor::logging::log<phosphor::logging::level::INFO>(
254 "ERROR: setProperty");
255 return -1;
256 }
257
258 return 0;
259}
260
261int8_t Manufacturing::disablePidControlService(const bool disable)
262{
263 try
264 {
265 auto dbus = getSdBus();
266 auto method = dbus->new_method_call(systemDService, systemDObjPath,
267 systemDMgrIntf,
268 disable ? "StopUnit" : "StartUnit");
269 method.append(pidControlService, "replace");
270 auto reply = dbus->call(method);
271 }
Patrick Williamsf944d2e2022-07-22 19:26:52 -0500272 catch (const sdbusplus::exception_t& e)
Vernon Mauerya3702c12019-05-22 13:20:59 -0700273 {
274 phosphor::logging::log<phosphor::logging::level::INFO>(
275 "ERROR: phosphor-pid-control service start or stop failed");
276 return -1;
277 }
278 return 0;
279}
280
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700281ipmi::RspType<uint8_t, // Signal value
282 std::optional<uint16_t> // Fan tach value
283 >
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +0530284 appMTMGetSignal(ipmi::Context::ptr ctx, uint8_t signalTypeByte,
Richard Marian Thomaiyar147daec2019-06-15 07:43:48 +0530285 uint8_t instance, uint8_t actionByte)
Vernon Mauerya3702c12019-05-22 13:20:59 -0700286{
Ayushi Smritie0511e52019-08-27 17:30:34 +0000287 // mfg filter logic is used to allow MTM get signal command only in
288 // manfacturing mode.
Vernon Mauerya3702c12019-05-22 13:20:59 -0700289
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700290 SmSignalGet signalType = static_cast<SmSignalGet>(signalTypeByte);
291 SmActionGet action = static_cast<SmActionGet>(actionByte);
292
293 switch (signalType)
294 {
anil kumar appana98705b32019-09-11 14:15:50 +0000295 case SmSignalGet::smChassisIntrusion:
296 {
297 ipmi::Value reply;
298 if (mtm.getProperty(intrusionService, intrusionPath, intrusionIntf,
299 "Status", &reply) < 0)
300 {
301 return ipmi::responseInvalidFieldRequest();
302 }
303 std::string* intrusionStatus = std::get_if<std::string>(&reply);
304 if (!intrusionStatus)
305 {
306 return ipmi::responseUnspecifiedError();
307 }
308
309 uint8_t status = 0;
310 if (!intrusionStatus->compare("Normal"))
311 {
312 status = static_cast<uint8_t>(IntrusionStatus::normal);
313 }
314 else if (!intrusionStatus->compare("HardwareIntrusion"))
315 {
316 status =
317 static_cast<uint8_t>(IntrusionStatus::hardwareIntrusion);
318 }
319 else if (!intrusionStatus->compare("TamperingDetected"))
320 {
321 status =
322 static_cast<uint8_t>(IntrusionStatus::tamperingDetected);
323 }
324 else
325 {
326 return ipmi::responseUnspecifiedError();
327 }
328 return ipmi::responseSuccess(status, std::nullopt);
329 }
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700330 case SmSignalGet::smFanPwmGet:
331 {
332 ipmi::Value reply;
Richard Marian Thomaiyar147daec2019-06-15 07:43:48 +0530333 std::string fullPath = fanPwmPath + std::to_string(instance + 1);
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700334 if (mtm.getProperty(fanService, fullPath, fanIntf, "Value",
335 &reply) < 0)
336 {
337 return ipmi::responseInvalidFieldRequest();
338 }
339 double* doubleVal = std::get_if<double>(&reply);
340 if (doubleVal == nullptr)
341 {
342 return ipmi::responseUnspecifiedError();
343 }
344 uint8_t sensorVal = std::round(*doubleVal);
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +0530345 resetMtmTimer(ctx);
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700346 return ipmi::responseSuccess(sensorVal, std::nullopt);
347 }
348 break;
349 case SmSignalGet::smFanTachometerGet:
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700350 {
Richard Marian Thomaiyar147daec2019-06-15 07:43:48 +0530351 boost::system::error_code ec;
352 using objFlatMap = boost::container::flat_map<
353 std::string, boost::container::flat_map<
354 std::string, std::vector<std::string>>>;
355
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +0530356 auto flatMap = ctx->bus->yield_method_call<objFlatMap>(
357 ctx->yield, ec, "xyz.openbmc_project.ObjectMapper",
Richard Marian Thomaiyar147daec2019-06-15 07:43:48 +0530358 "/xyz/openbmc_project/object_mapper",
359 "xyz.openbmc_project.ObjectMapper", "GetSubTree",
360 fanTachBasePath, 0, std::array<const char*, 1>{fanIntf});
361 if (ec)
362 {
363 phosphor::logging::log<phosphor::logging::level::ERR>(
364 "Failed to query fan tach sub tree objects");
365 return ipmi::responseUnspecifiedError();
366 }
367 if (instance >= flatMap.size())
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700368 {
369 return ipmi::responseInvalidFieldRequest();
370 }
Richard Marian Thomaiyar147daec2019-06-15 07:43:48 +0530371 auto itr = flatMap.nth(instance);
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700372 ipmi::Value reply;
Richard Marian Thomaiyar147daec2019-06-15 07:43:48 +0530373 if (mtm.getProperty(fanService, itr->first, fanIntf, "Value",
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700374 &reply) < 0)
375 {
376 return ipmi::responseInvalidFieldRequest();
377 }
378
379 double* doubleVal = std::get_if<double>(&reply);
380 if (doubleVal == nullptr)
381 {
382 return ipmi::responseUnspecifiedError();
383 }
384 uint8_t sensorVal = FAN_PRESENT | FAN_SENSOR_PRESENT;
385 std::optional<uint16_t> fanTach = std::round(*doubleVal);
386
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +0530387 resetMtmTimer(ctx);
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700388 return ipmi::responseSuccess(sensorVal, fanTach);
389 }
390 break;
Richard Marian Thomaiyar8e5e2b02019-08-01 07:50:55 +0530391 case SmSignalGet::smIdentifyButton:
392 {
393 if (action == SmActionGet::revert || action == SmActionGet::ignore)
394 {
395 // ButtonMasked property is not supported for ID button as it is
396 // unnecessary. Hence if requested for revert / ignore, override
397 // it to sample action to make tools happy.
398 action = SmActionGet::sample;
399 }
400 // fall-through
401 }
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700402 case SmSignalGet::smResetButton:
403 case SmSignalGet::smPowerButton:
404 case SmSignalGet::smNMIButton:
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700405 {
406 std::string path;
407 if (getGpioPathForSmSignal(signalType, path) < 0)
408 {
409 return ipmi::responseInvalidFieldRequest();
410 }
411
412 switch (action)
413 {
414 case SmActionGet::sample:
415 phosphor::logging::log<phosphor::logging::level::INFO>(
416 "case SmActionGet::sample");
417 break;
418 case SmActionGet::ignore:
419 {
420 phosphor::logging::log<phosphor::logging::level::INFO>(
421 "case SmActionGet::ignore");
422 if (mtm.setProperty(buttonService, path, buttonIntf,
423 "ButtonMasked", true) < 0)
424 {
425 return ipmi::responseUnspecifiedError();
426 }
427 }
428 break;
429 case SmActionGet::revert:
430 {
431 phosphor::logging::log<phosphor::logging::level::INFO>(
432 "case SmActionGet::revert");
433 if (mtm.setProperty(buttonService, path, buttonIntf,
434 "ButtonMasked", false) < 0)
435 {
436 return ipmi::responseUnspecifiedError();
437 }
438 }
439 break;
440
441 default:
442 return ipmi::responseInvalidFieldRequest();
443 break;
444 }
445
446 ipmi::Value reply;
447 if (mtm.getProperty(buttonService, path, buttonIntf,
448 "ButtonPressed", &reply) < 0)
449 {
450 return ipmi::responseUnspecifiedError();
451 }
452 bool* valPtr = std::get_if<bool>(&reply);
453 if (valPtr == nullptr)
454 {
455 return ipmi::responseUnspecifiedError();
456 }
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +0530457 resetMtmTimer(ctx);
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700458 uint8_t sensorVal = *valPtr;
459 return ipmi::responseSuccess(sensorVal, std::nullopt);
460 }
461 break;
Richard Marian Thomaiyar1b74a212019-08-03 13:26:17 +0530462 case SmSignalGet::smNcsiDiag:
463 {
464 constexpr const char* netBasePath = "/sys/class/net/eth";
465 constexpr const char* carrierSuffix = "/carrier";
466 std::ifstream netIfs(netBasePath + std::to_string(instance) +
467 carrierSuffix);
468 if (!netIfs.good())
469 {
470 return ipmi::responseInvalidFieldRequest();
471 }
472 std::string carrier;
473 netIfs >> carrier;
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +0530474 resetMtmTimer(ctx);
Richard Marian Thomaiyar1b74a212019-08-03 13:26:17 +0530475 return ipmi::responseSuccess(
476 static_cast<uint8_t>(std::stoi(carrier)), std::nullopt);
477 }
478 break;
Vernon Mauerya3702c12019-05-22 13:20:59 -0700479 default:
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700480 return ipmi::responseInvalidFieldRequest();
Vernon Mauerya3702c12019-05-22 13:20:59 -0700481 break;
482 }
Vernon Mauerya3702c12019-05-22 13:20:59 -0700483}
484
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +0530485ipmi::RspType<> appMTMSetSignal(ipmi::Context::ptr ctx, uint8_t signalTypeByte,
486 uint8_t instance, uint8_t actionByte,
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000487 std::optional<uint8_t> pwmSpeed)
Vernon Mauerya3702c12019-05-22 13:20:59 -0700488{
Ayushi Smritie0511e52019-08-27 17:30:34 +0000489 // mfg filter logic is used to allow MTM set signal command only in
490 // manfacturing mode.
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000491
492 SmSignalSet signalType = static_cast<SmSignalSet>(signalTypeByte);
493 SmActionSet action = static_cast<SmActionSet>(actionByte);
494 Cc retCode = ccSuccess;
495 int8_t ret = 0;
496
497 switch (signalType)
498 {
499 case SmSignalSet::smPowerFaultLed:
500 case SmSignalSet::smSystemReadyLed:
501 case SmSignalSet::smIdentifyLed:
502 switch (action)
503 {
504 case SmActionSet::forceDeasserted:
Vernon Mauerya3702c12019-05-22 13:20:59 -0700505 {
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000506 phosphor::logging::log<phosphor::logging::level::INFO>(
507 "case SmActionSet::forceDeasserted");
Vernon Mauerya3702c12019-05-22 13:20:59 -0700508
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000509 retCode = ledStoreAndSet(signalType, std::string("Off"));
510 if (retCode != ccSuccess)
Vernon Mauerya3702c12019-05-22 13:20:59 -0700511 {
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000512 return ipmi::response(retCode);
Vernon Mauerya3702c12019-05-22 13:20:59 -0700513 }
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000514 mtm.revertTimer.start(revertTimeOut);
Vernon Mauerya3702c12019-05-22 13:20:59 -0700515 }
516 break;
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000517 case SmActionSet::forceAsserted:
Vernon Mauerya3702c12019-05-22 13:20:59 -0700518 {
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000519 phosphor::logging::log<phosphor::logging::level::INFO>(
520 "case SmActionSet::forceAsserted");
Vernon Mauerya3702c12019-05-22 13:20:59 -0700521
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000522 retCode = ledStoreAndSet(signalType, std::string("On"));
523 if (retCode != ccSuccess)
524 {
525 return ipmi::response(retCode);
526 }
527 mtm.revertTimer.start(revertTimeOut);
528 if (SmSignalSet::smPowerFaultLed == signalType)
529 {
530 // Deassert "system ready"
531 retCode = ledStoreAndSet(SmSignalSet::smSystemReadyLed,
532 std::string("Off"));
533 }
534 else if (SmSignalSet::smSystemReadyLed == signalType)
535 {
536 // Deassert "fault led"
537 retCode = ledStoreAndSet(SmSignalSet::smPowerFaultLed,
538 std::string("Off"));
539 }
540 }
541 break;
542 case SmActionSet::revert:
543 {
544 phosphor::logging::log<phosphor::logging::level::INFO>(
545 "case SmActionSet::revert");
546 retCode = ledRevert(signalType);
547 }
548 break;
549 default:
550 {
551 return ipmi::responseInvalidFieldRequest();
552 }
553 }
554 break;
555 case SmSignalSet::smFanPowerSpeed:
556 {
557 if ((action == SmActionSet::forceAsserted) && (!pwmSpeed))
558 {
559 return ipmi::responseReqDataLenInvalid();
560 }
561
562 if ((action == SmActionSet::forceAsserted) && (*pwmSpeed > 100))
563 {
564 return ipmi::responseInvalidFieldRequest();
565 }
566
567 uint8_t pwmValue = 0;
568 switch (action)
569 {
570 case SmActionSet::revert:
571 {
572 if (mtm.revertFanPWM)
573 {
574 ret = mtm.disablePidControlService(false);
Vernon Mauerya3702c12019-05-22 13:20:59 -0700575 if (ret < 0)
576 {
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000577 return ipmi::responseUnspecifiedError();
Vernon Mauerya3702c12019-05-22 13:20:59 -0700578 }
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000579 mtm.revertFanPWM = false;
Vernon Mauerya3702c12019-05-22 13:20:59 -0700580 }
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000581 }
582 break;
583 case SmActionSet::forceAsserted:
584 {
585 pwmValue = *pwmSpeed;
586 } // fall-through
587 case SmActionSet::forceDeasserted:
588 {
589 if (!mtm.revertFanPWM)
Vernon Mauerya3702c12019-05-22 13:20:59 -0700590 {
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000591 ret = mtm.disablePidControlService(true);
592 if (ret < 0)
593 {
594 return ipmi::responseUnspecifiedError();
595 }
596 mtm.revertFanPWM = true;
Vernon Mauerya3702c12019-05-22 13:20:59 -0700597 }
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000598 mtm.revertTimer.start(revertTimeOut);
599 std::string fanPwmInstancePath =
600 fanPwmPath + std::to_string(instance + 1);
601
602 ret =
603 mtm.setProperty(fanService, fanPwmInstancePath, fanIntf,
604 "Value", static_cast<double>(pwmValue));
605 if (ret < 0)
606 {
607 return ipmi::responseUnspecifiedError();
608 }
609 }
610 break;
611 default:
612 {
613 return ipmi::responseInvalidFieldRequest();
Vernon Mauerya3702c12019-05-22 13:20:59 -0700614 }
615 }
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000616 }
617 break;
Ayushi Smritid872a4a2019-10-15 10:20:11 +0530618 case SmSignalSet::smSpeaker:
619 {
620 phosphor::logging::log<phosphor::logging::level::INFO>(
621 "Performing Speaker SmActionSet",
622 phosphor::logging::entry("ACTION=%d",
623 static_cast<uint8_t>(action)));
624 switch (action)
625 {
626 case SmActionSet::forceAsserted:
627 {
628 char beepDevName[] = "/dev/input/event0";
629 if (mtm.mtmTestBeepFd != -1)
630 {
631 phosphor::logging::log<phosphor::logging::level::INFO>(
632 "mtm beep device is opened already!");
633 // returning success as already beep is in progress
634 return ipmi::response(retCode);
635 }
636
637 if ((mtm.mtmTestBeepFd =
638 ::open(beepDevName, O_RDWR | O_CLOEXEC)) < 0)
639 {
640 phosphor::logging::log<phosphor::logging::level::ERR>(
641 "Failed to open input device");
642 return ipmi::responseUnspecifiedError();
643 }
644
645 struct input_event event;
646 event.type = EV_SND;
647 event.code = SND_TONE;
648 event.value = 2000;
649
650 if (::write(mtm.mtmTestBeepFd, &event,
651 sizeof(struct input_event)) !=
652 sizeof(struct input_event))
653 {
654 phosphor::logging::log<phosphor::logging::level::ERR>(
655 "Failed to write a tone sound event");
656 ::close(mtm.mtmTestBeepFd);
657 mtm.mtmTestBeepFd = -1;
658 return ipmi::responseUnspecifiedError();
659 }
660 mtm.revertTimer.start(revertTimeOut);
661 }
662 break;
663 case SmActionSet::revert:
664 case SmActionSet::forceDeasserted:
665 {
666 if (mtm.mtmTestBeepFd != -1)
667 {
668 ::close(mtm.mtmTestBeepFd);
669 mtm.mtmTestBeepFd = -1;
670 }
671 }
672 break;
673 default:
674 {
675 return ipmi::responseInvalidFieldRequest();
676 }
677 }
678 }
679 break;
Richard Marian Thomaiyar3594c6d2019-11-19 21:29:04 +0530680 case SmSignalSet::smDiskFaultLed:
681 {
682 boost::system::error_code ec;
683 using objPaths = std::vector<std::string>;
684 std::string driveBasePath =
685 "/xyz/openbmc_project/inventory/item/drive/";
686 static constexpr const char* driveLedIntf =
687 "xyz.openbmc_project.Led.Group";
688 static constexpr const char* hsbpService =
689 "xyz.openbmc_project.HsbpManager";
690
691 auto driveList = ctx->bus->yield_method_call<objPaths>(
692 ctx->yield, ec, "xyz.openbmc_project.ObjectMapper",
693 "/xyz/openbmc_project/object_mapper",
694 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths",
695 driveBasePath, 0, std::array<const char*, 1>{driveLedIntf});
696 if (ec)
697 {
698 phosphor::logging::log<phosphor::logging::level::ERR>(
699 "Failed to query HSBP drive sub tree objects");
700 return ipmi::responseUnspecifiedError();
701 }
702 std::string driveObjPath =
703 driveBasePath + "Drive_" + std::to_string(instance + 1);
704 if (std::find(driveList.begin(), driveList.end(), driveObjPath) ==
705 driveList.end())
706 {
707 return ipmi::responseInvalidFieldRequest();
708 }
709 bool driveLedState = false;
710 switch (action)
711 {
712 case SmActionSet::forceAsserted:
713 {
714 driveLedState = true;
715 }
716 break;
717 case SmActionSet::revert:
718 {
719 driveLedState = false;
720 }
721 break;
722 case SmActionSet::forceDeasserted:
723 {
724 driveLedState = false;
725 }
726 break;
727 default:
728 {
729 return ipmi::responseInvalidFieldRequest();
730 }
731 }
732 ret = mtm.setProperty(hsbpService, driveObjPath, driveLedIntf,
733 "Asserted", driveLedState);
734 if (ret < 0)
735 {
736 return ipmi::responseUnspecifiedError();
737 }
738 }
739 break;
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000740 default:
741 {
742 return ipmi::responseInvalidFieldRequest();
Vernon Mauerya3702c12019-05-22 13:20:59 -0700743 }
744 }
Richard Marian Thomaiyar4cc10152019-08-02 23:21:45 +0530745 if (retCode == ccSuccess)
746 {
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +0530747 resetMtmTimer(ctx);
Richard Marian Thomaiyar4cc10152019-08-02 23:21:45 +0530748 }
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000749 return ipmi::response(retCode);
Vernon Mauerya3702c12019-05-22 13:20:59 -0700750}
751
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +0530752ipmi::RspType<> mtmKeepAlive(ipmi::Context::ptr ctx, uint8_t reserved,
Richard Marian Thomaiyar666dd012019-08-02 20:55:37 +0530753 const std::array<char, 5>& intentionalSignature)
754{
Ayushi Smritie0511e52019-08-27 17:30:34 +0000755 // mfg filter logic is used to allow MTM keep alive command only in
756 // manfacturing mode
757
Richard Marian Thomaiyar666dd012019-08-02 20:55:37 +0530758 constexpr std::array<char, 5> signatureOk = {'I', 'N', 'T', 'E', 'L'};
759 if (intentionalSignature != signatureOk || reserved != 0)
760 {
761 return ipmi::responseInvalidFieldRequest();
762 }
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +0530763 return ipmi::response(resetMtmTimer(ctx));
Richard Marian Thomaiyar666dd012019-08-02 20:55:37 +0530764}
765
Ayushi Smritie0511e52019-08-27 17:30:34 +0000766static constexpr unsigned int makeCmdKey(unsigned int netFn, unsigned int cmd)
767{
768 return (netFn << 8) | cmd;
769}
770
Yong Li85feb132019-08-09 16:06:03 +0800771ipmi::Cc mfgFilterMessage(ipmi::message::Request::ptr request)
772{
Ayushi Smritie0511e52019-08-27 17:30:34 +0000773 // Restricted commands, must be executed only in Manufacturing mode
774 switch (makeCmdKey(request->ctx->netFn, request->ctx->cmd))
Yong Li85feb132019-08-09 16:06:03 +0800775 {
Ayushi Smritie0511e52019-08-27 17:30:34 +0000776 // i2c master write read command needs additional checking
777 case makeCmdKey(ipmi::netFnApp, ipmi::app::cmdMasterWriteRead):
778 if (request->payload.size() > 4)
779 {
Richard Marian Thomaiyarae13ac62019-12-17 15:45:12 +0530780 // Allow write data count > 1 only in Special mode
781 if (mtm.getMfgMode() == SpecialMode::none)
Ayushi Smritie0511e52019-08-27 17:30:34 +0000782 {
783 return ipmi::ccInsufficientPrivilege;
784 }
785 }
jayaprakash Mutyala2d4a0192019-11-11 22:12:45 +0000786 return ipmi::ccSuccess;
Ayushi Smritie0511e52019-08-27 17:30:34 +0000787 case makeCmdKey(ipmi::netFnOemOne,
788 ipmi::intel::general::cmdGetSmSignal):
789 case makeCmdKey(ipmi::netFnOemOne,
790 ipmi::intel::general::cmdSetSmSignal):
791 case makeCmdKey(ipmi::netFnOemOne,
792 ipmi::intel::general::cmdMtmKeepAlive):
793 case makeCmdKey(ipmi::netFnOemOne,
794 ipmi::intel::general::cmdSetManufacturingData):
795 case makeCmdKey(ipmi::netFnOemOne,
796 ipmi::intel::general::cmdGetManufacturingData):
Vernon Mauery27d23562021-08-12 10:43:39 -0700797 case makeCmdKey(ipmi::intel::netFnGeneral,
798 ipmi::intel::general::cmdSetFITcLayout):
Arun P. Mohanan06584cd2021-08-13 20:56:01 +0530799 case makeCmdKey(ipmi::netFnOemOne,
800 ipmi::intel::general::cmdMTMBMCFeatureControl):
Ayushi Smritie0511e52019-08-27 17:30:34 +0000801 case makeCmdKey(ipmi::netFnStorage, ipmi::storage::cmdWriteFruData):
AppaRao Puli9a13daa2020-07-13 17:53:00 +0530802 case makeCmdKey(ipmi::netFnOemTwo, ipmi::intel::platform::cmdClearCMOS):
Ayushi Smritie0511e52019-08-27 17:30:34 +0000803
Richard Marian Thomaiyarae13ac62019-12-17 15:45:12 +0530804 // Check for Special mode
805 if (mtm.getMfgMode() == SpecialMode::none)
Yong Li85feb132019-08-09 16:06:03 +0800806 {
Ayushi Smritie0511e52019-08-27 17:30:34 +0000807 return ipmi::ccInvalidCommand;
Yong Li85feb132019-08-09 16:06:03 +0800808 }
jayaprakash Mutyala2d4a0192019-11-11 22:12:45 +0000809 return ipmi::ccSuccess;
810 case makeCmdKey(ipmi::netFnStorage, ipmi::storage::cmdDeleteSelEntry):
811 {
812 return ipmi::ccInvalidCommand;
813 }
Yong Li85feb132019-08-09 16:06:03 +0800814 }
Yong Li85feb132019-08-09 16:06:03 +0800815 return ipmi::ccSuccess;
816}
817
Richard Marian Thomaiyar1f0839c2019-08-25 20:10:52 +0530818static constexpr uint8_t maxEthSize = 6;
819static constexpr uint8_t maxSupportedEth = 3;
820static constexpr const char* factoryEthAddrBaseFileName =
821 "/var/sofs/factory-settings/network/mac/eth";
822
Zhikui Renad129c62022-04-05 20:11:24 -0700823using ObjectType = boost::container::flat_map<
824 std::string, boost::container::flat_map<std::string, DbusVariant>>;
825using ManagedObjectType =
826 boost::container::flat_map<sdbusplus::message::object_path, ObjectType>;
827
828bool findFruDevice(const std::shared_ptr<sdbusplus::asio::connection>& bus,
829 boost::asio::yield_context& yield, uint64_t& macOffset,
830 uint64_t& busNum, uint64_t& address)
831{
832 boost::system::error_code ec;
833
834 // GetAll the objects under service FruDevice
835 ec = boost::system::errc::make_error_code(boost::system::errc::success);
836 auto obj = bus->yield_method_call<ManagedObjectType>(
837 yield, ec, "xyz.openbmc_project.EntityManager", "/",
838 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
839 if (ec)
840 {
841 phosphor::logging::log<phosphor::logging::level::ERR>(
842 "GetMangagedObjects failed",
843 phosphor::logging::entry("ERROR=%s", ec.message().c_str()));
844 return false;
845 }
846
847 for (const auto& [path, fru] : obj)
848 {
849 for (const auto& [intf, propMap] : fru)
850 {
851 if (intf == "xyz.openbmc_project.Inventory.Item.Board.Motherboard")
852 {
853 auto findBus = propMap.find("FruBus");
854 auto findAddress = propMap.find("FruAddress");
855 auto findMacOffset = propMap.find("MacOffset");
856 if (findBus == propMap.end() || findAddress == propMap.end() ||
857 findMacOffset == propMap.end())
858 {
859 continue;
860 }
861
862 auto fruBus = std::get_if<uint64_t>(&findBus->second);
863 auto fruAddress = std::get_if<uint64_t>(&findAddress->second);
864 auto macFruOffset =
865 std::get_if<uint64_t>(&findMacOffset->second);
866 if (!fruBus || !fruAddress || !macFruOffset)
867 {
868 phosphor::logging::log<phosphor::logging::level::INFO>(
869 "ERROR: MotherBoard FRU config data type invalid, not "
870 "used");
871 return false;
872 }
873 busNum = *fruBus;
874 address = *fruAddress;
875 macOffset = *macFruOffset;
876 return true;
877 }
878 }
879 }
880 return false;
881}
882
Zhikui Ren98fa87e2022-05-04 08:10:29 -0700883static constexpr uint64_t fruEnd = 0xff;
884// write rolls over within current page, need to keep mac within a page
885static constexpr uint64_t fruPageSize = 0x8;
886// MAC record struct: HEADER, MAC DATA, CheckSum
887static constexpr uint64_t macRecordSize = maxEthSize + 2;
888static_assert(fruPageSize >= macRecordSize,
889 "macRecordSize greater than eeprom page size");
890static constexpr uint8_t macHeader = 0x40;
891// Calculate new checksum for fru info area
892static uint8_t calculateChecksum(std::vector<uint8_t>::const_iterator iter,
893 std::vector<uint8_t>::const_iterator end)
894{
895 constexpr int checksumMod = 256;
896 uint8_t sum = std::accumulate(iter, end, static_cast<uint8_t>(0));
897 return (checksumMod - sum) % checksumMod;
898}
899
Zhikui Renad129c62022-04-05 20:11:24 -0700900bool readMacFromFru(ipmi::Context::ptr ctx, uint8_t macIndex,
901 std::array<uint8_t, maxEthSize>& ethData)
902{
Zhikui Renad129c62022-04-05 20:11:24 -0700903 uint64_t macOffset = fruEnd;
904 uint64_t fruBus = 0;
905 uint64_t fruAddress = 0;
906
907 if (findFruDevice(ctx->bus, ctx->yield, macOffset, fruBus, fruAddress))
908 {
909 phosphor::logging::log<phosphor::logging::level::INFO>(
910 "Found mac fru",
911 phosphor::logging::entry("BUS=%d", static_cast<uint8_t>(fruBus)),
912 phosphor::logging::entry("ADDRESS=%d",
913 static_cast<uint8_t>(fruAddress)));
914
Zhikui Ren98fa87e2022-05-04 08:10:29 -0700915 if (macOffset % fruPageSize)
916 {
917 macOffset = (macOffset / fruPageSize + 1) * fruPageSize;
918 }
919 macOffset += macIndex * fruPageSize;
920 if ((macOffset + macRecordSize) > fruEnd)
Zhikui Renad129c62022-04-05 20:11:24 -0700921 {
922 phosphor::logging::log<phosphor::logging::level::ERR>(
923 "ERROR: read fru mac failed, offset invalid");
924 return false;
925 }
926 std::vector<uint8_t> writeData;
Zhikui Ren98fa87e2022-05-04 08:10:29 -0700927 writeData.push_back(static_cast<uint8_t>(macOffset));
928 std::vector<uint8_t> readBuf(macRecordSize);
Zhikui Renad129c62022-04-05 20:11:24 -0700929 std::string i2cBus = "/dev/i2c-" + std::to_string(fruBus);
930 ipmi::Cc retI2C =
931 ipmi::i2cWriteRead(i2cBus, fruAddress, writeData, readBuf);
932 if (retI2C == ipmi::ccSuccess)
933 {
Zhikui Ren98fa87e2022-05-04 08:10:29 -0700934 uint8_t cs = calculateChecksum(readBuf.cbegin(), readBuf.cend());
935 if (cs == 0)
936 {
937 std::copy(++readBuf.begin(), --readBuf.end(), ethData.data());
938 return true;
939 }
Zhikui Renad129c62022-04-05 20:11:24 -0700940 }
941 }
942 return false;
943}
944
945ipmi::Cc writeMacToFru(ipmi::Context::ptr ctx, uint8_t macIndex,
946 std::array<uint8_t, maxEthSize>& ethData)
947{
Zhikui Renad129c62022-04-05 20:11:24 -0700948 uint64_t macOffset = fruEnd;
949 uint64_t fruBus = 0;
950 uint64_t fruAddress = 0;
951
952 if (findFruDevice(ctx->bus, ctx->yield, macOffset, fruBus, fruAddress))
953 {
954 phosphor::logging::log<phosphor::logging::level::INFO>(
955 "Found mac fru",
956 phosphor::logging::entry("BUS=%d", static_cast<uint8_t>(fruBus)),
957 phosphor::logging::entry("ADDRESS=%d",
958 static_cast<uint8_t>(fruAddress)));
959
Zhikui Ren98fa87e2022-05-04 08:10:29 -0700960 if (macOffset % fruPageSize)
961 {
962 macOffset = (macOffset / fruPageSize + 1) * fruPageSize;
963 }
964 macOffset += macIndex * fruPageSize;
965 if ((macOffset + macRecordSize) > fruEnd)
Zhikui Renad129c62022-04-05 20:11:24 -0700966 {
967 phosphor::logging::log<phosphor::logging::level::ERR>(
968 "ERROR: write mac fru failed, offset invalid.");
969 return ipmi::ccParmOutOfRange;
970 }
971 std::vector<uint8_t> writeData;
Zhikui Ren98fa87e2022-05-04 08:10:29 -0700972 writeData.reserve(macRecordSize + 1); // include start location
973 writeData.push_back(static_cast<uint8_t>(macOffset));
974 writeData.push_back(macHeader);
Zhikui Renad129c62022-04-05 20:11:24 -0700975 std::for_each(ethData.cbegin(), ethData.cend(),
976 [&](uint8_t i) { writeData.push_back(i); });
Zhikui Ren98fa87e2022-05-04 08:10:29 -0700977 uint8_t macCheckSum =
978 calculateChecksum(++writeData.cbegin(), writeData.cend());
979 writeData.push_back(macCheckSum);
Zhikui Renad129c62022-04-05 20:11:24 -0700980
981 std::string i2cBus = "/dev/i2c-" + std::to_string(fruBus);
982 std::vector<uint8_t> readBuf;
983 ipmi::Cc ret =
984 ipmi::i2cWriteRead(i2cBus, fruAddress, writeData, readBuf);
Zhikui Ren98fa87e2022-05-04 08:10:29 -0700985
Zhikui Ren0408e792022-06-07 21:03:33 -0700986 // prepare for read to detect chip is write protected
987 writeData.resize(1);
988 readBuf.resize(maxEthSize + 1); // include macHeader
989
Zhikui Renad129c62022-04-05 20:11:24 -0700990 switch (ret)
991 {
992 case ipmi::ccSuccess:
Zhikui Ren98fa87e2022-05-04 08:10:29 -0700993 // Wait for internal write cycle to complete
994 // example: ATMEL 24c0x chip has Twr spec as 5ms
995
996 // Ideally we want yield wait, but currently following code
997 // crash with "thread not supported"
998 // boost::asio::deadline_timer timer(
999 // boost::asio::get_associated_executor(ctx->yield),
1000 // boost::posix_time::seconds(1));
1001 // timer.async_wait(ctx->yield);
1002 // use usleep as temp WA
1003 usleep(5000);
1004 if (ipmi::i2cWriteRead(i2cBus, fruAddress, writeData,
1005 readBuf) == ipmi::ccSuccess)
Zhikui Renad129c62022-04-05 20:11:24 -07001006 {
1007 if (std::equal(ethData.begin(), ethData.end(),
Zhikui Ren98fa87e2022-05-04 08:10:29 -07001008 ++readBuf.begin())) // skip macHeader
Zhikui Renad129c62022-04-05 20:11:24 -07001009 {
1010 return ipmi::ccSuccess;
1011 }
1012 phosphor::logging::log<phosphor::logging::level::INFO>(
1013 "INFO: write mac fru verify failed, fru may be write "
1014 "protected.");
1015 }
1016 return ipmi::ccCommandNotAvailable;
Zhikui Ren0408e792022-06-07 21:03:33 -07001017 default:
1018 if (ipmi::i2cWriteRead(i2cBus, fruAddress, writeData,
1019 readBuf) == ipmi::ccSuccess)
1020 {
1021 phosphor::logging::log<phosphor::logging::level::INFO>(
1022 "INFO: write mac fru failed, but successfully read "
1023 "from fru, fru may be write protected.");
1024 return ipmi::ccCommandNotAvailable;
1025 }
1026 else // assume failure is due to no eeprom on board
1027 {
1028 phosphor::logging::log<phosphor::logging::level::ERR>(
1029 "ERROR: write mac fru failed, assume no eeprom is "
1030 "available.");
1031 }
Zhikui Renad129c62022-04-05 20:11:24 -07001032 break;
1033 }
1034 }
1035 // no FRU eeprom found
1036 return ipmi::ccDestinationUnavailable;
1037}
1038
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +05301039ipmi::RspType<> setManufacturingData(ipmi::Context::ptr ctx, uint8_t dataType,
Richard Marian Thomaiyar1f0839c2019-08-25 20:10:52 +05301040 std::array<uint8_t, maxEthSize> ethData)
1041{
Zhikui Renad129c62022-04-05 20:11:24 -07001042 // mfg filter logic will restrict this command executing only in mfg
1043 // mode.
Richard Marian Thomaiyar1f0839c2019-08-25 20:10:52 +05301044 if (dataType >= maxSupportedEth)
1045 {
1046 return ipmi::responseParmOutOfRange();
1047 }
1048
1049 constexpr uint8_t invalidData = 0;
1050 constexpr uint8_t validData = 1;
Zhikui Renad129c62022-04-05 20:11:24 -07001051
1052 ipmi::Cc ret = writeMacToFru(ctx, dataType, ethData);
1053 if (ret != ipmi::ccDestinationUnavailable)
1054 {
1055 resetMtmTimer(ctx);
1056 return response(ret);
1057 }
1058
Richard Marian Thomaiyar1f0839c2019-08-25 20:10:52 +05301059 constexpr uint8_t ethAddrStrSize =
1060 19; // XX:XX:XX:XX:XX:XX + \n + null termination;
1061 std::vector<uint8_t> buff(ethAddrStrSize);
1062 std::snprintf(reinterpret_cast<char*>(buff.data()), ethAddrStrSize,
1063 "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx\n", ethData.at(0),
1064 ethData.at(1), ethData.at(2), ethData.at(3), ethData.at(4),
1065 ethData.at(5));
1066 std::ofstream oEthFile(factoryEthAddrBaseFileName +
1067 std::to_string(dataType),
1068 std::ofstream::out);
1069 if (!oEthFile.good())
1070 {
1071 return ipmi::responseUnspecifiedError();
1072 }
1073
1074 oEthFile << reinterpret_cast<char*>(buff.data());
Johnathan Mantey6d83e4e2020-05-08 11:01:01 -07001075 oEthFile.flush();
Richard Marian Thomaiyar1f0839c2019-08-25 20:10:52 +05301076 oEthFile.close();
1077
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +05301078 resetMtmTimer(ctx);
Richard Marian Thomaiyar1f0839c2019-08-25 20:10:52 +05301079 return ipmi::responseSuccess();
1080}
1081
1082ipmi::RspType<uint8_t, std::array<uint8_t, maxEthSize>>
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +05301083 getManufacturingData(ipmi::Context::ptr ctx, uint8_t dataType)
Richard Marian Thomaiyar1f0839c2019-08-25 20:10:52 +05301084{
Zhikui Renad129c62022-04-05 20:11:24 -07001085 // mfg filter logic will restrict this command executing only in mfg
1086 // mode.
Richard Marian Thomaiyar1f0839c2019-08-25 20:10:52 +05301087 if (dataType >= maxSupportedEth)
1088 {
1089 return ipmi::responseParmOutOfRange();
1090 }
1091 std::array<uint8_t, maxEthSize> ethData{0};
1092 constexpr uint8_t invalidData = 0;
1093 constexpr uint8_t validData = 1;
1094
1095 std::ifstream iEthFile(factoryEthAddrBaseFileName +
1096 std::to_string(dataType),
1097 std::ifstream::in);
1098 if (!iEthFile.good())
1099 {
Zhikui Renad129c62022-04-05 20:11:24 -07001100 if (readMacFromFru(ctx, dataType, ethData))
1101 {
1102 resetMtmTimer(ctx);
1103 return ipmi::responseSuccess(validData, ethData);
1104 }
Richard Marian Thomaiyar1f0839c2019-08-25 20:10:52 +05301105 return ipmi::responseSuccess(invalidData, ethData);
1106 }
1107 std::string ethStr;
1108 iEthFile >> ethStr;
1109 uint8_t* data = ethData.data();
1110 std::sscanf(ethStr.c_str(), "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx",
1111 data, (data + 1), (data + 2), (data + 3), (data + 4),
1112 (data + 5));
1113
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +05301114 resetMtmTimer(ctx);
Richard Marian Thomaiyar1f0839c2019-08-25 20:10:52 +05301115 return ipmi::responseSuccess(validData, ethData);
1116}
1117
Zhikui Renad129c62022-04-05 20:11:24 -07001118/** @brief implements slot master write read IPMI command which can be used
1119 * for low-level I2C/SMBus write, read or write-read access for PCIE slots
Yong Lif267a672019-08-29 11:16:07 +08001120 * @param reserved - skip 6 bit
1121 * @param addressType - address type
1122 * @param bbSlotNum - baseboard slot number
1123 * @param riserSlotNum - riser slot number
1124 * @param reserved2 - skip 2 bit
1125 * @param slaveAddr - slave address
1126 * @param readCount - number of bytes to be read
1127 * @param writeData - data to be written
1128 *
1129 * @returns IPMI completion code plus response data
1130 */
1131ipmi::RspType<std::vector<uint8_t>>
1132 appSlotI2CMasterWriteRead(uint6_t reserved, uint2_t addressType,
1133 uint3_t bbSlotNum, uint3_t riserSlotNum,
Vernon Maueryfc3bc382022-06-02 13:52:04 -07001134 uint2_t reserved2, uint8_t slaveAddr,
Yong Lif267a672019-08-29 11:16:07 +08001135 uint8_t readCount, std::vector<uint8_t> writeData)
1136{
Vernon Maueryfc3bc382022-06-02 13:52:04 -07001137 if (reserved || reserved2)
1138 {
1139 return ipmi::responseInvalidFieldRequest();
1140 }
Yong Lif267a672019-08-29 11:16:07 +08001141 const size_t writeCount = writeData.size();
1142 std::string i2cBus;
1143 if (addressType == slotAddressTypeBus)
1144 {
1145 std::string path = "/dev/i2c-mux/Riser_" +
1146 std::to_string(static_cast<uint8_t>(bbSlotNum)) +
1147 "_Mux/Pcie_Slot_" +
1148 std::to_string(static_cast<uint8_t>(riserSlotNum));
1149
1150 if (std::filesystem::exists(path) && std::filesystem::is_symlink(path))
1151 {
1152 i2cBus = std::filesystem::read_symlink(path);
1153 }
1154 else
1155 {
1156 phosphor::logging::log<phosphor::logging::level::ERR>(
1157 "Master write read command: Cannot get BusID");
1158 return ipmi::responseInvalidFieldRequest();
1159 }
1160 }
1161 else if (addressType == slotAddressTypeUniqueid)
1162 {
1163 i2cBus = "/dev/i2c-" +
1164 std::to_string(static_cast<uint8_t>(bbSlotNum) |
1165 (static_cast<uint8_t>(riserSlotNum) << 3));
1166 }
1167 else
1168 {
1169 phosphor::logging::log<phosphor::logging::level::ERR>(
1170 "Master write read command: invalid request");
1171 return ipmi::responseInvalidFieldRequest();
1172 }
1173
Zhikui Renad129c62022-04-05 20:11:24 -07001174 // Allow single byte write as it is offset byte to read the data, rest
1175 // allow only in Special mode.
Yong Lif267a672019-08-29 11:16:07 +08001176 if (writeCount > 1)
1177 {
Richard Marian Thomaiyarae13ac62019-12-17 15:45:12 +05301178 if (mtm.getMfgMode() == SpecialMode::none)
Yong Lif267a672019-08-29 11:16:07 +08001179 {
1180 return ipmi::responseInsufficientPrivilege();
1181 }
1182 }
1183
1184 if (readCount > slotI2CMaxReadSize)
1185 {
1186 phosphor::logging::log<phosphor::logging::level::ERR>(
1187 "Master write read command: Read count exceeds limit");
1188 return ipmi::responseParmOutOfRange();
1189 }
1190
1191 if (!readCount && !writeCount)
1192 {
1193 phosphor::logging::log<phosphor::logging::level::ERR>(
1194 "Master write read command: Read & write count are 0");
1195 return ipmi::responseInvalidFieldRequest();
1196 }
1197
1198 std::vector<uint8_t> readBuf(readCount);
1199
1200 ipmi::Cc retI2C = ipmi::i2cWriteRead(i2cBus, slaveAddr, writeData, readBuf);
1201 if (retI2C != ipmi::ccSuccess)
1202 {
1203 return ipmi::response(retI2C);
1204 }
1205
1206 return ipmi::responseSuccess(readBuf);
1207}
Yong Li068b4f22019-09-17 16:32:18 +08001208
1209ipmi::RspType<> clearCMOS()
1210{
Zhikui Renad129c62022-04-05 20:11:24 -07001211 // There is an i2c device on bus 4, the slave address is 0x38. Based on
1212 // the spec, writing 0x1 to address 0x61 on this device, will trigger
1213 // the clear CMOS action.
Yong Lid0d010b2019-11-18 12:15:21 +08001214 constexpr uint8_t slaveAddr = 0x38;
Yong Li068b4f22019-09-17 16:32:18 +08001215 std::string i2cBus = "/dev/i2c-4";
Yong Lieaeb6cb2020-03-09 20:21:50 +08001216 std::vector<uint8_t> writeData = {0x61, 0x1};
Yong Li068b4f22019-09-17 16:32:18 +08001217 std::vector<uint8_t> readBuf(0);
1218
Yong Li068b4f22019-09-17 16:32:18 +08001219 ipmi::Cc retI2C = ipmi::i2cWriteRead(i2cBus, slaveAddr, writeData, readBuf);
1220 return ipmi::response(retI2C);
1221}
Vernon Mauery27d23562021-08-12 10:43:39 -07001222
1223ipmi::RspType<> setFITcLayout(uint32_t layout)
1224{
1225 static constexpr const char* factoryFITcLayout =
1226 "/var/sofs/factory-settings/layout/fitc";
1227 std::filesystem::path fitcDir =
1228 std::filesystem::path(factoryFITcLayout).parent_path();
1229 std::error_code ec;
1230 std::filesystem::create_directories(fitcDir, ec);
1231 if (ec)
1232 {
1233 return ipmi::responseUnspecifiedError();
1234 }
1235 try
1236 {
1237 std::ofstream file(factoryFITcLayout);
1238 file << layout;
1239 file.flush();
1240 file.close();
1241 }
1242 catch (const std::exception& e)
1243 {
1244 return ipmi::responseUnspecifiedError();
1245 }
1246
1247 return ipmi::responseSuccess();
1248}
1249
Arun P. Mohanan06584cd2021-08-13 20:56:01 +05301250static std::vector<std::string>
1251 getMCTPServiceConfigPaths(ipmi::Context::ptr& ctx)
1252{
1253 boost::system::error_code ec;
1254 auto configPaths = ctx->bus->yield_method_call<std::vector<std::string>>(
1255 ctx->yield, ec, "xyz.openbmc_project.ObjectMapper",
1256 "/xyz/openbmc_project/object_mapper",
1257 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths",
1258 "/xyz/openbmc_project/inventory/system/board", 2,
1259 std::array<const char*, 1>{
1260 "xyz.openbmc_project.Configuration.MctpConfiguration"});
1261 if (ec)
1262 {
1263 throw std::runtime_error(
1264 "Failed to query configuration sub tree objects");
1265 }
1266 return configPaths;
1267}
1268
1269static ipmi::RspType<> startOrStopService(ipmi::Context::ptr& ctx,
1270 const uint8_t enable,
ANJALI RAY1fe485c2021-12-17 16:41:30 +00001271 const std::string& serviceName,
1272 bool disableOrEnableUnitFiles = true)
Arun P. Mohanan06584cd2021-08-13 20:56:01 +05301273{
1274 constexpr bool runtimeOnly = false;
1275 constexpr bool force = false;
1276
1277 boost::system::error_code ec;
1278 switch (enable)
1279 {
1280 case ipmi::SupportedFeatureActions::stop:
1281 ctx->bus->yield_method_call(ctx->yield, ec, systemDService,
1282 systemDObjPath, systemDMgrIntf,
1283 "StopUnit", serviceName, "replace");
1284 break;
1285 case ipmi::SupportedFeatureActions::start:
1286 ctx->bus->yield_method_call(ctx->yield, ec, systemDService,
1287 systemDObjPath, systemDMgrIntf,
1288 "StartUnit", serviceName, "replace");
1289 break;
1290 case ipmi::SupportedFeatureActions::disable:
ANJALI RAY1fe485c2021-12-17 16:41:30 +00001291 if (disableOrEnableUnitFiles == true)
1292 {
1293 ctx->bus->yield_method_call(
1294 ctx->yield, ec, systemDService, systemDObjPath,
1295 systemDMgrIntf, "DisableUnitFiles",
1296 std::array<const char*, 1>{serviceName.c_str()},
1297 runtimeOnly);
1298 }
Arun P. Mohanan06584cd2021-08-13 20:56:01 +05301299 ctx->bus->yield_method_call(
1300 ctx->yield, ec, systemDService, systemDObjPath, systemDMgrIntf,
1301 "MaskUnitFiles",
1302 std::array<const char*, 1>{serviceName.c_str()}, runtimeOnly,
1303 force);
Arun P. Mohanan06584cd2021-08-13 20:56:01 +05301304 break;
1305 case ipmi::SupportedFeatureActions::enable:
1306 ctx->bus->yield_method_call(
1307 ctx->yield, ec, systemDService, systemDObjPath, systemDMgrIntf,
1308 "UnmaskUnitFiles",
1309 std::array<const char*, 1>{serviceName.c_str()}, runtimeOnly);
ANJALI RAY1fe485c2021-12-17 16:41:30 +00001310 if (disableOrEnableUnitFiles == true)
1311 {
1312 ctx->bus->yield_method_call(
1313 ctx->yield, ec, systemDService, systemDObjPath,
1314 systemDMgrIntf, "EnableUnitFiles",
1315 std::array<const char*, 1>{serviceName.c_str()},
1316 runtimeOnly, force);
1317 }
Arun P. Mohanan06584cd2021-08-13 20:56:01 +05301318 break;
1319 default:
1320 phosphor::logging::log<phosphor::logging::level::WARNING>(
1321 "ERROR: Invalid feature action selected",
1322 phosphor::logging::entry("ACTION=%d", enable));
1323 return ipmi::responseInvalidFieldRequest();
1324 }
1325 if (ec)
1326 {
1327 phosphor::logging::log<phosphor::logging::level::WARNING>(
1328 "ERROR: Service start or stop failed",
1329 phosphor::logging::entry("SERVICE=%s", serviceName.c_str()));
1330 return ipmi::responseUnspecifiedError();
1331 }
1332 return ipmi::responseSuccess();
1333}
1334
1335static std::string getMCTPServiceName(const std::string& objectPath)
1336{
1337 const auto serviceArgument = boost::algorithm::replace_all_copy(
1338 boost::algorithm::replace_first_copy(
1339 objectPath, "/xyz/openbmc_project/inventory/system/board/", ""),
1340 "/", "_2f");
1341 std::string unitName =
1342 "xyz.openbmc_project.mctpd@" + serviceArgument + ".service";
1343 return unitName;
1344}
1345
1346static ipmi::RspType<> handleMCTPFeature(ipmi::Context::ptr& ctx,
1347 const uint8_t enable,
1348 const std::string& binding)
1349{
1350 std::vector<std::string> configPaths;
1351 try
1352 {
1353 configPaths = getMCTPServiceConfigPaths(ctx);
1354 }
1355 catch (const std::exception& e)
1356 {
1357 phosphor::logging::log<phosphor::logging::level::ERR>(e.what());
1358 return ipmi::responseUnspecifiedError();
1359 }
1360
1361 for (const auto& objectPath : configPaths)
1362 {
1363 auto const pos = objectPath.find_last_of('/');
1364 if (binding == objectPath.substr(pos + 1))
1365 {
1366 return startOrStopService(ctx, enable,
ANJALI RAY1fe485c2021-12-17 16:41:30 +00001367 getMCTPServiceName(objectPath), false);
Arun P. Mohanan06584cd2021-08-13 20:56:01 +05301368 }
1369 }
1370 return ipmi::responseSuccess();
1371}
1372
1373/** @brief implements MTM BMC Feature Control IPMI command which can be
1374 * used to enable or disable the supported BMC features.
1375 * @param yield - context object that represents the currently executing
1376 * coroutine
1377 * @param feature - feature enum to enable or disable
1378 * @param enable - enable or disable the feature
1379 * @param featureArg - custom arguments for that feature
1380 * @param reserved - reserved for future use
1381 *
1382 * @returns IPMI completion code
1383 */
1384ipmi::RspType<> mtmBMCFeatureControl(ipmi::Context::ptr ctx,
1385 const uint8_t feature,
1386 const uint8_t enable,
1387 const uint8_t featureArg,
1388 const uint16_t reserved)
1389{
1390 if (reserved != 0)
1391 {
1392 return ipmi::responseInvalidFieldRequest();
1393 }
1394
1395 switch (feature)
1396 {
1397 case ipmi::SupportedFeatureControls::mctp:
1398 switch (featureArg)
1399 {
1400 case ipmi::SupportedMCTPBindings::mctpPCIe:
1401 return handleMCTPFeature(ctx, enable, "MCTP_PCIe");
1402 case ipmi::SupportedMCTPBindings::mctpSMBusHSBP:
1403 return handleMCTPFeature(ctx, enable, "MCTP_SMBus_HSBP");
1404 case ipmi::SupportedMCTPBindings::mctpSMBusPCIeSlot:
1405 return handleMCTPFeature(ctx, enable,
1406 "MCTP_SMBus_PCIe_slot");
1407 default:
1408 return ipmi::responseInvalidFieldRequest();
1409 }
1410 break;
Jason M. Bills5cb2c042021-08-17 12:03:39 -07001411 case ipmi::SupportedFeatureControls::pcieScan:
1412 if (featureArg != 0)
1413 {
1414 return ipmi::responseInvalidFieldRequest();
1415 }
1416 startOrStopService(ctx, enable, "xyz.openbmc_project.PCIe.service");
1417 break;
Arun P. Mohanan06584cd2021-08-13 20:56:01 +05301418 default:
1419 return ipmi::responseInvalidFieldRequest();
1420 }
1421 return ipmi::responseSuccess();
1422}
Vernon Mauerya3702c12019-05-22 13:20:59 -07001423} // namespace ipmi
1424
1425void register_mtm_commands() __attribute__((constructor));
1426void register_mtm_commands()
1427{
Jason M. Bills38d2b5a2019-06-03 16:26:11 -07001428 // <Get SM Signal>
Vernon Mauery98bbf692019-09-16 11:14:59 -07001429 ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnGeneral,
1430 ipmi::intel::general::cmdGetSmSignal,
1431 ipmi::Privilege::Admin, ipmi::appMTMGetSignal);
Vernon Mauerya3702c12019-05-22 13:20:59 -07001432
Vernon Mauery98bbf692019-09-16 11:14:59 -07001433 ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnGeneral,
1434 ipmi::intel::general::cmdSetSmSignal,
1435 ipmi::Privilege::Admin, ipmi::appMTMSetSignal);
Vernon Mauerya3702c12019-05-22 13:20:59 -07001436
Vernon Mauery98bbf692019-09-16 11:14:59 -07001437 ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnGeneral,
1438 ipmi::intel::general::cmdMtmKeepAlive,
1439 ipmi::Privilege::Admin, ipmi::mtmKeepAlive);
Richard Marian Thomaiyar666dd012019-08-02 20:55:37 +05301440
Vernon Mauery98bbf692019-09-16 11:14:59 -07001441 ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnGeneral,
1442 ipmi::intel::general::cmdSetManufacturingData,
1443 ipmi::Privilege::Admin, ipmi::setManufacturingData);
Richard Marian Thomaiyar1f0839c2019-08-25 20:10:52 +05301444
Vernon Mauery98bbf692019-09-16 11:14:59 -07001445 ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnGeneral,
1446 ipmi::intel::general::cmdGetManufacturingData,
1447 ipmi::Privilege::Admin, ipmi::getManufacturingData);
Richard Marian Thomaiyar1f0839c2019-08-25 20:10:52 +05301448
Vernon Mauery27d23562021-08-12 10:43:39 -07001449 ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnGeneral,
1450 ipmi::intel::general::cmdSetFITcLayout,
1451 ipmi::Privilege::Admin, ipmi::setFITcLayout);
1452
Arun P. Mohanan06584cd2021-08-13 20:56:01 +05301453 ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnGeneral,
1454 ipmi::intel::general::cmdMTMBMCFeatureControl,
1455 ipmi::Privilege::Admin, ipmi::mtmBMCFeatureControl);
1456
Vernon Mauery98bbf692019-09-16 11:14:59 -07001457 ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnApp,
1458 ipmi::intel::general::cmdSlotI2CMasterWriteRead,
1459 ipmi::Privilege::Admin,
1460 ipmi::appSlotI2CMasterWriteRead);
Yong Lif267a672019-08-29 11:16:07 +08001461
Yong Li068b4f22019-09-17 16:32:18 +08001462 ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnPlatform,
1463 ipmi::intel::platform::cmdClearCMOS,
1464 ipmi::Privilege::Admin, ipmi::clearCMOS);
1465
Vernon Mauery98bbf692019-09-16 11:14:59 -07001466 ipmi::registerFilter(ipmi::prioOemBase,
Yong Li85feb132019-08-09 16:06:03 +08001467 [](ipmi::message::Request::ptr request) {
1468 return ipmi::mfgFilterMessage(request);
1469 });
Vernon Mauerya3702c12019-05-22 13:20:59 -07001470}