blob: c44aa70df5690aced071ad5f36327c4f9367a8f5 [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 Williams69245b72021-09-02 09:23:01 -0500230 catch (const sdbusplus::exception::exception& 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 Williams69245b72021-09-02 09:23:01 -0500251 catch (const sdbusplus::exception::exception& 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 Williams69245b72021-09-02 09:23:01 -0500272 catch (const sdbusplus::exception::exception& 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
883bool readMacFromFru(ipmi::Context::ptr ctx, uint8_t macIndex,
884 std::array<uint8_t, maxEthSize>& ethData)
885{
886 constexpr uint64_t fruEnd = 0xff;
887 uint64_t macOffset = fruEnd;
888 uint64_t fruBus = 0;
889 uint64_t fruAddress = 0;
890
891 if (findFruDevice(ctx->bus, ctx->yield, macOffset, fruBus, fruAddress))
892 {
893 phosphor::logging::log<phosphor::logging::level::INFO>(
894 "Found mac fru",
895 phosphor::logging::entry("BUS=%d", static_cast<uint8_t>(fruBus)),
896 phosphor::logging::entry("ADDRESS=%d",
897 static_cast<uint8_t>(fruAddress)));
898
899 macOffset += macIndex * maxEthSize;
900 if ((macOffset + maxEthSize) > fruEnd)
901 {
902 phosphor::logging::log<phosphor::logging::level::ERR>(
903 "ERROR: read fru mac failed, offset invalid");
904 return false;
905 }
906 std::vector<uint8_t> writeData;
907 writeData.push_back(static_cast<uint8_t>(macOffset & 0xff));
908 std::vector<uint8_t> readBuf(maxEthSize);
909 std::string i2cBus = "/dev/i2c-" + std::to_string(fruBus);
910 ipmi::Cc retI2C =
911 ipmi::i2cWriteRead(i2cBus, fruAddress, writeData, readBuf);
912 if (retI2C == ipmi::ccSuccess)
913 {
914 std::copy(readBuf.begin(), readBuf.end(), ethData.data());
915 return true;
916 }
917 }
918 return false;
919}
920
921ipmi::Cc writeMacToFru(ipmi::Context::ptr ctx, uint8_t macIndex,
922 std::array<uint8_t, maxEthSize>& ethData)
923{
924 constexpr uint64_t fruEnd = 0xff;
925 uint64_t macOffset = fruEnd;
926 uint64_t fruBus = 0;
927 uint64_t fruAddress = 0;
928
929 if (findFruDevice(ctx->bus, ctx->yield, macOffset, fruBus, fruAddress))
930 {
931 phosphor::logging::log<phosphor::logging::level::INFO>(
932 "Found mac fru",
933 phosphor::logging::entry("BUS=%d", static_cast<uint8_t>(fruBus)),
934 phosphor::logging::entry("ADDRESS=%d",
935 static_cast<uint8_t>(fruAddress)));
936
937 macOffset += macIndex * maxEthSize;
938 if ((macOffset + maxEthSize) > fruEnd)
939 {
940 phosphor::logging::log<phosphor::logging::level::ERR>(
941 "ERROR: write mac fru failed, offset invalid.");
942 return ipmi::ccParmOutOfRange;
943 }
944 std::vector<uint8_t> writeData;
945 writeData.push_back(static_cast<uint8_t>(macOffset & 0xff));
946 std::for_each(ethData.cbegin(), ethData.cend(),
947 [&](uint8_t i) { writeData.push_back(i); });
948
949 std::string i2cBus = "/dev/i2c-" + std::to_string(fruBus);
950 std::vector<uint8_t> readBuf;
951 ipmi::Cc ret =
952 ipmi::i2cWriteRead(i2cBus, fruAddress, writeData, readBuf);
953 switch (ret)
954 {
955 case ipmi::ccSuccess:
956 // chip is write protected, if write is success but fails verify
957 writeData.resize(1);
958 readBuf.resize(maxEthSize);
959 if (ipmi::ccSuccess ==
960 ipmi::i2cWriteRead(i2cBus, fruAddress, writeData, readBuf))
961 {
962 if (std::equal(ethData.begin(), ethData.end(),
963 readBuf.begin()))
964 {
965 return ipmi::ccSuccess;
966 }
967 phosphor::logging::log<phosphor::logging::level::INFO>(
968 "INFO: write mac fru verify failed, fru may be write "
969 "protected.");
970 }
971 return ipmi::ccCommandNotAvailable;
972 default: // assumes no actual eeprom for other failure
973 phosphor::logging::log<phosphor::logging::level::ERR>(
974 "ERROR: write mac fru failed, assume no eeprom is "
975 "available.");
976 break;
977 }
978 }
979 // no FRU eeprom found
980 return ipmi::ccDestinationUnavailable;
981}
982
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +0530983ipmi::RspType<> setManufacturingData(ipmi::Context::ptr ctx, uint8_t dataType,
Richard Marian Thomaiyar1f0839c2019-08-25 20:10:52 +0530984 std::array<uint8_t, maxEthSize> ethData)
985{
Zhikui Renad129c62022-04-05 20:11:24 -0700986 // mfg filter logic will restrict this command executing only in mfg
987 // mode.
Richard Marian Thomaiyar1f0839c2019-08-25 20:10:52 +0530988 if (dataType >= maxSupportedEth)
989 {
990 return ipmi::responseParmOutOfRange();
991 }
992
993 constexpr uint8_t invalidData = 0;
994 constexpr uint8_t validData = 1;
Zhikui Renad129c62022-04-05 20:11:24 -0700995
996 ipmi::Cc ret = writeMacToFru(ctx, dataType, ethData);
997 if (ret != ipmi::ccDestinationUnavailable)
998 {
999 resetMtmTimer(ctx);
1000 return response(ret);
1001 }
1002
Richard Marian Thomaiyar1f0839c2019-08-25 20:10:52 +05301003 constexpr uint8_t ethAddrStrSize =
1004 19; // XX:XX:XX:XX:XX:XX + \n + null termination;
1005 std::vector<uint8_t> buff(ethAddrStrSize);
1006 std::snprintf(reinterpret_cast<char*>(buff.data()), ethAddrStrSize,
1007 "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx\n", ethData.at(0),
1008 ethData.at(1), ethData.at(2), ethData.at(3), ethData.at(4),
1009 ethData.at(5));
1010 std::ofstream oEthFile(factoryEthAddrBaseFileName +
1011 std::to_string(dataType),
1012 std::ofstream::out);
1013 if (!oEthFile.good())
1014 {
1015 return ipmi::responseUnspecifiedError();
1016 }
1017
1018 oEthFile << reinterpret_cast<char*>(buff.data());
Johnathan Mantey6d83e4e2020-05-08 11:01:01 -07001019 oEthFile.flush();
Richard Marian Thomaiyar1f0839c2019-08-25 20:10:52 +05301020 oEthFile.close();
1021
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +05301022 resetMtmTimer(ctx);
Richard Marian Thomaiyar1f0839c2019-08-25 20:10:52 +05301023 return ipmi::responseSuccess();
1024}
1025
1026ipmi::RspType<uint8_t, std::array<uint8_t, maxEthSize>>
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +05301027 getManufacturingData(ipmi::Context::ptr ctx, uint8_t dataType)
Richard Marian Thomaiyar1f0839c2019-08-25 20:10:52 +05301028{
Zhikui Renad129c62022-04-05 20:11:24 -07001029 // mfg filter logic will restrict this command executing only in mfg
1030 // mode.
Richard Marian Thomaiyar1f0839c2019-08-25 20:10:52 +05301031 if (dataType >= maxSupportedEth)
1032 {
1033 return ipmi::responseParmOutOfRange();
1034 }
1035 std::array<uint8_t, maxEthSize> ethData{0};
1036 constexpr uint8_t invalidData = 0;
1037 constexpr uint8_t validData = 1;
1038
1039 std::ifstream iEthFile(factoryEthAddrBaseFileName +
1040 std::to_string(dataType),
1041 std::ifstream::in);
1042 if (!iEthFile.good())
1043 {
Zhikui Renad129c62022-04-05 20:11:24 -07001044 if (readMacFromFru(ctx, dataType, ethData))
1045 {
1046 resetMtmTimer(ctx);
1047 return ipmi::responseSuccess(validData, ethData);
1048 }
Richard Marian Thomaiyar1f0839c2019-08-25 20:10:52 +05301049 return ipmi::responseSuccess(invalidData, ethData);
1050 }
1051 std::string ethStr;
1052 iEthFile >> ethStr;
1053 uint8_t* data = ethData.data();
1054 std::sscanf(ethStr.c_str(), "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx",
1055 data, (data + 1), (data + 2), (data + 3), (data + 4),
1056 (data + 5));
1057
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +05301058 resetMtmTimer(ctx);
Richard Marian Thomaiyar1f0839c2019-08-25 20:10:52 +05301059 return ipmi::responseSuccess(validData, ethData);
1060}
1061
Zhikui Renad129c62022-04-05 20:11:24 -07001062/** @brief implements slot master write read IPMI command which can be used
1063 * for low-level I2C/SMBus write, read or write-read access for PCIE slots
Yong Lif267a672019-08-29 11:16:07 +08001064 * @param reserved - skip 6 bit
1065 * @param addressType - address type
1066 * @param bbSlotNum - baseboard slot number
1067 * @param riserSlotNum - riser slot number
1068 * @param reserved2 - skip 2 bit
1069 * @param slaveAddr - slave address
1070 * @param readCount - number of bytes to be read
1071 * @param writeData - data to be written
1072 *
1073 * @returns IPMI completion code plus response data
1074 */
1075ipmi::RspType<std::vector<uint8_t>>
1076 appSlotI2CMasterWriteRead(uint6_t reserved, uint2_t addressType,
1077 uint3_t bbSlotNum, uint3_t riserSlotNum,
1078 uint2_t resvered2, uint8_t slaveAddr,
1079 uint8_t readCount, std::vector<uint8_t> writeData)
1080{
1081 const size_t writeCount = writeData.size();
1082 std::string i2cBus;
1083 if (addressType == slotAddressTypeBus)
1084 {
1085 std::string path = "/dev/i2c-mux/Riser_" +
1086 std::to_string(static_cast<uint8_t>(bbSlotNum)) +
1087 "_Mux/Pcie_Slot_" +
1088 std::to_string(static_cast<uint8_t>(riserSlotNum));
1089
1090 if (std::filesystem::exists(path) && std::filesystem::is_symlink(path))
1091 {
1092 i2cBus = std::filesystem::read_symlink(path);
1093 }
1094 else
1095 {
1096 phosphor::logging::log<phosphor::logging::level::ERR>(
1097 "Master write read command: Cannot get BusID");
1098 return ipmi::responseInvalidFieldRequest();
1099 }
1100 }
1101 else if (addressType == slotAddressTypeUniqueid)
1102 {
1103 i2cBus = "/dev/i2c-" +
1104 std::to_string(static_cast<uint8_t>(bbSlotNum) |
1105 (static_cast<uint8_t>(riserSlotNum) << 3));
1106 }
1107 else
1108 {
1109 phosphor::logging::log<phosphor::logging::level::ERR>(
1110 "Master write read command: invalid request");
1111 return ipmi::responseInvalidFieldRequest();
1112 }
1113
Zhikui Renad129c62022-04-05 20:11:24 -07001114 // Allow single byte write as it is offset byte to read the data, rest
1115 // allow only in Special mode.
Yong Lif267a672019-08-29 11:16:07 +08001116 if (writeCount > 1)
1117 {
Richard Marian Thomaiyarae13ac62019-12-17 15:45:12 +05301118 if (mtm.getMfgMode() == SpecialMode::none)
Yong Lif267a672019-08-29 11:16:07 +08001119 {
1120 return ipmi::responseInsufficientPrivilege();
1121 }
1122 }
1123
1124 if (readCount > slotI2CMaxReadSize)
1125 {
1126 phosphor::logging::log<phosphor::logging::level::ERR>(
1127 "Master write read command: Read count exceeds limit");
1128 return ipmi::responseParmOutOfRange();
1129 }
1130
1131 if (!readCount && !writeCount)
1132 {
1133 phosphor::logging::log<phosphor::logging::level::ERR>(
1134 "Master write read command: Read & write count are 0");
1135 return ipmi::responseInvalidFieldRequest();
1136 }
1137
1138 std::vector<uint8_t> readBuf(readCount);
1139
1140 ipmi::Cc retI2C = ipmi::i2cWriteRead(i2cBus, slaveAddr, writeData, readBuf);
1141 if (retI2C != ipmi::ccSuccess)
1142 {
1143 return ipmi::response(retI2C);
1144 }
1145
1146 return ipmi::responseSuccess(readBuf);
1147}
Yong Li068b4f22019-09-17 16:32:18 +08001148
1149ipmi::RspType<> clearCMOS()
1150{
Zhikui Renad129c62022-04-05 20:11:24 -07001151 // There is an i2c device on bus 4, the slave address is 0x38. Based on
1152 // the spec, writing 0x1 to address 0x61 on this device, will trigger
1153 // the clear CMOS action.
Yong Lid0d010b2019-11-18 12:15:21 +08001154 constexpr uint8_t slaveAddr = 0x38;
Yong Li068b4f22019-09-17 16:32:18 +08001155 std::string i2cBus = "/dev/i2c-4";
Yong Lieaeb6cb2020-03-09 20:21:50 +08001156 std::vector<uint8_t> writeData = {0x61, 0x1};
Yong Li068b4f22019-09-17 16:32:18 +08001157 std::vector<uint8_t> readBuf(0);
1158
Yong Li068b4f22019-09-17 16:32:18 +08001159 ipmi::Cc retI2C = ipmi::i2cWriteRead(i2cBus, slaveAddr, writeData, readBuf);
1160 return ipmi::response(retI2C);
1161}
Vernon Mauery27d23562021-08-12 10:43:39 -07001162
1163ipmi::RspType<> setFITcLayout(uint32_t layout)
1164{
1165 static constexpr const char* factoryFITcLayout =
1166 "/var/sofs/factory-settings/layout/fitc";
1167 std::filesystem::path fitcDir =
1168 std::filesystem::path(factoryFITcLayout).parent_path();
1169 std::error_code ec;
1170 std::filesystem::create_directories(fitcDir, ec);
1171 if (ec)
1172 {
1173 return ipmi::responseUnspecifiedError();
1174 }
1175 try
1176 {
1177 std::ofstream file(factoryFITcLayout);
1178 file << layout;
1179 file.flush();
1180 file.close();
1181 }
1182 catch (const std::exception& e)
1183 {
1184 return ipmi::responseUnspecifiedError();
1185 }
1186
1187 return ipmi::responseSuccess();
1188}
1189
Arun P. Mohanan06584cd2021-08-13 20:56:01 +05301190static std::vector<std::string>
1191 getMCTPServiceConfigPaths(ipmi::Context::ptr& ctx)
1192{
1193 boost::system::error_code ec;
1194 auto configPaths = ctx->bus->yield_method_call<std::vector<std::string>>(
1195 ctx->yield, ec, "xyz.openbmc_project.ObjectMapper",
1196 "/xyz/openbmc_project/object_mapper",
1197 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths",
1198 "/xyz/openbmc_project/inventory/system/board", 2,
1199 std::array<const char*, 1>{
1200 "xyz.openbmc_project.Configuration.MctpConfiguration"});
1201 if (ec)
1202 {
1203 throw std::runtime_error(
1204 "Failed to query configuration sub tree objects");
1205 }
1206 return configPaths;
1207}
1208
1209static ipmi::RspType<> startOrStopService(ipmi::Context::ptr& ctx,
1210 const uint8_t enable,
ANJALI RAY1fe485c2021-12-17 16:41:30 +00001211 const std::string& serviceName,
1212 bool disableOrEnableUnitFiles = true)
Arun P. Mohanan06584cd2021-08-13 20:56:01 +05301213{
1214 constexpr bool runtimeOnly = false;
1215 constexpr bool force = false;
1216
1217 boost::system::error_code ec;
1218 switch (enable)
1219 {
1220 case ipmi::SupportedFeatureActions::stop:
1221 ctx->bus->yield_method_call(ctx->yield, ec, systemDService,
1222 systemDObjPath, systemDMgrIntf,
1223 "StopUnit", serviceName, "replace");
1224 break;
1225 case ipmi::SupportedFeatureActions::start:
1226 ctx->bus->yield_method_call(ctx->yield, ec, systemDService,
1227 systemDObjPath, systemDMgrIntf,
1228 "StartUnit", serviceName, "replace");
1229 break;
1230 case ipmi::SupportedFeatureActions::disable:
ANJALI RAY1fe485c2021-12-17 16:41:30 +00001231 if (disableOrEnableUnitFiles == true)
1232 {
1233 ctx->bus->yield_method_call(
1234 ctx->yield, ec, systemDService, systemDObjPath,
1235 systemDMgrIntf, "DisableUnitFiles",
1236 std::array<const char*, 1>{serviceName.c_str()},
1237 runtimeOnly);
1238 }
Arun P. Mohanan06584cd2021-08-13 20:56:01 +05301239 ctx->bus->yield_method_call(
1240 ctx->yield, ec, systemDService, systemDObjPath, systemDMgrIntf,
1241 "MaskUnitFiles",
1242 std::array<const char*, 1>{serviceName.c_str()}, runtimeOnly,
1243 force);
Arun P. Mohanan06584cd2021-08-13 20:56:01 +05301244 break;
1245 case ipmi::SupportedFeatureActions::enable:
1246 ctx->bus->yield_method_call(
1247 ctx->yield, ec, systemDService, systemDObjPath, systemDMgrIntf,
1248 "UnmaskUnitFiles",
1249 std::array<const char*, 1>{serviceName.c_str()}, runtimeOnly);
ANJALI RAY1fe485c2021-12-17 16:41:30 +00001250 if (disableOrEnableUnitFiles == true)
1251 {
1252 ctx->bus->yield_method_call(
1253 ctx->yield, ec, systemDService, systemDObjPath,
1254 systemDMgrIntf, "EnableUnitFiles",
1255 std::array<const char*, 1>{serviceName.c_str()},
1256 runtimeOnly, force);
1257 }
Arun P. Mohanan06584cd2021-08-13 20:56:01 +05301258 break;
1259 default:
1260 phosphor::logging::log<phosphor::logging::level::WARNING>(
1261 "ERROR: Invalid feature action selected",
1262 phosphor::logging::entry("ACTION=%d", enable));
1263 return ipmi::responseInvalidFieldRequest();
1264 }
1265 if (ec)
1266 {
1267 phosphor::logging::log<phosphor::logging::level::WARNING>(
1268 "ERROR: Service start or stop failed",
1269 phosphor::logging::entry("SERVICE=%s", serviceName.c_str()));
1270 return ipmi::responseUnspecifiedError();
1271 }
1272 return ipmi::responseSuccess();
1273}
1274
1275static std::string getMCTPServiceName(const std::string& objectPath)
1276{
1277 const auto serviceArgument = boost::algorithm::replace_all_copy(
1278 boost::algorithm::replace_first_copy(
1279 objectPath, "/xyz/openbmc_project/inventory/system/board/", ""),
1280 "/", "_2f");
1281 std::string unitName =
1282 "xyz.openbmc_project.mctpd@" + serviceArgument + ".service";
1283 return unitName;
1284}
1285
1286static ipmi::RspType<> handleMCTPFeature(ipmi::Context::ptr& ctx,
1287 const uint8_t enable,
1288 const std::string& binding)
1289{
1290 std::vector<std::string> configPaths;
1291 try
1292 {
1293 configPaths = getMCTPServiceConfigPaths(ctx);
1294 }
1295 catch (const std::exception& e)
1296 {
1297 phosphor::logging::log<phosphor::logging::level::ERR>(e.what());
1298 return ipmi::responseUnspecifiedError();
1299 }
1300
1301 for (const auto& objectPath : configPaths)
1302 {
1303 auto const pos = objectPath.find_last_of('/');
1304 if (binding == objectPath.substr(pos + 1))
1305 {
1306 return startOrStopService(ctx, enable,
ANJALI RAY1fe485c2021-12-17 16:41:30 +00001307 getMCTPServiceName(objectPath), false);
Arun P. Mohanan06584cd2021-08-13 20:56:01 +05301308 }
1309 }
1310 return ipmi::responseSuccess();
1311}
1312
1313/** @brief implements MTM BMC Feature Control IPMI command which can be
1314 * used to enable or disable the supported BMC features.
1315 * @param yield - context object that represents the currently executing
1316 * coroutine
1317 * @param feature - feature enum to enable or disable
1318 * @param enable - enable or disable the feature
1319 * @param featureArg - custom arguments for that feature
1320 * @param reserved - reserved for future use
1321 *
1322 * @returns IPMI completion code
1323 */
1324ipmi::RspType<> mtmBMCFeatureControl(ipmi::Context::ptr ctx,
1325 const uint8_t feature,
1326 const uint8_t enable,
1327 const uint8_t featureArg,
1328 const uint16_t reserved)
1329{
1330 if (reserved != 0)
1331 {
1332 return ipmi::responseInvalidFieldRequest();
1333 }
1334
1335 switch (feature)
1336 {
1337 case ipmi::SupportedFeatureControls::mctp:
1338 switch (featureArg)
1339 {
1340 case ipmi::SupportedMCTPBindings::mctpPCIe:
1341 return handleMCTPFeature(ctx, enable, "MCTP_PCIe");
1342 case ipmi::SupportedMCTPBindings::mctpSMBusHSBP:
1343 return handleMCTPFeature(ctx, enable, "MCTP_SMBus_HSBP");
1344 case ipmi::SupportedMCTPBindings::mctpSMBusPCIeSlot:
1345 return handleMCTPFeature(ctx, enable,
1346 "MCTP_SMBus_PCIe_slot");
1347 default:
1348 return ipmi::responseInvalidFieldRequest();
1349 }
1350 break;
Jason M. Bills5cb2c042021-08-17 12:03:39 -07001351 case ipmi::SupportedFeatureControls::pcieScan:
1352 if (featureArg != 0)
1353 {
1354 return ipmi::responseInvalidFieldRequest();
1355 }
1356 startOrStopService(ctx, enable, "xyz.openbmc_project.PCIe.service");
1357 break;
Arun P. Mohanan06584cd2021-08-13 20:56:01 +05301358 default:
1359 return ipmi::responseInvalidFieldRequest();
1360 }
1361 return ipmi::responseSuccess();
1362}
Vernon Mauerya3702c12019-05-22 13:20:59 -07001363} // namespace ipmi
1364
1365void register_mtm_commands() __attribute__((constructor));
1366void register_mtm_commands()
1367{
Jason M. Bills38d2b5a2019-06-03 16:26:11 -07001368 // <Get SM Signal>
Vernon Mauery98bbf692019-09-16 11:14:59 -07001369 ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnGeneral,
1370 ipmi::intel::general::cmdGetSmSignal,
1371 ipmi::Privilege::Admin, ipmi::appMTMGetSignal);
Vernon Mauerya3702c12019-05-22 13:20:59 -07001372
Vernon Mauery98bbf692019-09-16 11:14:59 -07001373 ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnGeneral,
1374 ipmi::intel::general::cmdSetSmSignal,
1375 ipmi::Privilege::Admin, ipmi::appMTMSetSignal);
Vernon Mauerya3702c12019-05-22 13:20:59 -07001376
Vernon Mauery98bbf692019-09-16 11:14:59 -07001377 ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnGeneral,
1378 ipmi::intel::general::cmdMtmKeepAlive,
1379 ipmi::Privilege::Admin, ipmi::mtmKeepAlive);
Richard Marian Thomaiyar666dd012019-08-02 20:55:37 +05301380
Vernon Mauery98bbf692019-09-16 11:14:59 -07001381 ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnGeneral,
1382 ipmi::intel::general::cmdSetManufacturingData,
1383 ipmi::Privilege::Admin, ipmi::setManufacturingData);
Richard Marian Thomaiyar1f0839c2019-08-25 20:10:52 +05301384
Vernon Mauery98bbf692019-09-16 11:14:59 -07001385 ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnGeneral,
1386 ipmi::intel::general::cmdGetManufacturingData,
1387 ipmi::Privilege::Admin, ipmi::getManufacturingData);
Richard Marian Thomaiyar1f0839c2019-08-25 20:10:52 +05301388
Vernon Mauery27d23562021-08-12 10:43:39 -07001389 ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnGeneral,
1390 ipmi::intel::general::cmdSetFITcLayout,
1391 ipmi::Privilege::Admin, ipmi::setFITcLayout);
1392
Arun P. Mohanan06584cd2021-08-13 20:56:01 +05301393 ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnGeneral,
1394 ipmi::intel::general::cmdMTMBMCFeatureControl,
1395 ipmi::Privilege::Admin, ipmi::mtmBMCFeatureControl);
1396
Vernon Mauery98bbf692019-09-16 11:14:59 -07001397 ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnApp,
1398 ipmi::intel::general::cmdSlotI2CMasterWriteRead,
1399 ipmi::Privilege::Admin,
1400 ipmi::appSlotI2CMasterWriteRead);
Yong Lif267a672019-08-29 11:16:07 +08001401
Yong Li068b4f22019-09-17 16:32:18 +08001402 ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnPlatform,
1403 ipmi::intel::platform::cmdClearCMOS,
1404 ipmi::Privilege::Admin, ipmi::clearCMOS);
1405
Vernon Mauery98bbf692019-09-16 11:14:59 -07001406 ipmi::registerFilter(ipmi::prioOemBase,
Yong Li85feb132019-08-09 16:06:03 +08001407 [](ipmi::message::Request::ptr request) {
1408 return ipmi::mfgFilterMessage(request);
1409 });
Vernon Mauerya3702c12019-05-22 13:20:59 -07001410}