blob: 2bcb3489c5b6ddf7785efd1d60fd441e9800bd99 [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
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700226int8_t Manufacturing::getProperty(const std::string& service,
227 const std::string& path,
228 const std::string& interface,
229 const std::string& propertyName,
230 ipmi::Value* reply)
Vernon Mauerya3702c12019-05-22 13:20:59 -0700231{
232 try
233 {
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700234 *reply = ipmi::getDbusProperty(*getSdBus(), service, path, interface,
235 propertyName);
Vernon Mauerya3702c12019-05-22 13:20:59 -0700236 }
Patrick Williamsf944d2e2022-07-22 19:26:52 -0500237 catch (const sdbusplus::exception_t& e)
Vernon Mauerya3702c12019-05-22 13:20:59 -0700238 {
Alex Schendel4180cfe2022-11-29 10:46:11 -0800239 lg2::info("ERROR: getProperty");
Vernon Mauerya3702c12019-05-22 13:20:59 -0700240 return -1;
241 }
242
243 return 0;
244}
245
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700246int8_t Manufacturing::setProperty(const std::string& service,
247 const std::string& path,
248 const std::string& interface,
249 const std::string& propertyName,
250 ipmi::Value value)
Vernon Mauerya3702c12019-05-22 13:20:59 -0700251{
252 try
253 {
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700254 ipmi::setDbusProperty(*getSdBus(), service, path, interface,
Vernon Mauerya3702c12019-05-22 13:20:59 -0700255 propertyName, value);
256 }
Patrick Williamsf944d2e2022-07-22 19:26:52 -0500257 catch (const sdbusplus::exception_t& e)
Vernon Mauerya3702c12019-05-22 13:20:59 -0700258 {
Alex Schendel4180cfe2022-11-29 10:46:11 -0800259 lg2::info("ERROR: setProperty");
Vernon Mauerya3702c12019-05-22 13:20:59 -0700260 return -1;
261 }
262
263 return 0;
264}
265
266int8_t Manufacturing::disablePidControlService(const bool disable)
267{
268 try
269 {
270 auto dbus = getSdBus();
271 auto method = dbus->new_method_call(systemDService, systemDObjPath,
272 systemDMgrIntf,
273 disable ? "StopUnit" : "StartUnit");
274 method.append(pidControlService, "replace");
275 auto reply = dbus->call(method);
276 }
Patrick Williamsf944d2e2022-07-22 19:26:52 -0500277 catch (const sdbusplus::exception_t& e)
Vernon Mauerya3702c12019-05-22 13:20:59 -0700278 {
Alex Schendel4180cfe2022-11-29 10:46:11 -0800279 lg2::info("ERROR: phosphor-pid-control service start or stop failed");
Vernon Mauerya3702c12019-05-22 13:20:59 -0700280 return -1;
281 }
282 return 0;
283}
284
Alex Schendel90eb7872022-09-01 11:56:52 -0700285static bool findPwmName(ipmi::Context::ptr& ctx, uint8_t instance,
286 std::string& pwmName)
287{
288 boost::system::error_code ec{};
289 ObjectValueTree obj;
290
291 // GetAll the objects under service FruDevice
292 ec = getManagedObjects(ctx, "xyz.openbmc_project.EntityManager",
293 "/xyz/openbmc_project/inventory", obj);
294 if (ec)
295 {
Alex Schendel4180cfe2022-11-29 10:46:11 -0800296 lg2::error("GetMangagedObjects failed", "ERROR", ec.message().c_str());
Alex Schendel90eb7872022-09-01 11:56:52 -0700297 return false;
298 }
299 for (const auto& [path, objData] : obj)
300 {
301 for (const auto& [intf, propMap] : objData)
302 {
303 // Currently, these are the three different fan types supported.
304 if (intf == "xyz.openbmc_project.Configuration.AspeedFan" ||
305 intf == "xyz.openbmc_project.Configuration.I2CFan" ||
306 intf == "xyz.openbmc_project.Configuration.NuvotonFan")
307 {
Alex Schendel4180cfe2022-11-29 10:46:11 -0800308 std::string fanPath = "/Fan_";
Alex Schendel90eb7872022-09-01 11:56:52 -0700309
Alex Schendel4180cfe2022-11-29 10:46:11 -0800310 fanPath += std::to_string(instance);
311 std::string objPath = path.str;
312 objPath = objPath.substr(objPath.find_last_of("/"));
313 if (objPath != fanPath)
Alex Schendel90eb7872022-09-01 11:56:52 -0700314 {
315 continue;
316 }
317 auto connector = objData.find(intf + std::string(".Connector"));
318 if (connector == objData.end())
319 {
320 return false;
321 }
322 auto findPwmName = connector->second.find("PwmName");
323 if (findPwmName != connector->second.end())
324 {
325 auto fanPwmName =
326 std::get_if<std::string>(&findPwmName->second);
327 if (!fanPwmName)
328 {
Alex Schendel4180cfe2022-11-29 10:46:11 -0800329 lg2::error("PwmName parse ERROR.");
Alex Schendel90eb7872022-09-01 11:56:52 -0700330 return false;
331 }
332 pwmName = *fanPwmName;
333 return true;
334 }
335 auto findPwm = connector->second.find("Pwm");
336 if (findPwm == connector->second.end())
337 {
338 return false;
339 }
340 auto fanPwm = std::get_if<uint64_t>(&findPwm->second);
341 if (!fanPwm)
342 {
343 return false;
344 }
345 pwmName = "Pwm_" + std::to_string(*fanPwm + 1);
346 return true;
347 }
348 }
349 }
350 return false;
351}
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700352ipmi::RspType<uint8_t, // Signal value
353 std::optional<uint16_t> // Fan tach value
354 >
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +0530355 appMTMGetSignal(ipmi::Context::ptr ctx, uint8_t signalTypeByte,
Richard Marian Thomaiyar147daec2019-06-15 07:43:48 +0530356 uint8_t instance, uint8_t actionByte)
Vernon Mauerya3702c12019-05-22 13:20:59 -0700357{
Ayushi Smritie0511e52019-08-27 17:30:34 +0000358 // mfg filter logic is used to allow MTM get signal command only in
359 // manfacturing mode.
Vernon Mauerya3702c12019-05-22 13:20:59 -0700360
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700361 SmSignalGet signalType = static_cast<SmSignalGet>(signalTypeByte);
362 SmActionGet action = static_cast<SmActionGet>(actionByte);
363
364 switch (signalType)
365 {
anil kumar appana98705b32019-09-11 14:15:50 +0000366 case SmSignalGet::smChassisIntrusion:
367 {
368 ipmi::Value reply;
369 if (mtm.getProperty(intrusionService, intrusionPath, intrusionIntf,
370 "Status", &reply) < 0)
371 {
372 return ipmi::responseInvalidFieldRequest();
373 }
374 std::string* intrusionStatus = std::get_if<std::string>(&reply);
375 if (!intrusionStatus)
376 {
377 return ipmi::responseUnspecifiedError();
378 }
379
380 uint8_t status = 0;
381 if (!intrusionStatus->compare("Normal"))
382 {
383 status = static_cast<uint8_t>(IntrusionStatus::normal);
384 }
385 else if (!intrusionStatus->compare("HardwareIntrusion"))
386 {
387 status =
388 static_cast<uint8_t>(IntrusionStatus::hardwareIntrusion);
389 }
390 else if (!intrusionStatus->compare("TamperingDetected"))
391 {
392 status =
393 static_cast<uint8_t>(IntrusionStatus::tamperingDetected);
394 }
395 else
396 {
397 return ipmi::responseUnspecifiedError();
398 }
399 return ipmi::responseSuccess(status, std::nullopt);
400 }
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700401 case SmSignalGet::smFanPwmGet:
402 {
403 ipmi::Value reply;
Alex Schendel90eb7872022-09-01 11:56:52 -0700404 std::string pwmName, fullPath;
Alex Schendel4180cfe2022-11-29 10:46:11 -0800405 if (!findPwmName(ctx, instance + 1, pwmName))
Alex Schendel90eb7872022-09-01 11:56:52 -0700406 {
407 // The default PWM name is Pwm_#
408 pwmName = "Pwm_" + std::to_string(instance + 1);
409 }
410 fullPath = fanPwmPath + pwmName;
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700411 if (mtm.getProperty(fanService, fullPath, fanIntf, "Value",
412 &reply) < 0)
413 {
414 return ipmi::responseInvalidFieldRequest();
415 }
416 double* doubleVal = std::get_if<double>(&reply);
417 if (doubleVal == nullptr)
418 {
419 return ipmi::responseUnspecifiedError();
420 }
421 uint8_t sensorVal = std::round(*doubleVal);
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +0530422 resetMtmTimer(ctx);
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700423 return ipmi::responseSuccess(sensorVal, std::nullopt);
424 }
425 break;
426 case SmSignalGet::smFanTachometerGet:
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700427 {
Richard Marian Thomaiyar147daec2019-06-15 07:43:48 +0530428 boost::system::error_code ec;
429 using objFlatMap = boost::container::flat_map<
430 std::string, boost::container::flat_map<
431 std::string, std::vector<std::string>>>;
432
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +0530433 auto flatMap = ctx->bus->yield_method_call<objFlatMap>(
434 ctx->yield, ec, "xyz.openbmc_project.ObjectMapper",
Richard Marian Thomaiyar147daec2019-06-15 07:43:48 +0530435 "/xyz/openbmc_project/object_mapper",
436 "xyz.openbmc_project.ObjectMapper", "GetSubTree",
437 fanTachBasePath, 0, std::array<const char*, 1>{fanIntf});
438 if (ec)
439 {
Alex Schendel4180cfe2022-11-29 10:46:11 -0800440 lg2::error("Failed to query fan tach sub tree objects");
Richard Marian Thomaiyar147daec2019-06-15 07:43:48 +0530441 return ipmi::responseUnspecifiedError();
442 }
443 if (instance >= flatMap.size())
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700444 {
445 return ipmi::responseInvalidFieldRequest();
446 }
Richard Marian Thomaiyar147daec2019-06-15 07:43:48 +0530447 auto itr = flatMap.nth(instance);
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700448 ipmi::Value reply;
Richard Marian Thomaiyar147daec2019-06-15 07:43:48 +0530449 if (mtm.getProperty(fanService, itr->first, fanIntf, "Value",
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700450 &reply) < 0)
451 {
452 return ipmi::responseInvalidFieldRequest();
453 }
454
455 double* doubleVal = std::get_if<double>(&reply);
456 if (doubleVal == nullptr)
457 {
458 return ipmi::responseUnspecifiedError();
459 }
460 uint8_t sensorVal = FAN_PRESENT | FAN_SENSOR_PRESENT;
461 std::optional<uint16_t> fanTach = std::round(*doubleVal);
462
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +0530463 resetMtmTimer(ctx);
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700464 return ipmi::responseSuccess(sensorVal, fanTach);
465 }
466 break;
Richard Marian Thomaiyar8e5e2b02019-08-01 07:50:55 +0530467 case SmSignalGet::smIdentifyButton:
468 {
469 if (action == SmActionGet::revert || action == SmActionGet::ignore)
470 {
471 // ButtonMasked property is not supported for ID button as it is
472 // unnecessary. Hence if requested for revert / ignore, override
473 // it to sample action to make tools happy.
474 action = SmActionGet::sample;
475 }
Vernon Mauerydcff1502022-09-28 11:12:46 -0700476 [[fallthrough]];
Richard Marian Thomaiyar8e5e2b02019-08-01 07:50:55 +0530477 }
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700478 case SmSignalGet::smResetButton:
479 case SmSignalGet::smPowerButton:
480 case SmSignalGet::smNMIButton:
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700481 {
482 std::string path;
483 if (getGpioPathForSmSignal(signalType, path) < 0)
484 {
485 return ipmi::responseInvalidFieldRequest();
486 }
487
488 switch (action)
489 {
490 case SmActionGet::sample:
Alex Schendel4180cfe2022-11-29 10:46:11 -0800491 lg2::info("case SmActionGet::sample");
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700492 break;
493 case SmActionGet::ignore:
494 {
Alex Schendel4180cfe2022-11-29 10:46:11 -0800495 lg2::info("case SmActionGet::ignore");
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700496 if (mtm.setProperty(buttonService, path, buttonIntf,
497 "ButtonMasked", true) < 0)
498 {
499 return ipmi::responseUnspecifiedError();
500 }
501 }
502 break;
503 case SmActionGet::revert:
504 {
Alex Schendel4180cfe2022-11-29 10:46:11 -0800505 lg2::info("case SmActionGet::revert");
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700506 if (mtm.setProperty(buttonService, path, buttonIntf,
507 "ButtonMasked", false) < 0)
508 {
509 return ipmi::responseUnspecifiedError();
510 }
511 }
512 break;
513
514 default:
515 return ipmi::responseInvalidFieldRequest();
516 break;
517 }
518
519 ipmi::Value reply;
520 if (mtm.getProperty(buttonService, path, buttonIntf,
521 "ButtonPressed", &reply) < 0)
522 {
523 return ipmi::responseUnspecifiedError();
524 }
525 bool* valPtr = std::get_if<bool>(&reply);
526 if (valPtr == nullptr)
527 {
528 return ipmi::responseUnspecifiedError();
529 }
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +0530530 resetMtmTimer(ctx);
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700531 uint8_t sensorVal = *valPtr;
532 return ipmi::responseSuccess(sensorVal, std::nullopt);
533 }
534 break;
Richard Marian Thomaiyar1b74a212019-08-03 13:26:17 +0530535 case SmSignalGet::smNcsiDiag:
536 {
537 constexpr const char* netBasePath = "/sys/class/net/eth";
538 constexpr const char* carrierSuffix = "/carrier";
539 std::ifstream netIfs(netBasePath + std::to_string(instance) +
540 carrierSuffix);
541 if (!netIfs.good())
542 {
543 return ipmi::responseInvalidFieldRequest();
544 }
545 std::string carrier;
546 netIfs >> carrier;
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +0530547 resetMtmTimer(ctx);
Richard Marian Thomaiyar1b74a212019-08-03 13:26:17 +0530548 return ipmi::responseSuccess(
549 static_cast<uint8_t>(std::stoi(carrier)), std::nullopt);
550 }
551 break;
Vernon Mauerya3702c12019-05-22 13:20:59 -0700552 default:
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700553 return ipmi::responseInvalidFieldRequest();
Vernon Mauerya3702c12019-05-22 13:20:59 -0700554 break;
555 }
Vernon Mauerya3702c12019-05-22 13:20:59 -0700556}
557
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +0530558ipmi::RspType<> appMTMSetSignal(ipmi::Context::ptr ctx, uint8_t signalTypeByte,
559 uint8_t instance, uint8_t actionByte,
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000560 std::optional<uint8_t> pwmSpeed)
Vernon Mauerya3702c12019-05-22 13:20:59 -0700561{
Ayushi Smritie0511e52019-08-27 17:30:34 +0000562 // mfg filter logic is used to allow MTM set signal command only in
563 // manfacturing mode.
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000564
565 SmSignalSet signalType = static_cast<SmSignalSet>(signalTypeByte);
566 SmActionSet action = static_cast<SmActionSet>(actionByte);
567 Cc retCode = ccSuccess;
568 int8_t ret = 0;
569
570 switch (signalType)
571 {
572 case SmSignalSet::smPowerFaultLed:
573 case SmSignalSet::smSystemReadyLed:
574 case SmSignalSet::smIdentifyLed:
575 switch (action)
576 {
577 case SmActionSet::forceDeasserted:
Vernon Mauerya3702c12019-05-22 13:20:59 -0700578 {
Alex Schendel4180cfe2022-11-29 10:46:11 -0800579 lg2::info("case SmActionSet::forceDeasserted");
Vernon Mauerya3702c12019-05-22 13:20:59 -0700580
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000581 retCode = ledStoreAndSet(signalType, std::string("Off"));
582 if (retCode != ccSuccess)
Vernon Mauerya3702c12019-05-22 13:20:59 -0700583 {
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000584 return ipmi::response(retCode);
Vernon Mauerya3702c12019-05-22 13:20:59 -0700585 }
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000586 mtm.revertTimer.start(revertTimeOut);
Vernon Mauerya3702c12019-05-22 13:20:59 -0700587 }
588 break;
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000589 case SmActionSet::forceAsserted:
Vernon Mauerya3702c12019-05-22 13:20:59 -0700590 {
Alex Schendel4180cfe2022-11-29 10:46:11 -0800591 lg2::info("case SmActionSet::forceAsserted");
Vernon Mauerya3702c12019-05-22 13:20:59 -0700592
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000593 retCode = ledStoreAndSet(signalType, std::string("On"));
594 if (retCode != ccSuccess)
595 {
596 return ipmi::response(retCode);
597 }
598 mtm.revertTimer.start(revertTimeOut);
599 if (SmSignalSet::smPowerFaultLed == signalType)
600 {
601 // Deassert "system ready"
602 retCode = ledStoreAndSet(SmSignalSet::smSystemReadyLed,
603 std::string("Off"));
604 }
605 else if (SmSignalSet::smSystemReadyLed == signalType)
606 {
607 // Deassert "fault led"
608 retCode = ledStoreAndSet(SmSignalSet::smPowerFaultLed,
609 std::string("Off"));
610 }
611 }
612 break;
613 case SmActionSet::revert:
614 {
Alex Schendel4180cfe2022-11-29 10:46:11 -0800615 lg2::info("case SmActionSet::revert");
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000616 retCode = ledRevert(signalType);
617 }
618 break;
619 default:
620 {
621 return ipmi::responseInvalidFieldRequest();
622 }
623 }
624 break;
625 case SmSignalSet::smFanPowerSpeed:
626 {
627 if ((action == SmActionSet::forceAsserted) && (!pwmSpeed))
628 {
629 return ipmi::responseReqDataLenInvalid();
630 }
631
632 if ((action == SmActionSet::forceAsserted) && (*pwmSpeed > 100))
633 {
634 return ipmi::responseInvalidFieldRequest();
635 }
636
637 uint8_t pwmValue = 0;
638 switch (action)
639 {
640 case SmActionSet::revert:
641 {
642 if (mtm.revertFanPWM)
643 {
644 ret = mtm.disablePidControlService(false);
Vernon Mauerya3702c12019-05-22 13:20:59 -0700645 if (ret < 0)
646 {
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000647 return ipmi::responseUnspecifiedError();
Vernon Mauerya3702c12019-05-22 13:20:59 -0700648 }
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000649 mtm.revertFanPWM = false;
Vernon Mauerya3702c12019-05-22 13:20:59 -0700650 }
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000651 }
652 break;
653 case SmActionSet::forceAsserted:
654 {
655 pwmValue = *pwmSpeed;
656 } // fall-through
657 case SmActionSet::forceDeasserted:
658 {
659 if (!mtm.revertFanPWM)
Vernon Mauerya3702c12019-05-22 13:20:59 -0700660 {
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000661 ret = mtm.disablePidControlService(true);
662 if (ret < 0)
663 {
664 return ipmi::responseUnspecifiedError();
665 }
666 mtm.revertFanPWM = true;
Vernon Mauerya3702c12019-05-22 13:20:59 -0700667 }
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000668 mtm.revertTimer.start(revertTimeOut);
Alex Schendel90eb7872022-09-01 11:56:52 -0700669 std::string pwmName, fanPwmInstancePath;
Alex Schendel4180cfe2022-11-29 10:46:11 -0800670 if (!findPwmName(ctx, instance + 1, pwmName))
Alex Schendel90eb7872022-09-01 11:56:52 -0700671 {
672 pwmName = "Pwm_" + std::to_string(instance + 1);
673 }
674 fanPwmInstancePath = fanPwmPath + pwmName;
Patrick Williamsb37abfb2023-05-10 07:50:33 -0500675 ret = mtm.setProperty(fanService, fanPwmInstancePath,
676 fanIntf, "Value",
677 static_cast<double>(pwmValue));
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000678 if (ret < 0)
679 {
680 return ipmi::responseUnspecifiedError();
681 }
682 }
683 break;
684 default:
685 {
686 return ipmi::responseInvalidFieldRequest();
Vernon Mauerya3702c12019-05-22 13:20:59 -0700687 }
688 }
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000689 }
690 break;
Ayushi Smritid872a4a2019-10-15 10:20:11 +0530691 case SmSignalSet::smSpeaker:
692 {
Alex Schendel4180cfe2022-11-29 10:46:11 -0800693 lg2::info("Performing Speaker SmActionSet", "ACTION", lg2::dec,
694 static_cast<uint8_t>(action));
Ayushi Smritid872a4a2019-10-15 10:20:11 +0530695 switch (action)
696 {
697 case SmActionSet::forceAsserted:
698 {
699 char beepDevName[] = "/dev/input/event0";
700 if (mtm.mtmTestBeepFd != -1)
701 {
Alex Schendel4180cfe2022-11-29 10:46:11 -0800702 lg2::info("mtm beep device is opened already!");
Ayushi Smritid872a4a2019-10-15 10:20:11 +0530703 // returning success as already beep is in progress
704 return ipmi::response(retCode);
705 }
706
Patrick Williamsb37abfb2023-05-10 07:50:33 -0500707 if ((mtm.mtmTestBeepFd = ::open(beepDevName,
708 O_RDWR | O_CLOEXEC)) < 0)
Ayushi Smritid872a4a2019-10-15 10:20:11 +0530709 {
Alex Schendel4180cfe2022-11-29 10:46:11 -0800710 lg2::error("Failed to open input device");
Ayushi Smritid872a4a2019-10-15 10:20:11 +0530711 return ipmi::responseUnspecifiedError();
712 }
713
714 struct input_event event;
715 event.type = EV_SND;
716 event.code = SND_TONE;
717 event.value = 2000;
718
719 if (::write(mtm.mtmTestBeepFd, &event,
720 sizeof(struct input_event)) !=
721 sizeof(struct input_event))
722 {
Alex Schendel4180cfe2022-11-29 10:46:11 -0800723 lg2::error("Failed to write a tone sound event");
Ayushi Smritid872a4a2019-10-15 10:20:11 +0530724 ::close(mtm.mtmTestBeepFd);
725 mtm.mtmTestBeepFd = -1;
726 return ipmi::responseUnspecifiedError();
727 }
728 mtm.revertTimer.start(revertTimeOut);
729 }
730 break;
731 case SmActionSet::revert:
732 case SmActionSet::forceDeasserted:
733 {
734 if (mtm.mtmTestBeepFd != -1)
735 {
736 ::close(mtm.mtmTestBeepFd);
737 mtm.mtmTestBeepFd = -1;
738 }
739 }
740 break;
741 default:
742 {
743 return ipmi::responseInvalidFieldRequest();
744 }
745 }
746 }
747 break;
Richard Marian Thomaiyar3594c6d2019-11-19 21:29:04 +0530748 case SmSignalSet::smDiskFaultLed:
749 {
750 boost::system::error_code ec;
751 using objPaths = std::vector<std::string>;
752 std::string driveBasePath =
753 "/xyz/openbmc_project/inventory/item/drive/";
754 static constexpr const char* driveLedIntf =
755 "xyz.openbmc_project.Led.Group";
756 static constexpr const char* hsbpService =
757 "xyz.openbmc_project.HsbpManager";
758
759 auto driveList = ctx->bus->yield_method_call<objPaths>(
760 ctx->yield, ec, "xyz.openbmc_project.ObjectMapper",
761 "/xyz/openbmc_project/object_mapper",
762 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths",
763 driveBasePath, 0, std::array<const char*, 1>{driveLedIntf});
764 if (ec)
765 {
Alex Schendel4180cfe2022-11-29 10:46:11 -0800766 lg2::error("Failed to query HSBP drive sub tree objects");
Richard Marian Thomaiyar3594c6d2019-11-19 21:29:04 +0530767 return ipmi::responseUnspecifiedError();
768 }
Patrick Williamsb37abfb2023-05-10 07:50:33 -0500769 std::string driveObjPath = driveBasePath + "Drive_" +
770 std::to_string(instance + 1);
Richard Marian Thomaiyar3594c6d2019-11-19 21:29:04 +0530771 if (std::find(driveList.begin(), driveList.end(), driveObjPath) ==
772 driveList.end())
773 {
774 return ipmi::responseInvalidFieldRequest();
775 }
776 bool driveLedState = false;
777 switch (action)
778 {
779 case SmActionSet::forceAsserted:
780 {
781 driveLedState = true;
782 }
783 break;
784 case SmActionSet::revert:
785 {
786 driveLedState = false;
787 }
788 break;
789 case SmActionSet::forceDeasserted:
790 {
791 driveLedState = false;
792 }
793 break;
794 default:
795 {
796 return ipmi::responseInvalidFieldRequest();
797 }
798 }
799 ret = mtm.setProperty(hsbpService, driveObjPath, driveLedIntf,
800 "Asserted", driveLedState);
801 if (ret < 0)
802 {
803 return ipmi::responseUnspecifiedError();
804 }
805 }
806 break;
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000807 default:
808 {
809 return ipmi::responseInvalidFieldRequest();
Vernon Mauerya3702c12019-05-22 13:20:59 -0700810 }
811 }
Richard Marian Thomaiyar4cc10152019-08-02 23:21:45 +0530812 if (retCode == ccSuccess)
813 {
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +0530814 resetMtmTimer(ctx);
Richard Marian Thomaiyar4cc10152019-08-02 23:21:45 +0530815 }
Ayushi Smriti5e3bf552019-07-30 15:32:06 +0000816 return ipmi::response(retCode);
Vernon Mauerya3702c12019-05-22 13:20:59 -0700817}
818
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +0530819ipmi::RspType<> mtmKeepAlive(ipmi::Context::ptr ctx, uint8_t reserved,
Richard Marian Thomaiyar666dd012019-08-02 20:55:37 +0530820 const std::array<char, 5>& intentionalSignature)
821{
Ayushi Smritie0511e52019-08-27 17:30:34 +0000822 // mfg filter logic is used to allow MTM keep alive command only in
823 // manfacturing mode
824
Richard Marian Thomaiyar666dd012019-08-02 20:55:37 +0530825 constexpr std::array<char, 5> signatureOk = {'I', 'N', 'T', 'E', 'L'};
826 if (intentionalSignature != signatureOk || reserved != 0)
827 {
828 return ipmi::responseInvalidFieldRequest();
829 }
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +0530830 return ipmi::response(resetMtmTimer(ctx));
Richard Marian Thomaiyar666dd012019-08-02 20:55:37 +0530831}
832
Ayushi Smritie0511e52019-08-27 17:30:34 +0000833static constexpr unsigned int makeCmdKey(unsigned int netFn, unsigned int cmd)
834{
835 return (netFn << 8) | cmd;
836}
837
Yong Li85feb132019-08-09 16:06:03 +0800838ipmi::Cc mfgFilterMessage(ipmi::message::Request::ptr request)
839{
Ayushi Smritie0511e52019-08-27 17:30:34 +0000840 // Restricted commands, must be executed only in Manufacturing mode
841 switch (makeCmdKey(request->ctx->netFn, request->ctx->cmd))
Yong Li85feb132019-08-09 16:06:03 +0800842 {
Matt Simmering80d4d5f2023-02-15 15:18:51 -0800843 // i2c controller write read command needs additional checking
Ayushi Smritie0511e52019-08-27 17:30:34 +0000844 case makeCmdKey(ipmi::netFnApp, ipmi::app::cmdMasterWriteRead):
845 if (request->payload.size() > 4)
846 {
Richard Marian Thomaiyarae13ac62019-12-17 15:45:12 +0530847 // Allow write data count > 1 only in Special mode
848 if (mtm.getMfgMode() == SpecialMode::none)
Ayushi Smritie0511e52019-08-27 17:30:34 +0000849 {
850 return ipmi::ccInsufficientPrivilege;
851 }
852 }
jayaprakash Mutyala2d4a0192019-11-11 22:12:45 +0000853 return ipmi::ccSuccess;
Ayushi Smritie0511e52019-08-27 17:30:34 +0000854 case makeCmdKey(ipmi::netFnOemOne,
855 ipmi::intel::general::cmdGetSmSignal):
856 case makeCmdKey(ipmi::netFnOemOne,
857 ipmi::intel::general::cmdSetSmSignal):
858 case makeCmdKey(ipmi::netFnOemOne,
859 ipmi::intel::general::cmdMtmKeepAlive):
860 case makeCmdKey(ipmi::netFnOemOne,
861 ipmi::intel::general::cmdSetManufacturingData):
862 case makeCmdKey(ipmi::netFnOemOne,
863 ipmi::intel::general::cmdGetManufacturingData):
Vernon Mauery27d23562021-08-12 10:43:39 -0700864 case makeCmdKey(ipmi::intel::netFnGeneral,
865 ipmi::intel::general::cmdSetFITcLayout):
Arun P. Mohanan06584cd2021-08-13 20:56:01 +0530866 case makeCmdKey(ipmi::netFnOemOne,
867 ipmi::intel::general::cmdMTMBMCFeatureControl):
Ayushi Smritie0511e52019-08-27 17:30:34 +0000868 case makeCmdKey(ipmi::netFnStorage, ipmi::storage::cmdWriteFruData):
AppaRao Puli9a13daa2020-07-13 17:53:00 +0530869 case makeCmdKey(ipmi::netFnOemTwo, ipmi::intel::platform::cmdClearCMOS):
Ayushi Smritie0511e52019-08-27 17:30:34 +0000870
Richard Marian Thomaiyarae13ac62019-12-17 15:45:12 +0530871 // Check for Special mode
872 if (mtm.getMfgMode() == SpecialMode::none)
Yong Li85feb132019-08-09 16:06:03 +0800873 {
Ayushi Smritie0511e52019-08-27 17:30:34 +0000874 return ipmi::ccInvalidCommand;
Yong Li85feb132019-08-09 16:06:03 +0800875 }
jayaprakash Mutyala2d4a0192019-11-11 22:12:45 +0000876 return ipmi::ccSuccess;
877 case makeCmdKey(ipmi::netFnStorage, ipmi::storage::cmdDeleteSelEntry):
878 {
879 return ipmi::ccInvalidCommand;
880 }
Yong Li85feb132019-08-09 16:06:03 +0800881 }
Yong Li85feb132019-08-09 16:06:03 +0800882 return ipmi::ccSuccess;
883}
884
Richard Marian Thomaiyar1f0839c2019-08-25 20:10:52 +0530885static constexpr uint8_t maxEthSize = 6;
886static constexpr uint8_t maxSupportedEth = 3;
887static constexpr const char* factoryEthAddrBaseFileName =
888 "/var/sofs/factory-settings/network/mac/eth";
889
Alex Schendel097497f2022-10-07 14:37:15 -0700890bool findFruDevice(ipmi::Context::ptr& ctx, uint64_t& macOffset,
Zhikui Renad129c62022-04-05 20:11:24 -0700891 uint64_t& busNum, uint64_t& address)
892{
Alex Schendel097497f2022-10-07 14:37:15 -0700893 boost::system::error_code ec{};
894 ObjectValueTree obj;
Zhikui Renad129c62022-04-05 20:11:24 -0700895
896 // GetAll the objects under service FruDevice
Alex Schendel097497f2022-10-07 14:37:15 -0700897 ec = getManagedObjects(ctx, "xyz.openbmc_project.EntityManager",
898 "/xyz/openbmc_project/inventory", obj);
Zhikui Renad129c62022-04-05 20:11:24 -0700899 if (ec)
900 {
Alex Schendel4180cfe2022-11-29 10:46:11 -0800901 lg2::error("GetManagedObjects failed", "ERROR", ec.message().c_str());
Zhikui Renad129c62022-04-05 20:11:24 -0700902 return false;
903 }
904
905 for (const auto& [path, fru] : obj)
906 {
907 for (const auto& [intf, propMap] : fru)
908 {
909 if (intf == "xyz.openbmc_project.Inventory.Item.Board.Motherboard")
910 {
911 auto findBus = propMap.find("FruBus");
912 auto findAddress = propMap.find("FruAddress");
913 auto findMacOffset = propMap.find("MacOffset");
914 if (findBus == propMap.end() || findAddress == propMap.end() ||
915 findMacOffset == propMap.end())
916 {
917 continue;
918 }
919
920 auto fruBus = std::get_if<uint64_t>(&findBus->second);
921 auto fruAddress = std::get_if<uint64_t>(&findAddress->second);
922 auto macFruOffset =
923 std::get_if<uint64_t>(&findMacOffset->second);
924 if (!fruBus || !fruAddress || !macFruOffset)
925 {
Alex Schendel4180cfe2022-11-29 10:46:11 -0800926 lg2::info("ERROR: MotherBoard FRU config data type "
927 "invalid, not used");
Zhikui Renad129c62022-04-05 20:11:24 -0700928 return false;
929 }
930 busNum = *fruBus;
931 address = *fruAddress;
932 macOffset = *macFruOffset;
933 return true;
934 }
935 }
936 }
937 return false;
938}
939
Zhikui Ren98fa87e2022-05-04 08:10:29 -0700940static constexpr uint64_t fruEnd = 0xff;
941// write rolls over within current page, need to keep mac within a page
942static constexpr uint64_t fruPageSize = 0x8;
943// MAC record struct: HEADER, MAC DATA, CheckSum
944static constexpr uint64_t macRecordSize = maxEthSize + 2;
945static_assert(fruPageSize >= macRecordSize,
946 "macRecordSize greater than eeprom page size");
947static constexpr uint8_t macHeader = 0x40;
948// Calculate new checksum for fru info area
949static uint8_t calculateChecksum(std::vector<uint8_t>::const_iterator iter,
950 std::vector<uint8_t>::const_iterator end)
951{
952 constexpr int checksumMod = 256;
953 uint8_t sum = std::accumulate(iter, end, static_cast<uint8_t>(0));
954 return (checksumMod - sum) % checksumMod;
955}
956
Zhikui Renad129c62022-04-05 20:11:24 -0700957bool readMacFromFru(ipmi::Context::ptr ctx, uint8_t macIndex,
958 std::array<uint8_t, maxEthSize>& ethData)
959{
Zhikui Renad129c62022-04-05 20:11:24 -0700960 uint64_t macOffset = fruEnd;
961 uint64_t fruBus = 0;
962 uint64_t fruAddress = 0;
963
Alex Schendel097497f2022-10-07 14:37:15 -0700964 if (findFruDevice(ctx, macOffset, fruBus, fruAddress))
Zhikui Renad129c62022-04-05 20:11:24 -0700965 {
Alex Schendel4180cfe2022-11-29 10:46:11 -0800966 lg2::info("Found mac fru", "BUS", lg2::dec,
967 static_cast<uint8_t>(fruBus), "ADDRESS", lg2::dec,
968 static_cast<uint8_t>(fruAddress));
Zhikui Renad129c62022-04-05 20:11:24 -0700969
Zhikui Ren98fa87e2022-05-04 08:10:29 -0700970 if (macOffset % fruPageSize)
971 {
972 macOffset = (macOffset / fruPageSize + 1) * fruPageSize;
973 }
974 macOffset += macIndex * fruPageSize;
975 if ((macOffset + macRecordSize) > fruEnd)
Zhikui Renad129c62022-04-05 20:11:24 -0700976 {
Alex Schendel4180cfe2022-11-29 10:46:11 -0800977 lg2::error("ERROR: read fru mac failed, offset invalid");
Zhikui Renad129c62022-04-05 20:11:24 -0700978 return false;
979 }
980 std::vector<uint8_t> writeData;
Zhikui Ren98fa87e2022-05-04 08:10:29 -0700981 writeData.push_back(static_cast<uint8_t>(macOffset));
982 std::vector<uint8_t> readBuf(macRecordSize);
Zhikui Renad129c62022-04-05 20:11:24 -0700983 std::string i2cBus = "/dev/i2c-" + std::to_string(fruBus);
Patrick Williamsb37abfb2023-05-10 07:50:33 -0500984 ipmi::Cc retI2C = ipmi::i2cWriteRead(i2cBus, fruAddress, writeData,
985 readBuf);
Zhikui Renad129c62022-04-05 20:11:24 -0700986 if (retI2C == ipmi::ccSuccess)
987 {
Zhikui Ren98fa87e2022-05-04 08:10:29 -0700988 uint8_t cs = calculateChecksum(readBuf.cbegin(), readBuf.cend());
989 if (cs == 0)
990 {
991 std::copy(++readBuf.begin(), --readBuf.end(), ethData.data());
992 return true;
993 }
Zhikui Renad129c62022-04-05 20:11:24 -0700994 }
995 }
996 return false;
997}
998
999ipmi::Cc writeMacToFru(ipmi::Context::ptr ctx, uint8_t macIndex,
1000 std::array<uint8_t, maxEthSize>& ethData)
1001{
Zhikui Renad129c62022-04-05 20:11:24 -07001002 uint64_t macOffset = fruEnd;
1003 uint64_t fruBus = 0;
1004 uint64_t fruAddress = 0;
1005
Alex Schendel097497f2022-10-07 14:37:15 -07001006 if (findFruDevice(ctx, macOffset, fruBus, fruAddress))
Zhikui Renad129c62022-04-05 20:11:24 -07001007 {
Alex Schendel4180cfe2022-11-29 10:46:11 -08001008 lg2::info("Found mac fru", "BUS", lg2::dec,
1009 static_cast<uint8_t>(fruBus), "ADDRESS", lg2::dec,
1010 static_cast<uint8_t>(fruAddress));
Zhikui Renad129c62022-04-05 20:11:24 -07001011
Zhikui Ren98fa87e2022-05-04 08:10:29 -07001012 if (macOffset % fruPageSize)
1013 {
1014 macOffset = (macOffset / fruPageSize + 1) * fruPageSize;
1015 }
1016 macOffset += macIndex * fruPageSize;
1017 if ((macOffset + macRecordSize) > fruEnd)
Zhikui Renad129c62022-04-05 20:11:24 -07001018 {
Alex Schendel4180cfe2022-11-29 10:46:11 -08001019 lg2::error("ERROR: write mac fru failed, offset invalid.");
Zhikui Renad129c62022-04-05 20:11:24 -07001020 return ipmi::ccParmOutOfRange;
1021 }
1022 std::vector<uint8_t> writeData;
Zhikui Ren98fa87e2022-05-04 08:10:29 -07001023 writeData.reserve(macRecordSize + 1); // include start location
1024 writeData.push_back(static_cast<uint8_t>(macOffset));
1025 writeData.push_back(macHeader);
Zhikui Renad129c62022-04-05 20:11:24 -07001026 std::for_each(ethData.cbegin(), ethData.cend(),
1027 [&](uint8_t i) { writeData.push_back(i); });
Patrick Williamsb37abfb2023-05-10 07:50:33 -05001028 uint8_t macCheckSum = calculateChecksum(++writeData.cbegin(),
1029 writeData.cend());
Zhikui Ren98fa87e2022-05-04 08:10:29 -07001030 writeData.push_back(macCheckSum);
Zhikui Renad129c62022-04-05 20:11:24 -07001031
1032 std::string i2cBus = "/dev/i2c-" + std::to_string(fruBus);
1033 std::vector<uint8_t> readBuf;
Patrick Williamsb37abfb2023-05-10 07:50:33 -05001034 ipmi::Cc ret = ipmi::i2cWriteRead(i2cBus, fruAddress, writeData,
1035 readBuf);
Zhikui Ren98fa87e2022-05-04 08:10:29 -07001036
Zhikui Ren0408e792022-06-07 21:03:33 -07001037 // prepare for read to detect chip is write protected
1038 writeData.resize(1);
1039 readBuf.resize(maxEthSize + 1); // include macHeader
1040
Zhikui Renad129c62022-04-05 20:11:24 -07001041 switch (ret)
1042 {
1043 case ipmi::ccSuccess:
Zhikui Ren98fa87e2022-05-04 08:10:29 -07001044 // Wait for internal write cycle to complete
1045 // example: ATMEL 24c0x chip has Twr spec as 5ms
1046
1047 // Ideally we want yield wait, but currently following code
1048 // crash with "thread not supported"
1049 // boost::asio::deadline_timer timer(
1050 // boost::asio::get_associated_executor(ctx->yield),
1051 // boost::posix_time::seconds(1));
1052 // timer.async_wait(ctx->yield);
1053 // use usleep as temp WA
1054 usleep(5000);
1055 if (ipmi::i2cWriteRead(i2cBus, fruAddress, writeData,
1056 readBuf) == ipmi::ccSuccess)
Zhikui Renad129c62022-04-05 20:11:24 -07001057 {
1058 if (std::equal(ethData.begin(), ethData.end(),
Zhikui Ren98fa87e2022-05-04 08:10:29 -07001059 ++readBuf.begin())) // skip macHeader
Zhikui Renad129c62022-04-05 20:11:24 -07001060 {
1061 return ipmi::ccSuccess;
1062 }
Alex Schendel4180cfe2022-11-29 10:46:11 -08001063 lg2::info("INFO: write mac fru verify failed, fru may be "
1064 "write protected.");
Zhikui Renad129c62022-04-05 20:11:24 -07001065 }
1066 return ipmi::ccCommandNotAvailable;
Zhikui Ren0408e792022-06-07 21:03:33 -07001067 default:
1068 if (ipmi::i2cWriteRead(i2cBus, fruAddress, writeData,
1069 readBuf) == ipmi::ccSuccess)
1070 {
Alex Schendel4180cfe2022-11-29 10:46:11 -08001071 lg2::info("INFO: write mac fru failed, but successfully "
1072 "read from fru, fru may be write protected.");
Zhikui Ren0408e792022-06-07 21:03:33 -07001073 return ipmi::ccCommandNotAvailable;
1074 }
1075 else // assume failure is due to no eeprom on board
1076 {
Alex Schendel4180cfe2022-11-29 10:46:11 -08001077 lg2::error("ERROR: write mac fru failed, assume no eeprom "
1078 "is available.");
Zhikui Ren0408e792022-06-07 21:03:33 -07001079 }
Zhikui Renad129c62022-04-05 20:11:24 -07001080 break;
1081 }
1082 }
1083 // no FRU eeprom found
1084 return ipmi::ccDestinationUnavailable;
1085}
1086
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +05301087ipmi::RspType<> setManufacturingData(ipmi::Context::ptr ctx, uint8_t dataType,
Richard Marian Thomaiyar1f0839c2019-08-25 20:10:52 +05301088 std::array<uint8_t, maxEthSize> ethData)
1089{
Zhikui Renad129c62022-04-05 20:11:24 -07001090 // mfg filter logic will restrict this command executing only in mfg
1091 // mode.
Richard Marian Thomaiyar1f0839c2019-08-25 20:10:52 +05301092 if (dataType >= maxSupportedEth)
1093 {
1094 return ipmi::responseParmOutOfRange();
1095 }
1096
Zhikui Renad129c62022-04-05 20:11:24 -07001097 ipmi::Cc ret = writeMacToFru(ctx, dataType, ethData);
1098 if (ret != ipmi::ccDestinationUnavailable)
1099 {
1100 resetMtmTimer(ctx);
1101 return response(ret);
1102 }
1103
Richard Marian Thomaiyar1f0839c2019-08-25 20:10:52 +05301104 constexpr uint8_t ethAddrStrSize =
1105 19; // XX:XX:XX:XX:XX:XX + \n + null termination;
1106 std::vector<uint8_t> buff(ethAddrStrSize);
1107 std::snprintf(reinterpret_cast<char*>(buff.data()), ethAddrStrSize,
1108 "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx\n", ethData.at(0),
1109 ethData.at(1), ethData.at(2), ethData.at(3), ethData.at(4),
1110 ethData.at(5));
1111 std::ofstream oEthFile(factoryEthAddrBaseFileName +
1112 std::to_string(dataType),
1113 std::ofstream::out);
1114 if (!oEthFile.good())
1115 {
1116 return ipmi::responseUnspecifiedError();
1117 }
1118
1119 oEthFile << reinterpret_cast<char*>(buff.data());
Johnathan Mantey6d83e4e2020-05-08 11:01:01 -07001120 oEthFile.flush();
Richard Marian Thomaiyar1f0839c2019-08-25 20:10:52 +05301121 oEthFile.close();
1122
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +05301123 resetMtmTimer(ctx);
Richard Marian Thomaiyar1f0839c2019-08-25 20:10:52 +05301124 return ipmi::responseSuccess();
1125}
1126
1127ipmi::RspType<uint8_t, std::array<uint8_t, maxEthSize>>
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +05301128 getManufacturingData(ipmi::Context::ptr ctx, uint8_t dataType)
Richard Marian Thomaiyar1f0839c2019-08-25 20:10:52 +05301129{
Zhikui Renad129c62022-04-05 20:11:24 -07001130 // mfg filter logic will restrict this command executing only in mfg
1131 // mode.
Richard Marian Thomaiyar1f0839c2019-08-25 20:10:52 +05301132 if (dataType >= maxSupportedEth)
1133 {
1134 return ipmi::responseParmOutOfRange();
1135 }
1136 std::array<uint8_t, maxEthSize> ethData{0};
1137 constexpr uint8_t invalidData = 0;
1138 constexpr uint8_t validData = 1;
1139
1140 std::ifstream iEthFile(factoryEthAddrBaseFileName +
1141 std::to_string(dataType),
1142 std::ifstream::in);
1143 if (!iEthFile.good())
1144 {
Zhikui Renad129c62022-04-05 20:11:24 -07001145 if (readMacFromFru(ctx, dataType, ethData))
1146 {
1147 resetMtmTimer(ctx);
1148 return ipmi::responseSuccess(validData, ethData);
1149 }
Richard Marian Thomaiyar1f0839c2019-08-25 20:10:52 +05301150 return ipmi::responseSuccess(invalidData, ethData);
1151 }
1152 std::string ethStr;
1153 iEthFile >> ethStr;
1154 uint8_t* data = ethData.data();
1155 std::sscanf(ethStr.c_str(), "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx",
1156 data, (data + 1), (data + 2), (data + 3), (data + 4),
1157 (data + 5));
1158
Richard Marian Thomaiyar357ddc72019-09-01 22:40:07 +05301159 resetMtmTimer(ctx);
Richard Marian Thomaiyar1f0839c2019-08-25 20:10:52 +05301160 return ipmi::responseSuccess(validData, ethData);
1161}
1162
Matt Simmering80d4d5f2023-02-15 15:18:51 -08001163/** @brief implements slot controller write read IPMI command which can be used
Zhikui Renad129c62022-04-05 20:11:24 -07001164 * for low-level I2C/SMBus write, read or write-read access for PCIE slots
V-Sanjana7acb2d22022-11-15 11:13:54 +05301165 * @param reserved - skip 3 bit
1166 * @param muxType - mux type
Yong Lif267a672019-08-29 11:16:07 +08001167 * @param addressType - address type
1168 * @param bbSlotNum - baseboard slot number
1169 * @param riserSlotNum - riser slot number
1170 * @param reserved2 - skip 2 bit
Matt Simmering80d4d5f2023-02-15 15:18:51 -08001171 * @param targetAddr - target address
Yong Lif267a672019-08-29 11:16:07 +08001172 * @param readCount - number of bytes to be read
1173 * @param writeData - data to be written
1174 *
1175 * @returns IPMI completion code plus response data
1176 */
V-Sanjana7acb2d22022-11-15 11:13:54 +05301177
Matt Simmering80d4d5f2023-02-15 15:18:51 -08001178ipmi::RspType<std::vector<uint8_t>> appSlotI2CControllerWriteRead(
V-Sanjana7acb2d22022-11-15 11:13:54 +05301179 uint3_t reserved, uint3_t muxType, uint2_t addressType, uint3_t bbSlotNum,
Matt Simmering80d4d5f2023-02-15 15:18:51 -08001180 uint3_t riserSlotNum, uint2_t reserved2, uint8_t targetAddr,
V-Sanjana7acb2d22022-11-15 11:13:54 +05301181 uint8_t readCount, std::vector<uint8_t> writeData)
Yong Lif267a672019-08-29 11:16:07 +08001182{
Vernon Maueryfc3bc382022-06-02 13:52:04 -07001183 if (reserved || reserved2)
1184 {
1185 return ipmi::responseInvalidFieldRequest();
1186 }
Yong Lif267a672019-08-29 11:16:07 +08001187 const size_t writeCount = writeData.size();
1188 std::string i2cBus;
1189 if (addressType == slotAddressTypeBus)
1190 {
V-Sanjana7acb2d22022-11-15 11:13:54 +05301191 std::string path = "/dev/i2c-mux/";
1192 if (muxType == bbRiserMux)
1193 {
1194 path += "Riser_" + std::to_string(static_cast<uint8_t>(bbSlotNum)) +
1195 "_Mux/Pcie_Slot_" +
1196 std::to_string(static_cast<uint8_t>(riserSlotNum));
1197 }
1198 else if (muxType == leftRiserMux)
1199 {
1200 path += "Left_Riser_Mux/Slot_" +
1201 std::to_string(static_cast<uint8_t>(riserSlotNum));
1202 }
1203 else if (muxType == rightRiserMux)
1204 {
1205 path += "Right_Riser_Mux/Slot_" +
1206 std::to_string(static_cast<uint8_t>(riserSlotNum));
1207 }
1208 else if (muxType == pcieMux)
1209 {
1210 path += "PCIe_Mux/Slot_" +
1211 std::to_string(static_cast<uint8_t>(riserSlotNum));
1212 }
1213 else if (muxType == hsbpMux)
1214 {
1215 path += "HSBP_Mux/Slot" +
1216 std::to_string(static_cast<uint8_t>(riserSlotNum));
1217 }
1218 phosphor::logging::log<phosphor::logging::level::DEBUG>(
1219 ("Path is: " + path).c_str());
Yong Lif267a672019-08-29 11:16:07 +08001220 if (std::filesystem::exists(path) && std::filesystem::is_symlink(path))
1221 {
1222 i2cBus = std::filesystem::read_symlink(path);
1223 }
1224 else
1225 {
Matt Simmering80d4d5f2023-02-15 15:18:51 -08001226 lg2::error("Controller write read command: Cannot get BusID");
Yong Lif267a672019-08-29 11:16:07 +08001227 return ipmi::responseInvalidFieldRequest();
1228 }
1229 }
1230 else if (addressType == slotAddressTypeUniqueid)
1231 {
1232 i2cBus = "/dev/i2c-" +
1233 std::to_string(static_cast<uint8_t>(bbSlotNum) |
1234 (static_cast<uint8_t>(riserSlotNum) << 3));
1235 }
1236 else
1237 {
Matt Simmering80d4d5f2023-02-15 15:18:51 -08001238 lg2::error("Controller write read command: invalid request");
Yong Lif267a672019-08-29 11:16:07 +08001239 return ipmi::responseInvalidFieldRequest();
1240 }
1241
Zhikui Renad129c62022-04-05 20:11:24 -07001242 // Allow single byte write as it is offset byte to read the data, rest
1243 // allow only in Special mode.
Yong Lif267a672019-08-29 11:16:07 +08001244 if (writeCount > 1)
1245 {
Richard Marian Thomaiyarae13ac62019-12-17 15:45:12 +05301246 if (mtm.getMfgMode() == SpecialMode::none)
Yong Lif267a672019-08-29 11:16:07 +08001247 {
1248 return ipmi::responseInsufficientPrivilege();
1249 }
1250 }
1251
1252 if (readCount > slotI2CMaxReadSize)
1253 {
Matt Simmering80d4d5f2023-02-15 15:18:51 -08001254 lg2::error("Controller write read command: Read count exceeds limit");
Yong Lif267a672019-08-29 11:16:07 +08001255 return ipmi::responseParmOutOfRange();
1256 }
1257
1258 if (!readCount && !writeCount)
1259 {
Matt Simmering80d4d5f2023-02-15 15:18:51 -08001260 lg2::error("Controller write read command: Read & write count are 0");
Yong Lif267a672019-08-29 11:16:07 +08001261 return ipmi::responseInvalidFieldRequest();
1262 }
1263
1264 std::vector<uint8_t> readBuf(readCount);
1265
Patrick Williamsb37abfb2023-05-10 07:50:33 -05001266 ipmi::Cc retI2C = ipmi::i2cWriteRead(i2cBus, targetAddr, writeData,
1267 readBuf);
Yong Lif267a672019-08-29 11:16:07 +08001268 if (retI2C != ipmi::ccSuccess)
1269 {
1270 return ipmi::response(retI2C);
1271 }
1272
1273 return ipmi::responseSuccess(readBuf);
1274}
Yong Li068b4f22019-09-17 16:32:18 +08001275
1276ipmi::RspType<> clearCMOS()
1277{
Matt Simmering80d4d5f2023-02-15 15:18:51 -08001278 // There is an i2c device on bus 4, the target address is 0x38. Based on
Zhikui Renad129c62022-04-05 20:11:24 -07001279 // the spec, writing 0x1 to address 0x61 on this device, will trigger
1280 // the clear CMOS action.
Matt Simmering80d4d5f2023-02-15 15:18:51 -08001281 constexpr uint8_t targetAddr = 0x38;
Yong Li068b4f22019-09-17 16:32:18 +08001282 std::string i2cBus = "/dev/i2c-4";
Yong Lieaeb6cb2020-03-09 20:21:50 +08001283 std::vector<uint8_t> writeData = {0x61, 0x1};
Yong Li068b4f22019-09-17 16:32:18 +08001284 std::vector<uint8_t> readBuf(0);
1285
Patrick Williamsb37abfb2023-05-10 07:50:33 -05001286 ipmi::Cc retI2C = ipmi::i2cWriteRead(i2cBus, targetAddr, writeData,
1287 readBuf);
Yong Li068b4f22019-09-17 16:32:18 +08001288 return ipmi::response(retI2C);
1289}
Vernon Mauery27d23562021-08-12 10:43:39 -07001290
1291ipmi::RspType<> setFITcLayout(uint32_t layout)
1292{
1293 static constexpr const char* factoryFITcLayout =
1294 "/var/sofs/factory-settings/layout/fitc";
1295 std::filesystem::path fitcDir =
1296 std::filesystem::path(factoryFITcLayout).parent_path();
1297 std::error_code ec;
1298 std::filesystem::create_directories(fitcDir, ec);
1299 if (ec)
1300 {
1301 return ipmi::responseUnspecifiedError();
1302 }
1303 try
1304 {
1305 std::ofstream file(factoryFITcLayout);
1306 file << layout;
1307 file.flush();
1308 file.close();
1309 }
1310 catch (const std::exception& e)
1311 {
1312 return ipmi::responseUnspecifiedError();
1313 }
1314
1315 return ipmi::responseSuccess();
1316}
1317
Arun P. Mohanan06584cd2021-08-13 20:56:01 +05301318static std::vector<std::string>
1319 getMCTPServiceConfigPaths(ipmi::Context::ptr& ctx)
1320{
1321 boost::system::error_code ec;
1322 auto configPaths = ctx->bus->yield_method_call<std::vector<std::string>>(
1323 ctx->yield, ec, "xyz.openbmc_project.ObjectMapper",
1324 "/xyz/openbmc_project/object_mapper",
1325 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths",
1326 "/xyz/openbmc_project/inventory/system/board", 2,
1327 std::array<const char*, 1>{
1328 "xyz.openbmc_project.Configuration.MctpConfiguration"});
1329 if (ec)
1330 {
1331 throw std::runtime_error(
1332 "Failed to query configuration sub tree objects");
1333 }
1334 return configPaths;
1335}
1336
1337static ipmi::RspType<> startOrStopService(ipmi::Context::ptr& ctx,
1338 const uint8_t enable,
ANJALI RAY1fe485c2021-12-17 16:41:30 +00001339 const std::string& serviceName,
1340 bool disableOrEnableUnitFiles = true)
Arun P. Mohanan06584cd2021-08-13 20:56:01 +05301341{
1342 constexpr bool runtimeOnly = false;
1343 constexpr bool force = false;
1344
1345 boost::system::error_code ec;
1346 switch (enable)
1347 {
1348 case ipmi::SupportedFeatureActions::stop:
1349 ctx->bus->yield_method_call(ctx->yield, ec, systemDService,
1350 systemDObjPath, systemDMgrIntf,
1351 "StopUnit", serviceName, "replace");
1352 break;
1353 case ipmi::SupportedFeatureActions::start:
1354 ctx->bus->yield_method_call(ctx->yield, ec, systemDService,
1355 systemDObjPath, systemDMgrIntf,
1356 "StartUnit", serviceName, "replace");
1357 break;
1358 case ipmi::SupportedFeatureActions::disable:
ANJALI RAY1fe485c2021-12-17 16:41:30 +00001359 if (disableOrEnableUnitFiles == true)
1360 {
1361 ctx->bus->yield_method_call(
1362 ctx->yield, ec, systemDService, systemDObjPath,
1363 systemDMgrIntf, "DisableUnitFiles",
1364 std::array<const char*, 1>{serviceName.c_str()},
1365 runtimeOnly);
1366 }
Arun P. Mohanan06584cd2021-08-13 20:56:01 +05301367 ctx->bus->yield_method_call(
1368 ctx->yield, ec, systemDService, systemDObjPath, systemDMgrIntf,
1369 "MaskUnitFiles",
1370 std::array<const char*, 1>{serviceName.c_str()}, runtimeOnly,
1371 force);
Arun P. Mohanan06584cd2021-08-13 20:56:01 +05301372 break;
1373 case ipmi::SupportedFeatureActions::enable:
1374 ctx->bus->yield_method_call(
1375 ctx->yield, ec, systemDService, systemDObjPath, systemDMgrIntf,
1376 "UnmaskUnitFiles",
1377 std::array<const char*, 1>{serviceName.c_str()}, runtimeOnly);
ANJALI RAY1fe485c2021-12-17 16:41:30 +00001378 if (disableOrEnableUnitFiles == true)
1379 {
1380 ctx->bus->yield_method_call(
1381 ctx->yield, ec, systemDService, systemDObjPath,
1382 systemDMgrIntf, "EnableUnitFiles",
1383 std::array<const char*, 1>{serviceName.c_str()},
1384 runtimeOnly, force);
1385 }
Arun P. Mohanan06584cd2021-08-13 20:56:01 +05301386 break;
1387 default:
Alex Schendel4180cfe2022-11-29 10:46:11 -08001388 lg2::warning("ERROR: Invalid feature action selected", "ACTION",
1389 lg2::dec, enable);
Arun P. Mohanan06584cd2021-08-13 20:56:01 +05301390 return ipmi::responseInvalidFieldRequest();
1391 }
1392 if (ec)
1393 {
Alex Schendel4180cfe2022-11-29 10:46:11 -08001394 lg2::warning("ERROR: Service start or stop failed", "SERVICE",
1395 serviceName.c_str());
Arun P. Mohanan06584cd2021-08-13 20:56:01 +05301396 return ipmi::responseUnspecifiedError();
1397 }
1398 return ipmi::responseSuccess();
1399}
1400
1401static std::string getMCTPServiceName(const std::string& objectPath)
1402{
1403 const auto serviceArgument = boost::algorithm::replace_all_copy(
1404 boost::algorithm::replace_first_copy(
1405 objectPath, "/xyz/openbmc_project/inventory/system/board/", ""),
1406 "/", "_2f");
Patrick Williamsb37abfb2023-05-10 07:50:33 -05001407 std::string unitName = "xyz.openbmc_project.mctpd@" + serviceArgument +
1408 ".service";
Arun P. Mohanan06584cd2021-08-13 20:56:01 +05301409 return unitName;
1410}
1411
1412static ipmi::RspType<> handleMCTPFeature(ipmi::Context::ptr& ctx,
1413 const uint8_t enable,
1414 const std::string& binding)
1415{
1416 std::vector<std::string> configPaths;
1417 try
1418 {
1419 configPaths = getMCTPServiceConfigPaths(ctx);
1420 }
1421 catch (const std::exception& e)
1422 {
Alex Schendel4180cfe2022-11-29 10:46:11 -08001423 lg2::error(e.what());
Arun P. Mohanan06584cd2021-08-13 20:56:01 +05301424 return ipmi::responseUnspecifiedError();
1425 }
1426
1427 for (const auto& objectPath : configPaths)
1428 {
Patrick Williamsb37abfb2023-05-10 07:50:33 -05001429 const auto pos = objectPath.find_last_of('/');
Arun P. Mohanan06584cd2021-08-13 20:56:01 +05301430 if (binding == objectPath.substr(pos + 1))
1431 {
1432 return startOrStopService(ctx, enable,
ANJALI RAY1fe485c2021-12-17 16:41:30 +00001433 getMCTPServiceName(objectPath), false);
Arun P. Mohanan06584cd2021-08-13 20:56:01 +05301434 }
1435 }
1436 return ipmi::responseSuccess();
1437}
1438
Vasu V97c30902023-05-19 15:56:15 +05301439static bool isNum(const std::string& s)
1440{
1441 if (s.empty())
1442 {
1443 return false;
1444 }
1445 uint8_t busNumber;
1446 const auto sEnd = s.data() + s.size();
1447 const auto& [ptr, ec] = std::from_chars(s.data(), sEnd, busNumber);
1448 if (ec == std::errc() || ptr == sEnd)
1449 {
1450 return true;
1451 }
1452 return false;
1453}
1454
1455bool getBusNumFromPath(const std::string& path, std::string& busStr)
1456{
1457 std::vector<std::string> parts;
1458 boost::split(parts, path, boost::is_any_of("-"));
1459 if (parts.size() == 2)
1460 {
1461 busStr = parts[1];
1462 if (isNum(busStr))
1463 {
1464 return true;
1465 }
1466 }
1467 return false;
1468}
1469
1470static ipmi::RspType<> muxSlotDisable(ipmi::Context::ptr& ctx,
1471 std::string service, std::string muxName,
1472 uint8_t action, uint8_t slotNum)
1473{
1474 boost::system::error_code ec;
1475 const std::filesystem::path muxSymlinkDirPath =
1476 "/dev/i2c-mux/" + muxName + "/Slot_" + std::to_string(slotNum + 1);
1477 if (!std::filesystem::is_symlink(muxSymlinkDirPath))
1478 {
1479 return ipmi::responseInvalidFieldRequest();
1480 }
1481 std::string linkPath = std::filesystem::read_symlink(muxSymlinkDirPath);
1482
1483 std::string portNum;
1484 if (!getBusNumFromPath(linkPath, portNum))
1485 {
1486 return ipmi::responseInvalidFieldRequest();
1487 }
1488 auto res = ctx->bus->yield_method_call<int>(
1489 ctx->yield, ec, service, mctpObjPath, mctpBaseIntf, "SkipList",
1490 std::vector<uint8_t>{action, static_cast<uint8_t>(std::stoi(portNum))});
1491 if (ec)
1492 {
1493 lg2::error("Failed to set mctp skiplist");
1494 return ipmi::responseUnspecifiedError();
1495 }
1496
1497 if (!res)
1498 {
1499 return ipmi::responseResponseError();
1500 }
1501 return ipmi::responseSuccess();
1502}
1503
1504static ipmi::RspType<> handleMCTPSlotFeature(ipmi::Context::ptr& ctx,
1505 const uint8_t enable,
1506 const uint8_t featureArg)
1507{
1508 uint8_t slotNum = (featureArg & slotNumMask);
1509 switch ((featureArg & muxTypeMask) >> muxTypeShift)
1510 {
1511 case ipmi::SupportedFeatureMuxs::pcieMuxSlot:
1512 return muxSlotDisable(ctx, mctpPcieSlotService, "PCIe_Mux", enable,
1513 slotNum);
1514 break;
1515 case ipmi::SupportedFeatureMuxs::pcieMcioMuxSlot:
1516 return muxSlotDisable(ctx, mctpPcieSlotService, "PCIe_MCIO_Mux",
1517 enable, slotNum);
1518 break;
1519 case ipmi::SupportedFeatureMuxs::pcieM2EdSffMuxSlot:
1520 return muxSlotDisable(ctx, mctpPcieSlotService, "M2_EDSFF_Mux",
1521 enable, slotNum);
1522 break;
1523 case ipmi::SupportedFeatureMuxs::leftRaiserMuxSlot:
1524 return muxSlotDisable(ctx, mctpPcieSlotService, "Left_Riser_Mux",
1525 enable, slotNum);
1526 break;
1527 case ipmi::SupportedFeatureMuxs::rightRaiserMuxSlot:
1528 return muxSlotDisable(ctx, mctpPcieSlotService, "Right_Riser_Mux",
1529 enable, slotNum);
1530 break;
1531 case ipmi::SupportedFeatureMuxs::HsbpMuxSlot:
1532 return muxSlotDisable(ctx, mctpHsbpService, "HSBP_Mux", enable,
1533 slotNum);
1534 break;
1535 default:
1536 lg2::warning("ERROR: Invalid Mux slot selected");
1537 return ipmi::responseInvalidFieldRequest();
1538 }
1539}
1540
Arun P. Mohanan06584cd2021-08-13 20:56:01 +05301541/** @brief implements MTM BMC Feature Control IPMI command which can be
1542 * used to enable or disable the supported BMC features.
1543 * @param yield - context object that represents the currently executing
1544 * coroutine
1545 * @param feature - feature enum to enable or disable
1546 * @param enable - enable or disable the feature
1547 * @param featureArg - custom arguments for that feature
1548 * @param reserved - reserved for future use
1549 *
1550 * @returns IPMI completion code
1551 */
1552ipmi::RspType<> mtmBMCFeatureControl(ipmi::Context::ptr ctx,
1553 const uint8_t feature,
1554 const uint8_t enable,
1555 const uint8_t featureArg,
1556 const uint16_t reserved)
1557{
1558 if (reserved != 0)
1559 {
1560 return ipmi::responseInvalidFieldRequest();
1561 }
1562
1563 switch (feature)
1564 {
1565 case ipmi::SupportedFeatureControls::mctp:
1566 switch (featureArg)
1567 {
1568 case ipmi::SupportedMCTPBindings::mctpPCIe:
1569 return handleMCTPFeature(ctx, enable, "MCTP_PCIe");
1570 case ipmi::SupportedMCTPBindings::mctpSMBusHSBP:
1571 return handleMCTPFeature(ctx, enable, "MCTP_SMBus_HSBP");
1572 case ipmi::SupportedMCTPBindings::mctpSMBusPCIeSlot:
1573 return handleMCTPFeature(ctx, enable,
1574 "MCTP_SMBus_PCIe_slot");
1575 default:
1576 return ipmi::responseInvalidFieldRequest();
1577 }
1578 break;
Jason M. Bills5cb2c042021-08-17 12:03:39 -07001579 case ipmi::SupportedFeatureControls::pcieScan:
1580 if (featureArg != 0)
1581 {
1582 return ipmi::responseInvalidFieldRequest();
1583 }
1584 startOrStopService(ctx, enable, "xyz.openbmc_project.PCIe.service");
1585 break;
Vasu V97c30902023-05-19 15:56:15 +05301586 case ipmi::SupportedFeatureControls::mctpSlotSupport:
1587 return handleMCTPSlotFeature(ctx, enable, featureArg);
1588 break;
Arun P. Mohanan06584cd2021-08-13 20:56:01 +05301589 default:
1590 return ipmi::responseInvalidFieldRequest();
1591 }
1592 return ipmi::responseSuccess();
1593}
Vernon Mauerya3702c12019-05-22 13:20:59 -07001594} // namespace ipmi
1595
1596void register_mtm_commands() __attribute__((constructor));
1597void register_mtm_commands()
1598{
Jason M. Bills38d2b5a2019-06-03 16:26:11 -07001599 // <Get SM Signal>
Vernon Mauery98bbf692019-09-16 11:14:59 -07001600 ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnGeneral,
1601 ipmi::intel::general::cmdGetSmSignal,
1602 ipmi::Privilege::Admin, ipmi::appMTMGetSignal);
Vernon Mauerya3702c12019-05-22 13:20:59 -07001603
Vernon Mauery98bbf692019-09-16 11:14:59 -07001604 ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnGeneral,
1605 ipmi::intel::general::cmdSetSmSignal,
1606 ipmi::Privilege::Admin, ipmi::appMTMSetSignal);
Vernon Mauerya3702c12019-05-22 13:20:59 -07001607
Vernon Mauery98bbf692019-09-16 11:14:59 -07001608 ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnGeneral,
1609 ipmi::intel::general::cmdMtmKeepAlive,
1610 ipmi::Privilege::Admin, ipmi::mtmKeepAlive);
Richard Marian Thomaiyar666dd012019-08-02 20:55:37 +05301611
Vernon Mauery98bbf692019-09-16 11:14:59 -07001612 ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnGeneral,
1613 ipmi::intel::general::cmdSetManufacturingData,
1614 ipmi::Privilege::Admin, ipmi::setManufacturingData);
Richard Marian Thomaiyar1f0839c2019-08-25 20:10:52 +05301615
Vernon Mauery98bbf692019-09-16 11:14:59 -07001616 ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnGeneral,
1617 ipmi::intel::general::cmdGetManufacturingData,
1618 ipmi::Privilege::Admin, ipmi::getManufacturingData);
Richard Marian Thomaiyar1f0839c2019-08-25 20:10:52 +05301619
Vernon Mauery27d23562021-08-12 10:43:39 -07001620 ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnGeneral,
1621 ipmi::intel::general::cmdSetFITcLayout,
1622 ipmi::Privilege::Admin, ipmi::setFITcLayout);
1623
Arun P. Mohanan06584cd2021-08-13 20:56:01 +05301624 ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnGeneral,
1625 ipmi::intel::general::cmdMTMBMCFeatureControl,
1626 ipmi::Privilege::Admin, ipmi::mtmBMCFeatureControl);
1627
Vernon Mauery98bbf692019-09-16 11:14:59 -07001628 ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnApp,
Matt Simmering80d4d5f2023-02-15 15:18:51 -08001629 ipmi::intel::general::cmdSlotI2CControllerWriteRead,
Vernon Mauery98bbf692019-09-16 11:14:59 -07001630 ipmi::Privilege::Admin,
Matt Simmering80d4d5f2023-02-15 15:18:51 -08001631 ipmi::appSlotI2CControllerWriteRead);
Yong Lif267a672019-08-29 11:16:07 +08001632
Yong Li068b4f22019-09-17 16:32:18 +08001633 ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnPlatform,
1634 ipmi::intel::platform::cmdClearCMOS,
1635 ipmi::Privilege::Admin, ipmi::clearCMOS);
1636
Vernon Mauery98bbf692019-09-16 11:14:59 -07001637 ipmi::registerFilter(ipmi::prioOemBase,
Yong Li85feb132019-08-09 16:06:03 +08001638 [](ipmi::message::Request::ptr request) {
Patrick Williamsb37abfb2023-05-10 07:50:33 -05001639 return ipmi::mfgFilterMessage(request);
1640 });
Vernon Mauerya3702c12019-05-22 13:20:59 -07001641}