blob: 865b3a156d184d1fab0feeb5a5a9d4ed4520d6d5 [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
Vasu V97c30902023-05-19 15:56:15 +053027#include <charconv>
James Feistfcd2d3a2020-05-28 10:38:15 -070028#include <filesystem>
29#include <fstream>
30
Vernon Mauerya3702c12019-05-22 13:20:59 -070031namespace ipmi
32{
33
34Manufacturing mtm;
35
36static auto revertTimeOut =
37 std::chrono::duration_cast<std::chrono::microseconds>(
38 std::chrono::seconds(60)); // 1 minute timeout
39
V-Sanjana7acb2d22022-11-15 11:13:54 +053040static constexpr uint8_t bbRiserMux = 0;
41static constexpr uint8_t leftRiserMux = 1;
42static constexpr uint8_t rightRiserMux = 2;
43static constexpr uint8_t pcieMux = 3;
44static constexpr uint8_t hsbpMux = 4;
45
Yong Lif267a672019-08-29 11:16:07 +080046static constexpr uint8_t slotAddressTypeBus = 0;
47static constexpr uint8_t slotAddressTypeUniqueid = 1;
48static constexpr uint8_t slotI2CMaxReadSize = 35;
49
Vernon Mauerya3702c12019-05-22 13:20:59 -070050static constexpr const char* callbackMgrService =
51 "xyz.openbmc_project.CallbackManager";
52static constexpr const char* callbackMgrIntf =
53 "xyz.openbmc_project.CallbackManager";
54static constexpr const char* callbackMgrObjPath =
55 "/xyz/openbmc_project/CallbackManager";
56static constexpr const char* retriggerLedUpdate = "RetriggerLEDUpdate";
57
58const static constexpr char* systemDService = "org.freedesktop.systemd1";
59const static constexpr char* systemDObjPath = "/org/freedesktop/systemd1";
60const static constexpr char* systemDMgrIntf =
61 "org.freedesktop.systemd1.Manager";
62const static constexpr char* pidControlService = "phosphor-pid-control.service";
63
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +053064static inline Cc resetMtmTimer(ipmi::Context::ptr ctx)
Richard Marian Thomaiyar666dd012019-08-02 20:55:37 +053065{
Richard Marian Thomaiyar666dd012019-08-02 20:55:37 +053066 boost::system::error_code ec;
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +053067 ctx->bus->yield_method_call<>(ctx->yield, ec, specialModeService,
68 specialModeObjPath, specialModeIntf,
69 "ResetTimer");
Richard Marian Thomaiyar666dd012019-08-02 20:55:37 +053070 if (ec)
71 {
Alex Schendel4180cfe2022-11-29 10:46:11 -080072 lg2::error("Failed to reset the manufacturing mode timer");
Richard Marian Thomaiyar666dd012019-08-02 20:55:37 +053073 return ccUnspecifiedError;
74 }
75 return ccSuccess;
76}
77
Jason M. Bills38d2b5a2019-06-03 16:26:11 -070078int getGpioPathForSmSignal(const SmSignalGet signal, std::string& path)
Vernon Mauerya3702c12019-05-22 13:20:59 -070079{
Jason M. Bills38d2b5a2019-06-03 16:26:11 -070080 switch (signal)
81 {
82 case SmSignalGet::smPowerButton:
83 path = "/xyz/openbmc_project/chassis/buttons/power";
84 break;
85 case SmSignalGet::smResetButton:
86 path = "/xyz/openbmc_project/chassis/buttons/reset";
87 break;
88 case SmSignalGet::smNMIButton:
89 path = "/xyz/openbmc_project/chassis/buttons/nmi";
90 break;
Richard Marian Thomaiyar8e5e2b02019-08-01 07:50:55 +053091 case SmSignalGet::smIdentifyButton:
92 path = "/xyz/openbmc_project/chassis/buttons/id";
93 break;
Jason M. Bills38d2b5a2019-06-03 16:26:11 -070094 default:
95 return -1;
96 break;
97 }
98 return 0;
Vernon Mauerya3702c12019-05-22 13:20:59 -070099}
100
Patrick Venture37890392019-09-25 17:05:03 -0700101ipmi_ret_t ledStoreAndSet(SmSignalSet signal, const std::string& setState)
Vernon Mauerya3702c12019-05-22 13:20:59 -0700102{
103 LedProperty* ledProp = mtm.findLedProperty(signal);
104 if (ledProp == nullptr)
105 {
106 return IPMI_CC_INVALID_FIELD_REQUEST;
107 }
108
109 std::string ledName = ledProp->getName();
110 std::string ledService = ledServicePrefix + ledName;
111 std::string ledPath = ledPathPrefix + ledName;
112 ipmi::Value presentState;
113
114 if (false == ledProp->getLock())
115 {
116 if (mtm.getProperty(ledService.c_str(), ledPath.c_str(), ledIntf,
117 "State", &presentState) != 0)
118 {
119 return IPMI_CC_UNSPECIFIED_ERROR;
120 }
121 ledProp->setPrevState(std::get<std::string>(presentState));
122 ledProp->setLock(true);
123 if (signal == SmSignalSet::smPowerFaultLed ||
124 signal == SmSignalSet::smSystemReadyLed)
125 {
126 mtm.revertLedCallback = true;
127 }
128 }
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700129 if (mtm.setProperty(ledService, ledPath, ledIntf, "State",
Vernon Mauerya3702c12019-05-22 13:20:59 -0700130 ledStateStr + setState) != 0)
131 {
132 return IPMI_CC_UNSPECIFIED_ERROR;
133 }
134 return IPMI_CC_OK;
135}
136
137ipmi_ret_t ledRevert(SmSignalSet signal)
138{
139 LedProperty* ledProp = mtm.findLedProperty(signal);
140 if (ledProp == nullptr)
141 {
142 return IPMI_CC_INVALID_FIELD_REQUEST;
143 }
144 if (true == ledProp->getLock())
145 {
146 ledProp->setLock(false);
147 if (signal == SmSignalSet::smPowerFaultLed ||
148 signal == SmSignalSet::smSystemReadyLed)
149 {
150 try
151 {
152 ipmi::method_no_args::callDbusMethod(
153 *getSdBus(), callbackMgrService, callbackMgrObjPath,
154 callbackMgrIntf, retriggerLedUpdate);
155 }
Patrick Williamsbd51e6a2021-10-06 13:09:44 -0500156 catch (const sdbusplus::exception_t& e)
Vernon Mauerya3702c12019-05-22 13:20:59 -0700157 {
158 return IPMI_CC_UNSPECIFIED_ERROR;
159 }
160 mtm.revertLedCallback = false;
161 }
162 else
163 {
164 std::string ledName = ledProp->getName();
165 std::string ledService = ledServicePrefix + ledName;
166 std::string ledPath = ledPathPrefix + ledName;
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700167 if (mtm.setProperty(ledService, ledPath, ledIntf, "State",
168 ledProp->getPrevState()) != 0)
Vernon Mauerya3702c12019-05-22 13:20:59 -0700169 {
170 return IPMI_CC_UNSPECIFIED_ERROR;
171 }
172 }
173 }
174 return IPMI_CC_OK;
175}
176
177void Manufacturing::initData()
178{
Vernon Mauerya3702c12019-05-22 13:20:59 -0700179 ledPropertyList.push_back(
180 LedProperty(SmSignalSet::smPowerFaultLed, "status_amber"));
181 ledPropertyList.push_back(
182 LedProperty(SmSignalSet::smSystemReadyLed, "status_green"));
183 ledPropertyList.push_back(
184 LedProperty(SmSignalSet::smIdentifyLed, "identify"));
185}
186
187void Manufacturing::revertTimerHandler()
188{
Richard Marian Thomaiyarae13ac62019-12-17 15:45:12 +0530189#ifdef BMC_VALIDATION_UNSECURE_FEATURE
190 if (mtm.getMfgMode() == SpecialMode::valUnsecure)
191 {
192 // Don't revert the behaviour for validation unsecure mode.
193 return;
194 }
195#endif
Vernon Mauerya3702c12019-05-22 13:20:59 -0700196 if (revertFanPWM)
197 {
198 revertFanPWM = false;
199 disablePidControlService(false);
200 }
201
Ayushi Smritid872a4a2019-10-15 10:20:11 +0530202 if (mtmTestBeepFd != -1)
203 {
204 ::close(mtmTestBeepFd);
205 mtmTestBeepFd = -1;
206 }
207
Vernon Mauerya3702c12019-05-22 13:20:59 -0700208 for (const auto& ledProperty : ledPropertyList)
209 {
210 const std::string& ledName = ledProperty.getName();
Jayaprakash Mutyalaf3656142021-01-24 01:04:19 +0000211 if (ledName == "identify" && mtm.getMfgMode() == SpecialMode::mfg)
212 {
213 // Don't revert the behaviour for manufacturing mode
214 continue;
215 }
Vernon Mauerya3702c12019-05-22 13:20:59 -0700216 ledRevert(ledProperty.getSignal());
217 }
218}
219
220Manufacturing::Manufacturing() :
221 revertTimer([&](void) { revertTimerHandler(); })
222{
223 initData();
224}
225
Patrick Williams1bcced02024-08-16 15:20:24 -0400226int8_t Manufacturing::getProperty(
227 const std::string& service, const std::string& path,
228 const std::string& interface, 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
Patrick Williams1bcced02024-08-16 15:20:24 -0400245int8_t Manufacturing::setProperty(
246 const std::string& service, const std::string& path,
247 const std::string& interface, const std::string& propertyName,
248 ipmi::Value value)
Vernon Mauerya3702c12019-05-22 13:20:59 -0700249{
250 try
251 {
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700252 ipmi::setDbusProperty(*getSdBus(), service, path, interface,
Vernon Mauerya3702c12019-05-22 13:20:59 -0700253 propertyName, value);
254 }
Patrick Williamsf944d2e2022-07-22 19:26:52 -0500255 catch (const sdbusplus::exception_t& e)
Vernon Mauerya3702c12019-05-22 13:20:59 -0700256 {
Alex Schendel4180cfe2022-11-29 10:46:11 -0800257 lg2::info("ERROR: setProperty");
Vernon Mauerya3702c12019-05-22 13:20:59 -0700258 return -1;
259 }
260
261 return 0;
262}
263
264int8_t Manufacturing::disablePidControlService(const bool disable)
265{
266 try
267 {
268 auto dbus = getSdBus();
269 auto method = dbus->new_method_call(systemDService, systemDObjPath,
270 systemDMgrIntf,
271 disable ? "StopUnit" : "StartUnit");
272 method.append(pidControlService, "replace");
273 auto reply = dbus->call(method);
274 }
Patrick Williamsf944d2e2022-07-22 19:26:52 -0500275 catch (const sdbusplus::exception_t& e)
Vernon Mauerya3702c12019-05-22 13:20:59 -0700276 {
Alex Schendel4180cfe2022-11-29 10:46:11 -0800277 lg2::info("ERROR: phosphor-pid-control service start or stop failed");
Vernon Mauerya3702c12019-05-22 13:20:59 -0700278 return -1;
279 }
280 return 0;
281}
282
Alex Schendel90eb7872022-09-01 11:56:52 -0700283static bool findPwmName(ipmi::Context::ptr& ctx, uint8_t instance,
284 std::string& pwmName)
285{
286 boost::system::error_code ec{};
287 ObjectValueTree obj;
288
289 // GetAll the objects under service FruDevice
290 ec = getManagedObjects(ctx, "xyz.openbmc_project.EntityManager",
291 "/xyz/openbmc_project/inventory", obj);
292 if (ec)
293 {
Alex Schendel4180cfe2022-11-29 10:46:11 -0800294 lg2::error("GetMangagedObjects failed", "ERROR", ec.message().c_str());
Alex Schendel90eb7872022-09-01 11:56:52 -0700295 return false;
296 }
297 for (const auto& [path, objData] : obj)
298 {
299 for (const auto& [intf, propMap] : objData)
300 {
301 // Currently, these are the three different fan types supported.
302 if (intf == "xyz.openbmc_project.Configuration.AspeedFan" ||
303 intf == "xyz.openbmc_project.Configuration.I2CFan" ||
304 intf == "xyz.openbmc_project.Configuration.NuvotonFan")
305 {
Alex Schendel4180cfe2022-11-29 10:46:11 -0800306 std::string fanPath = "/Fan_";
Alex Schendel90eb7872022-09-01 11:56:52 -0700307
Alex Schendel4180cfe2022-11-29 10:46:11 -0800308 fanPath += std::to_string(instance);
309 std::string objPath = path.str;
310 objPath = objPath.substr(objPath.find_last_of("/"));
311 if (objPath != fanPath)
Alex Schendel90eb7872022-09-01 11:56:52 -0700312 {
313 continue;
314 }
315 auto connector = objData.find(intf + std::string(".Connector"));
316 if (connector == objData.end())
317 {
318 return false;
319 }
320 auto findPwmName = connector->second.find("PwmName");
321 if (findPwmName != connector->second.end())
322 {
323 auto fanPwmName =
324 std::get_if<std::string>(&findPwmName->second);
325 if (!fanPwmName)
326 {
Alex Schendel4180cfe2022-11-29 10:46:11 -0800327 lg2::error("PwmName parse ERROR.");
Alex Schendel90eb7872022-09-01 11:56:52 -0700328 return false;
329 }
330 pwmName = *fanPwmName;
331 return true;
332 }
333 auto findPwm = connector->second.find("Pwm");
334 if (findPwm == connector->second.end())
335 {
336 return false;
337 }
338 auto fanPwm = std::get_if<uint64_t>(&findPwm->second);
339 if (!fanPwm)
340 {
341 return false;
342 }
343 pwmName = "Pwm_" + std::to_string(*fanPwm + 1);
344 return true;
345 }
346 }
347 }
348 return false;
349}
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700350ipmi::RspType<uint8_t, // Signal value
351 std::optional<uint16_t> // Fan tach value
352 >
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +0530353 appMTMGetSignal(ipmi::Context::ptr ctx, uint8_t signalTypeByte,
Richard Marian Thomaiyar147daec2019-06-15 07:43:48 +0530354 uint8_t instance, uint8_t actionByte)
Vernon Mauerya3702c12019-05-22 13:20:59 -0700355{
Ayushi Smritie0511e52019-08-27 17:30:34 +0000356 // mfg filter logic is used to allow MTM get signal command only in
357 // manfacturing mode.
Vernon Mauerya3702c12019-05-22 13:20:59 -0700358
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700359 SmSignalGet signalType = static_cast<SmSignalGet>(signalTypeByte);
360 SmActionGet action = static_cast<SmActionGet>(actionByte);
361
362 switch (signalType)
363 {
anil kumar appana98705b32019-09-11 14:15:50 +0000364 case SmSignalGet::smChassisIntrusion:
365 {
366 ipmi::Value reply;
367 if (mtm.getProperty(intrusionService, intrusionPath, intrusionIntf,
368 "Status", &reply) < 0)
369 {
370 return ipmi::responseInvalidFieldRequest();
371 }
372 std::string* intrusionStatus = std::get_if<std::string>(&reply);
373 if (!intrusionStatus)
374 {
375 return ipmi::responseUnspecifiedError();
376 }
377
378 uint8_t status = 0;
379 if (!intrusionStatus->compare("Normal"))
380 {
381 status = static_cast<uint8_t>(IntrusionStatus::normal);
382 }
383 else if (!intrusionStatus->compare("HardwareIntrusion"))
384 {
385 status =
386 static_cast<uint8_t>(IntrusionStatus::hardwareIntrusion);
387 }
388 else if (!intrusionStatus->compare("TamperingDetected"))
389 {
390 status =
391 static_cast<uint8_t>(IntrusionStatus::tamperingDetected);
392 }
393 else
394 {
395 return ipmi::responseUnspecifiedError();
396 }
397 return ipmi::responseSuccess(status, std::nullopt);
398 }
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700399 case SmSignalGet::smFanPwmGet:
400 {
401 ipmi::Value reply;
Alex Schendel90eb7872022-09-01 11:56:52 -0700402 std::string pwmName, fullPath;
Alex Schendel4180cfe2022-11-29 10:46:11 -0800403 if (!findPwmName(ctx, instance + 1, pwmName))
Alex Schendel90eb7872022-09-01 11:56:52 -0700404 {
405 // The default PWM name is Pwm_#
406 pwmName = "Pwm_" + std::to_string(instance + 1);
407 }
408 fullPath = fanPwmPath + pwmName;
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700409 if (mtm.getProperty(fanService, fullPath, fanIntf, "Value",
410 &reply) < 0)
411 {
412 return ipmi::responseInvalidFieldRequest();
413 }
414 double* doubleVal = std::get_if<double>(&reply);
415 if (doubleVal == nullptr)
416 {
417 return ipmi::responseUnspecifiedError();
418 }
419 uint8_t sensorVal = std::round(*doubleVal);
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +0530420 resetMtmTimer(ctx);
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700421 return ipmi::responseSuccess(sensorVal, std::nullopt);
422 }
423 break;
424 case SmSignalGet::smFanTachometerGet:
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700425 {
Richard Marian Thomaiyar147daec2019-06-15 07:43:48 +0530426 boost::system::error_code ec;
427 using objFlatMap = boost::container::flat_map<
428 std::string, boost::container::flat_map<
429 std::string, std::vector<std::string>>>;
430
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +0530431 auto flatMap = ctx->bus->yield_method_call<objFlatMap>(
432 ctx->yield, ec, "xyz.openbmc_project.ObjectMapper",
Richard Marian Thomaiyar147daec2019-06-15 07:43:48 +0530433 "/xyz/openbmc_project/object_mapper",
434 "xyz.openbmc_project.ObjectMapper", "GetSubTree",
435 fanTachBasePath, 0, std::array<const char*, 1>{fanIntf});
436 if (ec)
437 {
Alex Schendel4180cfe2022-11-29 10:46:11 -0800438 lg2::error("Failed to query fan tach sub tree objects");
Richard Marian Thomaiyar147daec2019-06-15 07:43:48 +0530439 return ipmi::responseUnspecifiedError();
440 }
441 if (instance >= flatMap.size())
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700442 {
443 return ipmi::responseInvalidFieldRequest();
444 }
Richard Marian Thomaiyar147daec2019-06-15 07:43:48 +0530445 auto itr = flatMap.nth(instance);
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700446 ipmi::Value reply;
Richard Marian Thomaiyar147daec2019-06-15 07:43:48 +0530447 if (mtm.getProperty(fanService, itr->first, fanIntf, "Value",
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700448 &reply) < 0)
449 {
450 return ipmi::responseInvalidFieldRequest();
451 }
452
453 double* doubleVal = std::get_if<double>(&reply);
454 if (doubleVal == nullptr)
455 {
456 return ipmi::responseUnspecifiedError();
457 }
458 uint8_t sensorVal = FAN_PRESENT | FAN_SENSOR_PRESENT;
459 std::optional<uint16_t> fanTach = std::round(*doubleVal);
460
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +0530461 resetMtmTimer(ctx);
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700462 return ipmi::responseSuccess(sensorVal, fanTach);
463 }
464 break;
Richard Marian Thomaiyar8e5e2b02019-08-01 07:50:55 +0530465 case SmSignalGet::smIdentifyButton:
466 {
467 if (action == SmActionGet::revert || action == SmActionGet::ignore)
468 {
469 // ButtonMasked property is not supported for ID button as it is
470 // unnecessary. Hence if requested for revert / ignore, override
471 // it to sample action to make tools happy.
472 action = SmActionGet::sample;
473 }
Vernon Mauerydcff1502022-09-28 11:12:46 -0700474 [[fallthrough]];
Richard Marian Thomaiyar8e5e2b02019-08-01 07:50:55 +0530475 }
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700476 case SmSignalGet::smResetButton:
477 case SmSignalGet::smPowerButton:
478 case SmSignalGet::smNMIButton:
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700479 {
480 std::string path;
481 if (getGpioPathForSmSignal(signalType, path) < 0)
482 {
483 return ipmi::responseInvalidFieldRequest();
484 }
485
486 switch (action)
487 {
488 case SmActionGet::sample:
Alex Schendel4180cfe2022-11-29 10:46:11 -0800489 lg2::info("case SmActionGet::sample");
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700490 break;
491 case SmActionGet::ignore:
492 {
Alex Schendel4180cfe2022-11-29 10:46:11 -0800493 lg2::info("case SmActionGet::ignore");
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700494 if (mtm.setProperty(buttonService, path, buttonIntf,
495 "ButtonMasked", true) < 0)
496 {
497 return ipmi::responseUnspecifiedError();
498 }
499 }
500 break;
501 case SmActionGet::revert:
502 {
Alex Schendel4180cfe2022-11-29 10:46:11 -0800503 lg2::info("case SmActionGet::revert");
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700504 if (mtm.setProperty(buttonService, path, buttonIntf,
505 "ButtonMasked", false) < 0)
506 {
507 return ipmi::responseUnspecifiedError();
508 }
509 }
510 break;
511
512 default:
513 return ipmi::responseInvalidFieldRequest();
514 break;
515 }
516
517 ipmi::Value reply;
518 if (mtm.getProperty(buttonService, path, buttonIntf,
519 "ButtonPressed", &reply) < 0)
520 {
521 return ipmi::responseUnspecifiedError();
522 }
523 bool* valPtr = std::get_if<bool>(&reply);
524 if (valPtr == nullptr)
525 {
526 return ipmi::responseUnspecifiedError();
527 }
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +0530528 resetMtmTimer(ctx);
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700529 uint8_t sensorVal = *valPtr;
530 return ipmi::responseSuccess(sensorVal, std::nullopt);
531 }
532 break;
Richard Marian Thomaiyar1b74a212019-08-03 13:26:17 +0530533 case SmSignalGet::smNcsiDiag:
534 {
535 constexpr const char* netBasePath = "/sys/class/net/eth";
536 constexpr const char* carrierSuffix = "/carrier";
Patrick Williams1bcced02024-08-16 15:20:24 -0400537 std::ifstream netIfs(
538 netBasePath + std::to_string(instance) + carrierSuffix);
Richard Marian Thomaiyar1b74a212019-08-03 13:26:17 +0530539 if (!netIfs.good())
540 {
541 return ipmi::responseInvalidFieldRequest();
542 }
543 std::string carrier;
544 netIfs >> carrier;
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +0530545 resetMtmTimer(ctx);
Richard Marian Thomaiyar1b74a212019-08-03 13:26:17 +0530546 return ipmi::responseSuccess(
547 static_cast<uint8_t>(std::stoi(carrier)), std::nullopt);
548 }
549 break;
Vernon Mauerya3702c12019-05-22 13:20:59 -0700550 default:
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700551 return ipmi::responseInvalidFieldRequest();
Vernon Mauerya3702c12019-05-22 13:20:59 -0700552 break;
553 }
Vernon Mauerya3702c12019-05-22 13:20:59 -0700554}
555
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +0530556ipmi::RspType<> appMTMSetSignal(ipmi::Context::ptr ctx, uint8_t signalTypeByte,
557 uint8_t instance, uint8_t actionByte,
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000558 std::optional<uint8_t> pwmSpeed)
Vernon Mauerya3702c12019-05-22 13:20:59 -0700559{
Ayushi Smritie0511e52019-08-27 17:30:34 +0000560 // mfg filter logic is used to allow MTM set signal command only in
561 // manfacturing mode.
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000562
563 SmSignalSet signalType = static_cast<SmSignalSet>(signalTypeByte);
564 SmActionSet action = static_cast<SmActionSet>(actionByte);
565 Cc retCode = ccSuccess;
566 int8_t ret = 0;
567
568 switch (signalType)
569 {
570 case SmSignalSet::smPowerFaultLed:
571 case SmSignalSet::smSystemReadyLed:
572 case SmSignalSet::smIdentifyLed:
573 switch (action)
574 {
575 case SmActionSet::forceDeasserted:
Vernon Mauerya3702c12019-05-22 13:20:59 -0700576 {
Alex Schendel4180cfe2022-11-29 10:46:11 -0800577 lg2::info("case SmActionSet::forceDeasserted");
Vernon Mauerya3702c12019-05-22 13:20:59 -0700578
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000579 retCode = ledStoreAndSet(signalType, std::string("Off"));
580 if (retCode != ccSuccess)
Vernon Mauerya3702c12019-05-22 13:20:59 -0700581 {
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000582 return ipmi::response(retCode);
Vernon Mauerya3702c12019-05-22 13:20:59 -0700583 }
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000584 mtm.revertTimer.start(revertTimeOut);
Vernon Mauerya3702c12019-05-22 13:20:59 -0700585 }
586 break;
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000587 case SmActionSet::forceAsserted:
Vernon Mauerya3702c12019-05-22 13:20:59 -0700588 {
Alex Schendel4180cfe2022-11-29 10:46:11 -0800589 lg2::info("case SmActionSet::forceAsserted");
Vernon Mauerya3702c12019-05-22 13:20:59 -0700590
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000591 retCode = ledStoreAndSet(signalType, std::string("On"));
592 if (retCode != ccSuccess)
593 {
594 return ipmi::response(retCode);
595 }
596 mtm.revertTimer.start(revertTimeOut);
597 if (SmSignalSet::smPowerFaultLed == signalType)
598 {
599 // Deassert "system ready"
600 retCode = ledStoreAndSet(SmSignalSet::smSystemReadyLed,
601 std::string("Off"));
602 }
603 else if (SmSignalSet::smSystemReadyLed == signalType)
604 {
605 // Deassert "fault led"
606 retCode = ledStoreAndSet(SmSignalSet::smPowerFaultLed,
607 std::string("Off"));
608 }
609 }
610 break;
611 case SmActionSet::revert:
612 {
Alex Schendel4180cfe2022-11-29 10:46:11 -0800613 lg2::info("case SmActionSet::revert");
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000614 retCode = ledRevert(signalType);
615 }
616 break;
617 default:
618 {
619 return ipmi::responseInvalidFieldRequest();
620 }
621 }
622 break;
623 case SmSignalSet::smFanPowerSpeed:
624 {
625 if ((action == SmActionSet::forceAsserted) && (!pwmSpeed))
626 {
627 return ipmi::responseReqDataLenInvalid();
628 }
629
630 if ((action == SmActionSet::forceAsserted) && (*pwmSpeed > 100))
631 {
632 return ipmi::responseInvalidFieldRequest();
633 }
634
635 uint8_t pwmValue = 0;
636 switch (action)
637 {
638 case SmActionSet::revert:
639 {
640 if (mtm.revertFanPWM)
641 {
642 ret = mtm.disablePidControlService(false);
Vernon Mauerya3702c12019-05-22 13:20:59 -0700643 if (ret < 0)
644 {
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000645 return ipmi::responseUnspecifiedError();
Vernon Mauerya3702c12019-05-22 13:20:59 -0700646 }
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000647 mtm.revertFanPWM = false;
Vernon Mauerya3702c12019-05-22 13:20:59 -0700648 }
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000649 }
650 break;
651 case SmActionSet::forceAsserted:
652 {
653 pwmValue = *pwmSpeed;
654 } // fall-through
655 case SmActionSet::forceDeasserted:
656 {
657 if (!mtm.revertFanPWM)
Vernon Mauerya3702c12019-05-22 13:20:59 -0700658 {
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000659 ret = mtm.disablePidControlService(true);
660 if (ret < 0)
661 {
662 return ipmi::responseUnspecifiedError();
663 }
664 mtm.revertFanPWM = true;
Vernon Mauerya3702c12019-05-22 13:20:59 -0700665 }
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000666 mtm.revertTimer.start(revertTimeOut);
Alex Schendel90eb7872022-09-01 11:56:52 -0700667 std::string pwmName, fanPwmInstancePath;
Alex Schendel4180cfe2022-11-29 10:46:11 -0800668 if (!findPwmName(ctx, instance + 1, pwmName))
Alex Schendel90eb7872022-09-01 11:56:52 -0700669 {
670 pwmName = "Pwm_" + std::to_string(instance + 1);
671 }
672 fanPwmInstancePath = fanPwmPath + pwmName;
Patrick Williams1bcced02024-08-16 15:20:24 -0400673 ret =
674 mtm.setProperty(fanService, fanPwmInstancePath, fanIntf,
675 "Value", static_cast<double>(pwmValue));
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000676 if (ret < 0)
677 {
678 return ipmi::responseUnspecifiedError();
679 }
680 }
681 break;
682 default:
683 {
684 return ipmi::responseInvalidFieldRequest();
Vernon Mauerya3702c12019-05-22 13:20:59 -0700685 }
686 }
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000687 }
688 break;
Ayushi Smritid872a4a2019-10-15 10:20:11 +0530689 case SmSignalSet::smSpeaker:
690 {
Alex Schendel4180cfe2022-11-29 10:46:11 -0800691 lg2::info("Performing Speaker SmActionSet", "ACTION", lg2::dec,
692 static_cast<uint8_t>(action));
Ayushi Smritid872a4a2019-10-15 10:20:11 +0530693 switch (action)
694 {
695 case SmActionSet::forceAsserted:
696 {
697 char beepDevName[] = "/dev/input/event0";
698 if (mtm.mtmTestBeepFd != -1)
699 {
Alex Schendel4180cfe2022-11-29 10:46:11 -0800700 lg2::info("mtm beep device is opened already!");
Ayushi Smritid872a4a2019-10-15 10:20:11 +0530701 // returning success as already beep is in progress
702 return ipmi::response(retCode);
703 }
704
Patrick Williams1bcced02024-08-16 15:20:24 -0400705 if ((mtm.mtmTestBeepFd =
706 ::open(beepDevName, O_RDWR | O_CLOEXEC)) < 0)
Ayushi Smritid872a4a2019-10-15 10:20:11 +0530707 {
Alex Schendel4180cfe2022-11-29 10:46:11 -0800708 lg2::error("Failed to open input device");
Ayushi Smritid872a4a2019-10-15 10:20:11 +0530709 return ipmi::responseUnspecifiedError();
710 }
711
712 struct input_event event;
713 event.type = EV_SND;
714 event.code = SND_TONE;
715 event.value = 2000;
716
717 if (::write(mtm.mtmTestBeepFd, &event,
718 sizeof(struct input_event)) !=
719 sizeof(struct input_event))
720 {
Alex Schendel4180cfe2022-11-29 10:46:11 -0800721 lg2::error("Failed to write a tone sound event");
Ayushi Smritid872a4a2019-10-15 10:20:11 +0530722 ::close(mtm.mtmTestBeepFd);
723 mtm.mtmTestBeepFd = -1;
724 return ipmi::responseUnspecifiedError();
725 }
726 mtm.revertTimer.start(revertTimeOut);
727 }
728 break;
729 case SmActionSet::revert:
730 case SmActionSet::forceDeasserted:
731 {
732 if (mtm.mtmTestBeepFd != -1)
733 {
734 ::close(mtm.mtmTestBeepFd);
735 mtm.mtmTestBeepFd = -1;
736 }
737 }
738 break;
739 default:
740 {
741 return ipmi::responseInvalidFieldRequest();
742 }
743 }
744 }
745 break;
Richard Marian Thomaiyar3594c6d2019-11-19 21:29:04 +0530746 case SmSignalSet::smDiskFaultLed:
747 {
748 boost::system::error_code ec;
749 using objPaths = std::vector<std::string>;
750 std::string driveBasePath =
751 "/xyz/openbmc_project/inventory/item/drive/";
752 static constexpr const char* driveLedIntf =
753 "xyz.openbmc_project.Led.Group";
754 static constexpr const char* hsbpService =
755 "xyz.openbmc_project.HsbpManager";
756
757 auto driveList = ctx->bus->yield_method_call<objPaths>(
758 ctx->yield, ec, "xyz.openbmc_project.ObjectMapper",
759 "/xyz/openbmc_project/object_mapper",
760 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths",
761 driveBasePath, 0, std::array<const char*, 1>{driveLedIntf});
762 if (ec)
763 {
Alex Schendel4180cfe2022-11-29 10:46:11 -0800764 lg2::error("Failed to query HSBP drive sub tree objects");
Richard Marian Thomaiyar3594c6d2019-11-19 21:29:04 +0530765 return ipmi::responseUnspecifiedError();
766 }
Patrick Williams1bcced02024-08-16 15:20:24 -0400767 std::string driveObjPath =
768 driveBasePath + "Drive_" + std::to_string(instance + 1);
Richard Marian Thomaiyar3594c6d2019-11-19 21:29:04 +0530769 if (std::find(driveList.begin(), driveList.end(), driveObjPath) ==
770 driveList.end())
771 {
772 return ipmi::responseInvalidFieldRequest();
773 }
774 bool driveLedState = false;
775 switch (action)
776 {
777 case SmActionSet::forceAsserted:
778 {
779 driveLedState = true;
780 }
781 break;
782 case SmActionSet::revert:
783 {
784 driveLedState = false;
785 }
786 break;
787 case SmActionSet::forceDeasserted:
788 {
789 driveLedState = false;
790 }
791 break;
792 default:
793 {
794 return ipmi::responseInvalidFieldRequest();
795 }
796 }
797 ret = mtm.setProperty(hsbpService, driveObjPath, driveLedIntf,
798 "Asserted", driveLedState);
799 if (ret < 0)
800 {
801 return ipmi::responseUnspecifiedError();
802 }
803 }
804 break;
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000805 default:
806 {
807 return ipmi::responseInvalidFieldRequest();
Vernon Mauerya3702c12019-05-22 13:20:59 -0700808 }
809 }
Richard Marian Thomaiyar4cc10152019-08-02 23:21:45 +0530810 if (retCode == ccSuccess)
811 {
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +0530812 resetMtmTimer(ctx);
Richard Marian Thomaiyar4cc10152019-08-02 23:21:45 +0530813 }
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000814 return ipmi::response(retCode);
Vernon Mauerya3702c12019-05-22 13:20:59 -0700815}
816
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +0530817ipmi::RspType<> mtmKeepAlive(ipmi::Context::ptr ctx, uint8_t reserved,
Richard Marian Thomaiyar666dd012019-08-02 20:55:37 +0530818 const std::array<char, 5>& intentionalSignature)
819{
Ayushi Smritie0511e52019-08-27 17:30:34 +0000820 // mfg filter logic is used to allow MTM keep alive command only in
821 // manfacturing mode
822
Richard Marian Thomaiyar666dd012019-08-02 20:55:37 +0530823 constexpr std::array<char, 5> signatureOk = {'I', 'N', 'T', 'E', 'L'};
824 if (intentionalSignature != signatureOk || reserved != 0)
825 {
826 return ipmi::responseInvalidFieldRequest();
827 }
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +0530828 return ipmi::response(resetMtmTimer(ctx));
Richard Marian Thomaiyar666dd012019-08-02 20:55:37 +0530829}
830
Ayushi Smritie0511e52019-08-27 17:30:34 +0000831static constexpr unsigned int makeCmdKey(unsigned int netFn, unsigned int cmd)
832{
833 return (netFn << 8) | cmd;
834}
835
Yong Li85feb132019-08-09 16:06:03 +0800836ipmi::Cc mfgFilterMessage(ipmi::message::Request::ptr request)
837{
Ayushi Smritie0511e52019-08-27 17:30:34 +0000838 // Restricted commands, must be executed only in Manufacturing mode
839 switch (makeCmdKey(request->ctx->netFn, request->ctx->cmd))
Yong Li85feb132019-08-09 16:06:03 +0800840 {
Matt Simmering80d4d5f2023-02-15 15:18:51 -0800841 // i2c controller write read command needs additional checking
Ayushi Smritie0511e52019-08-27 17:30:34 +0000842 case makeCmdKey(ipmi::netFnApp, ipmi::app::cmdMasterWriteRead):
843 if (request->payload.size() > 4)
844 {
Richard Marian Thomaiyarae13ac62019-12-17 15:45:12 +0530845 // Allow write data count > 1 only in Special mode
846 if (mtm.getMfgMode() == SpecialMode::none)
Ayushi Smritie0511e52019-08-27 17:30:34 +0000847 {
848 return ipmi::ccInsufficientPrivilege;
849 }
850 }
jayaprakash Mutyala2d4a0192019-11-11 22:12:45 +0000851 return ipmi::ccSuccess;
Ayushi Smritie0511e52019-08-27 17:30:34 +0000852 case makeCmdKey(ipmi::netFnOemOne,
853 ipmi::intel::general::cmdGetSmSignal):
854 case makeCmdKey(ipmi::netFnOemOne,
855 ipmi::intel::general::cmdSetSmSignal):
856 case makeCmdKey(ipmi::netFnOemOne,
857 ipmi::intel::general::cmdMtmKeepAlive):
858 case makeCmdKey(ipmi::netFnOemOne,
859 ipmi::intel::general::cmdSetManufacturingData):
860 case makeCmdKey(ipmi::netFnOemOne,
861 ipmi::intel::general::cmdGetManufacturingData):
Vernon Mauery27d23562021-08-12 10:43:39 -0700862 case makeCmdKey(ipmi::intel::netFnGeneral,
863 ipmi::intel::general::cmdSetFITcLayout):
Arun P. Mohanan06584cd2021-08-13 20:56:01 +0530864 case makeCmdKey(ipmi::netFnOemOne,
865 ipmi::intel::general::cmdMTMBMCFeatureControl):
Ayushi Smritie0511e52019-08-27 17:30:34 +0000866 case makeCmdKey(ipmi::netFnStorage, ipmi::storage::cmdWriteFruData):
AppaRao Puli9a13daa2020-07-13 17:53:00 +0530867 case makeCmdKey(ipmi::netFnOemTwo, ipmi::intel::platform::cmdClearCMOS):
Ayushi Smritie0511e52019-08-27 17:30:34 +0000868
Richard Marian Thomaiyarae13ac62019-12-17 15:45:12 +0530869 // Check for Special mode
870 if (mtm.getMfgMode() == SpecialMode::none)
Yong Li85feb132019-08-09 16:06:03 +0800871 {
Ayushi Smritie0511e52019-08-27 17:30:34 +0000872 return ipmi::ccInvalidCommand;
Yong Li85feb132019-08-09 16:06:03 +0800873 }
jayaprakash Mutyala2d4a0192019-11-11 22:12:45 +0000874 return ipmi::ccSuccess;
875 case makeCmdKey(ipmi::netFnStorage, ipmi::storage::cmdDeleteSelEntry):
876 {
877 return ipmi::ccInvalidCommand;
878 }
Yong Li85feb132019-08-09 16:06:03 +0800879 }
Yong Li85feb132019-08-09 16:06:03 +0800880 return ipmi::ccSuccess;
881}
882
Richard Marian Thomaiyar1f0839c2019-08-25 20:10:52 +0530883static constexpr uint8_t maxEthSize = 6;
884static constexpr uint8_t maxSupportedEth = 3;
885static constexpr const char* factoryEthAddrBaseFileName =
886 "/var/sofs/factory-settings/network/mac/eth";
887
Alex Schendel097497f2022-10-07 14:37:15 -0700888bool findFruDevice(ipmi::Context::ptr& ctx, uint64_t& macOffset,
Zhikui Renad129c62022-04-05 20:11:24 -0700889 uint64_t& busNum, uint64_t& address)
890{
Alex Schendel097497f2022-10-07 14:37:15 -0700891 boost::system::error_code ec{};
892 ObjectValueTree obj;
Zhikui Renad129c62022-04-05 20:11:24 -0700893
894 // GetAll the objects under service FruDevice
Alex Schendel097497f2022-10-07 14:37:15 -0700895 ec = getManagedObjects(ctx, "xyz.openbmc_project.EntityManager",
896 "/xyz/openbmc_project/inventory", obj);
Zhikui Renad129c62022-04-05 20:11:24 -0700897 if (ec)
898 {
Alex Schendel4180cfe2022-11-29 10:46:11 -0800899 lg2::error("GetManagedObjects failed", "ERROR", ec.message().c_str());
Zhikui Renad129c62022-04-05 20:11:24 -0700900 return false;
901 }
902
903 for (const auto& [path, fru] : obj)
904 {
905 for (const auto& [intf, propMap] : fru)
906 {
907 if (intf == "xyz.openbmc_project.Inventory.Item.Board.Motherboard")
908 {
909 auto findBus = propMap.find("FruBus");
910 auto findAddress = propMap.find("FruAddress");
911 auto findMacOffset = propMap.find("MacOffset");
912 if (findBus == propMap.end() || findAddress == propMap.end() ||
913 findMacOffset == propMap.end())
914 {
915 continue;
916 }
917
918 auto fruBus = std::get_if<uint64_t>(&findBus->second);
919 auto fruAddress = std::get_if<uint64_t>(&findAddress->second);
920 auto macFruOffset =
921 std::get_if<uint64_t>(&findMacOffset->second);
922 if (!fruBus || !fruAddress || !macFruOffset)
923 {
Alex Schendel4180cfe2022-11-29 10:46:11 -0800924 lg2::info("ERROR: MotherBoard FRU config data type "
925 "invalid, not used");
Zhikui Renad129c62022-04-05 20:11:24 -0700926 return false;
927 }
928 busNum = *fruBus;
929 address = *fruAddress;
930 macOffset = *macFruOffset;
931 return true;
932 }
933 }
934 }
935 return false;
936}
937
Zhikui Ren98fa87e2022-05-04 08:10:29 -0700938static constexpr uint64_t fruEnd = 0xff;
939// write rolls over within current page, need to keep mac within a page
940static constexpr uint64_t fruPageSize = 0x8;
941// MAC record struct: HEADER, MAC DATA, CheckSum
942static constexpr uint64_t macRecordSize = maxEthSize + 2;
943static_assert(fruPageSize >= macRecordSize,
944 "macRecordSize greater than eeprom page size");
945static constexpr uint8_t macHeader = 0x40;
946// Calculate new checksum for fru info area
947static uint8_t calculateChecksum(std::vector<uint8_t>::const_iterator iter,
948 std::vector<uint8_t>::const_iterator end)
949{
950 constexpr int checksumMod = 256;
951 uint8_t sum = std::accumulate(iter, end, static_cast<uint8_t>(0));
952 return (checksumMod - sum) % checksumMod;
953}
954
Zhikui Renad129c62022-04-05 20:11:24 -0700955bool readMacFromFru(ipmi::Context::ptr ctx, uint8_t macIndex,
956 std::array<uint8_t, maxEthSize>& ethData)
957{
Zhikui Renad129c62022-04-05 20:11:24 -0700958 uint64_t macOffset = fruEnd;
959 uint64_t fruBus = 0;
960 uint64_t fruAddress = 0;
961
Alex Schendel097497f2022-10-07 14:37:15 -0700962 if (findFruDevice(ctx, macOffset, fruBus, fruAddress))
Zhikui Renad129c62022-04-05 20:11:24 -0700963 {
Alex Schendel4180cfe2022-11-29 10:46:11 -0800964 lg2::info("Found mac fru", "BUS", lg2::dec,
965 static_cast<uint8_t>(fruBus), "ADDRESS", lg2::dec,
966 static_cast<uint8_t>(fruAddress));
Zhikui Renad129c62022-04-05 20:11:24 -0700967
Zhikui Ren98fa87e2022-05-04 08:10:29 -0700968 if (macOffset % fruPageSize)
969 {
970 macOffset = (macOffset / fruPageSize + 1) * fruPageSize;
971 }
972 macOffset += macIndex * fruPageSize;
973 if ((macOffset + macRecordSize) > fruEnd)
Zhikui Renad129c62022-04-05 20:11:24 -0700974 {
Alex Schendel4180cfe2022-11-29 10:46:11 -0800975 lg2::error("ERROR: read fru mac failed, offset invalid");
Zhikui Renad129c62022-04-05 20:11:24 -0700976 return false;
977 }
978 std::vector<uint8_t> writeData;
Zhikui Ren98fa87e2022-05-04 08:10:29 -0700979 writeData.push_back(static_cast<uint8_t>(macOffset));
980 std::vector<uint8_t> readBuf(macRecordSize);
Zhikui Renad129c62022-04-05 20:11:24 -0700981 std::string i2cBus = "/dev/i2c-" + std::to_string(fruBus);
Patrick Williams1bcced02024-08-16 15:20:24 -0400982 ipmi::Cc retI2C =
983 ipmi::i2cWriteRead(i2cBus, fruAddress, writeData, readBuf);
Zhikui Renad129c62022-04-05 20:11:24 -0700984 if (retI2C == ipmi::ccSuccess)
985 {
Zhikui Ren98fa87e2022-05-04 08:10:29 -0700986 uint8_t cs = calculateChecksum(readBuf.cbegin(), readBuf.cend());
987 if (cs == 0)
988 {
989 std::copy(++readBuf.begin(), --readBuf.end(), ethData.data());
990 return true;
991 }
Zhikui Renad129c62022-04-05 20:11:24 -0700992 }
993 }
994 return false;
995}
996
997ipmi::Cc writeMacToFru(ipmi::Context::ptr ctx, uint8_t macIndex,
998 std::array<uint8_t, maxEthSize>& ethData)
999{
Zhikui Renad129c62022-04-05 20:11:24 -07001000 uint64_t macOffset = fruEnd;
1001 uint64_t fruBus = 0;
1002 uint64_t fruAddress = 0;
1003
Alex Schendel097497f2022-10-07 14:37:15 -07001004 if (findFruDevice(ctx, macOffset, fruBus, fruAddress))
Zhikui Renad129c62022-04-05 20:11:24 -07001005 {
Alex Schendel4180cfe2022-11-29 10:46:11 -08001006 lg2::info("Found mac fru", "BUS", lg2::dec,
1007 static_cast<uint8_t>(fruBus), "ADDRESS", lg2::dec,
1008 static_cast<uint8_t>(fruAddress));
Zhikui Renad129c62022-04-05 20:11:24 -07001009
Zhikui Ren98fa87e2022-05-04 08:10:29 -07001010 if (macOffset % fruPageSize)
1011 {
1012 macOffset = (macOffset / fruPageSize + 1) * fruPageSize;
1013 }
1014 macOffset += macIndex * fruPageSize;
1015 if ((macOffset + macRecordSize) > fruEnd)
Zhikui Renad129c62022-04-05 20:11:24 -07001016 {
Alex Schendel4180cfe2022-11-29 10:46:11 -08001017 lg2::error("ERROR: write mac fru failed, offset invalid.");
Zhikui Renad129c62022-04-05 20:11:24 -07001018 return ipmi::ccParmOutOfRange;
1019 }
1020 std::vector<uint8_t> writeData;
Zhikui Ren98fa87e2022-05-04 08:10:29 -07001021 writeData.reserve(macRecordSize + 1); // include start location
1022 writeData.push_back(static_cast<uint8_t>(macOffset));
1023 writeData.push_back(macHeader);
Zhikui Renad129c62022-04-05 20:11:24 -07001024 std::for_each(ethData.cbegin(), ethData.cend(),
1025 [&](uint8_t i) { writeData.push_back(i); });
Patrick Williams1bcced02024-08-16 15:20:24 -04001026 uint8_t macCheckSum =
1027 calculateChecksum(++writeData.cbegin(), writeData.cend());
Zhikui Ren98fa87e2022-05-04 08:10:29 -07001028 writeData.push_back(macCheckSum);
Zhikui Renad129c62022-04-05 20:11:24 -07001029
1030 std::string i2cBus = "/dev/i2c-" + std::to_string(fruBus);
1031 std::vector<uint8_t> readBuf;
Patrick Williams1bcced02024-08-16 15:20:24 -04001032 ipmi::Cc ret =
1033 ipmi::i2cWriteRead(i2cBus, fruAddress, writeData, readBuf);
Zhikui Ren98fa87e2022-05-04 08:10:29 -07001034
Zhikui Ren0408e792022-06-07 21:03:33 -07001035 // prepare for read to detect chip is write protected
1036 writeData.resize(1);
1037 readBuf.resize(maxEthSize + 1); // include macHeader
1038
Zhikui Renad129c62022-04-05 20:11:24 -07001039 switch (ret)
1040 {
1041 case ipmi::ccSuccess:
Zhikui Ren98fa87e2022-05-04 08:10:29 -07001042 // Wait for internal write cycle to complete
1043 // example: ATMEL 24c0x chip has Twr spec as 5ms
1044
1045 // Ideally we want yield wait, but currently following code
1046 // crash with "thread not supported"
1047 // boost::asio::deadline_timer timer(
1048 // boost::asio::get_associated_executor(ctx->yield),
1049 // boost::posix_time::seconds(1));
1050 // timer.async_wait(ctx->yield);
1051 // use usleep as temp WA
1052 usleep(5000);
1053 if (ipmi::i2cWriteRead(i2cBus, fruAddress, writeData,
1054 readBuf) == ipmi::ccSuccess)
Zhikui Renad129c62022-04-05 20:11:24 -07001055 {
1056 if (std::equal(ethData.begin(), ethData.end(),
Zhikui Ren98fa87e2022-05-04 08:10:29 -07001057 ++readBuf.begin())) // skip macHeader
Zhikui Renad129c62022-04-05 20:11:24 -07001058 {
1059 return ipmi::ccSuccess;
1060 }
Alex Schendel4180cfe2022-11-29 10:46:11 -08001061 lg2::info("INFO: write mac fru verify failed, fru may be "
1062 "write protected.");
Zhikui Renad129c62022-04-05 20:11:24 -07001063 }
1064 return ipmi::ccCommandNotAvailable;
Zhikui Ren0408e792022-06-07 21:03:33 -07001065 default:
1066 if (ipmi::i2cWriteRead(i2cBus, fruAddress, writeData,
1067 readBuf) == ipmi::ccSuccess)
1068 {
Alex Schendel4180cfe2022-11-29 10:46:11 -08001069 lg2::info("INFO: write mac fru failed, but successfully "
1070 "read from fru, fru may be write protected.");
Zhikui Ren0408e792022-06-07 21:03:33 -07001071 return ipmi::ccCommandNotAvailable;
1072 }
1073 else // assume failure is due to no eeprom on board
1074 {
Alex Schendel4180cfe2022-11-29 10:46:11 -08001075 lg2::error("ERROR: write mac fru failed, assume no eeprom "
1076 "is available.");
Zhikui Ren0408e792022-06-07 21:03:33 -07001077 }
Zhikui Renad129c62022-04-05 20:11:24 -07001078 break;
1079 }
1080 }
1081 // no FRU eeprom found
1082 return ipmi::ccDestinationUnavailable;
1083}
1084
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +05301085ipmi::RspType<> setManufacturingData(ipmi::Context::ptr ctx, uint8_t dataType,
Richard Marian Thomaiyar1f0839c2019-08-25 20:10:52 +05301086 std::array<uint8_t, maxEthSize> ethData)
1087{
Zhikui Renad129c62022-04-05 20:11:24 -07001088 // mfg filter logic will restrict this command executing only in mfg
1089 // mode.
Richard Marian Thomaiyar1f0839c2019-08-25 20:10:52 +05301090 if (dataType >= maxSupportedEth)
1091 {
1092 return ipmi::responseParmOutOfRange();
1093 }
1094
Zhikui Renad129c62022-04-05 20:11:24 -07001095 ipmi::Cc ret = writeMacToFru(ctx, dataType, ethData);
1096 if (ret != ipmi::ccDestinationUnavailable)
1097 {
1098 resetMtmTimer(ctx);
1099 return response(ret);
1100 }
1101
Richard Marian Thomaiyar1f0839c2019-08-25 20:10:52 +05301102 constexpr uint8_t ethAddrStrSize =
1103 19; // XX:XX:XX:XX:XX:XX + \n + null termination;
1104 std::vector<uint8_t> buff(ethAddrStrSize);
1105 std::snprintf(reinterpret_cast<char*>(buff.data()), ethAddrStrSize,
1106 "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx\n", ethData.at(0),
1107 ethData.at(1), ethData.at(2), ethData.at(3), ethData.at(4),
1108 ethData.at(5));
Patrick Williams1bcced02024-08-16 15:20:24 -04001109 std::ofstream oEthFile(
1110 factoryEthAddrBaseFileName + std::to_string(dataType),
1111 std::ofstream::out);
Richard Marian Thomaiyar1f0839c2019-08-25 20:10:52 +05301112 if (!oEthFile.good())
1113 {
1114 return ipmi::responseUnspecifiedError();
1115 }
1116
1117 oEthFile << reinterpret_cast<char*>(buff.data());
Johnathan Mantey6d83e4e2020-05-08 11:01:01 -07001118 oEthFile.flush();
Richard Marian Thomaiyar1f0839c2019-08-25 20:10:52 +05301119 oEthFile.close();
1120
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +05301121 resetMtmTimer(ctx);
Richard Marian Thomaiyar1f0839c2019-08-25 20:10:52 +05301122 return ipmi::responseSuccess();
1123}
1124
1125ipmi::RspType<uint8_t, std::array<uint8_t, maxEthSize>>
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +05301126 getManufacturingData(ipmi::Context::ptr ctx, uint8_t dataType)
Richard Marian Thomaiyar1f0839c2019-08-25 20:10:52 +05301127{
Zhikui Renad129c62022-04-05 20:11:24 -07001128 // mfg filter logic will restrict this command executing only in mfg
1129 // mode.
Richard Marian Thomaiyar1f0839c2019-08-25 20:10:52 +05301130 if (dataType >= maxSupportedEth)
1131 {
1132 return ipmi::responseParmOutOfRange();
1133 }
1134 std::array<uint8_t, maxEthSize> ethData{0};
1135 constexpr uint8_t invalidData = 0;
1136 constexpr uint8_t validData = 1;
1137
Patrick Williams1bcced02024-08-16 15:20:24 -04001138 std::ifstream iEthFile(
1139 factoryEthAddrBaseFileName + std::to_string(dataType),
1140 std::ifstream::in);
Richard Marian Thomaiyar1f0839c2019-08-25 20:10:52 +05301141 if (!iEthFile.good())
1142 {
Zhikui Renad129c62022-04-05 20:11:24 -07001143 if (readMacFromFru(ctx, dataType, ethData))
1144 {
1145 resetMtmTimer(ctx);
1146 return ipmi::responseSuccess(validData, ethData);
1147 }
Richard Marian Thomaiyar1f0839c2019-08-25 20:10:52 +05301148 return ipmi::responseSuccess(invalidData, ethData);
1149 }
1150 std::string ethStr;
1151 iEthFile >> ethStr;
1152 uint8_t* data = ethData.data();
1153 std::sscanf(ethStr.c_str(), "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx",
1154 data, (data + 1), (data + 2), (data + 3), (data + 4),
1155 (data + 5));
1156
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +05301157 resetMtmTimer(ctx);
Richard Marian Thomaiyar1f0839c2019-08-25 20:10:52 +05301158 return ipmi::responseSuccess(validData, ethData);
1159}
1160
Matt Simmering80d4d5f2023-02-15 15:18:51 -08001161/** @brief implements slot controller write read IPMI command which can be used
Zhikui Renad129c62022-04-05 20:11:24 -07001162 * for low-level I2C/SMBus write, read or write-read access for PCIE slots
V-Sanjana7acb2d22022-11-15 11:13:54 +05301163 * @param reserved - skip 3 bit
1164 * @param muxType - mux type
Yong Lif267a672019-08-29 11:16:07 +08001165 * @param addressType - address type
1166 * @param bbSlotNum - baseboard slot number
1167 * @param riserSlotNum - riser slot number
1168 * @param reserved2 - skip 2 bit
Matt Simmering80d4d5f2023-02-15 15:18:51 -08001169 * @param targetAddr - target address
Yong Lif267a672019-08-29 11:16:07 +08001170 * @param readCount - number of bytes to be read
1171 * @param writeData - data to be written
1172 *
1173 * @returns IPMI completion code plus response data
1174 */
V-Sanjana7acb2d22022-11-15 11:13:54 +05301175
Matt Simmering80d4d5f2023-02-15 15:18:51 -08001176ipmi::RspType<std::vector<uint8_t>> appSlotI2CControllerWriteRead(
V-Sanjana7acb2d22022-11-15 11:13:54 +05301177 uint3_t reserved, uint3_t muxType, uint2_t addressType, uint3_t bbSlotNum,
Matt Simmering80d4d5f2023-02-15 15:18:51 -08001178 uint3_t riserSlotNum, uint2_t reserved2, uint8_t targetAddr,
V-Sanjana7acb2d22022-11-15 11:13:54 +05301179 uint8_t readCount, std::vector<uint8_t> writeData)
Yong Lif267a672019-08-29 11:16:07 +08001180{
Vernon Maueryfc3bc382022-06-02 13:52:04 -07001181 if (reserved || reserved2)
1182 {
1183 return ipmi::responseInvalidFieldRequest();
1184 }
Yong Lif267a672019-08-29 11:16:07 +08001185 const size_t writeCount = writeData.size();
1186 std::string i2cBus;
1187 if (addressType == slotAddressTypeBus)
1188 {
V-Sanjana7acb2d22022-11-15 11:13:54 +05301189 std::string path = "/dev/i2c-mux/";
1190 if (muxType == bbRiserMux)
1191 {
1192 path += "Riser_" + std::to_string(static_cast<uint8_t>(bbSlotNum)) +
1193 "_Mux/Pcie_Slot_" +
1194 std::to_string(static_cast<uint8_t>(riserSlotNum));
1195 }
1196 else if (muxType == leftRiserMux)
1197 {
1198 path += "Left_Riser_Mux/Slot_" +
1199 std::to_string(static_cast<uint8_t>(riserSlotNum));
1200 }
1201 else if (muxType == rightRiserMux)
1202 {
1203 path += "Right_Riser_Mux/Slot_" +
1204 std::to_string(static_cast<uint8_t>(riserSlotNum));
1205 }
1206 else if (muxType == pcieMux)
1207 {
1208 path += "PCIe_Mux/Slot_" +
1209 std::to_string(static_cast<uint8_t>(riserSlotNum));
1210 }
1211 else if (muxType == hsbpMux)
1212 {
1213 path += "HSBP_Mux/Slot" +
1214 std::to_string(static_cast<uint8_t>(riserSlotNum));
1215 }
1216 phosphor::logging::log<phosphor::logging::level::DEBUG>(
1217 ("Path is: " + path).c_str());
Yong Lif267a672019-08-29 11:16:07 +08001218 if (std::filesystem::exists(path) && std::filesystem::is_symlink(path))
1219 {
1220 i2cBus = std::filesystem::read_symlink(path);
1221 }
1222 else
1223 {
Matt Simmering80d4d5f2023-02-15 15:18:51 -08001224 lg2::error("Controller write read command: Cannot get BusID");
Yong Lif267a672019-08-29 11:16:07 +08001225 return ipmi::responseInvalidFieldRequest();
1226 }
1227 }
1228 else if (addressType == slotAddressTypeUniqueid)
1229 {
1230 i2cBus = "/dev/i2c-" +
1231 std::to_string(static_cast<uint8_t>(bbSlotNum) |
1232 (static_cast<uint8_t>(riserSlotNum) << 3));
1233 }
1234 else
1235 {
Matt Simmering80d4d5f2023-02-15 15:18:51 -08001236 lg2::error("Controller write read command: invalid request");
Yong Lif267a672019-08-29 11:16:07 +08001237 return ipmi::responseInvalidFieldRequest();
1238 }
1239
Zhikui Renad129c62022-04-05 20:11:24 -07001240 // Allow single byte write as it is offset byte to read the data, rest
1241 // allow only in Special mode.
Yong Lif267a672019-08-29 11:16:07 +08001242 if (writeCount > 1)
1243 {
Richard Marian Thomaiyarae13ac62019-12-17 15:45:12 +05301244 if (mtm.getMfgMode() == SpecialMode::none)
Yong Lif267a672019-08-29 11:16:07 +08001245 {
1246 return ipmi::responseInsufficientPrivilege();
1247 }
1248 }
1249
1250 if (readCount > slotI2CMaxReadSize)
1251 {
Matt Simmering80d4d5f2023-02-15 15:18:51 -08001252 lg2::error("Controller write read command: Read count exceeds limit");
Yong Lif267a672019-08-29 11:16:07 +08001253 return ipmi::responseParmOutOfRange();
1254 }
1255
1256 if (!readCount && !writeCount)
1257 {
Matt Simmering80d4d5f2023-02-15 15:18:51 -08001258 lg2::error("Controller write read command: Read & write count are 0");
Yong Lif267a672019-08-29 11:16:07 +08001259 return ipmi::responseInvalidFieldRequest();
1260 }
1261
1262 std::vector<uint8_t> readBuf(readCount);
1263
Patrick Williams1bcced02024-08-16 15:20:24 -04001264 ipmi::Cc retI2C =
1265 ipmi::i2cWriteRead(i2cBus, targetAddr, writeData, readBuf);
Yong Lif267a672019-08-29 11:16:07 +08001266 if (retI2C != ipmi::ccSuccess)
1267 {
1268 return ipmi::response(retI2C);
1269 }
1270
1271 return ipmi::responseSuccess(readBuf);
1272}
Yong Li068b4f22019-09-17 16:32:18 +08001273
1274ipmi::RspType<> clearCMOS()
1275{
Matt Simmering80d4d5f2023-02-15 15:18:51 -08001276 // There is an i2c device on bus 4, the target address is 0x38. Based on
Zhikui Renad129c62022-04-05 20:11:24 -07001277 // the spec, writing 0x1 to address 0x61 on this device, will trigger
1278 // the clear CMOS action.
Matt Simmering80d4d5f2023-02-15 15:18:51 -08001279 constexpr uint8_t targetAddr = 0x38;
Yong Li068b4f22019-09-17 16:32:18 +08001280 std::string i2cBus = "/dev/i2c-4";
Yong Lieaeb6cb2020-03-09 20:21:50 +08001281 std::vector<uint8_t> writeData = {0x61, 0x1};
Yong Li068b4f22019-09-17 16:32:18 +08001282 std::vector<uint8_t> readBuf(0);
1283
Patrick Williams1bcced02024-08-16 15:20:24 -04001284 ipmi::Cc retI2C =
1285 ipmi::i2cWriteRead(i2cBus, targetAddr, writeData, readBuf);
Yong Li068b4f22019-09-17 16:32:18 +08001286 return ipmi::response(retI2C);
1287}
Vernon Mauery27d23562021-08-12 10:43:39 -07001288
1289ipmi::RspType<> setFITcLayout(uint32_t layout)
1290{
1291 static constexpr const char* factoryFITcLayout =
1292 "/var/sofs/factory-settings/layout/fitc";
1293 std::filesystem::path fitcDir =
1294 std::filesystem::path(factoryFITcLayout).parent_path();
1295 std::error_code ec;
1296 std::filesystem::create_directories(fitcDir, ec);
1297 if (ec)
1298 {
1299 return ipmi::responseUnspecifiedError();
1300 }
1301 try
1302 {
1303 std::ofstream file(factoryFITcLayout);
1304 file << layout;
1305 file.flush();
1306 file.close();
1307 }
1308 catch (const std::exception& e)
1309 {
1310 return ipmi::responseUnspecifiedError();
1311 }
1312
1313 return ipmi::responseSuccess();
1314}
1315
Arun P. Mohanan06584cd2021-08-13 20:56:01 +05301316static std::vector<std::string>
1317 getMCTPServiceConfigPaths(ipmi::Context::ptr& ctx)
1318{
1319 boost::system::error_code ec;
1320 auto configPaths = ctx->bus->yield_method_call<std::vector<std::string>>(
1321 ctx->yield, ec, "xyz.openbmc_project.ObjectMapper",
1322 "/xyz/openbmc_project/object_mapper",
1323 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths",
1324 "/xyz/openbmc_project/inventory/system/board", 2,
1325 std::array<const char*, 1>{
1326 "xyz.openbmc_project.Configuration.MctpConfiguration"});
1327 if (ec)
1328 {
1329 throw std::runtime_error(
1330 "Failed to query configuration sub tree objects");
1331 }
1332 return configPaths;
1333}
1334
Patrick Williams1bcced02024-08-16 15:20:24 -04001335static ipmi::RspType<> startOrStopService(
1336 ipmi::Context::ptr& ctx, const uint8_t enable,
1337 const std::string& serviceName, bool disableOrEnableUnitFiles = true)
Arun P. Mohanan06584cd2021-08-13 20:56:01 +05301338{
1339 constexpr bool runtimeOnly = false;
1340 constexpr bool force = false;
1341
1342 boost::system::error_code ec;
1343 switch (enable)
1344 {
1345 case ipmi::SupportedFeatureActions::stop:
1346 ctx->bus->yield_method_call(ctx->yield, ec, systemDService,
1347 systemDObjPath, systemDMgrIntf,
1348 "StopUnit", serviceName, "replace");
1349 break;
1350 case ipmi::SupportedFeatureActions::start:
1351 ctx->bus->yield_method_call(ctx->yield, ec, systemDService,
1352 systemDObjPath, systemDMgrIntf,
1353 "StartUnit", serviceName, "replace");
1354 break;
1355 case ipmi::SupportedFeatureActions::disable:
ANJALI RAY1fe485c2021-12-17 16:41:30 +00001356 if (disableOrEnableUnitFiles == true)
1357 {
1358 ctx->bus->yield_method_call(
1359 ctx->yield, ec, systemDService, systemDObjPath,
1360 systemDMgrIntf, "DisableUnitFiles",
1361 std::array<const char*, 1>{serviceName.c_str()},
1362 runtimeOnly);
1363 }
Arun P. Mohanan06584cd2021-08-13 20:56:01 +05301364 ctx->bus->yield_method_call(
1365 ctx->yield, ec, systemDService, systemDObjPath, systemDMgrIntf,
1366 "MaskUnitFiles",
1367 std::array<const char*, 1>{serviceName.c_str()}, runtimeOnly,
1368 force);
Arun P. Mohanan06584cd2021-08-13 20:56:01 +05301369 break;
1370 case ipmi::SupportedFeatureActions::enable:
1371 ctx->bus->yield_method_call(
1372 ctx->yield, ec, systemDService, systemDObjPath, systemDMgrIntf,
1373 "UnmaskUnitFiles",
1374 std::array<const char*, 1>{serviceName.c_str()}, runtimeOnly);
ANJALI RAY1fe485c2021-12-17 16:41:30 +00001375 if (disableOrEnableUnitFiles == true)
1376 {
1377 ctx->bus->yield_method_call(
1378 ctx->yield, ec, systemDService, systemDObjPath,
1379 systemDMgrIntf, "EnableUnitFiles",
1380 std::array<const char*, 1>{serviceName.c_str()},
1381 runtimeOnly, force);
1382 }
Arun P. Mohanan06584cd2021-08-13 20:56:01 +05301383 break;
1384 default:
Alex Schendel4180cfe2022-11-29 10:46:11 -08001385 lg2::warning("ERROR: Invalid feature action selected", "ACTION",
1386 lg2::dec, enable);
Arun P. Mohanan06584cd2021-08-13 20:56:01 +05301387 return ipmi::responseInvalidFieldRequest();
1388 }
1389 if (ec)
1390 {
Alex Schendel4180cfe2022-11-29 10:46:11 -08001391 lg2::warning("ERROR: Service start or stop failed", "SERVICE",
1392 serviceName.c_str());
Arun P. Mohanan06584cd2021-08-13 20:56:01 +05301393 return ipmi::responseUnspecifiedError();
1394 }
1395 return ipmi::responseSuccess();
1396}
1397
1398static std::string getMCTPServiceName(const std::string& objectPath)
1399{
1400 const auto serviceArgument = boost::algorithm::replace_all_copy(
1401 boost::algorithm::replace_first_copy(
1402 objectPath, "/xyz/openbmc_project/inventory/system/board/", ""),
1403 "/", "_2f");
Patrick Williams1bcced02024-08-16 15:20:24 -04001404 std::string unitName =
1405 "xyz.openbmc_project.mctpd@" + serviceArgument + ".service";
Arun P. Mohanan06584cd2021-08-13 20:56:01 +05301406 return unitName;
1407}
1408
Patrick Williams1bcced02024-08-16 15:20:24 -04001409static ipmi::RspType<> handleMCTPFeature(
1410 ipmi::Context::ptr& ctx, const uint8_t enable, const std::string& binding)
Arun P. Mohanan06584cd2021-08-13 20:56:01 +05301411{
1412 std::vector<std::string> configPaths;
1413 try
1414 {
1415 configPaths = getMCTPServiceConfigPaths(ctx);
1416 }
1417 catch (const std::exception& e)
1418 {
Alex Schendel4180cfe2022-11-29 10:46:11 -08001419 lg2::error(e.what());
Arun P. Mohanan06584cd2021-08-13 20:56:01 +05301420 return ipmi::responseUnspecifiedError();
1421 }
1422
1423 for (const auto& objectPath : configPaths)
1424 {
Patrick Williamsb37abfb2023-05-10 07:50:33 -05001425 const auto pos = objectPath.find_last_of('/');
Arun P. Mohanan06584cd2021-08-13 20:56:01 +05301426 if (binding == objectPath.substr(pos + 1))
1427 {
1428 return startOrStopService(ctx, enable,
ANJALI RAY1fe485c2021-12-17 16:41:30 +00001429 getMCTPServiceName(objectPath), false);
Arun P. Mohanan06584cd2021-08-13 20:56:01 +05301430 }
1431 }
1432 return ipmi::responseSuccess();
1433}
1434
Vasu V97c30902023-05-19 15:56:15 +05301435static bool isNum(const std::string& s)
1436{
1437 if (s.empty())
1438 {
1439 return false;
1440 }
1441 uint8_t busNumber;
1442 const auto sEnd = s.data() + s.size();
1443 const auto& [ptr, ec] = std::from_chars(s.data(), sEnd, busNumber);
1444 if (ec == std::errc() || ptr == sEnd)
1445 {
1446 return true;
1447 }
1448 return false;
1449}
1450
1451bool getBusNumFromPath(const std::string& path, std::string& busStr)
1452{
1453 std::vector<std::string> parts;
1454 boost::split(parts, path, boost::is_any_of("-"));
1455 if (parts.size() == 2)
1456 {
1457 busStr = parts[1];
1458 if (isNum(busStr))
1459 {
1460 return true;
1461 }
1462 }
1463 return false;
1464}
1465
1466static ipmi::RspType<> muxSlotDisable(ipmi::Context::ptr& ctx,
1467 std::string service, std::string muxName,
1468 uint8_t action, uint8_t slotNum)
1469{
1470 boost::system::error_code ec;
1471 const std::filesystem::path muxSymlinkDirPath =
1472 "/dev/i2c-mux/" + muxName + "/Slot_" + std::to_string(slotNum + 1);
1473 if (!std::filesystem::is_symlink(muxSymlinkDirPath))
1474 {
1475 return ipmi::responseInvalidFieldRequest();
1476 }
1477 std::string linkPath = std::filesystem::read_symlink(muxSymlinkDirPath);
1478
1479 std::string portNum;
1480 if (!getBusNumFromPath(linkPath, portNum))
1481 {
1482 return ipmi::responseInvalidFieldRequest();
1483 }
1484 auto res = ctx->bus->yield_method_call<int>(
1485 ctx->yield, ec, service, mctpObjPath, mctpBaseIntf, "SkipList",
1486 std::vector<uint8_t>{action, static_cast<uint8_t>(std::stoi(portNum))});
1487 if (ec)
1488 {
1489 lg2::error("Failed to set mctp skiplist");
1490 return ipmi::responseUnspecifiedError();
1491 }
1492
1493 if (!res)
1494 {
1495 return ipmi::responseResponseError();
1496 }
1497 return ipmi::responseSuccess();
1498}
1499
Patrick Williams1bcced02024-08-16 15:20:24 -04001500static ipmi::RspType<> handleMCTPSlotFeature(
1501 ipmi::Context::ptr& ctx, const uint8_t enable, const uint8_t featureArg)
Vasu V97c30902023-05-19 15:56:15 +05301502{
1503 uint8_t slotNum = (featureArg & slotNumMask);
1504 switch ((featureArg & muxTypeMask) >> muxTypeShift)
1505 {
1506 case ipmi::SupportedFeatureMuxs::pcieMuxSlot:
1507 return muxSlotDisable(ctx, mctpPcieSlotService, "PCIe_Mux", enable,
1508 slotNum);
1509 break;
1510 case ipmi::SupportedFeatureMuxs::pcieMcioMuxSlot:
1511 return muxSlotDisable(ctx, mctpPcieSlotService, "PCIe_MCIO_Mux",
1512 enable, slotNum);
1513 break;
1514 case ipmi::SupportedFeatureMuxs::pcieM2EdSffMuxSlot:
1515 return muxSlotDisable(ctx, mctpPcieSlotService, "M2_EDSFF_Mux",
1516 enable, slotNum);
1517 break;
1518 case ipmi::SupportedFeatureMuxs::leftRaiserMuxSlot:
1519 return muxSlotDisable(ctx, mctpPcieSlotService, "Left_Riser_Mux",
1520 enable, slotNum);
1521 break;
1522 case ipmi::SupportedFeatureMuxs::rightRaiserMuxSlot:
1523 return muxSlotDisable(ctx, mctpPcieSlotService, "Right_Riser_Mux",
1524 enable, slotNum);
1525 break;
1526 case ipmi::SupportedFeatureMuxs::HsbpMuxSlot:
1527 return muxSlotDisable(ctx, mctpHsbpService, "HSBP_Mux", enable,
1528 slotNum);
1529 break;
1530 default:
1531 lg2::warning("ERROR: Invalid Mux slot selected");
1532 return ipmi::responseInvalidFieldRequest();
1533 }
1534}
1535
Arun P. Mohanan06584cd2021-08-13 20:56:01 +05301536/** @brief implements MTM BMC Feature Control IPMI command which can be
1537 * used to enable or disable the supported BMC features.
1538 * @param yield - context object that represents the currently executing
1539 * coroutine
1540 * @param feature - feature enum to enable or disable
1541 * @param enable - enable or disable the feature
1542 * @param featureArg - custom arguments for that feature
1543 * @param reserved - reserved for future use
1544 *
1545 * @returns IPMI completion code
1546 */
Patrick Williams1bcced02024-08-16 15:20:24 -04001547ipmi::RspType<> mtmBMCFeatureControl(
1548 ipmi::Context::ptr ctx, const uint8_t feature, const uint8_t enable,
1549 const uint8_t featureArg, const uint16_t reserved)
Arun P. Mohanan06584cd2021-08-13 20:56:01 +05301550{
1551 if (reserved != 0)
1552 {
1553 return ipmi::responseInvalidFieldRequest();
1554 }
1555
1556 switch (feature)
1557 {
1558 case ipmi::SupportedFeatureControls::mctp:
1559 switch (featureArg)
1560 {
1561 case ipmi::SupportedMCTPBindings::mctpPCIe:
1562 return handleMCTPFeature(ctx, enable, "MCTP_PCIe");
1563 case ipmi::SupportedMCTPBindings::mctpSMBusHSBP:
1564 return handleMCTPFeature(ctx, enable, "MCTP_SMBus_HSBP");
1565 case ipmi::SupportedMCTPBindings::mctpSMBusPCIeSlot:
1566 return handleMCTPFeature(ctx, enable,
1567 "MCTP_SMBus_PCIe_slot");
1568 default:
1569 return ipmi::responseInvalidFieldRequest();
1570 }
1571 break;
Jason M. Bills5cb2c042021-08-17 12:03:39 -07001572 case ipmi::SupportedFeatureControls::pcieScan:
1573 if (featureArg != 0)
1574 {
1575 return ipmi::responseInvalidFieldRequest();
1576 }
1577 startOrStopService(ctx, enable, "xyz.openbmc_project.PCIe.service");
1578 break;
Vasu V97c30902023-05-19 15:56:15 +05301579 case ipmi::SupportedFeatureControls::mctpSlotSupport:
1580 return handleMCTPSlotFeature(ctx, enable, featureArg);
1581 break;
Arun P. Mohanan06584cd2021-08-13 20:56:01 +05301582 default:
1583 return ipmi::responseInvalidFieldRequest();
1584 }
1585 return ipmi::responseSuccess();
1586}
Vernon Mauerya3702c12019-05-22 13:20:59 -07001587} // namespace ipmi
1588
1589void register_mtm_commands() __attribute__((constructor));
1590void register_mtm_commands()
1591{
Jason M. Bills38d2b5a2019-06-03 16:26:11 -07001592 // <Get SM Signal>
Vernon Mauery98bbf692019-09-16 11:14:59 -07001593 ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnGeneral,
1594 ipmi::intel::general::cmdGetSmSignal,
1595 ipmi::Privilege::Admin, ipmi::appMTMGetSignal);
Vernon Mauerya3702c12019-05-22 13:20:59 -07001596
Vernon Mauery98bbf692019-09-16 11:14:59 -07001597 ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnGeneral,
1598 ipmi::intel::general::cmdSetSmSignal,
1599 ipmi::Privilege::Admin, ipmi::appMTMSetSignal);
Vernon Mauerya3702c12019-05-22 13:20:59 -07001600
Vernon Mauery98bbf692019-09-16 11:14:59 -07001601 ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnGeneral,
1602 ipmi::intel::general::cmdMtmKeepAlive,
1603 ipmi::Privilege::Admin, ipmi::mtmKeepAlive);
Richard Marian Thomaiyar666dd012019-08-02 20:55:37 +05301604
Vernon Mauery98bbf692019-09-16 11:14:59 -07001605 ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnGeneral,
1606 ipmi::intel::general::cmdSetManufacturingData,
1607 ipmi::Privilege::Admin, ipmi::setManufacturingData);
Richard Marian Thomaiyar1f0839c2019-08-25 20:10:52 +05301608
Vernon Mauery98bbf692019-09-16 11:14:59 -07001609 ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnGeneral,
1610 ipmi::intel::general::cmdGetManufacturingData,
1611 ipmi::Privilege::Admin, ipmi::getManufacturingData);
Richard Marian Thomaiyar1f0839c2019-08-25 20:10:52 +05301612
Vernon Mauery27d23562021-08-12 10:43:39 -07001613 ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnGeneral,
1614 ipmi::intel::general::cmdSetFITcLayout,
1615 ipmi::Privilege::Admin, ipmi::setFITcLayout);
1616
Arun P. Mohanan06584cd2021-08-13 20:56:01 +05301617 ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnGeneral,
1618 ipmi::intel::general::cmdMTMBMCFeatureControl,
1619 ipmi::Privilege::Admin, ipmi::mtmBMCFeatureControl);
1620
Vernon Mauery98bbf692019-09-16 11:14:59 -07001621 ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnApp,
Matt Simmering80d4d5f2023-02-15 15:18:51 -08001622 ipmi::intel::general::cmdSlotI2CControllerWriteRead,
Vernon Mauery98bbf692019-09-16 11:14:59 -07001623 ipmi::Privilege::Admin,
Matt Simmering80d4d5f2023-02-15 15:18:51 -08001624 ipmi::appSlotI2CControllerWriteRead);
Yong Lif267a672019-08-29 11:16:07 +08001625
Yong Li068b4f22019-09-17 16:32:18 +08001626 ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnPlatform,
1627 ipmi::intel::platform::cmdClearCMOS,
1628 ipmi::Privilege::Admin, ipmi::clearCMOS);
1629
Vernon Mauery98bbf692019-09-16 11:14:59 -07001630 ipmi::registerFilter(ipmi::prioOemBase,
Yong Li85feb132019-08-09 16:06:03 +08001631 [](ipmi::message::Request::ptr request) {
Patrick Williams1bcced02024-08-16 15:20:24 -04001632 return ipmi::mfgFilterMessage(request);
1633 });
Vernon Mauerya3702c12019-05-22 13:20:59 -07001634}