blob: fce6665e7f60da879b4f1acb327881a4ee694451 [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();
Vernon Mauerya3702c12019-05-22 13:20:59 -0700110 std::string ledPath = ledPathPrefix + ledName;
111 ipmi::Value presentState;
112
113 if (false == ledProp->getLock())
114 {
Jayaprakash Mutyalafc5c80e2024-10-18 07:07:46 +0000115 if (mtm.getProperty(ledService, ledPath.c_str(), ledIntf, "State",
116 &presentState) != 0)
Vernon Mauerya3702c12019-05-22 13:20:59 -0700117 {
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();
Vernon Mauerya3702c12019-05-22 13:20:59 -0700164 std::string ledPath = ledPathPrefix + ledName;
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700165 if (mtm.setProperty(ledService, ledPath, ledIntf, "State",
166 ledProp->getPrevState()) != 0)
Vernon Mauerya3702c12019-05-22 13:20:59 -0700167 {
168 return IPMI_CC_UNSPECIFIED_ERROR;
169 }
170 }
171 }
172 return IPMI_CC_OK;
173}
174
175void Manufacturing::initData()
176{
Vernon Mauerya3702c12019-05-22 13:20:59 -0700177 ledPropertyList.push_back(
178 LedProperty(SmSignalSet::smPowerFaultLed, "status_amber"));
179 ledPropertyList.push_back(
180 LedProperty(SmSignalSet::smSystemReadyLed, "status_green"));
181 ledPropertyList.push_back(
182 LedProperty(SmSignalSet::smIdentifyLed, "identify"));
183}
184
185void Manufacturing::revertTimerHandler()
186{
Richard Marian Thomaiyarae13ac62019-12-17 15:45:12 +0530187#ifdef BMC_VALIDATION_UNSECURE_FEATURE
188 if (mtm.getMfgMode() == SpecialMode::valUnsecure)
189 {
190 // Don't revert the behaviour for validation unsecure mode.
191 return;
192 }
193#endif
Vernon Mauerya3702c12019-05-22 13:20:59 -0700194 if (revertFanPWM)
195 {
196 revertFanPWM = false;
197 disablePidControlService(false);
198 }
199
Ayushi Smritid872a4a2019-10-15 10:20:11 +0530200 if (mtmTestBeepFd != -1)
201 {
202 ::close(mtmTestBeepFd);
203 mtmTestBeepFd = -1;
204 }
205
Vernon Mauerya3702c12019-05-22 13:20:59 -0700206 for (const auto& ledProperty : ledPropertyList)
207 {
208 const std::string& ledName = ledProperty.getName();
Jayaprakash Mutyalaf3656142021-01-24 01:04:19 +0000209 if (ledName == "identify" && mtm.getMfgMode() == SpecialMode::mfg)
210 {
211 // Don't revert the behaviour for manufacturing mode
212 continue;
213 }
Vernon Mauerya3702c12019-05-22 13:20:59 -0700214 ledRevert(ledProperty.getSignal());
215 }
216}
217
218Manufacturing::Manufacturing() :
219 revertTimer([&](void) { revertTimerHandler(); })
220{
221 initData();
222}
223
Patrick Williams1bcced02024-08-16 15:20:24 -0400224int8_t Manufacturing::getProperty(
225 const std::string& service, const std::string& path,
226 const std::string& interface, const std::string& propertyName,
227 ipmi::Value* reply)
Vernon Mauerya3702c12019-05-22 13:20:59 -0700228{
229 try
230 {
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700231 *reply = ipmi::getDbusProperty(*getSdBus(), service, path, interface,
232 propertyName);
Vernon Mauerya3702c12019-05-22 13:20:59 -0700233 }
Patrick Williamsf944d2e2022-07-22 19:26:52 -0500234 catch (const sdbusplus::exception_t& e)
Vernon Mauerya3702c12019-05-22 13:20:59 -0700235 {
Alex Schendel4180cfe2022-11-29 10:46:11 -0800236 lg2::info("ERROR: getProperty");
Vernon Mauerya3702c12019-05-22 13:20:59 -0700237 return -1;
238 }
239
240 return 0;
241}
242
Patrick Williams1bcced02024-08-16 15:20:24 -0400243int8_t Manufacturing::setProperty(
244 const std::string& service, const std::string& path,
245 const std::string& interface, const std::string& propertyName,
246 ipmi::Value value)
Vernon Mauerya3702c12019-05-22 13:20:59 -0700247{
248 try
249 {
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700250 ipmi::setDbusProperty(*getSdBus(), service, path, interface,
Vernon Mauerya3702c12019-05-22 13:20:59 -0700251 propertyName, value);
252 }
Patrick Williamsf944d2e2022-07-22 19:26:52 -0500253 catch (const sdbusplus::exception_t& e)
Vernon Mauerya3702c12019-05-22 13:20:59 -0700254 {
Alex Schendel4180cfe2022-11-29 10:46:11 -0800255 lg2::info("ERROR: setProperty");
Vernon Mauerya3702c12019-05-22 13:20:59 -0700256 return -1;
257 }
258
259 return 0;
260}
261
262int8_t Manufacturing::disablePidControlService(const bool disable)
263{
264 try
265 {
266 auto dbus = getSdBus();
267 auto method = dbus->new_method_call(systemDService, systemDObjPath,
268 systemDMgrIntf,
269 disable ? "StopUnit" : "StartUnit");
270 method.append(pidControlService, "replace");
271 auto reply = dbus->call(method);
272 }
Patrick Williamsf944d2e2022-07-22 19:26:52 -0500273 catch (const sdbusplus::exception_t& e)
Vernon Mauerya3702c12019-05-22 13:20:59 -0700274 {
Alex Schendel4180cfe2022-11-29 10:46:11 -0800275 lg2::info("ERROR: phosphor-pid-control service start or stop failed");
Vernon Mauerya3702c12019-05-22 13:20:59 -0700276 return -1;
277 }
278 return 0;
279}
280
Alex Schendel90eb7872022-09-01 11:56:52 -0700281static bool findPwmName(ipmi::Context::ptr& ctx, uint8_t instance,
282 std::string& pwmName)
283{
284 boost::system::error_code ec{};
285 ObjectValueTree obj;
286
287 // GetAll the objects under service FruDevice
288 ec = getManagedObjects(ctx, "xyz.openbmc_project.EntityManager",
289 "/xyz/openbmc_project/inventory", obj);
290 if (ec)
291 {
Alex Schendel4180cfe2022-11-29 10:46:11 -0800292 lg2::error("GetMangagedObjects failed", "ERROR", ec.message().c_str());
Alex Schendel90eb7872022-09-01 11:56:52 -0700293 return false;
294 }
295 for (const auto& [path, objData] : obj)
296 {
297 for (const auto& [intf, propMap] : objData)
298 {
299 // Currently, these are the three different fan types supported.
300 if (intf == "xyz.openbmc_project.Configuration.AspeedFan" ||
301 intf == "xyz.openbmc_project.Configuration.I2CFan" ||
302 intf == "xyz.openbmc_project.Configuration.NuvotonFan")
303 {
Alex Schendel4180cfe2022-11-29 10:46:11 -0800304 std::string fanPath = "/Fan_";
Alex Schendel90eb7872022-09-01 11:56:52 -0700305
Alex Schendel4180cfe2022-11-29 10:46:11 -0800306 fanPath += std::to_string(instance);
307 std::string objPath = path.str;
308 objPath = objPath.substr(objPath.find_last_of("/"));
309 if (objPath != fanPath)
Alex Schendel90eb7872022-09-01 11:56:52 -0700310 {
311 continue;
312 }
313 auto connector = objData.find(intf + std::string(".Connector"));
314 if (connector == objData.end())
315 {
316 return false;
317 }
318 auto findPwmName = connector->second.find("PwmName");
319 if (findPwmName != connector->second.end())
320 {
321 auto fanPwmName =
322 std::get_if<std::string>(&findPwmName->second);
323 if (!fanPwmName)
324 {
Alex Schendel4180cfe2022-11-29 10:46:11 -0800325 lg2::error("PwmName parse ERROR.");
Alex Schendel90eb7872022-09-01 11:56:52 -0700326 return false;
327 }
328 pwmName = *fanPwmName;
329 return true;
330 }
331 auto findPwm = connector->second.find("Pwm");
332 if (findPwm == connector->second.end())
333 {
334 return false;
335 }
336 auto fanPwm = std::get_if<uint64_t>(&findPwm->second);
337 if (!fanPwm)
338 {
339 return false;
340 }
341 pwmName = "Pwm_" + std::to_string(*fanPwm + 1);
342 return true;
343 }
344 }
345 }
346 return false;
347}
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700348ipmi::RspType<uint8_t, // Signal value
349 std::optional<uint16_t> // Fan tach value
350 >
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +0530351 appMTMGetSignal(ipmi::Context::ptr ctx, uint8_t signalTypeByte,
Richard Marian Thomaiyar147daec2019-06-15 07:43:48 +0530352 uint8_t instance, uint8_t actionByte)
Vernon Mauerya3702c12019-05-22 13:20:59 -0700353{
Ayushi Smritie0511e52019-08-27 17:30:34 +0000354 // mfg filter logic is used to allow MTM get signal command only in
355 // manfacturing mode.
Vernon Mauerya3702c12019-05-22 13:20:59 -0700356
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700357 SmSignalGet signalType = static_cast<SmSignalGet>(signalTypeByte);
358 SmActionGet action = static_cast<SmActionGet>(actionByte);
359
360 switch (signalType)
361 {
anil kumar appana98705b32019-09-11 14:15:50 +0000362 case SmSignalGet::smChassisIntrusion:
363 {
364 ipmi::Value reply;
365 if (mtm.getProperty(intrusionService, intrusionPath, intrusionIntf,
366 "Status", &reply) < 0)
367 {
368 return ipmi::responseInvalidFieldRequest();
369 }
370 std::string* intrusionStatus = std::get_if<std::string>(&reply);
371 if (!intrusionStatus)
372 {
373 return ipmi::responseUnspecifiedError();
374 }
375
376 uint8_t status = 0;
377 if (!intrusionStatus->compare("Normal"))
378 {
379 status = static_cast<uint8_t>(IntrusionStatus::normal);
380 }
381 else if (!intrusionStatus->compare("HardwareIntrusion"))
382 {
383 status =
384 static_cast<uint8_t>(IntrusionStatus::hardwareIntrusion);
385 }
386 else if (!intrusionStatus->compare("TamperingDetected"))
387 {
388 status =
389 static_cast<uint8_t>(IntrusionStatus::tamperingDetected);
390 }
391 else
392 {
393 return ipmi::responseUnspecifiedError();
394 }
395 return ipmi::responseSuccess(status, std::nullopt);
396 }
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700397 case SmSignalGet::smFanPwmGet:
398 {
399 ipmi::Value reply;
Alex Schendel90eb7872022-09-01 11:56:52 -0700400 std::string pwmName, fullPath;
Alex Schendel4180cfe2022-11-29 10:46:11 -0800401 if (!findPwmName(ctx, instance + 1, pwmName))
Alex Schendel90eb7872022-09-01 11:56:52 -0700402 {
403 // The default PWM name is Pwm_#
404 pwmName = "Pwm_" + std::to_string(instance + 1);
405 }
406 fullPath = fanPwmPath + pwmName;
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700407 if (mtm.getProperty(fanService, fullPath, fanIntf, "Value",
408 &reply) < 0)
409 {
410 return ipmi::responseInvalidFieldRequest();
411 }
412 double* doubleVal = std::get_if<double>(&reply);
413 if (doubleVal == nullptr)
414 {
415 return ipmi::responseUnspecifiedError();
416 }
417 uint8_t sensorVal = std::round(*doubleVal);
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +0530418 resetMtmTimer(ctx);
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700419 return ipmi::responseSuccess(sensorVal, std::nullopt);
420 }
421 break;
422 case SmSignalGet::smFanTachometerGet:
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700423 {
Richard Marian Thomaiyar147daec2019-06-15 07:43:48 +0530424 boost::system::error_code ec;
425 using objFlatMap = boost::container::flat_map<
426 std::string, boost::container::flat_map<
427 std::string, std::vector<std::string>>>;
428
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +0530429 auto flatMap = ctx->bus->yield_method_call<objFlatMap>(
430 ctx->yield, ec, "xyz.openbmc_project.ObjectMapper",
Richard Marian Thomaiyar147daec2019-06-15 07:43:48 +0530431 "/xyz/openbmc_project/object_mapper",
432 "xyz.openbmc_project.ObjectMapper", "GetSubTree",
433 fanTachBasePath, 0, std::array<const char*, 1>{fanIntf});
434 if (ec)
435 {
Alex Schendel4180cfe2022-11-29 10:46:11 -0800436 lg2::error("Failed to query fan tach sub tree objects");
Richard Marian Thomaiyar147daec2019-06-15 07:43:48 +0530437 return ipmi::responseUnspecifiedError();
438 }
439 if (instance >= flatMap.size())
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700440 {
441 return ipmi::responseInvalidFieldRequest();
442 }
Richard Marian Thomaiyar147daec2019-06-15 07:43:48 +0530443 auto itr = flatMap.nth(instance);
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700444 ipmi::Value reply;
Richard Marian Thomaiyar147daec2019-06-15 07:43:48 +0530445 if (mtm.getProperty(fanService, itr->first, fanIntf, "Value",
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700446 &reply) < 0)
447 {
448 return ipmi::responseInvalidFieldRequest();
449 }
450
451 double* doubleVal = std::get_if<double>(&reply);
452 if (doubleVal == nullptr)
453 {
454 return ipmi::responseUnspecifiedError();
455 }
456 uint8_t sensorVal = FAN_PRESENT | FAN_SENSOR_PRESENT;
457 std::optional<uint16_t> fanTach = std::round(*doubleVal);
458
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +0530459 resetMtmTimer(ctx);
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700460 return ipmi::responseSuccess(sensorVal, fanTach);
461 }
462 break;
Richard Marian Thomaiyar8e5e2b02019-08-01 07:50:55 +0530463 case SmSignalGet::smIdentifyButton:
464 {
465 if (action == SmActionGet::revert || action == SmActionGet::ignore)
466 {
467 // ButtonMasked property is not supported for ID button as it is
468 // unnecessary. Hence if requested for revert / ignore, override
469 // it to sample action to make tools happy.
470 action = SmActionGet::sample;
471 }
Vernon Mauerydcff1502022-09-28 11:12:46 -0700472 [[fallthrough]];
Richard Marian Thomaiyar8e5e2b02019-08-01 07:50:55 +0530473 }
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700474 case SmSignalGet::smResetButton:
475 case SmSignalGet::smPowerButton:
476 case SmSignalGet::smNMIButton:
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700477 {
478 std::string path;
479 if (getGpioPathForSmSignal(signalType, path) < 0)
480 {
481 return ipmi::responseInvalidFieldRequest();
482 }
483
484 switch (action)
485 {
486 case SmActionGet::sample:
Alex Schendel4180cfe2022-11-29 10:46:11 -0800487 lg2::info("case SmActionGet::sample");
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700488 break;
489 case SmActionGet::ignore:
490 {
Alex Schendel4180cfe2022-11-29 10:46:11 -0800491 lg2::info("case SmActionGet::ignore");
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700492 if (mtm.setProperty(buttonService, path, buttonIntf,
493 "ButtonMasked", true) < 0)
494 {
495 return ipmi::responseUnspecifiedError();
496 }
497 }
498 break;
499 case SmActionGet::revert:
500 {
Alex Schendel4180cfe2022-11-29 10:46:11 -0800501 lg2::info("case SmActionGet::revert");
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700502 if (mtm.setProperty(buttonService, path, buttonIntf,
503 "ButtonMasked", false) < 0)
504 {
505 return ipmi::responseUnspecifiedError();
506 }
507 }
508 break;
509
510 default:
511 return ipmi::responseInvalidFieldRequest();
512 break;
513 }
514
515 ipmi::Value reply;
516 if (mtm.getProperty(buttonService, path, buttonIntf,
517 "ButtonPressed", &reply) < 0)
518 {
519 return ipmi::responseUnspecifiedError();
520 }
521 bool* valPtr = std::get_if<bool>(&reply);
522 if (valPtr == nullptr)
523 {
524 return ipmi::responseUnspecifiedError();
525 }
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +0530526 resetMtmTimer(ctx);
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700527 uint8_t sensorVal = *valPtr;
528 return ipmi::responseSuccess(sensorVal, std::nullopt);
529 }
530 break;
Richard Marian Thomaiyar1b74a212019-08-03 13:26:17 +0530531 case SmSignalGet::smNcsiDiag:
532 {
533 constexpr const char* netBasePath = "/sys/class/net/eth";
534 constexpr const char* carrierSuffix = "/carrier";
Patrick Williams1bcced02024-08-16 15:20:24 -0400535 std::ifstream netIfs(
536 netBasePath + std::to_string(instance) + carrierSuffix);
Richard Marian Thomaiyar1b74a212019-08-03 13:26:17 +0530537 if (!netIfs.good())
538 {
539 return ipmi::responseInvalidFieldRequest();
540 }
541 std::string carrier;
542 netIfs >> carrier;
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +0530543 resetMtmTimer(ctx);
Richard Marian Thomaiyar1b74a212019-08-03 13:26:17 +0530544 return ipmi::responseSuccess(
545 static_cast<uint8_t>(std::stoi(carrier)), std::nullopt);
546 }
547 break;
Vernon Mauerya3702c12019-05-22 13:20:59 -0700548 default:
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700549 return ipmi::responseInvalidFieldRequest();
Vernon Mauerya3702c12019-05-22 13:20:59 -0700550 break;
551 }
Vernon Mauerya3702c12019-05-22 13:20:59 -0700552}
553
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +0530554ipmi::RspType<> appMTMSetSignal(ipmi::Context::ptr ctx, uint8_t signalTypeByte,
555 uint8_t instance, uint8_t actionByte,
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000556 std::optional<uint8_t> pwmSpeed)
Vernon Mauerya3702c12019-05-22 13:20:59 -0700557{
Ayushi Smritie0511e52019-08-27 17:30:34 +0000558 // mfg filter logic is used to allow MTM set signal command only in
559 // manfacturing mode.
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000560
561 SmSignalSet signalType = static_cast<SmSignalSet>(signalTypeByte);
562 SmActionSet action = static_cast<SmActionSet>(actionByte);
563 Cc retCode = ccSuccess;
564 int8_t ret = 0;
565
566 switch (signalType)
567 {
568 case SmSignalSet::smPowerFaultLed:
569 case SmSignalSet::smSystemReadyLed:
570 case SmSignalSet::smIdentifyLed:
571 switch (action)
572 {
573 case SmActionSet::forceDeasserted:
Vernon Mauerya3702c12019-05-22 13:20:59 -0700574 {
Alex Schendel4180cfe2022-11-29 10:46:11 -0800575 lg2::info("case SmActionSet::forceDeasserted");
Vernon Mauerya3702c12019-05-22 13:20:59 -0700576
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000577 retCode = ledStoreAndSet(signalType, std::string("Off"));
578 if (retCode != ccSuccess)
Vernon Mauerya3702c12019-05-22 13:20:59 -0700579 {
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000580 return ipmi::response(retCode);
Vernon Mauerya3702c12019-05-22 13:20:59 -0700581 }
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000582 mtm.revertTimer.start(revertTimeOut);
Vernon Mauerya3702c12019-05-22 13:20:59 -0700583 }
584 break;
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000585 case SmActionSet::forceAsserted:
Vernon Mauerya3702c12019-05-22 13:20:59 -0700586 {
Alex Schendel4180cfe2022-11-29 10:46:11 -0800587 lg2::info("case SmActionSet::forceAsserted");
Vernon Mauerya3702c12019-05-22 13:20:59 -0700588
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000589 retCode = ledStoreAndSet(signalType, std::string("On"));
590 if (retCode != ccSuccess)
591 {
592 return ipmi::response(retCode);
593 }
594 mtm.revertTimer.start(revertTimeOut);
595 if (SmSignalSet::smPowerFaultLed == signalType)
596 {
597 // Deassert "system ready"
598 retCode = ledStoreAndSet(SmSignalSet::smSystemReadyLed,
599 std::string("Off"));
600 }
601 else if (SmSignalSet::smSystemReadyLed == signalType)
602 {
603 // Deassert "fault led"
604 retCode = ledStoreAndSet(SmSignalSet::smPowerFaultLed,
605 std::string("Off"));
606 }
607 }
608 break;
609 case SmActionSet::revert:
610 {
Alex Schendel4180cfe2022-11-29 10:46:11 -0800611 lg2::info("case SmActionSet::revert");
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000612 retCode = ledRevert(signalType);
613 }
614 break;
615 default:
616 {
617 return ipmi::responseInvalidFieldRequest();
618 }
619 }
620 break;
621 case SmSignalSet::smFanPowerSpeed:
622 {
623 if ((action == SmActionSet::forceAsserted) && (!pwmSpeed))
624 {
625 return ipmi::responseReqDataLenInvalid();
626 }
627
628 if ((action == SmActionSet::forceAsserted) && (*pwmSpeed > 100))
629 {
630 return ipmi::responseInvalidFieldRequest();
631 }
632
633 uint8_t pwmValue = 0;
634 switch (action)
635 {
636 case SmActionSet::revert:
637 {
638 if (mtm.revertFanPWM)
639 {
640 ret = mtm.disablePidControlService(false);
Vernon Mauerya3702c12019-05-22 13:20:59 -0700641 if (ret < 0)
642 {
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000643 return ipmi::responseUnspecifiedError();
Vernon Mauerya3702c12019-05-22 13:20:59 -0700644 }
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000645 mtm.revertFanPWM = false;
Vernon Mauerya3702c12019-05-22 13:20:59 -0700646 }
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000647 }
648 break;
649 case SmActionSet::forceAsserted:
650 {
651 pwmValue = *pwmSpeed;
652 } // fall-through
653 case SmActionSet::forceDeasserted:
654 {
655 if (!mtm.revertFanPWM)
Vernon Mauerya3702c12019-05-22 13:20:59 -0700656 {
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000657 ret = mtm.disablePidControlService(true);
658 if (ret < 0)
659 {
660 return ipmi::responseUnspecifiedError();
661 }
662 mtm.revertFanPWM = true;
Vernon Mauerya3702c12019-05-22 13:20:59 -0700663 }
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000664 mtm.revertTimer.start(revertTimeOut);
Alex Schendel90eb7872022-09-01 11:56:52 -0700665 std::string pwmName, fanPwmInstancePath;
Alex Schendel4180cfe2022-11-29 10:46:11 -0800666 if (!findPwmName(ctx, instance + 1, pwmName))
Alex Schendel90eb7872022-09-01 11:56:52 -0700667 {
668 pwmName = "Pwm_" + std::to_string(instance + 1);
669 }
670 fanPwmInstancePath = fanPwmPath + pwmName;
Patrick Williams1bcced02024-08-16 15:20:24 -0400671 ret =
672 mtm.setProperty(fanService, fanPwmInstancePath, fanIntf,
673 "Value", static_cast<double>(pwmValue));
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000674 if (ret < 0)
675 {
676 return ipmi::responseUnspecifiedError();
677 }
678 }
679 break;
680 default:
681 {
682 return ipmi::responseInvalidFieldRequest();
Vernon Mauerya3702c12019-05-22 13:20:59 -0700683 }
684 }
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000685 }
686 break;
Ayushi Smritid872a4a2019-10-15 10:20:11 +0530687 case SmSignalSet::smSpeaker:
688 {
Alex Schendel4180cfe2022-11-29 10:46:11 -0800689 lg2::info("Performing Speaker SmActionSet", "ACTION", lg2::dec,
690 static_cast<uint8_t>(action));
Ayushi Smritid872a4a2019-10-15 10:20:11 +0530691 switch (action)
692 {
693 case SmActionSet::forceAsserted:
694 {
695 char beepDevName[] = "/dev/input/event0";
696 if (mtm.mtmTestBeepFd != -1)
697 {
Alex Schendel4180cfe2022-11-29 10:46:11 -0800698 lg2::info("mtm beep device is opened already!");
Ayushi Smritid872a4a2019-10-15 10:20:11 +0530699 // returning success as already beep is in progress
700 return ipmi::response(retCode);
701 }
702
Patrick Williams1bcced02024-08-16 15:20:24 -0400703 if ((mtm.mtmTestBeepFd =
704 ::open(beepDevName, O_RDWR | O_CLOEXEC)) < 0)
Ayushi Smritid872a4a2019-10-15 10:20:11 +0530705 {
Alex Schendel4180cfe2022-11-29 10:46:11 -0800706 lg2::error("Failed to open input device");
Ayushi Smritid872a4a2019-10-15 10:20:11 +0530707 return ipmi::responseUnspecifiedError();
708 }
709
710 struct input_event event;
711 event.type = EV_SND;
712 event.code = SND_TONE;
713 event.value = 2000;
714
715 if (::write(mtm.mtmTestBeepFd, &event,
716 sizeof(struct input_event)) !=
717 sizeof(struct input_event))
718 {
Alex Schendel4180cfe2022-11-29 10:46:11 -0800719 lg2::error("Failed to write a tone sound event");
Ayushi Smritid872a4a2019-10-15 10:20:11 +0530720 ::close(mtm.mtmTestBeepFd);
721 mtm.mtmTestBeepFd = -1;
722 return ipmi::responseUnspecifiedError();
723 }
724 mtm.revertTimer.start(revertTimeOut);
725 }
726 break;
727 case SmActionSet::revert:
728 case SmActionSet::forceDeasserted:
729 {
730 if (mtm.mtmTestBeepFd != -1)
731 {
732 ::close(mtm.mtmTestBeepFd);
733 mtm.mtmTestBeepFd = -1;
734 }
735 }
736 break;
737 default:
738 {
739 return ipmi::responseInvalidFieldRequest();
740 }
741 }
742 }
743 break;
Richard Marian Thomaiyar3594c6d2019-11-19 21:29:04 +0530744 case SmSignalSet::smDiskFaultLed:
745 {
746 boost::system::error_code ec;
747 using objPaths = std::vector<std::string>;
748 std::string driveBasePath =
749 "/xyz/openbmc_project/inventory/item/drive/";
750 static constexpr const char* driveLedIntf =
751 "xyz.openbmc_project.Led.Group";
752 static constexpr const char* hsbpService =
753 "xyz.openbmc_project.HsbpManager";
754
755 auto driveList = ctx->bus->yield_method_call<objPaths>(
756 ctx->yield, ec, "xyz.openbmc_project.ObjectMapper",
757 "/xyz/openbmc_project/object_mapper",
758 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths",
759 driveBasePath, 0, std::array<const char*, 1>{driveLedIntf});
760 if (ec)
761 {
Alex Schendel4180cfe2022-11-29 10:46:11 -0800762 lg2::error("Failed to query HSBP drive sub tree objects");
Richard Marian Thomaiyar3594c6d2019-11-19 21:29:04 +0530763 return ipmi::responseUnspecifiedError();
764 }
Patrick Williams1bcced02024-08-16 15:20:24 -0400765 std::string driveObjPath =
766 driveBasePath + "Drive_" + std::to_string(instance + 1);
Richard Marian Thomaiyar3594c6d2019-11-19 21:29:04 +0530767 if (std::find(driveList.begin(), driveList.end(), driveObjPath) ==
768 driveList.end())
769 {
770 return ipmi::responseInvalidFieldRequest();
771 }
772 bool driveLedState = false;
773 switch (action)
774 {
775 case SmActionSet::forceAsserted:
776 {
777 driveLedState = true;
778 }
779 break;
780 case SmActionSet::revert:
781 {
782 driveLedState = false;
783 }
784 break;
785 case SmActionSet::forceDeasserted:
786 {
787 driveLedState = false;
788 }
789 break;
790 default:
791 {
792 return ipmi::responseInvalidFieldRequest();
793 }
794 }
795 ret = mtm.setProperty(hsbpService, driveObjPath, driveLedIntf,
796 "Asserted", driveLedState);
797 if (ret < 0)
798 {
799 return ipmi::responseUnspecifiedError();
800 }
801 }
802 break;
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000803 default:
804 {
805 return ipmi::responseInvalidFieldRequest();
Vernon Mauerya3702c12019-05-22 13:20:59 -0700806 }
807 }
Richard Marian Thomaiyar4cc10152019-08-02 23:21:45 +0530808 if (retCode == ccSuccess)
809 {
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +0530810 resetMtmTimer(ctx);
Richard Marian Thomaiyar4cc10152019-08-02 23:21:45 +0530811 }
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000812 return ipmi::response(retCode);
Vernon Mauerya3702c12019-05-22 13:20:59 -0700813}
814
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +0530815ipmi::RspType<> mtmKeepAlive(ipmi::Context::ptr ctx, uint8_t reserved,
Richard Marian Thomaiyar666dd012019-08-02 20:55:37 +0530816 const std::array<char, 5>& intentionalSignature)
817{
Ayushi Smritie0511e52019-08-27 17:30:34 +0000818 // mfg filter logic is used to allow MTM keep alive command only in
819 // manfacturing mode
820
Richard Marian Thomaiyar666dd012019-08-02 20:55:37 +0530821 constexpr std::array<char, 5> signatureOk = {'I', 'N', 'T', 'E', 'L'};
822 if (intentionalSignature != signatureOk || reserved != 0)
823 {
824 return ipmi::responseInvalidFieldRequest();
825 }
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +0530826 return ipmi::response(resetMtmTimer(ctx));
Richard Marian Thomaiyar666dd012019-08-02 20:55:37 +0530827}
828
Ayushi Smritie0511e52019-08-27 17:30:34 +0000829static constexpr unsigned int makeCmdKey(unsigned int netFn, unsigned int cmd)
830{
831 return (netFn << 8) | cmd;
832}
833
Yong Li85feb132019-08-09 16:06:03 +0800834ipmi::Cc mfgFilterMessage(ipmi::message::Request::ptr request)
835{
Ayushi Smritie0511e52019-08-27 17:30:34 +0000836 // Restricted commands, must be executed only in Manufacturing mode
837 switch (makeCmdKey(request->ctx->netFn, request->ctx->cmd))
Yong Li85feb132019-08-09 16:06:03 +0800838 {
Matt Simmering80d4d5f2023-02-15 15:18:51 -0800839 // i2c controller write read command needs additional checking
Ayushi Smritie0511e52019-08-27 17:30:34 +0000840 case makeCmdKey(ipmi::netFnApp, ipmi::app::cmdMasterWriteRead):
841 if (request->payload.size() > 4)
842 {
Richard Marian Thomaiyarae13ac62019-12-17 15:45:12 +0530843 // Allow write data count > 1 only in Special mode
844 if (mtm.getMfgMode() == SpecialMode::none)
Ayushi Smritie0511e52019-08-27 17:30:34 +0000845 {
846 return ipmi::ccInsufficientPrivilege;
847 }
848 }
jayaprakash Mutyala2d4a0192019-11-11 22:12:45 +0000849 return ipmi::ccSuccess;
Ayushi Smritie0511e52019-08-27 17:30:34 +0000850 case makeCmdKey(ipmi::netFnOemOne,
851 ipmi::intel::general::cmdGetSmSignal):
852 case makeCmdKey(ipmi::netFnOemOne,
853 ipmi::intel::general::cmdSetSmSignal):
854 case makeCmdKey(ipmi::netFnOemOne,
855 ipmi::intel::general::cmdMtmKeepAlive):
856 case makeCmdKey(ipmi::netFnOemOne,
857 ipmi::intel::general::cmdSetManufacturingData):
858 case makeCmdKey(ipmi::netFnOemOne,
859 ipmi::intel::general::cmdGetManufacturingData):
Vernon Mauery27d23562021-08-12 10:43:39 -0700860 case makeCmdKey(ipmi::intel::netFnGeneral,
861 ipmi::intel::general::cmdSetFITcLayout):
Arun P. Mohanan06584cd2021-08-13 20:56:01 +0530862 case makeCmdKey(ipmi::netFnOemOne,
863 ipmi::intel::general::cmdMTMBMCFeatureControl):
Ayushi Smritie0511e52019-08-27 17:30:34 +0000864 case makeCmdKey(ipmi::netFnStorage, ipmi::storage::cmdWriteFruData):
AppaRao Puli9a13daa2020-07-13 17:53:00 +0530865 case makeCmdKey(ipmi::netFnOemTwo, ipmi::intel::platform::cmdClearCMOS):
Ayushi Smritie0511e52019-08-27 17:30:34 +0000866
Richard Marian Thomaiyarae13ac62019-12-17 15:45:12 +0530867 // Check for Special mode
868 if (mtm.getMfgMode() == SpecialMode::none)
Yong Li85feb132019-08-09 16:06:03 +0800869 {
Ayushi Smritie0511e52019-08-27 17:30:34 +0000870 return ipmi::ccInvalidCommand;
Yong Li85feb132019-08-09 16:06:03 +0800871 }
jayaprakash Mutyala2d4a0192019-11-11 22:12:45 +0000872 return ipmi::ccSuccess;
873 case makeCmdKey(ipmi::netFnStorage, ipmi::storage::cmdDeleteSelEntry):
874 {
875 return ipmi::ccInvalidCommand;
876 }
Yong Li85feb132019-08-09 16:06:03 +0800877 }
Yong Li85feb132019-08-09 16:06:03 +0800878 return ipmi::ccSuccess;
879}
880
Richard Marian Thomaiyar1f0839c2019-08-25 20:10:52 +0530881static constexpr uint8_t maxEthSize = 6;
882static constexpr uint8_t maxSupportedEth = 3;
883static constexpr const char* factoryEthAddrBaseFileName =
884 "/var/sofs/factory-settings/network/mac/eth";
885
Alex Schendel097497f2022-10-07 14:37:15 -0700886bool findFruDevice(ipmi::Context::ptr& ctx, uint64_t& macOffset,
Zhikui Renad129c62022-04-05 20:11:24 -0700887 uint64_t& busNum, uint64_t& address)
888{
Alex Schendel097497f2022-10-07 14:37:15 -0700889 boost::system::error_code ec{};
890 ObjectValueTree obj;
Zhikui Renad129c62022-04-05 20:11:24 -0700891
892 // GetAll the objects under service FruDevice
Alex Schendel097497f2022-10-07 14:37:15 -0700893 ec = getManagedObjects(ctx, "xyz.openbmc_project.EntityManager",
894 "/xyz/openbmc_project/inventory", obj);
Zhikui Renad129c62022-04-05 20:11:24 -0700895 if (ec)
896 {
Alex Schendel4180cfe2022-11-29 10:46:11 -0800897 lg2::error("GetManagedObjects failed", "ERROR", ec.message().c_str());
Zhikui Renad129c62022-04-05 20:11:24 -0700898 return false;
899 }
900
901 for (const auto& [path, fru] : obj)
902 {
903 for (const auto& [intf, propMap] : fru)
904 {
905 if (intf == "xyz.openbmc_project.Inventory.Item.Board.Motherboard")
906 {
907 auto findBus = propMap.find("FruBus");
908 auto findAddress = propMap.find("FruAddress");
909 auto findMacOffset = propMap.find("MacOffset");
910 if (findBus == propMap.end() || findAddress == propMap.end() ||
911 findMacOffset == propMap.end())
912 {
913 continue;
914 }
915
916 auto fruBus = std::get_if<uint64_t>(&findBus->second);
917 auto fruAddress = std::get_if<uint64_t>(&findAddress->second);
918 auto macFruOffset =
919 std::get_if<uint64_t>(&findMacOffset->second);
920 if (!fruBus || !fruAddress || !macFruOffset)
921 {
Alex Schendel4180cfe2022-11-29 10:46:11 -0800922 lg2::info("ERROR: MotherBoard FRU config data type "
923 "invalid, not used");
Zhikui Renad129c62022-04-05 20:11:24 -0700924 return false;
925 }
926 busNum = *fruBus;
927 address = *fruAddress;
928 macOffset = *macFruOffset;
929 return true;
930 }
931 }
932 }
933 return false;
934}
935
Zhikui Ren98fa87e2022-05-04 08:10:29 -0700936static constexpr uint64_t fruEnd = 0xff;
937// write rolls over within current page, need to keep mac within a page
938static constexpr uint64_t fruPageSize = 0x8;
939// MAC record struct: HEADER, MAC DATA, CheckSum
940static constexpr uint64_t macRecordSize = maxEthSize + 2;
941static_assert(fruPageSize >= macRecordSize,
942 "macRecordSize greater than eeprom page size");
943static constexpr uint8_t macHeader = 0x40;
944// Calculate new checksum for fru info area
945static uint8_t calculateChecksum(std::vector<uint8_t>::const_iterator iter,
946 std::vector<uint8_t>::const_iterator end)
947{
948 constexpr int checksumMod = 256;
949 uint8_t sum = std::accumulate(iter, end, static_cast<uint8_t>(0));
950 return (checksumMod - sum) % checksumMod;
951}
952
Zhikui Renad129c62022-04-05 20:11:24 -0700953bool readMacFromFru(ipmi::Context::ptr ctx, uint8_t macIndex,
954 std::array<uint8_t, maxEthSize>& ethData)
955{
Zhikui Renad129c62022-04-05 20:11:24 -0700956 uint64_t macOffset = fruEnd;
957 uint64_t fruBus = 0;
958 uint64_t fruAddress = 0;
959
Alex Schendel097497f2022-10-07 14:37:15 -0700960 if (findFruDevice(ctx, macOffset, fruBus, fruAddress))
Zhikui Renad129c62022-04-05 20:11:24 -0700961 {
Alex Schendel4180cfe2022-11-29 10:46:11 -0800962 lg2::info("Found mac fru", "BUS", lg2::dec,
963 static_cast<uint8_t>(fruBus), "ADDRESS", lg2::dec,
964 static_cast<uint8_t>(fruAddress));
Zhikui Renad129c62022-04-05 20:11:24 -0700965
Zhikui Ren98fa87e2022-05-04 08:10:29 -0700966 if (macOffset % fruPageSize)
967 {
968 macOffset = (macOffset / fruPageSize + 1) * fruPageSize;
969 }
970 macOffset += macIndex * fruPageSize;
971 if ((macOffset + macRecordSize) > fruEnd)
Zhikui Renad129c62022-04-05 20:11:24 -0700972 {
Alex Schendel4180cfe2022-11-29 10:46:11 -0800973 lg2::error("ERROR: read fru mac failed, offset invalid");
Zhikui Renad129c62022-04-05 20:11:24 -0700974 return false;
975 }
976 std::vector<uint8_t> writeData;
Zhikui Ren98fa87e2022-05-04 08:10:29 -0700977 writeData.push_back(static_cast<uint8_t>(macOffset));
978 std::vector<uint8_t> readBuf(macRecordSize);
Zhikui Renad129c62022-04-05 20:11:24 -0700979 std::string i2cBus = "/dev/i2c-" + std::to_string(fruBus);
Patrick Williams1bcced02024-08-16 15:20:24 -0400980 ipmi::Cc retI2C =
981 ipmi::i2cWriteRead(i2cBus, fruAddress, writeData, readBuf);
Zhikui Renad129c62022-04-05 20:11:24 -0700982 if (retI2C == ipmi::ccSuccess)
983 {
Zhikui Ren98fa87e2022-05-04 08:10:29 -0700984 uint8_t cs = calculateChecksum(readBuf.cbegin(), readBuf.cend());
985 if (cs == 0)
986 {
987 std::copy(++readBuf.begin(), --readBuf.end(), ethData.data());
988 return true;
989 }
Zhikui Renad129c62022-04-05 20:11:24 -0700990 }
991 }
992 return false;
993}
994
995ipmi::Cc writeMacToFru(ipmi::Context::ptr ctx, uint8_t macIndex,
996 std::array<uint8_t, maxEthSize>& ethData)
997{
Zhikui Renad129c62022-04-05 20:11:24 -0700998 uint64_t macOffset = fruEnd;
999 uint64_t fruBus = 0;
1000 uint64_t fruAddress = 0;
1001
Alex Schendel097497f2022-10-07 14:37:15 -07001002 if (findFruDevice(ctx, macOffset, fruBus, fruAddress))
Zhikui Renad129c62022-04-05 20:11:24 -07001003 {
Alex Schendel4180cfe2022-11-29 10:46:11 -08001004 lg2::info("Found mac fru", "BUS", lg2::dec,
1005 static_cast<uint8_t>(fruBus), "ADDRESS", lg2::dec,
1006 static_cast<uint8_t>(fruAddress));
Zhikui Renad129c62022-04-05 20:11:24 -07001007
Zhikui Ren98fa87e2022-05-04 08:10:29 -07001008 if (macOffset % fruPageSize)
1009 {
1010 macOffset = (macOffset / fruPageSize + 1) * fruPageSize;
1011 }
1012 macOffset += macIndex * fruPageSize;
1013 if ((macOffset + macRecordSize) > fruEnd)
Zhikui Renad129c62022-04-05 20:11:24 -07001014 {
Alex Schendel4180cfe2022-11-29 10:46:11 -08001015 lg2::error("ERROR: write mac fru failed, offset invalid.");
Zhikui Renad129c62022-04-05 20:11:24 -07001016 return ipmi::ccParmOutOfRange;
1017 }
1018 std::vector<uint8_t> writeData;
Zhikui Ren98fa87e2022-05-04 08:10:29 -07001019 writeData.reserve(macRecordSize + 1); // include start location
1020 writeData.push_back(static_cast<uint8_t>(macOffset));
1021 writeData.push_back(macHeader);
Zhikui Renad129c62022-04-05 20:11:24 -07001022 std::for_each(ethData.cbegin(), ethData.cend(),
1023 [&](uint8_t i) { writeData.push_back(i); });
Patrick Williams1bcced02024-08-16 15:20:24 -04001024 uint8_t macCheckSum =
1025 calculateChecksum(++writeData.cbegin(), writeData.cend());
Zhikui Ren98fa87e2022-05-04 08:10:29 -07001026 writeData.push_back(macCheckSum);
Zhikui Renad129c62022-04-05 20:11:24 -07001027
1028 std::string i2cBus = "/dev/i2c-" + std::to_string(fruBus);
1029 std::vector<uint8_t> readBuf;
Patrick Williams1bcced02024-08-16 15:20:24 -04001030 ipmi::Cc ret =
1031 ipmi::i2cWriteRead(i2cBus, fruAddress, writeData, readBuf);
Zhikui Ren98fa87e2022-05-04 08:10:29 -07001032
Zhikui Ren0408e792022-06-07 21:03:33 -07001033 // prepare for read to detect chip is write protected
1034 writeData.resize(1);
1035 readBuf.resize(maxEthSize + 1); // include macHeader
1036
Zhikui Renad129c62022-04-05 20:11:24 -07001037 switch (ret)
1038 {
1039 case ipmi::ccSuccess:
Zhikui Ren98fa87e2022-05-04 08:10:29 -07001040 // Wait for internal write cycle to complete
1041 // example: ATMEL 24c0x chip has Twr spec as 5ms
1042
1043 // Ideally we want yield wait, but currently following code
1044 // crash with "thread not supported"
1045 // boost::asio::deadline_timer timer(
1046 // boost::asio::get_associated_executor(ctx->yield),
1047 // boost::posix_time::seconds(1));
1048 // timer.async_wait(ctx->yield);
1049 // use usleep as temp WA
1050 usleep(5000);
1051 if (ipmi::i2cWriteRead(i2cBus, fruAddress, writeData,
1052 readBuf) == ipmi::ccSuccess)
Zhikui Renad129c62022-04-05 20:11:24 -07001053 {
1054 if (std::equal(ethData.begin(), ethData.end(),
Zhikui Ren98fa87e2022-05-04 08:10:29 -07001055 ++readBuf.begin())) // skip macHeader
Zhikui Renad129c62022-04-05 20:11:24 -07001056 {
1057 return ipmi::ccSuccess;
1058 }
Alex Schendel4180cfe2022-11-29 10:46:11 -08001059 lg2::info("INFO: write mac fru verify failed, fru may be "
1060 "write protected.");
Zhikui Renad129c62022-04-05 20:11:24 -07001061 }
1062 return ipmi::ccCommandNotAvailable;
Zhikui Ren0408e792022-06-07 21:03:33 -07001063 default:
1064 if (ipmi::i2cWriteRead(i2cBus, fruAddress, writeData,
1065 readBuf) == ipmi::ccSuccess)
1066 {
Alex Schendel4180cfe2022-11-29 10:46:11 -08001067 lg2::info("INFO: write mac fru failed, but successfully "
1068 "read from fru, fru may be write protected.");
Zhikui Ren0408e792022-06-07 21:03:33 -07001069 return ipmi::ccCommandNotAvailable;
1070 }
1071 else // assume failure is due to no eeprom on board
1072 {
Alex Schendel4180cfe2022-11-29 10:46:11 -08001073 lg2::error("ERROR: write mac fru failed, assume no eeprom "
1074 "is available.");
Zhikui Ren0408e792022-06-07 21:03:33 -07001075 }
Zhikui Renad129c62022-04-05 20:11:24 -07001076 break;
1077 }
1078 }
1079 // no FRU eeprom found
1080 return ipmi::ccDestinationUnavailable;
1081}
1082
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +05301083ipmi::RspType<> setManufacturingData(ipmi::Context::ptr ctx, uint8_t dataType,
Richard Marian Thomaiyar1f0839c2019-08-25 20:10:52 +05301084 std::array<uint8_t, maxEthSize> ethData)
1085{
Zhikui Renad129c62022-04-05 20:11:24 -07001086 // mfg filter logic will restrict this command executing only in mfg
1087 // mode.
Richard Marian Thomaiyar1f0839c2019-08-25 20:10:52 +05301088 if (dataType >= maxSupportedEth)
1089 {
1090 return ipmi::responseParmOutOfRange();
1091 }
1092
Zhikui Renad129c62022-04-05 20:11:24 -07001093 ipmi::Cc ret = writeMacToFru(ctx, dataType, ethData);
1094 if (ret != ipmi::ccDestinationUnavailable)
1095 {
1096 resetMtmTimer(ctx);
1097 return response(ret);
1098 }
1099
Richard Marian Thomaiyar1f0839c2019-08-25 20:10:52 +05301100 constexpr uint8_t ethAddrStrSize =
1101 19; // XX:XX:XX:XX:XX:XX + \n + null termination;
1102 std::vector<uint8_t> buff(ethAddrStrSize);
1103 std::snprintf(reinterpret_cast<char*>(buff.data()), ethAddrStrSize,
1104 "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx\n", ethData.at(0),
1105 ethData.at(1), ethData.at(2), ethData.at(3), ethData.at(4),
1106 ethData.at(5));
Patrick Williams1bcced02024-08-16 15:20:24 -04001107 std::ofstream oEthFile(
1108 factoryEthAddrBaseFileName + std::to_string(dataType),
1109 std::ofstream::out);
Richard Marian Thomaiyar1f0839c2019-08-25 20:10:52 +05301110 if (!oEthFile.good())
1111 {
1112 return ipmi::responseUnspecifiedError();
1113 }
1114
1115 oEthFile << reinterpret_cast<char*>(buff.data());
Johnathan Mantey6d83e4e2020-05-08 11:01:01 -07001116 oEthFile.flush();
Richard Marian Thomaiyar1f0839c2019-08-25 20:10:52 +05301117 oEthFile.close();
1118
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +05301119 resetMtmTimer(ctx);
Richard Marian Thomaiyar1f0839c2019-08-25 20:10:52 +05301120 return ipmi::responseSuccess();
1121}
1122
1123ipmi::RspType<uint8_t, std::array<uint8_t, maxEthSize>>
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +05301124 getManufacturingData(ipmi::Context::ptr ctx, uint8_t dataType)
Richard Marian Thomaiyar1f0839c2019-08-25 20:10:52 +05301125{
Zhikui Renad129c62022-04-05 20:11:24 -07001126 // mfg filter logic will restrict this command executing only in mfg
1127 // mode.
Richard Marian Thomaiyar1f0839c2019-08-25 20:10:52 +05301128 if (dataType >= maxSupportedEth)
1129 {
1130 return ipmi::responseParmOutOfRange();
1131 }
1132 std::array<uint8_t, maxEthSize> ethData{0};
1133 constexpr uint8_t invalidData = 0;
1134 constexpr uint8_t validData = 1;
1135
Patrick Williams1bcced02024-08-16 15:20:24 -04001136 std::ifstream iEthFile(
1137 factoryEthAddrBaseFileName + std::to_string(dataType),
1138 std::ifstream::in);
Richard Marian Thomaiyar1f0839c2019-08-25 20:10:52 +05301139 if (!iEthFile.good())
1140 {
Zhikui Renad129c62022-04-05 20:11:24 -07001141 if (readMacFromFru(ctx, dataType, ethData))
1142 {
1143 resetMtmTimer(ctx);
1144 return ipmi::responseSuccess(validData, ethData);
1145 }
Richard Marian Thomaiyar1f0839c2019-08-25 20:10:52 +05301146 return ipmi::responseSuccess(invalidData, ethData);
1147 }
1148 std::string ethStr;
1149 iEthFile >> ethStr;
1150 uint8_t* data = ethData.data();
1151 std::sscanf(ethStr.c_str(), "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx",
1152 data, (data + 1), (data + 2), (data + 3), (data + 4),
1153 (data + 5));
1154
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +05301155 resetMtmTimer(ctx);
Richard Marian Thomaiyar1f0839c2019-08-25 20:10:52 +05301156 return ipmi::responseSuccess(validData, ethData);
1157}
1158
Matt Simmering80d4d5f2023-02-15 15:18:51 -08001159/** @brief implements slot controller write read IPMI command which can be used
Zhikui Renad129c62022-04-05 20:11:24 -07001160 * for low-level I2C/SMBus write, read or write-read access for PCIE slots
V-Sanjana7acb2d22022-11-15 11:13:54 +05301161 * @param reserved - skip 3 bit
1162 * @param muxType - mux type
Yong Lif267a672019-08-29 11:16:07 +08001163 * @param addressType - address type
1164 * @param bbSlotNum - baseboard slot number
1165 * @param riserSlotNum - riser slot number
1166 * @param reserved2 - skip 2 bit
Matt Simmering80d4d5f2023-02-15 15:18:51 -08001167 * @param targetAddr - target address
Yong Lif267a672019-08-29 11:16:07 +08001168 * @param readCount - number of bytes to be read
1169 * @param writeData - data to be written
1170 *
1171 * @returns IPMI completion code plus response data
1172 */
V-Sanjana7acb2d22022-11-15 11:13:54 +05301173
Matt Simmering80d4d5f2023-02-15 15:18:51 -08001174ipmi::RspType<std::vector<uint8_t>> appSlotI2CControllerWriteRead(
V-Sanjana7acb2d22022-11-15 11:13:54 +05301175 uint3_t reserved, uint3_t muxType, uint2_t addressType, uint3_t bbSlotNum,
Matt Simmering80d4d5f2023-02-15 15:18:51 -08001176 uint3_t riserSlotNum, uint2_t reserved2, uint8_t targetAddr,
V-Sanjana7acb2d22022-11-15 11:13:54 +05301177 uint8_t readCount, std::vector<uint8_t> writeData)
Yong Lif267a672019-08-29 11:16:07 +08001178{
Vernon Maueryfc3bc382022-06-02 13:52:04 -07001179 if (reserved || reserved2)
1180 {
1181 return ipmi::responseInvalidFieldRequest();
1182 }
Yong Lif267a672019-08-29 11:16:07 +08001183 const size_t writeCount = writeData.size();
1184 std::string i2cBus;
1185 if (addressType == slotAddressTypeBus)
1186 {
V-Sanjana7acb2d22022-11-15 11:13:54 +05301187 std::string path = "/dev/i2c-mux/";
1188 if (muxType == bbRiserMux)
1189 {
1190 path += "Riser_" + std::to_string(static_cast<uint8_t>(bbSlotNum)) +
1191 "_Mux/Pcie_Slot_" +
1192 std::to_string(static_cast<uint8_t>(riserSlotNum));
1193 }
1194 else if (muxType == leftRiserMux)
1195 {
1196 path += "Left_Riser_Mux/Slot_" +
1197 std::to_string(static_cast<uint8_t>(riserSlotNum));
1198 }
1199 else if (muxType == rightRiserMux)
1200 {
1201 path += "Right_Riser_Mux/Slot_" +
1202 std::to_string(static_cast<uint8_t>(riserSlotNum));
1203 }
1204 else if (muxType == pcieMux)
1205 {
1206 path += "PCIe_Mux/Slot_" +
1207 std::to_string(static_cast<uint8_t>(riserSlotNum));
1208 }
1209 else if (muxType == hsbpMux)
1210 {
1211 path += "HSBP_Mux/Slot" +
1212 std::to_string(static_cast<uint8_t>(riserSlotNum));
1213 }
1214 phosphor::logging::log<phosphor::logging::level::DEBUG>(
1215 ("Path is: " + path).c_str());
Yong Lif267a672019-08-29 11:16:07 +08001216 if (std::filesystem::exists(path) && std::filesystem::is_symlink(path))
1217 {
1218 i2cBus = std::filesystem::read_symlink(path);
1219 }
1220 else
1221 {
Matt Simmering80d4d5f2023-02-15 15:18:51 -08001222 lg2::error("Controller write read command: Cannot get BusID");
Yong Lif267a672019-08-29 11:16:07 +08001223 return ipmi::responseInvalidFieldRequest();
1224 }
1225 }
1226 else if (addressType == slotAddressTypeUniqueid)
1227 {
1228 i2cBus = "/dev/i2c-" +
1229 std::to_string(static_cast<uint8_t>(bbSlotNum) |
1230 (static_cast<uint8_t>(riserSlotNum) << 3));
1231 }
1232 else
1233 {
Matt Simmering80d4d5f2023-02-15 15:18:51 -08001234 lg2::error("Controller write read command: invalid request");
Yong Lif267a672019-08-29 11:16:07 +08001235 return ipmi::responseInvalidFieldRequest();
1236 }
1237
Zhikui Renad129c62022-04-05 20:11:24 -07001238 // Allow single byte write as it is offset byte to read the data, rest
1239 // allow only in Special mode.
Yong Lif267a672019-08-29 11:16:07 +08001240 if (writeCount > 1)
1241 {
Richard Marian Thomaiyarae13ac62019-12-17 15:45:12 +05301242 if (mtm.getMfgMode() == SpecialMode::none)
Yong Lif267a672019-08-29 11:16:07 +08001243 {
1244 return ipmi::responseInsufficientPrivilege();
1245 }
1246 }
1247
1248 if (readCount > slotI2CMaxReadSize)
1249 {
Matt Simmering80d4d5f2023-02-15 15:18:51 -08001250 lg2::error("Controller write read command: Read count exceeds limit");
Yong Lif267a672019-08-29 11:16:07 +08001251 return ipmi::responseParmOutOfRange();
1252 }
1253
1254 if (!readCount && !writeCount)
1255 {
Matt Simmering80d4d5f2023-02-15 15:18:51 -08001256 lg2::error("Controller write read command: Read & write count are 0");
Yong Lif267a672019-08-29 11:16:07 +08001257 return ipmi::responseInvalidFieldRequest();
1258 }
1259
1260 std::vector<uint8_t> readBuf(readCount);
1261
Patrick Williams1bcced02024-08-16 15:20:24 -04001262 ipmi::Cc retI2C =
1263 ipmi::i2cWriteRead(i2cBus, targetAddr, writeData, readBuf);
Yong Lif267a672019-08-29 11:16:07 +08001264 if (retI2C != ipmi::ccSuccess)
1265 {
1266 return ipmi::response(retI2C);
1267 }
1268
1269 return ipmi::responseSuccess(readBuf);
1270}
Yong Li068b4f22019-09-17 16:32:18 +08001271
1272ipmi::RspType<> clearCMOS()
1273{
Matt Simmering80d4d5f2023-02-15 15:18:51 -08001274 // There is an i2c device on bus 4, the target address is 0x38. Based on
Zhikui Renad129c62022-04-05 20:11:24 -07001275 // the spec, writing 0x1 to address 0x61 on this device, will trigger
1276 // the clear CMOS action.
Matt Simmering80d4d5f2023-02-15 15:18:51 -08001277 constexpr uint8_t targetAddr = 0x38;
Yong Li068b4f22019-09-17 16:32:18 +08001278 std::string i2cBus = "/dev/i2c-4";
Yong Lieaeb6cb2020-03-09 20:21:50 +08001279 std::vector<uint8_t> writeData = {0x61, 0x1};
Yong Li068b4f22019-09-17 16:32:18 +08001280 std::vector<uint8_t> readBuf(0);
1281
Patrick Williams1bcced02024-08-16 15:20:24 -04001282 ipmi::Cc retI2C =
1283 ipmi::i2cWriteRead(i2cBus, targetAddr, writeData, readBuf);
Yong Li068b4f22019-09-17 16:32:18 +08001284 return ipmi::response(retI2C);
1285}
Vernon Mauery27d23562021-08-12 10:43:39 -07001286
1287ipmi::RspType<> setFITcLayout(uint32_t layout)
1288{
1289 static constexpr const char* factoryFITcLayout =
1290 "/var/sofs/factory-settings/layout/fitc";
1291 std::filesystem::path fitcDir =
1292 std::filesystem::path(factoryFITcLayout).parent_path();
1293 std::error_code ec;
1294 std::filesystem::create_directories(fitcDir, ec);
1295 if (ec)
1296 {
1297 return ipmi::responseUnspecifiedError();
1298 }
1299 try
1300 {
1301 std::ofstream file(factoryFITcLayout);
1302 file << layout;
1303 file.flush();
1304 file.close();
1305 }
1306 catch (const std::exception& e)
1307 {
1308 return ipmi::responseUnspecifiedError();
1309 }
1310
1311 return ipmi::responseSuccess();
1312}
1313
Arun P. Mohanan06584cd2021-08-13 20:56:01 +05301314static std::vector<std::string>
1315 getMCTPServiceConfigPaths(ipmi::Context::ptr& ctx)
1316{
1317 boost::system::error_code ec;
1318 auto configPaths = ctx->bus->yield_method_call<std::vector<std::string>>(
1319 ctx->yield, ec, "xyz.openbmc_project.ObjectMapper",
1320 "/xyz/openbmc_project/object_mapper",
1321 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths",
1322 "/xyz/openbmc_project/inventory/system/board", 2,
1323 std::array<const char*, 1>{
1324 "xyz.openbmc_project.Configuration.MctpConfiguration"});
1325 if (ec)
1326 {
1327 throw std::runtime_error(
1328 "Failed to query configuration sub tree objects");
1329 }
1330 return configPaths;
1331}
1332
Patrick Williams1bcced02024-08-16 15:20:24 -04001333static ipmi::RspType<> startOrStopService(
1334 ipmi::Context::ptr& ctx, const uint8_t enable,
1335 const std::string& serviceName, bool disableOrEnableUnitFiles = true)
Arun P. Mohanan06584cd2021-08-13 20:56:01 +05301336{
1337 constexpr bool runtimeOnly = false;
1338 constexpr bool force = false;
1339
1340 boost::system::error_code ec;
1341 switch (enable)
1342 {
1343 case ipmi::SupportedFeatureActions::stop:
1344 ctx->bus->yield_method_call(ctx->yield, ec, systemDService,
1345 systemDObjPath, systemDMgrIntf,
1346 "StopUnit", serviceName, "replace");
1347 break;
1348 case ipmi::SupportedFeatureActions::start:
1349 ctx->bus->yield_method_call(ctx->yield, ec, systemDService,
1350 systemDObjPath, systemDMgrIntf,
1351 "StartUnit", serviceName, "replace");
1352 break;
1353 case ipmi::SupportedFeatureActions::disable:
ANJALI RAY1fe485c2021-12-17 16:41:30 +00001354 if (disableOrEnableUnitFiles == true)
1355 {
1356 ctx->bus->yield_method_call(
1357 ctx->yield, ec, systemDService, systemDObjPath,
1358 systemDMgrIntf, "DisableUnitFiles",
1359 std::array<const char*, 1>{serviceName.c_str()},
1360 runtimeOnly);
1361 }
Arun P. Mohanan06584cd2021-08-13 20:56:01 +05301362 ctx->bus->yield_method_call(
1363 ctx->yield, ec, systemDService, systemDObjPath, systemDMgrIntf,
1364 "MaskUnitFiles",
1365 std::array<const char*, 1>{serviceName.c_str()}, runtimeOnly,
1366 force);
Arun P. Mohanan06584cd2021-08-13 20:56:01 +05301367 break;
1368 case ipmi::SupportedFeatureActions::enable:
1369 ctx->bus->yield_method_call(
1370 ctx->yield, ec, systemDService, systemDObjPath, systemDMgrIntf,
1371 "UnmaskUnitFiles",
1372 std::array<const char*, 1>{serviceName.c_str()}, runtimeOnly);
ANJALI RAY1fe485c2021-12-17 16:41:30 +00001373 if (disableOrEnableUnitFiles == true)
1374 {
1375 ctx->bus->yield_method_call(
1376 ctx->yield, ec, systemDService, systemDObjPath,
1377 systemDMgrIntf, "EnableUnitFiles",
1378 std::array<const char*, 1>{serviceName.c_str()},
1379 runtimeOnly, force);
1380 }
Arun P. Mohanan06584cd2021-08-13 20:56:01 +05301381 break;
1382 default:
Alex Schendel4180cfe2022-11-29 10:46:11 -08001383 lg2::warning("ERROR: Invalid feature action selected", "ACTION",
1384 lg2::dec, enable);
Arun P. Mohanan06584cd2021-08-13 20:56:01 +05301385 return ipmi::responseInvalidFieldRequest();
1386 }
1387 if (ec)
1388 {
Alex Schendel4180cfe2022-11-29 10:46:11 -08001389 lg2::warning("ERROR: Service start or stop failed", "SERVICE",
1390 serviceName.c_str());
Arun P. Mohanan06584cd2021-08-13 20:56:01 +05301391 return ipmi::responseUnspecifiedError();
1392 }
1393 return ipmi::responseSuccess();
1394}
1395
1396static std::string getMCTPServiceName(const std::string& objectPath)
1397{
1398 const auto serviceArgument = boost::algorithm::replace_all_copy(
1399 boost::algorithm::replace_first_copy(
1400 objectPath, "/xyz/openbmc_project/inventory/system/board/", ""),
1401 "/", "_2f");
Patrick Williams1bcced02024-08-16 15:20:24 -04001402 std::string unitName =
1403 "xyz.openbmc_project.mctpd@" + serviceArgument + ".service";
Arun P. Mohanan06584cd2021-08-13 20:56:01 +05301404 return unitName;
1405}
1406
Patrick Williams1bcced02024-08-16 15:20:24 -04001407static ipmi::RspType<> handleMCTPFeature(
1408 ipmi::Context::ptr& ctx, const uint8_t enable, const std::string& binding)
Arun P. Mohanan06584cd2021-08-13 20:56:01 +05301409{
1410 std::vector<std::string> configPaths;
1411 try
1412 {
1413 configPaths = getMCTPServiceConfigPaths(ctx);
1414 }
1415 catch (const std::exception& e)
1416 {
Alex Schendel4180cfe2022-11-29 10:46:11 -08001417 lg2::error(e.what());
Arun P. Mohanan06584cd2021-08-13 20:56:01 +05301418 return ipmi::responseUnspecifiedError();
1419 }
1420
1421 for (const auto& objectPath : configPaths)
1422 {
Patrick Williamsb37abfb2023-05-10 07:50:33 -05001423 const auto pos = objectPath.find_last_of('/');
Arun P. Mohanan06584cd2021-08-13 20:56:01 +05301424 if (binding == objectPath.substr(pos + 1))
1425 {
1426 return startOrStopService(ctx, enable,
ANJALI RAY1fe485c2021-12-17 16:41:30 +00001427 getMCTPServiceName(objectPath), false);
Arun P. Mohanan06584cd2021-08-13 20:56:01 +05301428 }
1429 }
1430 return ipmi::responseSuccess();
1431}
1432
Vasu V97c30902023-05-19 15:56:15 +05301433static bool isNum(const std::string& s)
1434{
1435 if (s.empty())
1436 {
1437 return false;
1438 }
1439 uint8_t busNumber;
1440 const auto sEnd = s.data() + s.size();
1441 const auto& [ptr, ec] = std::from_chars(s.data(), sEnd, busNumber);
1442 if (ec == std::errc() || ptr == sEnd)
1443 {
1444 return true;
1445 }
1446 return false;
1447}
1448
1449bool getBusNumFromPath(const std::string& path, std::string& busStr)
1450{
1451 std::vector<std::string> parts;
1452 boost::split(parts, path, boost::is_any_of("-"));
1453 if (parts.size() == 2)
1454 {
1455 busStr = parts[1];
1456 if (isNum(busStr))
1457 {
1458 return true;
1459 }
1460 }
1461 return false;
1462}
1463
1464static ipmi::RspType<> muxSlotDisable(ipmi::Context::ptr& ctx,
1465 std::string service, std::string muxName,
1466 uint8_t action, uint8_t slotNum)
1467{
1468 boost::system::error_code ec;
1469 const std::filesystem::path muxSymlinkDirPath =
1470 "/dev/i2c-mux/" + muxName + "/Slot_" + std::to_string(slotNum + 1);
1471 if (!std::filesystem::is_symlink(muxSymlinkDirPath))
1472 {
1473 return ipmi::responseInvalidFieldRequest();
1474 }
1475 std::string linkPath = std::filesystem::read_symlink(muxSymlinkDirPath);
1476
1477 std::string portNum;
1478 if (!getBusNumFromPath(linkPath, portNum))
1479 {
1480 return ipmi::responseInvalidFieldRequest();
1481 }
1482 auto res = ctx->bus->yield_method_call<int>(
1483 ctx->yield, ec, service, mctpObjPath, mctpBaseIntf, "SkipList",
1484 std::vector<uint8_t>{action, static_cast<uint8_t>(std::stoi(portNum))});
1485 if (ec)
1486 {
1487 lg2::error("Failed to set mctp skiplist");
1488 return ipmi::responseUnspecifiedError();
1489 }
1490
1491 if (!res)
1492 {
1493 return ipmi::responseResponseError();
1494 }
1495 return ipmi::responseSuccess();
1496}
1497
Patrick Williams1bcced02024-08-16 15:20:24 -04001498static ipmi::RspType<> handleMCTPSlotFeature(
1499 ipmi::Context::ptr& ctx, const uint8_t enable, const uint8_t featureArg)
Vasu V97c30902023-05-19 15:56:15 +05301500{
1501 uint8_t slotNum = (featureArg & slotNumMask);
1502 switch ((featureArg & muxTypeMask) >> muxTypeShift)
1503 {
1504 case ipmi::SupportedFeatureMuxs::pcieMuxSlot:
1505 return muxSlotDisable(ctx, mctpPcieSlotService, "PCIe_Mux", enable,
1506 slotNum);
1507 break;
1508 case ipmi::SupportedFeatureMuxs::pcieMcioMuxSlot:
1509 return muxSlotDisable(ctx, mctpPcieSlotService, "PCIe_MCIO_Mux",
1510 enable, slotNum);
1511 break;
1512 case ipmi::SupportedFeatureMuxs::pcieM2EdSffMuxSlot:
1513 return muxSlotDisable(ctx, mctpPcieSlotService, "M2_EDSFF_Mux",
1514 enable, slotNum);
1515 break;
1516 case ipmi::SupportedFeatureMuxs::leftRaiserMuxSlot:
1517 return muxSlotDisable(ctx, mctpPcieSlotService, "Left_Riser_Mux",
1518 enable, slotNum);
1519 break;
1520 case ipmi::SupportedFeatureMuxs::rightRaiserMuxSlot:
1521 return muxSlotDisable(ctx, mctpPcieSlotService, "Right_Riser_Mux",
1522 enable, slotNum);
1523 break;
1524 case ipmi::SupportedFeatureMuxs::HsbpMuxSlot:
1525 return muxSlotDisable(ctx, mctpHsbpService, "HSBP_Mux", enable,
1526 slotNum);
1527 break;
1528 default:
1529 lg2::warning("ERROR: Invalid Mux slot selected");
1530 return ipmi::responseInvalidFieldRequest();
1531 }
1532}
1533
Arun P. Mohanan06584cd2021-08-13 20:56:01 +05301534/** @brief implements MTM BMC Feature Control IPMI command which can be
1535 * used to enable or disable the supported BMC features.
1536 * @param yield - context object that represents the currently executing
1537 * coroutine
1538 * @param feature - feature enum to enable or disable
1539 * @param enable - enable or disable the feature
1540 * @param featureArg - custom arguments for that feature
1541 * @param reserved - reserved for future use
1542 *
1543 * @returns IPMI completion code
1544 */
Patrick Williams1bcced02024-08-16 15:20:24 -04001545ipmi::RspType<> mtmBMCFeatureControl(
1546 ipmi::Context::ptr ctx, const uint8_t feature, const uint8_t enable,
1547 const uint8_t featureArg, const uint16_t reserved)
Arun P. Mohanan06584cd2021-08-13 20:56:01 +05301548{
1549 if (reserved != 0)
1550 {
1551 return ipmi::responseInvalidFieldRequest();
1552 }
1553
1554 switch (feature)
1555 {
1556 case ipmi::SupportedFeatureControls::mctp:
1557 switch (featureArg)
1558 {
1559 case ipmi::SupportedMCTPBindings::mctpPCIe:
1560 return handleMCTPFeature(ctx, enable, "MCTP_PCIe");
1561 case ipmi::SupportedMCTPBindings::mctpSMBusHSBP:
1562 return handleMCTPFeature(ctx, enable, "MCTP_SMBus_HSBP");
1563 case ipmi::SupportedMCTPBindings::mctpSMBusPCIeSlot:
1564 return handleMCTPFeature(ctx, enable,
1565 "MCTP_SMBus_PCIe_slot");
1566 default:
1567 return ipmi::responseInvalidFieldRequest();
1568 }
1569 break;
Jason M. Bills5cb2c042021-08-17 12:03:39 -07001570 case ipmi::SupportedFeatureControls::pcieScan:
1571 if (featureArg != 0)
1572 {
1573 return ipmi::responseInvalidFieldRequest();
1574 }
1575 startOrStopService(ctx, enable, "xyz.openbmc_project.PCIe.service");
1576 break;
Vasu V97c30902023-05-19 15:56:15 +05301577 case ipmi::SupportedFeatureControls::mctpSlotSupport:
1578 return handleMCTPSlotFeature(ctx, enable, featureArg);
1579 break;
Arun P. Mohanan06584cd2021-08-13 20:56:01 +05301580 default:
1581 return ipmi::responseInvalidFieldRequest();
1582 }
1583 return ipmi::responseSuccess();
1584}
Vernon Mauerya3702c12019-05-22 13:20:59 -07001585} // namespace ipmi
1586
1587void register_mtm_commands() __attribute__((constructor));
1588void register_mtm_commands()
1589{
Jason M. Bills38d2b5a2019-06-03 16:26:11 -07001590 // <Get SM Signal>
Vernon Mauery98bbf692019-09-16 11:14:59 -07001591 ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnGeneral,
1592 ipmi::intel::general::cmdGetSmSignal,
1593 ipmi::Privilege::Admin, ipmi::appMTMGetSignal);
Vernon Mauerya3702c12019-05-22 13:20:59 -07001594
Vernon Mauery98bbf692019-09-16 11:14:59 -07001595 ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnGeneral,
1596 ipmi::intel::general::cmdSetSmSignal,
1597 ipmi::Privilege::Admin, ipmi::appMTMSetSignal);
Vernon Mauerya3702c12019-05-22 13:20:59 -07001598
Vernon Mauery98bbf692019-09-16 11:14:59 -07001599 ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnGeneral,
1600 ipmi::intel::general::cmdMtmKeepAlive,
1601 ipmi::Privilege::Admin, ipmi::mtmKeepAlive);
Richard Marian Thomaiyar666dd012019-08-02 20:55:37 +05301602
Vernon Mauery98bbf692019-09-16 11:14:59 -07001603 ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnGeneral,
1604 ipmi::intel::general::cmdSetManufacturingData,
1605 ipmi::Privilege::Admin, ipmi::setManufacturingData);
Richard Marian Thomaiyar1f0839c2019-08-25 20:10:52 +05301606
Vernon Mauery98bbf692019-09-16 11:14:59 -07001607 ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnGeneral,
1608 ipmi::intel::general::cmdGetManufacturingData,
1609 ipmi::Privilege::Admin, ipmi::getManufacturingData);
Richard Marian Thomaiyar1f0839c2019-08-25 20:10:52 +05301610
Vernon Mauery27d23562021-08-12 10:43:39 -07001611 ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnGeneral,
1612 ipmi::intel::general::cmdSetFITcLayout,
1613 ipmi::Privilege::Admin, ipmi::setFITcLayout);
1614
Arun P. Mohanan06584cd2021-08-13 20:56:01 +05301615 ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnGeneral,
1616 ipmi::intel::general::cmdMTMBMCFeatureControl,
1617 ipmi::Privilege::Admin, ipmi::mtmBMCFeatureControl);
1618
Vernon Mauery98bbf692019-09-16 11:14:59 -07001619 ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnApp,
Matt Simmering80d4d5f2023-02-15 15:18:51 -08001620 ipmi::intel::general::cmdSlotI2CControllerWriteRead,
Vernon Mauery98bbf692019-09-16 11:14:59 -07001621 ipmi::Privilege::Admin,
Matt Simmering80d4d5f2023-02-15 15:18:51 -08001622 ipmi::appSlotI2CControllerWriteRead);
Yong Lif267a672019-08-29 11:16:07 +08001623
Yong Li068b4f22019-09-17 16:32:18 +08001624 ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnPlatform,
1625 ipmi::intel::platform::cmdClearCMOS,
1626 ipmi::Privilege::Admin, ipmi::clearCMOS);
1627
Vernon Mauery98bbf692019-09-16 11:14:59 -07001628 ipmi::registerFilter(ipmi::prioOemBase,
Yong Li85feb132019-08-09 16:06:03 +08001629 [](ipmi::message::Request::ptr request) {
Patrick Williams1bcced02024-08-16 15:20:24 -04001630 return ipmi::mfgFilterMessage(request);
1631 });
Vernon Mauerya3702c12019-05-22 13:20:59 -07001632}