blob: eb141f5b716e0413e5fc9df28ccf00cbf96d0220 [file] [log] [blame]
Patrick Venture0b02be92018-08-31 11:55:55 -07001#include "config.h"
2
Tom Josephbe5eaa12017-07-12 19:54:44 +05303#include "dcmihandler.hpp"
Patrick Venture0b02be92018-08-31 11:55:55 -07004
Johnathan Mantey74a21022018-12-13 13:17:56 -08005#include "user_channel/channel_layer.hpp"
Patrick Venture0b02be92018-08-31 11:55:55 -07006
Vernon Mauerye08fbff2019-04-03 09:19:34 -07007#include <ipmid/api.hpp>
Vernon Mauery6a98fe72019-03-11 15:57:48 -07008#include <ipmid/utils.hpp>
Patrick Venture0b02be92018-08-31 11:55:55 -07009#include <nlohmann/json.hpp>
Tom Josephbe5eaa12017-07-12 19:54:44 +053010#include <phosphor-logging/elog-errors.hpp>
George Liu7fa28712024-07-17 19:26:23 +080011#include <phosphor-logging/lg2.hpp>
Andrew Geissler50c0c8f2017-07-11 16:18:51 -050012#include <sdbusplus/bus.hpp>
Patrick Venture0b02be92018-08-31 11:55:55 -070013#include <xyz/openbmc_project/Common/error.hpp>
Thang Tran55cbf552023-01-31 14:43:02 +070014#include <xyz/openbmc_project/Network/EthernetInterface/server.hpp>
Alexander Hansenf365c7f2025-11-14 12:08:34 +010015#include <xyz/openbmc_project/Sensor/Value/common.hpp>
Patrick Venture0b02be92018-08-31 11:55:55 -070016
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -050017#include <bitset>
18#include <cmath>
19#include <fstream>
20#include <variant>
21
Tom Josephbe5eaa12017-07-12 19:54:44 +053022using namespace phosphor::logging;
Willy Tu523e2d12023-09-05 11:36:48 -070023using sdbusplus::server::xyz::openbmc_project::network::EthernetInterface;
Thang Tran55cbf552023-01-31 14:43:02 +070024
Tom Josephbe5eaa12017-07-12 19:54:44 +053025using InternalFailure =
Willy Tu523e2d12023-09-05 11:36:48 -070026 sdbusplus::error::xyz::openbmc_project::common::InternalFailure;
Chris Austen1810bec2015-10-13 12:12:39 -050027
Alexander Hansenf365c7f2025-11-14 12:08:34 +010028using SensorValue = sdbusplus::common::xyz::openbmc_project::sensor::Value;
29
George Liu5087b072025-03-11 19:28:17 +080030void registerNetFnDcmiFunctions() __attribute__((constructor));
Chris Austen1810bec2015-10-13 12:12:39 -050031
Vernon Maueryd4222fd2023-07-27 11:26:51 -070032constexpr auto pcapInterface = "xyz.openbmc_project.Control.Power.Cap";
Andrew Geissler50c0c8f2017-07-11 16:18:51 -050033
Vernon Maueryd4222fd2023-07-27 11:26:51 -070034constexpr auto powerCapProp = "PowerCap";
35constexpr auto powerCapEnableProp = "PowerCapEnable";
Thang Trana95978d2025-06-04 10:39:15 +070036constexpr auto powerCapExceptActProp = "ExceptionAction";
37constexpr auto powerCapSamplPeriodProp = "SamplingPeriod";
38constexpr auto powerCapCorrectTimeProp = "CorrectionTime";
Andrew Geissler50c0c8f2017-07-11 16:18:51 -050039
40using namespace phosphor::logging;
41
Tom Josephb9d86f42017-07-26 18:03:47 +053042namespace dcmi
43{
Vernon Mauerydca47202023-07-27 11:32:01 -070044constexpr auto assetTagMaxOffset = 62;
45constexpr auto assetTagMaxSize = 63;
46constexpr auto maxBytes = 16;
47constexpr size_t maxCtrlIdStrLen = 63;
Tom Josephb9d86f42017-07-26 18:03:47 +053048
Vernon Maueryf038dc02023-07-27 14:06:11 -070049constexpr uint8_t parameterRevision = 2;
50constexpr uint8_t specMajorVersion = 1;
51constexpr uint8_t specMinorVersion = 5;
Vernon Mauery6475b5c2023-07-27 14:52:51 -070052constexpr uint8_t configParameterRevision = 1;
53constexpr auto option12Mask = 0x01;
54constexpr auto activateDhcpReply = 0x00;
55constexpr uint8_t dhcpTiming1 = 0x04; // 4 sec
56constexpr uint16_t dhcpTiming2 = 0x78; // 120 sec
57constexpr uint16_t dhcpTiming3 = 0x40; // 60 sec
58// When DHCP Option 12 is enabled the string "SendHostName=true" will be
59// added into n/w configuration file and the parameter
60// SendHostNameEnabled will set to true.
61constexpr auto dhcpOpt12Enabled = "SendHostNameEnabled";
62
63enum class DCMIConfigParameters : uint8_t
64{
65 ActivateDHCP = 1,
66 DiscoveryConfig,
67 DHCPTiming1,
68 DHCPTiming2,
69 DHCPTiming3,
70};
Vernon Maueryf038dc02023-07-27 14:06:11 -070071
Thang Trana95978d2025-06-04 10:39:15 +070072/*
73 * The list of Exception action options. Please refer to table 6-17 of DCMI
74 * specification version 1.5 for more information.
75 * https://www.intel.com/content/dam/www/public/us/en/documents/technical-specifications/dcmi-v1-5-rev-spec.pdf
76 */
77enum class ExceptActOptions : uint8_t
78{
79 NoAction = 0x00,
80 HardPowerOff,
81 Oem02,
82 Oem03,
83 Oem04,
84 Oem05,
85 Oem06,
86 Oem07,
87 Oem08,
88 Oem09,
89 Oem0A,
90 Oem0B,
91 Oem0C,
92 Oem0D,
93 Oem0E,
94 Oem0F,
95 Oem10,
96 LogEventOnly,
97};
98
99/*
100 * The PDIs only supports NoAction/HardPowerOff/LogEventOnly/Oem options for
101 * exception action.
102 */
103namespace DbusExceptAct
104{
105constexpr auto noAction =
106 "xyz.openbmc_project.Control.Power.Cap.ExceptionActions.NoAction";
107constexpr auto hardPowerOff =
108 "xyz.openbmc_project.Control.Power.Cap.ExceptionActions.HardPowerOff";
109constexpr auto logEventOnly =
110 "xyz.openbmc_project.Control.Power.Cap.ExceptionActions.LogEventOnly";
111constexpr auto oem =
112 "xyz.openbmc_project.Control.Power.Cap.ExceptionActions.Oem";
113} // namespace DbusExceptAct
114
Deepak Kodihalli0b459552018-02-06 06:25:12 -0600115// Refer Table 6-14, DCMI Entity ID Extension, DCMI v1.5 spec
Patrick Venture0b02be92018-08-31 11:55:55 -0700116static const std::map<uint8_t, std::string> entityIdToName{
117 {0x40, "inlet"}, {0x37, "inlet"}, {0x41, "cpu"},
118 {0x03, "cpu"}, {0x42, "baseboard"}, {0x07, "baseboard"}};
Deepak Kodihalli0b459552018-02-06 06:25:12 -0600119
Vernon Mauery6475b5c2023-07-27 14:52:51 -0700120nlohmann::json parseJSONConfig(const std::string& configFile)
121{
122 std::ifstream jsonFile(configFile);
123 if (!jsonFile.is_open())
124 {
George Liu7fa28712024-07-17 19:26:23 +0800125 lg2::error("Temperature readings JSON file not found");
Vernon Mauery6475b5c2023-07-27 14:52:51 -0700126 elog<InternalFailure>();
127 }
128
129 auto data = nlohmann::json::parse(jsonFile, nullptr, false);
130 if (data.is_discarded())
131 {
George Liu7fa28712024-07-17 19:26:23 +0800132 lg2::error("Temperature readings JSON parser failure");
Vernon Mauery6475b5c2023-07-27 14:52:51 -0700133 elog<InternalFailure>();
134 }
135
136 return data;
137}
138
Kirill Pakhomov2c2af2c2018-11-06 16:06:10 +0300139bool isDCMIPowerMgmtSupported()
140{
Vernon Maueryf4eb35d2023-07-27 11:08:49 -0700141 static bool parsed = false;
142 static bool supported = false;
143 if (!parsed)
144 {
145 auto data = parseJSONConfig(gDCMICapabilitiesConfig);
Kirill Pakhomov2c2af2c2018-11-06 16:06:10 +0300146
Vernon Maueryf4eb35d2023-07-27 11:08:49 -0700147 supported = (gDCMIPowerMgmtSupported ==
148 data.value(gDCMIPowerMgmtCapability, 0));
149 }
150 return supported;
Kirill Pakhomov2c2af2c2018-11-06 16:06:10 +0300151}
152
Thang Trana95978d2025-06-04 10:39:15 +0700153std::optional<ipmi::DbusObjectInfo> getPCapObject(ipmi::Context::ptr& ctx)
Andrew Geissler50c0c8f2017-07-11 16:18:51 -0500154{
Thang Trana95978d2025-06-04 10:39:15 +0700155 static std::optional<ipmi::DbusObjectInfo> pcapObject = std::nullopt;
156
157 if (pcapObject != std::nullopt)
158 {
159 return pcapObject;
160 }
161
162 ipmi::DbusObjectInfo objectPath{};
Patrick Williams1318a5e2024-08-16 15:19:54 -0400163 boost::system::error_code ec =
Thang Trana95978d2025-06-04 10:39:15 +0700164 ipmi::getDbusObject(ctx, pcapInterface, objectPath);
165
Vernon Maueryd4222fd2023-07-27 11:26:51 -0700166 if (ec.value())
Andrew Geissler50c0c8f2017-07-11 16:18:51 -0500167 {
Vernon Maueryd4222fd2023-07-27 11:26:51 -0700168 return std::nullopt;
George Liu3e3cc352023-07-26 15:59:31 +0800169 }
Thang Trana95978d2025-06-04 10:39:15 +0700170
171 pcapObject = objectPath;
172
173 return pcapObject;
174}
175
176std::optional<uint32_t> getPcap(ipmi::Context::ptr& ctx)
177{
178 std::optional<ipmi::DbusObjectInfo> pcapObject = getPCapObject(ctx);
179
180 if (pcapObject == std::nullopt)
181 {
182 return std::nullopt;
183 }
184
Vernon Maueryd4222fd2023-07-27 11:26:51 -0700185 uint32_t pcap{};
Thang Trana95978d2025-06-04 10:39:15 +0700186 boost::system::error_code ec = ipmi::getDbusProperty(
187 ctx, pcapObject.value().second.c_str(),
188 pcapObject.value().first.c_str(), pcapInterface, powerCapProp, pcap);
189
Vernon Maueryd4222fd2023-07-27 11:26:51 -0700190 if (ec.value())
George Liu3e3cc352023-07-26 15:59:31 +0800191 {
George Liu7fa28712024-07-17 19:26:23 +0800192 lg2::error("Error in getPcap prop: {ERROR}", "ERROR", ec.message());
Tom Josephb9d86f42017-07-26 18:03:47 +0530193 elog<InternalFailure>();
Vernon Maueryd4222fd2023-07-27 11:26:51 -0700194 return std::nullopt;
Andrew Geissler50c0c8f2017-07-11 16:18:51 -0500195 }
Thang Trana95978d2025-06-04 10:39:15 +0700196
Vernon Maueryd4222fd2023-07-27 11:26:51 -0700197 return pcap;
Andrew Geissler50c0c8f2017-07-11 16:18:51 -0500198}
199
Vernon Maueryd4222fd2023-07-27 11:26:51 -0700200std::optional<bool> getPcapEnabled(ipmi::Context::ptr& ctx)
Andrew Geissler50c0c8f2017-07-11 16:18:51 -0500201{
Thang Trana95978d2025-06-04 10:39:15 +0700202 std::optional<ipmi::DbusObjectInfo> pcapObject = getPCapObject(ctx);
203
204 if (pcapObject == std::nullopt)
Andrew Geissler50c0c8f2017-07-11 16:18:51 -0500205 {
Vernon Maueryd4222fd2023-07-27 11:26:51 -0700206 return std::nullopt;
George Liu3e3cc352023-07-26 15:59:31 +0800207 }
Thang Trana95978d2025-06-04 10:39:15 +0700208
Vernon Maueryd4222fd2023-07-27 11:26:51 -0700209 bool pcapEnabled{};
Thang Trana95978d2025-06-04 10:39:15 +0700210 boost::system::error_code ec =
211 ipmi::getDbusProperty(ctx, pcapObject.value().second.c_str(),
212 pcapObject.value().first.c_str(), pcapInterface,
213 powerCapEnableProp, pcapEnabled);
214
Vernon Maueryd4222fd2023-07-27 11:26:51 -0700215 if (ec.value())
George Liu3e3cc352023-07-26 15:59:31 +0800216 {
Thang Trana95978d2025-06-04 10:39:15 +0700217 lg2::error("Error in getPcapEnabled prop: {ERROR}", "ERROR",
218 ec.message());
Tom Josephb9d86f42017-07-26 18:03:47 +0530219 elog<InternalFailure>();
Vernon Maueryd4222fd2023-07-27 11:26:51 -0700220 return std::nullopt;
Andrew Geissler50c0c8f2017-07-11 16:18:51 -0500221 }
Thang Trana95978d2025-06-04 10:39:15 +0700222
Vernon Maueryd4222fd2023-07-27 11:26:51 -0700223 return pcapEnabled;
Andrew Geissler50c0c8f2017-07-11 16:18:51 -0500224}
Chris Austen1810bec2015-10-13 12:12:39 -0500225
Thang Trana95978d2025-06-04 10:39:15 +0700226std::optional<std::string> getPcapExceptAction(ipmi::Context::ptr& ctx)
227{
228 std::optional<ipmi::DbusObjectInfo> pcapObject = getPCapObject(ctx);
229
230 if (pcapObject == std::nullopt)
231 {
232 return std::nullopt;
233 }
234
235 std::string exceptActStr{};
236
237 boost::system::error_code ec =
238 ipmi::getDbusProperty(ctx, pcapObject.value().second.c_str(),
239 pcapObject.value().first.c_str(), pcapInterface,
240 powerCapExceptActProp, exceptActStr);
241
242 if (ec.value())
243 {
244 lg2::error("Error in getPcapExceptAction prop: {ERROR}", "ERROR",
245 ec.message());
246 elog<InternalFailure>();
247 return std::nullopt;
248 }
249
250 return exceptActStr;
251}
252
253std::optional<uint32_t> getPcapCorrectTime(ipmi::Context::ptr& ctx)
254{
255 std::optional<ipmi::DbusObjectInfo> pcapObject = getPCapObject(ctx);
256
257 if (pcapObject == std::nullopt)
258 {
259 return std::nullopt;
260 }
261
262 uint64_t pcapCorrectTimeUs{};
263 boost::system::error_code ec =
264 ipmi::getDbusProperty(ctx, pcapObject.value().second.c_str(),
265 pcapObject.value().first.c_str(), pcapInterface,
266 powerCapCorrectTimeProp, pcapCorrectTimeUs);
267 if (ec.value())
268 {
269 lg2::error("Error in getPcapCorrectTime prop: {ERROR}", "ERROR",
270 ec.message());
271 elog<InternalFailure>();
272 return std::nullopt;
273 }
274
275 return (uint32_t)(std::chrono::duration_cast<std::chrono::milliseconds>(
276 std::chrono::microseconds(pcapCorrectTimeUs)))
277 .count();
278}
279
280std::optional<uint16_t> getPcapSamplPeriod(ipmi::Context::ptr& ctx)
281{
282 std::optional<ipmi::DbusObjectInfo> pcapObject = getPCapObject(ctx);
283
284 if (pcapObject == std::nullopt)
285 {
286 return std::nullopt;
287 }
288
289 uint64_t pcapSamplPeriodUs{};
290 boost::system::error_code ec =
291 ipmi::getDbusProperty(ctx, pcapObject.value().second.c_str(),
292 pcapObject.value().first.c_str(), pcapInterface,
293 powerCapSamplPeriodProp, pcapSamplPeriodUs);
294 if (ec.value())
295 {
296 lg2::error("Error in getPcapSamplPeriod prop: {ERROR}", "ERROR",
297 ec.message());
298 elog<InternalFailure>();
299 return std::nullopt;
300 }
301
302 return (uint16_t)(std::chrono::duration_cast<std::chrono::seconds>(
303 std::chrono::microseconds(pcapSamplPeriodUs)))
304 .count();
305}
306
Vernon Maueryd4222fd2023-07-27 11:26:51 -0700307bool setPcap(ipmi::Context::ptr& ctx, const uint32_t powerCap)
Tom Joseph46fa37d2017-07-26 18:11:55 +0530308{
Thang Trana95978d2025-06-04 10:39:15 +0700309 std::optional<ipmi::DbusObjectInfo> pcapObject = getPCapObject(ctx);
310
311 if (pcapObject == std::nullopt)
Tom Joseph46fa37d2017-07-26 18:11:55 +0530312 {
Vernon Maueryd4222fd2023-07-27 11:26:51 -0700313 return false;
George Liu3e3cc352023-07-26 15:59:31 +0800314 }
Vernon Maueryd4222fd2023-07-27 11:26:51 -0700315
Thang Trana95978d2025-06-04 10:39:15 +0700316 boost::system::error_code ec =
317 ipmi::setDbusProperty(ctx, pcapObject.value().second.c_str(),
318 pcapObject.value().first.c_str(), pcapInterface,
319 powerCapProp, powerCap);
320
Vernon Maueryd4222fd2023-07-27 11:26:51 -0700321 if (ec.value())
George Liu3e3cc352023-07-26 15:59:31 +0800322 {
George Liu7fa28712024-07-17 19:26:23 +0800323 lg2::error("Error in setPcap property: {ERROR}", "ERROR", ec.message());
Tom Joseph46fa37d2017-07-26 18:11:55 +0530324 elog<InternalFailure>();
Vernon Maueryd4222fd2023-07-27 11:26:51 -0700325 return false;
Tom Joseph46fa37d2017-07-26 18:11:55 +0530326 }
Thang Trana95978d2025-06-04 10:39:15 +0700327
Vernon Maueryd4222fd2023-07-27 11:26:51 -0700328 return true;
Tom Joseph46fa37d2017-07-26 18:11:55 +0530329}
330
Vernon Maueryd4222fd2023-07-27 11:26:51 -0700331bool setPcapEnable(ipmi::Context::ptr& ctx, bool enabled)
Tom Joseph6c8d51b2017-07-26 18:18:06 +0530332{
Thang Trana95978d2025-06-04 10:39:15 +0700333 std::optional<ipmi::DbusObjectInfo> pcapObject = getPCapObject(ctx);
334
335 if (pcapObject == std::nullopt)
Tom Joseph6c8d51b2017-07-26 18:18:06 +0530336 {
Vernon Maueryd4222fd2023-07-27 11:26:51 -0700337 return false;
George Liu3e3cc352023-07-26 15:59:31 +0800338 }
Vernon Maueryd4222fd2023-07-27 11:26:51 -0700339
Thang Trana95978d2025-06-04 10:39:15 +0700340 boost::system::error_code ec =
341 ipmi::setDbusProperty(ctx, pcapObject.value().second.c_str(),
342 pcapObject.value().first.c_str(), pcapInterface,
343 powerCapEnableProp, enabled);
344
Vernon Maueryd4222fd2023-07-27 11:26:51 -0700345 if (ec.value())
George Liu3e3cc352023-07-26 15:59:31 +0800346 {
George Liu7fa28712024-07-17 19:26:23 +0800347 lg2::error("Error in setPcapEnabled property: {ERROR}", "ERROR",
348 ec.message());
Tom Joseph6c8d51b2017-07-26 18:18:06 +0530349 elog<InternalFailure>();
Vernon Maueryd4222fd2023-07-27 11:26:51 -0700350 return false;
Tom Joseph6c8d51b2017-07-26 18:18:06 +0530351 }
Vernon Maueryd4222fd2023-07-27 11:26:51 -0700352 return true;
Tom Joseph6c8d51b2017-07-26 18:18:06 +0530353}
354
Thang Trana95978d2025-06-04 10:39:15 +0700355bool setPcapExceptAction(ipmi::Context::ptr& ctx,
356 const std::string& pcapExceptAct)
357{
358 std::optional<ipmi::DbusObjectInfo> pcapObject = getPCapObject(ctx);
359
360 if (pcapObject == std::nullopt)
361 {
362 return false;
363 }
364
365 boost::system::error_code ec =
366 ipmi::setDbusProperty(ctx, pcapObject.value().second.c_str(),
367 pcapObject.value().first.c_str(), pcapInterface,
368 powerCapExceptActProp, pcapExceptAct);
369 if (ec.value())
370 {
371 lg2::error("Error in setPcapExceptAction property: {ERROR}", "ERROR",
372 ec.message());
373 elog<InternalFailure>();
374 return false;
375 }
376
377 return true;
378}
379
380bool setPcapCorrectTime(ipmi::Context::ptr& ctx, uint32_t pcapCorrectTime)
381{
382 std::optional<ipmi::DbusObjectInfo> pcapObject = getPCapObject(ctx);
383
384 if (pcapObject == std::nullopt)
385 {
386 return false;
387 }
388
389 /*
390 * Dbus is storing Correction time in microseconds unit.
391 * Therefore, we have to convert it from milisecond to microseconds.
392 */
393 uint64_t pcapCorrectTimeUs =
394 (uint64_t)(std::chrono::duration_cast<std::chrono::microseconds>(
395 std::chrono::milliseconds(pcapCorrectTime)))
396 .count();
397 boost::system::error_code ec =
398 ipmi::setDbusProperty(ctx, pcapObject.value().second.c_str(),
399 pcapObject.value().first.c_str(), pcapInterface,
400 powerCapCorrectTimeProp, pcapCorrectTimeUs);
401 if (ec.value())
402 {
403 lg2::error("Error in setPcapCorrectTime property: {ERROR}", "ERROR",
404 ec.message());
405 elog<InternalFailure>();
406 return false;
407 }
408
409 return true;
410}
411
412bool setPcapSamplPeriod(ipmi::Context::ptr& ctx, uint16_t pcapSamplPeriod)
413{
414 std::optional<ipmi::DbusObjectInfo> pcapObject = getPCapObject(ctx);
415
416 if (pcapObject == std::nullopt)
417 {
418 return false;
419 }
420
421 /*
422 * Dbus is storing Sampling periodic in microseconds unit.
423 * Therefore, we have to convert it from seconds to microseconds unit.
424 */
425 uint64_t pcapSamplPeriodUs =
426 (uint64_t)(std::chrono::duration_cast<std::chrono::microseconds>(
427 std::chrono::seconds(pcapSamplPeriod)))
428 .count();
429 boost::system::error_code ec =
430 ipmi::setDbusProperty(ctx, pcapObject.value().second.c_str(),
431 pcapObject.value().first.c_str(), pcapInterface,
432 powerCapSamplPeriodProp, pcapSamplPeriodUs);
433 if (ec.value())
434 {
435 lg2::error("Error in setPcapSamplPeriod property: {ERROR}", "ERROR",
436 ec.message());
437 elog<InternalFailure>();
438 return false;
439 }
440
441 return true;
442}
443
Vernon Mauerydca47202023-07-27 11:32:01 -0700444std::optional<std::string> readAssetTag(ipmi::Context::ptr& ctx)
Tom Josephbe5eaa12017-07-12 19:54:44 +0530445{
Tom Josephbe5eaa12017-07-12 19:54:44 +0530446 // Read the object tree with the inventory root to figure out the object
447 // that has implemented the Asset tag interface.
Vernon Mauerydca47202023-07-27 11:32:01 -0700448 ipmi::DbusObjectInfo objectInfo;
449 boost::system::error_code ec = getDbusObject(
450 ctx, dcmi::assetTagIntf, ipmi::sensor::inventoryRoot, "", objectInfo);
451 if (ec.value())
Tom Josephbe5eaa12017-07-12 19:54:44 +0530452 {
Vernon Mauerydca47202023-07-27 11:32:01 -0700453 return std::nullopt;
George Liu3e3cc352023-07-26 15:59:31 +0800454 }
Vernon Mauerydca47202023-07-27 11:32:01 -0700455
456 std::string assetTag{};
Patrick Williams1318a5e2024-08-16 15:19:54 -0400457 ec =
458 ipmi::getDbusProperty(ctx, objectInfo.second, objectInfo.first,
459 dcmi::assetTagIntf, dcmi::assetTagProp, assetTag);
Vernon Mauerydca47202023-07-27 11:32:01 -0700460 if (ec.value())
George Liu3e3cc352023-07-26 15:59:31 +0800461 {
George Liu7fa28712024-07-17 19:26:23 +0800462 lg2::error("Error in reading asset tag: {ERROR}", "ERROR",
463 ec.message());
Tom Josephbe5eaa12017-07-12 19:54:44 +0530464 elog<InternalFailure>();
Vernon Mauerydca47202023-07-27 11:32:01 -0700465 return std::nullopt;
Tom Josephbe5eaa12017-07-12 19:54:44 +0530466 }
Vernon Mauerydca47202023-07-27 11:32:01 -0700467
468 return assetTag;
Tom Josephbe5eaa12017-07-12 19:54:44 +0530469}
470
Vernon Mauerydca47202023-07-27 11:32:01 -0700471bool writeAssetTag(ipmi::Context::ptr& ctx, const std::string& assetTag)
Tom Josephbe5b9892017-07-15 00:55:23 +0530472{
Tom Josephbe5b9892017-07-15 00:55:23 +0530473 // Read the object tree with the inventory root to figure out the object
474 // that has implemented the Asset tag interface.
Vernon Mauerydca47202023-07-27 11:32:01 -0700475 ipmi::DbusObjectInfo objectInfo;
476 boost::system::error_code ec = getDbusObject(
477 ctx, dcmi::assetTagIntf, ipmi::sensor::inventoryRoot, "", objectInfo);
478 if (ec.value())
Tom Josephbe5b9892017-07-15 00:55:23 +0530479 {
Vernon Mauerydca47202023-07-27 11:32:01 -0700480 return false;
George Liu3e3cc352023-07-26 15:59:31 +0800481 }
Vernon Mauerydca47202023-07-27 11:32:01 -0700482
Patrick Williams1318a5e2024-08-16 15:19:54 -0400483 ec =
484 ipmi::setDbusProperty(ctx, objectInfo.second, objectInfo.first,
485 dcmi::assetTagIntf, dcmi::assetTagProp, assetTag);
Vernon Mauerydca47202023-07-27 11:32:01 -0700486 if (ec.value())
George Liu3e3cc352023-07-26 15:59:31 +0800487 {
George Liu7fa28712024-07-17 19:26:23 +0800488 lg2::error("Error in writing asset tag: {ERROR}", "ERROR",
489 ec.message());
Tom Josephbe5b9892017-07-15 00:55:23 +0530490 elog<InternalFailure>();
Vernon Mauerydca47202023-07-27 11:32:01 -0700491 return false;
Tom Josephbe5b9892017-07-15 00:55:23 +0530492 }
Vernon Mauerydca47202023-07-27 11:32:01 -0700493 return true;
Tom Josephbe5b9892017-07-15 00:55:23 +0530494}
495
Vernon Maueryefb5ae52023-07-27 11:35:43 -0700496std::optional<std::string> getHostName(ipmi::Context::ptr& ctx)
Vladislav Vovchenko8f7a6f62017-08-17 00:31:14 +0300497{
Vernon Maueryefb5ae52023-07-27 11:35:43 -0700498 std::string service{};
Patrick Williams1318a5e2024-08-16 15:19:54 -0400499 boost::system::error_code ec =
500 ipmi::getService(ctx, networkConfigIntf, networkConfigObj, service);
Vernon Maueryefb5ae52023-07-27 11:35:43 -0700501 if (ec.value())
502 {
503 return std::nullopt;
504 }
505 std::string hostname{};
506 ec = ipmi::getDbusProperty(ctx, service, networkConfigObj,
507 networkConfigIntf, hostNameProp, hostname);
508 if (ec.value())
509 {
George Liu7fa28712024-07-17 19:26:23 +0800510 lg2::error("Error fetching hostname");
Vernon Maueryefb5ae52023-07-27 11:35:43 -0700511 elog<InternalFailure>();
512 return std::nullopt;
513 }
514 return hostname;
Vladislav Vovchenko8f7a6f62017-08-17 00:31:14 +0300515}
516
Patrick Williams69b4c282025-03-03 11:19:13 -0500517std::optional<EthernetInterface::DHCPConf> getDHCPEnabled(
518 ipmi::Context::ptr& ctx)
Nagaraju Goruganti22be97b2018-02-07 01:19:59 -0600519{
Johnathan Mantey74a21022018-12-13 13:17:56 -0800520 auto ethdevice = ipmi::getChannelName(ethernetDefaultChannelNum);
Prithvi Pai1dd18d22025-09-19 14:27:03 +0530521 if (ethdevice.empty())
522 {
523 lg2::error("Channel name does not exist for channel {CHANNEL}",
524 "CHANNEL", ethernetDefaultChannelNum);
525 return std::nullopt;
526 }
Vernon Mauery6475b5c2023-07-27 14:52:51 -0700527 ipmi::DbusObjectInfo ethernetObj{};
528 boost::system::error_code ec = ipmi::getDbusObject(
529 ctx, ethernetIntf, networkRoot, ethdevice, ethernetObj);
530 if (ec.value())
Deepak Kodihalli0b459552018-02-06 06:25:12 -0600531 {
Vernon Mauery6475b5c2023-07-27 14:52:51 -0700532 return std::nullopt;
533 }
534 std::string service{};
535 ec = ipmi::getService(ctx, ethernetIntf, ethernetObj.first, service);
536 if (ec.value())
537 {
538 return std::nullopt;
539 }
540 std::string dhcpVal{};
541 ec = ipmi::getDbusProperty(ctx, service, ethernetObj.first, ethernetIntf,
542 "DHCPEnabled", dhcpVal);
543 if (ec.value())
544 {
545 return std::nullopt;
Deepak Kodihalli0b459552018-02-06 06:25:12 -0600546 }
547
Vernon Mauery6475b5c2023-07-27 14:52:51 -0700548 return EthernetInterface::convertDHCPConfFromString(dhcpVal);
549}
550
551std::optional<bool> getDHCPOption(ipmi::Context::ptr& ctx,
552 const std::string& prop)
553{
George Liucfd7fa82024-01-22 11:38:29 +0800554 ipmi::ObjectTree objectTree;
555 if (ipmi::getAllDbusObjects(ctx, networkRoot, dhcpIntf, objectTree))
Vernon Mauery6475b5c2023-07-27 14:52:51 -0700556 {
557 return std::nullopt;
Deepak Kodihalli0b459552018-02-06 06:25:12 -0600558 }
559
George Liucfd7fa82024-01-22 11:38:29 +0800560 for (const auto& [path, serviceMap] : objectTree)
561 {
562 for (const auto& [service, object] : serviceMap)
563 {
564 bool value{};
565 if (ipmi::getDbusProperty(ctx, service, path, dhcpIntf, prop,
566 value))
567 {
568 return std::nullopt;
569 }
570
571 if (value)
572 {
573 return true;
574 }
575 }
576 }
577
578 return false;
Vernon Mauery6475b5c2023-07-27 14:52:51 -0700579}
580
581bool setDHCPOption(ipmi::Context::ptr& ctx, std::string prop, bool value)
582{
George Liucfd7fa82024-01-22 11:38:29 +0800583 ipmi::ObjectTree objectTree;
584 if (ipmi::getAllDbusObjects(ctx, networkRoot, dhcpIntf, objectTree))
Vernon Mauery6475b5c2023-07-27 14:52:51 -0700585 {
George Liucfd7fa82024-01-22 11:38:29 +0800586 return false;
Vernon Mauery6475b5c2023-07-27 14:52:51 -0700587 }
George Liucfd7fa82024-01-22 11:38:29 +0800588
589 for (const auto& [path, serviceMap] : objectTree)
590 {
591 for (const auto& [service, object] : serviceMap)
592 {
593 if (ipmi::setDbusProperty(ctx, service, path, dhcpIntf, prop,
594 value))
595 {
596 return false;
597 }
598 }
599 }
600
601 return true;
Deepak Kodihalli0b459552018-02-06 06:25:12 -0600602}
603
Tom Josephbe5eaa12017-07-12 19:54:44 +0530604} // namespace dcmi
Chris Austen1810bec2015-10-13 12:12:39 -0500605
Vernon Maueryd4222fd2023-07-27 11:26:51 -0700606ipmi::RspType<uint16_t, // reserved
607 uint8_t, // exception actions
608 uint16_t, // power limit requested in watts
609 uint32_t, // correction time in milliseconds
610 uint16_t, // reserved
611 uint16_t // statistics sampling period in seconds
612 >
613 getPowerLimit(ipmi::Context::ptr ctx, uint16_t reserved)
Tom Josephb9d86f42017-07-26 18:03:47 +0530614{
Kirill Pakhomov2c2af2c2018-11-06 16:06:10 +0300615 if (!dcmi::isDCMIPowerMgmtSupported())
616 {
Vernon Maueryd4222fd2023-07-27 11:26:51 -0700617 return ipmi::responseInvalidCommand();
Kirill Pakhomov2c2af2c2018-11-06 16:06:10 +0300618 }
Vernon Maueryd4222fd2023-07-27 11:26:51 -0700619 if (reserved)
Tom Josephb9d86f42017-07-26 18:03:47 +0530620 {
Vernon Maueryd4222fd2023-07-27 11:26:51 -0700621 return ipmi::responseInvalidFieldRequest();
Tom Josephb9d86f42017-07-26 18:03:47 +0530622 }
623
Vernon Maueryd4222fd2023-07-27 11:26:51 -0700624 std::optional<uint16_t> pcapValue = dcmi::getPcap(ctx);
625 std::optional<bool> pcapEnable = dcmi::getPcapEnabled(ctx);
Thang Trana95978d2025-06-04 10:39:15 +0700626 std::optional<uint32_t> pcapCorrectTime = dcmi::getPcapCorrectTime(ctx);
627 std::optional<uint16_t> pcapSamplPeriod = dcmi::getPcapSamplPeriod(ctx);
628 std::optional<std::string> pcapExceptAct = dcmi::getPcapExceptAction(ctx);
629
630 if (!pcapValue || !pcapEnable || !pcapCorrectTime || !pcapSamplPeriod ||
631 !pcapExceptAct)
Vernon Maueryd4222fd2023-07-27 11:26:51 -0700632 {
633 return ipmi::responseUnspecifiedError();
634 }
635
636 constexpr uint16_t reserved1{};
637 constexpr uint16_t reserved2{};
Thang Trana95978d2025-06-04 10:39:15 +0700638 uint8_t exception;
639
640 std::string exceptAct = pcapExceptAct.value();
641
642 if (exceptAct == dcmi::DbusExceptAct::noAction)
643 {
644 exception = static_cast<uint8_t>(dcmi::ExceptActOptions::NoAction);
645 }
646 else if (exceptAct == dcmi::DbusExceptAct::hardPowerOff)
647 {
648 exception = static_cast<uint8_t>(dcmi::ExceptActOptions::HardPowerOff);
649 }
650 else if (exceptAct == dcmi::DbusExceptAct::logEventOnly)
651 {
652 exception = static_cast<uint8_t>(dcmi::ExceptActOptions::LogEventOnly);
653 }
654 else if (exceptAct == dcmi::DbusExceptAct::oem)
655 {
656 exception = static_cast<uint8_t>(dcmi::ExceptActOptions::Oem02);
657 }
658 else
659 {
660 return ipmi::responseUnspecifiedError();
661 }
662
663 if (!pcapEnable.value())
Tom Josephb9d86f42017-07-26 18:03:47 +0530664 {
Vernon Maueryd4222fd2023-07-27 11:26:51 -0700665 constexpr ipmi::Cc responseNoPowerLimitSet = 0x80;
Vernon Maueryd4222fd2023-07-27 11:26:51 -0700666 return ipmi::response(responseNoPowerLimitSet, reserved1, exception,
Thang Trana95978d2025-06-04 10:39:15 +0700667 pcapValue.value(), pcapCorrectTime.value(),
668 reserved2, pcapSamplPeriod.value());
Tom Josephb9d86f42017-07-26 18:03:47 +0530669 }
Thang Trana95978d2025-06-04 10:39:15 +0700670
671 return ipmi::responseSuccess(reserved1, exception, pcapValue.value(),
672 pcapCorrectTime.value(), reserved2,
673 pcapSamplPeriod.value());
Tom Josephb9d86f42017-07-26 18:03:47 +0530674}
675
Vernon Maueryd4222fd2023-07-27 11:26:51 -0700676ipmi::RspType<> setPowerLimit(ipmi::Context::ptr& ctx, uint16_t reserved1,
Thang Tranfd9c3612023-09-20 11:16:59 +0700677 uint8_t reserved2, uint8_t exceptionAction,
678 uint16_t powerLimit, uint32_t correctionTime,
679 uint16_t reserved3, uint16_t statsPeriod)
Tom Joseph46fa37d2017-07-26 18:11:55 +0530680{
Kirill Pakhomov2c2af2c2018-11-06 16:06:10 +0300681 if (!dcmi::isDCMIPowerMgmtSupported())
682 {
George Liu7fa28712024-07-17 19:26:23 +0800683 lg2::error("DCMI Power management is unsupported!");
Vernon Maueryd4222fd2023-07-27 11:26:51 -0700684 return ipmi::responseInvalidCommand();
Kirill Pakhomov2c2af2c2018-11-06 16:06:10 +0300685 }
686
Thang Trana95978d2025-06-04 10:39:15 +0700687 if (reserved1 || reserved2 || reserved3)
Tom Joseph46fa37d2017-07-26 18:11:55 +0530688 {
Vernon Maueryd4222fd2023-07-27 11:26:51 -0700689 return ipmi::responseInvalidFieldRequest();
Tom Joseph46fa37d2017-07-26 18:11:55 +0530690 }
Vernon Maueryd4222fd2023-07-27 11:26:51 -0700691
692 if (!dcmi::setPcap(ctx, powerLimit))
Tom Joseph46fa37d2017-07-26 18:11:55 +0530693 {
Vernon Maueryd4222fd2023-07-27 11:26:51 -0700694 return ipmi::responseUnspecifiedError();
Tom Joseph46fa37d2017-07-26 18:11:55 +0530695 }
696
Thang Trana95978d2025-06-04 10:39:15 +0700697 /*
698 * As defined in table 6-18 of DCMI specification version 1.5.
699 * The Exception action value is mapped as below
700 * 00h - No Action
701 * 01h - Hard Power Off system and log events to SEL
702 * 02h - 10h OEM defined actions
703 * 11h - Log event to SEL only
704 * 12h-FFh Reserved
705 */
706 if (exceptionAction >= 0x12)
707 {
708 return ipmi::responseUnspecifiedError();
709 }
710
711 std::string exceptActStr;
712
713 switch (static_cast<dcmi::ExceptActOptions>(exceptionAction))
714 {
715 case dcmi::ExceptActOptions::NoAction:
716 {
717 exceptActStr = dcmi::DbusExceptAct::noAction;
718 break;
719 }
720 case dcmi::ExceptActOptions::HardPowerOff:
721 {
722 exceptActStr = dcmi::DbusExceptAct::hardPowerOff;
723 break;
724 }
725 case dcmi::ExceptActOptions::LogEventOnly:
726 {
727 exceptActStr = dcmi::DbusExceptAct::logEventOnly;
728 break;
729 }
730 default:
731 {
732 exceptActStr = dcmi::DbusExceptAct::oem;
733 break;
734 }
735 }
736
737 if (!dcmi::setPcapExceptAction(ctx, exceptActStr))
738 {
739 return ipmi::responseUnspecifiedError();
740 }
741
742 if (!dcmi::setPcapCorrectTime(ctx, correctionTime))
743 {
744 return ipmi::responseUnspecifiedError();
745 }
746
747 if (!dcmi::setPcapSamplPeriod(ctx, statsPeriod))
748 {
749 return ipmi::responseUnspecifiedError();
750 }
Tom Joseph46fa37d2017-07-26 18:11:55 +0530751
Vernon Maueryd4222fd2023-07-27 11:26:51 -0700752 return ipmi::responseSuccess();
Tom Joseph46fa37d2017-07-26 18:11:55 +0530753}
754
Vernon Maueryd4222fd2023-07-27 11:26:51 -0700755ipmi::RspType<> applyPowerLimit(ipmi::Context::ptr& ctx, bool enabled,
756 uint7_t reserved1, uint16_t reserved2)
Tom Joseph6c8d51b2017-07-26 18:18:06 +0530757{
Kirill Pakhomov2c2af2c2018-11-06 16:06:10 +0300758 if (!dcmi::isDCMIPowerMgmtSupported())
759 {
George Liu7fa28712024-07-17 19:26:23 +0800760 lg2::error("DCMI Power management is unsupported!");
Vernon Maueryd4222fd2023-07-27 11:26:51 -0700761 return ipmi::responseInvalidCommand();
762 }
763 if (reserved1 || reserved2)
764 {
765 return ipmi::responseInvalidFieldRequest();
Kirill Pakhomov2c2af2c2018-11-06 16:06:10 +0300766 }
767
Vernon Maueryd4222fd2023-07-27 11:26:51 -0700768 if (!dcmi::setPcapEnable(ctx, enabled))
Tom Joseph6c8d51b2017-07-26 18:18:06 +0530769 {
Vernon Maueryd4222fd2023-07-27 11:26:51 -0700770 return ipmi::responseUnspecifiedError();
Tom Joseph6c8d51b2017-07-26 18:18:06 +0530771 }
772
George Liu7fa28712024-07-17 19:26:23 +0800773 lg2::info("Set Power Cap Enable: {POWERCAPENABLE}", "POWERCAPENABLE",
774 enabled);
Tom Joseph6c8d51b2017-07-26 18:18:06 +0530775
Vernon Maueryd4222fd2023-07-27 11:26:51 -0700776 return ipmi::responseSuccess();
Tom Joseph6c8d51b2017-07-26 18:18:06 +0530777}
778
Vernon Mauerydca47202023-07-27 11:32:01 -0700779ipmi::RspType<uint8_t, // total tag length
780 std::vector<char> // tag data
781 >
782 getAssetTag(ipmi::Context::ptr& ctx, uint8_t offset, uint8_t count)
Tom Joseph6f6dd4d2017-07-12 20:07:11 +0530783{
Vernon Mauerydca47202023-07-27 11:32:01 -0700784 // Verify offset to read and number of bytes to read are not exceeding
785 // the range.
786 if ((offset > dcmi::assetTagMaxOffset) || (count > dcmi::maxBytes) ||
787 ((offset + count) > dcmi::assetTagMaxSize))
Tom Joseph6f6dd4d2017-07-12 20:07:11 +0530788 {
Vernon Mauerydca47202023-07-27 11:32:01 -0700789 return ipmi::responseParmOutOfRange();
Tom Joseph6f6dd4d2017-07-12 20:07:11 +0530790 }
791
Vernon Mauerydca47202023-07-27 11:32:01 -0700792 std::optional<std::string> assetTagResp = dcmi::readAssetTag(ctx);
793 if (!assetTagResp)
Tom Joseph6f6dd4d2017-07-12 20:07:11 +0530794 {
Vernon Mauerydca47202023-07-27 11:32:01 -0700795 return ipmi::responseUnspecifiedError();
Tom Joseph6f6dd4d2017-07-12 20:07:11 +0530796 }
797
Vernon Mauerydca47202023-07-27 11:32:01 -0700798 std::string& assetTag = assetTagResp.value();
799 // If the asset tag is longer than 63 bytes, restrict it to 63 bytes to
800 // suit Get Asset Tag command.
Tom Joseph6f6dd4d2017-07-12 20:07:11 +0530801 if (assetTag.size() > dcmi::assetTagMaxSize)
802 {
803 assetTag.resize(dcmi::assetTagMaxSize);
804 }
805
Vernon Mauerydca47202023-07-27 11:32:01 -0700806 if (offset >= assetTag.size())
Tom Joseph6f6dd4d2017-07-12 20:07:11 +0530807 {
Vernon Mauerydca47202023-07-27 11:32:01 -0700808 return ipmi::responseParmOutOfRange();
Tom Joseph6f6dd4d2017-07-12 20:07:11 +0530809 }
810
Vernon Mauerydca47202023-07-27 11:32:01 -0700811 // silently truncate reads beyond the end of assetTag
812 if ((offset + count) >= assetTag.size())
813 {
814 count = assetTag.size() - offset;
815 }
Tom Joseph6f6dd4d2017-07-12 20:07:11 +0530816
Vernon Mauerydca47202023-07-27 11:32:01 -0700817 auto totalTagSize = static_cast<uint8_t>(assetTag.size());
818 std::vector<char> data{assetTag.begin() + offset,
819 assetTag.begin() + offset + count};
Tom Joseph6f6dd4d2017-07-12 20:07:11 +0530820
Vernon Mauerydca47202023-07-27 11:32:01 -0700821 return ipmi::responseSuccess(totalTagSize, data);
Tom Joseph6f6dd4d2017-07-12 20:07:11 +0530822}
823
Vernon Mauerydca47202023-07-27 11:32:01 -0700824ipmi::RspType<uint8_t // new asset tag length
825 >
826 setAssetTag(ipmi::Context::ptr& ctx, uint8_t offset, uint8_t count,
827 const std::vector<char>& data)
Tom Joseph545dd232017-07-12 20:20:49 +0530828{
Vernon Mauerydca47202023-07-27 11:32:01 -0700829 // Verify offset to read and number of bytes to read are not exceeding
830 // the range.
831 if ((offset > dcmi::assetTagMaxOffset) || (count > dcmi::maxBytes) ||
832 ((offset + count) > dcmi::assetTagMaxSize))
Tom Joseph545dd232017-07-12 20:20:49 +0530833 {
Vernon Mauerydca47202023-07-27 11:32:01 -0700834 return ipmi::responseParmOutOfRange();
835 }
836 if (data.size() != count)
837 {
838 return ipmi::responseReqDataLenInvalid();
Tom Joseph545dd232017-07-12 20:20:49 +0530839 }
840
Vernon Mauerydca47202023-07-27 11:32:01 -0700841 std::optional<std::string> assetTagResp = dcmi::readAssetTag(ctx);
842 if (!assetTagResp)
Tom Joseph545dd232017-07-12 20:20:49 +0530843 {
Vernon Mauerydca47202023-07-27 11:32:01 -0700844 return ipmi::responseUnspecifiedError();
Tom Joseph545dd232017-07-12 20:20:49 +0530845 }
Vernon Mauerydca47202023-07-27 11:32:01 -0700846
847 std::string& assetTag = assetTagResp.value();
848
849 if (offset > assetTag.size())
Tom Joseph545dd232017-07-12 20:20:49 +0530850 {
Vernon Mauerydca47202023-07-27 11:32:01 -0700851 return ipmi::responseParmOutOfRange();
Tom Joseph545dd232017-07-12 20:20:49 +0530852 }
Vernon Mauerydca47202023-07-27 11:32:01 -0700853
854 // operation is to truncate at offset and append new data
855 assetTag.resize(offset);
856 assetTag.append(data.begin(), data.end());
857
858 if (!dcmi::writeAssetTag(ctx, assetTag))
859 {
860 return ipmi::responseUnspecifiedError();
861 }
862
863 auto totalTagSize = static_cast<uint8_t>(assetTag.size());
864 return ipmi::responseSuccess(totalTagSize);
Tom Joseph545dd232017-07-12 20:20:49 +0530865}
866
Vernon Maueryefb5ae52023-07-27 11:35:43 -0700867ipmi::RspType<uint8_t, // length
868 std::vector<char> // data
869 >
870 getMgmntCtrlIdStr(ipmi::Context::ptr& ctx, uint8_t offset, uint8_t count)
Vladislav Vovchenko8f7a6f62017-08-17 00:31:14 +0300871{
Vernon Maueryefb5ae52023-07-27 11:35:43 -0700872 if (count > dcmi::maxBytes || offset + count > dcmi::maxCtrlIdStrLen)
Vladislav Vovchenko8f7a6f62017-08-17 00:31:14 +0300873 {
Vernon Maueryefb5ae52023-07-27 11:35:43 -0700874 return ipmi::responseParmOutOfRange();
Vladislav Vovchenko8f7a6f62017-08-17 00:31:14 +0300875 }
876
Vernon Maueryefb5ae52023-07-27 11:35:43 -0700877 std::optional<std::string> hostnameResp = dcmi::getHostName(ctx);
878 if (!hostnameResp)
Vladislav Vovchenko8f7a6f62017-08-17 00:31:14 +0300879 {
Vernon Maueryefb5ae52023-07-27 11:35:43 -0700880 return ipmi::responseUnspecifiedError();
Vladislav Vovchenko8f7a6f62017-08-17 00:31:14 +0300881 }
882
Vernon Maueryefb5ae52023-07-27 11:35:43 -0700883 std::string& hostname = hostnameResp.value();
884 // If the id string is longer than 63 bytes, restrict it to 63 bytes to
885 // suit set management ctrl str command.
886 if (hostname.size() > dcmi::maxCtrlIdStrLen)
Vladislav Vovchenko8f7a6f62017-08-17 00:31:14 +0300887 {
Vernon Maueryefb5ae52023-07-27 11:35:43 -0700888 hostname.resize(dcmi::maxCtrlIdStrLen);
Vladislav Vovchenko8f7a6f62017-08-17 00:31:14 +0300889 }
Vladislav Vovchenko8f7a6f62017-08-17 00:31:14 +0300890
Vernon Maueryefb5ae52023-07-27 11:35:43 -0700891 if (offset >= hostname.size())
892 {
893 return ipmi::responseParmOutOfRange();
894 }
895
896 // silently truncate reads beyond the end of hostname
897 if ((offset + count) >= hostname.size())
898 {
899 count = hostname.size() - offset;
900 }
901
902 auto nameSize = static_cast<uint8_t>(hostname.size());
903 std::vector<char> data{hostname.begin() + offset,
904 hostname.begin() + offset + count};
905
906 return ipmi::responseSuccess(nameSize, data);
Vladislav Vovchenko8f7a6f62017-08-17 00:31:14 +0300907}
908
Patrick Williams69b4c282025-03-03 11:19:13 -0500909ipmi::RspType<uint8_t> setMgmntCtrlIdStr(ipmi::Context::ptr& ctx,
910 uint8_t offset, uint8_t count,
911 std::vector<char> data)
Vladislav Vovchenko8f7a6f62017-08-17 00:31:14 +0300912{
Vernon Maueryefb5ae52023-07-27 11:35:43 -0700913 if ((offset > dcmi::maxCtrlIdStrLen) || (count > dcmi::maxBytes) ||
914 ((offset + count) > dcmi::maxCtrlIdStrLen))
Vladislav Vovchenko8f7a6f62017-08-17 00:31:14 +0300915 {
Vernon Maueryefb5ae52023-07-27 11:35:43 -0700916 return ipmi::responseParmOutOfRange();
917 }
918 if (data.size() != count)
919 {
920 return ipmi::responseReqDataLenInvalid();
921 }
922 bool terminalWrite{data.back() == '\0'};
923 if (terminalWrite)
924 {
925 // remove the null termination from the data (no need with std::string)
926 data.resize(count - 1);
Vladislav Vovchenko8f7a6f62017-08-17 00:31:14 +0300927 }
928
Vernon Maueryefb5ae52023-07-27 11:35:43 -0700929 static std::string hostname{};
930 // read in the current value if not starting at offset 0
931 if (hostname.size() == 0 && offset != 0)
Vladislav Vovchenko8f7a6f62017-08-17 00:31:14 +0300932 {
Vernon Maueryefb5ae52023-07-27 11:35:43 -0700933 /* read old ctrlIdStr */
934 std::optional<std::string> hostnameResp = dcmi::getHostName(ctx);
935 if (!hostnameResp)
Vladislav Vovchenko8f7a6f62017-08-17 00:31:14 +0300936 {
Vernon Maueryefb5ae52023-07-27 11:35:43 -0700937 return ipmi::responseUnspecifiedError();
Vladislav Vovchenko8f7a6f62017-08-17 00:31:14 +0300938 }
Vernon Maueryefb5ae52023-07-27 11:35:43 -0700939 hostname = hostnameResp.value();
940 hostname.resize(offset);
Vladislav Vovchenko8f7a6f62017-08-17 00:31:14 +0300941 }
942
Vernon Maueryefb5ae52023-07-27 11:35:43 -0700943 // operation is to truncate at offset and append new data
944 hostname.append(data.begin(), data.end());
945
946 // do the update if this is the last write
947 if (terminalWrite)
948 {
949 boost::system::error_code ec = ipmi::setDbusProperty(
950 ctx, dcmi::networkServiceName, dcmi::networkConfigObj,
951 dcmi::networkConfigIntf, dcmi::hostNameProp, hostname);
952 hostname.clear();
953 if (ec.value())
954 {
955 return ipmi::responseUnspecifiedError();
956 }
957 }
958
959 auto totalIdSize = static_cast<uint8_t>(offset + count);
960 return ipmi::responseSuccess(totalIdSize);
Vladislav Vovchenko8f7a6f62017-08-17 00:31:14 +0300961}
962
Vernon Maueryf038dc02023-07-27 14:06:11 -0700963ipmi::RspType<ipmi::message::Payload> getDCMICapabilities(uint8_t parameter)
Dhruvaraj Subhashchandrane29be412018-01-16 05:11:56 -0600964{
Kirill Pakhomova2573622018-11-02 19:00:18 +0300965 std::ifstream dcmiCapFile(dcmi::gDCMICapabilitiesConfig);
Dhruvaraj Subhashchandrane29be412018-01-16 05:11:56 -0600966 if (!dcmiCapFile.is_open())
967 {
George Liu7fa28712024-07-17 19:26:23 +0800968 lg2::error("DCMI Capabilities file not found");
Vernon Maueryf038dc02023-07-27 14:06:11 -0700969 return ipmi::responseUnspecifiedError();
Dhruvaraj Subhashchandrane29be412018-01-16 05:11:56 -0600970 }
971
972 auto data = nlohmann::json::parse(dcmiCapFile, nullptr, false);
973 if (data.is_discarded())
974 {
George Liu7fa28712024-07-17 19:26:23 +0800975 lg2::error("DCMI Capabilities JSON parser failure");
Vernon Maueryf038dc02023-07-27 14:06:11 -0700976 return ipmi::responseUnspecifiedError();
Dhruvaraj Subhashchandrane29be412018-01-16 05:11:56 -0600977 }
978
Vernon Maueryf038dc02023-07-27 14:06:11 -0700979 constexpr bool reserved1{};
980 constexpr uint5_t reserved5{};
981 constexpr uint7_t reserved7{};
982 constexpr uint8_t reserved8{};
983 constexpr uint16_t reserved16{};
Dhruvaraj Subhashchandrane29be412018-01-16 05:11:56 -0600984
Vernon Maueryf038dc02023-07-27 14:06:11 -0700985 ipmi::message::Payload payload;
986 payload.pack(dcmi::specMajorVersion, dcmi::specMinorVersion,
987 dcmi::parameterRevision);
988
989 enum class DCMICapParameters : uint8_t
Dhruvaraj Subhashchandrane29be412018-01-16 05:11:56 -0600990 {
Vernon Maueryf038dc02023-07-27 14:06:11 -0700991 SupportedDcmiCaps = 0x01, // Supported DCMI Capabilities
992 MandatoryPlatAttributes = 0x02, // Mandatory Platform Attributes
993 OptionalPlatAttributes = 0x03, // Optional Platform Attributes
994 ManageabilityAccessAttributes = 0x04, // Manageability Access Attributes
995 };
Dhruvaraj Subhashchandrane29be412018-01-16 05:11:56 -0600996
Vernon Maueryf038dc02023-07-27 14:06:11 -0700997 switch (static_cast<DCMICapParameters>(parameter))
Dhruvaraj Subhashchandrane29be412018-01-16 05:11:56 -0600998 {
Vernon Maueryf038dc02023-07-27 14:06:11 -0700999 case DCMICapParameters::SupportedDcmiCaps:
Dhruvaraj Subhashchandrane29be412018-01-16 05:11:56 -06001000 {
Vernon Maueryf038dc02023-07-27 14:06:11 -07001001 bool powerManagement = data.value("PowerManagement", 0);
1002 bool oobSecondaryLan = data.value("OOBSecondaryLan", 0);
1003 bool serialTMode = data.value("SerialTMODE", 0);
1004 bool inBandSystemInterfaceChannel =
1005 data.value("InBandSystemInterfaceChannel", 0);
1006 payload.pack(reserved8, powerManagement, reserved7,
1007 inBandSystemInterfaceChannel, serialTMode,
1008 oobSecondaryLan, reserved5);
1009 break;
Dhruvaraj Subhashchandrane29be412018-01-16 05:11:56 -06001010 }
Vernon Maueryf038dc02023-07-27 14:06:11 -07001011 // Mandatory Platform Attributes
1012 case DCMICapParameters::MandatoryPlatAttributes:
Dhruvaraj Subhashchandrane29be412018-01-16 05:11:56 -06001013 {
Vernon Maueryf038dc02023-07-27 14:06:11 -07001014 bool selAutoRollOver = data.value("SELAutoRollOver", 0);
1015 bool flushEntireSELUponRollOver =
1016 data.value("FlushEntireSELUponRollOver", 0);
1017 bool recordLevelSELFlushUponRollOver =
1018 data.value("RecordLevelSELFlushUponRollOver", 0);
Patrick Williams1318a5e2024-08-16 15:19:54 -04001019 uint12_t numberOfSELEntries =
1020 data.value("NumberOfSELEntries", 0xcac);
Vernon Maueryf038dc02023-07-27 14:06:11 -07001021 uint8_t tempMonitoringSamplingFreq =
1022 data.value("TempMonitoringSamplingFreq", 0);
1023 payload.pack(numberOfSELEntries, reserved1,
1024 recordLevelSELFlushUponRollOver,
1025 flushEntireSELUponRollOver, selAutoRollOver,
1026 reserved16, tempMonitoringSamplingFreq);
1027 break;
1028 }
1029 // Optional Platform Attributes
1030 case DCMICapParameters::OptionalPlatAttributes:
1031 {
Matt Simmering68d9d402023-11-09 14:22:11 -08001032 uint7_t powerMgmtDeviceTargetAddress =
Vernon Maueryf038dc02023-07-27 14:06:11 -07001033 data.value("PowerMgmtDeviceSlaveAddress", 0);
1034 uint4_t bmcChannelNumber = data.value("BMCChannelNumber", 0);
1035 uint4_t deviceRivision = data.value("DeviceRivision", 0);
Matt Simmering68d9d402023-11-09 14:22:11 -08001036 payload.pack(powerMgmtDeviceTargetAddress, reserved1,
1037 deviceRivision, bmcChannelNumber);
Vernon Maueryf038dc02023-07-27 14:06:11 -07001038 break;
1039 }
1040 // Manageability Access Attributes
1041 case DCMICapParameters::ManageabilityAccessAttributes:
1042 {
1043 uint8_t mandatoryPrimaryLanOOBSupport =
1044 data.value("MandatoryPrimaryLanOOBSupport", 0xff);
1045 uint8_t optionalSecondaryLanOOBSupport =
1046 data.value("OptionalSecondaryLanOOBSupport", 0xff);
1047 uint8_t optionalSerialOOBMTMODECapability =
1048 data.value("OptionalSerialOOBMTMODECapability", 0xff);
1049 payload.pack(mandatoryPrimaryLanOOBSupport,
1050 optionalSecondaryLanOOBSupport,
1051 optionalSerialOOBMTMODECapability);
1052 break;
1053 }
1054 default:
1055 {
George Liu7fa28712024-07-17 19:26:23 +08001056 lg2::error("Invalid input parameter");
Vernon Maueryf038dc02023-07-27 14:06:11 -07001057 return ipmi::responseInvalidFieldRequest();
Dhruvaraj Subhashchandrane29be412018-01-16 05:11:56 -06001058 }
1059 }
1060
Vernon Maueryf038dc02023-07-27 14:06:11 -07001061 return ipmi::responseSuccess(payload);
Dhruvaraj Subhashchandrane29be412018-01-16 05:11:56 -06001062}
1063
Deepak Kodihalliee717d72018-01-24 04:53:09 -06001064namespace dcmi
1065{
1066namespace temp_readings
1067{
1068
Patrick Williams69b4c282025-03-03 11:19:13 -05001069std::tuple<bool, bool, uint8_t> readTemp(ipmi::Context::ptr& ctx,
1070 const std::string& dbusService,
1071 const std::string& dbusPath)
Deepak Kodihallib1e8fba2018-01-24 04:57:10 -06001072{
1073 // Read the temperature value from d-bus object. Need some conversion.
Vernon Mauerycce9ffd2023-07-27 14:15:17 -07001074 // As per the interface xyz.openbmc_project.Sensor.Value, the
1075 // temperature is an double and in degrees C. It needs to be scaled by
1076 // using the formula Value * 10^Scale. The ipmi spec has the temperature
1077 // as a uint8_t, with a separate single bit for the sign.
Deepak Kodihallib1e8fba2018-01-24 04:57:10 -06001078
Vernon Mauerycce9ffd2023-07-27 14:15:17 -07001079 ipmi::PropertyMap result{};
1080 boost::system::error_code ec = ipmi::getAllDbusProperties(
Alexander Hansenf365c7f2025-11-14 12:08:34 +01001081 ctx, dbusService, dbusPath, SensorValue::interface, result);
Vernon Mauerycce9ffd2023-07-27 14:15:17 -07001082 if (ec.value())
1083 {
1084 return std::make_tuple(false, false, 0);
1085 }
Patrick Williams1318a5e2024-08-16 15:19:54 -04001086 auto temperature =
Alexander Hansenf365c7f2025-11-14 12:08:34 +01001087 std::visit(ipmi::VariantToDoubleVisitor(),
1088 result.at(SensorValue::property_names::value));
James Feist9cc0ea52018-10-09 10:53:11 -07001089 double absTemp = std::abs(temperature);
Deepak Kodihallib1e8fba2018-01-24 04:57:10 -06001090
James Feist9cc0ea52018-10-09 10:53:11 -07001091 auto findFactor = result.find("Scale");
1092 double factor = 0.0;
1093 if (findFactor != result.end())
Deepak Kodihallib1e8fba2018-01-24 04:57:10 -06001094 {
Vernon Maueryf442e112019-04-09 11:44:36 -07001095 factor = std::visit(ipmi::VariantToDoubleVisitor(), findFactor->second);
Deepak Kodihallib1e8fba2018-01-24 04:57:10 -06001096 }
James Feist9cc0ea52018-10-09 10:53:11 -07001097 double scale = std::pow(10, factor);
1098
1099 auto tempDegrees = absTemp * scale;
Vernon Mauerycce9ffd2023-07-27 14:15:17 -07001100 // Max absolute temp as per ipmi spec is 127.
1101 constexpr auto maxTemp = 127;
Deepak Kodihallib1e8fba2018-01-24 04:57:10 -06001102 if (tempDegrees > maxTemp)
1103 {
1104 tempDegrees = maxTemp;
1105 }
1106
Vernon Mauerycce9ffd2023-07-27 14:15:17 -07001107 return std::make_tuple(true, (temperature < 0),
1108 static_cast<uint8_t>(tempDegrees));
Deepak Kodihallib1e8fba2018-01-24 04:57:10 -06001109}
1110
Patrick Williams69b4c282025-03-03 11:19:13 -05001111std::tuple<std::vector<std::tuple<uint7_t, bool, uint8_t>>, uint8_t> read(
1112 ipmi::Context::ptr& ctx, const std::string& type, uint8_t instance,
1113 size_t count)
Deepak Kodihalliee717d72018-01-24 04:53:09 -06001114{
Vernon Mauerycce9ffd2023-07-27 14:15:17 -07001115 std::vector<std::tuple<uint7_t, bool, uint8_t>> response{};
Deepak Kodihallib1e8fba2018-01-24 04:57:10 -06001116
Kirill Pakhomova2573622018-11-02 19:00:18 +03001117 auto data = parseJSONConfig(gDCMISensorsConfig);
Vernon Mauerycce9ffd2023-07-27 14:15:17 -07001118 static const std::vector<nlohmann::json> empty{};
1119 std::vector<nlohmann::json> readings = data.value(type, empty);
Deepak Kodihallib1e8fba2018-01-24 04:57:10 -06001120 for (const auto& j : readings)
1121 {
Vernon Mauerycce9ffd2023-07-27 14:15:17 -07001122 // Max of 8 response data sets
1123 if (response.size() == count)
1124 {
1125 break;
1126 }
1127
Deepak Kodihallib1e8fba2018-01-24 04:57:10 -06001128 uint8_t instanceNum = j.value("instance", 0);
Vernon Mauerycce9ffd2023-07-27 14:15:17 -07001129 // Not in the instance range we're interested in
1130 if (instanceNum < instance)
Deepak Kodihallib1e8fba2018-01-24 04:57:10 -06001131 {
1132 continue;
1133 }
1134
1135 std::string path = j.value("dbus", "");
Vernon Mauerycce9ffd2023-07-27 14:15:17 -07001136 std::string service{};
Alexander Hansenf365c7f2025-11-14 12:08:34 +01001137 boost::system::error_code ec =
1138 ipmi::getService(ctx, SensorValue::interface, path, service);
Vernon Mauerycce9ffd2023-07-27 14:15:17 -07001139 if (ec.value())
Deepak Kodihallib1e8fba2018-01-24 04:57:10 -06001140 {
Vernon Mauerycce9ffd2023-07-27 14:15:17 -07001141 // not found on dbus
Deepak Kodihallib1e8fba2018-01-24 04:57:10 -06001142 continue;
1143 }
Vernon Mauerycce9ffd2023-07-27 14:15:17 -07001144
1145 const auto& [ok, sign, temp] = readTemp(ctx, service, path);
1146 if (ok)
1147 {
1148 response.emplace_back(uint7_t{temp}, sign, instanceNum);
1149 }
Deepak Kodihallib1e8fba2018-01-24 04:57:10 -06001150 }
1151
Vernon Mauerycce9ffd2023-07-27 14:15:17 -07001152 auto totalInstances =
1153 static_cast<uint8_t>(std::min(readings.size(), maxInstances));
1154 return std::make_tuple(response, totalInstances);
Deepak Kodihalliee717d72018-01-24 04:53:09 -06001155}
1156
Patrick Venture0b02be92018-08-31 11:55:55 -07001157} // namespace temp_readings
1158} // namespace dcmi
Deepak Kodihalliee717d72018-01-24 04:53:09 -06001159
Vernon Mauerycce9ffd2023-07-27 14:15:17 -07001160ipmi::RspType<uint8_t, // total instances for entity id
1161 uint8_t, // number of instances in this reply
1162 std::vector< // zero or more of the following two bytes
1163 std::tuple<uint7_t, // temperature value
1164 bool, // sign bit
1165 uint8_t // entity instance
1166 >>>
1167 getTempReadings(ipmi::Context::ptr& ctx, uint8_t sensorType,
1168 uint8_t entityId, uint8_t entityInstance,
1169 uint8_t instanceStart)
Deepak Kodihalliee717d72018-01-24 04:53:09 -06001170{
Vernon Mauerycce9ffd2023-07-27 14:15:17 -07001171 auto it = dcmi::entityIdToName.find(entityId);
Deepak Kodihalli0b459552018-02-06 06:25:12 -06001172 if (it == dcmi::entityIdToName.end())
Deepak Kodihalliee717d72018-01-24 04:53:09 -06001173 {
George Liu7fa28712024-07-17 19:26:23 +08001174 lg2::error("Unknown Entity ID: {ENTITY_ID}", "ENTITY_ID", entityId);
Vernon Mauerycce9ffd2023-07-27 14:15:17 -07001175 return ipmi::responseInvalidFieldRequest();
Deepak Kodihalliee717d72018-01-24 04:53:09 -06001176 }
1177
Vernon Mauerycce9ffd2023-07-27 14:15:17 -07001178 if (sensorType != dcmi::temperatureSensorType)
Deepak Kodihalliee717d72018-01-24 04:53:09 -06001179 {
George Liu7fa28712024-07-17 19:26:23 +08001180 lg2::error("Invalid sensor type: {SENSOR_TYPE}", "SENSOR_TYPE",
1181 sensorType);
Vernon Mauerycce9ffd2023-07-27 14:15:17 -07001182 return ipmi::responseInvalidFieldRequest();
Deepak Kodihalliee717d72018-01-24 04:53:09 -06001183 }
1184
Vernon Mauerycce9ffd2023-07-27 14:15:17 -07001185 uint8_t requestedRecords = (entityInstance == 0) ? dcmi::maxRecords : 1;
Deepak Kodihalliee717d72018-01-24 04:53:09 -06001186
Vernon Mauerycce9ffd2023-07-27 14:15:17 -07001187 // Read requested instances
1188 const auto& [temps, totalInstances] = dcmi::temp_readings::read(
1189 ctx, it->second, instanceStart, requestedRecords);
Deepak Kodihalliee717d72018-01-24 04:53:09 -06001190
Vernon Mauerycce9ffd2023-07-27 14:15:17 -07001191 auto numInstances = static_cast<uint8_t>(temps.size());
1192
1193 return ipmi::responseSuccess(totalInstances, numInstances, temps);
Deepak Kodihalliee717d72018-01-24 04:53:09 -06001194}
1195
Vernon Mauery6475b5c2023-07-27 14:52:51 -07001196ipmi::RspType<> setDCMIConfParams(ipmi::Context::ptr& ctx, uint8_t parameter,
1197 uint8_t setSelector,
1198 ipmi::message::Payload& payload)
Nagaraju Goruganti22be97b2018-02-07 01:19:59 -06001199{
Vernon Mauery6475b5c2023-07-27 14:52:51 -07001200 if (setSelector)
Nagaraju Goruganti22be97b2018-02-07 01:19:59 -06001201 {
Vernon Mauery6475b5c2023-07-27 14:52:51 -07001202 return ipmi::responseInvalidFieldRequest();
Nagaraju Goruganti22be97b2018-02-07 01:19:59 -06001203 }
Vernon Mauery6475b5c2023-07-27 14:52:51 -07001204 // Take action based on the Parameter Selector
1205 switch (static_cast<dcmi::DCMIConfigParameters>(parameter))
Nagaraju Goruganti22be97b2018-02-07 01:19:59 -06001206 {
Vernon Mauery6475b5c2023-07-27 14:52:51 -07001207 case dcmi::DCMIConfigParameters::ActivateDHCP:
Nagaraju Goruganti22be97b2018-02-07 01:19:59 -06001208 {
Vernon Mauery6475b5c2023-07-27 14:52:51 -07001209 uint7_t reserved{};
1210 bool activate{};
1211 if (payload.unpack(activate, reserved) || !payload.fullyUnpacked())
1212 {
1213 return ipmi::responseReqDataLenInvalid();
1214 }
1215 if (reserved)
1216 {
1217 return ipmi::responseInvalidFieldRequest();
1218 }
1219 std::optional<EthernetInterface::DHCPConf> dhcpEnabled =
1220 dcmi::getDHCPEnabled(ctx);
1221 if (!dhcpEnabled)
1222 {
1223 return ipmi::responseUnspecifiedError();
1224 }
1225 if (activate &&
1226 (dhcpEnabled.value() != EthernetInterface::DHCPConf::none))
1227 {
1228 // When these conditions are met we have to trigger DHCP
1229 // protocol restart using the latest parameter settings,
1230 // but as per n/w manager design, each time when we
1231 // update n/w parameters, n/w service is restarted. So
1232 // we no need to take any action in this case.
1233 }
1234 break;
Nagaraju Goruganti22be97b2018-02-07 01:19:59 -06001235 }
Vernon Mauery6475b5c2023-07-27 14:52:51 -07001236 case dcmi::DCMIConfigParameters::DiscoveryConfig:
1237 {
1238 bool option12{};
1239 uint6_t reserved1{};
1240 bool randBackOff{};
1241 if (payload.unpack(option12, reserved1, randBackOff) ||
1242 !payload.fullyUnpacked())
1243 {
1244 return ipmi::responseReqDataLenInvalid();
1245 }
1246 // Systemd-networkd doesn't support Random Back off
1247 if (reserved1 || randBackOff)
1248 {
1249 return ipmi::responseInvalidFieldRequest();
1250 }
1251 dcmi::setDHCPOption(ctx, dcmi::dhcpOpt12Enabled, option12);
1252 break;
1253 }
1254 // Systemd-networkd doesn't allow to configure DHCP timigs
1255 case dcmi::DCMIConfigParameters::DHCPTiming1:
1256 case dcmi::DCMIConfigParameters::DHCPTiming2:
1257 case dcmi::DCMIConfigParameters::DHCPTiming3:
1258 default:
1259 return ipmi::responseInvalidFieldRequest();
Nagaraju Goruganti22be97b2018-02-07 01:19:59 -06001260 }
Vernon Mauery6475b5c2023-07-27 14:52:51 -07001261 return ipmi::responseSuccess();
Nagaraju Goruganti22be97b2018-02-07 01:19:59 -06001262}
1263
Patrick Williams1318a5e2024-08-16 15:19:54 -04001264ipmi::RspType<ipmi::message::Payload> getDCMIConfParams(
1265 ipmi::Context::ptr& ctx, uint8_t parameter, uint8_t setSelector)
Nagaraju Goruganti22be97b2018-02-07 01:19:59 -06001266{
Vernon Mauery6475b5c2023-07-27 14:52:51 -07001267 if (setSelector)
Nagaraju Goruganti22be97b2018-02-07 01:19:59 -06001268 {
Vernon Mauery6475b5c2023-07-27 14:52:51 -07001269 return ipmi::responseInvalidFieldRequest();
Nagaraju Goruganti22be97b2018-02-07 01:19:59 -06001270 }
Vernon Mauery6475b5c2023-07-27 14:52:51 -07001271 ipmi::message::Payload payload;
1272 payload.pack(dcmi::specMajorVersion, dcmi::specMinorVersion,
1273 dcmi::configParameterRevision);
Nagaraju Goruganti22be97b2018-02-07 01:19:59 -06001274
Vernon Mauery6475b5c2023-07-27 14:52:51 -07001275 // Take action based on the Parameter Selector
1276 switch (static_cast<dcmi::DCMIConfigParameters>(parameter))
Nagaraju Goruganti22be97b2018-02-07 01:19:59 -06001277 {
Vernon Mauery6475b5c2023-07-27 14:52:51 -07001278 case dcmi::DCMIConfigParameters::ActivateDHCP:
1279 payload.pack(dcmi::activateDhcpReply);
1280 break;
1281 case dcmi::DCMIConfigParameters::DiscoveryConfig:
Nagaraju Goruganti22be97b2018-02-07 01:19:59 -06001282 {
Vernon Mauery6475b5c2023-07-27 14:52:51 -07001283 uint8_t discovery{};
1284 std::optional<bool> enabled =
1285 dcmi::getDHCPOption(ctx, dcmi::dhcpOpt12Enabled);
1286 if (!enabled.has_value())
1287 {
1288 return ipmi::responseUnspecifiedError();
1289 }
1290 if (enabled.value())
1291 {
1292 discovery = dcmi::option12Mask;
1293 }
1294 payload.pack(discovery);
1295 break;
Nagaraju Goruganti22be97b2018-02-07 01:19:59 -06001296 }
Vernon Mauery6475b5c2023-07-27 14:52:51 -07001297 // Get below values from Systemd-networkd source code
1298 case dcmi::DCMIConfigParameters::DHCPTiming1:
1299 payload.pack(dcmi::dhcpTiming1);
1300 break;
1301 case dcmi::DCMIConfigParameters::DHCPTiming2:
1302 payload.pack(dcmi::dhcpTiming2);
1303 break;
1304 case dcmi::DCMIConfigParameters::DHCPTiming3:
1305 payload.pack(dcmi::dhcpTiming3);
1306 break;
1307 default:
1308 return ipmi::responseInvalidFieldRequest();
Nagaraju Goruganti22be97b2018-02-07 01:19:59 -06001309 }
1310
Thang Tranaf762de2023-12-18 11:19:28 +07001311 return ipmi::responseSuccess(payload);
Nagaraju Goruganti22be97b2018-02-07 01:19:59 -06001312}
1313
Vernon Mauery056fab12023-07-27 14:25:24 -07001314static std::optional<uint16_t> readPower(ipmi::Context::ptr& ctx)
Marri Devender Rao66c5fda2018-01-18 10:48:37 -06001315{
Vernon Mauery056fab12023-07-27 14:25:24 -07001316 std::ifstream sensorFile(POWER_READING_SENSOR);
1317 std::string objectPath;
1318 if (!sensorFile.is_open())
1319 {
George Liu7fa28712024-07-17 19:26:23 +08001320 lg2::error(
1321 "Power reading configuration file not found: {POWER_SENSOR_FILE}",
1322 "POWER_SENSOR_FILE", std::string_view{POWER_READING_SENSOR});
Vernon Mauery056fab12023-07-27 14:25:24 -07001323 return std::nullopt;
1324 }
1325
1326 auto data = nlohmann::json::parse(sensorFile, nullptr, false);
1327 if (data.is_discarded())
1328 {
George Liu7fa28712024-07-17 19:26:23 +08001329 lg2::error("Error in parsing configuration file: {POWER_SENSOR_FILE}",
1330 "POWER_SENSOR_FILE", std::string_view{POWER_READING_SENSOR});
Vernon Mauery056fab12023-07-27 14:25:24 -07001331 return std::nullopt;
1332 }
1333
1334 objectPath = data.value("path", "");
1335 if (objectPath.empty())
1336 {
George Liu7fa28712024-07-17 19:26:23 +08001337 lg2::error(
1338 "Power sensor D-Bus object path is empty: {POWER_SENSOR_FILE}",
1339 "POWER_SENSOR_FILE", std::string_view{POWER_READING_SENSOR});
Vernon Mauery056fab12023-07-27 14:25:24 -07001340 return std::nullopt;
1341 }
1342
1343 // Return default value if failed to read from D-Bus object
1344 std::string service{};
Patrick Williams1318a5e2024-08-16 15:19:54 -04001345 boost::system::error_code ec =
Alexander Hansenf365c7f2025-11-14 12:08:34 +01001346 ipmi::getService(ctx, SensorValue::interface, objectPath, service);
Vernon Mauery056fab12023-07-27 14:25:24 -07001347 if (ec.value())
1348 {
George Liu7fa28712024-07-17 19:26:23 +08001349 lg2::error("Failed to fetch service for D-Bus object, "
1350 "object path: {OBJECT_PATH}, interface: {INTERFACE}",
1351 "OBJECT_PATH", objectPath, "INTERFACE",
Alexander Hansenf365c7f2025-11-14 12:08:34 +01001352 SensorValue::interface);
Vernon Mauery056fab12023-07-27 14:25:24 -07001353 return std::nullopt;
1354 }
1355
1356 // Read the sensor value and scale properties
1357 double value{};
Alexander Hansenf365c7f2025-11-14 12:08:34 +01001358 ec = ipmi::getDbusProperty(ctx, service, objectPath, SensorValue::interface,
1359 SensorValue::property_names::value, value);
Vernon Mauery056fab12023-07-27 14:25:24 -07001360 if (ec.value())
1361 {
George Liu7fa28712024-07-17 19:26:23 +08001362 lg2::error("Failed to read power value from D-Bus object, "
1363 "object path: {OBJECT_PATH}, interface: {INTERFACE}",
1364 "OBJECT_PATH", objectPath, "INTERFACE",
Alexander Hansenf365c7f2025-11-14 12:08:34 +01001365 SensorValue::interface);
Vernon Mauery056fab12023-07-27 14:25:24 -07001366 return std::nullopt;
1367 }
1368 auto power = static_cast<uint16_t>(value);
1369 return power;
1370}
1371
1372ipmi::RspType<uint16_t, // current power
1373 uint16_t, // minimum power
1374 uint16_t, // maximum power
1375 uint16_t, // average power
1376 uint32_t, // timestamp
1377 uint32_t, // sample period ms
1378 uint6_t, // reserved
1379 bool, // power measurement active
1380 bool // reserved
1381 >
1382 getPowerReading(ipmi::Context::ptr& ctx, uint8_t mode, uint8_t attributes,
1383 uint8_t reserved)
1384{
Kirill Pakhomov2c2af2c2018-11-06 16:06:10 +03001385 if (!dcmi::isDCMIPowerMgmtSupported())
1386 {
George Liu7fa28712024-07-17 19:26:23 +08001387 lg2::error("DCMI Power management is unsupported!");
Vernon Mauery056fab12023-07-27 14:25:24 -07001388 return ipmi::responseInvalidCommand();
1389 }
1390 if (reserved)
1391 {
1392 return ipmi::responseInvalidFieldRequest();
Kirill Pakhomov2c2af2c2018-11-06 16:06:10 +03001393 }
1394
Vernon Mauery056fab12023-07-27 14:25:24 -07001395 enum class PowerMode : uint8_t
1396 {
1397 SystemPowerStatistics = 1,
1398 EnhancedSystemPowerStatistics = 2,
1399 };
Marri Devender Rao9c966e02018-01-22 05:55:10 -06001400
Vernon Mauery056fab12023-07-27 14:25:24 -07001401 if (static_cast<PowerMode>(mode) != PowerMode::SystemPowerStatistics)
Marri Devender Rao66c5fda2018-01-18 10:48:37 -06001402 {
Vernon Mauery056fab12023-07-27 14:25:24 -07001403 return ipmi::responseInvalidFieldRequest();
Marri Devender Rao66c5fda2018-01-18 10:48:37 -06001404 }
Vernon Mauery056fab12023-07-27 14:25:24 -07001405 if (attributes)
Marri Devender Rao66c5fda2018-01-18 10:48:37 -06001406 {
Vernon Mauery056fab12023-07-27 14:25:24 -07001407 return ipmi::responseInvalidFieldRequest();
Marri Devender Rao66c5fda2018-01-18 10:48:37 -06001408 }
Marri Devender Rao9c966e02018-01-22 05:55:10 -06001409
Vernon Mauery056fab12023-07-27 14:25:24 -07001410 std::optional<uint16_t> powerResp = readPower(ctx);
1411 if (!powerResp)
1412 {
1413 return ipmi::responseUnspecifiedError();
1414 }
1415 auto& power = powerResp.value();
1416
Marri Devender Rao9c966e02018-01-22 05:55:10 -06001417 // TODO: openbmc/openbmc#2819
Gunnar Mills8466b792018-03-23 12:18:12 -05001418 // Minimum, Maximum, Average power, TimeFrame, TimeStamp,
Marri Devender Rao9c966e02018-01-22 05:55:10 -06001419 // PowerReadingState readings need to be populated
1420 // after Telemetry changes.
Vernon Mauery056fab12023-07-27 14:25:24 -07001421 constexpr uint32_t samplePeriod = 1;
1422 constexpr uint6_t reserved1 = 0;
1423 constexpr bool measurementActive = true;
1424 constexpr bool reserved2 = false;
1425 auto timestamp = static_cast<uint32_t>(time(nullptr));
1426 return ipmi::responseSuccess(power, power, power, power, timestamp,
1427 samplePeriod, reserved1, measurementActive,
1428 reserved2);
Marri Devender Rao66c5fda2018-01-18 10:48:37 -06001429}
1430
Deepak Kodihalli0b459552018-02-06 06:25:12 -06001431namespace dcmi
1432{
1433namespace sensor_info
1434{
1435
Patrick Williams69b4c282025-03-03 11:19:13 -05001436std::tuple<std::vector<uint16_t>, uint8_t> read(
1437 const std::string& type, uint8_t instance, const nlohmann::json& config,
1438 uint8_t count)
Deepak Kodihallidd4cff12018-02-06 06:48:29 -06001439{
Vernon Mauery53d0cf12023-07-27 14:32:44 -07001440 std::vector<uint16_t> responses{};
Deepak Kodihallidd4cff12018-02-06 06:48:29 -06001441
Vernon Mauery53d0cf12023-07-27 14:32:44 -07001442 static const std::vector<nlohmann::json> empty{};
1443 std::vector<nlohmann::json> readings = config.value(type, empty);
1444 uint8_t totalInstances = std::min(readings.size(), maxInstances);
Deepak Kodihallidd4cff12018-02-06 06:48:29 -06001445 for (const auto& reading : readings)
1446 {
Vernon Mauery53d0cf12023-07-27 14:32:44 -07001447 // limit to requested count
1448 if (responses.size() == count)
1449 {
1450 break;
1451 }
1452
Deepak Kodihallidd4cff12018-02-06 06:48:29 -06001453 uint8_t instanceNum = reading.value("instance", 0);
Vernon Mauery53d0cf12023-07-27 14:32:44 -07001454 // Not in the instance range we're interested in
1455 if (instanceNum < instance)
Deepak Kodihallidd4cff12018-02-06 06:48:29 -06001456 {
1457 continue;
1458 }
1459
Thang Tran5ea83fa2023-10-16 14:37:56 +07001460 uint16_t recordId = reading.value("record_id", 0);
Vernon Mauery53d0cf12023-07-27 14:32:44 -07001461 responses.emplace_back(recordId);
Deepak Kodihallidd4cff12018-02-06 06:48:29 -06001462 }
1463
Vernon Mauery53d0cf12023-07-27 14:32:44 -07001464 return std::make_tuple(responses, totalInstances);
Deepak Kodihalli0b459552018-02-06 06:25:12 -06001465}
1466
1467} // namespace sensor_info
1468} // namespace dcmi
1469
Vernon Mauery53d0cf12023-07-27 14:32:44 -07001470ipmi::RspType<uint8_t, // total available instances
1471 uint8_t, // number of records in this response
1472 std::vector<uint16_t> // records
1473 >
1474 getSensorInfo(uint8_t sensorType, uint8_t entityId, uint8_t entityInstance,
1475 uint8_t instanceStart)
Deepak Kodihalli0b459552018-02-06 06:25:12 -06001476{
Vernon Mauery53d0cf12023-07-27 14:32:44 -07001477 auto it = dcmi::entityIdToName.find(entityId);
Deepak Kodihalli0b459552018-02-06 06:25:12 -06001478 if (it == dcmi::entityIdToName.end())
1479 {
George Liu7fa28712024-07-17 19:26:23 +08001480 lg2::error("Unknown Entity ID: {ENTITY_ID}", "ENTITY_ID", entityId);
Vernon Mauery53d0cf12023-07-27 14:32:44 -07001481 return ipmi::responseInvalidFieldRequest();
Deepak Kodihalli0b459552018-02-06 06:25:12 -06001482 }
1483
Vernon Mauery53d0cf12023-07-27 14:32:44 -07001484 if (sensorType != dcmi::temperatureSensorType)
Deepak Kodihalli0b459552018-02-06 06:25:12 -06001485 {
George Liu7fa28712024-07-17 19:26:23 +08001486 lg2::error("Invalid sensor type: {SENSOR_TYPE}", "SENSOR_TYPE",
1487 sensorType);
Vernon Mauery53d0cf12023-07-27 14:32:44 -07001488 return ipmi::responseInvalidFieldRequest();
Deepak Kodihalli0b459552018-02-06 06:25:12 -06001489 }
1490
Vernon Mauery53d0cf12023-07-27 14:32:44 -07001491 nlohmann::json config = dcmi::parseJSONConfig(dcmi::gDCMISensorsConfig);
Deepak Kodihalli0b459552018-02-06 06:25:12 -06001492
Vernon Mauery53d0cf12023-07-27 14:32:44 -07001493 uint8_t requestedRecords = (entityInstance == 0) ? dcmi::maxRecords : 1;
1494 // Read requested instances
1495 const auto& [sensors, totalInstances] = dcmi::sensor_info::read(
1496 it->second, instanceStart, config, requestedRecords);
1497 uint8_t numRecords = sensors.size();
Deepak Kodihalli0b459552018-02-06 06:25:12 -06001498
Vernon Mauery53d0cf12023-07-27 14:32:44 -07001499 return ipmi::responseSuccess(totalInstances, numRecords, sensors);
Deepak Kodihalli0b459552018-02-06 06:25:12 -06001500}
1501
George Liu5087b072025-03-11 19:28:17 +08001502void registerNetFnDcmiFunctions()
Chris Austen1810bec2015-10-13 12:12:39 -05001503{
Tom05732372016-09-06 17:21:23 +05301504 // <Get Power Limit>
Vernon Maueryd4222fd2023-07-27 11:26:51 -07001505 registerGroupHandler(ipmi::prioOpenBmcBase, ipmi::groupDCMI,
1506 ipmi::dcmi::cmdGetPowerLimit, ipmi::Privilege::User,
1507 getPowerLimit);
Tom Joseph6f6dd4d2017-07-12 20:07:11 +05301508
Tom Joseph46fa37d2017-07-26 18:11:55 +05301509 // <Set Power Limit>
Vernon Maueryd4222fd2023-07-27 11:26:51 -07001510 registerGroupHandler(ipmi::prioOpenBmcBase, ipmi::groupDCMI,
1511 ipmi::dcmi::cmdSetPowerLimit,
1512 ipmi::Privilege::Operator, setPowerLimit);
Tom Joseph46fa37d2017-07-26 18:11:55 +05301513
Tom Joseph6c8d51b2017-07-26 18:18:06 +05301514 // <Activate/Deactivate Power Limit>
Vernon Maueryd4222fd2023-07-27 11:26:51 -07001515 registerGroupHandler(ipmi::prioOpenBmcBase, ipmi::groupDCMI,
1516 ipmi::dcmi::cmdActDeactivatePwrLimit,
1517 ipmi::Privilege::Operator, applyPowerLimit);
Tom Joseph6c8d51b2017-07-26 18:18:06 +05301518
Tom Joseph6f6dd4d2017-07-12 20:07:11 +05301519 // <Get Asset Tag>
Vernon Mauerydca47202023-07-27 11:32:01 -07001520 registerGroupHandler(ipmi::prioOpenBmcBase, ipmi::groupDCMI,
1521 ipmi::dcmi::cmdGetAssetTag, ipmi::Privilege::User,
1522 getAssetTag);
Tom Joseph545dd232017-07-12 20:20:49 +05301523
1524 // <Set Asset Tag>
Vernon Mauerydca47202023-07-27 11:32:01 -07001525 registerGroupHandler(ipmi::prioOpenBmcBase, ipmi::groupDCMI,
1526 ipmi::dcmi::cmdSetAssetTag, ipmi::Privilege::Operator,
1527 setAssetTag);
Vladislav Vovchenko8f7a6f62017-08-17 00:31:14 +03001528
Gunnar Mills8991dd62017-10-25 17:11:29 -05001529 // <Get Management Controller Identifier String>
Vernon Maueryefb5ae52023-07-27 11:35:43 -07001530 registerGroupHandler(ipmi::prioOpenBmcBase, ipmi::groupDCMI,
1531 ipmi::dcmi::cmdGetMgmtCntlrIdString,
1532 ipmi::Privilege::User, getMgmntCtrlIdStr);
Vladislav Vovchenko8f7a6f62017-08-17 00:31:14 +03001533
1534 // <Set Management Controller Identifier String>
Vernon Maueryefb5ae52023-07-27 11:35:43 -07001535 registerGroupHandler(ipmi::prioOpenBmcBase, ipmi::groupDCMI,
1536 ipmi::dcmi::cmdSetMgmtCntlrIdString,
1537 ipmi::Privilege::Admin, setMgmntCtrlIdStr);
Vladislav Vovchenko8f7a6f62017-08-17 00:31:14 +03001538
Dhruvaraj Subhashchandrane29be412018-01-16 05:11:56 -06001539 // <Get DCMI capabilities>
Vernon Maueryf038dc02023-07-27 14:06:11 -07001540 registerGroupHandler(ipmi::prioOpenBmcBase, ipmi::groupDCMI,
1541 ipmi::dcmi::cmdGetDcmiCapabilitiesInfo,
1542 ipmi::Privilege::User, getDCMICapabilities);
Deepak Kodihalliee717d72018-01-24 04:53:09 -06001543
Marri Devender Rao66c5fda2018-01-18 10:48:37 -06001544 // <Get Power Reading>
Vernon Mauery056fab12023-07-27 14:25:24 -07001545 registerGroupHandler(ipmi::prioOpenBmcBase, ipmi::groupDCMI,
1546 ipmi::dcmi::cmdGetPowerReading, ipmi::Privilege::User,
1547 getPowerReading);
1548
adarshgrami042e9db2022-09-15 10:34:34 +05301549// The Get sensor should get the senor details dynamically when
1550// FEATURE_DYNAMIC_SENSORS is enabled.
1551#ifndef FEATURE_DYNAMIC_SENSORS
Deepak Kodihalli0b459552018-02-06 06:25:12 -06001552 // <Get Sensor Info>
Vernon Mauery53d0cf12023-07-27 14:32:44 -07001553 registerGroupHandler(ipmi::prioOpenBmcBase, ipmi::groupDCMI,
1554 ipmi::dcmi::cmdGetDcmiSensorInfo,
1555 ipmi::Privilege::Operator, getSensorInfo);
Thang Tran3dad8262023-08-17 15:20:56 +07001556
1557 // <Get Temperature Readings>
1558 registerGroupHandler(ipmi::prioOpenBmcBase, ipmi::groupDCMI,
1559 ipmi::dcmi::cmdGetTemperatureReadings,
1560 ipmi::Privilege::User, getTempReadings);
adarshgrami042e9db2022-09-15 10:34:34 +05301561#endif
Nagaraju Goruganti22be97b2018-02-07 01:19:59 -06001562 // <Get DCMI Configuration Parameters>
Vernon Mauery6475b5c2023-07-27 14:52:51 -07001563 registerGroupHandler(ipmi::prioOpenBmcBase, ipmi::groupDCMI,
1564 ipmi::dcmi::cmdGetDcmiConfigParameters,
1565 ipmi::Privilege::User, getDCMIConfParams);
Nagaraju Goruganti22be97b2018-02-07 01:19:59 -06001566
1567 // <Set DCMI Configuration Parameters>
Vernon Mauery6475b5c2023-07-27 14:52:51 -07001568 registerGroupHandler(ipmi::prioOpenBmcBase, ipmi::groupDCMI,
1569 ipmi::dcmi::cmdSetDcmiConfigParameters,
1570 ipmi::Privilege::Admin, setDCMIConfParams);
Nagaraju Goruganti22be97b2018-02-07 01:19:59 -06001571
Chris Austen1810bec2015-10-13 12:12:39 -05001572 return;
1573}