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