blob: e6cb1028a8201cf4600c43b2368a32e4ff02874f [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>
Patrick Venture0b02be92018-08-31 11:55:55 -070015
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -050016#include <bitset>
17#include <cmath>
18#include <fstream>
19#include <variant>
20
Tom Josephbe5eaa12017-07-12 19:54:44 +053021using namespace phosphor::logging;
Willy Tu523e2d12023-09-05 11:36:48 -070022using sdbusplus::server::xyz::openbmc_project::network::EthernetInterface;
Thang Tran55cbf552023-01-31 14:43:02 +070023
Tom Josephbe5eaa12017-07-12 19:54:44 +053024using InternalFailure =
Willy Tu523e2d12023-09-05 11:36:48 -070025 sdbusplus::error::xyz::openbmc_project::common::InternalFailure;
Chris Austen1810bec2015-10-13 12:12:39 -050026
George Liu5087b072025-03-11 19:28:17 +080027void registerNetFnDcmiFunctions() __attribute__((constructor));
Chris Austen1810bec2015-10-13 12:12:39 -050028
Vernon Maueryd4222fd2023-07-27 11:26:51 -070029constexpr auto pcapInterface = "xyz.openbmc_project.Control.Power.Cap";
Andrew Geissler50c0c8f2017-07-11 16:18:51 -050030
Vernon Maueryd4222fd2023-07-27 11:26:51 -070031constexpr auto powerCapProp = "PowerCap";
32constexpr auto powerCapEnableProp = "PowerCapEnable";
Thang Trana95978d2025-06-04 10:39:15 +070033constexpr auto powerCapExceptActProp = "ExceptionAction";
34constexpr auto powerCapSamplPeriodProp = "SamplingPeriod";
35constexpr auto powerCapCorrectTimeProp = "CorrectionTime";
Andrew Geissler50c0c8f2017-07-11 16:18:51 -050036
37using namespace phosphor::logging;
38
Tom Josephb9d86f42017-07-26 18:03:47 +053039namespace dcmi
40{
Vernon Mauerydca47202023-07-27 11:32:01 -070041constexpr auto assetTagMaxOffset = 62;
42constexpr auto assetTagMaxSize = 63;
43constexpr auto maxBytes = 16;
44constexpr size_t maxCtrlIdStrLen = 63;
Tom Josephb9d86f42017-07-26 18:03:47 +053045
Vernon Maueryf038dc02023-07-27 14:06:11 -070046constexpr uint8_t parameterRevision = 2;
47constexpr uint8_t specMajorVersion = 1;
48constexpr uint8_t specMinorVersion = 5;
Vernon Mauerycce9ffd2023-07-27 14:15:17 -070049constexpr auto sensorValueIntf = "xyz.openbmc_project.Sensor.Value";
50constexpr auto sensorValueProp = "Value";
Vernon Mauery6475b5c2023-07-27 14:52:51 -070051constexpr uint8_t configParameterRevision = 1;
52constexpr auto option12Mask = 0x01;
53constexpr auto activateDhcpReply = 0x00;
54constexpr uint8_t dhcpTiming1 = 0x04; // 4 sec
55constexpr uint16_t dhcpTiming2 = 0x78; // 120 sec
56constexpr uint16_t dhcpTiming3 = 0x40; // 60 sec
57// When DHCP Option 12 is enabled the string "SendHostName=true" will be
58// added into n/w configuration file and the parameter
59// SendHostNameEnabled will set to true.
60constexpr auto dhcpOpt12Enabled = "SendHostNameEnabled";
61
62enum class DCMIConfigParameters : uint8_t
63{
64 ActivateDHCP = 1,
65 DiscoveryConfig,
66 DHCPTiming1,
67 DHCPTiming2,
68 DHCPTiming3,
69};
Vernon Maueryf038dc02023-07-27 14:06:11 -070070
Thang Trana95978d2025-06-04 10:39:15 +070071/*
72 * The list of Exception action options. Please refer to table 6-17 of DCMI
73 * specification version 1.5 for more information.
74 * https://www.intel.com/content/dam/www/public/us/en/documents/technical-specifications/dcmi-v1-5-rev-spec.pdf
75 */
76enum class ExceptActOptions : uint8_t
77{
78 NoAction = 0x00,
79 HardPowerOff,
80 Oem02,
81 Oem03,
82 Oem04,
83 Oem05,
84 Oem06,
85 Oem07,
86 Oem08,
87 Oem09,
88 Oem0A,
89 Oem0B,
90 Oem0C,
91 Oem0D,
92 Oem0E,
93 Oem0F,
94 Oem10,
95 LogEventOnly,
96};
97
98/*
99 * The PDIs only supports NoAction/HardPowerOff/LogEventOnly/Oem options for
100 * exception action.
101 */
102namespace DbusExceptAct
103{
104constexpr auto noAction =
105 "xyz.openbmc_project.Control.Power.Cap.ExceptionActions.NoAction";
106constexpr auto hardPowerOff =
107 "xyz.openbmc_project.Control.Power.Cap.ExceptionActions.HardPowerOff";
108constexpr auto logEventOnly =
109 "xyz.openbmc_project.Control.Power.Cap.ExceptionActions.LogEventOnly";
110constexpr auto oem =
111 "xyz.openbmc_project.Control.Power.Cap.ExceptionActions.Oem";
112} // namespace DbusExceptAct
113
Deepak Kodihalli0b459552018-02-06 06:25:12 -0600114// Refer Table 6-14, DCMI Entity ID Extension, DCMI v1.5 spec
Patrick Venture0b02be92018-08-31 11:55:55 -0700115static const std::map<uint8_t, std::string> entityIdToName{
116 {0x40, "inlet"}, {0x37, "inlet"}, {0x41, "cpu"},
117 {0x03, "cpu"}, {0x42, "baseboard"}, {0x07, "baseboard"}};
Deepak Kodihalli0b459552018-02-06 06:25:12 -0600118
Vernon Mauery6475b5c2023-07-27 14:52:51 -0700119nlohmann::json parseJSONConfig(const std::string& configFile)
120{
121 std::ifstream jsonFile(configFile);
122 if (!jsonFile.is_open())
123 {
George Liu7fa28712024-07-17 19:26:23 +0800124 lg2::error("Temperature readings JSON file not found");
Vernon Mauery6475b5c2023-07-27 14:52:51 -0700125 elog<InternalFailure>();
126 }
127
128 auto data = nlohmann::json::parse(jsonFile, nullptr, false);
129 if (data.is_discarded())
130 {
George Liu7fa28712024-07-17 19:26:23 +0800131 lg2::error("Temperature readings JSON parser failure");
Vernon Mauery6475b5c2023-07-27 14:52:51 -0700132 elog<InternalFailure>();
133 }
134
135 return data;
136}
137
Kirill Pakhomov2c2af2c2018-11-06 16:06:10 +0300138bool isDCMIPowerMgmtSupported()
139{
Vernon Maueryf4eb35d2023-07-27 11:08:49 -0700140 static bool parsed = false;
141 static bool supported = false;
142 if (!parsed)
143 {
144 auto data = parseJSONConfig(gDCMICapabilitiesConfig);
Kirill Pakhomov2c2af2c2018-11-06 16:06:10 +0300145
Vernon Maueryf4eb35d2023-07-27 11:08:49 -0700146 supported = (gDCMIPowerMgmtSupported ==
147 data.value(gDCMIPowerMgmtCapability, 0));
148 }
149 return supported;
Kirill Pakhomov2c2af2c2018-11-06 16:06:10 +0300150}
151
Thang Trana95978d2025-06-04 10:39:15 +0700152std::optional<ipmi::DbusObjectInfo> getPCapObject(ipmi::Context::ptr& ctx)
Andrew Geissler50c0c8f2017-07-11 16:18:51 -0500153{
Thang Trana95978d2025-06-04 10:39:15 +0700154 static std::optional<ipmi::DbusObjectInfo> pcapObject = std::nullopt;
155
156 if (pcapObject != std::nullopt)
157 {
158 return pcapObject;
159 }
160
161 ipmi::DbusObjectInfo objectPath{};
Patrick Williams1318a5e2024-08-16 15:19:54 -0400162 boost::system::error_code ec =
Thang Trana95978d2025-06-04 10:39:15 +0700163 ipmi::getDbusObject(ctx, pcapInterface, objectPath);
164
Vernon Maueryd4222fd2023-07-27 11:26:51 -0700165 if (ec.value())
Andrew Geissler50c0c8f2017-07-11 16:18:51 -0500166 {
Vernon Maueryd4222fd2023-07-27 11:26:51 -0700167 return std::nullopt;
George Liu3e3cc352023-07-26 15:59:31 +0800168 }
Thang Trana95978d2025-06-04 10:39:15 +0700169
170 pcapObject = objectPath;
171
172 return pcapObject;
173}
174
175std::optional<uint32_t> getPcap(ipmi::Context::ptr& ctx)
176{
177 std::optional<ipmi::DbusObjectInfo> pcapObject = getPCapObject(ctx);
178
179 if (pcapObject == std::nullopt)
180 {
181 return std::nullopt;
182 }
183
Vernon Maueryd4222fd2023-07-27 11:26:51 -0700184 uint32_t pcap{};
Thang Trana95978d2025-06-04 10:39:15 +0700185 boost::system::error_code ec = ipmi::getDbusProperty(
186 ctx, pcapObject.value().second.c_str(),
187 pcapObject.value().first.c_str(), pcapInterface, powerCapProp, pcap);
188
Vernon Maueryd4222fd2023-07-27 11:26:51 -0700189 if (ec.value())
George Liu3e3cc352023-07-26 15:59:31 +0800190 {
George Liu7fa28712024-07-17 19:26:23 +0800191 lg2::error("Error in getPcap prop: {ERROR}", "ERROR", ec.message());
Tom Josephb9d86f42017-07-26 18:03:47 +0530192 elog<InternalFailure>();
Vernon Maueryd4222fd2023-07-27 11:26:51 -0700193 return std::nullopt;
Andrew Geissler50c0c8f2017-07-11 16:18:51 -0500194 }
Thang Trana95978d2025-06-04 10:39:15 +0700195
Vernon Maueryd4222fd2023-07-27 11:26:51 -0700196 return pcap;
Andrew Geissler50c0c8f2017-07-11 16:18:51 -0500197}
198
Vernon Maueryd4222fd2023-07-27 11:26:51 -0700199std::optional<bool> getPcapEnabled(ipmi::Context::ptr& ctx)
Andrew Geissler50c0c8f2017-07-11 16:18:51 -0500200{
Thang Trana95978d2025-06-04 10:39:15 +0700201 std::optional<ipmi::DbusObjectInfo> pcapObject = getPCapObject(ctx);
202
203 if (pcapObject == std::nullopt)
Andrew Geissler50c0c8f2017-07-11 16:18:51 -0500204 {
Vernon Maueryd4222fd2023-07-27 11:26:51 -0700205 return std::nullopt;
George Liu3e3cc352023-07-26 15:59:31 +0800206 }
Thang Trana95978d2025-06-04 10:39:15 +0700207
Vernon Maueryd4222fd2023-07-27 11:26:51 -0700208 bool pcapEnabled{};
Thang Trana95978d2025-06-04 10:39:15 +0700209 boost::system::error_code ec =
210 ipmi::getDbusProperty(ctx, pcapObject.value().second.c_str(),
211 pcapObject.value().first.c_str(), pcapInterface,
212 powerCapEnableProp, pcapEnabled);
213
Vernon Maueryd4222fd2023-07-27 11:26:51 -0700214 if (ec.value())
George Liu3e3cc352023-07-26 15:59:31 +0800215 {
Thang Trana95978d2025-06-04 10:39:15 +0700216 lg2::error("Error in getPcapEnabled prop: {ERROR}", "ERROR",
217 ec.message());
Tom Josephb9d86f42017-07-26 18:03:47 +0530218 elog<InternalFailure>();
Vernon Maueryd4222fd2023-07-27 11:26:51 -0700219 return std::nullopt;
Andrew Geissler50c0c8f2017-07-11 16:18:51 -0500220 }
Thang Trana95978d2025-06-04 10:39:15 +0700221
Vernon Maueryd4222fd2023-07-27 11:26:51 -0700222 return pcapEnabled;
Andrew Geissler50c0c8f2017-07-11 16:18:51 -0500223}
Chris Austen1810bec2015-10-13 12:12:39 -0500224
Thang Trana95978d2025-06-04 10:39:15 +0700225std::optional<std::string> getPcapExceptAction(ipmi::Context::ptr& ctx)
226{
227 std::optional<ipmi::DbusObjectInfo> pcapObject = getPCapObject(ctx);
228
229 if (pcapObject == std::nullopt)
230 {
231 return std::nullopt;
232 }
233
234 std::string exceptActStr{};
235
236 boost::system::error_code ec =
237 ipmi::getDbusProperty(ctx, pcapObject.value().second.c_str(),
238 pcapObject.value().first.c_str(), pcapInterface,
239 powerCapExceptActProp, exceptActStr);
240
241 if (ec.value())
242 {
243 lg2::error("Error in getPcapExceptAction prop: {ERROR}", "ERROR",
244 ec.message());
245 elog<InternalFailure>();
246 return std::nullopt;
247 }
248
249 return exceptActStr;
250}
251
252std::optional<uint32_t> getPcapCorrectTime(ipmi::Context::ptr& ctx)
253{
254 std::optional<ipmi::DbusObjectInfo> pcapObject = getPCapObject(ctx);
255
256 if (pcapObject == std::nullopt)
257 {
258 return std::nullopt;
259 }
260
261 uint64_t pcapCorrectTimeUs{};
262 boost::system::error_code ec =
263 ipmi::getDbusProperty(ctx, pcapObject.value().second.c_str(),
264 pcapObject.value().first.c_str(), pcapInterface,
265 powerCapCorrectTimeProp, pcapCorrectTimeUs);
266 if (ec.value())
267 {
268 lg2::error("Error in getPcapCorrectTime prop: {ERROR}", "ERROR",
269 ec.message());
270 elog<InternalFailure>();
271 return std::nullopt;
272 }
273
274 return (uint32_t)(std::chrono::duration_cast<std::chrono::milliseconds>(
275 std::chrono::microseconds(pcapCorrectTimeUs)))
276 .count();
277}
278
279std::optional<uint16_t> getPcapSamplPeriod(ipmi::Context::ptr& ctx)
280{
281 std::optional<ipmi::DbusObjectInfo> pcapObject = getPCapObject(ctx);
282
283 if (pcapObject == std::nullopt)
284 {
285 return std::nullopt;
286 }
287
288 uint64_t pcapSamplPeriodUs{};
289 boost::system::error_code ec =
290 ipmi::getDbusProperty(ctx, pcapObject.value().second.c_str(),
291 pcapObject.value().first.c_str(), pcapInterface,
292 powerCapSamplPeriodProp, pcapSamplPeriodUs);
293 if (ec.value())
294 {
295 lg2::error("Error in getPcapSamplPeriod prop: {ERROR}", "ERROR",
296 ec.message());
297 elog<InternalFailure>();
298 return std::nullopt;
299 }
300
301 return (uint16_t)(std::chrono::duration_cast<std::chrono::seconds>(
302 std::chrono::microseconds(pcapSamplPeriodUs)))
303 .count();
304}
305
Vernon Maueryd4222fd2023-07-27 11:26:51 -0700306bool setPcap(ipmi::Context::ptr& ctx, const uint32_t powerCap)
Tom Joseph46fa37d2017-07-26 18:11:55 +0530307{
Thang Trana95978d2025-06-04 10:39:15 +0700308 std::optional<ipmi::DbusObjectInfo> pcapObject = getPCapObject(ctx);
309
310 if (pcapObject == std::nullopt)
Tom Joseph46fa37d2017-07-26 18:11:55 +0530311 {
Vernon Maueryd4222fd2023-07-27 11:26:51 -0700312 return false;
George Liu3e3cc352023-07-26 15:59:31 +0800313 }
Vernon Maueryd4222fd2023-07-27 11:26:51 -0700314
Thang Trana95978d2025-06-04 10:39:15 +0700315 boost::system::error_code ec =
316 ipmi::setDbusProperty(ctx, pcapObject.value().second.c_str(),
317 pcapObject.value().first.c_str(), pcapInterface,
318 powerCapProp, powerCap);
319
Vernon Maueryd4222fd2023-07-27 11:26:51 -0700320 if (ec.value())
George Liu3e3cc352023-07-26 15:59:31 +0800321 {
George Liu7fa28712024-07-17 19:26:23 +0800322 lg2::error("Error in setPcap property: {ERROR}", "ERROR", ec.message());
Tom Joseph46fa37d2017-07-26 18:11:55 +0530323 elog<InternalFailure>();
Vernon Maueryd4222fd2023-07-27 11:26:51 -0700324 return false;
Tom Joseph46fa37d2017-07-26 18:11:55 +0530325 }
Thang Trana95978d2025-06-04 10:39:15 +0700326
Vernon Maueryd4222fd2023-07-27 11:26:51 -0700327 return true;
Tom Joseph46fa37d2017-07-26 18:11:55 +0530328}
329
Vernon Maueryd4222fd2023-07-27 11:26:51 -0700330bool setPcapEnable(ipmi::Context::ptr& ctx, bool enabled)
Tom Joseph6c8d51b2017-07-26 18:18:06 +0530331{
Thang Trana95978d2025-06-04 10:39:15 +0700332 std::optional<ipmi::DbusObjectInfo> pcapObject = getPCapObject(ctx);
333
334 if (pcapObject == std::nullopt)
Tom Joseph6c8d51b2017-07-26 18:18:06 +0530335 {
Vernon Maueryd4222fd2023-07-27 11:26:51 -0700336 return false;
George Liu3e3cc352023-07-26 15:59:31 +0800337 }
Vernon Maueryd4222fd2023-07-27 11:26:51 -0700338
Thang Trana95978d2025-06-04 10:39:15 +0700339 boost::system::error_code ec =
340 ipmi::setDbusProperty(ctx, pcapObject.value().second.c_str(),
341 pcapObject.value().first.c_str(), pcapInterface,
342 powerCapEnableProp, enabled);
343
Vernon Maueryd4222fd2023-07-27 11:26:51 -0700344 if (ec.value())
George Liu3e3cc352023-07-26 15:59:31 +0800345 {
George Liu7fa28712024-07-17 19:26:23 +0800346 lg2::error("Error in setPcapEnabled property: {ERROR}", "ERROR",
347 ec.message());
Tom Joseph6c8d51b2017-07-26 18:18:06 +0530348 elog<InternalFailure>();
Vernon Maueryd4222fd2023-07-27 11:26:51 -0700349 return false;
Tom Joseph6c8d51b2017-07-26 18:18:06 +0530350 }
Vernon Maueryd4222fd2023-07-27 11:26:51 -0700351 return true;
Tom Joseph6c8d51b2017-07-26 18:18:06 +0530352}
353
Thang Trana95978d2025-06-04 10:39:15 +0700354bool setPcapExceptAction(ipmi::Context::ptr& ctx,
355 const std::string& pcapExceptAct)
356{
357 std::optional<ipmi::DbusObjectInfo> pcapObject = getPCapObject(ctx);
358
359 if (pcapObject == std::nullopt)
360 {
361 return false;
362 }
363
364 boost::system::error_code ec =
365 ipmi::setDbusProperty(ctx, pcapObject.value().second.c_str(),
366 pcapObject.value().first.c_str(), pcapInterface,
367 powerCapExceptActProp, pcapExceptAct);
368 if (ec.value())
369 {
370 lg2::error("Error in setPcapExceptAction property: {ERROR}", "ERROR",
371 ec.message());
372 elog<InternalFailure>();
373 return false;
374 }
375
376 return true;
377}
378
379bool setPcapCorrectTime(ipmi::Context::ptr& ctx, uint32_t pcapCorrectTime)
380{
381 std::optional<ipmi::DbusObjectInfo> pcapObject = getPCapObject(ctx);
382
383 if (pcapObject == std::nullopt)
384 {
385 return false;
386 }
387
388 /*
389 * Dbus is storing Correction time in microseconds unit.
390 * Therefore, we have to convert it from milisecond to microseconds.
391 */
392 uint64_t pcapCorrectTimeUs =
393 (uint64_t)(std::chrono::duration_cast<std::chrono::microseconds>(
394 std::chrono::milliseconds(pcapCorrectTime)))
395 .count();
396 boost::system::error_code ec =
397 ipmi::setDbusProperty(ctx, pcapObject.value().second.c_str(),
398 pcapObject.value().first.c_str(), pcapInterface,
399 powerCapCorrectTimeProp, pcapCorrectTimeUs);
400 if (ec.value())
401 {
402 lg2::error("Error in setPcapCorrectTime property: {ERROR}", "ERROR",
403 ec.message());
404 elog<InternalFailure>();
405 return false;
406 }
407
408 return true;
409}
410
411bool setPcapSamplPeriod(ipmi::Context::ptr& ctx, uint16_t pcapSamplPeriod)
412{
413 std::optional<ipmi::DbusObjectInfo> pcapObject = getPCapObject(ctx);
414
415 if (pcapObject == std::nullopt)
416 {
417 return false;
418 }
419
420 /*
421 * Dbus is storing Sampling periodic in microseconds unit.
422 * Therefore, we have to convert it from seconds to microseconds unit.
423 */
424 uint64_t pcapSamplPeriodUs =
425 (uint64_t)(std::chrono::duration_cast<std::chrono::microseconds>(
426 std::chrono::seconds(pcapSamplPeriod)))
427 .count();
428 boost::system::error_code ec =
429 ipmi::setDbusProperty(ctx, pcapObject.value().second.c_str(),
430 pcapObject.value().first.c_str(), pcapInterface,
431 powerCapSamplPeriodProp, pcapSamplPeriodUs);
432 if (ec.value())
433 {
434 lg2::error("Error in setPcapSamplPeriod property: {ERROR}", "ERROR",
435 ec.message());
436 elog<InternalFailure>();
437 return false;
438 }
439
440 return true;
441}
442
Vernon Mauerydca47202023-07-27 11:32:01 -0700443std::optional<std::string> readAssetTag(ipmi::Context::ptr& ctx)
Tom Josephbe5eaa12017-07-12 19:54:44 +0530444{
Tom Josephbe5eaa12017-07-12 19:54:44 +0530445 // Read the object tree with the inventory root to figure out the object
446 // that has implemented the Asset tag interface.
Vernon Mauerydca47202023-07-27 11:32:01 -0700447 ipmi::DbusObjectInfo objectInfo;
448 boost::system::error_code ec = getDbusObject(
449 ctx, dcmi::assetTagIntf, ipmi::sensor::inventoryRoot, "", objectInfo);
450 if (ec.value())
Tom Josephbe5eaa12017-07-12 19:54:44 +0530451 {
Vernon Mauerydca47202023-07-27 11:32:01 -0700452 return std::nullopt;
George Liu3e3cc352023-07-26 15:59:31 +0800453 }
Vernon Mauerydca47202023-07-27 11:32:01 -0700454
455 std::string assetTag{};
Patrick Williams1318a5e2024-08-16 15:19:54 -0400456 ec =
457 ipmi::getDbusProperty(ctx, objectInfo.second, objectInfo.first,
458 dcmi::assetTagIntf, dcmi::assetTagProp, assetTag);
Vernon Mauerydca47202023-07-27 11:32:01 -0700459 if (ec.value())
George Liu3e3cc352023-07-26 15:59:31 +0800460 {
George Liu7fa28712024-07-17 19:26:23 +0800461 lg2::error("Error in reading asset tag: {ERROR}", "ERROR",
462 ec.message());
Tom Josephbe5eaa12017-07-12 19:54:44 +0530463 elog<InternalFailure>();
Vernon Mauerydca47202023-07-27 11:32:01 -0700464 return std::nullopt;
Tom Josephbe5eaa12017-07-12 19:54:44 +0530465 }
Vernon Mauerydca47202023-07-27 11:32:01 -0700466
467 return assetTag;
Tom Josephbe5eaa12017-07-12 19:54:44 +0530468}
469
Vernon Mauerydca47202023-07-27 11:32:01 -0700470bool writeAssetTag(ipmi::Context::ptr& ctx, const std::string& assetTag)
Tom Josephbe5b9892017-07-15 00:55:23 +0530471{
Tom Josephbe5b9892017-07-15 00:55:23 +0530472 // Read the object tree with the inventory root to figure out the object
473 // that has implemented the Asset tag interface.
Vernon Mauerydca47202023-07-27 11:32:01 -0700474 ipmi::DbusObjectInfo objectInfo;
475 boost::system::error_code ec = getDbusObject(
476 ctx, dcmi::assetTagIntf, ipmi::sensor::inventoryRoot, "", objectInfo);
477 if (ec.value())
Tom Josephbe5b9892017-07-15 00:55:23 +0530478 {
Vernon Mauerydca47202023-07-27 11:32:01 -0700479 return false;
George Liu3e3cc352023-07-26 15:59:31 +0800480 }
Vernon Mauerydca47202023-07-27 11:32:01 -0700481
Patrick Williams1318a5e2024-08-16 15:19:54 -0400482 ec =
483 ipmi::setDbusProperty(ctx, objectInfo.second, objectInfo.first,
484 dcmi::assetTagIntf, dcmi::assetTagProp, assetTag);
Vernon Mauerydca47202023-07-27 11:32:01 -0700485 if (ec.value())
George Liu3e3cc352023-07-26 15:59:31 +0800486 {
George Liu7fa28712024-07-17 19:26:23 +0800487 lg2::error("Error in writing asset tag: {ERROR}", "ERROR",
488 ec.message());
Tom Josephbe5b9892017-07-15 00:55:23 +0530489 elog<InternalFailure>();
Vernon Mauerydca47202023-07-27 11:32:01 -0700490 return false;
Tom Josephbe5b9892017-07-15 00:55:23 +0530491 }
Vernon Mauerydca47202023-07-27 11:32:01 -0700492 return true;
Tom Josephbe5b9892017-07-15 00:55:23 +0530493}
494
Vernon Maueryefb5ae52023-07-27 11:35:43 -0700495std::optional<std::string> getHostName(ipmi::Context::ptr& ctx)
Vladislav Vovchenko8f7a6f62017-08-17 00:31:14 +0300496{
Vernon Maueryefb5ae52023-07-27 11:35:43 -0700497 std::string service{};
Patrick Williams1318a5e2024-08-16 15:19:54 -0400498 boost::system::error_code ec =
499 ipmi::getService(ctx, networkConfigIntf, networkConfigObj, service);
Vernon Maueryefb5ae52023-07-27 11:35:43 -0700500 if (ec.value())
501 {
502 return std::nullopt;
503 }
504 std::string hostname{};
505 ec = ipmi::getDbusProperty(ctx, service, networkConfigObj,
506 networkConfigIntf, hostNameProp, hostname);
507 if (ec.value())
508 {
George Liu7fa28712024-07-17 19:26:23 +0800509 lg2::error("Error fetching hostname");
Vernon Maueryefb5ae52023-07-27 11:35:43 -0700510 elog<InternalFailure>();
511 return std::nullopt;
512 }
513 return hostname;
Vladislav Vovchenko8f7a6f62017-08-17 00:31:14 +0300514}
515
Patrick Williams69b4c282025-03-03 11:19:13 -0500516std::optional<EthernetInterface::DHCPConf> getDHCPEnabled(
517 ipmi::Context::ptr& ctx)
Nagaraju Goruganti22be97b2018-02-07 01:19:59 -0600518{
Johnathan Mantey74a21022018-12-13 13:17:56 -0800519 auto ethdevice = ipmi::getChannelName(ethernetDefaultChannelNum);
Vernon Mauery6475b5c2023-07-27 14:52:51 -0700520 ipmi::DbusObjectInfo ethernetObj{};
521 boost::system::error_code ec = ipmi::getDbusObject(
522 ctx, ethernetIntf, networkRoot, ethdevice, ethernetObj);
523 if (ec.value())
Deepak Kodihalli0b459552018-02-06 06:25:12 -0600524 {
Vernon Mauery6475b5c2023-07-27 14:52:51 -0700525 return std::nullopt;
526 }
527 std::string service{};
528 ec = ipmi::getService(ctx, ethernetIntf, ethernetObj.first, service);
529 if (ec.value())
530 {
531 return std::nullopt;
532 }
533 std::string dhcpVal{};
534 ec = ipmi::getDbusProperty(ctx, service, ethernetObj.first, ethernetIntf,
535 "DHCPEnabled", dhcpVal);
536 if (ec.value())
537 {
538 return std::nullopt;
Deepak Kodihalli0b459552018-02-06 06:25:12 -0600539 }
540
Vernon Mauery6475b5c2023-07-27 14:52:51 -0700541 return EthernetInterface::convertDHCPConfFromString(dhcpVal);
542}
543
544std::optional<bool> getDHCPOption(ipmi::Context::ptr& ctx,
545 const std::string& prop)
546{
George Liucfd7fa82024-01-22 11:38:29 +0800547 ipmi::ObjectTree objectTree;
548 if (ipmi::getAllDbusObjects(ctx, networkRoot, dhcpIntf, objectTree))
Vernon Mauery6475b5c2023-07-27 14:52:51 -0700549 {
550 return std::nullopt;
Deepak Kodihalli0b459552018-02-06 06:25:12 -0600551 }
552
George Liucfd7fa82024-01-22 11:38:29 +0800553 for (const auto& [path, serviceMap] : objectTree)
554 {
555 for (const auto& [service, object] : serviceMap)
556 {
557 bool value{};
558 if (ipmi::getDbusProperty(ctx, service, path, dhcpIntf, prop,
559 value))
560 {
561 return std::nullopt;
562 }
563
564 if (value)
565 {
566 return true;
567 }
568 }
569 }
570
571 return false;
Vernon Mauery6475b5c2023-07-27 14:52:51 -0700572}
573
574bool setDHCPOption(ipmi::Context::ptr& ctx, std::string prop, bool value)
575{
George Liucfd7fa82024-01-22 11:38:29 +0800576 ipmi::ObjectTree objectTree;
577 if (ipmi::getAllDbusObjects(ctx, networkRoot, dhcpIntf, objectTree))
Vernon Mauery6475b5c2023-07-27 14:52:51 -0700578 {
George Liucfd7fa82024-01-22 11:38:29 +0800579 return false;
Vernon Mauery6475b5c2023-07-27 14:52:51 -0700580 }
George Liucfd7fa82024-01-22 11:38:29 +0800581
582 for (const auto& [path, serviceMap] : objectTree)
583 {
584 for (const auto& [service, object] : serviceMap)
585 {
586 if (ipmi::setDbusProperty(ctx, service, path, dhcpIntf, prop,
587 value))
588 {
589 return false;
590 }
591 }
592 }
593
594 return true;
Deepak Kodihalli0b459552018-02-06 06:25:12 -0600595}
596
Tom Josephbe5eaa12017-07-12 19:54:44 +0530597} // namespace dcmi
Chris Austen1810bec2015-10-13 12:12:39 -0500598
Vernon Maueryd4222fd2023-07-27 11:26:51 -0700599ipmi::RspType<uint16_t, // reserved
600 uint8_t, // exception actions
601 uint16_t, // power limit requested in watts
602 uint32_t, // correction time in milliseconds
603 uint16_t, // reserved
604 uint16_t // statistics sampling period in seconds
605 >
606 getPowerLimit(ipmi::Context::ptr ctx, uint16_t reserved)
Tom Josephb9d86f42017-07-26 18:03:47 +0530607{
Kirill Pakhomov2c2af2c2018-11-06 16:06:10 +0300608 if (!dcmi::isDCMIPowerMgmtSupported())
609 {
Vernon Maueryd4222fd2023-07-27 11:26:51 -0700610 return ipmi::responseInvalidCommand();
Kirill Pakhomov2c2af2c2018-11-06 16:06:10 +0300611 }
Vernon Maueryd4222fd2023-07-27 11:26:51 -0700612 if (reserved)
Tom Josephb9d86f42017-07-26 18:03:47 +0530613 {
Vernon Maueryd4222fd2023-07-27 11:26:51 -0700614 return ipmi::responseInvalidFieldRequest();
Tom Josephb9d86f42017-07-26 18:03:47 +0530615 }
616
Vernon Maueryd4222fd2023-07-27 11:26:51 -0700617 std::optional<uint16_t> pcapValue = dcmi::getPcap(ctx);
618 std::optional<bool> pcapEnable = dcmi::getPcapEnabled(ctx);
Thang Trana95978d2025-06-04 10:39:15 +0700619 std::optional<uint32_t> pcapCorrectTime = dcmi::getPcapCorrectTime(ctx);
620 std::optional<uint16_t> pcapSamplPeriod = dcmi::getPcapSamplPeriod(ctx);
621 std::optional<std::string> pcapExceptAct = dcmi::getPcapExceptAction(ctx);
622
623 if (!pcapValue || !pcapEnable || !pcapCorrectTime || !pcapSamplPeriod ||
624 !pcapExceptAct)
Vernon Maueryd4222fd2023-07-27 11:26:51 -0700625 {
626 return ipmi::responseUnspecifiedError();
627 }
628
629 constexpr uint16_t reserved1{};
630 constexpr uint16_t reserved2{};
Thang Trana95978d2025-06-04 10:39:15 +0700631 uint8_t exception;
632
633 std::string exceptAct = pcapExceptAct.value();
634
635 if (exceptAct == dcmi::DbusExceptAct::noAction)
636 {
637 exception = static_cast<uint8_t>(dcmi::ExceptActOptions::NoAction);
638 }
639 else if (exceptAct == dcmi::DbusExceptAct::hardPowerOff)
640 {
641 exception = static_cast<uint8_t>(dcmi::ExceptActOptions::HardPowerOff);
642 }
643 else if (exceptAct == dcmi::DbusExceptAct::logEventOnly)
644 {
645 exception = static_cast<uint8_t>(dcmi::ExceptActOptions::LogEventOnly);
646 }
647 else if (exceptAct == dcmi::DbusExceptAct::oem)
648 {
649 exception = static_cast<uint8_t>(dcmi::ExceptActOptions::Oem02);
650 }
651 else
652 {
653 return ipmi::responseUnspecifiedError();
654 }
655
656 if (!pcapEnable.value())
Tom Josephb9d86f42017-07-26 18:03:47 +0530657 {
Vernon Maueryd4222fd2023-07-27 11:26:51 -0700658 constexpr ipmi::Cc responseNoPowerLimitSet = 0x80;
Vernon Maueryd4222fd2023-07-27 11:26:51 -0700659 return ipmi::response(responseNoPowerLimitSet, reserved1, exception,
Thang Trana95978d2025-06-04 10:39:15 +0700660 pcapValue.value(), pcapCorrectTime.value(),
661 reserved2, pcapSamplPeriod.value());
Tom Josephb9d86f42017-07-26 18:03:47 +0530662 }
Thang Trana95978d2025-06-04 10:39:15 +0700663
664 return ipmi::responseSuccess(reserved1, exception, pcapValue.value(),
665 pcapCorrectTime.value(), reserved2,
666 pcapSamplPeriod.value());
Tom Josephb9d86f42017-07-26 18:03:47 +0530667}
668
Vernon Maueryd4222fd2023-07-27 11:26:51 -0700669ipmi::RspType<> setPowerLimit(ipmi::Context::ptr& ctx, uint16_t reserved1,
Thang Tranfd9c3612023-09-20 11:16:59 +0700670 uint8_t reserved2, uint8_t exceptionAction,
671 uint16_t powerLimit, uint32_t correctionTime,
672 uint16_t reserved3, uint16_t statsPeriod)
Tom Joseph46fa37d2017-07-26 18:11:55 +0530673{
Kirill Pakhomov2c2af2c2018-11-06 16:06:10 +0300674 if (!dcmi::isDCMIPowerMgmtSupported())
675 {
George Liu7fa28712024-07-17 19:26:23 +0800676 lg2::error("DCMI Power management is unsupported!");
Vernon Maueryd4222fd2023-07-27 11:26:51 -0700677 return ipmi::responseInvalidCommand();
Kirill Pakhomov2c2af2c2018-11-06 16:06:10 +0300678 }
679
Thang Trana95978d2025-06-04 10:39:15 +0700680 if (reserved1 || reserved2 || reserved3)
Tom Joseph46fa37d2017-07-26 18:11:55 +0530681 {
Vernon Maueryd4222fd2023-07-27 11:26:51 -0700682 return ipmi::responseInvalidFieldRequest();
Tom Joseph46fa37d2017-07-26 18:11:55 +0530683 }
Vernon Maueryd4222fd2023-07-27 11:26:51 -0700684
685 if (!dcmi::setPcap(ctx, powerLimit))
Tom Joseph46fa37d2017-07-26 18:11:55 +0530686 {
Vernon Maueryd4222fd2023-07-27 11:26:51 -0700687 return ipmi::responseUnspecifiedError();
Tom Joseph46fa37d2017-07-26 18:11:55 +0530688 }
689
Thang Trana95978d2025-06-04 10:39:15 +0700690 /*
691 * As defined in table 6-18 of DCMI specification version 1.5.
692 * The Exception action value is mapped as below
693 * 00h - No Action
694 * 01h - Hard Power Off system and log events to SEL
695 * 02h - 10h OEM defined actions
696 * 11h - Log event to SEL only
697 * 12h-FFh Reserved
698 */
699 if (exceptionAction >= 0x12)
700 {
701 return ipmi::responseUnspecifiedError();
702 }
703
704 std::string exceptActStr;
705
706 switch (static_cast<dcmi::ExceptActOptions>(exceptionAction))
707 {
708 case dcmi::ExceptActOptions::NoAction:
709 {
710 exceptActStr = dcmi::DbusExceptAct::noAction;
711 break;
712 }
713 case dcmi::ExceptActOptions::HardPowerOff:
714 {
715 exceptActStr = dcmi::DbusExceptAct::hardPowerOff;
716 break;
717 }
718 case dcmi::ExceptActOptions::LogEventOnly:
719 {
720 exceptActStr = dcmi::DbusExceptAct::logEventOnly;
721 break;
722 }
723 default:
724 {
725 exceptActStr = dcmi::DbusExceptAct::oem;
726 break;
727 }
728 }
729
730 if (!dcmi::setPcapExceptAction(ctx, exceptActStr))
731 {
732 return ipmi::responseUnspecifiedError();
733 }
734
735 if (!dcmi::setPcapCorrectTime(ctx, correctionTime))
736 {
737 return ipmi::responseUnspecifiedError();
738 }
739
740 if (!dcmi::setPcapSamplPeriod(ctx, statsPeriod))
741 {
742 return ipmi::responseUnspecifiedError();
743 }
Tom Joseph46fa37d2017-07-26 18:11:55 +0530744
Vernon Maueryd4222fd2023-07-27 11:26:51 -0700745 return ipmi::responseSuccess();
Tom Joseph46fa37d2017-07-26 18:11:55 +0530746}
747
Vernon Maueryd4222fd2023-07-27 11:26:51 -0700748ipmi::RspType<> applyPowerLimit(ipmi::Context::ptr& ctx, bool enabled,
749 uint7_t reserved1, uint16_t reserved2)
Tom Joseph6c8d51b2017-07-26 18:18:06 +0530750{
Kirill Pakhomov2c2af2c2018-11-06 16:06:10 +0300751 if (!dcmi::isDCMIPowerMgmtSupported())
752 {
George Liu7fa28712024-07-17 19:26:23 +0800753 lg2::error("DCMI Power management is unsupported!");
Vernon Maueryd4222fd2023-07-27 11:26:51 -0700754 return ipmi::responseInvalidCommand();
755 }
756 if (reserved1 || reserved2)
757 {
758 return ipmi::responseInvalidFieldRequest();
Kirill Pakhomov2c2af2c2018-11-06 16:06:10 +0300759 }
760
Vernon Maueryd4222fd2023-07-27 11:26:51 -0700761 if (!dcmi::setPcapEnable(ctx, enabled))
Tom Joseph6c8d51b2017-07-26 18:18:06 +0530762 {
Vernon Maueryd4222fd2023-07-27 11:26:51 -0700763 return ipmi::responseUnspecifiedError();
Tom Joseph6c8d51b2017-07-26 18:18:06 +0530764 }
765
George Liu7fa28712024-07-17 19:26:23 +0800766 lg2::info("Set Power Cap Enable: {POWERCAPENABLE}", "POWERCAPENABLE",
767 enabled);
Tom Joseph6c8d51b2017-07-26 18:18:06 +0530768
Vernon Maueryd4222fd2023-07-27 11:26:51 -0700769 return ipmi::responseSuccess();
Tom Joseph6c8d51b2017-07-26 18:18:06 +0530770}
771
Vernon Mauerydca47202023-07-27 11:32:01 -0700772ipmi::RspType<uint8_t, // total tag length
773 std::vector<char> // tag data
774 >
775 getAssetTag(ipmi::Context::ptr& ctx, uint8_t offset, uint8_t count)
Tom Joseph6f6dd4d2017-07-12 20:07:11 +0530776{
Vernon Mauerydca47202023-07-27 11:32:01 -0700777 // Verify offset to read and number of bytes to read are not exceeding
778 // the range.
779 if ((offset > dcmi::assetTagMaxOffset) || (count > dcmi::maxBytes) ||
780 ((offset + count) > dcmi::assetTagMaxSize))
Tom Joseph6f6dd4d2017-07-12 20:07:11 +0530781 {
Vernon Mauerydca47202023-07-27 11:32:01 -0700782 return ipmi::responseParmOutOfRange();
Tom Joseph6f6dd4d2017-07-12 20:07:11 +0530783 }
784
Vernon Mauerydca47202023-07-27 11:32:01 -0700785 std::optional<std::string> assetTagResp = dcmi::readAssetTag(ctx);
786 if (!assetTagResp)
Tom Joseph6f6dd4d2017-07-12 20:07:11 +0530787 {
Vernon Mauerydca47202023-07-27 11:32:01 -0700788 return ipmi::responseUnspecifiedError();
Tom Joseph6f6dd4d2017-07-12 20:07:11 +0530789 }
790
Vernon Mauerydca47202023-07-27 11:32:01 -0700791 std::string& assetTag = assetTagResp.value();
792 // If the asset tag is longer than 63 bytes, restrict it to 63 bytes to
793 // suit Get Asset Tag command.
Tom Joseph6f6dd4d2017-07-12 20:07:11 +0530794 if (assetTag.size() > dcmi::assetTagMaxSize)
795 {
796 assetTag.resize(dcmi::assetTagMaxSize);
797 }
798
Vernon Mauerydca47202023-07-27 11:32:01 -0700799 if (offset >= assetTag.size())
Tom Joseph6f6dd4d2017-07-12 20:07:11 +0530800 {
Vernon Mauerydca47202023-07-27 11:32:01 -0700801 return ipmi::responseParmOutOfRange();
Tom Joseph6f6dd4d2017-07-12 20:07:11 +0530802 }
803
Vernon Mauerydca47202023-07-27 11:32:01 -0700804 // silently truncate reads beyond the end of assetTag
805 if ((offset + count) >= assetTag.size())
806 {
807 count = assetTag.size() - offset;
808 }
Tom Joseph6f6dd4d2017-07-12 20:07:11 +0530809
Vernon Mauerydca47202023-07-27 11:32:01 -0700810 auto totalTagSize = static_cast<uint8_t>(assetTag.size());
811 std::vector<char> data{assetTag.begin() + offset,
812 assetTag.begin() + offset + count};
Tom Joseph6f6dd4d2017-07-12 20:07:11 +0530813
Vernon Mauerydca47202023-07-27 11:32:01 -0700814 return ipmi::responseSuccess(totalTagSize, data);
Tom Joseph6f6dd4d2017-07-12 20:07:11 +0530815}
816
Vernon Mauerydca47202023-07-27 11:32:01 -0700817ipmi::RspType<uint8_t // new asset tag length
818 >
819 setAssetTag(ipmi::Context::ptr& ctx, uint8_t offset, uint8_t count,
820 const std::vector<char>& data)
Tom Joseph545dd232017-07-12 20:20:49 +0530821{
Vernon Mauerydca47202023-07-27 11:32:01 -0700822 // Verify offset to read and number of bytes to read are not exceeding
823 // the range.
824 if ((offset > dcmi::assetTagMaxOffset) || (count > dcmi::maxBytes) ||
825 ((offset + count) > dcmi::assetTagMaxSize))
Tom Joseph545dd232017-07-12 20:20:49 +0530826 {
Vernon Mauerydca47202023-07-27 11:32:01 -0700827 return ipmi::responseParmOutOfRange();
828 }
829 if (data.size() != count)
830 {
831 return ipmi::responseReqDataLenInvalid();
Tom Joseph545dd232017-07-12 20:20:49 +0530832 }
833
Vernon Mauerydca47202023-07-27 11:32:01 -0700834 std::optional<std::string> assetTagResp = dcmi::readAssetTag(ctx);
835 if (!assetTagResp)
Tom Joseph545dd232017-07-12 20:20:49 +0530836 {
Vernon Mauerydca47202023-07-27 11:32:01 -0700837 return ipmi::responseUnspecifiedError();
Tom Joseph545dd232017-07-12 20:20:49 +0530838 }
Vernon Mauerydca47202023-07-27 11:32:01 -0700839
840 std::string& assetTag = assetTagResp.value();
841
842 if (offset > assetTag.size())
Tom Joseph545dd232017-07-12 20:20:49 +0530843 {
Vernon Mauerydca47202023-07-27 11:32:01 -0700844 return ipmi::responseParmOutOfRange();
Tom Joseph545dd232017-07-12 20:20:49 +0530845 }
Vernon Mauerydca47202023-07-27 11:32:01 -0700846
847 // operation is to truncate at offset and append new data
848 assetTag.resize(offset);
849 assetTag.append(data.begin(), data.end());
850
851 if (!dcmi::writeAssetTag(ctx, assetTag))
852 {
853 return ipmi::responseUnspecifiedError();
854 }
855
856 auto totalTagSize = static_cast<uint8_t>(assetTag.size());
857 return ipmi::responseSuccess(totalTagSize);
Tom Joseph545dd232017-07-12 20:20:49 +0530858}
859
Vernon Maueryefb5ae52023-07-27 11:35:43 -0700860ipmi::RspType<uint8_t, // length
861 std::vector<char> // data
862 >
863 getMgmntCtrlIdStr(ipmi::Context::ptr& ctx, uint8_t offset, uint8_t count)
Vladislav Vovchenko8f7a6f62017-08-17 00:31:14 +0300864{
Vernon Maueryefb5ae52023-07-27 11:35:43 -0700865 if (count > dcmi::maxBytes || offset + count > dcmi::maxCtrlIdStrLen)
Vladislav Vovchenko8f7a6f62017-08-17 00:31:14 +0300866 {
Vernon Maueryefb5ae52023-07-27 11:35:43 -0700867 return ipmi::responseParmOutOfRange();
Vladislav Vovchenko8f7a6f62017-08-17 00:31:14 +0300868 }
869
Vernon Maueryefb5ae52023-07-27 11:35:43 -0700870 std::optional<std::string> hostnameResp = dcmi::getHostName(ctx);
871 if (!hostnameResp)
Vladislav Vovchenko8f7a6f62017-08-17 00:31:14 +0300872 {
Vernon Maueryefb5ae52023-07-27 11:35:43 -0700873 return ipmi::responseUnspecifiedError();
Vladislav Vovchenko8f7a6f62017-08-17 00:31:14 +0300874 }
875
Vernon Maueryefb5ae52023-07-27 11:35:43 -0700876 std::string& hostname = hostnameResp.value();
877 // If the id string is longer than 63 bytes, restrict it to 63 bytes to
878 // suit set management ctrl str command.
879 if (hostname.size() > dcmi::maxCtrlIdStrLen)
Vladislav Vovchenko8f7a6f62017-08-17 00:31:14 +0300880 {
Vernon Maueryefb5ae52023-07-27 11:35:43 -0700881 hostname.resize(dcmi::maxCtrlIdStrLen);
Vladislav Vovchenko8f7a6f62017-08-17 00:31:14 +0300882 }
Vladislav Vovchenko8f7a6f62017-08-17 00:31:14 +0300883
Vernon Maueryefb5ae52023-07-27 11:35:43 -0700884 if (offset >= hostname.size())
885 {
886 return ipmi::responseParmOutOfRange();
887 }
888
889 // silently truncate reads beyond the end of hostname
890 if ((offset + count) >= hostname.size())
891 {
892 count = hostname.size() - offset;
893 }
894
895 auto nameSize = static_cast<uint8_t>(hostname.size());
896 std::vector<char> data{hostname.begin() + offset,
897 hostname.begin() + offset + count};
898
899 return ipmi::responseSuccess(nameSize, data);
Vladislav Vovchenko8f7a6f62017-08-17 00:31:14 +0300900}
901
Patrick Williams69b4c282025-03-03 11:19:13 -0500902ipmi::RspType<uint8_t> setMgmntCtrlIdStr(ipmi::Context::ptr& ctx,
903 uint8_t offset, uint8_t count,
904 std::vector<char> data)
Vladislav Vovchenko8f7a6f62017-08-17 00:31:14 +0300905{
Vernon Maueryefb5ae52023-07-27 11:35:43 -0700906 if ((offset > dcmi::maxCtrlIdStrLen) || (count > dcmi::maxBytes) ||
907 ((offset + count) > dcmi::maxCtrlIdStrLen))
Vladislav Vovchenko8f7a6f62017-08-17 00:31:14 +0300908 {
Vernon Maueryefb5ae52023-07-27 11:35:43 -0700909 return ipmi::responseParmOutOfRange();
910 }
911 if (data.size() != count)
912 {
913 return ipmi::responseReqDataLenInvalid();
914 }
915 bool terminalWrite{data.back() == '\0'};
916 if (terminalWrite)
917 {
918 // remove the null termination from the data (no need with std::string)
919 data.resize(count - 1);
Vladislav Vovchenko8f7a6f62017-08-17 00:31:14 +0300920 }
921
Vernon Maueryefb5ae52023-07-27 11:35:43 -0700922 static std::string hostname{};
923 // read in the current value if not starting at offset 0
924 if (hostname.size() == 0 && offset != 0)
Vladislav Vovchenko8f7a6f62017-08-17 00:31:14 +0300925 {
Vernon Maueryefb5ae52023-07-27 11:35:43 -0700926 /* read old ctrlIdStr */
927 std::optional<std::string> hostnameResp = dcmi::getHostName(ctx);
928 if (!hostnameResp)
Vladislav Vovchenko8f7a6f62017-08-17 00:31:14 +0300929 {
Vernon Maueryefb5ae52023-07-27 11:35:43 -0700930 return ipmi::responseUnspecifiedError();
Vladislav Vovchenko8f7a6f62017-08-17 00:31:14 +0300931 }
Vernon Maueryefb5ae52023-07-27 11:35:43 -0700932 hostname = hostnameResp.value();
933 hostname.resize(offset);
Vladislav Vovchenko8f7a6f62017-08-17 00:31:14 +0300934 }
935
Vernon Maueryefb5ae52023-07-27 11:35:43 -0700936 // operation is to truncate at offset and append new data
937 hostname.append(data.begin(), data.end());
938
939 // do the update if this is the last write
940 if (terminalWrite)
941 {
942 boost::system::error_code ec = ipmi::setDbusProperty(
943 ctx, dcmi::networkServiceName, dcmi::networkConfigObj,
944 dcmi::networkConfigIntf, dcmi::hostNameProp, hostname);
945 hostname.clear();
946 if (ec.value())
947 {
948 return ipmi::responseUnspecifiedError();
949 }
950 }
951
952 auto totalIdSize = static_cast<uint8_t>(offset + count);
953 return ipmi::responseSuccess(totalIdSize);
Vladislav Vovchenko8f7a6f62017-08-17 00:31:14 +0300954}
955
Vernon Maueryf038dc02023-07-27 14:06:11 -0700956ipmi::RspType<ipmi::message::Payload> getDCMICapabilities(uint8_t parameter)
Dhruvaraj Subhashchandrane29be412018-01-16 05:11:56 -0600957{
Kirill Pakhomova2573622018-11-02 19:00:18 +0300958 std::ifstream dcmiCapFile(dcmi::gDCMICapabilitiesConfig);
Dhruvaraj Subhashchandrane29be412018-01-16 05:11:56 -0600959 if (!dcmiCapFile.is_open())
960 {
George Liu7fa28712024-07-17 19:26:23 +0800961 lg2::error("DCMI Capabilities file not found");
Vernon Maueryf038dc02023-07-27 14:06:11 -0700962 return ipmi::responseUnspecifiedError();
Dhruvaraj Subhashchandrane29be412018-01-16 05:11:56 -0600963 }
964
965 auto data = nlohmann::json::parse(dcmiCapFile, nullptr, false);
966 if (data.is_discarded())
967 {
George Liu7fa28712024-07-17 19:26:23 +0800968 lg2::error("DCMI Capabilities JSON parser failure");
Vernon Maueryf038dc02023-07-27 14:06:11 -0700969 return ipmi::responseUnspecifiedError();
Dhruvaraj Subhashchandrane29be412018-01-16 05:11:56 -0600970 }
971
Vernon Maueryf038dc02023-07-27 14:06:11 -0700972 constexpr bool reserved1{};
973 constexpr uint5_t reserved5{};
974 constexpr uint7_t reserved7{};
975 constexpr uint8_t reserved8{};
976 constexpr uint16_t reserved16{};
Dhruvaraj Subhashchandrane29be412018-01-16 05:11:56 -0600977
Vernon Maueryf038dc02023-07-27 14:06:11 -0700978 ipmi::message::Payload payload;
979 payload.pack(dcmi::specMajorVersion, dcmi::specMinorVersion,
980 dcmi::parameterRevision);
981
982 enum class DCMICapParameters : uint8_t
Dhruvaraj Subhashchandrane29be412018-01-16 05:11:56 -0600983 {
Vernon Maueryf038dc02023-07-27 14:06:11 -0700984 SupportedDcmiCaps = 0x01, // Supported DCMI Capabilities
985 MandatoryPlatAttributes = 0x02, // Mandatory Platform Attributes
986 OptionalPlatAttributes = 0x03, // Optional Platform Attributes
987 ManageabilityAccessAttributes = 0x04, // Manageability Access Attributes
988 };
Dhruvaraj Subhashchandrane29be412018-01-16 05:11:56 -0600989
Vernon Maueryf038dc02023-07-27 14:06:11 -0700990 switch (static_cast<DCMICapParameters>(parameter))
Dhruvaraj Subhashchandrane29be412018-01-16 05:11:56 -0600991 {
Vernon Maueryf038dc02023-07-27 14:06:11 -0700992 case DCMICapParameters::SupportedDcmiCaps:
Dhruvaraj Subhashchandrane29be412018-01-16 05:11:56 -0600993 {
Vernon Maueryf038dc02023-07-27 14:06:11 -0700994 bool powerManagement = data.value("PowerManagement", 0);
995 bool oobSecondaryLan = data.value("OOBSecondaryLan", 0);
996 bool serialTMode = data.value("SerialTMODE", 0);
997 bool inBandSystemInterfaceChannel =
998 data.value("InBandSystemInterfaceChannel", 0);
999 payload.pack(reserved8, powerManagement, reserved7,
1000 inBandSystemInterfaceChannel, serialTMode,
1001 oobSecondaryLan, reserved5);
1002 break;
Dhruvaraj Subhashchandrane29be412018-01-16 05:11:56 -06001003 }
Vernon Maueryf038dc02023-07-27 14:06:11 -07001004 // Mandatory Platform Attributes
1005 case DCMICapParameters::MandatoryPlatAttributes:
Dhruvaraj Subhashchandrane29be412018-01-16 05:11:56 -06001006 {
Vernon Maueryf038dc02023-07-27 14:06:11 -07001007 bool selAutoRollOver = data.value("SELAutoRollOver", 0);
1008 bool flushEntireSELUponRollOver =
1009 data.value("FlushEntireSELUponRollOver", 0);
1010 bool recordLevelSELFlushUponRollOver =
1011 data.value("RecordLevelSELFlushUponRollOver", 0);
Patrick Williams1318a5e2024-08-16 15:19:54 -04001012 uint12_t numberOfSELEntries =
1013 data.value("NumberOfSELEntries", 0xcac);
Vernon Maueryf038dc02023-07-27 14:06:11 -07001014 uint8_t tempMonitoringSamplingFreq =
1015 data.value("TempMonitoringSamplingFreq", 0);
1016 payload.pack(numberOfSELEntries, reserved1,
1017 recordLevelSELFlushUponRollOver,
1018 flushEntireSELUponRollOver, selAutoRollOver,
1019 reserved16, tempMonitoringSamplingFreq);
1020 break;
1021 }
1022 // Optional Platform Attributes
1023 case DCMICapParameters::OptionalPlatAttributes:
1024 {
Matt Simmering68d9d402023-11-09 14:22:11 -08001025 uint7_t powerMgmtDeviceTargetAddress =
Vernon Maueryf038dc02023-07-27 14:06:11 -07001026 data.value("PowerMgmtDeviceSlaveAddress", 0);
1027 uint4_t bmcChannelNumber = data.value("BMCChannelNumber", 0);
1028 uint4_t deviceRivision = data.value("DeviceRivision", 0);
Matt Simmering68d9d402023-11-09 14:22:11 -08001029 payload.pack(powerMgmtDeviceTargetAddress, reserved1,
1030 deviceRivision, bmcChannelNumber);
Vernon Maueryf038dc02023-07-27 14:06:11 -07001031 break;
1032 }
1033 // Manageability Access Attributes
1034 case DCMICapParameters::ManageabilityAccessAttributes:
1035 {
1036 uint8_t mandatoryPrimaryLanOOBSupport =
1037 data.value("MandatoryPrimaryLanOOBSupport", 0xff);
1038 uint8_t optionalSecondaryLanOOBSupport =
1039 data.value("OptionalSecondaryLanOOBSupport", 0xff);
1040 uint8_t optionalSerialOOBMTMODECapability =
1041 data.value("OptionalSerialOOBMTMODECapability", 0xff);
1042 payload.pack(mandatoryPrimaryLanOOBSupport,
1043 optionalSecondaryLanOOBSupport,
1044 optionalSerialOOBMTMODECapability);
1045 break;
1046 }
1047 default:
1048 {
George Liu7fa28712024-07-17 19:26:23 +08001049 lg2::error("Invalid input parameter");
Vernon Maueryf038dc02023-07-27 14:06:11 -07001050 return ipmi::responseInvalidFieldRequest();
Dhruvaraj Subhashchandrane29be412018-01-16 05:11:56 -06001051 }
1052 }
1053
Vernon Maueryf038dc02023-07-27 14:06:11 -07001054 return ipmi::responseSuccess(payload);
Dhruvaraj Subhashchandrane29be412018-01-16 05:11:56 -06001055}
1056
Deepak Kodihalliee717d72018-01-24 04:53:09 -06001057namespace dcmi
1058{
1059namespace temp_readings
1060{
1061
Patrick Williams69b4c282025-03-03 11:19:13 -05001062std::tuple<bool, bool, uint8_t> readTemp(ipmi::Context::ptr& ctx,
1063 const std::string& dbusService,
1064 const std::string& dbusPath)
Deepak Kodihallib1e8fba2018-01-24 04:57:10 -06001065{
1066 // Read the temperature value from d-bus object. Need some conversion.
Vernon Mauerycce9ffd2023-07-27 14:15:17 -07001067 // As per the interface xyz.openbmc_project.Sensor.Value, the
1068 // temperature is an double and in degrees C. It needs to be scaled by
1069 // using the formula Value * 10^Scale. The ipmi spec has the temperature
1070 // as a uint8_t, with a separate single bit for the sign.
Deepak Kodihallib1e8fba2018-01-24 04:57:10 -06001071
Vernon Mauerycce9ffd2023-07-27 14:15:17 -07001072 ipmi::PropertyMap result{};
1073 boost::system::error_code ec = ipmi::getAllDbusProperties(
1074 ctx, dbusService, dbusPath, "xyz.openbmc_project.Sensor.Value", result);
1075 if (ec.value())
1076 {
1077 return std::make_tuple(false, false, 0);
1078 }
Patrick Williams1318a5e2024-08-16 15:19:54 -04001079 auto temperature =
1080 std::visit(ipmi::VariantToDoubleVisitor(), result.at("Value"));
James Feist9cc0ea52018-10-09 10:53:11 -07001081 double absTemp = std::abs(temperature);
Deepak Kodihallib1e8fba2018-01-24 04:57:10 -06001082
James Feist9cc0ea52018-10-09 10:53:11 -07001083 auto findFactor = result.find("Scale");
1084 double factor = 0.0;
1085 if (findFactor != result.end())
Deepak Kodihallib1e8fba2018-01-24 04:57:10 -06001086 {
Vernon Maueryf442e112019-04-09 11:44:36 -07001087 factor = std::visit(ipmi::VariantToDoubleVisitor(), findFactor->second);
Deepak Kodihallib1e8fba2018-01-24 04:57:10 -06001088 }
James Feist9cc0ea52018-10-09 10:53:11 -07001089 double scale = std::pow(10, factor);
1090
1091 auto tempDegrees = absTemp * scale;
Vernon Mauerycce9ffd2023-07-27 14:15:17 -07001092 // Max absolute temp as per ipmi spec is 127.
1093 constexpr auto maxTemp = 127;
Deepak Kodihallib1e8fba2018-01-24 04:57:10 -06001094 if (tempDegrees > maxTemp)
1095 {
1096 tempDegrees = maxTemp;
1097 }
1098
Vernon Mauerycce9ffd2023-07-27 14:15:17 -07001099 return std::make_tuple(true, (temperature < 0),
1100 static_cast<uint8_t>(tempDegrees));
Deepak Kodihallib1e8fba2018-01-24 04:57:10 -06001101}
1102
Patrick Williams69b4c282025-03-03 11:19:13 -05001103std::tuple<std::vector<std::tuple<uint7_t, bool, uint8_t>>, uint8_t> read(
1104 ipmi::Context::ptr& ctx, const std::string& type, uint8_t instance,
1105 size_t count)
Deepak Kodihalliee717d72018-01-24 04:53:09 -06001106{
Vernon Mauerycce9ffd2023-07-27 14:15:17 -07001107 std::vector<std::tuple<uint7_t, bool, uint8_t>> response{};
Deepak Kodihallib1e8fba2018-01-24 04:57:10 -06001108
Kirill Pakhomova2573622018-11-02 19:00:18 +03001109 auto data = parseJSONConfig(gDCMISensorsConfig);
Vernon Mauerycce9ffd2023-07-27 14:15:17 -07001110 static const std::vector<nlohmann::json> empty{};
1111 std::vector<nlohmann::json> readings = data.value(type, empty);
Deepak Kodihallib1e8fba2018-01-24 04:57:10 -06001112 for (const auto& j : readings)
1113 {
Vernon Mauerycce9ffd2023-07-27 14:15:17 -07001114 // Max of 8 response data sets
1115 if (response.size() == count)
1116 {
1117 break;
1118 }
1119
Deepak Kodihallib1e8fba2018-01-24 04:57:10 -06001120 uint8_t instanceNum = j.value("instance", 0);
Vernon Mauerycce9ffd2023-07-27 14:15:17 -07001121 // Not in the instance range we're interested in
1122 if (instanceNum < instance)
Deepak Kodihallib1e8fba2018-01-24 04:57:10 -06001123 {
1124 continue;
1125 }
1126
1127 std::string path = j.value("dbus", "");
Vernon Mauerycce9ffd2023-07-27 14:15:17 -07001128 std::string service{};
1129 boost::system::error_code ec = ipmi::getService(
1130 ctx, "xyz.openbmc_project.Sensor.Value", path, service);
1131 if (ec.value())
Deepak Kodihallib1e8fba2018-01-24 04:57:10 -06001132 {
Vernon Mauerycce9ffd2023-07-27 14:15:17 -07001133 // not found on dbus
Deepak Kodihallib1e8fba2018-01-24 04:57:10 -06001134 continue;
1135 }
Vernon Mauerycce9ffd2023-07-27 14:15:17 -07001136
1137 const auto& [ok, sign, temp] = readTemp(ctx, service, path);
1138 if (ok)
1139 {
1140 response.emplace_back(uint7_t{temp}, sign, instanceNum);
1141 }
Deepak Kodihallib1e8fba2018-01-24 04:57:10 -06001142 }
1143
Vernon Mauerycce9ffd2023-07-27 14:15:17 -07001144 auto totalInstances =
1145 static_cast<uint8_t>(std::min(readings.size(), maxInstances));
1146 return std::make_tuple(response, totalInstances);
Deepak Kodihalliee717d72018-01-24 04:53:09 -06001147}
1148
Patrick Venture0b02be92018-08-31 11:55:55 -07001149} // namespace temp_readings
1150} // namespace dcmi
Deepak Kodihalliee717d72018-01-24 04:53:09 -06001151
Vernon Mauerycce9ffd2023-07-27 14:15:17 -07001152ipmi::RspType<uint8_t, // total instances for entity id
1153 uint8_t, // number of instances in this reply
1154 std::vector< // zero or more of the following two bytes
1155 std::tuple<uint7_t, // temperature value
1156 bool, // sign bit
1157 uint8_t // entity instance
1158 >>>
1159 getTempReadings(ipmi::Context::ptr& ctx, uint8_t sensorType,
1160 uint8_t entityId, uint8_t entityInstance,
1161 uint8_t instanceStart)
Deepak Kodihalliee717d72018-01-24 04:53:09 -06001162{
Vernon Mauerycce9ffd2023-07-27 14:15:17 -07001163 auto it = dcmi::entityIdToName.find(entityId);
Deepak Kodihalli0b459552018-02-06 06:25:12 -06001164 if (it == dcmi::entityIdToName.end())
Deepak Kodihalliee717d72018-01-24 04:53:09 -06001165 {
George Liu7fa28712024-07-17 19:26:23 +08001166 lg2::error("Unknown Entity ID: {ENTITY_ID}", "ENTITY_ID", entityId);
Vernon Mauerycce9ffd2023-07-27 14:15:17 -07001167 return ipmi::responseInvalidFieldRequest();
Deepak Kodihalliee717d72018-01-24 04:53:09 -06001168 }
1169
Vernon Mauerycce9ffd2023-07-27 14:15:17 -07001170 if (sensorType != dcmi::temperatureSensorType)
Deepak Kodihalliee717d72018-01-24 04:53:09 -06001171 {
George Liu7fa28712024-07-17 19:26:23 +08001172 lg2::error("Invalid sensor type: {SENSOR_TYPE}", "SENSOR_TYPE",
1173 sensorType);
Vernon Mauerycce9ffd2023-07-27 14:15:17 -07001174 return ipmi::responseInvalidFieldRequest();
Deepak Kodihalliee717d72018-01-24 04:53:09 -06001175 }
1176
Vernon Mauerycce9ffd2023-07-27 14:15:17 -07001177 uint8_t requestedRecords = (entityInstance == 0) ? dcmi::maxRecords : 1;
Deepak Kodihalliee717d72018-01-24 04:53:09 -06001178
Vernon Mauerycce9ffd2023-07-27 14:15:17 -07001179 // Read requested instances
1180 const auto& [temps, totalInstances] = dcmi::temp_readings::read(
1181 ctx, it->second, instanceStart, requestedRecords);
Deepak Kodihalliee717d72018-01-24 04:53:09 -06001182
Vernon Mauerycce9ffd2023-07-27 14:15:17 -07001183 auto numInstances = static_cast<uint8_t>(temps.size());
1184
1185 return ipmi::responseSuccess(totalInstances, numInstances, temps);
Deepak Kodihalliee717d72018-01-24 04:53:09 -06001186}
1187
Vernon Mauery6475b5c2023-07-27 14:52:51 -07001188ipmi::RspType<> setDCMIConfParams(ipmi::Context::ptr& ctx, uint8_t parameter,
1189 uint8_t setSelector,
1190 ipmi::message::Payload& payload)
Nagaraju Goruganti22be97b2018-02-07 01:19:59 -06001191{
Vernon Mauery6475b5c2023-07-27 14:52:51 -07001192 if (setSelector)
Nagaraju Goruganti22be97b2018-02-07 01:19:59 -06001193 {
Vernon Mauery6475b5c2023-07-27 14:52:51 -07001194 return ipmi::responseInvalidFieldRequest();
Nagaraju Goruganti22be97b2018-02-07 01:19:59 -06001195 }
Vernon Mauery6475b5c2023-07-27 14:52:51 -07001196 // Take action based on the Parameter Selector
1197 switch (static_cast<dcmi::DCMIConfigParameters>(parameter))
Nagaraju Goruganti22be97b2018-02-07 01:19:59 -06001198 {
Vernon Mauery6475b5c2023-07-27 14:52:51 -07001199 case dcmi::DCMIConfigParameters::ActivateDHCP:
Nagaraju Goruganti22be97b2018-02-07 01:19:59 -06001200 {
Vernon Mauery6475b5c2023-07-27 14:52:51 -07001201 uint7_t reserved{};
1202 bool activate{};
1203 if (payload.unpack(activate, reserved) || !payload.fullyUnpacked())
1204 {
1205 return ipmi::responseReqDataLenInvalid();
1206 }
1207 if (reserved)
1208 {
1209 return ipmi::responseInvalidFieldRequest();
1210 }
1211 std::optional<EthernetInterface::DHCPConf> dhcpEnabled =
1212 dcmi::getDHCPEnabled(ctx);
1213 if (!dhcpEnabled)
1214 {
1215 return ipmi::responseUnspecifiedError();
1216 }
1217 if (activate &&
1218 (dhcpEnabled.value() != EthernetInterface::DHCPConf::none))
1219 {
1220 // When these conditions are met we have to trigger DHCP
1221 // protocol restart using the latest parameter settings,
1222 // but as per n/w manager design, each time when we
1223 // update n/w parameters, n/w service is restarted. So
1224 // we no need to take any action in this case.
1225 }
1226 break;
Nagaraju Goruganti22be97b2018-02-07 01:19:59 -06001227 }
Vernon Mauery6475b5c2023-07-27 14:52:51 -07001228 case dcmi::DCMIConfigParameters::DiscoveryConfig:
1229 {
1230 bool option12{};
1231 uint6_t reserved1{};
1232 bool randBackOff{};
1233 if (payload.unpack(option12, reserved1, randBackOff) ||
1234 !payload.fullyUnpacked())
1235 {
1236 return ipmi::responseReqDataLenInvalid();
1237 }
1238 // Systemd-networkd doesn't support Random Back off
1239 if (reserved1 || randBackOff)
1240 {
1241 return ipmi::responseInvalidFieldRequest();
1242 }
1243 dcmi::setDHCPOption(ctx, dcmi::dhcpOpt12Enabled, option12);
1244 break;
1245 }
1246 // Systemd-networkd doesn't allow to configure DHCP timigs
1247 case dcmi::DCMIConfigParameters::DHCPTiming1:
1248 case dcmi::DCMIConfigParameters::DHCPTiming2:
1249 case dcmi::DCMIConfigParameters::DHCPTiming3:
1250 default:
1251 return ipmi::responseInvalidFieldRequest();
Nagaraju Goruganti22be97b2018-02-07 01:19:59 -06001252 }
Vernon Mauery6475b5c2023-07-27 14:52:51 -07001253 return ipmi::responseSuccess();
Nagaraju Goruganti22be97b2018-02-07 01:19:59 -06001254}
1255
Patrick Williams1318a5e2024-08-16 15:19:54 -04001256ipmi::RspType<ipmi::message::Payload> getDCMIConfParams(
1257 ipmi::Context::ptr& ctx, uint8_t parameter, uint8_t setSelector)
Nagaraju Goruganti22be97b2018-02-07 01:19:59 -06001258{
Vernon Mauery6475b5c2023-07-27 14:52:51 -07001259 if (setSelector)
Nagaraju Goruganti22be97b2018-02-07 01:19:59 -06001260 {
Vernon Mauery6475b5c2023-07-27 14:52:51 -07001261 return ipmi::responseInvalidFieldRequest();
Nagaraju Goruganti22be97b2018-02-07 01:19:59 -06001262 }
Vernon Mauery6475b5c2023-07-27 14:52:51 -07001263 ipmi::message::Payload payload;
1264 payload.pack(dcmi::specMajorVersion, dcmi::specMinorVersion,
1265 dcmi::configParameterRevision);
Nagaraju Goruganti22be97b2018-02-07 01:19:59 -06001266
Vernon Mauery6475b5c2023-07-27 14:52:51 -07001267 // Take action based on the Parameter Selector
1268 switch (static_cast<dcmi::DCMIConfigParameters>(parameter))
Nagaraju Goruganti22be97b2018-02-07 01:19:59 -06001269 {
Vernon Mauery6475b5c2023-07-27 14:52:51 -07001270 case dcmi::DCMIConfigParameters::ActivateDHCP:
1271 payload.pack(dcmi::activateDhcpReply);
1272 break;
1273 case dcmi::DCMIConfigParameters::DiscoveryConfig:
Nagaraju Goruganti22be97b2018-02-07 01:19:59 -06001274 {
Vernon Mauery6475b5c2023-07-27 14:52:51 -07001275 uint8_t discovery{};
1276 std::optional<bool> enabled =
1277 dcmi::getDHCPOption(ctx, dcmi::dhcpOpt12Enabled);
1278 if (!enabled.has_value())
1279 {
1280 return ipmi::responseUnspecifiedError();
1281 }
1282 if (enabled.value())
1283 {
1284 discovery = dcmi::option12Mask;
1285 }
1286 payload.pack(discovery);
1287 break;
Nagaraju Goruganti22be97b2018-02-07 01:19:59 -06001288 }
Vernon Mauery6475b5c2023-07-27 14:52:51 -07001289 // Get below values from Systemd-networkd source code
1290 case dcmi::DCMIConfigParameters::DHCPTiming1:
1291 payload.pack(dcmi::dhcpTiming1);
1292 break;
1293 case dcmi::DCMIConfigParameters::DHCPTiming2:
1294 payload.pack(dcmi::dhcpTiming2);
1295 break;
1296 case dcmi::DCMIConfigParameters::DHCPTiming3:
1297 payload.pack(dcmi::dhcpTiming3);
1298 break;
1299 default:
1300 return ipmi::responseInvalidFieldRequest();
Nagaraju Goruganti22be97b2018-02-07 01:19:59 -06001301 }
1302
Thang Tranaf762de2023-12-18 11:19:28 +07001303 return ipmi::responseSuccess(payload);
Nagaraju Goruganti22be97b2018-02-07 01:19:59 -06001304}
1305
Vernon Mauery056fab12023-07-27 14:25:24 -07001306static std::optional<uint16_t> readPower(ipmi::Context::ptr& ctx)
Marri Devender Rao66c5fda2018-01-18 10:48:37 -06001307{
Vernon Mauery056fab12023-07-27 14:25:24 -07001308 std::ifstream sensorFile(POWER_READING_SENSOR);
1309 std::string objectPath;
1310 if (!sensorFile.is_open())
1311 {
George Liu7fa28712024-07-17 19:26:23 +08001312 lg2::error(
1313 "Power reading configuration file not found: {POWER_SENSOR_FILE}",
1314 "POWER_SENSOR_FILE", std::string_view{POWER_READING_SENSOR});
Vernon Mauery056fab12023-07-27 14:25:24 -07001315 return std::nullopt;
1316 }
1317
1318 auto data = nlohmann::json::parse(sensorFile, nullptr, false);
1319 if (data.is_discarded())
1320 {
George Liu7fa28712024-07-17 19:26:23 +08001321 lg2::error("Error in parsing configuration file: {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 objectPath = data.value("path", "");
1327 if (objectPath.empty())
1328 {
George Liu7fa28712024-07-17 19:26:23 +08001329 lg2::error(
1330 "Power sensor D-Bus object path is empty: {POWER_SENSOR_FILE}",
1331 "POWER_SENSOR_FILE", std::string_view{POWER_READING_SENSOR});
Vernon Mauery056fab12023-07-27 14:25:24 -07001332 return std::nullopt;
1333 }
1334
1335 // Return default value if failed to read from D-Bus object
1336 std::string service{};
Patrick Williams1318a5e2024-08-16 15:19:54 -04001337 boost::system::error_code ec =
1338 ipmi::getService(ctx, dcmi::sensorValueIntf, objectPath, service);
Vernon Mauery056fab12023-07-27 14:25:24 -07001339 if (ec.value())
1340 {
George Liu7fa28712024-07-17 19:26:23 +08001341 lg2::error("Failed to fetch service for D-Bus object, "
1342 "object path: {OBJECT_PATH}, interface: {INTERFACE}",
1343 "OBJECT_PATH", objectPath, "INTERFACE",
1344 dcmi::sensorValueIntf);
Vernon Mauery056fab12023-07-27 14:25:24 -07001345 return std::nullopt;
1346 }
1347
1348 // Read the sensor value and scale properties
1349 double value{};
1350 ec = ipmi::getDbusProperty(ctx, service, objectPath, dcmi::sensorValueIntf,
1351 dcmi::sensorValueProp, value);
1352 if (ec.value())
1353 {
George Liu7fa28712024-07-17 19:26:23 +08001354 lg2::error("Failed to read power value from D-Bus object, "
1355 "object path: {OBJECT_PATH}, interface: {INTERFACE}",
1356 "OBJECT_PATH", objectPath, "INTERFACE",
1357 dcmi::sensorValueIntf);
Vernon Mauery056fab12023-07-27 14:25:24 -07001358 return std::nullopt;
1359 }
1360 auto power = static_cast<uint16_t>(value);
1361 return power;
1362}
1363
1364ipmi::RspType<uint16_t, // current power
1365 uint16_t, // minimum power
1366 uint16_t, // maximum power
1367 uint16_t, // average power
1368 uint32_t, // timestamp
1369 uint32_t, // sample period ms
1370 uint6_t, // reserved
1371 bool, // power measurement active
1372 bool // reserved
1373 >
1374 getPowerReading(ipmi::Context::ptr& ctx, uint8_t mode, uint8_t attributes,
1375 uint8_t reserved)
1376{
Kirill Pakhomov2c2af2c2018-11-06 16:06:10 +03001377 if (!dcmi::isDCMIPowerMgmtSupported())
1378 {
George Liu7fa28712024-07-17 19:26:23 +08001379 lg2::error("DCMI Power management is unsupported!");
Vernon Mauery056fab12023-07-27 14:25:24 -07001380 return ipmi::responseInvalidCommand();
1381 }
1382 if (reserved)
1383 {
1384 return ipmi::responseInvalidFieldRequest();
Kirill Pakhomov2c2af2c2018-11-06 16:06:10 +03001385 }
1386
Vernon Mauery056fab12023-07-27 14:25:24 -07001387 enum class PowerMode : uint8_t
1388 {
1389 SystemPowerStatistics = 1,
1390 EnhancedSystemPowerStatistics = 2,
1391 };
Marri Devender Rao9c966e02018-01-22 05:55:10 -06001392
Vernon Mauery056fab12023-07-27 14:25:24 -07001393 if (static_cast<PowerMode>(mode) != PowerMode::SystemPowerStatistics)
Marri Devender Rao66c5fda2018-01-18 10:48:37 -06001394 {
Vernon Mauery056fab12023-07-27 14:25:24 -07001395 return ipmi::responseInvalidFieldRequest();
Marri Devender Rao66c5fda2018-01-18 10:48:37 -06001396 }
Vernon Mauery056fab12023-07-27 14:25:24 -07001397 if (attributes)
Marri Devender Rao66c5fda2018-01-18 10:48:37 -06001398 {
Vernon Mauery056fab12023-07-27 14:25:24 -07001399 return ipmi::responseInvalidFieldRequest();
Marri Devender Rao66c5fda2018-01-18 10:48:37 -06001400 }
Marri Devender Rao9c966e02018-01-22 05:55:10 -06001401
Vernon Mauery056fab12023-07-27 14:25:24 -07001402 std::optional<uint16_t> powerResp = readPower(ctx);
1403 if (!powerResp)
1404 {
1405 return ipmi::responseUnspecifiedError();
1406 }
1407 auto& power = powerResp.value();
1408
Marri Devender Rao9c966e02018-01-22 05:55:10 -06001409 // TODO: openbmc/openbmc#2819
Gunnar Mills8466b792018-03-23 12:18:12 -05001410 // Minimum, Maximum, Average power, TimeFrame, TimeStamp,
Marri Devender Rao9c966e02018-01-22 05:55:10 -06001411 // PowerReadingState readings need to be populated
1412 // after Telemetry changes.
Vernon Mauery056fab12023-07-27 14:25:24 -07001413 constexpr uint32_t samplePeriod = 1;
1414 constexpr uint6_t reserved1 = 0;
1415 constexpr bool measurementActive = true;
1416 constexpr bool reserved2 = false;
1417 auto timestamp = static_cast<uint32_t>(time(nullptr));
1418 return ipmi::responseSuccess(power, power, power, power, timestamp,
1419 samplePeriod, reserved1, measurementActive,
1420 reserved2);
Marri Devender Rao66c5fda2018-01-18 10:48:37 -06001421}
1422
Deepak Kodihalli0b459552018-02-06 06:25:12 -06001423namespace dcmi
1424{
1425namespace sensor_info
1426{
1427
Patrick Williams69b4c282025-03-03 11:19:13 -05001428std::tuple<std::vector<uint16_t>, uint8_t> read(
1429 const std::string& type, uint8_t instance, const nlohmann::json& config,
1430 uint8_t count)
Deepak Kodihallidd4cff12018-02-06 06:48:29 -06001431{
Vernon Mauery53d0cf12023-07-27 14:32:44 -07001432 std::vector<uint16_t> responses{};
Deepak Kodihallidd4cff12018-02-06 06:48:29 -06001433
Vernon Mauery53d0cf12023-07-27 14:32:44 -07001434 static const std::vector<nlohmann::json> empty{};
1435 std::vector<nlohmann::json> readings = config.value(type, empty);
1436 uint8_t totalInstances = std::min(readings.size(), maxInstances);
Deepak Kodihallidd4cff12018-02-06 06:48:29 -06001437 for (const auto& reading : readings)
1438 {
Vernon Mauery53d0cf12023-07-27 14:32:44 -07001439 // limit to requested count
1440 if (responses.size() == count)
1441 {
1442 break;
1443 }
1444
Deepak Kodihallidd4cff12018-02-06 06:48:29 -06001445 uint8_t instanceNum = reading.value("instance", 0);
Vernon Mauery53d0cf12023-07-27 14:32:44 -07001446 // Not in the instance range we're interested in
1447 if (instanceNum < instance)
Deepak Kodihallidd4cff12018-02-06 06:48:29 -06001448 {
1449 continue;
1450 }
1451
Thang Tran5ea83fa2023-10-16 14:37:56 +07001452 uint16_t recordId = reading.value("record_id", 0);
Vernon Mauery53d0cf12023-07-27 14:32:44 -07001453 responses.emplace_back(recordId);
Deepak Kodihallidd4cff12018-02-06 06:48:29 -06001454 }
1455
Vernon Mauery53d0cf12023-07-27 14:32:44 -07001456 return std::make_tuple(responses, totalInstances);
Deepak Kodihalli0b459552018-02-06 06:25:12 -06001457}
1458
1459} // namespace sensor_info
1460} // namespace dcmi
1461
Vernon Mauery53d0cf12023-07-27 14:32:44 -07001462ipmi::RspType<uint8_t, // total available instances
1463 uint8_t, // number of records in this response
1464 std::vector<uint16_t> // records
1465 >
1466 getSensorInfo(uint8_t sensorType, uint8_t entityId, uint8_t entityInstance,
1467 uint8_t instanceStart)
Deepak Kodihalli0b459552018-02-06 06:25:12 -06001468{
Vernon Mauery53d0cf12023-07-27 14:32:44 -07001469 auto it = dcmi::entityIdToName.find(entityId);
Deepak Kodihalli0b459552018-02-06 06:25:12 -06001470 if (it == dcmi::entityIdToName.end())
1471 {
George Liu7fa28712024-07-17 19:26:23 +08001472 lg2::error("Unknown Entity ID: {ENTITY_ID}", "ENTITY_ID", entityId);
Vernon Mauery53d0cf12023-07-27 14:32:44 -07001473 return ipmi::responseInvalidFieldRequest();
Deepak Kodihalli0b459552018-02-06 06:25:12 -06001474 }
1475
Vernon Mauery53d0cf12023-07-27 14:32:44 -07001476 if (sensorType != dcmi::temperatureSensorType)
Deepak Kodihalli0b459552018-02-06 06:25:12 -06001477 {
George Liu7fa28712024-07-17 19:26:23 +08001478 lg2::error("Invalid sensor type: {SENSOR_TYPE}", "SENSOR_TYPE",
1479 sensorType);
Vernon Mauery53d0cf12023-07-27 14:32:44 -07001480 return ipmi::responseInvalidFieldRequest();
Deepak Kodihalli0b459552018-02-06 06:25:12 -06001481 }
1482
Vernon Mauery53d0cf12023-07-27 14:32:44 -07001483 nlohmann::json config = dcmi::parseJSONConfig(dcmi::gDCMISensorsConfig);
Deepak Kodihalli0b459552018-02-06 06:25:12 -06001484
Vernon Mauery53d0cf12023-07-27 14:32:44 -07001485 uint8_t requestedRecords = (entityInstance == 0) ? dcmi::maxRecords : 1;
1486 // Read requested instances
1487 const auto& [sensors, totalInstances] = dcmi::sensor_info::read(
1488 it->second, instanceStart, config, requestedRecords);
1489 uint8_t numRecords = sensors.size();
Deepak Kodihalli0b459552018-02-06 06:25:12 -06001490
Vernon Mauery53d0cf12023-07-27 14:32:44 -07001491 return ipmi::responseSuccess(totalInstances, numRecords, sensors);
Deepak Kodihalli0b459552018-02-06 06:25:12 -06001492}
1493
George Liu5087b072025-03-11 19:28:17 +08001494void registerNetFnDcmiFunctions()
Chris Austen1810bec2015-10-13 12:12:39 -05001495{
Tom05732372016-09-06 17:21:23 +05301496 // <Get Power Limit>
Vernon Maueryd4222fd2023-07-27 11:26:51 -07001497 registerGroupHandler(ipmi::prioOpenBmcBase, ipmi::groupDCMI,
1498 ipmi::dcmi::cmdGetPowerLimit, ipmi::Privilege::User,
1499 getPowerLimit);
Tom Joseph6f6dd4d2017-07-12 20:07:11 +05301500
Tom Joseph46fa37d2017-07-26 18:11:55 +05301501 // <Set Power Limit>
Vernon Maueryd4222fd2023-07-27 11:26:51 -07001502 registerGroupHandler(ipmi::prioOpenBmcBase, ipmi::groupDCMI,
1503 ipmi::dcmi::cmdSetPowerLimit,
1504 ipmi::Privilege::Operator, setPowerLimit);
Tom Joseph46fa37d2017-07-26 18:11:55 +05301505
Tom Joseph6c8d51b2017-07-26 18:18:06 +05301506 // <Activate/Deactivate Power Limit>
Vernon Maueryd4222fd2023-07-27 11:26:51 -07001507 registerGroupHandler(ipmi::prioOpenBmcBase, ipmi::groupDCMI,
1508 ipmi::dcmi::cmdActDeactivatePwrLimit,
1509 ipmi::Privilege::Operator, applyPowerLimit);
Tom Joseph6c8d51b2017-07-26 18:18:06 +05301510
Tom Joseph6f6dd4d2017-07-12 20:07:11 +05301511 // <Get Asset Tag>
Vernon Mauerydca47202023-07-27 11:32:01 -07001512 registerGroupHandler(ipmi::prioOpenBmcBase, ipmi::groupDCMI,
1513 ipmi::dcmi::cmdGetAssetTag, ipmi::Privilege::User,
1514 getAssetTag);
Tom Joseph545dd232017-07-12 20:20:49 +05301515
1516 // <Set Asset Tag>
Vernon Mauerydca47202023-07-27 11:32:01 -07001517 registerGroupHandler(ipmi::prioOpenBmcBase, ipmi::groupDCMI,
1518 ipmi::dcmi::cmdSetAssetTag, ipmi::Privilege::Operator,
1519 setAssetTag);
Vladislav Vovchenko8f7a6f62017-08-17 00:31:14 +03001520
Gunnar Mills8991dd62017-10-25 17:11:29 -05001521 // <Get Management Controller Identifier String>
Vernon Maueryefb5ae52023-07-27 11:35:43 -07001522 registerGroupHandler(ipmi::prioOpenBmcBase, ipmi::groupDCMI,
1523 ipmi::dcmi::cmdGetMgmtCntlrIdString,
1524 ipmi::Privilege::User, getMgmntCtrlIdStr);
Vladislav Vovchenko8f7a6f62017-08-17 00:31:14 +03001525
1526 // <Set Management Controller Identifier String>
Vernon Maueryefb5ae52023-07-27 11:35:43 -07001527 registerGroupHandler(ipmi::prioOpenBmcBase, ipmi::groupDCMI,
1528 ipmi::dcmi::cmdSetMgmtCntlrIdString,
1529 ipmi::Privilege::Admin, setMgmntCtrlIdStr);
Vladislav Vovchenko8f7a6f62017-08-17 00:31:14 +03001530
Dhruvaraj Subhashchandrane29be412018-01-16 05:11:56 -06001531 // <Get DCMI capabilities>
Vernon Maueryf038dc02023-07-27 14:06:11 -07001532 registerGroupHandler(ipmi::prioOpenBmcBase, ipmi::groupDCMI,
1533 ipmi::dcmi::cmdGetDcmiCapabilitiesInfo,
1534 ipmi::Privilege::User, getDCMICapabilities);
Deepak Kodihalliee717d72018-01-24 04:53:09 -06001535
Marri Devender Rao66c5fda2018-01-18 10:48:37 -06001536 // <Get Power Reading>
Vernon Mauery056fab12023-07-27 14:25:24 -07001537 registerGroupHandler(ipmi::prioOpenBmcBase, ipmi::groupDCMI,
1538 ipmi::dcmi::cmdGetPowerReading, ipmi::Privilege::User,
1539 getPowerReading);
1540
adarshgrami042e9db2022-09-15 10:34:34 +05301541// The Get sensor should get the senor details dynamically when
1542// FEATURE_DYNAMIC_SENSORS is enabled.
1543#ifndef FEATURE_DYNAMIC_SENSORS
Deepak Kodihalli0b459552018-02-06 06:25:12 -06001544 // <Get Sensor Info>
Vernon Mauery53d0cf12023-07-27 14:32:44 -07001545 registerGroupHandler(ipmi::prioOpenBmcBase, ipmi::groupDCMI,
1546 ipmi::dcmi::cmdGetDcmiSensorInfo,
1547 ipmi::Privilege::Operator, getSensorInfo);
Thang Tran3dad8262023-08-17 15:20:56 +07001548
1549 // <Get Temperature Readings>
1550 registerGroupHandler(ipmi::prioOpenBmcBase, ipmi::groupDCMI,
1551 ipmi::dcmi::cmdGetTemperatureReadings,
1552 ipmi::Privilege::User, getTempReadings);
adarshgrami042e9db2022-09-15 10:34:34 +05301553#endif
Nagaraju Goruganti22be97b2018-02-07 01:19:59 -06001554 // <Get DCMI Configuration Parameters>
Vernon Mauery6475b5c2023-07-27 14:52:51 -07001555 registerGroupHandler(ipmi::prioOpenBmcBase, ipmi::groupDCMI,
1556 ipmi::dcmi::cmdGetDcmiConfigParameters,
1557 ipmi::Privilege::User, getDCMIConfParams);
Nagaraju Goruganti22be97b2018-02-07 01:19:59 -06001558
1559 // <Set DCMI Configuration Parameters>
Vernon Mauery6475b5c2023-07-27 14:52:51 -07001560 registerGroupHandler(ipmi::prioOpenBmcBase, ipmi::groupDCMI,
1561 ipmi::dcmi::cmdSetDcmiConfigParameters,
1562 ipmi::Privilege::Admin, setDCMIConfParams);
Nagaraju Goruganti22be97b2018-02-07 01:19:59 -06001563
Chris Austen1810bec2015-10-13 12:12:39 -05001564 return;
1565}