blob: 3104c3e57b49ea87d8a428e6e1904c17ce73e9e3 [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>
Alex Schendel4180cfe2022-11-29 10:46:11 -080024#include <phosphor-logging/lg2.hpp>
Jason M. Bills0748c692022-09-08 15:34:08 -070025#include <types.hpp>
Vernon Mauerya3702c12019-05-22 13:20:59 -070026
James Feistfcd2d3a2020-05-28 10:38:15 -070027#include <filesystem>
28#include <fstream>
29
Vernon Mauerya3702c12019-05-22 13:20:59 -070030namespace ipmi
31{
32
33Manufacturing mtm;
34
35static auto revertTimeOut =
36 std::chrono::duration_cast<std::chrono::microseconds>(
37 std::chrono::seconds(60)); // 1 minute timeout
38
V-Sanjana7acb2d22022-11-15 11:13:54 +053039static constexpr uint8_t bbRiserMux = 0;
40static constexpr uint8_t leftRiserMux = 1;
41static constexpr uint8_t rightRiserMux = 2;
42static constexpr uint8_t pcieMux = 3;
43static constexpr uint8_t hsbpMux = 4;
44
Yong Lif267a672019-08-29 11:16:07 +080045static constexpr uint8_t slotAddressTypeBus = 0;
46static constexpr uint8_t slotAddressTypeUniqueid = 1;
47static constexpr uint8_t slotI2CMaxReadSize = 35;
48
Vernon Mauerya3702c12019-05-22 13:20:59 -070049static constexpr const char* callbackMgrService =
50 "xyz.openbmc_project.CallbackManager";
51static constexpr const char* callbackMgrIntf =
52 "xyz.openbmc_project.CallbackManager";
53static constexpr const char* callbackMgrObjPath =
54 "/xyz/openbmc_project/CallbackManager";
55static constexpr const char* retriggerLedUpdate = "RetriggerLEDUpdate";
56
57const static constexpr char* systemDService = "org.freedesktop.systemd1";
58const static constexpr char* systemDObjPath = "/org/freedesktop/systemd1";
59const static constexpr char* systemDMgrIntf =
60 "org.freedesktop.systemd1.Manager";
61const static constexpr char* pidControlService = "phosphor-pid-control.service";
62
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +053063static inline Cc resetMtmTimer(ipmi::Context::ptr ctx)
Richard Marian Thomaiyar666dd012019-08-02 20:55:37 +053064{
Richard Marian Thomaiyar666dd012019-08-02 20:55:37 +053065 boost::system::error_code ec;
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +053066 ctx->bus->yield_method_call<>(ctx->yield, ec, specialModeService,
67 specialModeObjPath, specialModeIntf,
68 "ResetTimer");
Richard Marian Thomaiyar666dd012019-08-02 20:55:37 +053069 if (ec)
70 {
Alex Schendel4180cfe2022-11-29 10:46:11 -080071 lg2::error("Failed to reset the manufacturing mode timer");
Richard Marian Thomaiyar666dd012019-08-02 20:55:37 +053072 return ccUnspecifiedError;
73 }
74 return ccSuccess;
75}
76
Jason M. Bills38d2b5a2019-06-03 16:26:11 -070077int getGpioPathForSmSignal(const SmSignalGet signal, std::string& path)
Vernon Mauerya3702c12019-05-22 13:20:59 -070078{
Jason M. Bills38d2b5a2019-06-03 16:26:11 -070079 switch (signal)
80 {
81 case SmSignalGet::smPowerButton:
82 path = "/xyz/openbmc_project/chassis/buttons/power";
83 break;
84 case SmSignalGet::smResetButton:
85 path = "/xyz/openbmc_project/chassis/buttons/reset";
86 break;
87 case SmSignalGet::smNMIButton:
88 path = "/xyz/openbmc_project/chassis/buttons/nmi";
89 break;
Richard Marian Thomaiyar8e5e2b02019-08-01 07:50:55 +053090 case SmSignalGet::smIdentifyButton:
91 path = "/xyz/openbmc_project/chassis/buttons/id";
92 break;
Jason M. Bills38d2b5a2019-06-03 16:26:11 -070093 default:
94 return -1;
95 break;
96 }
97 return 0;
Vernon Mauerya3702c12019-05-22 13:20:59 -070098}
99
Patrick Venture37890392019-09-25 17:05:03 -0700100ipmi_ret_t ledStoreAndSet(SmSignalSet signal, const std::string& setState)
Vernon Mauerya3702c12019-05-22 13:20:59 -0700101{
102 LedProperty* ledProp = mtm.findLedProperty(signal);
103 if (ledProp == nullptr)
104 {
105 return IPMI_CC_INVALID_FIELD_REQUEST;
106 }
107
108 std::string ledName = ledProp->getName();
109 std::string ledService = ledServicePrefix + ledName;
110 std::string ledPath = ledPathPrefix + ledName;
111 ipmi::Value presentState;
112
113 if (false == ledProp->getLock())
114 {
115 if (mtm.getProperty(ledService.c_str(), ledPath.c_str(), ledIntf,
116 "State", &presentState) != 0)
117 {
118 return IPMI_CC_UNSPECIFIED_ERROR;
119 }
120 ledProp->setPrevState(std::get<std::string>(presentState));
121 ledProp->setLock(true);
122 if (signal == SmSignalSet::smPowerFaultLed ||
123 signal == SmSignalSet::smSystemReadyLed)
124 {
125 mtm.revertLedCallback = true;
126 }
127 }
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700128 if (mtm.setProperty(ledService, ledPath, ledIntf, "State",
Vernon Mauerya3702c12019-05-22 13:20:59 -0700129 ledStateStr + setState) != 0)
130 {
131 return IPMI_CC_UNSPECIFIED_ERROR;
132 }
133 return IPMI_CC_OK;
134}
135
136ipmi_ret_t ledRevert(SmSignalSet signal)
137{
138 LedProperty* ledProp = mtm.findLedProperty(signal);
139 if (ledProp == nullptr)
140 {
141 return IPMI_CC_INVALID_FIELD_REQUEST;
142 }
143 if (true == ledProp->getLock())
144 {
145 ledProp->setLock(false);
146 if (signal == SmSignalSet::smPowerFaultLed ||
147 signal == SmSignalSet::smSystemReadyLed)
148 {
149 try
150 {
151 ipmi::method_no_args::callDbusMethod(
152 *getSdBus(), callbackMgrService, callbackMgrObjPath,
153 callbackMgrIntf, retriggerLedUpdate);
154 }
Patrick Williamsbd51e6a2021-10-06 13:09:44 -0500155 catch (const sdbusplus::exception_t& e)
Vernon Mauerya3702c12019-05-22 13:20:59 -0700156 {
157 return IPMI_CC_UNSPECIFIED_ERROR;
158 }
159 mtm.revertLedCallback = false;
160 }
161 else
162 {
163 std::string ledName = ledProp->getName();
164 std::string ledService = ledServicePrefix + ledName;
165 std::string ledPath = ledPathPrefix + ledName;
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700166 if (mtm.setProperty(ledService, ledPath, ledIntf, "State",
167 ledProp->getPrevState()) != 0)
Vernon Mauerya3702c12019-05-22 13:20:59 -0700168 {
169 return IPMI_CC_UNSPECIFIED_ERROR;
170 }
171 }
172 }
173 return IPMI_CC_OK;
174}
175
176void Manufacturing::initData()
177{
Vernon Mauerya3702c12019-05-22 13:20:59 -0700178 ledPropertyList.push_back(
179 LedProperty(SmSignalSet::smPowerFaultLed, "status_amber"));
180 ledPropertyList.push_back(
181 LedProperty(SmSignalSet::smSystemReadyLed, "status_green"));
182 ledPropertyList.push_back(
183 LedProperty(SmSignalSet::smIdentifyLed, "identify"));
184}
185
186void Manufacturing::revertTimerHandler()
187{
Richard Marian Thomaiyarae13ac62019-12-17 15:45:12 +0530188#ifdef BMC_VALIDATION_UNSECURE_FEATURE
189 if (mtm.getMfgMode() == SpecialMode::valUnsecure)
190 {
191 // Don't revert the behaviour for validation unsecure mode.
192 return;
193 }
194#endif
Vernon Mauerya3702c12019-05-22 13:20:59 -0700195 if (revertFanPWM)
196 {
197 revertFanPWM = false;
198 disablePidControlService(false);
199 }
200
Ayushi Smritid872a4a2019-10-15 10:20:11 +0530201 if (mtmTestBeepFd != -1)
202 {
203 ::close(mtmTestBeepFd);
204 mtmTestBeepFd = -1;
205 }
206
Vernon Mauerya3702c12019-05-22 13:20:59 -0700207 for (const auto& ledProperty : ledPropertyList)
208 {
209 const std::string& ledName = ledProperty.getName();
Jayaprakash Mutyalaf3656142021-01-24 01:04:19 +0000210 if (ledName == "identify" && mtm.getMfgMode() == SpecialMode::mfg)
211 {
212 // Don't revert the behaviour for manufacturing mode
213 continue;
214 }
Vernon Mauerya3702c12019-05-22 13:20:59 -0700215 ledRevert(ledProperty.getSignal());
216 }
217}
218
219Manufacturing::Manufacturing() :
220 revertTimer([&](void) { revertTimerHandler(); })
221{
222 initData();
223}
224
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700225int8_t Manufacturing::getProperty(const std::string& service,
226 const std::string& path,
227 const std::string& interface,
228 const std::string& propertyName,
229 ipmi::Value* reply)
Vernon Mauerya3702c12019-05-22 13:20:59 -0700230{
231 try
232 {
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700233 *reply = ipmi::getDbusProperty(*getSdBus(), service, path, interface,
234 propertyName);
Vernon Mauerya3702c12019-05-22 13:20:59 -0700235 }
Patrick Williamsf944d2e2022-07-22 19:26:52 -0500236 catch (const sdbusplus::exception_t& e)
Vernon Mauerya3702c12019-05-22 13:20:59 -0700237 {
Alex Schendel4180cfe2022-11-29 10:46:11 -0800238 lg2::info("ERROR: getProperty");
Vernon Mauerya3702c12019-05-22 13:20:59 -0700239 return -1;
240 }
241
242 return 0;
243}
244
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700245int8_t Manufacturing::setProperty(const std::string& service,
246 const std::string& path,
247 const std::string& interface,
248 const std::string& propertyName,
249 ipmi::Value value)
Vernon Mauerya3702c12019-05-22 13:20:59 -0700250{
251 try
252 {
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700253 ipmi::setDbusProperty(*getSdBus(), service, path, interface,
Vernon Mauerya3702c12019-05-22 13:20:59 -0700254 propertyName, value);
255 }
Patrick Williamsf944d2e2022-07-22 19:26:52 -0500256 catch (const sdbusplus::exception_t& e)
Vernon Mauerya3702c12019-05-22 13:20:59 -0700257 {
Alex Schendel4180cfe2022-11-29 10:46:11 -0800258 lg2::info("ERROR: setProperty");
Vernon Mauerya3702c12019-05-22 13:20:59 -0700259 return -1;
260 }
261
262 return 0;
263}
264
265int8_t Manufacturing::disablePidControlService(const bool disable)
266{
267 try
268 {
269 auto dbus = getSdBus();
270 auto method = dbus->new_method_call(systemDService, systemDObjPath,
271 systemDMgrIntf,
272 disable ? "StopUnit" : "StartUnit");
273 method.append(pidControlService, "replace");
274 auto reply = dbus->call(method);
275 }
Patrick Williamsf944d2e2022-07-22 19:26:52 -0500276 catch (const sdbusplus::exception_t& e)
Vernon Mauerya3702c12019-05-22 13:20:59 -0700277 {
Alex Schendel4180cfe2022-11-29 10:46:11 -0800278 lg2::info("ERROR: phosphor-pid-control service start or stop failed");
Vernon Mauerya3702c12019-05-22 13:20:59 -0700279 return -1;
280 }
281 return 0;
282}
283
Alex Schendel90eb7872022-09-01 11:56:52 -0700284static bool findPwmName(ipmi::Context::ptr& ctx, uint8_t instance,
285 std::string& pwmName)
286{
287 boost::system::error_code ec{};
288 ObjectValueTree obj;
289
290 // GetAll the objects under service FruDevice
291 ec = getManagedObjects(ctx, "xyz.openbmc_project.EntityManager",
292 "/xyz/openbmc_project/inventory", obj);
293 if (ec)
294 {
Alex Schendel4180cfe2022-11-29 10:46:11 -0800295 lg2::error("GetMangagedObjects failed", "ERROR", ec.message().c_str());
Alex Schendel90eb7872022-09-01 11:56:52 -0700296 return false;
297 }
298 for (const auto& [path, objData] : obj)
299 {
300 for (const auto& [intf, propMap] : objData)
301 {
302 // Currently, these are the three different fan types supported.
303 if (intf == "xyz.openbmc_project.Configuration.AspeedFan" ||
304 intf == "xyz.openbmc_project.Configuration.I2CFan" ||
305 intf == "xyz.openbmc_project.Configuration.NuvotonFan")
306 {
Alex Schendel4180cfe2022-11-29 10:46:11 -0800307 std::string fanPath = "/Fan_";
Alex Schendel90eb7872022-09-01 11:56:52 -0700308
Alex Schendel4180cfe2022-11-29 10:46:11 -0800309 fanPath += std::to_string(instance);
310 std::string objPath = path.str;
311 objPath = objPath.substr(objPath.find_last_of("/"));
312 if (objPath != fanPath)
Alex Schendel90eb7872022-09-01 11:56:52 -0700313 {
314 continue;
315 }
316 auto connector = objData.find(intf + std::string(".Connector"));
317 if (connector == objData.end())
318 {
319 return false;
320 }
321 auto findPwmName = connector->second.find("PwmName");
322 if (findPwmName != connector->second.end())
323 {
324 auto fanPwmName =
325 std::get_if<std::string>(&findPwmName->second);
326 if (!fanPwmName)
327 {
Alex Schendel4180cfe2022-11-29 10:46:11 -0800328 lg2::error("PwmName parse ERROR.");
Alex Schendel90eb7872022-09-01 11:56:52 -0700329 return false;
330 }
331 pwmName = *fanPwmName;
332 return true;
333 }
334 auto findPwm = connector->second.find("Pwm");
335 if (findPwm == connector->second.end())
336 {
337 return false;
338 }
339 auto fanPwm = std::get_if<uint64_t>(&findPwm->second);
340 if (!fanPwm)
341 {
342 return false;
343 }
344 pwmName = "Pwm_" + std::to_string(*fanPwm + 1);
345 return true;
346 }
347 }
348 }
349 return false;
350}
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700351ipmi::RspType<uint8_t, // Signal value
352 std::optional<uint16_t> // Fan tach value
353 >
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +0530354 appMTMGetSignal(ipmi::Context::ptr ctx, uint8_t signalTypeByte,
Richard Marian Thomaiyar147daec2019-06-15 07:43:48 +0530355 uint8_t instance, uint8_t actionByte)
Vernon Mauerya3702c12019-05-22 13:20:59 -0700356{
Ayushi Smritie0511e52019-08-27 17:30:34 +0000357 // mfg filter logic is used to allow MTM get signal command only in
358 // manfacturing mode.
Vernon Mauerya3702c12019-05-22 13:20:59 -0700359
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700360 SmSignalGet signalType = static_cast<SmSignalGet>(signalTypeByte);
361 SmActionGet action = static_cast<SmActionGet>(actionByte);
362
363 switch (signalType)
364 {
anil kumar appana98705b32019-09-11 14:15:50 +0000365 case SmSignalGet::smChassisIntrusion:
366 {
367 ipmi::Value reply;
368 if (mtm.getProperty(intrusionService, intrusionPath, intrusionIntf,
369 "Status", &reply) < 0)
370 {
371 return ipmi::responseInvalidFieldRequest();
372 }
373 std::string* intrusionStatus = std::get_if<std::string>(&reply);
374 if (!intrusionStatus)
375 {
376 return ipmi::responseUnspecifiedError();
377 }
378
379 uint8_t status = 0;
380 if (!intrusionStatus->compare("Normal"))
381 {
382 status = static_cast<uint8_t>(IntrusionStatus::normal);
383 }
384 else if (!intrusionStatus->compare("HardwareIntrusion"))
385 {
386 status =
387 static_cast<uint8_t>(IntrusionStatus::hardwareIntrusion);
388 }
389 else if (!intrusionStatus->compare("TamperingDetected"))
390 {
391 status =
392 static_cast<uint8_t>(IntrusionStatus::tamperingDetected);
393 }
394 else
395 {
396 return ipmi::responseUnspecifiedError();
397 }
398 return ipmi::responseSuccess(status, std::nullopt);
399 }
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700400 case SmSignalGet::smFanPwmGet:
401 {
402 ipmi::Value reply;
Alex Schendel90eb7872022-09-01 11:56:52 -0700403 std::string pwmName, fullPath;
Alex Schendel4180cfe2022-11-29 10:46:11 -0800404 if (!findPwmName(ctx, instance + 1, pwmName))
Alex Schendel90eb7872022-09-01 11:56:52 -0700405 {
406 // The default PWM name is Pwm_#
407 pwmName = "Pwm_" + std::to_string(instance + 1);
408 }
409 fullPath = fanPwmPath + pwmName;
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700410 if (mtm.getProperty(fanService, fullPath, fanIntf, "Value",
411 &reply) < 0)
412 {
413 return ipmi::responseInvalidFieldRequest();
414 }
415 double* doubleVal = std::get_if<double>(&reply);
416 if (doubleVal == nullptr)
417 {
418 return ipmi::responseUnspecifiedError();
419 }
420 uint8_t sensorVal = std::round(*doubleVal);
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +0530421 resetMtmTimer(ctx);
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700422 return ipmi::responseSuccess(sensorVal, std::nullopt);
423 }
424 break;
425 case SmSignalGet::smFanTachometerGet:
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700426 {
Richard Marian Thomaiyar147daec2019-06-15 07:43:48 +0530427 boost::system::error_code ec;
428 using objFlatMap = boost::container::flat_map<
429 std::string, boost::container::flat_map<
430 std::string, std::vector<std::string>>>;
431
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +0530432 auto flatMap = ctx->bus->yield_method_call<objFlatMap>(
433 ctx->yield, ec, "xyz.openbmc_project.ObjectMapper",
Richard Marian Thomaiyar147daec2019-06-15 07:43:48 +0530434 "/xyz/openbmc_project/object_mapper",
435 "xyz.openbmc_project.ObjectMapper", "GetSubTree",
436 fanTachBasePath, 0, std::array<const char*, 1>{fanIntf});
437 if (ec)
438 {
Alex Schendel4180cfe2022-11-29 10:46:11 -0800439 lg2::error("Failed to query fan tach sub tree objects");
Richard Marian Thomaiyar147daec2019-06-15 07:43:48 +0530440 return ipmi::responseUnspecifiedError();
441 }
442 if (instance >= flatMap.size())
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700443 {
444 return ipmi::responseInvalidFieldRequest();
445 }
Richard Marian Thomaiyar147daec2019-06-15 07:43:48 +0530446 auto itr = flatMap.nth(instance);
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700447 ipmi::Value reply;
Richard Marian Thomaiyar147daec2019-06-15 07:43:48 +0530448 if (mtm.getProperty(fanService, itr->first, fanIntf, "Value",
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700449 &reply) < 0)
450 {
451 return ipmi::responseInvalidFieldRequest();
452 }
453
454 double* doubleVal = std::get_if<double>(&reply);
455 if (doubleVal == nullptr)
456 {
457 return ipmi::responseUnspecifiedError();
458 }
459 uint8_t sensorVal = FAN_PRESENT | FAN_SENSOR_PRESENT;
460 std::optional<uint16_t> fanTach = std::round(*doubleVal);
461
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +0530462 resetMtmTimer(ctx);
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700463 return ipmi::responseSuccess(sensorVal, fanTach);
464 }
465 break;
Richard Marian Thomaiyar8e5e2b02019-08-01 07:50:55 +0530466 case SmSignalGet::smIdentifyButton:
467 {
468 if (action == SmActionGet::revert || action == SmActionGet::ignore)
469 {
470 // ButtonMasked property is not supported for ID button as it is
471 // unnecessary. Hence if requested for revert / ignore, override
472 // it to sample action to make tools happy.
473 action = SmActionGet::sample;
474 }
475 // fall-through
476 }
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700477 case SmSignalGet::smResetButton:
478 case SmSignalGet::smPowerButton:
479 case SmSignalGet::smNMIButton:
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700480 {
481 std::string path;
482 if (getGpioPathForSmSignal(signalType, path) < 0)
483 {
484 return ipmi::responseInvalidFieldRequest();
485 }
486
487 switch (action)
488 {
489 case SmActionGet::sample:
Alex Schendel4180cfe2022-11-29 10:46:11 -0800490 lg2::info("case SmActionGet::sample");
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700491 break;
492 case SmActionGet::ignore:
493 {
Alex Schendel4180cfe2022-11-29 10:46:11 -0800494 lg2::info("case SmActionGet::ignore");
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700495 if (mtm.setProperty(buttonService, path, buttonIntf,
496 "ButtonMasked", true) < 0)
497 {
498 return ipmi::responseUnspecifiedError();
499 }
500 }
501 break;
502 case SmActionGet::revert:
503 {
Alex Schendel4180cfe2022-11-29 10:46:11 -0800504 lg2::info("case SmActionGet::revert");
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700505 if (mtm.setProperty(buttonService, path, buttonIntf,
506 "ButtonMasked", false) < 0)
507 {
508 return ipmi::responseUnspecifiedError();
509 }
510 }
511 break;
512
513 default:
514 return ipmi::responseInvalidFieldRequest();
515 break;
516 }
517
518 ipmi::Value reply;
519 if (mtm.getProperty(buttonService, path, buttonIntf,
520 "ButtonPressed", &reply) < 0)
521 {
522 return ipmi::responseUnspecifiedError();
523 }
524 bool* valPtr = std::get_if<bool>(&reply);
525 if (valPtr == nullptr)
526 {
527 return ipmi::responseUnspecifiedError();
528 }
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +0530529 resetMtmTimer(ctx);
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700530 uint8_t sensorVal = *valPtr;
531 return ipmi::responseSuccess(sensorVal, std::nullopt);
532 }
533 break;
Richard Marian Thomaiyar1b74a212019-08-03 13:26:17 +0530534 case SmSignalGet::smNcsiDiag:
535 {
536 constexpr const char* netBasePath = "/sys/class/net/eth";
537 constexpr const char* carrierSuffix = "/carrier";
538 std::ifstream netIfs(netBasePath + std::to_string(instance) +
539 carrierSuffix);
540 if (!netIfs.good())
541 {
542 return ipmi::responseInvalidFieldRequest();
543 }
544 std::string carrier;
545 netIfs >> carrier;
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +0530546 resetMtmTimer(ctx);
Richard Marian Thomaiyar1b74a212019-08-03 13:26:17 +0530547 return ipmi::responseSuccess(
548 static_cast<uint8_t>(std::stoi(carrier)), std::nullopt);
549 }
550 break;
Vernon Mauerya3702c12019-05-22 13:20:59 -0700551 default:
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700552 return ipmi::responseInvalidFieldRequest();
Vernon Mauerya3702c12019-05-22 13:20:59 -0700553 break;
554 }
Vernon Mauerya3702c12019-05-22 13:20:59 -0700555}
556
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +0530557ipmi::RspType<> appMTMSetSignal(ipmi::Context::ptr ctx, uint8_t signalTypeByte,
558 uint8_t instance, uint8_t actionByte,
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000559 std::optional<uint8_t> pwmSpeed)
Vernon Mauerya3702c12019-05-22 13:20:59 -0700560{
Ayushi Smritie0511e52019-08-27 17:30:34 +0000561 // mfg filter logic is used to allow MTM set signal command only in
562 // manfacturing mode.
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000563
564 SmSignalSet signalType = static_cast<SmSignalSet>(signalTypeByte);
565 SmActionSet action = static_cast<SmActionSet>(actionByte);
566 Cc retCode = ccSuccess;
567 int8_t ret = 0;
568
569 switch (signalType)
570 {
571 case SmSignalSet::smPowerFaultLed:
572 case SmSignalSet::smSystemReadyLed:
573 case SmSignalSet::smIdentifyLed:
574 switch (action)
575 {
576 case SmActionSet::forceDeasserted:
Vernon Mauerya3702c12019-05-22 13:20:59 -0700577 {
Alex Schendel4180cfe2022-11-29 10:46:11 -0800578 lg2::info("case SmActionSet::forceDeasserted");
Vernon Mauerya3702c12019-05-22 13:20:59 -0700579
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000580 retCode = ledStoreAndSet(signalType, std::string("Off"));
581 if (retCode != ccSuccess)
Vernon Mauerya3702c12019-05-22 13:20:59 -0700582 {
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000583 return ipmi::response(retCode);
Vernon Mauerya3702c12019-05-22 13:20:59 -0700584 }
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000585 mtm.revertTimer.start(revertTimeOut);
Vernon Mauerya3702c12019-05-22 13:20:59 -0700586 }
587 break;
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000588 case SmActionSet::forceAsserted:
Vernon Mauerya3702c12019-05-22 13:20:59 -0700589 {
Alex Schendel4180cfe2022-11-29 10:46:11 -0800590 lg2::info("case SmActionSet::forceAsserted");
Vernon Mauerya3702c12019-05-22 13:20:59 -0700591
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000592 retCode = ledStoreAndSet(signalType, std::string("On"));
593 if (retCode != ccSuccess)
594 {
595 return ipmi::response(retCode);
596 }
597 mtm.revertTimer.start(revertTimeOut);
598 if (SmSignalSet::smPowerFaultLed == signalType)
599 {
600 // Deassert "system ready"
601 retCode = ledStoreAndSet(SmSignalSet::smSystemReadyLed,
602 std::string("Off"));
603 }
604 else if (SmSignalSet::smSystemReadyLed == signalType)
605 {
606 // Deassert "fault led"
607 retCode = ledStoreAndSet(SmSignalSet::smPowerFaultLed,
608 std::string("Off"));
609 }
610 }
611 break;
612 case SmActionSet::revert:
613 {
Alex Schendel4180cfe2022-11-29 10:46:11 -0800614 lg2::info("case SmActionSet::revert");
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000615 retCode = ledRevert(signalType);
616 }
617 break;
618 default:
619 {
620 return ipmi::responseInvalidFieldRequest();
621 }
622 }
623 break;
624 case SmSignalSet::smFanPowerSpeed:
625 {
626 if ((action == SmActionSet::forceAsserted) && (!pwmSpeed))
627 {
628 return ipmi::responseReqDataLenInvalid();
629 }
630
631 if ((action == SmActionSet::forceAsserted) && (*pwmSpeed > 100))
632 {
633 return ipmi::responseInvalidFieldRequest();
634 }
635
636 uint8_t pwmValue = 0;
637 switch (action)
638 {
639 case SmActionSet::revert:
640 {
641 if (mtm.revertFanPWM)
642 {
643 ret = mtm.disablePidControlService(false);
Vernon Mauerya3702c12019-05-22 13:20:59 -0700644 if (ret < 0)
645 {
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000646 return ipmi::responseUnspecifiedError();
Vernon Mauerya3702c12019-05-22 13:20:59 -0700647 }
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000648 mtm.revertFanPWM = false;
Vernon Mauerya3702c12019-05-22 13:20:59 -0700649 }
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000650 }
651 break;
652 case SmActionSet::forceAsserted:
653 {
654 pwmValue = *pwmSpeed;
655 } // fall-through
656 case SmActionSet::forceDeasserted:
657 {
658 if (!mtm.revertFanPWM)
Vernon Mauerya3702c12019-05-22 13:20:59 -0700659 {
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000660 ret = mtm.disablePidControlService(true);
661 if (ret < 0)
662 {
663 return ipmi::responseUnspecifiedError();
664 }
665 mtm.revertFanPWM = true;
Vernon Mauerya3702c12019-05-22 13:20:59 -0700666 }
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000667 mtm.revertTimer.start(revertTimeOut);
Alex Schendel90eb7872022-09-01 11:56:52 -0700668 std::string pwmName, fanPwmInstancePath;
Alex Schendel4180cfe2022-11-29 10:46:11 -0800669 if (!findPwmName(ctx, instance + 1, pwmName))
Alex Schendel90eb7872022-09-01 11:56:52 -0700670 {
671 pwmName = "Pwm_" + std::to_string(instance + 1);
672 }
673 fanPwmInstancePath = fanPwmPath + pwmName;
Patrick Williamsb37abfb2023-05-10 07:50:33 -0500674 ret = mtm.setProperty(fanService, fanPwmInstancePath,
675 fanIntf, "Value",
676 static_cast<double>(pwmValue));
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000677 if (ret < 0)
678 {
679 return ipmi::responseUnspecifiedError();
680 }
681 }
682 break;
683 default:
684 {
685 return ipmi::responseInvalidFieldRequest();
Vernon Mauerya3702c12019-05-22 13:20:59 -0700686 }
687 }
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000688 }
689 break;
Ayushi Smritid872a4a2019-10-15 10:20:11 +0530690 case SmSignalSet::smSpeaker:
691 {
Alex Schendel4180cfe2022-11-29 10:46:11 -0800692 lg2::info("Performing Speaker SmActionSet", "ACTION", lg2::dec,
693 static_cast<uint8_t>(action));
Ayushi Smritid872a4a2019-10-15 10:20:11 +0530694 switch (action)
695 {
696 case SmActionSet::forceAsserted:
697 {
698 char beepDevName[] = "/dev/input/event0";
699 if (mtm.mtmTestBeepFd != -1)
700 {
Alex Schendel4180cfe2022-11-29 10:46:11 -0800701 lg2::info("mtm beep device is opened already!");
Ayushi Smritid872a4a2019-10-15 10:20:11 +0530702 // returning success as already beep is in progress
703 return ipmi::response(retCode);
704 }
705
Patrick Williamsb37abfb2023-05-10 07:50:33 -0500706 if ((mtm.mtmTestBeepFd = ::open(beepDevName,
707 O_RDWR | O_CLOEXEC)) < 0)
Ayushi Smritid872a4a2019-10-15 10:20:11 +0530708 {
Alex Schendel4180cfe2022-11-29 10:46:11 -0800709 lg2::error("Failed to open input device");
Ayushi Smritid872a4a2019-10-15 10:20:11 +0530710 return ipmi::responseUnspecifiedError();
711 }
712
713 struct input_event event;
714 event.type = EV_SND;
715 event.code = SND_TONE;
716 event.value = 2000;
717
718 if (::write(mtm.mtmTestBeepFd, &event,
719 sizeof(struct input_event)) !=
720 sizeof(struct input_event))
721 {
Alex Schendel4180cfe2022-11-29 10:46:11 -0800722 lg2::error("Failed to write a tone sound event");
Ayushi Smritid872a4a2019-10-15 10:20:11 +0530723 ::close(mtm.mtmTestBeepFd);
724 mtm.mtmTestBeepFd = -1;
725 return ipmi::responseUnspecifiedError();
726 }
727 mtm.revertTimer.start(revertTimeOut);
728 }
729 break;
730 case SmActionSet::revert:
731 case SmActionSet::forceDeasserted:
732 {
733 if (mtm.mtmTestBeepFd != -1)
734 {
735 ::close(mtm.mtmTestBeepFd);
736 mtm.mtmTestBeepFd = -1;
737 }
738 }
739 break;
740 default:
741 {
742 return ipmi::responseInvalidFieldRequest();
743 }
744 }
745 }
746 break;
Richard Marian Thomaiyar3594c6d2019-11-19 21:29:04 +0530747 case SmSignalSet::smDiskFaultLed:
748 {
749 boost::system::error_code ec;
750 using objPaths = std::vector<std::string>;
751 std::string driveBasePath =
752 "/xyz/openbmc_project/inventory/item/drive/";
753 static constexpr const char* driveLedIntf =
754 "xyz.openbmc_project.Led.Group";
755 static constexpr const char* hsbpService =
756 "xyz.openbmc_project.HsbpManager";
757
758 auto driveList = ctx->bus->yield_method_call<objPaths>(
759 ctx->yield, ec, "xyz.openbmc_project.ObjectMapper",
760 "/xyz/openbmc_project/object_mapper",
761 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths",
762 driveBasePath, 0, std::array<const char*, 1>{driveLedIntf});
763 if (ec)
764 {
Alex Schendel4180cfe2022-11-29 10:46:11 -0800765 lg2::error("Failed to query HSBP drive sub tree objects");
Richard Marian Thomaiyar3594c6d2019-11-19 21:29:04 +0530766 return ipmi::responseUnspecifiedError();
767 }
Patrick Williamsb37abfb2023-05-10 07:50:33 -0500768 std::string driveObjPath = driveBasePath + "Drive_" +
769 std::to_string(instance + 1);
Richard Marian Thomaiyar3594c6d2019-11-19 21:29:04 +0530770 if (std::find(driveList.begin(), driveList.end(), driveObjPath) ==
771 driveList.end())
772 {
773 return ipmi::responseInvalidFieldRequest();
774 }
775 bool driveLedState = false;
776 switch (action)
777 {
778 case SmActionSet::forceAsserted:
779 {
780 driveLedState = true;
781 }
782 break;
783 case SmActionSet::revert:
784 {
785 driveLedState = false;
786 }
787 break;
788 case SmActionSet::forceDeasserted:
789 {
790 driveLedState = false;
791 }
792 break;
793 default:
794 {
795 return ipmi::responseInvalidFieldRequest();
796 }
797 }
798 ret = mtm.setProperty(hsbpService, driveObjPath, driveLedIntf,
799 "Asserted", driveLedState);
800 if (ret < 0)
801 {
802 return ipmi::responseUnspecifiedError();
803 }
804 }
805 break;
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000806 default:
807 {
808 return ipmi::responseInvalidFieldRequest();
Vernon Mauerya3702c12019-05-22 13:20:59 -0700809 }
810 }
Richard Marian Thomaiyar4cc10152019-08-02 23:21:45 +0530811 if (retCode == ccSuccess)
812 {
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +0530813 resetMtmTimer(ctx);
Richard Marian Thomaiyar4cc10152019-08-02 23:21:45 +0530814 }
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000815 return ipmi::response(retCode);
Vernon Mauerya3702c12019-05-22 13:20:59 -0700816}
817
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +0530818ipmi::RspType<> mtmKeepAlive(ipmi::Context::ptr ctx, uint8_t reserved,
Richard Marian Thomaiyar666dd012019-08-02 20:55:37 +0530819 const std::array<char, 5>& intentionalSignature)
820{
Ayushi Smritie0511e52019-08-27 17:30:34 +0000821 // mfg filter logic is used to allow MTM keep alive command only in
822 // manfacturing mode
823
Richard Marian Thomaiyar666dd012019-08-02 20:55:37 +0530824 constexpr std::array<char, 5> signatureOk = {'I', 'N', 'T', 'E', 'L'};
825 if (intentionalSignature != signatureOk || reserved != 0)
826 {
827 return ipmi::responseInvalidFieldRequest();
828 }
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +0530829 return ipmi::response(resetMtmTimer(ctx));
Richard Marian Thomaiyar666dd012019-08-02 20:55:37 +0530830}
831
Ayushi Smritie0511e52019-08-27 17:30:34 +0000832static constexpr unsigned int makeCmdKey(unsigned int netFn, unsigned int cmd)
833{
834 return (netFn << 8) | cmd;
835}
836
Yong Li85feb132019-08-09 16:06:03 +0800837ipmi::Cc mfgFilterMessage(ipmi::message::Request::ptr request)
838{
Ayushi Smritie0511e52019-08-27 17:30:34 +0000839 // Restricted commands, must be executed only in Manufacturing mode
840 switch (makeCmdKey(request->ctx->netFn, request->ctx->cmd))
Yong Li85feb132019-08-09 16:06:03 +0800841 {
Matt Simmering80d4d5f2023-02-15 15:18:51 -0800842 // i2c controller write read command needs additional checking
Ayushi Smritie0511e52019-08-27 17:30:34 +0000843 case makeCmdKey(ipmi::netFnApp, ipmi::app::cmdMasterWriteRead):
844 if (request->payload.size() > 4)
845 {
Richard Marian Thomaiyarae13ac62019-12-17 15:45:12 +0530846 // Allow write data count > 1 only in Special mode
847 if (mtm.getMfgMode() == SpecialMode::none)
Ayushi Smritie0511e52019-08-27 17:30:34 +0000848 {
849 return ipmi::ccInsufficientPrivilege;
850 }
851 }
jayaprakash Mutyala2d4a0192019-11-11 22:12:45 +0000852 return ipmi::ccSuccess;
Ayushi Smritie0511e52019-08-27 17:30:34 +0000853 case makeCmdKey(ipmi::netFnOemOne,
854 ipmi::intel::general::cmdGetSmSignal):
855 case makeCmdKey(ipmi::netFnOemOne,
856 ipmi::intel::general::cmdSetSmSignal):
857 case makeCmdKey(ipmi::netFnOemOne,
858 ipmi::intel::general::cmdMtmKeepAlive):
859 case makeCmdKey(ipmi::netFnOemOne,
860 ipmi::intel::general::cmdSetManufacturingData):
861 case makeCmdKey(ipmi::netFnOemOne,
862 ipmi::intel::general::cmdGetManufacturingData):
Vernon Mauery27d23562021-08-12 10:43:39 -0700863 case makeCmdKey(ipmi::intel::netFnGeneral,
864 ipmi::intel::general::cmdSetFITcLayout):
Arun P. Mohanan06584cd2021-08-13 20:56:01 +0530865 case makeCmdKey(ipmi::netFnOemOne,
866 ipmi::intel::general::cmdMTMBMCFeatureControl):
Ayushi Smritie0511e52019-08-27 17:30:34 +0000867 case makeCmdKey(ipmi::netFnStorage, ipmi::storage::cmdWriteFruData):
AppaRao Puli9a13daa2020-07-13 17:53:00 +0530868 case makeCmdKey(ipmi::netFnOemTwo, ipmi::intel::platform::cmdClearCMOS):
Ayushi Smritie0511e52019-08-27 17:30:34 +0000869
Richard Marian Thomaiyarae13ac62019-12-17 15:45:12 +0530870 // Check for Special mode
871 if (mtm.getMfgMode() == SpecialMode::none)
Yong Li85feb132019-08-09 16:06:03 +0800872 {
Ayushi Smritie0511e52019-08-27 17:30:34 +0000873 return ipmi::ccInvalidCommand;
Yong Li85feb132019-08-09 16:06:03 +0800874 }
jayaprakash Mutyala2d4a0192019-11-11 22:12:45 +0000875 return ipmi::ccSuccess;
876 case makeCmdKey(ipmi::netFnStorage, ipmi::storage::cmdDeleteSelEntry):
877 {
878 return ipmi::ccInvalidCommand;
879 }
Yong Li85feb132019-08-09 16:06:03 +0800880 }
Yong Li85feb132019-08-09 16:06:03 +0800881 return ipmi::ccSuccess;
882}
883
Richard Marian Thomaiyar1f0839c2019-08-25 20:10:52 +0530884static constexpr uint8_t maxEthSize = 6;
885static constexpr uint8_t maxSupportedEth = 3;
886static constexpr const char* factoryEthAddrBaseFileName =
887 "/var/sofs/factory-settings/network/mac/eth";
888
Alex Schendel097497f2022-10-07 14:37:15 -0700889bool findFruDevice(ipmi::Context::ptr& ctx, uint64_t& macOffset,
Zhikui Renad129c62022-04-05 20:11:24 -0700890 uint64_t& busNum, uint64_t& address)
891{
Alex Schendel097497f2022-10-07 14:37:15 -0700892 boost::system::error_code ec{};
893 ObjectValueTree obj;
Zhikui Renad129c62022-04-05 20:11:24 -0700894
895 // GetAll the objects under service FruDevice
Alex Schendel097497f2022-10-07 14:37:15 -0700896 ec = getManagedObjects(ctx, "xyz.openbmc_project.EntityManager",
897 "/xyz/openbmc_project/inventory", obj);
Zhikui Renad129c62022-04-05 20:11:24 -0700898 if (ec)
899 {
Alex Schendel4180cfe2022-11-29 10:46:11 -0800900 lg2::error("GetManagedObjects failed", "ERROR", ec.message().c_str());
Zhikui Renad129c62022-04-05 20:11:24 -0700901 return false;
902 }
903
904 for (const auto& [path, fru] : obj)
905 {
906 for (const auto& [intf, propMap] : fru)
907 {
908 if (intf == "xyz.openbmc_project.Inventory.Item.Board.Motherboard")
909 {
910 auto findBus = propMap.find("FruBus");
911 auto findAddress = propMap.find("FruAddress");
912 auto findMacOffset = propMap.find("MacOffset");
913 if (findBus == propMap.end() || findAddress == propMap.end() ||
914 findMacOffset == propMap.end())
915 {
916 continue;
917 }
918
919 auto fruBus = std::get_if<uint64_t>(&findBus->second);
920 auto fruAddress = std::get_if<uint64_t>(&findAddress->second);
921 auto macFruOffset =
922 std::get_if<uint64_t>(&findMacOffset->second);
923 if (!fruBus || !fruAddress || !macFruOffset)
924 {
Alex Schendel4180cfe2022-11-29 10:46:11 -0800925 lg2::info("ERROR: MotherBoard FRU config data type "
926 "invalid, not used");
Zhikui Renad129c62022-04-05 20:11:24 -0700927 return false;
928 }
929 busNum = *fruBus;
930 address = *fruAddress;
931 macOffset = *macFruOffset;
932 return true;
933 }
934 }
935 }
936 return false;
937}
938
Zhikui Ren98fa87e2022-05-04 08:10:29 -0700939static constexpr uint64_t fruEnd = 0xff;
940// write rolls over within current page, need to keep mac within a page
941static constexpr uint64_t fruPageSize = 0x8;
942// MAC record struct: HEADER, MAC DATA, CheckSum
943static constexpr uint64_t macRecordSize = maxEthSize + 2;
944static_assert(fruPageSize >= macRecordSize,
945 "macRecordSize greater than eeprom page size");
946static constexpr uint8_t macHeader = 0x40;
947// Calculate new checksum for fru info area
948static uint8_t calculateChecksum(std::vector<uint8_t>::const_iterator iter,
949 std::vector<uint8_t>::const_iterator end)
950{
951 constexpr int checksumMod = 256;
952 uint8_t sum = std::accumulate(iter, end, static_cast<uint8_t>(0));
953 return (checksumMod - sum) % checksumMod;
954}
955
Zhikui Renad129c62022-04-05 20:11:24 -0700956bool readMacFromFru(ipmi::Context::ptr ctx, uint8_t macIndex,
957 std::array<uint8_t, maxEthSize>& ethData)
958{
Zhikui Renad129c62022-04-05 20:11:24 -0700959 uint64_t macOffset = fruEnd;
960 uint64_t fruBus = 0;
961 uint64_t fruAddress = 0;
962
Alex Schendel097497f2022-10-07 14:37:15 -0700963 if (findFruDevice(ctx, macOffset, fruBus, fruAddress))
Zhikui Renad129c62022-04-05 20:11:24 -0700964 {
Alex Schendel4180cfe2022-11-29 10:46:11 -0800965 lg2::info("Found mac fru", "BUS", lg2::dec,
966 static_cast<uint8_t>(fruBus), "ADDRESS", lg2::dec,
967 static_cast<uint8_t>(fruAddress));
Zhikui Renad129c62022-04-05 20:11:24 -0700968
Zhikui Ren98fa87e2022-05-04 08:10:29 -0700969 if (macOffset % fruPageSize)
970 {
971 macOffset = (macOffset / fruPageSize + 1) * fruPageSize;
972 }
973 macOffset += macIndex * fruPageSize;
974 if ((macOffset + macRecordSize) > fruEnd)
Zhikui Renad129c62022-04-05 20:11:24 -0700975 {
Alex Schendel4180cfe2022-11-29 10:46:11 -0800976 lg2::error("ERROR: read fru mac failed, offset invalid");
Zhikui Renad129c62022-04-05 20:11:24 -0700977 return false;
978 }
979 std::vector<uint8_t> writeData;
Zhikui Ren98fa87e2022-05-04 08:10:29 -0700980 writeData.push_back(static_cast<uint8_t>(macOffset));
981 std::vector<uint8_t> readBuf(macRecordSize);
Zhikui Renad129c62022-04-05 20:11:24 -0700982 std::string i2cBus = "/dev/i2c-" + std::to_string(fruBus);
Patrick Williamsb37abfb2023-05-10 07:50:33 -0500983 ipmi::Cc retI2C = ipmi::i2cWriteRead(i2cBus, fruAddress, writeData,
984 readBuf);
Zhikui Renad129c62022-04-05 20:11:24 -0700985 if (retI2C == ipmi::ccSuccess)
986 {
Zhikui Ren98fa87e2022-05-04 08:10:29 -0700987 uint8_t cs = calculateChecksum(readBuf.cbegin(), readBuf.cend());
988 if (cs == 0)
989 {
990 std::copy(++readBuf.begin(), --readBuf.end(), ethData.data());
991 return true;
992 }
Zhikui Renad129c62022-04-05 20:11:24 -0700993 }
994 }
995 return false;
996}
997
998ipmi::Cc writeMacToFru(ipmi::Context::ptr ctx, uint8_t macIndex,
999 std::array<uint8_t, maxEthSize>& ethData)
1000{
Zhikui Renad129c62022-04-05 20:11:24 -07001001 uint64_t macOffset = fruEnd;
1002 uint64_t fruBus = 0;
1003 uint64_t fruAddress = 0;
1004
Alex Schendel097497f2022-10-07 14:37:15 -07001005 if (findFruDevice(ctx, macOffset, fruBus, fruAddress))
Zhikui Renad129c62022-04-05 20:11:24 -07001006 {
Alex Schendel4180cfe2022-11-29 10:46:11 -08001007 lg2::info("Found mac fru", "BUS", lg2::dec,
1008 static_cast<uint8_t>(fruBus), "ADDRESS", lg2::dec,
1009 static_cast<uint8_t>(fruAddress));
Zhikui Renad129c62022-04-05 20:11:24 -07001010
Zhikui Ren98fa87e2022-05-04 08:10:29 -07001011 if (macOffset % fruPageSize)
1012 {
1013 macOffset = (macOffset / fruPageSize + 1) * fruPageSize;
1014 }
1015 macOffset += macIndex * fruPageSize;
1016 if ((macOffset + macRecordSize) > fruEnd)
Zhikui Renad129c62022-04-05 20:11:24 -07001017 {
Alex Schendel4180cfe2022-11-29 10:46:11 -08001018 lg2::error("ERROR: write mac fru failed, offset invalid.");
Zhikui Renad129c62022-04-05 20:11:24 -07001019 return ipmi::ccParmOutOfRange;
1020 }
1021 std::vector<uint8_t> writeData;
Zhikui Ren98fa87e2022-05-04 08:10:29 -07001022 writeData.reserve(macRecordSize + 1); // include start location
1023 writeData.push_back(static_cast<uint8_t>(macOffset));
1024 writeData.push_back(macHeader);
Zhikui Renad129c62022-04-05 20:11:24 -07001025 std::for_each(ethData.cbegin(), ethData.cend(),
1026 [&](uint8_t i) { writeData.push_back(i); });
Patrick Williamsb37abfb2023-05-10 07:50:33 -05001027 uint8_t macCheckSum = calculateChecksum(++writeData.cbegin(),
1028 writeData.cend());
Zhikui Ren98fa87e2022-05-04 08:10:29 -07001029 writeData.push_back(macCheckSum);
Zhikui Renad129c62022-04-05 20:11:24 -07001030
1031 std::string i2cBus = "/dev/i2c-" + std::to_string(fruBus);
1032 std::vector<uint8_t> readBuf;
Patrick Williamsb37abfb2023-05-10 07:50:33 -05001033 ipmi::Cc ret = ipmi::i2cWriteRead(i2cBus, fruAddress, writeData,
1034 readBuf);
Zhikui Ren98fa87e2022-05-04 08:10:29 -07001035
Zhikui Ren0408e792022-06-07 21:03:33 -07001036 // prepare for read to detect chip is write protected
1037 writeData.resize(1);
1038 readBuf.resize(maxEthSize + 1); // include macHeader
1039
Zhikui Renad129c62022-04-05 20:11:24 -07001040 switch (ret)
1041 {
1042 case ipmi::ccSuccess:
Zhikui Ren98fa87e2022-05-04 08:10:29 -07001043 // Wait for internal write cycle to complete
1044 // example: ATMEL 24c0x chip has Twr spec as 5ms
1045
1046 // Ideally we want yield wait, but currently following code
1047 // crash with "thread not supported"
1048 // boost::asio::deadline_timer timer(
1049 // boost::asio::get_associated_executor(ctx->yield),
1050 // boost::posix_time::seconds(1));
1051 // timer.async_wait(ctx->yield);
1052 // use usleep as temp WA
1053 usleep(5000);
1054 if (ipmi::i2cWriteRead(i2cBus, fruAddress, writeData,
1055 readBuf) == ipmi::ccSuccess)
Zhikui Renad129c62022-04-05 20:11:24 -07001056 {
1057 if (std::equal(ethData.begin(), ethData.end(),
Zhikui Ren98fa87e2022-05-04 08:10:29 -07001058 ++readBuf.begin())) // skip macHeader
Zhikui Renad129c62022-04-05 20:11:24 -07001059 {
1060 return ipmi::ccSuccess;
1061 }
Alex Schendel4180cfe2022-11-29 10:46:11 -08001062 lg2::info("INFO: write mac fru verify failed, fru may be "
1063 "write protected.");
Zhikui Renad129c62022-04-05 20:11:24 -07001064 }
1065 return ipmi::ccCommandNotAvailable;
Zhikui Ren0408e792022-06-07 21:03:33 -07001066 default:
1067 if (ipmi::i2cWriteRead(i2cBus, fruAddress, writeData,
1068 readBuf) == ipmi::ccSuccess)
1069 {
Alex Schendel4180cfe2022-11-29 10:46:11 -08001070 lg2::info("INFO: write mac fru failed, but successfully "
1071 "read from fru, fru may be write protected.");
Zhikui Ren0408e792022-06-07 21:03:33 -07001072 return ipmi::ccCommandNotAvailable;
1073 }
1074 else // assume failure is due to no eeprom on board
1075 {
Alex Schendel4180cfe2022-11-29 10:46:11 -08001076 lg2::error("ERROR: write mac fru failed, assume no eeprom "
1077 "is available.");
Zhikui Ren0408e792022-06-07 21:03:33 -07001078 }
Zhikui Renad129c62022-04-05 20:11:24 -07001079 break;
1080 }
1081 }
1082 // no FRU eeprom found
1083 return ipmi::ccDestinationUnavailable;
1084}
1085
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +05301086ipmi::RspType<> setManufacturingData(ipmi::Context::ptr ctx, uint8_t dataType,
Richard Marian Thomaiyar1f0839c2019-08-25 20:10:52 +05301087 std::array<uint8_t, maxEthSize> ethData)
1088{
Zhikui Renad129c62022-04-05 20:11:24 -07001089 // mfg filter logic will restrict this command executing only in mfg
1090 // mode.
Richard Marian Thomaiyar1f0839c2019-08-25 20:10:52 +05301091 if (dataType >= maxSupportedEth)
1092 {
1093 return ipmi::responseParmOutOfRange();
1094 }
1095
1096 constexpr uint8_t invalidData = 0;
1097 constexpr uint8_t validData = 1;
Zhikui Renad129c62022-04-05 20:11:24 -07001098
1099 ipmi::Cc ret = writeMacToFru(ctx, dataType, ethData);
1100 if (ret != ipmi::ccDestinationUnavailable)
1101 {
1102 resetMtmTimer(ctx);
1103 return response(ret);
1104 }
1105
Richard Marian Thomaiyar1f0839c2019-08-25 20:10:52 +05301106 constexpr uint8_t ethAddrStrSize =
1107 19; // XX:XX:XX:XX:XX:XX + \n + null termination;
1108 std::vector<uint8_t> buff(ethAddrStrSize);
1109 std::snprintf(reinterpret_cast<char*>(buff.data()), ethAddrStrSize,
1110 "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx\n", ethData.at(0),
1111 ethData.at(1), ethData.at(2), ethData.at(3), ethData.at(4),
1112 ethData.at(5));
1113 std::ofstream oEthFile(factoryEthAddrBaseFileName +
1114 std::to_string(dataType),
1115 std::ofstream::out);
1116 if (!oEthFile.good())
1117 {
1118 return ipmi::responseUnspecifiedError();
1119 }
1120
1121 oEthFile << reinterpret_cast<char*>(buff.data());
Johnathan Mantey6d83e4e2020-05-08 11:01:01 -07001122 oEthFile.flush();
Richard Marian Thomaiyar1f0839c2019-08-25 20:10:52 +05301123 oEthFile.close();
1124
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +05301125 resetMtmTimer(ctx);
Richard Marian Thomaiyar1f0839c2019-08-25 20:10:52 +05301126 return ipmi::responseSuccess();
1127}
1128
1129ipmi::RspType<uint8_t, std::array<uint8_t, maxEthSize>>
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +05301130 getManufacturingData(ipmi::Context::ptr ctx, uint8_t dataType)
Richard Marian Thomaiyar1f0839c2019-08-25 20:10:52 +05301131{
Zhikui Renad129c62022-04-05 20:11:24 -07001132 // mfg filter logic will restrict this command executing only in mfg
1133 // mode.
Richard Marian Thomaiyar1f0839c2019-08-25 20:10:52 +05301134 if (dataType >= maxSupportedEth)
1135 {
1136 return ipmi::responseParmOutOfRange();
1137 }
1138 std::array<uint8_t, maxEthSize> ethData{0};
1139 constexpr uint8_t invalidData = 0;
1140 constexpr uint8_t validData = 1;
1141
1142 std::ifstream iEthFile(factoryEthAddrBaseFileName +
1143 std::to_string(dataType),
1144 std::ifstream::in);
1145 if (!iEthFile.good())
1146 {
Zhikui Renad129c62022-04-05 20:11:24 -07001147 if (readMacFromFru(ctx, dataType, ethData))
1148 {
1149 resetMtmTimer(ctx);
1150 return ipmi::responseSuccess(validData, ethData);
1151 }
Richard Marian Thomaiyar1f0839c2019-08-25 20:10:52 +05301152 return ipmi::responseSuccess(invalidData, ethData);
1153 }
1154 std::string ethStr;
1155 iEthFile >> ethStr;
1156 uint8_t* data = ethData.data();
1157 std::sscanf(ethStr.c_str(), "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx",
1158 data, (data + 1), (data + 2), (data + 3), (data + 4),
1159 (data + 5));
1160
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +05301161 resetMtmTimer(ctx);
Richard Marian Thomaiyar1f0839c2019-08-25 20:10:52 +05301162 return ipmi::responseSuccess(validData, ethData);
1163}
1164
Matt Simmering80d4d5f2023-02-15 15:18:51 -08001165/** @brief implements slot controller write read IPMI command which can be used
Zhikui Renad129c62022-04-05 20:11:24 -07001166 * for low-level I2C/SMBus write, read or write-read access for PCIE slots
V-Sanjana7acb2d22022-11-15 11:13:54 +05301167 * @param reserved - skip 3 bit
1168 * @param muxType - mux type
Yong Lif267a672019-08-29 11:16:07 +08001169 * @param addressType - address type
1170 * @param bbSlotNum - baseboard slot number
1171 * @param riserSlotNum - riser slot number
1172 * @param reserved2 - skip 2 bit
Matt Simmering80d4d5f2023-02-15 15:18:51 -08001173 * @param targetAddr - target address
Yong Lif267a672019-08-29 11:16:07 +08001174 * @param readCount - number of bytes to be read
1175 * @param writeData - data to be written
1176 *
1177 * @returns IPMI completion code plus response data
1178 */
V-Sanjana7acb2d22022-11-15 11:13:54 +05301179
Matt Simmering80d4d5f2023-02-15 15:18:51 -08001180ipmi::RspType<std::vector<uint8_t>> appSlotI2CControllerWriteRead(
V-Sanjana7acb2d22022-11-15 11:13:54 +05301181 uint3_t reserved, uint3_t muxType, uint2_t addressType, uint3_t bbSlotNum,
Matt Simmering80d4d5f2023-02-15 15:18:51 -08001182 uint3_t riserSlotNum, uint2_t reserved2, uint8_t targetAddr,
V-Sanjana7acb2d22022-11-15 11:13:54 +05301183 uint8_t readCount, std::vector<uint8_t> writeData)
Yong Lif267a672019-08-29 11:16:07 +08001184{
Vernon Maueryfc3bc382022-06-02 13:52:04 -07001185 if (reserved || reserved2)
1186 {
1187 return ipmi::responseInvalidFieldRequest();
1188 }
Yong Lif267a672019-08-29 11:16:07 +08001189 const size_t writeCount = writeData.size();
1190 std::string i2cBus;
1191 if (addressType == slotAddressTypeBus)
1192 {
V-Sanjana7acb2d22022-11-15 11:13:54 +05301193 std::string path = "/dev/i2c-mux/";
1194 if (muxType == bbRiserMux)
1195 {
1196 path += "Riser_" + std::to_string(static_cast<uint8_t>(bbSlotNum)) +
1197 "_Mux/Pcie_Slot_" +
1198 std::to_string(static_cast<uint8_t>(riserSlotNum));
1199 }
1200 else if (muxType == leftRiserMux)
1201 {
1202 path += "Left_Riser_Mux/Slot_" +
1203 std::to_string(static_cast<uint8_t>(riserSlotNum));
1204 }
1205 else if (muxType == rightRiserMux)
1206 {
1207 path += "Right_Riser_Mux/Slot_" +
1208 std::to_string(static_cast<uint8_t>(riserSlotNum));
1209 }
1210 else if (muxType == pcieMux)
1211 {
1212 path += "PCIe_Mux/Slot_" +
1213 std::to_string(static_cast<uint8_t>(riserSlotNum));
1214 }
1215 else if (muxType == hsbpMux)
1216 {
1217 path += "HSBP_Mux/Slot" +
1218 std::to_string(static_cast<uint8_t>(riserSlotNum));
1219 }
1220 phosphor::logging::log<phosphor::logging::level::DEBUG>(
1221 ("Path is: " + path).c_str());
Yong Lif267a672019-08-29 11:16:07 +08001222 if (std::filesystem::exists(path) && std::filesystem::is_symlink(path))
1223 {
1224 i2cBus = std::filesystem::read_symlink(path);
1225 }
1226 else
1227 {
Matt Simmering80d4d5f2023-02-15 15:18:51 -08001228 lg2::error("Controller write read command: Cannot get BusID");
Yong Lif267a672019-08-29 11:16:07 +08001229 return ipmi::responseInvalidFieldRequest();
1230 }
1231 }
1232 else if (addressType == slotAddressTypeUniqueid)
1233 {
1234 i2cBus = "/dev/i2c-" +
1235 std::to_string(static_cast<uint8_t>(bbSlotNum) |
1236 (static_cast<uint8_t>(riserSlotNum) << 3));
1237 }
1238 else
1239 {
Matt Simmering80d4d5f2023-02-15 15:18:51 -08001240 lg2::error("Controller write read command: invalid request");
Yong Lif267a672019-08-29 11:16:07 +08001241 return ipmi::responseInvalidFieldRequest();
1242 }
1243
Zhikui Renad129c62022-04-05 20:11:24 -07001244 // Allow single byte write as it is offset byte to read the data, rest
1245 // allow only in Special mode.
Yong Lif267a672019-08-29 11:16:07 +08001246 if (writeCount > 1)
1247 {
Richard Marian Thomaiyarae13ac62019-12-17 15:45:12 +05301248 if (mtm.getMfgMode() == SpecialMode::none)
Yong Lif267a672019-08-29 11:16:07 +08001249 {
1250 return ipmi::responseInsufficientPrivilege();
1251 }
1252 }
1253
1254 if (readCount > slotI2CMaxReadSize)
1255 {
Matt Simmering80d4d5f2023-02-15 15:18:51 -08001256 lg2::error("Controller write read command: Read count exceeds limit");
Yong Lif267a672019-08-29 11:16:07 +08001257 return ipmi::responseParmOutOfRange();
1258 }
1259
1260 if (!readCount && !writeCount)
1261 {
Matt Simmering80d4d5f2023-02-15 15:18:51 -08001262 lg2::error("Controller write read command: Read & write count are 0");
Yong Lif267a672019-08-29 11:16:07 +08001263 return ipmi::responseInvalidFieldRequest();
1264 }
1265
1266 std::vector<uint8_t> readBuf(readCount);
1267
Patrick Williamsb37abfb2023-05-10 07:50:33 -05001268 ipmi::Cc retI2C = ipmi::i2cWriteRead(i2cBus, targetAddr, writeData,
1269 readBuf);
Yong Lif267a672019-08-29 11:16:07 +08001270 if (retI2C != ipmi::ccSuccess)
1271 {
1272 return ipmi::response(retI2C);
1273 }
1274
1275 return ipmi::responseSuccess(readBuf);
1276}
Yong Li068b4f22019-09-17 16:32:18 +08001277
1278ipmi::RspType<> clearCMOS()
1279{
Matt Simmering80d4d5f2023-02-15 15:18:51 -08001280 // There is an i2c device on bus 4, the target address is 0x38. Based on
Zhikui Renad129c62022-04-05 20:11:24 -07001281 // the spec, writing 0x1 to address 0x61 on this device, will trigger
1282 // the clear CMOS action.
Matt Simmering80d4d5f2023-02-15 15:18:51 -08001283 constexpr uint8_t targetAddr = 0x38;
Yong Li068b4f22019-09-17 16:32:18 +08001284 std::string i2cBus = "/dev/i2c-4";
Yong Lieaeb6cb2020-03-09 20:21:50 +08001285 std::vector<uint8_t> writeData = {0x61, 0x1};
Yong Li068b4f22019-09-17 16:32:18 +08001286 std::vector<uint8_t> readBuf(0);
1287
Patrick Williamsb37abfb2023-05-10 07:50:33 -05001288 ipmi::Cc retI2C = ipmi::i2cWriteRead(i2cBus, targetAddr, writeData,
1289 readBuf);
Yong Li068b4f22019-09-17 16:32:18 +08001290 return ipmi::response(retI2C);
1291}
Vernon Mauery27d23562021-08-12 10:43:39 -07001292
1293ipmi::RspType<> setFITcLayout(uint32_t layout)
1294{
1295 static constexpr const char* factoryFITcLayout =
1296 "/var/sofs/factory-settings/layout/fitc";
1297 std::filesystem::path fitcDir =
1298 std::filesystem::path(factoryFITcLayout).parent_path();
1299 std::error_code ec;
1300 std::filesystem::create_directories(fitcDir, ec);
1301 if (ec)
1302 {
1303 return ipmi::responseUnspecifiedError();
1304 }
1305 try
1306 {
1307 std::ofstream file(factoryFITcLayout);
1308 file << layout;
1309 file.flush();
1310 file.close();
1311 }
1312 catch (const std::exception& e)
1313 {
1314 return ipmi::responseUnspecifiedError();
1315 }
1316
1317 return ipmi::responseSuccess();
1318}
1319
Arun P. Mohanan06584cd2021-08-13 20:56:01 +05301320static std::vector<std::string>
1321 getMCTPServiceConfigPaths(ipmi::Context::ptr& ctx)
1322{
1323 boost::system::error_code ec;
1324 auto configPaths = ctx->bus->yield_method_call<std::vector<std::string>>(
1325 ctx->yield, ec, "xyz.openbmc_project.ObjectMapper",
1326 "/xyz/openbmc_project/object_mapper",
1327 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths",
1328 "/xyz/openbmc_project/inventory/system/board", 2,
1329 std::array<const char*, 1>{
1330 "xyz.openbmc_project.Configuration.MctpConfiguration"});
1331 if (ec)
1332 {
1333 throw std::runtime_error(
1334 "Failed to query configuration sub tree objects");
1335 }
1336 return configPaths;
1337}
1338
1339static ipmi::RspType<> startOrStopService(ipmi::Context::ptr& ctx,
1340 const uint8_t enable,
ANJALI RAY1fe485c2021-12-17 16:41:30 +00001341 const std::string& serviceName,
1342 bool disableOrEnableUnitFiles = true)
Arun P. Mohanan06584cd2021-08-13 20:56:01 +05301343{
1344 constexpr bool runtimeOnly = false;
1345 constexpr bool force = false;
1346
1347 boost::system::error_code ec;
1348 switch (enable)
1349 {
1350 case ipmi::SupportedFeatureActions::stop:
1351 ctx->bus->yield_method_call(ctx->yield, ec, systemDService,
1352 systemDObjPath, systemDMgrIntf,
1353 "StopUnit", serviceName, "replace");
1354 break;
1355 case ipmi::SupportedFeatureActions::start:
1356 ctx->bus->yield_method_call(ctx->yield, ec, systemDService,
1357 systemDObjPath, systemDMgrIntf,
1358 "StartUnit", serviceName, "replace");
1359 break;
1360 case ipmi::SupportedFeatureActions::disable:
ANJALI RAY1fe485c2021-12-17 16:41:30 +00001361 if (disableOrEnableUnitFiles == true)
1362 {
1363 ctx->bus->yield_method_call(
1364 ctx->yield, ec, systemDService, systemDObjPath,
1365 systemDMgrIntf, "DisableUnitFiles",
1366 std::array<const char*, 1>{serviceName.c_str()},
1367 runtimeOnly);
1368 }
Arun P. Mohanan06584cd2021-08-13 20:56:01 +05301369 ctx->bus->yield_method_call(
1370 ctx->yield, ec, systemDService, systemDObjPath, systemDMgrIntf,
1371 "MaskUnitFiles",
1372 std::array<const char*, 1>{serviceName.c_str()}, runtimeOnly,
1373 force);
Arun P. Mohanan06584cd2021-08-13 20:56:01 +05301374 break;
1375 case ipmi::SupportedFeatureActions::enable:
1376 ctx->bus->yield_method_call(
1377 ctx->yield, ec, systemDService, systemDObjPath, systemDMgrIntf,
1378 "UnmaskUnitFiles",
1379 std::array<const char*, 1>{serviceName.c_str()}, runtimeOnly);
ANJALI RAY1fe485c2021-12-17 16:41:30 +00001380 if (disableOrEnableUnitFiles == true)
1381 {
1382 ctx->bus->yield_method_call(
1383 ctx->yield, ec, systemDService, systemDObjPath,
1384 systemDMgrIntf, "EnableUnitFiles",
1385 std::array<const char*, 1>{serviceName.c_str()},
1386 runtimeOnly, force);
1387 }
Arun P. Mohanan06584cd2021-08-13 20:56:01 +05301388 break;
1389 default:
Alex Schendel4180cfe2022-11-29 10:46:11 -08001390 lg2::warning("ERROR: Invalid feature action selected", "ACTION",
1391 lg2::dec, enable);
Arun P. Mohanan06584cd2021-08-13 20:56:01 +05301392 return ipmi::responseInvalidFieldRequest();
1393 }
1394 if (ec)
1395 {
Alex Schendel4180cfe2022-11-29 10:46:11 -08001396 lg2::warning("ERROR: Service start or stop failed", "SERVICE",
1397 serviceName.c_str());
Arun P. Mohanan06584cd2021-08-13 20:56:01 +05301398 return ipmi::responseUnspecifiedError();
1399 }
1400 return ipmi::responseSuccess();
1401}
1402
1403static std::string getMCTPServiceName(const std::string& objectPath)
1404{
1405 const auto serviceArgument = boost::algorithm::replace_all_copy(
1406 boost::algorithm::replace_first_copy(
1407 objectPath, "/xyz/openbmc_project/inventory/system/board/", ""),
1408 "/", "_2f");
Patrick Williamsb37abfb2023-05-10 07:50:33 -05001409 std::string unitName = "xyz.openbmc_project.mctpd@" + serviceArgument +
1410 ".service";
Arun P. Mohanan06584cd2021-08-13 20:56:01 +05301411 return unitName;
1412}
1413
1414static ipmi::RspType<> handleMCTPFeature(ipmi::Context::ptr& ctx,
1415 const uint8_t enable,
1416 const std::string& binding)
1417{
1418 std::vector<std::string> configPaths;
1419 try
1420 {
1421 configPaths = getMCTPServiceConfigPaths(ctx);
1422 }
1423 catch (const std::exception& e)
1424 {
Alex Schendel4180cfe2022-11-29 10:46:11 -08001425 lg2::error(e.what());
Arun P. Mohanan06584cd2021-08-13 20:56:01 +05301426 return ipmi::responseUnspecifiedError();
1427 }
1428
1429 for (const auto& objectPath : configPaths)
1430 {
Patrick Williamsb37abfb2023-05-10 07:50:33 -05001431 const auto pos = objectPath.find_last_of('/');
Arun P. Mohanan06584cd2021-08-13 20:56:01 +05301432 if (binding == objectPath.substr(pos + 1))
1433 {
1434 return startOrStopService(ctx, enable,
ANJALI RAY1fe485c2021-12-17 16:41:30 +00001435 getMCTPServiceName(objectPath), false);
Arun P. Mohanan06584cd2021-08-13 20:56:01 +05301436 }
1437 }
1438 return ipmi::responseSuccess();
1439}
1440
1441/** @brief implements MTM BMC Feature Control IPMI command which can be
1442 * used to enable or disable the supported BMC features.
1443 * @param yield - context object that represents the currently executing
1444 * coroutine
1445 * @param feature - feature enum to enable or disable
1446 * @param enable - enable or disable the feature
1447 * @param featureArg - custom arguments for that feature
1448 * @param reserved - reserved for future use
1449 *
1450 * @returns IPMI completion code
1451 */
1452ipmi::RspType<> mtmBMCFeatureControl(ipmi::Context::ptr ctx,
1453 const uint8_t feature,
1454 const uint8_t enable,
1455 const uint8_t featureArg,
1456 const uint16_t reserved)
1457{
1458 if (reserved != 0)
1459 {
1460 return ipmi::responseInvalidFieldRequest();
1461 }
1462
1463 switch (feature)
1464 {
1465 case ipmi::SupportedFeatureControls::mctp:
1466 switch (featureArg)
1467 {
1468 case ipmi::SupportedMCTPBindings::mctpPCIe:
1469 return handleMCTPFeature(ctx, enable, "MCTP_PCIe");
1470 case ipmi::SupportedMCTPBindings::mctpSMBusHSBP:
1471 return handleMCTPFeature(ctx, enable, "MCTP_SMBus_HSBP");
1472 case ipmi::SupportedMCTPBindings::mctpSMBusPCIeSlot:
1473 return handleMCTPFeature(ctx, enable,
1474 "MCTP_SMBus_PCIe_slot");
1475 default:
1476 return ipmi::responseInvalidFieldRequest();
1477 }
1478 break;
Jason M. Bills5cb2c042021-08-17 12:03:39 -07001479 case ipmi::SupportedFeatureControls::pcieScan:
1480 if (featureArg != 0)
1481 {
1482 return ipmi::responseInvalidFieldRequest();
1483 }
1484 startOrStopService(ctx, enable, "xyz.openbmc_project.PCIe.service");
1485 break;
Arun P. Mohanan06584cd2021-08-13 20:56:01 +05301486 default:
1487 return ipmi::responseInvalidFieldRequest();
1488 }
1489 return ipmi::responseSuccess();
1490}
Vernon Mauerya3702c12019-05-22 13:20:59 -07001491} // namespace ipmi
1492
1493void register_mtm_commands() __attribute__((constructor));
1494void register_mtm_commands()
1495{
Jason M. Bills38d2b5a2019-06-03 16:26:11 -07001496 // <Get SM Signal>
Vernon Mauery98bbf692019-09-16 11:14:59 -07001497 ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnGeneral,
1498 ipmi::intel::general::cmdGetSmSignal,
1499 ipmi::Privilege::Admin, ipmi::appMTMGetSignal);
Vernon Mauerya3702c12019-05-22 13:20:59 -07001500
Vernon Mauery98bbf692019-09-16 11:14:59 -07001501 ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnGeneral,
1502 ipmi::intel::general::cmdSetSmSignal,
1503 ipmi::Privilege::Admin, ipmi::appMTMSetSignal);
Vernon Mauerya3702c12019-05-22 13:20:59 -07001504
Vernon Mauery98bbf692019-09-16 11:14:59 -07001505 ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnGeneral,
1506 ipmi::intel::general::cmdMtmKeepAlive,
1507 ipmi::Privilege::Admin, ipmi::mtmKeepAlive);
Richard Marian Thomaiyar666dd012019-08-02 20:55:37 +05301508
Vernon Mauery98bbf692019-09-16 11:14:59 -07001509 ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnGeneral,
1510 ipmi::intel::general::cmdSetManufacturingData,
1511 ipmi::Privilege::Admin, ipmi::setManufacturingData);
Richard Marian Thomaiyar1f0839c2019-08-25 20:10:52 +05301512
Vernon Mauery98bbf692019-09-16 11:14:59 -07001513 ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnGeneral,
1514 ipmi::intel::general::cmdGetManufacturingData,
1515 ipmi::Privilege::Admin, ipmi::getManufacturingData);
Richard Marian Thomaiyar1f0839c2019-08-25 20:10:52 +05301516
Vernon Mauery27d23562021-08-12 10:43:39 -07001517 ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnGeneral,
1518 ipmi::intel::general::cmdSetFITcLayout,
1519 ipmi::Privilege::Admin, ipmi::setFITcLayout);
1520
Arun P. Mohanan06584cd2021-08-13 20:56:01 +05301521 ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnGeneral,
1522 ipmi::intel::general::cmdMTMBMCFeatureControl,
1523 ipmi::Privilege::Admin, ipmi::mtmBMCFeatureControl);
1524
Vernon Mauery98bbf692019-09-16 11:14:59 -07001525 ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnApp,
Matt Simmering80d4d5f2023-02-15 15:18:51 -08001526 ipmi::intel::general::cmdSlotI2CControllerWriteRead,
Vernon Mauery98bbf692019-09-16 11:14:59 -07001527 ipmi::Privilege::Admin,
Matt Simmering80d4d5f2023-02-15 15:18:51 -08001528 ipmi::appSlotI2CControllerWriteRead);
Yong Lif267a672019-08-29 11:16:07 +08001529
Yong Li068b4f22019-09-17 16:32:18 +08001530 ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnPlatform,
1531 ipmi::intel::platform::cmdClearCMOS,
1532 ipmi::Privilege::Admin, ipmi::clearCMOS);
1533
Vernon Mauery98bbf692019-09-16 11:14:59 -07001534 ipmi::registerFilter(ipmi::prioOemBase,
Yong Li85feb132019-08-09 16:06:03 +08001535 [](ipmi::message::Request::ptr request) {
Patrick Williamsb37abfb2023-05-10 07:50:33 -05001536 return ipmi::mfgFilterMessage(request);
1537 });
Vernon Mauerya3702c12019-05-22 13:20:59 -07001538}